diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 4217faaa8bc6c..4716950c955f1 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1198,6 +1198,8 @@ ORIGIN: ../../../flutter/impeller/entity/contents/filters/srgb_to_linear_filter_ ORIGIN: ../../../flutter/impeller/entity/contents/filters/srgb_to_linear_filter_contents.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/contents/filters/yuv_to_rgb_filter_contents.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/contents/filters/yuv_to_rgb_filter_contents.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/entity/contents/foreground_blend_contents.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/entity/contents/foreground_blend_contents.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/contents/framebuffer_blend_contents.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/contents/framebuffer_blend_contents.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/contents/gradient_generator.cc + ../../../flutter/LICENSE @@ -3777,6 +3779,8 @@ FILE: ../../../flutter/impeller/entity/contents/filters/srgb_to_linear_filter_co FILE: ../../../flutter/impeller/entity/contents/filters/srgb_to_linear_filter_contents.h FILE: ../../../flutter/impeller/entity/contents/filters/yuv_to_rgb_filter_contents.cc FILE: ../../../flutter/impeller/entity/contents/filters/yuv_to_rgb_filter_contents.h +FILE: ../../../flutter/impeller/entity/contents/foreground_blend_contents.cc +FILE: ../../../flutter/impeller/entity/contents/foreground_blend_contents.h FILE: ../../../flutter/impeller/entity/contents/framebuffer_blend_contents.cc FILE: ../../../flutter/impeller/entity/contents/framebuffer_blend_contents.h FILE: ../../../flutter/impeller/entity/contents/gradient_generator.cc diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index 54af14f3796a6..db1a195b55d3c 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -180,6 +180,8 @@ impeller_component("entity") { "contents/filters/srgb_to_linear_filter_contents.h", "contents/filters/yuv_to_rgb_filter_contents.cc", "contents/filters/yuv_to_rgb_filter_contents.h", + "contents/foreground_blend_contents.cc", + "contents/foreground_blend_contents.h", "contents/framebuffer_blend_contents.cc", "contents/framebuffer_blend_contents.h", "contents/gradient_generator.cc", diff --git a/impeller/entity/contents/filters/blend_filter_contents.cc b/impeller/entity/contents/filters/blend_filter_contents.cc index 1548d80c82da3..e8d7859845cee 100644 --- a/impeller/entity/contents/filters/blend_filter_contents.cc +++ b/impeller/entity/contents/filters/blend_filter_contents.cc @@ -12,6 +12,7 @@ #include "impeller/entity/contents/content_context.h" #include "impeller/entity/contents/contents.h" #include "impeller/entity/contents/filters/inputs/filter_input.h" +#include "impeller/entity/contents/foreground_blend_contents.h" #include "impeller/entity/contents/solid_color_contents.h" #include "impeller/entity/entity.h" #include "impeller/geometry/path_builder.h" @@ -373,6 +374,20 @@ std::optional BlendFilterContents::RenderFilter( } if (blend_mode_ <= Entity::kLastAdvancedBlendMode) { + auto potential_alpha = GetAlpha().value_or(1.0); + if (inputs.size() == 1 && foreground_color_.has_value() && + potential_alpha >= 1.0 - kEhCloseEnough) { + auto contents = std::make_shared(); + contents->SetBlendMode(blend_mode_); + contents->SetCoverage(coverage); + contents->SetSrcInput(inputs[0]); + contents->SetForegroundColor(foreground_color_.value()); + Entity entity; + entity.SetTransformation(Matrix::MakeTranslation(coverage.origin)); + entity.SetContents(std::move(contents)); + return entity; + } + return advanced_blend_proc_(inputs, renderer, entity, coverage, foreground_color_, GetAbsorbOpacity(), GetAlpha()); diff --git a/impeller/entity/contents/foreground_blend_contents.cc b/impeller/entity/contents/foreground_blend_contents.cc new file mode 100644 index 0000000000000..93863efcabec5 --- /dev/null +++ b/impeller/entity/contents/foreground_blend_contents.cc @@ -0,0 +1,159 @@ +// 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 "foreground_blend_contents.h" + +#include "flutter/impeller/entity/contents/content_context.h" +#include "flutter/impeller/renderer/render_pass.h" +#include "flutter/impeller/renderer/sampler_library.h" + +namespace impeller { + +AdvancedForegroundBlendContents::AdvancedForegroundBlendContents() {} + +AdvancedForegroundBlendContents::~AdvancedForegroundBlendContents() {} + +void AdvancedForegroundBlendContents::SetBlendMode(BlendMode blend_mode) { + FML_DCHECK(blend_mode > Entity::kLastPipelineBlendMode); + blend_mode_ = blend_mode; +} + +void AdvancedForegroundBlendContents::SetSrcInput( + std::shared_ptr input) { + input_ = std::move(input); +} + +void AdvancedForegroundBlendContents::SetForegroundColor(Color color) { + foreground_color_ = color; +} + +void AdvancedForegroundBlendContents::SetCoverage(Rect rect) { + rect_ = rect; +} + +std::optional AdvancedForegroundBlendContents::GetCoverage( + const Entity& entity) const { + return rect_.TransformBounds(entity.GetTransformation()); +} + +bool AdvancedForegroundBlendContents::Render(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const { + using VS = BlendScreenPipeline::VertexShader; + using FS = BlendScreenPipeline::FragmentShader; + + auto& host_buffer = pass.GetTransientsBuffer(); + + auto dst_snapshot = input_->GetSnapshot(renderer, entity); + if (!dst_snapshot.has_value()) { + return false; + } + auto maybe_dst_uvs = dst_snapshot->GetCoverageUVs(rect_); + if (!maybe_dst_uvs.has_value()) { + return false; + } + auto dst_uvs = maybe_dst_uvs.value(); + + auto size = rect_.size; + VertexBufferBuilder vtx_builder; + vtx_builder.AddVertices({ + {Point(0, 0), dst_uvs[0], dst_uvs[0]}, + {Point(size.width, 0), dst_uvs[1], dst_uvs[1]}, + {Point(size.width, size.height), dst_uvs[3], dst_uvs[3]}, + {Point(0, 0), dst_uvs[0], dst_uvs[0]}, + {Point(size.width, size.height), dst_uvs[3], dst_uvs[3]}, + {Point(0, size.height), dst_uvs[2], dst_uvs[2]}, + }); + auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer); + + Command cmd; + cmd.label = "Foreground Advanced Blend Filter"; + cmd.BindVertices(vtx_buffer); + cmd.stencil_reference = entity.GetStencilDepth(); + auto options = OptionsFromPass(pass); + + switch (blend_mode_) { + case BlendMode::kScreen: + cmd.pipeline = renderer.GetBlendScreenPipeline(options); + break; + case BlendMode::kOverlay: + cmd.pipeline = renderer.GetBlendOverlayPipeline(options); + break; + case BlendMode::kDarken: + cmd.pipeline = renderer.GetBlendDarkenPipeline(options); + break; + case BlendMode::kLighten: + cmd.pipeline = renderer.GetBlendLightenPipeline(options); + break; + case BlendMode::kColorDodge: + cmd.pipeline = renderer.GetBlendColorDodgePipeline(options); + break; + case BlendMode::kColorBurn: + cmd.pipeline = renderer.GetBlendColorBurnPipeline(options); + break; + case BlendMode::kHardLight: + cmd.pipeline = renderer.GetBlendHardLightPipeline(options); + break; + case BlendMode::kSoftLight: + cmd.pipeline = renderer.GetBlendSoftLightPipeline(options); + break; + case BlendMode::kDifference: + cmd.pipeline = renderer.GetBlendDifferencePipeline(options); + break; + case BlendMode::kExclusion: + cmd.pipeline = renderer.GetBlendExclusionPipeline(options); + break; + case BlendMode::kMultiply: + cmd.pipeline = renderer.GetBlendMultiplyPipeline(options); + break; + case BlendMode::kHue: + cmd.pipeline = renderer.GetBlendHuePipeline(options); + break; + case BlendMode::kSaturation: + cmd.pipeline = renderer.GetBlendSaturationPipeline(options); + break; + case BlendMode::kColor: + cmd.pipeline = renderer.GetBlendColorPipeline(options); + break; + case BlendMode::kLuminosity: + cmd.pipeline = renderer.GetBlendLuminosityPipeline(options); + break; + default: + return false; + } + + FS::BlendInfo blend_info; + VS::FrameInfo frame_info; + + auto dst_sampler_descriptor = dst_snapshot->sampler_descriptor; + if (renderer.GetDeviceCapabilities().SupportsDecalTileMode()) { + dst_sampler_descriptor.width_address_mode = SamplerAddressMode::kDecal; + dst_sampler_descriptor.height_address_mode = SamplerAddressMode::kDecal; + } + auto dst_sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler( + dst_sampler_descriptor); + FS::BindTextureSamplerDst(cmd, dst_snapshot->texture, dst_sampler); + frame_info.dst_y_coord_scale = dst_snapshot->texture->GetYCoordScale(); + blend_info.dst_input_alpha = dst_snapshot->opacity; + + blend_info.color_factor = 1; + blend_info.color = foreground_color_; + // This texture will not be sampled from due to the color factor. But + // this is present so that validation doesn't trip on a missing + // binding. + FS::BindTextureSamplerSrc(cmd, dst_snapshot->texture, dst_sampler); + + auto blend_uniform = host_buffer.EmplaceUniform(blend_info); + FS::BindBlendInfo(cmd, blend_uniform); + + frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * + entity.GetTransformation(); + + auto uniform_view = host_buffer.EmplaceUniform(frame_info); + VS::BindFrameInfo(cmd, uniform_view); + + return pass.AddCommand(cmd); +} + +} // namespace impeller diff --git a/impeller/entity/contents/foreground_blend_contents.h b/impeller/entity/contents/foreground_blend_contents.h new file mode 100644 index 0000000000000..5a36353707410 --- /dev/null +++ b/impeller/entity/contents/foreground_blend_contents.h @@ -0,0 +1,50 @@ +// 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 "flutter/fml/macros.h" +#include "flutter/impeller/core/texture.h" +#include "flutter/impeller/entity/contents/color_source_contents.h" +#include "flutter/impeller/entity/contents/filters/inputs/filter_input.h" +#include "flutter/impeller/entity/entity.h" + +namespace impeller { + +/// @brief Optimized advanced blend that avoids a second subpass when there is +/// only a single input and a foreground color. +/// +/// These contents cannot absorb opacity. +class AdvancedForegroundBlendContents : public Contents { + public: + AdvancedForegroundBlendContents(); + + ~AdvancedForegroundBlendContents(); + + void SetBlendMode(BlendMode blend_mode); + + void SetSrcInput(std::shared_ptr input); + + void SetForegroundColor(Color color); + + void SetCoverage(Rect rect); + + private: + // |Contents| + std::optional GetCoverage(const Entity& entity) const override; + + // |Contents| + bool Render(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const override; + + Color foreground_color_; + BlendMode blend_mode_; + std::shared_ptr input_; + Rect rect_; + + FML_DISALLOW_COPY_AND_ASSIGN(AdvancedForegroundBlendContents); +}; + +} // namespace impeller diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index b7ad908cd0160..ab4ab77b1d9de 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -2451,5 +2451,21 @@ TEST_P(EntityTest, InheritOpacityTest) { ASSERT_FALSE(runtime_effect->CanInheritOpacity(entity)); } +TEST_P(EntityTest, ColorFilterWithForegroundColorAdvancedBlend) { + auto image = CreateTextureForFixture("boston.jpg"); + auto filter = ColorFilterContents::MakeBlend( + BlendMode::kColorBurn, FilterInput::Make({image}), Color::Red()); + + auto callback = [&](ContentContext& context, RenderPass& pass) -> bool { + Entity entity; + entity.SetTransformation(Matrix::MakeScale(GetContentScale()) * + Matrix::MakeTranslation({500, 300}) * + Matrix::MakeScale(Vector2{0.5, 0.5})); + entity.SetContents(filter); + return entity.Render(context, pass); + }; + ASSERT_TRUE(OpenPlaygroundHere(callback)); +} + } // namespace testing } // namespace impeller