From 60aee1c46b52852e140f05748f69e38f4e3de36e Mon Sep 17 00:00:00 2001 From: Alec Mouri Date: Mon, 28 Oct 2019 16:18:59 -0700 Subject: [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 --- libs/gui/DisplayEventReceiver.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'libs/gui/DisplayEventReceiver.cpp') 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) { -- cgit v1.2.3-59-g8ed1b From 271de040ffc1371251a9741d2f347642a5de0995 Mon Sep 17 00:00:00 2001 From: Alec Mouri Date: Mon, 27 Apr 2020 22:38:19 -0700 Subject: Receive refresh rate callbacks from DMS AChoreographer will use DMS as the source of truth for these callbacks instead of SurfaceFlinger. Bug: 154874011 Test: ChoreographerNativeTest Tes: Manually verify that HWUI is processing refresh rate callbacks Change-Id: I961a7d1ab335800d3e260ba7564ddca9c0595cfc --- include/android/choreographer.h | 156 ++++++++++ libs/gui/DisplayEventDispatcher.cpp | 13 +- libs/gui/DisplayEventReceiver.cpp | 5 +- libs/gui/IDisplayEventConnection.cpp | 15 +- libs/gui/include/gui/DisplayEventDispatcher.h | 3 +- libs/gui/include/gui/DisplayEventReceiver.h | 5 +- libs/gui/include/gui/IDisplayEventConnection.h | 6 +- libs/nativedisplay/AChoreographer.cpp | 324 +++++++++++++++++---- libs/nativedisplay/Android.bp | 17 +- .../private/android/choreographer.h | 55 ++++ libs/nativedisplay/include/android/choreographer.h | 152 ---------- .../surfacetexture/surface_texture_platform.h | 16 +- libs/nativedisplay/libnativedisplay.map.txt | 17 ++ .../surfacetexture/surface_texture.cpp | 31 ++ services/surfaceflinger/Scheduler/EventThread.cpp | 27 +- services/surfaceflinger/Scheduler/EventThread.h | 11 +- .../tests/unittests/mock/MockEventThread.h | 2 +- 17 files changed, 585 insertions(+), 270 deletions(-) create mode 100644 include/android/choreographer.h create mode 100644 libs/nativedisplay/include-private/private/android/choreographer.h delete mode 100644 libs/nativedisplay/include/android/choreographer.h (limited to 'libs/gui/DisplayEventReceiver.cpp') diff --git a/include/android/choreographer.h b/include/android/choreographer.h new file mode 100644 index 0000000000..c1c4a72cd3 --- /dev/null +++ b/include/android/choreographer.h @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2015 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. + */ + +/** + * @addtogroup Choreographer + * @{ + */ + +/** + * @file choreographer.h + */ + +#ifndef ANDROID_CHOREOGRAPHER_H +#define ANDROID_CHOREOGRAPHER_H + +#include +#include + +__BEGIN_DECLS + +struct AChoreographer; +typedef struct AChoreographer AChoreographer; + +/** + * Prototype of the function that is called when a new frame is being rendered. + * It's passed the time that the frame is being rendered as nanoseconds in the + * CLOCK_MONOTONIC time base, as well as the data pointer provided by the + * application that registered a callback. All callbacks that run as part of + * rendering a frame will observe the same frame time, so it should be used + * whenever events need to be synchronized (e.g. animations). + */ +typedef void (*AChoreographer_frameCallback)(long frameTimeNanos, void* data); + +/** + * Prototype of the function that is called when a new frame is being rendered. + * It's passed the time that the frame is being rendered as nanoseconds in the + * CLOCK_MONOTONIC time base, as well as the data pointer provided by the + * application that registered a callback. All callbacks that run as part of + * rendering a frame will observe the same frame time, so it should be used + * whenever events need to be synchronized (e.g. animations). + */ +typedef void (*AChoreographer_frameCallback64)(int64_t frameTimeNanos, void* data); + +/** + * 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); + +#if __ANDROID_API__ >= 24 + +/** + * Get the AChoreographer instance for the current thread. This must be called + * on an ALooper thread. + * + * Available since API level 24. + */ +AChoreographer* AChoreographer_getInstance() __INTRODUCED_IN(24); + +/** + * Deprecated: Use AChoreographer_postFrameCallback64 instead. + */ +void AChoreographer_postFrameCallback(AChoreographer* choreographer, + AChoreographer_frameCallback callback, void* data) + __INTRODUCED_IN(24) __DEPRECATED_IN(29); + +/** + * Deprecated: Use AChoreographer_postFrameCallbackDelayed64 instead. + */ +void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer, + AChoreographer_frameCallback callback, void* data, + long delayMillis) __INTRODUCED_IN(24) + __DEPRECATED_IN(29); + +#endif /* __ANDROID_API__ >= 24 */ + +#if __ANDROID_API__ >= 29 + +/** + * Power a callback to be run on the next frame. The data pointer provided will + * be passed to the callback function when it's called. + * + * Available since API level 29. + */ +void AChoreographer_postFrameCallback64(AChoreographer* choreographer, + AChoreographer_frameCallback64 callback, void* data) + __INTRODUCED_IN(29); + +/** + * Post a callback to be run on the frame following the specified delay. The + * data pointer provided will be passed to the callback function when it's + * called. + * + * Available since API level 29. + */ +void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer, + AChoreographer_frameCallback64 callback, void* data, + uint32_t delayMillis) __INTRODUCED_IN(29); + +#endif /* __ANDROID_API__ >= 29 */ + +#if __ANDROID_API__ >= 30 + +/** + * Registers a callback to be run when the display refresh rate changes. The + * data pointer provided will be passed to the callback function when it's + * called. The same callback may be registered multiple times, provided that a + * different data pointer is provided each time. + * + * If an application registers a callback for this choreographer instance when + * no new callbacks were previously registered, that callback is guaranteed to + * be dispatched. However, if the callback and associated data pointer are + * unregistered prior to running the callback, then the callback may be silently + * dropped. + * + * This api is thread-safe. Any thread is allowed to register a new refresh + * rate callback for the choreographer instance. + */ +void AChoreographer_registerRefreshRateCallback(AChoreographer* choreographer, + AChoreographer_refreshRateCallback, void* data); + +/** + * Unregisters a callback to be run when the display refresh rate changes, along + * with the data pointer previously provided when registering the callback. The + * callback is only unregistered when the data pointer matches one that was + * previously registered. + * + * This api is thread-safe. Any thread is allowed to unregister an existing + * refresh rate callback for the choreographer instance. When a refresh rate + * callback and associated data pointer are unregistered, then there is a + * guarantee that when the unregistration completes that that callback will not + * be run with the data pointer passed. + */ +void AChoreographer_unregisterRefreshRateCallback(AChoreographer* choreographer, + AChoreographer_refreshRateCallback, void* data); +#endif /* __ANDROID_API__ >= 30 */ + +__END_DECLS + +#endif // ANDROID_CHOREOGRAPHER_H + +/** @} */ diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp index 15f966d062..b33bc9e556 100644 --- a/libs/gui/DisplayEventDispatcher.cpp +++ b/libs/gui/DisplayEventDispatcher.cpp @@ -36,10 +36,7 @@ static const size_t EVENT_BUFFER_SIZE = 100; DisplayEventDispatcher::DisplayEventDispatcher(const sp& looper, ISurfaceComposer::VsyncSource vsyncSource, ISurfaceComposer::ConfigChanged configChanged) - : mLooper(looper), - mReceiver(vsyncSource, configChanged), - mWaitingForVsync(false), - mConfigChangeFlag(configChanged) { + : mLooper(looper), mReceiver(vsyncSource, configChanged), mWaitingForVsync(false) { ALOGV("dispatcher %p ~ Initializing display event dispatcher.", this); } @@ -92,16 +89,12 @@ status_t DisplayEventDispatcher::scheduleVsync() { return OK; } -void DisplayEventDispatcher::toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag) { - if (mConfigChangeFlag == configChangeFlag) { - return; - } - status_t status = mReceiver.toggleConfigEvents(configChangeFlag); +void DisplayEventDispatcher::requestLatestConfig() { + status_t status = mReceiver.requestLatestConfig(); if (status) { ALOGW("Failed enable config events, status=%d", status); return; } - mConfigChangeFlag = configChangeFlag; } int DisplayEventDispatcher::getFd() const { diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp index fd6aaf8b46..1fed509003 100644 --- a/libs/gui/DisplayEventReceiver.cpp +++ b/libs/gui/DisplayEventReceiver.cpp @@ -79,10 +79,9 @@ status_t DisplayEventReceiver::requestNextVsync() { return NO_INIT; } -status_t DisplayEventReceiver::toggleConfigEvents( - ISurfaceComposer::ConfigChanged configChangeFlag) { +status_t DisplayEventReceiver::requestLatestConfig() { if (mEventConnection != nullptr) { - mEventConnection->toggleConfigEvents(configChangeFlag); + mEventConnection->requestLatestConfig(); return NO_ERROR; } return NO_INIT; diff --git a/libs/gui/IDisplayEventConnection.cpp b/libs/gui/IDisplayEventConnection.cpp index dda5acf8a7..aa74bfd3f8 100644 --- a/libs/gui/IDisplayEventConnection.cpp +++ b/libs/gui/IDisplayEventConnection.cpp @@ -26,8 +26,8 @@ enum class Tag : uint32_t { STEAL_RECEIVE_CHANNEL = IBinder::FIRST_CALL_TRANSACTION, SET_VSYNC_RATE, REQUEST_NEXT_VSYNC, - TOGGLE_CONFIG_EVENTS, - LAST = TOGGLE_CONFIG_EVENTS, + REQUEST_LATEST_CONFIG, + LAST = REQUEST_LATEST_CONFIG, }; } // Anonymous namespace @@ -55,10 +55,9 @@ public: Tag::REQUEST_NEXT_VSYNC); } - void toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag) override { - callRemoteAsync(Tag::TOGGLE_CONFIG_EVENTS, - configChangeFlag); + void requestLatestConfig() override { + callRemoteAsync( + Tag::REQUEST_LATEST_CONFIG); } }; @@ -81,8 +80,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); + case Tag::REQUEST_LATEST_CONFIG: + return callLocalAsync(data, reply, &IDisplayEventConnection::requestLatestConfig); } } diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h index fcdf6bfa7e..f210c34196 100644 --- a/libs/gui/include/gui/DisplayEventDispatcher.h +++ b/libs/gui/include/gui/DisplayEventDispatcher.h @@ -31,7 +31,7 @@ public: status_t initialize(); void dispose(); status_t scheduleVsync(); - void toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag); + void requestLatestConfig(); int getFd() const; virtual int handleEvent(int receiveFd, int events, void* data); @@ -42,7 +42,6 @@ private: sp 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, diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h index d9a0253781..8d49184caf 100644 --- a/libs/gui/include/gui/DisplayEventReceiver.h +++ b/libs/gui/include/gui/DisplayEventReceiver.h @@ -147,9 +147,10 @@ public: status_t requestNextVsync(); /* - * toggleConfigEvents() toggles delivery of config change events. + * requestLatestConfig() force-requests the current config for the primary + * display. */ - status_t toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag); + status_t requestLatestConfig(); private: sp mEventConnection; diff --git a/libs/gui/include/gui/IDisplayEventConnection.h b/libs/gui/include/gui/IDisplayEventConnection.h index 8b35ef6486..674aafd81c 100644 --- a/libs/gui/include/gui/IDisplayEventConnection.h +++ b/libs/gui/include/gui/IDisplayEventConnection.h @@ -53,11 +53,9 @@ public: virtual void requestNextVsync() = 0; // Asynchronous /* - * togglesConfigEvents() configures whether or not display config changes - * should be propagated. + * requestLatestConfig() requests the config for the primary display. */ - virtual void toggleConfigEvents( - ISurfaceComposer::ConfigChanged configChangeFlag) = 0; // Asynchronous + virtual void requestLatestConfig() = 0; // Asynchronous }; class BnDisplayEventConnection : public SafeBnInterface { diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp index 0ff33ac747..ea51245ac6 100644 --- a/libs/nativedisplay/AChoreographer.cpp +++ b/libs/nativedisplay/AChoreographer.cpp @@ -17,24 +17,63 @@ #define LOG_TAG "Choreographer" //#define LOG_NDEBUG 0 -#include +#include #include #include #include +#include +#include #include -#include #include #include +#include #include #include #include -namespace android { +namespace { +struct { + // Global JVM that is provided by zygote + JavaVM* jvm = nullptr; + struct { + jclass clazz; + jmethodID getInstance; + jmethodID registerNativeChoreographerForRefreshRateCallbacks; + jmethodID unregisterNativeChoreographerForRefreshRateCallbacks; + } displayManagerGlobal; +} gJni; + +// Gets the JNIEnv* for this thread, and performs one-off initialization if we +// have never retrieved a JNIEnv* pointer before. +JNIEnv* getJniEnv() { + if (gJni.jvm == nullptr) { + ALOGW("AChoreographer: No JVM provided!"); + return nullptr; + } + + JNIEnv* env = nullptr; + if (gJni.jvm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) { + ALOGD("Attaching thread to JVM for AChoreographer"); + JavaVMAttachArgs args = {JNI_VERSION_1_4, "AChoreographer_env", NULL}; + jint attachResult = gJni.jvm->AttachCurrentThreadAsDaemon(&env, (void*)&args); + if (attachResult != JNI_OK) { + ALOGE("Unable to attach thread. Error: %d", attachResult); + return nullptr; + } + } + if (env == nullptr) { + ALOGW("AChoreographer: No JNI env available!"); + } + return env; +} -static inline const char* toString(bool value) { +inline const char* toString(bool value) { return value ? "true" : "false"; } +} // namespace + +namespace android { struct FrameCallback { AChoreographer_frameCallback callback; @@ -52,24 +91,43 @@ struct FrameCallback { struct RefreshRateCallback { AChoreographer_refreshRateCallback callback; void* data; + bool firstCallbackFired = false; }; +class Choreographer; + +struct { + std::mutex lock; + std::vector ptrs GUARDED_BY(lock); + bool registeredToDisplayManager GUARDED_BY(lock) = false; + + std::atomic mLastKnownVsync = -1; +} gChoreographers; + class Choreographer : public DisplayEventDispatcher, public MessageHandler { public: - explicit Choreographer(const sp& looper); + explicit Choreographer(const sp& looper) EXCLUDES(gChoreographers.lock); void postFrameCallbackDelayed(AChoreographer_frameCallback cb, AChoreographer_frameCallback64 cb64, void* data, nsecs_t delay); - void registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data); + void registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data) + EXCLUDES(gChoreographers.lock); void unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data); + // Drains the queue of pending vsync periods and dispatches refresh rate + // updates to callbacks. + // The assumption is that this method is only called on a single + // processing thread, either by looper or by AChoreographer_handleEvents + void handleRefreshRateUpdates(); + void scheduleLatestConfigRequest(); enum { MSG_SCHEDULE_CALLBACKS = 0, - MSG_SCHEDULE_VSYNC = 1 + MSG_SCHEDULE_VSYNC = 1, + MSG_HANDLE_REFRESH_RATE_UPDATES = 2, }; virtual void handleMessage(const Message& message) override; static Choreographer* getForThread(); - virtual ~Choreographer() = default; + virtual ~Choreographer() override EXCLUDES(gChoreographers.lock); private: Choreographer(const Choreographer&) = delete; @@ -81,21 +139,17 @@ private: void scheduleCallbacks(); + std::mutex mLock; // Protected by mLock std::priority_queue mFrameCallbacks; - - // Protected by mLock std::vector mRefreshRateCallbacks; - nsecs_t mVsyncPeriod = 0; - mutable Mutex mLock; + nsecs_t mLatestVsyncPeriod = -1; const sp mLooper; const std::thread::id mThreadId; - const std::optional mInternalDisplayId; }; - static thread_local Choreographer* gChoreographer; Choreographer* Choreographer::getForThread() { if (gChoreographer == nullptr) { @@ -115,17 +169,47 @@ Choreographer* Choreographer::getForThread() { } Choreographer::Choreographer(const sp& looper) - : DisplayEventDispatcher(looper), + : DisplayEventDispatcher(looper, ISurfaceComposer::VsyncSource::eVsyncSourceApp, + ISurfaceComposer::ConfigChanged::eConfigChangedDispatch), mLooper(looper), - mThreadId(std::this_thread::get_id()), - mInternalDisplayId(SurfaceComposerClient::getInternalDisplayId()) {} + mThreadId(std::this_thread::get_id()) { + std::lock_guard _l(gChoreographers.lock); + gChoreographers.ptrs.push_back(this); +} + +Choreographer::~Choreographer() { + std::lock_guard _l(gChoreographers.lock); + gChoreographers.ptrs.erase(std::remove_if(gChoreographers.ptrs.begin(), + gChoreographers.ptrs.end(), + [=](Choreographer* c) { return c == this; }), + gChoreographers.ptrs.end()); + // Only poke DisplayManagerGlobal to unregister if we previously registered + // callbacks. + if (gChoreographers.ptrs.empty() && gChoreographers.registeredToDisplayManager) { + JNIEnv* env = getJniEnv(); + if (env == nullptr) { + ALOGW("JNI environment is unavailable, skipping choreographer cleanup"); + return; + } + jobject dmg = env->CallStaticObjectMethod(gJni.displayManagerGlobal.clazz, + gJni.displayManagerGlobal.getInstance); + if (dmg == nullptr) { + ALOGW("DMS is not initialized yet, skipping choreographer cleanup"); + } else { + env->CallVoidMethod(dmg, + gJni.displayManagerGlobal + .unregisterNativeChoreographerForRefreshRateCallbacks); + env->DeleteLocalRef(dmg); + } + } +} void Choreographer::postFrameCallbackDelayed( AChoreographer_frameCallback cb, AChoreographer_frameCallback64 cb64, void* data, nsecs_t delay) { nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); FrameCallback callback{cb, cb64, data, now + delay}; { - AutoMutex _l{mLock}; + std::lock_guard _l{mLock}; mFrameCallbacks.push(callback); } if (callback.dueTime <= now) { @@ -150,37 +234,68 @@ void Choreographer::postFrameCallbackDelayed( } void Choreographer::registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data) { + std::lock_guard _l{mLock}; + for (const auto& callback : mRefreshRateCallbacks) { + // Don't re-add callbacks. + if (cb == callback.callback && data == callback.data) { + return; + } + } + mRefreshRateCallbacks.emplace_back( + RefreshRateCallback{.callback = cb, .data = data, .firstCallbackFired = false}); + bool needsRegistration = false; { - AutoMutex _l{mLock}; - for (const auto& callback : mRefreshRateCallbacks) { - // Don't re-add callbacks. - if (cb == callback.callback && data == callback.data) { - return; + std::lock_guard _l2(gChoreographers.lock); + needsRegistration = !gChoreographers.registeredToDisplayManager; + } + if (needsRegistration) { + JNIEnv* env = getJniEnv(); + jobject dmg = env->CallStaticObjectMethod(gJni.displayManagerGlobal.clazz, + gJni.displayManagerGlobal.getInstance); + if (env == nullptr) { + ALOGW("JNI environment is unavailable, skipping registeration"); + return; + } + if (dmg == nullptr) { + ALOGW("DMS is not initialized yet: skipping registration"); + return; + } else { + env->CallVoidMethod(dmg, + gJni.displayManagerGlobal + .registerNativeChoreographerForRefreshRateCallbacks, + reinterpret_cast(this)); + env->DeleteLocalRef(dmg); + { + std::lock_guard _l2(gChoreographers.lock); + gChoreographers.registeredToDisplayManager = true; } } - mRefreshRateCallbacks.emplace_back(RefreshRateCallback{cb, data}); - toggleConfigEvents(ISurfaceComposer::ConfigChanged::eConfigChangedDispatch); + } else { + scheduleLatestConfigRequest(); } } void Choreographer::unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data) { - { - AutoMutex _l{mLock}; - mRefreshRateCallbacks.erase(std::remove_if(mRefreshRateCallbacks.begin(), - mRefreshRateCallbacks.end(), - [&](const RefreshRateCallback& callback) { - return cb == callback.callback && - data == callback.data; - }), - mRefreshRateCallbacks.end()); - if (mRefreshRateCallbacks.empty()) { - toggleConfigEvents(ISurfaceComposer::ConfigChanged::eConfigChangedSuppress); - // If callbacks are empty then clear out the most recently seen - // vsync period so that when another callback is registered then the - // up-to-date refresh rate can be communicated to the app again. - mVsyncPeriod = 0; - } + std::lock_guard _l{mLock}; + mRefreshRateCallbacks.erase(std::remove_if(mRefreshRateCallbacks.begin(), + mRefreshRateCallbacks.end(), + [&](const RefreshRateCallback& callback) { + return cb == callback.callback && + data == callback.data; + }), + mRefreshRateCallbacks.end()); +} + +void Choreographer::scheduleLatestConfigRequest() { + if (mLooper != nullptr) { + Message m{MSG_HANDLE_REFRESH_RATE_UPDATES}; + mLooper->sendMessage(this, m); + } else { + // If the looper thread is detached from Choreographer, then refresh rate + // changes will be handled in AChoreographer_handlePendingEvents, so we + // need to redispatch a config from SF + requestLatestConfig(); } } @@ -188,7 +303,7 @@ void Choreographer::scheduleCallbacks() { const nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); nsecs_t dueTime; { - AutoMutex _{mLock}; + std::lock_guard _l{mLock}; // If there are no pending callbacks then don't schedule a vsync if (mFrameCallbacks.empty()) { return; @@ -203,13 +318,35 @@ void Choreographer::scheduleCallbacks() { } } +void Choreographer::handleRefreshRateUpdates() { + std::vector callbacks{}; + const nsecs_t pendingPeriod = gChoreographers.mLastKnownVsync.load(); + const nsecs_t lastPeriod = mLatestVsyncPeriod; + if (pendingPeriod > 0) { + mLatestVsyncPeriod = pendingPeriod; + } + { + std::lock_guard _l{mLock}; + for (auto& cb : mRefreshRateCallbacks) { + callbacks.push_back(cb); + cb.firstCallbackFired = true; + } + } + + for (auto& cb : callbacks) { + if (!cb.firstCallbackFired || (pendingPeriod > 0 && pendingPeriod != lastPeriod)) { + cb.callback(pendingPeriod, cb.data); + } + } +} + // TODO(b/74619554): The PhysicalDisplayId is ignored because SF only emits VSYNC events for the // internal display and DisplayEventReceiver::requestNextVsync only allows requesting VSYNC for // the internal display implicitly. void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t) { std::vector callbacks{}; { - AutoMutex _l{mLock}; + std::lock_guard _l{mLock}; nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); while (!mFrameCallbacks.empty() && mFrameCallbacks.top().dueTime < now) { callbacks.push_back(mFrameCallbacks.top()); @@ -236,20 +373,29 @@ void Choreographer::dispatchHotplug(nsecs_t, PhysicalDisplayId displayId, bool c // 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, +void Choreographer::dispatchConfigChanged(nsecs_t, PhysicalDisplayId displayId, int32_t configId, nsecs_t vsyncPeriod) { + ALOGV("choreographer %p ~ received config change event " + "(displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", configId=%d).", + this, displayId, configId); + + const nsecs_t lastPeriod = mLatestVsyncPeriod; + std::vector callbacks{}; { - 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); - } + std::lock_guard _l{mLock}; + for (auto& cb : mRefreshRateCallbacks) { + callbacks.push_back(cb); + cb.firstCallbackFired = true; + } + } + + for (auto& cb : callbacks) { + if (!cb.firstCallbackFired || (vsyncPeriod > 0 && vsyncPeriod != lastPeriod)) { + cb.callback(vsyncPeriod, cb.data); } - mVsyncPeriod = vsyncPeriod; } + + mLatestVsyncPeriod = vsyncPeriod; } void Choreographer::handleMessage(const Message& message) { @@ -260,19 +406,80 @@ void Choreographer::handleMessage(const Message& message) { case MSG_SCHEDULE_VSYNC: scheduleVsync(); break; + case MSG_HANDLE_REFRESH_RATE_UPDATES: + handleRefreshRateUpdates(); + break; } } -} - -/* Glue for the NDK interface */ - +} // namespace android using namespace android; static inline Choreographer* AChoreographer_to_Choreographer(AChoreographer* choreographer) { return reinterpret_cast(choreographer); } +// Glue for private C api +namespace android { +void AChoreographer_signalRefreshRateCallbacks(nsecs_t vsyncPeriod) EXCLUDES(gChoreographers.lock) { + std::lock_guard _l(gChoreographers.lock); + gChoreographers.mLastKnownVsync.store(vsyncPeriod); + for (auto c : gChoreographers.ptrs) { + c->scheduleLatestConfigRequest(); + } +} + +void AChoreographer_initJVM(JNIEnv* env) { + env->GetJavaVM(&gJni.jvm); + // Now we need to find the java classes. + jclass dmgClass = env->FindClass("android/hardware/display/DisplayManagerGlobal"); + gJni.displayManagerGlobal.clazz = static_cast(env->NewGlobalRef(dmgClass)); + gJni.displayManagerGlobal.getInstance = + env->GetStaticMethodID(dmgClass, "getInstance", + "()Landroid/hardware/display/DisplayManagerGlobal;"); + gJni.displayManagerGlobal.registerNativeChoreographerForRefreshRateCallbacks = + env->GetMethodID(dmgClass, "registerNativeChoreographerForRefreshRateCallbacks", "()V"); + gJni.displayManagerGlobal.unregisterNativeChoreographerForRefreshRateCallbacks = + env->GetMethodID(dmgClass, "unregisterNativeChoreographerForRefreshRateCallbacks", + "()V"); +} + +AChoreographer* AChoreographer_routeGetInstance() { + return AChoreographer_getInstance(); +} +void AChoreographer_routePostFrameCallback(AChoreographer* choreographer, + AChoreographer_frameCallback callback, void* data) { + return AChoreographer_postFrameCallback(choreographer, callback, data); +} +void AChoreographer_routePostFrameCallbackDelayed(AChoreographer* choreographer, + AChoreographer_frameCallback callback, void* data, + long delayMillis) { + return AChoreographer_postFrameCallbackDelayed(choreographer, callback, data, delayMillis); +} +void AChoreographer_routePostFrameCallback64(AChoreographer* choreographer, + AChoreographer_frameCallback64 callback, void* data) { + return AChoreographer_postFrameCallback64(choreographer, callback, data); +} +void AChoreographer_routePostFrameCallbackDelayed64(AChoreographer* choreographer, + AChoreographer_frameCallback64 callback, + void* data, uint32_t delayMillis) { + return AChoreographer_postFrameCallbackDelayed64(choreographer, callback, data, delayMillis); +} +void AChoreographer_routeRegisterRefreshRateCallback(AChoreographer* choreographer, + AChoreographer_refreshRateCallback callback, + void* data) { + return AChoreographer_registerRefreshRateCallback(choreographer, callback, data); +} +void AChoreographer_routeUnregisterRefreshRateCallback(AChoreographer* choreographer, + AChoreographer_refreshRateCallback callback, + void* data) { + return AChoreographer_unregisterRefreshRateCallback(choreographer, callback, data); +} + +} // namespace android + +/* Glue for the NDK interface */ + static inline const Choreographer* AChoreographer_to_Choreographer( const AChoreographer* choreographer) { return reinterpret_cast(choreographer); @@ -343,5 +550,6 @@ void AChoreographer_handlePendingEvents(AChoreographer* choreographer, void* dat // Pass dummy fd and events args to handleEvent, since the underlying // DisplayEventDispatcher doesn't need them outside of validating that a // Looper instance didn't break, but these args circumvent those checks. - AChoreographer_to_Choreographer(choreographer)->handleEvent(-1, Looper::EVENT_INPUT, data); + Choreographer* impl = AChoreographer_to_Choreographer(choreographer); + impl->handleEvent(-1, Looper::EVENT_INPUT, data); } diff --git a/libs/nativedisplay/Android.bp b/libs/nativedisplay/Android.bp index c9565780e0..f56b3a2178 100644 --- a/libs/nativedisplay/Android.bp +++ b/libs/nativedisplay/Android.bp @@ -12,23 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -ndk_headers { - name: "libnativedisplay_ndk_headers", - from: "include/android", - to: "android", - srcs: ["include/android/*.h"], - license: "NOTICE", -} - cc_library_headers { name: "libnativedisplay_headers", - export_include_dirs: ["include"], + export_include_dirs: ["include",], } -cc_library { +cc_library_shared { name: "libnativedisplay", export_include_dirs: [ "include", + "include-private", ], clang: true, @@ -63,6 +56,10 @@ cc_library { "libnativehelper", ], + export_shared_lib_headers: [ + "libnativehelper", + ], + header_libs: [ "libnativedisplay_headers", ], diff --git a/libs/nativedisplay/include-private/private/android/choreographer.h b/libs/nativedisplay/include-private/private/android/choreographer.h new file mode 100644 index 0000000000..21649304bf --- /dev/null +++ b/libs/nativedisplay/include-private/private/android/choreographer.h @@ -0,0 +1,55 @@ +/* + * Copyright 2020 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 + +namespace android { + +// Registers the global JVM for AChoreographer +void AChoreographer_initJVM(JNIEnv* env); + +// Signals all AChoregorapher* instances that a new vsync period is available +// for consumption by callbacks. +void AChoreographer_signalRefreshRateCallbacks(int64_t vsyncPeriod); + +// Trampoline functions allowing libandroid.so to define the NDK symbols without including +// the entirety of libnativedisplay as a whole static lib. As libnativedisplay +// maintains global state, libnativedisplay can never be directly statically +// linked so that global state won't be duplicated. This way libandroid.so can +// reroute the NDK methods into the implementations defined by libnativedisplay +AChoreographer* AChoreographer_routeGetInstance(); +void AChoreographer_routePostFrameCallback(AChoreographer* choreographer, + AChoreographer_frameCallback callback, void* data); +void AChoreographer_routePostFrameCallbackDelayed(AChoreographer* choreographer, + AChoreographer_frameCallback callback, void* data, + long delayMillis); +void AChoreographer_routePostFrameCallback64(AChoreographer* choreographer, + AChoreographer_frameCallback64 callback, void* data); +void AChoreographer_routePostFrameCallbackDelayed64(AChoreographer* choreographer, + AChoreographer_frameCallback64 callback, + void* data, uint32_t delayMillis); +void AChoreographer_routeRegisterRefreshRateCallback(AChoreographer* choreographer, + AChoreographer_refreshRateCallback callback, + void* data); +void AChoreographer_routeUnregisterRefreshRateCallback(AChoreographer* choreographer, + AChoreographer_refreshRateCallback callback, + void* data); + +} // namespace android diff --git a/libs/nativedisplay/include/android/choreographer.h b/libs/nativedisplay/include/android/choreographer.h deleted file mode 100644 index 5fd3de9f3c..0000000000 --- a/libs/nativedisplay/include/android/choreographer.h +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (C) 2015 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. - */ - -/** - * @addtogroup Choreographer - * @{ - */ - -/** - * @file choreographer.h - */ - -#ifndef ANDROID_CHOREOGRAPHER_H -#define ANDROID_CHOREOGRAPHER_H - -#include -#include - -__BEGIN_DECLS - -struct AChoreographer; -typedef struct AChoreographer AChoreographer; - -/** - * Prototype of the function that is called when a new frame is being rendered. - * It's passed the time that the frame is being rendered as nanoseconds in the - * CLOCK_MONOTONIC time base, as well as the data pointer provided by the - * application that registered a callback. All callbacks that run as part of - * rendering a frame will observe the same frame time, so it should be used - * whenever events need to be synchronized (e.g. animations). - */ -typedef void (*AChoreographer_frameCallback)(long frameTimeNanos, void* data); - -/** - * Prototype of the function that is called when a new frame is being rendered. - * It's passed the time that the frame is being rendered as nanoseconds in the - * CLOCK_MONOTONIC time base, as well as the data pointer provided by the - * application that registered a callback. All callbacks that run as part of - * rendering a frame will observe the same frame time, so it should be used - * whenever events need to be synchronized (e.g. animations). - */ -typedef void (*AChoreographer_frameCallback64)(int64_t frameTimeNanos, void* data); - -/** - * 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); - -#if __ANDROID_API__ >= 24 - -/** - * Get the AChoreographer instance for the current thread. This must be called - * on an ALooper thread. - * - * Available since API level 24. - */ -AChoreographer* AChoreographer_getInstance() __INTRODUCED_IN(24); - -/** - * Deprecated: Use AChoreographer_postFrameCallback64 instead. - */ -void AChoreographer_postFrameCallback(AChoreographer* choreographer, - AChoreographer_frameCallback callback, void* data) __INTRODUCED_IN(24) __DEPRECATED_IN(29); - -/** - * Deprecated: Use AChoreographer_postFrameCallbackDelayed64 instead. - */ -void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer, - AChoreographer_frameCallback callback, void* data, - long delayMillis) __INTRODUCED_IN(24) __DEPRECATED_IN(29); - -#endif /* __ANDROID_API__ >= 24 */ - -#if __ANDROID_API__ >= 29 - -/** - * Power a callback to be run on the next frame. The data pointer provided will - * be passed to the callback function when it's called. - * - * Available since API level 29. - */ -void AChoreographer_postFrameCallback64(AChoreographer* choreographer, - AChoreographer_frameCallback64 callback, void* data) __INTRODUCED_IN(29); - -/** - * Post a callback to be run on the frame following the specified delay. The - * data pointer provided will be passed to the callback function when it's - * called. - * - * Available since API level 29. - */ -void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer, - AChoreographer_frameCallback64 callback, void* data, uint32_t delayMillis) __INTRODUCED_IN(29); - -#endif /* __ANDROID_API__ >= 29 */ - -#if __ANDROID_API__ >= 30 - -/** - * Registers a callback to be run when the display refresh rate changes. The - * data pointer provided will be passed to the callback function when it's - * called. The same callback may be registered multiple times, provided that a - * different data pointer is provided each time. - * - * If an application registers a callback for this choreographer instance when - * no new callbacks were previously registered, that callback is guaranteed to - * be dispatched. However, if the callback and associated data pointer are - * unregistered prior to running the callback, then the callback may be silently - * dropped. - * - * This api is thread-safe. Any thread is allowed to register a new refresh - * rate callback for the choreographer instance. - */ -void AChoreographer_registerRefreshRateCallback(AChoreographer* choreographer, - AChoreographer_refreshRateCallback, void* data); - -/** - * Unregisters a callback to be run when the display refresh rate changes, along - * with the data pointer previously provided when registering the callback. The - * callback is only unregistered when the data pointer matches one that was - * previously registered. - * - * This api is thread-safe. Any thread is allowed to unregister an existing - * refresh rate callback for the choreographer instance. When a refresh rate - * callback and associated data pointer are unregistered, then there is a - * guarantee that when the unregistration completes that that callback will not - * be run with the data pointer passed. - */ -void AChoreographer_unregisterRefreshRateCallback(AChoreographer* choreographer, - AChoreographer_refreshRateCallback, void* data); -#endif /* __ANDROID_API__ >= 30 */ - -__END_DECLS - -#endif // ANDROID_CHOREOGRAPHER_H - -/** @} */ diff --git a/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h b/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h index 6a94a771e8..e2d036bfb0 100644 --- a/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h +++ b/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h @@ -19,7 +19,7 @@ #include #include - +#include #include // This file provides a facade API on top of SurfaceTexture, which avoids using @@ -30,6 +30,20 @@ struct ASurfaceTexture; namespace android { +// Trampoline functions allowing libandroid.so to define the NDK symbols without including +// the entirety of libnativedisplay as a whole static lib. As libnativedisplay +// maintains global state, libnativedisplay can never be directly statically +// linked so that global state won't be duplicated. This way libandroid.so can +// reroute the NDK methods into the implementations defined by libnativedisplay +ANativeWindow* ASurfaceTexture_routeAcquireANativeWindow(ASurfaceTexture* st); +int ASurfaceTexture_routeAttachToGLContext(ASurfaceTexture* st, uint32_t texName); +int ASurfaceTexture_routeDetachFromGLContext(ASurfaceTexture* st); +void ASurfaceTexture_routeRelease(ASurfaceTexture* st); +int ASurfaceTexture_routeUpdateTexImage(ASurfaceTexture* st); +void ASurfaceTexture_routeGetTransformMatrix(ASurfaceTexture* st, float mtx[16]); +int64_t ASurfaceTexture_routeGetTimestamp(ASurfaceTexture* st); +ASurfaceTexture* ASurfaceTexture_routeFromSurfaceTexture(JNIEnv* env, jobject surfacetexture); + /** * ASurfaceTexture_getCurrentTextureTarget returns the texture target of the * current texture. diff --git a/libs/nativedisplay/libnativedisplay.map.txt b/libs/nativedisplay/libnativedisplay.map.txt index 483fb25cb7..fc59431d08 100644 --- a/libs/nativedisplay/libnativedisplay.map.txt +++ b/libs/nativedisplay/libnativedisplay.map.txt @@ -20,6 +20,15 @@ LIBNATIVEDISPLAY { LIBNATIVEDISPLAY_PLATFORM { global: extern "C++" { + android::AChoreographer_initJVM*; + android::AChoreographer_routeGetInstance*; + android::AChoreographer_routePostFrameCallback*; + android::AChoreographer_routePostFrameCallbackDelayed*; + android::AChoreographer_routePostFrameCallback64*; + android::AChoreographer_routePostFrameCallbackDelayed64*; + android::AChoreographer_routeRegisterRefreshRateCallback*; + android::AChoreographer_routeUnregisterRefreshRateCallback*; + android::AChoreographer_signalRefreshRateCallbacks*; android::ADisplay_acquirePhysicalDisplays*; android::ADisplay_release*; android::ADisplay_getMaxSupportedFps*; @@ -36,6 +45,14 @@ LIBNATIVEDISPLAY_PLATFORM { android::ASurfaceTexture_takeConsumerOwnership*; android::ASurfaceTexture_releaseConsumerOwnership*; android::ASurfaceTexture_dequeueBuffer*; + android::ASurfaceTexture_routeAcquireANativeWindow*; + android::ASurfaceTexture_routeAttachToGLContext*; + android::ASurfaceTexture_routeDetachFromGLContext*; + android::ASurfaceTexture_routeGetTimestamp*; + android::ASurfaceTexture_routeGetTransformMatrix*; + android::ASurfaceTexture_routeUpdateTexImage*; + android::ASurfaceTexture_routeFromSurfaceTexture*; + android::ASurfaceTexture_routeRelease*; android::SurfaceTexture*; }; ASurfaceTexture_acquireANativeWindow; diff --git a/libs/nativedisplay/surfacetexture/surface_texture.cpp b/libs/nativedisplay/surfacetexture/surface_texture.cpp index 1670fbba57..d1bcd8d1b1 100644 --- a/libs/nativedisplay/surfacetexture/surface_texture.cpp +++ b/libs/nativedisplay/surfacetexture/surface_texture.cpp @@ -149,6 +149,37 @@ int64_t ASurfaceTexture_getTimestamp(ASurfaceTexture* st) { // The following functions are private/unstable API. namespace android { +ANativeWindow* ASurfaceTexture_routeAcquireANativeWindow(ASurfaceTexture* st) { + return ASurfaceTexture_acquireANativeWindow(st); +} + +int ASurfaceTexture_routeAttachToGLContext(ASurfaceTexture* st, uint32_t texName) { + return ASurfaceTexture_attachToGLContext(st, texName); +} + +void ASurfaceTexture_routeRelease(ASurfaceTexture* st) { + return ASurfaceTexture_release(st); +} + +int ASurfaceTexture_routeDetachFromGLContext(ASurfaceTexture* st) { + return ASurfaceTexture_detachFromGLContext(st); +} + +int ASurfaceTexture_routeUpdateTexImage(ASurfaceTexture* st) { + return ASurfaceTexture_updateTexImage(st); +} + +void ASurfaceTexture_routeGetTransformMatrix(ASurfaceTexture* st, float mtx[16]) { + return ASurfaceTexture_getTransformMatrix(st, mtx); +} + +int64_t ASurfaceTexture_routeGetTimestamp(ASurfaceTexture* st) { + return ASurfaceTexture_getTimestamp(st); +} + +ASurfaceTexture* ASurfaceTexture_routeFromSurfaceTexture(JNIEnv* env, jobject surfacetexture) { + return ASurfaceTexture_fromSurfaceTexture(env, surfacetexture); +} unsigned int ASurfaceTexture_getCurrentTextureTarget(ASurfaceTexture* st) { return st->consumer->getCurrentTextureTarget(); diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp index 5dedb6a1e7..cee36a121f 100644 --- a/services/surfaceflinger/Scheduler/EventThread.cpp +++ b/services/surfaceflinger/Scheduler/EventThread.cpp @@ -152,16 +152,9 @@ 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(); - } +void EventThreadConnection::requestLatestConfig() { + ATRACE_NAME("requestLatestConfig"); + mEventThread->requestLatestConfig(this); } status_t EventThreadConnection::postEvent(const DisplayEventReceiver::Event& event) { @@ -276,8 +269,12 @@ void EventThread::requestNextVsync(const sp& connection) } } -void EventThread::requestLatestConfig() { +void EventThread::requestLatestConfig(const sp& connection) { std::lock_guard lock(mMutex); + if (connection->mForcedConfigChangeDispatch) { + return; + } + connection->mForcedConfigChangeDispatch = true; auto pendingConfigChange = std::find_if(std::begin(mPendingEvents), std::end(mPendingEvents), [&](const DisplayEventReceiver::Event& event) { @@ -384,6 +381,10 @@ void EventThread::threadMain(std::unique_lock& lock) { vsyncRequested |= connection->vsyncRequest != VSyncRequest::None; if (event && shouldConsumeEvent(*event, connection)) { + if (event->header.type == DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED && + connection->mForcedConfigChangeDispatch) { + connection->mForcedConfigChangeDispatch = false; + } consumers.push_back(connection); } @@ -459,8 +460,8 @@ bool EventThread::shouldConsumeEvent(const DisplayEventReceiver::Event& event, return true; case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED: { - const bool forcedDispatch = connection->mForcedConfigChangeDispatch.exchange(false); - return forcedDispatch || + const bool oneTimeDispatch = connection->mForcedConfigChangeDispatch; + return oneTimeDispatch || connection->mConfigChanged == ISurfaceComposer::eConfigChangedDispatch; } diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h index 9e7086eb0c..64acbd72d0 100644 --- a/services/surfaceflinger/Scheduler/EventThread.h +++ b/services/surfaceflinger/Scheduler/EventThread.h @@ -81,19 +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; + void requestLatestConfig() override; // asynchronous // Called in response to requestNextVsync. const ResyncCallback resyncCallback; VSyncRequest vsyncRequest = VSyncRequest::None; - std::atomic mConfigChanged = + 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 mForcedConfigChangeDispatch = false; + bool mForcedConfigChangeDispatch = false; private: virtual void onFirstRef(); @@ -129,11 +129,10 @@ public: virtual void setVsyncRate(uint32_t rate, const sp& connection) = 0; // Requests the next vsync. If resetIdleTimer is set to true, it resets the idle timer. virtual void requestNextVsync(const sp& 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; + virtual void requestLatestConfig(const sp& connection) = 0; // Retrieves the number of event connections tracked by this EventThread. virtual size_t getEventThreadConnectionCount() = 0; @@ -154,7 +153,7 @@ public: status_t registerDisplayEventConnection(const sp& connection) override; void setVsyncRate(uint32_t rate, const sp& connection) override; void requestNextVsync(const sp& connection) override; - void requestLatestConfig() override; + void requestLatestConfig(const sp& connection) override; // called before the screen is turned off from main thread void onScreenReleased() override; diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h index 50eb390471..054aaf8ae1 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h +++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h @@ -40,7 +40,7 @@ public: status_t(const sp &)); MOCK_METHOD2(setVsyncRate, void(uint32_t, const sp &)); MOCK_METHOD1(requestNextVsync, void(const sp &)); - MOCK_METHOD0(requestLatestConfig, void()); + MOCK_METHOD1(requestLatestConfig, void(const sp &)); MOCK_METHOD1(pauseVsyncCallback, void(bool)); MOCK_METHOD0(getEventThreadConnectionCount, size_t()); }; -- cgit v1.2.3-59-g8ed1b