[Impeller] Return entity from filters instead of a snapshot (#39560)

diff --git a/impeller/entity/contents/contents.cc b/impeller/entity/contents/contents.cc
index 7346996..965ade2 100644
--- a/impeller/entity/contents/contents.cc
+++ b/impeller/entity/contents/contents.cc
@@ -7,6 +7,7 @@
 
 #include "fml/logging.h"
 #include "impeller/entity/contents/content_context.h"
+#include "impeller/entity/contents/texture_contents.h"
 #include "impeller/renderer/command_buffer.h"
 #include "impeller/renderer/formats.h"
 #include "impeller/renderer/render_pass.h"
@@ -35,6 +36,30 @@
   return opts;
 }
 
+std::optional<Entity> Contents::EntityFromSnapshot(
+    const std::optional<Snapshot>& snapshot,
+    BlendMode blend_mode,
+    uint32_t stencil_depth) {
+  if (!snapshot.has_value()) {
+    return std::nullopt;
+  }
+
+  auto texture_rect = Rect::MakeSize(snapshot->texture->GetSize());
+
+  auto contents = TextureContents::MakeRect(texture_rect);
+  contents->SetTexture(snapshot->texture);
+  contents->SetSamplerDescriptor(snapshot->sampler_descriptor);
+  contents->SetSourceRect(texture_rect);
+  contents->SetOpacity(snapshot->opacity);
+
+  Entity entity;
+  entity.SetBlendMode(blend_mode);
+  entity.SetStencilDepth(stencil_depth);
+  entity.SetTransformation(snapshot->transform);
+  entity.SetContents(contents);
+  return entity;
+}
+
 Contents::Contents() = default;
 
 Contents::~Contents() = default;
diff --git a/impeller/entity/contents/contents.h b/impeller/entity/contents/contents.h
index ec99190..2c5f3ab 100644
--- a/impeller/entity/contents/contents.h
+++ b/impeller/entity/contents/contents.h
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "flutter/fml/macros.h"
+#include "impeller/geometry/color.h"
 #include "impeller/geometry/rect.h"
 #include "impeller/renderer/sampler_descriptor.h"
 #include "impeller/renderer/snapshot.h"
@@ -40,6 +41,12 @@
     std::optional<Rect> coverage = std::nullopt;
   };
 
+  /// @brief  Create an entity that renders a given snapshot.
+  static std::optional<Entity> EntityFromSnapshot(
+      const std::optional<Snapshot>& snapshot,
+      BlendMode blend_mode = BlendMode::kSourceOver,
+      uint32_t stencil_depth = 0);
+
   virtual bool Render(const ContentContext& renderer,
                       const Entity& entity,
                       RenderPass& pass) const = 0;
diff --git a/impeller/entity/contents/filters/blend_filter_contents.cc b/impeller/entity/contents/filters/blend_filter_contents.cc
index ddfc723..42b5553 100644
--- a/impeller/entity/contents/filters/blend_filter_contents.cc
+++ b/impeller/entity/contents/filters/blend_filter_contents.cc
@@ -17,6 +17,7 @@
 #include "impeller/renderer/formats.h"
 #include "impeller/renderer/render_pass.h"
 #include "impeller/renderer/sampler_library.h"
+#include "impeller/renderer/snapshot.h"
 
 namespace impeller {
 
@@ -30,7 +31,7 @@
     ContentContext::*)(ContentContextOptions) const;
 
 template <typename TPipeline>
-static std::optional<Snapshot> AdvancedBlend(
+static std::optional<Entity> AdvancedBlend(
     const FilterInput::Vector& inputs,
     const ContentContext& renderer,
     const Entity& entity,
@@ -67,11 +68,19 @@
   if (!foreground_color.has_value()) {
     src_snapshot = inputs[1]->GetSnapshot(renderer, entity);
     if (!src_snapshot.has_value()) {
-      return dst_snapshot;
+      if (!dst_snapshot.has_value()) {
+        return std::nullopt;
+      }
+      return Contents::EntityFromSnapshot(dst_snapshot, entity.GetBlendMode(),
+                                          entity.GetStencilDepth());
     }
     auto maybe_src_uvs = src_snapshot->GetCoverageUVs(coverage);
     if (!maybe_src_uvs.has_value()) {
-      return dst_snapshot;
+      if (!dst_snapshot.has_value()) {
+        return std::nullopt;
+      }
+      return Contents::EntityFromSnapshot(dst_snapshot, entity.GetBlendMode(),
+                                          entity.GetStencilDepth());
     }
     src_uvs = maybe_src_uvs.value();
   }
@@ -147,17 +156,19 @@
   }
   out_texture->SetLabel("Advanced Blend Filter Texture");
 
-  return Snapshot{.texture = out_texture,
-                  .transform = Matrix::MakeTranslation(coverage.origin),
-                  // Since we absorbed the transform of the inputs and used the
-                  // respective snapshot sampling modes when blending, pass on
-                  // the default NN clamp sampler.
-                  .sampler_descriptor = {},
-                  .opacity = (absorb_opacity ? 1.0f : dst_snapshot->opacity) *
-                             alpha.value_or(1.0)};
+  return Contents::EntityFromSnapshot(
+      Snapshot{.texture = out_texture,
+               .transform = Matrix::MakeTranslation(coverage.origin),
+               // Since we absorbed the transform of the inputs and used the
+               // respective snapshot sampling modes when blending, pass on
+               // the default NN clamp sampler.
+               .sampler_descriptor = {},
+               .opacity = (absorb_opacity ? 1.0f : dst_snapshot->opacity) *
+                          alpha.value_or(1.0)},
+      entity.GetBlendMode(), entity.GetStencilDepth());
 }
 
-static std::optional<Snapshot> PipelineBlend(
+static std::optional<Entity> PipelineBlend(
     const FilterInput::Vector& inputs,
     const ContentContext& renderer,
     const Entity& entity,
@@ -269,28 +280,31 @@
   }
   out_texture->SetLabel("Pipeline Blend Filter Texture");
 
-  return Snapshot{.texture = out_texture,
-                  .transform = Matrix::MakeTranslation(coverage.origin),
-                  // Since we absorbed the transform of the inputs and used the
-                  // respective snapshot sampling modes when blending, pass on
-                  // the default NN clamp sampler.
-                  .sampler_descriptor = {},
-                  .opacity = (absorb_opacity ? 1.0f : dst_snapshot->opacity) *
-                             alpha.value_or(1.0)};
+  return Contents::EntityFromSnapshot(
+      Snapshot{.texture = out_texture,
+               .transform = Matrix::MakeTranslation(coverage.origin),
+               // Since we absorbed the transform of the inputs and used the
+               // respective snapshot sampling modes when blending, pass on
+               // the default NN clamp sampler.
+               .sampler_descriptor = {},
+               .opacity = (absorb_opacity ? 1.0f : dst_snapshot->opacity) *
+                          alpha.value_or(1.0)},
+      entity.GetBlendMode(), entity.GetStencilDepth());
 }
 
-#define BLEND_CASE(mode)                                                       \
-  case BlendMode::k##mode:                                                     \
-    advanced_blend_proc_ =                                                     \
-        [](const FilterInput::Vector& inputs, const ContentContext& renderer,  \
-           const Entity& entity, const Rect& coverage,                         \
-           std::optional<Color> fg_color, bool absorb_opacity,                 \
-           std::optional<Scalar> alpha) {                                      \
-          PipelineProc p = &ContentContext::GetBlend##mode##Pipeline;          \
-          return AdvancedBlend<BlendScreenPipeline>(inputs, renderer, entity,  \
-                                                    coverage, fg_color,        \
-                                                    absorb_opacity, p, alpha); \
-        };                                                                     \
+#define BLEND_CASE(mode)                                                     \
+  case BlendMode::k##mode:                                                   \
+    advanced_blend_proc_ = [](const FilterInput::Vector& inputs,             \
+                              const ContentContext& renderer,                \
+                              const Entity& entity, const Rect& coverage,    \
+                              std::optional<Color> fg_color,                 \
+                              bool absorb_opacity,                           \
+                              std::optional<Scalar> alpha) {                 \
+      PipelineProc p = &ContentContext::GetBlend##mode##Pipeline;            \
+      return AdvancedBlend<Blend##mode##Pipeline>(inputs, renderer, entity,  \
+                                                  coverage, fg_color,        \
+                                                  absorb_opacity, p, alpha); \
+    };                                                                       \
     break;
 
 void BlendFilterContents::SetBlendMode(BlendMode blend_mode) {
@@ -328,7 +342,7 @@
   foreground_color_ = color;
 }
 
-std::optional<Snapshot> BlendFilterContents::RenderFilter(
+std::optional<Entity> BlendFilterContents::RenderFilter(
     const FilterInput::Vector& inputs,
     const ContentContext& renderer,
     const Entity& entity,
diff --git a/impeller/entity/contents/filters/blend_filter_contents.h b/impeller/entity/contents/filters/blend_filter_contents.h
index 47734ff..1b7f51a 100644
--- a/impeller/entity/contents/filters/blend_filter_contents.h
+++ b/impeller/entity/contents/filters/blend_filter_contents.h
@@ -11,14 +11,14 @@
 
 class BlendFilterContents : public ColorFilterContents {
  public:
-  using AdvancedBlendProc = std::function<std::optional<Snapshot>(
-      const FilterInput::Vector& inputs,
-      const ContentContext& renderer,
-      const Entity& entity,
-      const Rect& coverage,
-      std::optional<Color> foreground_color,
-      bool absorb_opacity,
-      std::optional<Scalar> alpha)>;
+  using AdvancedBlendProc =
+      std::function<std::optional<Entity>(const FilterInput::Vector& inputs,
+                                          const ContentContext& renderer,
+                                          const Entity& entity,
+                                          const Rect& coverage,
+                                          std::optional<Color> foreground_color,
+                                          bool absorb_opacity,
+                                          std::optional<Scalar> alpha)>;
 
   BlendFilterContents();
 
@@ -32,11 +32,11 @@
 
  private:
   // |FilterContents|
-  std::optional<Snapshot> RenderFilter(const FilterInput::Vector& inputs,
-                                       const ContentContext& renderer,
-                                       const Entity& entity,
-                                       const Matrix& effect_transform,
-                                       const Rect& coverage) const override;
+  std::optional<Entity> RenderFilter(const FilterInput::Vector& inputs,
+                                     const ContentContext& renderer,
+                                     const Entity& entity,
+                                     const Matrix& effect_transform,
+                                     const Rect& coverage) const override;
 
   BlendMode blend_mode_ = BlendMode::kSourceOver;
   AdvancedBlendProc advanced_blend_proc_;
diff --git a/impeller/entity/contents/filters/border_mask_blur_filter_contents.cc b/impeller/entity/contents/filters/border_mask_blur_filter_contents.cc
index 847cfaf..d9376c8 100644
--- a/impeller/entity/contents/filters/border_mask_blur_filter_contents.cc
+++ b/impeller/entity/contents/filters/border_mask_blur_filter_contents.cc
@@ -47,7 +47,7 @@
   }
 }
 
-std::optional<Snapshot> BorderMaskBlurFilterContents::RenderFilter(
+std::optional<Entity> BorderMaskBlurFilterContents::RenderFilter(
     const FilterInput::Vector& inputs,
     const ContentContext& renderer,
     const Entity& entity,
@@ -126,9 +126,11 @@
   }
   out_texture->SetLabel("BorderMaskBlurFilter Texture");
 
-  return Snapshot{.texture = out_texture,
-                  .transform = Matrix::MakeTranslation(coverage.origin),
-                  .opacity = input_snapshot->opacity};
+  return Contents::EntityFromSnapshot(
+      Snapshot{.texture = out_texture,
+               .transform = Matrix::MakeTranslation(coverage.origin),
+               .opacity = input_snapshot->opacity},
+      entity.GetBlendMode(), entity.GetStencilDepth());
 }
 
 std::optional<Rect> BorderMaskBlurFilterContents::GetFilterCoverage(
diff --git a/impeller/entity/contents/filters/border_mask_blur_filter_contents.h b/impeller/entity/contents/filters/border_mask_blur_filter_contents.h
index 9d971f5..06dc386 100644
--- a/impeller/entity/contents/filters/border_mask_blur_filter_contents.h
+++ b/impeller/entity/contents/filters/border_mask_blur_filter_contents.h
@@ -29,12 +29,11 @@
 
  private:
   // |FilterContents|
-  std::optional<Snapshot> RenderFilter(
-      const FilterInput::Vector& input_textures,
-      const ContentContext& renderer,
-      const Entity& entity,
-      const Matrix& effect_transform,
-      const Rect& coverage) const override;
+  std::optional<Entity> RenderFilter(const FilterInput::Vector& input_textures,
+                                     const ContentContext& renderer,
+                                     const Entity& entity,
+                                     const Matrix& effect_transform,
+                                     const Rect& coverage) const override;
 
   Sigma sigma_x_;
   Sigma sigma_y_;
diff --git a/impeller/entity/contents/filters/color_matrix_filter_contents.cc b/impeller/entity/contents/filters/color_matrix_filter_contents.cc
index 66e8464..16cef66 100644
--- a/impeller/entity/contents/filters/color_matrix_filter_contents.cc
+++ b/impeller/entity/contents/filters/color_matrix_filter_contents.cc
@@ -23,7 +23,7 @@
   matrix_ = matrix;
 }
 
-std::optional<Snapshot> ColorMatrixFilterContents::RenderFilter(
+std::optional<Entity> ColorMatrixFilterContents::RenderFilter(
     const FilterInput::Vector& inputs,
     const ContentContext& renderer,
     const Entity& entity,
@@ -104,11 +104,12 @@
   }
   out_texture->SetLabel("ColorMatrixFilter Texture");
 
-  return Snapshot{
-      .texture = out_texture,
-      .transform = input_snapshot->transform,
-      .sampler_descriptor = input_snapshot->sampler_descriptor,
-      .opacity = GetAbsorbOpacity() ? 1.0f : input_snapshot->opacity};
+  return Contents::EntityFromSnapshot(
+      Snapshot{.texture = out_texture,
+               .transform = input_snapshot->transform,
+               .sampler_descriptor = input_snapshot->sampler_descriptor,
+               .opacity = GetAbsorbOpacity() ? 1.0f : input_snapshot->opacity},
+      entity.GetBlendMode(), entity.GetStencilDepth());
 }
 
 }  // namespace impeller
diff --git a/impeller/entity/contents/filters/color_matrix_filter_contents.h b/impeller/entity/contents/filters/color_matrix_filter_contents.h
index 3486365..fb3a320 100644
--- a/impeller/entity/contents/filters/color_matrix_filter_contents.h
+++ b/impeller/entity/contents/filters/color_matrix_filter_contents.h
@@ -24,12 +24,11 @@
 
  private:
   // |FilterContents|
-  std::optional<Snapshot> RenderFilter(
-      const FilterInput::Vector& input_textures,
-      const ContentContext& renderer,
-      const Entity& entity,
-      const Matrix& effect_transform,
-      const Rect& coverage) const override;
+  std::optional<Entity> RenderFilter(const FilterInput::Vector& input_textures,
+                                     const ContentContext& renderer,
+                                     const Entity& entity,
+                                     const Matrix& effect_transform,
+                                     const Rect& coverage) const override;
 
   ColorMatrix matrix_;
 
diff --git a/impeller/entity/contents/filters/filter_contents.cc b/impeller/entity/contents/filters/filter_contents.cc
index cf10504..48c0b08 100644
--- a/impeller/entity/contents/filters/filter_contents.cc
+++ b/impeller/entity/contents/filters/filter_contents.cc
@@ -167,26 +167,11 @@
 
   // Run the filter.
 
-  auto maybe_snapshot = RenderToSnapshot(renderer, entity);
-  if (!maybe_snapshot.has_value()) {
-    return false;
+  auto maybe_entity = GetEntity(renderer, entity);
+  if (!maybe_entity.has_value()) {
+    return true;
   }
-  auto& snapshot = maybe_snapshot.value();
-
-  // Draw the result texture, respecting the transform and clip stack.
-
-  auto texture_rect = Rect::MakeSize(snapshot.texture->GetSize());
-  auto contents = TextureContents::MakeRect(texture_rect);
-  contents->SetTexture(snapshot.texture);
-  contents->SetSamplerDescriptor(snapshot.sampler_descriptor);
-  contents->SetSourceRect(texture_rect);
-  contents->SetOpacity(snapshot.opacity);
-
-  Entity e;
-  e.SetBlendMode(entity.GetBlendMode());
-  e.SetStencilDepth(entity.GetStencilDepth());
-  e.SetTransformation(snapshot.transform);
-  return contents->Render(renderer, e, pass);
+  return maybe_entity->Render(renderer, pass);
 }
 
 std::optional<Rect> FilterContents::GetLocalCoverage(
@@ -234,11 +219,8 @@
   return result;
 }
 
-std::optional<Snapshot> FilterContents::RenderToSnapshot(
-    const ContentContext& renderer,
-    const Entity& entity,
-    const std::optional<SamplerDescriptor>& sampler_descriptor,
-    bool msaa_enabled) const {
+std::optional<Entity> FilterContents::GetEntity(const ContentContext& renderer,
+                                                const Entity& entity) const {
   Entity entity_with_local_transform = entity;
   entity_with_local_transform.SetTransformation(
       GetTransform(entity.GetTransformation()));
@@ -252,6 +234,21 @@
                       effect_transform_, coverage.value());
 }
 
+std::optional<Snapshot> FilterContents::RenderToSnapshot(
+    const ContentContext& renderer,
+    const Entity& entity,
+    const std::optional<SamplerDescriptor>& sampler_descriptor,
+    bool msaa_enabled) const {
+  // Resolve the render instruction (entity) from the filter and render it to a
+  // snapshot.
+  if (std::optional<Entity> result = GetEntity(renderer, entity);
+      result.has_value()) {
+    return result->GetContents()->RenderToSnapshot(renderer, result.value());
+  }
+
+  return std::nullopt;
+}
+
 Matrix FilterContents::GetLocalTransform(const Matrix& parent_transform) const {
   return Matrix();
 }
diff --git a/impeller/entity/contents/filters/filter_contents.h b/impeller/entity/contents/filters/filter_contents.h
index 74c1039..00959b6 100644
--- a/impeller/entity/contents/filters/filter_contents.h
+++ b/impeller/entity/contents/filters/filter_contents.h
@@ -107,6 +107,10 @@
   ///         filter. Note that this is in addition to the entity's transform.
   void SetEffectTransform(Matrix effect_transform);
 
+  /// @brief  Create an Entity that renders this filter's output.
+  std::optional<Entity> GetEntity(const ContentContext& renderer,
+                                  const Entity& entity) const;
+
   // |Contents|
   bool Render(const ContentContext& renderer,
               const Entity& entity,
@@ -132,13 +136,12 @@
       const Entity& entity,
       const Matrix& effect_transform) const;
 
-  /// @brief  Converts zero or more filter inputs into a new texture.
-  virtual std::optional<Snapshot> RenderFilter(
-      const FilterInput::Vector& inputs,
-      const ContentContext& renderer,
-      const Entity& entity,
-      const Matrix& effect_transform,
-      const Rect& coverage) const = 0;
+  /// @brief  Converts zero or more filter inputs into a render instruction.
+  virtual std::optional<Entity> RenderFilter(const FilterInput::Vector& inputs,
+                                             const ContentContext& renderer,
+                                             const Entity& entity,
+                                             const Matrix& effect_transform,
+                                             const Rect& coverage) const = 0;
 
   std::optional<Rect> GetLocalCoverage(const Entity& local_entity) const;
 
diff --git a/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc b/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc
index d3d0567..87cecd9 100644
--- a/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc
+++ b/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc
@@ -81,7 +81,7 @@
   source_override_ = std::move(source_override);
 }
 
-std::optional<Snapshot> DirectionalGaussianBlurFilterContents::RenderFilter(
+std::optional<Entity> DirectionalGaussianBlurFilterContents::RenderFilter(
     const FilterInput::Vector& inputs,
     const ContentContext& renderer,
     const Entity& entity,
@@ -106,7 +106,9 @@
   }
 
   if (blur_sigma_.sigma < kEhCloseEnough) {
-    return input_snapshot.value();  // No blur to render.
+    return Contents::EntityFromSnapshot(
+        input_snapshot.value(), entity.GetBlendMode(),
+        entity.GetStencilDepth());  // No blur to render.
   }
 
   auto radius = Radius{blur_sigma_}.radius;
@@ -120,7 +122,9 @@
   // If the radius length is < .5, the shader will take at most 1 sample,
   // resulting in no blur.
   if (transformed_blur_radius_length < .5) {
-    return input_snapshot.value();  // No blur to render.
+    return Contents::EntityFromSnapshot(
+        input_snapshot.value(), entity.GetBlendMode(),
+        entity.GetStencilDepth());  // No blur to render.
   }
 
   // A matrix that rotates the snapshot space such that the blur direction is
@@ -283,14 +287,15 @@
   sampler_desc.min_filter = MinMagFilter::kLinear;
   sampler_desc.mag_filter = MinMagFilter::kLinear;
 
-  return Snapshot{
-      .texture = out_texture,
-      .transform =
-          texture_rotate.Invert() *
-          Matrix::MakeTranslation(pass_texture_rect.origin) *
-          Matrix::MakeScale((1 / scale) * (scaled_size / floored_size)),
-      .sampler_descriptor = sampler_desc,
-      .opacity = input_snapshot->opacity};
+  return Contents::EntityFromSnapshot(
+      Snapshot{.texture = out_texture,
+               .transform = texture_rotate.Invert() *
+                            Matrix::MakeTranslation(pass_texture_rect.origin) *
+                            Matrix::MakeScale((1 / scale) *
+                                              (scaled_size / floored_size)),
+               .sampler_descriptor = sampler_desc,
+               .opacity = input_snapshot->opacity},
+      entity.GetBlendMode(), entity.GetStencilDepth());
 }
 
 std::optional<Rect> DirectionalGaussianBlurFilterContents::GetFilterCoverage(
diff --git a/impeller/entity/contents/filters/gaussian_blur_filter_contents.h b/impeller/entity/contents/filters/gaussian_blur_filter_contents.h
index 7c4e44d..b69f662 100644
--- a/impeller/entity/contents/filters/gaussian_blur_filter_contents.h
+++ b/impeller/entity/contents/filters/gaussian_blur_filter_contents.h
@@ -37,12 +37,11 @@
 
  private:
   // |FilterContents|
-  std::optional<Snapshot> RenderFilter(
-      const FilterInput::Vector& input_textures,
-      const ContentContext& renderer,
-      const Entity& entity,
-      const Matrix& effect_transform,
-      const Rect& coverage) const override;
+  std::optional<Entity> RenderFilter(const FilterInput::Vector& input_textures,
+                                     const ContentContext& renderer,
+                                     const Entity& entity,
+                                     const Matrix& effect_transform,
+                                     const Rect& coverage) const override;
   Sigma blur_sigma_;
   Sigma secondary_blur_sigma_;
   Vector2 blur_direction_;
diff --git a/impeller/entity/contents/filters/linear_to_srgb_filter_contents.cc b/impeller/entity/contents/filters/linear_to_srgb_filter_contents.cc
index 98d397a..48a2b0c 100644
--- a/impeller/entity/contents/filters/linear_to_srgb_filter_contents.cc
+++ b/impeller/entity/contents/filters/linear_to_srgb_filter_contents.cc
@@ -17,7 +17,7 @@
 
 LinearToSrgbFilterContents::~LinearToSrgbFilterContents() = default;
 
-std::optional<Snapshot> LinearToSrgbFilterContents::RenderFilter(
+std::optional<Entity> LinearToSrgbFilterContents::RenderFilter(
     const FilterInput::Vector& inputs,
     const ContentContext& renderer,
     const Entity& entity,
@@ -81,11 +81,12 @@
   }
   out_texture->SetLabel("LinearToSrgb Texture");
 
-  return Snapshot{
-      .texture = out_texture,
-      .transform = input_snapshot->transform,
-      .sampler_descriptor = input_snapshot->sampler_descriptor,
-      .opacity = GetAbsorbOpacity() ? 1.0f : input_snapshot->opacity};
+  return Contents::EntityFromSnapshot(
+      Snapshot{.texture = out_texture,
+               .transform = input_snapshot->transform,
+               .sampler_descriptor = input_snapshot->sampler_descriptor,
+               .opacity = GetAbsorbOpacity() ? 1.0f : input_snapshot->opacity},
+      entity.GetBlendMode(), entity.GetStencilDepth());
 }
 
 }  // namespace impeller
diff --git a/impeller/entity/contents/filters/linear_to_srgb_filter_contents.h b/impeller/entity/contents/filters/linear_to_srgb_filter_contents.h
index 00b7116..ee2390c 100644
--- a/impeller/entity/contents/filters/linear_to_srgb_filter_contents.h
+++ b/impeller/entity/contents/filters/linear_to_srgb_filter_contents.h
@@ -17,12 +17,11 @@
 
  private:
   // |FilterContents|
-  std::optional<Snapshot> RenderFilter(
-      const FilterInput::Vector& input_textures,
-      const ContentContext& renderer,
-      const Entity& entity,
-      const Matrix& effect_transform,
-      const Rect& coverage) const override;
+  std::optional<Entity> RenderFilter(const FilterInput::Vector& input_textures,
+                                     const ContentContext& renderer,
+                                     const Entity& entity,
+                                     const Matrix& effect_transform,
+                                     const Rect& coverage) const override;
 
   FML_DISALLOW_COPY_AND_ASSIGN(LinearToSrgbFilterContents);
 };
diff --git a/impeller/entity/contents/filters/local_matrix_filter_contents.cc b/impeller/entity/contents/filters/local_matrix_filter_contents.cc
index 5cafbb5..a5214b4 100644
--- a/impeller/entity/contents/filters/local_matrix_filter_contents.cc
+++ b/impeller/entity/contents/filters/local_matrix_filter_contents.cc
@@ -19,13 +19,15 @@
   return matrix_;
 }
 
-std::optional<Snapshot> LocalMatrixFilterContents::RenderFilter(
+std::optional<Entity> LocalMatrixFilterContents::RenderFilter(
     const FilterInput::Vector& inputs,
     const ContentContext& renderer,
     const Entity& entity,
     const Matrix& effect_transform,
     const Rect& coverage) const {
-  return inputs[0]->GetSnapshot(renderer, entity);
+  return Contents::EntityFromSnapshot(inputs[0]->GetSnapshot(renderer, entity),
+                                      entity.GetBlendMode(),
+                                      entity.GetStencilDepth());
 }
 
 }  // namespace impeller
diff --git a/impeller/entity/contents/filters/local_matrix_filter_contents.h b/impeller/entity/contents/filters/local_matrix_filter_contents.h
index 9d310c9..824f1c3 100644
--- a/impeller/entity/contents/filters/local_matrix_filter_contents.h
+++ b/impeller/entity/contents/filters/local_matrix_filter_contents.h
@@ -22,12 +22,11 @@
 
  private:
   // |FilterContents|
-  std::optional<Snapshot> RenderFilter(
-      const FilterInput::Vector& input_textures,
-      const ContentContext& renderer,
-      const Entity& entity,
-      const Matrix& effect_transform,
-      const Rect& coverage) const override;
+  std::optional<Entity> RenderFilter(const FilterInput::Vector& input_textures,
+                                     const ContentContext& renderer,
+                                     const Entity& entity,
+                                     const Matrix& effect_transform,
+                                     const Rect& coverage) const override;
 
   Matrix matrix_;
 
diff --git a/impeller/entity/contents/filters/matrix_filter_contents.cc b/impeller/entity/contents/filters/matrix_filter_contents.cc
index e8d5281..45b6aad 100644
--- a/impeller/entity/contents/filters/matrix_filter_contents.cc
+++ b/impeller/entity/contents/filters/matrix_filter_contents.cc
@@ -18,7 +18,7 @@
   sampler_descriptor_ = std::move(desc);
 }
 
-std::optional<Snapshot> MatrixFilterContents::RenderFilter(
+std::optional<Entity> MatrixFilterContents::RenderFilter(
     const FilterInput::Vector& inputs,
     const ContentContext& renderer,
     const Entity& entity,
@@ -34,7 +34,8 @@
                         entity.GetTransformation().Invert() *  //
                         snapshot->transform;
   snapshot->sampler_descriptor = sampler_descriptor_;
-  return snapshot;
+  return Contents::EntityFromSnapshot(snapshot, entity.GetBlendMode(),
+                                      entity.GetStencilDepth());
 }
 
 std::optional<Rect> MatrixFilterContents::GetFilterCoverage(
diff --git a/impeller/entity/contents/filters/matrix_filter_contents.h b/impeller/entity/contents/filters/matrix_filter_contents.h
index f5f3880..2ccdd2b 100644
--- a/impeller/entity/contents/filters/matrix_filter_contents.h
+++ b/impeller/entity/contents/filters/matrix_filter_contents.h
@@ -27,12 +27,11 @@
 
  private:
   // |FilterContents|
-  std::optional<Snapshot> RenderFilter(
-      const FilterInput::Vector& input_textures,
-      const ContentContext& renderer,
-      const Entity& entity,
-      const Matrix& effect_transform,
-      const Rect& coverage) const override;
+  std::optional<Entity> RenderFilter(const FilterInput::Vector& input_textures,
+                                     const ContentContext& renderer,
+                                     const Entity& entity,
+                                     const Matrix& effect_transform,
+                                     const Rect& coverage) const override;
 
   Matrix matrix_;
   SamplerDescriptor sampler_descriptor_ = {};
diff --git a/impeller/entity/contents/filters/morphology_filter_contents.cc b/impeller/entity/contents/filters/morphology_filter_contents.cc
index 42c1d79..da20f07 100644
--- a/impeller/entity/contents/filters/morphology_filter_contents.cc
+++ b/impeller/entity/contents/filters/morphology_filter_contents.cc
@@ -34,7 +34,7 @@
   morph_type_ = morph_type;
 }
 
-std::optional<Snapshot> DirectionalMorphologyFilterContents::RenderFilter(
+std::optional<Entity> DirectionalMorphologyFilterContents::RenderFilter(
     const FilterInput::Vector& inputs,
     const ContentContext& renderer,
     const Entity& entity,
@@ -57,7 +57,9 @@
   }
 
   if (radius_.radius < kEhCloseEnough) {
-    return input_snapshot.value();
+    return Contents::EntityFromSnapshot(input_snapshot.value(),
+                                        entity.GetBlendMode(),
+                                        entity.GetStencilDepth());
   }
 
   auto maybe_input_uvs = input_snapshot->GetCoverageUVs(coverage);
@@ -139,10 +141,12 @@
   sampler_desc.min_filter = MinMagFilter::kLinear;
   sampler_desc.mag_filter = MinMagFilter::kLinear;
 
-  return Snapshot{.texture = out_texture,
-                  .transform = Matrix::MakeTranslation(coverage.origin),
-                  .sampler_descriptor = sampler_desc,
-                  .opacity = input_snapshot->opacity};
+  return Contents::EntityFromSnapshot(
+      Snapshot{.texture = out_texture,
+               .transform = Matrix::MakeTranslation(coverage.origin),
+               .sampler_descriptor = sampler_desc,
+               .opacity = input_snapshot->opacity},
+      entity.GetBlendMode(), entity.GetStencilDepth());
 }
 
 std::optional<Rect> DirectionalMorphologyFilterContents::GetFilterCoverage(
diff --git a/impeller/entity/contents/filters/morphology_filter_contents.h b/impeller/entity/contents/filters/morphology_filter_contents.h
index 0028039..6c4ac20 100644
--- a/impeller/entity/contents/filters/morphology_filter_contents.h
+++ b/impeller/entity/contents/filters/morphology_filter_contents.h
@@ -31,12 +31,11 @@
 
  private:
   // |FilterContents|
-  std::optional<Snapshot> RenderFilter(
-      const FilterInput::Vector& input_textures,
-      const ContentContext& renderer,
-      const Entity& entity,
-      const Matrix& effect_transform,
-      const Rect& coverage) const override;
+  std::optional<Entity> RenderFilter(const FilterInput::Vector& input_textures,
+                                     const ContentContext& renderer,
+                                     const Entity& entity,
+                                     const Matrix& effect_transform,
+                                     const Rect& coverage) const override;
 
   Radius radius_;
   Vector2 direction_;
diff --git a/impeller/entity/contents/filters/srgb_to_linear_filter_contents.cc b/impeller/entity/contents/filters/srgb_to_linear_filter_contents.cc
index ee483b6..7b38cdb 100644
--- a/impeller/entity/contents/filters/srgb_to_linear_filter_contents.cc
+++ b/impeller/entity/contents/filters/srgb_to_linear_filter_contents.cc
@@ -17,7 +17,7 @@
 
 SrgbToLinearFilterContents::~SrgbToLinearFilterContents() = default;
 
-std::optional<Snapshot> SrgbToLinearFilterContents::RenderFilter(
+std::optional<Entity> SrgbToLinearFilterContents::RenderFilter(
     const FilterInput::Vector& inputs,
     const ContentContext& renderer,
     const Entity& entity,
@@ -81,11 +81,12 @@
   }
   out_texture->SetLabel("SrgbToLinear Texture");
 
-  return Snapshot{
-      .texture = out_texture,
-      .transform = input_snapshot->transform,
-      .sampler_descriptor = input_snapshot->sampler_descriptor,
-      .opacity = GetAbsorbOpacity() ? 1.0f : input_snapshot->opacity};
+  return Contents::EntityFromSnapshot(
+      Snapshot{.texture = out_texture,
+               .transform = input_snapshot->transform,
+               .sampler_descriptor = input_snapshot->sampler_descriptor,
+               .opacity = GetAbsorbOpacity() ? 1.0f : input_snapshot->opacity},
+      entity.GetBlendMode(), entity.GetStencilDepth());
 }
 
 }  // namespace impeller
diff --git a/impeller/entity/contents/filters/srgb_to_linear_filter_contents.h b/impeller/entity/contents/filters/srgb_to_linear_filter_contents.h
index 07b3307..6568e9d 100644
--- a/impeller/entity/contents/filters/srgb_to_linear_filter_contents.h
+++ b/impeller/entity/contents/filters/srgb_to_linear_filter_contents.h
@@ -17,12 +17,11 @@
 
  private:
   // |FilterContents|
-  std::optional<Snapshot> RenderFilter(
-      const FilterInput::Vector& input_textures,
-      const ContentContext& renderer,
-      const Entity& entity,
-      const Matrix& effect_transform,
-      const Rect& coverage) const override;
+  std::optional<Entity> RenderFilter(const FilterInput::Vector& input_textures,
+                                     const ContentContext& renderer,
+                                     const Entity& entity,
+                                     const Matrix& effect_transform,
+                                     const Rect& coverage) const override;
 
   FML_DISALLOW_COPY_AND_ASSIGN(SrgbToLinearFilterContents);
 };
diff --git a/impeller/entity/contents/filters/yuv_to_rgb_filter_contents.cc b/impeller/entity/contents/filters/yuv_to_rgb_filter_contents.cc
index b00a4b9..26b9fd9 100644
--- a/impeller/entity/contents/filters/yuv_to_rgb_filter_contents.cc
+++ b/impeller/entity/contents/filters/yuv_to_rgb_filter_contents.cc
@@ -36,7 +36,7 @@
   yuv_color_space_ = yuv_color_space;
 }
 
-std::optional<Snapshot> YUVToRGBFilterContents::RenderFilter(
+std::optional<Entity> YUVToRGBFilterContents::RenderFilter(
     const FilterInput::Vector& inputs,
     const ContentContext& renderer,
     const Entity& entity,
@@ -118,7 +118,9 @@
   }
   out_texture->SetLabel("YUVToRGB Texture");
 
-  return Snapshot{.texture = out_texture};
+  return Contents::EntityFromSnapshot(Snapshot{.texture = out_texture},
+                                      entity.GetBlendMode(),
+                                      entity.GetStencilDepth());
 }
 
 }  // namespace impeller
diff --git a/impeller/entity/contents/filters/yuv_to_rgb_filter_contents.h b/impeller/entity/contents/filters/yuv_to_rgb_filter_contents.h
index 7e15cb5..45ac58c 100644
--- a/impeller/entity/contents/filters/yuv_to_rgb_filter_contents.h
+++ b/impeller/entity/contents/filters/yuv_to_rgb_filter_contents.h
@@ -18,12 +18,11 @@
 
  private:
   // |FilterContents|
-  std::optional<Snapshot> RenderFilter(
-      const FilterInput::Vector& input_textures,
-      const ContentContext& renderer,
-      const Entity& entity,
-      const Matrix& effect_transform,
-      const Rect& coverage) const override;
+  std::optional<Entity> RenderFilter(const FilterInput::Vector& input_textures,
+                                     const ContentContext& renderer,
+                                     const Entity& entity,
+                                     const Matrix& effect_transform,
+                                     const Rect& coverage) const override;
 
   YUVColorSpace yuv_color_space_ = YUVColorSpace::kBT601LimitedRange;