SF: Move/Refactor calculateWorkingSet to CompositionEngine

The functionality is made part of CompositionEngine::present()

Test: atest libsurfaceflinger_unittest libcompositionengine_test
Bug: 121291683
Change-Id: I0eb74f9fe8421468fbc1048aec23d7c91ce4f302
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
index 1001cf7..6f689ad 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
@@ -22,6 +22,7 @@
 #include <compositionengine/Display.h>
 #include <compositionengine/Layer.h>
 #include <compositionengine/OutputColorSetting.h>
+#include <math/mat4.h>
 
 namespace android::compositionengine {
 
@@ -52,6 +53,16 @@
     // Forces a color mode on the outputs being refreshed
     ui::ColorMode forceOutputColorMode{ui::ColorMode::NATIVE};
 
+    // If true, there was a geometry update this frame
+    bool updatingGeometryThisFrame{false};
+
+    // The color matrix to use for this
+    // frame. Only set if the color transform is changing this frame.
+    std::optional<mat4> colorTransformMatrix;
+
+    // If true, client composition is always used.
+    bool devOptForceClientComposition{false};
+
     // If set, causes the dirty regions to flash with the delay
     std::optional<std::chrono::microseconds> devOptFlashDirtyRegionsDelay;
 };
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index e71e972..a509ca8 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -21,7 +21,6 @@
 #include <string>
 #include <unordered_map>
 
-#include <math/mat4.h>
 #include <renderengine/LayerSettings.h>
 #include <ui/Fence.h>
 #include <ui/GraphicTypes.h>
@@ -44,6 +43,7 @@
 class OutputLayer;
 
 struct CompositionRefreshArgs;
+struct LayerFECompositionState;
 
 namespace impl {
 struct OutputCompositionState;
@@ -56,6 +56,7 @@
 public:
     using OutputLayers = std::vector<std::unique_ptr<compositionengine::OutputLayer>>;
     using ReleasedLayers = std::vector<wp<LayerFE>>;
+    using UniqueFELayerStateMap = std::unordered_map<LayerFE*, LayerFECompositionState*>;
 
     struct FrameFences {
         sp<Fence> presentFence{Fence::NO_FENCE};
@@ -90,9 +91,6 @@
     // belongsInOutput for full details.
     virtual void setLayerStackFilter(uint32_t layerStackId, bool isInternal) = 0;
 
-    // Sets the color transform matrix to use
-    virtual void setColorTransform(const mat4&) = 0;
-
     // Sets the output color mode
     virtual void setColorProfile(const ColorProfile&) = 0;
 
@@ -159,20 +157,26 @@
     // Takes (moves) the set of layers being released this frame.
     virtual ReleasedLayers takeReleasedLayers() = 0;
 
-    // Presents the output, finalizing all composition details
-    virtual void present(const compositionengine::CompositionRefreshArgs&) = 0;
+    // Prepare the output, updating the OutputLayers used in the output
+    virtual void prepare(CompositionRefreshArgs&) = 0;
 
-    // Updates the color mode used on this output
-    virtual void updateColorProfile(const CompositionRefreshArgs&) = 0;
+    // Presents the output, finalizing all composition details
+    virtual void present(const CompositionRefreshArgs&) = 0;
+
+    // Latches the front-end layer state for each output layer
+    virtual void updateLayerStateFromFE(const CompositionRefreshArgs&) const = 0;
 
 protected:
     virtual void setDisplayColorProfile(std::unique_ptr<DisplayColorProfile>) = 0;
     virtual void setRenderSurface(std::unique_ptr<RenderSurface>) = 0;
 
+    virtual void updateAndWriteCompositionState(const CompositionRefreshArgs&) = 0;
+    virtual void setColorTransform(const CompositionRefreshArgs&) = 0;
+    virtual void updateColorProfile(const CompositionRefreshArgs&) = 0;
     virtual void beginFrame() = 0;
     virtual void prepareFrame() = 0;
-    virtual void devOptRepaintFlash(const compositionengine::CompositionRefreshArgs&) = 0;
-    virtual void finishFrame(const compositionengine::CompositionRefreshArgs&) = 0;
+    virtual void devOptRepaintFlash(const CompositionRefreshArgs&) = 0;
+    virtual void finishFrame(const CompositionRefreshArgs&) = 0;
     virtual std::optional<base::unique_fd> composeSurfaces(const Region&) = 0;
     virtual void postFramebuffer() = 0;
     virtual void chooseCompositionStrategy() = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
index d476b85..6340b14 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
@@ -45,6 +45,8 @@
 
     void preComposition(CompositionRefreshArgs&) override;
 
+    void updateLayerStateFromFE(CompositionRefreshArgs& args);
+
     // Testing
     void setNeedsAnotherUpdateForTest(bool);
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index dba112eb..bd1aa08 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -40,7 +40,7 @@
 
     // compositionengine::Output overrides
     void dump(std::string&) const override;
-    void setColorTransform(const mat4&) override;
+    void setColorTransform(const compositionengine::CompositionRefreshArgs&) override;
     void setColorProfile(const ColorProfile&) override;
     void chooseCompositionStrategy() override;
     bool getSkipColorTransform() const override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index 22bad24..d826161 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -44,7 +44,7 @@
     void setBounds(const ui::Size&) override;
     void setLayerStackFilter(uint32_t layerStackId, bool isInternal) override;
 
-    void setColorTransform(const mat4&) override;
+    void setColorTransform(const compositionengine::CompositionRefreshArgs&) override;
     void setColorProfile(const ColorProfile&) override;
 
     void dump(std::string&) const override;
@@ -75,10 +75,12 @@
     void setReleasedLayers(ReleasedLayers&&) override;
     ReleasedLayers takeReleasedLayers() override;
 
+    void prepare(compositionengine::CompositionRefreshArgs&) override;
     void present(const compositionengine::CompositionRefreshArgs&) override;
 
+    void updateLayerStateFromFE(const CompositionRefreshArgs&) const override;
+    void updateAndWriteCompositionState(const compositionengine::CompositionRefreshArgs&) override;
     void updateColorProfile(const compositionengine::CompositionRefreshArgs&) override;
-
     void beginFrame() override;
     void prepareFrame() override;
     void devOptRepaintFlash(const compositionengine::CompositionRefreshArgs&) override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index 1078f11..17d3d3f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -86,11 +86,8 @@
     // True if the last composition frame had visible layers
     bool lastCompositionHadVisibleLayers{false};
 
-    // The color transform to apply
-    android_color_transform_t colorTransform{HAL_COLOR_TRANSFORM_IDENTITY};
-
-    // The color transform matrix to apply, corresponding with colorTransform.
-    mat4 colorTransformMat;
+    // The color transform matrix to apply
+    mat4 colorTransformMatrix;
 
     // Current active color mode
     ui::ColorMode colorMode{ui::ColorMode::NATIVE};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
index 5a5c36a..e3254ac 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
@@ -46,6 +46,7 @@
 
     MOCK_METHOD1(present, void(CompositionRefreshArgs&));
     MOCK_METHOD1(updateCursorAsync, void(CompositionRefreshArgs&));
+
     MOCK_METHOD1(preComposition, void(CompositionRefreshArgs&));
 };
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index feca929..33925d5 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -41,7 +41,7 @@
     MOCK_METHOD1(setBounds, void(const ui::Size&));
     MOCK_METHOD2(setLayerStackFilter, void(uint32_t, bool));
 
-    MOCK_METHOD1(setColorTransform, void(const mat4&));
+    MOCK_METHOD1(setColorTransform, void(const compositionengine::CompositionRefreshArgs&));
     MOCK_METHOD1(setColorProfile, void(const ColorProfile&));
 
     MOCK_CONST_METHOD1(dump, void(std::string&));
@@ -74,8 +74,11 @@
     MOCK_METHOD1(setReleasedLayers, void(ReleasedLayers&&));
     MOCK_METHOD0(takeReleasedLayers, ReleasedLayers());
 
+    MOCK_METHOD1(prepare, void(compositionengine::CompositionRefreshArgs&));
     MOCK_METHOD1(present, void(const compositionengine::CompositionRefreshArgs&));
 
+    MOCK_CONST_METHOD1(updateLayerStateFromFE, void(const CompositionRefreshArgs&));
+    MOCK_METHOD1(updateAndWriteCompositionState, void(const CompositionRefreshArgs&));
     MOCK_METHOD1(updateColorProfile, void(const compositionengine::CompositionRefreshArgs&));
 
     MOCK_METHOD0(beginFrame, void());
diff --git a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
index 6ed50aa..590c596 100644
--- a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
@@ -73,6 +73,12 @@
 
 void CompositionEngine::present(CompositionRefreshArgs& args) {
     for (const auto& output : args.outputs) {
+        output->prepare(args);
+    }
+
+    updateLayerStateFromFE(args);
+
+    for (const auto& output : args.outputs) {
         output->present(args);
     }
 }
@@ -115,5 +121,12 @@
     mNeedsAnotherUpdate = value;
 }
 
+void CompositionEngine::updateLayerStateFromFE(CompositionRefreshArgs& args) {
+    // Update the composition state from each front-end layer
+    for (const auto& output : args.outputs) {
+        output->updateLayerStateFromFE(args);
+    }
+}
+
 } // namespace impl
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 108720a..000a294 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -69,11 +69,15 @@
     mId.reset();
 }
 
-void Display::setColorTransform(const mat4& transform) {
-    Output::setColorTransform(transform);
+void Display::setColorTransform(const compositionengine::CompositionRefreshArgs& args) {
+    Output::setColorTransform(args);
+
+    if (!mId || CC_LIKELY(!args.colorTransformMatrix)) {
+        return;
+    }
 
     auto& hwc = getCompositionEngine().getHwComposer();
-    status_t result = hwc.setColorTransform(*mId, transform);
+    status_t result = hwc.setColorTransform(*mId, *args.colorTransformMatrix);
     ALOGE_IF(result != NO_ERROR, "Failed to set color transform on display \"%s\": %d",
              mId ? to_string(*mId).c_str() : "", result);
 }
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 635e450..903ca98 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -99,17 +99,12 @@
     dirtyEntireOutput();
 }
 
-void Output::setColorTransform(const mat4& transform) {
-    if (mState.colorTransformMat == transform) {
+void Output::setColorTransform(const compositionengine::CompositionRefreshArgs& args) {
+    if (!args.colorTransformMatrix || mState.colorTransformMatrix == *args.colorTransformMatrix) {
         return;
     }
 
-    const bool isIdentity = (transform == mat4());
-    const auto newColorTransform =
-            isIdentity ? HAL_COLOR_TRANSFORM_IDENTITY : HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX;
-
-    mState.colorTransform = newColorTransform;
-    mState.colorTransformMat = transform;
+    mState.colorTransformMatrix = *args.colorTransformMatrix;
 
     dirtyEntireOutput();
 }
@@ -260,7 +255,22 @@
     return std::move(mReleasedLayers);
 }
 
+void Output::prepare(compositionengine::CompositionRefreshArgs& refreshArgs) {
+    if (CC_LIKELY(!refreshArgs.updatingGeometryThisFrame)) {
+        return;
+    }
+
+    uint32_t zOrder = 0;
+    for (auto& layer : mOutputLayersOrderedByZ) {
+        // Assign a simple Z order sequence to each visible layer.
+        layer->editState().z = zOrder++;
+    }
+}
+
 void Output::present(const compositionengine::CompositionRefreshArgs& refreshArgs) {
+    updateColorProfile(refreshArgs);
+    updateAndWriteCompositionState(refreshArgs);
+    setColorTransform(refreshArgs);
     beginFrame();
     prepareFrame();
     devOptRepaintFlash(refreshArgs);
@@ -268,6 +278,30 @@
     postFramebuffer();
 }
 
+void Output::updateLayerStateFromFE(const CompositionRefreshArgs& args) const {
+    for (auto& layer : mOutputLayersOrderedByZ) {
+        layer->getLayerFE().latchCompositionState(layer->getLayer().editState().frontEnd,
+                                                  args.updatingGeometryThisFrame);
+    }
+}
+
+void Output::updateAndWriteCompositionState(
+        const compositionengine::CompositionRefreshArgs& refreshArgs) {
+    ATRACE_CALL();
+    ALOGV(__FUNCTION__);
+
+    for (auto& layer : mOutputLayersOrderedByZ) {
+        if (refreshArgs.devOptForceClientComposition) {
+            layer->editState().forceClientComposition = true;
+        }
+
+        layer->updateCompositionState(refreshArgs.updatingGeometryThisFrame);
+
+        // Send the updated state to the HWC, if appropriate.
+        layer->writeStateToHWC(refreshArgs.updatingGeometryThisFrame);
+    }
+}
+
 void Output::updateColorProfile(const compositionengine::CompositionRefreshArgs& refreshArgs) {
     setColorProfile(pickColorProfile(refreshArgs));
 }
@@ -491,7 +525,7 @@
 
     // Compute the global color transform matrix.
     if (!mState.usesDeviceComposition && !getSkipColorTransform()) {
-        clientCompositionDisplay.colorTransform = mState.colorTransformMat;
+        clientCompositionDisplay.colorTransform = mState.colorTransformMatrix;
     }
 
     // Note: Updated by generateClientCompositionRequests
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
index 3e47fe2..0fcc308 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
@@ -47,7 +47,7 @@
     dumpVal(out, "colorMode", toString(colorMode), colorMode);
     dumpVal(out, "renderIntent", toString(renderIntent), renderIntent);
     dumpVal(out, "dataspace", toString(dataspace), dataspace);
-    dumpVal(out, "colorTransform", colorTransform);
+    dumpVal(out, "colorTransformMatrix", colorTransformMatrix);
     dumpVal(out, "target dataspace", toString(targetDataspace), targetDataspace);
 
     out.append("\n");
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 06e3a70..008e631 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -138,23 +138,26 @@
  */
 
 TEST_F(DisplayTest, setColorTransformSetsTransform) {
+    // No change does nothing
+    CompositionRefreshArgs refreshArgs;
+    refreshArgs.colorTransformMatrix = std::nullopt;
+    mDisplay.setColorTransform(refreshArgs);
+
     // Identity matrix sets an identity state value
-    const mat4 identity;
+    const mat4 kIdentity;
 
-    EXPECT_CALL(mHwComposer, setColorTransform(DEFAULT_DISPLAY_ID, identity)).Times(1);
+    EXPECT_CALL(mHwComposer, setColorTransform(DEFAULT_DISPLAY_ID, kIdentity)).Times(1);
 
-    mDisplay.setColorTransform(identity);
-
-    EXPECT_EQ(HAL_COLOR_TRANSFORM_IDENTITY, mDisplay.getState().colorTransform);
+    refreshArgs.colorTransformMatrix = kIdentity;
+    mDisplay.setColorTransform(refreshArgs);
 
     // Non-identity matrix sets a non-identity state value
-    const mat4 nonIdentity = mat4() * 2;
+    const mat4 kNonIdentity = mat4() * 2;
 
-    EXPECT_CALL(mHwComposer, setColorTransform(DEFAULT_DISPLAY_ID, nonIdentity)).Times(1);
+    EXPECT_CALL(mHwComposer, setColorTransform(DEFAULT_DISPLAY_ID, kNonIdentity)).Times(1);
 
-    mDisplay.setColorTransform(nonIdentity);
-
-    EXPECT_EQ(HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX, mDisplay.getState().colorTransform);
+    refreshArgs.colorTransformMatrix = kNonIdentity;
+    mDisplay.setColorTransform(refreshArgs);
 }
 
 /*
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index dccad58..1d5f2f0 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -45,6 +45,10 @@
 constexpr auto TR_IDENT = 0u;
 constexpr auto TR_ROT_90 = HAL_TRANSFORM_ROT_90;
 
+const mat4 kIdentity;
+const mat4 kNonIdentityHalf = mat4() * 0.5;
+const mat4 kNonIdentityQuarter = mat4() * 0.25;
+
 struct OutputTest : public testing::Test {
     OutputTest() {
         mOutput.setDisplayColorProfileForTest(
@@ -171,38 +175,84 @@
  * Output::setColorTransform
  */
 
-TEST_F(OutputTest, setColorTransformSetsTransform) {
-    // Identity matrix sets an identity state value
-    const mat4 identity;
+TEST_F(OutputTest, setColorTransformWithNoChangeFlaggedSkipsUpdates) {
+    mOutput.editState().colorTransformMatrix = kIdentity;
 
-    mOutput.setColorTransform(identity);
+    // If no colorTransformMatrix is set the update should be skipped.
+    CompositionRefreshArgs refreshArgs;
+    refreshArgs.colorTransformMatrix = std::nullopt;
 
-    EXPECT_EQ(HAL_COLOR_TRANSFORM_IDENTITY, mOutput.getState().colorTransform);
-    EXPECT_EQ(identity, mOutput.getState().colorTransformMat);
+    mOutput.setColorTransform(refreshArgs);
 
-    // Since identity is the default, the dirty region should be unchanged (empty)
+    // The internal state should be unchanged
+    EXPECT_EQ(kIdentity, mOutput.getState().colorTransformMatrix);
+
+    // No dirty region should be set
     EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region()));
+}
 
-    // Non-identity matrix sets a non-identity state value
-    const mat4 nonIdentityHalf = mat4() * 0.5;
+TEST_F(OutputTest, setColorTransformWithNoActualChangeSkipsUpdates) {
+    mOutput.editState().colorTransformMatrix = kIdentity;
 
-    mOutput.setColorTransform(nonIdentityHalf);
+    // Attempting to set the same colorTransformMatrix that is already set should
+    // also skip the update.
+    CompositionRefreshArgs refreshArgs;
+    refreshArgs.colorTransformMatrix = kIdentity;
 
-    EXPECT_EQ(HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX, mOutput.getState().colorTransform);
-    EXPECT_EQ(nonIdentityHalf, mOutput.getState().colorTransformMat);
+    mOutput.setColorTransform(refreshArgs);
 
-    // Since this is a state change, the entire output should now be dirty.
+    // The internal state should be unchanged
+    EXPECT_EQ(kIdentity, mOutput.getState().colorTransformMatrix);
+
+    // No dirty region should be set
+    EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region()));
+}
+
+TEST_F(OutputTest, setColorTransformPerformsUpdateToIdentity) {
+    mOutput.editState().colorTransformMatrix = kNonIdentityHalf;
+
+    // Setting a different colorTransformMatrix should perform the update.
+    CompositionRefreshArgs refreshArgs;
+    refreshArgs.colorTransformMatrix = kIdentity;
+
+    mOutput.setColorTransform(refreshArgs);
+
+    // The internal state should have been updated
+    EXPECT_EQ(kIdentity, mOutput.getState().colorTransformMatrix);
+
+    // The dirtyRegion should be set to the full display size
     EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
+}
 
-    // Non-identity matrix sets a non-identity state value
-    const mat4 nonIdentityQuarter = mat4() * 0.25;
+TEST_F(OutputTest, setColorTransformPerformsUpdateForIdentityToHalf) {
+    mOutput.editState().colorTransformMatrix = kIdentity;
 
-    mOutput.setColorTransform(nonIdentityQuarter);
+    // Setting a different colorTransformMatrix should perform the update.
+    CompositionRefreshArgs refreshArgs;
+    refreshArgs.colorTransformMatrix = kNonIdentityHalf;
 
-    EXPECT_EQ(HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX, mOutput.getState().colorTransform);
-    EXPECT_EQ(nonIdentityQuarter, mOutput.getState().colorTransformMat);
+    mOutput.setColorTransform(refreshArgs);
 
-    // Since this is a state change, the entire output should now be dirty.
+    // The internal state should have been updated
+    EXPECT_EQ(kNonIdentityHalf, mOutput.getState().colorTransformMatrix);
+
+    // The dirtyRegion should be set to the full display size
+    EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
+}
+
+TEST_F(OutputTest, setColorTransformPerformsUpdateForHalfToQuarter) {
+    mOutput.editState().colorTransformMatrix = kNonIdentityHalf;
+
+    // Setting a different colorTransformMatrix should perform the update.
+    CompositionRefreshArgs refreshArgs;
+    refreshArgs.colorTransformMatrix = kNonIdentityQuarter;
+
+    mOutput.setColorTransform(refreshArgs);
+
+    // The internal state should have been updated
+    EXPECT_EQ(kNonIdentityQuarter, mOutput.getState().colorTransformMatrix);
+
+    // The dirtyRegion should be set to the full display size
     EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
 }
 
@@ -502,7 +552,7 @@
         mOutput.editState().transform = ui::Transform{kDefaultOutputOrientation};
         mOutput.editState().orientation = kDefaultOutputOrientation;
         mOutput.editState().dataspace = kDefaultOutputDataspace;
-        mOutput.editState().colorTransformMat = kDefaultColorTransformMat;
+        mOutput.editState().colorTransformMatrix = kDefaultColorTransformMat;
         mOutput.editState().isSecure = true;
         mOutput.editState().needsFiltering = false;
         mOutput.editState().usesClientComposition = true;