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
4 changes: 4 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -42528,6 +42528,8 @@ ORIGIN: ../../../flutter/impeller/entity/shaders/glyph_atlas.frag + ../../../flu
ORIGIN: ../../../flutter/impeller/entity/shaders/glyph_atlas.vert + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/gradients/conical_gradient_fill.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/gradients/conical_gradient_ssbo_fill.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/gradients/fast_gradient.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/gradients/fast_gradient.vert + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/gradients/gradient_fill.vert + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/gradients/linear_gradient_fill.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/gradients/linear_gradient_ssbo_fill.frag + ../../../flutter/LICENSE
Expand Down Expand Up @@ -45391,6 +45393,8 @@ FILE: ../../../flutter/impeller/entity/shaders/glyph_atlas.frag
FILE: ../../../flutter/impeller/entity/shaders/glyph_atlas.vert
FILE: ../../../flutter/impeller/entity/shaders/gradients/conical_gradient_fill.frag
FILE: ../../../flutter/impeller/entity/shaders/gradients/conical_gradient_ssbo_fill.frag
FILE: ../../../flutter/impeller/entity/shaders/gradients/fast_gradient.frag
FILE: ../../../flutter/impeller/entity/shaders/gradients/fast_gradient.vert
FILE: ../../../flutter/impeller/entity/shaders/gradients/gradient_fill.vert
FILE: ../../../flutter/impeller/entity/shaders/gradients/linear_gradient_fill.frag
FILE: ../../../flutter/impeller/entity/shaders/gradients/linear_gradient_ssbo_fill.frag
Expand Down
84 changes: 84 additions & 0 deletions impeller/aiks/aiks_gradient_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -734,5 +734,89 @@ TEST_P(AiksTest, GradientStrokesRenderCorrectly) {
ASSERT_TRUE(OpenPlaygroundHere(callback));
}

// Draws two gradients that should look identical (except that one is an RRECT).
TEST_P(AiksTest, FastGradientTestHorizontal) {
Canvas canvas;
Paint paint;
canvas.Translate({100.0f, 0, 0});

std::vector<Color> colors = {Color::Red(), Color::Blue(), Color::Green()};
std::vector<Scalar> stops = {0.0, 0.1, 1.0};

paint.color_source = ColorSource::MakeLinearGradient(
{0, 0}, {300, 0}, std::move(colors), std::move(stops),
Entity::TileMode::kClamp, {});

paint.color = Color(1.0, 1.0, 1.0, 1.0);
canvas.DrawRect(Rect::MakeXYWH(0, 0, 300, 300), paint);
canvas.Translate({400, 0, 0});
canvas.DrawRRect(Rect::MakeXYWH(0, 0, 300, 300), Size(4, 4), paint);

ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}

// Draws two gradients that should look identical (except that one is an RRECT).
TEST_P(AiksTest, FastGradientTestVertical) {
Canvas canvas;
Paint paint;
canvas.Translate({100.0f, 0, 0});

std::vector<Color> colors = {Color::Red(), Color::Blue(), Color::Green()};
std::vector<Scalar> stops = {0.0, 0.1, 1.0};

paint.color_source = ColorSource::MakeLinearGradient(
{0, 0}, {0, 300}, std::move(colors), std::move(stops),
Entity::TileMode::kClamp, {});

paint.color = Color(1.0, 1.0, 1.0, 1.0);
canvas.DrawRect(Rect::MakeXYWH(0, 0, 300, 300), paint);
canvas.Translate({400, 0, 0});
canvas.DrawRRect(Rect::MakeXYWH(0, 0, 300, 300), Size(4, 4), paint);

ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}

// Draws two gradients that should look identical (except that one is an RRECT).
TEST_P(AiksTest, FastGradientTestHorizontalReversed) {
Canvas canvas;
Paint paint;
canvas.Translate({100.0f, 0, 0});

std::vector<Color> colors = {Color::Red(), Color::Blue(), Color::Green()};
std::vector<Scalar> stops = {0.0, 0.1, 1.0};

paint.color_source = ColorSource::MakeLinearGradient(
{300, 0}, {0, 0}, std::move(colors), std::move(stops),
Entity::TileMode::kClamp, {});

paint.color = Color(1.0, 1.0, 1.0, 1.0);
canvas.DrawRect(Rect::MakeXYWH(0, 0, 300, 300), paint);
canvas.Translate({400, 0, 0});
canvas.DrawRRect(Rect::MakeXYWH(0, 0, 300, 300), Size(4, 4), paint);

ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}

// Draws two gradients that should look identical (except that one is an RRECT).
TEST_P(AiksTest, FastGradientTestVerticalReversed) {
Canvas canvas;
Paint paint;
canvas.Translate({100.0f, 0, 0});

std::vector<Color> colors = {Color::Red(), Color::Blue(), Color::Green()};
std::vector<Scalar> stops = {0.0, 0.1, 1.0};

paint.color_source = ColorSource::MakeLinearGradient(
{0, 300}, {0, 0}, std::move(colors), std::move(stops),
Entity::TileMode::kClamp, {});

paint.color = Color(1.0, 1.0, 1.0, 1.0);
canvas.DrawRect(Rect::MakeXYWH(0, 0, 300, 300), paint);
canvas.Translate({400, 0, 0});
canvas.DrawRRect(Rect::MakeXYWH(0, 0, 300, 300), Size(4, 4), paint);

ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}

} // namespace testing
} // namespace impeller
2 changes: 2 additions & 0 deletions impeller/entity/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ impeller_shaders("entity_shaders") {
"shaders/filters/linear_to_srgb_filter.frag",
"shaders/filters/morphology_filter.frag",
"shaders/blending/vertices_uber.frag",
"shaders/gradients/fast_gradient.vert",
"shaders/gradients/fast_gradient.frag",
]
}

Expand Down
1 change: 1 addition & 0 deletions impeller/entity/contents/content_context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ ContentContext::ContentContext(
{
solid_fill_pipelines_.CreateDefault(*context_, options);
texture_pipelines_.CreateDefault(*context_, options);
fast_gradient_pipelines_.CreateDefault(*context_, options);

if (context_->GetCapabilities()->SupportsSSBO()) {
linear_gradient_ssbo_fill_pipelines_.CreateDefault(*context_, options);
Expand Down
10 changes: 10 additions & 0 deletions impeller/entity/contents/content_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
#include "impeller/entity/clip.vert.h"
#include "impeller/entity/color_matrix_color_filter.frag.h"
#include "impeller/entity/conical_gradient_fill.frag.h"
#include "impeller/entity/fast_gradient.frag.h"
#include "impeller/entity/fast_gradient.vert.h"
#include "impeller/entity/filter_position.vert.h"
#include "impeller/entity/filter_position_uv.vert.h"
#include "impeller/entity/gaussian.frag.h"
Expand Down Expand Up @@ -76,6 +78,8 @@

namespace impeller {

using FastGradientPipeline =
RenderPipelineHandle<FastGradientVertexShader, FastGradientFragmentShader>;
using LinearGradientFillPipeline =
RenderPipelineHandle<GradientFillVertexShader,
LinearGradientFillFragmentShader>;
Expand Down Expand Up @@ -376,6 +380,11 @@ class ContentContext {

std::shared_ptr<Tessellator> GetTessellator() const;

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

std::shared_ptr<Pipeline<PipelineDescriptor>> GetLinearGradientFillPipeline(
ContentContextOptions opts) const {
return GetPipeline(linear_gradient_fill_pipelines_, opts);
Expand Down Expand Up @@ -856,6 +865,7 @@ class ContentContext {
// map.

mutable Variants<SolidFillPipeline> solid_fill_pipelines_;
mutable Variants<FastGradientPipeline> fast_gradient_pipelines_;
mutable Variants<LinearGradientFillPipeline> linear_gradient_fill_pipelines_;
mutable Variants<RadialGradientFillPipeline> radial_gradient_fill_pipelines_;
mutable Variants<ConicalGradientFillPipeline>
Expand Down
86 changes: 86 additions & 0 deletions impeller/entity/contents/linear_gradient_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "impeller/entity/contents/gradient_generator.h"
#include "impeller/entity/entity.h"
#include "impeller/renderer/render_pass.h"
#include "impeller/renderer/vertex_buffer_builder.h"

namespace impeller {

Expand Down Expand Up @@ -53,9 +54,94 @@ bool LinearGradientContents::IsOpaque() const {
return true;
}

// A much faster (in terms of ALU) linear gradient that uses vertex
// interpolation to perform all color computation. Requires that the geometry of
// the gradient is divided into regions based on the stop values.
// Currently restricted to rect geometry where the start and end points are
// perfectly horizontal/vertical, but could easily be expanded to StC cases
// provided that the start/end are on or outside of the coverage rect.
bool LinearGradientContents::FastLinearGradient(const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) const {
using VS = FastGradientPipeline::VertexShader;
using FS = FastGradientPipeline::FragmentShader;

auto options = OptionsFromPassAndEntity(pass, entity);
options.primitive_type = PrimitiveType::kTriangle;
Geometry& geometry = *GetGeometry();

// We already know this is an axis aligned rectangle, so the coverage will
// be approximately the same as the geometry. For non axis-algined rectangles,
// we can force stencil then cover (not done here). We give an identity
// transform to avoid double transforming the gradient.
std::optional<Rect> maybe_rect = geometry.GetCoverage(Matrix());
if (!maybe_rect.has_value()) {
return false;
}
Rect rect = maybe_rect.value();
bool horizontal_axis = start_point_.y == end_point_.y;

// Compute the locations of each breakpoint along the primary axis, then
// create a rectangle that joins each segment. There will be two triangles
// between each pair of points.
VertexBufferBuilder<VS::PerVertexData> vtx_builder;
vtx_builder.Reserve(6 * (stops_.size() - 1));
Point prev = start_point_;
for (auto i = 1u; i < stops_.size(); i++) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Beautiful.

Scalar t = stops_[i];
Point current = (1.0 - t) * start_point_ + t * end_point_;
Rect section = horizontal_axis
? Rect::MakeXYWH(prev.x, rect.GetY(), current.x - prev.x,
rect.GetHeight())

: Rect::MakeXYWH(rect.GetX(), prev.y, rect.GetWidth(),
current.y - prev.y);
vtx_builder.AddVertices({
{section.GetLeftTop(), colors_[i - 1]},
{section.GetRightTop(), horizontal_axis ? colors_[i] : colors_[i - 1]},
{section.GetLeftBottom(),
horizontal_axis ? colors_[i - 1] : colors_[i]},
{section.GetRightTop(), horizontal_axis ? colors_[i] : colors_[i - 1]},
{section.GetLeftBottom(),
horizontal_axis ? colors_[i - 1] : colors_[i]},
{section.GetRightBottom(), colors_[i]},
});
prev = current;
}
auto& host_buffer = renderer.GetTransientsBuffer();

pass.SetLabel("LinearGradient");
pass.SetVertexBuffer(vtx_builder.CreateVertexBuffer(host_buffer));
pass.SetPipeline(renderer.GetFastGradientPipeline(options));
pass.SetStencilReference(0);

// Take the pre-populated vertex shader uniform struct and set managed
// values.
VS::FrameInfo frame_info;
frame_info.mvp = entity.GetShaderTransform(pass);

VS::BindFrameInfo(pass, host_buffer.EmplaceUniform(frame_info));

FS::FragInfo frag_info;
frag_info.alpha =
GetOpacityFactor() * GetGeometry()->ComputeAlphaCoverage(entity);

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

return pass.Draw().ok();
}

bool LinearGradientContents::Render(const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) const {
// TODO(148651): The fast path is overly restrictive, following the design in
// https://github.com/flutter/flutter/issues/148651 support for more cases can
// be gradually added.
if (GetGeometry()->IsAxisAlignedRect() &&
(start_point_.x == end_point_.x || start_point_.y == end_point_.y) &&
GetInverseEffectTransform().IsIdentity()) {
return FastLinearGradient(renderer, entity, pass);
}
if (renderer.GetDeviceCapabilities().SupportsSSBO()) {
return RenderSSBO(renderer, entity, pass);
}
Expand Down
4 changes: 4 additions & 0 deletions impeller/entity/contents/linear_gradient_contents.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ class LinearGradientContents final : public ColorSourceContents {
const Entity& entity,
RenderPass& pass) const;

bool FastLinearGradient(const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) const;

Point start_point_;
Point end_point_;
std::vector<Color> colors_;
Expand Down
26 changes: 26 additions & 0 deletions impeller/entity/shaders/gradients/fast_gradient.frag
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// 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.

precision mediump float;

#include <impeller/color.glsl>
#include <impeller/dithering.glsl>
#include <impeller/types.glsl>

uniform FragInfo {
float alpha;
}
frag_info;

in vec4 v_color;

out vec4 frag_color;

void main() {
frag_color = IPPremultiply(v_color) * frag_info.alpha;
// mod operator is not supported in GLES 2.0
Copy link
Contributor

Choose a reason for hiding this comment

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

Whats with this comment? Also, even though the operator is not supported, you can use the function.

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 shader would fail to load on GLES because the ordered dither uses a bunch of bit manipulation logic?

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh. Gotcha. That method has a mod in there somewhere.

We should be able to fix it using the mod() function though. It's just the operator that's bad in ES.

Copy link
Contributor

Choose a reason for hiding this comment

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

Nothing to do in this patch. Was just curious.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ahh TIL!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

what an odd restriction

#ifndef IMPELLER_TARGET_OPENGLES
frag_color = IPOrderedDither8x8(frag_color, gl_FragCoord.xy);
#endif // IMPELLER_TARGET_OPENGLES
}
23 changes: 23 additions & 0 deletions impeller/entity/shaders/gradients/fast_gradient.vert
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// 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/transform.glsl>
#include <impeller/types.glsl>

uniform FrameInfo {
mat4 mvp;
}
frame_info;

in vec2 position;
in vec4 color;

// The geometry of the fast gradient draws is designed so that the
// varying unit will perform the correct color interpolation.
out vec4 v_color;
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe add a comment here saying, "We exploit the simplicity of this gradient and make the varying unit perform the interpolation instead of performing the calculations ourselves."

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done


void main() {
gl_Position = frame_info.mvp * vec4(position, 0.0, 1.0);
v_color = color;
}
Loading