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
diff --git a/services/surfaceflinger/FpsReporter.h b/services/surfaceflinger/FpsReporter.h
new file mode 100644
index 0000000..d64b3dc
--- /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