diff options
author | 2019-10-28 16:18:59 -0700 | |
---|---|---|
committer | 2019-12-26 10:28:47 -0800 | |
commit | 60aee1c46b52852e140f05748f69e38f4e3de36e (patch) | |
tree | 7a29657039f1f8911fdef5689aa39e7fd4f3f6bd | |
parent | 516de508aa52a0907a496890dc191501481bca7b (diff) |
[AChoreographer] Add refresh rate callback.
This will augment the NDK to respond to display events where the display
refresh rate changes. Consumers of this api will include:
* HWUI, for implementing a policy for determining whether to use
render-ahead,
* Swappy, to potentially replace jumping into Java from native code to
respond to display evnets there.
* Any other native app that would rely on the up-to-date display refresh
rate.
Currently however this is not yet exposed to NDK as CTS is not yet
written. Once CTS is written then this will be formally exposed to NDK.
For now we'll leave these as APEX apis to represent incremental
progress.
Bug: 136262896
Test: builds
Change-Id: I66d393f93eb5d681547411e330ef1b8950a35c5d
-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)); }; |