From c033c0ed3402e42c412e803ef3ccdfc50108d70e Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Tue, 16 Aug 2022 14:03:36 -0700 Subject: [PATCH] [Impeller] Avoid unnecessary pre-pass when filtering images (flutter/engine#35422) --- engine/src/flutter/impeller/aiks/canvas.cc | 3 +-- .../impeller/aiks/paint_pass_delegate.cc | 4 +-- .../contents/filters/filter_contents.cc | 5 ++-- .../filters/gaussian_blur_filter_contents.cc | 24 ++++++++++------- .../entity/contents/texture_contents.cc | 26 +++++++++++++++++++ .../entity/contents/texture_contents.h | 12 ++++++++- .../flutter/impeller/entity/entity_pass.cc | 3 +-- .../src/flutter/impeller/renderer/snapshot.h | 5 +++- 8 files changed, 61 insertions(+), 21 deletions(-) diff --git a/engine/src/flutter/impeller/aiks/canvas.cc b/engine/src/flutter/impeller/aiks/canvas.cc index 6fd63ee662e..2e167659e6b 100644 --- a/engine/src/flutter/impeller/aiks/canvas.cc +++ b/engine/src/flutter/impeller/aiks/canvas.cc @@ -274,8 +274,7 @@ void Canvas::DrawImageRect(std::shared_ptr image, return; } - auto contents = std::make_shared(); - contents->SetPath(PathBuilder{}.AddRect(dest).TakePath()); + auto contents = TextureContents::MakeRect(dest); contents->SetTexture(image->GetTexture()); contents->SetSourceRect(source); contents->SetSamplerDescriptor(std::move(sampler)); diff --git a/engine/src/flutter/impeller/aiks/paint_pass_delegate.cc b/engine/src/flutter/impeller/aiks/paint_pass_delegate.cc index f5d6598309f..146549f604d 100644 --- a/engine/src/flutter/impeller/aiks/paint_pass_delegate.cc +++ b/engine/src/flutter/impeller/aiks/paint_pass_delegate.cc @@ -34,9 +34,7 @@ bool PaintPassDelegate::CanCollapseIntoParentPass() { // |EntityPassDelgate| std::shared_ptr PaintPassDelegate::CreateContentsForSubpassTarget( std::shared_ptr target) { - auto contents = std::make_shared(); - contents->SetPath( - PathBuilder{}.AddRect(Rect::MakeSize(target->GetSize())).TakePath()); + auto contents = TextureContents::MakeRect(Rect::MakeSize(target->GetSize())); contents->SetTexture(target); contents->SetSourceRect(Rect::MakeSize(target->GetSize())); contents->SetOpacity(paint_.color.alpha); diff --git a/engine/src/flutter/impeller/entity/contents/filters/filter_contents.cc b/engine/src/flutter/impeller/entity/contents/filters/filter_contents.cc index 60b1900e981..cc5c76c930b 100644 --- a/engine/src/flutter/impeller/entity/contents/filters/filter_contents.cc +++ b/engine/src/flutter/impeller/entity/contents/filters/filter_contents.cc @@ -152,10 +152,9 @@ bool FilterContents::Render(const ContentContext& renderer, // Draw the result texture, respecting the transform and clip stack. - auto contents = std::make_shared(); - contents->SetPath( - PathBuilder{}.AddRect(filter_coverage.value()).GetCurrentPath()); + auto contents = TextureContents::MakeRect(filter_coverage.value()); contents->SetTexture(snapshot.texture); + contents->SetSamplerDescriptor(snapshot.sampler_descriptor); contents->SetSourceRect(Rect::MakeSize(snapshot.texture->GetSize())); Entity e; diff --git a/engine/src/flutter/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc b/engine/src/flutter/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc index 3de4ad2e71d..43b53d702a2 100644 --- a/engine/src/flutter/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc +++ b/engine/src/flutter/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc @@ -104,6 +104,7 @@ std::optional DirectionalGaussianBlurFilterContents::RenderFilter( if (!input_snapshot.has_value()) { return std::nullopt; } + auto maybe_input_uvs = input_snapshot->GetCoverageUVs(coverage); if (!maybe_input_uvs.has_value()) { return std::nullopt; @@ -164,12 +165,6 @@ std::optional DirectionalGaussianBlurFilterContents::RenderFilter( frag_info.outer_blur_factor = outer_blur_factor_; frag_info.texture_size = Point(input_snapshot->GetCoverage().value().size); - SamplerDescriptor sampler_desc; - sampler_desc.min_filter = MinMagFilter::kLinear; - sampler_desc.mag_filter = MinMagFilter::kLinear; - auto sampler = - renderer.GetContext()->GetSamplerLibrary()->GetSampler(sampler_desc); - Command cmd; cmd.label = "Gaussian Blur Filter"; auto options = OptionsFromPass(pass); @@ -177,8 +172,14 @@ std::optional DirectionalGaussianBlurFilterContents::RenderFilter( cmd.pipeline = renderer.GetGaussianBlurPipeline(options); cmd.BindVertices(vtx_buffer); - FS::BindTextureSampler(cmd, input_snapshot->texture, sampler); - FS::BindAlphaMaskSampler(cmd, source_snapshot->texture, sampler); + FS::BindTextureSampler( + cmd, input_snapshot->texture, + renderer.GetContext()->GetSamplerLibrary()->GetSampler( + input_snapshot->sampler_descriptor)); + FS::BindAlphaMaskSampler( + cmd, source_snapshot->texture, + renderer.GetContext()->GetSamplerLibrary()->GetSampler( + source_snapshot->sampler_descriptor)); VS::BindFrameInfo(cmd, host_buffer.EmplaceUniform(frame_info)); FS::BindFragInfo(cmd, host_buffer.EmplaceUniform(frag_info)); @@ -191,8 +192,13 @@ std::optional DirectionalGaussianBlurFilterContents::RenderFilter( } out_texture->SetLabel("DirectionalGaussianBlurFilter Texture"); + SamplerDescriptor sampler_desc; + sampler_desc.min_filter = MinMagFilter::kLinear; + sampler_desc.mag_filter = MinMagFilter::kLinear; + return Snapshot{.texture = out_texture, - .transform = Matrix::MakeTranslation(coverage.origin)}; + .transform = Matrix::MakeTranslation(coverage.origin), + .sampler_descriptor = sampler_desc}; } std::optional DirectionalGaussianBlurFilterContents::GetFilterCoverage( diff --git a/engine/src/flutter/impeller/entity/contents/texture_contents.cc b/engine/src/flutter/impeller/entity/contents/texture_contents.cc index 10a72daab70..4e77668d1dd 100644 --- a/engine/src/flutter/impeller/entity/contents/texture_contents.cc +++ b/engine/src/flutter/impeller/entity/contents/texture_contents.cc @@ -4,12 +4,14 @@ #include "texture_contents.h" +#include #include #include "impeller/entity/contents/content_context.h" #include "impeller/entity/entity.h" #include "impeller/entity/texture_fill.frag.h" #include "impeller/entity/texture_fill.vert.h" +#include "impeller/geometry/path_builder.h" #include "impeller/renderer/render_pass.h" #include "impeller/renderer/sampler_library.h" #include "impeller/tessellator/tessellator.h" @@ -20,8 +22,16 @@ TextureContents::TextureContents() = default; TextureContents::~TextureContents() = default; +std::shared_ptr TextureContents::MakeRect(Rect destination) { + auto contents = std::make_shared(); + contents->path_ = PathBuilder{}.AddRect(destination).TakePath(); + contents->is_rect_ = true; + return contents; +} + void TextureContents::SetPath(Path path) { path_ = std::move(path); + is_rect_ = false; } void TextureContents::SetTexture(std::shared_ptr texture) { @@ -43,6 +53,22 @@ std::optional TextureContents::GetCoverage(const Entity& entity) const { return path_.GetTransformedBoundingBox(entity.GetTransformation()); }; +std::optional TextureContents::RenderToSnapshot( + const ContentContext& renderer, + const Entity& entity) const { + // Passthrough textures that have simple rectangle paths and complete source + // rects. + if (is_rect_ && source_rect_ == Rect::MakeSize(texture_->GetSize())) { + auto scale = + Vector2(path_.GetBoundingBox()->size / Size(texture_->GetSize())); + return Snapshot{ + .texture = texture_, + .transform = entity.GetTransformation() * Matrix::MakeScale(scale), + .sampler_descriptor = sampler_descriptor_}; + } + return Contents::RenderToSnapshot(renderer, entity); +} + bool TextureContents::Render(const ContentContext& renderer, const Entity& entity, RenderPass& pass) const { diff --git a/engine/src/flutter/impeller/entity/contents/texture_contents.h b/engine/src/flutter/impeller/entity/contents/texture_contents.h index 4b02d88c0b6..b218a08503a 100644 --- a/engine/src/flutter/impeller/entity/contents/texture_contents.h +++ b/engine/src/flutter/impeller/entity/contents/texture_contents.h @@ -23,6 +23,11 @@ class TextureContents final : public Contents { ~TextureContents() override; + /// @brief A common case factory that marks the texture contents as having a + /// destination rectangle. In this situation, a subpass can be avoided + /// when image filters are applied. + static std::shared_ptr MakeRect(Rect destination); + void SetPath(Path path); void SetTexture(std::shared_ptr texture); @@ -42,13 +47,18 @@ class TextureContents final : public Contents { // |Contents| std::optional GetCoverage(const Entity& entity) const override; + // |Contents| + std::optional RenderToSnapshot(const ContentContext& renderer, + const Entity& entity) const override; + // |Contents| bool Render(const ContentContext& renderer, const Entity& entity, RenderPass& pass) const override; - public: + private: Path path_; + bool is_rect_ = false; std::shared_ptr texture_; SamplerDescriptor sampler_descriptor_ = {}; diff --git a/engine/src/flutter/impeller/entity/entity_pass.cc b/engine/src/flutter/impeller/entity/entity_pass.cc index 2a317b042fd..832dfe82734 100644 --- a/engine/src/flutter/impeller/entity/entity_pass.cc +++ b/engine/src/flutter/impeller/entity/entity_pass.cc @@ -161,8 +161,7 @@ bool EntityPass::Render(ContentContext& renderer, { auto size_rect = Rect::MakeSize(offscreen_target.GetRenderTargetSize()); - auto contents = std::make_shared(); - contents->SetPath(PathBuilder{}.AddRect(size_rect).TakePath()); + auto contents = TextureContents::MakeRect(size_rect); contents->SetTexture(offscreen_target.GetRenderTargetTexture()); contents->SetSourceRect(size_rect); diff --git a/engine/src/flutter/impeller/renderer/snapshot.h b/engine/src/flutter/impeller/renderer/snapshot.h index a0fe419b069..65a4c9a73cf 100644 --- a/engine/src/flutter/impeller/renderer/snapshot.h +++ b/engine/src/flutter/impeller/renderer/snapshot.h @@ -11,6 +11,7 @@ #include "flutter/fml/macros.h" #include "impeller/geometry/matrix.h" #include "impeller/geometry/rect.h" +#include "impeller/renderer/sampler_descriptor.h" #include "impeller/renderer/texture.h" namespace impeller { @@ -18,12 +19,14 @@ namespace impeller { class ContentContext; class Entity; -/// Represents a texture and its intended draw position. +/// Represents a texture and its intended draw transform/sampler configuration. struct Snapshot { std::shared_ptr texture; /// The transform that should be applied to this texture for rendering. Matrix transform; + SamplerDescriptor sampler_descriptor; + std::optional GetCoverage() const; /// @brief Get the transform that converts screen space coordinates to the UV