-
Notifications
You must be signed in to change notification settings - Fork 6k
[Impeller] only use framebuffer advanced blends if available. #52284
Changes from 1 commit
a0a9c62
a2f9cad
9d8ae07
427da51
180c3fa
2dfc062
9707132
6fc0314
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,15 +8,20 @@ | |
| #include <memory> | ||
| #include <optional> | ||
|
|
||
| #include "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" | ||
| #include "impeller/entity/contents/filters/color_filter_contents.h" | ||
| #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<Entity> PipelineBlend( | |
| entity.GetBlendMode()); | ||
| } | ||
|
|
||
| std::optional<Entity> BlendFilterContents::CreateFramebufferAdvancedBlend( | ||
| const FilterInput::Vector& inputs, | ||
| const ContentContext& renderer, | ||
| const Entity& entity, | ||
| const Rect& coverage, | ||
| std::optional<Color> foreground_color, | ||
| BlendMode blend_mode, | ||
| std::optional<Scalar> 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<VS::PerVertexData> 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<Texture> src_texture; | ||
| if (foreground_color.has_value()) { | ||
| TextureDescriptor desc; | ||
| desc.size = {1, 1}; | ||
| desc.format = PixelFormat::kR8G8B8A8UNormInt; | ||
| desc.storage_mode = StorageMode::kDevicePrivate; | ||
| src_texture = | ||
| renderer.GetContext()->GetResourceAllocator()->CreateTexture(desc); | ||
| if (!src_texture) { | ||
| return false; | ||
| } | ||
|
|
||
| if (!src_texture->SetContents(foreground_color->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<VS::PerVertexData> 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<const Sampler>& 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<CommandBuffer> 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_ = \ | ||
|
|
@@ -738,6 +972,11 @@ std::optional<Entity> BlendFilterContents::RenderFilter( | |
| } | ||
|
|
||
| if (blend_mode_ <= Entity::kLastAdvancedBlendMode) { | ||
| if (renderer.GetDeviceCapabilities().SupportsFramebufferFetch()) { | ||
| return CreateFramebufferAdvancedBlend( | ||
| inputs, renderer, entity, coverage, foreground_color_.value(), | ||
| blend_mode_, GetAlpha(), GetAbsorbOpacity()); | ||
| } | ||
| if (inputs.size() == 1 && foreground_color_.has_value()) { | ||
| return CreateForegroundAdvancedBlend( | ||
| inputs[0], renderer, entity, coverage, foreground_color_.value(), | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -78,6 +78,20 @@ class BlendFilterContents : public ColorFilterContents { | |
| std::optional<Scalar> alpha, | ||
| ColorFilterContents::AbsorbOpacity absorb_opacity) const; | ||
|
|
||
| /// @brief Implements the advanced blends filters in terms of the framebuffer | ||
| /// blend filters. | ||
| /// | ||
| /// This allows a substantial reduction in the number of bootstrapped filters. | ||
| std::optional<Entity> CreateFramebufferAdvancedBlend( | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also document the
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Framebuffer advanced blends vs the other kind (pipeline blend?) I think are our own inventions of terminology (which totally make sense). But it would be good to point it out either here or in a glossary. |
||
| const FilterInput::Vector& inputs, | ||
| const ContentContext& renderer, | ||
| const Entity& entity, | ||
| const Rect& coverage, | ||
| std::optional<Color> foreground_color, | ||
| BlendMode blend_mode, | ||
| std::optional<Scalar> 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. | ||
| /// | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I thought we were always doing this. So this is is only ever relevant on OpenGL ES and the desktop Metal (which I keep forgetting exists) stuff right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And iOS simulator!