From 4dccc413ddf822ca6f1856fd2d8ab80185203eed Mon Sep 17 00:00:00 2001 From: Lloyd Pique Date: Mon, 22 Jan 2018 17:21:36 -0800 Subject: SF: Separate SurfaceInterceptor into interface and impl SurfaceInterceptor is now an abstract interface. impl::SurfaceInterceptor is the normal implementation. This allows unit tests to replace it with a GMock. Test: Builds Bug: 74827900 Change-Id: I4d5bb2649b0c7a8ec1e6c0abf01ab53174d06a19 --- services/surfaceflinger/EventThread.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'services/surfaceflinger/EventThread.cpp') diff --git a/services/surfaceflinger/EventThread.cpp b/services/surfaceflinger/EventThread.cpp index 1f4f5a53f4..bb9c0703ed 100644 --- a/services/surfaceflinger/EventThread.cpp +++ b/services/surfaceflinger/EventThread.cpp @@ -217,7 +217,7 @@ Vector > EventThread::waitForEventLocked( if (timestamp) { // we have a vsync event to dispatch if (mInterceptVSyncs) { - mFlinger.mInterceptor.saveVSyncEvent(timestamp); + mFlinger.mInterceptor->saveVSyncEvent(timestamp); } *event = mVSyncEvent[i]; mVSyncEvent[i].header.timestamp = 0; -- cgit v1.2.3-59-g8ed1b From 00a6fa2f33e2a9e42e4108677fb4abacfca6fd11 Mon Sep 17 00:00:00 2001 From: Dominik Laskowski Date: Wed, 6 Jun 2018 16:42:02 -0700 Subject: SF: Decouple EventThread from DisplayDevice EventThread uses DisplayDevice::DisplayType constants, which will be removed in a follow-up CL. This CL replaces them with local constants as a stopgap until stable display IDs are propagated through the SF/WM interface. Bug: 74619554 Test: libsurfaceflinger_unittest Change-Id: I68be363dc58c5e3aa17d05fba156d520a01fa775 --- services/surfaceflinger/EventThread.cpp | 42 ++++++++++------------ services/surfaceflinger/EventThread.h | 16 +++++---- services/surfaceflinger/SurfaceFlinger.cpp | 15 ++++++-- .../tests/unittests/DisplayTransactionTest.cpp | 14 ++++++-- .../tests/unittests/EventThreadTest.cpp | 33 +++++++++-------- .../tests/unittests/mock/MockEventThread.h | 2 +- 6 files changed, 69 insertions(+), 53 deletions(-) (limited to 'services/surfaceflinger/EventThread.cpp') diff --git a/services/surfaceflinger/EventThread.cpp b/services/surfaceflinger/EventThread.cpp index bc271c8ec5..5a8fd25270 100644 --- a/services/surfaceflinger/EventThread.cpp +++ b/services/surfaceflinger/EventThread.cpp @@ -155,20 +155,17 @@ void EventThread::onVSyncEvent(nsecs_t timestamp) { mCondition.notify_all(); } -void EventThread::onHotplugReceived(int type, bool connected) { - ALOGE_IF(type >= DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES, - "received hotplug event for an invalid display (id=%d)", type); - +void EventThread::onHotplugReceived(DisplayType displayType, bool connected) { std::lock_guard lock(mMutex); - if (type < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) { - DisplayEventReceiver::Event event; - event.header.type = DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG; - event.header.id = type; - event.header.timestamp = systemTime(); - event.hotplug.connected = connected; - mPendingEvents.add(event); - mCondition.notify_all(); - } + + DisplayEventReceiver::Event event; + event.header.type = DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG; + event.header.id = displayType == DisplayType::Primary ? 0 : 1; + event.header.timestamp = systemTime(); + event.hotplug.connected = connected; + + mPendingEvents.add(event); + mCondition.notify_all(); } void EventThread::threadMain() NO_THREAD_SAFETY_ANALYSIS { @@ -205,7 +202,7 @@ void EventThread::threadMain() NO_THREAD_SAFETY_ANALYSIS { // This will return when (1) a vsync event has been received, and (2) there was // at least one connection interested in receiving it when we started waiting. Vector > EventThread::waitForEventLocked( - std::unique_lock* lock, DisplayEventReceiver::Event* event) { + std::unique_lock* lock, DisplayEventReceiver::Event* outEvent) { Vector > signalConnections; while (signalConnections.isEmpty() && mKeepRunning) { @@ -214,16 +211,16 @@ Vector > EventThread::waitForEventLocked( size_t vsyncCount = 0; nsecs_t timestamp = 0; - for (int32_t i = 0; i < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES; i++) { - timestamp = mVSyncEvent[i].header.timestamp; + for (auto& event : mVSyncEvent) { + timestamp = event.header.timestamp; if (timestamp) { // we have a vsync event to dispatch if (mInterceptVSyncsCallback) { mInterceptVSyncsCallback(timestamp); } - *event = mVSyncEvent[i]; - mVSyncEvent[i].header.timestamp = 0; - vsyncCount = mVSyncEvent[i].vsync.count; + *outEvent = event; + event.header.timestamp = 0; + vsyncCount = event.vsync.count; break; } } @@ -233,7 +230,7 @@ Vector > EventThread::waitForEventLocked( eventPending = !mPendingEvents.isEmpty(); if (eventPending) { // we have some other event to dispatch - *event = mPendingEvents[0]; + *outEvent = mPendingEvents[0]; mPendingEvents.removeAt(0); } } @@ -319,7 +316,7 @@ Vector > EventThread::waitForEventLocked( // FIXME: how do we decide which display id the fake // vsync came from ? mVSyncEvent[0].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC; - mVSyncEvent[0].header.id = DisplayDevice::DISPLAY_PRIMARY; + mVSyncEvent[0].header.id = 0; mVSyncEvent[0].header.timestamp = systemTime(SYSTEM_TIME_MONOTONIC); mVSyncEvent[0].vsync.count++; } @@ -364,8 +361,7 @@ void EventThread::dump(String8& result) const { result.appendFormat("VSYNC state: %s\n", mDebugVsyncEnabled ? "enabled" : "disabled"); result.appendFormat(" soft-vsync: %s\n", mUseSoftwareVSync ? "enabled" : "disabled"); result.appendFormat(" numListeners=%zu,\n events-delivered: %u\n", - mDisplayEventConnections.size(), - mVSyncEvent[DisplayDevice::DISPLAY_PRIMARY].vsync.count); + mDisplayEventConnections.size(), mVSyncEvent[0].vsync.count); for (size_t i = 0; i < mDisplayEventConnections.size(); i++) { sp connection = mDisplayEventConnections.itemAt(i).promote(); result.appendFormat(" %p: count=%d\n", connection.get(), diff --git a/services/surfaceflinger/EventThread.h b/services/surfaceflinger/EventThread.h index 9c13ed2755..a0262b2ad9 100644 --- a/services/surfaceflinger/EventThread.h +++ b/services/surfaceflinger/EventThread.h @@ -16,9 +16,11 @@ #pragma once -#include #include + +#include #include +#include #include #include @@ -31,8 +33,6 @@ #include #include -#include "DisplayDevice.h" - // --------------------------------------------------------------------------- namespace android { // --------------------------------------------------------------------------- @@ -59,6 +59,9 @@ public: class EventThread { public: + // TODO: Remove once stable display IDs are plumbed through SF/WM interface. + enum class DisplayType { Primary, External }; + virtual ~EventThread(); virtual sp createEventConnection() const = 0; @@ -70,7 +73,7 @@ public: virtual void onScreenAcquired() = 0; // called when receiving a hotplug event - virtual void onHotplugReceived(int type, bool connected) = 0; + virtual void onHotplugReceived(DisplayType displayType, bool connected) = 0; virtual void dump(String8& result) const = 0; @@ -122,7 +125,7 @@ public: void onScreenAcquired() override; // called when receiving a hotplug event - void onHotplugReceived(int type, bool connected) override; + void onHotplugReceived(DisplayType displayType, bool connected) override; void dump(String8& result) const override; @@ -155,8 +158,7 @@ private: // protected by mLock SortedVector> mDisplayEventConnections GUARDED_BY(mMutex); Vector mPendingEvents GUARDED_BY(mMutex); - DisplayEventReceiver::Event mVSyncEvent[DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES] GUARDED_BY( - mMutex); + std::array mVSyncEvent GUARDED_BY(mMutex); bool mUseSoftwareVSync GUARDED_BY(mMutex) = false; bool mVsyncEnabled GUARDED_BY(mMutex) = false; bool mKeepRunning GUARDED_BY(mMutex) = true; diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index db534cc60a..5acf2b8f85 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -2293,8 +2293,11 @@ void SurfaceFlinger::processDisplayChangesLocked() { if (const auto display = getDisplayDeviceLocked(draw.keyAt(i))) { display->disconnect(getHwComposer()); } - if (draw[i].type < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) - mEventThread->onHotplugReceived(draw[i].type, false); + if (draw[i].type == DisplayDevice::DISPLAY_PRIMARY) { + mEventThread->onHotplugReceived(EventThread::DisplayType::Primary, false); + } else if (draw[i].type == DisplayDevice::DISPLAY_EXTERNAL) { + mEventThread->onHotplugReceived(EventThread::DisplayType::External, false); + } mDisplays.erase(draw.keyAt(i)); } else { // this display is in both lists. see if something changed. @@ -2395,7 +2398,13 @@ void SurfaceFlinger::processDisplayChangesLocked() { setupNewDisplayDeviceInternal(displayToken, displayId, state, dispSurface, producer)); if (!state.isVirtual()) { - mEventThread->onHotplugReceived(state.type, true); + if (state.type == DisplayDevice::DISPLAY_PRIMARY) { + mEventThread->onHotplugReceived(EventThread::DisplayType::Primary, + true); + } else if (state.type == DisplayDevice::DISPLAY_EXTERNAL) { + mEventThread->onHotplugReceived(EventThread::DisplayType::External, + true); + } } } } diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp index 7928cba108..8d18d76fff 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp @@ -1165,13 +1165,23 @@ void HandleTransactionLockedTest::setupCommonCallExpectationsForConnectProcessin Case::PerFrameMetadataSupport::setupComposerCallExpectations(this); EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1); - EXPECT_CALL(*mEventThread, onHotplugReceived(Case::Display::TYPE, true)).Times(1); + EXPECT_CALL(*mEventThread, + onHotplugReceived(Case::Display::TYPE == DisplayDevice::DISPLAY_PRIMARY + ? EventThread::DisplayType::Primary + : EventThread::DisplayType::External, + true)) + .Times(1); } template void HandleTransactionLockedTest::setupCommonCallExpectationsForDisconnectProcessing() { EXPECT_CALL(*mSurfaceInterceptor, saveDisplayDeletion(_)).Times(1); - EXPECT_CALL(*mEventThread, onHotplugReceived(Case::Display::TYPE, false)).Times(1); + EXPECT_CALL(*mEventThread, + onHotplugReceived(Case::Display::TYPE == DisplayDevice::DISPLAY_PRIMARY + ? EventThread::DisplayType::Primary + : EventThread::DisplayType::External, + false)) + .Times(1); } template diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp index 80fdb80264..19747bd442 100644 --- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp +++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp @@ -71,7 +71,8 @@ protected: ConnectionEventRecorder& connectionEventRecorder, nsecs_t expectedTimestamp, unsigned expectedCount); void expectVsyncEventReceivedByConnection(nsecs_t expectedTimestamp, unsigned expectedCount); - void expectHotplugEventReceivedByConnection(int expectedDisplayType, bool expectedConnected); + void expectHotplugEventReceivedByConnection(EventThread::DisplayType expectedDisplayType, + bool expectedConnected); AsyncCallRecorder mVSyncSetEnabledCallRecorder; AsyncCallRecorder mVSyncSetCallbackCallRecorder; @@ -169,13 +170,16 @@ void EventThreadTest::expectVsyncEventReceivedByConnection(nsecs_t expectedTimes expectedCount); } -void EventThreadTest::expectHotplugEventReceivedByConnection(int expectedDisplayType, - bool expectedConnected) { +void EventThreadTest::expectHotplugEventReceivedByConnection( + EventThread::DisplayType expectedDisplayType, bool expectedConnected) { + const uint32_t expectedDisplayId = + expectedDisplayType == EventThread::DisplayType::Primary ? 0 : 1; + auto args = mConnectionEventCallRecorder.waitForCall(); ASSERT_TRUE(args.has_value()); const auto& event = std::get<0>(args.value()); EXPECT_EQ(DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG, event.header.type); - EXPECT_EQ(static_cast(expectedDisplayType), event.header.id); + EXPECT_EQ(expectedDisplayId, event.header.id); EXPECT_EQ(expectedConnected, event.hotplug.connected); } @@ -394,28 +398,23 @@ TEST_F(EventThreadTest, setPhaseOffsetForwardsToVSyncSource) { } TEST_F(EventThreadTest, postHotplugPrimaryDisconnect) { - mThread->onHotplugReceived(DisplayDevice::DISPLAY_PRIMARY, false); - expectHotplugEventReceivedByConnection(DisplayDevice::DISPLAY_PRIMARY, false); + mThread->onHotplugReceived(EventThread::DisplayType::Primary, false); + expectHotplugEventReceivedByConnection(EventThread::DisplayType::Primary, false); } TEST_F(EventThreadTest, postHotplugPrimaryConnect) { - mThread->onHotplugReceived(DisplayDevice::DISPLAY_PRIMARY, true); - expectHotplugEventReceivedByConnection(DisplayDevice::DISPLAY_PRIMARY, true); + mThread->onHotplugReceived(EventThread::DisplayType::Primary, true); + expectHotplugEventReceivedByConnection(EventThread::DisplayType::Primary, true); } TEST_F(EventThreadTest, postHotplugExternalDisconnect) { - mThread->onHotplugReceived(DisplayDevice::DISPLAY_EXTERNAL, false); - expectHotplugEventReceivedByConnection(DisplayDevice::DISPLAY_EXTERNAL, false); + mThread->onHotplugReceived(EventThread::DisplayType::External, false); + expectHotplugEventReceivedByConnection(EventThread::DisplayType::External, false); } TEST_F(EventThreadTest, postHotplugExternalConnect) { - mThread->onHotplugReceived(DisplayDevice::DISPLAY_EXTERNAL, true); - expectHotplugEventReceivedByConnection(DisplayDevice::DISPLAY_EXTERNAL, true); -} - -TEST_F(EventThreadTest, postHotplugVirtualDisconnectIsFilteredOut) { - mThread->onHotplugReceived(DisplayDevice::DISPLAY_VIRTUAL, false); - EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value()); + mThread->onHotplugReceived(EventThread::DisplayType::External, true); + expectHotplugEventReceivedByConnection(EventThread::DisplayType::External, true); } } // namespace diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h index e6ea6634c0..df9bfc6d96 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h +++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h @@ -31,7 +31,7 @@ public: MOCK_CONST_METHOD0(createEventConnection, sp()); MOCK_METHOD0(onScreenReleased, void()); MOCK_METHOD0(onScreenAcquired, void()); - MOCK_METHOD2(onHotplugReceived, void(int, bool)); + MOCK_METHOD2(onHotplugReceived, void(DisplayType, bool)); MOCK_CONST_METHOD1(dump, void(String8&)); MOCK_METHOD1(setPhaseOffset, void(nsecs_t phaseOffset)); }; -- cgit v1.2.3-59-g8ed1b From fefcb58f5789f6ac441ae0a342d4d0878236bf94 Mon Sep 17 00:00:00 2001 From: Ana Krulec Date: Tue, 7 Aug 2018 14:22:37 -0700 Subject: SF: Move relevant scheduler files into one directory. Scheduler (see go/surface-flinger-scheduler) is going to live in its own directory. This CL just moves the relevant files into that directory. No changes to business logic. Test: all SF tests pass. Change-Id: Iff0717f9867316b28e68fd8311bd9fdc4e029951 --- services/surfaceflinger/Android.bp | 8 +- services/surfaceflinger/BufferLayerConsumer.cpp | 2 +- services/surfaceflinger/DispSync.cpp | 730 --------------------- services/surfaceflinger/DispSync.h | 245 ------- services/surfaceflinger/EventControlThread.cpp | 75 --- services/surfaceflinger/EventControlThread.h | 63 -- services/surfaceflinger/EventThread.cpp | 409 ------------ services/surfaceflinger/EventThread.h | 173 ----- services/surfaceflinger/MessageQueue.cpp | 166 ----- services/surfaceflinger/MessageQueue.h | 143 ---- services/surfaceflinger/MonitoredProducer.cpp | 5 +- services/surfaceflinger/Scheduler/DispSync.cpp | 729 ++++++++++++++++++++ services/surfaceflinger/Scheduler/DispSync.h | 245 +++++++ .../Scheduler/EventControlThread.cpp | 75 +++ .../surfaceflinger/Scheduler/EventControlThread.h | 63 ++ services/surfaceflinger/Scheduler/EventThread.cpp | 410 ++++++++++++ services/surfaceflinger/Scheduler/EventThread.h | 173 +++++ services/surfaceflinger/Scheduler/MessageQueue.cpp | 166 +++++ services/surfaceflinger/Scheduler/MessageQueue.h | 143 ++++ services/surfaceflinger/Scheduler/VSyncModulator.h | 148 +++++ services/surfaceflinger/SurfaceFlinger.cpp | 9 +- services/surfaceflinger/SurfaceFlinger.h | 15 +- services/surfaceflinger/VSyncModulator.h | 159 ----- .../tests/unittests/EventControlThreadTest.cpp | 2 +- .../tests/unittests/EventThreadTest.cpp | 2 +- .../tests/unittests/mock/MockDispSync.h | 2 +- .../tests/unittests/mock/MockEventControlThread.h | 2 +- .../tests/unittests/mock/MockEventThread.h | 2 +- .../tests/unittests/mock/MockMessageQueue.h | 2 +- 29 files changed, 2177 insertions(+), 2189 deletions(-) delete mode 100644 services/surfaceflinger/DispSync.cpp delete mode 100644 services/surfaceflinger/DispSync.h delete mode 100644 services/surfaceflinger/EventControlThread.cpp delete mode 100644 services/surfaceflinger/EventControlThread.h delete mode 100644 services/surfaceflinger/EventThread.cpp delete mode 100644 services/surfaceflinger/EventThread.h delete mode 100644 services/surfaceflinger/MessageQueue.cpp delete mode 100644 services/surfaceflinger/MessageQueue.h create mode 100644 services/surfaceflinger/Scheduler/DispSync.cpp create mode 100644 services/surfaceflinger/Scheduler/DispSync.h create mode 100644 services/surfaceflinger/Scheduler/EventControlThread.cpp create mode 100644 services/surfaceflinger/Scheduler/EventControlThread.h create mode 100644 services/surfaceflinger/Scheduler/EventThread.cpp create mode 100644 services/surfaceflinger/Scheduler/EventThread.h create mode 100644 services/surfaceflinger/Scheduler/MessageQueue.cpp create mode 100644 services/surfaceflinger/Scheduler/MessageQueue.h create mode 100644 services/surfaceflinger/Scheduler/VSyncModulator.h delete mode 100644 services/surfaceflinger/VSyncModulator.h (limited to 'services/surfaceflinger/EventThread.cpp') diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp index 3fa1311ce9..d0aa742fef 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -105,11 +105,8 @@ filegroup { "DisplayHardware/HWComposerBufferCache.cpp", "DisplayHardware/PowerAdvisor.cpp", "DisplayHardware/VirtualDisplaySurface.cpp", - "DispSync.cpp", "Effects/Daltonizer.cpp", - "EventControlThread.cpp", "EventLog/EventLog.cpp", - "EventThread.cpp", "FrameTracker.cpp", "GpuService.cpp", "Layer.cpp", @@ -118,7 +115,6 @@ filegroup { "LayerRejecter.cpp", "LayerStats.cpp", "LayerVector.cpp", - "MessageQueue.cpp", "MonitoredProducer.cpp", "RenderArea.cpp", "RenderEngine/Description.cpp", @@ -131,6 +127,10 @@ filegroup { "RenderEngine/RenderEngine.cpp", "RenderEngine/Surface.cpp", "RenderEngine/Texture.cpp", + "Scheduler/DispSync.cpp", + "Scheduler/EventControlThread.cpp", + "Scheduler/EventThread.cpp", + "Scheduler/MessageQueue.cpp", "StartPropertySetThread.cpp", "SurfaceFlinger.cpp", "SurfaceInterceptor.cpp", diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp index 8788d47451..9096d4c719 100644 --- a/services/surfaceflinger/BufferLayerConsumer.cpp +++ b/services/surfaceflinger/BufferLayerConsumer.cpp @@ -21,10 +21,10 @@ #include "BufferLayerConsumer.h" -#include "DispSync.h" #include "Layer.h" #include "RenderEngine/Image.h" #include "RenderEngine/RenderEngine.h" +#include "Scheduler/DispSync.h" #include diff --git a/services/surfaceflinger/DispSync.cpp b/services/surfaceflinger/DispSync.cpp deleted file mode 100644 index b789d04ce9..0000000000 --- a/services/surfaceflinger/DispSync.cpp +++ /dev/null @@ -1,730 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define ATRACE_TAG ATRACE_TAG_GRAPHICS -//#define LOG_NDEBUG 0 - -// This is needed for stdint.h to define INT64_MAX in C++ -#define __STDC_LIMIT_MACROS - -#include - -#include - -#include -#include -#include -#include - -#include - -#include "DispSync.h" -#include "EventLog/EventLog.h" -#include "SurfaceFlinger.h" - -using std::max; -using std::min; - -namespace android { - -DispSync::~DispSync() = default; - -namespace impl { - -// Setting this to true enables verbose tracing that can be used to debug -// vsync event model or phase issues. -static const bool kTraceDetailedInfo = false; - -// Setting this to true adds a zero-phase tracer for correlating with hardware -// vsync events -static const bool kEnableZeroPhaseTracer = false; - -// This is the threshold used to determine when hardware vsync events are -// needed to re-synchronize the software vsync model with the hardware. The -// error metric used is the mean of the squared difference between each -// present time and the nearest software-predicted vsync. -static const nsecs_t kErrorThreshold = 160000000000; // 400 usec squared - -#undef LOG_TAG -#define LOG_TAG "DispSyncThread" -class DispSyncThread : public Thread { -public: - explicit DispSyncThread(const char* name) - : mName(name), - mStop(false), - mPeriod(0), - mPhase(0), - mReferenceTime(0), - mWakeupLatency(0), - mFrameNumber(0) {} - - virtual ~DispSyncThread() {} - - void updateModel(nsecs_t period, nsecs_t phase, nsecs_t referenceTime) { - if (kTraceDetailedInfo) ATRACE_CALL(); - Mutex::Autolock lock(mMutex); - mPeriod = period; - mPhase = phase; - mReferenceTime = referenceTime; - ALOGV("[%s] updateModel: mPeriod = %" PRId64 ", mPhase = %" PRId64 - " mReferenceTime = %" PRId64, - mName, ns2us(mPeriod), ns2us(mPhase), ns2us(mReferenceTime)); - mCond.signal(); - } - - void stop() { - if (kTraceDetailedInfo) ATRACE_CALL(); - Mutex::Autolock lock(mMutex); - mStop = true; - mCond.signal(); - } - - virtual bool threadLoop() { - status_t err; - nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - - while (true) { - std::vector callbackInvocations; - - nsecs_t targetTime = 0; - - { // Scope for lock - Mutex::Autolock lock(mMutex); - - if (kTraceDetailedInfo) { - ATRACE_INT64("DispSync:Frame", mFrameNumber); - } - ALOGV("[%s] Frame %" PRId64, mName, mFrameNumber); - ++mFrameNumber; - - if (mStop) { - return false; - } - - if (mPeriod == 0) { - err = mCond.wait(mMutex); - if (err != NO_ERROR) { - ALOGE("error waiting for new events: %s (%d)", strerror(-err), err); - return false; - } - continue; - } - - targetTime = computeNextEventTimeLocked(now); - - bool isWakeup = false; - - if (now < targetTime) { - if (kTraceDetailedInfo) ATRACE_NAME("DispSync waiting"); - - if (targetTime == INT64_MAX) { - ALOGV("[%s] Waiting forever", mName); - err = mCond.wait(mMutex); - } else { - ALOGV("[%s] Waiting until %" PRId64, mName, ns2us(targetTime)); - err = mCond.waitRelative(mMutex, targetTime - now); - } - - if (err == TIMED_OUT) { - isWakeup = true; - } else if (err != NO_ERROR) { - ALOGE("error waiting for next event: %s (%d)", strerror(-err), err); - return false; - } - } - - now = systemTime(SYSTEM_TIME_MONOTONIC); - - // Don't correct by more than 1.5 ms - static const nsecs_t kMaxWakeupLatency = us2ns(1500); - - if (isWakeup) { - mWakeupLatency = ((mWakeupLatency * 63) + (now - targetTime)) / 64; - mWakeupLatency = min(mWakeupLatency, kMaxWakeupLatency); - if (kTraceDetailedInfo) { - ATRACE_INT64("DispSync:WakeupLat", now - targetTime); - ATRACE_INT64("DispSync:AvgWakeupLat", mWakeupLatency); - } - } - - callbackInvocations = gatherCallbackInvocationsLocked(now); - } - - if (callbackInvocations.size() > 0) { - fireCallbackInvocations(callbackInvocations); - } - } - - return false; - } - - status_t addEventListener(const char* name, nsecs_t phase, DispSync::Callback* callback) { - if (kTraceDetailedInfo) ATRACE_CALL(); - Mutex::Autolock lock(mMutex); - - for (size_t i = 0; i < mEventListeners.size(); i++) { - if (mEventListeners[i].mCallback == callback) { - return BAD_VALUE; - } - } - - EventListener listener; - listener.mName = name; - listener.mPhase = phase; - listener.mCallback = callback; - - // We want to allow the firstmost future event to fire without - // allowing any past events to fire - listener.mLastEventTime = systemTime() - mPeriod / 2 + mPhase - mWakeupLatency; - - mEventListeners.push_back(listener); - - mCond.signal(); - - return NO_ERROR; - } - - status_t removeEventListener(DispSync::Callback* callback) { - if (kTraceDetailedInfo) ATRACE_CALL(); - Mutex::Autolock lock(mMutex); - - for (std::vector::iterator it = mEventListeners.begin(); - it != mEventListeners.end(); ++it) { - if (it->mCallback == callback) { - mEventListeners.erase(it); - mCond.signal(); - return NO_ERROR; - } - } - - return BAD_VALUE; - } - - status_t changePhaseOffset(DispSync::Callback* callback, nsecs_t phase) { - if (kTraceDetailedInfo) ATRACE_CALL(); - Mutex::Autolock lock(mMutex); - - for (auto& eventListener : mEventListeners) { - if (eventListener.mCallback == callback) { - const nsecs_t oldPhase = eventListener.mPhase; - eventListener.mPhase = phase; - - // Pretend that the last time this event was handled at the same frame but with the - // new offset to allow for a seamless offset change without double-firing or - // skipping. - nsecs_t diff = oldPhase - phase; - if (diff > mPeriod / 2) { - diff -= mPeriod; - } else if (diff < -mPeriod / 2) { - diff += mPeriod; - } - eventListener.mLastEventTime -= diff; - mCond.signal(); - return NO_ERROR; - } - } - - return BAD_VALUE; - } - -private: - struct EventListener { - const char* mName; - nsecs_t mPhase; - nsecs_t mLastEventTime; - DispSync::Callback* mCallback; - }; - - struct CallbackInvocation { - DispSync::Callback* mCallback; - nsecs_t mEventTime; - }; - - nsecs_t computeNextEventTimeLocked(nsecs_t now) { - if (kTraceDetailedInfo) ATRACE_CALL(); - ALOGV("[%s] computeNextEventTimeLocked", mName); - nsecs_t nextEventTime = INT64_MAX; - for (size_t i = 0; i < mEventListeners.size(); i++) { - nsecs_t t = computeListenerNextEventTimeLocked(mEventListeners[i], now); - - if (t < nextEventTime) { - nextEventTime = t; - } - } - - ALOGV("[%s] nextEventTime = %" PRId64, mName, ns2us(nextEventTime)); - return nextEventTime; - } - - std::vector gatherCallbackInvocationsLocked(nsecs_t now) { - if (kTraceDetailedInfo) ATRACE_CALL(); - ALOGV("[%s] gatherCallbackInvocationsLocked @ %" PRId64, mName, ns2us(now)); - - std::vector callbackInvocations; - nsecs_t onePeriodAgo = now - mPeriod; - - for (auto& eventListener : mEventListeners) { - nsecs_t t = computeListenerNextEventTimeLocked(eventListener, onePeriodAgo); - - if (t < now) { - CallbackInvocation ci; - ci.mCallback = eventListener.mCallback; - ci.mEventTime = t; - ALOGV("[%s] [%s] Preparing to fire", mName, eventListener.mName); - callbackInvocations.push_back(ci); - eventListener.mLastEventTime = t; - } - } - - return callbackInvocations; - } - - nsecs_t computeListenerNextEventTimeLocked(const EventListener& listener, nsecs_t baseTime) { - if (kTraceDetailedInfo) ATRACE_CALL(); - ALOGV("[%s] [%s] computeListenerNextEventTimeLocked(%" PRId64 ")", mName, listener.mName, - ns2us(baseTime)); - - nsecs_t lastEventTime = listener.mLastEventTime + mWakeupLatency; - ALOGV("[%s] lastEventTime: %" PRId64, mName, ns2us(lastEventTime)); - if (baseTime < lastEventTime) { - baseTime = lastEventTime; - ALOGV("[%s] Clamping baseTime to lastEventTime -> %" PRId64, mName, ns2us(baseTime)); - } - - baseTime -= mReferenceTime; - ALOGV("[%s] Relative baseTime = %" PRId64, mName, ns2us(baseTime)); - nsecs_t phase = mPhase + listener.mPhase; - ALOGV("[%s] Phase = %" PRId64, mName, ns2us(phase)); - baseTime -= phase; - ALOGV("[%s] baseTime - phase = %" PRId64, mName, ns2us(baseTime)); - - // If our previous time is before the reference (because the reference - // has since been updated), the division by mPeriod will truncate - // towards zero instead of computing the floor. Since in all cases - // before the reference we want the next time to be effectively now, we - // set baseTime to -mPeriod so that numPeriods will be -1. - // When we add 1 and the phase, we will be at the correct event time for - // this period. - if (baseTime < 0) { - ALOGV("[%s] Correcting negative baseTime", mName); - baseTime = -mPeriod; - } - - nsecs_t numPeriods = baseTime / mPeriod; - ALOGV("[%s] numPeriods = %" PRId64, mName, numPeriods); - nsecs_t t = (numPeriods + 1) * mPeriod + phase; - ALOGV("[%s] t = %" PRId64, mName, ns2us(t)); - t += mReferenceTime; - ALOGV("[%s] Absolute t = %" PRId64, mName, ns2us(t)); - - // Check that it's been slightly more than half a period since the last - // event so that we don't accidentally fall into double-rate vsyncs - if (t - listener.mLastEventTime < (3 * mPeriod / 5)) { - t += mPeriod; - ALOGV("[%s] Modifying t -> %" PRId64, mName, ns2us(t)); - } - - t -= mWakeupLatency; - ALOGV("[%s] Corrected for wakeup latency -> %" PRId64, mName, ns2us(t)); - - return t; - } - - void fireCallbackInvocations(const std::vector& callbacks) { - if (kTraceDetailedInfo) ATRACE_CALL(); - for (size_t i = 0; i < callbacks.size(); i++) { - callbacks[i].mCallback->onDispSyncEvent(callbacks[i].mEventTime); - } - } - - const char* const mName; - - bool mStop; - - nsecs_t mPeriod; - nsecs_t mPhase; - nsecs_t mReferenceTime; - nsecs_t mWakeupLatency; - - int64_t mFrameNumber; - - std::vector mEventListeners; - - Mutex mMutex; - Condition mCond; -}; - -#undef LOG_TAG -#define LOG_TAG "DispSync" - -class ZeroPhaseTracer : public DispSync::Callback { -public: - ZeroPhaseTracer() : mParity(false) {} - - virtual void onDispSyncEvent(nsecs_t /*when*/) { - mParity = !mParity; - ATRACE_INT("ZERO_PHASE_VSYNC", mParity ? 1 : 0); - } - -private: - bool mParity; -}; - -DispSync::DispSync(const char* name) - : mName(name), mRefreshSkipCount(0), mThread(new DispSyncThread(name)) {} - -DispSync::~DispSync() {} - -void DispSync::init(bool hasSyncFramework, int64_t dispSyncPresentTimeOffset) { - mIgnorePresentFences = !hasSyncFramework; - mPresentTimeOffset = dispSyncPresentTimeOffset; - mThread->run("DispSync", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE); - - // set DispSync to SCHED_FIFO to minimize jitter - struct sched_param param = {0}; - param.sched_priority = 2; - if (sched_setscheduler(mThread->getTid(), SCHED_FIFO, ¶m) != 0) { - ALOGE("Couldn't set SCHED_FIFO for DispSyncThread"); - } - - reset(); - beginResync(); - - if (kTraceDetailedInfo && kEnableZeroPhaseTracer) { - mZeroPhaseTracer = std::make_unique(); - addEventListener("ZeroPhaseTracer", 0, mZeroPhaseTracer.get()); - } -} - -void DispSync::reset() { - Mutex::Autolock lock(mMutex); - resetLocked(); -} - -void DispSync::resetLocked() { - mPhase = 0; - mReferenceTime = 0; - mModelUpdated = false; - mNumResyncSamples = 0; - mFirstResyncSample = 0; - mNumResyncSamplesSincePresent = 0; - resetErrorLocked(); -} - -bool DispSync::addPresentFence(const std::shared_ptr& fenceTime) { - Mutex::Autolock lock(mMutex); - - if (mIgnorePresentFences) { - return true; - } - - mPresentFences[mPresentSampleOffset] = fenceTime; - mPresentSampleOffset = (mPresentSampleOffset + 1) % NUM_PRESENT_SAMPLES; - mNumResyncSamplesSincePresent = 0; - - updateErrorLocked(); - - return !mModelUpdated || mError > kErrorThreshold; -} - -void DispSync::beginResync() { - Mutex::Autolock lock(mMutex); - ALOGV("[%s] beginResync", mName); - mModelUpdated = false; - mNumResyncSamples = 0; -} - -bool DispSync::addResyncSample(nsecs_t timestamp) { - Mutex::Autolock lock(mMutex); - - ALOGV("[%s] addResyncSample(%" PRId64 ")", mName, ns2us(timestamp)); - - size_t idx = (mFirstResyncSample + mNumResyncSamples) % MAX_RESYNC_SAMPLES; - mResyncSamples[idx] = timestamp; - if (mNumResyncSamples == 0) { - mPhase = 0; - mReferenceTime = timestamp; - ALOGV("[%s] First resync sample: mPeriod = %" PRId64 ", mPhase = 0, " - "mReferenceTime = %" PRId64, - mName, ns2us(mPeriod), ns2us(mReferenceTime)); - mThread->updateModel(mPeriod, mPhase, mReferenceTime); - } - - if (mNumResyncSamples < MAX_RESYNC_SAMPLES) { - mNumResyncSamples++; - } else { - mFirstResyncSample = (mFirstResyncSample + 1) % MAX_RESYNC_SAMPLES; - } - - updateModelLocked(); - - if (mNumResyncSamplesSincePresent++ > MAX_RESYNC_SAMPLES_WITHOUT_PRESENT) { - resetErrorLocked(); - } - - if (mIgnorePresentFences) { - // If we're ignoring the present fences we have no way to know whether - // or not we're synchronized with the HW vsyncs, so we just request - // that the HW vsync events be turned on. - return true; - } - - // Check against kErrorThreshold / 2 to add some hysteresis before having to - // resync again - bool modelLocked = mModelUpdated && mError < (kErrorThreshold / 2); - ALOGV("[%s] addResyncSample returning %s", mName, modelLocked ? "locked" : "unlocked"); - return !modelLocked; -} - -void DispSync::endResync() {} - -status_t DispSync::addEventListener(const char* name, nsecs_t phase, Callback* callback) { - Mutex::Autolock lock(mMutex); - return mThread->addEventListener(name, phase, callback); -} - -void DispSync::setRefreshSkipCount(int count) { - Mutex::Autolock lock(mMutex); - ALOGD("setRefreshSkipCount(%d)", count); - mRefreshSkipCount = count; - updateModelLocked(); -} - -status_t DispSync::removeEventListener(Callback* callback) { - Mutex::Autolock lock(mMutex); - return mThread->removeEventListener(callback); -} - -status_t DispSync::changePhaseOffset(Callback* callback, nsecs_t phase) { - Mutex::Autolock lock(mMutex); - return mThread->changePhaseOffset(callback, phase); -} - -void DispSync::setPeriod(nsecs_t period) { - Mutex::Autolock lock(mMutex); - mPeriodBase = mPeriod = period; - mPhase = 0; - mReferenceTime = 0; - mThread->updateModel(mPeriod, mPhase, mReferenceTime); -} - -void DispSync::scalePeriod(uint32_t multiplier, uint32_t divisor) { - Mutex::Autolock lock(mMutex); - - // if only 1 of the properties is updated, we will get to this - // point "attempting" to set the scale to 1 when it is already - // 1. Check that special case so that we don't do a useless - // update of the model. - if ((multiplier == 1) && (divisor == 1) && (mPeriod == mPeriodBase)) - return; - - mPeriod = mPeriodBase * multiplier / divisor; - mThread->updateModel(mPeriod, mPhase, mReferenceTime); -} - -nsecs_t DispSync::getPeriod() { - // lock mutex as mPeriod changes multiple times in updateModelLocked - Mutex::Autolock lock(mMutex); - return mPeriod; -} - -void DispSync::updateModelLocked() { - ALOGV("[%s] updateModelLocked %zu", mName, mNumResyncSamples); - if (mNumResyncSamples >= MIN_RESYNC_SAMPLES_FOR_UPDATE) { - ALOGV("[%s] Computing...", mName); - nsecs_t durationSum = 0; - nsecs_t minDuration = INT64_MAX; - nsecs_t maxDuration = 0; - for (size_t i = 1; i < mNumResyncSamples; i++) { - size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES; - size_t prev = (idx + MAX_RESYNC_SAMPLES - 1) % MAX_RESYNC_SAMPLES; - nsecs_t duration = mResyncSamples[idx] - mResyncSamples[prev]; - durationSum += duration; - minDuration = min(minDuration, duration); - maxDuration = max(maxDuration, duration); - } - - // Exclude the min and max from the average - durationSum -= minDuration + maxDuration; - mPeriodBase = mPeriod = durationSum / (mNumResyncSamples - 3); - - ALOGV("[%s] mPeriod = %" PRId64, mName, ns2us(mPeriod)); - - double sampleAvgX = 0; - double sampleAvgY = 0; - double scale = 2.0 * M_PI / double(mPeriod); - // Intentionally skip the first sample - for (size_t i = 1; i < mNumResyncSamples; i++) { - size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES; - nsecs_t sample = mResyncSamples[idx] - mReferenceTime; - double samplePhase = double(sample % mPeriod) * scale; - sampleAvgX += cos(samplePhase); - sampleAvgY += sin(samplePhase); - } - - sampleAvgX /= double(mNumResyncSamples - 1); - sampleAvgY /= double(mNumResyncSamples - 1); - - mPhase = nsecs_t(atan2(sampleAvgY, sampleAvgX) / scale); - - ALOGV("[%s] mPhase = %" PRId64, mName, ns2us(mPhase)); - - if (mPhase < -(mPeriod / 2)) { - mPhase += mPeriod; - ALOGV("[%s] Adjusting mPhase -> %" PRId64, mName, ns2us(mPhase)); - } - - if (kTraceDetailedInfo) { - ATRACE_INT64("DispSync:Period", mPeriod); - ATRACE_INT64("DispSync:Phase", mPhase + mPeriod / 2); - } - - // Artificially inflate the period if requested. - mPeriod += mPeriod * mRefreshSkipCount; - - mThread->updateModel(mPeriod, mPhase, mReferenceTime); - mModelUpdated = true; - } -} - -void DispSync::updateErrorLocked() { - if (!mModelUpdated) { - return; - } - - // Need to compare present fences against the un-adjusted refresh period, - // since they might arrive between two events. - nsecs_t period = mPeriod / (1 + mRefreshSkipCount); - - int numErrSamples = 0; - nsecs_t sqErrSum = 0; - - for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) { - // Only check for the cached value of signal time to avoid unecessary - // syscalls. It is the responsibility of the DispSync owner to - // call getSignalTime() periodically so the cache is updated when the - // fence signals. - nsecs_t time = mPresentFences[i]->getCachedSignalTime(); - if (time == Fence::SIGNAL_TIME_PENDING || time == Fence::SIGNAL_TIME_INVALID) { - continue; - } - - nsecs_t sample = time - mReferenceTime; - if (sample <= mPhase) { - continue; - } - - nsecs_t sampleErr = (sample - mPhase) % period; - if (sampleErr > period / 2) { - sampleErr -= period; - } - sqErrSum += sampleErr * sampleErr; - numErrSamples++; - } - - if (numErrSamples > 0) { - mError = sqErrSum / numErrSamples; - mZeroErrSamplesCount = 0; - } else { - mError = 0; - // Use mod ACCEPTABLE_ZERO_ERR_SAMPLES_COUNT to avoid log spam. - mZeroErrSamplesCount++; - ALOGE_IF((mZeroErrSamplesCount % ACCEPTABLE_ZERO_ERR_SAMPLES_COUNT) == 0, - "No present times for model error."); - } - - if (kTraceDetailedInfo) { - ATRACE_INT64("DispSync:Error", mError); - } -} - -void DispSync::resetErrorLocked() { - mPresentSampleOffset = 0; - mError = 0; - mZeroErrSamplesCount = 0; - for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) { - mPresentFences[i] = FenceTime::NO_FENCE; - } -} - -nsecs_t DispSync::computeNextRefresh(int periodOffset) const { - Mutex::Autolock lock(mMutex); - nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - nsecs_t phase = mReferenceTime + mPhase; - return (((now - phase) / mPeriod) + periodOffset + 1) * mPeriod + phase; -} - -void DispSync::setIgnorePresentFences(bool ignore) { - Mutex::Autolock lock(mMutex); - if (mIgnorePresentFences != ignore) { - mIgnorePresentFences = ignore; - resetLocked(); - } -} - -void DispSync::dump(String8& result) const { - Mutex::Autolock lock(mMutex); - result.appendFormat("present fences are %s\n", mIgnorePresentFences ? "ignored" : "used"); - result.appendFormat("mPeriod: %" PRId64 " ns (%.3f fps; skipCount=%d)\n", mPeriod, - 1000000000.0 / mPeriod, mRefreshSkipCount); - result.appendFormat("mPhase: %" PRId64 " ns\n", mPhase); - result.appendFormat("mError: %" PRId64 " ns (sqrt=%.1f)\n", mError, sqrt(mError)); - result.appendFormat("mNumResyncSamplesSincePresent: %d (limit %d)\n", - mNumResyncSamplesSincePresent, MAX_RESYNC_SAMPLES_WITHOUT_PRESENT); - result.appendFormat("mNumResyncSamples: %zd (max %d)\n", mNumResyncSamples, MAX_RESYNC_SAMPLES); - - result.appendFormat("mResyncSamples:\n"); - nsecs_t previous = -1; - for (size_t i = 0; i < mNumResyncSamples; i++) { - size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES; - nsecs_t sampleTime = mResyncSamples[idx]; - if (i == 0) { - result.appendFormat(" %" PRId64 "\n", sampleTime); - } else { - result.appendFormat(" %" PRId64 " (+%" PRId64 ")\n", sampleTime, - sampleTime - previous); - } - previous = sampleTime; - } - - result.appendFormat("mPresentFences [%d]:\n", NUM_PRESENT_SAMPLES); - nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - previous = Fence::SIGNAL_TIME_INVALID; - for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) { - size_t idx = (i + mPresentSampleOffset) % NUM_PRESENT_SAMPLES; - nsecs_t presentTime = mPresentFences[idx]->getSignalTime(); - if (presentTime == Fence::SIGNAL_TIME_PENDING) { - result.appendFormat(" [unsignaled fence]\n"); - } else if (presentTime == Fence::SIGNAL_TIME_INVALID) { - result.appendFormat(" [invalid fence]\n"); - } else if (previous == Fence::SIGNAL_TIME_PENDING || - previous == Fence::SIGNAL_TIME_INVALID) { - result.appendFormat(" %" PRId64 " (%.3f ms ago)\n", presentTime, - (now - presentTime) / 1000000.0); - } else { - result.appendFormat(" %" PRId64 " (+%" PRId64 " / %.3f) (%.3f ms ago)\n", presentTime, - presentTime - previous, (presentTime - previous) / (double)mPeriod, - (now - presentTime) / 1000000.0); - } - previous = presentTime; - } - - result.appendFormat("current monotonic time: %" PRId64 "\n", now); -} - -} // namespace impl - -} // namespace android diff --git a/services/surfaceflinger/DispSync.h b/services/surfaceflinger/DispSync.h deleted file mode 100644 index 183966feba..0000000000 --- a/services/surfaceflinger/DispSync.h +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_DISPSYNC_H -#define ANDROID_DISPSYNC_H - -#include - -#include -#include -#include - -#include - -#include - -namespace android { - -class String8; -class FenceTime; - -class DispSync { -public: - class Callback { - public: - virtual ~Callback() = default; - virtual void onDispSyncEvent(nsecs_t when) = 0; - }; - - virtual ~DispSync(); - - virtual void reset() = 0; - virtual bool addPresentFence(const std::shared_ptr&) = 0; - virtual void beginResync() = 0; - virtual bool addResyncSample(nsecs_t timestamp) = 0; - virtual void endResync() = 0; - virtual void setPeriod(nsecs_t period) = 0; - virtual void scalePeriod(const uint32_t multiplier, uint32_t divisor) = 0; - virtual nsecs_t getPeriod() = 0; - virtual void setRefreshSkipCount(int count) = 0; - virtual status_t addEventListener(const char* name, nsecs_t phase, Callback* callback) = 0; - virtual status_t removeEventListener(Callback* callback) = 0; - virtual status_t changePhaseOffset(Callback* callback, nsecs_t phase) = 0; - virtual nsecs_t computeNextRefresh(int periodOffset) const = 0; - virtual void setIgnorePresentFences(bool ignore) = 0; - - virtual void dump(String8& result) const = 0; -}; - -namespace impl { - -class DispSyncThread; - -// DispSync maintains a model of the periodic hardware-based vsync events of a -// display and uses that model to execute period callbacks at specific phase -// offsets from the hardware vsync events. The model is constructed by -// feeding consecutive hardware event timestamps to the DispSync object via -// the addResyncSample method. -// -// The model is validated using timestamps from Fence objects that are passed -// to the DispSync object via the addPresentFence method. These fence -// timestamps should correspond to a hardware vsync event, but they need not -// be consecutive hardware vsync times. If this method determines that the -// current model accurately represents the hardware event times it will return -// false to indicate that a resynchronization (via addResyncSample) is not -// needed. -class DispSync : public android::DispSync { -public: - explicit DispSync(const char* name); - ~DispSync() override; - - void init(bool hasSyncFramework, int64_t dispSyncPresentTimeOffset); - - // reset clears the resync samples and error value. - void reset() override; - - // addPresentFence adds a fence for use in validating the current vsync - // event model. The fence need not be signaled at the time - // addPresentFence is called. When the fence does signal, its timestamp - // should correspond to a hardware vsync event. Unlike the - // addResyncSample method, the timestamps of consecutive fences need not - // correspond to consecutive hardware vsync events. - // - // This method should be called with the retire fence from each HWComposer - // set call that affects the display. - bool addPresentFence(const std::shared_ptr& fenceTime) override; - - // The beginResync, addResyncSample, and endResync methods are used to re- - // synchronize the DispSync's model to the hardware vsync events. The re- - // synchronization process involves first calling beginResync, then - // calling addResyncSample with a sequence of consecutive hardware vsync - // event timestamps, and finally calling endResync when addResyncSample - // indicates that no more samples are needed by returning false. - // - // This resynchronization process should be performed whenever the display - // is turned on (i.e. once immediately after it's turned on) and whenever - // addPresentFence returns true indicating that the model has drifted away - // from the hardware vsync events. - void beginResync() override; - bool addResyncSample(nsecs_t timestamp) override; - void endResync() override; - - // The setPeriod method sets the vsync event model's period to a specific - // value. This should be used to prime the model when a display is first - // turned on. It should NOT be used after that. - void setPeriod(nsecs_t period) override; - - // The scalePeriod method applies the multiplier and divisor to - // scale the vsync event model's period. The function is added - // for an experimental test mode and should not be used outside - // of that purpose. - void scalePeriod(const uint32_t multiplier, uint32_t divisor); - - // The getPeriod method returns the current vsync period. - nsecs_t getPeriod() override; - - // setRefreshSkipCount specifies an additional number of refresh - // cycles to skip. For example, on a 60Hz display, a skip count of 1 - // will result in events happening at 30Hz. Default is zero. The idea - // is to sacrifice smoothness for battery life. - void setRefreshSkipCount(int count) override; - - // addEventListener registers a callback to be called repeatedly at the - // given phase offset from the hardware vsync events. The callback is - // called from a separate thread and it should return reasonably quickly - // (i.e. within a few hundred microseconds). - status_t addEventListener(const char* name, nsecs_t phase, Callback* callback) override; - - // removeEventListener removes an already-registered event callback. Once - // this method returns that callback will no longer be called by the - // DispSync object. - status_t removeEventListener(Callback* callback) override; - - // changePhaseOffset changes the phase offset of an already-registered event callback. The - // method will make sure that there is no skipping or double-firing on the listener per frame, - // even when changing the offsets multiple times. - status_t changePhaseOffset(Callback* callback, nsecs_t phase) override; - - // computeNextRefresh computes when the next refresh is expected to begin. - // The periodOffset value can be used to move forward or backward; an - // offset of zero is the next refresh, -1 is the previous refresh, 1 is - // the refresh after next. etc. - nsecs_t computeNextRefresh(int periodOffset) const override; - - // In certain situations the present fences aren't a good indicator of vsync - // time, e.g. when vr flinger is active, or simply aren't available, - // e.g. when the sync framework isn't present. Use this method to toggle - // whether or not DispSync ignores present fences. If present fences are - // ignored, DispSync will always ask for hardware vsync events by returning - // true from addPresentFence() and addResyncSample(). - void setIgnorePresentFences(bool ignore) override; - - // dump appends human-readable debug info to the result string. - void dump(String8& result) const override; - -private: - void updateModelLocked(); - void updateErrorLocked(); - void resetLocked(); - void resetErrorLocked(); - - enum { MAX_RESYNC_SAMPLES = 32 }; - enum { MIN_RESYNC_SAMPLES_FOR_UPDATE = 6 }; - enum { NUM_PRESENT_SAMPLES = 8 }; - enum { MAX_RESYNC_SAMPLES_WITHOUT_PRESENT = 4 }; - enum { ACCEPTABLE_ZERO_ERR_SAMPLES_COUNT = 64 }; - - const char* const mName; - - // mPeriod is the computed period of the modeled vsync events in - // nanoseconds. - nsecs_t mPeriod; - nsecs_t mPeriodBase; - - // mPhase is the phase offset of the modeled vsync events. It is the - // number of nanoseconds from time 0 to the first vsync event. - nsecs_t mPhase; - - // mReferenceTime is the reference time of the modeled vsync events. - // It is the nanosecond timestamp of the first vsync event after a resync. - nsecs_t mReferenceTime; - - // mError is the computed model error. It is based on the difference - // between the estimated vsync event times and those observed in the - // mPresentFences array. - nsecs_t mError; - - // mZeroErrSamplesCount keeps track of how many times in a row there were - // zero timestamps available in the mPresentFences array. - // Used to sanity check that we are able to calculate the model error. - size_t mZeroErrSamplesCount; - - // Whether we have updated the vsync event model since the last resync. - bool mModelUpdated; - - // These member variables are the state used during the resynchronization - // process to store information about the hardware vsync event times used - // to compute the model. - nsecs_t mResyncSamples[MAX_RESYNC_SAMPLES]; - size_t mFirstResyncSample; - size_t mNumResyncSamples; - int mNumResyncSamplesSincePresent; - - // These member variables store information about the present fences used - // to validate the currently computed model. - std::shared_ptr mPresentFences[NUM_PRESENT_SAMPLES]{FenceTime::NO_FENCE}; - size_t mPresentSampleOffset; - - int mRefreshSkipCount; - - // mThread is the thread from which all the callbacks are called. - sp mThread; - - // mMutex is used to protect access to all member variables. - mutable Mutex mMutex; - - // This is the offset from the present fence timestamps to the corresponding - // vsync event. - int64_t mPresentTimeOffset; - - // Ignore present (retire) fences if the device doesn't have support for the - // sync framework - bool mIgnorePresentFences; - - std::unique_ptr mZeroPhaseTracer; -}; - -} // namespace impl - -} // namespace android - -#endif // ANDROID_DISPSYNC_H diff --git a/services/surfaceflinger/EventControlThread.cpp b/services/surfaceflinger/EventControlThread.cpp deleted file mode 100644 index fb6cff5705..0000000000 --- a/services/surfaceflinger/EventControlThread.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include - -#include -#include -#include - -#include "EventControlThread.h" - -namespace android { - -EventControlThread::~EventControlThread() = default; - -namespace impl { - -EventControlThread::EventControlThread(EventControlThread::SetVSyncEnabledFunction function) - : mSetVSyncEnabled(function) { - pthread_setname_np(mThread.native_handle(), "EventControlThread"); - - pid_t tid = pthread_gettid_np(mThread.native_handle()); - setpriority(PRIO_PROCESS, tid, ANDROID_PRIORITY_URGENT_DISPLAY); - set_sched_policy(tid, SP_FOREGROUND); -} - -EventControlThread::~EventControlThread() { - { - std::lock_guard lock(mMutex); - mKeepRunning = false; - mCondition.notify_all(); - } - mThread.join(); -} - -void EventControlThread::setVsyncEnabled(bool enabled) { - std::lock_guard lock(mMutex); - mVsyncEnabled = enabled; - mCondition.notify_all(); -} - -// Unfortunately std::unique_lock gives warnings with -Wthread-safety -void EventControlThread::threadMain() NO_THREAD_SAFETY_ANALYSIS { - auto keepRunning = true; - auto currentVsyncEnabled = false; - - while (keepRunning) { - mSetVSyncEnabled(currentVsyncEnabled); - - std::unique_lock lock(mMutex); - mCondition.wait(lock, [this, currentVsyncEnabled, keepRunning]() NO_THREAD_SAFETY_ANALYSIS { - return currentVsyncEnabled != mVsyncEnabled || keepRunning != mKeepRunning; - }); - currentVsyncEnabled = mVsyncEnabled; - keepRunning = mKeepRunning; - } -} - -} // namespace impl -} // namespace android diff --git a/services/surfaceflinger/EventControlThread.h b/services/surfaceflinger/EventControlThread.h deleted file mode 100644 index cafae53400..0000000000 --- a/services/surfaceflinger/EventControlThread.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include -#include -#include - -#include - -namespace android { - -class EventControlThread { -public: - virtual ~EventControlThread(); - - virtual void setVsyncEnabled(bool enabled) = 0; -}; - -namespace impl { - -class EventControlThread final : public android::EventControlThread { -public: - using SetVSyncEnabledFunction = std::function; - - explicit EventControlThread(SetVSyncEnabledFunction function); - ~EventControlThread(); - - // EventControlThread implementation - void setVsyncEnabled(bool enabled) override; - -private: - void threadMain(); - - std::mutex mMutex; - std::condition_variable mCondition; - - const SetVSyncEnabledFunction mSetVSyncEnabled; - bool mVsyncEnabled GUARDED_BY(mMutex) = false; - bool mKeepRunning GUARDED_BY(mMutex) = true; - - // Must be last so that everything is initialized before the thread starts. - std::thread mThread{&EventControlThread::threadMain, this}; -}; - -} // namespace impl -} // namespace android diff --git a/services/surfaceflinger/EventThread.cpp b/services/surfaceflinger/EventThread.cpp deleted file mode 100644 index 5a8fd25270..0000000000 --- a/services/surfaceflinger/EventThread.cpp +++ /dev/null @@ -1,409 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define ATRACE_TAG ATRACE_TAG_GRAPHICS - -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include -#include -#include - -#include "EventThread.h" - -using namespace std::chrono_literals; - -// --------------------------------------------------------------------------- - -namespace android { - -// --------------------------------------------------------------------------- - -EventThread::~EventThread() = default; - -namespace impl { - -EventThread::EventThread(VSyncSource* src, ResyncWithRateLimitCallback resyncWithRateLimitCallback, - InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName) - : mVSyncSource(src), - mResyncWithRateLimitCallback(resyncWithRateLimitCallback), - mInterceptVSyncsCallback(interceptVSyncsCallback) { - for (auto& event : mVSyncEvent) { - event.header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC; - event.header.id = 0; - event.header.timestamp = 0; - event.vsync.count = 0; - } - - mThread = std::thread(&EventThread::threadMain, this); - - pthread_setname_np(mThread.native_handle(), threadName); - - pid_t tid = pthread_gettid_np(mThread.native_handle()); - - // Use SCHED_FIFO to minimize jitter - constexpr int EVENT_THREAD_PRIORITY = 2; - struct sched_param param = {0}; - param.sched_priority = EVENT_THREAD_PRIORITY; - if (pthread_setschedparam(mThread.native_handle(), SCHED_FIFO, ¶m) != 0) { - ALOGE("Couldn't set SCHED_FIFO for EventThread"); - } - - set_sched_policy(tid, SP_FOREGROUND); -} - -EventThread::~EventThread() { - { - std::lock_guard lock(mMutex); - mKeepRunning = false; - mCondition.notify_all(); - } - mThread.join(); -} - -void EventThread::setPhaseOffset(nsecs_t phaseOffset) { - std::lock_guard lock(mMutex); - mVSyncSource->setPhaseOffset(phaseOffset); -} - -sp EventThread::createEventConnection() const { - return new Connection(const_cast(this)); -} - -status_t EventThread::registerDisplayEventConnection( - const sp& connection) { - std::lock_guard lock(mMutex); - mDisplayEventConnections.add(connection); - mCondition.notify_all(); - return NO_ERROR; -} - -void EventThread::removeDisplayEventConnectionLocked(const wp& connection) { - mDisplayEventConnections.remove(connection); -} - -void EventThread::setVsyncRate(uint32_t count, const sp& connection) { - if (int32_t(count) >= 0) { // server must protect against bad params - std::lock_guard lock(mMutex); - const int32_t new_count = (count == 0) ? -1 : count; - if (connection->count != new_count) { - connection->count = new_count; - mCondition.notify_all(); - } - } -} - -void EventThread::requestNextVsync(const sp& connection) { - std::lock_guard lock(mMutex); - - if (mResyncWithRateLimitCallback) { - mResyncWithRateLimitCallback(); - } - - if (connection->count < 0) { - connection->count = 0; - mCondition.notify_all(); - } -} - -void EventThread::onScreenReleased() { - std::lock_guard lock(mMutex); - if (!mUseSoftwareVSync) { - // disable reliance on h/w vsync - mUseSoftwareVSync = true; - mCondition.notify_all(); - } -} - -void EventThread::onScreenAcquired() { - std::lock_guard lock(mMutex); - if (mUseSoftwareVSync) { - // resume use of h/w vsync - mUseSoftwareVSync = false; - mCondition.notify_all(); - } -} - -void EventThread::onVSyncEvent(nsecs_t timestamp) { - std::lock_guard lock(mMutex); - mVSyncEvent[0].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC; - mVSyncEvent[0].header.id = 0; - mVSyncEvent[0].header.timestamp = timestamp; - mVSyncEvent[0].vsync.count++; - mCondition.notify_all(); -} - -void EventThread::onHotplugReceived(DisplayType displayType, bool connected) { - std::lock_guard lock(mMutex); - - DisplayEventReceiver::Event event; - event.header.type = DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG; - event.header.id = displayType == DisplayType::Primary ? 0 : 1; - event.header.timestamp = systemTime(); - event.hotplug.connected = connected; - - mPendingEvents.add(event); - mCondition.notify_all(); -} - -void EventThread::threadMain() NO_THREAD_SAFETY_ANALYSIS { - std::unique_lock lock(mMutex); - while (mKeepRunning) { - DisplayEventReceiver::Event event; - Vector > signalConnections; - signalConnections = waitForEventLocked(&lock, &event); - - // dispatch events to listeners... - const size_t count = signalConnections.size(); - for (size_t i = 0; i < count; i++) { - const sp& conn(signalConnections[i]); - // now see if we still need to report this event - status_t err = conn->postEvent(event); - if (err == -EAGAIN || err == -EWOULDBLOCK) { - // The destination doesn't accept events anymore, it's probably - // full. For now, we just drop the events on the floor. - // FIXME: Note that some events cannot be dropped and would have - // to be re-sent later. - // Right-now we don't have the ability to do this. - ALOGW("EventThread: dropping event (%08x) for connection %p", event.header.type, - conn.get()); - } else if (err < 0) { - // handle any other error on the pipe as fatal. the only - // reasonable thing to do is to clean-up this connection. - // The most common error we'll get here is -EPIPE. - removeDisplayEventConnectionLocked(signalConnections[i]); - } - } - } -} - -// This will return when (1) a vsync event has been received, and (2) there was -// at least one connection interested in receiving it when we started waiting. -Vector > EventThread::waitForEventLocked( - std::unique_lock* lock, DisplayEventReceiver::Event* outEvent) { - Vector > signalConnections; - - while (signalConnections.isEmpty() && mKeepRunning) { - bool eventPending = false; - bool waitForVSync = false; - - size_t vsyncCount = 0; - nsecs_t timestamp = 0; - for (auto& event : mVSyncEvent) { - timestamp = event.header.timestamp; - if (timestamp) { - // we have a vsync event to dispatch - if (mInterceptVSyncsCallback) { - mInterceptVSyncsCallback(timestamp); - } - *outEvent = event; - event.header.timestamp = 0; - vsyncCount = event.vsync.count; - break; - } - } - - if (!timestamp) { - // no vsync event, see if there are some other event - eventPending = !mPendingEvents.isEmpty(); - if (eventPending) { - // we have some other event to dispatch - *outEvent = mPendingEvents[0]; - mPendingEvents.removeAt(0); - } - } - - // find out connections waiting for events - size_t count = mDisplayEventConnections.size(); - for (size_t i = 0; i < count;) { - sp connection(mDisplayEventConnections[i].promote()); - if (connection != nullptr) { - bool added = false; - if (connection->count >= 0) { - // we need vsync events because at least - // one connection is waiting for it - waitForVSync = true; - if (timestamp) { - // we consume the event only if it's time - // (ie: we received a vsync event) - if (connection->count == 0) { - // fired this time around - connection->count = -1; - signalConnections.add(connection); - added = true; - } else if (connection->count == 1 || - (vsyncCount % connection->count) == 0) { - // continuous event, and time to report it - signalConnections.add(connection); - added = true; - } - } - } - - if (eventPending && !timestamp && !added) { - // we don't have a vsync event to process - // (timestamp==0), but we have some pending - // messages. - signalConnections.add(connection); - } - ++i; - } else { - // we couldn't promote this reference, the connection has - // died, so clean-up! - mDisplayEventConnections.removeAt(i); - --count; - } - } - - // Here we figure out if we need to enable or disable vsyncs - if (timestamp && !waitForVSync) { - // we received a VSYNC but we have no clients - // don't report it, and disable VSYNC events - disableVSyncLocked(); - } else if (!timestamp && waitForVSync) { - // we have at least one client, so we want vsync enabled - // (TODO: this function is called right after we finish - // notifying clients of a vsync, so this call will be made - // at the vsync rate, e.g. 60fps. If we can accurately - // track the current state we could avoid making this call - // so often.) - enableVSyncLocked(); - } - - // note: !timestamp implies signalConnections.isEmpty(), because we - // don't populate signalConnections if there's no vsync pending - if (!timestamp && !eventPending) { - // wait for something to happen - if (waitForVSync) { - // This is where we spend most of our time, waiting - // for vsync events and new client registrations. - // - // If the screen is off, we can't use h/w vsync, so we - // use a 16ms timeout instead. It doesn't need to be - // precise, we just need to keep feeding our clients. - // - // We don't want to stall if there's a driver bug, so we - // use a (long) timeout when waiting for h/w vsync, and - // generate fake events when necessary. - bool softwareSync = mUseSoftwareVSync; - auto timeout = softwareSync ? 16ms : 1000ms; - if (mCondition.wait_for(*lock, timeout) == std::cv_status::timeout) { - if (!softwareSync) { - ALOGW("Timed out waiting for hw vsync; faking it"); - } - // FIXME: how do we decide which display id the fake - // vsync came from ? - mVSyncEvent[0].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC; - mVSyncEvent[0].header.id = 0; - mVSyncEvent[0].header.timestamp = systemTime(SYSTEM_TIME_MONOTONIC); - mVSyncEvent[0].vsync.count++; - } - } else { - // Nobody is interested in vsync, so we just want to sleep. - // h/w vsync should be disabled, so this will wait until we - // get a new connection, or an existing connection becomes - // interested in receiving vsync again. - mCondition.wait(*lock); - } - } - } - - // here we're guaranteed to have a timestamp and some connections to signal - // (The connections might have dropped out of mDisplayEventConnections - // while we were asleep, but we'll still have strong references to them.) - return signalConnections; -} - -void EventThread::enableVSyncLocked() { - if (!mUseSoftwareVSync) { - // never enable h/w VSYNC when screen is off - if (!mVsyncEnabled) { - mVsyncEnabled = true; - mVSyncSource->setCallback(this); - mVSyncSource->setVSyncEnabled(true); - } - } - mDebugVsyncEnabled = true; -} - -void EventThread::disableVSyncLocked() { - if (mVsyncEnabled) { - mVsyncEnabled = false; - mVSyncSource->setVSyncEnabled(false); - mDebugVsyncEnabled = false; - } -} - -void EventThread::dump(String8& result) const { - std::lock_guard lock(mMutex); - result.appendFormat("VSYNC state: %s\n", mDebugVsyncEnabled ? "enabled" : "disabled"); - result.appendFormat(" soft-vsync: %s\n", mUseSoftwareVSync ? "enabled" : "disabled"); - result.appendFormat(" numListeners=%zu,\n events-delivered: %u\n", - mDisplayEventConnections.size(), mVSyncEvent[0].vsync.count); - for (size_t i = 0; i < mDisplayEventConnections.size(); i++) { - sp connection = mDisplayEventConnections.itemAt(i).promote(); - result.appendFormat(" %p: count=%d\n", connection.get(), - connection != nullptr ? connection->count : 0); - } -} - -// --------------------------------------------------------------------------- - -EventThread::Connection::Connection(EventThread* eventThread) - : count(-1), mEventThread(eventThread), mChannel(gui::BitTube::DefaultSize) {} - -EventThread::Connection::~Connection() { - // do nothing here -- clean-up will happen automatically - // when the main thread wakes up -} - -void EventThread::Connection::onFirstRef() { - // NOTE: mEventThread doesn't hold a strong reference on us - mEventThread->registerDisplayEventConnection(this); -} - -status_t EventThread::Connection::stealReceiveChannel(gui::BitTube* outChannel) { - outChannel->setReceiveFd(mChannel.moveReceiveFd()); - return NO_ERROR; -} - -status_t EventThread::Connection::setVsyncRate(uint32_t count) { - mEventThread->setVsyncRate(count, this); - return NO_ERROR; -} - -void EventThread::Connection::requestNextVsync() { - mEventThread->requestNextVsync(this); -} - -status_t EventThread::Connection::postEvent(const DisplayEventReceiver::Event& event) { - ssize_t size = DisplayEventReceiver::sendEvents(&mChannel, &event, 1); - return size < 0 ? status_t(size) : status_t(NO_ERROR); -} - -// --------------------------------------------------------------------------- - -} // namespace impl -} // namespace android diff --git a/services/surfaceflinger/EventThread.h b/services/surfaceflinger/EventThread.h deleted file mode 100644 index a0262b2ad9..0000000000 --- a/services/surfaceflinger/EventThread.h +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - -#include -#include - -// --------------------------------------------------------------------------- -namespace android { -// --------------------------------------------------------------------------- - -class EventThreadTest; -class SurfaceFlinger; -class String8; - -// --------------------------------------------------------------------------- - -class VSyncSource { -public: - class Callback { - public: - virtual ~Callback() {} - virtual void onVSyncEvent(nsecs_t when) = 0; - }; - - virtual ~VSyncSource() {} - virtual void setVSyncEnabled(bool enable) = 0; - virtual void setCallback(Callback* callback) = 0; - virtual void setPhaseOffset(nsecs_t phaseOffset) = 0; -}; - -class EventThread { -public: - // TODO: Remove once stable display IDs are plumbed through SF/WM interface. - enum class DisplayType { Primary, External }; - - virtual ~EventThread(); - - virtual sp createEventConnection() const = 0; - - // called before the screen is turned off from main thread - virtual void onScreenReleased() = 0; - - // called after the screen is turned on from main thread - virtual void onScreenAcquired() = 0; - - // called when receiving a hotplug event - virtual void onHotplugReceived(DisplayType displayType, bool connected) = 0; - - virtual void dump(String8& result) const = 0; - - virtual void setPhaseOffset(nsecs_t phaseOffset) = 0; -}; - -namespace impl { - -class EventThread : public android::EventThread, private VSyncSource::Callback { - class Connection : public BnDisplayEventConnection { - public: - explicit Connection(EventThread* eventThread); - virtual ~Connection(); - - virtual status_t postEvent(const DisplayEventReceiver::Event& event); - - // count >= 1 : continuous event. count is the vsync rate - // count == 0 : one-shot event that has not fired - // count ==-1 : one-shot event that fired this round / disabled - int32_t count; - - private: - virtual void onFirstRef(); - status_t stealReceiveChannel(gui::BitTube* outChannel) override; - status_t setVsyncRate(uint32_t count) override; - void requestNextVsync() override; // asynchronous - EventThread* const mEventThread; - gui::BitTube mChannel; - }; - -public: - using ResyncWithRateLimitCallback = std::function; - using InterceptVSyncsCallback = std::function; - - EventThread(VSyncSource* src, ResyncWithRateLimitCallback resyncWithRateLimitCallback, - InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName); - ~EventThread(); - - sp createEventConnection() const override; - status_t registerDisplayEventConnection(const sp& connection); - - void setVsyncRate(uint32_t count, const sp& connection); - void requestNextVsync(const sp& connection); - - // called before the screen is turned off from main thread - void onScreenReleased() override; - - // called after the screen is turned on from main thread - void onScreenAcquired() override; - - // called when receiving a hotplug event - void onHotplugReceived(DisplayType displayType, bool connected) override; - - void dump(String8& result) const override; - - void setPhaseOffset(nsecs_t phaseOffset) override; - -private: - friend EventThreadTest; - - void threadMain(); - Vector> waitForEventLocked(std::unique_lock* lock, - DisplayEventReceiver::Event* event) - REQUIRES(mMutex); - - void removeDisplayEventConnectionLocked(const wp& connection) REQUIRES(mMutex); - void enableVSyncLocked() REQUIRES(mMutex); - void disableVSyncLocked() REQUIRES(mMutex); - - // Implements VSyncSource::Callback - void onVSyncEvent(nsecs_t timestamp) override; - - // constants - VSyncSource* const mVSyncSource GUARDED_BY(mMutex) = nullptr; - const ResyncWithRateLimitCallback mResyncWithRateLimitCallback; - const InterceptVSyncsCallback mInterceptVSyncsCallback; - - std::thread mThread; - mutable std::mutex mMutex; - mutable std::condition_variable mCondition; - - // protected by mLock - SortedVector> mDisplayEventConnections GUARDED_BY(mMutex); - Vector mPendingEvents GUARDED_BY(mMutex); - std::array mVSyncEvent GUARDED_BY(mMutex); - bool mUseSoftwareVSync GUARDED_BY(mMutex) = false; - bool mVsyncEnabled GUARDED_BY(mMutex) = false; - bool mKeepRunning GUARDED_BY(mMutex) = true; - - // for debugging - bool mDebugVsyncEnabled GUARDED_BY(mMutex) = false; -}; - -// --------------------------------------------------------------------------- - -} // namespace impl -} // namespace android diff --git a/services/surfaceflinger/MessageQueue.cpp b/services/surfaceflinger/MessageQueue.cpp deleted file mode 100644 index 056d381eb9..0000000000 --- a/services/surfaceflinger/MessageQueue.cpp +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include - -#include - -#include -#include -#include - -#include -#include - -#include "EventThread.h" -#include "MessageQueue.h" -#include "SurfaceFlinger.h" - -namespace android { - -// --------------------------------------------------------------------------- - -MessageBase::MessageBase() : MessageHandler() {} - -MessageBase::~MessageBase() {} - -void MessageBase::handleMessage(const Message&) { - this->handler(); - barrier.open(); -}; - -// --------------------------------------------------------------------------- - -MessageQueue::~MessageQueue() = default; - -// --------------------------------------------------------------------------- - -namespace impl { - -void MessageQueue::Handler::dispatchRefresh() { - if ((android_atomic_or(eventMaskRefresh, &mEventMask) & eventMaskRefresh) == 0) { - mQueue.mLooper->sendMessage(this, Message(MessageQueue::REFRESH)); - } -} - -void MessageQueue::Handler::dispatchInvalidate() { - if ((android_atomic_or(eventMaskInvalidate, &mEventMask) & eventMaskInvalidate) == 0) { - mQueue.mLooper->sendMessage(this, Message(MessageQueue::INVALIDATE)); - } -} - -void MessageQueue::Handler::handleMessage(const Message& message) { - switch (message.what) { - case INVALIDATE: - android_atomic_and(~eventMaskInvalidate, &mEventMask); - mQueue.mFlinger->onMessageReceived(message.what); - break; - case REFRESH: - android_atomic_and(~eventMaskRefresh, &mEventMask); - mQueue.mFlinger->onMessageReceived(message.what); - break; - } -} - -// --------------------------------------------------------------------------- - -void MessageQueue::init(const sp& flinger) { - mFlinger = flinger; - mLooper = new Looper(true); - mHandler = new Handler(*this); -} - -void MessageQueue::setEventThread(android::EventThread* eventThread) { - if (mEventThread == eventThread) { - return; - } - - if (mEventTube.getFd() >= 0) { - mLooper->removeFd(mEventTube.getFd()); - } - - mEventThread = eventThread; - mEvents = eventThread->createEventConnection(); - mEvents->stealReceiveChannel(&mEventTube); - mLooper->addFd(mEventTube.getFd(), 0, Looper::EVENT_INPUT, MessageQueue::cb_eventReceiver, - this); -} - -void MessageQueue::waitMessage() { - do { - IPCThreadState::self()->flushCommands(); - int32_t ret = mLooper->pollOnce(-1); - switch (ret) { - case Looper::POLL_WAKE: - case Looper::POLL_CALLBACK: - continue; - case Looper::POLL_ERROR: - ALOGE("Looper::POLL_ERROR"); - continue; - case Looper::POLL_TIMEOUT: - // timeout (should not happen) - continue; - default: - // should not happen - ALOGE("Looper::pollOnce() returned unknown status %d", ret); - continue; - } - } while (true); -} - -status_t MessageQueue::postMessage(const sp& messageHandler, nsecs_t relTime) { - const Message dummyMessage; - if (relTime > 0) { - mLooper->sendMessageDelayed(relTime, messageHandler, dummyMessage); - } else { - mLooper->sendMessage(messageHandler, dummyMessage); - } - return NO_ERROR; -} - -void MessageQueue::invalidate() { - mEvents->requestNextVsync(); -} - -void MessageQueue::refresh() { - mHandler->dispatchRefresh(); -} - -int MessageQueue::cb_eventReceiver(int fd, int events, void* data) { - MessageQueue* queue = reinterpret_cast(data); - return queue->eventReceiver(fd, events); -} - -int MessageQueue::eventReceiver(int /*fd*/, int /*events*/) { - ssize_t n; - DisplayEventReceiver::Event buffer[8]; - while ((n = DisplayEventReceiver::getEvents(&mEventTube, buffer, 8)) > 0) { - for (int i = 0; i < n; i++) { - if (buffer[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) { - mHandler->dispatchInvalidate(); - break; - } - } - } - return 1; -} - -// --------------------------------------------------------------------------- - -} // namespace impl -} // namespace android diff --git a/services/surfaceflinger/MessageQueue.h b/services/surfaceflinger/MessageQueue.h deleted file mode 100644 index 90d1c72450..0000000000 --- a/services/surfaceflinger/MessageQueue.h +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_MESSAGE_QUEUE_H -#define ANDROID_MESSAGE_QUEUE_H - -#include -#include -#include - -#include -#include -#include - -#include -#include - -#include "Barrier.h" - -#include - -namespace android { - -class EventThread; -class SurfaceFlinger; - -// --------------------------------------------------------------------------- - -class MessageBase : public MessageHandler { -public: - MessageBase(); - - // return true if message has a handler - virtual bool handler() = 0; - - // waits for the handler to be processed - void wait() const { barrier.wait(); } - -protected: - virtual ~MessageBase(); - -private: - virtual void handleMessage(const Message& message); - - mutable Barrier barrier; -}; - -class LambdaMessage : public MessageBase { -public: - explicit LambdaMessage(std::function handler) - : MessageBase(), mHandler(std::move(handler)) {} - - bool handler() override { - mHandler(); - // This return value is no longer checked, so it's always safe to return true - return true; - } - -private: - const std::function mHandler; -}; - -// --------------------------------------------------------------------------- - -class MessageQueue { -public: - enum { - INVALIDATE = 0, - REFRESH = 1, - }; - - virtual ~MessageQueue(); - - virtual void init(const sp& flinger) = 0; - virtual void setEventThread(EventThread* events) = 0; - virtual void waitMessage() = 0; - virtual status_t postMessage(const sp& message, nsecs_t reltime = 0) = 0; - virtual void invalidate() = 0; - virtual void refresh() = 0; -}; - -// --------------------------------------------------------------------------- - -namespace impl { - -class MessageQueue final : public android::MessageQueue { - class Handler : public MessageHandler { - enum { eventMaskInvalidate = 0x1, eventMaskRefresh = 0x2, eventMaskTransaction = 0x4 }; - MessageQueue& mQueue; - int32_t mEventMask; - - public: - explicit Handler(MessageQueue& queue) : mQueue(queue), mEventMask(0) {} - virtual void handleMessage(const Message& message); - void dispatchRefresh(); - void dispatchInvalidate(); - }; - - friend class Handler; - - sp mFlinger; - sp mLooper; - android::EventThread* mEventThread; - sp mEvents; - gui::BitTube mEventTube; - sp mHandler; - - static int cb_eventReceiver(int fd, int events, void* data); - int eventReceiver(int fd, int events); - -public: - ~MessageQueue() override = default; - void init(const sp& flinger) override; - void setEventThread(android::EventThread* events) override; - - void waitMessage() override; - status_t postMessage(const sp& message, nsecs_t reltime = 0) override; - - // sends INVALIDATE message at next VSYNC - void invalidate() override; - // sends REFRESH message at next VSYNC - void refresh() override; -}; - -// --------------------------------------------------------------------------- - -} // namespace impl -} // namespace android - -#endif /* ANDROID_MESSAGE_QUEUE_H */ diff --git a/services/surfaceflinger/MonitoredProducer.cpp b/services/surfaceflinger/MonitoredProducer.cpp index 389fbd23e3..06e3d9c154 100644 --- a/services/surfaceflinger/MonitoredProducer.cpp +++ b/services/surfaceflinger/MonitoredProducer.cpp @@ -14,10 +14,11 @@ * limitations under the License. */ -#include "MessageQueue.h" #include "MonitoredProducer.h" -#include "SurfaceFlinger.h" #include "Layer.h" +#include "SurfaceFlinger.h" + +#include "Scheduler/MessageQueue.h" namespace android { diff --git a/services/surfaceflinger/Scheduler/DispSync.cpp b/services/surfaceflinger/Scheduler/DispSync.cpp new file mode 100644 index 0000000000..9d9acd3116 --- /dev/null +++ b/services/surfaceflinger/Scheduler/DispSync.cpp @@ -0,0 +1,729 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define ATRACE_TAG ATRACE_TAG_GRAPHICS +//#define LOG_NDEBUG 0 + +// This is needed for stdint.h to define INT64_MAX in C++ +#define __STDC_LIMIT_MACROS + +#include + +#include + +#include +#include +#include +#include + +#include + +#include "DispSync.h" +#include "EventLog/EventLog.h" +#include "SurfaceFlinger.h" + +using std::max; +using std::min; + +namespace android { + +DispSync::~DispSync() = default; + +namespace impl { + +// Setting this to true enables verbose tracing that can be used to debug +// vsync event model or phase issues. +static const bool kTraceDetailedInfo = false; + +// Setting this to true adds a zero-phase tracer for correlating with hardware +// vsync events +static const bool kEnableZeroPhaseTracer = false; + +// This is the threshold used to determine when hardware vsync events are +// needed to re-synchronize the software vsync model with the hardware. The +// error metric used is the mean of the squared difference between each +// present time and the nearest software-predicted vsync. +static const nsecs_t kErrorThreshold = 160000000000; // 400 usec squared + +#undef LOG_TAG +#define LOG_TAG "DispSyncThread" +class DispSyncThread : public Thread { +public: + explicit DispSyncThread(const char* name) + : mName(name), + mStop(false), + mPeriod(0), + mPhase(0), + mReferenceTime(0), + mWakeupLatency(0), + mFrameNumber(0) {} + + virtual ~DispSyncThread() {} + + void updateModel(nsecs_t period, nsecs_t phase, nsecs_t referenceTime) { + if (kTraceDetailedInfo) ATRACE_CALL(); + Mutex::Autolock lock(mMutex); + mPeriod = period; + mPhase = phase; + mReferenceTime = referenceTime; + ALOGV("[%s] updateModel: mPeriod = %" PRId64 ", mPhase = %" PRId64 + " mReferenceTime = %" PRId64, + mName, ns2us(mPeriod), ns2us(mPhase), ns2us(mReferenceTime)); + mCond.signal(); + } + + void stop() { + if (kTraceDetailedInfo) ATRACE_CALL(); + Mutex::Autolock lock(mMutex); + mStop = true; + mCond.signal(); + } + + virtual bool threadLoop() { + status_t err; + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + + while (true) { + std::vector callbackInvocations; + + nsecs_t targetTime = 0; + + { // Scope for lock + Mutex::Autolock lock(mMutex); + + if (kTraceDetailedInfo) { + ATRACE_INT64("DispSync:Frame", mFrameNumber); + } + ALOGV("[%s] Frame %" PRId64, mName, mFrameNumber); + ++mFrameNumber; + + if (mStop) { + return false; + } + + if (mPeriod == 0) { + err = mCond.wait(mMutex); + if (err != NO_ERROR) { + ALOGE("error waiting for new events: %s (%d)", strerror(-err), err); + return false; + } + continue; + } + + targetTime = computeNextEventTimeLocked(now); + + bool isWakeup = false; + + if (now < targetTime) { + if (kTraceDetailedInfo) ATRACE_NAME("DispSync waiting"); + + if (targetTime == INT64_MAX) { + ALOGV("[%s] Waiting forever", mName); + err = mCond.wait(mMutex); + } else { + ALOGV("[%s] Waiting until %" PRId64, mName, ns2us(targetTime)); + err = mCond.waitRelative(mMutex, targetTime - now); + } + + if (err == TIMED_OUT) { + isWakeup = true; + } else if (err != NO_ERROR) { + ALOGE("error waiting for next event: %s (%d)", strerror(-err), err); + return false; + } + } + + now = systemTime(SYSTEM_TIME_MONOTONIC); + + // Don't correct by more than 1.5 ms + static const nsecs_t kMaxWakeupLatency = us2ns(1500); + + if (isWakeup) { + mWakeupLatency = ((mWakeupLatency * 63) + (now - targetTime)) / 64; + mWakeupLatency = min(mWakeupLatency, kMaxWakeupLatency); + if (kTraceDetailedInfo) { + ATRACE_INT64("DispSync:WakeupLat", now - targetTime); + ATRACE_INT64("DispSync:AvgWakeupLat", mWakeupLatency); + } + } + + callbackInvocations = gatherCallbackInvocationsLocked(now); + } + + if (callbackInvocations.size() > 0) { + fireCallbackInvocations(callbackInvocations); + } + } + + return false; + } + + status_t addEventListener(const char* name, nsecs_t phase, DispSync::Callback* callback) { + if (kTraceDetailedInfo) ATRACE_CALL(); + Mutex::Autolock lock(mMutex); + + for (size_t i = 0; i < mEventListeners.size(); i++) { + if (mEventListeners[i].mCallback == callback) { + return BAD_VALUE; + } + } + + EventListener listener; + listener.mName = name; + listener.mPhase = phase; + listener.mCallback = callback; + + // We want to allow the firstmost future event to fire without + // allowing any past events to fire + listener.mLastEventTime = systemTime() - mPeriod / 2 + mPhase - mWakeupLatency; + + mEventListeners.push_back(listener); + + mCond.signal(); + + return NO_ERROR; + } + + status_t removeEventListener(DispSync::Callback* callback) { + if (kTraceDetailedInfo) ATRACE_CALL(); + Mutex::Autolock lock(mMutex); + + for (std::vector::iterator it = mEventListeners.begin(); + it != mEventListeners.end(); ++it) { + if (it->mCallback == callback) { + mEventListeners.erase(it); + mCond.signal(); + return NO_ERROR; + } + } + + return BAD_VALUE; + } + + status_t changePhaseOffset(DispSync::Callback* callback, nsecs_t phase) { + if (kTraceDetailedInfo) ATRACE_CALL(); + Mutex::Autolock lock(mMutex); + + for (auto& eventListener : mEventListeners) { + if (eventListener.mCallback == callback) { + const nsecs_t oldPhase = eventListener.mPhase; + eventListener.mPhase = phase; + + // Pretend that the last time this event was handled at the same frame but with the + // new offset to allow for a seamless offset change without double-firing or + // skipping. + nsecs_t diff = oldPhase - phase; + if (diff > mPeriod / 2) { + diff -= mPeriod; + } else if (diff < -mPeriod / 2) { + diff += mPeriod; + } + eventListener.mLastEventTime -= diff; + mCond.signal(); + return NO_ERROR; + } + } + + return BAD_VALUE; + } + +private: + struct EventListener { + const char* mName; + nsecs_t mPhase; + nsecs_t mLastEventTime; + DispSync::Callback* mCallback; + }; + + struct CallbackInvocation { + DispSync::Callback* mCallback; + nsecs_t mEventTime; + }; + + nsecs_t computeNextEventTimeLocked(nsecs_t now) { + if (kTraceDetailedInfo) ATRACE_CALL(); + ALOGV("[%s] computeNextEventTimeLocked", mName); + nsecs_t nextEventTime = INT64_MAX; + for (size_t i = 0; i < mEventListeners.size(); i++) { + nsecs_t t = computeListenerNextEventTimeLocked(mEventListeners[i], now); + + if (t < nextEventTime) { + nextEventTime = t; + } + } + + ALOGV("[%s] nextEventTime = %" PRId64, mName, ns2us(nextEventTime)); + return nextEventTime; + } + + std::vector gatherCallbackInvocationsLocked(nsecs_t now) { + if (kTraceDetailedInfo) ATRACE_CALL(); + ALOGV("[%s] gatherCallbackInvocationsLocked @ %" PRId64, mName, ns2us(now)); + + std::vector callbackInvocations; + nsecs_t onePeriodAgo = now - mPeriod; + + for (auto& eventListener : mEventListeners) { + nsecs_t t = computeListenerNextEventTimeLocked(eventListener, onePeriodAgo); + + if (t < now) { + CallbackInvocation ci; + ci.mCallback = eventListener.mCallback; + ci.mEventTime = t; + ALOGV("[%s] [%s] Preparing to fire", mName, eventListener.mName); + callbackInvocations.push_back(ci); + eventListener.mLastEventTime = t; + } + } + + return callbackInvocations; + } + + nsecs_t computeListenerNextEventTimeLocked(const EventListener& listener, nsecs_t baseTime) { + if (kTraceDetailedInfo) ATRACE_CALL(); + ALOGV("[%s] [%s] computeListenerNextEventTimeLocked(%" PRId64 ")", mName, listener.mName, + ns2us(baseTime)); + + nsecs_t lastEventTime = listener.mLastEventTime + mWakeupLatency; + ALOGV("[%s] lastEventTime: %" PRId64, mName, ns2us(lastEventTime)); + if (baseTime < lastEventTime) { + baseTime = lastEventTime; + ALOGV("[%s] Clamping baseTime to lastEventTime -> %" PRId64, mName, ns2us(baseTime)); + } + + baseTime -= mReferenceTime; + ALOGV("[%s] Relative baseTime = %" PRId64, mName, ns2us(baseTime)); + nsecs_t phase = mPhase + listener.mPhase; + ALOGV("[%s] Phase = %" PRId64, mName, ns2us(phase)); + baseTime -= phase; + ALOGV("[%s] baseTime - phase = %" PRId64, mName, ns2us(baseTime)); + + // If our previous time is before the reference (because the reference + // has since been updated), the division by mPeriod will truncate + // towards zero instead of computing the floor. Since in all cases + // before the reference we want the next time to be effectively now, we + // set baseTime to -mPeriod so that numPeriods will be -1. + // When we add 1 and the phase, we will be at the correct event time for + // this period. + if (baseTime < 0) { + ALOGV("[%s] Correcting negative baseTime", mName); + baseTime = -mPeriod; + } + + nsecs_t numPeriods = baseTime / mPeriod; + ALOGV("[%s] numPeriods = %" PRId64, mName, numPeriods); + nsecs_t t = (numPeriods + 1) * mPeriod + phase; + ALOGV("[%s] t = %" PRId64, mName, ns2us(t)); + t += mReferenceTime; + ALOGV("[%s] Absolute t = %" PRId64, mName, ns2us(t)); + + // Check that it's been slightly more than half a period since the last + // event so that we don't accidentally fall into double-rate vsyncs + if (t - listener.mLastEventTime < (3 * mPeriod / 5)) { + t += mPeriod; + ALOGV("[%s] Modifying t -> %" PRId64, mName, ns2us(t)); + } + + t -= mWakeupLatency; + ALOGV("[%s] Corrected for wakeup latency -> %" PRId64, mName, ns2us(t)); + + return t; + } + + void fireCallbackInvocations(const std::vector& callbacks) { + if (kTraceDetailedInfo) ATRACE_CALL(); + for (size_t i = 0; i < callbacks.size(); i++) { + callbacks[i].mCallback->onDispSyncEvent(callbacks[i].mEventTime); + } + } + + const char* const mName; + + bool mStop; + + nsecs_t mPeriod; + nsecs_t mPhase; + nsecs_t mReferenceTime; + nsecs_t mWakeupLatency; + + int64_t mFrameNumber; + + std::vector mEventListeners; + + Mutex mMutex; + Condition mCond; +}; + +#undef LOG_TAG +#define LOG_TAG "DispSync" + +class ZeroPhaseTracer : public DispSync::Callback { +public: + ZeroPhaseTracer() : mParity(false) {} + + virtual void onDispSyncEvent(nsecs_t /*when*/) { + mParity = !mParity; + ATRACE_INT("ZERO_PHASE_VSYNC", mParity ? 1 : 0); + } + +private: + bool mParity; +}; + +DispSync::DispSync(const char* name) + : mName(name), mRefreshSkipCount(0), mThread(new DispSyncThread(name)) {} + +DispSync::~DispSync() {} + +void DispSync::init(bool hasSyncFramework, int64_t dispSyncPresentTimeOffset) { + mIgnorePresentFences = !hasSyncFramework; + mPresentTimeOffset = dispSyncPresentTimeOffset; + mThread->run("DispSync", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE); + + // set DispSync to SCHED_FIFO to minimize jitter + struct sched_param param = {0}; + param.sched_priority = 2; + if (sched_setscheduler(mThread->getTid(), SCHED_FIFO, ¶m) != 0) { + ALOGE("Couldn't set SCHED_FIFO for DispSyncThread"); + } + + reset(); + beginResync(); + + if (kTraceDetailedInfo && kEnableZeroPhaseTracer) { + mZeroPhaseTracer = std::make_unique(); + addEventListener("ZeroPhaseTracer", 0, mZeroPhaseTracer.get()); + } +} + +void DispSync::reset() { + Mutex::Autolock lock(mMutex); + resetLocked(); +} + +void DispSync::resetLocked() { + mPhase = 0; + mReferenceTime = 0; + mModelUpdated = false; + mNumResyncSamples = 0; + mFirstResyncSample = 0; + mNumResyncSamplesSincePresent = 0; + resetErrorLocked(); +} + +bool DispSync::addPresentFence(const std::shared_ptr& fenceTime) { + Mutex::Autolock lock(mMutex); + + if (mIgnorePresentFences) { + return true; + } + + mPresentFences[mPresentSampleOffset] = fenceTime; + mPresentSampleOffset = (mPresentSampleOffset + 1) % NUM_PRESENT_SAMPLES; + mNumResyncSamplesSincePresent = 0; + + updateErrorLocked(); + + return !mModelUpdated || mError > kErrorThreshold; +} + +void DispSync::beginResync() { + Mutex::Autolock lock(mMutex); + ALOGV("[%s] beginResync", mName); + mModelUpdated = false; + mNumResyncSamples = 0; +} + +bool DispSync::addResyncSample(nsecs_t timestamp) { + Mutex::Autolock lock(mMutex); + + ALOGV("[%s] addResyncSample(%" PRId64 ")", mName, ns2us(timestamp)); + + size_t idx = (mFirstResyncSample + mNumResyncSamples) % MAX_RESYNC_SAMPLES; + mResyncSamples[idx] = timestamp; + if (mNumResyncSamples == 0) { + mPhase = 0; + mReferenceTime = timestamp; + ALOGV("[%s] First resync sample: mPeriod = %" PRId64 ", mPhase = 0, " + "mReferenceTime = %" PRId64, + mName, ns2us(mPeriod), ns2us(mReferenceTime)); + mThread->updateModel(mPeriod, mPhase, mReferenceTime); + } + + if (mNumResyncSamples < MAX_RESYNC_SAMPLES) { + mNumResyncSamples++; + } else { + mFirstResyncSample = (mFirstResyncSample + 1) % MAX_RESYNC_SAMPLES; + } + + updateModelLocked(); + + if (mNumResyncSamplesSincePresent++ > MAX_RESYNC_SAMPLES_WITHOUT_PRESENT) { + resetErrorLocked(); + } + + if (mIgnorePresentFences) { + // If we're ignoring the present fences we have no way to know whether + // or not we're synchronized with the HW vsyncs, so we just request + // that the HW vsync events be turned on. + return true; + } + + // Check against kErrorThreshold / 2 to add some hysteresis before having to + // resync again + bool modelLocked = mModelUpdated && mError < (kErrorThreshold / 2); + ALOGV("[%s] addResyncSample returning %s", mName, modelLocked ? "locked" : "unlocked"); + return !modelLocked; +} + +void DispSync::endResync() {} + +status_t DispSync::addEventListener(const char* name, nsecs_t phase, Callback* callback) { + Mutex::Autolock lock(mMutex); + return mThread->addEventListener(name, phase, callback); +} + +void DispSync::setRefreshSkipCount(int count) { + Mutex::Autolock lock(mMutex); + ALOGD("setRefreshSkipCount(%d)", count); + mRefreshSkipCount = count; + updateModelLocked(); +} + +status_t DispSync::removeEventListener(Callback* callback) { + Mutex::Autolock lock(mMutex); + return mThread->removeEventListener(callback); +} + +status_t DispSync::changePhaseOffset(Callback* callback, nsecs_t phase) { + Mutex::Autolock lock(mMutex); + return mThread->changePhaseOffset(callback, phase); +} + +void DispSync::setPeriod(nsecs_t period) { + Mutex::Autolock lock(mMutex); + mPeriodBase = mPeriod = period; + mPhase = 0; + mReferenceTime = 0; + mThread->updateModel(mPeriod, mPhase, mReferenceTime); +} + +void DispSync::scalePeriod(uint32_t multiplier, uint32_t divisor) { + Mutex::Autolock lock(mMutex); + + // if only 1 of the properties is updated, we will get to this + // point "attempting" to set the scale to 1 when it is already + // 1. Check that special case so that we don't do a useless + // update of the model. + if ((multiplier == 1) && (divisor == 1) && (mPeriod == mPeriodBase)) return; + + mPeriod = mPeriodBase * multiplier / divisor; + mThread->updateModel(mPeriod, mPhase, mReferenceTime); +} + +nsecs_t DispSync::getPeriod() { + // lock mutex as mPeriod changes multiple times in updateModelLocked + Mutex::Autolock lock(mMutex); + return mPeriod; +} + +void DispSync::updateModelLocked() { + ALOGV("[%s] updateModelLocked %zu", mName, mNumResyncSamples); + if (mNumResyncSamples >= MIN_RESYNC_SAMPLES_FOR_UPDATE) { + ALOGV("[%s] Computing...", mName); + nsecs_t durationSum = 0; + nsecs_t minDuration = INT64_MAX; + nsecs_t maxDuration = 0; + for (size_t i = 1; i < mNumResyncSamples; i++) { + size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES; + size_t prev = (idx + MAX_RESYNC_SAMPLES - 1) % MAX_RESYNC_SAMPLES; + nsecs_t duration = mResyncSamples[idx] - mResyncSamples[prev]; + durationSum += duration; + minDuration = min(minDuration, duration); + maxDuration = max(maxDuration, duration); + } + + // Exclude the min and max from the average + durationSum -= minDuration + maxDuration; + mPeriodBase = mPeriod = durationSum / (mNumResyncSamples - 3); + + ALOGV("[%s] mPeriod = %" PRId64, mName, ns2us(mPeriod)); + + double sampleAvgX = 0; + double sampleAvgY = 0; + double scale = 2.0 * M_PI / double(mPeriod); + // Intentionally skip the first sample + for (size_t i = 1; i < mNumResyncSamples; i++) { + size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES; + nsecs_t sample = mResyncSamples[idx] - mReferenceTime; + double samplePhase = double(sample % mPeriod) * scale; + sampleAvgX += cos(samplePhase); + sampleAvgY += sin(samplePhase); + } + + sampleAvgX /= double(mNumResyncSamples - 1); + sampleAvgY /= double(mNumResyncSamples - 1); + + mPhase = nsecs_t(atan2(sampleAvgY, sampleAvgX) / scale); + + ALOGV("[%s] mPhase = %" PRId64, mName, ns2us(mPhase)); + + if (mPhase < -(mPeriod / 2)) { + mPhase += mPeriod; + ALOGV("[%s] Adjusting mPhase -> %" PRId64, mName, ns2us(mPhase)); + } + + if (kTraceDetailedInfo) { + ATRACE_INT64("DispSync:Period", mPeriod); + ATRACE_INT64("DispSync:Phase", mPhase + mPeriod / 2); + } + + // Artificially inflate the period if requested. + mPeriod += mPeriod * mRefreshSkipCount; + + mThread->updateModel(mPeriod, mPhase, mReferenceTime); + mModelUpdated = true; + } +} + +void DispSync::updateErrorLocked() { + if (!mModelUpdated) { + return; + } + + // Need to compare present fences against the un-adjusted refresh period, + // since they might arrive between two events. + nsecs_t period = mPeriod / (1 + mRefreshSkipCount); + + int numErrSamples = 0; + nsecs_t sqErrSum = 0; + + for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) { + // Only check for the cached value of signal time to avoid unecessary + // syscalls. It is the responsibility of the DispSync owner to + // call getSignalTime() periodically so the cache is updated when the + // fence signals. + nsecs_t time = mPresentFences[i]->getCachedSignalTime(); + if (time == Fence::SIGNAL_TIME_PENDING || time == Fence::SIGNAL_TIME_INVALID) { + continue; + } + + nsecs_t sample = time - mReferenceTime; + if (sample <= mPhase) { + continue; + } + + nsecs_t sampleErr = (sample - mPhase) % period; + if (sampleErr > period / 2) { + sampleErr -= period; + } + sqErrSum += sampleErr * sampleErr; + numErrSamples++; + } + + if (numErrSamples > 0) { + mError = sqErrSum / numErrSamples; + mZeroErrSamplesCount = 0; + } else { + mError = 0; + // Use mod ACCEPTABLE_ZERO_ERR_SAMPLES_COUNT to avoid log spam. + mZeroErrSamplesCount++; + ALOGE_IF((mZeroErrSamplesCount % ACCEPTABLE_ZERO_ERR_SAMPLES_COUNT) == 0, + "No present times for model error."); + } + + if (kTraceDetailedInfo) { + ATRACE_INT64("DispSync:Error", mError); + } +} + +void DispSync::resetErrorLocked() { + mPresentSampleOffset = 0; + mError = 0; + mZeroErrSamplesCount = 0; + for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) { + mPresentFences[i] = FenceTime::NO_FENCE; + } +} + +nsecs_t DispSync::computeNextRefresh(int periodOffset) const { + Mutex::Autolock lock(mMutex); + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t phase = mReferenceTime + mPhase; + return (((now - phase) / mPeriod) + periodOffset + 1) * mPeriod + phase; +} + +void DispSync::setIgnorePresentFences(bool ignore) { + Mutex::Autolock lock(mMutex); + if (mIgnorePresentFences != ignore) { + mIgnorePresentFences = ignore; + resetLocked(); + } +} + +void DispSync::dump(String8& result) const { + Mutex::Autolock lock(mMutex); + result.appendFormat("present fences are %s\n", mIgnorePresentFences ? "ignored" : "used"); + result.appendFormat("mPeriod: %" PRId64 " ns (%.3f fps; skipCount=%d)\n", mPeriod, + 1000000000.0 / mPeriod, mRefreshSkipCount); + result.appendFormat("mPhase: %" PRId64 " ns\n", mPhase); + result.appendFormat("mError: %" PRId64 " ns (sqrt=%.1f)\n", mError, sqrt(mError)); + result.appendFormat("mNumResyncSamplesSincePresent: %d (limit %d)\n", + mNumResyncSamplesSincePresent, MAX_RESYNC_SAMPLES_WITHOUT_PRESENT); + result.appendFormat("mNumResyncSamples: %zd (max %d)\n", mNumResyncSamples, MAX_RESYNC_SAMPLES); + + result.appendFormat("mResyncSamples:\n"); + nsecs_t previous = -1; + for (size_t i = 0; i < mNumResyncSamples; i++) { + size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES; + nsecs_t sampleTime = mResyncSamples[idx]; + if (i == 0) { + result.appendFormat(" %" PRId64 "\n", sampleTime); + } else { + result.appendFormat(" %" PRId64 " (+%" PRId64 ")\n", sampleTime, + sampleTime - previous); + } + previous = sampleTime; + } + + result.appendFormat("mPresentFences [%d]:\n", NUM_PRESENT_SAMPLES); + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + previous = Fence::SIGNAL_TIME_INVALID; + for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) { + size_t idx = (i + mPresentSampleOffset) % NUM_PRESENT_SAMPLES; + nsecs_t presentTime = mPresentFences[idx]->getSignalTime(); + if (presentTime == Fence::SIGNAL_TIME_PENDING) { + result.appendFormat(" [unsignaled fence]\n"); + } else if (presentTime == Fence::SIGNAL_TIME_INVALID) { + result.appendFormat(" [invalid fence]\n"); + } else if (previous == Fence::SIGNAL_TIME_PENDING || + previous == Fence::SIGNAL_TIME_INVALID) { + result.appendFormat(" %" PRId64 " (%.3f ms ago)\n", presentTime, + (now - presentTime) / 1000000.0); + } else { + result.appendFormat(" %" PRId64 " (+%" PRId64 " / %.3f) (%.3f ms ago)\n", presentTime, + presentTime - previous, (presentTime - previous) / (double)mPeriod, + (now - presentTime) / 1000000.0); + } + previous = presentTime; + } + + result.appendFormat("current monotonic time: %" PRId64 "\n", now); +} + +} // namespace impl + +} // namespace android diff --git a/services/surfaceflinger/Scheduler/DispSync.h b/services/surfaceflinger/Scheduler/DispSync.h new file mode 100644 index 0000000000..183966feba --- /dev/null +++ b/services/surfaceflinger/Scheduler/DispSync.h @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_DISPSYNC_H +#define ANDROID_DISPSYNC_H + +#include + +#include +#include +#include + +#include + +#include + +namespace android { + +class String8; +class FenceTime; + +class DispSync { +public: + class Callback { + public: + virtual ~Callback() = default; + virtual void onDispSyncEvent(nsecs_t when) = 0; + }; + + virtual ~DispSync(); + + virtual void reset() = 0; + virtual bool addPresentFence(const std::shared_ptr&) = 0; + virtual void beginResync() = 0; + virtual bool addResyncSample(nsecs_t timestamp) = 0; + virtual void endResync() = 0; + virtual void setPeriod(nsecs_t period) = 0; + virtual void scalePeriod(const uint32_t multiplier, uint32_t divisor) = 0; + virtual nsecs_t getPeriod() = 0; + virtual void setRefreshSkipCount(int count) = 0; + virtual status_t addEventListener(const char* name, nsecs_t phase, Callback* callback) = 0; + virtual status_t removeEventListener(Callback* callback) = 0; + virtual status_t changePhaseOffset(Callback* callback, nsecs_t phase) = 0; + virtual nsecs_t computeNextRefresh(int periodOffset) const = 0; + virtual void setIgnorePresentFences(bool ignore) = 0; + + virtual void dump(String8& result) const = 0; +}; + +namespace impl { + +class DispSyncThread; + +// DispSync maintains a model of the periodic hardware-based vsync events of a +// display and uses that model to execute period callbacks at specific phase +// offsets from the hardware vsync events. The model is constructed by +// feeding consecutive hardware event timestamps to the DispSync object via +// the addResyncSample method. +// +// The model is validated using timestamps from Fence objects that are passed +// to the DispSync object via the addPresentFence method. These fence +// timestamps should correspond to a hardware vsync event, but they need not +// be consecutive hardware vsync times. If this method determines that the +// current model accurately represents the hardware event times it will return +// false to indicate that a resynchronization (via addResyncSample) is not +// needed. +class DispSync : public android::DispSync { +public: + explicit DispSync(const char* name); + ~DispSync() override; + + void init(bool hasSyncFramework, int64_t dispSyncPresentTimeOffset); + + // reset clears the resync samples and error value. + void reset() override; + + // addPresentFence adds a fence for use in validating the current vsync + // event model. The fence need not be signaled at the time + // addPresentFence is called. When the fence does signal, its timestamp + // should correspond to a hardware vsync event. Unlike the + // addResyncSample method, the timestamps of consecutive fences need not + // correspond to consecutive hardware vsync events. + // + // This method should be called with the retire fence from each HWComposer + // set call that affects the display. + bool addPresentFence(const std::shared_ptr& fenceTime) override; + + // The beginResync, addResyncSample, and endResync methods are used to re- + // synchronize the DispSync's model to the hardware vsync events. The re- + // synchronization process involves first calling beginResync, then + // calling addResyncSample with a sequence of consecutive hardware vsync + // event timestamps, and finally calling endResync when addResyncSample + // indicates that no more samples are needed by returning false. + // + // This resynchronization process should be performed whenever the display + // is turned on (i.e. once immediately after it's turned on) and whenever + // addPresentFence returns true indicating that the model has drifted away + // from the hardware vsync events. + void beginResync() override; + bool addResyncSample(nsecs_t timestamp) override; + void endResync() override; + + // The setPeriod method sets the vsync event model's period to a specific + // value. This should be used to prime the model when a display is first + // turned on. It should NOT be used after that. + void setPeriod(nsecs_t period) override; + + // The scalePeriod method applies the multiplier and divisor to + // scale the vsync event model's period. The function is added + // for an experimental test mode and should not be used outside + // of that purpose. + void scalePeriod(const uint32_t multiplier, uint32_t divisor); + + // The getPeriod method returns the current vsync period. + nsecs_t getPeriod() override; + + // setRefreshSkipCount specifies an additional number of refresh + // cycles to skip. For example, on a 60Hz display, a skip count of 1 + // will result in events happening at 30Hz. Default is zero. The idea + // is to sacrifice smoothness for battery life. + void setRefreshSkipCount(int count) override; + + // addEventListener registers a callback to be called repeatedly at the + // given phase offset from the hardware vsync events. The callback is + // called from a separate thread and it should return reasonably quickly + // (i.e. within a few hundred microseconds). + status_t addEventListener(const char* name, nsecs_t phase, Callback* callback) override; + + // removeEventListener removes an already-registered event callback. Once + // this method returns that callback will no longer be called by the + // DispSync object. + status_t removeEventListener(Callback* callback) override; + + // changePhaseOffset changes the phase offset of an already-registered event callback. The + // method will make sure that there is no skipping or double-firing on the listener per frame, + // even when changing the offsets multiple times. + status_t changePhaseOffset(Callback* callback, nsecs_t phase) override; + + // computeNextRefresh computes when the next refresh is expected to begin. + // The periodOffset value can be used to move forward or backward; an + // offset of zero is the next refresh, -1 is the previous refresh, 1 is + // the refresh after next. etc. + nsecs_t computeNextRefresh(int periodOffset) const override; + + // In certain situations the present fences aren't a good indicator of vsync + // time, e.g. when vr flinger is active, or simply aren't available, + // e.g. when the sync framework isn't present. Use this method to toggle + // whether or not DispSync ignores present fences. If present fences are + // ignored, DispSync will always ask for hardware vsync events by returning + // true from addPresentFence() and addResyncSample(). + void setIgnorePresentFences(bool ignore) override; + + // dump appends human-readable debug info to the result string. + void dump(String8& result) const override; + +private: + void updateModelLocked(); + void updateErrorLocked(); + void resetLocked(); + void resetErrorLocked(); + + enum { MAX_RESYNC_SAMPLES = 32 }; + enum { MIN_RESYNC_SAMPLES_FOR_UPDATE = 6 }; + enum { NUM_PRESENT_SAMPLES = 8 }; + enum { MAX_RESYNC_SAMPLES_WITHOUT_PRESENT = 4 }; + enum { ACCEPTABLE_ZERO_ERR_SAMPLES_COUNT = 64 }; + + const char* const mName; + + // mPeriod is the computed period of the modeled vsync events in + // nanoseconds. + nsecs_t mPeriod; + nsecs_t mPeriodBase; + + // mPhase is the phase offset of the modeled vsync events. It is the + // number of nanoseconds from time 0 to the first vsync event. + nsecs_t mPhase; + + // mReferenceTime is the reference time of the modeled vsync events. + // It is the nanosecond timestamp of the first vsync event after a resync. + nsecs_t mReferenceTime; + + // mError is the computed model error. It is based on the difference + // between the estimated vsync event times and those observed in the + // mPresentFences array. + nsecs_t mError; + + // mZeroErrSamplesCount keeps track of how many times in a row there were + // zero timestamps available in the mPresentFences array. + // Used to sanity check that we are able to calculate the model error. + size_t mZeroErrSamplesCount; + + // Whether we have updated the vsync event model since the last resync. + bool mModelUpdated; + + // These member variables are the state used during the resynchronization + // process to store information about the hardware vsync event times used + // to compute the model. + nsecs_t mResyncSamples[MAX_RESYNC_SAMPLES]; + size_t mFirstResyncSample; + size_t mNumResyncSamples; + int mNumResyncSamplesSincePresent; + + // These member variables store information about the present fences used + // to validate the currently computed model. + std::shared_ptr mPresentFences[NUM_PRESENT_SAMPLES]{FenceTime::NO_FENCE}; + size_t mPresentSampleOffset; + + int mRefreshSkipCount; + + // mThread is the thread from which all the callbacks are called. + sp mThread; + + // mMutex is used to protect access to all member variables. + mutable Mutex mMutex; + + // This is the offset from the present fence timestamps to the corresponding + // vsync event. + int64_t mPresentTimeOffset; + + // Ignore present (retire) fences if the device doesn't have support for the + // sync framework + bool mIgnorePresentFences; + + std::unique_ptr mZeroPhaseTracer; +}; + +} // namespace impl + +} // namespace android + +#endif // ANDROID_DISPSYNC_H diff --git a/services/surfaceflinger/Scheduler/EventControlThread.cpp b/services/surfaceflinger/Scheduler/EventControlThread.cpp new file mode 100644 index 0000000000..fb6cff5705 --- /dev/null +++ b/services/surfaceflinger/Scheduler/EventControlThread.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include +#include +#include + +#include "EventControlThread.h" + +namespace android { + +EventControlThread::~EventControlThread() = default; + +namespace impl { + +EventControlThread::EventControlThread(EventControlThread::SetVSyncEnabledFunction function) + : mSetVSyncEnabled(function) { + pthread_setname_np(mThread.native_handle(), "EventControlThread"); + + pid_t tid = pthread_gettid_np(mThread.native_handle()); + setpriority(PRIO_PROCESS, tid, ANDROID_PRIORITY_URGENT_DISPLAY); + set_sched_policy(tid, SP_FOREGROUND); +} + +EventControlThread::~EventControlThread() { + { + std::lock_guard lock(mMutex); + mKeepRunning = false; + mCondition.notify_all(); + } + mThread.join(); +} + +void EventControlThread::setVsyncEnabled(bool enabled) { + std::lock_guard lock(mMutex); + mVsyncEnabled = enabled; + mCondition.notify_all(); +} + +// Unfortunately std::unique_lock gives warnings with -Wthread-safety +void EventControlThread::threadMain() NO_THREAD_SAFETY_ANALYSIS { + auto keepRunning = true; + auto currentVsyncEnabled = false; + + while (keepRunning) { + mSetVSyncEnabled(currentVsyncEnabled); + + std::unique_lock lock(mMutex); + mCondition.wait(lock, [this, currentVsyncEnabled, keepRunning]() NO_THREAD_SAFETY_ANALYSIS { + return currentVsyncEnabled != mVsyncEnabled || keepRunning != mKeepRunning; + }); + currentVsyncEnabled = mVsyncEnabled; + keepRunning = mKeepRunning; + } +} + +} // namespace impl +} // namespace android diff --git a/services/surfaceflinger/Scheduler/EventControlThread.h b/services/surfaceflinger/Scheduler/EventControlThread.h new file mode 100644 index 0000000000..cafae53400 --- /dev/null +++ b/services/surfaceflinger/Scheduler/EventControlThread.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include +#include + +#include + +namespace android { + +class EventControlThread { +public: + virtual ~EventControlThread(); + + virtual void setVsyncEnabled(bool enabled) = 0; +}; + +namespace impl { + +class EventControlThread final : public android::EventControlThread { +public: + using SetVSyncEnabledFunction = std::function; + + explicit EventControlThread(SetVSyncEnabledFunction function); + ~EventControlThread(); + + // EventControlThread implementation + void setVsyncEnabled(bool enabled) override; + +private: + void threadMain(); + + std::mutex mMutex; + std::condition_variable mCondition; + + const SetVSyncEnabledFunction mSetVSyncEnabled; + bool mVsyncEnabled GUARDED_BY(mMutex) = false; + bool mKeepRunning GUARDED_BY(mMutex) = true; + + // Must be last so that everything is initialized before the thread starts. + std::thread mThread{&EventControlThread::threadMain, this}; +}; + +} // namespace impl +} // namespace android diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp new file mode 100644 index 0000000000..b84177c1da --- /dev/null +++ b/services/surfaceflinger/Scheduler/EventThread.cpp @@ -0,0 +1,410 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include + +#include "EventThread.h" + +using namespace std::chrono_literals; + +// --------------------------------------------------------------------------- + +namespace android { + +// --------------------------------------------------------------------------- + +EventThread::~EventThread() = default; + +namespace impl { + +EventThread::EventThread(VSyncSource* src, ResyncWithRateLimitCallback resyncWithRateLimitCallback, + InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName) + : mVSyncSource(src), + mResyncWithRateLimitCallback(resyncWithRateLimitCallback), + mInterceptVSyncsCallback(interceptVSyncsCallback) { + for (auto& event : mVSyncEvent) { + event.header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC; + event.header.id = 0; + event.header.timestamp = 0; + event.vsync.count = 0; + } + + mThread = std::thread(&EventThread::threadMain, this); + + pthread_setname_np(mThread.native_handle(), threadName); + + pid_t tid = pthread_gettid_np(mThread.native_handle()); + + // Use SCHED_FIFO to minimize jitter + constexpr int EVENT_THREAD_PRIORITY = 2; + struct sched_param param = {0}; + param.sched_priority = EVENT_THREAD_PRIORITY; + if (pthread_setschedparam(mThread.native_handle(), SCHED_FIFO, ¶m) != 0) { + ALOGE("Couldn't set SCHED_FIFO for EventThread"); + } + + set_sched_policy(tid, SP_FOREGROUND); +} + +EventThread::~EventThread() { + { + std::lock_guard lock(mMutex); + mKeepRunning = false; + mCondition.notify_all(); + } + mThread.join(); +} + +void EventThread::setPhaseOffset(nsecs_t phaseOffset) { + std::lock_guard lock(mMutex); + mVSyncSource->setPhaseOffset(phaseOffset); +} + +sp EventThread::createEventConnection() const { + return new Connection(const_cast(this)); +} + +status_t EventThread::registerDisplayEventConnection( + const sp& connection) { + std::lock_guard lock(mMutex); + mDisplayEventConnections.add(connection); + mCondition.notify_all(); + return NO_ERROR; +} + +void EventThread::removeDisplayEventConnectionLocked( + const wp& connection) { + mDisplayEventConnections.remove(connection); +} + +void EventThread::setVsyncRate(uint32_t count, const sp& connection) { + if (int32_t(count) >= 0) { // server must protect against bad params + std::lock_guard lock(mMutex); + const int32_t new_count = (count == 0) ? -1 : count; + if (connection->count != new_count) { + connection->count = new_count; + mCondition.notify_all(); + } + } +} + +void EventThread::requestNextVsync(const sp& connection) { + std::lock_guard lock(mMutex); + + if (mResyncWithRateLimitCallback) { + mResyncWithRateLimitCallback(); + } + + if (connection->count < 0) { + connection->count = 0; + mCondition.notify_all(); + } +} + +void EventThread::onScreenReleased() { + std::lock_guard lock(mMutex); + if (!mUseSoftwareVSync) { + // disable reliance on h/w vsync + mUseSoftwareVSync = true; + mCondition.notify_all(); + } +} + +void EventThread::onScreenAcquired() { + std::lock_guard lock(mMutex); + if (mUseSoftwareVSync) { + // resume use of h/w vsync + mUseSoftwareVSync = false; + mCondition.notify_all(); + } +} + +void EventThread::onVSyncEvent(nsecs_t timestamp) { + std::lock_guard lock(mMutex); + mVSyncEvent[0].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC; + mVSyncEvent[0].header.id = 0; + mVSyncEvent[0].header.timestamp = timestamp; + mVSyncEvent[0].vsync.count++; + mCondition.notify_all(); +} + +void EventThread::onHotplugReceived(DisplayType displayType, bool connected) { + std::lock_guard lock(mMutex); + + DisplayEventReceiver::Event event; + event.header.type = DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG; + event.header.id = displayType == DisplayType::Primary ? 0 : 1; + event.header.timestamp = systemTime(); + event.hotplug.connected = connected; + + mPendingEvents.add(event); + mCondition.notify_all(); +} + +void EventThread::threadMain() NO_THREAD_SAFETY_ANALYSIS { + std::unique_lock lock(mMutex); + while (mKeepRunning) { + DisplayEventReceiver::Event event; + Vector > signalConnections; + signalConnections = waitForEventLocked(&lock, &event); + + // dispatch events to listeners... + const size_t count = signalConnections.size(); + for (size_t i = 0; i < count; i++) { + const sp& conn(signalConnections[i]); + // now see if we still need to report this event + status_t err = conn->postEvent(event); + if (err == -EAGAIN || err == -EWOULDBLOCK) { + // The destination doesn't accept events anymore, it's probably + // full. For now, we just drop the events on the floor. + // FIXME: Note that some events cannot be dropped and would have + // to be re-sent later. + // Right-now we don't have the ability to do this. + ALOGW("EventThread: dropping event (%08x) for connection %p", event.header.type, + conn.get()); + } else if (err < 0) { + // handle any other error on the pipe as fatal. the only + // reasonable thing to do is to clean-up this connection. + // The most common error we'll get here is -EPIPE. + removeDisplayEventConnectionLocked(signalConnections[i]); + } + } + } +} + +// This will return when (1) a vsync event has been received, and (2) there was +// at least one connection interested in receiving it when we started waiting. +Vector > EventThread::waitForEventLocked( + std::unique_lock* lock, DisplayEventReceiver::Event* outEvent) { + Vector > signalConnections; + + while (signalConnections.isEmpty() && mKeepRunning) { + bool eventPending = false; + bool waitForVSync = false; + + size_t vsyncCount = 0; + nsecs_t timestamp = 0; + for (auto& event : mVSyncEvent) { + timestamp = event.header.timestamp; + if (timestamp) { + // we have a vsync event to dispatch + if (mInterceptVSyncsCallback) { + mInterceptVSyncsCallback(timestamp); + } + *outEvent = event; + event.header.timestamp = 0; + vsyncCount = event.vsync.count; + break; + } + } + + if (!timestamp) { + // no vsync event, see if there are some other event + eventPending = !mPendingEvents.isEmpty(); + if (eventPending) { + // we have some other event to dispatch + *outEvent = mPendingEvents[0]; + mPendingEvents.removeAt(0); + } + } + + // find out connections waiting for events + size_t count = mDisplayEventConnections.size(); + for (size_t i = 0; i < count;) { + sp connection(mDisplayEventConnections[i].promote()); + if (connection != nullptr) { + bool added = false; + if (connection->count >= 0) { + // we need vsync events because at least + // one connection is waiting for it + waitForVSync = true; + if (timestamp) { + // we consume the event only if it's time + // (ie: we received a vsync event) + if (connection->count == 0) { + // fired this time around + connection->count = -1; + signalConnections.add(connection); + added = true; + } else if (connection->count == 1 || + (vsyncCount % connection->count) == 0) { + // continuous event, and time to report it + signalConnections.add(connection); + added = true; + } + } + } + + if (eventPending && !timestamp && !added) { + // we don't have a vsync event to process + // (timestamp==0), but we have some pending + // messages. + signalConnections.add(connection); + } + ++i; + } else { + // we couldn't promote this reference, the connection has + // died, so clean-up! + mDisplayEventConnections.removeAt(i); + --count; + } + } + + // Here we figure out if we need to enable or disable vsyncs + if (timestamp && !waitForVSync) { + // we received a VSYNC but we have no clients + // don't report it, and disable VSYNC events + disableVSyncLocked(); + } else if (!timestamp && waitForVSync) { + // we have at least one client, so we want vsync enabled + // (TODO: this function is called right after we finish + // notifying clients of a vsync, so this call will be made + // at the vsync rate, e.g. 60fps. If we can accurately + // track the current state we could avoid making this call + // so often.) + enableVSyncLocked(); + } + + // note: !timestamp implies signalConnections.isEmpty(), because we + // don't populate signalConnections if there's no vsync pending + if (!timestamp && !eventPending) { + // wait for something to happen + if (waitForVSync) { + // This is where we spend most of our time, waiting + // for vsync events and new client registrations. + // + // If the screen is off, we can't use h/w vsync, so we + // use a 16ms timeout instead. It doesn't need to be + // precise, we just need to keep feeding our clients. + // + // We don't want to stall if there's a driver bug, so we + // use a (long) timeout when waiting for h/w vsync, and + // generate fake events when necessary. + bool softwareSync = mUseSoftwareVSync; + auto timeout = softwareSync ? 16ms : 1000ms; + if (mCondition.wait_for(*lock, timeout) == std::cv_status::timeout) { + if (!softwareSync) { + ALOGW("Timed out waiting for hw vsync; faking it"); + } + // FIXME: how do we decide which display id the fake + // vsync came from ? + mVSyncEvent[0].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC; + mVSyncEvent[0].header.id = 0; + mVSyncEvent[0].header.timestamp = systemTime(SYSTEM_TIME_MONOTONIC); + mVSyncEvent[0].vsync.count++; + } + } else { + // Nobody is interested in vsync, so we just want to sleep. + // h/w vsync should be disabled, so this will wait until we + // get a new connection, or an existing connection becomes + // interested in receiving vsync again. + mCondition.wait(*lock); + } + } + } + + // here we're guaranteed to have a timestamp and some connections to signal + // (The connections might have dropped out of mDisplayEventConnections + // while we were asleep, but we'll still have strong references to them.) + return signalConnections; +} + +void EventThread::enableVSyncLocked() { + if (!mUseSoftwareVSync) { + // never enable h/w VSYNC when screen is off + if (!mVsyncEnabled) { + mVsyncEnabled = true; + mVSyncSource->setCallback(this); + mVSyncSource->setVSyncEnabled(true); + } + } + mDebugVsyncEnabled = true; +} + +void EventThread::disableVSyncLocked() { + if (mVsyncEnabled) { + mVsyncEnabled = false; + mVSyncSource->setVSyncEnabled(false); + mDebugVsyncEnabled = false; + } +} + +void EventThread::dump(String8& result) const { + std::lock_guard lock(mMutex); + result.appendFormat("VSYNC state: %s\n", mDebugVsyncEnabled ? "enabled" : "disabled"); + result.appendFormat(" soft-vsync: %s\n", mUseSoftwareVSync ? "enabled" : "disabled"); + result.appendFormat(" numListeners=%zu,\n events-delivered: %u\n", + mDisplayEventConnections.size(), mVSyncEvent[0].vsync.count); + for (size_t i = 0; i < mDisplayEventConnections.size(); i++) { + sp connection = mDisplayEventConnections.itemAt(i).promote(); + result.appendFormat(" %p: count=%d\n", connection.get(), + connection != nullptr ? connection->count : 0); + } +} + +// --------------------------------------------------------------------------- + +EventThread::Connection::Connection(EventThread* eventThread) + : count(-1), mEventThread(eventThread), mChannel(gui::BitTube::DefaultSize) {} + +EventThread::Connection::~Connection() { + // do nothing here -- clean-up will happen automatically + // when the main thread wakes up +} + +void EventThread::Connection::onFirstRef() { + // NOTE: mEventThread doesn't hold a strong reference on us + mEventThread->registerDisplayEventConnection(this); +} + +status_t EventThread::Connection::stealReceiveChannel(gui::BitTube* outChannel) { + outChannel->setReceiveFd(mChannel.moveReceiveFd()); + return NO_ERROR; +} + +status_t EventThread::Connection::setVsyncRate(uint32_t count) { + mEventThread->setVsyncRate(count, this); + return NO_ERROR; +} + +void EventThread::Connection::requestNextVsync() { + mEventThread->requestNextVsync(this); +} + +status_t EventThread::Connection::postEvent(const DisplayEventReceiver::Event& event) { + ssize_t size = DisplayEventReceiver::sendEvents(&mChannel, &event, 1); + return size < 0 ? status_t(size) : status_t(NO_ERROR); +} + +// --------------------------------------------------------------------------- + +} // namespace impl +} // namespace android diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h new file mode 100644 index 0000000000..a0262b2ad9 --- /dev/null +++ b/services/surfaceflinger/Scheduler/EventThread.h @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include + +// --------------------------------------------------------------------------- +namespace android { +// --------------------------------------------------------------------------- + +class EventThreadTest; +class SurfaceFlinger; +class String8; + +// --------------------------------------------------------------------------- + +class VSyncSource { +public: + class Callback { + public: + virtual ~Callback() {} + virtual void onVSyncEvent(nsecs_t when) = 0; + }; + + virtual ~VSyncSource() {} + virtual void setVSyncEnabled(bool enable) = 0; + virtual void setCallback(Callback* callback) = 0; + virtual void setPhaseOffset(nsecs_t phaseOffset) = 0; +}; + +class EventThread { +public: + // TODO: Remove once stable display IDs are plumbed through SF/WM interface. + enum class DisplayType { Primary, External }; + + virtual ~EventThread(); + + virtual sp createEventConnection() const = 0; + + // called before the screen is turned off from main thread + virtual void onScreenReleased() = 0; + + // called after the screen is turned on from main thread + virtual void onScreenAcquired() = 0; + + // called when receiving a hotplug event + virtual void onHotplugReceived(DisplayType displayType, bool connected) = 0; + + virtual void dump(String8& result) const = 0; + + virtual void setPhaseOffset(nsecs_t phaseOffset) = 0; +}; + +namespace impl { + +class EventThread : public android::EventThread, private VSyncSource::Callback { + class Connection : public BnDisplayEventConnection { + public: + explicit Connection(EventThread* eventThread); + virtual ~Connection(); + + virtual status_t postEvent(const DisplayEventReceiver::Event& event); + + // count >= 1 : continuous event. count is the vsync rate + // count == 0 : one-shot event that has not fired + // count ==-1 : one-shot event that fired this round / disabled + int32_t count; + + private: + virtual void onFirstRef(); + status_t stealReceiveChannel(gui::BitTube* outChannel) override; + status_t setVsyncRate(uint32_t count) override; + void requestNextVsync() override; // asynchronous + EventThread* const mEventThread; + gui::BitTube mChannel; + }; + +public: + using ResyncWithRateLimitCallback = std::function; + using InterceptVSyncsCallback = std::function; + + EventThread(VSyncSource* src, ResyncWithRateLimitCallback resyncWithRateLimitCallback, + InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName); + ~EventThread(); + + sp createEventConnection() const override; + status_t registerDisplayEventConnection(const sp& connection); + + void setVsyncRate(uint32_t count, const sp& connection); + void requestNextVsync(const sp& connection); + + // called before the screen is turned off from main thread + void onScreenReleased() override; + + // called after the screen is turned on from main thread + void onScreenAcquired() override; + + // called when receiving a hotplug event + void onHotplugReceived(DisplayType displayType, bool connected) override; + + void dump(String8& result) const override; + + void setPhaseOffset(nsecs_t phaseOffset) override; + +private: + friend EventThreadTest; + + void threadMain(); + Vector> waitForEventLocked(std::unique_lock* lock, + DisplayEventReceiver::Event* event) + REQUIRES(mMutex); + + void removeDisplayEventConnectionLocked(const wp& connection) REQUIRES(mMutex); + void enableVSyncLocked() REQUIRES(mMutex); + void disableVSyncLocked() REQUIRES(mMutex); + + // Implements VSyncSource::Callback + void onVSyncEvent(nsecs_t timestamp) override; + + // constants + VSyncSource* const mVSyncSource GUARDED_BY(mMutex) = nullptr; + const ResyncWithRateLimitCallback mResyncWithRateLimitCallback; + const InterceptVSyncsCallback mInterceptVSyncsCallback; + + std::thread mThread; + mutable std::mutex mMutex; + mutable std::condition_variable mCondition; + + // protected by mLock + SortedVector> mDisplayEventConnections GUARDED_BY(mMutex); + Vector mPendingEvents GUARDED_BY(mMutex); + std::array mVSyncEvent GUARDED_BY(mMutex); + bool mUseSoftwareVSync GUARDED_BY(mMutex) = false; + bool mVsyncEnabled GUARDED_BY(mMutex) = false; + bool mKeepRunning GUARDED_BY(mMutex) = true; + + // for debugging + bool mDebugVsyncEnabled GUARDED_BY(mMutex) = false; +}; + +// --------------------------------------------------------------------------- + +} // namespace impl +} // namespace android diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp new file mode 100644 index 0000000000..056d381eb9 --- /dev/null +++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include + +#include "EventThread.h" +#include "MessageQueue.h" +#include "SurfaceFlinger.h" + +namespace android { + +// --------------------------------------------------------------------------- + +MessageBase::MessageBase() : MessageHandler() {} + +MessageBase::~MessageBase() {} + +void MessageBase::handleMessage(const Message&) { + this->handler(); + barrier.open(); +}; + +// --------------------------------------------------------------------------- + +MessageQueue::~MessageQueue() = default; + +// --------------------------------------------------------------------------- + +namespace impl { + +void MessageQueue::Handler::dispatchRefresh() { + if ((android_atomic_or(eventMaskRefresh, &mEventMask) & eventMaskRefresh) == 0) { + mQueue.mLooper->sendMessage(this, Message(MessageQueue::REFRESH)); + } +} + +void MessageQueue::Handler::dispatchInvalidate() { + if ((android_atomic_or(eventMaskInvalidate, &mEventMask) & eventMaskInvalidate) == 0) { + mQueue.mLooper->sendMessage(this, Message(MessageQueue::INVALIDATE)); + } +} + +void MessageQueue::Handler::handleMessage(const Message& message) { + switch (message.what) { + case INVALIDATE: + android_atomic_and(~eventMaskInvalidate, &mEventMask); + mQueue.mFlinger->onMessageReceived(message.what); + break; + case REFRESH: + android_atomic_and(~eventMaskRefresh, &mEventMask); + mQueue.mFlinger->onMessageReceived(message.what); + break; + } +} + +// --------------------------------------------------------------------------- + +void MessageQueue::init(const sp& flinger) { + mFlinger = flinger; + mLooper = new Looper(true); + mHandler = new Handler(*this); +} + +void MessageQueue::setEventThread(android::EventThread* eventThread) { + if (mEventThread == eventThread) { + return; + } + + if (mEventTube.getFd() >= 0) { + mLooper->removeFd(mEventTube.getFd()); + } + + mEventThread = eventThread; + mEvents = eventThread->createEventConnection(); + mEvents->stealReceiveChannel(&mEventTube); + mLooper->addFd(mEventTube.getFd(), 0, Looper::EVENT_INPUT, MessageQueue::cb_eventReceiver, + this); +} + +void MessageQueue::waitMessage() { + do { + IPCThreadState::self()->flushCommands(); + int32_t ret = mLooper->pollOnce(-1); + switch (ret) { + case Looper::POLL_WAKE: + case Looper::POLL_CALLBACK: + continue; + case Looper::POLL_ERROR: + ALOGE("Looper::POLL_ERROR"); + continue; + case Looper::POLL_TIMEOUT: + // timeout (should not happen) + continue; + default: + // should not happen + ALOGE("Looper::pollOnce() returned unknown status %d", ret); + continue; + } + } while (true); +} + +status_t MessageQueue::postMessage(const sp& messageHandler, nsecs_t relTime) { + const Message dummyMessage; + if (relTime > 0) { + mLooper->sendMessageDelayed(relTime, messageHandler, dummyMessage); + } else { + mLooper->sendMessage(messageHandler, dummyMessage); + } + return NO_ERROR; +} + +void MessageQueue::invalidate() { + mEvents->requestNextVsync(); +} + +void MessageQueue::refresh() { + mHandler->dispatchRefresh(); +} + +int MessageQueue::cb_eventReceiver(int fd, int events, void* data) { + MessageQueue* queue = reinterpret_cast(data); + return queue->eventReceiver(fd, events); +} + +int MessageQueue::eventReceiver(int /*fd*/, int /*events*/) { + ssize_t n; + DisplayEventReceiver::Event buffer[8]; + while ((n = DisplayEventReceiver::getEvents(&mEventTube, buffer, 8)) > 0) { + for (int i = 0; i < n; i++) { + if (buffer[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) { + mHandler->dispatchInvalidate(); + break; + } + } + } + return 1; +} + +// --------------------------------------------------------------------------- + +} // namespace impl +} // namespace android diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h new file mode 100644 index 0000000000..90d1c72450 --- /dev/null +++ b/services/surfaceflinger/Scheduler/MessageQueue.h @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_MESSAGE_QUEUE_H +#define ANDROID_MESSAGE_QUEUE_H + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "Barrier.h" + +#include + +namespace android { + +class EventThread; +class SurfaceFlinger; + +// --------------------------------------------------------------------------- + +class MessageBase : public MessageHandler { +public: + MessageBase(); + + // return true if message has a handler + virtual bool handler() = 0; + + // waits for the handler to be processed + void wait() const { barrier.wait(); } + +protected: + virtual ~MessageBase(); + +private: + virtual void handleMessage(const Message& message); + + mutable Barrier barrier; +}; + +class LambdaMessage : public MessageBase { +public: + explicit LambdaMessage(std::function handler) + : MessageBase(), mHandler(std::move(handler)) {} + + bool handler() override { + mHandler(); + // This return value is no longer checked, so it's always safe to return true + return true; + } + +private: + const std::function mHandler; +}; + +// --------------------------------------------------------------------------- + +class MessageQueue { +public: + enum { + INVALIDATE = 0, + REFRESH = 1, + }; + + virtual ~MessageQueue(); + + virtual void init(const sp& flinger) = 0; + virtual void setEventThread(EventThread* events) = 0; + virtual void waitMessage() = 0; + virtual status_t postMessage(const sp& message, nsecs_t reltime = 0) = 0; + virtual void invalidate() = 0; + virtual void refresh() = 0; +}; + +// --------------------------------------------------------------------------- + +namespace impl { + +class MessageQueue final : public android::MessageQueue { + class Handler : public MessageHandler { + enum { eventMaskInvalidate = 0x1, eventMaskRefresh = 0x2, eventMaskTransaction = 0x4 }; + MessageQueue& mQueue; + int32_t mEventMask; + + public: + explicit Handler(MessageQueue& queue) : mQueue(queue), mEventMask(0) {} + virtual void handleMessage(const Message& message); + void dispatchRefresh(); + void dispatchInvalidate(); + }; + + friend class Handler; + + sp mFlinger; + sp mLooper; + android::EventThread* mEventThread; + sp mEvents; + gui::BitTube mEventTube; + sp mHandler; + + static int cb_eventReceiver(int fd, int events, void* data); + int eventReceiver(int fd, int events); + +public: + ~MessageQueue() override = default; + void init(const sp& flinger) override; + void setEventThread(android::EventThread* events) override; + + void waitMessage() override; + status_t postMessage(const sp& message, nsecs_t reltime = 0) override; + + // sends INVALIDATE message at next VSYNC + void invalidate() override; + // sends REFRESH message at next VSYNC + void refresh() override; +}; + +// --------------------------------------------------------------------------- + +} // namespace impl +} // namespace android + +#endif /* ANDROID_MESSAGE_QUEUE_H */ diff --git a/services/surfaceflinger/Scheduler/VSyncModulator.h b/services/surfaceflinger/Scheduler/VSyncModulator.h new file mode 100644 index 0000000000..7dfad43f70 --- /dev/null +++ b/services/surfaceflinger/Scheduler/VSyncModulator.h @@ -0,0 +1,148 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include + +using namespace android::surfaceflinger; + +namespace android { + +/* + * Modulates the vsync-offsets depending on current SurfaceFlinger state. + */ +class VSyncModulator { +private: + // Number of frames we'll keep the early phase offsets once they are activated. This acts as a + // low-pass filter in case the client isn't quick enough in sending new transactions. + const int MIN_EARLY_FRAME_COUNT = 2; + +public: + struct Offsets { + nsecs_t sf; + nsecs_t app; + }; + + enum TransactionStart { EARLY, NORMAL }; + + // Sets the phase offsets + // + // sfEarly: The phase offset when waking up SF early, which happens when marking a transaction + // as early. May be the same as late, in which case we don't shift offsets. + // sfEarlyGl: Like sfEarly, but only if we used GL composition. If we use both GL composition + // and the transaction was marked as early, we'll use sfEarly. + // sfLate: The regular SF vsync phase offset. + // appEarly: Like sfEarly, but for the app-vsync + // appEarlyGl: Like sfEarlyGl, but for the app-vsync. + // appLate: The regular app vsync phase offset. + void setPhaseOffsets(Offsets early, Offsets earlyGl, Offsets late) { + mEarlyOffsets = early; + mEarlyGlOffsets = earlyGl; + mLateOffsets = late; + mOffsets = late; + } + + Offsets getEarlyOffsets() const { return mEarlyOffsets; } + + Offsets getEarlyGlOffsets() const { return mEarlyGlOffsets; } + + void setEventThreads(EventThread* sfEventThread, EventThread* appEventThread) { + mSfEventThread = sfEventThread; + mAppEventThread = appEventThread; + } + + void setTransactionStart(TransactionStart transactionStart) { + if (transactionStart == TransactionStart::EARLY) { + mRemainingEarlyFrameCount = MIN_EARLY_FRAME_COUNT; + } + + // An early transaction stays an early transaction. + if (transactionStart == mTransactionStart || mTransactionStart == TransactionStart::EARLY) { + return; + } + mTransactionStart = transactionStart; + updateOffsets(); + } + + void onTransactionHandled() { + if (mTransactionStart == TransactionStart::NORMAL) return; + mTransactionStart = TransactionStart::NORMAL; + updateOffsets(); + } + + void onRefreshed(bool usedRenderEngine) { + bool updateOffsetsNeeded = false; + if (mRemainingEarlyFrameCount > 0) { + mRemainingEarlyFrameCount--; + updateOffsetsNeeded = true; + } + if (usedRenderEngine != mLastFrameUsedRenderEngine) { + mLastFrameUsedRenderEngine = usedRenderEngine; + updateOffsetsNeeded = true; + } + if (updateOffsetsNeeded) { + updateOffsets(); + } + } + +private: + void updateOffsets() { + const Offsets desired = getOffsets(); + const Offsets current = mOffsets; + + bool changed = false; + if (desired.sf != current.sf) { + mSfEventThread->setPhaseOffset(desired.sf); + changed = true; + } + if (desired.app != current.app) { + mAppEventThread->setPhaseOffset(desired.app); + changed = true; + } + + if (changed) { + mOffsets = desired; + } + } + + Offsets getOffsets() { + if (mTransactionStart == TransactionStart::EARLY || mRemainingEarlyFrameCount > 0) { + return mEarlyOffsets; + } else if (mLastFrameUsedRenderEngine) { + return mEarlyGlOffsets; + } else { + return mLateOffsets; + } + } + + Offsets mLateOffsets; + Offsets mEarlyOffsets; + Offsets mEarlyGlOffsets; + + EventThread* mSfEventThread = nullptr; + EventThread* mAppEventThread = nullptr; + + std::atomic mOffsets; + + std::atomic mTransactionStart = TransactionStart::NORMAL; + std::atomic mLastFrameUsedRenderEngine = false; + std::atomic mRemainingEarlyFrameCount = 0; +}; + +} // namespace android diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 8310455ac5..d87df59dfa 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -70,10 +70,7 @@ #include "Colorizer.h" #include "ContainerLayer.h" #include "DdmConnection.h" -#include "DispSync.h" #include "DisplayDevice.h" -#include "EventControlThread.h" -#include "EventThread.h" #include "Layer.h" #include "LayerVector.h" #include "MonitoredProducer.h" @@ -85,10 +82,12 @@ #include "DisplayHardware/FramebufferSurface.h" #include "DisplayHardware/HWComposer.h" #include "DisplayHardware/VirtualDisplaySurface.h" - #include "Effects/Daltonizer.h" - #include "RenderEngine/RenderEngine.h" +#include "Scheduler/DispSync.h" +#include "Scheduler/EventControlThread.h" +#include "Scheduler/EventThread.h" + #include #include diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 12f4185e80..5ceea3a0e2 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -55,23 +55,22 @@ #include "Barrier.h" #include "DisplayDevice.h" -#include "DispSync.h" -#include "EventThread.h" #include "FrameTracker.h" +#include "LayerBE.h" #include "LayerStats.h" #include "LayerVector.h" -#include "MessageQueue.h" +#include "StartPropertySetThread.h" #include "SurfaceInterceptor.h" #include "SurfaceTracing.h" -#include "StartPropertySetThread.h" -#include "TimeStats/TimeStats.h" -#include "LayerBE.h" -#include "VSyncModulator.h" #include "DisplayHardware/HWC2.h" #include "DisplayHardware/HWComposer.h" - #include "Effects/Daltonizer.h" +#include "Scheduler/DispSync.h" +#include "Scheduler/EventThread.h" +#include "Scheduler/MessageQueue.h" +#include "Scheduler/VSyncModulator.h" +#include "TimeStats/TimeStats.h" #include #include diff --git a/services/surfaceflinger/VSyncModulator.h b/services/surfaceflinger/VSyncModulator.h deleted file mode 100644 index e071a599c9..0000000000 --- a/services/surfaceflinger/VSyncModulator.h +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -#include - -using namespace android::surfaceflinger; - -namespace android { - -/* - * Modulates the vsync-offsets depending on current SurfaceFlinger state. - */ -class VSyncModulator { -private: - - // Number of frames we'll keep the early phase offsets once they are activated. This acts as a - // low-pass filter in case the client isn't quick enough in sending new transactions. - const int MIN_EARLY_FRAME_COUNT = 2; - -public: - - struct Offsets { - nsecs_t sf; - nsecs_t app; - }; - - enum TransactionStart { - EARLY, - NORMAL - }; - - // Sets the phase offsets - // - // sfEarly: The phase offset when waking up SF early, which happens when marking a transaction - // as early. May be the same as late, in which case we don't shift offsets. - // sfEarlyGl: Like sfEarly, but only if we used GL composition. If we use both GL composition - // and the transaction was marked as early, we'll use sfEarly. - // sfLate: The regular SF vsync phase offset. - // appEarly: Like sfEarly, but for the app-vsync - // appEarlyGl: Like sfEarlyGl, but for the app-vsync. - // appLate: The regular app vsync phase offset. - void setPhaseOffsets(Offsets early, Offsets earlyGl, Offsets late) { - mEarlyOffsets = early; - mEarlyGlOffsets = earlyGl; - mLateOffsets = late; - mOffsets = late; - } - - Offsets getEarlyOffsets() const { - return mEarlyOffsets; - } - - Offsets getEarlyGlOffsets() const { - return mEarlyGlOffsets; - } - - void setEventThreads(EventThread* sfEventThread, EventThread* appEventThread) { - mSfEventThread = sfEventThread; - mAppEventThread = appEventThread; - } - - void setTransactionStart(TransactionStart transactionStart) { - - if (transactionStart == TransactionStart::EARLY) { - mRemainingEarlyFrameCount = MIN_EARLY_FRAME_COUNT; - } - - // An early transaction stays an early transaction. - if (transactionStart == mTransactionStart || mTransactionStart == TransactionStart::EARLY) { - return; - } - mTransactionStart = transactionStart; - updateOffsets(); - } - - void onTransactionHandled() { - if (mTransactionStart == TransactionStart::NORMAL) return; - mTransactionStart = TransactionStart::NORMAL; - updateOffsets(); - } - - void onRefreshed(bool usedRenderEngine) { - bool updateOffsetsNeeded = false; - if (mRemainingEarlyFrameCount > 0) { - mRemainingEarlyFrameCount--; - updateOffsetsNeeded = true; - } - if (usedRenderEngine != mLastFrameUsedRenderEngine) { - mLastFrameUsedRenderEngine = usedRenderEngine; - updateOffsetsNeeded = true; - } - if (updateOffsetsNeeded) { - updateOffsets(); - } - } - -private: - - void updateOffsets() { - const Offsets desired = getOffsets(); - const Offsets current = mOffsets; - - bool changed = false; - if (desired.sf != current.sf) { - mSfEventThread->setPhaseOffset(desired.sf); - changed = true; - } - if (desired.app != current.app) { - mAppEventThread->setPhaseOffset(desired.app); - changed = true; - } - - if (changed) { - mOffsets = desired; - } - } - - Offsets getOffsets() { - if (mTransactionStart == TransactionStart::EARLY || mRemainingEarlyFrameCount > 0) { - return mEarlyOffsets; - } else if (mLastFrameUsedRenderEngine) { - return mEarlyGlOffsets; - } else { - return mLateOffsets; - } - } - - Offsets mLateOffsets; - Offsets mEarlyOffsets; - Offsets mEarlyGlOffsets; - - EventThread* mSfEventThread = nullptr; - EventThread* mAppEventThread = nullptr; - - std::atomic mOffsets; - - std::atomic mTransactionStart = TransactionStart::NORMAL; - std::atomic mLastFrameUsedRenderEngine = false; - std::atomic mRemainingEarlyFrameCount = 0; -}; - -} // namespace android diff --git a/services/surfaceflinger/tests/unittests/EventControlThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventControlThreadTest.cpp index b34645463f..9dc4193ecc 100644 --- a/services/surfaceflinger/tests/unittests/EventControlThreadTest.cpp +++ b/services/surfaceflinger/tests/unittests/EventControlThreadTest.cpp @@ -23,7 +23,7 @@ #include #include "AsyncCallRecorder.h" -#include "EventControlThread.h" +#include "Scheduler/EventControlThread.h" namespace android { namespace { diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp index 19747bd442..fb3b7a2951 100644 --- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp +++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp @@ -25,7 +25,7 @@ #include #include "AsyncCallRecorder.h" -#include "EventThread.h" +#include "Scheduler/EventThread.h" using namespace std::chrono_literals; using namespace std::placeholders; diff --git a/services/surfaceflinger/tests/unittests/mock/MockDispSync.h b/services/surfaceflinger/tests/unittests/mock/MockDispSync.h index cd8d943dff..06a6b690c3 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockDispSync.h +++ b/services/surfaceflinger/tests/unittests/mock/MockDispSync.h @@ -19,7 +19,7 @@ #include #include -#include "DispSync.h" +#include "Scheduler/DispSync.h" namespace android { namespace mock { diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.h index 8ac09a962d..6ef352a548 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.h +++ b/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.h @@ -18,7 +18,7 @@ #include -#include "EventControlThread.h" +#include "Scheduler/EventControlThread.h" namespace android { namespace mock { diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h index df9bfc6d96..ad2463dac2 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h +++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h @@ -18,7 +18,7 @@ #include -#include "EventThread.h" +#include "Scheduler/EventThread.h" namespace android { namespace mock { diff --git a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h index cf07cf7ba9..8d503f4070 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h +++ b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h @@ -18,7 +18,7 @@ #include -#include "MessageQueue.h" +#include "Scheduler/MessageQueue.h" namespace android { namespace mock { -- cgit v1.2.3-59-g8ed1b