| 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/geometry/color.h"
8 #include "impeller/renderer/command_buffer.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 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 {
31 .type = PrimitiveType::kTriangleStrip,
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 {
58 .type = PrimitiveType::kTriangleStrip,
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),
158 DefaultUniformAlignment());
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.
238 size_t PointFieldGeometry::ComputeCircleDivisions(Scalar scaled_radius,
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 {
263 return GeometryVertexType::kPosition;
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.