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 1 commit
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
Next Next commit
[Impeller] use uniform array for more efficient small gradients
  • Loading branch information
jonahwilliams committed Nov 16, 2022
commit ac5289a37f86c24e00f005d66b31a85d3bd31fc5
1 change: 1 addition & 0 deletions impeller/entity/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ impeller_shaders("entity_shaders") {
"shaders/linear_to_srgb_filter.frag",
"shaders/linear_to_srgb_filter.vert",
"shaders/linear_gradient_fill.frag",
"shaders/linear_gradient_fixed_fill.frag",
"shaders/morphology_filter.frag",
"shaders/morphology_filter.vert",
"shaders/position_color.vert",
Expand Down
2 changes: 2 additions & 0 deletions impeller/entity/contents/content_context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,8 @@ ContentContext::ContentContext(std::shared_ptr<Context> context)
CreateDefaultPipeline<SolidFillPipeline>(*context_);
linear_gradient_fill_pipelines_[{}] =
CreateDefaultPipeline<LinearGradientFillPipeline>(*context_);
linear_gradient_fixed_fill_pipelines_[{}] =
CreateDefaultPipeline<LinearGradientFixedFillPipeline>(*context_);
radial_gradient_fill_pipelines_[{}] =
CreateDefaultPipeline<RadialGradientFillPipeline>(*context_);
sweep_gradient_fill_pipelines_[{}] =
Expand Down
9 changes: 9 additions & 0 deletions impeller/entity/contents/content_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
#include "impeller/entity/vertices.frag.h"
#include "impeller/entity/yuv_to_rgb_filter.frag.h"
#include "impeller/entity/yuv_to_rgb_filter.vert.h"
#include "impeller/entity/linear_gradient_fixed_fill.frag.h"
#include "impeller/renderer/formats.h"
#include "impeller/renderer/pipeline.h"

Expand All @@ -76,6 +77,8 @@ namespace impeller {

using LinearGradientFillPipeline =
RenderPipelineT<GradientFillVertexShader, LinearGradientFillFragmentShader>;
using LinearGradientFixedFillPipeline =
RenderPipelineT<GradientFillVertexShader, LinearGradientFixedFillFragmentShader>;
using SolidFillPipeline =
RenderPipelineT<SolidFillVertexShader, SolidFillFragmentShader>;
using RadialGradientFillPipeline =
Expand Down Expand Up @@ -210,6 +213,11 @@ class ContentContext {
return GetPipeline(linear_gradient_fill_pipelines_, opts);
}

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

std::shared_ptr<Pipeline<PipelineDescriptor>> GetRadialGradientFillPipeline(
ContentContextOptions opts) const {
return GetPipeline(radial_gradient_fill_pipelines_, opts);
Expand Down Expand Up @@ -414,6 +422,7 @@ class ContentContext {
// map.
mutable Variants<SolidFillPipeline> solid_fill_pipelines_;
mutable Variants<LinearGradientFillPipeline> linear_gradient_fill_pipelines_;
mutable Variants<LinearGradientFixedFillPipeline> linear_gradient_fixed_fill_pipelines_;
mutable Variants<RadialGradientFillPipeline> radial_gradient_fill_pipelines_;
mutable Variants<SweepGradientFillPipeline> sweep_gradient_fill_pipelines_;
mutable Variants<RRectBlurPipeline> rrect_blur_pipelines_;
Expand Down
5 changes: 1 addition & 4 deletions impeller/entity/contents/gradient_generator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,15 @@

#include "flutter/fml/logging.h"
#include "impeller/entity/contents/content_context.h"
#include "impeller/geometry/gradient.h"
#include "impeller/renderer/context.h"
#include "impeller/renderer/render_pass.h"
#include "impeller/renderer/texture.h"

namespace impeller {

std::shared_ptr<Texture> CreateGradientTexture(
const std::vector<Color>& colors,
const std::vector<Scalar>& stops,
const GradientData& gradient_data,
const std::shared_ptr<impeller::Context>& context) {
auto gradient_data = CreateGradientBuffer(colors, stops);
if (gradient_data.texture_size == 0) {
FML_DLOG(ERROR) << "Invalid gradient data.";
return nullptr;
Expand Down
6 changes: 3 additions & 3 deletions impeller/entity/contents/gradient_generator.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,18 @@
#include "impeller/geometry/color.h"
#include "impeller/geometry/path.h"
#include "impeller/geometry/point.h"
#include "impeller/geometry/gradient.h"

namespace impeller {

class Context;

/**
* @brief Create a host visible texture that contains the gradient defined
* by the provided colors and stops.
* by the provided gradient data.
*/
std::shared_ptr<Texture> CreateGradientTexture(
const std::vector<Color>& colors,
const std::vector<Scalar>& stops,
const GradientData& gradient_data,
const std::shared_ptr<impeller::Context>& context);

} // namespace impeller
64 changes: 63 additions & 1 deletion impeller/entity/contents/linear_gradient_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,22 @@ void LinearGradientContents::SetTileMode(Entity::TileMode tile_mode) {
bool LinearGradientContents::Render(const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) const {
auto gradient_data = CreateGradientBuffer(colors_, stops_);
if (gradient_data.texture_size < 16) {
return RenderTexture(gradient_data, renderer, entity, pass);
}
return RenderFixed(gradient_data, renderer, entity, pass);
}

bool LinearGradientContents::RenderTexture(const GradientData& gradient_data,
const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) const {
using VS = LinearGradientFillPipeline::VertexShader;
using FS = LinearGradientFillPipeline::FragmentShader;

auto gradient_texture =
CreateGradientTexture(colors_, stops_, renderer.GetContext());
CreateGradientTexture(gradient_data, renderer.GetContext());
if (gradient_texture == nullptr) {
return false;
}
Expand Down Expand Up @@ -106,4 +117,55 @@ bool LinearGradientContents::Render(const ContentContext& renderer,
return true;
}

bool LinearGradientContents::RenderFixed(const GradientData& gradient_data,
const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) const {
using VS = LinearGradientFixedFillPipeline::VertexShader;
using FS = LinearGradientFixedFillPipeline::FragmentShader;

FS::GradientInfo gradient_info;
gradient_info.start_point = start_point_;
gradient_info.end_point = end_point_;
gradient_info.tile_mode = static_cast<Scalar>(tile_mode_);
gradient_info.alpha = GetAlpha();
gradient_info.colors_length = gradient_data.texture_size;
for (auto i = 0u; i < gradient_data.colors.size(); i++) {
gradient_info.colors[i] = gradient_data.colors[i];
}

VS::FrameInfo frame_info;
frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) *
entity.GetTransformation();
frame_info.matrix = GetInverseMatrix();

Command cmd;
cmd.label = "LinearGradientFixedFill";
cmd.stencil_reference = entity.GetStencilDepth();

auto geometry_result =
GetGeometry()->GetPositionBuffer(renderer, entity, pass);
auto options = OptionsFromPassAndEntity(pass, entity);
if (geometry_result.prevent_overdraw) {
options.stencil_compare = CompareFunction::kEqual;
options.stencil_operation = StencilOperation::kIncrementClamp;
}
options.primitive_type = geometry_result.type;
cmd.pipeline = renderer.GetLinearGradientFixedFillPipeline(options);

cmd.BindVertices(geometry_result.vertex_buffer);
FS::BindGradientInfo(
cmd, pass.GetTransientsBuffer().EmplaceUniform(gradient_info));
VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(frame_info));

if (!pass.AddCommand(std::move(cmd))) {
return false;
}

if (geometry_result.prevent_overdraw) {
return ClipRestoreContents().Render(renderer, entity, pass);
}
return true;
}

} // namespace impeller
11 changes: 11 additions & 0 deletions impeller/entity/contents/linear_gradient_contents.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "impeller/entity/contents/color_source_contents.h"
#include "impeller/entity/entity.h"
#include "impeller/geometry/color.h"
#include "impeller/geometry/gradient.h"
#include "impeller/geometry/path.h"
#include "impeller/geometry/point.h"

Expand Down Expand Up @@ -42,6 +43,16 @@ class LinearGradientContents final : public ColorSourceContents {
void SetTileMode(Entity::TileMode tile_mode);

private:
bool RenderTexture(const GradientData& gradient_data,
const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) const;

bool RenderFixed(const GradientData& gradient_data,
const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) const;

Point start_point_;
Point end_point_;
std::vector<Color> colors_;
Expand Down
4 changes: 3 additions & 1 deletion impeller/entity/contents/radial_gradient_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "impeller/entity/geometry.h"
#include "impeller/renderer/render_pass.h"
#include "impeller/renderer/sampler_library.h"
#include "impeller/geometry/gradient.h"

namespace impeller {

Expand Down Expand Up @@ -50,8 +51,9 @@ bool RadialGradientContents::Render(const ContentContext& renderer,
using VS = RadialGradientFillPipeline::VertexShader;
using FS = RadialGradientFillPipeline::FragmentShader;

auto gradient_data = CreateGradientBuffer(colors_, stops_);
auto gradient_texture =
CreateGradientTexture(colors_, stops_, renderer.GetContext());
CreateGradientTexture(gradient_data, renderer.GetContext());
if (gradient_texture == nullptr) {
return false;
}
Expand Down
4 changes: 3 additions & 1 deletion impeller/entity/contents/sweep_gradient_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "impeller/entity/entity.h"
#include "impeller/renderer/render_pass.h"
#include "impeller/renderer/sampler_library.h"
#include "impeller/geometry/gradient.h"

namespace impeller {

Expand Down Expand Up @@ -55,8 +56,9 @@ bool SweepGradientContents::Render(const ContentContext& renderer,
using VS = SweepGradientFillPipeline::VertexShader;
using FS = SweepGradientFillPipeline::FragmentShader;

auto gradient_data = CreateGradientBuffer(colors_, stops_);
auto gradient_texture =
CreateGradientTexture(colors_, stops_, renderer.GetContext());
CreateGradientTexture(gradient_data, renderer.GetContext());
if (gradient_texture == nullptr) {
return false;
}
Expand Down
47 changes: 47 additions & 0 deletions impeller/entity/shaders/linear_gradient_fixed_fill.frag
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// 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/texture.glsl>

uniform GradientInfo {
vec2 start_point;
vec2 end_point;
float alpha;
float tile_mode;
float colors_length;
vec4 colors[16];
} gradient_info;

in vec2 v_position;

out vec4 frag_color;

void main() {
float len = length(gradient_info.end_point - gradient_info.start_point);
float dot = dot(
v_position - gradient_info.start_point,
gradient_info.end_point - gradient_info.start_point
);
float t = dot / (len * len);
if ((t < 0.0 || t > 1.0) && gradient_info.tile_mode == kTileModeDecal) {
frag_color = vec4(0);
return;
}

t = IPFloatTile(t, gradient_info.tile_mode);
if (gradient_info.colors_length == 2) {
frag_color = mix(gradient_info.colors[0], gradient_info.colors[1], t);
return;
}
if (t == 1.0) {
frag_color = gradient_info.colors[int(gradient_info.colors_length) - 1];
return;
}

float rough_index = gradient_info.colors_length * t;
float lower_index = floor(rough_index);
float scale = rough_index - lower_index;

frag_color = mix(gradient_info.colors[int(lower_index)], gradient_info.colors[int(lower_index + 1)], scale);
}
45 changes: 27 additions & 18 deletions impeller/geometry/gradient.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,16 @@

namespace impeller {

static void AppendColor(const Color& color, std::vector<uint8_t>* colors) {
auto converted = color.ToR8G8B8A8();
colors->push_back(converted[0]);
colors->push_back(converted[1]);
colors->push_back(converted[2]);
colors->push_back(converted[3]);
static void AppendColor(const Color& color, GradientData* data) {
if (data->texture_size > 16) {
auto converted = color.ToR8G8B8A8();
data->color_bytes.push_back(converted[0]);
data->color_bytes.push_back(converted[1]);
data->color_bytes.push_back(converted[2]);
data->color_bytes.push_back(converted[3]);
} else {
data->colors.push_back(color);
}
}

GradientData CreateGradientBuffer(const std::vector<Color>& colors,
Expand Down Expand Up @@ -43,28 +47,36 @@ GradientData CreateGradientBuffer(const std::vector<Color>& colors,
texture_size = std::min(
static_cast<uint32_t>(std::round(1.0 / minimum_delta)) + 1, 1024u);
}
std::vector<uint8_t> color_stop_channels;
color_stop_channels.reserve(texture_size * 4);
GradientData data = {
.color_bytes = {},
.colors = {},
.texture_size = texture_size,
};
if (texture_size > 16) {
data.color_bytes.reserve(texture_size * 4);
} else {
data.colors.reserve(texture_size);
}

if (texture_size == colors.size() && colors.size() <= 1024) {
for (auto i = 0u; i < colors.size(); i++) {
AppendColor(colors[i], &color_stop_channels);
AppendColor(colors[i], &data);
}
} else {
Color previous_color = colors[0];
auto previous_stop = 0.0;
auto previous_color_index = 0;

// The first index is always equal to the first color, exactly.
AppendColor(previous_color, &color_stop_channels);
AppendColor(previous_color, &data);

for (auto i = 1u; i < texture_size - 1; i++) {
auto scaled_i = i / (texture_size - 1.0);
Color next_color = colors[previous_color_index + 1];
auto next_stop = stops[previous_color_index + 1];
// We're almost exactly equal to the next stop.
if (ScalarNearlyEqual(scaled_i, next_stop)) {
AppendColor(next_color, &color_stop_channels);
AppendColor(next_color, &data);

previous_color = next_color;
previous_stop = next_stop;
Expand All @@ -74,7 +86,7 @@ GradientData CreateGradientBuffer(const std::vector<Color>& colors,
auto t = (scaled_i - previous_stop) / (next_stop - previous_stop);
auto mixed_color = Color::lerp(previous_color, next_color, t);

AppendColor(mixed_color, &color_stop_channels);
AppendColor(mixed_color, &data);
} else {
// We've slightly overshot the previous stop.
previous_color = next_color;
Expand All @@ -86,16 +98,13 @@ GradientData CreateGradientBuffer(const std::vector<Color>& colors,
auto t = (scaled_i - previous_stop) / (next_stop - previous_stop);
auto mixed_color = Color::lerp(previous_color, next_color, t);

AppendColor(mixed_color, &color_stop_channels);
AppendColor(mixed_color, &data);
}
}
// The last index is always equal to the last color, exactly.
AppendColor(colors.back(), &color_stop_channels);
AppendColor(colors.back(), &data);
}
return GradientData{
.color_bytes = std::move(color_stop_channels),
.texture_size = texture_size,
};
return data;
}

} // namespace impeller
1 change: 1 addition & 0 deletions impeller/geometry/gradient.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ namespace impeller {
// If texture_size is 0 then the gradient is invalid.
struct GradientData {
std::vector<uint8_t> color_bytes;
std::vector<Color> colors;
uint32_t texture_size;
};

Expand Down