Skip to content

Commit

Permalink
[Impeller] Switch to uniform arrays for gradient data on non-SSBO har…
Browse files Browse the repository at this point in the history
…dware (flutter#56441)

Currently the most generalized form of the Impeller gradient shaders uses an SSBO to store the gradient information, but SSBO data is not supported on older platforms. To make the capability more general we introduce variants of the gradient shaders that uses uniform arrays which are more widely supported.
  • Loading branch information
flar authored Nov 8, 2024
1 parent 1b567e8 commit 3e23be9
Show file tree
Hide file tree
Showing 21 changed files with 1,492 additions and 25 deletions.
8 changes: 8 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -43078,15 +43078,19 @@ 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/conical_gradient_uniform_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
ORIGIN: ../../../flutter/impeller/entity/shaders/gradients/linear_gradient_uniform_fill.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/gradients/radial_gradient_fill.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/gradients/radial_gradient_ssbo_fill.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/gradients/radial_gradient_uniform_fill.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/gradients/sweep_gradient_fill.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/gradients/sweep_gradient_ssbo_fill.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/gradients/sweep_gradient_uniform_fill.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/rrect_blur.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/rrect_blur.vert + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/runtime_effect.vert + ../../../flutter/LICENSE
Expand Down Expand Up @@ -45940,15 +45944,19 @@ 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/conical_gradient_uniform_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
FILE: ../../../flutter/impeller/entity/shaders/gradients/linear_gradient_uniform_fill.frag
FILE: ../../../flutter/impeller/entity/shaders/gradients/radial_gradient_fill.frag
FILE: ../../../flutter/impeller/entity/shaders/gradients/radial_gradient_ssbo_fill.frag
FILE: ../../../flutter/impeller/entity/shaders/gradients/radial_gradient_uniform_fill.frag
FILE: ../../../flutter/impeller/entity/shaders/gradients/sweep_gradient_fill.frag
FILE: ../../../flutter/impeller/entity/shaders/gradients/sweep_gradient_ssbo_fill.frag
FILE: ../../../flutter/impeller/entity/shaders/gradients/sweep_gradient_uniform_fill.frag
FILE: ../../../flutter/impeller/entity/shaders/rrect_blur.frag
FILE: ../../../flutter/impeller/entity/shaders/rrect_blur.vert
FILE: ../../../flutter/impeller/entity/shaders/runtime_effect.vert
Expand Down
117 changes: 117 additions & 0 deletions impeller/display_list/aiks_dl_gradient_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,123 @@ TEST_P(AiksTest, CanRenderLinearGradientWithOverlappingStopsClamp) {
CanRenderLinearGradientWithOverlappingStops(this, DlTileMode::kClamp);
}

namespace {
void CanRenderGradientWithIncompleteStops(AiksTest* aiks_test,
DlColorSourceType type) {
const DlTileMode tile_modes[4] = {
DlTileMode::kClamp,
DlTileMode::kRepeat,
DlTileMode::kMirror,
DlTileMode::kDecal,
};
const DlScalar test_size = 250;
const DlScalar test_border = 25;
const DlScalar gradient_size = 50;
const DlScalar quadrant_size = test_size + test_border * 2;

DisplayListBuilder builder;
builder.DrawRect(DlRect::MakeWH(quadrant_size * 2, quadrant_size * 2),
DlPaint().setColor(DlColor::kDarkGrey()));

for (int quadrant = 0; quadrant < 4; quadrant++) {
builder.Save();
builder.Translate((quadrant & 1) * quadrant_size + test_border,
(quadrant >> 1) * quadrant_size + test_border);

if (type == DlColorSourceType::kLinearGradient) {
// Alignment lines for the gradient edges/repeats/mirrors/etc.
// (rendered under the gradient so as not to obscure it)
DlPoint center = DlPoint(test_size, test_size) * 0.5;
DlScalar ten_percent = gradient_size * 0.1;
for (int i = gradient_size / 2; i <= test_size / 2; i += gradient_size) {
auto draw_at = [=](DlCanvas& canvas, DlScalar offset, DlColor color) {
DlPaint line_paint;
line_paint.setColor(color);
// strokewidth of 2 straddles the dividing line
line_paint.setStrokeWidth(2.0f);
line_paint.setDrawStyle(DlDrawStyle::kStroke);

DlPoint along(offset, offset);
DlScalar across_distance = test_size / 2 + 10 - offset;
DlPoint across(across_distance, -across_distance);

canvas.DrawLine(center - along - across, //
center - along + across, //
line_paint);
canvas.DrawLine(center + along - across, //
center + along + across, //
line_paint);
};
// White line is at the edge of the gradient
// Grey lines are where the 0.1 and 0.9 color stops land
draw_at(builder, i - ten_percent, DlColor::kMidGrey());
draw_at(builder, i, DlColor::kWhite());
draw_at(builder, i + ten_percent, DlColor::kMidGrey());
}
}

std::vector<DlColor> colors = {
DlColor::kGreen(),
DlColor::kPurple(),
DlColor::kOrange(),
DlColor::kBlue(),
};
std::vector<Scalar> stops = {0.1, 0.3, 0.7, 0.9};

DlPaint paint;
switch (type) {
case DlColorSourceType::kLinearGradient:
paint.setColorSource(DlColorSource::MakeLinear(
{test_size / 2 - gradient_size / 2,
test_size / 2 - gradient_size / 2},
{test_size / 2 + gradient_size / 2,
test_size / 2 + gradient_size / 2},
stops.size(), colors.data(), stops.data(), tile_modes[quadrant]));
break;
case DlColorSourceType::kRadialGradient:
paint.setColorSource(DlColorSource::MakeRadial(
{test_size / 2, test_size / 2}, gradient_size, //
stops.size(), colors.data(), stops.data(), tile_modes[quadrant]));
break;
case DlColorSourceType::kConicalGradient:
paint.setColorSource(DlColorSource::MakeConical(
{test_size / 2, test_size / 2}, 0,
{test_size / 2 + 20, test_size / 2 - 10}, gradient_size,
stops.size(), colors.data(), stops.data(), tile_modes[quadrant]));
break;
case DlColorSourceType::kSweepGradient:
paint.setColorSource(DlColorSource::MakeSweep(
{test_size / 2, test_size / 2}, 0, 45, //
stops.size(), colors.data(), stops.data(), tile_modes[quadrant]));
break;
default:
FML_UNREACHABLE();
}

builder.DrawRect(SkRect::MakeXYWH(0, 0, test_size, test_size), paint);
builder.Restore();
}

ASSERT_TRUE(aiks_test->OpenPlaygroundHere(builder.Build()));
}
} // namespace

TEST_P(AiksTest, CanRenderLinearGradientWithIncompleteStops) {
CanRenderGradientWithIncompleteStops(this,
DlColorSourceType::kLinearGradient);
}
TEST_P(AiksTest, CanRenderRadialGradientWithIncompleteStops) {
CanRenderGradientWithIncompleteStops(this,
DlColorSourceType::kRadialGradient);
}
TEST_P(AiksTest, CanRenderConicalGradientWithIncompleteStops) {
CanRenderGradientWithIncompleteStops(this,
DlColorSourceType::kConicalGradient);
}
TEST_P(AiksTest, CanRenderSweepGradientWithIncompleteStops) {
CanRenderGradientWithIncompleteStops(this, DlColorSourceType::kSweepGradient);
}

namespace {
void CanRenderLinearGradientManyColors(AiksTest* aiks_test,
DlTileMode tile_mode) {
Expand Down
8 changes: 6 additions & 2 deletions impeller/entity/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,22 @@ impeller_shaders("entity_shaders") {
"shaders/blending/advanced_blend.frag",
"shaders/clip.frag",
"shaders/clip.vert",
"shaders/gradients/conical_gradient_fill.frag",
"shaders/glyph_atlas.frag",
"shaders/glyph_atlas.vert",
"shaders/gradients/gradient_fill.vert",
"shaders/gradients/conical_gradient_fill.frag",
"shaders/gradients/conical_gradient_uniform_fill.frag",
"shaders/gradients/linear_gradient_fill.frag",
"shaders/gradients/linear_gradient_uniform_fill.frag",
"shaders/gradients/radial_gradient_fill.frag",
"shaders/gradients/radial_gradient_uniform_fill.frag",
"shaders/gradients/sweep_gradient_fill.frag",
"shaders/gradients/sweep_gradient_uniform_fill.frag",
"shaders/rrect_blur.vert",
"shaders/rrect_blur.frag",
"shaders/runtime_effect.vert",
"shaders/solid_fill.frag",
"shaders/solid_fill.vert",
"shaders/gradients/sweep_gradient_fill.frag",
"shaders/texture_fill.frag",
"shaders/texture_fill.vert",
"shaders/texture_uv_fill.vert",
Expand Down
55 changes: 55 additions & 0 deletions impeller/entity/contents/conical_gradient_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,24 @@ void ConicalGradientContents::SetFocus(std::optional<Point> focus,
focus_radius_ = radius;
}

#define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0]))
#define UNIFORM_FRAG_INFO(t) \
t##GradientUniformFillPipeline::FragmentShader::FragInfo
#define UNIFORM_COLOR_SIZE ARRAY_LEN(UNIFORM_FRAG_INFO(Conical)::colors)
#define UNIFORM_STOP_SIZE ARRAY_LEN(UNIFORM_FRAG_INFO(Conical)::stop_pairs)
static_assert(UNIFORM_COLOR_SIZE == kMaxUniformGradientStops);
static_assert(UNIFORM_STOP_SIZE == kMaxUniformGradientStops / 2);

bool ConicalGradientContents::Render(const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) const {
if (renderer.GetDeviceCapabilities().SupportsSSBO()) {
return RenderSSBO(renderer, entity, pass);
}
if (colors_.size() <= kMaxUniformGradientStops &&
stops_.size() <= kMaxUniformGradientStops) {
return RenderUniform(renderer, entity, pass);
}
return RenderTexture(renderer, entity, pass);
}

Expand Down Expand Up @@ -107,6 +119,49 @@ bool ConicalGradientContents::RenderSSBO(const ContentContext& renderer,
});
}

bool ConicalGradientContents::RenderUniform(const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) const {
using VS = ConicalGradientUniformFillPipeline::VertexShader;
using FS = ConicalGradientUniformFillPipeline::FragmentShader;

VS::FrameInfo frame_info;
frame_info.matrix = GetInverseEffectTransform();

PipelineBuilderCallback pipeline_callback =
[&renderer](ContentContextOptions options) {
return renderer.GetConicalGradientUniformFillPipeline(options);
};
return ColorSourceContents::DrawGeometry<VS>(
renderer, entity, pass, pipeline_callback, frame_info,
[this, &renderer, &entity](RenderPass& pass) {
FS::FragInfo frag_info;
frag_info.center = center_;
if (focus_) {
frag_info.focus = focus_.value();
frag_info.focus_radius = focus_radius_;
} else {
frag_info.focus = center_;
frag_info.focus_radius = 0.0;
}
frag_info.radius = radius_;
frag_info.tile_mode = static_cast<Scalar>(tile_mode_);
frag_info.alpha =
GetOpacityFactor() *
GetGeometry()->ComputeAlphaCoverage(entity.GetTransform());
frag_info.colors_length = PopulateUniformGradientColors(
colors_, stops_, frag_info.colors, frag_info.stop_pairs);
frag_info.decal_border_color = decal_border_color_;

pass.SetCommandLabel("ConicalGradientUniformFill");

FS::BindFragInfo(
pass, renderer.GetTransientsBuffer().EmplaceUniform(frag_info));

return true;
});
}

bool ConicalGradientContents::RenderTexture(const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) const {
Expand Down
5 changes: 5 additions & 0 deletions impeller/entity/contents/conical_gradient_contents.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ class ConicalGradientContents final : public ColorSourceContents {
bool RenderSSBO(const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) const;

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

Point center_;
Scalar radius_ = 0.0f;
std::vector<Color> colors_;
Expand Down
6 changes: 6 additions & 0 deletions impeller/entity/contents/content_context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,12 @@ ContentContext::ContentContext(
conical_gradient_ssbo_fill_pipelines_.CreateDefault(*context_, options);
sweep_gradient_ssbo_fill_pipelines_.CreateDefault(*context_, options);
} else {
linear_gradient_uniform_fill_pipelines_.CreateDefault(*context_, options);
radial_gradient_uniform_fill_pipelines_.CreateDefault(*context_, options);
conical_gradient_uniform_fill_pipelines_.CreateDefault(*context_,
options);
sweep_gradient_uniform_fill_pipelines_.CreateDefault(*context_, options);

linear_gradient_fill_pipelines_.CreateDefault(*context_, options);
radial_gradient_fill_pipelines_.CreateDefault(*context_, options);
conical_gradient_fill_pipelines_.CreateDefault(*context_, options);
Expand Down
45 changes: 45 additions & 0 deletions impeller/entity/contents/content_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@
#include "impeller/entity/tiled_texture_fill.frag.h"
#include "impeller/entity/yuv_to_rgb_filter.frag.h"

#include "impeller/entity/conical_gradient_uniform_fill.frag.h"
#include "impeller/entity/linear_gradient_uniform_fill.frag.h"
#include "impeller/entity/radial_gradient_uniform_fill.frag.h"
#include "impeller/entity/sweep_gradient_uniform_fill.frag.h"

#include "impeller/entity/conical_gradient_ssbo_fill.frag.h"
#include "impeller/entity/linear_gradient_ssbo_fill.frag.h"
#include "impeller/entity/radial_gradient_ssbo_fill.frag.h"
Expand Down Expand Up @@ -92,6 +97,18 @@ using ConicalGradientFillPipeline =
using SweepGradientFillPipeline =
RenderPipelineHandle<GradientFillVertexShader,
SweepGradientFillFragmentShader>;
using LinearGradientUniformFillPipeline =
RenderPipelineHandle<GradientFillVertexShader,
LinearGradientUniformFillFragmentShader>;
using ConicalGradientUniformFillPipeline =
RenderPipelineHandle<GradientFillVertexShader,
ConicalGradientUniformFillFragmentShader>;
using RadialGradientUniformFillPipeline =
RenderPipelineHandle<GradientFillVertexShader,
RadialGradientUniformFillFragmentShader>;
using SweepGradientUniformFillPipeline =
RenderPipelineHandle<GradientFillVertexShader,
SweepGradientUniformFillFragmentShader>;
using LinearGradientSSBOFillPipeline =
RenderPipelineHandle<GradientFillVertexShader,
LinearGradientSsboFillFragmentShader>;
Expand Down Expand Up @@ -370,6 +387,26 @@ class ContentContext {
return GetPipeline(linear_gradient_fill_pipelines_, opts);
}

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

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

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

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

std::shared_ptr<Pipeline<PipelineDescriptor>>
GetLinearGradientSSBOFillPipeline(ContentContextOptions opts) const {
FML_DCHECK(GetDeviceCapabilities().SupportsSSBO());
Expand Down Expand Up @@ -867,6 +904,14 @@ class ContentContext {
mutable Variants<ConicalGradientFillPipeline>
conical_gradient_fill_pipelines_;
mutable Variants<SweepGradientFillPipeline> sweep_gradient_fill_pipelines_;
mutable Variants<LinearGradientUniformFillPipeline>
linear_gradient_uniform_fill_pipelines_;
mutable Variants<RadialGradientUniformFillPipeline>
radial_gradient_uniform_fill_pipelines_;
mutable Variants<ConicalGradientUniformFillPipeline>
conical_gradient_uniform_fill_pipelines_;
mutable Variants<SweepGradientUniformFillPipeline>
sweep_gradient_uniform_fill_pipelines_;
mutable Variants<LinearGradientSSBOFillPipeline>
linear_gradient_ssbo_fill_pipelines_;
mutable Variants<RadialGradientSSBOFillPipeline>
Expand Down
27 changes: 27 additions & 0 deletions impeller/entity/contents/gradient_generator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,31 @@ std::vector<StopData> CreateGradientColors(const std::vector<Color>& colors,
return result;
}

int PopulateUniformGradientColors(
const std::vector<Color>& colors,
const std::vector<Scalar>& stops,
Vector4 frag_info_colors[kMaxUniformGradientStops],
Vector4 frag_info_stop_pairs[kMaxUniformGradientStops / 2]) {
FML_DCHECK(stops.size() == colors.size());

Scalar last_stop = 0;
int index = 0;
for (auto i = 0u; i < stops.size() && i < kMaxUniformGradientStops; i++) {
Scalar cur_stop = stops[i];
Scalar delta = cur_stop - last_stop;
Scalar inverse_delta = delta == 0.0f ? 0.0 : 1.0 / delta;
frag_info_colors[index] = colors[i];
if ((i & 1) == 0) {
frag_info_stop_pairs[index / 2].x = cur_stop;
frag_info_stop_pairs[index / 2].y = inverse_delta;
} else {
frag_info_stop_pairs[index / 2].z = cur_stop;
frag_info_stop_pairs[index / 2].w = inverse_delta;
}
last_stop = cur_stop;
index++;
}
return index;
}

} // namespace impeller
Loading

0 comments on commit 3e23be9

Please sign in to comment.