Skip client composition requests

Keep track of client compositions requests in CompositionEngine and
the buffer used to render the request. If the requests do not change
and the buffer matches, reuse the buffer instead of going
to client composition.

Certain layers properties (buffer format, rounded corner or shadows)
will force the device to go into GPU or mixed composition. In mixed
composition scenarios, we can avoid redundant client composition
requests by reusing the RenderSurface buffers. If the device goes
into GPU composition then all the layers will be composited and there
will be no performance benefit.

Bug: b/136561771, b/144690120

Test: dump SurfaceFlinger --timestats -dump
Test: manual testing with mixed and gpu composition scenarios
Test: atest libcompositionengine_test libsurfaceflinger_unittest SurfaceFlinger_test
Change-Id: I466d5dcded0c9fcfa64bc72fd91dfaddd795f315
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 1b1b58b..a894005 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -353,6 +353,9 @@
     property_get("debug.sf.luma_sampling", value, "1");
     mLumaSampling = atoi(value);
 
+    property_get("debug.sf.disable_client_composition_cache", value, "1");
+    mDisableClientCompositionCache = atoi(value);
+
     // We should be reading 'persist.sys.sf.color_saturation' here
     // but since /data may be encrypted, we need to wait until after vold
     // comes online to attempt to read the property. The property is
@@ -1893,13 +1896,19 @@
     mHadClientComposition =
             std::any_of(mDisplays.cbegin(), mDisplays.cend(), [](const auto& tokenDisplayPair) {
                 auto& displayDevice = tokenDisplayPair.second;
-                return displayDevice->getCompositionDisplay()->getState().usesClientComposition;
+                return displayDevice->getCompositionDisplay()->getState().usesClientComposition &&
+                        !displayDevice->getCompositionDisplay()->getState().reusedClientComposition;
             });
     mHadDeviceComposition =
             std::any_of(mDisplays.cbegin(), mDisplays.cend(), [](const auto& tokenDisplayPair) {
                 auto& displayDevice = tokenDisplayPair.second;
                 return displayDevice->getCompositionDisplay()->getState().usesDeviceComposition;
             });
+    mReusedClientComposition =
+            std::any_of(mDisplays.cbegin(), mDisplays.cend(), [](const auto& tokenDisplayPair) {
+                auto& displayDevice = tokenDisplayPair.second;
+                return displayDevice->getCompositionDisplay()->getState().reusedClientComposition;
+            });
 
     mVSyncModulator->onRefreshed(mHadClientComposition);
 
@@ -2079,6 +2088,10 @@
         mTimeStats->incrementClientCompositionFrames();
     }
 
+    if (mReusedClientComposition) {
+        mTimeStats->incrementClientCompositionReusedFrames();
+    }
+
     mTimeStats->setPresentFenceGlobal(presentFenceTime);
 
     if (displayDevice && getHwComposer().isConnected(*displayDevice->getId()) &&
@@ -5408,7 +5421,7 @@
     const auto& displayViewport = renderArea.getDisplayViewport();
 
     renderengine::DisplaySettings clientCompositionDisplay;
-    std::vector<renderengine::LayerSettings> clientCompositionLayers;
+    std::vector<compositionengine::LayerFE::LayerSettings> clientCompositionLayers;
 
     // assume that bounds are never offset, and that they are the same as the
     // buffer bounds.
@@ -5469,7 +5482,7 @@
 
     const float alpha = RenderArea::getCaptureFillValue(renderArea.getCaptureFill());
 
-    renderengine::LayerSettings fillLayer;
+    compositionengine::LayerFE::LayerSettings fillLayer;
     fillLayer.source.buffer.buffer = nullptr;
     fillLayer.source.solidColor = half3(0.0, 0.0, 0.0);
     fillLayer.geometry.boundaries = FloatRect(0.0, 0.0, 1.0, 1.0);
@@ -5490,7 +5503,7 @@
         };
         auto result = layer->prepareClientComposition(targetSettings);
         if (result) {
-            std::optional<renderengine::LayerSettings> shadowLayer =
+            std::optional<compositionengine::LayerFE::LayerSettings> shadowLayer =
                     layer->prepareShadowClientComposition(*result, displayViewport,
                                                           clientCompositionDisplay.outputDataspace);
             if (shadowLayer) {
@@ -5500,13 +5513,20 @@
         }
     });
 
+    std::vector<const renderengine::LayerSettings*> clientCompositionLayerPointers;
+    clientCompositionLayers.reserve(clientCompositionLayers.size());
+    std::transform(clientCompositionLayers.begin(), clientCompositionLayers.end(),
+                   std::back_inserter(clientCompositionLayerPointers),
+                   [](compositionengine::LayerFE::LayerSettings& settings)
+                           -> renderengine::LayerSettings* { return &settings; });
+
     clientCompositionDisplay.clearRegion = clearRegion;
     // Use an empty fence for the buffer fence, since we just created the buffer so
     // there is no need for synchronization with the GPU.
     base::unique_fd bufferFence;
     base::unique_fd drawFence;
     getRenderEngine().useProtectedContext(false);
-    getRenderEngine().drawLayers(clientCompositionDisplay, clientCompositionLayers, buffer,
+    getRenderEngine().drawLayers(clientCompositionDisplay, clientCompositionLayerPointers, buffer,
                                  /*useFramebufferCache=*/false, std::move(bufferFence), &drawFence);
 
     *outSyncFd = drawFence.release();