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

[Impeller] support overlapping stops with SSBO gradients. #39174

Merged
merged 14 commits into from
Jan 27, 2023
Merged
56 changes: 55 additions & 1 deletion impeller/aiks/aiks_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,59 @@ TEST_P(AiksTest, CanRenderLinearGradient) {
ASSERT_TRUE(OpenPlaygroundHere(callback));
}

TEST_P(AiksTest, CanRenderLinearGradientWithOverlappingStops) {
auto callback = [&](AiksContext& renderer, RenderTarget& render_target) {
const char* tile_mode_names[] = {"Clamp", "Repeat", "Mirror", "Decal"};
const Entity::TileMode tile_modes[] = {
Entity::TileMode::kClamp, Entity::TileMode::kRepeat,
Entity::TileMode::kMirror, Entity::TileMode::kDecal};

static int selected_tile_mode = 0;
static float alpha = 1;
ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::SliderFloat("Alpha", &alpha, 0, 1);
ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names,
sizeof(tile_mode_names) / sizeof(char*));
static Matrix matrix = {
1, 0, 0, 0, //
0, 1, 0, 0, //
0, 0, 1, 0, //
0, 0, 0, 1 //
};
std::string label = "##1";
for (int i = 0; i < 4; i++) {
ImGui::InputScalarN(label.c_str(), ImGuiDataType_Float, &(matrix.vec[i]),
4, NULL, NULL, "%.2f", 0);
label[2]++;
}
ImGui::End();

Canvas canvas;
Paint paint;
canvas.Translate({100.0, 100.0, 0});
auto tile_mode = tile_modes[selected_tile_mode];
paint.color_source = [tile_mode]() {
std::vector<Color> colors = {Color{0.9568, 0.2627, 0.2118, 1.0},
Color{0.9568, 0.2627, 0.2118, 1.0},
Color{0.1294, 0.5882, 0.9529, 1.0},
Color{0.1294, 0.5882, 0.9529, 1.0}};
std::vector<Scalar> stops = {0.0, 0.5, 0.5, 1.0};

auto contents = std::make_shared<LinearGradientContents>();
contents->SetEndPoints({0, 0}, {500, 500});
contents->SetColors(std::move(colors));
contents->SetStops(std::move(stops));
contents->SetTileMode(tile_mode);
contents->SetEffectTransform(matrix);
return contents;
};
paint.color = Color(1.0, 1.0, 1.0, alpha);
canvas.DrawRect({0, 0, 500, 500}, paint);
return renderer.Render(canvas.EndRecordingAsPicture(), render_target);
};
ASSERT_TRUE(OpenPlaygroundHere(callback));
}

TEST_P(AiksTest, CanRenderLinearGradientManyColors) {
auto callback = [&](AiksContext& renderer, RenderTarget& render_target) {
const char* tile_mode_names[] = {"Clamp", "Repeat", "Mirror", "Decal"};
Expand Down Expand Up @@ -1117,7 +1170,8 @@ TEST_P(AiksTest, CanRenderItalicizedText) {

TEST_P(AiksTest, CanRenderEmojiTextFrame) {
Canvas canvas;
ASSERT_TRUE(RenderTextInCanvas(GetContext(), canvas, "😀 😃 😄 😁 😆 😅 😂 🤣 🥲 😊",
ASSERT_TRUE(RenderTextInCanvas(GetContext(), canvas,
"😀 😃 😄 😁 😆 😅 😂 🤣 🥲 😊",
#if FML_OS_MACOSX
"Apple Color Emoji.ttc"));
#else
Expand Down
11 changes: 11 additions & 0 deletions impeller/entity/contents/gradient_generator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,15 @@ std::shared_ptr<Texture> CreateGradientTexture(
return texture;
}

std::vector<StopData> CreateGradientColors(const std::vector<Color>& colors,
const std::vector<Scalar>& stops) {
FML_DCHECK(stops.size() == colors.size());

std::vector<StopData> result(stops.size());
for (auto i = 0u; i < stops.size(); i++) {
result[i] = {.color = colors[i], .stop = stops[i]};
}
return result;
}

} // namespace impeller
17 changes: 17 additions & 0 deletions impeller/entity/contents/gradient_generator.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "impeller/geometry/gradient.h"
#include "impeller/geometry/path.h"
#include "impeller/geometry/point.h"
#include "impeller/renderer/shader_types.h"

namespace impeller {

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

struct StopData {
Color color;
Scalar stop;
Padding<12> _padding_;
};

/**
* @brief Populate a vector with the color and stop data for a gradient
*
* @param colors
* @param stops
* @return StopData
*/
std::vector<StopData> CreateGradientColors(const std::vector<Color>& colors,
const std::vector<Scalar>& stops);

} // namespace impeller
4 changes: 2 additions & 2 deletions impeller/entity/contents/linear_gradient_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -129,11 +129,11 @@ bool LinearGradientContents::RenderSSBO(const ContentContext& renderer,
gradient_info.alpha = GetAlpha();

auto& host_buffer = pass.GetTransientsBuffer();
auto colors = CreateGradientColors(colors_, stops_).value_or(colors_);
auto colors = CreateGradientColors(colors_, stops_);

gradient_info.colors_length = colors.size();
auto color_buffer = host_buffer.Emplace(
colors.data(), colors.size() * sizeof(Color), alignof(Color));
colors.data(), colors.size() * sizeof(StopData), alignof(StopData));

VS::FrameInfo frame_info;
frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) *
Expand Down
4 changes: 2 additions & 2 deletions impeller/entity/contents/radial_gradient_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,11 @@ bool RadialGradientContents::RenderSSBO(const ContentContext& renderer,
gradient_info.alpha = GetAlpha();

auto& host_buffer = pass.GetTransientsBuffer();
auto colors = CreateGradientColors(colors_, stops_).value_or(colors_);
auto colors = CreateGradientColors(colors_, stops_);

gradient_info.colors_length = colors.size();
auto color_buffer = host_buffer.Emplace(
colors.data(), colors.size() * sizeof(Color), alignof(Color));
colors.data(), colors.size() * sizeof(StopData), alignof(StopData));

VS::FrameInfo frame_info;
frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) *
Expand Down
4 changes: 2 additions & 2 deletions impeller/entity/contents/sweep_gradient_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,11 @@ bool SweepGradientContents::RenderSSBO(const ContentContext& renderer,
gradient_info.alpha = GetAlpha();

auto& host_buffer = pass.GetTransientsBuffer();
auto colors = CreateGradientColors(colors_, stops_).value_or(colors_);
auto colors = CreateGradientColors(colors_, stops_);

gradient_info.colors_length = colors.size();
auto color_buffer = host_buffer.Emplace(
colors.data(), colors.size() * sizeof(Color), alignof(Color));
colors.data(), colors.size() * sizeof(StopData), alignof(StopData));

VS::FrameInfo frame_info;
frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) *
Expand Down
31 changes: 24 additions & 7 deletions impeller/entity/shaders/linear_gradient_ssbo_fill.frag
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,13 @@
#include <impeller/texture.glsl>
#include <impeller/types.glsl>

readonly buffer ColorData {
vec4 colors[];
struct ColorPoint {
vec4 color;
float stop;
};

layout(std140) readonly buffer ColorData {
ColorPoint colors[];
}
color_data;

Expand Down Expand Up @@ -35,10 +40,22 @@ void main() {
return;
}
t = IPFloatTile(t, gradient_info.tile_mode);
vec3 values = IPComputeFixedGradientValues(t, gradient_info.colors_length);

frag_color = mix(color_data.colors[int(values.x)],
color_data.colors[int(values.y)], values.z);
frag_color =
vec4(frag_color.xyz * frag_color.a, frag_color.a) * gradient_info.alpha;
vec4 result_color = vec4(0);
for (int i = 1; i < gradient_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;
}
}
frag_color = vec4(result_color.xyz * result_color.a, result_color.a) *
gradient_info.alpha;
}
31 changes: 24 additions & 7 deletions impeller/entity/shaders/radial_gradient_ssbo_fill.frag
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,13 @@
#include <impeller/texture.glsl>
#include <impeller/types.glsl>

readonly buffer ColorData {
vec4 colors[];
struct ColorPoint {
vec4 color;
float stop;
};

layout(std140) readonly buffer ColorData {
ColorPoint colors[];
}
color_data;

Expand All @@ -33,10 +38,22 @@ void main() {
return;
}
t = IPFloatTile(t, gradient_info.tile_mode);
vec3 values = IPComputeFixedGradientValues(t, gradient_info.colors_length);

frag_color = mix(color_data.colors[int(values.x)],
color_data.colors[int(values.y)], values.z);
frag_color =
vec4(frag_color.xyz * frag_color.a, frag_color.a) * gradient_info.alpha;
vec4 result_color = vec4(0);
for (int i = 1; i < gradient_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;
}
}
frag_color = vec4(result_color.xyz * result_color.a, result_color.a) *
gradient_info.alpha;
}
31 changes: 24 additions & 7 deletions impeller/entity/shaders/sweep_gradient_ssbo_fill.frag
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,13 @@
#include <impeller/texture.glsl>
#include <impeller/types.glsl>

readonly buffer ColorData {
vec4 colors[];
struct ColorPoint {
vec4 color;
float stop;
};

layout(std140) readonly buffer ColorData {
ColorPoint colors[];
}
color_data;

Expand Down Expand Up @@ -37,10 +42,22 @@ void main() {
return;
}
t = IPFloatTile(t, gradient_info.tile_mode);
vec3 values = IPComputeFixedGradientValues(t, gradient_info.colors_length);

frag_color = mix(color_data.colors[int(values.x)],
color_data.colors[int(values.y)], values.z);
frag_color =
vec4(frag_color.xyz * frag_color.a, frag_color.a) * gradient_info.alpha;
vec4 result_color = vec4(0);
for (int i = 1; i < gradient_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;
}
}
frag_color = vec4(result_color.xyz * result_color.a, result_color.a) *
gradient_info.alpha;
}
68 changes: 0 additions & 68 deletions impeller/geometry/geometry_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1912,73 +1912,5 @@ TEST(GeometryTest, Gradient) {
}
}

TEST(GeometryTest, GradientSSBO) {
{
// Simple 2 color gradient produces std::nullopt, as original
// color vector should be used.
std::vector<Color> colors = {Color::Red(), Color::Blue()};
std::vector<Scalar> stops = {0.0, 1.0};

auto gradient = CreateGradientColors(colors, stops);

ASSERT_EQ(gradient, std::nullopt);
}

{
// Gradient with duplicate stops does not create an empty texture.
std::vector<Color> colors = {Color::Red(), Color::Yellow(), Color::Black(),
Color::Blue()};
std::vector<Scalar> stops = {0.0, 0.25, 0.25, 1.0};

auto gradient = CreateGradientColors(colors, stops);
ASSERT_EQ(gradient.value().size(), 5u);
}

{
// Simple N color gradient produces color buffer containing exactly those
// values.
std::vector<Color> colors = {Color::Red(), Color::Blue(), Color::Green(),
Color::White()};
std::vector<Scalar> stops = {0.0, 0.33, 0.66, 1.0};

auto gradient = CreateGradientColors(colors, stops);

ASSERT_EQ(gradient, std::nullopt);
}

{
// Gradient with color stops will lerp and scale buffer.
std::vector<Color> colors = {Color::Red(), Color::Blue(), Color::Green()};
std::vector<Scalar> stops = {0.0, 0.25, 1.0};

auto gradient = CreateGradientColors(colors, stops);

std::vector<Color> lerped_colors = {
Color::Red(),
Color::Blue(),
Color::lerp(Color::Blue(), Color::Green(), 0.3333),
Color::lerp(Color::Blue(), Color::Green(), 0.6666),
Color::Green(),
};

ASSERT_COLORS_NEAR(gradient.value(), lerped_colors);
ASSERT_EQ(gradient.value().size(), 5u);
}

{
// Gradient size is capped at 1024.
std::vector<Color> colors = {};
std::vector<Scalar> stops = {};
for (auto i = 0u; i < 1025; i++) {
colors.push_back(Color::Blue());
stops.push_back(i / 1025.0);
}

auto gradient = CreateGradientColors(colors, stops);

ASSERT_EQ(gradient.value().size(), 1024u);
}
}

} // namespace testing
} // namespace impeller
Loading