diff --git a/impeller/entity/contents/content_context.cc b/impeller/entity/contents/content_context.cc index bd3e05c135f6a..797d51cde4a5e 100644 --- a/impeller/entity/contents/content_context.cc +++ b/impeller/entity/contents/content_context.cc @@ -372,54 +372,54 @@ ContentContext::ContentContext( framebuffer_blend_softlight_pipelines_.CreateDefault( *context_, options_trianglestrip, {static_cast(BlendSelectValues::kSoftLight), supports_decal}); + } else { + blend_color_pipelines_.CreateDefault( + *context_, options_trianglestrip, + {static_cast(BlendSelectValues::kColor), supports_decal}); + blend_colorburn_pipelines_.CreateDefault( + *context_, options_trianglestrip, + {static_cast(BlendSelectValues::kColorBurn), supports_decal}); + blend_colordodge_pipelines_.CreateDefault( + *context_, options_trianglestrip, + {static_cast(BlendSelectValues::kColorDodge), supports_decal}); + blend_darken_pipelines_.CreateDefault( + *context_, options_trianglestrip, + {static_cast(BlendSelectValues::kDarken), supports_decal}); + blend_difference_pipelines_.CreateDefault( + *context_, options_trianglestrip, + {static_cast(BlendSelectValues::kDifference), supports_decal}); + blend_exclusion_pipelines_.CreateDefault( + *context_, options_trianglestrip, + {static_cast(BlendSelectValues::kExclusion), supports_decal}); + blend_hardlight_pipelines_.CreateDefault( + *context_, options_trianglestrip, + {static_cast(BlendSelectValues::kHardLight), supports_decal}); + blend_hue_pipelines_.CreateDefault( + *context_, options_trianglestrip, + {static_cast(BlendSelectValues::kHue), supports_decal}); + blend_lighten_pipelines_.CreateDefault( + *context_, options_trianglestrip, + {static_cast(BlendSelectValues::kLighten), supports_decal}); + blend_luminosity_pipelines_.CreateDefault( + *context_, options_trianglestrip, + {static_cast(BlendSelectValues::kLuminosity), supports_decal}); + blend_multiply_pipelines_.CreateDefault( + *context_, options_trianglestrip, + {static_cast(BlendSelectValues::kMultiply), supports_decal}); + blend_overlay_pipelines_.CreateDefault( + *context_, options_trianglestrip, + {static_cast(BlendSelectValues::kOverlay), supports_decal}); + blend_saturation_pipelines_.CreateDefault( + *context_, options_trianglestrip, + {static_cast(BlendSelectValues::kSaturation), supports_decal}); + blend_screen_pipelines_.CreateDefault( + *context_, options_trianglestrip, + {static_cast(BlendSelectValues::kScreen), supports_decal}); + blend_softlight_pipelines_.CreateDefault( + *context_, options_trianglestrip, + {static_cast(BlendSelectValues::kSoftLight), supports_decal}); } - blend_color_pipelines_.CreateDefault( - *context_, options_trianglestrip, - {static_cast(BlendSelectValues::kColor), supports_decal}); - blend_colorburn_pipelines_.CreateDefault( - *context_, options_trianglestrip, - {static_cast(BlendSelectValues::kColorBurn), supports_decal}); - blend_colordodge_pipelines_.CreateDefault( - *context_, options_trianglestrip, - {static_cast(BlendSelectValues::kColorDodge), supports_decal}); - blend_darken_pipelines_.CreateDefault( - *context_, options_trianglestrip, - {static_cast(BlendSelectValues::kDarken), supports_decal}); - blend_difference_pipelines_.CreateDefault( - *context_, options_trianglestrip, - {static_cast(BlendSelectValues::kDifference), supports_decal}); - blend_exclusion_pipelines_.CreateDefault( - *context_, options_trianglestrip, - {static_cast(BlendSelectValues::kExclusion), supports_decal}); - blend_hardlight_pipelines_.CreateDefault( - *context_, options_trianglestrip, - {static_cast(BlendSelectValues::kHardLight), supports_decal}); - blend_hue_pipelines_.CreateDefault( - *context_, options_trianglestrip, - {static_cast(BlendSelectValues::kHue), supports_decal}); - blend_lighten_pipelines_.CreateDefault( - *context_, options_trianglestrip, - {static_cast(BlendSelectValues::kLighten), supports_decal}); - blend_luminosity_pipelines_.CreateDefault( - *context_, options_trianglestrip, - {static_cast(BlendSelectValues::kLuminosity), supports_decal}); - blend_multiply_pipelines_.CreateDefault( - *context_, options_trianglestrip, - {static_cast(BlendSelectValues::kMultiply), supports_decal}); - blend_overlay_pipelines_.CreateDefault( - *context_, options_trianglestrip, - {static_cast(BlendSelectValues::kOverlay), supports_decal}); - blend_saturation_pipelines_.CreateDefault( - *context_, options_trianglestrip, - {static_cast(BlendSelectValues::kSaturation), supports_decal}); - blend_screen_pipelines_.CreateDefault( - *context_, options_trianglestrip, - {static_cast(BlendSelectValues::kScreen), supports_decal}); - blend_softlight_pipelines_.CreateDefault( - *context_, options_trianglestrip, - {static_cast(BlendSelectValues::kSoftLight), supports_decal}); - rrect_blur_pipelines_.CreateDefault(*context_, options_trianglestrip); texture_strict_src_pipelines_.CreateDefault(*context_, options); position_uv_pipelines_.CreateDefault(*context_, options); diff --git a/impeller/entity/contents/filters/blend_filter_contents.cc b/impeller/entity/contents/filters/blend_filter_contents.cc index 2e353dbafa691..7f3ac8e322af1 100644 --- a/impeller/entity/contents/filters/blend_filter_contents.cc +++ b/impeller/entity/contents/filters/blend_filter_contents.cc @@ -8,8 +8,11 @@ #include #include +#include "flutter/fml/logging.h" #include "impeller/base/strings.h" #include "impeller/core/formats.h" +#include "impeller/core/sampler_descriptor.h" +#include "impeller/core/texture_descriptor.h" #include "impeller/entity/contents/anonymous_contents.h" #include "impeller/entity/contents/content_context.h" #include "impeller/entity/contents/contents.h" @@ -17,6 +20,8 @@ #include "impeller/entity/contents/filters/inputs/filter_input.h" #include "impeller/entity/contents/solid_color_contents.h" #include "impeller/entity/entity.h" +#include "impeller/entity/texture_fill.frag.h" +#include "impeller/entity/texture_fill.vert.h" #include "impeller/geometry/color.h" #include "impeller/renderer/render_pass.h" #include "impeller/renderer/snapshot.h" @@ -660,6 +665,235 @@ static std::optional PipelineBlend( entity.GetBlendMode()); } +std::optional BlendFilterContents::CreateFramebufferAdvancedBlend( + const FilterInput::Vector& inputs, + const ContentContext& renderer, + const Entity& entity, + const Rect& coverage, + std::optional foreground_color, + BlendMode blend_mode, + std::optional alpha, + ColorFilterContents::AbsorbOpacity absorb_opacity) const { + // This works with either 2 contents or 1 contents and a foreground color. + FML_DCHECK(inputs.size() == 2u || + (inputs.size() == 1u && foreground_color.has_value())); + + auto dst_snapshot = + inputs[0]->GetSnapshot("ForegroundAdvancedBlend", renderer, entity); + if (!dst_snapshot.has_value()) { + return std::nullopt; + } + + ContentContext::SubpassCallback subpass_callback = [&](const ContentContext& + renderer, + RenderPass& pass) { + // First, we create a new render pass and populate it with the contents + // of the first (dst) input. + HostBuffer& host_buffer = renderer.GetTransientsBuffer(); + + { + using FS = TextureFillFragmentShader; + using VS = TextureFillVertexShader; + + pass.SetCommandLabel("Framebuffer Advanced Blend"); + auto pipeline_options = OptionsFromPass(pass); + pipeline_options.primitive_type = PrimitiveType::kTriangleStrip; + pass.SetPipeline(renderer.GetTexturePipeline(pipeline_options)); + + VS::FrameInfo frame_info; + frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1)); + frame_info.texture_sampler_y_coord_scale = 1.0; + + FS::FragInfo frag_info; + frag_info.alpha = 1.0; + + VertexBufferBuilder vtx_builder; + vtx_builder.AddVertices({ + {{0, 0}, {0, 0}}, + {Point(1, 0), {1, 0}}, + {Point(0, 1), {0, 1}}, + {Point(1, 1), {1, 1}}, + }); + auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer); + pass.SetVertexBuffer(std::move(vtx_buffer)); + + VS::BindFrameInfo(pass, host_buffer.EmplaceUniform(frame_info)); + FS::BindFragInfo(pass, host_buffer.EmplaceUniform(frag_info)); + FS::BindTextureSampler( + pass, dst_snapshot->texture, + renderer.GetContext()->GetSamplerLibrary()->GetSampler({})); + + if (!pass.Draw().ok()) { + return false; + } + } + + { + using VS = FramebufferBlendScreenPipeline::VertexShader; + using FS = FramebufferBlendScreenPipeline::FragmentShader; + + // Next, we render the second contents to a snapshot, or create a 1x1 + // texture for the foreground color. + std::shared_ptr src_texture; + if (foreground_color.has_value()) { + TextureDescriptor desc; + desc.size = {1, 1}; + desc.format = PixelFormat::kR8G8B8A8UNormInt; + desc.storage_mode = StorageMode::kHostVisible; + src_texture = + renderer.GetContext()->GetResourceAllocator()->CreateTexture(desc); + if (!src_texture) { + return false; + } + + if (!src_texture->SetContents( + foreground_color->Premultiply().ToR8G8B8A8().data(), 4u)) { + return false; + } + } else { + auto src_snapshot = + inputs[0]->GetSnapshot("ForegroundAdvancedBlend", renderer, entity); + if (!src_snapshot.has_value()) { + return false; + } + // This doesn't really handle the case where the transforms are wildly + // different, but we only need to support blending two contents together + // in limited circumstances (mask blur). + src_texture = src_snapshot->texture; + } + + VertexBufferBuilder vtx_builder; + vtx_builder.AddVertices({ + {Point(0, 0), Point(0, 0)}, + {Point(1, 0), Point(1, 0)}, + {Point(0, 1), Point(0, 1)}, + {Point(1, 1), Point(1, 1)}, + }); + + auto options = OptionsFromPass(pass); + options.blend_mode = BlendMode::kSource; + options.primitive_type = PrimitiveType::kTriangleStrip; + + pass.SetCommandLabel("Framebuffer Advanced Blend Filter"); + pass.SetVertexBuffer(vtx_builder.CreateVertexBuffer(host_buffer)); + + switch (blend_mode) { + case BlendMode::kScreen: + pass.SetPipeline(renderer.GetFramebufferBlendScreenPipeline(options)); + break; + case BlendMode::kOverlay: + pass.SetPipeline( + renderer.GetFramebufferBlendOverlayPipeline(options)); + break; + case BlendMode::kDarken: + pass.SetPipeline(renderer.GetFramebufferBlendDarkenPipeline(options)); + break; + case BlendMode::kLighten: + pass.SetPipeline( + renderer.GetFramebufferBlendLightenPipeline(options)); + break; + case BlendMode::kColorDodge: + pass.SetPipeline( + renderer.GetFramebufferBlendColorDodgePipeline(options)); + break; + case BlendMode::kColorBurn: + pass.SetPipeline( + renderer.GetFramebufferBlendColorBurnPipeline(options)); + break; + case BlendMode::kHardLight: + pass.SetPipeline( + renderer.GetFramebufferBlendHardLightPipeline(options)); + break; + case BlendMode::kSoftLight: + pass.SetPipeline( + renderer.GetFramebufferBlendSoftLightPipeline(options)); + break; + case BlendMode::kDifference: + pass.SetPipeline( + renderer.GetFramebufferBlendDifferencePipeline(options)); + break; + case BlendMode::kExclusion: + pass.SetPipeline( + renderer.GetFramebufferBlendExclusionPipeline(options)); + break; + case BlendMode::kMultiply: + pass.SetPipeline( + renderer.GetFramebufferBlendMultiplyPipeline(options)); + break; + case BlendMode::kHue: + pass.SetPipeline(renderer.GetFramebufferBlendHuePipeline(options)); + break; + case BlendMode::kSaturation: + pass.SetPipeline( + renderer.GetFramebufferBlendSaturationPipeline(options)); + break; + case BlendMode::kColor: + pass.SetPipeline(renderer.GetFramebufferBlendColorPipeline(options)); + break; + case BlendMode::kLuminosity: + pass.SetPipeline( + renderer.GetFramebufferBlendLuminosityPipeline(options)); + break; + default: + return false; + } + + VS::FrameInfo frame_info; + FS::FragInfo frag_info; + + auto src_sampler_descriptor = SamplerDescriptor{}; + if (renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode()) { + src_sampler_descriptor.width_address_mode = SamplerAddressMode::kDecal; + src_sampler_descriptor.height_address_mode = SamplerAddressMode::kDecal; + } + const std::unique_ptr& src_sampler = + renderer.GetContext()->GetSamplerLibrary()->GetSampler( + src_sampler_descriptor); + FS::BindTextureSamplerSrc(pass, src_texture, src_sampler); + + frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1)); + frame_info.src_y_coord_scale = src_texture->GetYCoordScale(); + VS::BindFrameInfo(pass, host_buffer.EmplaceUniform(frame_info)); + + frag_info.src_input_alpha = 1.0; + FS::BindFragInfo(pass, host_buffer.EmplaceUniform(frag_info)); + + return pass.Draw().ok(); + } + }; + + std::shared_ptr cmd_buffer = + renderer.GetContext()->CreateCommandBuffer(); + auto render_target = + renderer.MakeSubpass("FramebufferBlend", dst_snapshot->texture->GetSize(), + cmd_buffer, subpass_callback); + + if (!render_target.ok()) { + return std::nullopt; + } + + if (!renderer.GetContext() + ->GetCommandQueue() + ->Submit(/*buffers=*/{std::move(cmd_buffer)}) + .ok()) { + return std::nullopt; + } + + return Entity::FromSnapshot( + Snapshot{ + .texture = render_target.value().GetRenderTargetTexture(), + .transform = dst_snapshot->transform, + // 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 == ColorFilterContents::AbsorbOpacity::kYes + ? 1.0f + : dst_snapshot->opacity) * + alpha.value_or(1.0)}, + entity.GetBlendMode()); +} + #define BLEND_CASE(mode) \ case BlendMode::k##mode: \ advanced_blend_proc_ = \ @@ -739,6 +973,11 @@ std::optional BlendFilterContents::RenderFilter( } if (blend_mode_ <= Entity::kLastAdvancedBlendMode) { + if (renderer.GetDeviceCapabilities().SupportsFramebufferFetch()) { + return CreateFramebufferAdvancedBlend(inputs, renderer, entity, coverage, + foreground_color_, blend_mode_, + GetAlpha(), GetAbsorbOpacity()); + } if (inputs.size() == 1 && foreground_color_.has_value() && GetAbsorbOpacity() == ColorFilterContents::AbsorbOpacity::kYes) { return CreateForegroundAdvancedBlend( diff --git a/impeller/entity/contents/filters/blend_filter_contents.h b/impeller/entity/contents/filters/blend_filter_contents.h index 562f306034ebd..468ff18ae0e36 100644 --- a/impeller/entity/contents/filters/blend_filter_contents.h +++ b/impeller/entity/contents/filters/blend_filter_contents.h @@ -78,6 +78,23 @@ class BlendFilterContents : public ColorFilterContents { std::optional alpha, ColorFilterContents::AbsorbOpacity absorb_opacity) const; + /// @brief Implements the advanced blends filters in terms of the framebuffer + /// blend filters. + /// + /// This requires device support for frameuffer fetch, + /// `Capabilities::SupportsFramebufferFetch` must be true + /// This allows a substantial reduction in the number of bootstrapped + /// shaders. + std::optional CreateFramebufferAdvancedBlend( + const FilterInput::Vector& inputs, + const ContentContext& renderer, + const Entity& entity, + const Rect& coverage, + std::optional foreground_color, + BlendMode blend_mode, + std::optional alpha, + ColorFilterContents::AbsorbOpacity absorb_opacity) const; + /// @brief Optimized porter-duff blend that avoids a second subpass when there /// is only a single input and a foreground color. /// diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index 61594bd4d2e64..a739fc71b6088 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -2688,22 +2688,23 @@ TEST_P(EntityTest, AdvancedBlendCoverageHintIsNotResetByEntityPass) { TEST_P(EntityTest, SpecializationConstantsAreAppliedToVariants) { auto content_context = GetContentContext(); - auto default_color_burn = content_context->GetBlendColorBurnPipeline({ + auto default_gyph = content_context->GetGlyphAtlasPipeline({ .color_attachment_pixel_format = PixelFormat::kR8G8B8A8UNormInt, .has_depth_stencil_attachments = false, }); - auto alt_color_burn = content_context->GetBlendColorBurnPipeline( + auto alt_gyph = content_context->GetGlyphAtlasPipeline( {.color_attachment_pixel_format = PixelFormat::kR8G8B8A8UNormInt, .has_depth_stencil_attachments = true}); - ASSERT_NE(default_color_burn, alt_color_burn); - ASSERT_EQ(default_color_burn->GetDescriptor().GetSpecializationConstants(), - alt_color_burn->GetDescriptor().GetSpecializationConstants()); + EXPECT_NE(default_gyph, alt_gyph); + EXPECT_EQ(default_gyph->GetDescriptor().GetSpecializationConstants(), + alt_gyph->GetDescriptor().GetSpecializationConstants()); - auto decal_supported = static_cast( - GetContext()->GetCapabilities()->SupportsDecalSamplerAddressMode()); - std::vector expected_constants = {5, decal_supported}; - ASSERT_EQ(default_color_burn->GetDescriptor().GetSpecializationConstants(), + auto use_a8 = GetContext()->GetCapabilities()->GetDefaultGlyphAtlasFormat() == + PixelFormat::kA8UNormInt; + + std::vector expected_constants = {static_cast(use_a8)}; + EXPECT_EQ(default_gyph->GetDescriptor().GetSpecializationConstants(), expected_constants); }