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 6 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
2 changes: 1 addition & 1 deletion impeller/aiks/paint.cc
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ std::shared_ptr<Contents> Paint::WithFilters(
std::shared_ptr<Contents> input,
std::optional<bool> is_solid_color) const {
bool is_solid_color_val = is_solid_color.value_or(!color_source);
input = WithColorFilter(input);
input = WithColorFilter(input, /*absorb_opacity=*/true);
input = WithInvertFilter(input);
input = WithMaskBlur(input, is_solid_color_val, Matrix());
input = WithImageFilter(input, Matrix(), /*is_subpass=*/false);
Expand Down
46 changes: 46 additions & 0 deletions impeller/compiler/shader_lib/impeller/blending.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -194,4 +194,50 @@ f16vec3 IPBlendLuminosity(f16vec3 dst, f16vec3 src) {
return IPSetLuminosity(dst, IPLuminosity(src));
}

/// Pipeline Blends.

f16vec4 IPBlendSourceOver(f16vec4 src, f16vec4 dst) {
return src + dst * (1.0hf - src.a);
}

f16vec4 IPBlendDestinationOver(f16vec4 src, f16vec4 dst) {
return dst + src * (1.0hf - dst.a);
}

f16vec4 IPBlendSourceIn(f16vec4 src, f16vec4 dst) {
return src * dst.a;
}

f16vec4 IPBlendDestinationIn(f16vec4 src, f16vec4 dst) {
return dst * src.a;
}

f16vec4 IPBlendSourceOut(f16vec4 src, f16vec4 dst) {
return src * (1.0hf - dst.a);
}

f16vec4 IPBlendDestinationOut(f16vec4 src, f16vec4 dst) {
return dst * (1.0hf - src.a);
}

f16vec4 IPBlendSourceATop(f16vec4 src, f16vec4 dst) {
return src * dst.a + dst * (1.0hf - src.a);
}

f16vec4 IPBlendDestinationATop(f16vec4 src, f16vec4 dst) {
return dst * src.a + src * (1.0hf - dst.a);
}

f16vec4 IPBlendXOR(f16vec4 src, f16vec4 dst) {
return src * (1.0hf - dst.a) + dst * (1.0hf - src.a);
}

f16vec4 IPBlendPlus(f16vec4 src, f16vec4 dst) {
return min(src + dst, f16vec4(1.0hf));
}

f16vec4 IPBlendModulate(f16vec4 src, f16vec4 dst) {
return src * dst;
}

#endif
1 change: 1 addition & 0 deletions impeller/entity/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ impeller_shaders("entity_shaders") {
"shaders/vertices.frag",
"shaders/yuv_to_rgb_filter.frag",
"shaders/yuv_to_rgb_filter.vert",
"shaders/blending/pipeline_blend.frag",
]
}

Expand Down
2 changes: 2 additions & 0 deletions impeller/entity/contents/content_context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,8 @@ ContentContext::ContentContext(std::shared_ptr<Context> context)
CreateDefaultPipeline<GeometryColorPipeline>(*context_);
yuv_to_rgb_filter_pipelines_[{}] =
CreateDefaultPipeline<YUVToRGBFilterPipeline>(*context_);
pipeline_blend_pipelines_[{}] =
CreateDefaultPipeline<PipelineBlendPipeline>(*context_);

if (solid_fill_pipelines_[{}]->GetDescriptor().has_value()) {
auto clip_pipeline_descriptor =
Expand Down
10 changes: 9 additions & 1 deletion impeller/entity/contents/content_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include "impeller/entity/linear_to_srgb_filter.vert.h"
#include "impeller/entity/morphology_filter.frag.h"
#include "impeller/entity/morphology_filter.vert.h"
#include "impeller/entity/pipeline_blend.frag.h"
#include "impeller/entity/radial_gradient_fill.frag.h"
#include "impeller/entity/rrect_blur.frag.h"
#include "impeller/entity/rrect_blur.vert.h"
Expand Down Expand Up @@ -125,7 +126,6 @@ using RadialGradientSSBOFillPipeline =
using SweepGradientSSBOFillPipeline =
RenderPipelineT<GradientFillVertexShader,
SweepGradientSsboFillFragmentShader>;
using BlendPipeline = RenderPipelineT<BlendVertexShader, BlendFragmentShader>;
using RRectBlurPipeline =
RenderPipelineT<RrectBlurVertexShader, RrectBlurFragmentShader>;
using BlendPipeline = RenderPipelineT<BlendVertexShader, BlendFragmentShader>;
Expand Down Expand Up @@ -165,6 +165,8 @@ using GlyphAtlasPipeline =
RenderPipelineT<GlyphAtlasVertexShader, GlyphAtlasFragmentShader>;
using GlyphAtlasSdfPipeline =
RenderPipelineT<GlyphAtlasSdfVertexShader, GlyphAtlasSdfFragmentShader>;
using PipelineBlendPipeline =
RenderPipelineT<BlendVertexShader, PipelineBlendFragmentShader>;
// Instead of requiring new shaders for clips, the solid fill stages are used
// to redirect writing to the stencil instead of color attachments.
using ClipPipeline =
Expand Down Expand Up @@ -469,6 +471,11 @@ class ContentContext {
return GetPipeline(yuv_to_rgb_filter_pipelines_, opts);
}

std::shared_ptr<Pipeline<PipelineDescriptor>> GetPipelineBlendPipeline(
ContentContextOptions opts) const {
return GetPipeline(pipeline_blend_pipelines_, opts);
}

// Advanced blends.

std::shared_ptr<Pipeline<PipelineDescriptor>> GetBlendColorPipeline(
Expand Down Expand Up @@ -705,6 +712,7 @@ class ContentContext {
mutable Variants<GlyphAtlasSdfPipeline> glyph_atlas_sdf_pipelines_;
mutable Variants<GeometryColorPipeline> geometry_color_pipelines_;
mutable Variants<YUVToRGBFilterPipeline> yuv_to_rgb_filter_pipelines_;
mutable Variants<PipelineBlendPipeline> pipeline_blend_pipelines_;
// Advanced blends.
mutable Variants<BlendColorPipeline> blend_color_pipelines_;
mutable Variants<BlendColorBurnPipeline> blend_colorburn_pipelines_;
Expand Down
115 changes: 114 additions & 1 deletion impeller/entity/contents/filters/blend_filter_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,115 @@ std::optional<Entity> BlendFilterContents::CreateForegroundBlend(
return sub_entity;
}

std::optional<Entity> BlendFilterContents::CreatePipelineForegroundBlend(
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, absorb_opacity, alpha](
const ContentContext& renderer,
const Entity& entity, RenderPass& pass) -> bool {
using VS = PipelineBlendPipeline::VertexShader;
using FS = PipelineBlendPipeline::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]},
{Point(origin.x + size.width, origin.y), dst_uvs[1]},
{Point(origin.x + size.width, origin.y + size.height), dst_uvs[3]},
{origin, dst_uvs[0]},
{Point(origin.x + size.width, origin.y + size.height), dst_uvs[3]},
{Point(origin.x, origin.y + size.height), dst_uvs[2]},
});
auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer);

Command cmd;
cmd.label = "Foreground Pipeline Blend Filter";
cmd.BindVertices(vtx_buffer);
cmd.stencil_reference = entity.GetStencilDepth();
auto options = OptionsFromPass(pass);
cmd.pipeline = renderer.GetPipelineBlendPipeline(options);

FS::FragInfo frag_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.texture_sampler_y_coord_scale =
dst_snapshot->texture->GetYCoordScale();

frag_info.color = foreground_color.Premultiply();
frag_info.input_alpha =
absorb_opacity ? dst_snapshot->opacity * alpha.value_or(1.0) : 1.0;

frag_info.operation = static_cast<Scalar>(blend_mode);

FS::BindFragInfo(cmd, host_buffer.EmplaceUniform(frag_info));

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;
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 @@ -531,6 +640,11 @@ std::optional<Entity> BlendFilterContents::RenderFilter(
}

if (blend_mode_ <= Entity::kLastPipelineBlendMode) {
if (inputs.size() == 1 && foreground_color_.has_value()) {
return CreatePipelineForegroundBlend(
inputs[0], renderer, entity, coverage, foreground_color_.value(),
blend_mode_, GetAlpha(), GetAbsorbOpacity());
}
return PipelineBlend(inputs, renderer, entity, coverage, blend_mode_,
foreground_color_, GetAbsorbOpacity(), GetAlpha());
}
Expand All @@ -541,7 +655,6 @@ std::optional<Entity> BlendFilterContents::RenderFilter(
foreground_color_.value(), blend_mode_,
GetAlpha(), GetAbsorbOpacity());
}

return advanced_blend_proc_(inputs, renderer, entity, coverage,
foreground_color_, GetAbsorbOpacity(),
GetAlpha());
Expand Down
10 changes: 10 additions & 0 deletions impeller/entity/contents/filters/blend_filter_contents.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@ class BlendFilterContents : public ColorFilterContents {
void SetForegroundColor(std::optional<Color> color);

private:
std::optional<Entity> CreatePipelineForegroundBlend(
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;

// |FilterContents|
std::optional<Entity> RenderFilter(const FilterInput::Vector& inputs,
const ContentContext& renderer,
Expand Down
7 changes: 4 additions & 3 deletions impeller/entity/contents/tiled_texture_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// found in the LICENSE file.

#include "impeller/entity/contents/tiled_texture_contents.h"
#include <iostream>

#include "impeller/entity/contents/clip_contents.h"
#include "impeller/entity/contents/content_context.h"
Expand Down Expand Up @@ -88,8 +89,8 @@ SamplerDescriptor TiledTextureContents::CreateDescriptor(

bool TiledTextureContents::UsesEmulatedTileMode(
const Capabilities& capabilities) const {
return TileModeToAddressMode(x_tile_mode_, capabilities).has_value() &&
TileModeToAddressMode(y_tile_mode_, capabilities).has_value();
return !TileModeToAddressMode(x_tile_mode_, capabilities).has_value() ||
!TileModeToAddressMode(y_tile_mode_, capabilities).has_value();
}

bool TiledTextureContents::Render(const ContentContext& renderer,
Expand Down Expand Up @@ -121,7 +122,7 @@ bool TiledTextureContents::Render(const ContentContext& renderer,
frame_info.texture_sampler_y_coord_scale = texture_->GetYCoordScale();

Command cmd;
cmd.label = "TiledTextureFill";
cmd.label = uses_emulated_tile_mode ? "TiledTextureFill" : "TextureFill";
cmd.stencil_reference = entity.GetStencilDepth();

auto options = OptionsFromPassAndEntity(pass, entity);
Expand Down
76 changes: 76 additions & 0 deletions impeller/entity/shaders/blending/pipeline_blend.frag
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// 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 <impeller/blending.glsl>
#include <impeller/color.glsl>
#include <impeller/texture.glsl>
#include <impeller/types.glsl>

uniform f16sampler2D texture_sampler_dst;

// This must be kept in sync with the values in impeller/geometry/color.h
// clear, src, dst are excluded as these do not require blending.

const float16_t kSourceOver = 3.0hf;
const float16_t kDestinationOver = 4.0hf;
const float16_t kSourceIn = 5.0hf;
const float16_t kDestinationIn = 6.0hf;
const float16_t kSourceOut = 7.0hf;
const float16_t kDestinationOut = 8.0hf;
const float16_t kSourceATop = 9.0hf;
const float16_t kDestinationATop = 10.0hf;
const float16_t kXor = 11.0hf;
const float16_t kPlus = 12.0hf;
const float16_t kModulate = 13.0hf;

uniform FragInfo {
float16_t operation;
float16_t input_alpha;
f16vec4 color;
}
frag_info;

in vec2 v_texture_coords;

out f16vec4 frag_color;

f16vec4 Sample(f16sampler2D texture_sampler, vec2 texture_coords) {
// gles 2.0 is the only backend without native decal support.
#ifdef IMPELLER_TARGET_OPENGLES
return IPSampleDecal(texture_sampler, texture_coords);
#else
return texture(texture_sampler, texture_coords);
#endif
}

// Note: this shader reduces the number of branches required by conditionally
// modifying the foreground color.
void main() {
f16vec4 dst_color =
texture(texture_sampler_dst, v_texture_coords) * frag_info.input_alpha;

if (frag_info.operation == kSourceOver) {
frag_color = IPBlendSourceOver(frag_info.color, dst_color);
} else if (frag_info.operation == kDestinationOver) {
frag_color = IPBlendDestinationOver(frag_info.color, dst_color);
} else if (frag_info.operation == kSourceIn) {
frag_color = IPBlendSourceIn(frag_info.color, dst_color);
} else if (frag_info.operation == kDestinationIn) {
frag_color = IPBlendDestinationIn(frag_info.color, dst_color);
} else if (frag_info.operation == kSourceOut) {
frag_color = IPBlendSourceOut(frag_info.color, dst_color);
} else if (frag_info.operation == kDestinationOut) {
frag_color = IPBlendDestinationOut(frag_info.color, dst_color);
} else if (frag_info.operation == kSourceATop) {
frag_color = IPBlendSourceATop(frag_info.color, dst_color);
} else if (frag_info.operation == kDestinationATop) {
frag_color = IPBlendDestinationATop(frag_info.color, dst_color);
} else if (frag_info.operation == kXor) {
frag_color = IPBlendXOR(frag_info.color, dst_color);
} else if (frag_info.operation == kPlus) {
frag_color = IPBlendPlus(frag_info.color, dst_color);
} else {
frag_color = IPBlendModulate(frag_info.color, dst_color);
}
}