diff options
-rw-r--r-- | libs/gui/DisplayEventDispatcher.cpp | 19 | ||||
-rw-r--r-- | libs/gui/DisplayEventReceiver.cpp | 8 | ||||
-rw-r--r-- | libs/gui/IDisplayEventConnection.cpp | 11 | ||||
-rw-r--r-- | libs/gui/include/gui/DisplayEventDispatcher.h | 4 | ||||
-rw-r--r-- | libs/gui/include/gui/DisplayEventReceiver.h | 6 | ||||
-rw-r--r-- | libs/gui/include/gui/IDisplayEventConnection.h | 9 | ||||
-rw-r--r-- | libs/nativedisplay/AChoreographer.cpp | 100 | ||||
-rw-r--r-- | libs/nativedisplay/include/apex/choreographer.h | 43 | ||||
-rw-r--r-- | libs/nativedisplay/libnativedisplay.map.txt | 2 | ||||
-rw-r--r-- | services/surfaceflinger/Scheduler/EventThread.cpp | 50 | ||||
-rw-r--r-- | services/surfaceflinger/Scheduler/EventThread.h | 22 | ||||
-rw-r--r-- | services/surfaceflinger/Scheduler/Scheduler.cpp | 4 | ||||
-rw-r--r-- | services/surfaceflinger/Scheduler/Scheduler.h | 3 | ||||
-rw-r--r-- | services/surfaceflinger/SurfaceFlinger.cpp | 25 | ||||
-rw-r--r-- | services/surfaceflinger/tests/unittests/EventThreadTest.cpp | 23 | ||||
-rw-r--r-- | services/surfaceflinger/tests/unittests/mock/MockEventThread.h | 3 |
16 files changed, 280 insertions, 52 deletions
diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp index 54f383e915..208d729812 100644 --- a/libs/gui/DisplayEventDispatcher.cpp +++ b/libs/gui/DisplayEventDispatcher.cpp @@ -36,7 +36,10 @@ static const size_t EVENT_BUFFER_SIZE = 100; DisplayEventDispatcher::DisplayEventDispatcher(const sp<Looper>& looper, ISurfaceComposer::VsyncSource vsyncSource, ISurfaceComposer::ConfigChanged configChanged) - : mLooper(looper), mReceiver(vsyncSource, configChanged), mWaitingForVsync(false) { + : mLooper(looper), + mReceiver(vsyncSource, configChanged), + mWaitingForVsync(false), + mConfigChangeFlag(configChanged) { ALOGV("dispatcher %p ~ Initializing display event dispatcher.", this); } @@ -86,6 +89,18 @@ status_t DisplayEventDispatcher::scheduleVsync() { return OK; } +void DisplayEventDispatcher::toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag) { + if (mConfigChangeFlag == configChangeFlag) { + return; + } + status_t status = mReceiver.toggleConfigEvents(configChangeFlag); + if (status) { + ALOGW("Failed enable config events, status=%d", status); + return; + } + mConfigChangeFlag = configChangeFlag; +} + int DisplayEventDispatcher::handleEvent(int, int events, void*) { if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) { ALOGE("Display event receiver pipe was closed or an error occurred. " @@ -140,7 +155,7 @@ bool DisplayEventDispatcher::processPendingEvents(nsecs_t* outTimestamp, break; case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED: dispatchConfigChanged(ev.header.timestamp, ev.header.displayId, - ev.config.configId); + ev.config.configId, ev.config.vsyncPeriod); break; default: ALOGW("dispatcher %p ~ ignoring unknown event type %#x", this, ev.header.type); diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp index b8faa2df4c..fd6aaf8b46 100644 --- a/libs/gui/DisplayEventReceiver.cpp +++ b/libs/gui/DisplayEventReceiver.cpp @@ -79,6 +79,14 @@ status_t DisplayEventReceiver::requestNextVsync() { return NO_INIT; } +status_t DisplayEventReceiver::toggleConfigEvents( + ISurfaceComposer::ConfigChanged configChangeFlag) { + if (mEventConnection != nullptr) { + mEventConnection->toggleConfigEvents(configChangeFlag); + return NO_ERROR; + } + return NO_INIT; +} ssize_t DisplayEventReceiver::getEvents(DisplayEventReceiver::Event* events, size_t count) { diff --git a/libs/gui/IDisplayEventConnection.cpp b/libs/gui/IDisplayEventConnection.cpp index c0e246fa15..dda5acf8a7 100644 --- a/libs/gui/IDisplayEventConnection.cpp +++ b/libs/gui/IDisplayEventConnection.cpp @@ -26,7 +26,8 @@ enum class Tag : uint32_t { STEAL_RECEIVE_CHANNEL = IBinder::FIRST_CALL_TRANSACTION, SET_VSYNC_RATE, REQUEST_NEXT_VSYNC, - LAST = REQUEST_NEXT_VSYNC, + TOGGLE_CONFIG_EVENTS, + LAST = TOGGLE_CONFIG_EVENTS, }; } // Anonymous namespace @@ -53,6 +54,12 @@ public: callRemoteAsync<decltype(&IDisplayEventConnection::requestNextVsync)>( Tag::REQUEST_NEXT_VSYNC); } + + void toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag) override { + callRemoteAsync<decltype( + &IDisplayEventConnection::toggleConfigEvents)>(Tag::TOGGLE_CONFIG_EVENTS, + configChangeFlag); + } }; // Out-of-line virtual method definition to trigger vtable emission in this translation unit (see @@ -74,6 +81,8 @@ status_t BnDisplayEventConnection::onTransact(uint32_t code, const Parcel& data, return callLocal(data, reply, &IDisplayEventConnection::setVsyncRate); case Tag::REQUEST_NEXT_VSYNC: return callLocalAsync(data, reply, &IDisplayEventConnection::requestNextVsync); + case Tag::TOGGLE_CONFIG_EVENTS: + return callLocalAsync(data, reply, &IDisplayEventConnection::toggleConfigEvents); } } diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h index f0b7ff5001..0b718010e4 100644 --- a/libs/gui/include/gui/DisplayEventDispatcher.h +++ b/libs/gui/include/gui/DisplayEventDispatcher.h @@ -31,6 +31,7 @@ public: status_t initialize(); void dispose(); status_t scheduleVsync(); + void toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag); protected: virtual ~DisplayEventDispatcher() = default; @@ -39,12 +40,13 @@ private: sp<Looper> mLooper; DisplayEventReceiver mReceiver; bool mWaitingForVsync; + ISurfaceComposer::ConfigChanged mConfigChangeFlag; virtual void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) = 0; virtual void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) = 0; virtual void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId, - int32_t configId) = 0; + int32_t configId, nsecs_t vsyncPeriod) = 0; virtual int handleEvent(int receiveFd, int events, void* data); bool processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId, diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h index a558cf9e18..109e28b9dc 100644 --- a/libs/gui/include/gui/DisplayEventReceiver.h +++ b/libs/gui/include/gui/DisplayEventReceiver.h @@ -73,6 +73,7 @@ public: struct Config { int32_t configId; + nsecs_t vsyncPeriod; }; Header header; @@ -144,6 +145,11 @@ public: */ status_t requestNextVsync(); + /* + * toggleConfigEvents() toggles delivery of config change events. + */ + status_t toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag); + private: sp<IDisplayEventConnection> mEventConnection; std::unique_ptr<gui::BitTube> mDataChannel; diff --git a/libs/gui/include/gui/IDisplayEventConnection.h b/libs/gui/include/gui/IDisplayEventConnection.h index d783f74d7c..8b35ef6486 100644 --- a/libs/gui/include/gui/IDisplayEventConnection.h +++ b/libs/gui/include/gui/IDisplayEventConnection.h @@ -18,7 +18,7 @@ #include <binder/IInterface.h> #include <binder/SafeInterface.h> - +#include <gui/ISurfaceComposer.h> #include <utils/Errors.h> #include <cstdint> @@ -51,6 +51,13 @@ public: * requestNextVsync() schedules the next vsync event. It has no effect if the vsync rate is > 0. */ virtual void requestNextVsync() = 0; // Asynchronous + + /* + * togglesConfigEvents() configures whether or not display config changes + * should be propagated. + */ + virtual void toggleConfigEvents( + ISurfaceComposer::ConfigChanged configChangeFlag) = 0; // Asynchronous }; class BnDisplayEventConnection : public SafeBnInterface<IDisplayEventConnection> { diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp index 05ff93e5c1..7e71ede419 100644 --- a/libs/nativedisplay/AChoreographer.cpp +++ b/libs/nativedisplay/AChoreographer.cpp @@ -17,11 +17,7 @@ #define LOG_TAG "Choreographer" //#define LOG_NDEBUG 0 -#include <cinttypes> -#include <queue> -#include <thread> - -#include <android/choreographer.h> +#include <apex/choreographer.h> #include <gui/DisplayEventDispatcher.h> #include <gui/ISurfaceComposer.h> #include <gui/SurfaceComposerClient.h> @@ -29,6 +25,11 @@ #include <utils/Mutex.h> #include <utils/Timers.h> +#include <cinttypes> +#include <optional> +#include <queue> +#include <thread> + namespace android { static inline const char* toString(bool value) { @@ -48,11 +49,17 @@ struct FrameCallback { } }; +struct RefreshRateCallback { + AChoreographer_refreshRateCallback callback; + void* data; +}; class Choreographer : public DisplayEventDispatcher, public MessageHandler { public: void postFrameCallbackDelayed(AChoreographer_frameCallback cb, AChoreographer_frameCallback64 cb64, void* data, nsecs_t delay); + void registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data); + void unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb); enum { MSG_SCHEDULE_CALLBACKS = 0, @@ -71,18 +78,23 @@ private: void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) override; void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override; - void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId, - int32_t configId) override; + void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t configId, + nsecs_t vsyncPeriod) override; void scheduleCallbacks(); // Protected by mLock - std::priority_queue<FrameCallback> mCallbacks; + std::priority_queue<FrameCallback> mFrameCallbacks; + + // Protected by mLock + std::vector<RefreshRateCallback> mRefreshRateCallbacks; + nsecs_t mVsyncPeriod = 0; mutable Mutex mLock; const sp<Looper> mLooper; const std::thread::id mThreadId; + const std::optional<PhysicalDisplayId> mInternalDisplayId; }; @@ -104,9 +116,11 @@ Choreographer* Choreographer::getForThread() { return gChoreographer; } -Choreographer::Choreographer(const sp<Looper>& looper) : - DisplayEventDispatcher(looper), mLooper(looper), mThreadId(std::this_thread::get_id()) { -} +Choreographer::Choreographer(const sp<Looper>& looper) + : DisplayEventDispatcher(looper), + mLooper(looper), + mThreadId(std::this_thread::get_id()), + mInternalDisplayId(SurfaceComposerClient::getInternalDisplayId()) {} void Choreographer::postFrameCallbackDelayed( AChoreographer_frameCallback cb, AChoreographer_frameCallback64 cb64, void* data, nsecs_t delay) { @@ -114,7 +128,7 @@ void Choreographer::postFrameCallbackDelayed( FrameCallback callback{cb, cb64, data, now + delay}; { AutoMutex _l{mLock}; - mCallbacks.push(callback); + mFrameCallbacks.push(callback); } if (callback.dueTime <= now) { if (std::this_thread::get_id() != mThreadId) { @@ -129,10 +143,31 @@ void Choreographer::postFrameCallbackDelayed( } } +void Choreographer::registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data) { + { + AutoMutex _l{mLock}; + mRefreshRateCallbacks.emplace_back(RefreshRateCallback{cb, data}); + toggleConfigEvents(ISurfaceComposer::ConfigChanged::eConfigChangedDispatch); + } +} + +void Choreographer::unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb) { + { + AutoMutex _l{mLock}; + std::remove_if(mRefreshRateCallbacks.begin(), mRefreshRateCallbacks.end(), + [&](const RefreshRateCallback& callback) { + return cb == callback.callback; + }); + if (mRefreshRateCallbacks.empty()) { + toggleConfigEvents(ISurfaceComposer::ConfigChanged::eConfigChangedSuppress); + } + } +} + void Choreographer::scheduleCallbacks() { AutoMutex _{mLock}; nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - if (mCallbacks.top().dueTime <= now) { + if (mFrameCallbacks.top().dueTime <= now) { ALOGV("choreographer %p ~ scheduling vsync", this); scheduleVsync(); return; @@ -147,9 +182,9 @@ void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t { AutoMutex _l{mLock}; nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - while (!mCallbacks.empty() && mCallbacks.top().dueTime < now) { - callbacks.push_back(mCallbacks.top()); - mCallbacks.pop(); + while (!mFrameCallbacks.empty() && mFrameCallbacks.top().dueTime < now) { + callbacks.push_back(mFrameCallbacks.top()); + mFrameCallbacks.pop(); } } for (const auto& cb : callbacks) { @@ -167,11 +202,25 @@ void Choreographer::dispatchHotplug(nsecs_t, PhysicalDisplayId displayId, bool c this, displayId, toString(connected)); } -void Choreographer::dispatchConfigChanged(nsecs_t, PhysicalDisplayId displayId, - int32_t configId) { - ALOGV("choreographer %p ~ received config changed event (displayId=%" - ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", configId=%s), ignoring.", - this, displayId, toString(configId)); +// TODO(b/74619554): The PhysicalDisplayId is ignored because currently +// Choreographer only supports dispatching VSYNC events for the internal +// display, so as such Choreographer does not support the notion of multiple +// displays. When multi-display choreographer is properly supported, then +// PhysicalDisplayId should no longer be ignored. +void Choreographer::dispatchConfigChanged(nsecs_t, PhysicalDisplayId, int32_t, + nsecs_t vsyncPeriod) { + { + AutoMutex _l{mLock}; + for (const auto& cb : mRefreshRateCallbacks) { + // Only perform the callback when the old refresh rate is different + // from the new refresh rate, so that we don't dispatch the callback + // on every single configuration change. + if (mVsyncPeriod != vsyncPeriod) { + cb.callback(vsyncPeriod, cb.data); + mVsyncPeriod = vsyncPeriod; + } + } + } } void Choreographer::handleMessage(const Message& message) { @@ -223,3 +272,12 @@ void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer, AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed( nullptr, callback, data, ms2ns(delayMillis)); } +void AChoreographer_registerRefreshRateCallback(AChoreographer* choreographer, + AChoreographer_refreshRateCallback callback, + void* data) { + AChoreographer_to_Choreographer(choreographer)->registerRefreshRateCallback(callback, data); +} +void AChoreographer_unregisterRefreshRateCallback(AChoreographer* choreographer, + AChoreographer_refreshRateCallback callback) { + AChoreographer_to_Choreographer(choreographer)->unregisterRefreshRateCallback(callback); +} diff --git a/libs/nativedisplay/include/apex/choreographer.h b/libs/nativedisplay/include/apex/choreographer.h new file mode 100644 index 0000000000..352213edc1 --- /dev/null +++ b/libs/nativedisplay/include/apex/choreographer.h @@ -0,0 +1,43 @@ +/* + * Copyright 2019 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 <android/choreographer.h> +#include <inttypes.h> + +__BEGIN_DECLS + +/** + * Prototype of the function that is called when the display refresh rate + * changes. It's passed the new vsync period in nanoseconds, as well as the data + * pointer provided by the application that registered a callback. + */ +typedef void (*AChoreographer_refreshRateCallback)(int64_t vsyncPeriodNanos, void* data); + +/** + * Registers a callback to be run when the display refresh rate changes. + */ +void AChoreographer_registerRefreshRateCallback(AChoreographer* choreographer, + AChoreographer_refreshRateCallback, void* data); + +/** + * Unregisters a callback to be run when the display refresh rate changes. + */ +void AChoreographer_unregisterRefreshRateCallback(AChoreographer* choreographer, + AChoreographer_refreshRateCallback); + +__END_DECLS diff --git a/libs/nativedisplay/libnativedisplay.map.txt b/libs/nativedisplay/libnativedisplay.map.txt index 3b29c18fc3..3b6a24171b 100644 --- a/libs/nativedisplay/libnativedisplay.map.txt +++ b/libs/nativedisplay/libnativedisplay.map.txt @@ -5,6 +5,8 @@ LIBNATIVEDISPLAY { AChoreographer_postFrameCallbackDelayed; # apex # introduced=30 AChoreographer_postFrameCallback64; # apex # introduced=30 AChoreographer_postFrameCallbackDelayed64; # apex # introduced=30 + AChoreographer_registerRefreshRateCallback; # apex # introduced=30 + AChoreographer_unregisterRefreshRateCallback; # apex # introduced=30 local: *; }; diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp index ff800c3dbf..5bdef5807f 100644 --- a/services/surfaceflinger/Scheduler/EventThread.cpp +++ b/services/surfaceflinger/Scheduler/EventThread.cpp @@ -103,10 +103,11 @@ DisplayEventReceiver::Event makeVSync(PhysicalDisplayId displayId, nsecs_t times } DisplayEventReceiver::Event makeConfigChanged(PhysicalDisplayId displayId, - HwcConfigIndexType configId) { + HwcConfigIndexType configId, nsecs_t vsyncPeriod) { DisplayEventReceiver::Event event; event.header = {DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED, displayId, systemTime()}; event.config.configId = configId.value(); + event.config.vsyncPeriod = vsyncPeriod; return event; } @@ -116,7 +117,7 @@ EventThreadConnection::EventThreadConnection(EventThread* eventThread, ResyncCallback resyncCallback, ISurfaceComposer::ConfigChanged configChanged) : resyncCallback(std::move(resyncCallback)), - configChanged(configChanged), + mConfigChanged(configChanged), mEventThread(eventThread), mChannel(gui::BitTube::DefaultSize) {} @@ -145,6 +146,18 @@ void EventThreadConnection::requestNextVsync() { mEventThread->requestNextVsync(this); } +void EventThreadConnection::toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag) { + ATRACE_NAME("enableConfigEvents"); + mConfigChanged = configChangeFlag; + + // In principle it's possible for rapidly toggling config events to drop an + // event here, but it's unlikely in practice. + if (configChangeFlag == ISurfaceComposer::eConfigChangedDispatch) { + mForcedConfigChangeDispatch = true; + mEventThread->requestLatestConfig(); + } +} + status_t EventThreadConnection::postEvent(const DisplayEventReceiver::Event& event) { ssize_t size = DisplayEventReceiver::sendEvents(&mChannel, &event, 1); return size < 0 ? status_t(size) : status_t(NO_ERROR); @@ -257,6 +270,24 @@ void EventThread::requestNextVsync(const sp<EventThreadConnection>& connection) } } +void EventThread::requestLatestConfig() { + std::lock_guard<std::mutex> lock(mMutex); + auto pendingConfigChange = + std::find_if(std::begin(mPendingEvents), std::end(mPendingEvents), + [&](const DisplayEventReceiver::Event& event) { + return event.header.type == + DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED; + }); + + // If we didn't find a pending config change event, then push out the + // latest one we've ever seen. + if (pendingConfigChange == std::end(mPendingEvents)) { + mPendingEvents.push_back(mLastConfigChangeEvent); + } + + mCondition.notify_all(); +} + void EventThread::onScreenReleased() { std::lock_guard<std::mutex> lock(mMutex); if (!mVSyncState || mVSyncState->synthetic) { @@ -292,10 +323,11 @@ void EventThread::onHotplugReceived(PhysicalDisplayId displayId, bool connected) mCondition.notify_all(); } -void EventThread::onConfigChanged(PhysicalDisplayId displayId, HwcConfigIndexType configId) { +void EventThread::onConfigChanged(PhysicalDisplayId displayId, HwcConfigIndexType configId, + nsecs_t vsyncPeriod) { std::lock_guard<std::mutex> lock(mMutex); - mPendingEvents.push_back(makeConfigChanged(displayId, configId)); + mPendingEvents.push_back(makeConfigChanged(displayId, configId, vsyncPeriod)); mCondition.notify_all(); } @@ -325,6 +357,9 @@ void EventThread::threadMain(std::unique_lock<std::mutex>& lock) { mInterceptVSyncsCallback(event->header.timestamp); } break; + case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED: + mLastConfigChangeEvent = *event; + break; } } @@ -398,8 +433,11 @@ bool EventThread::shouldConsumeEvent(const DisplayEventReceiver::Event& event, case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG: return true; - case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED: - return connection->configChanged == ISurfaceComposer::eConfigChangedDispatch; + case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED: { + const bool forcedDispatch = connection->mForcedConfigChangeDispatch.exchange(false); + return forcedDispatch || + connection->mConfigChanged == ISurfaceComposer::eConfigChangedDispatch; + } case DisplayEventReceiver::DISPLAY_EVENT_VSYNC: switch (connection->vsyncRequest) { diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h index a42546c7df..641b2a5782 100644 --- a/services/surfaceflinger/Scheduler/EventThread.h +++ b/services/surfaceflinger/Scheduler/EventThread.h @@ -81,12 +81,19 @@ public: status_t stealReceiveChannel(gui::BitTube* outChannel) override; status_t setVsyncRate(uint32_t rate) override; void requestNextVsync() override; // asynchronous + void toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag) override; // Called in response to requestNextVsync. const ResyncCallback resyncCallback; VSyncRequest vsyncRequest = VSyncRequest::None; - const ISurfaceComposer::ConfigChanged configChanged; + std::atomic<ISurfaceComposer::ConfigChanged> mConfigChanged = + ISurfaceComposer::ConfigChanged::eConfigChangedSuppress; + // Store whether we need to force dispatching a config change separately - + // if mConfigChanged ever changes before the config change is dispatched + // then we still need to propagate an initial config to the app if we + // haven't already. + std::atomic<bool> mForcedConfigChangeDispatch = false; private: virtual void onFirstRef(); @@ -110,7 +117,8 @@ public: virtual void onHotplugReceived(PhysicalDisplayId displayId, bool connected) = 0; // called when SF changes the active config and apps needs to be notified about the change - virtual void onConfigChanged(PhysicalDisplayId displayId, HwcConfigIndexType configId) = 0; + virtual void onConfigChanged(PhysicalDisplayId displayId, HwcConfigIndexType configId, + nsecs_t vsyncPeriod) = 0; virtual void dump(std::string& result) const = 0; @@ -121,6 +129,11 @@ public: virtual void setVsyncRate(uint32_t rate, const sp<EventThreadConnection>& connection) = 0; // Requests the next vsync. If resetIdleTimer is set to true, it resets the idle timer. virtual void requestNextVsync(const sp<EventThreadConnection>& connection) = 0; + + // Dispatches the most recent configuration + // Usage of this method assumes that only the primary internal display + // supports multiple display configurations. + virtual void requestLatestConfig() = 0; }; namespace impl { @@ -138,6 +151,7 @@ public: status_t registerDisplayEventConnection(const sp<EventThreadConnection>& connection) override; void setVsyncRate(uint32_t rate, const sp<EventThreadConnection>& connection) override; void requestNextVsync(const sp<EventThreadConnection>& connection) override; + void requestLatestConfig() override; // called before the screen is turned off from main thread void onScreenReleased() override; @@ -147,7 +161,8 @@ public: void onHotplugReceived(PhysicalDisplayId displayId, bool connected) override; - void onConfigChanged(PhysicalDisplayId displayId, HwcConfigIndexType configId) override; + void onConfigChanged(PhysicalDisplayId displayId, HwcConfigIndexType configId, + nsecs_t vsyncPeriod) override; void dump(std::string& result) const override; @@ -182,6 +197,7 @@ private: std::vector<wp<EventThreadConnection>> mDisplayEventConnections GUARDED_BY(mMutex); std::deque<DisplayEventReceiver::Event> mPendingEvents GUARDED_BY(mMutex); + DisplayEventReceiver::Event mLastConfigChangeEvent GUARDED_BY(mMutex); // VSYNC state of connected display. struct VSyncState { diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index 71a6a2f066..8fe7fcb6fc 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -186,9 +186,9 @@ void Scheduler::onScreenReleased(ConnectionHandle handle) { } void Scheduler::onConfigChanged(ConnectionHandle handle, PhysicalDisplayId displayId, - HwcConfigIndexType configId) { + HwcConfigIndexType configId, nsecs_t vsyncPeriod) { RETURN_IF_INVALID_HANDLE(handle); - mConnections[handle].thread->onConfigChanged(displayId, configId); + mConnections[handle].thread->onConfigChanged(displayId, configId, vsyncPeriod); } void Scheduler::dump(ConnectionHandle handle, std::string& result) const { diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index 15277ceb56..d1d57159cb 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -75,7 +75,8 @@ public: sp<EventThreadConnection> getEventConnection(ConnectionHandle); void onHotplugReceived(ConnectionHandle, PhysicalDisplayId, bool connected); - void onConfigChanged(ConnectionHandle, PhysicalDisplayId, HwcConfigIndexType configId); + void onConfigChanged(ConnectionHandle, PhysicalDisplayId, HwcConfigIndexType configId, + nsecs_t vsyncPeriod); void onScreenAcquired(ConnectionHandle); void onScreenReleased(ConnectionHandle); diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index fed2395fc6..6e827ac4bb 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -956,8 +956,11 @@ void SurfaceFlinger::setActiveConfigInternal() { ATRACE_INT("ActiveConfigFPS", refreshRate.fps); if (mUpcomingActiveConfig.event != Scheduler::ConfigEvent::None) { + const nsecs_t vsyncPeriod = + mRefreshRateConfigs->getRefreshRateFromConfigId(mUpcomingActiveConfig.configId) + .vsyncPeriod; mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value, - mUpcomingActiveConfig.configId); + mUpcomingActiveConfig.configId, vsyncPeriod); } } @@ -2614,6 +2617,17 @@ void SurfaceFlinger::initScheduler(DisplayId primaryDisplayId) { mRegionSamplingThread = new RegionSamplingThread(*this, *mScheduler, RegionSamplingThread::EnvironmentTimingTunables()); + // Dispatch a config change request for the primary display on scheduler + // initialization, so that the EventThreads always contain a reference to a + // prior configuration. + // + // This is a bit hacky, but this avoids a back-pointer into the main SF + // classes from EventThread, and there should be no run-time binder cost + // anyway since there are no connected apps at this point. + const nsecs_t vsyncPeriod = + mRefreshRateConfigs->getRefreshRateFromConfigId(currentConfig).vsyncPeriod; + mScheduler->onConfigChanged(mAppConnectionHandle, primaryDisplayId.value, currentConfig, + vsyncPeriod); } void SurfaceFlinger::commitTransaction() @@ -5499,7 +5513,10 @@ status_t SurfaceFlinger::setDesiredDisplayConfigSpecsInternal(const sp<DisplayDe auto configId = HwcConfigIndexType(defaultConfig); display->setActiveConfig(configId); - mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value, configId); + const nsecs_t vsyncPeriod = + mRefreshRateConfigs->getRefreshRateFromConfigId(configId).vsyncPeriod; + mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value, configId, + vsyncPeriod); return NO_ERROR; } @@ -5523,8 +5540,10 @@ status_t SurfaceFlinger::setDesiredDisplayConfigSpecsInternal(const sp<DisplayDe // TODO(b/140204874): This hack triggers a notification that something has changed, so // that listeners that care about a change in allowed configs can get the notification. // Giving current ActiveConfig so that most other listeners would just drop the event + const nsecs_t vsyncPeriod = + mRefreshRateConfigs->getRefreshRateFromConfigId(display->getActiveConfig()).vsyncPeriod; mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value, - display->getActiveConfig()); + display->getActiveConfig(), vsyncPeriod); if (mRefreshRateConfigs->refreshRateSwitchingSupported()) { auto configId = mScheduler->getPreferredConfigId(); diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp index 80bca021ab..61d4f47c21 100644 --- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp +++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp @@ -85,7 +85,8 @@ protected: void expectHotplugEventReceivedByConnection(PhysicalDisplayId expectedDisplayId, bool expectedConnected); void expectConfigChangedEventReceivedByConnection(PhysicalDisplayId expectedDisplayId, - int32_t expectedConfigId); + int32_t expectedConfigId, + nsecs_t expectedVsyncPeriod); AsyncCallRecorder<void (*)(bool)> mVSyncSetEnabledCallRecorder; AsyncCallRecorder<void (*)(VSyncSource::Callback*)> mVSyncSetCallbackCallRecorder; @@ -209,13 +210,15 @@ void EventThreadTest::expectHotplugEventReceivedByConnection(PhysicalDisplayId e } void EventThreadTest::expectConfigChangedEventReceivedByConnection( - PhysicalDisplayId expectedDisplayId, int32_t expectedConfigId) { + PhysicalDisplayId expectedDisplayId, int32_t expectedConfigId, + nsecs_t expectedVsyncPeriod) { auto args = mConnectionEventCallRecorder.waitForCall(); ASSERT_TRUE(args.has_value()); const auto& event = std::get<0>(args.value()); EXPECT_EQ(DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED, event.header.type); EXPECT_EQ(expectedDisplayId, event.header.displayId); EXPECT_EQ(expectedConfigId, event.config.configId); + EXPECT_EQ(expectedVsyncPeriod, event.config.vsyncPeriod); } namespace { @@ -450,18 +453,18 @@ TEST_F(EventThreadTest, postHotplugExternalConnect) { } TEST_F(EventThreadTest, postConfigChangedPrimary) { - mThread->onConfigChanged(INTERNAL_DISPLAY_ID, HwcConfigIndexType(7)); - expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 7); + mThread->onConfigChanged(INTERNAL_DISPLAY_ID, HwcConfigIndexType(7), 16666666); + expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 7, 16666666); } TEST_F(EventThreadTest, postConfigChangedExternal) { - mThread->onConfigChanged(EXTERNAL_DISPLAY_ID, HwcConfigIndexType(5)); - expectConfigChangedEventReceivedByConnection(EXTERNAL_DISPLAY_ID, 5); + mThread->onConfigChanged(EXTERNAL_DISPLAY_ID, HwcConfigIndexType(5), 16666666); + expectConfigChangedEventReceivedByConnection(EXTERNAL_DISPLAY_ID, 5, 16666666); } TEST_F(EventThreadTest, postConfigChangedPrimary64bit) { - mThread->onConfigChanged(DISPLAY_ID_64BIT, HwcConfigIndexType(7)); - expectConfigChangedEventReceivedByConnection(DISPLAY_ID_64BIT, 7); + mThread->onConfigChanged(DISPLAY_ID_64BIT, HwcConfigIndexType(7), 16666666); + expectConfigChangedEventReceivedByConnection(DISPLAY_ID_64BIT, 7, 16666666); } TEST_F(EventThreadTest, suppressConfigChanged) { @@ -470,8 +473,8 @@ TEST_F(EventThreadTest, suppressConfigChanged) { createConnection(suppressConnectionEventRecorder, ISurfaceComposer::eConfigChangedSuppress); - mThread->onConfigChanged(INTERNAL_DISPLAY_ID, HwcConfigIndexType(9)); - expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 9); + mThread->onConfigChanged(INTERNAL_DISPLAY_ID, HwcConfigIndexType(9), 16666666); + expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 9, 16666666); auto args = suppressConnectionEventRecorder.waitForCall(); ASSERT_FALSE(args.has_value()); diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h index f7c380490b..9bda095a48 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h +++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h @@ -33,13 +33,14 @@ public: MOCK_METHOD0(onScreenReleased, void()); MOCK_METHOD0(onScreenAcquired, void()); MOCK_METHOD2(onHotplugReceived, void(PhysicalDisplayId, bool)); - MOCK_METHOD2(onConfigChanged, void(PhysicalDisplayId, HwcConfigIndexType)); + MOCK_METHOD3(onConfigChanged, void(PhysicalDisplayId, HwcConfigIndexType, nsecs_t)); MOCK_CONST_METHOD1(dump, void(std::string&)); MOCK_METHOD1(setPhaseOffset, void(nsecs_t phaseOffset)); MOCK_METHOD1(registerDisplayEventConnection, status_t(const sp<android::EventThreadConnection> &)); MOCK_METHOD2(setVsyncRate, void(uint32_t, const sp<android::EventThreadConnection> &)); MOCK_METHOD1(requestNextVsync, void(const sp<android::EventThreadConnection> &)); + MOCK_METHOD0(requestLatestConfig, void()); MOCK_METHOD1(pauseVsyncCallback, void(bool)); }; |