| 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
5 #include "impeller/entity/geometry/point_field_geometry.h"
6
7 #include "impeller/renderer/command_buffer.h"
8 #include "impeller/renderer/compute_command.h"
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 PointFieldGeometry::~PointFieldGeometry() = default;
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 {
33 .type = PrimitiveType::kTriangle,
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 {
61 .type = PrimitiveType::kTriangle,
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),
155 DefaultUniformAlignment());
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.
244 size_t PointFieldGeometry::ComputeCircleDivisions(Scalar scaled_radius,
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 {
269 return GeometryVertexType::kPosition;
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.