[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/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp
index 54f383e..208d729 100644
--- a/libs/gui/DisplayEventDispatcher.cpp
+++ b/libs/gui/DisplayEventDispatcher.cpp
@@ -36,7 +36,10 @@
DisplayEventDispatcher::DisplayEventDispatcher(const sp<Looper>& looper,
ISurfaceComposer::VsyncSource vsyncSource,
ISurfaceComposer::ConfigChanged configChanged)
- : mLooper(looper), mReceiver(vsyncSource, configChanged), mWaitingForVsync(false) {
+ : mLooper(looper),
+ mReceiver(vsyncSource, configChanged),
+ mWaitingForVsync(false),
+ mConfigChangeFlag(configChanged) {
ALOGV("dispatcher %p ~ Initializing display event dispatcher.", this);
}
@@ -86,6 +89,18 @@
return OK;
}
+void DisplayEventDispatcher::toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag) {
+ if (mConfigChangeFlag == configChangeFlag) {
+ return;
+ }
+ status_t status = mReceiver.toggleConfigEvents(configChangeFlag);
+ if (status) {
+ ALOGW("Failed enable config events, status=%d", status);
+ return;
+ }
+ mConfigChangeFlag = configChangeFlag;
+}
+
int DisplayEventDispatcher::handleEvent(int, int events, void*) {
if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) {
ALOGE("Display event receiver pipe was closed or an error occurred. "
@@ -140,7 +155,7 @@
break;
case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED:
dispatchConfigChanged(ev.header.timestamp, ev.header.displayId,
- ev.config.configId);
+ ev.config.configId, ev.config.vsyncPeriod);
break;
default:
ALOGW("dispatcher %p ~ ignoring unknown event type %#x", this, ev.header.type);
diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp
index b8faa2d..fd6aaf8 100644
--- a/libs/gui/DisplayEventReceiver.cpp
+++ b/libs/gui/DisplayEventReceiver.cpp
@@ -79,6 +79,14 @@
return NO_INIT;
}
+status_t DisplayEventReceiver::toggleConfigEvents(
+ ISurfaceComposer::ConfigChanged configChangeFlag) {
+ if (mEventConnection != nullptr) {
+ mEventConnection->toggleConfigEvents(configChangeFlag);
+ return NO_ERROR;
+ }
+ return NO_INIT;
+}
ssize_t DisplayEventReceiver::getEvents(DisplayEventReceiver::Event* events,
size_t count) {
diff --git a/libs/gui/IDisplayEventConnection.cpp b/libs/gui/IDisplayEventConnection.cpp
index c0e246f..dda5acf 100644
--- a/libs/gui/IDisplayEventConnection.cpp
+++ b/libs/gui/IDisplayEventConnection.cpp
@@ -26,7 +26,8 @@
STEAL_RECEIVE_CHANNEL = IBinder::FIRST_CALL_TRANSACTION,
SET_VSYNC_RATE,
REQUEST_NEXT_VSYNC,
- LAST = REQUEST_NEXT_VSYNC,
+ TOGGLE_CONFIG_EVENTS,
+ LAST = TOGGLE_CONFIG_EVENTS,
};
} // Anonymous namespace
@@ -53,6 +54,12 @@
callRemoteAsync<decltype(&IDisplayEventConnection::requestNextVsync)>(
Tag::REQUEST_NEXT_VSYNC);
}
+
+ void toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag) override {
+ callRemoteAsync<decltype(
+ &IDisplayEventConnection::toggleConfigEvents)>(Tag::TOGGLE_CONFIG_EVENTS,
+ configChangeFlag);
+ }
};
// Out-of-line virtual method definition to trigger vtable emission in this translation unit (see
@@ -74,6 +81,8 @@
return callLocal(data, reply, &IDisplayEventConnection::setVsyncRate);
case Tag::REQUEST_NEXT_VSYNC:
return callLocalAsync(data, reply, &IDisplayEventConnection::requestNextVsync);
+ case Tag::TOGGLE_CONFIG_EVENTS:
+ return callLocalAsync(data, reply, &IDisplayEventConnection::toggleConfigEvents);
}
}
diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h
index f0b7ff5..0b71801 100644
--- a/libs/gui/include/gui/DisplayEventDispatcher.h
+++ b/libs/gui/include/gui/DisplayEventDispatcher.h
@@ -31,6 +31,7 @@
status_t initialize();
void dispose();
status_t scheduleVsync();
+ void toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag);
protected:
virtual ~DisplayEventDispatcher() = default;
@@ -39,12 +40,13 @@
sp<Looper> mLooper;
DisplayEventReceiver mReceiver;
bool mWaitingForVsync;
+ ISurfaceComposer::ConfigChanged mConfigChangeFlag;
virtual void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) = 0;
virtual void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId,
bool connected) = 0;
virtual void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId,
- int32_t configId) = 0;
+ int32_t configId, nsecs_t vsyncPeriod) = 0;
virtual int handleEvent(int receiveFd, int events, void* data);
bool processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId,
diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h
index a558cf9..109e28b 100644
--- a/libs/gui/include/gui/DisplayEventReceiver.h
+++ b/libs/gui/include/gui/DisplayEventReceiver.h
@@ -73,6 +73,7 @@
struct Config {
int32_t configId;
+ nsecs_t vsyncPeriod;
};
Header header;
@@ -144,6 +145,11 @@
*/
status_t requestNextVsync();
+ /*
+ * toggleConfigEvents() toggles delivery of config change events.
+ */
+ status_t toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag);
+
private:
sp<IDisplayEventConnection> mEventConnection;
std::unique_ptr<gui::BitTube> mDataChannel;
diff --git a/libs/gui/include/gui/IDisplayEventConnection.h b/libs/gui/include/gui/IDisplayEventConnection.h
index d783f74..8b35ef6 100644
--- a/libs/gui/include/gui/IDisplayEventConnection.h
+++ b/libs/gui/include/gui/IDisplayEventConnection.h
@@ -18,7 +18,7 @@
#include <binder/IInterface.h>
#include <binder/SafeInterface.h>
-
+#include <gui/ISurfaceComposer.h>
#include <utils/Errors.h>
#include <cstdint>
@@ -51,6 +51,13 @@
* requestNextVsync() schedules the next vsync event. It has no effect if the vsync rate is > 0.
*/
virtual void requestNextVsync() = 0; // Asynchronous
+
+ /*
+ * togglesConfigEvents() configures whether or not display config changes
+ * should be propagated.
+ */
+ virtual void toggleConfigEvents(
+ ISurfaceComposer::ConfigChanged configChangeFlag) = 0; // Asynchronous
};
class BnDisplayEventConnection : public SafeBnInterface<IDisplayEventConnection> {
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index 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);
+}
diff --git a/libs/nativedisplay/include/apex/choreographer.h b/libs/nativedisplay/include/apex/choreographer.h
new file mode 100644
index 0000000..352213e
--- /dev/null
+++ b/libs/nativedisplay/include/apex/choreographer.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/choreographer.h>
+#include <inttypes.h>
+
+__BEGIN_DECLS
+
+/**
+ * Prototype of the function that is called when the display refresh rate
+ * changes. It's passed the new vsync period in nanoseconds, as well as the data
+ * pointer provided by the application that registered a callback.
+ */
+typedef void (*AChoreographer_refreshRateCallback)(int64_t vsyncPeriodNanos, void* data);
+
+/**
+ * Registers a callback to be run when the display refresh rate changes.
+ */
+void AChoreographer_registerRefreshRateCallback(AChoreographer* choreographer,
+ AChoreographer_refreshRateCallback, void* data);
+
+/**
+ * Unregisters a callback to be run when the display refresh rate changes.
+ */
+void AChoreographer_unregisterRefreshRateCallback(AChoreographer* choreographer,
+ AChoreographer_refreshRateCallback);
+
+__END_DECLS
diff --git a/libs/nativedisplay/libnativedisplay.map.txt b/libs/nativedisplay/libnativedisplay.map.txt
index 3b29c18..3b6a241 100644
--- a/libs/nativedisplay/libnativedisplay.map.txt
+++ b/libs/nativedisplay/libnativedisplay.map.txt
@@ -5,6 +5,8 @@
AChoreographer_postFrameCallbackDelayed; # apex # introduced=30
AChoreographer_postFrameCallback64; # apex # introduced=30
AChoreographer_postFrameCallbackDelayed64; # apex # introduced=30
+ AChoreographer_registerRefreshRateCallback; # apex # introduced=30
+ AChoreographer_unregisterRefreshRateCallback; # apex # introduced=30
local:
*;
};