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 (renderer.GetDeviceCapabilities().SupportsCompute()) {
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 = renderer.GetTransientsBuffer();
30  return {
32  .vertex_buffer = vtx_builder->CreateVertexBuffer(host_buffer),
33  .transform = entity.GetShaderTransform(pass),
34  };
35 }
36 
37 GeometryResult PointFieldGeometry::GetPositionUVBuffer(
38  Rect texture_coverage,
39  Matrix effect_transform,
40  const ContentContext& renderer,
41  const Entity& entity,
42  RenderPass& pass) const {
43  if (renderer.GetDeviceCapabilities().SupportsCompute()) {
44  return GetPositionBufferGPU(renderer, entity, pass, texture_coverage,
45  effect_transform);
46  }
47 
48  auto vtx_builder = GetPositionBufferCPU(renderer, entity, pass);
49  if (!vtx_builder.has_value()) {
50  return {};
51  }
52  auto uv_vtx_builder =
53  ComputeUVGeometryCPU(vtx_builder.value(), {0, 0},
54  texture_coverage.GetSize(), effect_transform);
55 
56  auto& host_buffer = renderer.GetTransientsBuffer();
57  return {
59  .vertex_buffer = uv_vtx_builder.CreateVertexBuffer(host_buffer),
60  .transform = entity.GetShaderTransform(pass),
61  };
62 }
63 
64 std::optional<VertexBufferBuilder<SolidFillVertexShader::PerVertexData>>
65 PointFieldGeometry::GetPositionBufferCPU(const ContentContext& renderer,
66  const Entity& entity,
67  RenderPass& pass) const {
68  if (radius_ < 0.0) {
69  return std::nullopt;
70  }
71  auto transform = entity.GetTransform();
72  auto determinant = transform.GetDeterminant();
73  if (determinant == 0) {
74  return std::nullopt;
75  }
76 
77  Scalar min_size = 1.0f / sqrt(std::abs(determinant));
78  Scalar radius = std::max(radius_, min_size);
79 
80  VertexBufferBuilder<SolidFillVertexShader::PerVertexData> vtx_builder;
81 
82  if (round_) {
83  // Get triangulation relative to {0, 0} so we can translate it to each
84  // point in turn.
85  auto generator =
86  renderer.GetTessellator()->FilledCircle(transform, {}, radius);
87  FML_DCHECK(generator.GetTriangleType() == PrimitiveType::kTriangleStrip);
88  std::vector<Point> circle_vertices;
89  circle_vertices.reserve(generator.GetVertexCount());
90  generator.GenerateVertices([&circle_vertices](const Point& p) { //
91  circle_vertices.push_back(p);
92  });
93  FML_DCHECK(circle_vertices.size() == generator.GetVertexCount());
94 
95  vtx_builder.Reserve((circle_vertices.size() + 2) * points_.size() - 2);
96  for (auto& center : points_) {
97  if (vtx_builder.HasVertices()) {
98  vtx_builder.AppendVertex(vtx_builder.Last());
99  vtx_builder.AppendVertex({center + circle_vertices[0]});
100  }
101 
102  for (auto& vertex : circle_vertices) {
103  vtx_builder.AppendVertex({center + vertex});
104  }
105  }
106  } else {
107  vtx_builder.Reserve(6 * points_.size() - 2);
108  for (auto& point : points_) {
109  auto first = Point(point.x - radius, point.y - radius);
110 
111  if (vtx_builder.HasVertices()) {
112  vtx_builder.AppendVertex(vtx_builder.Last());
113  vtx_builder.AppendVertex({first});
114  }
115 
116  // Z pattern from UL -> UR -> LL -> LR
117  vtx_builder.AppendVertex({first});
118  vtx_builder.AppendVertex({{point.x + radius, point.y - radius}});
119  vtx_builder.AppendVertex({{point.x - radius, point.y + radius}});
120  vtx_builder.AppendVertex({{point.x + radius, point.y + radius}});
121  }
122  }
123 
124  return vtx_builder;
125 }
126 
127 GeometryResult PointFieldGeometry::GetPositionBufferGPU(
128  const ContentContext& renderer,
129  const Entity& entity,
130  RenderPass& pass,
131  std::optional<Rect> texture_coverage,
132  std::optional<Matrix> effect_transform) const {
133  FML_DCHECK(renderer.GetDeviceCapabilities().SupportsCompute());
134  if (radius_ < 0.0) {
135  return {};
136  }
137  Scalar determinant = entity.GetTransform().GetDeterminant();
138  if (determinant == 0) {
139  return {};
140  }
141 
142  Scalar min_size = 1.0f / sqrt(std::abs(determinant));
143  Scalar radius = std::max(radius_, min_size);
144 
145  size_t vertices_per_geom = ComputeCircleDivisions(
146  entity.GetTransform().GetMaxBasisLength() * radius, round_);
147 
148  size_t points_per_circle = 3 + (vertices_per_geom - 3) * 3;
149  size_t total = points_per_circle * points_.size();
150 
151  std::shared_ptr<CommandBuffer> cmd_buffer =
152  renderer.GetContext()->CreateCommandBuffer();
153  std::shared_ptr<ComputePass> compute_pass = cmd_buffer->CreateComputePass();
154  HostBuffer& host_buffer = renderer.GetTransientsBuffer();
155 
156  BufferView points_data =
157  host_buffer.Emplace(points_.data(), points_.size() * sizeof(Point),
159 
160  BufferView geometry_buffer =
161  host_buffer.Emplace(nullptr, total * sizeof(Point),
162  std::max(DefaultUniformAlignment(), alignof(Point)));
163 
164  BufferView output;
165  {
166  using PS = PointsComputeShader;
167 
168  compute_pass->SetPipeline(renderer.GetPointComputePipeline());
169  compute_pass->SetCommandLabel("Points Geometry");
170 
171  PS::FrameInfo frame_info;
172  frame_info.count = points_.size();
173  frame_info.radius = round_ ? radius : radius * kSqrt2;
174  frame_info.radian_start = round_ ? 0.0f : kPiOver4;
175  frame_info.radian_step = k2Pi / vertices_per_geom;
176  frame_info.points_per_circle = points_per_circle;
177  frame_info.divisions_per_circle = vertices_per_geom;
178 
179  PS::BindFrameInfo(*compute_pass, host_buffer.EmplaceUniform(frame_info));
180  PS::BindGeometryData(*compute_pass, geometry_buffer);
181  PS::BindPointData(*compute_pass, points_data);
182 
183  if (!compute_pass->Compute(ISize(total, 1)).ok()) {
184  return {};
185  }
186  output = geometry_buffer;
187  }
188 
189  if (texture_coverage.has_value() && effect_transform.has_value()) {
190  BufferView geometry_uv_buffer = host_buffer.Emplace(
191  nullptr, total * sizeof(Vector4),
192  std::max(DefaultUniformAlignment(), alignof(Vector4)));
193 
194  using UV = UvComputeShader;
195 
196  compute_pass->AddBufferMemoryBarrier();
197  compute_pass->SetCommandLabel("UV Geometry");
198  compute_pass->SetPipeline(renderer.GetUvComputePipeline());
199 
200  UV::FrameInfo frame_info;
201  frame_info.count = total;
202  frame_info.effect_transform = effect_transform.value();
203  frame_info.texture_origin = {0, 0};
204  frame_info.texture_size = Vector2(texture_coverage.value().GetSize());
205 
206  UV::BindFrameInfo(*compute_pass, host_buffer.EmplaceUniform(frame_info));
207  UV::BindGeometryData(*compute_pass, geometry_buffer);
208  UV::BindGeometryUVData(*compute_pass, geometry_uv_buffer);
209 
210  if (!compute_pass->Compute(ISize(total, 1)).ok()) {
211  return {};
212  }
213  output = geometry_uv_buffer;
214  }
215 
216  if (!compute_pass->EncodeCommands()) {
217  return {};
218  }
219  if (!renderer.GetContext()
220  ->GetCommandQueue()
221  ->Submit({std::move(cmd_buffer)})
222  .ok()) {
223  return {};
224  }
225 
226  return {
227  .type = PrimitiveType::kTriangle,
228  .vertex_buffer = {.vertex_buffer = std::move(output),
229  .vertex_count = total,
230  .index_type = IndexType::kNone},
231  .transform = entity.GetShaderTransform(pass),
232  };
233 }
234 
235 /// @brief Compute the number of vertices to divide each circle into.
236 ///
237 /// @return the number of vertices.
239  bool round) {
240  if (!round) {
241  return 4;
242  }
243 
244  // Note: these values are approximated based on the values returned from
245  // the decomposition of 4 cubics performed by Path::CreatePolyline.
246  if (scaled_radius < 1.0) {
247  return 4;
248  }
249  if (scaled_radius < 2.0) {
250  return 8;
251  }
252  if (scaled_radius < 12.0) {
253  return 24;
254  }
255  if (scaled_radius < 22.0) {
256  return 34;
257  }
258  return std::min(scaled_radius, 140.0f);
259 }
260 
261 // |Geometry|
262 GeometryVertexType PointFieldGeometry::GetVertexType() const {
264 }
265 
266 // |Geometry|
267 std::optional<Rect> PointFieldGeometry::GetCoverage(
268  const Matrix& transform) const {
269  if (points_.size() > 0) {
270  // Doesn't use MakePointBounds as this isn't resilient to points that
271  // all lie along the same axis.
272  auto first = points_.begin();
273  auto last = points_.end();
274  auto left = first->x;
275  auto top = first->y;
276  auto right = first->x;
277  auto bottom = first->y;
278  for (auto it = first + 1; it < last; ++it) {
279  left = std::min(left, it->x);
280  top = std::min(top, it->y);
281  right = std::max(right, it->x);
282  bottom = std::max(bottom, it->y);
283  }
284  auto coverage = Rect::MakeLTRB(left - radius_, top - radius_,
285  right + radius_, bottom + radius_);
286  return coverage.TransformBounds(transform);
287  }
288  return std::nullopt;
289 }
290 
291 } // namespace impeller
impeller::Entity::GetShaderTransform
Matrix GetShaderTransform(const RenderPass &pass) const
Get the vertex shader transform used for drawing this Entity.
Definition: entity.cc:53
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
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:320
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:93
impeller::GeometryVertexType
GeometryVertexType
Definition: geometry.h:49
impeller::PrimitiveType::kTriangle
@ kTriangle
impeller::Entity
Definition: entity.h:21
impeller::PrimitiveType::kTriangleStrip
@ kTriangleStrip
impeller::Point
TPoint< Scalar > Point
Definition: point.h:316
impeller::k2Pi
constexpr float k2Pi
Definition: constants.h:29
impeller::GeometryResult
Definition: geometry.h:20
impeller::IndexType::kNone
@ kNone
Does not use the index buffer.
impeller::Rect
TRect< Scalar > Rect
Definition: rect.h:661
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:33
command_buffer.h
impeller::ContentContext::GetDeviceCapabilities
const Capabilities & GetDeviceCapabilities() const
Definition: content_context.cc:568
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:238
color.h
impeller::TRect< Scalar >::MakeLTRB
constexpr static TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition: rect.h:129
impeller
Definition: aiks_blur_unittests.cc:20
impeller::kPosition
@ kPosition
Definition: geometry.h:50
impeller::ContentContext
Definition: content_context.h:392
impeller::kPiOver4
constexpr float kPiOver4
Definition: constants.h:35
impeller::ContentContext::GetTransientsBuffer
HostBuffer & GetTransientsBuffer() const
Retrieve the currnent host buffer for transient storage.
Definition: content_context.h:833
impeller::Capabilities::SupportsCompute
virtual bool SupportsCompute() const =0
Whether the context backend supports ComputePass.