SF: Add ability to get basic geometry

For computing the layer visibility, CompositionEngine will need to get
basic geometry state for every layer, without getting everything. Add a
new request type for the basic geometry state, and modify the existing
code in computeVisibleRegions to use it rather than accessing directly
through the front-end layer pointer.

Test: atest libsurfaceflinger_unittest libcompositionengine_test
Test: go/wm-smoke
Bug: 121291683
Change-Id: Ie286fe1986a4c383ee390c1f646c7a8a5b8c14f4
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index db4f969..57cca69 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -39,9 +39,26 @@
     // process.
     virtual bool onPreComposition(nsecs_t refreshStartTime) = 0;
 
-    // Latches the output-independent state. If includeGeometry is false, the
-    // geometry state can be skipped.
-    virtual void latchCompositionState(LayerFECompositionState&, bool includeGeometry) const = 0;
+    // Used with latchCompositionState()
+    enum class StateSubset {
+        // Gets the basic geometry (bounds, transparent region, visibility,
+        // transforms, alpha) for the layer, for computing visibility and
+        // coverage.
+        BasicGeometry,
+
+        // Gets the full geometry (crops, buffer transforms, metadata) and
+        // content (buffer or color) state for the layer.
+        GeometryAndContent,
+
+        // Gets the per frame content (buffer or color) state the layer.
+        Content,
+    };
+
+    // Latches the output-independent composition state for the layer. The
+    // StateSubset argument selects what portion of the state is actually needed
+    // by the CompositionEngine code, since computing everything may be
+    // expensive.
+    virtual void latchCompositionState(LayerFECompositionState&, StateSubset) const = 0;
 
     // Latches the minimal bit of state for the cursor for a fast asynchronous
     // update.
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index b066cd1..530f49a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -40,6 +40,45 @@
     // the next geometry change.
     bool forceClientComposition{false};
 
+    // TODO(b/121291683): Reorganize and rename the contents of this structure
+
+    /*
+     * Visibility state
+     */
+    // the layer stack this layer belongs to
+    std::optional<uint32_t> layerStackId;
+
+    // If true, this layer should be only visible on the internal display
+    bool internalOnly{false};
+
+    // If false, this layer should not be considered visible
+    bool isVisible{true};
+
+    // True if the layer is completely opaque
+    bool isOpaque{true};
+
+    // If true, invalidates the entire visible region
+    bool contentDirty{false};
+
+    // The alpha value for this layer
+    float alpha{1.f};
+
+    // The transform from layer local coordinates to composition coordinates
+    ui::Transform geomLayerTransform;
+
+    // The inverse of the layer transform
+    ui::Transform geomInverseLayerTransform;
+
+    // The hint from the layer producer as to what portion of the layer is
+    // transparent.
+    Region transparentRegionHint;
+
+    // The blend mode for this layer
+    Hwc2::IComposerClient::BlendMode blendMode{Hwc2::IComposerClient::BlendMode::INVALID};
+
+    // The bounds of the layer in layer local coordinates
+    FloatRect geomLayerBounds;
+
     /*
      * Geometry state
      */
@@ -48,23 +87,9 @@
     bool geomUsesSourceCrop{false};
     bool geomBufferUsesDisplayInverseTransform{false};
     uint32_t geomBufferTransform{0};
-    ui::Transform geomLayerTransform;
-    ui::Transform geomInverseLayerTransform;
     Rect geomBufferSize;
     Rect geomContentCrop;
     Rect geomCrop;
-    Region geomActiveTransparentRegion;
-    FloatRect geomLayerBounds;
-
-    /*
-     * Presentation
-     */
-
-    // The blend mode for this layer
-    Hwc2::IComposerClient::BlendMode blendMode{Hwc2::IComposerClient::BlendMode::INVALID};
-
-    // The alpha value for this layer
-    float alpha{1.f};
 
     /*
      * Extra metadata
@@ -113,9 +138,6 @@
     mat4 colorTransform;
     bool colorTransformIsIdentity{true};
 
-    // True if the layer is completely opaque
-    bool isOpaque{true};
-
     // True if the layer has protected content
     bool hasProtectedContent{false};
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index a509ca8..43f44af 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -132,7 +132,7 @@
     // A layer belongs to the output if its layerStackId matches. Additionally
     // if the layer should only show in the internal (primary) display only and
     // this output allows that.
-    virtual bool belongsInOutput(uint32_t layerStackId, bool internalOnly) const = 0;
+    virtual bool belongsInOutput(std::optional<uint32_t> layerStackId, bool internalOnly) const = 0;
 
     // Returns a pointer to the output layer corresponding to the given layer on
     // this output, or nullptr if the layer does not have one
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index d826161..ece5b1c 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -62,7 +62,7 @@
     OutputCompositionState& editState() override;
 
     Region getDirtyRegion(bool repaintEverything) const override;
-    bool belongsInOutput(uint32_t, bool) const override;
+    bool belongsInOutput(std::optional<uint32_t>, bool) const override;
 
     compositionengine::OutputLayer* getOutputLayerForLayer(
             compositionengine::Layer*) const override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
index e280295..3eada3c 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
@@ -32,7 +32,8 @@
 
     MOCK_METHOD1(onPreComposition, bool(nsecs_t));
 
-    MOCK_CONST_METHOD2(latchCompositionState, void(LayerFECompositionState&, bool));
+    MOCK_CONST_METHOD2(latchCompositionState,
+                       void(LayerFECompositionState&, compositionengine::LayerFE::StateSubset));
     MOCK_CONST_METHOD1(latchCursorCompositionState, void(LayerFECompositionState&));
     MOCK_METHOD1(prepareClientComposition,
                  std::optional<renderengine::LayerSettings>(
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 33925d5..2f24c15 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -59,7 +59,7 @@
     MOCK_METHOD0(editState, OutputCompositionState&());
 
     MOCK_CONST_METHOD1(getDirtyRegion, Region(bool));
-    MOCK_CONST_METHOD2(belongsInOutput, bool(uint32_t, bool));
+    MOCK_CONST_METHOD2(belongsInOutput, bool(std::optional<uint32_t>, bool));
 
     MOCK_CONST_METHOD1(getOutputLayerForLayer,
                        compositionengine::OutputLayer*(compositionengine::Layer*));
diff --git a/services/surfaceflinger/CompositionEngine/src/LayerCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerCompositionState.cpp
index 0dc4bf1..c5debf6 100644
--- a/services/surfaceflinger/CompositionEngine/src/LayerCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/LayerCompositionState.cpp
@@ -45,7 +45,7 @@
     dumpVal(out, "geomBufferTransform", state.geomBufferTransform);
 
     out.append("\n      ");
-    dumpVal(out, "geomActiveTransparentRegion", state.geomActiveTransparentRegion);
+    dumpVal(out, "transparentRegionHint", state.transparentRegionHint);
 
     out.append("      ");
     dumpVal(out, "geomLayerBounds", state.geomLayerBounds);
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 9f4f259..4dfdeba 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -212,10 +212,11 @@
     return dirty;
 }
 
-bool Output::belongsInOutput(uint32_t layerStackId, bool internalOnly) const {
+bool Output::belongsInOutput(std::optional<uint32_t> layerStackId, bool internalOnly) const {
     // The layerStackId's must match, and also the layer must not be internal
     // only when not on an internal output.
-    return (layerStackId == mState.layerStackId) && (!internalOnly || mState.layerStackInternal);
+    return layerStackId && (*layerStackId == mState.layerStackId) &&
+            (!internalOnly || mState.layerStackInternal);
 }
 
 compositionengine::OutputLayer* Output::getOutputLayerForLayer(
@@ -281,7 +282,9 @@
 void Output::updateLayerStateFromFE(const CompositionRefreshArgs& args) const {
     for (auto& layer : mOutputLayersOrderedByZ) {
         layer->getLayerFE().latchCompositionState(layer->getLayer().editState().frontEnd,
-                                                  args.updatingGeometryThisFrame);
+                                                  args.updatingGeometryThisFrame
+                                                          ? LayerFE::StateSubset::GeometryAndContent
+                                                          : LayerFE::StateSubset::Content);
     }
 }
 
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 21f0ce8..3a0ebf8 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -102,7 +102,7 @@
     // pixels in the buffer.
 
     FloatRect activeCropFloat =
-            reduce(layerState.geomLayerBounds, layerState.geomActiveTransparentRegion);
+            reduce(layerState.geomLayerBounds, layerState.transparentRegionHint);
 
     const Rect& viewport = mOutput.getState().viewport;
     const ui::Transform& layerTransform = layerState.geomLayerTransform;
@@ -209,7 +209,7 @@
 
     // apply the layer's transform, followed by the display's global transform
     // here we're guaranteed that the layer's transform preserves rects
-    Region activeTransparentRegion = layerState.geomActiveTransparentRegion;
+    Region activeTransparentRegion = layerState.transparentRegionHint;
     const ui::Transform& layerTransform = layerState.geomLayerTransform;
     const ui::Transform& inverseLayerTransform = layerState.geomInverseLayerTransform;
     const Rect& bufferSize = layerState.geomBufferSize;
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index 2276dc3..65691ff 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -124,7 +124,7 @@
         // set one specific value to something different.
         mLayerState.frontEnd.geomUsesSourceCrop = true;
         mLayerState.frontEnd.geomContentCrop = Rect{0, 0, 1920, 1080};
-        mLayerState.frontEnd.geomActiveTransparentRegion = Region{};
+        mLayerState.frontEnd.transparentRegionHint = Region{};
         mLayerState.frontEnd.geomLayerBounds = FloatRect{0.f, 0.f, 1920.f, 1080.f};
         mLayerState.frontEnd.geomLayerTransform = ui::Transform{TR_IDENT};
         mLayerState.frontEnd.geomBufferSize = Rect{0, 0, 1920, 1080};
@@ -231,7 +231,7 @@
         // Set reasonable default values for a simple case. Each test will
         // set one specific value to something different.
 
-        mLayerState.frontEnd.geomActiveTransparentRegion = Region{};
+        mLayerState.frontEnd.transparentRegionHint = Region{};
         mLayerState.frontEnd.geomLayerTransform = ui::Transform{TR_IDENT};
         mLayerState.frontEnd.geomBufferSize = Rect{0, 0, 1920, 1080};
         mLayerState.frontEnd.geomBufferUsesDisplayInverseTransform = false;
@@ -256,7 +256,7 @@
 }
 
 TEST_F(OutputLayerDisplayFrameTest, fullActiveTransparentRegionReturnsEmptyFrame) {
-    mLayerState.frontEnd.geomActiveTransparentRegion = Region{Rect{0, 0, 1920, 1080}};
+    mLayerState.frontEnd.transparentRegionHint = Region{Rect{0, 0, 1920, 1080}};
     const Rect expected{0, 0, 0, 0};
     EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
 }
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index b0e8e36..70d9871 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -356,6 +356,10 @@
     // If the output accepts layerStack1 and internal-only layers....
     mOutput.setLayerStackFilter(layerStack1, true);
 
+    // A layer with no layerStack does not belong to it, internal-only or not.
+    EXPECT_FALSE(mOutput.belongsInOutput(std::nullopt, false));
+    EXPECT_FALSE(mOutput.belongsInOutput(std::nullopt, true));
+
     // Any layer with layerStack1 belongs to it, internal-only or not.
     EXPECT_TRUE(mOutput.belongsInOutput(layerStack1, false));
     EXPECT_TRUE(mOutput.belongsInOutput(layerStack1, true));
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 5e5302d..974488d 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -417,14 +417,43 @@
     win.bottom -= roundedCornersCrop.top;
 }
 
+void Layer::latchBasicGeometry(compositionengine::LayerFECompositionState& compositionState) const {
+    const auto& drawingState{getDrawingState()};
+    const uint32_t layerStack = getLayerStack();
+    const auto alpha = static_cast<float>(getAlpha());
+    const bool opaque = isOpaque(drawingState);
+    const bool usesRoundedCorners = getRoundedCornerState().radius != 0.f;
+
+    auto blendMode = Hwc2::IComposerClient::BlendMode::NONE;
+    if (!opaque || alpha != 1.0f) {
+        blendMode = mPremultipliedAlpha ? Hwc2::IComposerClient::BlendMode::PREMULTIPLIED
+                                        : Hwc2::IComposerClient::BlendMode::COVERAGE;
+    }
+
+    // TODO(b/121291683): Instead of filling in a passed-in compositionState
+    // structure, switch to Layer owning the structure and have
+    // CompositionEngine be able to get a reference to it.
+
+    compositionState.layerStackId =
+            (layerStack != ~0u) ? std::make_optional(layerStack) : std::nullopt;
+    compositionState.internalOnly = getPrimaryDisplayOnly();
+    compositionState.isVisible = isVisible();
+    compositionState.isOpaque = opaque && !usesRoundedCorners && alpha == 1.f;
+
+    compositionState.contentDirty = contentDirty;
+    contentDirty = false;
+
+    compositionState.geomLayerBounds = mBounds;
+    compositionState.geomLayerTransform = getTransform();
+    compositionState.geomInverseLayerTransform = compositionState.geomLayerTransform.inverse();
+    compositionState.transparentRegionHint = getActiveTransparentRegion(drawingState);
+
+    compositionState.blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode);
+    compositionState.alpha = alpha;
+}
+
 void Layer::latchGeometry(compositionengine::LayerFECompositionState& compositionState) const {
     const auto& drawingState{getDrawingState()};
-    auto alpha = static_cast<float>(getAlpha());
-    auto blendMode = HWC2::BlendMode::None;
-    if (!isOpaque(drawingState) || alpha != 1.0f) {
-        blendMode =
-                mPremultipliedAlpha ? HWC2::BlendMode::Premultiplied : HWC2::BlendMode::Coverage;
-    }
 
     int type = drawingState.metadata.getInt32(METADATA_WINDOW_TYPE, 0);
     int appId = drawingState.metadata.getInt32(METADATA_OWNER_UID, 0);
@@ -439,20 +468,14 @@
         }
     }
 
-    compositionState.geomLayerTransform = getTransform();
-    compositionState.geomInverseLayerTransform = compositionState.geomLayerTransform.inverse();
     compositionState.geomBufferSize = getBufferSize(drawingState);
     compositionState.geomContentCrop = getContentCrop();
     compositionState.geomCrop = getCrop(drawingState);
     compositionState.geomBufferTransform = mCurrentTransform;
     compositionState.geomBufferUsesDisplayInverseTransform = getTransformToDisplayInverse();
-    compositionState.geomActiveTransparentRegion = getActiveTransparentRegion(drawingState);
-    compositionState.geomLayerBounds = mBounds;
     compositionState.geomUsesSourceCrop = usesSourceCrop();
     compositionState.isSecure = isSecure();
 
-    compositionState.blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode);
-    compositionState.alpha = alpha;
     compositionState.type = type;
     compositionState.appId = appId;
 }
@@ -498,12 +521,24 @@
 }
 
 void Layer::latchCompositionState(compositionengine::LayerFECompositionState& compositionState,
-                                  bool includeGeometry) const {
-    if (includeGeometry) {
-        latchGeometry(compositionState);
-    }
+                                  compositionengine::LayerFE::StateSubset subset) const {
+    using StateSubset = compositionengine::LayerFE::StateSubset;
 
-    latchPerFrameState(compositionState);
+    switch (subset) {
+        case StateSubset::BasicGeometry:
+            latchBasicGeometry(compositionState);
+            break;
+
+        case StateSubset::GeometryAndContent:
+            latchBasicGeometry(compositionState);
+            latchGeometry(compositionState);
+            latchPerFrameState(compositionState);
+            break;
+
+        case StateSubset::Content:
+            latchPerFrameState(compositionState);
+            break;
+    }
 }
 
 const char* Layer::getDebugName() const {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 1486efe..02c78c9 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -471,7 +471,7 @@
      */
     bool onPreComposition(nsecs_t) override;
     void latchCompositionState(compositionengine::LayerFECompositionState&,
-                               bool includeGeometry) const override;
+                               compositionengine::LayerFE::StateSubset subset) const override;
     void latchCursorCompositionState(compositionengine::LayerFECompositionState&) const override;
     std::optional<renderengine::LayerSettings> prepareClientComposition(
             compositionengine::LayerFE::ClientCompositionTargetSettings&) override;
@@ -479,6 +479,7 @@
     const char* getDebugName() const override;
 
 protected:
+    void latchBasicGeometry(compositionengine::LayerFECompositionState& outState) const;
     void latchGeometry(compositionengine::LayerFECompositionState& outState) const;
     virtual void latchPerFrameState(compositionengine::LayerFECompositionState& outState) const;
 
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 3498419..6d853f9 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2757,11 +2757,22 @@
             return;
         }
 
-        // start with the whole surface at its current location
-        const Layer::State& s(layer->getDrawingState());
+        // Note: Converts a wp<LayerFE> to a sp<LayerFE>
+        auto layerFE = compositionLayer->getLayerFE();
+        if (layerFE == nullptr) {
+            return;
+        }
+
+        // Request a snapshot of the subset of state relevant to visibility
+        // determination
+        layerFE->latchCompositionState(compositionLayer->editState().frontEnd,
+                                       compositionengine::LayerFE::StateSubset::BasicGeometry);
+
+        // Work with a read-only copy of the snapshot
+        const auto& layerFEState = compositionLayer->getState().frontEnd;
 
         // only consider the layers on the given layer stack
-        if (!display->belongsInOutput(layer->getLayerStack(), layer->getPrimaryDisplayOnly())) {
+        if (!display->belongsInOutput(layerFEState.layerStackId, layerFEState.internalOnly)) {
             return;
         }
 
@@ -2795,18 +2806,17 @@
         Region transparentRegion;
 
         // handle hidden surfaces by setting the visible region to empty
-        if (CC_LIKELY(layer->isVisible())) {
-            const bool translucent = !layer->isOpaque(s);
-            Rect bounds(layer->getScreenBounds());
-
-            visibleRegion.set(bounds);
-            ui::Transform tr = layer->getTransform();
+        if (CC_LIKELY(layerFEState.isVisible)) {
+            // Get the visible region
+            visibleRegion.set(
+                    Rect(layerFEState.geomLayerTransform.transform(layerFEState.geomLayerBounds)));
+            const ui::Transform& tr = layerFEState.geomLayerTransform;
             if (!visibleRegion.isEmpty()) {
                 // Remove the transparent area from the visible region
-                if (translucent) {
+                if (!layerFEState.isOpaque) {
                     if (tr.preserveRects()) {
                         // transform the transparent region
-                        transparentRegion = tr.transform(layer->getActiveTransparentRegion(s));
+                        transparentRegion = tr.transform(layerFEState.transparentRegionHint);
                     } else {
                         // transformation too complex, can't do the
                         // transparent region optimization.
@@ -2816,9 +2826,8 @@
 
                 // compute the opaque region
                 const int32_t layerOrientation = tr.getOrientation();
-                if (layer->getAlpha() == 1.0f && !translucent &&
-                        layer->getRoundedCornerState().radius == 0.0f &&
-                        ((layerOrientation & ui::Transform::ROT_INVALID) == false)) {
+                if (layerFEState.isOpaque &&
+                    ((layerOrientation & ui::Transform::ROT_INVALID) == false)) {
                     // the opaque region is the layer's footprint
                     opaqueRegion = visibleRegion;
                 }
@@ -2848,12 +2857,11 @@
                 prevOutputLayer ? prevOutputLayer->getState().coveredRegion : kEmptyRegion;
 
         // compute this layer's dirty region
-        if (layer->contentDirty) {
+        if (layerFEState.contentDirty) {
             // we need to invalidate the whole region
             dirty = visibleRegion;
             // as well, as the old visible region
             dirty.orSelf(oldVisibleRegion);
-            layer->contentDirty = false;
         } else {
             /* compute the exposed region:
              *   the exposed region consists of two components:
@@ -2892,8 +2900,6 @@
         }
 
         const auto displayId = displayDevice->getId();
-        sp<compositionengine::LayerFE> layerFE = compositionLayer->getLayerFE();
-        LOG_ALWAYS_FATAL_IF(layerFE.get() == nullptr);
 
         outLayersSortedByZ.emplace_back(
                 display->getOrCreateOutputLayer(displayId, compositionLayer, layerFE));