15 : points_(
std::move(points)), radius_(radius), round_(round) {}
22 return GetPositionBufferGPU(renderer, entity, pass);
24 auto vtx_builder = GetPositionBufferCPU(renderer, entity, pass);
25 if (!vtx_builder.has_value()) {
32 .vertex_buffer = vtx_builder->CreateVertexBuffer(host_buffer),
34 .prevent_overdraw =
false,
38 GeometryResult PointFieldGeometry::GetPositionUVBuffer(
39 Rect texture_coverage,
40 Matrix effect_transform,
41 const ContentContext& renderer,
43 RenderPass& pass)
const {
45 return GetPositionBufferGPU(renderer, entity, pass, texture_coverage,
49 auto vtx_builder = GetPositionBufferCPU(renderer, entity, pass);
50 if (!vtx_builder.has_value()) {
55 texture_coverage.GetSize(), effect_transform);
57 auto& host_buffer = pass.GetTransientsBuffer();
60 .vertex_buffer = uv_vtx_builder.CreateVertexBuffer(host_buffer),
61 .transform = pass.GetOrthographicTransform() * entity.GetTransform(),
62 .prevent_overdraw =
false,
66 std::optional<VertexBufferBuilder<SolidFillVertexShader::PerVertexData>>
67 PointFieldGeometry::GetPositionBufferCPU(
const ContentContext& renderer,
69 RenderPass& pass)
const {
73 auto transform = entity.GetTransform();
74 auto determinant = transform.GetDeterminant();
75 if (determinant == 0) {
79 Scalar min_size = 1.0f / sqrt(std::abs(determinant));
80 Scalar radius = std::max(radius_, min_size);
82 VertexBufferBuilder<SolidFillVertexShader::PerVertexData> vtx_builder;
88 renderer.GetTessellator()->FilledCircle(transform, {}, radius);
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);
95 FML_DCHECK(circle_vertices.size() == generator.GetVertexCount());
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]});
104 for (
auto& vertex : circle_vertices) {
105 vtx_builder.AppendVertex({center + vertex});
109 vtx_builder.Reserve(6 * points_.size() - 2);
110 for (
auto& point : points_) {
111 auto first =
Point(point.x - radius, point.y - radius);
113 if (vtx_builder.HasVertices()) {
114 vtx_builder.AppendVertex(vtx_builder.Last());
115 vtx_builder.AppendVertex({first});
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}});
129 GeometryResult PointFieldGeometry::GetPositionBufferGPU(
130 const ContentContext& renderer,
131 const Entity& entity,
133 std::optional<Rect> texture_coverage,
134 std::optional<Matrix> effect_transform)
const {
135 FML_DCHECK(renderer.GetDeviceCapabilities().SupportsCompute());
139 auto determinant = entity.GetTransform().GetDeterminant();
140 if (determinant == 0) {
144 Scalar min_size = 1.0f / sqrt(std::abs(determinant));
145 Scalar radius = std::max(radius_, min_size);
148 entity.GetTransform().GetMaxBasisLength() * radius, round_);
150 auto points_per_circle = 3 + (vertices_per_geom - 3) * 3;
151 auto total = points_per_circle * points_.size();
153 auto cmd_buffer = renderer.GetContext()->CreateCommandBuffer();
154 auto compute_pass = cmd_buffer->CreateComputePass();
155 auto& host_buffer = compute_pass->GetTransientsBuffer();
158 host_buffer.Emplace(points_.data(), points_.size() *
sizeof(
Point),
161 DeviceBufferDescriptor buffer_desc;
162 buffer_desc.size = total *
sizeof(
Point);
165 auto geometry_buffer = renderer.GetContext()
166 ->GetResourceAllocator()
167 ->CreateBuffer(buffer_desc)
172 using PS = PointsComputeShader;
175 cmd.pipeline = renderer.GetPointComputePipeline();
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;
185 PS::BindFrameInfo(cmd, host_buffer.EmplaceUniform(frame_info));
186 PS::BindGeometryData(cmd, geometry_buffer);
187 PS::BindPointData(cmd, points_data);
189 if (!compute_pass->AddCommand(std::move(cmd))) {
192 output = geometry_buffer;
195 if (texture_coverage.has_value() && effect_transform.has_value()) {
196 DeviceBufferDescriptor buffer_desc;
197 buffer_desc.size = total *
sizeof(Vector4);
200 auto geometry_uv_buffer = renderer.GetContext()
201 ->GetResourceAllocator()
202 ->CreateBuffer(buffer_desc)
205 using UV = UvComputeShader;
209 cmd.pipeline = renderer.GetUvComputePipeline();
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());
217 UV::BindFrameInfo(cmd, host_buffer.EmplaceUniform(frame_info));
218 UV::BindGeometryData(cmd, geometry_buffer);
219 UV::BindGeometryUVData(cmd, geometry_uv_buffer);
221 if (!compute_pass->AddCommand(std::move(cmd))) {
224 output = geometry_uv_buffer;
227 compute_pass->SetGridSize(
ISize(total, 1));
228 compute_pass->SetThreadGroupSize(
ISize(total, 1));
230 if (!compute_pass->EncodeCommands() || !cmd_buffer->SubmitCommands()) {
236 .vertex_buffer = {.vertex_buffer = output,
237 .vertex_count = total,
239 .transform = pass.GetOrthographicTransform() * entity.GetTransform(),
240 .prevent_overdraw =
false,
255 if (scaled_radius < 1.0) {
258 if (scaled_radius < 2.0) {
261 if (scaled_radius < 12.0) {
264 if (scaled_radius < 22.0) {
267 return std::min(scaled_radius, 140.0f);
284 std::optional<Rect> PointFieldGeometry::GetCoverage(
285 const Matrix& transform)
const {
286 if (points_.size() > 0) {
289 auto first = points_.begin();
290 auto last = points_.end();
291 auto left = first->x;
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);
302 right + radius_, bottom + radius_);
303 return coverage.TransformBounds(transform);