[sf] optimize snapshot updates

Modify the snapshot update logic so its easier to maintain. Instead of
walking the tree and updating all the snapshots, this cl introduces a
preliminary step of walking though all the requested changes and
updating the affected snapshots by merging the changes to the snapshot.
This is followed by walking down the tree and updating properties that
are dependent on the parent snapshots.

If the changes are confined to buffer updates, then the fast path avoids
walking down the tree.

Bug: 238781169
Test: presubmit

Change-Id: Ic9aa66c376bf7ea80e38b321dd08b8d0f69559a9
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
index 5913d4b..163d345 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
@@ -16,7 +16,7 @@
 
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 #undef LOG_TAG
-#define LOG_TAG "LayerHierarchy"
+#define LOG_TAG "SurfaceFlinger"
 
 #include "LayerHierarchy.h"
 #include "LayerLog.h"
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.h b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
index b25b731..5389ada 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.h
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
@@ -42,10 +42,10 @@
 class LayerHierarchy {
 public:
     enum Variant : uint32_t {
-        Attached,
-        Detached,
-        Relative,
-        Mirror,
+        Attached, // child of the parent
+        Detached, // child of the parent but currently relative parented to another layer
+        Relative, // relative child of the parent
+        Mirror,   // mirrored from another layer
         ftl_first = Attached,
         ftl_last = Mirror,
     };
diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
index c9eb9c4..1712137 100644
--- a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
@@ -17,7 +17,7 @@
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
 #undef LOG_TAG
-#define LOG_TAG "LayerLifecycleManager"
+#define LOG_TAG "SurfaceFlinger"
 
 #include "LayerLifecycleManager.h"
 #include "Client.h" // temporarily needed for LayerCreationArgs
@@ -51,6 +51,7 @@
                              it->second.owner.getDebugString().c_str());
         }
         mAddedLayers.push_back(newLayer.get());
+        mChangedLayers.push_back(newLayer.get());
         layer.parentId = linkLayer(layer.parentId, layer.id);
         layer.relativeParentId = linkLayer(layer.relativeParentId, layer.id);
         if (layer.layerStackToMirror != ui::INVALID_LAYER_STACK) {
@@ -202,6 +203,10 @@
                 continue;
             }
 
+            if (layer->changes.get() == 0) {
+                mChangedLayers.push_back(layer);
+            }
+
             if (transaction.flags & ISurfaceComposer::eAnimation) {
                 layer->changes |= RequestedLayerState::Changes::Animation;
             }
@@ -244,6 +249,7 @@
                     bgColorLayer->what |= layer_state_t::eColorChanged |
                             layer_state_t::eDataspaceChanged | layer_state_t::eAlphaChanged;
                     bgColorLayer->changes |= RequestedLayerState::Changes::Content;
+                    mChangedLayers.push_back(bgColorLayer);
                     mGlobalChanges |= RequestedLayerState::Changes::Content;
                 }
             }
@@ -290,6 +296,7 @@
         }
     }
     mDestroyedLayers.clear();
+    mChangedLayers.clear();
     mGlobalChanges.clear();
 }
 
@@ -310,10 +317,25 @@
     return mDestroyedLayers;
 }
 
+const std::vector<RequestedLayerState*>& LayerLifecycleManager::getChangedLayers() const {
+    return mChangedLayers;
+}
+
 const ftl::Flags<RequestedLayerState::Changes> LayerLifecycleManager::getGlobalChanges() const {
     return mGlobalChanges;
 }
 
+const RequestedLayerState* LayerLifecycleManager::getLayerFromId(uint32_t id) const {
+    if (id == UNASSIGNED_LAYER_ID) {
+        return nullptr;
+    }
+    auto it = mIdToLayer.find(id);
+    if (it == mIdToLayer.end()) {
+        return nullptr;
+    }
+    return &it->second.owner;
+}
+
 RequestedLayerState* LayerLifecycleManager::getLayerFromId(uint32_t id) {
     if (id == UNASSIGNED_LAYER_ID) {
         return nullptr;
diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
index f0d2c22..48571bf 100644
--- a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
+++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
@@ -76,7 +76,9 @@
     void removeLifecycleListener(std::shared_ptr<ILifecycleListener>);
     const std::vector<std::unique_ptr<RequestedLayerState>>& getLayers() const;
     const std::vector<std::unique_ptr<RequestedLayerState>>& getDestroyedLayers() const;
+    const std::vector<RequestedLayerState*>& getChangedLayers() const;
     const ftl::Flags<RequestedLayerState::Changes> getGlobalChanges() const;
+    const RequestedLayerState* getLayerFromId(uint32_t) const;
 
 private:
     friend class LayerLifecycleManagerTest;
@@ -111,6 +113,8 @@
     // Keeps track of all the layers that were added in order. Changes will be cleared once
     // committed.
     std::vector<RequestedLayerState*> mAddedLayers;
+    // Keeps track of new and layers with states changes since last commit.
+    std::vector<RequestedLayerState*> mChangedLayers;
 };
 
 } // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
index 3caeebe..f0826c6 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
@@ -16,7 +16,7 @@
 
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 #undef LOG_TAG
-#define LOG_TAG "LayerSnapshot"
+#define LOG_TAG "SurfaceFlinger"
 
 #include "LayerSnapshot.h"
 
@@ -24,6 +24,23 @@
 
 using namespace ftl::flag_operators;
 
+namespace {
+
+void updateSurfaceDamage(const RequestedLayerState& requested, bool hasReadyFrame,
+                         bool forceFullDamage, Region& outSurfaceDamageRegion) {
+    if (!hasReadyFrame) {
+        outSurfaceDamageRegion.clear();
+        return;
+    }
+    if (forceFullDamage) {
+        outSurfaceDamageRegion = Region::INVALID_REGION;
+    } else {
+        outSurfaceDamageRegion = requested.surfaceDamageRegion;
+    }
+}
+
+} // namespace
+
 LayerSnapshot::LayerSnapshot(const RequestedLayerState& state,
                              const LayerHierarchy::TraversalPath& path)
       : path(path) {
@@ -51,9 +68,11 @@
     uid = state.ownerUid;
     pid = state.ownerPid;
     changes = RequestedLayerState::Changes::Created;
+    clientChanges = 0;
     mirrorRootPath = path.variant == LayerHierarchy::Variant::Mirror
             ? path
             : LayerHierarchy::TraversalPath::ROOT;
+    reachablilty = LayerSnapshot::Reachablilty::Unreachable;
 }
 
 // As documented in libhardware header, formats in the range
@@ -131,6 +150,10 @@
 }
 
 bool LayerSnapshot::getIsVisible() const {
+    if (reachablilty != LayerSnapshot::Reachablilty::Reachable) {
+        return false;
+    }
+
     if (handleSkipScreenshotFlag & outputFilter.toInternalDisplay) {
         return false;
     }
@@ -148,12 +171,16 @@
 
 std::string LayerSnapshot::getIsVisibleReason() const {
     // not visible
-    if (handleSkipScreenshotFlag & outputFilter.toInternalDisplay) return "eLayerSkipScreenshot";
-    if (!hasSomethingToDraw()) return "!hasSomethingToDraw";
-    if (invalidTransform) return "invalidTransform";
+    if (reachablilty == LayerSnapshot::Reachablilty::Unreachable)
+        return "layer not reachable from root";
+    if (reachablilty == LayerSnapshot::Reachablilty::ReachableByRelativeParent)
+        return "layer only reachable via relative parent";
     if (isHiddenByPolicyFromParent) return "hidden by parent or layer flag";
     if (isHiddenByPolicyFromRelativeParent) return "hidden by relative parent";
+    if (handleSkipScreenshotFlag & outputFilter.toInternalDisplay) return "eLayerSkipScreenshot";
+    if (invalidTransform) return "invalidTransform";
     if (color.a == 0.0f && !hasBlur()) return "alpha = 0 and no blur";
+    if (!hasSomethingToDraw()) return "!hasSomethingToDraw";
 
     // visible
     std::stringstream reason;
@@ -177,8 +204,9 @@
 }
 
 bool LayerSnapshot::hasInputInfo() const {
-    return inputInfo.token != nullptr ||
-            inputInfo.inputConfig.test(gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL);
+    return (inputInfo.token != nullptr ||
+            inputInfo.inputConfig.test(gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL)) &&
+            reachablilty == Reachablilty::Reachable;
 }
 
 std::string LayerSnapshot::getDebugString() const {
@@ -191,8 +219,16 @@
           << " geomLayerTransform={tx=" << geomLayerTransform.tx()
           << ",ty=" << geomLayerTransform.ty() << "}"
           << "}";
-    debug << " input{ touchCropId=" << touchCropId
-          << " replaceTouchableRegionWithCrop=" << inputInfo.replaceTouchableRegionWithCrop << "}";
+    if (hasInputInfo()) {
+        debug << " input{"
+              << "(" << inputInfo.inputConfig.string() << ")";
+        if (touchCropId != UNASSIGNED_LAYER_ID) debug << " touchCropId=" << touchCropId;
+        if (inputInfo.replaceTouchableRegionWithCrop) debug << " replaceTouchableRegionWithCrop";
+        auto touchableRegion = inputInfo.touchableRegion.getBounds();
+        debug << " touchableRegion={" << touchableRegion.left << "," << touchableRegion.top << ","
+              << touchableRegion.bottom << "," << touchableRegion.right << "}"
+              << "}";
+    }
     return debug.str();
 }
 
@@ -203,4 +239,172 @@
     return geomBufferSize.toFloatRect();
 }
 
+Hwc2::IComposerClient::BlendMode LayerSnapshot::getBlendMode(
+        const RequestedLayerState& requested) const {
+    auto blendMode = Hwc2::IComposerClient::BlendMode::NONE;
+    if (alpha != 1.0f || !contentOpaque) {
+        blendMode = requested.premultipliedAlpha ? Hwc2::IComposerClient::BlendMode::PREMULTIPLIED
+                                                 : Hwc2::IComposerClient::BlendMode::COVERAGE;
+    }
+    return blendMode;
+}
+
+void LayerSnapshot::merge(const RequestedLayerState& requested, bool forceUpdate,
+                          bool displayChanges, bool forceFullDamage,
+                          uint32_t displayRotationFlags) {
+    clientChanges = requested.what;
+    changes = requested.changes;
+    contentDirty = requested.what & layer_state_t::CONTENT_DIRTY;
+    // TODO(b/238781169) scope down the changes to only buffer updates.
+    hasReadyFrame = requested.hasReadyFrame();
+    sidebandStreamHasFrame = requested.hasSidebandStreamFrame();
+    updateSurfaceDamage(requested, hasReadyFrame, forceFullDamage, surfaceDamage);
+
+    if (forceUpdate || requested.what & layer_state_t::eTransparentRegionChanged) {
+        transparentRegionHint = requested.transparentRegion;
+    }
+    if (forceUpdate || requested.what & layer_state_t::eFlagsChanged) {
+        layerOpaqueFlagSet =
+                (requested.flags & layer_state_t::eLayerOpaque) == layer_state_t::eLayerOpaque;
+    }
+    if (forceUpdate || requested.what & layer_state_t::eBufferTransformChanged) {
+        geomBufferTransform = requested.bufferTransform;
+    }
+    if (forceUpdate || requested.what & layer_state_t::eTransformToDisplayInverseChanged) {
+        geomBufferUsesDisplayInverseTransform = requested.transformToDisplayInverse;
+    }
+    if (forceUpdate || requested.what & layer_state_t::eDataspaceChanged) {
+        dataspace = requested.dataspace;
+    }
+    if (forceUpdate || requested.what & layer_state_t::eExtendedRangeBrightnessChanged) {
+        currentHdrSdrRatio = requested.currentHdrSdrRatio;
+        desiredHdrSdrRatio = requested.desiredHdrSdrRatio;
+    }
+    if (forceUpdate || requested.what & layer_state_t::eCachingHintChanged) {
+        cachingHint = requested.cachingHint;
+    }
+    if (forceUpdate || requested.what & layer_state_t::eHdrMetadataChanged) {
+        hdrMetadata = requested.hdrMetadata;
+    }
+    if (forceUpdate || requested.what & layer_state_t::eSidebandStreamChanged) {
+        sidebandStream = requested.sidebandStream;
+    }
+    if (forceUpdate || requested.what & layer_state_t::eShadowRadiusChanged) {
+        shadowRadius = requested.shadowRadius;
+        shadowSettings.length = requested.shadowRadius;
+    }
+    if (forceUpdate || requested.what & layer_state_t::eFrameRateSelectionPriority) {
+        frameRateSelectionPriority = requested.frameRateSelectionPriority;
+    }
+    if (forceUpdate || requested.what & layer_state_t::eColorSpaceAgnosticChanged) {
+        isColorspaceAgnostic = requested.colorSpaceAgnostic;
+    }
+    if (forceUpdate || requested.what & layer_state_t::eDimmingEnabledChanged) {
+        dimmingEnabled = requested.dimmingEnabled;
+    }
+    if (forceUpdate || requested.what & layer_state_t::eCropChanged) {
+        geomCrop = requested.crop;
+    }
+
+    if (forceUpdate ||
+        requested.what &
+                (layer_state_t::eFlagsChanged | layer_state_t::eBufferChanged |
+                 layer_state_t::eSidebandStreamChanged)) {
+        compositionType = requested.getCompositionType();
+    }
+
+    if (forceUpdate || requested.what & layer_state_t::eInputInfoChanged) {
+        if (requested.windowInfoHandle) {
+            inputInfo = *requested.windowInfoHandle->getInfo();
+        } else {
+            inputInfo = {};
+            // b/271132344 revisit this and see if we can always use the layers uid/pid
+            inputInfo.name = requested.name;
+            inputInfo.ownerUid = requested.ownerUid;
+            inputInfo.ownerPid = requested.ownerPid;
+        }
+        inputInfo.id = static_cast<int32_t>(uniqueSequence);
+        touchCropId = requested.touchCropId;
+    }
+
+    if (forceUpdate ||
+        requested.what &
+                (layer_state_t::eColorChanged | layer_state_t::eBufferChanged |
+                 layer_state_t::eSidebandStreamChanged)) {
+        color.rgb = requested.getColor().rgb;
+    }
+
+    if (forceUpdate || requested.what & layer_state_t::eBufferChanged) {
+        acquireFence =
+                (requested.externalTexture &&
+                 requested.bufferData->flags.test(BufferData::BufferDataChange::fenceChanged))
+                ? requested.bufferData->acquireFence
+                : Fence::NO_FENCE;
+        buffer = requested.externalTexture ? requested.externalTexture->getBuffer() : nullptr;
+        externalTexture = requested.externalTexture;
+        frameNumber = (requested.bufferData) ? requested.bufferData->frameNumber : 0;
+        hasProtectedContent = requested.externalTexture &&
+                requested.externalTexture->getUsage() & GRALLOC_USAGE_PROTECTED;
+        geomUsesSourceCrop = hasBufferOrSidebandStream();
+    }
+
+    if (forceUpdate ||
+        requested.what &
+                (layer_state_t::eCropChanged | layer_state_t::eBufferCropChanged |
+                 layer_state_t::eBufferTransformChanged |
+                 layer_state_t::eTransformToDisplayInverseChanged) ||
+        requested.changes.test(RequestedLayerState::Changes::BufferSize) || displayChanges) {
+        bufferSize = requested.getBufferSize(displayRotationFlags);
+        geomBufferSize = bufferSize;
+        croppedBufferSize = requested.getCroppedBufferSize(bufferSize);
+        geomContentCrop = requested.getBufferCrop();
+    }
+
+    if (forceUpdate ||
+        requested.what &
+                (layer_state_t::eFlagsChanged | layer_state_t::eDestinationFrameChanged |
+                 layer_state_t::ePositionChanged | layer_state_t::eMatrixChanged |
+                 layer_state_t::eBufferTransformChanged |
+                 layer_state_t::eTransformToDisplayInverseChanged) ||
+        requested.changes.test(RequestedLayerState::Changes::BufferSize) || displayChanges) {
+        localTransform = requested.getTransform(displayRotationFlags);
+        localTransformInverse = localTransform.inverse();
+    }
+
+    if (forceUpdate || requested.what & (layer_state_t::eColorChanged) ||
+        requested.changes.test(RequestedLayerState::Changes::BufferSize)) {
+        color.rgb = requested.getColor().rgb;
+    }
+
+    if (forceUpdate ||
+        requested.what &
+                (layer_state_t::eBufferChanged | layer_state_t::eDataspaceChanged |
+                 layer_state_t::eApiChanged)) {
+        isHdrY410 = requested.dataspace == ui::Dataspace::BT2020_ITU_PQ &&
+                requested.api == NATIVE_WINDOW_API_MEDIA &&
+                requested.bufferData->getPixelFormat() == HAL_PIXEL_FORMAT_RGBA_1010102;
+    }
+
+    if (forceUpdate ||
+        requested.what &
+                (layer_state_t::eBufferChanged | layer_state_t::eDataspaceChanged |
+                 layer_state_t::eApiChanged | layer_state_t::eShadowRadiusChanged |
+                 layer_state_t::eBlurRegionsChanged | layer_state_t::eStretchChanged)) {
+        forceClientComposition = isHdrY410 || shadowSettings.length > 0 ||
+                requested.blurRegions.size() > 0 || stretchEffect.hasEffect();
+    }
+
+    if (forceUpdate ||
+        requested.what &
+                (layer_state_t::eColorChanged | layer_state_t::eShadowRadiusChanged |
+                 layer_state_t::eBlurRegionsChanged | layer_state_t::eBackgroundBlurRadiusChanged |
+                 layer_state_t::eCornerRadiusChanged | layer_state_t::eAlphaChanged |
+                 layer_state_t::eFlagsChanged | layer_state_t::eBufferChanged |
+                 layer_state_t::eSidebandStreamChanged)) {
+        contentOpaque = isContentOpaque();
+        isOpaque = contentOpaque && !roundedCorner.hasRoundedCorners() && color.a == 1.f;
+        blendMode = getBlendMode(requested);
+    }
+}
+
 } // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.h b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
index b167d3e..2f45d52 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
@@ -18,6 +18,7 @@
 
 #include <compositionengine/LayerFECompositionState.h>
 #include <renderengine/LayerSettings.h>
+#include "DisplayHardware/ComposerHal.h"
 #include "LayerHierarchy.h"
 #include "RequestedLayerState.h"
 #include "Scheduler/LayerInfo.h"
@@ -57,6 +58,7 @@
     bool isHiddenByPolicyFromParent = false;
     bool isHiddenByPolicyFromRelativeParent = false;
     ftl::Flags<RequestedLayerState::Changes> changes;
+    uint64_t clientChanges = 0;
     // Some consumers of this snapshot (input, layer traces) rely on each snapshot to be unique.
     // For mirrored layers, snapshots will have the same sequence so this unique id provides
     // an alternative identifier when needed.
@@ -93,11 +95,37 @@
     bool handleSkipScreenshotFlag = false;
     int32_t frameRateSelectionPriority;
     LayerHierarchy::TraversalPath mirrorRootPath;
-    bool unreachable = true;
     uint32_t touchCropId;
-    uid_t uid;
-    pid_t pid;
+    gui::Uid uid = gui::Uid::INVALID;
+    gui::Pid pid = gui::Pid::INVALID;
     ChildState childState;
+    enum class Reachablilty : uint32_t {
+        // Can traverse the hierarchy from a root node and reach this snapshot
+        Reachable,
+        // Cannot traverse the hierarchy from a root node and reach this snapshot
+        Unreachable,
+        // Can only reach this node from a relative parent. This means the nodes parents are
+        // not reachable.
+        // See example scenario:
+        // ROOT
+        // ├── 1
+        // │   ├── 11
+        // │   │   └── 111
+        // │   ├── 12
+        // │   │   └ - 111 (relative)
+        // │   ├── 13
+        // │   └── 14
+        // │       └ * 12 (mirroring)
+        // └── 2
+        // 111 will create two snapshots, first when visited from 1 -> 12 or 1 -> 11 and the
+        // second when visited from 1 -> 14 -> 12. Because its parent 11 doesn't exist in the
+        // mirrored hierarchy, the second snapshot will be marked as ReachableByRelativeParent.
+        // This snapshot doesn't have any valid properties because it cannot inherit from its
+        // parent. Therefore, snapshots that are not reachable will be ignored for composition
+        // and input.
+        ReachableByRelativeParent
+    };
+    Reachablilty reachablilty;
 
     static bool isOpaqueFormat(PixelFormat format);
     static bool isTransformValid(const ui::Transform& t);
@@ -116,6 +144,10 @@
     std::string getIsVisibleReason() const;
     bool hasInputInfo() const;
     FloatRect sourceBounds() const;
+    Hwc2::IComposerClient::BlendMode getBlendMode(const RequestedLayerState& requested) const;
+
+    void merge(const RequestedLayerState& requested, bool forceUpdate, bool displayChanges,
+               bool forceFullDamage, uint32_t displayRotationFlags);
 };
 
 } // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index a266493..21f0a67 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -17,13 +17,14 @@
 // #define LOG_NDEBUG 0
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 #undef LOG_TAG
-#define LOG_TAG "LayerSnapshotBuilder"
+#define LOG_TAG "SurfaceFlinger"
 
 #include <numeric>
 #include <optional>
 
 #include <ftl/small_map.h>
 #include <gui/TraceUtils.h>
+#include <ui/DisplayMap.h>
 #include <ui/FloatRect.h>
 
 #include "DisplayHardware/HWC2.h"
@@ -257,19 +258,6 @@
     return blendMode;
 }
 
-void updateSurfaceDamage(const RequestedLayerState& requested, bool hasReadyFrame,
-                         bool forceFullDamage, Region& outSurfaceDamageRegion) {
-    if (!hasReadyFrame) {
-        outSurfaceDamageRegion.clear();
-        return;
-    }
-    if (forceFullDamage) {
-        outSurfaceDamageRegion = Region::INVALID_REGION;
-    } else {
-        outSurfaceDamageRegion = requested.surfaceDamageRegion;
-    }
-}
-
 void updateVisibility(LayerSnapshot& snapshot, bool visible) {
     snapshot.isVisible = visible;
 
@@ -287,6 +275,8 @@
     const bool visibleForInput =
             snapshot.hasInputInfo() ? snapshot.canReceiveInput() : snapshot.isVisible;
     snapshot.inputInfo.setInputConfig(gui::WindowInfo::InputConfig::NOT_VISIBLE, !visibleForInput);
+    LLOGV(snapshot.sequence, "updating visibility %s %s", visible ? "true" : "false",
+          snapshot.getDebugString().c_str());
 }
 
 bool needsInputInfo(const LayerSnapshot& snapshot, const RequestedLayerState& requested) {
@@ -329,18 +319,31 @@
 
 void clearChanges(LayerSnapshot& snapshot) {
     snapshot.changes.clear();
+    snapshot.clientChanges = 0;
     snapshot.contentDirty = false;
     snapshot.hasReadyFrame = false;
     snapshot.sidebandStreamHasFrame = false;
     snapshot.surfaceDamage.clear();
 }
 
+// TODO (b/259407931): Remove.
+uint32_t getPrimaryDisplayRotationFlags(
+        const ui::DisplayMap<ui::LayerStack, frontend::DisplayInfo>& displays) {
+    for (auto& [_, display] : displays) {
+        if (display.isPrimary) {
+            return display.rotationFlags;
+        }
+    }
+    return 0;
+}
+
 } // namespace
 
 LayerSnapshot LayerSnapshotBuilder::getRootSnapshot() {
     LayerSnapshot snapshot;
     snapshot.path = LayerHierarchy::TraversalPath::ROOT;
     snapshot.changes = ftl::Flags<RequestedLayerState::Changes>();
+    snapshot.clientChanges = 0;
     snapshot.isHiddenByPolicyFromParent = false;
     snapshot.isHiddenByPolicyFromRelativeParent = false;
     snapshot.parentTransform.reset();
@@ -374,43 +377,44 @@
 }
 
 bool LayerSnapshotBuilder::tryFastUpdate(const Args& args) {
-    if (args.forceUpdate != ForceUpdateFlags::NONE || args.displayChanges) {
-        // force update requested, or we have display changes, so skip the fast path
-        return false;
-    }
+    const bool forceUpdate = args.forceUpdate != ForceUpdateFlags::NONE;
 
-    if (args.layerLifecycleManager.getGlobalChanges().get() == 0) {
+    if (args.layerLifecycleManager.getGlobalChanges().get() == 0 && !forceUpdate &&
+        !args.displayChanges) {
         return true;
     }
 
-    if (args.layerLifecycleManager.getGlobalChanges() != RequestedLayerState::Changes::Content) {
-        // We have changes that require us to walk the hierarchy and update child layers.
-        // No fast path for you.
-        return false;
-    }
-
     // There are only content changes which do not require any child layer snapshots to be updated.
     ALOGV("%s", __func__);
     ATRACE_NAME("FastPath");
 
-    // Collect layers with changes
-    ftl::SmallMap<uint32_t, RequestedLayerState*, 10> layersWithChanges;
-    for (auto& layer : args.layerLifecycleManager.getLayers()) {
-        if (layer->changes.test(RequestedLayerState::Changes::Content)) {
-            layersWithChanges.emplace_or_replace(layer->id, layer.get());
+    uint32_t primaryDisplayRotationFlags = getPrimaryDisplayRotationFlags(args.displays);
+    if (forceUpdate || args.displayChanges) {
+        for (auto& snapshot : mSnapshots) {
+            const RequestedLayerState* requested =
+                    args.layerLifecycleManager.getLayerFromId(snapshot->path.id);
+            if (!requested) continue;
+            snapshot->merge(*requested, forceUpdate, args.displayChanges, args.forceFullDamage,
+                            primaryDisplayRotationFlags);
+        }
+        return false;
+    }
+
+    // Walk through all the updated requested layer states and update the corresponding snapshots.
+    for (const RequestedLayerState* requested : args.layerLifecycleManager.getChangedLayers()) {
+        auto range = mIdToSnapshots.equal_range(requested->id);
+        for (auto it = range.first; it != range.second; it++) {
+            it->second->merge(*requested, forceUpdate, args.displayChanges, args.forceFullDamage,
+                              primaryDisplayRotationFlags);
         }
     }
 
-    // Walk through the snapshots, clearing previous change flags and updating the snapshots
-    // if needed.
-    for (auto& snapshot : mSnapshots) {
-        auto it = layersWithChanges.find(snapshot->path.id);
-        if (it != layersWithChanges.end()) {
-            ALOGV("%s fast path snapshot changes = %s", __func__,
-                  mRootSnapshot.changes.string().c_str());
-            LayerHierarchy::TraversalPath root = LayerHierarchy::TraversalPath::ROOT;
-            updateSnapshot(*snapshot, args, *it->second, mRootSnapshot, root);
-        }
+    if ((args.layerLifecycleManager.getGlobalChanges().get() &
+         ~(RequestedLayerState::Changes::Content | RequestedLayerState::Changes::Buffer).get()) !=
+        0) {
+        // We have changes that require us to walk the hierarchy and update child layers.
+        // No fast path for you.
+        return false;
     }
     return true;
 }
@@ -429,7 +433,15 @@
     if (args.forceUpdate == ForceUpdateFlags::HIERARCHY) {
         mRootSnapshot.changes |=
                 RequestedLayerState::Changes::Hierarchy | RequestedLayerState::Changes::Visibility;
+        mRootSnapshot.clientChanges |= layer_state_t::eReparent;
     }
+
+    for (auto& snapshot : mSnapshots) {
+        if (snapshot->reachablilty == LayerSnapshot::Reachablilty::Reachable) {
+            snapshot->reachablilty = LayerSnapshot::Reachablilty::Unreachable;
+        }
+    }
+
     LayerHierarchy::TraversalPath root = LayerHierarchy::TraversalPath::ROOT;
     if (args.root.getLayer()) {
         // The hierarchy can have a root layer when used for screenshots otherwise, it will have
@@ -468,13 +480,26 @@
     auto it = mSnapshots.begin();
     while (it < mSnapshots.end()) {
         auto& traversalPath = it->get()->path;
-        if (!it->get()->unreachable &&
-            destroyedLayerIds.find(traversalPath.id) == destroyedLayerIds.end()) {
+        const bool unreachable =
+                it->get()->reachablilty == LayerSnapshot::Reachablilty::Unreachable;
+        const bool isClone = traversalPath.isClone();
+        const bool layerIsDestroyed =
+                destroyedLayerIds.find(traversalPath.id) != destroyedLayerIds.end();
+        const bool destroySnapshot = (unreachable && isClone) || layerIsDestroyed;
+
+        if (!destroySnapshot) {
             it++;
             continue;
         }
 
-        mIdToSnapshot.erase(traversalPath);
+        mPathToSnapshot.erase(traversalPath);
+
+        auto range = mIdToSnapshots.equal_range(traversalPath.id);
+        auto matchingSnapshot =
+                std::find_if(range.first, range.second, [&traversalPath](auto& snapshotWithId) {
+                    return snapshotWithId.second->path == traversalPath;
+                });
+        mIdToSnapshots.erase(matchingSnapshot);
         mNeedsTouchableRegionCrop.erase(traversalPath);
         mSnapshots.back()->globalZ = it->get()->globalZ;
         std::iter_swap(it, mSnapshots.end() - 1);
@@ -507,8 +532,12 @@
     const RequestedLayerState* layer = hierarchy.getLayer();
     LayerSnapshot* snapshot = getSnapshot(traversalPath);
     const bool newSnapshot = snapshot == nullptr;
+    uint32_t primaryDisplayRotationFlags = getPrimaryDisplayRotationFlags(args.displays);
     if (newSnapshot) {
         snapshot = createSnapshot(traversalPath, *layer, parentSnapshot);
+        snapshot->merge(*layer, /*forceUpdate=*/true, /*displayChanges=*/true, args.forceFullDamage,
+                        primaryDisplayRotationFlags);
+        snapshot->changes |= RequestedLayerState::Changes::Created;
     }
     scheduler::LayerInfo::FrameRate oldFrameRate = snapshot->frameRate;
     if (traversalPath.isRelative()) {
@@ -546,8 +575,8 @@
 }
 
 LayerSnapshot* LayerSnapshotBuilder::getSnapshot(const LayerHierarchy::TraversalPath& id) const {
-    auto it = mIdToSnapshot.find(id);
-    return it == mIdToSnapshot.end() ? nullptr : it->second;
+    auto it = mPathToSnapshot.find(id);
+    return it == mPathToSnapshot.end() ? nullptr : it->second;
 }
 
 LayerSnapshot* LayerSnapshotBuilder::createSnapshot(const LayerHierarchy::TraversalPath& path,
@@ -559,7 +588,9 @@
     if (path.isClone() && path.variant != LayerHierarchy::Variant::Mirror) {
         snapshot->mirrorRootPath = parentSnapshot.mirrorRootPath;
     }
-    mIdToSnapshot[path] = snapshot;
+    mPathToSnapshot[path] = snapshot;
+
+    mIdToSnapshots.emplace(path.id, snapshot);
     return snapshot;
 }
 
@@ -574,20 +605,15 @@
     }
     mResortSnapshots = false;
 
-    for (auto& snapshot : mSnapshots) {
-        snapshot->unreachable = snapshot->path.isClone();
-    }
-
     size_t globalZ = 0;
     args.root.traverseInZOrder(
             [this, &globalZ](const LayerHierarchy&,
                              const LayerHierarchy::TraversalPath& traversalPath) -> bool {
                 LayerSnapshot* snapshot = getSnapshot(traversalPath);
                 if (!snapshot) {
-                    return false;
+                    return true;
                 }
 
-                snapshot->unreachable = false;
                 if (snapshot->getIsVisible() || snapshot->hasInputInfo()) {
                     updateVisibility(*snapshot, snapshot->getIsVisible());
                     size_t oldZ = snapshot->globalZ;
@@ -610,7 +636,7 @@
         mSnapshots[globalZ]->globalZ = globalZ;
         /* mark unreachable snapshots as explicitly invisible */
         updateVisibility(*mSnapshots[globalZ], false);
-        if (mSnapshots[globalZ]->unreachable) {
+        if (mSnapshots[globalZ]->reachablilty == LayerSnapshot::Reachablilty::Unreachable) {
             hasUnreachableSnapshots = true;
         }
         globalZ++;
@@ -634,7 +660,9 @@
             snapshot.relativeLayerMetadata = parentSnapshot.relativeLayerMetadata;
         }
     }
-    snapshot.isVisible = snapshot.getIsVisible();
+    if (snapshot.reachablilty == LayerSnapshot::Reachablilty::Unreachable) {
+        snapshot.reachablilty = LayerSnapshot::Reachablilty::ReachableByRelativeParent;
+    }
 }
 
 void LayerSnapshotBuilder::updateChildState(LayerSnapshot& snapshot,
@@ -675,16 +703,6 @@
     snapshot.relativeLayerMetadata.mMap.clear();
 }
 
-// TODO (b/259407931): Remove.
-uint32_t getPrimaryDisplayRotationFlags(const DisplayInfos& displays) {
-    for (auto& [_, display] : displays) {
-        if (display.isPrimary) {
-            return display.rotationFlags;
-        }
-    }
-    return 0;
-}
-
 void LayerSnapshotBuilder::updateSnapshot(LayerSnapshot& snapshot, const Args& args,
                                           const RequestedLayerState& requested,
                                           const LayerSnapshot& parentSnapshot,
@@ -694,82 +712,69 @@
             (RequestedLayerState::Changes::Hierarchy | RequestedLayerState::Changes::Geometry |
              RequestedLayerState::Changes::Visibility | RequestedLayerState::Changes::Metadata |
              RequestedLayerState::Changes::AffectsChildren |
-             RequestedLayerState::Changes::FrameRate);
-    snapshot.changes |= parentChanges | requested.changes;
+             RequestedLayerState::Changes::FrameRate | RequestedLayerState::Changes::GameMode);
+    snapshot.changes |= parentChanges;
+    if (args.displayChanges) snapshot.changes |= RequestedLayerState::Changes::Geometry;
+    snapshot.reachablilty = LayerSnapshot::Reachablilty::Reachable;
+    snapshot.clientChanges |= (parentSnapshot.clientChanges & layer_state_t::AFFECTS_CHILDREN);
     snapshot.isHiddenByPolicyFromParent = parentSnapshot.isHiddenByPolicyFromParent ||
             parentSnapshot.invalidTransform || requested.isHiddenByPolicy() ||
             (args.excludeLayerIds.find(path.id) != args.excludeLayerIds.end());
-    snapshot.contentDirty = requested.what & layer_state_t::CONTENT_DIRTY;
-    // TODO(b/238781169) scope down the changes to only buffer updates.
-    snapshot.hasReadyFrame = requested.hasReadyFrame();
-    snapshot.sidebandStreamHasFrame = requested.hasSidebandStreamFrame();
-    updateSurfaceDamage(requested, snapshot.hasReadyFrame, args.forceFullDamage,
-                        snapshot.surfaceDamage);
-    snapshot.outputFilter.layerStack = parentSnapshot.path == LayerHierarchy::TraversalPath::ROOT
-            ? requested.layerStack
-            : parentSnapshot.outputFilter.layerStack;
 
-    uint32_t primaryDisplayRotationFlags = getPrimaryDisplayRotationFlags(args.displays);
     const bool forceUpdate = args.forceUpdate == ForceUpdateFlags::ALL ||
+            snapshot.clientChanges & layer_state_t::eReparent ||
             snapshot.changes.any(RequestedLayerState::Changes::Visibility |
                                  RequestedLayerState::Changes::Created);
 
-    // always update the buffer regardless of visibility
-    if (forceUpdate || requested.what & layer_state_t::BUFFER_CHANGES || args.displayChanges) {
-        snapshot.acquireFence =
-                (requested.externalTexture &&
-                 requested.bufferData->flags.test(BufferData::BufferDataChange::fenceChanged))
-                ? requested.bufferData->acquireFence
-                : Fence::NO_FENCE;
-        snapshot.buffer =
-                requested.externalTexture ? requested.externalTexture->getBuffer() : nullptr;
-        snapshot.bufferSize = requested.getBufferSize(primaryDisplayRotationFlags);
-        snapshot.geomBufferSize = snapshot.bufferSize;
-        snapshot.croppedBufferSize = requested.getCroppedBufferSize(snapshot.bufferSize);
-        snapshot.dataspace = requested.dataspace;
-        snapshot.externalTexture = requested.externalTexture;
-        snapshot.frameNumber = (requested.bufferData) ? requested.bufferData->frameNumber : 0;
-        snapshot.geomBufferTransform = requested.bufferTransform;
-        snapshot.geomBufferUsesDisplayInverseTransform = requested.transformToDisplayInverse;
-        snapshot.geomContentCrop = requested.getBufferCrop();
-        snapshot.geomUsesSourceCrop = snapshot.hasBufferOrSidebandStream();
-        snapshot.hasProtectedContent = requested.externalTexture &&
-                requested.externalTexture->getUsage() & GRALLOC_USAGE_PROTECTED;
-        snapshot.isHdrY410 = requested.dataspace == ui::Dataspace::BT2020_ITU_PQ &&
-                requested.api == NATIVE_WINDOW_API_MEDIA &&
-                requested.bufferData->getPixelFormat() == HAL_PIXEL_FORMAT_RGBA_1010102;
-        snapshot.sidebandStream = requested.sidebandStream;
-        snapshot.transparentRegionHint = requested.transparentRegion;
-        snapshot.color.rgb = requested.getColor().rgb;
-        snapshot.currentHdrSdrRatio = requested.currentHdrSdrRatio;
-        snapshot.desiredHdrSdrRatio = requested.desiredHdrSdrRatio;
+    if (forceUpdate || snapshot.clientChanges & layer_state_t::eLayerStackChanged) {
+        // If root layer, use the layer stack otherwise get the parent's layer stack.
+        snapshot.outputFilter.layerStack =
+                parentSnapshot.path == LayerHierarchy::TraversalPath::ROOT
+                ? requested.layerStack
+                : parentSnapshot.outputFilter.layerStack;
     }
 
     if (snapshot.isHiddenByPolicyFromParent &&
         !snapshot.changes.test(RequestedLayerState::Changes::Created)) {
         if (forceUpdate ||
-            snapshot.changes.any(RequestedLayerState::Changes::Hierarchy |
-                                 RequestedLayerState::Changes::Geometry |
+            snapshot.changes.any(RequestedLayerState::Changes::Geometry |
                                  RequestedLayerState::Changes::Input)) {
             updateInput(snapshot, requested, parentSnapshot, path, args);
         }
         return;
     }
 
-    if (forceUpdate || snapshot.changes.any(RequestedLayerState::Changes::AffectsChildren)) {
-        // If root layer, use the layer stack otherwise get the parent's layer stack.
+    if (forceUpdate || snapshot.changes.any(RequestedLayerState::Changes::Mirror)) {
+        // Display mirrors are always placed in a VirtualDisplay so we never want to capture layers
+        // marked as skip capture
+        snapshot.handleSkipScreenshotFlag = parentSnapshot.handleSkipScreenshotFlag ||
+                (requested.layerStackToMirror != ui::INVALID_LAYER_STACK);
+    }
+
+    if (forceUpdate || snapshot.clientChanges & layer_state_t::eAlphaChanged) {
         snapshot.color.a = parentSnapshot.color.a * requested.color.a;
         snapshot.alpha = snapshot.color.a;
         snapshot.inputInfo.alpha = snapshot.color.a;
+    }
 
+    if (forceUpdate || snapshot.clientChanges & layer_state_t::eFlagsChanged) {
         snapshot.isSecure =
                 parentSnapshot.isSecure || (requested.flags & layer_state_t::eLayerSecure);
-        snapshot.isTrustedOverlay = parentSnapshot.isTrustedOverlay || requested.isTrustedOverlay;
         snapshot.outputFilter.toInternalDisplay = parentSnapshot.outputFilter.toInternalDisplay ||
                 (requested.flags & layer_state_t::eLayerSkipScreenshot);
+    }
+
+    if (forceUpdate || snapshot.clientChanges & layer_state_t::eTrustedOverlayChanged) {
+        snapshot.isTrustedOverlay = parentSnapshot.isTrustedOverlay || requested.isTrustedOverlay;
+    }
+
+    if (forceUpdate || snapshot.clientChanges & layer_state_t::eStretchChanged) {
         snapshot.stretchEffect = (requested.stretchEffect.hasEffect())
                 ? requested.stretchEffect
                 : parentSnapshot.stretchEffect;
+    }
+
+    if (forceUpdate || snapshot.clientChanges & layer_state_t::eColorTransformChanged) {
         if (!parentSnapshot.colorTransformIsIdentity) {
             snapshot.colorTransform = parentSnapshot.colorTransform * requested.colorTransform;
             snapshot.colorTransformIsIdentity = false;
@@ -777,16 +782,20 @@
             snapshot.colorTransform = requested.colorTransform;
             snapshot.colorTransformIsIdentity = !requested.hasColorTransform;
         }
+    }
+
+    if (forceUpdate || snapshot.changes.test(RequestedLayerState::Changes::GameMode)) {
         snapshot.gameMode = requested.metadata.has(gui::METADATA_GAME_MODE)
                 ? requested.gameMode
                 : parentSnapshot.gameMode;
-        // Display mirrors are always placed in a VirtualDisplay so we never want to capture layers
-        // marked as skip capture
-        snapshot.handleSkipScreenshotFlag = parentSnapshot.handleSkipScreenshotFlag ||
-                (requested.layerStackToMirror != ui::INVALID_LAYER_STACK);
+        updateMetadata(snapshot, requested, args);
+        if (args.includeMetadata) {
+            snapshot.layerMetadata = parentSnapshot.layerMetadata;
+            snapshot.layerMetadata.merge(requested.metadata);
+        }
     }
 
-    if (forceUpdate || snapshot.changes.any(RequestedLayerState::Changes::AffectsChildren) ||
+    if (forceUpdate || snapshot.clientChanges & layer_state_t::eFixedTransformHintChanged ||
         args.displayChanges) {
         snapshot.fixedTransformHint = requested.fixedTransformHint != ui::Transform::ROT_INVALID
                 ? requested.fixedTransformHint
@@ -802,9 +811,7 @@
         }
     }
 
-    if (forceUpdate ||
-        snapshot.changes.any(RequestedLayerState::Changes::FrameRate |
-                             RequestedLayerState::Changes::Hierarchy)) {
+    if (forceUpdate || snapshot.changes.any(RequestedLayerState::Changes::FrameRate)) {
         snapshot.frameRate = (requested.requestedFrameRate.rate.isValid() ||
                               (requested.requestedFrameRate.type ==
                                scheduler::LayerInfo::FrameRateCompatibility::NoVote))
@@ -812,23 +819,10 @@
                 : parentSnapshot.frameRate;
     }
 
-    if (forceUpdate || requested.what & layer_state_t::eMetadataChanged) {
-        updateMetadata(snapshot, requested, args);
-    }
-
-    if (forceUpdate || requested.changes.get() != 0) {
-        snapshot.compositionType = requested.getCompositionType();
-        snapshot.dimmingEnabled = requested.dimmingEnabled;
-        snapshot.layerOpaqueFlagSet =
-                (requested.flags & layer_state_t::eLayerOpaque) == layer_state_t::eLayerOpaque;
-        snapshot.cachingHint = requested.cachingHint;
-        snapshot.frameRateSelectionPriority = requested.frameRateSelectionPriority;
-    }
-
-    if (forceUpdate || snapshot.changes.any(RequestedLayerState::Changes::Content) ||
-        snapshot.changes.any(RequestedLayerState::Changes::AffectsChildren)) {
-        snapshot.color.rgb = requested.getColor().rgb;
-        snapshot.isColorspaceAgnostic = requested.colorSpaceAgnostic;
+    if (forceUpdate ||
+        snapshot.clientChanges &
+                (layer_state_t::eBackgroundBlurRadiusChanged | layer_state_t::eBlurRegionsChanged |
+                 layer_state_t::eAlphaChanged)) {
         snapshot.backgroundBlurRadius = args.supportsBlur
                 ? static_cast<int>(parentSnapshot.color.a * (float)requested.backgroundBlurRadius)
                 : 0;
@@ -836,29 +830,30 @@
         for (auto& region : snapshot.blurRegions) {
             region.alpha = region.alpha * snapshot.color.a;
         }
-        snapshot.hdrMetadata = requested.hdrMetadata;
     }
 
-    if (forceUpdate ||
-        snapshot.changes.any(RequestedLayerState::Changes::Hierarchy |
-                             RequestedLayerState::Changes::Geometry)) {
+    if (forceUpdate || snapshot.changes.any(RequestedLayerState::Changes::Geometry)) {
+        uint32_t primaryDisplayRotationFlags = getPrimaryDisplayRotationFlags(args.displays);
         updateLayerBounds(snapshot, requested, parentSnapshot, primaryDisplayRotationFlags);
+    }
+
+    if (forceUpdate || snapshot.clientChanges & layer_state_t::eCornerRadiusChanged ||
+        snapshot.changes.any(RequestedLayerState::Changes::Geometry)) {
         updateRoundedCorner(snapshot, requested, parentSnapshot);
     }
 
+    if (forceUpdate || snapshot.clientChanges & layer_state_t::eShadowRadiusChanged ||
+        snapshot.changes.any(RequestedLayerState::Changes::Geometry)) {
+        updateShadows(snapshot, requested, args.globalShadowSettings);
+    }
+
     if (forceUpdate ||
-        snapshot.changes.any(RequestedLayerState::Changes::Hierarchy |
-                             RequestedLayerState::Changes::Geometry |
+        snapshot.changes.any(RequestedLayerState::Changes::Geometry |
                              RequestedLayerState::Changes::Input)) {
         updateInput(snapshot, requested, parentSnapshot, path, args);
     }
 
     // computed snapshot properties
-    updateShadows(snapshot, requested, args.globalShadowSettings);
-    if (args.includeMetadata) {
-        snapshot.layerMetadata = parentSnapshot.layerMetadata;
-        snapshot.layerMetadata.merge(requested.metadata);
-    }
     snapshot.forceClientComposition = snapshot.isHdrY410 || snapshot.shadowSettings.length > 0 ||
             requested.blurRegions.size() > 0 || snapshot.stretchEffect.hasEffect();
     snapshot.contentOpaque = snapshot.isContentOpaque();
@@ -914,10 +909,6 @@
                                              const RequestedLayerState& requested,
                                              const LayerSnapshot& parentSnapshot,
                                              uint32_t primaryDisplayRotationFlags) {
-    snapshot.croppedBufferSize = requested.getCroppedBufferSize(snapshot.bufferSize);
-    snapshot.geomCrop = requested.crop;
-    snapshot.localTransform = requested.getTransform(primaryDisplayRotationFlags);
-    snapshot.localTransformInverse = snapshot.localTransform.inverse();
     snapshot.geomLayerTransform = parentSnapshot.geomLayerTransform * snapshot.localTransform;
     const bool transformWasInvalid = snapshot.invalidTransform;
     snapshot.invalidTransform = !LayerSnapshot::isTransformValid(snapshot.geomLayerTransform);
@@ -974,11 +965,8 @@
     }
 }
 
-void LayerSnapshotBuilder::updateShadows(LayerSnapshot& snapshot,
-                                         const RequestedLayerState& requested,
+void LayerSnapshotBuilder::updateShadows(LayerSnapshot& snapshot, const RequestedLayerState&,
                                          const renderengine::ShadowSettings& globalShadowSettings) {
-    snapshot.shadowRadius = requested.shadowRadius;
-    snapshot.shadowSettings.length = requested.shadowRadius;
     if (snapshot.shadowRadius > 0.f) {
         snapshot.shadowSettings = globalShadowSettings;
 
@@ -1058,10 +1046,11 @@
         snapshot.inputInfo.inputConfig |= gui::WindowInfo::InputConfig::DROP_INPUT;
     }
 
-    auto cropLayerSnapshot = getSnapshot(requested.touchCropId);
-    if (cropLayerSnapshot) {
+    if (requested.touchCropId != UNASSIGNED_LAYER_ID || path.isClone()) {
         mNeedsTouchableRegionCrop.insert(path);
-    } else if (snapshot.inputInfo.replaceTouchableRegionWithCrop) {
+    }
+    auto cropLayerSnapshot = getSnapshot(requested.touchCropId);
+    if (!cropLayerSnapshot && snapshot.inputInfo.replaceTouchableRegionWithCrop) {
         FloatRect inputBounds = getInputBounds(snapshot, /*fillParentBounds=*/true).first;
         Rect inputBoundsInDisplaySpace =
                 getInputBoundsInDisplaySpace(snapshot, inputBounds, displayInfo.transform);
@@ -1081,8 +1070,6 @@
         // Cloned layers shouldn't handle watch outside since their z order is not determined by
         // WM or the client.
         snapshot.inputInfo.inputConfig.clear(gui::WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH);
-
-        mNeedsTouchableRegionCrop.insert(path);
     }
 }
 
@@ -1139,7 +1126,7 @@
             RequestedLayerState::Changes::Input;
 
     if (args.forceUpdate != ForceUpdateFlags::ALL &&
-        !args.layerLifecycleManager.getGlobalChanges().any(AFFECTS_INPUT)) {
+        !args.layerLifecycleManager.getGlobalChanges().any(AFFECTS_INPUT) && !args.displayChanges) {
         return;
     }
 
@@ -1148,6 +1135,8 @@
         if (!snapshot) {
             continue;
         }
+        LLOGV(snapshot->sequence, "updateTouchableRegionCrop=%s",
+              snapshot->getDebugString().c_str());
         const std::optional<frontend::DisplayInfo> displayInfoOpt =
                 args.displays.get(snapshot->outputFilter.layerStack);
         static frontend::DisplayInfo sDefaultInfo = {.isSecure = false};
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
index 2e46dc6..c81a5d2 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
@@ -122,7 +122,9 @@
 
     std::unordered_map<LayerHierarchy::TraversalPath, LayerSnapshot*,
                        LayerHierarchy::TraversalPathHash>
-            mIdToSnapshot;
+            mPathToSnapshot;
+    std::multimap<uint32_t, LayerSnapshot*> mIdToSnapshots;
+
     // Track snapshots that needs touchable region crop from other snapshots
     std::unordered_set<LayerHierarchy::TraversalPath, LayerHierarchy::TraversalPathHash>
             mNeedsTouchableRegionCrop;
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index bde2d05..5738262 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -13,10 +13,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+// #define LOG_NDEBUG 0
 
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 #undef LOG_TAG
-#define LOG_TAG "RequestedLayerState"
+#define LOG_TAG "SurfaceFlinger"
 
 #include <log/log.h>
 #include <private/android_filesystem_config.h>
@@ -132,12 +133,16 @@
     const half oldAlpha = color.a;
     const bool hadBuffer = externalTexture != nullptr;
     uint64_t oldFramenumber = hadBuffer ? bufferData->frameNumber : 0;
+    const ui::Size oldBufferSize = hadBuffer
+            ? ui::Size(externalTexture->getWidth(), externalTexture->getHeight())
+            : ui::Size();
     const bool hadSideStream = sidebandStream != nullptr;
     const layer_state_t& clientState = resolvedComposerState.state;
     const bool hadBlur = hasBlur();
     uint64_t clientChanges = what | layer_state_t::diff(clientState);
     layer_state_t::merge(clientState);
     what = clientChanges;
+    LLOGV(layerId, "requested=%" PRIu64 "flags=%" PRIu64, clientState.what, clientChanges);
 
     if (clientState.what & layer_state_t::eFlagsChanged) {
         if ((oldFlags ^ flags) & layer_state_t::eLayerHidden) {
@@ -154,6 +159,13 @@
         const bool hasBuffer = externalTexture != nullptr;
         if (hasBuffer || hasBuffer != hadBuffer) {
             changes |= RequestedLayerState::Changes::Buffer;
+            const ui::Size newBufferSize = hasBuffer
+                    ? ui::Size(externalTexture->getWidth(), externalTexture->getHeight())
+                    : ui::Size();
+            if (oldBufferSize != newBufferSize) {
+                changes |= RequestedLayerState::Changes::BufferSize;
+                changes |= RequestedLayerState::Changes::Geometry;
+            }
         }
 
         if (hasBuffer != hadBuffer) {
@@ -281,7 +293,7 @@
             // child layers.
             if (static_cast<int32_t>(gameMode) != requestedGameMode) {
                 gameMode = static_cast<gui::GameMode>(requestedGameMode);
-                changes |= RequestedLayerState::Changes::AffectsChildren;
+                changes |= RequestedLayerState::Changes::GameMode;
             }
         }
     }
@@ -372,7 +384,7 @@
     return (flags & layer_state_t::eLayerHidden) == layer_state_t::eLayerHidden;
 };
 half4 RequestedLayerState::getColor() const {
-    if ((sidebandStream != nullptr) || (externalTexture != nullptr)) {
+    if (sidebandStream || externalTexture) {
         return {0._hf, 0._hf, 0._hf, color.a};
     }
     return color;
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
index 0ef50bc..02e3bac 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.h
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
@@ -54,6 +54,8 @@
         Buffer = 1u << 15,
         SidebandStream = 1u << 16,
         Animation = 1u << 17,
+        BufferSize = 1u << 18,
+        GameMode = 1u << 19,
     };
     static Rect reduce(const Rect& win, const Region& exclude);
     RequestedLayerState(const LayerCreationArgs&);
@@ -91,10 +93,10 @@
     const uint32_t textureName;
     // The owner of the layer. If created from a non system process, it will be the calling uid.
     // If created from a system process, the value can be passed in.
-    const uid_t ownerUid;
+    const gui::Uid ownerUid;
     // The owner pid of the layer. If created from a non system process, it will be the calling pid.
     // If created from a system process, the value can be passed in.
-    const pid_t ownerPid;
+    const gui::Pid ownerPid;
     bool dataspaceRequested;
     bool hasColorTransform;
     bool premultipliedAlpha{true};
diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
index 9cbe0bb..fa8eb3c 100644
--- a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
+++ b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
@@ -16,7 +16,7 @@
 
 // #define LOG_NDEBUG 0
 #undef LOG_TAG
-#define LOG_TAG "TransactionHandler"
+#define LOG_TAG "SurfaceFlinger"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
 #include <cutils/trace.h>
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index 427a85c..1c7581b 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -428,7 +428,7 @@
 
     layerInfo->set_is_relative_of(requestedState.isRelativeOf);
 
-    layerInfo->set_owner_uid(requestedState.ownerUid);
+    layerInfo->set_owner_uid(requestedState.ownerUid.val());
 
     if ((traceFlags & LayerTracing::TRACE_INPUT) && snapshot.hasInputInfo()) {
         LayerProtoHelper::writeToProto(snapshot.inputInfo, {},
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index b494529..c364ccb 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2173,6 +2173,7 @@
 bool SurfaceFlinger::updateLayerSnapshotsLegacy(VsyncId vsyncId, frontend::Update& update,
                                                 bool transactionsFlushed,
                                                 bool& outTransactionsAreEmpty) {
+    ATRACE_CALL();
     bool needsTraversal = false;
     if (transactionsFlushed) {
         needsTraversal |= commitMirrorDisplays(vsyncId);
@@ -2225,7 +2226,7 @@
 bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, frontend::Update& update,
                                           bool transactionsFlushed, bool& outTransactionsAreEmpty) {
     using Changes = frontend::RequestedLayerState::Changes;
-    ATRACE_NAME("updateLayerSnapshots");
+    ATRACE_CALL();
     {
         mLayerLifecycleManager.addLayers(std::move(update.newLayers));
         mLayerLifecycleManager.applyTransactions(update.transactions);
@@ -8165,7 +8166,7 @@
                     if (layerStack && snapshot->outputFilter.layerStack != *layerStack) {
                         return;
                     }
-                    if (uid != CaptureArgs::UNSET_UID && snapshot->uid != uid) {
+                    if (uid != CaptureArgs::UNSET_UID && snapshot->uid != gui::Uid(uid)) {
                         return;
                     }
                     if (!snapshot->hasSomethingToDraw()) {
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index 12cf070..5da893e 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -228,6 +228,7 @@
     setAlpha(1, 0.5);
     setAlpha(122, 0.5);
     UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_EQ(getSnapshot(1)->alpha, 0.5f);
     EXPECT_EQ(getSnapshot(12)->alpha, 0.5f);
     EXPECT_EQ(getSnapshot(1221)->alpha, 0.25f);
 }
@@ -236,28 +237,30 @@
 TEST_F(LayerSnapshotTest, UpdateClearsPreviousChangeStates) {
     setCrop(1, Rect(1, 2, 3, 4));
     UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
-    EXPECT_TRUE(getSnapshot(1)->changes.get() != 0);
-    EXPECT_TRUE(getSnapshot(11)->changes.get() != 0);
+    EXPECT_TRUE(getSnapshot(1)->changes.test(RequestedLayerState::Changes::Geometry));
+    EXPECT_TRUE(getSnapshot(11)->changes.test(RequestedLayerState::Changes::Geometry));
     setCrop(2, Rect(1, 2, 3, 4));
     UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
-    EXPECT_TRUE(getSnapshot(2)->changes.get() != 0);
-    EXPECT_TRUE(getSnapshot(1)->changes.get() == 0);
-    EXPECT_TRUE(getSnapshot(11)->changes.get() == 0);
+    EXPECT_TRUE(getSnapshot(2)->changes.test(RequestedLayerState::Changes::Geometry));
+    EXPECT_FALSE(getSnapshot(1)->changes.test(RequestedLayerState::Changes::Geometry));
+    EXPECT_FALSE(getSnapshot(11)->changes.test(RequestedLayerState::Changes::Geometry));
 }
 
 TEST_F(LayerSnapshotTest, FastPathClearsPreviousChangeStates) {
     setColor(11, {1._hf, 0._hf, 0._hf});
     UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
-    EXPECT_TRUE(getSnapshot(11)->changes.get() != 0);
-    EXPECT_TRUE(getSnapshot(1)->changes.get() == 0);
+    EXPECT_EQ(getSnapshot(11)->changes, RequestedLayerState::Changes::Content);
+    EXPECT_EQ(getSnapshot(11)->clientChanges, layer_state_t::eColorChanged);
+    EXPECT_EQ(getSnapshot(1)->changes.get(), 0u);
     UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
-    EXPECT_TRUE(getSnapshot(11)->changes.get() == 0);
+    EXPECT_EQ(getSnapshot(11)->changes.get(), 0u);
 }
 
 TEST_F(LayerSnapshotTest, FastPathSetsChangeFlagToContent) {
     setColor(1, {1._hf, 0._hf, 0._hf});
     UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
     EXPECT_EQ(getSnapshot(1)->changes, RequestedLayerState::Changes::Content);
+    EXPECT_EQ(getSnapshot(1)->clientChanges, layer_state_t::eColorChanged);
 }
 
 TEST_F(LayerSnapshotTest, GameMode) {
@@ -270,7 +273,9 @@
     transactions.back().states.front().layerId = 1;
     transactions.back().states.front().state.layerId = static_cast<int32_t>(1);
     mLifecycleManager.applyTransactions(transactions);
+    EXPECT_EQ(mLifecycleManager.getGlobalChanges(), RequestedLayerState::Changes::GameMode);
     UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_EQ(getSnapshot(1)->clientChanges, layer_state_t::eMetadataChanged);
     EXPECT_EQ(static_cast<int32_t>(getSnapshot(1)->gameMode), 42);
     EXPECT_EQ(static_cast<int32_t>(getSnapshot(11)->gameMode), 42);
 }
@@ -309,7 +314,7 @@
     EXPECT_EQ(getSnapshot(1)->frameRate.type, scheduler::LayerInfo::FrameRateCompatibility::NoVote);
 }
 
-TEST_F(LayerSnapshotTest, canCropTouchableRegion) {
+TEST_F(LayerSnapshotTest, CanCropTouchableRegion) {
     // ROOT
     // ├── 1
     // │   ├── 11