Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 46 additions & 46 deletions impeller/entity/contents/content_context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -372,54 +372,54 @@ ContentContext::ContentContext(
framebuffer_blend_softlight_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<Scalar>(BlendSelectValues::kSoftLight), supports_decal});
} else {
Copy link
Member

@chinmaygarde chinmaygarde Apr 22, 2024

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And iOS simulator!

blend_color_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<Scalar>(BlendSelectValues::kColor), supports_decal});
blend_colorburn_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<Scalar>(BlendSelectValues::kColorBurn), supports_decal});
blend_colordodge_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<Scalar>(BlendSelectValues::kColorDodge), supports_decal});
blend_darken_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<Scalar>(BlendSelectValues::kDarken), supports_decal});
blend_difference_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<Scalar>(BlendSelectValues::kDifference), supports_decal});
blend_exclusion_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<Scalar>(BlendSelectValues::kExclusion), supports_decal});
blend_hardlight_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<Scalar>(BlendSelectValues::kHardLight), supports_decal});
blend_hue_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<Scalar>(BlendSelectValues::kHue), supports_decal});
blend_lighten_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<Scalar>(BlendSelectValues::kLighten), supports_decal});
blend_luminosity_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<Scalar>(BlendSelectValues::kLuminosity), supports_decal});
blend_multiply_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<Scalar>(BlendSelectValues::kMultiply), supports_decal});
blend_overlay_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<Scalar>(BlendSelectValues::kOverlay), supports_decal});
blend_saturation_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<Scalar>(BlendSelectValues::kSaturation), supports_decal});
blend_screen_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<Scalar>(BlendSelectValues::kScreen), supports_decal});
blend_softlight_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<Scalar>(BlendSelectValues::kSoftLight), supports_decal});
}

blend_color_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<Scalar>(BlendSelectValues::kColor), supports_decal});
blend_colorburn_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<Scalar>(BlendSelectValues::kColorBurn), supports_decal});
blend_colordodge_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<Scalar>(BlendSelectValues::kColorDodge), supports_decal});
blend_darken_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<Scalar>(BlendSelectValues::kDarken), supports_decal});
blend_difference_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<Scalar>(BlendSelectValues::kDifference), supports_decal});
blend_exclusion_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<Scalar>(BlendSelectValues::kExclusion), supports_decal});
blend_hardlight_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<Scalar>(BlendSelectValues::kHardLight), supports_decal});
blend_hue_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<Scalar>(BlendSelectValues::kHue), supports_decal});
blend_lighten_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<Scalar>(BlendSelectValues::kLighten), supports_decal});
blend_luminosity_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<Scalar>(BlendSelectValues::kLuminosity), supports_decal});
blend_multiply_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<Scalar>(BlendSelectValues::kMultiply), supports_decal});
blend_overlay_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<Scalar>(BlendSelectValues::kOverlay), supports_decal});
blend_saturation_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<Scalar>(BlendSelectValues::kSaturation), supports_decal});
blend_screen_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<Scalar>(BlendSelectValues::kScreen), supports_decal});
blend_softlight_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<Scalar>(BlendSelectValues::kSoftLight), supports_decal});

rrect_blur_pipelines_.CreateDefault(*context_, options_trianglestrip);
texture_strict_src_pipelines_.CreateDefault(*context_, options);
position_uv_pipelines_.CreateDefault(*context_, options);
Expand Down
239 changes: 239 additions & 0 deletions impeller/entity/contents/filters/blend_filter_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,20 @@
#include <memory>
#include <optional>

#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"
#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"
Expand Down Expand Up @@ -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::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<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_ = \
Expand Down Expand Up @@ -739,6 +973,11 @@ std::optional<Entity> 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(
Expand Down
17 changes: 17 additions & 0 deletions impeller/entity/contents/filters/blend_filter_contents.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,23 @@ 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 requires device support for frameuffer fetch,
/// `Capabilities::SupportsFramebufferFetch` must be true
/// This allows a substantial reduction in the number of bootstrapped
/// shaders.
std::optional<Entity> CreateFramebufferAdvancedBlend(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also document the SupportsFramebufferFetch requirement?

Copy link
Member

Choose a reason for hiding this comment

The 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.
///
Expand Down
Loading