Jank callback API refactor.
Removes the old work-arounds for missing jank callbacks.
Removes the jank data from the transaction completed callback.
Adds new function to ISurfaceComposer to register jank listeners.
With the new API, jank data is only sent over binder periodically
(every ~50 frames) and on a background thread. It is also only tracked
for layers where there is a listener registered.
Test: manual, libsurfaceflinger_unittest
Bug: http://b/336461947
Flag: EXEMPT refactor
Change-Id: I3238ce604571832523525cf098832c7352879826
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index af91bb3..e2ac1d5 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -86,7 +86,8 @@
return (((int64_t)getpid()) << 32) | ++idCounter;
}
-void emptyCallback(nsecs_t, const sp<Fence>&, const std::vector<SurfaceControlStats>&) {}
+constexpr int64_t INVALID_VSYNC = -1;
+
} // namespace
const std::string SurfaceComposerClient::kEmpty{};
@@ -207,9 +208,168 @@
return DefaultComposerClient::getComposerClient();
}
+// ---------------------------------------------------------------------------
+
JankDataListener::~JankDataListener() {
}
+status_t JankDataListener::flushJankData() {
+ if (mLayerId == -1) {
+ return INVALID_OPERATION;
+ }
+
+ binder::Status status = ComposerServiceAIDL::getComposerService()->flushJankData(mLayerId);
+ return statusTFromBinderStatus(status);
+}
+
+std::mutex JankDataListenerFanOut::sFanoutInstanceMutex;
+std::unordered_map<int32_t, sp<JankDataListenerFanOut>> JankDataListenerFanOut::sFanoutInstances;
+
+binder::Status JankDataListenerFanOut::onJankData(const std::vector<gui::JankData>& jankData) {
+ // Find the highest VSync ID.
+ int64_t lastVsync = jankData.empty()
+ ? 0
+ : std::max_element(jankData.begin(), jankData.end(),
+ [](const gui::JankData& jd1, const gui::JankData& jd2) {
+ return jd1.frameVsyncId < jd2.frameVsyncId;
+ })
+ ->frameVsyncId;
+
+ // Fan out the jank data callback.
+ std::vector<wp<JankDataListener>> listenersToRemove;
+ for (auto listener : getActiveListeners()) {
+ if (!listener->onJankDataAvailable(jankData) ||
+ (listener->mRemoveAfter >= 0 && listener->mRemoveAfter <= lastVsync)) {
+ listenersToRemove.push_back(listener);
+ }
+ }
+
+ return removeListeners(listenersToRemove)
+ ? binder::Status::ok()
+ : binder::Status::fromExceptionCode(binder::Status::EX_NULL_POINTER);
+}
+
+status_t JankDataListenerFanOut::addListener(sp<SurfaceControl> sc, sp<JankDataListener> listener) {
+ sp<IBinder> layer = sc->getHandle();
+ if (layer == nullptr) {
+ return UNEXPECTED_NULL;
+ }
+ int32_t layerId = sc->getLayerId();
+
+ sFanoutInstanceMutex.lock();
+ auto it = sFanoutInstances.find(layerId);
+ bool registerNeeded = it == sFanoutInstances.end();
+ sp<JankDataListenerFanOut> fanout;
+ if (registerNeeded) {
+ fanout = sp<JankDataListenerFanOut>::make(layerId);
+ sFanoutInstances.insert({layerId, fanout});
+ } else {
+ fanout = it->second;
+ }
+
+ fanout->mMutex.lock();
+ fanout->mListeners.insert(listener);
+ fanout->mMutex.unlock();
+
+ sFanoutInstanceMutex.unlock();
+
+ if (registerNeeded) {
+ binder::Status status =
+ ComposerServiceAIDL::getComposerService()->addJankListener(layer, fanout);
+ return statusTFromBinderStatus(status);
+ }
+ return OK;
+}
+
+status_t JankDataListenerFanOut::removeListener(sp<JankDataListener> listener) {
+ int32_t layerId = listener->mLayerId;
+ if (layerId == -1) {
+ return INVALID_OPERATION;
+ }
+
+ int64_t removeAfter = INVALID_VSYNC;
+ sp<JankDataListenerFanOut> fanout;
+
+ sFanoutInstanceMutex.lock();
+ auto it = sFanoutInstances.find(layerId);
+ if (it != sFanoutInstances.end()) {
+ fanout = it->second;
+ removeAfter = fanout->updateAndGetRemovalVSync();
+ }
+
+ if (removeAfter != INVALID_VSYNC) {
+ // Remove this instance from the map, so that no new listeners are added
+ // while we're scheduled to be removed.
+ sFanoutInstances.erase(layerId);
+ }
+ sFanoutInstanceMutex.unlock();
+
+ if (removeAfter < 0) {
+ return OK;
+ }
+
+ binder::Status status =
+ ComposerServiceAIDL::getComposerService()->removeJankListener(layerId, fanout,
+ removeAfter);
+ return statusTFromBinderStatus(status);
+}
+
+std::vector<sp<JankDataListener>> JankDataListenerFanOut::getActiveListeners() {
+ std::scoped_lock<std::mutex> lock(mMutex);
+
+ std::vector<sp<JankDataListener>> listeners;
+ for (auto it = mListeners.begin(); it != mListeners.end();) {
+ auto listener = it->promote();
+ if (!listener) {
+ it = mListeners.erase(it);
+ } else {
+ listeners.push_back(std::move(listener));
+ it++;
+ }
+ }
+ return listeners;
+}
+
+bool JankDataListenerFanOut::removeListeners(const std::vector<wp<JankDataListener>>& listeners) {
+ std::scoped_lock<std::mutex> fanoutLock(sFanoutInstanceMutex);
+ std::scoped_lock<std::mutex> listenersLock(mMutex);
+
+ for (auto listener : listeners) {
+ mListeners.erase(listener);
+ }
+
+ if (mListeners.empty()) {
+ sFanoutInstances.erase(mLayerId);
+ return false;
+ }
+ return true;
+}
+
+int64_t JankDataListenerFanOut::updateAndGetRemovalVSync() {
+ std::scoped_lock<std::mutex> lock(mMutex);
+ if (mRemoveAfter >= 0) {
+ // We've already been scheduled to be removed. Don't schedule again.
+ return INVALID_VSYNC;
+ }
+
+ int64_t removeAfter = 0;
+ for (auto it = mListeners.begin(); it != mListeners.end();) {
+ auto listener = it->promote();
+ if (!listener) {
+ it = mListeners.erase(it);
+ } else if (listener->mRemoveAfter < 0) {
+ // We have at least one listener that's still interested. Don't remove.
+ return INVALID_VSYNC;
+ } else {
+ removeAfter = std::max(removeAfter, listener->mRemoveAfter);
+ it++;
+ }
+ }
+
+ mRemoveAfter = removeAfter;
+ return removeAfter;
+}
+
// ---------------------------------------------------------------------------
// TransactionCompletedListener does not use ANDROID_SINGLETON_STATIC_INSTANCE because it needs
@@ -256,14 +416,6 @@
surfaceControls,
CallbackId::Type callbackType) {
std::lock_guard<std::mutex> lock(mMutex);
- return addCallbackFunctionLocked(callbackFunction, surfaceControls, callbackType);
-}
-
-CallbackId TransactionCompletedListener::addCallbackFunctionLocked(
- const TransactionCompletedCallback& callbackFunction,
- const std::unordered_set<sp<SurfaceControl>, SurfaceComposerClient::SCHash>&
- surfaceControls,
- CallbackId::Type callbackType) {
startListeningLocked();
CallbackId callbackId(getNextIdLocked(), callbackType);
@@ -272,33 +424,11 @@
for (const auto& surfaceControl : surfaceControls) {
callbackSurfaceControls[surfaceControl->getHandle()] = surfaceControl;
-
- if (callbackType == CallbackId::Type::ON_COMPLETE &&
- mJankListeners.count(surfaceControl->getLayerId()) != 0) {
- callbackId.includeJankData = true;
- }
}
return callbackId;
}
-void TransactionCompletedListener::addJankListener(const sp<JankDataListener>& listener,
- sp<SurfaceControl> surfaceControl) {
- std::lock_guard<std::mutex> lock(mMutex);
- mJankListeners.insert({surfaceControl->getLayerId(), listener});
-}
-
-void TransactionCompletedListener::removeJankListener(const sp<JankDataListener>& listener) {
- std::lock_guard<std::mutex> lock(mMutex);
- for (auto it = mJankListeners.begin(); it != mJankListeners.end();) {
- if (it->second == listener) {
- it = mJankListeners.erase(it);
- } else {
- it++;
- }
- }
-}
-
void TransactionCompletedListener::setReleaseBufferCallback(const ReleaseCallbackId& callbackId,
ReleaseBufferCallback listener) {
std::scoped_lock<std::mutex> lock(mMutex);
@@ -325,32 +455,20 @@
}
void TransactionCompletedListener::addSurfaceControlToCallbacks(
- SurfaceComposerClient::CallbackInfo& callbackInfo,
- const sp<SurfaceControl>& surfaceControl) {
+ const sp<SurfaceControl>& surfaceControl,
+ const std::unordered_set<CallbackId, CallbackIdHash>& callbackIds) {
std::lock_guard<std::mutex> lock(mMutex);
- bool includingJankData = false;
- for (auto callbackId : callbackInfo.callbackIds) {
+ for (auto callbackId : callbackIds) {
mCallbacks[callbackId].surfaceControls.emplace(std::piecewise_construct,
std::forward_as_tuple(
surfaceControl->getHandle()),
std::forward_as_tuple(surfaceControl));
- includingJankData = includingJankData || callbackId.includeJankData;
- }
-
- // If no registered callback is requesting jank data, but there is a jank listener registered
- // on the new surface control, add a synthetic callback that requests the jank data.
- if (!includingJankData && mJankListeners.count(surfaceControl->getLayerId()) != 0) {
- CallbackId callbackId =
- addCallbackFunctionLocked(&emptyCallback, callbackInfo.surfaceControls,
- CallbackId::Type::ON_COMPLETE);
- callbackInfo.callbackIds.emplace(callbackId);
}
}
void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) {
std::unordered_map<CallbackId, CallbackTranslation, CallbackIdHash> callbacksMap;
- std::multimap<int32_t, sp<JankDataListener>> jankListenersMap;
{
std::lock_guard<std::mutex> lock(mMutex);
@@ -366,7 +484,6 @@
* sp<SurfaceControl> that could possibly exist for the callbacks.
*/
callbacksMap = mCallbacks;
- jankListenersMap = mJankListeners;
for (const auto& transactionStats : listenerStats.transactionStats) {
for (auto& callbackId : transactionStats.callbackIds) {
mCallbacks.erase(callbackId);
@@ -486,12 +603,6 @@
transactionStats.presentFence, surfaceStats);
}
}
-
- if (surfaceStats.jankData.empty()) continue;
- auto jankRange = jankListenersMap.equal_range(layerId);
- for (auto it = jankRange.first; it != jankRange.second; it++) {
- it->second->onJankDataAvailable(surfaceStats.jankData);
- }
}
}
}
@@ -1004,8 +1115,9 @@
// register all surface controls for all callbackIds for this listener that is merging
for (const auto& surfaceControl : currentProcessCallbackInfo.surfaceControls) {
- mTransactionCompletedListener->addSurfaceControlToCallbacks(currentProcessCallbackInfo,
- surfaceControl);
+ mTransactionCompletedListener
+ ->addSurfaceControlToCallbacks(surfaceControl,
+ currentProcessCallbackInfo.callbackIds);
}
}
@@ -1361,7 +1473,7 @@
auto& callbackInfo = mListenerCallbacks[TransactionCompletedListener::getIInstance()];
callbackInfo.surfaceControls.insert(sc);
- mTransactionCompletedListener->addSurfaceControlToCallbacks(callbackInfo, sc);
+ mTransactionCompletedListener->addSurfaceControlToCallbacks(sc, callbackInfo.callbackIds);
}
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setPosition(