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
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
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