Compose a layer with a picture profile passed in by an app

Forwards the picture profile to Composer HAL during composition. The
priority of a layer's profile, passed in by an app, plays a role in
deciding which requested profile actually get sent to Composer HAL
when limited picture processing hardware resources come into play.

Bug: 337330263
Test: atest OutputLayerWriteStateToHWCTest
Test: atest OutputUpdateAndWriteCompositionStateTest
Flag: com.android.graphics.libgui.flags.apply_picture_profiles
Change-Id: I396d4928c46002844df3c707421974f30cb8d98b
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index fe7dd9a..99e68eb 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <android-base/stringprintf.h>
+#include <com_android_graphics_libgui_flags.h>
 #include <com_android_graphics_surfaceflinger_flags.h>
 #include <compositionengine/LayerFECompositionState.h>
 #include <compositionengine/impl/Output.h>
@@ -37,11 +38,14 @@
 #include <cstdint>
 #include <variant>
 
+#include <com_android_graphics_surfaceflinger_flags.h>
+
 #include <common/FlagManager.h>
 #include <common/test/FlagUtils.h>
 #include "CallOrderStateMachineHelper.h"
 #include "RegionMatcher.h"
 #include "mock/DisplayHardware/MockHWC2.h"
+#include "mock/DisplayHardware/MockHWComposer.h"
 
 namespace android::compositionengine {
 namespace {
@@ -142,6 +146,24 @@
     public:
         using impl::Output::injectOutputLayerForTest;
         virtual void injectOutputLayerForTest(std::unique_ptr<compositionengine::OutputLayer>) = 0;
+
+        virtual ftl::Optional<DisplayId> getDisplayId() const override { return mId; }
+
+        virtual bool hasPictureProcessing() const override { return mHasPictureProcessing; }
+        virtual int32_t getMaxLayerPictureProfiles() const override {
+            return mMaxLayerPictureProfiles;
+        }
+
+        void setDisplayIdForTest(DisplayId value) { mId = value; }
+
+        void setHasPictureProcessingForTest(bool value) { mHasPictureProcessing = value; }
+
+        void setMaxLayerPictureProfilesForTest(int32_t value) { mMaxLayerPictureProfiles = value; }
+
+    private:
+        ftl::Optional<DisplayId> mId;
+        bool mHasPictureProcessing;
+        int32_t mMaxLayerPictureProfiles;
     };
 
     static std::shared_ptr<Output> createOutput(
@@ -157,6 +179,7 @@
         mOutput->editState().displaySpace.setBounds(
                 ui::Size(kDefaultDisplaySize.getWidth(), kDefaultDisplaySize.getHeight()));
         EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine));
+        EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer));
     }
 
     void injectOutputLayer(InjectedLayer& layer) {
@@ -169,6 +192,7 @@
 
     static const Rect kDefaultDisplaySize;
 
+    StrictMock<::android::mock::HWComposer> mHwComposer;
     StrictMock<mock::CompositionEngine> mCompositionEngine;
     StrictMock<renderengine::mock::RenderEngine> mRenderEngine;
     mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
@@ -5043,6 +5067,123 @@
     mOutput->writeCompositionState(args);
 }
 
+TEST_F(OutputUpdateAndWriteCompositionStateTest, assignsDisplayProfileBasedOnLayerPriority) {
+    if (!com_android_graphics_libgui_flags_apply_picture_profiles()) {
+        GTEST_SKIP() << "Feature flag disabled, skipping";
+    }
+
+    mOutput->setDisplayIdForTest(PhysicalDisplayId::fromPort(1));
+    // Has only one display-global picture processing pipeline
+    mOutput->setHasPictureProcessingForTest(true);
+    mOutput->setMaxLayerPictureProfilesForTest(0);
+
+    InjectedLayer layer1;
+    injectOutputLayer(layer1);
+    PictureProfileHandle profileForLayer1(1);
+    EXPECT_CALL(*layer1.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(3));
+    EXPECT_CALL(*layer1.outputLayer, getPictureProfileHandle())
+            .WillRepeatedly(ReturnRef(profileForLayer1));
+
+    InjectedLayer layer2;
+    injectOutputLayer(layer2);
+    PictureProfileHandle profileForLayer2(2);
+    EXPECT_CALL(*layer2.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(1));
+    EXPECT_CALL(*layer2.outputLayer, getPictureProfileHandle())
+            .WillRepeatedly(ReturnRef(profileForLayer2));
+
+    InjectedLayer layer3;
+    injectOutputLayer(layer3);
+    PictureProfileHandle profileForLayer3(3);
+    EXPECT_CALL(*layer3.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(2));
+    EXPECT_CALL(*layer3.outputLayer, getPictureProfileHandle())
+            .WillRepeatedly(ReturnRef(profileForLayer3));
+
+    // Because StrictMock
+    EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
+    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(_, _, _, _));
+    EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(_, _, _, _, _));
+    EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
+    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(_, _, _, _));
+    EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(_, _, _, _, _));
+    EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
+    EXPECT_CALL(*layer3.outputLayer, updateCompositionState(_, _, _, _));
+    EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(_, _, _, _, _));
+
+    // No layer picture profiles should be committed
+    EXPECT_CALL(*layer1.outputLayer, commitPictureProfileToCompositionState).Times(0);
+    EXPECT_CALL(*layer2.outputLayer, commitPictureProfileToCompositionState).Times(0);
+    EXPECT_CALL(*layer3.outputLayer, commitPictureProfileToCompositionState).Times(0);
+
+    // Sets display picture profile to the highest priority layer's profile
+    EXPECT_CALL(mHwComposer, setDisplayPictureProfileHandle(_, Eq(profileForLayer2)));
+
+    mOutput->editState().isEnabled = true;
+    CompositionRefreshArgs args;
+    args.updatingGeometryThisFrame = false;
+    args.devOptForceClientComposition = false;
+    mOutput->updateCompositionState(args);
+    mOutput->planComposition();
+    mOutput->writeCompositionState(args);
+}
+
+TEST_F(OutputUpdateAndWriteCompositionStateTest, assignsLayerProfileBasedOnLayerPriority) {
+    if (!com_android_graphics_libgui_flags_apply_picture_profiles()) {
+        GTEST_SKIP() << "Feature flag disabled, skipping";
+    }
+    mOutput->setDisplayIdForTest(PhysicalDisplayId::fromPort(1));
+    // Has 2 layer-specific picture processing pipelines
+    mOutput->setHasPictureProcessingForTest(true);
+    mOutput->setMaxLayerPictureProfilesForTest(2);
+
+    InjectedLayer layer1;
+    injectOutputLayer(layer1);
+    PictureProfileHandle profileForLayer1(1);
+    EXPECT_CALL(*layer1.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(3));
+    EXPECT_CALL(*layer1.outputLayer, getPictureProfileHandle())
+            .WillRepeatedly(ReturnRef(profileForLayer1));
+
+    InjectedLayer layer2;
+    injectOutputLayer(layer2);
+    PictureProfileHandle profileForLayer2(2);
+    EXPECT_CALL(*layer2.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(1));
+    EXPECT_CALL(*layer2.outputLayer, getPictureProfileHandle())
+            .WillRepeatedly(ReturnRef(profileForLayer2));
+
+    InjectedLayer layer3;
+    injectOutputLayer(layer3);
+    PictureProfileHandle profileForLayer3(3);
+    EXPECT_CALL(*layer3.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(2));
+    EXPECT_CALL(*layer3.outputLayer, getPictureProfileHandle())
+            .WillRepeatedly(ReturnRef(profileForLayer3));
+
+    // Because StrictMock
+    EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
+    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(_, _, _, _));
+    EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(_, _, _, _, _));
+    EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
+    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(_, _, _, _));
+    EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(_, _, _, _, _));
+    EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
+    EXPECT_CALL(*layer3.outputLayer, updateCompositionState(_, _, _, _));
+    EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(_, _, _, _, _));
+
+    // The two highest priority layers should have their picture profiles committed
+    EXPECT_CALL(*layer1.outputLayer, commitPictureProfileToCompositionState).Times(0);
+    EXPECT_CALL(*layer2.outputLayer, commitPictureProfileToCompositionState);
+    EXPECT_CALL(*layer3.outputLayer, commitPictureProfileToCompositionState);
+
+    // No display picture profile is sent
+    EXPECT_CALL(mHwComposer, setDisplayPictureProfileHandle).Times(0);
+
+    mOutput->editState().isEnabled = true;
+    CompositionRefreshArgs args;
+    args.updatingGeometryThisFrame = false;
+    args.devOptForceClientComposition = false;
+    mOutput->updateCompositionState(args);
+    mOutput->planComposition();
+    mOutput->writeCompositionState(args);
+}
+
 TEST_F(GenerateClientCompositionRequestsTest, handlesLandscapeModeSplitScreenRequests) {
     // In split-screen landscape mode, the screen is rotated 90 degrees, with
     // one layer on the left covering the left side of the output, and one layer