diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index 81745404155e0..b8a5c7dc73357 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -18,7 +18,9 @@ #include "impeller/aiks/paint_pass_delegate.h" #include "impeller/aiks/testing/context_spy.h" #include "impeller/entity/contents/color_source_contents.h" +#include "impeller/entity/contents/conical_gradient_contents.h" #include "impeller/entity/contents/filters/inputs/filter_input.h" +#include "impeller/entity/contents/linear_gradient_contents.h" #include "impeller/entity/contents/scene_contents.h" #include "impeller/entity/contents/solid_color_contents.h" #include "impeller/entity/contents/tiled_texture_contents.h" @@ -393,6 +395,30 @@ TEST_P(AiksTest, CanRenderLinearGradientDecal) { CanRenderLinearGradient(this, Entity::TileMode::kDecal); } +TEST_P(AiksTest, CanRenderLinearGradientDecalWithColorFilter) { + Canvas canvas; + canvas.Scale(GetContentScale()); + Paint paint; + canvas.Translate({100.0f, 0, 0}); + + std::vector colors = {Color{0.9568, 0.2627, 0.2118, 1.0}, + Color{0.1294, 0.5882, 0.9529, 0.0}}; + std::vector stops = {0.0, 1.0}; + + paint.color_source = ColorSource::MakeLinearGradient( + {0, 0}, {200, 200}, std::move(colors), std::move(stops), + Entity::TileMode::kDecal, {}); + // Overlay the gradient with 25% green. This should appear as the entire + // rectangle being drawn with 25% green, including the border area outside the + // decal gradient. + paint.color_filter = ColorFilter::MakeBlend(BlendMode::kSourceOver, + Color::Green().WithAlpha(0.25)); + + paint.color = Color(1.0, 1.0, 1.0, 1.0); + canvas.DrawRect({0, 0, 600, 600}, paint); + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + namespace { void CanRenderLinearGradientWithOverlappingStops(AiksTest* aiks_test, Entity::TileMode tile_mode) { @@ -2867,5 +2893,35 @@ TEST_P(AiksTest, MatrixBackdropFilter) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } +TEST_P(AiksTest, SolidColorApplyColorFilter) { + auto contents = SolidColorContents(); + contents.SetColor(Color::CornflowerBlue().WithAlpha(0.75)); + auto result = contents.ApplyColorFilter([](const Color& color) { + return color.Blend(Color::LimeGreen().WithAlpha(0.75), BlendMode::kScreen); + }); + ASSERT_TRUE(result); + ASSERT_COLOR_NEAR(contents.GetColor(), + Color(0.433247, 0.879523, 0.825324, 0.75)); +} + +#define APPLY_COLOR_FILTER_GRADIENT_TEST(name) \ + TEST_P(AiksTest, name##GradientApplyColorFilter) { \ + auto contents = name##GradientContents(); \ + contents.SetColors({Color::CornflowerBlue().WithAlpha(0.75)}); \ + auto result = contents.ApplyColorFilter([](const Color& color) { \ + return color.Blend(Color::LimeGreen().WithAlpha(0.75), \ + BlendMode::kScreen); \ + }); \ + ASSERT_TRUE(result); \ + \ + std::vector expected = {Color(0.433247, 0.879523, 0.825324, 0.75)}; \ + ASSERT_COLORS_NEAR(contents.GetColors(), expected); \ + } + +APPLY_COLOR_FILTER_GRADIENT_TEST(Linear); +APPLY_COLOR_FILTER_GRADIENT_TEST(Radial); +APPLY_COLOR_FILTER_GRADIENT_TEST(Conical); +APPLY_COLOR_FILTER_GRADIENT_TEST(Sweep); + } // namespace testing } // namespace impeller diff --git a/impeller/aiks/color_filter.cc b/impeller/aiks/color_filter.cc index cff57962d6244..a684a44643544 100644 --- a/impeller/aiks/color_filter.cc +++ b/impeller/aiks/color_filter.cc @@ -43,7 +43,7 @@ BlendColorFilter::BlendColorFilter(BlendMode blend_mode, Color color) BlendColorFilter::~BlendColorFilter() = default; -std::shared_ptr BlendColorFilter::GetColorFilter( +std::shared_ptr BlendColorFilter::WrapWithGPUColorFilter( std::shared_ptr input, bool absorb_opacity) const { auto filter = @@ -67,7 +67,7 @@ MatrixColorFilter::MatrixColorFilter(ColorMatrix color_matrix) MatrixColorFilter::~MatrixColorFilter() = default; -std::shared_ptr MatrixColorFilter::GetColorFilter( +std::shared_ptr MatrixColorFilter::WrapWithGPUColorFilter( std::shared_ptr input, bool absorb_opacity) const { auto filter = @@ -90,7 +90,8 @@ SrgbToLinearColorFilter::SrgbToLinearColorFilter() = default; SrgbToLinearColorFilter::~SrgbToLinearColorFilter() = default; -std::shared_ptr SrgbToLinearColorFilter::GetColorFilter( +std::shared_ptr +SrgbToLinearColorFilter::WrapWithGPUColorFilter( std::shared_ptr input, bool absorb_opacity) const { auto filter = ColorFilterContents::MakeSrgbToLinearFilter({std::move(input)}); @@ -111,7 +112,8 @@ LinearToSrgbColorFilter::LinearToSrgbColorFilter() = default; LinearToSrgbColorFilter::~LinearToSrgbColorFilter() = default; -std::shared_ptr LinearToSrgbColorFilter::GetColorFilter( +std::shared_ptr +LinearToSrgbColorFilter::WrapWithGPUColorFilter( std::shared_ptr input, bool absorb_opacity) const { auto filter = ColorFilterContents::MakeSrgbToLinearFilter({std::move(input)}); diff --git a/impeller/aiks/color_filter.h b/impeller/aiks/color_filter.h index 972d8f3278ed9..3ae4d960488ee 100644 --- a/impeller/aiks/color_filter.h +++ b/impeller/aiks/color_filter.h @@ -17,6 +17,8 @@ struct Paint; class ColorFilter { public: + /// A procedure that filters a given unpremultiplied color to produce a new + /// unpremultiplied color. using ColorFilterProc = std::function; ColorFilter(); @@ -32,10 +34,19 @@ class ColorFilter { static std::shared_ptr MakeLinearToSrgb(); - virtual std::shared_ptr GetColorFilter( + /// @brief Wraps the given filter input with a GPU-based filter that will + /// perform the color operation. The given input will first be + /// rendered to a texture and then filtered. + /// + /// Note that this operation has no consideration for the original + /// geometry mask of the filter input. And the entire input texture is + /// treated as color information. + virtual std::shared_ptr WrapWithGPUColorFilter( std::shared_ptr input, bool absorb_opacity) const = 0; + /// @brief Returns a function that can be used to filter unpremultiplied + /// Impeller Colors on the CPU. virtual ColorFilterProc GetCPUColorFilterProc() const = 0; }; @@ -50,7 +61,7 @@ class BlendColorFilter final : public ColorFilter { ~BlendColorFilter() override; // |ColorFilter| - std::shared_ptr GetColorFilter( + std::shared_ptr WrapWithGPUColorFilter( std::shared_ptr input, bool absorb_opacity) const override; @@ -73,7 +84,7 @@ class MatrixColorFilter final : public ColorFilter { ~MatrixColorFilter() override; // |ColorFilter| - std::shared_ptr GetColorFilter( + std::shared_ptr WrapWithGPUColorFilter( std::shared_ptr input, bool absorb_opacity) const override; @@ -95,7 +106,7 @@ class SrgbToLinearColorFilter final : public ColorFilter { ~SrgbToLinearColorFilter() override; // |ColorFilter| - std::shared_ptr GetColorFilter( + std::shared_ptr WrapWithGPUColorFilter( std::shared_ptr input, bool absorb_opacity) const override; @@ -114,7 +125,7 @@ class LinearToSrgbColorFilter final : public ColorFilter { ~LinearToSrgbColorFilter() override; // |ColorFilter| - std::shared_ptr GetColorFilter( + std::shared_ptr WrapWithGPUColorFilter( std::shared_ptr input, bool absorb_opacity) const override; diff --git a/impeller/aiks/color_source.cc b/impeller/aiks/color_source.cc index 7d4550ad0734b..e5c6d13e4e2e7 100644 --- a/impeller/aiks/color_source.cc +++ b/impeller/aiks/color_source.cc @@ -175,7 +175,8 @@ ColorSource ColorSource::MakeImage(std::shared_ptr texture, if (paint.color_filter) { TiledTextureContents::ColorFilterProc filter_proc = [color_filter = paint.color_filter](FilterInput::Ref input) { - return color_filter->GetColorFilter(std::move(input), false); + return color_filter->WrapWithGPUColorFilter(std::move(input), + false); }; contents->SetColorFilter(filter_proc); } diff --git a/impeller/aiks/paint.cc b/impeller/aiks/paint.cc index 81a1c9626e165..5cf342ee38cc4 100644 --- a/impeller/aiks/paint.cc +++ b/impeller/aiks/paint.cc @@ -89,11 +89,20 @@ std::shared_ptr Paint::WithColorFilter( if (color_source.GetType() == ColorSource::Type::kImage) { return input; } - if (color_filter) { - input = - color_filter->GetColorFilter(FilterInput::Make(input), absorb_opacity); + + if (!color_filter) { + return input; } - return input; + + // Attempt to apply the color filter on the CPU first. + // Note: This is not just an optimization; some color sources rely on + // CPU-applied color filters to behave properly. + if (input->ApplyColorFilter(color_filter->GetCPUColorFilterProc())) { + return input; + } + + return color_filter->WrapWithGPUColorFilter(FilterInput::Make(input), + absorb_opacity); } /// A color matrix which inverts colors. diff --git a/impeller/compiler/shader_lib/impeller/texture.glsl b/impeller/compiler/shader_lib/impeller/texture.glsl index cd0e33c248bbe..c973dd2971699 100644 --- a/impeller/compiler/shader_lib/impeller/texture.glsl +++ b/impeller/compiler/shader_lib/impeller/texture.glsl @@ -114,10 +114,11 @@ vec4 IPSampleLinearWithTileMode(sampler2D tex, float y_coord_scale, vec2 half_texel, float x_tile_mode, - float y_tile_mode) { + float y_tile_mode, + vec4 decal_border_color) { if (x_tile_mode == kTileModeDecal && (coords.x < 0 || coords.x >= 1) || y_tile_mode == kTileModeDecal && (coords.y < 0 || coords.y >= 1)) { - return vec4(0); + return decal_border_color; } return IPSampleLinear(tex, IPVec2Tile(coords, x_tile_mode, y_tile_mode), @@ -152,9 +153,10 @@ vec4 IPSampleLinearWithTileMode(sampler2D tex, vec2 coords, float y_coord_scale, vec2 half_texel, - float tile_mode) { + float tile_mode, + vec4 decal_border_color) { return IPSampleLinearWithTileMode(tex, coords, y_coord_scale, half_texel, - tile_mode, tile_mode); + tile_mode, tile_mode, decal_border_color); } #endif diff --git a/impeller/display_list/dl_dispatcher.cc b/impeller/display_list/dl_dispatcher.cc index b099ee34fc97e..45fcc09a2508f 100644 --- a/impeller/display_list/dl_dispatcher.cc +++ b/impeller/display_list/dl_dispatcher.cc @@ -665,7 +665,7 @@ static Paint::ImageFilterProc ToImageFilterProc( // "absorb opacity" flag to false. For image filters, the snapshot // opacity needs to be deferred until the result of the filter chain is // being blended with the layer. - return filter->GetColorFilter(std::move(input), false); + return filter->WrapWithGPUColorFilter(std::move(input), false); }; break; } diff --git a/impeller/entity/contents/conical_gradient_contents.cc b/impeller/entity/contents/conical_gradient_contents.cc index c40fcff86f6f5..ee979eaec6c00 100644 --- a/impeller/entity/contents/conical_gradient_contents.cc +++ b/impeller/entity/contents/conical_gradient_contents.cc @@ -70,6 +70,7 @@ bool ConicalGradientContents::RenderSSBO(const ContentContext& renderer, frag_info.center = center_; frag_info.radius = radius_; frag_info.tile_mode = static_cast(tile_mode_); + frag_info.decal_border_color = decal_border_color_; frag_info.alpha = GetOpacity(); if (focus_) { frag_info.focus = focus_.value(); @@ -140,6 +141,7 @@ bool ConicalGradientContents::RenderTexture(const ContentContext& renderer, frag_info.center = center_; frag_info.radius = radius_; frag_info.tile_mode = static_cast(tile_mode_); + frag_info.decal_border_color = decal_border_color_; frag_info.texture_sampler_y_coord_scale = gradient_texture->GetYCoordScale(); frag_info.alpha = GetOpacity(); frag_info.half_texel = Vector2(0.5 / gradient_texture->GetSize().width, @@ -193,4 +195,13 @@ bool ConicalGradientContents::RenderTexture(const ContentContext& renderer, return true; } +bool ConicalGradientContents::ApplyColorFilter( + const ColorFilterProc& color_filter_proc) { + for (Color& color : colors_) { + color = color_filter_proc(color); + } + decal_border_color_ = color_filter_proc(decal_border_color_); + return true; +} + } // namespace impeller diff --git a/impeller/entity/contents/conical_gradient_contents.h b/impeller/entity/contents/conical_gradient_contents.h index d46ae86a0eabf..f9701a5a445b3 100644 --- a/impeller/entity/contents/conical_gradient_contents.h +++ b/impeller/entity/contents/conical_gradient_contents.h @@ -29,6 +29,10 @@ class ConicalGradientContents final : public ColorSourceContents { const Entity& entity, RenderPass& pass) const override; + // |Contents| + [[nodiscard]] bool ApplyColorFilter( + const ColorFilterProc& color_filter_proc) override; + void SetCenterAndRadius(Point center, Scalar radius); void SetColors(std::vector colors); @@ -56,6 +60,7 @@ class ConicalGradientContents final : public ColorSourceContents { std::vector colors_; std::vector stops_; Entity::TileMode tile_mode_; + Color decal_border_color_ = Color::BlackTransparent(); std::optional focus_; Scalar focus_radius_ = 0.0f; diff --git a/impeller/entity/contents/contents.cc b/impeller/entity/contents/contents.cc index 28abe929732ed..85e70e534e061 100644 --- a/impeller/entity/contents/contents.cc +++ b/impeller/entity/contents/contents.cc @@ -123,6 +123,11 @@ std::optional Contents::AsBackgroundColor(const Entity& entity, return {}; } +bool Contents::ApplyColorFilter( + const Contents::ColorFilterProc& color_filter_proc) { + return false; +} + bool Contents::ShouldRender(const Entity& entity, const std::optional& stencil_coverage) const { if (!stencil_coverage.has_value()) { diff --git a/impeller/entity/contents/contents.h b/impeller/entity/contents/contents.h index afbd259d1849d..461d1d3cfe5be 100644 --- a/impeller/entity/contents/contents.h +++ b/impeller/entity/contents/contents.h @@ -30,6 +30,10 @@ ContentContextOptions OptionsFromPassAndEntity(const RenderPass& pass, class Contents { public: + /// A procedure that filters a given unpremultiplied color to produce a new + /// unpremultiplied color. + using ColorFilterProc = std::function; + struct StencilCoverage { enum class Type { kNoChange, kAppend, kRestore }; @@ -126,6 +130,23 @@ class Contents { virtual std::optional AsBackgroundColor(const Entity& entity, ISize target_size) const; + /// @brief If possible, applies a color filter to this contents inputs on + /// the CPU. + /// + /// This method will either fully apply the color filter or + /// perform no action. Partial/incorrect application of the color + /// filter will never occur. + /// + /// @param[in] color_filter_proc A function that filters a given + /// unpremultiplied color to produce a new + /// unpremultiplied color. + /// + /// @return True if the color filter was able to be fully applied to all + /// all relevant inputs. Otherwise, this operation is a no-op and + /// false is returned. + [[nodiscard]] virtual bool ApplyColorFilter( + const ColorFilterProc& color_filter_proc); + private: std::optional coverage_hint_; std::optional color_source_size_; diff --git a/impeller/entity/contents/linear_gradient_contents.cc b/impeller/entity/contents/linear_gradient_contents.cc index 9660304e7ab75..f1542919bb487 100644 --- a/impeller/entity/contents/linear_gradient_contents.cc +++ b/impeller/entity/contents/linear_gradient_contents.cc @@ -82,6 +82,7 @@ bool LinearGradientContents::RenderTexture(const ContentContext& renderer, frag_info.start_point = start_point_; frag_info.end_point = end_point_; frag_info.tile_mode = static_cast(tile_mode_); + frag_info.decal_border_color = decal_border_color_; frag_info.texture_sampler_y_coord_scale = gradient_texture->GetYCoordScale(); frag_info.alpha = GetOpacity(); frag_info.half_texel = Vector2(0.5 / gradient_texture->GetSize().width, @@ -138,6 +139,7 @@ bool LinearGradientContents::RenderSSBO(const ContentContext& renderer, frag_info.start_point = start_point_; frag_info.end_point = end_point_; frag_info.tile_mode = static_cast(tile_mode_); + frag_info.decal_border_color = decal_border_color_; frag_info.alpha = GetOpacity(); auto& host_buffer = pass.GetTransientsBuffer(); @@ -184,4 +186,13 @@ bool LinearGradientContents::RenderSSBO(const ContentContext& renderer, return true; } +bool LinearGradientContents::ApplyColorFilter( + const ColorFilterProc& color_filter_proc) { + for (Color& color : colors_) { + color = color_filter_proc(color); + } + decal_border_color_ = color_filter_proc(decal_border_color_); + return true; +} + } // namespace impeller diff --git a/impeller/entity/contents/linear_gradient_contents.h b/impeller/entity/contents/linear_gradient_contents.h index 93d10eaf2591b..58509f709af9e 100644 --- a/impeller/entity/contents/linear_gradient_contents.h +++ b/impeller/entity/contents/linear_gradient_contents.h @@ -33,6 +33,10 @@ class LinearGradientContents final : public ColorSourceContents { const Entity& entity, RenderPass& pass) const override; + // |Contents| + [[nodiscard]] bool ApplyColorFilter( + const ColorFilterProc& color_filter_proc) override; + void SetEndPoints(Point start_point, Point end_point); void SetColors(std::vector colors); @@ -59,6 +63,7 @@ class LinearGradientContents final : public ColorSourceContents { std::vector colors_; std::vector stops_; Entity::TileMode tile_mode_; + Color decal_border_color_ = Color::BlackTransparent(); FML_DISALLOW_COPY_AND_ASSIGN(LinearGradientContents); }; diff --git a/impeller/entity/contents/radial_gradient_contents.cc b/impeller/entity/contents/radial_gradient_contents.cc index 48185e87f3249..9da36e6beb31c 100644 --- a/impeller/entity/contents/radial_gradient_contents.cc +++ b/impeller/entity/contents/radial_gradient_contents.cc @@ -76,6 +76,7 @@ bool RadialGradientContents::RenderSSBO(const ContentContext& renderer, frag_info.center = center_; frag_info.radius = radius_; frag_info.tile_mode = static_cast(tile_mode_); + frag_info.decal_border_color = decal_border_color_; frag_info.alpha = GetOpacity(); auto& host_buffer = pass.GetTransientsBuffer(); @@ -139,6 +140,7 @@ bool RadialGradientContents::RenderTexture(const ContentContext& renderer, frag_info.center = center_; frag_info.radius = radius_; frag_info.tile_mode = static_cast(tile_mode_); + frag_info.decal_border_color = decal_border_color_; frag_info.texture_sampler_y_coord_scale = gradient_texture->GetYCoordScale(); frag_info.alpha = GetOpacity(); frag_info.half_texel = Vector2(0.5 / gradient_texture->GetSize().width, @@ -185,4 +187,13 @@ bool RadialGradientContents::RenderTexture(const ContentContext& renderer, return true; } +bool RadialGradientContents::ApplyColorFilter( + const ColorFilterProc& color_filter_proc) { + for (Color& color : colors_) { + color = color_filter_proc(color); + } + decal_border_color_ = color_filter_proc(decal_border_color_); + return true; +} + } // namespace impeller diff --git a/impeller/entity/contents/radial_gradient_contents.h b/impeller/entity/contents/radial_gradient_contents.h index 1114b26dfe6db..5db915289c2a1 100644 --- a/impeller/entity/contents/radial_gradient_contents.h +++ b/impeller/entity/contents/radial_gradient_contents.h @@ -32,6 +32,10 @@ class RadialGradientContents final : public ColorSourceContents { const Entity& entity, RenderPass& pass) const override; + // |Contents| + [[nodiscard]] bool ApplyColorFilter( + const ColorFilterProc& color_filter_proc) override; + void SetCenterAndRadius(Point center, Scalar radius); void SetColors(std::vector colors); @@ -57,6 +61,7 @@ class RadialGradientContents final : public ColorSourceContents { std::vector colors_; std::vector stops_; Entity::TileMode tile_mode_; + Color decal_border_color_ = Color::BlackTransparent(); FML_DISALLOW_COPY_AND_ASSIGN(RadialGradientContents); }; diff --git a/impeller/entity/contents/solid_color_contents.cc b/impeller/entity/contents/solid_color_contents.cc index 91cf094f0d5b2..f0392e7df995d 100644 --- a/impeller/entity/contents/solid_color_contents.cc +++ b/impeller/entity/contents/solid_color_contents.cc @@ -110,4 +110,10 @@ std::optional SolidColorContents::AsBackgroundColor( : std::optional(); } +bool SolidColorContents::ApplyColorFilter( + const ColorFilterProc& color_filter_proc) { + color_ = color_filter_proc(color_); + return true; +} + } // namespace impeller diff --git a/impeller/entity/contents/solid_color_contents.h b/impeller/entity/contents/solid_color_contents.h index 497d08d49f86f..6af6489c13902 100644 --- a/impeller/entity/contents/solid_color_contents.h +++ b/impeller/entity/contents/solid_color_contents.h @@ -52,6 +52,10 @@ class SolidColorContents final : public ColorSourceContents { std::optional AsBackgroundColor(const Entity& entity, ISize target_size) const override; + // |Contents| + [[nodiscard]] bool ApplyColorFilter( + const ColorFilterProc& color_filter_proc) override; + private: Color color_; diff --git a/impeller/entity/contents/sweep_gradient_contents.cc b/impeller/entity/contents/sweep_gradient_contents.cc index 1979d612ab743..951ff4f3ad60e 100644 --- a/impeller/entity/contents/sweep_gradient_contents.cc +++ b/impeller/entity/contents/sweep_gradient_contents.cc @@ -82,6 +82,7 @@ bool SweepGradientContents::RenderSSBO(const ContentContext& renderer, frag_info.bias = bias_; frag_info.scale = scale_; frag_info.tile_mode = static_cast(tile_mode_); + frag_info.decal_border_color = decal_border_color_; frag_info.alpha = GetOpacity(); auto& host_buffer = pass.GetTransientsBuffer(); @@ -147,6 +148,7 @@ bool SweepGradientContents::RenderTexture(const ContentContext& renderer, frag_info.scale = scale_; frag_info.texture_sampler_y_coord_scale = gradient_texture->GetYCoordScale(); frag_info.tile_mode = static_cast(tile_mode_); + frag_info.decal_border_color = decal_border_color_; frag_info.alpha = GetOpacity(); frag_info.half_texel = Vector2(0.5 / gradient_texture->GetSize().width, 0.5 / gradient_texture->GetSize().height); @@ -192,4 +194,13 @@ bool SweepGradientContents::RenderTexture(const ContentContext& renderer, return true; } +bool SweepGradientContents::ApplyColorFilter( + const ColorFilterProc& color_filter_proc) { + for (Color& color : colors_) { + color = color_filter_proc(color); + } + decal_border_color_ = color_filter_proc(decal_border_color_); + return true; +} + } // namespace impeller diff --git a/impeller/entity/contents/sweep_gradient_contents.h b/impeller/entity/contents/sweep_gradient_contents.h index ed776dee9d617..bb86437dbb58d 100644 --- a/impeller/entity/contents/sweep_gradient_contents.h +++ b/impeller/entity/contents/sweep_gradient_contents.h @@ -33,6 +33,10 @@ class SweepGradientContents final : public ColorSourceContents { const Entity& entity, RenderPass& pass) const override; + // |Contents| + [[nodiscard]] bool ApplyColorFilter( + const ColorFilterProc& color_filter_proc) override; + void SetCenterAndAngles(Point center, Degrees start_angle, Degrees end_angle); void SetColors(std::vector colors); @@ -60,6 +64,7 @@ class SweepGradientContents final : public ColorSourceContents { std::vector colors_; std::vector stops_; Entity::TileMode tile_mode_; + Color decal_border_color_ = Color::BlackTransparent(); FML_DISALLOW_COPY_AND_ASSIGN(SweepGradientContents); }; diff --git a/impeller/entity/shaders/conical_gradient_fill.frag b/impeller/entity/shaders/conical_gradient_fill.frag index 41d4e05b2c869..159b95c1c3ccc 100644 --- a/impeller/entity/shaders/conical_gradient_fill.frag +++ b/impeller/entity/shaders/conical_gradient_fill.frag @@ -4,6 +4,7 @@ precision mediump float; +#include #include #include #include @@ -14,6 +15,7 @@ uniform FragInfo { highp vec2 center; float radius; float tile_mode; + vec4 decal_border_color; float texture_sampler_y_coord_scale; float alpha; vec2 half_texel; @@ -35,9 +37,12 @@ void main() { } float t = res.x; - frag_color = IPSampleLinearWithTileMode( - texture_sampler, vec2(t, 0.5), frag_info.texture_sampler_y_coord_scale, - frag_info.half_texel, frag_info.tile_mode); frag_color = - vec4(frag_color.xyz * frag_color.a, frag_color.a) * frag_info.alpha; + IPSampleLinearWithTileMode(texture_sampler, // + vec2(t, 0.5), // + frag_info.texture_sampler_y_coord_scale, // + frag_info.half_texel, // + frag_info.tile_mode, // + frag_info.decal_border_color); + frag_color = IPPremultiply(frag_color) * frag_info.alpha; } diff --git a/impeller/entity/shaders/conical_gradient_ssbo_fill.frag b/impeller/entity/shaders/conical_gradient_ssbo_fill.frag index 3363efa36f58c..63c86698bf52e 100644 --- a/impeller/entity/shaders/conical_gradient_ssbo_fill.frag +++ b/impeller/entity/shaders/conical_gradient_ssbo_fill.frag @@ -4,6 +4,7 @@ precision mediump float; +#include #include #include #include @@ -22,6 +23,7 @@ uniform FragInfo { highp vec2 center; float radius; float tile_mode; + vec4 decal_border_color; float alpha; int colors_length; vec2 focus; @@ -36,33 +38,29 @@ out vec4 frag_color; void main() { vec2 res = IPComputeConicalT(frag_info.focus, frag_info.focus_radius, frag_info.center, frag_info.radius, v_position); - if (res.y < 0.0) { - frag_color = vec4(0); - return; - } float t = res.x; - if ((t < 0.0 || t > 1.0) && frag_info.tile_mode == kTileModeDecal) { - frag_color = vec4(0); - return; - } - t = IPFloatTile(t, frag_info.tile_mode); - vec4 result_color = vec4(0); - for (int i = 1; i < frag_info.colors_length; i++) { - ColorPoint prev_point = color_data.colors[i - 1]; - ColorPoint current_point = color_data.colors[i]; - if (t >= prev_point.stop && t <= current_point.stop) { - float delta = (current_point.stop - prev_point.stop); - if (delta < 0.001) { - result_color = current_point.color; - } else { - float ratio = (t - prev_point.stop) / delta; - result_color = mix(prev_point.color, current_point.color, ratio); + if (res.y < 0.0 || + ((t < 0.0 || t > 1.0) && frag_info.tile_mode == kTileModeDecal)) { + result_color = frag_info.decal_border_color; + } else { + t = IPFloatTile(t, frag_info.tile_mode); + + for (int i = 1; i < frag_info.colors_length; i++) { + ColorPoint prev_point = color_data.colors[i - 1]; + ColorPoint current_point = color_data.colors[i]; + if (t >= prev_point.stop && t <= current_point.stop) { + float delta = (current_point.stop - prev_point.stop); + if (delta < 0.001) { + result_color = current_point.color; + } else { + float ratio = (t - prev_point.stop) / delta; + result_color = mix(prev_point.color, current_point.color, ratio); + } + break; } - break; } } - frag_color = - vec4(result_color.xyz * result_color.a, result_color.a) * frag_info.alpha; + frag_color = IPPremultiply(result_color) * frag_info.alpha; } diff --git a/impeller/entity/shaders/linear_gradient_fill.frag b/impeller/entity/shaders/linear_gradient_fill.frag index ea52286701c52..4d9a90f896464 100644 --- a/impeller/entity/shaders/linear_gradient_fill.frag +++ b/impeller/entity/shaders/linear_gradient_fill.frag @@ -4,6 +4,7 @@ precision mediump float; +#include #include #include @@ -13,6 +14,7 @@ uniform FragInfo { highp vec2 start_point; highp vec2 end_point; float tile_mode; + vec4 decal_border_color; float texture_sampler_y_coord_scale; float alpha; vec2 half_texel; @@ -28,9 +30,12 @@ void main() { vec2 start_to_position = v_position - frag_info.start_point; float t = dot(start_to_position, start_to_end) / dot(start_to_end, start_to_end); - frag_color = IPSampleLinearWithTileMode( - texture_sampler, vec2(t, 0.5), frag_info.texture_sampler_y_coord_scale, - frag_info.half_texel, frag_info.tile_mode); frag_color = - vec4(frag_color.xyz * frag_color.a, frag_color.a) * frag_info.alpha; + IPSampleLinearWithTileMode(texture_sampler, // + vec2(t, 0.5), // + frag_info.texture_sampler_y_coord_scale, // + frag_info.half_texel, // + frag_info.tile_mode, // + frag_info.decal_border_color); + frag_color = IPPremultiply(frag_color) * frag_info.alpha; } diff --git a/impeller/entity/shaders/linear_gradient_ssbo_fill.frag b/impeller/entity/shaders/linear_gradient_ssbo_fill.frag index 03bd5082d1c92..2b70fc8845d98 100644 --- a/impeller/entity/shaders/linear_gradient_ssbo_fill.frag +++ b/impeller/entity/shaders/linear_gradient_ssbo_fill.frag @@ -4,6 +4,7 @@ precision mediump float; +#include #include #include @@ -22,6 +23,7 @@ uniform FragInfo { highp vec2 end_point; float alpha; float tile_mode; + vec4 decal_border_color; int colors_length; } frag_info; @@ -37,25 +39,24 @@ void main() { dot(start_to_position, start_to_end) / dot(start_to_end, start_to_end); if ((t < 0.0 || t > 1.0) && frag_info.tile_mode == kTileModeDecal) { - frag_color = vec4(0); - return; - } - t = IPFloatTile(t, frag_info.tile_mode); - - for (int i = 1; i < frag_info.colors_length; i++) { - ColorPoint prev_point = color_data.colors[i - 1]; - ColorPoint current_point = color_data.colors[i]; - if (t >= prev_point.stop && t <= current_point.stop) { - float delta = (current_point.stop - prev_point.stop); - if (delta < 0.001) { - frag_color = current_point.color; - } else { - float ratio = (t - prev_point.stop) / delta; - frag_color = mix(prev_point.color, current_point.color, ratio); + frag_color = frag_info.decal_border_color; + } else { + t = IPFloatTile(t, frag_info.tile_mode); + + for (int i = 1; i < frag_info.colors_length; i++) { + ColorPoint prev_point = color_data.colors[i - 1]; + ColorPoint current_point = color_data.colors[i]; + if (t >= prev_point.stop && t <= current_point.stop) { + float delta = (current_point.stop - prev_point.stop); + if (delta < 0.001) { + frag_color = current_point.color; + } else { + float ratio = (t - prev_point.stop) / delta; + frag_color = mix(prev_point.color, current_point.color, ratio); + } + break; } - break; } } - frag_color = - vec4(frag_color.xyz * frag_color.a, frag_color.a) * frag_info.alpha; + frag_color = IPPremultiply(frag_color) * frag_info.alpha; } diff --git a/impeller/entity/shaders/radial_gradient_fill.frag b/impeller/entity/shaders/radial_gradient_fill.frag index 6524d4501550d..c1bbe80af7e06 100644 --- a/impeller/entity/shaders/radial_gradient_fill.frag +++ b/impeller/entity/shaders/radial_gradient_fill.frag @@ -4,6 +4,7 @@ precision mediump float; +#include #include #include @@ -13,6 +14,7 @@ uniform FragInfo { highp vec2 center; float radius; float tile_mode; + vec4 decal_border_color; float texture_sampler_y_coord_scale; float alpha; vec2 half_texel; @@ -26,9 +28,12 @@ out vec4 frag_color; void main() { float len = length(v_position - frag_info.center); float t = len / frag_info.radius; - frag_color = IPSampleLinearWithTileMode( - texture_sampler, vec2(t, 0.5), frag_info.texture_sampler_y_coord_scale, - frag_info.half_texel, frag_info.tile_mode); frag_color = - vec4(frag_color.xyz * frag_color.a, frag_color.a) * frag_info.alpha; + IPSampleLinearWithTileMode(texture_sampler, // + vec2(t, 0.5), // + frag_info.texture_sampler_y_coord_scale, // + frag_info.half_texel, // + frag_info.tile_mode, // + frag_info.decal_border_color); + frag_color = IPPremultiply(frag_color) * frag_info.alpha; } diff --git a/impeller/entity/shaders/radial_gradient_ssbo_fill.frag b/impeller/entity/shaders/radial_gradient_ssbo_fill.frag index d95b282ee78c8..aab3cf3e2e854 100644 --- a/impeller/entity/shaders/radial_gradient_ssbo_fill.frag +++ b/impeller/entity/shaders/radial_gradient_ssbo_fill.frag @@ -4,6 +4,7 @@ precision mediump float; +#include #include #include #include @@ -22,6 +23,7 @@ uniform FragInfo { highp vec2 center; float radius; float tile_mode; + vec4 decal_border_color; float alpha; int colors_length; } @@ -35,27 +37,26 @@ void main() { float len = length(v_position - frag_info.center); float t = len / frag_info.radius; - if ((t < 0.0 || t > 1.0) && frag_info.tile_mode == kTileModeDecal) { - frag_color = vec4(0); - return; - } - t = IPFloatTile(t, frag_info.tile_mode); - vec4 result_color = vec4(0); - for (int i = 1; i < frag_info.colors_length; i++) { - ColorPoint prev_point = color_data.colors[i - 1]; - ColorPoint current_point = color_data.colors[i]; - if (t >= prev_point.stop && t <= current_point.stop) { - float delta = (current_point.stop - prev_point.stop); - if (delta < 0.001) { - result_color = current_point.color; - } else { - float ratio = (t - prev_point.stop) / delta; - result_color = mix(prev_point.color, current_point.color, ratio); + if ((t < 0.0 || t > 1.0) && frag_info.tile_mode == kTileModeDecal) { + result_color = frag_info.decal_border_color; + } else { + t = IPFloatTile(t, frag_info.tile_mode); + + for (int i = 1; i < frag_info.colors_length; i++) { + ColorPoint prev_point = color_data.colors[i - 1]; + ColorPoint current_point = color_data.colors[i]; + if (t >= prev_point.stop && t <= current_point.stop) { + float delta = (current_point.stop - prev_point.stop); + if (delta < 0.001) { + result_color = current_point.color; + } else { + float ratio = (t - prev_point.stop) / delta; + result_color = mix(prev_point.color, current_point.color, ratio); + } + break; } - break; } } - frag_color = - vec4(result_color.xyz * result_color.a, result_color.a) * frag_info.alpha; + frag_color = IPPremultiply(result_color) * frag_info.alpha; } diff --git a/impeller/entity/shaders/sweep_gradient_fill.frag b/impeller/entity/shaders/sweep_gradient_fill.frag index 6c483ba5d5a0f..fba9e2b4586e2 100644 --- a/impeller/entity/shaders/sweep_gradient_fill.frag +++ b/impeller/entity/shaders/sweep_gradient_fill.frag @@ -4,6 +4,7 @@ precision mediump float; +#include #include #include #include @@ -15,6 +16,7 @@ uniform FragInfo { float bias; float scale; float tile_mode; + vec4 decal_border_color; float texture_sampler_y_coord_scale; float alpha; vec2 half_texel; @@ -30,9 +32,12 @@ void main() { float angle = atan(-coord.y, -coord.x); float t = (angle * k1Over2Pi + 0.5 + frag_info.bias) * frag_info.scale; - frag_color = IPSampleLinearWithTileMode( - texture_sampler, vec2(t, 0.5), frag_info.texture_sampler_y_coord_scale, - frag_info.half_texel, frag_info.tile_mode); frag_color = - vec4(frag_color.xyz * frag_color.a, frag_color.a) * frag_info.alpha; + IPSampleLinearWithTileMode(texture_sampler, // + vec2(t, 0.5), // + frag_info.texture_sampler_y_coord_scale, // + frag_info.half_texel, // + frag_info.tile_mode, // + frag_info.decal_border_color); + frag_color = IPPremultiply(frag_color) * frag_info.alpha; } diff --git a/impeller/entity/shaders/sweep_gradient_ssbo_fill.frag b/impeller/entity/shaders/sweep_gradient_ssbo_fill.frag index 9e9f3e9748417..cdc7347c4fa2d 100644 --- a/impeller/entity/shaders/sweep_gradient_ssbo_fill.frag +++ b/impeller/entity/shaders/sweep_gradient_ssbo_fill.frag @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include #include #include #include @@ -22,6 +23,7 @@ uniform FragInfo { float bias; float scale; float tile_mode; + vec4 decal_border_color; float alpha; int colors_length; } @@ -36,27 +38,26 @@ void main() { float angle = atan(-coord.y, -coord.x); float t = (angle * k1Over2Pi + 0.5 + frag_info.bias) * frag_info.scale; - if ((t < 0.0 || t > 1.0) && frag_info.tile_mode == kTileModeDecal) { - frag_color = vec4(0); - return; - } - t = IPFloatTile(t, frag_info.tile_mode); - vec4 result_color = vec4(0); - for (int i = 1; i < frag_info.colors_length; i++) { - ColorPoint prev_point = color_data.colors[i - 1]; - ColorPoint current_point = color_data.colors[i]; - if (t >= prev_point.stop && t <= current_point.stop) { - float delta = (current_point.stop - prev_point.stop); - if (delta < 0.001) { - result_color = current_point.color; - } else { - float ratio = (t - prev_point.stop) / delta; - result_color = mix(prev_point.color, current_point.color, ratio); + if ((t < 0.0 || t > 1.0) && frag_info.tile_mode == kTileModeDecal) { + result_color = frag_info.decal_border_color; + } else { + t = IPFloatTile(t, frag_info.tile_mode); + + for (int i = 1; i < frag_info.colors_length; i++) { + ColorPoint prev_point = color_data.colors[i - 1]; + ColorPoint current_point = color_data.colors[i]; + if (t >= prev_point.stop && t <= current_point.stop) { + float delta = (current_point.stop - prev_point.stop); + if (delta < 0.001) { + result_color = current_point.color; + } else { + float ratio = (t - prev_point.stop) / delta; + result_color = mix(prev_point.color, current_point.color, ratio); + } + break; } - break; } } - frag_color = - vec4(result_color.xyz * result_color.a, result_color.a) * frag_info.alpha; + frag_color = IPPremultiply(result_color) * frag_info.alpha; } diff --git a/impeller/tools/malioc.json b/impeller/tools/malioc.json index af1046a384828..edbfb7f89f619 100644 --- a/impeller/tools/malioc.json +++ b/impeller/tools/malioc.json @@ -1921,9 +1921,9 @@ "arith_cvt" ], "longest_path_cycles": [ - 0.824999988079071, + 0.800000011920929, 0.328125, - 0.824999988079071, + 0.800000011920929, 0.0625, 0.0, 0.25, @@ -1955,9 +1955,9 @@ "arith_cvt" ], "total_cycles": [ - 1.125, + 1.1375000476837158, 0.699999988079071, - 1.125, + 1.1375000476837158, 0.3125, 0.0, 0.25, @@ -1966,7 +1966,7 @@ }, "stack_spill_bytes": 0, "thread_occupancy": 100, - "uniform_registers_used": 38, + "uniform_registers_used": 40, "work_registers_used": 9 } } @@ -1985,7 +1985,7 @@ "uses_late_zs_update": false, "variants": { "Main": { - "fp16_arithmetic": 67, + "fp16_arithmetic": 68, "has_stack_spilling": false, "performance": { "longest_path_bound_pipelines": [ @@ -2013,9 +2013,9 @@ "varying" ], "shortest_path_cycles": [ - 0.15625, - 0.0, - 0.15625, + 0.1875, + 0.0625, + 0.1875, 0.0, 0.0, 0.25, @@ -2025,9 +2025,9 @@ "load_store" ], "total_cycles": [ - 1.375, - 0.800000011920929, - 1.375, + 1.3125, + 0.8125, + 1.3125, 0.375, 4.0, 0.25, @@ -2036,7 +2036,7 @@ }, "stack_spill_bytes": 0, "thread_occupancy": 100, - "uniform_registers_used": 34, + "uniform_registers_used": 36, "work_registers_used": 15 } } @@ -6652,9 +6652,9 @@ "arith_cvt" ], "longest_path_cycles": [ - 0.925000011920929, + 0.887499988079071, 0.4375, - 0.925000011920929, + 0.887499988079071, 0.1875, 0.0, 0.25, @@ -6686,9 +6686,9 @@ "arith_cvt" ], "total_cycles": [ - 1.2625000476837158, + 1.28125, 0.824999988079071, - 1.2625000476837158, + 1.28125, 0.4375, 0.0, 0.25, @@ -6697,7 +6697,7 @@ }, "stack_spill_bytes": 0, "thread_occupancy": 100, - "uniform_registers_used": 38, + "uniform_registers_used": 40, "work_registers_used": 21 } } @@ -7958,9 +7958,9 @@ "arith_cvt" ], "longest_path_cycles": [ - 0.421875, + 0.390625, 0.296875, - 0.421875, + 0.390625, 0.125, 0.0, 0.25, @@ -7979,9 +7979,9 @@ "varying" ], "shortest_path_cycles": [ + 0.203125, 0.1875, - 0.1875, - 0.1875, + 0.203125, 0.125, 0.0, 0.25, @@ -7992,9 +7992,9 @@ "arith_cvt" ], "total_cycles": [ - 0.5, + 0.515625, 0.328125, - 0.5, + 0.515625, 0.125, 0.0, 0.25, @@ -8003,7 +8003,7 @@ }, "stack_spill_bytes": 0, "thread_occupancy": 100, - "uniform_registers_used": 14, + "uniform_registers_used": 16, "work_registers_used": 20 } } @@ -8902,9 +8902,9 @@ "arith_cvt" ], "longest_path_cycles": [ - 0.453125, + 0.421875, 0.3125, - 0.453125, + 0.421875, 0.1875, 0.0, 0.25, @@ -8923,9 +8923,9 @@ "varying" ], "shortest_path_cycles": [ - 0.21875, + 0.234375, 0.203125, - 0.21875, + 0.234375, 0.1875, 0.0, 0.25, @@ -8936,9 +8936,9 @@ "arith_cvt" ], "total_cycles": [ - 0.53125, + 0.546875, 0.34375, - 0.53125, + 0.546875, 0.1875, 0.0, 0.25, @@ -8947,7 +8947,7 @@ }, "stack_spill_bytes": 0, "thread_occupancy": 100, - "uniform_registers_used": 12, + "uniform_registers_used": 14, "work_registers_used": 20 } } @@ -9946,12 +9946,13 @@ "performance": { "longest_path_bound_pipelines": [ "arith_total", + "arith_fma", "arith_cvt" ], "longest_path_cycles": [ - 0.484375, 0.46875, - 0.484375, + 0.46875, + 0.46875, 0.375, 0.0, 0.25, @@ -9995,7 +9996,7 @@ }, "stack_spill_bytes": 0, "thread_occupancy": 100, - "uniform_registers_used": 18, + "uniform_registers_used": 20, "work_registers_used": 24 } } @@ -11221,12 +11222,14 @@ "performance": { "longest_path_bound_pipelines": [ "arith_total", - "arith_cvt" + "arith_cvt", + "varying", + "texture" ], "longest_path_cycles": [ - 0.28125, + 0.25, 0.234375, - 0.28125, + 0.25, 0.0, 0.0, 0.25, @@ -11247,7 +11250,7 @@ "shortest_path_cycles": [ 0.140625, 0.140625, - 0.125, + 0.140625, 0.0, 0.0, 0.25, @@ -11258,9 +11261,9 @@ "arith_cvt" ], "total_cycles": [ - 0.328125, + 0.34375, 0.265625, - 0.328125, + 0.34375, 0.0, 0.0, 0.25, @@ -11269,7 +11272,7 @@ }, "stack_spill_bytes": 0, "thread_occupancy": 100, - "uniform_registers_used": 14, + "uniform_registers_used": 16, "work_registers_used": 7 } } @@ -11316,9 +11319,9 @@ "varying" ], "shortest_path_cycles": [ - 0.125, - 0.09375, - 0.125, + 0.15625, + 0.15625, + 0.15625, 0.0625, 0.0, 0.25, @@ -11328,9 +11331,9 @@ "load_store" ], "total_cycles": [ - 0.65625, + 0.637499988079071, 0.375, - 0.65625, + 0.637499988079071, 0.125, 4.0, 0.25, @@ -11339,7 +11342,7 @@ }, "stack_spill_bytes": 0, "thread_occupancy": 100, - "uniform_registers_used": 14, + "uniform_registers_used": 16, "work_registers_used": 15 } } @@ -11983,9 +11986,9 @@ "arith_cvt" ], "longest_path_cycles": [ - 0.3125, + 0.28125, 0.25, - 0.3125, + 0.28125, 0.0625, 0.0, 0.25, @@ -12004,9 +12007,9 @@ "varying" ], "shortest_path_cycles": [ + 0.171875, 0.15625, - 0.15625, - 0.15625, + 0.171875, 0.0625, 0.0, 0.25, @@ -12017,9 +12020,9 @@ "arith_cvt" ], "total_cycles": [ - 0.359375, + 0.375, 0.28125, - 0.359375, + 0.375, 0.0625, 0.0, 0.25, @@ -12028,7 +12031,7 @@ }, "stack_spill_bytes": 0, "thread_occupancy": 100, - "uniform_registers_used": 14, + "uniform_registers_used": 16, "work_registers_used": 7 } } @@ -12075,9 +12078,9 @@ "varying" ], "shortest_path_cycles": [ - 0.140625, - 0.109375, - 0.140625, + 0.171875, + 0.171875, + 0.171875, 0.0625, 0.0, 0.25, @@ -12087,9 +12090,9 @@ "load_store" ], "total_cycles": [ - 0.625, + 0.609375, 0.375, - 0.625, + 0.609375, 0.125, 4.0, 0.25, @@ -12098,7 +12101,7 @@ }, "stack_spill_bytes": 0, "thread_occupancy": 100, - "uniform_registers_used": 12, + "uniform_registers_used": 14, "work_registers_used": 15 } } @@ -12742,7 +12745,7 @@ "longest_path_cycles": [ 0.421875, 0.421875, - 0.34375, + 0.3125, 0.25, 0.0, 0.25, @@ -12764,7 +12767,7 @@ "shortest_path_cycles": [ 0.328125, 0.328125, - 0.1875, + 0.203125, 0.25, 0.0, 0.25, @@ -12777,7 +12780,7 @@ "total_cycles": [ 0.453125, 0.453125, - 0.390625, + 0.40625, 0.25, 0.0, 0.25, @@ -12786,8 +12789,8 @@ }, "stack_spill_bytes": 0, "thread_occupancy": 100, - "uniform_registers_used": 18, - "work_registers_used": 17 + "uniform_registers_used": 20, + "work_registers_used": 13 } } } @@ -12831,12 +12834,12 @@ ], "shortest_path_bound_pipelines": [ "arith_total", - "arith_sfu" + "arith_fma" ], "shortest_path_cycles": [ - 0.3125, - 0.28125, - 0.21875, + 0.390625, + 0.390625, + 0.25, 0.3125, 0.0, 0.25, @@ -12846,9 +12849,9 @@ "load_store" ], "total_cycles": [ - 0.699999988079071, + 0.6875, 0.65625, - 0.699999988079071, + 0.6875, 0.375, 4.0, 0.25, @@ -12857,7 +12860,7 @@ }, "stack_spill_bytes": 0, "thread_occupancy": 100, - "uniform_registers_used": 16, + "uniform_registers_used": 20, "work_registers_used": 19 } }