[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
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index 05ff93e..7e71ede 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 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 @@
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 @@
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 @@
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::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 @@
{
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 @@
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 @@
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);
+}