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),
37 GeometryResult PointFieldGeometry::GetPositionUVBuffer(
38 Rect texture_coverage,
39 Matrix effect_transform,
40 const ContentContext& renderer,
42 RenderPass& pass)
const {
43 if (renderer.GetDeviceCapabilities().SupportsCompute()) {
44 return GetPositionBufferGPU(renderer, entity, pass, texture_coverage,
48 auto vtx_builder = GetPositionBufferCPU(renderer, entity, pass);
49 if (!vtx_builder.has_value()) {
54 texture_coverage.GetSize(), effect_transform);
56 auto& host_buffer = renderer.GetTransientsBuffer();
59 .vertex_buffer = uv_vtx_builder.CreateVertexBuffer(host_buffer),
60 .transform = entity.GetShaderTransform(pass),
64 std::optional<VertexBufferBuilder<SolidFillVertexShader::PerVertexData>>
65 PointFieldGeometry::GetPositionBufferCPU(
const ContentContext& renderer,
67 RenderPass& pass)
const {
71 auto transform = entity.GetTransform();
72 auto determinant = transform.GetDeterminant();
73 if (determinant == 0) {
77 Scalar min_size = 1.0f / sqrt(std::abs(determinant));
78 Scalar radius = std::max(radius_, min_size);
80 VertexBufferBuilder<SolidFillVertexShader::PerVertexData> vtx_builder;
86 renderer.GetTessellator()->FilledCircle(transform, {}, radius);
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);
93 FML_DCHECK(circle_vertices.size() == generator.GetVertexCount());
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]});
102 for (
auto& vertex : circle_vertices) {
103 vtx_builder.AppendVertex({center + vertex});
107 vtx_builder.Reserve(6 * points_.size() - 2);
108 for (
auto& point : points_) {
109 auto first =
Point(point.x - radius, point.y - radius);
111 if (vtx_builder.HasVertices()) {
112 vtx_builder.AppendVertex(vtx_builder.Last());
113 vtx_builder.AppendVertex({first});
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}});
127 GeometryResult PointFieldGeometry::GetPositionBufferGPU(
128 const ContentContext& renderer,
129 const Entity& entity,
131 std::optional<Rect> texture_coverage,
132 std::optional<Matrix> effect_transform)
const {
133 FML_DCHECK(renderer.GetDeviceCapabilities().SupportsCompute());
137 Scalar determinant = entity.GetTransform().GetDeterminant();
138 if (determinant == 0) {
142 Scalar min_size = 1.0f / sqrt(std::abs(determinant));
143 Scalar radius = std::max(radius_, min_size);
146 entity.GetTransform().GetMaxBasisLength() * radius, round_);
148 size_t points_per_circle = 3 + (vertices_per_geom - 3) * 3;
149 size_t total = points_per_circle * points_.size();
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();
156 BufferView points_data =
157 host_buffer.Emplace(points_.data(), points_.size() *
sizeof(
Point),
160 BufferView geometry_buffer =
161 host_buffer.Emplace(
nullptr, total *
sizeof(
Point),
166 using PS = PointsComputeShader;
168 compute_pass->SetPipeline(renderer.GetPointComputePipeline());
169 compute_pass->SetCommandLabel(
"Points Geometry");
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;
179 PS::BindFrameInfo(*compute_pass, host_buffer.EmplaceUniform(frame_info));
180 PS::BindGeometryData(*compute_pass, geometry_buffer);
181 PS::BindPointData(*compute_pass, points_data);
183 if (!compute_pass->Compute(
ISize(total, 1)).ok()) {
186 output = geometry_buffer;
189 if (texture_coverage.has_value() && effect_transform.has_value()) {
190 BufferView geometry_uv_buffer = host_buffer.Emplace(
191 nullptr, total *
sizeof(Vector4),
194 using UV = UvComputeShader;
196 compute_pass->AddBufferMemoryBarrier();
197 compute_pass->SetCommandLabel(
"UV Geometry");
198 compute_pass->SetPipeline(renderer.GetUvComputePipeline());
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());
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);
210 if (!compute_pass->Compute(
ISize(total, 1)).ok()) {
213 output = geometry_uv_buffer;
216 if (!compute_pass->EncodeCommands()) {
219 if (!renderer.GetContext()
221 ->Submit({std::move(cmd_buffer)})
228 .vertex_buffer = {.vertex_buffer = std::move(output),
229 .vertex_count = total,
231 .transform = entity.GetShaderTransform(pass),
246 if (scaled_radius < 1.0) {
249 if (scaled_radius < 2.0) {
252 if (scaled_radius < 12.0) {
255 if (scaled_radius < 22.0) {
258 return std::min(scaled_radius, 140.0f);
267 std::optional<Rect> PointFieldGeometry::GetCoverage(
268 const Matrix& transform)
const {
269 if (points_.size() > 0) {
272 auto first = points_.begin();
273 auto last = points_.end();
274 auto left = first->x;
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);
285 right + radius_, bottom + radius_);
286 return coverage.TransformBounds(transform);