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