diff --git a/impeller/entity/contents/content_context.cc b/impeller/entity/contents/content_context.cc index d76131fd400a2..886bc54e95225 100644 --- a/impeller/entity/contents/content_context.cc +++ b/impeller/entity/contents/content_context.cc @@ -405,13 +405,12 @@ bool ContentContext::IsValid() const { return is_valid_; } -std::shared_ptr ContentContext::MakeSubpass( +fml::StatusOr ContentContext::MakeSubpass( const std::string& label, ISize texture_size, const SubpassCallback& subpass_callback, bool msaa_enabled) const { - auto context = GetContext(); - + std::shared_ptr context = GetContext(); RenderTarget subpass_target; if (context->GetCapabilities()->SupportsOffscreenMSAA() && msaa_enabled) { subpass_target = RenderTarget::CreateOffscreenMSAA( @@ -428,32 +427,41 @@ std::shared_ptr ContentContext::MakeSubpass( std::nullopt // stencil_attachment_config ); } + return MakeSubpass(label, subpass_target, subpass_callback); +} + +fml::StatusOr ContentContext::MakeSubpass( + const std::string& label, + const RenderTarget& subpass_target, + const SubpassCallback& subpass_callback) const { + std::shared_ptr context = GetContext(); + auto subpass_texture = subpass_target.GetRenderTargetTexture(); if (!subpass_texture) { - return nullptr; + return fml::Status(fml::StatusCode::kUnknown, ""); } auto sub_command_buffer = context->CreateCommandBuffer(); sub_command_buffer->SetLabel(SPrintF("%s CommandBuffer", label.c_str())); if (!sub_command_buffer) { - return nullptr; + return fml::Status(fml::StatusCode::kUnknown, ""); } auto sub_renderpass = sub_command_buffer->CreateRenderPass(subpass_target); if (!sub_renderpass) { - return nullptr; + return fml::Status(fml::StatusCode::kUnknown, ""); } sub_renderpass->SetLabel(SPrintF("%s RenderPass", label.c_str())); if (!subpass_callback(*this, *sub_renderpass)) { - return nullptr; + return fml::Status(fml::StatusCode::kUnknown, ""); } if (!sub_command_buffer->EncodeAndSubmit(sub_renderpass)) { - return nullptr; + return fml::Status(fml::StatusCode::kUnknown, ""); } - return subpass_texture; + return subpass_target; } #if IMPELLER_ENABLE_3D diff --git a/impeller/entity/contents/content_context.h b/impeller/entity/contents/content_context.h index 12d6120608d04..98dc447f009fb 100644 --- a/impeller/entity/contents/content_context.h +++ b/impeller/entity/contents/content_context.h @@ -12,6 +12,7 @@ #include "flutter/fml/build_config.h" #include "flutter/fml/logging.h" +#include "flutter/fml/status_or.h" #include "impeller/base/validation.h" #include "impeller/core/formats.h" #include "impeller/entity/entity.h" @@ -692,10 +693,17 @@ class ContentContext { /// @brief Creates a new texture of size `texture_size` and calls /// `subpass_callback` with a `RenderPass` for drawing to the texture. - std::shared_ptr MakeSubpass(const std::string& label, - ISize texture_size, - const SubpassCallback& subpass_callback, - bool msaa_enabled = true) const; + fml::StatusOr MakeSubpass( + const std::string& label, + ISize texture_size, + const SubpassCallback& subpass_callback, + bool msaa_enabled = true) const; + + /// Makes a subpass that will render to `subpass_target`. + fml::StatusOr MakeSubpass( + const std::string& label, + const RenderTarget& subpass_target, + const SubpassCallback& subpass_callback) const; std::shared_ptr GetLazyGlyphAtlas() const { return lazy_glyph_atlas_; diff --git a/impeller/entity/contents/contents.cc b/impeller/entity/contents/contents.cc index c87c690366cb9..ede2f7e15a1e3 100644 --- a/impeller/entity/contents/contents.cc +++ b/impeller/entity/contents/contents.cc @@ -79,7 +79,7 @@ std::optional Contents::RenderToSnapshot( } } - auto texture = renderer.MakeSubpass( + fml::StatusOr render_target = renderer.MakeSubpass( label, ISize::Ceil(coverage->GetSize()), [&contents = *this, &entity, &coverage](const ContentContext& renderer, RenderPass& pass) -> bool { @@ -92,12 +92,12 @@ std::optional Contents::RenderToSnapshot( }, msaa_enabled); - if (!texture) { + if (!render_target.ok()) { return std::nullopt; } auto snapshot = Snapshot{ - .texture = texture, + .texture = render_target.value().GetRenderTargetTexture(), .transform = Matrix::MakeTranslation(coverage->GetOrigin()), }; if (sampler_descriptor.has_value()) { diff --git a/impeller/entity/contents/filters/blend_filter_contents.cc b/impeller/entity/contents/filters/blend_filter_contents.cc index 866bd8041bbc4..cb56c3f1eafac 100644 --- a/impeller/entity/contents/filters/blend_filter_contents.cc +++ b/impeller/entity/contents/filters/blend_filter_contents.cc @@ -223,15 +223,15 @@ static std::optional AdvancedBlend( return true; }; - auto out_texture = renderer.MakeSubpass( + fml::StatusOr render_target = renderer.MakeSubpass( "Advanced Blend Filter", ISize(subpass_coverage.GetSize()), callback); - if (!out_texture) { + if (!render_target.ok()) { return std::nullopt; } return Entity::FromSnapshot( Snapshot{ - .texture = out_texture, + .texture = render_target.value().GetRenderTargetTexture(), .transform = Matrix::MakeTranslation(subpass_coverage.GetOrigin()), // Since we absorbed the transform of the inputs and used the // respective snapshot sampling modes when blending, pass on @@ -646,16 +646,16 @@ static std::optional PipelineBlend( return true; }; - auto out_texture = renderer.MakeSubpass( + fml::StatusOr render_target = renderer.MakeSubpass( "Pipeline Blend Filter", ISize(subpass_coverage.GetSize()), callback); - if (!out_texture) { + if (!render_target.ok()) { return std::nullopt; } return Entity::FromSnapshot( Snapshot{ - .texture = out_texture, + .texture = render_target.value().GetRenderTargetTexture(), .transform = Matrix::MakeTranslation(subpass_coverage.GetOrigin()), // Since we absorbed the transform of the inputs and used the // respective snapshot sampling modes when blending, pass on diff --git a/impeller/entity/contents/filters/directional_gaussian_blur_filter_contents.cc b/impeller/entity/contents/filters/directional_gaussian_blur_filter_contents.cc index ff07b7a45c5eb..5760d7ac250d2 100644 --- a/impeller/entity/contents/filters/directional_gaussian_blur_filter_contents.cc +++ b/impeller/entity/contents/filters/directional_gaussian_blur_filter_contents.cc @@ -37,8 +37,8 @@ Sigma ScaleSigma(Sigma sigma) { DirectionalGaussianBlurFilterContents::DirectionalGaussianBlurFilterContents() = default; -DirectionalGaussianBlurFilterContents::~ -DirectionalGaussianBlurFilterContents() = default; +DirectionalGaussianBlurFilterContents:: + ~DirectionalGaussianBlurFilterContents() = default; void DirectionalGaussianBlurFilterContents::SetSigma(Sigma sigma) { blur_sigma_ = sigma; @@ -256,10 +256,10 @@ std::optional DirectionalGaussianBlurFilterContents::RenderFilter( Vector2 scaled_size = pass_texture_rect.GetSize() * scale; ISize floored_size = ISize(scaled_size.x, scaled_size.y); - auto out_texture = renderer.MakeSubpass("Directional Gaussian Blur Filter", - floored_size, subpass_callback); + fml::StatusOr render_target = renderer.MakeSubpass( + "Directional Gaussian Blur Filter", floored_size, subpass_callback); - if (!out_texture) { + if (!render_target.ok()) { return std::nullopt; } @@ -271,7 +271,7 @@ std::optional DirectionalGaussianBlurFilterContents::RenderFilter( return Entity::FromSnapshot( Snapshot{ - .texture = out_texture, + .texture = render_target.value().GetRenderTargetTexture(), .transform = texture_rotate.Invert() * Matrix::MakeTranslation(pass_texture_rect.GetOrigin()) * diff --git a/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc b/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc index e2ac83eee4819..453c37dd4b28b 100644 --- a/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc +++ b/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc @@ -65,7 +65,7 @@ void SetTileMode(SamplerDescriptor* descriptor, /// Makes a subpass that will render the scaled down input and add the /// transparent gutter required for the blur halo. -std::shared_ptr MakeDownsampleSubpass( +fml::StatusOr MakeDownsampleSubpass( const ContentContext& renderer, std::shared_ptr input_texture, const SamplerDescriptor& sampler_descriptor, @@ -110,21 +110,24 @@ std::shared_ptr MakeDownsampleSubpass( return true; }; - std::shared_ptr out_texture = renderer.MakeSubpass( + fml::StatusOr render_target = renderer.MakeSubpass( "Gaussian Blur Filter", subpass_size, subpass_callback); - return out_texture; + return render_target; } -std::shared_ptr MakeBlurSubpass( +fml::StatusOr MakeBlurSubpass( const ContentContext& renderer, - std::shared_ptr input_texture, + const RenderTarget& input_pass, const SamplerDescriptor& sampler_descriptor, Entity::TileMode tile_mode, - const GaussianBlurFragmentShader::BlurInfo& blur_info) { + const GaussianBlurFragmentShader::BlurInfo& blur_info, + std::optional destination_target) { if (blur_info.blur_sigma < kEhCloseEnough) { - return input_texture; + return input_pass; } + std::shared_ptr input_texture = input_pass.GetRenderTargetTexture(); + // TODO(gaaclarke): This blurs the whole image, but because we know the clip // region we could focus on just blurring that. ISize subpass_size = input_texture->GetSize(); @@ -171,9 +174,13 @@ std::shared_ptr MakeBlurSubpass( return true; }; - std::shared_ptr out_texture = renderer.MakeSubpass( - "Gaussian Blur Filter", subpass_size, subpass_callback); - return out_texture; + if (destination_target.has_value()) { + return renderer.MakeSubpass("Gaussian Blur Filter", + destination_target.value(), subpass_callback); + } else { + return renderer.MakeSubpass("Gaussian Blur Filter", subpass_size, + subpass_callback); + } } } // namespace @@ -293,38 +300,65 @@ std::optional GaussianBlurFilterContents::RenderFilter( Quad uvs = CalculateUVs(inputs[0], entity, source_rect_padded, input_snapshot->texture->GetSize()); - std::shared_ptr pass1_out_texture = MakeDownsampleSubpass( + fml::StatusOr pass1_out = MakeDownsampleSubpass( renderer, input_snapshot->texture, input_snapshot->sampler_descriptor, uvs, subpass_size, tile_mode_); - Vector2 pass1_pixel_size = 1.0 / Vector2(pass1_out_texture->GetSize()); + if (!pass1_out.ok()) { + return std::nullopt; + } - std::shared_ptr pass2_out_texture = - MakeBlurSubpass(renderer, pass1_out_texture, + Vector2 pass1_pixel_size = + 1.0 / Vector2(pass1_out.value().GetRenderTargetTexture()->GetSize()); + + fml::StatusOr pass2_out = + MakeBlurSubpass(renderer, /*input_pass=*/pass1_out.value(), input_snapshot->sampler_descriptor, tile_mode_, GaussianBlurFragmentShader::BlurInfo{ .blur_uv_offset = Point(0.0, pass1_pixel_size.y), .blur_sigma = scaled_sigma.y * effective_scalar.y, .blur_radius = blur_radius.y * effective_scalar.y, .step_size = 1.0, - }); + }, + /*destination_target=*/std::nullopt); - // TODO(gaaclarke): Make this pass reuse the texture from pass1. - std::shared_ptr pass3_out_texture = - MakeBlurSubpass(renderer, pass2_out_texture, + if (!pass2_out.ok()) { + return std::nullopt; + } + + // Only ping pong if the first pass actually created a render target. + auto pass3_destination = pass2_out.value().GetRenderTargetTexture() != + pass1_out.value().GetRenderTargetTexture() + ? std::optional(pass1_out.value()) + : std::optional(std::nullopt); + + fml::StatusOr pass3_out = + MakeBlurSubpass(renderer, /*input_pass=*/pass2_out.value(), input_snapshot->sampler_descriptor, tile_mode_, GaussianBlurFragmentShader::BlurInfo{ .blur_uv_offset = Point(pass1_pixel_size.x, 0.0), .blur_sigma = scaled_sigma.x * effective_scalar.x, .blur_radius = blur_radius.x * effective_scalar.x, .step_size = 1.0, - }); + }, + pass3_destination); + + if (!pass3_out.ok()) { + return std::nullopt; + } + + // The ping-pong approach requires that each render pass output has the same + // size. + FML_DCHECK((pass1_out.value().GetRenderTargetSize() == + pass2_out.value().GetRenderTargetSize()) && + (pass2_out.value().GetRenderTargetSize() == + pass3_out.value().GetRenderTargetSize())); SamplerDescriptor sampler_desc = MakeSamplerDescriptor( MinMagFilter::kLinear, SamplerAddressMode::kClampToEdge); return Entity::FromSnapshot( - Snapshot{.texture = pass3_out_texture, + Snapshot{.texture = pass3_out.value().GetRenderTargetTexture(), .transform = input_snapshot->transform * padding_snapshot_adjustment * Matrix::MakeScale(1 / effective_scalar), diff --git a/impeller/entity/contents/filters/morphology_filter_contents.cc b/impeller/entity/contents/filters/morphology_filter_contents.cc index abba353fe218d..f94156e8c0dc6 100644 --- a/impeller/entity/contents/filters/morphology_filter_contents.cc +++ b/impeller/entity/contents/filters/morphology_filter_contents.cc @@ -137,9 +137,9 @@ std::optional DirectionalMorphologyFilterContents::RenderFilter( return pass.AddCommand(std::move(cmd)); }; - auto out_texture = renderer.MakeSubpass("Directional Morphology Filter", - ISize(coverage.GetSize()), callback); - if (!out_texture) { + fml::StatusOr render_target = renderer.MakeSubpass( + "Directional Morphology Filter", ISize(coverage.GetSize()), callback); + if (!render_target.ok()) { return std::nullopt; } @@ -148,7 +148,7 @@ std::optional DirectionalMorphologyFilterContents::RenderFilter( sampler_desc.mag_filter = MinMagFilter::kLinear; return Entity::FromSnapshot( - Snapshot{.texture = out_texture, + Snapshot{.texture = render_target.value().GetRenderTargetTexture(), .transform = Matrix::MakeTranslation(coverage.GetOrigin()), .sampler_descriptor = sampler_desc, .opacity = input_snapshot->opacity},