Flutter Impeller
point_field_geometry.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 
9 
10 namespace impeller {
11 
12 PointFieldGeometry::PointFieldGeometry(std::vector<Point> points,
13  Scalar radius,
14  bool round)
15  : points_(std::move(points)), radius_(radius), round_(round) {}
16 
18 
19 GeometryResult PointFieldGeometry::GetPositionBuffer(
20  const ContentContext& renderer,
21  const Entity& entity,
22  RenderPass& pass) {
23  if (renderer.GetDeviceCapabilities().SupportsCompute()) {
24  return GetPositionBufferGPU(renderer, entity, pass);
25  }
26  auto vtx_builder = GetPositionBufferCPU(renderer, entity, pass);
27  if (!vtx_builder.has_value()) {
28  return {};
29  }
30 
31  auto& host_buffer = pass.GetTransientsBuffer();
32  return {
34  .vertex_buffer = vtx_builder->CreateVertexBuffer(host_buffer),
35  .transform = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) *
36  entity.GetTransformation(),
37  .prevent_overdraw = false,
38  };
39 }
40 
41 GeometryResult PointFieldGeometry::GetPositionUVBuffer(
42  Rect texture_coverage,
43  Matrix effect_transform,
44  const ContentContext& renderer,
45  const Entity& entity,
46  RenderPass& pass) {
47  if (renderer.GetDeviceCapabilities().SupportsCompute()) {
48  return GetPositionBufferGPU(renderer, entity, pass, texture_coverage,
49  effect_transform);
50  }
51 
52  auto vtx_builder = GetPositionBufferCPU(renderer, entity, pass);
53  if (!vtx_builder.has_value()) {
54  return {};
55  }
56  auto uv_vtx_builder = ComputeUVGeometryCPU(
57  vtx_builder.value(), {0, 0}, texture_coverage.size, effect_transform);
58 
59  auto& host_buffer = pass.GetTransientsBuffer();
60  return {
62  .vertex_buffer = uv_vtx_builder.CreateVertexBuffer(host_buffer),
63  .transform = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) *
64  entity.GetTransformation(),
65  .prevent_overdraw = false,
66  };
67 }
68 
69 std::optional<VertexBufferBuilder<SolidFillVertexShader::PerVertexData>>
70 PointFieldGeometry::GetPositionBufferCPU(const ContentContext& renderer,
71  const Entity& entity,
72  RenderPass& pass) {
73  if (radius_ < 0.0) {
74  return std::nullopt;
75  }
76  auto determinant = entity.GetTransformation().GetDeterminant();
77  if (determinant == 0) {
78  return std::nullopt;
79  }
80 
81  Scalar min_size = 1.0f / sqrt(std::abs(determinant));
82  Scalar radius = std::max(radius_, min_size);
83 
84  auto vertices_per_geom = ComputeCircleDivisions(
85  entity.GetTransformation().GetMaxBasisLength() * radius, round_);
86  auto points_per_circle = 3 + (vertices_per_geom - 3) * 3;
87  auto total = points_per_circle * points_.size();
88  auto radian_start = round_ ? 0.0f : 0.785398f;
89  auto radian_step = k2Pi / vertices_per_geom;
90 
91  VertexBufferBuilder<SolidFillVertexShader::PerVertexData> vtx_builder;
92  vtx_builder.Reserve(total);
93 
94  /// Precompute all relative points and angles for a fixed geometry size.
95  auto elapsed_angle = radian_start;
96  std::vector<Point> angle_table(vertices_per_geom);
97  for (auto i = 0u; i < vertices_per_geom; i++) {
98  angle_table[i] = Point(cos(elapsed_angle), sin(elapsed_angle)) * radius;
99  elapsed_angle += radian_step;
100  }
101 
102  for (auto i = 0u; i < points_.size(); i++) {
103  auto center = points_[i];
104 
105  auto origin = center + angle_table[0];
106  vtx_builder.AppendVertex({origin});
107 
108  auto pt1 = center + angle_table[1];
109  vtx_builder.AppendVertex({pt1});
110 
111  auto pt2 = center + angle_table[2];
112  vtx_builder.AppendVertex({pt2});
113 
114  for (auto j = 0u; j < vertices_per_geom - 3; j++) {
115  vtx_builder.AppendVertex({origin});
116  vtx_builder.AppendVertex({pt2});
117 
118  pt2 = center + angle_table[j + 3];
119  vtx_builder.AppendVertex({pt2});
120  }
121  }
122  return vtx_builder;
123 }
124 
125 GeometryResult PointFieldGeometry::GetPositionBufferGPU(
126  const ContentContext& renderer,
127  const Entity& entity,
128  RenderPass& pass,
129  std::optional<Rect> texture_coverage,
130  std::optional<Matrix> effect_transform) {
131  FML_DCHECK(renderer.GetDeviceCapabilities().SupportsCompute());
132  if (radius_ < 0.0) {
133  return {};
134  }
135  auto determinant = entity.GetTransformation().GetDeterminant();
136  if (determinant == 0) {
137  return {};
138  }
139 
140  Scalar min_size = 1.0f / sqrt(std::abs(determinant));
141  Scalar radius = std::max(radius_, min_size);
142 
143  auto vertices_per_geom = ComputeCircleDivisions(
144  entity.GetTransformation().GetMaxBasisLength() * radius, round_);
145 
146  auto points_per_circle = 3 + (vertices_per_geom - 3) * 3;
147  auto total = points_per_circle * points_.size();
148 
149  auto cmd_buffer = renderer.GetContext()->CreateCommandBuffer();
150  auto compute_pass = cmd_buffer->CreateComputePass();
151  auto& host_buffer = compute_pass->GetTransientsBuffer();
152 
153  auto points_data =
154  host_buffer.Emplace(points_.data(), points_.size() * sizeof(Point),
156 
157  DeviceBufferDescriptor buffer_desc;
158  buffer_desc.size = total * sizeof(Point);
159  buffer_desc.storage_mode = StorageMode::kDevicePrivate;
160 
161  auto geometry_buffer = renderer.GetContext()
162  ->GetResourceAllocator()
163  ->CreateBuffer(buffer_desc)
164  ->AsBufferView();
165 
166  BufferView output;
167  {
168  using PS = PointsComputeShader;
169  ComputeCommand cmd;
170  DEBUG_COMMAND_INFO(cmd, "Points Geometry");
171  cmd.pipeline = renderer.GetPointComputePipeline();
172 
173  PS::FrameInfo frame_info;
174  frame_info.count = points_.size();
175  frame_info.radius = radius;
176  frame_info.radian_start = round_ ? 0.0f : kPiOver4;
177  frame_info.radian_step = k2Pi / vertices_per_geom;
178  frame_info.points_per_circle = points_per_circle;
179  frame_info.divisions_per_circle = vertices_per_geom;
180 
181  PS::BindFrameInfo(cmd, host_buffer.EmplaceUniform(frame_info));
182  PS::BindGeometryData(cmd, geometry_buffer);
183  PS::BindPointData(cmd, points_data);
184 
185  if (!compute_pass->AddCommand(std::move(cmd))) {
186  return {};
187  }
188  output = geometry_buffer;
189  }
190 
191  if (texture_coverage.has_value() && effect_transform.has_value()) {
192  DeviceBufferDescriptor buffer_desc;
193  buffer_desc.size = total * sizeof(Vector4);
194  buffer_desc.storage_mode = StorageMode::kDevicePrivate;
195 
196  auto geometry_uv_buffer = renderer.GetContext()
197  ->GetResourceAllocator()
198  ->CreateBuffer(buffer_desc)
199  ->AsBufferView();
200 
201  using UV = UvComputeShader;
202 
203  ComputeCommand cmd;
204  DEBUG_COMMAND_INFO(cmd, "UV Geometry");
205  cmd.pipeline = renderer.GetUvComputePipeline();
206 
207  UV::FrameInfo frame_info;
208  frame_info.count = total;
209  frame_info.effect_transform = effect_transform.value();
210  frame_info.texture_origin = {0, 0};
211  frame_info.texture_size = Vector2(texture_coverage.value().size);
212 
213  UV::BindFrameInfo(cmd, host_buffer.EmplaceUniform(frame_info));
214  UV::BindGeometryData(cmd, geometry_buffer);
215  UV::BindGeometryUVData(cmd, geometry_uv_buffer);
216 
217  if (!compute_pass->AddCommand(std::move(cmd))) {
218  return {};
219  }
220  output = geometry_uv_buffer;
221  }
222 
223  compute_pass->SetGridSize(ISize(total, 1));
224  compute_pass->SetThreadGroupSize(ISize(total, 1));
225 
226  if (!compute_pass->EncodeCommands() || !cmd_buffer->SubmitCommands()) {
227  return {};
228  }
229 
230  return {
231  .type = PrimitiveType::kTriangle,
232  .vertex_buffer = {.vertex_buffer = output,
233  .vertex_count = total,
234  .index_type = IndexType::kNone},
235  .transform = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) *
236  entity.GetTransformation(),
237  .prevent_overdraw = false,
238  };
239 }
240 
241 /// @brief Compute the number of vertices to divide each circle into.
242 ///
243 /// @return the number of vertices.
245  bool round) {
246  if (!round) {
247  return 4;
248  }
249 
250  // Note: these values are approximated based on the values returned from
251  // the decomposition of 4 cubics performed by Path::CreatePolyline.
252  if (scaled_radius < 1.0) {
253  return 4;
254  }
255  if (scaled_radius < 2.0) {
256  return 8;
257  }
258  if (scaled_radius < 12.0) {
259  return 24;
260  }
261  if (scaled_radius < 22.0) {
262  return 34;
263  }
264  return std::min(scaled_radius, 140.0f);
265 }
266 
267 // |Geometry|
268 GeometryVertexType PointFieldGeometry::GetVertexType() const {
270 }
271 
272 // |Geometry|
273 std::optional<Rect> PointFieldGeometry::GetCoverage(
274  const Matrix& transform) const {
275  if (points_.size() > 0) {
276  // Doesn't use MakePointBounds as this isn't resilient to points that
277  // all lie along the same axis.
278  auto first = points_.begin();
279  auto last = points_.end();
280  auto left = first->x;
281  auto top = first->y;
282  auto right = first->x;
283  auto bottom = first->y;
284  for (auto it = first + 1; it < last; ++it) {
285  left = std::min(left, it->x);
286  top = std::min(top, it->y);
287  right = std::max(right, it->x);
288  bottom = std::max(bottom, it->y);
289  }
290  auto coverage = Rect::MakeLTRB(left - radius_, top - radius_,
291  right + radius_, bottom + radius_);
292  return coverage.TransformBounds(transform);
293  }
294  return std::nullopt;
295 }
296 
297 } // namespace impeller
DEBUG_COMMAND_INFO
#define DEBUG_COMMAND_INFO(obj, arg)
Definition: command.h:31
impeller::Scalar
float Scalar
Definition: scalar.h:15
impeller::DefaultUniformAlignment
constexpr size_t DefaultUniformAlignment()
Definition: platform.h:14
point_field_geometry.h
impeller::PointFieldGeometry::PointFieldGeometry
PointFieldGeometry(std::vector< Point > points, Scalar radius, bool round)
Definition: point_field_geometry.cc:12
impeller::Vector2
Point Vector2
Definition: point.h:310
impeller::Matrix::GetDeterminant
Scalar GetDeterminant() const
Definition: matrix.cc:162
impeller::ComputeUVGeometryCPU
VertexBufferBuilder< TextureFillVertexShader::PerVertexData > ComputeUVGeometryCPU(VertexBufferBuilder< SolidFillVertexShader::PerVertexData > &input, Point texture_origin, Size texture_coverage, Matrix effect_transform)
Compute UV geometry for a VBB that contains only position geometry.
Definition: geometry.cc:50
impeller::GeometryVertexType
GeometryVertexType
Definition: geometry.h:25
impeller::Entity::GetTransformation
const Matrix & GetTransformation() const
Definition: entity.cc:49
impeller::Entity
Definition: entity.h:21
impeller::RenderPass::GetRenderTargetSize
ISize GetRenderTargetSize() const
Definition: render_pass.cc:30
impeller::Point
TPoint< Scalar > Point
Definition: point.h:306
impeller::k2Pi
constexpr float k2Pi
Definition: constants.h:28
impeller::StorageMode::kDevicePrivate
@ kDevicePrivate
impeller::GeometryResult
Definition: geometry.h:18
impeller::IndexType::kNone
@ kNone
Does not use the index buffer.
impeller::Rect
TRect< Scalar > Rect
Definition: rect.h:306
impeller::ISize
TSize< int64_t > ISize
Definition: size.h:136
impeller::RenderPass
Render passes encode render commands directed as one specific render target into an underlying comman...
Definition: render_pass.h:27
command_buffer.h
impeller::ContentContext::GetDeviceCapabilities
const Capabilities & GetDeviceCapabilities() const
Definition: content_context.cc:444
std
Definition: comparable.h:98
impeller::PointFieldGeometry::ComputeCircleDivisions
static size_t ComputeCircleDivisions(Scalar scaled_radius, bool round)
Compute the number of vertices to divide each circle into.
Definition: point_field_geometry.cc:244
compute_command.h
impeller::Matrix::MakeOrthographic
static constexpr Matrix MakeOrthographic(TSize< T > size)
Definition: matrix.h:448
impeller::PrimitiveType::kTriangle
@ kTriangle
impeller::TRect< Scalar >::MakeLTRB
constexpr static TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition: rect.h:40
impeller::PointFieldGeometry::~PointFieldGeometry
~PointFieldGeometry()
impeller
Definition: aiks_context.cc:10
impeller::kPosition
@ kPosition
Definition: geometry.h:26
impeller::ContentContext
Definition: content_context.h:344
impeller::kPiOver4
constexpr float kPiOver4
Definition: constants.h:34
impeller::RenderPass::GetTransientsBuffer
HostBuffer & GetTransientsBuffer()
Definition: render_pass.cc:34
impeller::Capabilities::SupportsCompute
virtual bool SupportsCompute() const =0
Whether the context backend supports ComputePass.