summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Alec Mouri <alecmouri@google.com> 2021-01-05 12:57:36 -0800
committer Alec Mouri <alecmouri@google.com> 2021-02-23 10:31:31 -0800
commitadebf5c63121f7986b2ee609ed4b6904e5290285 (patch)
treef53553e3e66657f270a208f9555023b24a8a0e87
parentec1691271f4732807a21fd678bfddb3c7b25c3ae (diff)
Support streaming fps updates for a layer subtree to sysui via listener.
The supported flow is: * A binder listener is registered with SurfaceFlinger that is associated with a task's SurfaceControl. * Every frame, for every listener that is registered, the fps for each layer subtree is computed and reported back to each listener via onFpsUpdated. * Fps for the layer subtree is computed via FrameTimeline: the layer IDs for the subtree are gathered into a set, and FrameTimeline internally finds all DisplayFrames containing at least one layer, then obtains the present time for each of those frames, then computes the aggregated fps from those frames. Pragmatically, this should correspond with the last half second of activity for high refresh rate devices. No heuristics are used to select the "correct" layer from the layer subtree. If a game is rendering to two layers at a cadence of 30fps but offset by one 60hz vsync, then the reported fps will be 60fps because both layers will be counted on the same linear timeline. No rate limiting is applied in this patch either. The caller is able to hack in rate-limiting by immediately unregistering the listener after a FPS callback is dispatched, and then reregistered. Architecturally, it is not hard to add in rate-limiting at the SurfaceFlinger level, but that can be added in a follow-up patch since this patch is already large. Bug: 174956756 Test: libsurfaceflinger_unittest Test: E2E verification with custom listener outputting to logcat Change-Id: I792bc19cad18b6aee6c6e644bca9da40a0f15099
-rw-r--r--libs/gui/ISurfaceComposer.cpp65
-rw-r--r--libs/gui/SurfaceComposerClient.cpp9
-rw-r--r--libs/gui/aidl/android/gui/IFpsListener.aidl24
-rw-r--r--libs/gui/include/gui/ISurfaceComposer.h34
-rw-r--r--libs/gui/include/gui/SurfaceComposerClient.h3
-rw-r--r--libs/gui/tests/Surface_test.cpp5
-rw-r--r--services/surfaceflinger/Android.bp1
-rw-r--r--services/surfaceflinger/FpsReporter.cpp74
-rw-r--r--services/surfaceflinger/FpsReporter.h65
-rw-r--r--services/surfaceflinger/FrameTimeline/FrameTimeline.cpp85
-rw-r--r--services/surfaceflinger/FrameTimeline/FrameTimeline.h19
-rw-r--r--services/surfaceflinger/Layer.cpp7
-rw-r--r--services/surfaceflinger/Layer.h3
-rw-r--r--services/surfaceflinger/SurfaceFlinger.cpp30
-rw-r--r--services/surfaceflinger/SurfaceFlinger.h5
-rw-r--r--services/surfaceflinger/tests/unittests/Android.bp1
-rw-r--r--services/surfaceflinger/tests/unittests/FpsReporterTest.cpp184
-rw-r--r--services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp277
-rw-r--r--services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h1
19 files changed, 805 insertions, 87 deletions
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 2e4f858a9d..45d210f4b9 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -17,15 +17,10 @@
// tag as surfaceflinger
#define LOG_TAG "SurfaceFlinger"
-#include <stdint.h>
-#include <sys/types.h>
-
#include <android/gui/ITransactionTraceListener.h>
-
-#include <binder/Parcel.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
-
+#include <binder/Parcel.h>
#include <gui/IDisplayEventConnection.h>
#include <gui/IGraphicBufferProducer.h>
#include <gui/IRegionSamplingListener.h>
@@ -33,16 +28,15 @@
#include <gui/ISurfaceComposerClient.h>
#include <gui/LayerDebugInfo.h>
#include <gui/LayerState.h>
-
+#include <stdint.h>
+#include <sys/types.h>
#include <system/graphics.h>
-
#include <ui/DisplayMode.h>
#include <ui/DisplayStatInfo.h>
#include <ui/DisplayState.h>
#include <ui/DynamicDisplayInfo.h>
#include <ui/HdrCapabilities.h>
#include <ui/StaticDisplayInfo.h>
-
#include <utils/Log.h>
// ---------------------------------------------------------------------------
@@ -796,6 +790,33 @@ public:
return error;
}
+ virtual status_t addFpsListener(const sp<IBinder>& layerHandle,
+ const sp<gui::IFpsListener>& listener) {
+ Parcel data, reply;
+ SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor());
+ SAFE_PARCEL(data.writeStrongBinder, layerHandle);
+ SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(listener));
+ const status_t error =
+ remote()->transact(BnSurfaceComposer::ADD_FPS_LISTENER, data, &reply);
+ if (error != OK) {
+ ALOGE("addFpsListener: Failed to transact");
+ }
+ return error;
+ }
+
+ virtual status_t removeFpsListener(const sp<gui::IFpsListener>& listener) {
+ Parcel data, reply;
+ SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor());
+ SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(listener));
+
+ const status_t error =
+ remote()->transact(BnSurfaceComposer::REMOVE_FPS_LISTENER, data, &reply);
+ if (error != OK) {
+ ALOGE("removeFpsListener: Failed to transact");
+ }
+ return error;
+ }
+
status_t setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
ui::DisplayModeId defaultMode, bool allowGroupSwitching,
float primaryRefreshRateMin, float primaryRefreshRateMax,
@@ -1716,6 +1737,32 @@ status_t BnSurfaceComposer::onTransact(
}
return removeRegionSamplingListener(listener);
}
+ case ADD_FPS_LISTENER: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IBinder> layerHandle;
+ status_t result = data.readNullableStrongBinder(&layerHandle);
+ if (result != NO_ERROR) {
+ ALOGE("addFpsListener: Failed to read layer handle");
+ return result;
+ }
+ sp<gui::IFpsListener> listener;
+ result = data.readNullableStrongBinder(&listener);
+ if (result != NO_ERROR) {
+ ALOGE("addFpsListener: Failed to read listener");
+ return result;
+ }
+ return addFpsListener(layerHandle, listener);
+ }
+ case REMOVE_FPS_LISTENER: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<gui::IFpsListener> listener;
+ status_t result = data.readNullableStrongBinder(&listener);
+ if (result != NO_ERROR) {
+ ALOGE("removeFpsListener: Failed to read listener");
+ return result;
+ }
+ return removeFpsListener(listener);
+ }
case SET_DESIRED_DISPLAY_MODE_SPECS: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
sp<IBinder> displayToken = data.readStrongBinder();
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 1a643c2409..b97eeec465 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1994,6 +1994,15 @@ status_t SurfaceComposerClient::removeRegionSamplingListener(
return ComposerService::getComposerService()->removeRegionSamplingListener(listener);
}
+status_t SurfaceComposerClient::addFpsListener(const sp<IBinder>& layerHandle,
+ const sp<gui::IFpsListener>& listener) {
+ return ComposerService::getComposerService()->addFpsListener(layerHandle, listener);
+}
+
+status_t SurfaceComposerClient::removeFpsListener(const sp<gui::IFpsListener>& listener) {
+ return ComposerService::getComposerService()->removeFpsListener(listener);
+}
+
bool SurfaceComposerClient::getDisplayBrightnessSupport(const sp<IBinder>& displayToken) {
bool support = false;
ComposerService::getComposerService()->getDisplayBrightnessSupport(displayToken, &support);
diff --git a/libs/gui/aidl/android/gui/IFpsListener.aidl b/libs/gui/aidl/android/gui/IFpsListener.aidl
new file mode 100644
index 0000000000..63d333c940
--- /dev/null
+++ b/libs/gui/aidl/android/gui/IFpsListener.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2021 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 */
+oneway interface IFpsListener {
+
+ // Reports the most recent recorded fps for the tree rooted at this layer
+ void onFpsReported(float fps);
+} \ No newline at end of file
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index d0ab4802e1..9c6dfd1876 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -16,21 +16,17 @@
#pragma once
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <binder/IBinder.h>
-#include <binder/IInterface.h>
-
+#include <android/gui/IFpsListener.h>
#include <android/gui/IScreenCaptureListener.h>
#include <android/gui/ITransactionTraceListener.h>
+#include <binder/IBinder.h>
+#include <binder/IInterface.h>
#include <gui/FrameTimelineInfo.h>
#include <gui/ITransactionCompletedListener.h>
-
#include <input/Flags.h>
-
#include <math/vec4.h>
-
+#include <stdint.h>
+#include <sys/types.h>
#include <ui/ConfigStoreTypes.h>
#include <ui/DisplayId.h>
#include <ui/DisplayMode.h>
@@ -40,7 +36,6 @@
#include <ui/GraphicTypes.h>
#include <ui/PixelFormat.h>
#include <ui/Rotation.h>
-
#include <utils/Errors.h>
#include <utils/RefBase.h>
#include <utils/Timers.h>
@@ -369,6 +364,23 @@ public:
*/
virtual status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) = 0;
+ /* Registers a listener that streams fps updates from SurfaceFlinger.
+ *
+ * The listener will stream fps updates for the layer tree rooted at layerHandle. Usually, this
+ * should be tied to a task. Layers that are not descendants of that task are out of scope for
+ * FPS computations.
+ *
+ * Multiple listeners may be supported.
+ *
+ * Requires the ACCESS_SURFACE_FLINGER permission.
+ */
+ virtual status_t addFpsListener(const sp<IBinder>& layerHandle,
+ const sp<gui::IFpsListener>& listener) = 0;
+ /*
+ * Removes a listener that was streaming fps updates from SurfaceFlinger.
+ */
+ virtual status_t removeFpsListener(const sp<gui::IFpsListener>& listener) = 0;
+
/* Sets the refresh rate boundaries for the display.
*
* The primary refresh rate range represents display manager's general guidance on the display
@@ -574,6 +586,8 @@ public:
GET_GPU_CONTEXT_PRIORITY,
GET_EXTRA_BUFFER_COUNT,
GET_DYNAMIC_DISPLAY_INFO,
+ ADD_FPS_LISTENER,
+ REMOVE_FPS_LISTENER,
// Always append new enum to the end.
};
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 6f0f18c131..56b341f17e 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -595,6 +595,9 @@ public:
const sp<IBinder>& stopLayerHandle,
const sp<IRegionSamplingListener>& listener);
static status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener);
+ static status_t addFpsListener(const sp<IBinder>& layerHandle,
+ const sp<gui::IFpsListener>& listener);
+ static status_t removeFpsListener(const sp<gui::IFpsListener>& listener);
private:
virtual void onFirstRef();
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 3397198fa0..7adafdd33a 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -832,6 +832,11 @@ public:
const sp<IRegionSamplingListener>& /*listener*/) override {
return NO_ERROR;
}
+ status_t addFpsListener(const sp<IBinder>& /*layerHandle*/,
+ const sp<gui::IFpsListener>& /*listener*/) {
+ return NO_ERROR;
+ }
+ status_t removeFpsListener(const sp<gui::IFpsListener>& /*listener*/) { return NO_ERROR; }
status_t setDesiredDisplayModeSpecs(const sp<IBinder>& /*displayToken*/,
ui::DisplayModeId /*defaultMode*/,
bool /*allowGroupSwitching*/,
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 625f315b00..470059ad02 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -154,6 +154,7 @@ filegroup {
"DisplayRenderArea.cpp",
"Effects/Daltonizer.cpp",
"EventLog/EventLog.cpp",
+ "FpsReporter.cpp",
"FrameTracer/FrameTracer.cpp",
"FrameTracker.cpp",
"Layer.cpp",
diff --git a/services/surfaceflinger/FpsReporter.cpp b/services/surfaceflinger/FpsReporter.cpp
new file mode 100644
index 0000000000..c7dbf88d32
--- /dev/null
+++ b/services/surfaceflinger/FpsReporter.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2021 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "FpsReporter"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "FpsReporter.h"
+
+#include "Layer.h"
+
+namespace android {
+
+FpsReporter::FpsReporter(frametimeline::FrameTimeline& frameTimeline)
+ : mFrameTimeline(frameTimeline) {}
+
+void FpsReporter::dispatchLayerFps() const {
+ std::vector<TrackedListener> localListeners;
+ {
+ std::scoped_lock lock(mMutex);
+ if (mListeners.empty()) {
+ return;
+ }
+
+ std::transform(mListeners.begin(), mListeners.end(), std::back_inserter(localListeners),
+ [](const std::pair<wp<IBinder>, TrackedListener>& entry) {
+ return entry.second;
+ });
+ }
+
+ for (const auto& listener : localListeners) {
+ sp<Layer> promotedLayer = listener.layer.promote();
+ if (promotedLayer != nullptr) {
+ std::unordered_set<int32_t> layerIds;
+
+ promotedLayer->traverse(LayerVector::StateSet::Drawing,
+ [&](Layer* layer) { layerIds.insert(layer->getSequence()); });
+
+ listener.listener->onFpsReported(mFrameTimeline.computeFps(layerIds));
+ }
+ }
+}
+
+void FpsReporter::binderDied(const wp<IBinder>& who) {
+ std::scoped_lock lock(mMutex);
+ mListeners.erase(who);
+}
+
+void FpsReporter::addListener(const sp<gui::IFpsListener>& listener, const wp<Layer>& layer) {
+ sp<IBinder> asBinder = IInterface::asBinder(listener);
+ asBinder->linkToDeath(this);
+ std::lock_guard lock(mMutex);
+ mListeners.emplace(wp<IBinder>(asBinder), TrackedListener{listener, layer});
+}
+
+void FpsReporter::removeListener(const sp<gui::IFpsListener>& listener) {
+ std::lock_guard lock(mMutex);
+ mListeners.erase(wp<IBinder>(IInterface::asBinder(listener)));
+}
+
+} // namespace android \ No newline at end of file
diff --git a/services/surfaceflinger/FpsReporter.h b/services/surfaceflinger/FpsReporter.h
new file mode 100644
index 0000000000..d64b3dc6cf
--- /dev/null
+++ b/services/surfaceflinger/FpsReporter.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2021 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-base/thread_annotations.h>
+#include <android/gui/IFpsListener.h>
+#include <binder/IBinder.h>
+
+#include <unordered_map>
+
+#include "FrameTimeline/FrameTimeline.h"
+
+namespace android {
+
+class Layer;
+
+class FpsReporter : public IBinder::DeathRecipient {
+public:
+ FpsReporter(frametimeline::FrameTimeline& frameTimeline);
+
+ // Dispatches updated layer fps values for the registered listeners
+ // This method promotes Layer weak pointers and performs layer stack traversals, so mStateLock
+ // must be held when calling this method.
+ void dispatchLayerFps() const EXCLUDES(mMutex);
+
+ // Override for IBinder::DeathRecipient
+ void binderDied(const wp<IBinder>&) override;
+
+ // Registers an Fps listener that listens to fps updates for the provided layer
+ void addListener(const sp<gui::IFpsListener>& listener, const wp<Layer>& layer);
+ // Deregisters an Fps listener
+ void removeListener(const sp<gui::IFpsListener>& listener);
+
+private:
+ mutable std::mutex mMutex;
+ struct WpHash {
+ size_t operator()(const wp<IBinder>& p) const {
+ return std::hash<IBinder*>()(p.unsafe_get());
+ }
+ };
+
+ struct TrackedListener {
+ sp<gui::IFpsListener> listener;
+ wp<Layer> layer;
+ };
+
+ frametimeline::FrameTimeline& mFrameTimeline;
+ std::unordered_map<wp<IBinder>, TrackedListener, WpHash> mListeners GUARDED_BY(mMutex);
+};
+
+} // namespace android \ No newline at end of file
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index da04202ef2..ff000c9721 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -19,12 +19,15 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include "FrameTimeline.h"
+
#include <android-base/stringprintf.h>
#include <utils/Log.h>
#include <utils/Trace.h>
+
#include <chrono>
#include <cinttypes>
#include <numeric>
+#include <unordered_set>
namespace android::frametimeline {
@@ -277,8 +280,8 @@ int64_t TraceCookieCounter::getCookieForTracing() {
}
SurfaceFrame::SurfaceFrame(const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid,
- uid_t ownerUid, std::string layerName, std::string debugName,
- PredictionState predictionState,
+ uid_t ownerUid, int32_t layerId, std::string layerName,
+ std::string debugName, PredictionState predictionState,
frametimeline::TimelineItem&& predictions,
std::shared_ptr<TimeStats> timeStats,
JankClassificationThresholds thresholds,
@@ -289,6 +292,7 @@ SurfaceFrame::SurfaceFrame(const FrameTimelineInfo& frameTimelineInfo, pid_t own
mOwnerUid(ownerUid),
mLayerName(std::move(layerName)),
mDebugName(std::move(debugName)),
+ mLayerId(layerId),
mPresentState(PresentState::Unknown),
mPredictionState(predictionState),
mPredictions(predictions),
@@ -397,6 +401,8 @@ void SurfaceFrame::dump(std::string& result, const std::string& indent, nsecs_t
StringAppendF(&result, "Scheduled rendering rate: %d fps\n",
mRenderRate ? mRenderRate->getIntValue() : 0);
StringAppendF(&result, "%s", indent.c_str());
+ StringAppendF(&result, "Layer ID : %d\n", mLayerId);
+ StringAppendF(&result, "%s", indent.c_str());
StringAppendF(&result, "Present State : %s\n", toString(mPresentState).c_str());
StringAppendF(&result, "%s", indent.c_str());
if (mPresentState == PresentState::Dropped) {
@@ -458,7 +464,9 @@ void SurfaceFrame::onPresent(nsecs_t presentTime, int32_t displayFrameJankType,
const nsecs_t presentDelta = mActuals.presentTime - mPredictions.presentTime;
const nsecs_t deadlineDelta = mActuals.endTime - mPredictions.endTime;
- const nsecs_t deltaToVsync = std::abs(presentDelta) % refreshRate.getPeriodNsecs();
+ const nsecs_t deltaToVsync = refreshRate.getPeriodNsecs() > 0
+ ? std::abs(presentDelta) % refreshRate.getPeriodNsecs()
+ : 0;
if (deadlineDelta > mJankClassificationThresholds.deadlineThreshold) {
mFrameReadyMetadata = FrameReadyMetadata::LateFinish;
@@ -698,11 +706,11 @@ void FrameTimeline::registerDataSource() {
}
std::shared_ptr<SurfaceFrame> FrameTimeline::createSurfaceFrameForToken(
- const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid,
+ const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid, int32_t layerId,
std::string layerName, std::string debugName) {
ATRACE_CALL();
if (frameTimelineInfo.vsyncId == FrameTimelineInfo::INVALID_VSYNC_ID) {
- return std::make_shared<SurfaceFrame>(frameTimelineInfo, ownerPid, ownerUid,
+ return std::make_shared<SurfaceFrame>(frameTimelineInfo, ownerPid, ownerUid, layerId,
std::move(layerName), std::move(debugName),
PredictionState::None, TimelineItem(), mTimeStats,
mJankClassificationThresholds, &mTraceCookieCounter);
@@ -710,13 +718,13 @@ std::shared_ptr<SurfaceFrame> FrameTimeline::createSurfaceFrameForToken(
std::optional<TimelineItem> predictions =
mTokenManager.getPredictionsForToken(frameTimelineInfo.vsyncId);
if (predictions) {
- return std::make_shared<SurfaceFrame>(frameTimelineInfo, ownerPid, ownerUid,
+ return std::make_shared<SurfaceFrame>(frameTimelineInfo, ownerPid, ownerUid, layerId,
std::move(layerName), std::move(debugName),
PredictionState::Valid, std::move(*predictions),
mTimeStats, mJankClassificationThresholds,
&mTraceCookieCounter);
}
- return std::make_shared<SurfaceFrame>(frameTimelineInfo, ownerPid, ownerUid,
+ return std::make_shared<SurfaceFrame>(frameTimelineInfo, ownerPid, ownerUid, layerId,
std::move(layerName), std::move(debugName),
PredictionState::Expired, TimelineItem(), mTimeStats,
mJankClassificationThresholds, &mTraceCookieCounter);
@@ -804,7 +812,9 @@ void FrameTimeline::DisplayFrame::onPresent(nsecs_t signalTime) {
// How far off was the presentDelta when compared to the vsyncPeriod. Used in checking if there
// was a prediction error or not.
- nsecs_t deltaToVsync = std::abs(presentDelta) % mRefreshRate.getPeriodNsecs();
+ nsecs_t deltaToVsync = mRefreshRate.getPeriodNsecs() > 0
+ ? std::abs(presentDelta) % mRefreshRate.getPeriodNsecs()
+ : 0;
if (std::abs(presentDelta) > mJankClassificationThresholds.presentThreshold) {
mFramePresentMetadata = presentDelta > 0 ? FramePresentMetadata::LatePresent
: FramePresentMetadata::EarlyPresent;
@@ -968,6 +978,65 @@ void FrameTimeline::DisplayFrame::trace(pid_t surfaceFlingerPid) const {
}
}
+float FrameTimeline::computeFps(const std::unordered_set<int32_t>& layerIds) {
+ if (layerIds.empty()) {
+ return 0.0f;
+ }
+
+ std::vector<nsecs_t> presentTimes;
+ {
+ std::scoped_lock lock(mMutex);
+ presentTimes.reserve(mDisplayFrames.size());
+ for (size_t i = 0; i < mDisplayFrames.size(); i++) {
+ const auto& displayFrame = mDisplayFrames[i];
+ if (displayFrame->getActuals().presentTime <= 0) {
+ continue;
+ }
+ for (const auto& surfaceFrame : displayFrame->getSurfaceFrames()) {
+ if (surfaceFrame->getPresentState() == SurfaceFrame::PresentState::Presented &&
+ layerIds.count(surfaceFrame->getLayerId()) > 0) {
+ // We're looking for DisplayFrames that presents at least one layer from
+ // layerIds, so push the present time and skip looking through the rest of the
+ // SurfaceFrames.
+ presentTimes.push_back(displayFrame->getActuals().presentTime);
+ break;
+ }
+ }
+ }
+ }
+
+ // FPS can't be computed when there's fewer than 2 presented frames.
+ if (presentTimes.size() <= 1) {
+ return 0.0f;
+ }
+
+ nsecs_t priorPresentTime = -1;
+ nsecs_t totalPresentToPresentWalls = 0;
+
+ for (const nsecs_t presentTime : presentTimes) {
+ if (priorPresentTime == -1) {
+ priorPresentTime = presentTime;
+ continue;
+ }
+
+ totalPresentToPresentWalls += (presentTime - priorPresentTime);
+ priorPresentTime = presentTime;
+ }
+
+ if (CC_UNLIKELY(totalPresentToPresentWalls <= 0)) {
+ ALOGW("Invalid total present-to-present duration when computing fps: %" PRId64,
+ totalPresentToPresentWalls);
+ return 0.0f;
+ }
+
+ const constexpr nsecs_t kOneSecond =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count();
+ // (10^9 nanoseconds / second) * (N present deltas) / (total nanoseconds in N present deltas) =
+ // M frames / second
+ return kOneSecond * static_cast<nsecs_t>((presentTimes.size() - 1)) /
+ static_cast<float>(totalPresentToPresentWalls);
+}
+
void FrameTimeline::flushPendingPresentFences() {
for (size_t i = 0; i < mPendingPresentFences.size(); i++) {
const auto& pendingPresentFence = mPendingPresentFences[i];
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index 8f3157d80c..d65769b8f4 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -156,9 +156,10 @@ public:
// Only FrameTimeline can construct a SurfaceFrame as it provides Predictions(through
// TokenManager), Thresholds and TimeStats pointer.
SurfaceFrame(const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid,
- std::string layerName, std::string debugName, PredictionState predictionState,
- TimelineItem&& predictions, std::shared_ptr<TimeStats> timeStats,
- JankClassificationThresholds thresholds, TraceCookieCounter* traceCookieCounter);
+ int32_t layerId, std::string layerName, std::string debugName,
+ PredictionState predictionState, TimelineItem&& predictions,
+ std::shared_ptr<TimeStats> timeStats, JankClassificationThresholds thresholds,
+ TraceCookieCounter* traceCookieCounter);
~SurfaceFrame() = default;
// Returns std::nullopt if the frame hasn't been classified yet.
@@ -199,6 +200,7 @@ public:
// Getter functions used only by FrameTimelineTests and SurfaceFrame internally
TimelineItem getActuals() const;
pid_t getOwnerPid() const { return mOwnerPid; };
+ int32_t getLayerId() const { return mLayerId; };
PredictionState getPredictionState() const;
PresentState getPresentState() const;
FrameReadyMetadata getFrameReadyMetadata() const;
@@ -221,6 +223,7 @@ private:
const uid_t mOwnerUid;
const std::string mLayerName;
const std::string mDebugName;
+ const int32_t mLayerId;
PresentState mPresentState GUARDED_BY(mMutex);
const PredictionState mPredictionState;
const TimelineItem mPredictions;
@@ -267,7 +270,7 @@ public:
// Debug name is the human-readable debugging string for dumpsys.
virtual std::shared_ptr<SurfaceFrame> createSurfaceFrameForToken(
const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid,
- std::string layerName, std::string debugName) = 0;
+ int32_t layerId, std::string layerName, std::string debugName) = 0;
// Adds a new SurfaceFrame to the current DisplayFrame. Frames from multiple layers can be
// composited into one display frame.
@@ -292,6 +295,11 @@ public:
// Sets the max number of display frames that can be stored. Called by SF backdoor.
virtual void setMaxDisplayFrames(uint32_t size);
+ // Computes the historical fps for the provided set of layer IDs
+ // The fps is compted from the linear timeline of present timestamps for DisplayFrames
+ // containing at least one layer ID.
+ virtual float computeFps(const std::unordered_set<int32_t>& layerIds);
+
// Restores the max number of display frames to default. Called by SF backdoor.
virtual void reset() = 0;
};
@@ -417,13 +425,14 @@ public:
frametimeline::TokenManager* getTokenManager() override { return &mTokenManager; }
std::shared_ptr<SurfaceFrame> createSurfaceFrameForToken(
const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid,
- std::string layerName, std::string debugName) override;
+ int32_t layerId, std::string layerName, std::string debugName) override;
void addSurfaceFrame(std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame) override;
void setSfWakeUp(int64_t token, nsecs_t wakeupTime, Fps refreshRate) override;
void setSfPresent(nsecs_t sfPresentTime,
const std::shared_ptr<FenceTime>& presentFence) override;
void parseArgs(const Vector<String16>& args, std::string& result) override;
void setMaxDisplayFrames(uint32_t size) override;
+ float computeFps(const std::unordered_set<int32_t>& layerIds) override;
void reset() override;
// Sets up the perfetto tracing backend and data source.
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 6a03996736..ca12653c54 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -1578,7 +1578,8 @@ void Layer::addSurfaceFramePresentedForBuffer(
std::shared_ptr<frametimeline::SurfaceFrame> Layer::createSurfaceFrameForTransaction(
const FrameTimelineInfo& info, nsecs_t postTime) {
auto surfaceFrame =
- mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid, mName,
+ mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid,
+ getSequence(), mName,
mTransactionName);
// For Transactions, the post time is considered to be both queue and acquire fence time.
surfaceFrame->setActualQueueTime(postTime);
@@ -1594,8 +1595,8 @@ std::shared_ptr<frametimeline::SurfaceFrame> Layer::createSurfaceFrameForTransac
std::shared_ptr<frametimeline::SurfaceFrame> Layer::createSurfaceFrameForBuffer(
const FrameTimelineInfo& info, nsecs_t queueTime, std::string debugName) {
auto surfaceFrame =
- mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid, mName,
- debugName);
+ mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid,
+ getSequence(), mName, debugName);
// For buffers, acquire fence time will set during latch.
surfaceFrame->setActualQueueTime(queueTime);
const auto fps = mFlinger->mScheduler->getFrameRateOverride(getOwnerUid());
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 14617565be..c8bb30c218 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -501,6 +501,7 @@ public:
// one empty rect.
virtual void useSurfaceDamage() {}
virtual void useEmptyDamage() {}
+ Region getVisibleRegion(const DisplayDevice*) const;
virtual void incrementPendingBufferCount() {}
@@ -1008,6 +1009,7 @@ protected:
// For unit tests
friend class TestableSurfaceFlinger;
+ friend class FpsReporterTest;
friend class RefreshRateSelectionTest;
friend class SetFrameRateTest;
friend class TransactionFrameTracerTest;
@@ -1164,7 +1166,6 @@ private:
virtual bool canDrawShadows() const { return true; }
Hwc2::IComposerClient::Composition getCompositionType(const DisplayDevice&) const;
- Region getVisibleRegion(const DisplayDevice*) const;
/**
* Returns an unsorted vector of all layers that are part of this tree.
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index da8360edaf..c42b3f7f0b 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -108,6 +108,7 @@
#include "DisplayRenderArea.h"
#include "EffectLayer.h"
#include "Effects/Daltonizer.h"
+#include "FpsReporter.h"
#include "FrameTimeline/FrameTimeline.h"
#include "FrameTracer/FrameTracer.h"
#include "Layer.h"
@@ -1452,6 +1453,25 @@ status_t SurfaceFlinger::removeRegionSamplingListener(const sp<IRegionSamplingLi
return NO_ERROR;
}
+status_t SurfaceFlinger::addFpsListener(const sp<IBinder>& layerHandle,
+ const sp<gui::IFpsListener>& listener) {
+ if (!listener) {
+ return BAD_VALUE;
+ }
+
+ const wp<Layer> layer = fromHandle(layerHandle);
+ mFpsReporter->addListener(listener, layer);
+ return NO_ERROR;
+}
+
+status_t SurfaceFlinger::removeFpsListener(const sp<gui::IFpsListener>& listener) {
+ if (!listener) {
+ return BAD_VALUE;
+ }
+ mFpsReporter->removeListener(listener);
+ return NO_ERROR;
+}
+
status_t SurfaceFlinger::getDisplayBrightnessSupport(const sp<IBinder>& displayToken,
bool* outSupport) const {
if (!displayToken || !outSupport) {
@@ -2138,6 +2158,13 @@ void SurfaceFlinger::postComposition() {
}
});
+ {
+ Mutex::Autolock lock(mStateLock);
+ if (mFpsReporter) {
+ mFpsReporter->dispatchLayerFps();
+ }
+ }
+
mTransactionCallbackInvoker.addPresentFence(mPreviousPresentFences[0]);
mTransactionCallbackInvoker.sendCallbacks();
@@ -2954,6 +2981,7 @@ void SurfaceFlinger::initScheduler(const DisplayDeviceState& displayState) {
mRegionSamplingThread =
new RegionSamplingThread(*this, *mScheduler,
RegionSamplingThread::EnvironmentTimingTunables());
+ mFpsReporter = new FpsReporter(*mFrameTimeline);
// Dispatch a mode change request for the primary display on scheduler
// initialization, so that the EventThreads always contain a reference to a
// prior configuration.
@@ -4940,6 +4968,8 @@ status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) {
case GET_DISPLAYED_CONTENT_SAMPLE:
case NOTIFY_POWER_BOOST:
case SET_GLOBAL_SHADOW_SETTINGS:
+ case ADD_FPS_LISTENER:
+ case REMOVE_FPS_LISTENER:
case ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN: {
// ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN is used by CTS tests, which acquire the
// necessary permission dynamically. Don't use the permission cache for this check.
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 21cd2a5682..e603c5df3a 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -89,6 +89,7 @@ namespace android {
class Client;
class EventThread;
+class FpsReporter;
class HWComposer;
struct SetInputWindowsListener;
class IGraphicBufferProducer;
@@ -592,6 +593,9 @@ private:
status_t addRegionSamplingListener(const Rect& samplingArea, const sp<IBinder>& stopLayerHandle,
const sp<IRegionSamplingListener>& listener) override;
status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) override;
+ status_t addFpsListener(const sp<IBinder>& layerHandle,
+ const sp<gui::IFpsListener>& listener) override;
+ status_t removeFpsListener(const sp<gui::IFpsListener>& listener) override;
status_t setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
ui::DisplayModeId displayModeId, bool allowGroupSwitching,
float primaryRefreshRateMin, float primaryRefreshRateMax,
@@ -1268,6 +1272,7 @@ private:
bool mLumaSampling = true;
sp<RegionSamplingThread> mRegionSamplingThread;
+ sp<FpsReporter> mFpsReporter;
ui::DisplayPrimaries mInternalDisplayPrimaries;
const float mInternalDisplayDensity;
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 2ac6b092b5..3c1b9d8c80 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -53,6 +53,7 @@ cc_test {
"DisplayDevice_GetBestColorModeTest.cpp",
"DisplayDevice_SetProjectionTest.cpp",
"EventThreadTest.cpp",
+ "FpsReporterTest.cpp",
"FpsTest.cpp",
"FrameTimelineTest.cpp",
"HWComposerTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
new file mode 100644
index 0000000000..a9e5df3e9c
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2021 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "FpsReporterTest"
+
+#include <android/gui/BnFpsListener.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <gui/LayerMetadata.h>
+
+#include "BufferQueueLayer.h"
+#include "BufferStateLayer.h"
+#include "EffectLayer.h"
+#include "FpsReporter.h"
+#include "Layer.h"
+#include "TestableSurfaceFlinger.h"
+#include "mock/DisplayHardware/MockComposer.h"
+#include "mock/MockEventThread.h"
+#include "mock/MockFrameTimeline.h"
+#include "mock/MockVsyncController.h"
+
+namespace android {
+
+using testing::_;
+using testing::DoAll;
+using testing::Mock;
+using testing::Return;
+using testing::SetArgPointee;
+using testing::UnorderedElementsAre;
+
+using android::Hwc2::IComposer;
+using android::Hwc2::IComposerClient;
+
+using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
+
+struct TestableFpsListener : public gui::BnFpsListener {
+ TestableFpsListener() {}
+
+ float lastReportedFps = 0;
+
+ binder::Status onFpsReported(float fps) override {
+ lastReportedFps = fps;
+ return binder::Status::ok();
+ }
+};
+
+/**
+ * This class covers all the test that are related to refresh rate selection.
+ */
+class FpsReporterTest : public testing::Test {
+public:
+ FpsReporterTest();
+ ~FpsReporterTest() override;
+
+protected:
+ static constexpr int DEFAULT_DISPLAY_WIDTH = 1920;
+ static constexpr int DEFAULT_DISPLAY_HEIGHT = 1024;
+ static constexpr uint32_t WIDTH = 100;
+ static constexpr uint32_t HEIGHT = 100;
+ static constexpr uint32_t LAYER_FLAGS = 0;
+ static constexpr int32_t PRIORITY_UNSET = -1;
+
+ void setupScheduler();
+ void setupComposer(uint32_t virtualDisplayCount);
+ sp<BufferStateLayer> createBufferStateLayer();
+
+ TestableSurfaceFlinger mFlinger;
+ Hwc2::mock::Composer* mComposer = nullptr;
+ mock::FrameTimeline mFrameTimeline =
+ mock::FrameTimeline(std::make_shared<impl::TimeStats>(), 0);
+
+ sp<Client> mClient;
+ sp<Layer> mParent;
+ sp<Layer> mTarget;
+ sp<Layer> mChild;
+ sp<Layer> mGrandChild;
+ sp<Layer> mUnrelated;
+
+ sp<TestableFpsListener> mFpsListener;
+ sp<FpsReporter> mFpsReporter = new FpsReporter(mFrameTimeline);
+};
+
+FpsReporterTest::FpsReporterTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+
+ setupScheduler();
+ setupComposer(0);
+ mFpsListener = new TestableFpsListener();
+}
+
+FpsReporterTest::~FpsReporterTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+}
+
+sp<BufferStateLayer> FpsReporterTest::createBufferStateLayer() {
+ sp<Client> client;
+ LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", WIDTH, HEIGHT,
+ LAYER_FLAGS, LayerMetadata());
+ return new BufferStateLayer(args);
+}
+
+void FpsReporterTest::setupScheduler() {
+ auto eventThread = std::make_unique<mock::EventThread>();
+ auto sfEventThread = std::make_unique<mock::EventThread>();
+
+ EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
+ EXPECT_CALL(*eventThread, createEventConnection(_, _))
+ .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
+ ResyncCallback())));
+
+ EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
+ EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
+ .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
+ ResyncCallback())));
+
+ auto vsyncController = std::make_unique<mock::VsyncController>();
+ auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
+
+ EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+ EXPECT_CALL(*vsyncTracker, currentPeriod())
+ .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
+ EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+ mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
+ std::move(eventThread), std::move(sfEventThread));
+}
+
+void FpsReporterTest::setupComposer(uint32_t virtualDisplayCount) {
+ mComposer = new Hwc2::mock::Composer();
+ EXPECT_CALL(*mComposer, getMaxVirtualDisplayCount()).WillOnce(Return(virtualDisplayCount));
+ mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
+
+ Mock::VerifyAndClear(mComposer);
+}
+
+namespace {
+
+TEST_F(FpsReporterTest, callsListeners) {
+ mParent = createBufferStateLayer();
+ mTarget = createBufferStateLayer();
+ mChild = createBufferStateLayer();
+ mGrandChild = createBufferStateLayer();
+ mUnrelated = createBufferStateLayer();
+ mParent->addChild(mTarget);
+ mTarget->addChild(mChild);
+ mChild->addChild(mGrandChild);
+ mParent->commitChildList();
+
+ float expectedFps = 44.0;
+
+ EXPECT_CALL(mFrameTimeline,
+ computeFps(UnorderedElementsAre(mTarget->getSequence(), mChild->getSequence(),
+ mGrandChild->getSequence())))
+ .WillOnce(Return(expectedFps));
+
+ mFpsReporter->addListener(mFpsListener, mTarget);
+ mFpsReporter->dispatchLayerFps();
+ EXPECT_EQ(expectedFps, mFpsListener->lastReportedFps);
+ mFpsReporter->removeListener(mFpsListener);
+ Mock::VerifyAndClearExpectations(&mFrameTimeline);
+
+ EXPECT_CALL(mFrameTimeline, computeFps(_)).Times(0);
+ mFpsReporter->dispatchLayerFps();
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index b8c1607562..9a4e020468 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -170,6 +170,8 @@ static constexpr const uid_t sUidOne = 0;
static constexpr pid_t sPidOne = 10;
static constexpr pid_t sPidTwo = 20;
static constexpr int32_t sInputEventId = 5;
+static constexpr int32_t sLayerIdOne = 1;
+static constexpr int32_t sLayerIdTwo = 2;
TEST_F(FrameTimelineTest, tokenManagerRemovesStalePredictions) {
int64_t token1 = mTokenManager->generateTokenForPredictions({0, 0, 0});
@@ -187,17 +189,20 @@ TEST_F(FrameTimelineTest, tokenManagerRemovesStalePredictions) {
}
TEST_F(FrameTimelineTest, createSurfaceFrameForToken_getOwnerPidReturnsCorrectPid) {
- auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne,
- sLayerNameOne, sLayerNameOne);
- auto surfaceFrame2 = mFrameTimeline->createSurfaceFrameForToken({}, sPidTwo, sUidOne,
- sLayerNameOne, sLayerNameOne);
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
+ sLayerNameOne, sLayerNameOne);
+ auto surfaceFrame2 =
+ mFrameTimeline->createSurfaceFrameForToken({}, sPidTwo, sUidOne, sLayerIdOne,
+ sLayerNameOne, sLayerNameOne);
EXPECT_EQ(surfaceFrame1->getOwnerPid(), sPidOne);
EXPECT_EQ(surfaceFrame2->getOwnerPid(), sPidTwo);
}
TEST_F(FrameTimelineTest, createSurfaceFrameForToken_noToken) {
- auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne,
- sLayerNameOne, sLayerNameOne);
+ auto surfaceFrame =
+ mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
+ sLayerNameOne, sLayerNameOne);
EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::None);
}
@@ -206,7 +211,7 @@ TEST_F(FrameTimelineTest, createSurfaceFrameForToken_expiredToken) {
flushTokens(systemTime() + maxTokenRetentionTime);
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
- sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne);
EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Expired);
}
@@ -215,7 +220,7 @@ TEST_F(FrameTimelineTest, createSurfaceFrameForToken_validToken) {
int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
- sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne);
EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Valid);
EXPECT_EQ(compareTimelineItems(surfaceFrame->getPredictions(), TimelineItem(10, 20, 30)), true);
@@ -226,7 +231,7 @@ TEST_F(FrameTimelineTest, createSurfaceFrameForToken_validInputEventId) {
constexpr int32_t inputEventId = 1;
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({token1, inputEventId}, sPidOne, sUidOne,
- sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne);
EXPECT_EQ(inputEventId, surfaceFrame->getInputEventId());
}
@@ -236,7 +241,7 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_droppedFramesNotUpdated) {
int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
- sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne);
// Set up the display frame
mFrameTimeline->setSfWakeUp(token1, 20, Fps::fromPeriodNsecs(11));
@@ -263,10 +268,12 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_presentedFramesUpdated) {
int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 30});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
- sUidOne, sLayerNameTwo, sLayerNameTwo);
+ sUidOne, sLayerIdTwo, sLayerNameTwo,
+ sLayerNameTwo);
mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -307,8 +314,8 @@ TEST_F(FrameTimelineTest, displayFramesSlidingWindowMovesAfterLimit) {
{22 + frameTimeFactor, 26 + frameTimeFactor, 30 + frameTimeFactor});
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId},
- sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne);
+ sPidOne, sUidOne, sLayerIdOne,
+ sLayerNameOne, sLayerNameOne);
mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor, Fps::fromPeriodNsecs(11));
surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame);
@@ -329,7 +336,8 @@ TEST_F(FrameTimelineTest, displayFramesSlidingWindowMovesAfterLimit) {
{22 + frameTimeFactor, 26 + frameTimeFactor, 30 + frameTimeFactor});
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor, Fps::fromPeriodNsecs(11));
surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame);
@@ -343,18 +351,18 @@ TEST_F(FrameTimelineTest, displayFramesSlidingWindowMovesAfterLimit) {
}
TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceAfterQueue) {
- auto surfaceFrame =
- mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, 0, "acquireFenceAfterQueue",
- "acquireFenceAfterQueue");
+ auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, 0, sLayerIdOne,
+ "acquireFenceAfterQueue",
+ "acquireFenceAfterQueue");
surfaceFrame->setActualQueueTime(123);
surfaceFrame->setAcquireFenceTime(456);
EXPECT_EQ(surfaceFrame->getActuals().endTime, 456);
}
TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceBeforeQueue) {
- auto surfaceFrame =
- mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, 0, "acquireFenceAfterQueue",
- "acquireFenceAfterQueue");
+ auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, 0, sLayerIdOne,
+ "acquireFenceAfterQueue",
+ "acquireFenceAfterQueue");
surfaceFrame->setActualQueueTime(456);
surfaceFrame->setAcquireFenceTime(123);
EXPECT_EQ(surfaceFrame->getActuals().endTime, 456);
@@ -367,8 +375,8 @@ TEST_F(FrameTimelineTest, setMaxDisplayFramesSetsSizeProperly) {
// Size shouldn't exceed maxDisplayFrames - 64
for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
auto surfaceFrame =
- mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne);
+ mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
+ sLayerNameOne, sLayerNameOne);
int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
mFrameTimeline->setSfWakeUp(sfToken, 22, Fps::fromPeriodNsecs(11));
surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -383,8 +391,8 @@ TEST_F(FrameTimelineTest, setMaxDisplayFramesSetsSizeProperly) {
for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
auto surfaceFrame =
- mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne);
+ mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
+ sLayerNameOne, sLayerNameOne);
int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
mFrameTimeline->setSfWakeUp(sfToken, 22, Fps::fromPeriodNsecs(11));
surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -399,8 +407,8 @@ TEST_F(FrameTimelineTest, setMaxDisplayFramesSetsSizeProperly) {
for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
auto surfaceFrame =
- mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne);
+ mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
+ sLayerNameOne, sLayerNameOne);
int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
mFrameTimeline->setSfWakeUp(sfToken, 22, Fps::fromPeriodNsecs(11));
surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -434,7 +442,8 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_reportsLongSfCpu) {
std::chrono::nanoseconds(60ms).count()});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
mFrameTimeline->setSfWakeUp(sfToken1, std::chrono::nanoseconds(52ms).count(), refreshRate);
surfaceFrame1->setAcquireFenceTime(std::chrono::nanoseconds(20ms).count());
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -460,7 +469,8 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_reportsDisplayMiss) {
std::chrono::nanoseconds(60ms).count()});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
mFrameTimeline->setSfWakeUp(sfToken1, std::chrono::nanoseconds(52ms).count(), refreshRate);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
surfaceFrame1->setAcquireFenceTime(std::chrono::nanoseconds(20ms).count());
@@ -488,7 +498,8 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppMiss) {
std::chrono::nanoseconds(90ms).count()});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
surfaceFrame1->setAcquireFenceTime(std::chrono::nanoseconds(45ms).count());
mFrameTimeline->setSfWakeUp(sfToken1, std::chrono::nanoseconds(52ms).count(), refreshRate);
@@ -519,7 +530,8 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_reportsSfScheduling) {
std::chrono::nanoseconds(60ms).count()});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
surfaceFrame1->setAcquireFenceTime(std::chrono::nanoseconds(50ms).count());
mFrameTimeline->setSfWakeUp(sfToken1, std::chrono::nanoseconds(52ms).count(), refreshRate);
@@ -550,7 +562,8 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_reportsSfPredictionError) {
std::chrono::nanoseconds(60ms).count()});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
surfaceFrame1->setAcquireFenceTime(std::chrono::nanoseconds(40ms).count());
mFrameTimeline->setSfWakeUp(sfToken1, std::chrono::nanoseconds(52ms).count(), refreshRate);
@@ -580,7 +593,8 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppBufferStuffing) {
std::chrono::nanoseconds(90ms).count()});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
surfaceFrame1->setAcquireFenceTime(std::chrono::nanoseconds(40ms).count());
mFrameTimeline->setSfWakeUp(sfToken1, std::chrono::nanoseconds(82ms).count(), refreshRate);
@@ -614,7 +628,8 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppMissWithRenderRate) {
std::chrono::nanoseconds(90ms).count()});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
surfaceFrame1->setAcquireFenceTime(std::chrono::nanoseconds(45ms).count());
mFrameTimeline->setSfWakeUp(sfToken1, std::chrono::nanoseconds(52ms).count(), refreshRate);
@@ -641,7 +656,7 @@ TEST_F(FrameTimelineTest, tracing_noPacketsSentWithoutTraceStart) {
int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
- sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne);
// Set up the display frame
mFrameTimeline->setSfWakeUp(token1, 20, Fps::fromPeriodNsecs(11));
@@ -667,7 +682,7 @@ TEST_F(FrameTimelineTest, tracing_sanityTest) {
int64_t token2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
- sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne);
// Set up the display frame
mFrameTimeline->setSfWakeUp(token2, 20, Fps::fromPeriodNsecs(11));
@@ -710,8 +725,9 @@ TEST_F(FrameTimelineTest, traceSurfaceFrame_invalidTokenDoesNotEmitTracePacket)
tracingSession->StartBlocking();
int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
- auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne,
- sLayerNameOne, sLayerNameOne);
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
+ sLayerNameOne, sLayerNameOne);
// Set up the display frame
mFrameTimeline->setSfWakeUp(token1, 20, Fps::fromPeriodNsecs(11));
@@ -1015,10 +1031,12 @@ TEST_F(FrameTimelineTest, traceSurfaceFrame_emitsValidTracePacket) {
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
surfaceFrame1->setActualQueueTime(10);
surfaceFrame1->setDropTime(15);
@@ -1174,7 +1192,7 @@ TEST_F(FrameTimelineTest, traceSurfaceFrame_predictionExpiredDoesNotTraceExpecte
flushTokens(systemTime() + maxTokenRetentionTime);
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, /*inputEventId*/ 0},
- sPidOne, sUidOne, sLayerNameOne,
+ sPidOne, sUidOne, sLayerIdOne, sLayerNameOne,
sLayerNameOne);
surfaceFrame1->setActualQueueTime(appEndTime);
surfaceFrame1->setAcquireFenceTime(appEndTime);
@@ -1246,7 +1264,8 @@ TEST_F(FrameTimelineTest, jankClassification_presentOnTimeDoesNotClassify) {
int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 30});
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame);
@@ -1401,7 +1420,8 @@ TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishEarlyPresen
int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({25, 36, 70});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
surfaceFrame1->setAcquireFenceTime(16);
mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1420,7 +1440,8 @@ TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishEarlyPresen
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
surfaceFrame2->setAcquireFenceTime(36);
mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(11));
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1479,7 +1500,8 @@ TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishLatePresent
int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({25, 36, 70});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
surfaceFrame1->setAcquireFenceTime(16);
mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1498,7 +1520,8 @@ TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishLatePresent
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
surfaceFrame2->setAcquireFenceTime(36);
mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(11));
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1556,7 +1579,8 @@ TEST_F(FrameTimelineTest, jankClassification_surfaceFrameLateFinishEarlyPresent)
int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({5, 26, 60});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
surfaceFrame1->setAcquireFenceTime(40);
mFrameTimeline->setSfWakeUp(sfToken1, 42, Fps::fromPeriodNsecs(11));
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1599,7 +1623,8 @@ TEST_F(FrameTimelineTest, jankClassification_surfaceFrameLateFinishLatePresent)
int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({25, 36, 50});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
surfaceFrame1->setAcquireFenceTime(26);
mFrameTimeline->setSfWakeUp(sfToken1, 32, Fps::fromPeriodNsecs(11));
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1618,7 +1643,8 @@ TEST_F(FrameTimelineTest, jankClassification_surfaceFrameLateFinishLatePresent)
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
surfaceFrame2->setAcquireFenceTime(40);
mFrameTimeline->setSfWakeUp(sfToken2, 43, Fps::fromPeriodNsecs(11));
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1671,7 +1697,8 @@ TEST_F(FrameTimelineTest, jankClassification_multiJankBufferStuffingAndAppDeadli
int64_t sfToken2 = mTokenManager->generateTokenForPredictions({112, 116, 120});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
surfaceFrame1->setAcquireFenceTime(50);
mFrameTimeline->setSfWakeUp(sfToken1, 52, Fps::fromPeriodNsecs(30));
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1690,7 +1717,8 @@ TEST_F(FrameTimelineTest, jankClassification_multiJankBufferStuffingAndAppDeadli
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
surfaceFrame2->setAcquireFenceTime(84);
mFrameTimeline->setSfWakeUp(sfToken2, 112, Fps::fromPeriodNsecs(30));
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented, 54);
@@ -1735,4 +1763,151 @@ TEST_F(FrameTimelineTest, jankClassification_multiJankBufferStuffingAndAppDeadli
EXPECT_EQ(presentedSurfaceFrame2.getJankType(),
JankType::AppDeadlineMissed | JankType::BufferStuffing);
}
+
+TEST_F(FrameTimelineTest, computeFps_noLayerIds_returnsZero) {
+ EXPECT_EQ(mFrameTimeline->computeFps({}), 0.0f);
+}
+
+TEST_F(FrameTimelineTest, computeFps_singleDisplayFrame_returnsZero) {
+ const auto oneHundredMs = std::chrono::duration_cast<std::chrono::nanoseconds>(100ms).count();
+
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+ sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ presentFence1->signalForTest(oneHundredMs);
+ mFrameTimeline->setSfPresent(oneHundredMs, presentFence1);
+
+ EXPECT_EQ(mFrameTimeline->computeFps({sLayerIdOne}), 0.0f);
+}
+
+TEST_F(FrameTimelineTest, computeFps_twoDisplayFrames_oneLayer) {
+ const auto oneHundredMs = std::chrono::duration_cast<std::chrono::nanoseconds>(100ms).count();
+ const auto twoHundredMs = std::chrono::duration_cast<std::chrono::nanoseconds>(200ms).count();
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+ sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ presentFence1->signalForTest(oneHundredMs);
+ mFrameTimeline->setSfPresent(oneHundredMs, presentFence1);
+
+ auto surfaceFrame2 =
+ mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+ sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame2);
+ presentFence2->signalForTest(twoHundredMs);
+ mFrameTimeline->setSfPresent(twoHundredMs, presentFence2);
+
+ EXPECT_EQ(mFrameTimeline->computeFps({sLayerIdOne}), 10.0);
+}
+
+TEST_F(FrameTimelineTest, computeFps_twoDisplayFrames_twoLayers) {
+ const auto oneHundredMs = std::chrono::duration_cast<std::chrono::nanoseconds>(100ms).count();
+ const auto twoHundredMs = std::chrono::duration_cast<std::chrono::nanoseconds>(200ms).count();
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+ sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ presentFence1->signalForTest(oneHundredMs);
+ mFrameTimeline->setSfPresent(oneHundredMs, presentFence1);
+
+ auto surfaceFrame2 =
+ mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+ sLayerIdTwo, sLayerNameTwo, sLayerNameTwo);
+ auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame2);
+ presentFence2->signalForTest(twoHundredMs);
+ mFrameTimeline->setSfPresent(twoHundredMs, presentFence2);
+
+ EXPECT_EQ(mFrameTimeline->computeFps({sLayerIdOne, sLayerIdTwo}), 10.0f);
+}
+
+TEST_F(FrameTimelineTest, computeFps_filtersOutLayers) {
+ const auto oneHundredMs = std::chrono::duration_cast<std::chrono::nanoseconds>(100ms).count();
+ const auto twoHundredMs = std::chrono::duration_cast<std::chrono::nanoseconds>(200ms).count();
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+ sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ presentFence1->signalForTest(oneHundredMs);
+ mFrameTimeline->setSfPresent(oneHundredMs, presentFence1);
+
+ auto surfaceFrame2 =
+ mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+ sLayerIdTwo, sLayerNameTwo, sLayerNameTwo);
+ auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame2);
+ presentFence2->signalForTest(twoHundredMs);
+ mFrameTimeline->setSfPresent(twoHundredMs, presentFence2);
+
+ EXPECT_EQ(mFrameTimeline->computeFps({sLayerIdOne}), 0.0f);
+}
+
+TEST_F(FrameTimelineTest, computeFps_averagesOverMultipleFrames) {
+ const auto oneHundredMs = std::chrono::duration_cast<std::chrono::nanoseconds>(100ms).count();
+ const auto twoHundredMs = std::chrono::duration_cast<std::chrono::nanoseconds>(200ms).count();
+ const auto threeHundredMs = std::chrono::duration_cast<std::chrono::nanoseconds>(300ms).count();
+ const auto fiveHundredMs = std::chrono::duration_cast<std::chrono::nanoseconds>(500ms).count();
+ const auto sixHundredMs = std::chrono::duration_cast<std::chrono::nanoseconds>(600ms).count();
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+ sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ presentFence1->signalForTest(oneHundredMs);
+ mFrameTimeline->setSfPresent(oneHundredMs, presentFence1);
+
+ auto surfaceFrame2 =
+ mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+ sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame2);
+ presentFence2->signalForTest(twoHundredMs);
+ mFrameTimeline->setSfPresent(twoHundredMs, presentFence2);
+
+ auto surfaceFrame3 =
+ mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+ sLayerIdTwo, sLayerNameTwo, sLayerNameTwo);
+ auto presentFence3 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ surfaceFrame3->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame3);
+ presentFence3->signalForTest(threeHundredMs);
+ mFrameTimeline->setSfPresent(threeHundredMs, presentFence3);
+
+ auto surfaceFrame4 =
+ mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+ sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ auto presentFence4 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ surfaceFrame4->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame4);
+ presentFence4->signalForTest(fiveHundredMs);
+ mFrameTimeline->setSfPresent(fiveHundredMs, presentFence4);
+
+ auto surfaceFrame5 =
+ mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+ sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ auto presentFence5 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ // Dropped frames will be excluded from fps computation
+ surfaceFrame5->setPresentState(SurfaceFrame::PresentState::Dropped);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame5);
+ presentFence5->signalForTest(sixHundredMs);
+ mFrameTimeline->setSfPresent(sixHundredMs, presentFence5);
+
+ EXPECT_EQ(mFrameTimeline->computeFps({sLayerIdOne}), 5.0f);
+}
+
} // namespace android::frametimeline
diff --git a/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h b/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h
index 44b9b73fd4..b9d17946b9 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h
@@ -33,6 +33,7 @@ public:
MOCK_METHOD1(addSurfaceFrame, void(std::shared_ptr<frametimeline::SurfaceFrame>));
MOCK_METHOD3(setSfWakeUp, void(int64_t, nsecs_t, Fps));
MOCK_METHOD2(setSfPresent, void(nsecs_t, const std::shared_ptr<FenceTime>&));
+ MOCK_METHOD1(computeFps, float(const std::unordered_set<int32_t>&));
};
} // namespace android::mock