Flutter Engine
 
Loading...
Searching...
No Matches
linear_gradient_contents.cc
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
6
15
16namespace impeller {
17
19
21
22void LinearGradientContents::SetEndPoints(Point start_point, Point end_point) {
23 start_point_ = start_point;
24 end_point_ = end_point;
25}
26
27void LinearGradientContents::SetColors(std::vector<Color> colors) {
28 colors_ = std::move(colors);
29}
30
31void LinearGradientContents::SetStops(std::vector<Scalar> stops) {
32 stops_ = std::move(stops);
33}
34
35const std::vector<Color>& LinearGradientContents::GetColors() const {
36 return colors_;
37}
38
39const std::vector<Scalar>& LinearGradientContents::GetStops() const {
40 return stops_;
41}
42
44 tile_mode_ = tile_mode;
45}
46
48 if (GetOpacityFactor() < 1 || tile_mode_ == Entity::TileMode::kDecal) {
49 return false;
50 }
51 for (auto color : colors_) {
52 if (!color.IsOpaque()) {
53 return false;
54 }
55 }
57}
58
59bool LinearGradientContents::CanApplyFastGradient() const {
60 if (!GetInverseEffectTransform().IsIdentity()) {
61 return false;
62 }
63 std::optional<Rect> maybe_rect = GetGeometry()->GetCoverage(Matrix());
64 if (!maybe_rect.has_value()) {
65 return false;
66 }
67 Rect rect = maybe_rect.value();
68
69 if (ScalarNearlyEqual(start_point_.x, end_point_.x)) {
70 // Sort start and end to make on-rect comparisons easier.
71 Point start = (start_point_.y < end_point_.y) ? start_point_ : end_point_;
72 Point end = (start_point_.y < end_point_.y) ? end_point_ : start_point_;
73 // The exact x positon doesn't matter for a vertical gradient, but the y
74 // position must be nearly on the rectangle.
75 if (ScalarNearlyEqual(start.y, rect.GetTop()) &&
76 ScalarNearlyEqual(end.y, rect.GetBottom())) {
77 return true;
78 }
79 return false;
80 }
81
82 if (ScalarNearlyEqual(start_point_.y, end_point_.y)) {
83 // Sort start and end to make on-rect comparisons easier.
84 Point start = (start_point_.x < end_point_.x) ? start_point_ : end_point_;
85 Point end = (start_point_.x < end_point_.x) ? end_point_ : start_point_;
86 // The exact y positon doesn't matter for a horizontal gradient, but the x
87 // position must be nearly on the rectangle.
88 if (ScalarNearlyEqual(start.x, rect.GetLeft()) &&
89 ScalarNearlyEqual(end.x, rect.GetRight())) {
90 return true;
91 }
92 return false;
93 }
94
95 return false;
96}
97
98// A much faster (in terms of ALU) linear gradient that uses vertex
99// interpolation to perform all color computation. Requires that the geometry of
100// the gradient is divided into regions based on the stop values.
101// Currently restricted to rect geometry where the start and end points are
102// perfectly horizontal/vertical, but could easily be expanded to StC cases
103// provided that the start/end are on or outside of the coverage rect.
104bool LinearGradientContents::FastLinearGradient(const ContentContext& renderer,
105 const Entity& entity,
106 RenderPass& pass) const {
107 using VS = FastGradientPipeline::VertexShader;
108 using FS = FastGradientPipeline::FragmentShader;
109
110 const Geometry* geometry = GetGeometry();
111 bool force_stencil = !geometry->IsAxisAlignedRect();
112
113 auto geom_callback = [&](const ContentContext& renderer, const Entity& entity,
114 RenderPass& pass,
115 const Geometry* geometry) -> GeometryResult {
116 // We already know this is an axis aligned rectangle, so the coverage will
117 // be approximately the same as the geometry. For non axis-algined
118 // rectangles, we can force stencil then cover (not done here). We give an
119 // identity transform to avoid double transforming the gradient.
120 std::optional<Rect> maybe_rect = geometry->GetCoverage(Matrix());
121 if (!maybe_rect.has_value()) {
122 return {};
123 }
124 Rect rect = maybe_rect.value();
125 bool horizontal_axis = start_point_.y == end_point_.y;
126
127 // Compute the locations of each breakpoint along the primary axis, then
128 // create a rectangle that joins each segment. There will be two triangles
129 // between each pair of points.
130 VertexBufferBuilder<VS::PerVertexData> vtx_builder;
131 vtx_builder.Reserve(6 * (stops_.size() - 1));
132 Point prev = start_point_;
133 for (auto i = 1u; i < stops_.size(); i++) {
134 Scalar t = stops_[i];
135 Point current = (1.0 - t) * start_point_ + t * end_point_;
136 Rect section = horizontal_axis
137 ? Rect::MakeXYWH(prev.x, rect.GetY(),
138 current.x - prev.x, rect.GetHeight())
139
140 : Rect::MakeXYWH(rect.GetX(), prev.y, rect.GetWidth(),
141 current.y - prev.y);
142 vtx_builder.AddVertices({
143 {section.GetLeftTop(), colors_[i - 1]},
144 {section.GetRightTop(),
145 horizontal_axis ? colors_[i] : colors_[i - 1]},
146 {section.GetLeftBottom(),
147 horizontal_axis ? colors_[i - 1] : colors_[i]},
148 {section.GetRightTop(),
149 horizontal_axis ? colors_[i] : colors_[i - 1]},
150 {section.GetLeftBottom(),
151 horizontal_axis ? colors_[i - 1] : colors_[i]},
152 {section.GetRightBottom(), colors_[i]},
153 });
154 prev = current;
155 }
156 return GeometryResult{
158 .vertex_buffer = vtx_builder.CreateVertexBuffer(
159 renderer.GetTransientsDataBuffer(),
160 renderer.GetTransientsIndexesBuffer()),
161 .transform = entity.GetShaderTransform(pass),
162 };
163 };
164
165 pass.SetLabel("LinearGradient");
166
167 VS::FrameInfo frame_info;
168
169 PipelineBuilderCallback pipeline_callback =
170 [&renderer](ContentContextOptions options) {
171 return renderer.GetFastGradientPipeline(options);
172 };
173 return ColorSourceContents::DrawGeometry<VS>(
174 renderer, entity, pass, pipeline_callback, frame_info,
175 [this, &renderer, &entity](RenderPass& pass) {
176 auto& data_host_buffer = renderer.GetTransientsDataBuffer();
177
178 FS::FragInfo frag_info;
179 frag_info.alpha =
181 GetGeometry()->ComputeAlphaCoverage(entity.GetTransform());
182
183 FS::BindFragInfo(pass, data_host_buffer.EmplaceUniform(frag_info));
184
185 return true;
186 },
187 /*force_stencil=*/force_stencil, geom_callback);
188}
189
190#define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0]))
191#define UNIFORM_FRAG_INFO(t) \
192 t##GradientUniformFillPipeline::FragmentShader::FragInfo
193#define UNIFORM_COLOR_SIZE ARRAY_LEN(UNIFORM_FRAG_INFO(Linear)::colors)
194#define UNIFORM_STOP_SIZE ARRAY_LEN(UNIFORM_FRAG_INFO(Linear)::stop_pairs)
196static_assert(UNIFORM_STOP_SIZE == kMaxUniformGradientStops / 2);
197
199 const Entity& entity,
200 RenderPass& pass) const {
201 // TODO(148651): The fast path is overly restrictive, following the design in
202 // https://github.com/flutter/flutter/issues/148651 support for more cases can
203 // be gradually added.
204 if (CanApplyFastGradient()) {
205 return FastLinearGradient(renderer, entity, pass);
206 }
207 if (renderer.GetDeviceCapabilities().SupportsSSBO()) {
208 return RenderSSBO(renderer, entity, pass);
209 }
210 if (colors_.size() <= kMaxUniformGradientStops &&
211 stops_.size() <= kMaxUniformGradientStops) {
212 return RenderUniform(renderer, entity, pass);
213 }
214 return RenderTexture(renderer, entity, pass);
215}
216
217bool LinearGradientContents::RenderTexture(const ContentContext& renderer,
218 const Entity& entity,
219 RenderPass& pass) const {
220 using VS = LinearGradientFillPipeline::VertexShader;
221 using FS = LinearGradientFillPipeline::FragmentShader;
222
223 VS::FrameInfo frame_info;
224 frame_info.matrix = GetInverseEffectTransform();
225
226 PipelineBuilderCallback pipeline_callback =
227 [&renderer](ContentContextOptions options) {
228 return renderer.GetLinearGradientFillPipeline(options);
229 };
230 return ColorSourceContents::DrawGeometry<VS>(
231 renderer, entity, pass, pipeline_callback, frame_info,
232 [this, &renderer, &entity](RenderPass& pass) {
233 auto gradient_data = CreateGradientBuffer(colors_, stops_);
234 auto gradient_texture =
235 CreateGradientTexture(gradient_data, renderer.GetContext());
236 if (gradient_texture == nullptr) {
237 return false;
238 }
239
240 FS::FragInfo frag_info;
241 frag_info.start_point = start_point_;
242 frag_info.end_point = end_point_;
243 frag_info.tile_mode = static_cast<Scalar>(tile_mode_);
244 frag_info.decal_border_color = decal_border_color_;
245 frag_info.texture_sampler_y_coord_scale =
246 gradient_texture->GetYCoordScale();
247 frag_info.alpha =
250 ;
251 frag_info.half_texel =
252 Vector2(0.5 / gradient_texture->GetSize().width,
253 0.5 / gradient_texture->GetSize().height);
254
255 pass.SetCommandLabel("LinearGradientFill");
256
257 SamplerDescriptor sampler_desc;
258 sampler_desc.min_filter = MinMagFilter::kLinear;
259 sampler_desc.mag_filter = MinMagFilter::kLinear;
260
261 FS::BindTextureSampler(
262 pass, std::move(gradient_texture),
263 renderer.GetContext()->GetSamplerLibrary()->GetSampler(
264 sampler_desc));
265 FS::BindFragInfo(
266 pass, renderer.GetTransientsDataBuffer().EmplaceUniform(frag_info));
267 return true;
268 });
269}
270
271namespace {
272Scalar CalculateInverseDotStartToEnd(Point start_point, Point end_point) {
273 Point start_to_end = end_point - start_point;
274 Scalar dot =
275 (start_to_end.x * start_to_end.x + start_to_end.y * start_to_end.y);
276 return dot == 0.0f ? 0.0f : 1.0f / dot;
277}
278} // namespace
279
280bool LinearGradientContents::RenderSSBO(const ContentContext& renderer,
281 const Entity& entity,
282 RenderPass& pass) const {
283 using VS = LinearGradientSSBOFillPipeline::VertexShader;
284 using FS = LinearGradientSSBOFillPipeline::FragmentShader;
285
286 VS::FrameInfo frame_info;
287 frame_info.matrix = GetInverseEffectTransform();
288
289 PipelineBuilderCallback pipeline_callback =
290 [&renderer](ContentContextOptions options) {
291 return renderer.GetLinearGradientSSBOFillPipeline(options);
292 };
293 return ColorSourceContents::DrawGeometry<VS>(
294 renderer, entity, pass, pipeline_callback, frame_info,
295 [this, &renderer, &entity](RenderPass& pass) {
296 FS::FragInfo frag_info;
297 frag_info.start_point = start_point_;
298 frag_info.end_point = end_point_;
299 frag_info.tile_mode = static_cast<Scalar>(tile_mode_);
300 frag_info.decal_border_color = decal_border_color_;
301 frag_info.alpha =
303 GetGeometry()->ComputeAlphaCoverage(entity.GetTransform());
304 frag_info.start_to_end = end_point_ - start_point_;
305 frag_info.inverse_dot_start_to_end =
306 CalculateInverseDotStartToEnd(start_point_, end_point_);
307
308 auto& data_host_buffer = renderer.GetTransientsDataBuffer();
309 auto colors = CreateGradientColors(colors_, stops_);
310
311 frag_info.colors_length = colors.size();
312 auto color_buffer = data_host_buffer.Emplace(
313 colors.data(), colors.size() * sizeof(StopData),
314 renderer.GetDeviceCapabilities()
315 .GetMinimumStorageBufferAlignment());
316
317 pass.SetCommandLabel("LinearGradientSSBOFill");
318
319 FS::BindFragInfo(pass, data_host_buffer.EmplaceUniform(frag_info));
320 FS::BindColorData(pass, color_buffer);
321
322 return true;
323 });
324}
325
326bool LinearGradientContents::RenderUniform(const ContentContext& renderer,
327 const Entity& entity,
328 RenderPass& pass) const {
329 using VS = LinearGradientUniformFillPipeline::VertexShader;
330 using FS = LinearGradientUniformFillPipeline::FragmentShader;
331
332 VS::FrameInfo frame_info;
333 frame_info.matrix = GetInverseEffectTransform();
334
335 PipelineBuilderCallback pipeline_callback =
336 [&renderer](ContentContextOptions options) {
337 return renderer.GetLinearGradientUniformFillPipeline(options);
338 };
339 return ColorSourceContents::DrawGeometry<VS>(
340 renderer, entity, pass, pipeline_callback, frame_info,
341 [this, &renderer, &entity](RenderPass& pass) {
342 FS::FragInfo frag_info;
343 frag_info.start_point = start_point_;
344 frag_info.start_to_end = end_point_ - start_point_;
345 frag_info.alpha =
347 GetGeometry()->ComputeAlphaCoverage(entity.GetTransform());
348 frag_info.tile_mode = static_cast<Scalar>(tile_mode_);
349 frag_info.colors_length = PopulateUniformGradientColors(
350 colors_, stops_, frag_info.colors, frag_info.stop_pairs);
351 frag_info.inverse_dot_start_to_end =
352 CalculateInverseDotStartToEnd(start_point_, end_point_);
353 frag_info.decal_border_color = decal_border_color_;
354
355 pass.SetCommandLabel("LinearGradientUniformFill");
356
357 FS::BindFragInfo(
358 pass, renderer.GetTransientsDataBuffer().EmplaceUniform(frag_info));
359
360 return true;
361 });
362}
363
365 const ColorFilterProc& color_filter_proc) {
366 for (Color& color : colors_) {
367 color = color_filter_proc(color);
368 }
369 decal_border_color_ = color_filter_proc(decal_border_color_);
370 return true;
371}
372
373} // namespace impeller
virtual bool SupportsSSBO() const =0
Whether the context backend supports binding Shader Storage Buffer Objects (SSBOs) to pipelines.
const Geometry * GetGeometry() const
Get the geometry that this contents will use to render.
Scalar GetOpacityFactor() const
Get the opacity factor for this color source.
bool AppliesAlphaForStrokeCoverage(const Matrix &transform) const
Whether the entity should be treated as non-opaque due to stroke geometry requiring alpha for coverag...
const Matrix & GetInverseEffectTransform() const
Set the inverted effect transform for this color source.
std::function< PipelineRef(ContentContextOptions)> PipelineBuilderCallback
HostBuffer & GetTransientsDataBuffer() const
Retrieve the current host buffer for transient storage of other non-index data.
PipelineRef GetLinearGradientFillPipeline(ContentContextOptions opts) const
const Capabilities & GetDeviceCapabilities() const
std::shared_ptr< Context > GetContext() const
const Matrix & GetTransform() const
Get the global transform matrix for this Entity.
Definition entity.cc:44
virtual std::optional< Rect > GetCoverage(const Matrix &transform) const =0
virtual Scalar ComputeAlphaCoverage(const Matrix &transform) const
Definition geometry.h:125
BufferView EmplaceUniform(const UniformType &uniform)
Emplace uniform data onto the host buffer. Ensure that backend specific uniform alignment requirement...
Definition host_buffer.h:47
const std::vector< Color > & GetColors() const
void SetTileMode(Entity::TileMode tile_mode)
const std::vector< Scalar > & GetStops() const
void SetColors(std::vector< Color > colors)
bool ApplyColorFilter(const ColorFilterProc &color_filter_proc) override
If possible, applies a color filter to this contents inputs on the CPU.
bool Render(const ContentContext &renderer, const Entity &entity, RenderPass &pass) const override
void SetEndPoints(Point start_point, Point end_point)
void SetStops(std::vector< Scalar > stops)
bool IsOpaque(const Matrix &transform) const override
Whether this Contents only emits opaque source colors from the fragment stage. This value does not ac...
Render passes encode render commands directed as one specific render target into an underlying comman...
Definition render_pass.h:30
#define UNIFORM_STOP_SIZE
#define UNIFORM_COLOR_SIZE
double y
Point Vector2
Definition point.h:331
float Scalar
Definition scalar.h:19
TRect< Scalar > Rect
Definition rect.h:788
TPoint< Scalar > Point
Definition point.h:327
LinePipeline::FragmentShader FS
int PopulateUniformGradientColors(const std::vector< Color > &colors, const std::vector< Scalar > &stops, Vector4 frag_info_colors[kMaxUniformGradientStops], Vector4 frag_info_stop_pairs[kMaxUniformGradientStops/2])
Populate 2 arrays with the colors and stop data for a gradient.
std::function< Color(Color)> ColorFilterProc
std::vector< StopData > CreateGradientColors(const std::vector< Color > &colors, const std::vector< Scalar > &stops)
Populate a vector with the color and stop data for a gradient.
LinePipeline::VertexShader VS
std::shared_ptr< Texture > CreateGradientTexture(const GradientData &gradient_data, const std::shared_ptr< impeller::Context > &context)
Create a host visible texture that contains the gradient defined by the provided gradient data.
GradientData CreateGradientBuffer(const std::vector< Color > &colors, const std::vector< Scalar > &stops)
Populate a vector with the interpolated color bytes for the linear gradient described by colors and s...
Definition gradient.cc:20
constexpr bool ScalarNearlyEqual(Scalar x, Scalar y, Scalar tolerance=kEhCloseEnough)
Definition scalar.h:36
static constexpr uint32_t kMaxUniformGradientStops
Scalar alpha
Definition color.h:143
A 4x4 matrix using column-major storage.
Definition matrix.h:37
static constexpr TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition rect.h:136
const size_t start
const size_t end