Flutter Impeller
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 
10 #include "impeller/entity/entity.h"
15 
16 namespace impeller {
17 
19 
21 
22 void LinearGradientContents::SetEndPoints(Point start_point, Point end_point) {
23  start_point_ = start_point;
24  end_point_ = end_point;
25 }
26 
27 void LinearGradientContents::SetColors(std::vector<Color> colors) {
28  colors_ = std::move(colors);
29 }
30 
31 void LinearGradientContents::SetStops(std::vector<Scalar> stops) {
32  stops_ = std::move(stops);
33 }
34 
35 const std::vector<Color>& LinearGradientContents::GetColors() const {
36  return colors_;
37 }
38 
39 const 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 
59 bool 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.
104 bool LinearGradientContents::FastLinearGradient(const ContentContext& renderer,
105  const Entity& entity,
106  RenderPass& pass) const {
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{
157  .type = PrimitiveType::kTriangle,
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 =
180  GetOpacityFactor() *
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)
196 static_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 
217 bool LinearGradientContents::RenderTexture(const ContentContext& renderer,
218  const Entity& entity,
219  RenderPass& pass) const {
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 =
248  GetOpacityFactor() *
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 
271 namespace {
272 Scalar 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 
280 bool LinearGradientContents::RenderSSBO(const ContentContext& renderer,
281  const Entity& entity,
282  RenderPass& pass) const {
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 =
302  GetOpacityFactor() *
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 
326 bool LinearGradientContents::RenderUniform(const ContentContext& renderer,
327  const Entity& entity,
328  RenderPass& pass) const {
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 =
346  GetOpacityFactor() *
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
PipelineRef GetLinearGradientFillPipeline(ContentContextOptions opts) const
const Capabilities & GetDeviceCapabilities() const
HostBuffer & GetTransientsDataBuffer() const
Retrieve the current host buffer for transient storage of other non-index data.
std::shared_ptr< Context > GetContext() const
std::function< Color(Color)> ColorFilterProc
Definition: contents.h:35
const Matrix & GetTransform() const
Get the global transform matrix for this Entity.
Definition: entity.cc:44
virtual Scalar ComputeAlphaCoverage(const Matrix &transform) const
Definition: geometry.h:125
virtual std::optional< Rect > GetCoverage(const Matrix &transform) const =0
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
FragmentShader_ FragmentShader
Definition: pipeline.h:164
#define UNIFORM_STOP_SIZE
#define UNIFORM_COLOR_SIZE
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::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
constexpr static TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition: rect.h:136
const size_t start
const size_t end