From 3235eac2a0431e7a1ce41f35a41902f526c619b8 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Mon, 21 Mar 2022 17:48:28 -0700 Subject: [PATCH] Separate filters into different TUs (#93) --- impeller/entity/BUILD.gn | 8 +- impeller/entity/contents/filter_contents.cc | 472 ------------------ .../contents/filters/blend_filter_contents.cc | 188 +++++++ .../contents/filters/blend_filter_contents.h | 36 ++ .../contents/filters/filter_contents.cc | 194 +++++++ .../contents/{ => filters}/filter_contents.h | 67 --- .../filters/gaussian_blur_filter_contents.cc | 101 ++++ .../filters/gaussian_blur_filter_contents.h | 40 ++ impeller/entity/entity_unittests.cc | 2 +- 9 files changed, 566 insertions(+), 542 deletions(-) delete mode 100644 impeller/entity/contents/filter_contents.cc create mode 100644 impeller/entity/contents/filters/blend_filter_contents.cc create mode 100644 impeller/entity/contents/filters/blend_filter_contents.h create mode 100644 impeller/entity/contents/filters/filter_contents.cc rename impeller/entity/contents/{ => filters}/filter_contents.h (54%) create mode 100644 impeller/entity/contents/filters/gaussian_blur_filter_contents.cc create mode 100644 impeller/entity/contents/filters/gaussian_blur_filter_contents.h diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index 39e828ff5e03d..e04288155e803 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -37,8 +37,12 @@ impeller_component("entity") { "contents/content_context.h", "contents/contents.cc", "contents/contents.h", - "contents/filter_contents.cc", - "contents/filter_contents.h", + "contents/filters/blend_filter_contents.cc", + "contents/filters/blend_filter_contents.h", + "contents/filters/filter_contents.cc", + "contents/filters/filter_contents.h", + "contents/filters/gaussian_blur_filter_contents.cc", + "contents/filters/gaussian_blur_filter_contents.h", "contents/linear_gradient_contents.cc", "contents/linear_gradient_contents.h", "contents/solid_color_contents.cc", diff --git a/impeller/entity/contents/filter_contents.cc b/impeller/entity/contents/filter_contents.cc deleted file mode 100644 index 579318d51bf30..0000000000000 --- a/impeller/entity/contents/filter_contents.cc +++ /dev/null @@ -1,472 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "filter_contents.h" - -#include -#include -#include -#include -#include - -#include "flutter/fml/logging.h" -#include "impeller/base/validation.h" -#include "impeller/entity/contents/content_context.h" -#include "impeller/entity/contents/solid_color_contents.h" -#include "impeller/entity/contents/texture_contents.h" -#include "impeller/entity/entity.h" -#include "impeller/geometry/path_builder.h" -#include "impeller/renderer/command_buffer.h" -#include "impeller/renderer/formats.h" -#include "impeller/renderer/render_pass.h" -#include "impeller/renderer/sampler_descriptor.h" -#include "impeller/renderer/sampler_library.h" - -namespace impeller { - -/******************************************************************************* - ******* FilterContents - ******************************************************************************/ - -std::shared_ptr FilterContents::MakeBlend( - Entity::BlendMode blend_mode, - InputTextures input_textures) { - if (blend_mode > Entity::BlendMode::kLastAdvancedBlendMode) { - VALIDATION_LOG << "Invalid blend mode " << static_cast(blend_mode) - << " passed to FilterContents::MakeBlend."; - return nullptr; - } - - if (input_textures.size() < 2 || - blend_mode <= Entity::BlendMode::kLastPipelineBlendMode) { - auto blend = std::make_shared(); - blend->SetInputTextures(input_textures); - blend->SetBlendMode(blend_mode); - return blend; - } - - if (blend_mode <= Entity::BlendMode::kLastAdvancedBlendMode) { - InputVariant blend = input_textures[0]; - for (auto in_i = input_textures.begin() + 1; in_i < input_textures.end(); - in_i++) { - auto new_blend = std::make_shared(); - new_blend->SetInputTextures({blend, *in_i}); - new_blend->SetBlendMode(blend_mode); - blend = new_blend; - } - return std::get>(blend); - } - - FML_UNREACHABLE(); -} - -std::shared_ptr FilterContents::MakeDirectionalGaussianBlur( - InputVariant input_texture, - Scalar radius, - Vector2 direction, - bool clip_border) { - auto blur = std::make_shared(); - blur->SetInputTextures({input_texture}); - blur->SetRadius(radius); - blur->SetDirection(direction); - blur->SetClipBorder(clip_border); - return blur; -} - -std::shared_ptr FilterContents::MakeGaussianBlur( - InputVariant input_texture, - Scalar radius, - bool clip_border) { - auto x_blur = MakeDirectionalGaussianBlur(input_texture, radius, Point(1, 0), - clip_border); - return MakeDirectionalGaussianBlur(x_blur, radius, Point(0, 1), false); -} - -FilterContents::FilterContents() = default; - -FilterContents::~FilterContents() = default; - -void FilterContents::SetInputTextures(InputTextures input_textures) { - input_textures_ = std::move(input_textures); -} - -bool FilterContents::Render(const ContentContext& renderer, - const Entity& entity, - RenderPass& pass) const { - // Run the filter. - - auto maybe_texture = RenderFilterToTexture(renderer, entity, pass); - if (!maybe_texture.has_value()) { - return false; - } - auto& texture = maybe_texture.value(); - - // Draw the resulting texture to the given destination rect, respecting the - // transform and clip stack. - - auto contents = std::make_shared(); - contents->SetTexture(texture); - contents->SetSourceRect(IRect::MakeSize(texture->GetSize())); - - return contents->Render(renderer, entity, pass); -} - -std::optional> FilterContents::RenderFilterToTexture( - const ContentContext& renderer, - const Entity& entity, - RenderPass& pass) const { - auto output_size = GetOutputSize(input_textures_); - if (output_size.IsZero()) { - return std::nullopt; - } - - // Resolve all inputs as textures. - - std::vector> input_textures; - input_textures.reserve(input_textures_.size()); - for (const auto& input : input_textures_) { - if (auto filter = std::get_if>(&input)) { - auto texture = - filter->get()->RenderFilterToTexture(renderer, entity, pass); - if (!texture.has_value()) { - return std::nullopt; - } - input_textures.push_back(std::move(texture.value())); - } else if (auto texture = std::get_if>(&input)) { - input_textures.push_back(*texture); - } else { - FML_UNREACHABLE(); - } - } - - // Create a new texture and render the filter to it. - - auto context = renderer.GetContext(); - - auto subpass_target = RenderTarget::CreateOffscreen(*context, output_size); - auto subpass_texture = subpass_target.GetRenderTargetTexture(); - if (!subpass_texture) { - return std::nullopt; - } - - auto sub_command_buffer = context->CreateRenderCommandBuffer(); - sub_command_buffer->SetLabel("Offscreen Filter Command Buffer"); - if (!sub_command_buffer) { - return std::nullopt; - } - - auto sub_renderpass = sub_command_buffer->CreateRenderPass(subpass_target); - if (!sub_renderpass) { - return std::nullopt; - } - sub_renderpass->SetLabel("OffscreenFilterPass"); - - if (!RenderFilter(input_textures, renderer, *sub_renderpass)) { - return std::nullopt; - } - - if (!sub_renderpass->EncodeCommands(*context->GetTransientsAllocator())) { - return std::nullopt; - } - - if (!sub_command_buffer->SubmitCommands()) { - return std::nullopt; - } - - return subpass_texture; -} - -ISize FilterContents::GetOutputSize() const { - if (input_textures_.empty()) { - return {}; - } - return GetOutputSize(input_textures_); -} - -ISize FilterContents::GetOutputSize(const InputTextures& input_textures) const { - if (auto filter = - std::get_if>(&input_textures[0])) { - return filter->get()->GetOutputSize(input_textures); - } - - if (auto texture = - std::get_if>(&input_textures[0])) { - return texture->get()->GetSize(); - } - - FML_UNREACHABLE(); -} - -/******************************************************************************* - ******* BlendFilterContents - ******************************************************************************/ - -BlendFilterContents::BlendFilterContents() { - SetBlendMode(Entity::BlendMode::kSourceOver); -} - -BlendFilterContents::~BlendFilterContents() = default; - -using PipelineProc = - std::shared_ptr (ContentContext::*)(ContentContextOptions) const; - -template -static void AdvancedBlendPass(std::shared_ptr input_d, - std::shared_ptr input_s, - std::shared_ptr sampler, - const ContentContext& renderer, - RenderPass& pass, - Command& cmd) {} - -template -static bool AdvancedBlend( - const std::vector>& input_textures, - const ContentContext& renderer, - RenderPass& pass, - PipelineProc pipeline_proc) { - if (input_textures.size() < 2) { - return false; - } - - auto& host_buffer = pass.GetTransientsBuffer(); - - VertexBufferBuilder vtx_builder; - vtx_builder.AddVertices({ - {Point(0, 0), Point(0, 0)}, - {Point(1, 0), Point(1, 0)}, - {Point(1, 1), Point(1, 1)}, - {Point(0, 0), Point(0, 0)}, - {Point(1, 1), Point(1, 1)}, - {Point(0, 1), Point(0, 1)}, - }); - auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer); - - typename VS::FrameInfo frame_info; - frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1)); - - auto uniform_view = host_buffer.EmplaceUniform(frame_info); - auto sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler({}); - - auto options = OptionsFromPass(pass); - options.blend_mode = Entity::BlendMode::kSource; - std::shared_ptr pipeline = - std::invoke(pipeline_proc, renderer, options); - - Command cmd; - cmd.label = "Advanced Blend Filter"; - cmd.BindVertices(vtx_buffer); - cmd.pipeline = std::move(pipeline); - VS::BindFrameInfo(cmd, uniform_view); - - FS::BindTextureSamplerDst(cmd, input_textures[0], sampler); - FS::BindTextureSamplerSrc(cmd, input_textures[1], sampler); - pass.AddCommand(cmd); - - return true; -} - -void BlendFilterContents::SetBlendMode(Entity::BlendMode blend_mode) { - if (blend_mode > Entity::BlendMode::kLastAdvancedBlendMode) { - VALIDATION_LOG << "Invalid blend mode " << static_cast(blend_mode) - << " assigned to BlendFilterContents."; - } - - blend_mode_ = blend_mode; - - if (blend_mode > Entity::BlendMode::kLastPipelineBlendMode) { - static_assert(Entity::BlendMode::kLastAdvancedBlendMode == - Entity::BlendMode::kScreen); - - switch (blend_mode) { - case Entity::BlendMode::kScreen: - advanced_blend_proc_ = - [](const std::vector>& input_textures, - const ContentContext& renderer, RenderPass& pass) { - PipelineProc p = &ContentContext::GetTextureBlendScreenPipeline; - return AdvancedBlend( - input_textures, renderer, pass, p); - }; - break; - default: - FML_UNREACHABLE(); - } - } -} - -static bool BasicBlend( - const std::vector>& input_textures, - const ContentContext& renderer, - RenderPass& pass, - Entity::BlendMode basic_blend) { - using VS = TextureBlendPipeline::VertexShader; - using FS = TextureBlendPipeline::FragmentShader; - - auto& host_buffer = pass.GetTransientsBuffer(); - - VertexBufferBuilder vtx_builder; - vtx_builder.AddVertices({ - {Point(0, 0), Point(0, 0)}, - {Point(1, 0), Point(1, 0)}, - {Point(1, 1), Point(1, 1)}, - {Point(0, 0), Point(0, 0)}, - {Point(1, 1), Point(1, 1)}, - {Point(0, 1), Point(0, 1)}, - }); - auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer); - - VS::FrameInfo frame_info; - frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1)); - - auto uniform_view = host_buffer.EmplaceUniform(frame_info); - auto sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler({}); - - // Draw the first texture using kSource. - - Command cmd; - cmd.label = "Basic Blend Filter"; - cmd.BindVertices(vtx_buffer); - auto options = OptionsFromPass(pass); - options.blend_mode = Entity::BlendMode::kSource; - cmd.pipeline = renderer.GetTextureBlendPipeline(options); - FS::BindTextureSamplerSrc(cmd, input_textures[0], sampler); - VS::BindFrameInfo(cmd, uniform_view); - pass.AddCommand(cmd); - - if (input_textures.size() < 2) { - return true; - } - - // Write subsequent textures using the selected blend mode. - - options.blend_mode = basic_blend; - cmd.pipeline = renderer.GetTextureBlendPipeline(options); - - for (auto texture_i = input_textures.begin() + 1; - texture_i < input_textures.end(); texture_i++) { - FS::BindTextureSamplerSrc(cmd, *texture_i, sampler); - pass.AddCommand(cmd); - } - - return true; -} - -bool BlendFilterContents::RenderFilter( - const std::vector>& input_textures, - const ContentContext& renderer, - RenderPass& pass) const { - if (input_textures.empty()) { - return true; - } - - if (input_textures.size() == 1) { - // Nothing to blend. - return BasicBlend(input_textures, renderer, pass, - Entity::BlendMode::kSource); - } - - if (blend_mode_ <= Entity::BlendMode::kLastPipelineBlendMode) { - return BasicBlend(input_textures, renderer, pass, blend_mode_); - } - - if (blend_mode_ <= Entity::BlendMode::kLastAdvancedBlendMode) { - return advanced_blend_proc_(input_textures, renderer, pass); - } - - FML_UNREACHABLE(); -} - -/******************************************************************************* - ******* DirectionalGaussianBlurFilterContents - ******************************************************************************/ - -DirectionalGaussianBlurFilterContents::DirectionalGaussianBlurFilterContents() = - default; - -DirectionalGaussianBlurFilterContents:: - ~DirectionalGaussianBlurFilterContents() = default; - -void DirectionalGaussianBlurFilterContents::SetRadius(Scalar radius) { - radius_ = std::max(radius, 1e-3f); -} - -void DirectionalGaussianBlurFilterContents::SetDirection(Vector2 direction) { - direction_ = direction.Normalize(); -} - -void DirectionalGaussianBlurFilterContents::SetClipBorder(bool clip) { - clip_ = clip; -} - -bool DirectionalGaussianBlurFilterContents::RenderFilter( - const std::vector>& input_textures, - const ContentContext& renderer, - RenderPass& pass) const { - using VS = GaussianBlurPipeline::VertexShader; - using FS = GaussianBlurPipeline::FragmentShader; - - auto& host_buffer = pass.GetTransientsBuffer(); - - ISize size = FilterContents::GetOutputSize(); - Point uv_offset = clip_ ? (Point(radius_, radius_) / size) : Point(); - // LTRB - Scalar uv[4] = { - -uv_offset.x, - -uv_offset.y, - 1 + uv_offset.x, - 1 + uv_offset.y, - }; - - VertexBufferBuilder vtx_builder; - vtx_builder.AddVertices({ - {Point(0, 0), Point(uv[0], uv[1])}, - {Point(size.width, 0), Point(uv[2], uv[1])}, - {Point(size.width, size.height), Point(uv[2], uv[3])}, - {Point(0, 0), Point(uv[0], uv[1])}, - {Point(size.width, size.height), Point(uv[2], uv[3])}, - {Point(0, size.height), Point(uv[0], uv[3])}, - }); - auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer); - - VS::FrameInfo frame_info; - frame_info.mvp = Matrix::MakeOrthographic(size); - frame_info.texture_size = Point(size); - frame_info.blur_radius = radius_; - frame_info.blur_direction = direction_; - - auto uniform_view = host_buffer.EmplaceUniform(frame_info); - auto sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler({}); - - Command cmd; - cmd.label = "Gaussian Blur Filter"; - auto options = OptionsFromPass(pass); - options.blend_mode = Entity::BlendMode::kSource; - cmd.pipeline = renderer.GetGaussianBlurPipeline(options); - cmd.BindVertices(vtx_buffer); - VS::BindFrameInfo(cmd, uniform_view); - for (const auto& texture : input_textures) { - FS::BindTextureSampler(cmd, texture, sampler); - pass.AddCommand(cmd); - } - - return true; -} - -ISize DirectionalGaussianBlurFilterContents::GetOutputSize( - const InputTextures& input_textures) const { - ISize size; - if (auto filter = - std::get_if>(&input_textures[0])) { - size = filter->get()->GetOutputSize(); - } else if (auto texture = - std::get_if>(&input_textures[0])) { - size = texture->get()->GetSize(); - } else { - FML_UNREACHABLE(); - } - - return size + (clip_ ? ISize(radius_ * 2, radius_ * 2) : ISize()); -} - -} // namespace impeller diff --git a/impeller/entity/contents/filters/blend_filter_contents.cc b/impeller/entity/contents/filters/blend_filter_contents.cc new file mode 100644 index 0000000000000..5f1e89c0eb7de --- /dev/null +++ b/impeller/entity/contents/filters/blend_filter_contents.cc @@ -0,0 +1,188 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/entity/contents/filters/blend_filter_contents.h" + +#include "impeller/entity/contents/content_context.h" +#include "impeller/renderer/render_pass.h" +#include "impeller/renderer/sampler_library.h" + +namespace impeller { + +BlendFilterContents::BlendFilterContents() { + SetBlendMode(Entity::BlendMode::kSourceOver); +} + +BlendFilterContents::~BlendFilterContents() = default; + +using PipelineProc = + std::shared_ptr (ContentContext::*)(ContentContextOptions) const; + +template +static void AdvancedBlendPass(std::shared_ptr input_d, + std::shared_ptr input_s, + std::shared_ptr sampler, + const ContentContext& renderer, + RenderPass& pass, + Command& cmd) {} + +template +static bool AdvancedBlend( + const std::vector>& input_textures, + const ContentContext& renderer, + RenderPass& pass, + PipelineProc pipeline_proc) { + if (input_textures.size() < 2) { + return false; + } + + auto& host_buffer = pass.GetTransientsBuffer(); + + VertexBufferBuilder vtx_builder; + vtx_builder.AddVertices({ + {Point(0, 0), Point(0, 0)}, + {Point(1, 0), Point(1, 0)}, + {Point(1, 1), Point(1, 1)}, + {Point(0, 0), Point(0, 0)}, + {Point(1, 1), Point(1, 1)}, + {Point(0, 1), Point(0, 1)}, + }); + auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer); + + typename VS::FrameInfo frame_info; + frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1)); + + auto uniform_view = host_buffer.EmplaceUniform(frame_info); + auto sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler({}); + + auto options = OptionsFromPass(pass); + options.blend_mode = Entity::BlendMode::kSource; + std::shared_ptr pipeline = + std::invoke(pipeline_proc, renderer, options); + + Command cmd; + cmd.label = "Advanced Blend Filter"; + cmd.BindVertices(vtx_buffer); + cmd.pipeline = std::move(pipeline); + VS::BindFrameInfo(cmd, uniform_view); + + FS::BindTextureSamplerDst(cmd, input_textures[0], sampler); + FS::BindTextureSamplerSrc(cmd, input_textures[1], sampler); + pass.AddCommand(cmd); + + return true; +} + +void BlendFilterContents::SetBlendMode(Entity::BlendMode blend_mode) { + if (blend_mode > Entity::BlendMode::kLastAdvancedBlendMode) { + VALIDATION_LOG << "Invalid blend mode " << static_cast(blend_mode) + << " assigned to BlendFilterContents."; + } + + blend_mode_ = blend_mode; + + if (blend_mode > Entity::BlendMode::kLastPipelineBlendMode) { + static_assert(Entity::BlendMode::kLastAdvancedBlendMode == + Entity::BlendMode::kScreen); + + switch (blend_mode) { + case Entity::BlendMode::kScreen: + advanced_blend_proc_ = + [](const std::vector>& input_textures, + const ContentContext& renderer, RenderPass& pass) { + PipelineProc p = &ContentContext::GetTextureBlendScreenPipeline; + return AdvancedBlend( + input_textures, renderer, pass, p); + }; + break; + default: + FML_UNREACHABLE(); + } + } +} + +static bool BasicBlend( + const std::vector>& input_textures, + const ContentContext& renderer, + RenderPass& pass, + Entity::BlendMode basic_blend) { + using VS = TextureBlendPipeline::VertexShader; + using FS = TextureBlendPipeline::FragmentShader; + + auto& host_buffer = pass.GetTransientsBuffer(); + + VertexBufferBuilder vtx_builder; + vtx_builder.AddVertices({ + {Point(0, 0), Point(0, 0)}, + {Point(1, 0), Point(1, 0)}, + {Point(1, 1), Point(1, 1)}, + {Point(0, 0), Point(0, 0)}, + {Point(1, 1), Point(1, 1)}, + {Point(0, 1), Point(0, 1)}, + }); + auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer); + + VS::FrameInfo frame_info; + frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1)); + + auto uniform_view = host_buffer.EmplaceUniform(frame_info); + auto sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler({}); + + // Draw the first texture using kSource. + + Command cmd; + cmd.label = "Basic Blend Filter"; + cmd.BindVertices(vtx_buffer); + auto options = OptionsFromPass(pass); + options.blend_mode = Entity::BlendMode::kSource; + cmd.pipeline = renderer.GetTextureBlendPipeline(options); + FS::BindTextureSamplerSrc(cmd, input_textures[0], sampler); + VS::BindFrameInfo(cmd, uniform_view); + pass.AddCommand(cmd); + + if (input_textures.size() < 2) { + return true; + } + + // Write subsequent textures using the selected blend mode. + + options.blend_mode = basic_blend; + cmd.pipeline = renderer.GetTextureBlendPipeline(options); + + for (auto texture_i = input_textures.begin() + 1; + texture_i < input_textures.end(); texture_i++) { + FS::BindTextureSamplerSrc(cmd, *texture_i, sampler); + pass.AddCommand(cmd); + } + + return true; +} + +bool BlendFilterContents::RenderFilter( + const std::vector>& input_textures, + const ContentContext& renderer, + RenderPass& pass) const { + if (input_textures.empty()) { + return true; + } + + if (input_textures.size() == 1) { + // Nothing to blend. + return BasicBlend(input_textures, renderer, pass, + Entity::BlendMode::kSource); + } + + if (blend_mode_ <= Entity::BlendMode::kLastPipelineBlendMode) { + return BasicBlend(input_textures, renderer, pass, blend_mode_); + } + + if (blend_mode_ <= Entity::BlendMode::kLastAdvancedBlendMode) { + return advanced_blend_proc_(input_textures, renderer, pass); + } + + FML_UNREACHABLE(); +} + +} // namespace impeller diff --git a/impeller/entity/contents/filters/blend_filter_contents.h b/impeller/entity/contents/filters/blend_filter_contents.h new file mode 100644 index 0000000000000..442cc54b3b05e --- /dev/null +++ b/impeller/entity/contents/filters/blend_filter_contents.h @@ -0,0 +1,36 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "impeller/entity/contents/filters/filter_contents.h" + +namespace impeller { + +class BlendFilterContents : public FilterContents { + public: + using AdvancedBlendProc = std::function>& input_textures, + const ContentContext& renderer, + RenderPass& pass)>; + + BlendFilterContents(); + + ~BlendFilterContents() override; + + void SetBlendMode(Entity::BlendMode blend_mode); + + private: + // |FilterContents| + bool RenderFilter(const std::vector>& input_textures, + const ContentContext& renderer, + RenderPass& pass) const override; + + Entity::BlendMode blend_mode_; + AdvancedBlendProc advanced_blend_proc_; + + FML_DISALLOW_COPY_AND_ASSIGN(BlendFilterContents); +}; + +} // namespace impeller diff --git a/impeller/entity/contents/filters/filter_contents.cc b/impeller/entity/contents/filters/filter_contents.cc new file mode 100644 index 0000000000000..033fdeed3183e --- /dev/null +++ b/impeller/entity/contents/filters/filter_contents.cc @@ -0,0 +1,194 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/entity/contents/filters/filter_contents.h" + +#include +#include +#include + +#include "flutter/fml/logging.h" +#include "impeller/base/validation.h" +#include "impeller/entity/contents/content_context.h" +#include "impeller/entity/contents/filters/blend_filter_contents.h" +#include "impeller/entity/contents/filters/gaussian_blur_filter_contents.h" +#include "impeller/entity/contents/texture_contents.h" +#include "impeller/entity/entity.h" +#include "impeller/geometry/path_builder.h" +#include "impeller/renderer/command_buffer.h" +#include "impeller/renderer/formats.h" +#include "impeller/renderer/render_pass.h" + +namespace impeller { + +std::shared_ptr FilterContents::MakeBlend( + Entity::BlendMode blend_mode, + InputTextures input_textures) { + if (blend_mode > Entity::BlendMode::kLastAdvancedBlendMode) { + VALIDATION_LOG << "Invalid blend mode " << static_cast(blend_mode) + << " passed to FilterContents::MakeBlend."; + return nullptr; + } + + if (input_textures.size() < 2 || + blend_mode <= Entity::BlendMode::kLastPipelineBlendMode) { + auto blend = std::make_shared(); + blend->SetInputTextures(input_textures); + blend->SetBlendMode(blend_mode); + return blend; + } + + if (blend_mode <= Entity::BlendMode::kLastAdvancedBlendMode) { + InputVariant blend = input_textures[0]; + for (auto in_i = input_textures.begin() + 1; in_i < input_textures.end(); + in_i++) { + auto new_blend = std::make_shared(); + new_blend->SetInputTextures({blend, *in_i}); + new_blend->SetBlendMode(blend_mode); + blend = new_blend; + } + return std::get>(blend); + } + + FML_UNREACHABLE(); +} + +std::shared_ptr FilterContents::MakeDirectionalGaussianBlur( + InputVariant input_texture, + Scalar radius, + Vector2 direction, + bool clip_border) { + auto blur = std::make_shared(); + blur->SetInputTextures({input_texture}); + blur->SetRadius(radius); + blur->SetDirection(direction); + blur->SetClipBorder(clip_border); + return blur; +} + +std::shared_ptr FilterContents::MakeGaussianBlur( + InputVariant input_texture, + Scalar radius, + bool clip_border) { + auto x_blur = MakeDirectionalGaussianBlur(input_texture, radius, Point(1, 0), + clip_border); + return MakeDirectionalGaussianBlur(x_blur, radius, Point(0, 1), false); +} + +FilterContents::FilterContents() = default; + +FilterContents::~FilterContents() = default; + +void FilterContents::SetInputTextures(InputTextures input_textures) { + input_textures_ = std::move(input_textures); +} + +bool FilterContents::Render(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const { + // Run the filter. + + auto maybe_texture = RenderFilterToTexture(renderer, entity, pass); + if (!maybe_texture.has_value()) { + return false; + } + auto& texture = maybe_texture.value(); + + // Draw the resulting texture to the given destination rect, respecting the + // transform and clip stack. + + auto contents = std::make_shared(); + contents->SetTexture(texture); + contents->SetSourceRect(IRect::MakeSize(texture->GetSize())); + + return contents->Render(renderer, entity, pass); +} + +std::optional> FilterContents::RenderFilterToTexture( + const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const { + auto output_size = GetOutputSize(input_textures_); + if (output_size.IsZero()) { + return std::nullopt; + } + + // Resolve all inputs as textures. + + std::vector> input_textures; + input_textures.reserve(input_textures_.size()); + for (const auto& input : input_textures_) { + if (auto filter = std::get_if>(&input)) { + auto texture = + filter->get()->RenderFilterToTexture(renderer, entity, pass); + if (!texture.has_value()) { + return std::nullopt; + } + input_textures.push_back(std::move(texture.value())); + } else if (auto texture = std::get_if>(&input)) { + input_textures.push_back(*texture); + } else { + FML_UNREACHABLE(); + } + } + + // Create a new texture and render the filter to it. + + auto context = renderer.GetContext(); + + auto subpass_target = RenderTarget::CreateOffscreen(*context, output_size); + auto subpass_texture = subpass_target.GetRenderTargetTexture(); + if (!subpass_texture) { + return std::nullopt; + } + + auto sub_command_buffer = context->CreateRenderCommandBuffer(); + sub_command_buffer->SetLabel("Offscreen Filter Command Buffer"); + if (!sub_command_buffer) { + return std::nullopt; + } + + auto sub_renderpass = sub_command_buffer->CreateRenderPass(subpass_target); + if (!sub_renderpass) { + return std::nullopt; + } + sub_renderpass->SetLabel("OffscreenFilterPass"); + + if (!RenderFilter(input_textures, renderer, *sub_renderpass)) { + return std::nullopt; + } + + if (!sub_renderpass->EncodeCommands(*context->GetTransientsAllocator())) { + return std::nullopt; + } + + if (!sub_command_buffer->SubmitCommands()) { + return std::nullopt; + } + + return subpass_texture; +} + +ISize FilterContents::GetOutputSize() const { + if (input_textures_.empty()) { + return {}; + } + return GetOutputSize(input_textures_); +} + +ISize FilterContents::GetOutputSize(const InputTextures& input_textures) const { + if (auto filter = + std::get_if>(&input_textures[0])) { + return filter->get()->GetOutputSize(input_textures); + } + + if (auto texture = + std::get_if>(&input_textures[0])) { + return texture->get()->GetSize(); + } + + FML_UNREACHABLE(); +} + +} // namespace impeller diff --git a/impeller/entity/contents/filter_contents.h b/impeller/entity/contents/filters/filter_contents.h similarity index 54% rename from impeller/entity/contents/filter_contents.h rename to impeller/entity/contents/filters/filter_contents.h index eca9c0aa1a783..7c5b29ff2858e 100644 --- a/impeller/entity/contents/filter_contents.h +++ b/impeller/entity/contents/filters/filter_contents.h @@ -8,7 +8,6 @@ #include #include -#include "impeller/entity/contents/contents.h" #include "impeller/entity/entity.h" #include "impeller/renderer/formats.h" @@ -16,10 +15,6 @@ namespace impeller { class Pipeline; -/******************************************************************************* - ******* FilterContents - ******************************************************************************/ - class FilterContents : public Contents { public: using InputVariant = @@ -85,66 +80,4 @@ class FilterContents : public Contents { FML_DISALLOW_COPY_AND_ASSIGN(FilterContents); }; -/******************************************************************************* - ******* BlendFilterContents - ******************************************************************************/ - -class BlendFilterContents : public FilterContents { - public: - using AdvancedBlendProc = std::function>& input_textures, - const ContentContext& renderer, - RenderPass& pass)>; - - BlendFilterContents(); - - ~BlendFilterContents() override; - - void SetBlendMode(Entity::BlendMode blend_mode); - - private: - // |FilterContents| - bool RenderFilter(const std::vector>& input_textures, - const ContentContext& renderer, - RenderPass& pass) const override; - - Entity::BlendMode blend_mode_; - AdvancedBlendProc advanced_blend_proc_; - - FML_DISALLOW_COPY_AND_ASSIGN(BlendFilterContents); -}; - -/******************************************************************************* - ******* DirectionalGaussionBlurFilterContents - ******************************************************************************/ - -class DirectionalGaussianBlurFilterContents final : public FilterContents { - public: - DirectionalGaussianBlurFilterContents(); - - ~DirectionalGaussianBlurFilterContents() override; - - void SetRadius(Scalar radius); - - void SetDirection(Vector2 direction); - - void SetClipBorder(bool clip); - - private: - // |FilterContents| - bool RenderFilter(const std::vector>& input_textures, - const ContentContext& renderer, - RenderPass& pass) const override; - - // |FilterContents| - virtual ISize GetOutputSize( - const InputTextures& input_textures) const override; - - Scalar radius_ = 0; - Vector2 direction_; - bool clip_ = false; - - FML_DISALLOW_COPY_AND_ASSIGN(DirectionalGaussianBlurFilterContents); -}; - } // namespace impeller diff --git a/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc b/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc new file mode 100644 index 0000000000000..f677f37cf362a --- /dev/null +++ b/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc @@ -0,0 +1,101 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/entity/contents/filters/gaussian_blur_filter_contents.h" + +#include "impeller/entity/contents/content_context.h" +#include "impeller/renderer/render_pass.h" +#include "impeller/renderer/sampler_library.h" + +namespace impeller { + +DirectionalGaussianBlurFilterContents::DirectionalGaussianBlurFilterContents() = + default; + +DirectionalGaussianBlurFilterContents:: + ~DirectionalGaussianBlurFilterContents() = default; + +void DirectionalGaussianBlurFilterContents::SetRadius(Scalar radius) { + radius_ = std::max(radius, 1e-3f); +} + +void DirectionalGaussianBlurFilterContents::SetDirection(Vector2 direction) { + direction_ = direction.Normalize(); +} + +void DirectionalGaussianBlurFilterContents::SetClipBorder(bool clip) { + clip_ = clip; +} + +bool DirectionalGaussianBlurFilterContents::RenderFilter( + const std::vector>& input_textures, + const ContentContext& renderer, + RenderPass& pass) const { + using VS = GaussianBlurPipeline::VertexShader; + using FS = GaussianBlurPipeline::FragmentShader; + + auto& host_buffer = pass.GetTransientsBuffer(); + + ISize size = FilterContents::GetOutputSize(); + Point uv_offset = clip_ ? (Point(radius_, radius_) / size) : Point(); + // LTRB + Scalar uv[4] = { + -uv_offset.x, + -uv_offset.y, + 1 + uv_offset.x, + 1 + uv_offset.y, + }; + + VertexBufferBuilder vtx_builder; + vtx_builder.AddVertices({ + {Point(0, 0), Point(uv[0], uv[1])}, + {Point(size.width, 0), Point(uv[2], uv[1])}, + {Point(size.width, size.height), Point(uv[2], uv[3])}, + {Point(0, 0), Point(uv[0], uv[1])}, + {Point(size.width, size.height), Point(uv[2], uv[3])}, + {Point(0, size.height), Point(uv[0], uv[3])}, + }); + auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer); + + VS::FrameInfo frame_info; + frame_info.mvp = Matrix::MakeOrthographic(size); + frame_info.texture_size = Point(size); + frame_info.blur_radius = radius_; + frame_info.blur_direction = direction_; + + auto uniform_view = host_buffer.EmplaceUniform(frame_info); + auto sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler({}); + + Command cmd; + cmd.label = "Gaussian Blur Filter"; + auto options = OptionsFromPass(pass); + options.blend_mode = Entity::BlendMode::kSource; + cmd.pipeline = renderer.GetGaussianBlurPipeline(options); + cmd.BindVertices(vtx_buffer); + VS::BindFrameInfo(cmd, uniform_view); + for (const auto& texture : input_textures) { + FS::BindTextureSampler(cmd, texture, sampler); + pass.AddCommand(cmd); + } + + return true; +} + +ISize DirectionalGaussianBlurFilterContents::GetOutputSize( + const InputTextures& input_textures) const { + ISize size; + if (auto filter = + std::get_if>(&input_textures[0])) { + size = filter->get()->GetOutputSize(); + } else if (auto texture = + std::get_if>(&input_textures[0])) { + size = texture->get()->GetSize(); + } else { + FML_UNREACHABLE(); + } + + return size + (clip_ ? ISize(radius_ * 2, radius_ * 2) : ISize()); +} + +} // namespace impeller diff --git a/impeller/entity/contents/filters/gaussian_blur_filter_contents.h b/impeller/entity/contents/filters/gaussian_blur_filter_contents.h new file mode 100644 index 0000000000000..cdf6c470dba7e --- /dev/null +++ b/impeller/entity/contents/filters/gaussian_blur_filter_contents.h @@ -0,0 +1,40 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "impeller/entity/contents/filters/filter_contents.h" + +namespace impeller { + +class DirectionalGaussianBlurFilterContents final : public FilterContents { + public: + DirectionalGaussianBlurFilterContents(); + + ~DirectionalGaussianBlurFilterContents() override; + + void SetRadius(Scalar radius); + + void SetDirection(Vector2 direction); + + void SetClipBorder(bool clip); + + private: + // |FilterContents| + bool RenderFilter(const std::vector>& input_textures, + const ContentContext& renderer, + RenderPass& pass) const override; + + // |FilterContents| + virtual ISize GetOutputSize( + const InputTextures& input_textures) const override; + + Scalar radius_ = 0; + Vector2 direction_; + bool clip_ = false; + + FML_DISALLOW_COPY_AND_ASSIGN(DirectionalGaussianBlurFilterContents); +}; + +} // namespace impeller diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index 1208b8073d571..6f02cf6353cd2 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -3,7 +3,7 @@ // found in the LICENSE file. #include "flutter/testing/testing.h" -#include "impeller/entity/contents/filter_contents.h" +#include "impeller/entity/contents/filters/filter_contents.h" #include "impeller/entity/contents/solid_color_contents.h" #include "impeller/entity/contents/solid_stroke_contents.h" #include "impeller/entity/entity.h"