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
169 changes: 169 additions & 0 deletions impeller/entity/contents/filters/blend_filter_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <optional>

#include "impeller/core/formats.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/inputs/filter_input.h"
Expand Down Expand Up @@ -178,6 +179,168 @@ static std::optional<Entity> AdvancedBlend(
entity.GetBlendMode(), entity.GetStencilDepth());
}

std::optional<Entity> BlendFilterContents::CreateForegroundBlend(
const std::shared_ptr<FilterInput>& input,
const ContentContext& renderer,
const Entity& entity,
const Rect& coverage,
Color foreground_color,
BlendMode blend_mode,
std::optional<Scalar> alpha,
bool absorb_opacity) const {
auto dst_snapshot = input->GetSnapshot(renderer, entity);
if (!dst_snapshot.has_value()) {
return std::nullopt;
}

RenderProc render_proc = [foreground_color, coverage, dst_snapshot,
blend_mode, alpha, absorb_opacity](
const ContentContext& renderer,
const Entity& entity, RenderPass& pass) -> bool {
using VS = BlendScreenPipeline::VertexShader;
using FS = BlendScreenPipeline::FragmentShader;

auto& host_buffer = pass.GetTransientsBuffer();

auto maybe_dst_uvs = dst_snapshot->GetCoverageUVs(coverage);
if (!maybe_dst_uvs.has_value()) {
return false;
}
auto dst_uvs = maybe_dst_uvs.value();

auto size = coverage.size;
auto origin = coverage.origin;
VertexBufferBuilder<VS::PerVertexData> vtx_builder;
vtx_builder.AddVertices({
{origin, dst_uvs[0], dst_uvs[0]},
{Point(origin.x + size.width, origin.y), dst_uvs[1], dst_uvs[1]},
{Point(origin.x + size.width, origin.y + size.height), dst_uvs[3],
dst_uvs[3]},
{origin, dst_uvs[0], dst_uvs[0]},
{Point(origin.x + size.width, origin.y + size.height), dst_uvs[3],
dst_uvs[3]},
{Point(origin.x, origin.y + 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 =
absorb_opacity ? dst_snapshot->opacity * alpha.value_or(1.0) : 1.0;

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());

auto uniform_view = host_buffer.EmplaceUniform(frame_info);
VS::BindFrameInfo(cmd, uniform_view);

return pass.AddCommand(cmd);
};
CoverageProc coverage_proc =
[coverage](const Entity& entity) -> std::optional<Rect> {
return coverage;
};

auto contents = AnonymousContents::Make(render_proc, coverage_proc);

// If there is pending opacity but it was not absorbed by this entity, we have
// to convert this back to a snapshot so it can be passed on. This generally
// implies that there is another filter about to run, so we'd perform this
// operation anyway.
auto potential_opacity = alpha.value_or(1.0) * dst_snapshot->opacity;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This seems like a reasonable thing to do?

Copy link
Member

Choose a reason for hiding this comment

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

Yeah I think this makes sense, although it'll probably just end up getting absorbed right away due to no filter being chained most of the time. Maybe we can come up with a mechanism to reconcile this later.

if (!absorb_opacity && potential_opacity < 1.0) {
auto result_snapshot = contents->RenderToSnapshot(renderer, entity);
if (!result_snapshot.has_value()) {
return std::nullopt;
}
result_snapshot->opacity = potential_opacity;
return Entity::FromSnapshot(result_snapshot.value(), entity.GetBlendMode(),
entity.GetStencilDepth());
}

Entity sub_entity;
sub_entity.SetContents(std::move(contents));
sub_entity.SetStencilDepth(entity.GetStencilDepth());
sub_entity.SetTransformation(entity.GetTransformation());

return sub_entity;
}

static std::optional<Entity> PipelineBlend(
const FilterInput::Vector& inputs,
const ContentContext& renderer,
Expand Down Expand Up @@ -373,6 +536,12 @@ std::optional<Entity> BlendFilterContents::RenderFilter(
}

if (blend_mode_ <= Entity::kLastAdvancedBlendMode) {
if (inputs.size() == 1 && foreground_color_.has_value()) {
return CreateForegroundBlend(inputs[0], renderer, entity, coverage,
foreground_color_.value(), blend_mode_,
GetAlpha(), GetAbsorbOpacity());
}

return advanced_blend_proc_(inputs, renderer, entity, coverage,
foreground_color_, GetAbsorbOpacity(),
GetAlpha());
Expand Down
14 changes: 14 additions & 0 deletions impeller/entity/contents/filters/blend_filter_contents.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,20 @@ class BlendFilterContents : public ColorFilterContents {
const Matrix& effect_transform,
const Rect& coverage) const override;

/// @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.
std::optional<Entity> CreateForegroundBlend(
const std::shared_ptr<FilterInput>& input,
const ContentContext& renderer,
const Entity& entity,
const Rect& coverage,
Color foreground_color,
BlendMode blend_mode,
std::optional<Scalar> alpha,
bool absorb_opacity) const;

BlendMode blend_mode_ = BlendMode::kSourceOver;
AdvancedBlendProc advanced_blend_proc_;
std::optional<Color> foreground_color_;
Expand Down