[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/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