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  data_host_buffer.GetMinimumUniformAlignment());
315 
316  pass.SetCommandLabel("LinearGradientSSBOFill");
317 
318  FS::BindFragInfo(pass, data_host_buffer.EmplaceUniform(frag_info));
319  FS::BindColorData(pass, color_buffer);
320 
321  return true;
322  });
323 }
324 
325 bool LinearGradientContents::RenderUniform(const ContentContext& renderer,
326  const Entity& entity,
327  RenderPass& pass) const {
330 
331  VS::FrameInfo frame_info;
332  frame_info.matrix = GetInverseEffectTransform();
333 
334  PipelineBuilderCallback pipeline_callback =
335  [&renderer](ContentContextOptions options) {
336  return renderer.GetLinearGradientUniformFillPipeline(options);
337  };
338  return ColorSourceContents::DrawGeometry<VS>(
339  renderer, entity, pass, pipeline_callback, frame_info,
340  [this, &renderer, &entity](RenderPass& pass) {
341  FS::FragInfo frag_info;
342  frag_info.start_point = start_point_;
343  frag_info.start_to_end = end_point_ - start_point_;
344  frag_info.alpha =
345  GetOpacityFactor() *
346  GetGeometry()->ComputeAlphaCoverage(entity.GetTransform());
347  frag_info.tile_mode = static_cast<Scalar>(tile_mode_);
348  frag_info.colors_length = PopulateUniformGradientColors(
349  colors_, stops_, frag_info.colors, frag_info.stop_pairs);
350  frag_info.inverse_dot_start_to_end =
351  CalculateInverseDotStartToEnd(start_point_, end_point_);
352  frag_info.decal_border_color = decal_border_color_;
353 
354  pass.SetCommandLabel("LinearGradientUniformFill");
355 
356  FS::BindFragInfo(
357  pass, renderer.GetTransientsDataBuffer().EmplaceUniform(frag_info));
358 
359  return true;
360  });
361 }
362 
364  const ColorFilterProc& color_filter_proc) {
365  for (Color& color : colors_) {
366  color = color_filter_proc(color);
367  }
368  decal_border_color_ = color_filter_proc(decal_border_color_);
369  return true;
370 }
371 
372 } // 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:792
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