SF: Add composition strategy prediction stats

Track number of frames we attempted to predict
the composition strategy and the number of
frames it was successful. This should only be used
as a convenient way to debug locally. Composition
strategy prediction hit and misses are also tracked
via trace tags and can be extracted for metrics.

Enable stats: adb shell su root dumpsys SurfaceFlinger --timestats -enable
Dump stats: adb shell su root dumpsys SurfaceFlinger --timestats -dump -maxlayers 0
Clear stats: adb shell su root dumpsys SurfaceFlinger --timestats -clear

Test: adb shell
Bug: 220031739
Change-Id: I073172977b3df9c8b6894acdc1e327898cab2580
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/GpuCompositionResult.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/GpuCompositionResult.h
index ed1ddc1..2b1f50f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/GpuCompositionResult.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/GpuCompositionResult.h
@@ -22,9 +22,6 @@
 namespace android::compositionengine::impl {
 
 struct GpuCompositionResult {
-    // True if composition strategy was predicted successfully.
-    bool succeeded = false;
-
     // Composition ready fence.
     base::unique_fd fence{};
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index 92f22b6..ade9b25 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -141,6 +141,18 @@
     // This is slightly distinct from nits, in that nits cannot be passed to hw composer.
     std::optional<float> displayBrightness = std::nullopt;
 
+    enum class CompositionStrategyPredictionState : uint32_t {
+        // Composition strategy prediction did not run for this frame.
+        DISABLED = 0,
+        // Composition strategy predicted successfully for this frame.
+        SUCCESS = 1,
+        // Composition strategy prediction failed for this frame.
+        FAIL = 2,
+    };
+
+    CompositionStrategyPredictionState strategyPrediction =
+            CompositionStrategyPredictionState::DISABLED;
+
     // Debugging
     void dump(std::string& result) const;
 };
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 8d560d7..e4bd325 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -58,7 +58,8 @@
 Output::~Output() = default;
 
 namespace impl {
-
+using CompositionStrategyPredictionState =
+        OutputCompositionState::CompositionStrategyPredictionState;
 namespace {
 
 template <typename T>
@@ -966,6 +967,7 @@
     }
 
     auto changes = chooseCompositionStrategy();
+    outputState.strategyPrediction = CompositionStrategyPredictionState::DISABLED;
     outputState.previousDeviceRequestedChanges = changes;
     if (changes) {
         applyCompositionStrategy(changes);
@@ -1002,7 +1004,8 @@
 
     auto changes = hwcResult.valid() ? hwcResult.get() : std::nullopt;
     const bool predictionSucceeded = dequeueSucceeded && changes == previousChanges;
-    compositionResult.succeeded = predictionSucceeded;
+    state.strategyPrediction = predictionSucceeded ? CompositionStrategyPredictionState::SUCCESS
+                                                   : CompositionStrategyPredictionState::FAIL;
     if (!predictionSucceeded) {
         ATRACE_NAME("CompositionStrategyPredictionMiss");
         if (changes) {
@@ -1044,15 +1047,15 @@
 void Output::finishFrame(const CompositionRefreshArgs& refreshArgs, GpuCompositionResult&& result) {
     ATRACE_CALL();
     ALOGV(__FUNCTION__);
-
-    if (!getState().isEnabled) {
+    const auto& outputState = getState();
+    if (!outputState.isEnabled) {
         return;
     }
 
     std::optional<base::unique_fd> optReadyFence;
     std::shared_ptr<renderengine::ExternalTexture> buffer;
     base::unique_fd bufferFence;
-    if (result.succeeded) {
+    if (outputState.strategyPrediction == CompositionStrategyPredictionState::SUCCESS) {
         optReadyFence = std::move(result.fence);
     } else {
         if (result.bufferAvailable()) {
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
index 482250a..7188281 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
@@ -18,6 +18,19 @@
 #include <compositionengine/impl/OutputCompositionState.h>
 
 namespace android::compositionengine::impl {
+using CompositionStrategyPredictionState =
+        OutputCompositionState::CompositionStrategyPredictionState;
+
+std::string toString(CompositionStrategyPredictionState state) {
+    switch (state) {
+        case CompositionStrategyPredictionState::DISABLED:
+            return "Disabled";
+        case CompositionStrategyPredictionState::SUCCESS:
+            return "Success";
+        case CompositionStrategyPredictionState::FAIL:
+            return "Fail";
+    }
+}
 
 void OutputCompositionState::dump(std::string& out) const {
     out.append("   ");
@@ -56,6 +69,7 @@
     dumpVal(out, "sdrWhitePointNits", sdrWhitePointNits);
     dumpVal(out, "clientTargetBrightness", clientTargetBrightness);
     dumpVal(out, "displayBrightness", displayBrightness);
+    dumpVal(out, "compositionStrategyPredictionState", toString(strategyPrediction));
 
     out.append("\n");
 }
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 4e875c8..0c5ea79 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -74,6 +74,9 @@
 constexpr OutputColorSetting kVendorSpecifiedOutputColorSetting =
         static_cast<OutputColorSetting>(0x100);
 
+using CompositionStrategyPredictionState = android::compositionengine::impl::
+        OutputCompositionState::CompositionStrategyPredictionState;
+
 struct OutputPartialMockBase : public impl::Output {
     // compositionengine::Output overrides
     const OutputCompositionState& getState() const override { return mState; }
@@ -1021,6 +1024,7 @@
     EXPECT_CALL(*mRenderSurface, prepareFrame(false, true));
 
     mOutput.prepareFrame();
+    EXPECT_EQ(mOutput.getState().strategyPrediction, CompositionStrategyPredictionState::DISABLED);
 }
 
 // Note: Use OutputTest and not OutputPrepareFrameTest, so the real
@@ -1036,6 +1040,7 @@
 
     EXPECT_TRUE(mOutput->getState().usesClientComposition);
     EXPECT_FALSE(mOutput->getState().usesDeviceComposition);
+    EXPECT_EQ(mOutput->getState().strategyPrediction, CompositionStrategyPredictionState::DISABLED);
 }
 
 struct OutputPrepareFrameAsyncTest : public testing::Test {
@@ -1084,7 +1089,7 @@
     EXPECT_CALL(mOutput, composeSurfaces(_, Ref(mRefreshArgs), _, _));
 
     impl::GpuCompositionResult result = mOutput.prepareFrameAsync(mRefreshArgs);
-    EXPECT_TRUE(result.succeeded);
+    EXPECT_EQ(mOutput.getState().strategyPrediction, CompositionStrategyPredictionState::SUCCESS);
     EXPECT_FALSE(result.bufferAvailable());
 }
 
@@ -1104,7 +1109,7 @@
     EXPECT_CALL(mOutput, chooseCompositionStrategyAsync()).WillOnce([&] { return p.get_future(); });
 
     impl::GpuCompositionResult result = mOutput.prepareFrameAsync(mRefreshArgs);
-    EXPECT_FALSE(result.succeeded);
+    EXPECT_EQ(mOutput.getState().strategyPrediction, CompositionStrategyPredictionState::FAIL);
     EXPECT_FALSE(result.bufferAvailable());
 }
 
@@ -1132,7 +1137,7 @@
     EXPECT_CALL(mOutput, composeSurfaces(_, Ref(mRefreshArgs), _, _));
 
     impl::GpuCompositionResult result = mOutput.prepareFrameAsync(mRefreshArgs);
-    EXPECT_FALSE(result.succeeded);
+    EXPECT_EQ(mOutput.getState().strategyPrediction, CompositionStrategyPredictionState::FAIL);
     EXPECT_TRUE(result.bufferAvailable());
 }
 
@@ -1161,7 +1166,7 @@
     EXPECT_CALL(mOutput, composeSurfaces(_, Ref(mRefreshArgs), _, _));
 
     impl::GpuCompositionResult result = mOutput.prepareFrameAsync(mRefreshArgs);
-    EXPECT_FALSE(result.succeeded);
+    EXPECT_EQ(mOutput.getState().strategyPrediction, CompositionStrategyPredictionState::FAIL);
     EXPECT_TRUE(result.bufferAvailable());
 }
 
@@ -3005,22 +3010,21 @@
 
 TEST_F(OutputFinishFrameTest, predictionSucceeded) {
     mOutput.mState.isEnabled = true;
-
+    mOutput.mState.strategyPrediction = CompositionStrategyPredictionState::SUCCESS;
     InSequence seq;
     EXPECT_CALL(*mRenderSurface, queueBuffer(_));
 
     impl::GpuCompositionResult result;
-    result.succeeded = true;
     mOutput.finishFrame(mRefreshArgs, std::move(result));
 }
 
 TEST_F(OutputFinishFrameTest, predictionFailedAndBufferIsReused) {
     mOutput.mState.isEnabled = true;
+    mOutput.mState.strategyPrediction = CompositionStrategyPredictionState::FAIL;
 
     InSequence seq;
 
     impl::GpuCompositionResult result;
-    result.succeeded = false;
     result.buffer =
             std::make_shared<renderengine::mock::FakeExternalTexture>(1, 1,
                                                                       HAL_PIXEL_FORMAT_RGBA_8888, 1,