Loading libs/gui/ITransactionCompletedListener.cpp +2 −46 Original line number Original line Diff line number Diff line Loading @@ -43,12 +43,6 @@ enum class Tag : uint32_t { } // Anonymous namespace } // Anonymous namespace namespace { // Anonymous constexpr int32_t kSerializedCallbackTypeOnCompelteWithJankData = 2; } // Anonymous namespace status_t FrameEventHistoryStats::writeToParcel(Parcel* output) const { status_t FrameEventHistoryStats::writeToParcel(Parcel* output) const { status_t err = output->writeUint64(frameNumber); status_t err = output->writeUint64(frameNumber); if (err != NO_ERROR) return err; if (err != NO_ERROR) return err; Loading Loading @@ -119,23 +113,6 @@ status_t FrameEventHistoryStats::readFromParcel(const Parcel* input) { return err; return err; } } JankData::JankData() : frameVsyncId(FrameTimelineInfo::INVALID_VSYNC_ID), jankType(JankType::None) {} status_t JankData::writeToParcel(Parcel* output) const { SAFE_PARCEL(output->writeInt64, frameVsyncId); SAFE_PARCEL(output->writeInt32, jankType); SAFE_PARCEL(output->writeInt64, frameIntervalNs); return NO_ERROR; } status_t JankData::readFromParcel(const Parcel* input) { SAFE_PARCEL(input->readInt64, &frameVsyncId); SAFE_PARCEL(input->readInt32, &jankType); SAFE_PARCEL(input->readInt64, &frameIntervalNs); return NO_ERROR; } status_t SurfaceStats::writeToParcel(Parcel* output) const { status_t SurfaceStats::writeToParcel(Parcel* output) const { SAFE_PARCEL(output->writeStrongBinder, surfaceControl); SAFE_PARCEL(output->writeStrongBinder, surfaceControl); if (const auto* acquireFence = std::get_if<sp<Fence>>(&acquireTimeOrFence)) { if (const auto* acquireFence = std::get_if<sp<Fence>>(&acquireTimeOrFence)) { Loading @@ -160,10 +137,6 @@ status_t SurfaceStats::writeToParcel(Parcel* output) const { SAFE_PARCEL(output->writeUint32, currentMaxAcquiredBufferCount); SAFE_PARCEL(output->writeUint32, currentMaxAcquiredBufferCount); SAFE_PARCEL(output->writeParcelable, eventStats); SAFE_PARCEL(output->writeParcelable, eventStats); SAFE_PARCEL(output->writeInt32, static_cast<int32_t>(jankData.size())); for (const auto& data : jankData) { SAFE_PARCEL(output->writeParcelable, data); } SAFE_PARCEL(output->writeParcelable, previousReleaseCallbackId); SAFE_PARCEL(output->writeParcelable, previousReleaseCallbackId); return NO_ERROR; return NO_ERROR; } } Loading Loading @@ -200,13 +173,6 @@ status_t SurfaceStats::readFromParcel(const Parcel* input) { SAFE_PARCEL(input->readUint32, ¤tMaxAcquiredBufferCount); SAFE_PARCEL(input->readUint32, ¤tMaxAcquiredBufferCount); SAFE_PARCEL(input->readParcelable, &eventStats); SAFE_PARCEL(input->readParcelable, &eventStats); int32_t jankData_size = 0; SAFE_PARCEL_READ_SIZE(input->readInt32, &jankData_size, input->dataSize()); for (int i = 0; i < jankData_size; i++) { JankData data; SAFE_PARCEL(input->readParcelable, &data); jankData.push_back(data); } SAFE_PARCEL(input->readParcelable, &previousReleaseCallbackId); SAFE_PARCEL(input->readParcelable, &previousReleaseCallbackId); return NO_ERROR; return NO_ERROR; } } Loading Loading @@ -371,11 +337,7 @@ ListenerCallbacks ListenerCallbacks::filter(CallbackId::Type type) const { status_t CallbackId::writeToParcel(Parcel* output) const { status_t CallbackId::writeToParcel(Parcel* output) const { SAFE_PARCEL(output->writeInt64, id); SAFE_PARCEL(output->writeInt64, id); if (type == Type::ON_COMPLETE && includeJankData) { SAFE_PARCEL(output->writeInt32, kSerializedCallbackTypeOnCompelteWithJankData); } else { SAFE_PARCEL(output->writeInt32, static_cast<int32_t>(type)); SAFE_PARCEL(output->writeInt32, static_cast<int32_t>(type)); } return NO_ERROR; return NO_ERROR; } } Loading @@ -383,13 +345,7 @@ status_t CallbackId::readFromParcel(const Parcel* input) { SAFE_PARCEL(input->readInt64, &id); SAFE_PARCEL(input->readInt64, &id); int32_t typeAsInt; int32_t typeAsInt; SAFE_PARCEL(input->readInt32, &typeAsInt); SAFE_PARCEL(input->readInt32, &typeAsInt); if (typeAsInt == kSerializedCallbackTypeOnCompelteWithJankData) { type = Type::ON_COMPLETE; includeJankData = true; } else { type = static_cast<CallbackId::Type>(typeAsInt); type = static_cast<CallbackId::Type>(typeAsInt); includeJankData = false; } return NO_ERROR; return NO_ERROR; } } Loading libs/gui/SurfaceComposerClient.cpp +168 −56 Original line number Original line Diff line number Diff line Loading @@ -86,7 +86,8 @@ int64_t generateId() { return (((int64_t)getpid()) << 32) | ++idCounter; return (((int64_t)getpid()) << 32) | ++idCounter; } } void emptyCallback(nsecs_t, const sp<Fence>&, const std::vector<SurfaceControlStats>&) {} constexpr int64_t INVALID_VSYNC = -1; } // namespace } // namespace const std::string SurfaceComposerClient::kEmpty{}; const std::string SurfaceComposerClient::kEmpty{}; Loading Loading @@ -207,9 +208,168 @@ sp<SurfaceComposerClient> SurfaceComposerClient::getDefault() { return DefaultComposerClient::getComposerClient(); return DefaultComposerClient::getComposerClient(); } } // --------------------------------------------------------------------------- JankDataListener::~JankDataListener() { 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 // TransactionCompletedListener does not use ANDROID_SINGLETON_STATIC_INSTANCE because it needs Loading Loading @@ -256,14 +416,6 @@ CallbackId TransactionCompletedListener::addCallbackFunction( surfaceControls, surfaceControls, CallbackId::Type callbackType) { CallbackId::Type callbackType) { std::lock_guard<std::mutex> lock(mMutex); 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(); startListeningLocked(); CallbackId callbackId(getNextIdLocked(), callbackType); CallbackId callbackId(getNextIdLocked(), callbackType); Loading @@ -272,33 +424,11 @@ CallbackId TransactionCompletedListener::addCallbackFunctionLocked( for (const auto& surfaceControl : surfaceControls) { for (const auto& surfaceControl : surfaceControls) { callbackSurfaceControls[surfaceControl->getHandle()] = surfaceControl; callbackSurfaceControls[surfaceControl->getHandle()] = surfaceControl; if (callbackType == CallbackId::Type::ON_COMPLETE && mJankListeners.count(surfaceControl->getLayerId()) != 0) { callbackId.includeJankData = true; } } } return callbackId; 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, void TransactionCompletedListener::setReleaseBufferCallback(const ReleaseCallbackId& callbackId, ReleaseBufferCallback listener) { ReleaseBufferCallback listener) { std::scoped_lock<std::mutex> lock(mMutex); std::scoped_lock<std::mutex> lock(mMutex); Loading @@ -325,32 +455,20 @@ void TransactionCompletedListener::removeSurfaceStatsListener(void* context, voi } } void TransactionCompletedListener::addSurfaceControlToCallbacks( 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); std::lock_guard<std::mutex> lock(mMutex); bool includingJankData = false; for (auto callbackId : callbackIds) { for (auto callbackId : callbackInfo.callbackIds) { mCallbacks[callbackId].surfaceControls.emplace(std::piecewise_construct, mCallbacks[callbackId].surfaceControls.emplace(std::piecewise_construct, std::forward_as_tuple( std::forward_as_tuple( surfaceControl->getHandle()), surfaceControl->getHandle()), std::forward_as_tuple(surfaceControl)); 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) { void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) { std::unordered_map<CallbackId, CallbackTranslation, CallbackIdHash> callbacksMap; std::unordered_map<CallbackId, CallbackTranslation, CallbackIdHash> callbacksMap; std::multimap<int32_t, sp<JankDataListener>> jankListenersMap; { { std::lock_guard<std::mutex> lock(mMutex); std::lock_guard<std::mutex> lock(mMutex); Loading @@ -366,7 +484,6 @@ void TransactionCompletedListener::onTransactionCompleted(ListenerStats listener * sp<SurfaceControl> that could possibly exist for the callbacks. * sp<SurfaceControl> that could possibly exist for the callbacks. */ */ callbacksMap = mCallbacks; callbacksMap = mCallbacks; jankListenersMap = mJankListeners; for (const auto& transactionStats : listenerStats.transactionStats) { for (const auto& transactionStats : listenerStats.transactionStats) { for (auto& callbackId : transactionStats.callbackIds) { for (auto& callbackId : transactionStats.callbackIds) { mCallbacks.erase(callbackId); mCallbacks.erase(callbackId); Loading Loading @@ -486,12 +603,6 @@ void TransactionCompletedListener::onTransactionCompleted(ListenerStats listener transactionStats.presentFence, surfaceStats); 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); } } } } } } } Loading Loading @@ -1004,8 +1115,9 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Tr // register all surface controls for all callbackIds for this listener that is merging // register all surface controls for all callbackIds for this listener that is merging for (const auto& surfaceControl : currentProcessCallbackInfo.surfaceControls) { for (const auto& surfaceControl : currentProcessCallbackInfo.surfaceControls) { mTransactionCompletedListener->addSurfaceControlToCallbacks(currentProcessCallbackInfo, mTransactionCompletedListener surfaceControl); ->addSurfaceControlToCallbacks(surfaceControl, currentProcessCallbackInfo.callbackIds); } } } } Loading Loading @@ -1362,7 +1474,7 @@ void SurfaceComposerClient::Transaction::registerSurfaceControlForCallback( auto& callbackInfo = mListenerCallbacks[TransactionCompletedListener::getIInstance()]; auto& callbackInfo = mListenerCallbacks[TransactionCompletedListener::getIInstance()]; callbackInfo.surfaceControls.insert(sc); callbackInfo.surfaceControls.insert(sc); mTransactionCompletedListener->addSurfaceControlToCallbacks(callbackInfo, sc); mTransactionCompletedListener->addSurfaceControlToCallbacks(sc, callbackInfo.callbackIds); } } SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setPosition( SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setPosition( Loading libs/gui/aidl/android/gui/IJankListener.aidl 0 → 100644 +29 −0 Original line number Original line Diff line number Diff line /* * Copyright 2024 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. */ package android.gui; import android.gui.JankData; /** @hide */ interface IJankListener { /** * Callback reporting jank data of the most recent frames. * @See {@link ISurfaceComposer#addJankListener(IBinder, IJankListener)} */ void onJankData(in JankData[] data); } libs/gui/aidl/android/gui/ISurfaceComposer.aidl +19 −0 Original line number Original line Diff line number Diff line Loading @@ -42,6 +42,7 @@ import android.gui.ISurfaceComposerClient; import android.gui.ITunnelModeEnabledListener; import android.gui.ITunnelModeEnabledListener; import android.gui.IWindowInfosListener; import android.gui.IWindowInfosListener; import android.gui.IWindowInfosPublisher; import android.gui.IWindowInfosPublisher; import android.gui.IJankListener; import android.gui.LayerCaptureArgs; import android.gui.LayerCaptureArgs; import android.gui.OverlayProperties; import android.gui.OverlayProperties; import android.gui.PullAtomData; import android.gui.PullAtomData; Loading Loading @@ -580,4 +581,22 @@ interface ISurfaceComposer { * This method should not block the ShutdownThread therefore it's handled asynchronously. * This method should not block the ShutdownThread therefore it's handled asynchronously. */ */ oneway void notifyShutdown(); oneway void notifyShutdown(); /** * Registers the jank listener on the given layer to receive jank data of future frames. */ void addJankListener(IBinder layer, IJankListener listener); /** * Flushes any pending jank data on the given layer to any registered listeners on that layer. */ oneway void flushJankData(int layerId); /** * Schedules the removal of the jank listener from the given layer after the VSync with the * specified ID. Use a value <= 0 for afterVsync to remove the listener immediately. The given * listener will not be removed before the given VSync, but may still receive data for frames * past the provided VSync. */ oneway void removeJankListener(int layerId, IJankListener listener, long afterVsync); } } libs/gui/aidl/android/gui/JankData.aidl 0 → 100644 +35 −0 Original line number Original line Diff line number Diff line /* * Copyright 2024 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. */ package android.gui; /** @hide */ parcelable JankData { /** * Identifier for the frame submitted with Transaction.setFrameTimelineVsyncId */ long frameVsyncId; /** * Bitmask of jank types that occurred. */ int jankType; /** * Expected duration in nanoseconds of this frame. */ long frameIntervalNs; } Loading
libs/gui/ITransactionCompletedListener.cpp +2 −46 Original line number Original line Diff line number Diff line Loading @@ -43,12 +43,6 @@ enum class Tag : uint32_t { } // Anonymous namespace } // Anonymous namespace namespace { // Anonymous constexpr int32_t kSerializedCallbackTypeOnCompelteWithJankData = 2; } // Anonymous namespace status_t FrameEventHistoryStats::writeToParcel(Parcel* output) const { status_t FrameEventHistoryStats::writeToParcel(Parcel* output) const { status_t err = output->writeUint64(frameNumber); status_t err = output->writeUint64(frameNumber); if (err != NO_ERROR) return err; if (err != NO_ERROR) return err; Loading Loading @@ -119,23 +113,6 @@ status_t FrameEventHistoryStats::readFromParcel(const Parcel* input) { return err; return err; } } JankData::JankData() : frameVsyncId(FrameTimelineInfo::INVALID_VSYNC_ID), jankType(JankType::None) {} status_t JankData::writeToParcel(Parcel* output) const { SAFE_PARCEL(output->writeInt64, frameVsyncId); SAFE_PARCEL(output->writeInt32, jankType); SAFE_PARCEL(output->writeInt64, frameIntervalNs); return NO_ERROR; } status_t JankData::readFromParcel(const Parcel* input) { SAFE_PARCEL(input->readInt64, &frameVsyncId); SAFE_PARCEL(input->readInt32, &jankType); SAFE_PARCEL(input->readInt64, &frameIntervalNs); return NO_ERROR; } status_t SurfaceStats::writeToParcel(Parcel* output) const { status_t SurfaceStats::writeToParcel(Parcel* output) const { SAFE_PARCEL(output->writeStrongBinder, surfaceControl); SAFE_PARCEL(output->writeStrongBinder, surfaceControl); if (const auto* acquireFence = std::get_if<sp<Fence>>(&acquireTimeOrFence)) { if (const auto* acquireFence = std::get_if<sp<Fence>>(&acquireTimeOrFence)) { Loading @@ -160,10 +137,6 @@ status_t SurfaceStats::writeToParcel(Parcel* output) const { SAFE_PARCEL(output->writeUint32, currentMaxAcquiredBufferCount); SAFE_PARCEL(output->writeUint32, currentMaxAcquiredBufferCount); SAFE_PARCEL(output->writeParcelable, eventStats); SAFE_PARCEL(output->writeParcelable, eventStats); SAFE_PARCEL(output->writeInt32, static_cast<int32_t>(jankData.size())); for (const auto& data : jankData) { SAFE_PARCEL(output->writeParcelable, data); } SAFE_PARCEL(output->writeParcelable, previousReleaseCallbackId); SAFE_PARCEL(output->writeParcelable, previousReleaseCallbackId); return NO_ERROR; return NO_ERROR; } } Loading Loading @@ -200,13 +173,6 @@ status_t SurfaceStats::readFromParcel(const Parcel* input) { SAFE_PARCEL(input->readUint32, ¤tMaxAcquiredBufferCount); SAFE_PARCEL(input->readUint32, ¤tMaxAcquiredBufferCount); SAFE_PARCEL(input->readParcelable, &eventStats); SAFE_PARCEL(input->readParcelable, &eventStats); int32_t jankData_size = 0; SAFE_PARCEL_READ_SIZE(input->readInt32, &jankData_size, input->dataSize()); for (int i = 0; i < jankData_size; i++) { JankData data; SAFE_PARCEL(input->readParcelable, &data); jankData.push_back(data); } SAFE_PARCEL(input->readParcelable, &previousReleaseCallbackId); SAFE_PARCEL(input->readParcelable, &previousReleaseCallbackId); return NO_ERROR; return NO_ERROR; } } Loading Loading @@ -371,11 +337,7 @@ ListenerCallbacks ListenerCallbacks::filter(CallbackId::Type type) const { status_t CallbackId::writeToParcel(Parcel* output) const { status_t CallbackId::writeToParcel(Parcel* output) const { SAFE_PARCEL(output->writeInt64, id); SAFE_PARCEL(output->writeInt64, id); if (type == Type::ON_COMPLETE && includeJankData) { SAFE_PARCEL(output->writeInt32, kSerializedCallbackTypeOnCompelteWithJankData); } else { SAFE_PARCEL(output->writeInt32, static_cast<int32_t>(type)); SAFE_PARCEL(output->writeInt32, static_cast<int32_t>(type)); } return NO_ERROR; return NO_ERROR; } } Loading @@ -383,13 +345,7 @@ status_t CallbackId::readFromParcel(const Parcel* input) { SAFE_PARCEL(input->readInt64, &id); SAFE_PARCEL(input->readInt64, &id); int32_t typeAsInt; int32_t typeAsInt; SAFE_PARCEL(input->readInt32, &typeAsInt); SAFE_PARCEL(input->readInt32, &typeAsInt); if (typeAsInt == kSerializedCallbackTypeOnCompelteWithJankData) { type = Type::ON_COMPLETE; includeJankData = true; } else { type = static_cast<CallbackId::Type>(typeAsInt); type = static_cast<CallbackId::Type>(typeAsInt); includeJankData = false; } return NO_ERROR; return NO_ERROR; } } Loading
libs/gui/SurfaceComposerClient.cpp +168 −56 Original line number Original line Diff line number Diff line Loading @@ -86,7 +86,8 @@ int64_t generateId() { return (((int64_t)getpid()) << 32) | ++idCounter; return (((int64_t)getpid()) << 32) | ++idCounter; } } void emptyCallback(nsecs_t, const sp<Fence>&, const std::vector<SurfaceControlStats>&) {} constexpr int64_t INVALID_VSYNC = -1; } // namespace } // namespace const std::string SurfaceComposerClient::kEmpty{}; const std::string SurfaceComposerClient::kEmpty{}; Loading Loading @@ -207,9 +208,168 @@ sp<SurfaceComposerClient> SurfaceComposerClient::getDefault() { return DefaultComposerClient::getComposerClient(); return DefaultComposerClient::getComposerClient(); } } // --------------------------------------------------------------------------- JankDataListener::~JankDataListener() { 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 // TransactionCompletedListener does not use ANDROID_SINGLETON_STATIC_INSTANCE because it needs Loading Loading @@ -256,14 +416,6 @@ CallbackId TransactionCompletedListener::addCallbackFunction( surfaceControls, surfaceControls, CallbackId::Type callbackType) { CallbackId::Type callbackType) { std::lock_guard<std::mutex> lock(mMutex); 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(); startListeningLocked(); CallbackId callbackId(getNextIdLocked(), callbackType); CallbackId callbackId(getNextIdLocked(), callbackType); Loading @@ -272,33 +424,11 @@ CallbackId TransactionCompletedListener::addCallbackFunctionLocked( for (const auto& surfaceControl : surfaceControls) { for (const auto& surfaceControl : surfaceControls) { callbackSurfaceControls[surfaceControl->getHandle()] = surfaceControl; callbackSurfaceControls[surfaceControl->getHandle()] = surfaceControl; if (callbackType == CallbackId::Type::ON_COMPLETE && mJankListeners.count(surfaceControl->getLayerId()) != 0) { callbackId.includeJankData = true; } } } return callbackId; 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, void TransactionCompletedListener::setReleaseBufferCallback(const ReleaseCallbackId& callbackId, ReleaseBufferCallback listener) { ReleaseBufferCallback listener) { std::scoped_lock<std::mutex> lock(mMutex); std::scoped_lock<std::mutex> lock(mMutex); Loading @@ -325,32 +455,20 @@ void TransactionCompletedListener::removeSurfaceStatsListener(void* context, voi } } void TransactionCompletedListener::addSurfaceControlToCallbacks( 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); std::lock_guard<std::mutex> lock(mMutex); bool includingJankData = false; for (auto callbackId : callbackIds) { for (auto callbackId : callbackInfo.callbackIds) { mCallbacks[callbackId].surfaceControls.emplace(std::piecewise_construct, mCallbacks[callbackId].surfaceControls.emplace(std::piecewise_construct, std::forward_as_tuple( std::forward_as_tuple( surfaceControl->getHandle()), surfaceControl->getHandle()), std::forward_as_tuple(surfaceControl)); 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) { void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) { std::unordered_map<CallbackId, CallbackTranslation, CallbackIdHash> callbacksMap; std::unordered_map<CallbackId, CallbackTranslation, CallbackIdHash> callbacksMap; std::multimap<int32_t, sp<JankDataListener>> jankListenersMap; { { std::lock_guard<std::mutex> lock(mMutex); std::lock_guard<std::mutex> lock(mMutex); Loading @@ -366,7 +484,6 @@ void TransactionCompletedListener::onTransactionCompleted(ListenerStats listener * sp<SurfaceControl> that could possibly exist for the callbacks. * sp<SurfaceControl> that could possibly exist for the callbacks. */ */ callbacksMap = mCallbacks; callbacksMap = mCallbacks; jankListenersMap = mJankListeners; for (const auto& transactionStats : listenerStats.transactionStats) { for (const auto& transactionStats : listenerStats.transactionStats) { for (auto& callbackId : transactionStats.callbackIds) { for (auto& callbackId : transactionStats.callbackIds) { mCallbacks.erase(callbackId); mCallbacks.erase(callbackId); Loading Loading @@ -486,12 +603,6 @@ void TransactionCompletedListener::onTransactionCompleted(ListenerStats listener transactionStats.presentFence, surfaceStats); 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); } } } } } } } Loading Loading @@ -1004,8 +1115,9 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Tr // register all surface controls for all callbackIds for this listener that is merging // register all surface controls for all callbackIds for this listener that is merging for (const auto& surfaceControl : currentProcessCallbackInfo.surfaceControls) { for (const auto& surfaceControl : currentProcessCallbackInfo.surfaceControls) { mTransactionCompletedListener->addSurfaceControlToCallbacks(currentProcessCallbackInfo, mTransactionCompletedListener surfaceControl); ->addSurfaceControlToCallbacks(surfaceControl, currentProcessCallbackInfo.callbackIds); } } } } Loading Loading @@ -1362,7 +1474,7 @@ void SurfaceComposerClient::Transaction::registerSurfaceControlForCallback( auto& callbackInfo = mListenerCallbacks[TransactionCompletedListener::getIInstance()]; auto& callbackInfo = mListenerCallbacks[TransactionCompletedListener::getIInstance()]; callbackInfo.surfaceControls.insert(sc); callbackInfo.surfaceControls.insert(sc); mTransactionCompletedListener->addSurfaceControlToCallbacks(callbackInfo, sc); mTransactionCompletedListener->addSurfaceControlToCallbacks(sc, callbackInfo.callbackIds); } } SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setPosition( SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setPosition( Loading
libs/gui/aidl/android/gui/IJankListener.aidl 0 → 100644 +29 −0 Original line number Original line Diff line number Diff line /* * Copyright 2024 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. */ package android.gui; import android.gui.JankData; /** @hide */ interface IJankListener { /** * Callback reporting jank data of the most recent frames. * @See {@link ISurfaceComposer#addJankListener(IBinder, IJankListener)} */ void onJankData(in JankData[] data); }
libs/gui/aidl/android/gui/ISurfaceComposer.aidl +19 −0 Original line number Original line Diff line number Diff line Loading @@ -42,6 +42,7 @@ import android.gui.ISurfaceComposerClient; import android.gui.ITunnelModeEnabledListener; import android.gui.ITunnelModeEnabledListener; import android.gui.IWindowInfosListener; import android.gui.IWindowInfosListener; import android.gui.IWindowInfosPublisher; import android.gui.IWindowInfosPublisher; import android.gui.IJankListener; import android.gui.LayerCaptureArgs; import android.gui.LayerCaptureArgs; import android.gui.OverlayProperties; import android.gui.OverlayProperties; import android.gui.PullAtomData; import android.gui.PullAtomData; Loading Loading @@ -580,4 +581,22 @@ interface ISurfaceComposer { * This method should not block the ShutdownThread therefore it's handled asynchronously. * This method should not block the ShutdownThread therefore it's handled asynchronously. */ */ oneway void notifyShutdown(); oneway void notifyShutdown(); /** * Registers the jank listener on the given layer to receive jank data of future frames. */ void addJankListener(IBinder layer, IJankListener listener); /** * Flushes any pending jank data on the given layer to any registered listeners on that layer. */ oneway void flushJankData(int layerId); /** * Schedules the removal of the jank listener from the given layer after the VSync with the * specified ID. Use a value <= 0 for afterVsync to remove the listener immediately. The given * listener will not be removed before the given VSync, but may still receive data for frames * past the provided VSync. */ oneway void removeJankListener(int layerId, IJankListener listener, long afterVsync); } }
libs/gui/aidl/android/gui/JankData.aidl 0 → 100644 +35 −0 Original line number Original line Diff line number Diff line /* * Copyright 2024 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. */ package android.gui; /** @hide */ parcelable JankData { /** * Identifier for the frame submitted with Transaction.setFrameTimelineVsyncId */ long frameVsyncId; /** * Bitmask of jank types that occurred. */ int jankType; /** * Expected duration in nanoseconds of this frame. */ long frameIntervalNs; }