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