forked from firka/flutter
[Impeller] add a generic porter duff blend foreground shader. (flutter/engine#41098)
The pipeline blend component of https://github.com/flutter/flutter/issues/124025 . This should also land alongside https://github.com/flutter/flutter/pull/124640 which was necessary before we had completely optimized the image filters for texture inputs. Fixes https://github.com/flutter/flutter/issues/121650 Fixes https://github.com/flutter/flutter/issues/124025
This commit is contained in:
@@ -1278,6 +1278,7 @@ ORIGIN: ../../../flutter/impeller/entity/shaders/blending/ios/framebuffer_blend_
|
||||
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/ios/framebuffer_blend_saturation.frag + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/ios/framebuffer_blend_screen.frag + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/ios/framebuffer_blend_softlight.frag + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/impeller/entity/shaders/blending/porter_duff_blend.frag + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/impeller/entity/shaders/border_mask_blur.frag + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/impeller/entity/shaders/border_mask_blur.vert + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/impeller/entity/shaders/color_matrix_color_filter.frag + ../../../flutter/LICENSE
|
||||
@@ -3867,6 +3868,7 @@ FILE: ../../../flutter/impeller/entity/shaders/blending/ios/framebuffer_blend_ov
|
||||
FILE: ../../../flutter/impeller/entity/shaders/blending/ios/framebuffer_blend_saturation.frag
|
||||
FILE: ../../../flutter/impeller/entity/shaders/blending/ios/framebuffer_blend_screen.frag
|
||||
FILE: ../../../flutter/impeller/entity/shaders/blending/ios/framebuffer_blend_softlight.frag
|
||||
FILE: ../../../flutter/impeller/entity/shaders/blending/porter_duff_blend.frag
|
||||
FILE: ../../../flutter/impeller/entity/shaders/border_mask_blur.frag
|
||||
FILE: ../../../flutter/impeller/entity/shaders/border_mask_blur.vert
|
||||
FILE: ../../../flutter/impeller/entity/shaders/color_matrix_color_filter.frag
|
||||
|
||||
@@ -2035,6 +2035,26 @@ TEST_P(AiksTest, TranslucentSaveLayerWithBlendImageFilterAndDrawsCorrectly) {
|
||||
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
|
||||
}
|
||||
|
||||
TEST_P(AiksTest, TranslucentSaveLayerWithColorImageFilterAndDrawsCorrectly) {
|
||||
Canvas canvas;
|
||||
|
||||
canvas.DrawRect(Rect::MakeXYWH(100, 100, 300, 300), {.color = Color::Blue()});
|
||||
|
||||
canvas.SaveLayer({
|
||||
.color = Color::Black().WithAlpha(0.5),
|
||||
.color_filter =
|
||||
[](FilterInput::Ref input) {
|
||||
return ColorFilterContents::MakeBlend(
|
||||
BlendMode::kDestinationOver, {std::move(input)}, Color::Red());
|
||||
},
|
||||
});
|
||||
|
||||
canvas.DrawRect(Rect::MakeXYWH(100, 500, 300, 300), {.color = Color::Blue()});
|
||||
canvas.Restore();
|
||||
|
||||
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
|
||||
}
|
||||
|
||||
TEST_P(AiksTest, TranslucentSaveLayerImageDrawsCorrectly) {
|
||||
Canvas canvas;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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/porter_duff_blend.frag",
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -287,6 +287,8 @@ ContentContext::ContentContext(std::shared_ptr<Context> context)
|
||||
CreateDefaultPipeline<GeometryColorPipeline>(*context_);
|
||||
yuv_to_rgb_filter_pipelines_[{}] =
|
||||
CreateDefaultPipeline<YUVToRGBFilterPipeline>(*context_);
|
||||
porter_duff_blend_pipelines_[{}] =
|
||||
CreateDefaultPipeline<PorterDuffBlendPipeline>(*context_);
|
||||
|
||||
if (solid_fill_pipelines_[{}]->GetDescriptor().has_value()) {
|
||||
auto clip_pipeline_descriptor =
|
||||
|
||||
@@ -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/porter_duff_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"
|
||||
@@ -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>;
|
||||
@@ -165,6 +165,8 @@ using GlyphAtlasPipeline =
|
||||
RenderPipelineT<GlyphAtlasVertexShader, GlyphAtlasFragmentShader>;
|
||||
using GlyphAtlasSdfPipeline =
|
||||
RenderPipelineT<GlyphAtlasSdfVertexShader, GlyphAtlasSdfFragmentShader>;
|
||||
using PorterDuffBlendPipeline =
|
||||
RenderPipelineT<BlendVertexShader, PorterDuffBlendFragmentShader>;
|
||||
// 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 =
|
||||
@@ -469,6 +471,11 @@ class ContentContext {
|
||||
return GetPipeline(yuv_to_rgb_filter_pipelines_, opts);
|
||||
}
|
||||
|
||||
std::shared_ptr<Pipeline<PipelineDescriptor>> GetPorterDuffBlendPipeline(
|
||||
ContentContextOptions opts) const {
|
||||
return GetPipeline(porter_duff_blend_pipelines_, opts);
|
||||
}
|
||||
|
||||
// Advanced blends.
|
||||
|
||||
std::shared_ptr<Pipeline<PipelineDescriptor>> GetBlendColorPipeline(
|
||||
@@ -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<PorterDuffBlendPipeline> porter_duff_blend_pipelines_;
|
||||
// Advanced blends.
|
||||
mutable Variants<BlendColorPipeline> blend_color_pipelines_;
|
||||
mutable Variants<BlendColorBurnPipeline> blend_colorburn_pipelines_;
|
||||
|
||||
@@ -179,7 +179,7 @@ static std::optional<Entity> AdvancedBlend(
|
||||
entity.GetBlendMode(), entity.GetStencilDepth());
|
||||
}
|
||||
|
||||
std::optional<Entity> BlendFilterContents::CreateForegroundBlend(
|
||||
std::optional<Entity> BlendFilterContents::CreateForegroundAdvancedBlend(
|
||||
const std::shared_ptr<FilterInput>& input,
|
||||
const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
@@ -318,21 +318,145 @@ std::optional<Entity> BlendFilterContents::CreateForegroundBlend(
|
||||
|
||||
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 sub_entity;
|
||||
sub_entity.SetContents(std::move(contents));
|
||||
sub_entity.SetStencilDepth(entity.GetStencilDepth());
|
||||
sub_entity.SetTransformation(entity.GetTransformation());
|
||||
|
||||
return sub_entity;
|
||||
}
|
||||
|
||||
constexpr std::array<std::array<Scalar, 5>, 15> kPorterDuffCoefficients = {{
|
||||
{0, 0, 0, 0, 0}, // Clear
|
||||
{1, 0, 0, 0, 0}, // Source
|
||||
{0, 0, 1, 0, 0}, // Destination
|
||||
{1, 0, 1, -1, 0}, // SourceOver
|
||||
{1, -1, 1, 0, 0}, // DestinationOver
|
||||
{0, 1, 0, 0, 0}, // SourceIn
|
||||
{0, 0, 0, 1, 0}, // DestinationIn
|
||||
{1, -1, 0, 0, 0}, // SourceOut
|
||||
{0, 0, 1, -1, 0}, // DestinationOut
|
||||
{0, 1, 1, -1, 0}, // SourceATop
|
||||
{1, -1, 0, 1, 0}, // DestinationATop
|
||||
{1, -1, 1, -1, 0}, // Xor
|
||||
{1, 0, 1, 0, 0}, // Plus
|
||||
{0, 0, 0, 0, 1}, // Modulate
|
||||
{0, 0, 1, 0, -1}, // Screen
|
||||
}};
|
||||
|
||||
std::optional<Entity> BlendFilterContents::CreateForegroundPorterDuffBlend(
|
||||
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;
|
||||
}
|
||||
|
||||
if (blend_mode == BlendMode::kClear) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (blend_mode == BlendMode::kDestination) {
|
||||
return Entity::FromSnapshot(dst_snapshot, entity.GetBlendMode(),
|
||||
entity.GetStencilDepth());
|
||||
}
|
||||
|
||||
if (blend_mode == BlendMode::kSource) {
|
||||
auto contents = std::make_shared<SolidColorContents>();
|
||||
contents->SetGeometry(Geometry::MakeRect(coverage));
|
||||
contents->SetColor(foreground_color);
|
||||
|
||||
Entity foreground_entity;
|
||||
foreground_entity.SetBlendMode(entity.GetBlendMode());
|
||||
foreground_entity.SetStencilDepth(entity.GetStencilDepth());
|
||||
foreground_entity.SetContents(std::move(contents));
|
||||
return foreground_entity;
|
||||
}
|
||||
|
||||
RenderProc render_proc = [foreground_color, coverage, dst_snapshot,
|
||||
blend_mode, absorb_opacity, alpha](
|
||||
const ContentContext& renderer,
|
||||
const Entity& entity, RenderPass& pass) -> bool {
|
||||
using VS = PorterDuffBlendPipeline::VertexShader;
|
||||
using FS = PorterDuffBlendPipeline::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 PorterDuff Blend Filter";
|
||||
cmd.BindVertices(vtx_buffer);
|
||||
cmd.stencil_reference = entity.GetStencilDepth();
|
||||
auto options = OptionsFromPass(pass);
|
||||
cmd.pipeline = renderer.GetPorterDuffBlendPipeline(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;
|
||||
|
||||
auto blend_coefficients =
|
||||
kPorterDuffCoefficients[static_cast<int>(blend_mode)];
|
||||
frag_info.src_coeff = blend_coefficients[0];
|
||||
frag_info.src_coeff_dst_alpha = blend_coefficients[1];
|
||||
frag_info.dst_coeff = blend_coefficients[2];
|
||||
frag_info.dst_coeff_src_alpha = blend_coefficients[3];
|
||||
frag_info.dst_coeff_src_color = blend_coefficients[4];
|
||||
|
||||
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);
|
||||
|
||||
Entity sub_entity;
|
||||
sub_entity.SetContents(std::move(contents));
|
||||
sub_entity.SetStencilDepth(entity.GetStencilDepth());
|
||||
@@ -531,17 +655,23 @@ std::optional<Entity> BlendFilterContents::RenderFilter(
|
||||
}
|
||||
|
||||
if (blend_mode_ <= Entity::kLastPipelineBlendMode) {
|
||||
if (inputs.size() == 1 && foreground_color_.has_value() &&
|
||||
GetAbsorbOpacity()) {
|
||||
return CreateForegroundPorterDuffBlend(
|
||||
inputs[0], renderer, entity, coverage, foreground_color_.value(),
|
||||
blend_mode_, GetAlpha(), GetAbsorbOpacity());
|
||||
}
|
||||
return PipelineBlend(inputs, renderer, entity, coverage, blend_mode_,
|
||||
foreground_color_, GetAbsorbOpacity(), GetAlpha());
|
||||
}
|
||||
|
||||
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());
|
||||
if (inputs.size() == 1 && foreground_color_.has_value() &&
|
||||
GetAbsorbOpacity()) {
|
||||
return CreateForegroundAdvancedBlend(
|
||||
inputs[0], renderer, entity, coverage, foreground_color_.value(),
|
||||
blend_mode_, GetAlpha(), GetAbsorbOpacity());
|
||||
}
|
||||
|
||||
return advanced_blend_proc_(inputs, renderer, entity, coverage,
|
||||
foreground_color_, GetAbsorbOpacity(),
|
||||
GetAlpha());
|
||||
|
||||
@@ -42,7 +42,21 @@ class BlendFilterContents : public ColorFilterContents {
|
||||
/// only a single input and a foreground color.
|
||||
///
|
||||
/// These contents cannot absorb opacity.
|
||||
std::optional<Entity> CreateForegroundBlend(
|
||||
std::optional<Entity> CreateForegroundAdvancedBlend(
|
||||
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;
|
||||
|
||||
/// @brief Optimized porter-duff 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> CreateForegroundPorterDuffBlend(
|
||||
const std::shared_ptr<FilterInput>& input,
|
||||
const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
|
||||
@@ -88,8 +88,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,
|
||||
@@ -121,7 +121,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);
|
||||
|
||||
@@ -2467,6 +2467,70 @@ TEST_P(EntityTest, ColorFilterWithForegroundColorAdvancedBlend) {
|
||||
ASSERT_TRUE(OpenPlaygroundHere(callback));
|
||||
}
|
||||
|
||||
TEST_P(EntityTest, ColorFilterWithForegroundColorClearBlend) {
|
||||
auto image = CreateTextureForFixture("boston.jpg");
|
||||
auto filter = ColorFilterContents::MakeBlend(
|
||||
BlendMode::kClear, 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));
|
||||
}
|
||||
|
||||
TEST_P(EntityTest, ColorFilterWithForegroundColorSrcBlend) {
|
||||
auto image = CreateTextureForFixture("boston.jpg");
|
||||
auto filter = ColorFilterContents::MakeBlend(
|
||||
BlendMode::kSource, 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));
|
||||
}
|
||||
|
||||
TEST_P(EntityTest, ColorFilterWithForegroundColorDstBlend) {
|
||||
auto image = CreateTextureForFixture("boston.jpg");
|
||||
auto filter = ColorFilterContents::MakeBlend(
|
||||
BlendMode::kDestination, 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));
|
||||
}
|
||||
|
||||
TEST_P(EntityTest, ColorFilterWithForegroundColorSrcInBlend) {
|
||||
auto image = CreateTextureForFixture("boston.jpg");
|
||||
auto filter = ColorFilterContents::MakeBlend(
|
||||
BlendMode::kSourceIn, 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));
|
||||
}
|
||||
|
||||
TEST_P(EntityTest, CoverageForStrokePathWithNegativeValuesInTransform) {
|
||||
auto arrow_head = PathBuilder{}
|
||||
.MoveTo({50, 120})
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
// 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;
|
||||
|
||||
uniform FragInfo {
|
||||
float16_t src_coeff;
|
||||
float16_t src_coeff_dst_alpha;
|
||||
float16_t dst_coeff;
|
||||
float16_t dst_coeff_src_alpha;
|
||||
float16_t dst_coeff_src_color;
|
||||
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
|
||||
}
|
||||
|
||||
void main() {
|
||||
f16vec4 dst =
|
||||
texture(texture_sampler_dst, v_texture_coords) * frag_info.input_alpha;
|
||||
f16vec4 src = frag_info.color;
|
||||
frag_color =
|
||||
src * (frag_info.src_coeff + dst.a * frag_info.src_coeff_dst_alpha) +
|
||||
dst * (frag_info.dst_coeff + src.a * frag_info.dst_coeff_src_alpha +
|
||||
src * frag_info.dst_coeff_src_color);
|
||||
}
|
||||
@@ -8442,6 +8442,124 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"flutter/impeller/entity/gles/porter_duff_blend.frag.gles": {
|
||||
"Mali-G78": {
|
||||
"core": "Mali-G78",
|
||||
"filename": "flutter/impeller/entity/gles/porter_duff_blend.frag.gles",
|
||||
"has_side_effects": false,
|
||||
"has_uniform_computation": true,
|
||||
"modifies_coverage": false,
|
||||
"reads_color_buffer": false,
|
||||
"type": "Fragment",
|
||||
"uses_late_zs_test": false,
|
||||
"uses_late_zs_update": false,
|
||||
"variants": {
|
||||
"Main": {
|
||||
"fp16_arithmetic": 100,
|
||||
"has_stack_spilling": false,
|
||||
"performance": {
|
||||
"longest_path_bound_pipelines": [
|
||||
"varying",
|
||||
"texture"
|
||||
],
|
||||
"longest_path_cycles": [
|
||||
0.09375,
|
||||
0.09375,
|
||||
0.03125,
|
||||
0.0,
|
||||
0.0,
|
||||
0.25,
|
||||
0.25
|
||||
],
|
||||
"pipelines": [
|
||||
"arith_total",
|
||||
"arith_fma",
|
||||
"arith_cvt",
|
||||
"arith_sfu",
|
||||
"load_store",
|
||||
"varying",
|
||||
"texture"
|
||||
],
|
||||
"shortest_path_bound_pipelines": [
|
||||
"varying",
|
||||
"texture"
|
||||
],
|
||||
"shortest_path_cycles": [
|
||||
0.09375,
|
||||
0.09375,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.25,
|
||||
0.25
|
||||
],
|
||||
"total_bound_pipelines": [
|
||||
"varying",
|
||||
"texture"
|
||||
],
|
||||
"total_cycles": [
|
||||
0.09375,
|
||||
0.09375,
|
||||
0.03125,
|
||||
0.0,
|
||||
0.0,
|
||||
0.25,
|
||||
0.25
|
||||
]
|
||||
},
|
||||
"stack_spill_bytes": 0,
|
||||
"thread_occupancy": 100,
|
||||
"uniform_registers_used": 10,
|
||||
"work_registers_used": 19
|
||||
}
|
||||
}
|
||||
},
|
||||
"Mali-T880": {
|
||||
"core": "Mali-T880",
|
||||
"filename": "flutter/impeller/entity/gles/porter_duff_blend.frag.gles",
|
||||
"has_uniform_computation": false,
|
||||
"type": "Fragment",
|
||||
"variants": {
|
||||
"Main": {
|
||||
"has_stack_spilling": false,
|
||||
"performance": {
|
||||
"longest_path_bound_pipelines": [
|
||||
"arithmetic"
|
||||
],
|
||||
"longest_path_cycles": [
|
||||
1.649999976158142,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"pipelines": [
|
||||
"arithmetic",
|
||||
"load_store",
|
||||
"texture"
|
||||
],
|
||||
"shortest_path_bound_pipelines": [
|
||||
"arithmetic"
|
||||
],
|
||||
"shortest_path_cycles": [
|
||||
1.649999976158142,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"total_bound_pipelines": [
|
||||
"arithmetic"
|
||||
],
|
||||
"total_cycles": [
|
||||
2.0,
|
||||
1.0,
|
||||
1.0
|
||||
]
|
||||
},
|
||||
"thread_occupancy": 100,
|
||||
"uniform_registers_used": 2,
|
||||
"work_registers_used": 2
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"flutter/impeller/entity/gles/position_color.vert.gles": {
|
||||
"Mali-G78": {
|
||||
"core": "Mali-G78",
|
||||
@@ -11857,6 +11975,79 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"flutter/impeller/entity/porter_duff_blend.frag.vkspv": {
|
||||
"Mali-G78": {
|
||||
"core": "Mali-G78",
|
||||
"filename": "flutter/impeller/entity/porter_duff_blend.frag.vkspv",
|
||||
"has_side_effects": false,
|
||||
"has_uniform_computation": true,
|
||||
"modifies_coverage": false,
|
||||
"reads_color_buffer": false,
|
||||
"type": "Fragment",
|
||||
"uses_late_zs_test": false,
|
||||
"uses_late_zs_update": false,
|
||||
"variants": {
|
||||
"Main": {
|
||||
"fp16_arithmetic": 55,
|
||||
"has_stack_spilling": false,
|
||||
"performance": {
|
||||
"longest_path_bound_pipelines": [
|
||||
"varying",
|
||||
"texture"
|
||||
],
|
||||
"longest_path_cycles": [
|
||||
0.140625,
|
||||
0.140625,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.25,
|
||||
0.25
|
||||
],
|
||||
"pipelines": [
|
||||
"arith_total",
|
||||
"arith_fma",
|
||||
"arith_cvt",
|
||||
"arith_sfu",
|
||||
"load_store",
|
||||
"varying",
|
||||
"texture"
|
||||
],
|
||||
"shortest_path_bound_pipelines": [
|
||||
"varying",
|
||||
"texture"
|
||||
],
|
||||
"shortest_path_cycles": [
|
||||
0.140625,
|
||||
0.140625,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.25,
|
||||
0.25
|
||||
],
|
||||
"total_bound_pipelines": [
|
||||
"varying",
|
||||
"texture"
|
||||
],
|
||||
"total_cycles": [
|
||||
0.140625,
|
||||
0.140625,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.25,
|
||||
0.25
|
||||
]
|
||||
},
|
||||
"stack_spill_bytes": 0,
|
||||
"thread_occupancy": 100,
|
||||
"uniform_registers_used": 14,
|
||||
"work_registers_used": 10
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"flutter/impeller/entity/position_color.vert.vkspv": {
|
||||
"Mali-G78": {
|
||||
"core": "Mali-G78",
|
||||
|
||||
Reference in New Issue
Block a user