Refactor SkImageFilter usage to cache results.

If an SkImageFilter is used, create an image
snapshot with the filter applied to avoid
re-computing it on each draw invocation

Bug: 188450217
Test: Re-ran CTS tests

Change-Id: Ib790669e14ada9d4ebbfac958d699e2b5242f2d7
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 9a9e6d4..332f7e6 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -306,11 +306,17 @@
         info.damageAccumulator->popTransform();
         syncProperties();
 
-        const StretchEffect& stagingStretch =
-            mProperties.layerProperties().getStretchEffect();
+        auto& layerProperties = mProperties.layerProperties();
+        const StretchEffect& stagingStretch = layerProperties.getStretchEffect();
         if (stagingStretch.isEmpty()) {
             mStretchMask.clear();
         }
+
+        if (layerProperties.getImageFilter() == nullptr) {
+            mSnapshotResult.snapshot = nullptr;
+            mTargetImageFilter = nullptr;
+        }
+
         // We could try to be clever and only re-damage if the matrix changed.
         // However, we don't need to worry about that. The cost of over-damaging
         // here is only going to be a single additional map rect of this node
@@ -321,6 +327,44 @@
     }
 }
 
+std::optional<RenderNode::SnapshotResult> RenderNode::updateSnapshotIfRequired(
+    GrRecordingContext* context,
+    const SkImageFilter* imageFilter,
+    const SkIRect& clipBounds
+) {
+    auto* layerSurface = getLayerSurface();
+    if (layerSurface == nullptr) {
+        return std::nullopt;
+    }
+
+    sk_sp<SkImage> snapshot = layerSurface->makeImageSnapshot();
+    const auto subset = SkIRect::MakeWH(properties().getWidth(),
+                                        properties().getHeight());
+    // If we don't have an ImageFilter just return the snapshot
+    if (imageFilter == nullptr) {
+        mSnapshotResult.snapshot = snapshot;
+        mSnapshotResult.outSubset = subset;
+        mSnapshotResult.outOffset = SkIPoint::Make(0.0f, 0.0f);
+        mImageFilterClipBounds = clipBounds;
+        mTargetImageFilter = nullptr;
+    } else if (mSnapshotResult.snapshot == nullptr ||
+        imageFilter != mTargetImageFilter.get() ||
+        mImageFilterClipBounds != clipBounds) {
+        // Otherwise create a new snapshot with the given filter and snapshot
+        mSnapshotResult.snapshot =
+                snapshot->makeWithFilter(context,
+                                         imageFilter,
+                                         subset,
+                                         clipBounds,
+                                         &mSnapshotResult.outSubset,
+                                         &mSnapshotResult.outOffset);
+        mTargetImageFilter = sk_ref_sp(imageFilter);
+        mImageFilterClipBounds = clipBounds;
+    }
+
+    return mSnapshotResult;
+}
+
 void RenderNode::syncDisplayList(TreeObserver& observer, TreeInfo* info) {
     // Make sure we inc first so that we don't fluctuate between 0 and 1,
     // which would thrash the layer cache
@@ -411,6 +455,8 @@
     if (hasLayer()) {
         this->setLayerSurface(nullptr);
     }
+    mSnapshotResult.snapshot = nullptr;
+    mTargetImageFilter = nullptr;
     if (mDisplayList) {
         mDisplayList.updateChildren([](RenderNode* child) { child->destroyLayers(); });
     }