Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 8567d96

Browse files
[Impeller] support overlapping stops with SSBO gradients. (#39174)
* [impeller] gradient stops * Support overlapping stops * ++ * update test values * Use padding type * bytes not floats * move to entity * ++ * Update gradient_generator.h * Update aiks_unittests.cc * ++
1 parent a1e657a commit 8567d96

12 files changed

+161
-187
lines changed

impeller/aiks/aiks_unittests.cc

+55-1
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,59 @@ TEST_P(AiksTest, CanRenderLinearGradient) {
381381
ASSERT_TRUE(OpenPlaygroundHere(callback));
382382
}
383383

384+
TEST_P(AiksTest, CanRenderLinearGradientWithOverlappingStops) {
385+
auto callback = [&](AiksContext& renderer, RenderTarget& render_target) {
386+
const char* tile_mode_names[] = {"Clamp", "Repeat", "Mirror", "Decal"};
387+
const Entity::TileMode tile_modes[] = {
388+
Entity::TileMode::kClamp, Entity::TileMode::kRepeat,
389+
Entity::TileMode::kMirror, Entity::TileMode::kDecal};
390+
391+
static int selected_tile_mode = 0;
392+
static float alpha = 1;
393+
ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
394+
ImGui::SliderFloat("Alpha", &alpha, 0, 1);
395+
ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names,
396+
sizeof(tile_mode_names) / sizeof(char*));
397+
static Matrix matrix = {
398+
1, 0, 0, 0, //
399+
0, 1, 0, 0, //
400+
0, 0, 1, 0, //
401+
0, 0, 0, 1 //
402+
};
403+
std::string label = "##1";
404+
for (int i = 0; i < 4; i++) {
405+
ImGui::InputScalarN(label.c_str(), ImGuiDataType_Float, &(matrix.vec[i]),
406+
4, NULL, NULL, "%.2f", 0);
407+
label[2]++;
408+
}
409+
ImGui::End();
410+
411+
Canvas canvas;
412+
Paint paint;
413+
canvas.Translate({100.0, 100.0, 0});
414+
auto tile_mode = tile_modes[selected_tile_mode];
415+
paint.color_source = [tile_mode]() {
416+
std::vector<Color> colors = {Color{0.9568, 0.2627, 0.2118, 1.0},
417+
Color{0.9568, 0.2627, 0.2118, 1.0},
418+
Color{0.1294, 0.5882, 0.9529, 1.0},
419+
Color{0.1294, 0.5882, 0.9529, 1.0}};
420+
std::vector<Scalar> stops = {0.0, 0.5, 0.5, 1.0};
421+
422+
auto contents = std::make_shared<LinearGradientContents>();
423+
contents->SetEndPoints({0, 0}, {500, 500});
424+
contents->SetColors(std::move(colors));
425+
contents->SetStops(std::move(stops));
426+
contents->SetTileMode(tile_mode);
427+
contents->SetEffectTransform(matrix);
428+
return contents;
429+
};
430+
paint.color = Color(1.0, 1.0, 1.0, alpha);
431+
canvas.DrawRect({0, 0, 500, 500}, paint);
432+
return renderer.Render(canvas.EndRecordingAsPicture(), render_target);
433+
};
434+
ASSERT_TRUE(OpenPlaygroundHere(callback));
435+
}
436+
384437
TEST_P(AiksTest, CanRenderLinearGradientManyColors) {
385438
auto callback = [&](AiksContext& renderer, RenderTarget& render_target) {
386439
const char* tile_mode_names[] = {"Clamp", "Repeat", "Mirror", "Decal"};
@@ -1117,7 +1170,8 @@ TEST_P(AiksTest, CanRenderItalicizedText) {
11171170

11181171
TEST_P(AiksTest, CanRenderEmojiTextFrame) {
11191172
Canvas canvas;
1120-
ASSERT_TRUE(RenderTextInCanvas(GetContext(), canvas, "😀 😃 😄 😁 😆 😅 😂 🤣 🥲 😊",
1173+
ASSERT_TRUE(RenderTextInCanvas(GetContext(), canvas,
1174+
"😀 😃 😄 😁 😆 😅 😂 🤣 🥲 😊",
11211175
#if FML_OS_MACOSX
11221176
"Apple Color Emoji.ttc"));
11231177
#else

impeller/entity/contents/gradient_generator.cc

+11
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,15 @@ std::shared_ptr<Texture> CreateGradientTexture(
4242
return texture;
4343
}
4444

45+
std::vector<StopData> CreateGradientColors(const std::vector<Color>& colors,
46+
const std::vector<Scalar>& stops) {
47+
FML_DCHECK(stops.size() == colors.size());
48+
49+
std::vector<StopData> result(stops.size());
50+
for (auto i = 0u; i < stops.size(); i++) {
51+
result[i] = {.color = colors[i], .stop = stops[i]};
52+
}
53+
return result;
54+
}
55+
4556
} // namespace impeller

impeller/entity/contents/gradient_generator.h

+17
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "impeller/geometry/gradient.h"
1515
#include "impeller/geometry/path.h"
1616
#include "impeller/geometry/point.h"
17+
#include "impeller/renderer/shader_types.h"
1718

1819
namespace impeller {
1920

@@ -27,4 +28,20 @@ std::shared_ptr<Texture> CreateGradientTexture(
2728
const GradientData& gradient_data,
2829
const std::shared_ptr<impeller::Context>& context);
2930

31+
struct StopData {
32+
Color color;
33+
Scalar stop;
34+
Padding<12> _padding_;
35+
};
36+
37+
/**
38+
* @brief Populate a vector with the color and stop data for a gradient
39+
*
40+
* @param colors
41+
* @param stops
42+
* @return StopData
43+
*/
44+
std::vector<StopData> CreateGradientColors(const std::vector<Color>& colors,
45+
const std::vector<Scalar>& stops);
46+
3047
} // namespace impeller

impeller/entity/contents/linear_gradient_contents.cc

+2-2
Original file line numberDiff line numberDiff line change
@@ -129,11 +129,11 @@ bool LinearGradientContents::RenderSSBO(const ContentContext& renderer,
129129
gradient_info.alpha = GetAlpha();
130130

131131
auto& host_buffer = pass.GetTransientsBuffer();
132-
auto colors = CreateGradientColors(colors_, stops_).value_or(colors_);
132+
auto colors = CreateGradientColors(colors_, stops_);
133133

134134
gradient_info.colors_length = colors.size();
135135
auto color_buffer = host_buffer.Emplace(
136-
colors.data(), colors.size() * sizeof(Color), alignof(Color));
136+
colors.data(), colors.size() * sizeof(StopData), alignof(StopData));
137137

138138
VS::FrameInfo frame_info;
139139
frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) *

impeller/entity/contents/radial_gradient_contents.cc

+2-2
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,11 @@ bool RadialGradientContents::RenderSSBO(const ContentContext& renderer,
6767
gradient_info.alpha = GetAlpha();
6868

6969
auto& host_buffer = pass.GetTransientsBuffer();
70-
auto colors = CreateGradientColors(colors_, stops_).value_or(colors_);
70+
auto colors = CreateGradientColors(colors_, stops_);
7171

7272
gradient_info.colors_length = colors.size();
7373
auto color_buffer = host_buffer.Emplace(
74-
colors.data(), colors.size() * sizeof(Color), alignof(Color));
74+
colors.data(), colors.size() * sizeof(StopData), alignof(StopData));
7575

7676
VS::FrameInfo frame_info;
7777
frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) *

impeller/entity/contents/sweep_gradient_contents.cc

+2-2
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,11 @@ bool SweepGradientContents::RenderSSBO(const ContentContext& renderer,
7373
gradient_info.alpha = GetAlpha();
7474

7575
auto& host_buffer = pass.GetTransientsBuffer();
76-
auto colors = CreateGradientColors(colors_, stops_).value_or(colors_);
76+
auto colors = CreateGradientColors(colors_, stops_);
7777

7878
gradient_info.colors_length = colors.size();
7979
auto color_buffer = host_buffer.Emplace(
80-
colors.data(), colors.size() * sizeof(Color), alignof(Color));
80+
colors.data(), colors.size() * sizeof(StopData), alignof(StopData));
8181

8282
VS::FrameInfo frame_info;
8383
frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) *

impeller/entity/shaders/linear_gradient_ssbo_fill.frag

+24-7
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,13 @@
66
#include <impeller/texture.glsl>
77
#include <impeller/types.glsl>
88

9-
readonly buffer ColorData {
10-
vec4 colors[];
9+
struct ColorPoint {
10+
vec4 color;
11+
float stop;
12+
};
13+
14+
layout(std140) readonly buffer ColorData {
15+
ColorPoint colors[];
1116
}
1217
color_data;
1318

@@ -35,10 +40,22 @@ void main() {
3540
return;
3641
}
3742
t = IPFloatTile(t, gradient_info.tile_mode);
38-
vec3 values = IPComputeFixedGradientValues(t, gradient_info.colors_length);
3943

40-
frag_color = mix(color_data.colors[int(values.x)],
41-
color_data.colors[int(values.y)], values.z);
42-
frag_color =
43-
vec4(frag_color.xyz * frag_color.a, frag_color.a) * gradient_info.alpha;
44+
vec4 result_color = vec4(0);
45+
for (int i = 1; i < gradient_info.colors_length; i++) {
46+
ColorPoint prev_point = color_data.colors[i - 1];
47+
ColorPoint current_point = color_data.colors[i];
48+
if (t >= prev_point.stop && t <= current_point.stop) {
49+
float delta = (current_point.stop - prev_point.stop);
50+
if (delta < 0.001) {
51+
result_color = current_point.color;
52+
} else {
53+
float ratio = (t - prev_point.stop) / delta;
54+
result_color = mix(prev_point.color, current_point.color, ratio);
55+
}
56+
break;
57+
}
58+
}
59+
frag_color = vec4(result_color.xyz * result_color.a, result_color.a) *
60+
gradient_info.alpha;
4461
}

impeller/entity/shaders/radial_gradient_ssbo_fill.frag

+24-7
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,13 @@
66
#include <impeller/texture.glsl>
77
#include <impeller/types.glsl>
88

9-
readonly buffer ColorData {
10-
vec4 colors[];
9+
struct ColorPoint {
10+
vec4 color;
11+
float stop;
12+
};
13+
14+
layout(std140) readonly buffer ColorData {
15+
ColorPoint colors[];
1116
}
1217
color_data;
1318

@@ -33,10 +38,22 @@ void main() {
3338
return;
3439
}
3540
t = IPFloatTile(t, gradient_info.tile_mode);
36-
vec3 values = IPComputeFixedGradientValues(t, gradient_info.colors_length);
3741

38-
frag_color = mix(color_data.colors[int(values.x)],
39-
color_data.colors[int(values.y)], values.z);
40-
frag_color =
41-
vec4(frag_color.xyz * frag_color.a, frag_color.a) * gradient_info.alpha;
42+
vec4 result_color = vec4(0);
43+
for (int i = 1; i < gradient_info.colors_length; i++) {
44+
ColorPoint prev_point = color_data.colors[i - 1];
45+
ColorPoint current_point = color_data.colors[i];
46+
if (t >= prev_point.stop && t <= current_point.stop) {
47+
float delta = (current_point.stop - prev_point.stop);
48+
if (delta < 0.001) {
49+
result_color = current_point.color;
50+
} else {
51+
float ratio = (t - prev_point.stop) / delta;
52+
result_color = mix(prev_point.color, current_point.color, ratio);
53+
}
54+
break;
55+
}
56+
}
57+
frag_color = vec4(result_color.xyz * result_color.a, result_color.a) *
58+
gradient_info.alpha;
4259
}

impeller/entity/shaders/sweep_gradient_ssbo_fill.frag

+24-7
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,13 @@
77
#include <impeller/texture.glsl>
88
#include <impeller/types.glsl>
99

10-
readonly buffer ColorData {
11-
vec4 colors[];
10+
struct ColorPoint {
11+
vec4 color;
12+
float stop;
13+
};
14+
15+
layout(std140) readonly buffer ColorData {
16+
ColorPoint colors[];
1217
}
1318
color_data;
1419

@@ -37,10 +42,22 @@ void main() {
3742
return;
3843
}
3944
t = IPFloatTile(t, gradient_info.tile_mode);
40-
vec3 values = IPComputeFixedGradientValues(t, gradient_info.colors_length);
4145

42-
frag_color = mix(color_data.colors[int(values.x)],
43-
color_data.colors[int(values.y)], values.z);
44-
frag_color =
45-
vec4(frag_color.xyz * frag_color.a, frag_color.a) * gradient_info.alpha;
46+
vec4 result_color = vec4(0);
47+
for (int i = 1; i < gradient_info.colors_length; i++) {
48+
ColorPoint prev_point = color_data.colors[i - 1];
49+
ColorPoint current_point = color_data.colors[i];
50+
if (t >= prev_point.stop && t <= current_point.stop) {
51+
float delta = (current_point.stop - prev_point.stop);
52+
if (delta < 0.001) {
53+
result_color = current_point.color;
54+
} else {
55+
float ratio = (t - prev_point.stop) / delta;
56+
result_color = mix(prev_point.color, current_point.color, ratio);
57+
}
58+
break;
59+
}
60+
}
61+
frag_color = vec4(result_color.xyz * result_color.a, result_color.a) *
62+
gradient_info.alpha;
4663
}

impeller/geometry/geometry_unittests.cc

-68
Original file line numberDiff line numberDiff line change
@@ -1914,73 +1914,5 @@ TEST(GeometryTest, Gradient) {
19141914
}
19151915
}
19161916

1917-
TEST(GeometryTest, GradientSSBO) {
1918-
{
1919-
// Simple 2 color gradient produces std::nullopt, as original
1920-
// color vector should be used.
1921-
std::vector<Color> colors = {Color::Red(), Color::Blue()};
1922-
std::vector<Scalar> stops = {0.0, 1.0};
1923-
1924-
auto gradient = CreateGradientColors(colors, stops);
1925-
1926-
ASSERT_EQ(gradient, std::nullopt);
1927-
}
1928-
1929-
{
1930-
// Gradient with duplicate stops does not create an empty texture.
1931-
std::vector<Color> colors = {Color::Red(), Color::Yellow(), Color::Black(),
1932-
Color::Blue()};
1933-
std::vector<Scalar> stops = {0.0, 0.25, 0.25, 1.0};
1934-
1935-
auto gradient = CreateGradientColors(colors, stops);
1936-
ASSERT_EQ(gradient.value().size(), 5u);
1937-
}
1938-
1939-
{
1940-
// Simple N color gradient produces color buffer containing exactly those
1941-
// values.
1942-
std::vector<Color> colors = {Color::Red(), Color::Blue(), Color::Green(),
1943-
Color::White()};
1944-
std::vector<Scalar> stops = {0.0, 0.33, 0.66, 1.0};
1945-
1946-
auto gradient = CreateGradientColors(colors, stops);
1947-
1948-
ASSERT_EQ(gradient, std::nullopt);
1949-
}
1950-
1951-
{
1952-
// Gradient with color stops will lerp and scale buffer.
1953-
std::vector<Color> colors = {Color::Red(), Color::Blue(), Color::Green()};
1954-
std::vector<Scalar> stops = {0.0, 0.25, 1.0};
1955-
1956-
auto gradient = CreateGradientColors(colors, stops);
1957-
1958-
std::vector<Color> lerped_colors = {
1959-
Color::Red(),
1960-
Color::Blue(),
1961-
Color::lerp(Color::Blue(), Color::Green(), 0.3333),
1962-
Color::lerp(Color::Blue(), Color::Green(), 0.6666),
1963-
Color::Green(),
1964-
};
1965-
1966-
ASSERT_COLORS_NEAR(gradient.value(), lerped_colors);
1967-
ASSERT_EQ(gradient.value().size(), 5u);
1968-
}
1969-
1970-
{
1971-
// Gradient size is capped at 1024.
1972-
std::vector<Color> colors = {};
1973-
std::vector<Scalar> stops = {};
1974-
for (auto i = 0u; i < 1025; i++) {
1975-
colors.push_back(Color::Blue());
1976-
stops.push_back(i / 1025.0);
1977-
}
1978-
1979-
auto gradient = CreateGradientColors(colors, stops);
1980-
1981-
ASSERT_EQ(gradient.value().size(), 1024u);
1982-
}
1983-
}
1984-
19851917
} // namespace testing
19861918
} // namespace impeller

0 commit comments

Comments
 (0)