diff options
-rw-r--r-- | include/android/choreographer.h (renamed from libs/nativedisplay/include/android/choreographer.h) | 14 | ||||
-rw-r--r-- | libs/gui/DisplayEventDispatcher.cpp | 13 | ||||
-rw-r--r-- | libs/gui/DisplayEventReceiver.cpp | 5 | ||||
-rw-r--r-- | libs/gui/IDisplayEventConnection.cpp | 15 | ||||
-rw-r--r-- | libs/gui/include/gui/DisplayEventDispatcher.h | 3 | ||||
-rw-r--r-- | libs/gui/include/gui/DisplayEventReceiver.h | 5 | ||||
-rw-r--r-- | libs/gui/include/gui/IDisplayEventConnection.h | 6 | ||||
-rw-r--r-- | libs/nativedisplay/AChoreographer.cpp | 324 | ||||
-rw-r--r-- | libs/nativedisplay/Android.bp | 17 | ||||
-rw-r--r-- | libs/nativedisplay/include-private/private/android/choreographer.h | 55 | ||||
-rw-r--r-- | libs/nativedisplay/include/surfacetexture/surface_texture_platform.h | 16 | ||||
-rw-r--r-- | libs/nativedisplay/libnativedisplay.map.txt | 17 | ||||
-rw-r--r-- | libs/nativedisplay/surfacetexture/surface_texture.cpp | 31 | ||||
-rw-r--r-- | services/surfaceflinger/Scheduler/EventThread.cpp | 27 | ||||
-rw-r--r-- | services/surfaceflinger/Scheduler/EventThread.h | 11 | ||||
-rw-r--r-- | services/surfaceflinger/tests/unittests/mock/MockEventThread.h | 2 |
16 files changed, 438 insertions, 123 deletions
diff --git a/libs/nativedisplay/include/android/choreographer.h b/include/android/choreographer.h index 5fd3de9f3c..c1c4a72cd3 100644 --- a/libs/nativedisplay/include/android/choreographer.h +++ b/include/android/choreographer.h @@ -75,14 +75,16 @@ 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); + 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); + AChoreographer_frameCallback callback, void* data, + long delayMillis) __INTRODUCED_IN(24) + __DEPRECATED_IN(29); #endif /* __ANDROID_API__ >= 24 */ @@ -95,7 +97,8 @@ void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer, * Available since API level 29. */ void AChoreographer_postFrameCallback64(AChoreographer* choreographer, - AChoreographer_frameCallback64 callback, void* data) __INTRODUCED_IN(29); + AChoreographer_frameCallback64 callback, void* data) + __INTRODUCED_IN(29); /** * Post a callback to be run on the frame following the specified delay. The @@ -105,7 +108,8 @@ void AChoreographer_postFrameCallback64(AChoreographer* choreographer, * Available since API level 29. */ void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer, - AChoreographer_frameCallback64 callback, void* data, uint32_t delayMillis) __INTRODUCED_IN(29); + AChoreographer_frameCallback64 callback, void* data, + uint32_t delayMillis) __INTRODUCED_IN(29); #endif /* __ANDROID_API__ >= 29 */ 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>& 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<decltype( - &IDisplayEventConnection::toggleConfigEvents)>(Tag::TOGGLE_CONFIG_EVENTS, - configChangeFlag); + void requestLatestConfig() override { + callRemoteAsync<decltype(&IDisplayEventConnection::requestLatestConfig)>( + 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<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, 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<IDisplayEventConnection> 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<IDisplayEventConnection> { 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 <apex/choreographer.h> +#include <android-base/thread_annotations.h> #include <gui/DisplayEventDispatcher.h> #include <gui/ISurfaceComposer.h> #include <gui/SurfaceComposerClient.h> +#include <nativehelper/JNIHelp.h> +#include <private/android/choreographer.h> #include <utils/Looper.h> -#include <utils/Mutex.h> #include <utils/Timers.h> #include <cinttypes> +#include <mutex> #include <optional> #include <queue> #include <thread> -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<Choreographer*> ptrs GUARDED_BY(lock); + bool registeredToDisplayManager GUARDED_BY(lock) = false; + + std::atomic<nsecs_t> mLastKnownVsync = -1; +} gChoreographers; + class Choreographer : public DisplayEventDispatcher, public MessageHandler { public: - explicit Choreographer(const sp<Looper>& looper); + explicit Choreographer(const sp<Looper>& 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<FrameCallback> mFrameCallbacks; - - // Protected by mLock std::vector<RefreshRateCallback> mRefreshRateCallbacks; - nsecs_t mVsyncPeriod = 0; - mutable Mutex mLock; + nsecs_t mLatestVsyncPeriod = -1; const sp<Looper> mLooper; const std::thread::id mThreadId; - const std::optional<PhysicalDisplayId> mInternalDisplayId; }; - static thread_local Choreographer* gChoreographer; Choreographer* Choreographer::getForThread() { if (gChoreographer == nullptr) { @@ -115,17 +169,47 @@ Choreographer* Choreographer::getForThread() { } Choreographer::Choreographer(const sp<Looper>& 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<std::mutex> _l(gChoreographers.lock); + gChoreographers.ptrs.push_back(this); +} + +Choreographer::~Choreographer() { + std::lock_guard<std::mutex> _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<std::mutex> _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<std::mutex> _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<std::mutex> _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<int64_t>(this)); + env->DeleteLocalRef(dmg); + { + std::lock_guard<std::mutex> _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<std::mutex> _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<std::mutex> _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<RefreshRateCallback> callbacks{}; + const nsecs_t pendingPeriod = gChoreographers.mLastKnownVsync.load(); + const nsecs_t lastPeriod = mLatestVsyncPeriod; + if (pendingPeriod > 0) { + mLatestVsyncPeriod = pendingPeriod; + } + { + std::lock_guard<std::mutex> _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<FrameCallback> callbacks{}; { - AutoMutex _l{mLock}; + std::lock_guard<std::mutex> _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<RefreshRateCallback> 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<std::mutex> _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*>(choreographer); } +// Glue for private C api +namespace android { +void AChoreographer_signalRefreshRateCallbacks(nsecs_t vsyncPeriod) EXCLUDES(gChoreographers.lock) { + std::lock_guard<std::mutex> _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<jclass>(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<const Choreographer*>(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 <apex/choreographer.h> +#include <inttypes.h> +#include <nativehelper/JNIHelp.h> + +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/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 <EGL/egl.h> #include <EGL/eglext.h> - +#include <nativehelper/JNIHelp.h> #include <system/graphics.h> // 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<EventThreadConnection>& connection) } } -void EventThread::requestLatestConfig() { +void EventThread::requestLatestConfig(const sp<EventThreadConnection>& connection) { std::lock_guard<std::mutex> 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<std::mutex>& 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<ISurfaceComposer::ConfigChanged> 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<bool> mForcedConfigChangeDispatch = false; + bool mForcedConfigChangeDispatch = false; private: virtual void onFirstRef(); @@ -129,11 +129,10 @@ 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; + virtual void requestLatestConfig(const sp<EventThreadConnection>& 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<EventThreadConnection>& connection) override; void setVsyncRate(uint32_t rate, const sp<EventThreadConnection>& connection) override; void requestNextVsync(const sp<EventThreadConnection>& connection) override; - void requestLatestConfig() override; + void requestLatestConfig(const sp<EventThreadConnection>& 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<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(requestLatestConfig, void(const sp<android::EventThreadConnection> &)); MOCK_METHOD1(pauseVsyncCallback, void(bool)); MOCK_METHOD0(getEventThreadConnectionCount, size_t()); }; |