Add Shared timeline jank classification listener (1/2)
Adds the ability to register a listener that gets informed about
SF' jank classifications via the TransactionCompleted interface
Bug: 17475548
Test: FrameTimelineTest
Test: Register listener, ensure data flows back
Change-Id: Ie42c508da605c03569eadab6ab18b7315b35d247
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index 69f7894..2dacae1 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -17,6 +17,8 @@
#define LOG_TAG "ITransactionCompletedListener"
//#define LOG_NDEBUG 0
+#include <gui/LayerState.h>
+#include <gui/ISurfaceComposer.h>
#include <gui/ITransactionCompletedListener.h>
namespace android {
@@ -90,61 +92,63 @@
return err;
}
-status_t SurfaceStats::writeToParcel(Parcel* output) const {
- status_t err = output->writeStrongBinder(surfaceControl);
- if (err != NO_ERROR) {
- return err;
- }
- err = output->writeInt64(acquireTime);
- if (err != NO_ERROR) {
- return err;
- }
- if (previousReleaseFence) {
- err = output->writeBool(true);
- if (err != NO_ERROR) {
- return err;
- }
- err = output->write(*previousReleaseFence);
- } else {
- err = output->writeBool(false);
- }
- err = output->writeUint32(transformHint);
- if (err != NO_ERROR) {
- return err;
- }
+JankData::JankData() :
+ frameVsyncId(ISurfaceComposer::INVALID_VSYNC_ID),
+ jankType(JankType::None) {
+}
- err = output->writeParcelable(eventStats);
- return err;
+status_t JankData::writeToParcel(Parcel* output) const {
+ SAFE_PARCEL(output->writeInt64, frameVsyncId);
+ SAFE_PARCEL(output->writeInt32, static_cast<int32_t>(jankType));
+ return NO_ERROR;
+}
+
+status_t JankData::readFromParcel(const Parcel* input) {
+ SAFE_PARCEL(input->readInt64, &frameVsyncId);
+ int32_t jankTypeInt;
+ SAFE_PARCEL(input->readInt32, &jankTypeInt);
+ jankType = static_cast<JankType>(jankTypeInt);
+ return NO_ERROR;
+}
+
+status_t SurfaceStats::writeToParcel(Parcel* output) const {
+ SAFE_PARCEL(output->writeStrongBinder, surfaceControl);
+ SAFE_PARCEL(output->writeInt64, acquireTime);
+ if (previousReleaseFence) {
+ SAFE_PARCEL(output->writeBool, true);
+ SAFE_PARCEL(output->write, *previousReleaseFence);
+ } else {
+ SAFE_PARCEL(output->writeBool, false);
+ }
+ SAFE_PARCEL(output->writeUint32, transformHint);
+ 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);
+ }
+ return NO_ERROR;
}
status_t SurfaceStats::readFromParcel(const Parcel* input) {
- status_t err = input->readStrongBinder(&surfaceControl);
- if (err != NO_ERROR) {
- return err;
- }
- err = input->readInt64(&acquireTime);
- if (err != NO_ERROR) {
- return err;
- }
+ SAFE_PARCEL(input->readStrongBinder, &surfaceControl);
+ SAFE_PARCEL(input->readInt64, &acquireTime);
bool hasFence = false;
- err = input->readBool(&hasFence);
- if (err != NO_ERROR) {
- return err;
- }
+ SAFE_PARCEL(input->readBool, &hasFence);
if (hasFence) {
previousReleaseFence = new Fence();
- err = input->read(*previousReleaseFence);
- if (err != NO_ERROR) {
- return err;
- }
+ SAFE_PARCEL(input->read, *previousReleaseFence);
}
- err = input->readUint32(&transformHint);
- if (err != NO_ERROR) {
- return err;
- }
+ SAFE_PARCEL(input->readUint32, &transformHint);
+ SAFE_PARCEL(input->readParcelable, &eventStats);
- err = input->readParcelable(&eventStats);
- return err;
+ 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);
+ }
+ return NO_ERROR;
}
status_t TransactionStats::writeToParcel(Parcel* output) const {
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 47a08ab..9ed7d1c 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -125,6 +125,9 @@
return DefaultComposerClient::getComposerClient();
}
+JankDataListener::~JankDataListener() {
+}
+
// ---------------------------------------------------------------------------
// TransactionCompletedListener does not use ANDROID_SINGLETON_STATIC_INSTANCE because it needs
@@ -174,6 +177,23 @@
return callbackId;
}
+void TransactionCompletedListener::addJankListener(const sp<JankDataListener>& listener,
+ sp<SurfaceControl> surfaceControl) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mJankListeners.insert({surfaceControl->getHandle(), 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::addSurfaceControlToCallbacks(
const sp<SurfaceControl>& surfaceControl,
const std::unordered_set<CallbackId>& callbackIds) {
@@ -189,6 +209,7 @@
void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) {
std::unordered_map<CallbackId, CallbackTranslation> callbacksMap;
+ std::multimap<sp<IBinder>, sp<JankDataListener>> jankListenersMap;
{
std::lock_guard<std::mutex> lock(mMutex);
@@ -204,6 +225,7 @@
* 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);
@@ -236,6 +258,13 @@
callbackFunction(transactionStats.latchTime, transactionStats.presentFence,
surfaceControlStats);
}
+ for (const auto& surfaceStats : transactionStats.surfaceStats) {
+ if (surfaceStats.jankData.empty()) continue;
+ for (auto it = jankListenersMap.find(surfaceStats.surfaceControl);
+ it != jankListenersMap.end(); it++) {
+ it->second->onJankDataAvailable(surfaceStats.jankData);
+ }
+ }
}
}
diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h
index c58634b..26b3840 100644
--- a/libs/gui/include/gui/ITransactionCompletedListener.h
+++ b/libs/gui/include/gui/ITransactionCompletedListener.h
@@ -16,6 +16,8 @@
#pragma once
+#include "JankInfo.h"
+
#include <binder/IInterface.h>
#include <binder/Parcel.h>
#include <binder/Parcelable.h>
@@ -57,6 +59,27 @@
nsecs_t dequeueReadyTime;
};
+/**
+ * Jank information representing SurfaceFlinger's jank classification about frames for a specific
+ * surface.
+ */
+class JankData : public Parcelable {
+public:
+ status_t writeToParcel(Parcel* output) const override;
+ status_t readFromParcel(const Parcel* input) override;
+
+ JankData();
+ JankData(int64_t frameVsyncId, JankType jankType)
+ : frameVsyncId(frameVsyncId),
+ jankType(jankType) {}
+
+ // Identifier for the frame submitted with Transaction.setFrameTimelineVsyncId
+ int64_t frameVsyncId;
+
+ // The type of jank occurred
+ JankType jankType;
+};
+
class SurfaceStats : public Parcelable {
public:
status_t writeToParcel(Parcel* output) const override;
@@ -64,18 +87,21 @@
SurfaceStats() = default;
SurfaceStats(const sp<IBinder>& sc, nsecs_t time, const sp<Fence>& prevReleaseFence,
- uint32_t hint, FrameEventHistoryStats frameEventStats)
+ uint32_t hint, FrameEventHistoryStats frameEventStats,
+ std::vector<JankData> jankData)
: surfaceControl(sc),
acquireTime(time),
previousReleaseFence(prevReleaseFence),
transformHint(hint),
- eventStats(frameEventStats) {}
+ eventStats(frameEventStats),
+ jankData(std::move(jankData)) {}
sp<IBinder> surfaceControl;
nsecs_t acquireTime = -1;
sp<Fence> previousReleaseFence;
uint32_t transformHint = 0;
FrameEventHistoryStats eventStats;
+ std::vector<JankData> jankData;
};
class TransactionStats : public Parcelable {
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 2eb97f2..f1845ee 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -619,6 +619,12 @@
// ---------------------------------------------------------------------------
+class JankDataListener : public VirtualLightRefBase {
+public:
+ virtual ~JankDataListener() = 0;
+ virtual void onJankDataAvailable(const std::vector<JankData>& jankData) = 0;
+};
+
class TransactionCompletedListener : public BnTransactionCompletedListener {
TransactionCompletedListener();
@@ -637,6 +643,7 @@
};
std::unordered_map<CallbackId, CallbackTranslation> mCallbacks GUARDED_BY(mMutex);
+ std::multimap<sp<IBinder>, sp<JankDataListener>> mJankListeners GUARDED_BY(mMutex);
public:
static sp<TransactionCompletedListener> getInstance();
@@ -652,6 +659,18 @@
void addSurfaceControlToCallbacks(const sp<SurfaceControl>& surfaceControl,
const std::unordered_set<CallbackId>& callbackIds);
+ /*
+ * Adds a jank listener to be informed about SurfaceFlinger's jank classification for a specific
+ * surface. Jank classifications arrive as part of the transaction callbacks about previous
+ * frames submitted to this Surface.
+ */
+ void addJankListener(const sp<JankDataListener>& listener, sp<SurfaceControl> surfaceControl);
+
+ /**
+ * Removes a jank listener previously added to addJankCallback.
+ */
+ void removeJankListener(const sp<JankDataListener>& listener);
+
// Overrides BnTransactionCompletedListener's onTransactionCompleted
void onTransactionCompleted(ListenerStats stats) override;
};
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index dc99986..325ecfe 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -440,7 +440,7 @@
mName, mFrameTimelineVsyncId);
surfaceFrame->setActualQueueTime(systemTime());
- mQueueItems.push_back({item, std::move(surfaceFrame)});
+ mQueueItems.push_back({item, surfaceFrame});
mQueuedFrames++;
// Wake up any pending callbacks
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index b45900e..5a6b9bc 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -129,10 +129,10 @@
Condition mQueueItemCondition;
struct BufferData {
- BufferData(BufferItem item, std::unique_ptr<frametimeline::SurfaceFrame> surfaceFrame)
- : item(item), surfaceFrame(std::move(surfaceFrame)) {}
+ BufferData(BufferItem item, std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame)
+ : item(item), surfaceFrame(surfaceFrame) {}
BufferItem item;
- std::unique_ptr<frametimeline::SurfaceFrame> surfaceFrame;
+ std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame;
};
std::vector<BufferData> mQueueItems;
std::atomic<uint64_t> mLastFrameNumberReceived{0};
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 963e541..4efff75 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -100,14 +100,30 @@
}
}
+void BufferStateLayer::onSurfaceFrameCreated(
+ const std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame) {
+ mPendingJankClassifications.emplace_back(surfaceFrame);
+}
+
void BufferStateLayer::releasePendingBuffer(nsecs_t dequeueReadyTime) {
for (const auto& handle : mDrawingState.callbackHandles) {
handle->transformHint = mTransformHint;
handle->dequeueReadyTime = dequeueReadyTime;
}
+ std::vector<JankData> jankData;
+ jankData.reserve(mPendingJankClassifications.size());
+ while (!mPendingJankClassifications.empty()
+ && mPendingJankClassifications.front()->getJankType()) {
+ std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame =
+ mPendingJankClassifications.front();
+ mPendingJankClassifications.pop_front();
+ jankData.emplace_back(
+ JankData(surfaceFrame->getToken(), surfaceFrame->getJankType().value()));
+ }
+
mFlinger->getTransactionCompletedThread().finalizePendingCallbackHandles(
- mDrawingState.callbackHandles);
+ mDrawingState.callbackHandles, jankData);
mDrawingState.callbackHandles = {};
@@ -382,7 +398,7 @@
void BufferStateLayer::forceSendCallbacks() {
mFlinger->getTransactionCompletedThread().finalizePendingCallbackHandles(
- mCurrentState.callbackHandles);
+ mCurrentState.callbackHandles, std::vector<JankData>());
}
bool BufferStateLayer::setTransparentRegionHint(const Region& transparent) {
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 42be62a..0f0b48a 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -115,6 +115,7 @@
protected:
void gatherBufferInfo() override;
uint64_t getHeadFrameNumber(nsecs_t expectedPresentTime) const;
+ void onSurfaceFrameCreated(const std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame);
private:
friend class SlotGenerationTest;
@@ -158,6 +159,8 @@
bool mReleasePreviousBuffer = false;
nsecs_t mCallbackHandleAcquireTime = -1;
+ std::deque<std::shared_ptr<android::frametimeline::SurfaceFrame>> mPendingJankClassifications;
+
// TODO(marissaw): support sticky transform for LEGACY camera mode
class HwcSlotGenerator : public ClientCache::ErasedRecipient {
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index b45c213..b09f07a 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -265,8 +265,11 @@
mJankMetadata = jankMetadata;
}
-JankType SurfaceFrame::getJankType() const {
+std::optional<JankType> SurfaceFrame::getJankType() const {
std::lock_guard<std::mutex> lock(mMutex);
+ if (mActuals.presentTime == 0) {
+ return std::nullopt;
+ }
return mJankType;
}
@@ -386,37 +389,36 @@
this->surfaceFrames.reserve(kNumSurfaceFramesInitial);
}
-std::unique_ptr<android::frametimeline::SurfaceFrame> FrameTimeline::createSurfaceFrameForToken(
+std::shared_ptr<android::frametimeline::SurfaceFrame> FrameTimeline::createSurfaceFrameForToken(
pid_t ownerPid, uid_t ownerUid, std::string layerName, std::string debugName,
std::optional<int64_t> token) {
ATRACE_CALL();
if (!token) {
- return std::make_unique<impl::SurfaceFrame>(ISurfaceComposer::INVALID_VSYNC_ID, ownerPid,
+ return std::make_shared<impl::SurfaceFrame>(ISurfaceComposer::INVALID_VSYNC_ID,ownerPid,
ownerUid, std::move(layerName),
std::move(debugName), PredictionState::None,
TimelineItem());
}
std::optional<TimelineItem> predictions = mTokenManager.getPredictionsForToken(*token);
if (predictions) {
- return std::make_unique<impl::SurfaceFrame>(*token, ownerPid, ownerUid,
+ return std::make_shared<impl::SurfaceFrame>(*token, ownerPid, ownerUid,
std::move(layerName), std::move(debugName),
PredictionState::Valid,
std::move(*predictions));
}
- return std::make_unique<impl::SurfaceFrame>(*token, ownerPid, ownerUid, std::move(layerName),
+ return std::make_shared<impl::SurfaceFrame>(*token, ownerPid, ownerUid, std::move(layerName),
std::move(debugName), PredictionState::Expired,
TimelineItem());
}
void FrameTimeline::addSurfaceFrame(
- std::unique_ptr<android::frametimeline::SurfaceFrame> surfaceFrame,
+ std::shared_ptr<android::frametimeline::SurfaceFrame> surfaceFrame,
SurfaceFrame::PresentState state) {
ATRACE_CALL();
surfaceFrame->setPresentState(state);
- std::unique_ptr<impl::SurfaceFrame> implSurfaceFrame(
- static_cast<impl::SurfaceFrame*>(surfaceFrame.release()));
std::lock_guard<std::mutex> lock(mMutex);
- mCurrentDisplayFrame->surfaceFrames.push_back(std::move(implSurfaceFrame));
+ mCurrentDisplayFrame->surfaceFrames.push_back(
+ std::static_pointer_cast<impl::SurfaceFrame>(surfaceFrame));
}
void FrameTimeline::setSfWakeUp(int64_t token, nsecs_t wakeUpTime) {
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index fe83ebf..084935b 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -120,6 +120,12 @@
virtual void setActualStartTime(nsecs_t actualStartTime) = 0;
virtual void setActualQueueTime(nsecs_t actualQueueTime) = 0;
virtual void setAcquireFenceTime(nsecs_t acquireFenceTime) = 0;
+
+ // Retrieves jank classification, if it's already been classified.
+ virtual std::optional<JankType> getJankType() const = 0;
+
+ // Token identifying the frame.
+ virtual int64_t getToken() const = 0;
};
/*
@@ -138,13 +144,13 @@
// Create a new surface frame, set the predictions based on a token and return it to the caller.
// Sets the PredictionState of SurfaceFrame.
// Debug name is the human-readable debugging string for dumpsys.
- virtual std::unique_ptr<SurfaceFrame> createSurfaceFrameForToken(
+ virtual std::shared_ptr<SurfaceFrame> createSurfaceFrameForToken(
pid_t ownerPid, uid_t ownerUid, std::string layerName, std::string debugName,
std::optional<int64_t> token) = 0;
// Adds a new SurfaceFrame to the current DisplayFrame. Frames from multiple layers can be
// composited into one display frame.
- virtual void addSurfaceFrame(std::unique_ptr<SurfaceFrame> surfaceFrame,
+ virtual void addSurfaceFrame(std::shared_ptr<SurfaceFrame> surfaceFrame,
SurfaceFrame::PresentState state) = 0;
// The first function called by SF for the current DisplayFrame. Fetches SF predictions based on
@@ -209,8 +215,8 @@
PresentState getPresentState() const override;
PredictionState getPredictionState() const override { return mPredictionState; };
pid_t getOwnerPid() const override { return mOwnerPid; };
- JankType getJankType() const;
- int64_t getToken() const { return mToken; };
+ std::optional<JankType> getJankType() const override;
+ int64_t getToken() const override { return mToken; };
nsecs_t getBaseTime() const;
uid_t getOwnerUid() const { return mOwnerUid; };
const std::string& getName() const { return mLayerName; };
@@ -258,10 +264,10 @@
~FrameTimeline() = default;
frametimeline::TokenManager* getTokenManager() override { return &mTokenManager; }
- std::unique_ptr<frametimeline::SurfaceFrame> createSurfaceFrameForToken(
+ std::shared_ptr<frametimeline::SurfaceFrame> createSurfaceFrameForToken(
pid_t ownerPid, uid_t ownerUid, std::string layerName, std::string debugName,
std::optional<int64_t> token) override;
- void addSurfaceFrame(std::unique_ptr<frametimeline::SurfaceFrame> surfaceFrame,
+ void addSurfaceFrame(std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame,
SurfaceFrame::PresentState state) override;
void setSfWakeUp(int64_t token, nsecs_t wakeupTime) override;
void setSfPresent(nsecs_t sfPresentTime,
@@ -299,7 +305,7 @@
TimelineItem surfaceFlingerActuals;
// Collection of predictions and actual values sent over by Layers
- std::vector<std::unique_ptr<SurfaceFrame>> surfaceFrames;
+ std::vector<std::shared_ptr<SurfaceFrame>> surfaceFrames;
PredictionState predictionState = PredictionState::None;
JankType jankType = JankType::None; // Enum for the type of jank
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index ab0d3df..cfaa59d 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -901,17 +901,17 @@
? std::nullopt
: std::make_optional(stateToCommit->frameTimelineVsyncId);
- auto surfaceFrame =
+ mSurfaceFrame =
mFlinger->mFrameTimeline->createSurfaceFrameForToken(getOwnerPid(), getOwnerUid(),
mName, mTransactionName,
vsyncId);
- surfaceFrame->setActualQueueTime(stateToCommit->postTime);
+ mSurfaceFrame->setActualQueueTime(stateToCommit->postTime);
// For transactions we set the acquire fence time to the post time as we
// don't have a buffer. For BufferStateLayer it is overridden in
// BufferStateLayer::applyPendingStates
- surfaceFrame->setAcquireFenceTime(stateToCommit->postTime);
+ mSurfaceFrame->setAcquireFenceTime(stateToCommit->postTime);
- mSurfaceFrame = std::move(surfaceFrame);
+ onSurfaceFrameCreated(mSurfaceFrame);
}
mCurrentState.modified = false;
@@ -1057,7 +1057,7 @@
void Layer::commitTransaction(const State& stateToCommit) {
mDrawingState = stateToCommit;
- mFlinger->mFrameTimeline->addSurfaceFrame(std::move(mSurfaceFrame), PresentState::Presented);
+ mFlinger->mFrameTimeline->addSurfaceFrame(mSurfaceFrame, PresentState::Presented);
}
uint32_t Layer::getTransactionFlags(uint32_t flags) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 75d68a1..b30793a 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -942,6 +942,7 @@
virtual void commitTransaction(const State& stateToCommit);
virtual bool applyPendingStates(State* stateToCommit);
virtual uint32_t doTransactionResize(uint32_t flags, Layer::State* stateToCommit);
+ virtual void onSurfaceFrameCreated(const std::shared_ptr<frametimeline::SurfaceFrame>&) {}
// Returns mCurrentScaling mode (originating from the
// Client) or mOverrideScalingMode mode (originating from
@@ -1066,7 +1067,7 @@
const InputWindowInfo::Type mWindowType;
// Can only be accessed with the SF state lock held.
- std::unique_ptr<frametimeline::SurfaceFrame> mSurfaceFrame;
+ std::shared_ptr<frametimeline::SurfaceFrame> mSurfaceFrame;
// The owner of the layer. If created from a non system process, it will be the calling uid.
// If created from a system process, the value can be passed in.
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 773b3ab..cb56ecc 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1986,12 +1986,14 @@
mScheduler->onDisplayRefreshed(presentTime);
- postFrame();
- postComposition();
-
+ // Set presentation information before calling postComposition, such that jank information from
+ // this' frame classification is already available when sending jank info to clients.
mFrameTimeline->setSfPresent(systemTime(),
std::make_shared<FenceTime>(mPreviousPresentFences[0]));
+ postFrame();
+ postComposition();
+
const bool prevFrameHadClientComposition = mHadClientComposition;
mHadClientComposition = std::any_of(displays.cbegin(), displays.cend(), [](const auto& pair) {
diff --git a/services/surfaceflinger/TransactionCompletedThread.cpp b/services/surfaceflinger/TransactionCompletedThread.cpp
index ca24493..1797af4 100644
--- a/services/surfaceflinger/TransactionCompletedThread.cpp
+++ b/services/surfaceflinger/TransactionCompletedThread.cpp
@@ -153,7 +153,7 @@
}
status_t TransactionCompletedThread::finalizePendingCallbackHandles(
- const std::deque<sp<CallbackHandle>>& handles) {
+ const std::deque<sp<CallbackHandle>>& handles, const std::vector<JankData>& jankData) {
if (handles.empty()) {
return NO_ERROR;
}
@@ -186,7 +186,7 @@
ALOGW("cannot find listener in mPendingTransactions");
}
- status_t err = addCallbackHandle(handle);
+ status_t err = addCallbackHandle(handle, jankData);
if (err != NO_ERROR) {
ALOGE("could not add callback handle");
return err;
@@ -204,7 +204,7 @@
return BAD_VALUE;
}
- return addCallbackHandle(handle);
+ return addCallbackHandle(handle, std::vector<JankData>());
}
status_t TransactionCompletedThread::findTransactionStats(
@@ -225,7 +225,8 @@
return BAD_VALUE;
}
-status_t TransactionCompletedThread::addCallbackHandle(const sp<CallbackHandle>& handle) {
+status_t TransactionCompletedThread::addCallbackHandle(const sp<CallbackHandle>& handle,
+ const std::vector<JankData>& jankData) {
// If we can't find the transaction stats something has gone wrong. The client should call
// startRegistration before trying to add a callback handle.
TransactionStats* transactionStats;
@@ -246,7 +247,7 @@
handle->dequeueReadyTime);
transactionStats->surfaceStats.emplace_back(surfaceControl, handle->acquireTime,
handle->previousReleaseFence,
- handle->transformHint, eventStats);
+ handle->transformHint, eventStats, jankData);
}
return NO_ERROR;
}
diff --git a/services/surfaceflinger/TransactionCompletedThread.h b/services/surfaceflinger/TransactionCompletedThread.h
index f50147a..c4ba7e4 100644
--- a/services/surfaceflinger/TransactionCompletedThread.h
+++ b/services/surfaceflinger/TransactionCompletedThread.h
@@ -73,7 +73,8 @@
// presented.
status_t registerPendingCallbackHandle(const sp<CallbackHandle>& handle);
// Notifies the TransactionCompletedThread that a pending CallbackHandle has been presented.
- status_t finalizePendingCallbackHandles(const std::deque<sp<CallbackHandle>>& handles);
+ status_t finalizePendingCallbackHandles(const std::deque<sp<CallbackHandle>>& handles,
+ const std::vector<JankData>& jankData);
// Adds the Transaction CallbackHandle from a layer that does not need to be relatched and
// presented this frame.
@@ -93,7 +94,8 @@
const std::vector<CallbackId>& callbackIds,
TransactionStats** outTransactionStats) REQUIRES(mMutex);
- status_t addCallbackHandle(const sp<CallbackHandle>& handle) REQUIRES(mMutex);
+ status_t addCallbackHandle(const sp<CallbackHandle>& handle,
+ const std::vector<JankData>& jankData) REQUIRES(mMutex);
class ThreadDeathRecipient : public IBinder::DeathRecipient {
public:
diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
index 31a5126..538b10d 100644
--- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
+++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
@@ -69,6 +69,7 @@
using Transaction = SurfaceComposerClient::Transaction;
using Attribute = V2_4::IComposerClient::Attribute;
+using Display = V2_1::Display;
///////////////////////////////////////////////
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index 411e780..43b5afe 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -205,7 +205,7 @@
// Set up the display frame
mFrameTimeline->setSfWakeUp(token1, 20);
- mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1), SurfaceFrame::PresentState::Dropped);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1, SurfaceFrame::PresentState::Dropped);
mFrameTimeline->setSfPresent(25, presentFence1);
presentFence1->signalForTest(30);
@@ -229,11 +229,11 @@
sLayerNameOne, surfaceFrameToken1);
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameTwo,
- sLayerNameTwo, surfaceFrameToken1);
+ sLayerNameTwo, surfaceFrameToken2);
mFrameTimeline->setSfWakeUp(sfToken1, 22);
- mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1),
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1,
SurfaceFrame::PresentState::Presented);
- mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame2),
+ mFrameTimeline->addSurfaceFrame(surfaceFrame2,
SurfaceFrame::PresentState::Presented);
mFrameTimeline->setSfPresent(26, presentFence1);
auto displayFrame = getDisplayFrame(0);
@@ -246,13 +246,16 @@
EXPECT_EQ(presentedSurfaceFrame1.getActuals().presentTime, 0);
EXPECT_EQ(presentedSurfaceFrame2.getActuals().presentTime, 0);
+ EXPECT_EQ(surfaceFrame1->getToken(), surfaceFrameToken1);
+ EXPECT_EQ(surfaceFrame2->getToken(), surfaceFrameToken2);
+
// Trigger a flush by finalizing the next DisplayFrame
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
auto surfaceFrame3 =
mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
sLayerNameOne, surfaceFrameToken2);
mFrameTimeline->setSfWakeUp(sfToken2, 52);
- mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame3), SurfaceFrame::PresentState::Dropped);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame3, SurfaceFrame::PresentState::Dropped);
mFrameTimeline->setSfPresent(56, presentFence2);
displayFrame = getDisplayFrame(0);
@@ -260,6 +263,8 @@
EXPECT_EQ(displayFrame->surfaceFlingerActuals.presentTime, 42);
EXPECT_EQ(presentedSurfaceFrame1.getActuals().presentTime, 42);
EXPECT_EQ(presentedSurfaceFrame2.getActuals().presentTime, 42);
+ EXPECT_NE(surfaceFrame1->getJankType(), std::nullopt);
+ EXPECT_NE(surfaceFrame2->getJankType(), std::nullopt);
}
TEST_F(FrameTimelineTest, displayFramesSlidingWindowMovesAfterLimit) {
@@ -275,7 +280,7 @@
mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
sLayerNameOne, surfaceFrameToken);
mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor);
- mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame),
+ mFrameTimeline->addSurfaceFrame(surfaceFrame,
SurfaceFrame::PresentState::Presented);
mFrameTimeline->setSfPresent(27 + frameTimeFactor, presentFence);
presentFence->signalForTest(32 + frameTimeFactor);
@@ -297,7 +302,7 @@
mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
sLayerNameOne, surfaceFrameToken);
mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor);
- mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame), SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame, SurfaceFrame::PresentState::Presented);
mFrameTimeline->setSfPresent(27 + frameTimeFactor, presentFence);
presentFence->signalForTest(32 + frameTimeFactor);
displayFrame0 = getDisplayFrame(0);
@@ -337,7 +342,7 @@
sLayerNameOne, std::nullopt);
int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
mFrameTimeline->setSfWakeUp(sfToken, 22);
- mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame),
+ mFrameTimeline->addSurfaceFrame(surfaceFrame,
SurfaceFrame::PresentState::Presented);
mFrameTimeline->setSfPresent(27, presentFence);
}
@@ -353,7 +358,7 @@
sLayerNameOne, std::nullopt);
int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
mFrameTimeline->setSfWakeUp(sfToken, 22);
- mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame),
+ mFrameTimeline->addSurfaceFrame(surfaceFrame,
SurfaceFrame::PresentState::Presented);
mFrameTimeline->setSfPresent(27, presentFence);
}
@@ -369,7 +374,7 @@
sLayerNameOne, std::nullopt);
int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
mFrameTimeline->setSfWakeUp(sfToken, 22);
- mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame),
+ mFrameTimeline->addSurfaceFrame(surfaceFrame,
SurfaceFrame::PresentState::Presented);
mFrameTimeline->setSfPresent(27, presentFence);
}
@@ -396,10 +401,10 @@
sLayerNameOne, surfaceFrameToken1);
mFrameTimeline->setSfWakeUp(sfToken1,
std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count());
- mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1),
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1,
SurfaceFrame::PresentState::Presented);
presentFence1->signalForTest(
- std::chrono::duration_cast<std::chrono::nanoseconds>(90ms).count());
+ std::chrono::duration_cast<std::chrono::nanoseconds>(70ms).count());
mFrameTimeline->setSfPresent(std::chrono::duration_cast<std::chrono::nanoseconds>(59ms).count(),
presentFence1);
@@ -423,12 +428,14 @@
sLayerNameOne, surfaceFrameToken1);
mFrameTimeline->setSfWakeUp(sfToken1,
std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count());
- mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1),
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1,
SurfaceFrame::PresentState::Presented);
presentFence1->signalForTest(
std::chrono::duration_cast<std::chrono::nanoseconds>(90ms).count());
mFrameTimeline->setSfPresent(std::chrono::duration_cast<std::chrono::nanoseconds>(59ms).count(),
presentFence1);
+ EXPECT_NE(surfaceFrame1->getJankType(), std::nullopt);
+ EXPECT_TRUE((surfaceFrame1->getJankType().value() & JankType::Display) != 0);
}
TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppMiss) {
@@ -453,12 +460,15 @@
mFrameTimeline->setSfWakeUp(sfToken1,
std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count());
- mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1),
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1,
SurfaceFrame::PresentState::Presented);
presentFence1->signalForTest(
std::chrono::duration_cast<std::chrono::nanoseconds>(90ms).count());
mFrameTimeline->setSfPresent(std::chrono::duration_cast<std::chrono::nanoseconds>(56ms).count(),
presentFence1);
+
+ EXPECT_NE(surfaceFrame1->getJankType(), std::nullopt);
+ EXPECT_TRUE((surfaceFrame1->getJankType().value() & JankType::AppDeadlineMissed) != 0);
}
/*
diff --git a/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h b/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h
index 81c32fe..6b12536 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h
@@ -31,7 +31,7 @@
MOCK_METHOD0(onBootFinished, void());
MOCK_METHOD2(addSurfaceFrame,
- void(std::unique_ptr<frametimeline::SurfaceFrame>,
+ void(std::shared_ptr<frametimeline::SurfaceFrame>,
frametimeline::SurfaceFrame::PresentState));
MOCK_METHOD2(setSfWakeUp, void(int64_t, nsecs_t));
MOCK_METHOD2(setSfPresent, void(nsecs_t, const std::shared_ptr<FenceTime>&));