SF: Move/Refactor rebuildLayerStacks and computeVisibleRegions

This is the one big final move of code to CompositionEngine.

The logic is restructured, though the basic blocks still correspond to
what existed before to allow for easier conflict resolution across the
move.

The function to compute the per-layer visibility information is still
much bigger than I like -- perhaps this can be cleaned up later after
more unit test coverage is introduced.

Test: atest libsurfaceflinger_unittest libcompositionengine_test
Test: atest CtsColorModeTestCases
Test: atest CtsDisplayTestCases
Test: atest CtsGraphicsTestCases
Test: atest CtsUiRenderingTestCases
Test: atest CtsViewTestCases
Test: atest android.media.cts.EncodeVirtualDisplayWithCompositionTest
Test: go/wm-smoke
Bug: 121291683
Change-Id: Ia3fb6c0fb0bb847ab737a1f92617d3724c08ea54
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
index 6f689ad..90158c7 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
@@ -18,6 +18,7 @@
 
 #include <chrono>
 #include <optional>
+#include <vector>
 
 #include <compositionengine/Display.h>
 #include <compositionengine/Layer.h>
@@ -28,6 +29,7 @@
 
 using Layers = std::vector<std::shared_ptr<compositionengine::Layer>>;
 using Outputs = std::vector<std::shared_ptr<compositionengine::Output>>;
+using RawLayers = std::vector<compositionengine::Layer*>;
 
 /**
  * A parameter object for refreshing a set of outputs
@@ -41,6 +43,9 @@
     // front.
     Layers layers;
 
+    // All the layers that have queued updates.
+    RawLayers layersWithQueuedFrames;
+
     // If true, forces the entire display to be considered dirty and repainted
     bool repaintEverything{false};
 
@@ -53,6 +58,9 @@
     // Forces a color mode on the outputs being refreshed
     ui::ColorMode forceOutputColorMode{ui::ColorMode::NATIVE};
 
+    // If true, the complete output geometry needs to be recomputed this frame
+    bool updatingOutputGeometryThisFrame{false};
+
     // If true, there was a geometry update this frame
     bool updatingGeometryThisFrame{false};
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index 57cca69..e585769 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <optional>
+#include <unordered_set>
 
 #include <renderengine/LayerSettings.h>
 #include <utils/RefBase.h>
@@ -99,5 +100,13 @@
     virtual const char* getDebugName() const = 0;
 };
 
+// TODO(b/121291683): Specialize std::hash<> for sp<T> so these and others can
+// be removed.
+struct LayerFESpHash {
+    size_t operator()(const sp<LayerFE>& p) const { return std::hash<LayerFE*>()(p.get()); }
+};
+
+using LayerFESet = std::unordered_set<sp<LayerFE>, LayerFESpHash>;
+
 } // namespace compositionengine
 } // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 80fce2c..d374baa 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -21,6 +21,7 @@
 #include <string>
 #include <unordered_map>
 
+#include <compositionengine/LayerFE.h>
 #include <renderengine/LayerSettings.h>
 #include <ui/Fence.h>
 #include <ui/GraphicTypes.h>
@@ -71,6 +72,22 @@
         ui::Dataspace colorSpaceAgnosticDataspace{ui::Dataspace::UNKNOWN};
     };
 
+    // Use internally to incrementally compute visibility/coverage
+    struct CoverageState {
+        explicit CoverageState(LayerFESet& latchedLayers) : latchedLayers(latchedLayers) {}
+
+        // The set of layers that had been latched for the coverage calls, to
+        // avoid duplicate requests to obtain the same front-end layer state.
+        LayerFESet& latchedLayers;
+
+        // The region of the output which is covered by layers
+        Region aboveCoveredLayers;
+        // The region of the output which is opaquely covered by layers
+        Region aboveOpaqueLayers;
+        // The region of the output which should be considered dirty
+        Region dirtyRegion;
+    };
+
     virtual ~Output();
 
     // Returns true if the output is valid. This is meant to be checked post-
@@ -164,7 +181,7 @@
     virtual ReleasedLayers takeReleasedLayers() = 0;
 
     // Prepare the output, updating the OutputLayers used in the output
-    virtual void prepare(CompositionRefreshArgs&) = 0;
+    virtual void prepare(const CompositionRefreshArgs&, LayerFESet&) = 0;
 
     // Presents the output, finalizing all composition details
     virtual void present(const CompositionRefreshArgs&) = 0;
@@ -176,6 +193,13 @@
     virtual void setDisplayColorProfile(std::unique_ptr<DisplayColorProfile>) = 0;
     virtual void setRenderSurface(std::unique_ptr<RenderSurface>) = 0;
 
+    virtual void rebuildLayerStacks(const compositionengine::CompositionRefreshArgs&,
+                                    LayerFESet&) = 0;
+    virtual void collectVisibleLayers(const CompositionRefreshArgs&, CoverageState&) = 0;
+    virtual std::unique_ptr<OutputLayer> getOutputLayerIfVisible(
+            std::shared_ptr<compositionengine::Layer>, CoverageState&) = 0;
+    virtual void setReleasedLayers(const compositionengine::CompositionRefreshArgs&) = 0;
+
     virtual void updateAndWriteCompositionState(const CompositionRefreshArgs&) = 0;
     virtual void setColorTransform(const CompositionRefreshArgs&) = 0;
     virtual void updateColorProfile(const CompositionRefreshArgs&) = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index 2e595d6..b5d8325 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -42,6 +42,8 @@
     void dump(std::string&) const override;
     std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(
             const std::shared_ptr<Layer>&, const sp<LayerFE>&) const override;
+    using compositionengine::impl::Output::setReleasedLayers;
+    void setReleasedLayers(const compositionengine::CompositionRefreshArgs&) override;
     void setColorTransform(const compositionengine::CompositionRefreshArgs&) override;
     void setColorProfile(const ColorProfile&) override;
     void chooseCompositionStrategy() override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index 6dde86d..fa6cd27 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -77,9 +77,17 @@
     void setReleasedLayers(ReleasedLayers&&) override;
     ReleasedLayers takeReleasedLayers() override;
 
-    void prepare(compositionengine::CompositionRefreshArgs&) override;
+    void prepare(const compositionengine::CompositionRefreshArgs&, LayerFESet&) override;
     void present(const compositionengine::CompositionRefreshArgs&) override;
 
+    void rebuildLayerStacks(const compositionengine::CompositionRefreshArgs&, LayerFESet&) override;
+    void collectVisibleLayers(const compositionengine::CompositionRefreshArgs&,
+                              compositionengine::Output::CoverageState&) override;
+    std::unique_ptr<compositionengine::OutputLayer> getOutputLayerIfVisible(
+            std::shared_ptr<compositionengine::Layer>,
+            compositionengine::Output::CoverageState&) override;
+    void setReleasedLayers(const compositionengine::CompositionRefreshArgs&) override;
+
     void updateLayerStateFromFE(const CompositionRefreshArgs&) const override;
     void updateAndWriteCompositionState(const compositionengine::CompositionRefreshArgs&) override;
     void updateColorProfile(const compositionengine::CompositionRefreshArgs&) override;
@@ -91,11 +99,14 @@
     void postFramebuffer() override;
 
     // Testing
+    const ReleasedLayers& getReleasedLayersForTest() const;
     void setDisplayColorProfileForTest(std::unique_ptr<compositionengine::DisplayColorProfile>);
     void setRenderSurfaceForTest(std::unique_ptr<compositionengine::RenderSurface>);
 
 protected:
     const CompositionEngine& getCompositionEngine() const;
+    std::unique_ptr<compositionengine::OutputLayer> takeOutputLayerForLayer(
+            compositionengine::Layer*);
     void chooseCompositionStrategy() override;
     bool getSkipColorTransform() const override;
     compositionengine::Output::FrameFences presentAndGetFrameFences() override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 8cf7f79..286a20f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -79,9 +79,20 @@
     MOCK_METHOD1(setReleasedLayers, void(ReleasedLayers&&));
     MOCK_METHOD0(takeReleasedLayers, ReleasedLayers());
 
-    MOCK_METHOD1(prepare, void(compositionengine::CompositionRefreshArgs&));
+    MOCK_METHOD2(prepare, void(const compositionengine::CompositionRefreshArgs&, LayerFESet&));
     MOCK_METHOD1(present, void(const compositionengine::CompositionRefreshArgs&));
 
+    MOCK_METHOD2(rebuildLayerStacks,
+                 void(const compositionengine::CompositionRefreshArgs&, LayerFESet&));
+    MOCK_METHOD2(collectVisibleLayers,
+                 void(const compositionengine::CompositionRefreshArgs&,
+                      compositionengine::Output::CoverageState&));
+    MOCK_METHOD2(getOutputLayerIfVisible,
+                 std::unique_ptr<compositionengine::OutputLayer>(
+                         std::shared_ptr<compositionengine::Layer>,
+                         compositionengine::Output::CoverageState&));
+    MOCK_METHOD1(setReleasedLayers, void(const compositionengine::CompositionRefreshArgs&));
+
     MOCK_CONST_METHOD1(updateLayerStateFromFE, void(const CompositionRefreshArgs&));
     MOCK_METHOD1(updateAndWriteCompositionState, void(const CompositionRefreshArgs&));
     MOCK_METHOD1(updateColorProfile, void(const compositionengine::CompositionRefreshArgs&));
diff --git a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
index 590c596..713266f 100644
--- a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
@@ -72,8 +72,20 @@
 }
 
 void CompositionEngine::present(CompositionRefreshArgs& args) {
-    for (const auto& output : args.outputs) {
-        output->prepare(args);
+    ATRACE_CALL();
+    ALOGV(__FUNCTION__);
+
+    preComposition(args);
+
+    {
+        // latchedLayers is used to track the set of front-end layer state that
+        // has been latched across all outputs for the prepare step, and is not
+        // needed for anything else.
+        LayerFESet latchedLayers;
+
+        for (const auto& output : args.outputs) {
+            output->prepare(args, latchedLayers);
+        }
     }
 
     updateLayerStateFromFE(args);
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 49727df..fe8fa21 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -158,6 +158,39 @@
     return result;
 }
 
+void Display::setReleasedLayers(const compositionengine::CompositionRefreshArgs& refreshArgs) {
+    Output::setReleasedLayers(refreshArgs);
+
+    if (!mId || refreshArgs.layersWithQueuedFrames.empty()) {
+        return;
+    }
+
+    // For layers that are being removed from a HWC display, and that have
+    // queued frames, add them to a a list of released layers so we can properly
+    // set a fence.
+    compositionengine::Output::ReleasedLayers releasedLayers;
+
+    // Any non-null entries in the current list of layers are layers that are no
+    // longer going to be visible
+    for (auto& layer : getOutputLayersOrderedByZ()) {
+        if (!layer) {
+            continue;
+        }
+
+        sp<compositionengine::LayerFE> layerFE(&layer->getLayerFE());
+        const bool hasQueuedFrames =
+                std::find(refreshArgs.layersWithQueuedFrames.cbegin(),
+                          refreshArgs.layersWithQueuedFrames.cend(),
+                          &layer->getLayer()) != refreshArgs.layersWithQueuedFrames.cend();
+
+        if (hasQueuedFrames) {
+            releasedLayers.emplace_back(layerFE);
+        }
+    }
+
+    setReleasedLayers(std::move(releasedLayers));
+}
+
 void Display::chooseCompositionStrategy() {
     ATRACE_CALL();
     ALOGV(__FUNCTION__);
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index a9b1d55..83df628 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -40,6 +40,27 @@
 
 namespace impl {
 
+namespace {
+
+template <typename T>
+class Reversed {
+public:
+    explicit Reversed(const T& container) : mContainer(container) {}
+    auto begin() { return mContainer.rbegin(); }
+    auto end() { return mContainer.rend(); }
+
+private:
+    const T& mContainer;
+};
+
+// Helper for enumerating over a container in reverse order
+template <typename T>
+Reversed<T> reversed(const T& c) {
+    return Reversed<T>(c);
+}
+
+} // namespace
+
 Output::Output(const CompositionEngine& compositionEngine)
       : mCompositionEngine(compositionEngine) {}
 
@@ -176,6 +197,10 @@
     mDisplayColorProfile = std::move(mode);
 }
 
+const Output::ReleasedLayers& Output::getReleasedLayersForTest() const {
+    return mReleasedLayers;
+}
+
 void Output::setDisplayColorProfileForTest(
         std::unique_ptr<compositionengine::DisplayColorProfile> mode) {
     mDisplayColorProfile = std::move(mode);
@@ -238,15 +263,24 @@
     return nullptr;
 }
 
-std::unique_ptr<compositionengine::OutputLayer> Output::getOrCreateOutputLayer(
-        std::shared_ptr<compositionengine::Layer> layer, sp<compositionengine::LayerFE> layerFE) {
+std::unique_ptr<compositionengine::OutputLayer> Output::takeOutputLayerForLayer(
+        compositionengine::Layer* layer) {
+    // Removes the outputLayer from mOutputLayersorderedByZ and transfers ownership to the caller.
     for (auto& outputLayer : mOutputLayersOrderedByZ) {
-        if (outputLayer && &outputLayer->getLayer() == layer.get()) {
+        if (outputLayer && &outputLayer->getLayer() == layer) {
             return std::move(outputLayer);
         }
     }
+    return nullptr;
+}
 
-    return createOutputLayer(layer, layerFE);
+std::unique_ptr<compositionengine::OutputLayer> Output::getOrCreateOutputLayer(
+        std::shared_ptr<compositionengine::Layer> layer, sp<compositionengine::LayerFE> layerFE) {
+    auto result = takeOutputLayerForLayer(layer.get());
+    if (!result) {
+        result = createOutputLayer(layer, layerFE);
+    }
+    return result;
 }
 
 std::unique_ptr<compositionengine::OutputLayer> Output::createOutputLayer(
@@ -271,19 +305,18 @@
     return std::move(mReleasedLayers);
 }
 
-void Output::prepare(compositionengine::CompositionRefreshArgs& refreshArgs) {
-    if (CC_LIKELY(!refreshArgs.updatingGeometryThisFrame)) {
-        return;
-    }
+void Output::prepare(const compositionengine::CompositionRefreshArgs& refreshArgs,
+                     LayerFESet& geomSnapshots) {
+    ATRACE_CALL();
+    ALOGV(__FUNCTION__);
 
-    uint32_t zOrder = 0;
-    for (auto& layer : mOutputLayersOrderedByZ) {
-        // Assign a simple Z order sequence to each visible layer.
-        layer->editState().z = zOrder++;
-    }
+    rebuildLayerStacks(refreshArgs, geomSnapshots);
 }
 
 void Output::present(const compositionengine::CompositionRefreshArgs& refreshArgs) {
+    ATRACE_CALL();
+    ALOGV(__FUNCTION__);
+
     updateColorProfile(refreshArgs);
     updateAndWriteCompositionState(refreshArgs);
     setColorTransform(refreshArgs);
@@ -294,6 +327,249 @@
     postFramebuffer();
 }
 
+void Output::rebuildLayerStacks(const compositionengine::CompositionRefreshArgs& refreshArgs,
+                                LayerFESet& layerFESet) {
+    ATRACE_CALL();
+    ALOGV(__FUNCTION__);
+
+    // Do nothing if this output is not enabled or there is no need to perform this update
+    if (!mState.isEnabled || CC_LIKELY(!refreshArgs.updatingOutputGeometryThisFrame)) {
+        return;
+    }
+
+    // Process the layers to determine visibility and coverage
+    compositionengine::Output::CoverageState coverage{layerFESet};
+    collectVisibleLayers(refreshArgs, coverage);
+
+    // Compute the resulting coverage for this output, and store it for later
+    const ui::Transform& tr = mState.transform;
+    Region undefinedRegion{mState.bounds};
+    undefinedRegion.subtractSelf(tr.transform(coverage.aboveOpaqueLayers));
+
+    mState.undefinedRegion = undefinedRegion;
+    mState.dirtyRegion.orSelf(coverage.dirtyRegion);
+}
+
+void Output::collectVisibleLayers(const compositionengine::CompositionRefreshArgs& refreshArgs,
+                                  compositionengine::Output::CoverageState& coverage) {
+    // We build up a list of all layers that are going to be visible in the new
+    // frame.
+    compositionengine::Output::OutputLayers newLayersSortedByZ;
+
+    // Evaluate the layers from front to back to determine what is visible. This
+    // also incrementally calculates the coverage information for each layer as
+    // well as the entire output.
+    for (auto& layer : reversed(refreshArgs.layers)) {
+        // Incrementally process the coverage for each layer, obtaining an
+        // optional outputLayer if the layer is visible.
+        auto outputLayer = getOutputLayerIfVisible(layer, coverage);
+        if (outputLayer) {
+            newLayersSortedByZ.emplace_back(std::move(outputLayer));
+        }
+
+        // TODO(b/121291683): Stop early if the output is completely covered and
+        // no more layers could even be visible underneath the ones on top.
+    }
+
+    // Since we walked the layers in reverse order, we need to reverse
+    // newLayersSortedByZ to get the back-to-front ordered list of layers.
+    std::reverse(newLayersSortedByZ.begin(), newLayersSortedByZ.end());
+
+    // Generate a simple Z-order values to each visible output layer
+    uint32_t zOrder = 0;
+    for (auto& outputLayer : newLayersSortedByZ) {
+        outputLayer->editState().z = zOrder++;
+    }
+
+    setReleasedLayers(refreshArgs);
+
+    mOutputLayersOrderedByZ = std::move(newLayersSortedByZ);
+}
+
+std::unique_ptr<compositionengine::OutputLayer> Output::getOutputLayerIfVisible(
+        std::shared_ptr<compositionengine::Layer> layer,
+        compositionengine::Output::CoverageState& coverage) {
+    // Note: Converts a wp<LayerFE> to a sp<LayerFE>
+    auto layerFE = layer->getLayerFE();
+    if (layerFE == nullptr) {
+        return nullptr;
+    }
+
+    // Ensure we have a snapshot of the basic geometry layer state. Limit the
+    // snapshots to once per frame for each candidate layer, as layers may
+    // appear on multiple outputs.
+    if (!coverage.latchedLayers.count(layerFE)) {
+        coverage.latchedLayers.insert(layerFE);
+        layerFE->latchCompositionState(layer->editState().frontEnd,
+                                       compositionengine::LayerFE::StateSubset::BasicGeometry);
+    }
+
+    // Obtain a read-only reference to the front-end layer state
+    const auto& layerFEState = layer->getState().frontEnd;
+
+    // Only consider the layers on the given layer stack
+    if (!belongsInOutput(layer.get())) {
+        return nullptr;
+    }
+
+    /*
+     * opaqueRegion: area of a surface that is fully opaque.
+     */
+    Region opaqueRegion;
+
+    /*
+     * visibleRegion: area of a surface that is visible on screen and not fully
+     * transparent. This is essentially the layer's footprint minus the opaque
+     * regions above it. Areas covered by a translucent surface are considered
+     * visible.
+     */
+    Region visibleRegion;
+
+    /*
+     * coveredRegion: area of a surface that is covered by all visible regions
+     * above it (which includes the translucent areas).
+     */
+    Region coveredRegion;
+
+    /*
+     * transparentRegion: area of a surface that is hinted to be completely
+     * transparent. This is only used to tell when the layer has no visible non-
+     * transparent regions and can be removed from the layer list. It does not
+     * affect the visibleRegion of this layer or any layers beneath it. The hint
+     * may not be correct if apps don't respect the SurfaceView restrictions
+     * (which, sadly, some don't).
+     */
+    Region transparentRegion;
+
+    // handle hidden surfaces by setting the visible region to empty
+    if (CC_UNLIKELY(!layerFEState.isVisible)) {
+        return nullptr;
+    }
+
+    const ui::Transform& tr = layerFEState.geomLayerTransform;
+
+    // Get the visible region
+    // TODO(b/121291683): Is it worth creating helper methods on LayerFEState
+    // for computations like this?
+    visibleRegion.set(Rect(tr.transform(layerFEState.geomLayerBounds)));
+
+    if (visibleRegion.isEmpty()) {
+        return nullptr;
+    }
+
+    // Remove the transparent area from the visible region
+    if (!layerFEState.isOpaque) {
+        if (tr.preserveRects()) {
+            // transform the transparent region
+            transparentRegion = tr.transform(layerFEState.transparentRegionHint);
+        } else {
+            // transformation too complex, can't do the
+            // transparent region optimization.
+            transparentRegion.clear();
+        }
+    }
+
+    // compute the opaque region
+    const int32_t layerOrientation = tr.getOrientation();
+    if (layerFEState.isOpaque && ((layerOrientation & ui::Transform::ROT_INVALID) == 0)) {
+        // If we one of the simple category of transforms (0/90/180/270 rotation
+        // + any flip), then the opaque region is the layer's footprint.
+        // Otherwise we don't try and compute the opaque region since there may
+        // be errors at the edges, and we treat the entire layer as
+        // translucent.
+        opaqueRegion = visibleRegion;
+    }
+
+    // Clip the covered region to the visible region
+    coveredRegion = coverage.aboveCoveredLayers.intersect(visibleRegion);
+
+    // Update accumAboveCoveredLayers for next (lower) layer
+    coverage.aboveCoveredLayers.orSelf(visibleRegion);
+
+    // subtract the opaque region covered by the layers above us
+    visibleRegion.subtractSelf(coverage.aboveOpaqueLayers);
+
+    if (visibleRegion.isEmpty()) {
+        return nullptr;
+    }
+
+    // Get coverage information for the layer as previously displayed,
+    // also taking over ownership from mOutputLayersorderedByZ.
+    auto prevOutputLayer = takeOutputLayerForLayer(layer.get());
+
+    //  Get coverage information for the layer as previously displayed
+    // TODO(b/121291683): Define kEmptyRegion as a constant in Region.h
+    const Region kEmptyRegion;
+    const Region& oldVisibleRegion =
+            prevOutputLayer ? prevOutputLayer->getState().visibleRegion : kEmptyRegion;
+    const Region& oldCoveredRegion =
+            prevOutputLayer ? prevOutputLayer->getState().coveredRegion : kEmptyRegion;
+
+    // compute this layer's dirty region
+    Region dirty;
+    if (layerFEState.contentDirty) {
+        // we need to invalidate the whole region
+        dirty = visibleRegion;
+        // as well, as the old visible region
+        dirty.orSelf(oldVisibleRegion);
+    } else {
+        /* compute the exposed region:
+         *   the exposed region consists of two components:
+         *   1) what's VISIBLE now and was COVERED before
+         *   2) what's EXPOSED now less what was EXPOSED before
+         *
+         * note that (1) is conservative, we start with the whole visible region
+         * but only keep what used to be covered by something -- which mean it
+         * may have been exposed.
+         *
+         * (2) handles areas that were not covered by anything but got exposed
+         * because of a resize.
+         *
+         */
+        const Region newExposed = visibleRegion - coveredRegion;
+        const Region oldExposed = oldVisibleRegion - oldCoveredRegion;
+        dirty = (visibleRegion & oldCoveredRegion) | (newExposed - oldExposed);
+    }
+    dirty.subtractSelf(coverage.aboveOpaqueLayers);
+
+    // accumulate to the screen dirty region
+    coverage.dirtyRegion.orSelf(dirty);
+
+    // Update accumAboveOpaqueLayers for next (lower) layer
+    coverage.aboveOpaqueLayers.orSelf(opaqueRegion);
+
+    // Compute the visible non-transparent region
+    Region visibleNonTransparentRegion = visibleRegion.subtract(transparentRegion);
+
+    // Peform the final check to see if this layer is visible on this output
+    // TODO(b/121291683): Why does this not use visibleRegion? (see outputSpaceVisibleRegion below)
+    Region drawRegion(mState.transform.transform(visibleNonTransparentRegion));
+    drawRegion.andSelf(mState.bounds);
+    if (drawRegion.isEmpty()) {
+        return nullptr;
+    }
+
+    // The layer is visible. Either reuse the existing outputLayer if we have
+    // one, or create a new one if we do not.
+    std::unique_ptr<compositionengine::OutputLayer> result =
+            prevOutputLayer ? std::move(prevOutputLayer) : createOutputLayer(layer, layerFE);
+
+    // Store the layer coverage information into the layer state as some of it
+    // is useful later.
+    auto& outputLayerState = result->editState();
+    outputLayerState.visibleRegion = visibleRegion;
+    outputLayerState.visibleNonTransparentRegion = visibleNonTransparentRegion;
+    outputLayerState.coveredRegion = coveredRegion;
+    outputLayerState.outputSpaceVisibleRegion =
+            mState.transform.transform(outputLayerState.visibleRegion.intersect(mState.viewport));
+
+    return result;
+}
+
+void Output::setReleasedLayers(const compositionengine::CompositionRefreshArgs&) {
+    // The base class does nothing with this call.
+}
+
 void Output::updateLayerStateFromFE(const CompositionRefreshArgs& args) const {
     for (auto& layer : mOutputLayersOrderedByZ) {
         layer->getLayerFE().latchCompositionState(layer->getLayer().editState().frontEnd,
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 6821ec1..74b3ada 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -277,6 +277,79 @@
 }
 
 /*
+ * Display::setReleasedLayers()
+ */
+
+TEST_F(DisplayTest, setReleasedLayersDoesNothingIfNotHwcDisplay) {
+    std::shared_ptr<impl::Display> nonHwcDisplay{
+            impl::createDisplay(mCompositionEngine, DisplayCreationArgsBuilder().build())};
+
+    sp<mock::LayerFE> layerXLayerFE = new StrictMock<mock::LayerFE>();
+    mock::Layer layerXLayer;
+
+    {
+        Output::ReleasedLayers releasedLayers;
+        releasedLayers.emplace_back(layerXLayerFE);
+        nonHwcDisplay->setReleasedLayers(std::move(releasedLayers));
+    }
+
+    CompositionRefreshArgs refreshArgs;
+    refreshArgs.layersWithQueuedFrames.push_back(&layerXLayer);
+
+    nonHwcDisplay->setReleasedLayers(refreshArgs);
+
+    const auto& releasedLayers = nonHwcDisplay->getReleasedLayersForTest();
+    ASSERT_EQ(1, releasedLayers.size());
+}
+
+TEST_F(DisplayTest, setReleasedLayersDoesNothingIfNoLayersWithQueuedFrames) {
+    sp<mock::LayerFE> layerXLayerFE = new StrictMock<mock::LayerFE>();
+
+    {
+        Output::ReleasedLayers releasedLayers;
+        releasedLayers.emplace_back(layerXLayerFE);
+        mDisplay.setReleasedLayers(std::move(releasedLayers));
+    }
+
+    CompositionRefreshArgs refreshArgs;
+    mDisplay.setReleasedLayers(refreshArgs);
+
+    const auto& releasedLayers = mDisplay.getReleasedLayersForTest();
+    ASSERT_EQ(1, releasedLayers.size());
+}
+
+TEST_F(DisplayTest, setReleasedLayers) {
+    sp<mock::LayerFE> layer1LayerFE = new StrictMock<mock::LayerFE>();
+    sp<mock::LayerFE> layer2LayerFE = new StrictMock<mock::LayerFE>();
+    sp<mock::LayerFE> layer3LayerFE = new StrictMock<mock::LayerFE>();
+    sp<mock::LayerFE> layerXLayerFE = new StrictMock<mock::LayerFE>();
+    mock::Layer layer1Layer;
+    mock::Layer layer2Layer;
+    mock::Layer layer3Layer;
+    mock::Layer layerXLayer;
+
+    EXPECT_CALL(*mLayer1, getLayer()).WillRepeatedly(ReturnRef(layer1Layer));
+    EXPECT_CALL(*mLayer1, getLayerFE()).WillRepeatedly(ReturnRef(*layer1LayerFE.get()));
+    EXPECT_CALL(*mLayer2, getLayer()).WillRepeatedly(ReturnRef(layer2Layer));
+    EXPECT_CALL(*mLayer2, getLayerFE()).WillRepeatedly(ReturnRef(*layer2LayerFE.get()));
+    EXPECT_CALL(*mLayer3, getLayer()).WillRepeatedly(ReturnRef(layer3Layer));
+    EXPECT_CALL(*mLayer3, getLayerFE()).WillRepeatedly(ReturnRef(*layer3LayerFE.get()));
+
+    CompositionRefreshArgs refreshArgs;
+    refreshArgs.layersWithQueuedFrames.push_back(&layer1Layer);
+    refreshArgs.layersWithQueuedFrames.push_back(&layer2Layer);
+    refreshArgs.layersWithQueuedFrames.push_back(&layerXLayer);
+    refreshArgs.layersWithQueuedFrames.push_back(nullptr);
+
+    mDisplay.setReleasedLayers(refreshArgs);
+
+    const auto& releasedLayers = mDisplay.getReleasedLayersForTest();
+    ASSERT_EQ(2, releasedLayers.size());
+    ASSERT_EQ(layer1LayerFE.get(), releasedLayers[0].promote().get());
+    ASSERT_EQ(layer2LayerFE.get(), releasedLayers[1].promote().get());
+}
+
+/*
  * Display::chooseCompositionStrategy()
  */
 
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 344c5e7..f1508c7 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1784,6 +1784,7 @@
     mRefreshPending = false;
 
     compositionengine::CompositionRefreshArgs refreshArgs;
+    refreshArgs.outputs.reserve(mDisplays.size());
     for (const auto& [_, display] : mDisplays) {
         refreshArgs.outputs.push_back(display->getCompositionDisplay());
     }
@@ -1791,13 +1792,21 @@
         auto compositionLayer = layer->getCompositionLayer();
         if (compositionLayer) refreshArgs.layers.push_back(compositionLayer);
     });
+    refreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size());
+    for (sp<Layer> layer : mLayersWithQueuedFrames) {
+        auto compositionLayer = layer->getCompositionLayer();
+        if (compositionLayer) refreshArgs.layersWithQueuedFrames.push_back(compositionLayer.get());
+    }
+
     refreshArgs.repaintEverything = mRepaintEverything.exchange(false);
     refreshArgs.outputColorSetting = useColorManagement
             ? mDisplayColorSetting
             : compositionengine::OutputColorSetting::kUnmanaged;
     refreshArgs.colorSpaceAgnosticDataspace = mColorSpaceAgnosticDataspace;
     refreshArgs.forceOutputColorMode = mForceColorMode;
-    refreshArgs.updatingGeometryThisFrame = mGeometryInvalid;
+
+    refreshArgs.updatingOutputGeometryThisFrame = mVisibleRegionsDirty;
+    refreshArgs.updatingGeometryThisFrame = mGeometryInvalid || mVisibleRegionsDirty;
 
     if (CC_UNLIKELY(mDrawingState.colorMatrixChanged)) {
         refreshArgs.colorTransformMatrix = mDrawingState.colorMatrix;
@@ -1811,13 +1820,10 @@
                 std::chrono::milliseconds(mDebugRegion > 1 ? mDebugRegion : 0);
     }
 
-    mCompositionEngine->preComposition(refreshArgs);
-    rebuildLayerStacks();
-    refreshArgs.updatingGeometryThisFrame = mGeometryInvalid; // Can be set by rebuildLayerStacks()
-    mCompositionEngine->present(refreshArgs);
-
     mGeometryInvalid = false;
 
+    mCompositionEngine->present(refreshArgs);
+
     postFrame();
     postComposition();
 
@@ -2079,77 +2085,6 @@
     }
 }
 
-void SurfaceFlinger::rebuildLayerStacks() {
-    ATRACE_CALL();
-    ALOGV("rebuildLayerStacks");
-
-    // rebuild the visible layer list per screen
-    if (CC_UNLIKELY(mVisibleRegionsDirty)) {
-        ATRACE_NAME("rebuildLayerStacks VR Dirty");
-        invalidateHwcGeometry();
-
-        std::vector<sp<compositionengine::LayerFE>> layersWithQueuedFrames;
-        layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size());
-        for (sp<Layer> layer : mLayersWithQueuedFrames) {
-            layersWithQueuedFrames.push_back(layer);
-        }
-
-        for (const auto& pair : mDisplays) {
-            const auto& displayDevice = pair.second;
-            auto display = displayDevice->getCompositionDisplay();
-            const auto& displayState = display->getState();
-            Region opaqueRegion;
-            Region dirtyRegion;
-            compositionengine::Output::OutputLayers layersSortedByZ;
-            const ui::Transform& tr = displayState.transform;
-            const Rect bounds = displayState.bounds;
-            if (!displayState.isEnabled) {
-                continue;
-            }
-
-            computeVisibleRegions(displayDevice, dirtyRegion, opaqueRegion, layersSortedByZ);
-
-            // computeVisibleRegions walks the layers in reverse-Z order. Reverse
-            // to get a back-to-front ordering.
-            std::reverse(layersSortedByZ.begin(), layersSortedByZ.end());
-
-            display->setOutputLayersOrderedByZ(std::move(layersSortedByZ));
-
-            compositionengine::Output::ReleasedLayers releasedLayers;
-            if (displayDevice->getId() && !layersWithQueuedFrames.empty()) {
-                // For layers that are being removed from a HWC display,
-                // and that have queued frames, add them to a a list of
-                // released layers so we can properly set a fence.
-
-                // Any non-null entries in the current list of layers are layers
-                // that are no longer going to be visible
-                for (auto& layer : display->getOutputLayersOrderedByZ()) {
-                    if (!layer) {
-                        continue;
-                    }
-
-                    sp<compositionengine::LayerFE> layerFE(&layer->getLayerFE());
-                    const bool hasQueuedFrames =
-                            std::find(layersWithQueuedFrames.cbegin(),
-                                      layersWithQueuedFrames.cend(),
-                                      layerFE) != layersWithQueuedFrames.cend();
-
-                    if (hasQueuedFrames) {
-                        releasedLayers.emplace_back(layerFE);
-                    }
-                }
-            }
-            display->setReleasedLayers(std::move(releasedLayers));
-
-            Region undefinedRegion{bounds};
-            undefinedRegion.subtractSelf(tr.transform(opaqueRegion));
-
-            display->editState().undefinedRegion = undefinedRegion;
-            display->editState().dirtyRegion.orSelf(dirtyRegion);
-        }
-    }
-}
-
 void SurfaceFlinger::postFrame()
 {
     // |mStateLock| not needed as we are on the main thread
@@ -2506,7 +2441,7 @@
         // display is used to calculate the hint, otherwise we use the
         // default display.
         //
-        // NOTE: we do this here, rather than in rebuildLayerStacks() so that
+        // NOTE: we do this here, rather than when presenting the display so that
         // the hint is set before we acquire a buffer from the surface texture.
         //
         // NOTE: layer transactions have taken place already, so we use their
@@ -2737,181 +2672,6 @@
     }
 }
 
-void SurfaceFlinger::computeVisibleRegions(
-        const sp<const DisplayDevice>& displayDevice, Region& outDirtyRegion,
-        Region& outOpaqueRegion,
-        std::vector<std::unique_ptr<compositionengine::OutputLayer>>& outLayersSortedByZ) {
-    ATRACE_CALL();
-    ALOGV("computeVisibleRegions");
-
-    auto display = displayDevice->getCompositionDisplay();
-
-    Region aboveOpaqueLayers;
-    Region aboveCoveredLayers;
-    Region dirty;
-
-    outDirtyRegion.clear();
-
-    mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
-        auto compositionLayer = layer->getCompositionLayer();
-        if (compositionLayer == nullptr) {
-            return;
-        }
-
-        // 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(compositionLayer.get())) {
-            return;
-        }
-
-        /*
-         * opaqueRegion: area of a surface that is fully opaque.
-         */
-        Region opaqueRegion;
-
-        /*
-         * visibleRegion: area of a surface that is visible on screen
-         * and not fully transparent. This is essentially the layer's
-         * footprint minus the opaque regions above it.
-         * Areas covered by a translucent surface are considered visible.
-         */
-        Region visibleRegion;
-
-        /*
-         * coveredRegion: area of a surface that is covered by all
-         * visible regions above it (which includes the translucent areas).
-         */
-        Region coveredRegion;
-
-        /*
-         * transparentRegion: area of a surface that is hinted to be completely
-         * transparent. This is only used to tell when the layer has no visible
-         * non-transparent regions and can be removed from the layer list. It
-         * does not affect the visibleRegion of this layer or any layers
-         * beneath it. The hint may not be correct if apps don't respect the
-         * SurfaceView restrictions (which, sadly, some don't).
-         */
-        Region transparentRegion;
-
-        // handle hidden surfaces by setting the visible region to empty
-        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 (!layerFEState.isOpaque) {
-                    if (tr.preserveRects()) {
-                        // transform the transparent region
-                        transparentRegion = tr.transform(layerFEState.transparentRegionHint);
-                    } else {
-                        // transformation too complex, can't do the
-                        // transparent region optimization.
-                        transparentRegion.clear();
-                    }
-                }
-
-                // compute the opaque region
-                const int32_t layerOrientation = tr.getOrientation();
-                if (layerFEState.isOpaque &&
-                    ((layerOrientation & ui::Transform::ROT_INVALID) == false)) {
-                    // the opaque region is the layer's footprint
-                    opaqueRegion = visibleRegion;
-                }
-            }
-        }
-
-        if (visibleRegion.isEmpty()) {
-            return;
-        }
-
-        // Clip the covered region to the visible region
-        coveredRegion = aboveCoveredLayers.intersect(visibleRegion);
-
-        // Update aboveCoveredLayers for next (lower) layer
-        aboveCoveredLayers.orSelf(visibleRegion);
-
-        // subtract the opaque region covered by the layers above us
-        visibleRegion.subtractSelf(aboveOpaqueLayers);
-
-        //  Get coverage information for the layer as previously displayed
-        auto prevOutputLayer = display->getOutputLayerForLayer(compositionLayer.get());
-        // TODO(b/121291683): Define this as a constant in Region.h
-        const Region kEmptyRegion;
-        const Region& oldVisibleRegion =
-                prevOutputLayer ? prevOutputLayer->getState().visibleRegion : kEmptyRegion;
-        const Region& oldCoveredRegion =
-                prevOutputLayer ? prevOutputLayer->getState().coveredRegion : kEmptyRegion;
-
-        // compute this layer's dirty region
-        if (layerFEState.contentDirty) {
-            // we need to invalidate the whole region
-            dirty = visibleRegion;
-            // as well, as the old visible region
-            dirty.orSelf(oldVisibleRegion);
-        } else {
-            /* compute the exposed region:
-             *   the exposed region consists of two components:
-             *   1) what's VISIBLE now and was COVERED before
-             *   2) what's EXPOSED now less what was EXPOSED before
-             *
-             * note that (1) is conservative, we start with the whole
-             * visible region but only keep what used to be covered by
-             * something -- which mean it may have been exposed.
-             *
-             * (2) handles areas that were not covered by anything but got
-             * exposed because of a resize.
-             */
-            const Region newExposed = visibleRegion - coveredRegion;
-            const Region oldExposed = oldVisibleRegion - oldCoveredRegion;
-            dirty = (visibleRegion&oldCoveredRegion) | (newExposed-oldExposed);
-        }
-        dirty.subtractSelf(aboveOpaqueLayers);
-
-        // accumulate to the screen dirty region
-        outDirtyRegion.orSelf(dirty);
-
-        // Update aboveOpaqueLayers for next (lower) layer
-        aboveOpaqueLayers.orSelf(opaqueRegion);
-
-        // Compute the visible non-transparent region
-        Region visibleNonTransparentRegion = visibleRegion.subtract(transparentRegion);
-
-        // Setup an output layer for this output if the layer is visible on this
-        // output
-        const auto& displayState = display->getState();
-        Region drawRegion(displayState.transform.transform(visibleNonTransparentRegion));
-        drawRegion.andSelf(displayState.bounds);
-        if (drawRegion.isEmpty()) {
-            return;
-        }
-
-        outLayersSortedByZ.emplace_back(display->getOrCreateOutputLayer(compositionLayer, layerFE));
-        auto& outputLayerState = outLayersSortedByZ.back()->editState();
-        outputLayerState.visibleRegion = std::move(visibleRegion);
-        outputLayerState.visibleNonTransparentRegion = std::move(visibleNonTransparentRegion);
-        outputLayerState.coveredRegion = std::move(coveredRegion);
-        outputLayerState.outputSpaceVisibleRegion = displayState.transform.transform(
-                outputLayerState.visibleRegion.intersect(displayState.viewport));
-    });
-
-    outOpaqueRegion = aboveOpaqueLayers;
-}
-
 void SurfaceFlinger::invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty) {
     for (const auto& [token, displayDevice] : mDisplays) {
         auto display = displayDevice->getCompositionDisplay();
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index beb43d0..17cd821 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -745,9 +745,6 @@
      * Compositing
      */
     void invalidateHwcGeometry();
-    void computeVisibleRegions(
-            const sp<const DisplayDevice>& display, Region& dirtyRegion, Region& opaqueRegion,
-            std::vector<std::unique_ptr<compositionengine::OutputLayer>>& outputLayers);
 
     void postComposition();
     void getCompositorTiming(CompositorTiming* compositorTiming);
@@ -755,7 +752,6 @@
                                 std::shared_ptr<FenceTime>& presentFenceTime);
     void setCompositorTimingSnapped(const DisplayStatInfo& stats,
                                     nsecs_t compositeToPresentLatency);
-    void rebuildLayerStacks();
 
     void postFrame();