chrono_batch_render/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
421 / 426
98.8%
Branches:
325 / 373
87.1%
Functions:
24 / 24
100.0%

scene/tables/meshes.cpp
Line Branch Exec Source
1 // SPDX-License-Identifier: BSD-3-Clause
2 #include "chrono_batch_render/scene/tables/meshes.h"
3
4 #include <algorithm>
5 #include <bit>
6 #include <span>
7
8 #include "chrono_batch_render/device/commands.h"
9 #include "chrono_batch_render/device/queue.h"
10 #include "chrono_batch_render/scene/kernels.h"
11 #include "chrono_batch_render/scene/records/render_tables.h"
12 #include "chrono_batch_render/scene/schema/columns.h"
13 #include "chrono_batch_render/scene/tables/kernels/meshes.h"
14 #include "chrono_batch_render/stage/publish/change_set.h"
15 #include "chrono_batch_render/util/profiler.h"
16
17 namespace chrono::batch_render {
18
19 namespace {
20
21 constexpr uint32_t kPreparedScan = 1024;
22
23 constexpr ColumnRef kInputs[] = {kPointBasedPoints, kMeshFaceVertexIndices, kMeshFaceVertexCounts,
24 kMeshHoleIndices};
25
26 constexpr TableId kTableOutputs[] = {TableId::Meshes, TableId::MeshPoints};
27
28 }
29
30 2616 std::span<const ColumnRef> MeshTable::Inputs() const {
31 2616 return kInputs;
32 }
33
34 399 std::span<const TableId> MeshTable::TableOutputs() const {
35 399 return kTableOutputs;
36 }
37
38 592 void MeshTable::Describe(RenderTables& tables) const {
39
1/2
✓ Branch 0 taken 592 times.
✗ Branch 1 not taken.
592 tables.meshes.address = m_records.Buffer() ? m_records.Buffer()->Address() : 0;
40 592 }
41
42 17 std::optional<MeshShape> MeshTable::Find(uint32_t prim) const {
43 17 const auto found = m_meshes.find(prim);
44
4/4
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 13 times.
33 if (found == m_meshes.end() || !found->second.key) {
45 4 return std::nullopt;
46 }
47 13 const Shape& shape = m_shapes.at(*found->second.key);
48 13 const Topology& topology = m_topologies.at(found->second.key->topology);
49
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 13 times.
13 return MeshShape{shape.blas, shape.pointCount, topology.triangleCount, shape.users};
50 }
51
52 592 void MeshTable::Triangulate() {
53 592 std::vector<Topology*> pending;
54 592 std::vector<TopologyJob> jobs;
55 592 uint32_t faces = 0;
56 592 uint32_t holes = 0;
57
4/4
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 98 times.
✓ Branch 2 taken 142 times.
✓ Branch 3 taken 592 times.
734 for (auto& [key, topology] : m_topologies) {
58
2/2
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 98 times.
142 if (topology.ready) {
59 44 continue;
60 }
61 98 TopologyJob job{};
62 98 job.indices.address = key.indices;
63 98 job.counts.address = key.counts;
64 98 job.holes.address = key.holes;
65 98 job.indexCount = key.indexCount;
66 98 job.faceCount = key.faceCount;
67 98 job.holeCount = key.holeCount;
68 98 job.firstFace = faces;
69 98 faces += key.faceCount;
70
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 96 times.
98 holes = std::max(holes, key.holeCount);
71
1/1
✓ Branch 1 taken 98 times.
98 jobs.push_back(job);
72
1/1
✓ Branch 1 taken 98 times.
98 pending.push_back(&topology);
73 }
74
2/2
✓ Branch 0 taken 94 times.
✓ Branch 1 taken 498 times.
592 if (jobs.empty()) {
75 return;
76 }
77
5/6
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 92 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 2 times.
✓ Branch 10 taken 94 times.
94 CHRONO_BATCH_RENDER_ZONE("Geometry/Triangulate");
78
1/1
✓ Branch 1 taken 94 times.
94 m_stats.triangulated += uint32_t(jobs.size());
79
1/1
✓ Branch 1 taken 94 times.
94 ColumnStore& store = m_structure.GetColumnStore();
80
1/1
✓ Branch 1 taken 94 times.
94 device::Device& device = store.GetDevice();
81
1/1
✓ Branch 1 taken 94 times.
94 device::Queue& queue = device.GetQueue();
82 940 const auto scratch = [&](uint64_t words, const char* label) {
83
1/1
✓ Branch 1 taken 846 times.
846 return device.CreateBuffer({.bytes = std::max<uint64_t>(words, 1) * 4, .label = label});
84
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 846 times.
1692 };
85 376 const auto scan = [&](uint32_t count) -> const algorithms::Scan& {
86
2/2
✓ Branch 0 taken 78 times.
✓ Branch 1 taken 204 times.
282 if (count > m_scanCapacity) {
87
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 36 times.
78 m_scanCapacity = std::bit_ceil(count);
88 78 m_scan = std::make_unique<algorithms::Scan>(device, m_scanCapacity);
89 }
90 282 return *m_scan;
91 94 };
92 658 const auto dispatch = [&](device::CommandEncoder& encoder, const char* entry,
93 const TriangulateParams& params, uint32_t threads) {
94 564 encoder.Dispatch(Kernels().Entry(entry), params,
95 564 {.x = device::GroupCount(threads, kTopologyGroup)});
96 658 };
97
98
1/1
✓ Branch 1 taken 94 times.
94 const Ref<device::Buffer> jobBuffer =
99
1/1
✓ Branch 1 taken 94 times.
94 device.CreateBuffer({.label = "topology jobs"}, std::span<const TopologyJob>(jobs));
100 94 const Ref<device::Buffer> work = scratch(faces, "face work");
101
1/1
✓ Branch 1 taken 94 times.
94 const Ref<device::Buffer> holeFlags = scratch(faces, "face holes");
102
1/1
✓ Branch 1 taken 94 times.
94 const Ref<device::Buffer> faceStarts = scratch(faces, "face starts");
103
1/1
✓ Branch 1 taken 94 times.
94 const Ref<device::Buffer> triangleStarts = scratch(faces, "face triangle starts");
104
1/1
✓ Branch 1 taken 94 times.
94 const Ref<device::Buffer> sizes = scratch(jobs.size() * 2, "topology sizes");
105 94 TriangulateParams params{};
106
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 92 times.
94 params.jobs.address = jobBuffer->Address();
107 94 params.work.address = work->Address();
108 94 params.holes.address = holeFlags->Address();
109 94 params.faceStarts.address = faceStarts->Address();
110 94 params.triangleStarts.address = triangleStarts->Address();
111 94 params.sizes.address = sizes->Address();
112
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 92 times.
94 params.jobCount = uint32_t(jobs.size());
113 94 params.faceCount = faces;
114 94 params.holeCount = holes;
115
4/5
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 92 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 2 times.
94 static const profiler::ZoneId zone = profiler::ZoneId::Named("Geometry/Triangulate");
116 94 {
117
1/1
✓ Branch 1 taken 94 times.
94 device::CommandEncoder encoder = queue.CreateEncoder();
118 94 {
119
1/1
✓ Branch 1 taken 94 times.
94 const device::ZoneScope scope(encoder, zone);
120
1/1
✓ Branch 1 taken 94 times.
94 encoder.Fill(*sizes);
121
1/1
✓ Branch 1 taken 94 times.
94 encoder.Fill(*holeFlags);
122
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 92 times.
94 if (holes > 0) {
123
3/3
✓ Branch 1 taken 2 times.
✓ Branch 4 taken 2 times.
✓ Branch 7 taken 2 times.
4 encoder.Dispatch(
124
2/2
✓ Branch 1 taken 2 times.
✓ Branch 4 taken 2 times.
2 Kernels().Entry("MarkHoles"), params,
125
1/1
✓ Branch 1 taken 2 times.
2 {.x = device::GroupCount(holes, kTopologyGroup), .y = params.jobCount});
126 }
127
1/1
✓ Branch 1 taken 94 times.
94 dispatch(encoder, "FaceSizes", params, faces);
128
2/2
✓ Branch 1 taken 94 times.
✓ Branch 4 taken 94 times.
94 scan(faces).ExclusiveSum(encoder, *work, *faceStarts, faces);
129
1/1
✓ Branch 1 taken 94 times.
94 dispatch(encoder, "FaceTriangles", params, faces);
130
2/2
✓ Branch 1 taken 94 times.
✓ Branch 4 taken 94 times.
94 scan(faces).ExclusiveSum(encoder, *work, *triangleStarts, faces);
131
1/1
✓ Branch 1 taken 94 times.
94 dispatch(encoder, "TopologySizes", params, params.jobCount);
132 94 }
133
2/2
✓ Branch 1 taken 94 times.
✓ Branch 4 taken 94 times.
94 queue.Submit(encoder.Finish());
134 ✗ }
135
1/3
✓ Branch 1 taken 94 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
94 std::vector<uint32_t> counted(jobs.size() * 2);
136
2/2
✓ Branch 1 taken 94 times.
✓ Branch 4 taken 94 times.
94 queue.Read(sizes->Slice(0, counted.size() * 4), counted.data());
137
138
1/1
✓ Branch 1 taken 94 times.
94 ArrayPool& ints = store.Arrays(ColumnType::IntArray);
139 94 std::vector<TopologyJob> emitted;
140 94 uint32_t corners = 0;
141 94 uint32_t points = 0;
142
2/2
✓ Branch 0 taken 98 times.
✓ Branch 1 taken 94 times.
192 for (uint32_t i = 0; i < jobs.size(); ++i) {
143
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 98 times.
98 Topology& topology = *pending[i];
144 98 topology.ready = true;
145 98 topology.triangleCount = counted[i * 2];
146 98 topology.covered = counted[i * 2 + 1];
147
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 98 times.
98 if (topology.triangleCount == 0) {
148 ✗ continue;
149 }
150 98 const uint32_t count = topology.triangleCount;
151
1/1
✓ Branch 1 taken 98 times.
98 topology.triangles = ints.Allocate(count * 3);
152
1/1
✓ Branch 1 taken 98 times.
98 topology.corners = ints.Allocate(count * 3);
153
1/1
✓ Branch 1 taken 98 times.
98 topology.faces = ints.Allocate(count);
154
1/1
✓ Branch 1 taken 98 times.
98 topology.starts = ints.Allocate(topology.covered + 1);
155
1/1
✓ Branch 1 taken 98 times.
98 topology.adjacency = ints.Allocate(count * 3);
156
1/1
✓ Branch 1 taken 98 times.
98 TopologyJob& job = jobs[i];
157 98 job.triangles.address = topology.triangles.address;
158 98 job.corners.address = topology.corners.address;
159 98 job.faces.address = topology.faces.address;
160 98 job.starts.address = topology.starts.address;
161 98 job.adjacency.address = topology.adjacency.address;
162 98 job.triangleCount = count;
163 98 job.covered = topology.covered;
164 98 job.firstCorner = corners;
165 98 job.firstPoint = points;
166 98 corners += count * 3;
167 98 points += topology.covered;
168
1/1
✓ Branch 1 taken 98 times.
98 emitted.push_back(job);
169 }
170
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 94 times.
94 if (emitted.empty()) {
171 ✗ return;
172 }
173
2/2
✓ Branch 0 taken 89 times.
✓ Branch 1 taken 5 times.
94 if (corners > m_sortCapacity) {
174
1/2
✓ Branch 0 taken 89 times.
✗ Branch 1 not taken.
89 m_sortCapacity = std::bit_ceil(corners);
175
1/1
✓ Branch 1 taken 89 times.
89 m_sort = std::make_unique<algorithms::RadixSort>(device, m_sortCapacity, 4);
176 }
177
1/1
✓ Branch 1 taken 94 times.
94 const Ref<device::Buffer> updated =
178
1/1
✓ Branch 1 taken 94 times.
94 device.CreateBuffer({.label = "topology jobs"}, std::span<const TopologyJob>(jobs));
179 94 const Ref<device::Buffer> emittedBuffer =
180
2/2
✓ Branch 1 taken 94 times.
✓ Branch 4 taken 94 times.
94 device.CreateBuffer({.label = "emitted topologies"}, std::span<const TopologyJob>(emitted));
181 94 const Ref<device::Buffer> keys = scratch(corners, "corner points");
182
1/1
✓ Branch 1 taken 94 times.
94 const Ref<device::Buffer> values = scratch(corners, "corner order");
183
1/1
✓ Branch 1 taken 94 times.
94 const Ref<device::Buffer> histogram = scratch(points, "point triangle counts");
184
1/1
✓ Branch 1 taken 94 times.
94 const Ref<device::Buffer> pointStarts = scratch(points, "point starts");
185
1/1
✓ Branch 1 taken 94 times.
94 params.jobs.address = updated->Address();
186 94 params.emitted.address = emittedBuffer->Address();
187 94 params.keys.address = keys->Address();
188 94 params.values.address = values->Address();
189 94 params.histogram.address = histogram->Address();
190 94 params.pointStarts.address = pointStarts->Address();
191
1/1
✓ Branch 1 taken 94 times.
94 params.emittedCount = uint32_t(emitted.size());
192 94 params.cornerCount = corners;
193 94 params.pointCount = points;
194
1/1
✓ Branch 1 taken 94 times.
94 device::CommandEncoder encoder = queue.CreateEncoder();
195 94 {
196
1/1
✓ Branch 1 taken 94 times.
94 const device::ZoneScope scope(encoder, zone);
197
1/1
✓ Branch 1 taken 94 times.
94 encoder.Fill(*histogram);
198
1/1
✓ Branch 1 taken 94 times.
94 dispatch(encoder, "EmitTriangles", params, faces);
199
2/3
✓ Branch 0 taken 94 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 94 times.
188 m_sort->Sort(encoder, *keys, *values, corners,
200
2/4
✓ Branch 0 taken 94 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 94 times.
✗ Branch 3 not taken.
188 std::max<int>(1, std::bit_width(points - 1)));
201
2/2
✓ Branch 1 taken 94 times.
✓ Branch 4 taken 94 times.
94 scan(points).ExclusiveSum(encoder, *histogram, *pointStarts, points);
202
1/1
✓ Branch 1 taken 94 times.
94 dispatch(encoder, "WriteAdjacency", params, corners);
203
1/1
✓ Branch 1 taken 94 times.
94 dispatch(encoder, "WriteStarts", params, points);
204 94 }
205
2/2
✓ Branch 1 taken 94 times.
✓ Branch 4 taken 94 times.
94 queue.Submit(encoder.Finish());
206 2190 }
207
208 1184 void MeshTable::Prune() {
209
2/2
✓ Branch 0 taken 117 times.
✓ Branch 1 taken 1067 times.
1184 if (!m_stale) {
210 return;
211 }
212 117 m_stale = false;
213 117 ColumnStore& store = m_structure.GetColumnStore();
214 117 ArrayPool& ints = store.Arrays(ColumnType::IntArray);
215 117 std::erase_if(m_shapes, [&](const auto& entry) {
216
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 128 times.
140 if (entry.second.users > 0) {
217 return false;
218 }
219 12 store.Arrays(ColumnType::Float3Array).Free(entry.second.normals);
220 12 store.Arrays(ColumnType::Float3Array).Free(entry.second.bounds);
221 12 return true;
222 });
223 117 std::erase_if(m_topologies, [&](const auto& entry) {
224
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 113 times.
122 if (entry.second.users > 0) {
225 return false;
226 }
227 9 const Topology& topology = entry.second;
228
2/2
✓ Branch 0 taken 45 times.
✓ Branch 1 taken 9 times.
54 for (const ArrayRef& array : {topology.triangles, topology.corners, topology.faces,
229 topology.starts, topology.adjacency}) {
230 45 ints.Free(array);
231 }
232 9 return true;
233 });
234 }
235
236 592 void MeshTable::Settle() {
237
3/4
✓ Branch 0 taken 592 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 101 times.
✓ Branch 3 taken 491 times.
592 m_stale = m_stale || !m_placing.empty();
238
2/2
✓ Branch 0 taken 149 times.
✓ Branch 1 taken 592 times.
741 for (const auto& placing : m_placing) {
239 149 m_topologies.try_emplace(placing.second.topology);
240 }
241 592 Triangulate();
242 592 ColumnStore& store = m_structure.GetColumnStore();
243
2/2
✓ Branch 1 taken 149 times.
✓ Branch 2 taken 592 times.
741 for (const auto& [prim, key] : m_placing) {
244 149 Mesh& mesh = m_meshes.at(prim);
245 149 Topology& topology = m_topologies.at(key.topology);
246
3/4
✓ Branch 0 taken 149 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 147 times.
✓ Branch 3 taken 2 times.
149 if (topology.triangleCount > 0 && topology.covered <= key.pointCount) {
247 147 const auto [entry, fresh] = m_shapes.try_emplace(key);
248
2/2
✓ Branch 0 taken 111 times.
✓ Branch 1 taken 36 times.
147 Shape& shape = entry->second;
249
2/2
✓ Branch 0 taken 111 times.
✓ Branch 1 taken 36 times.
147 if (fresh) {
250 111 const ArrayRef points = store.Array(kPointBasedPoints, mesh.pointsRow);
251 111 ArrayPool& float3s = store.Arrays(ColumnType::Float3Array);
252 111 shape.pointCount = points.count;
253 111 shape.normals = float3s.Allocate(points.count);
254 111 shape.bounds = float3s.Allocate(2);
255 444 shape.blas = m_builder.CreateBlas(device::AccelTriangles{
256 .vertices =
257 111 float3s.Range(points).Slice(0, uint64_t(points.count) * sizeof(Float3)),
258 .stride = sizeof(Float3),
259 111 .vertexCount = points.count,
260 111 .indices = store.Arrays(ColumnType::IntArray)
261 111 .Range(topology.triangles)
262 111 .Slice(0, uint64_t(topology.triangleCount) * 12),
263 111 .count = topology.triangleCount,
264
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 111 times.
111 .refittable = true});
265 111 shape.build = true;
266 }
267 147 ++shape.users;
268 147 ++topology.users;
269 147 mesh.key = key;
270 147 mesh.shape = &shape;
271 }
272 149 Link(mesh);
273 }
274 592 m_placing.clear();
275 592 }
276
277 151 void MeshTable::Place(uint32_t prim) {
278 151 const Prims& prims = m_structure.GetPrims();
279 151 Mesh mesh;
280 151 mesh.prim = prim;
281 151 mesh.generation = prims.Generation(prim);
282 151 mesh.pointsRow = prims.Row(prim, SchemaId::PointBased);
283 151 mesh.meshRow = prims.Row(prim, SchemaId::Mesh);
284 151 m_byPointsRow[mesh.pointsRow] = prim;
285 151 m_byMeshRow[mesh.meshRow] = prim;
286
287 151 m_meshes[prim] = mesh;
288 151 const ShapeKey key = Key(mesh);
289
5/6
✓ Branch 0 taken 150 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 150 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 time.
✓ Branch 5 taken 149 times.
151 if (key.pointCount == 0 || key.topology.indexCount == 0 || key.topology.faceCount == 0) {
290 2 Link(mesh);
291 2 return;
292 }
293 149 m_topologies.try_emplace(key.topology);
294 149 m_placing.emplace_back(prim, key);
295 }
296
297 151 MeshTable::ShapeKey MeshTable::Key(const Mesh& mesh) const {
298 151 const ColumnStore& store = m_structure.GetColumnStore();
299 151 const ArrayRef points = store.Array(kPointBasedPoints, mesh.pointsRow);
300 151 const ArrayRef indices = store.Array(kMeshFaceVertexIndices, mesh.meshRow);
301 151 const ArrayRef counts = store.Array(kMeshFaceVertexCounts, mesh.meshRow);
302 151 const ArrayRef holes = store.Array(kMeshHoleIndices, mesh.meshRow);
303 151 return {
304 151 points.address,
305 151 points.count,
306 151 {indices.address, indices.count, counts.address, counts.count, holes.address, holes.count}};
307 }
308
309 19 void MeshTable::Release(Mesh& mesh) {
310
1/2
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
19 if (mesh.key) {
311 19 --m_shapes.at(*mesh.key).users;
312 19 --m_topologies.at(mesh.key->topology).users;
313
1/2
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
19 mesh.key.reset();
314 19 mesh.shape = nullptr;
315 19 m_stale = true;
316 }
317 19 }
318
319 151 void MeshTable::Link(const Mesh& mesh) {
320 151 MeshRecord record{};
321
2/2
✓ Branch 0 taken 147 times.
✓ Branch 1 taken 4 times.
151 if (mesh.key) {
322 147 const Shape& shape = m_shapes.at(*mesh.key);
323 147 const Topology& topology = m_topologies.at(mesh.key->topology);
324 147 record.blas = shape.blas->Handle();
325 147 record.corners = topology.corners;
326 147 record.faces = topology.faces;
327 147 record.starts = topology.starts;
328 147 record.adjacency = topology.adjacency;
329 147 record.normals = shape.normals;
330 147 record.bounds = shape.bounds;
331 }
332
3/4
✓ Branch 1 taken 147 times.
✓ Branch 2 taken 4 times.
✓ Branch 4 taken 147 times.
✗ Branch 5 not taken.
151 if (m_records.Set(mesh.meshRow, record) && m_structure.GetPrims().Alive(mesh.prim)) {
333 147 m_relinked.push_back(m_structure.GetPrims().Record(mesh.prim).world);
334 }
335 151 }
336
337 592 void MeshTable::Run(const ChangeSet& changes, TableVersions& versions) {
338
4/5
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 587 times.
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 5 times.
592 CHRONO_BATCH_RENDER_ZONE("Geometry/Run");
339
2/2
✓ Branch 0 taken 320 times.
✓ Branch 1 taken 272 times.
592 const Prims& prims = m_structure.GetPrims();
340 592 m_stats.built = 0;
341 592 m_stats.refit = 0;
342 592 m_stats.triangulated = 0;
343 7921 const auto isMesh = [&](uint32_t prim) {
344
4/4
✓ Branch 1 taken 7313 times.
✓ Branch 2 taken 16 times.
✓ Branch 5 taken 7177 times.
✓ Branch 6 taken 136 times.
7329 return prims.Alive(prim) && IsA(prims.Record(prim).schema, SchemaId::Mesh);
345 592 };
346 596 const auto forget = [&](uint32_t prim) {
347 4 Mesh& mesh = m_meshes.at(prim);
348 4 Release(mesh);
349 4 m_byPointsRow.erase(mesh.pointsRow);
350 4 m_byMeshRow.erase(mesh.meshRow);
351 4 m_meshes.erase(prim);
352 596 };
353
354 592 std::vector<uint32_t> placed;
355
2/2
✓ Branch 0 taken 320 times.
✓ Branch 1 taken 272 times.
592 if (changes.resync) {
356
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 320 times.
320 while (!m_meshes.empty()) {
357
0/1
✗ Branch 2 not taken.
✗ forget(m_meshes.begin()->first);
358 }
359
1/1
✓ Branch 1 taken 320 times.
320 Prune();
360
2/2
✓ Branch 0 taken 2078 times.
✓ Branch 1 taken 320 times.
2398 for (uint32_t prim = 0; prim < prims.Size(); ++prim) {
361
3/3
✓ Branch 1 taken 2078 times.
✓ Branch 3 taken 135 times.
✓ Branch 4 taken 1943 times.
2078 if (isMesh(prim)) {
362
1/1
✓ Branch 1 taken 135 times.
135 Place(prim);
363
1/1
✓ Branch 1 taken 135 times.
135 placed.push_back(prim);
364 }
365 }
366 } else {
367
2/2
✓ Branch 0 taken 145 times.
✓ Branch 1 taken 272 times.
417 for (const StructureEntry& entry : changes.removed) {
368 145 const auto found = m_meshes.find(entry.prim);
369
3/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 141 times.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
149 if (found != m_meshes.end() && found->second.generation == entry.generation) {
370
1/1
✓ Branch 1 taken 4 times.
4 forget(entry.prim);
371 }
372 }
373
2/2
✓ Branch 0 taken 5251 times.
✓ Branch 1 taken 272 times.
5523 for (const StructureEntry& entry : changes.added) {
374
5/6
✓ Branch 1 taken 5251 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 5250 times.
✓ Branch 6 taken 1 time.
✓ Branch 8 taken 1 time.
✗ Branch 9 not taken.
5251 if (isMesh(entry.prim) && prims.Generation(entry.prim) == entry.generation) {
375
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
1 if (m_meshes.contains(entry.prim)) {
376
0/1
✗ Branch 1 not taken.
✗ forget(entry.prim);
377 }
378
1/1
✓ Branch 1 taken 1 time.
1 Place(entry.prim);
379
1/1
✓ Branch 1 taken 1 time.
1 placed.push_back(entry.prim);
380 }
381 }
382
5/6
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 269 times.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 3 times.
✓ Branch 10 taken 272 times.
272 CHRONO_BATCH_RENDER_ZONE("Geometry/Changes");
383 272 std::ranges::sort(placed);
384 1360 const auto collect = [&](ColumnRef column, const auto& owners, std::vector<uint32_t>& out) {
385
2/2
✓ Branch 2 taken 141 times.
✓ Branch 3 taken 1088 times.
1229 for (const uint32_t row : changes.ArrayRows(column)) {
386
2/2
✓ Branch 0 taken 35 times.
✓ Branch 1 taken 106 times.
141 const auto owner = owners.find(row);
387
4/4
✓ Branch 0 taken 35 times.
✓ Branch 1 taken 106 times.
✓ Branch 3 taken 32 times.
✓ Branch 4 taken 3 times.
176 if (owner != owners.end() && !std::ranges::binary_search(placed, owner->second)) {
388 32 out.push_back(owner->second);
389 }
390 }
391 1088 std::ranges::sort(out);
392 1088 out.erase(std::ranges::unique(out).begin(), out.end());
393 1360 };
394 272 std::vector<uint32_t> moved;
395 272 std::vector<uint32_t> reshaped;
396
1/1
✓ Branch 1 taken 272 times.
272 collect(kPointBasedPoints, m_byPointsRow, moved);
397
1/1
✓ Branch 1 taken 272 times.
272 collect(kMeshFaceVertexIndices, m_byMeshRow, reshaped);
398
1/1
✓ Branch 1 taken 272 times.
272 collect(kMeshFaceVertexCounts, m_byMeshRow, reshaped);
399
1/1
✓ Branch 1 taken 272 times.
272 collect(kMeshHoleIndices, m_byMeshRow, reshaped);
400 272 const ColumnStore& store = m_structure.GetColumnStore();
401 272 std::vector<uint32_t> rekeyed;
402
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 272 times.
295 for (const uint32_t prim : moved) {
403
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 22 times.
23 if (std::ranges::binary_search(reshaped, prim)) {
404 1 continue;
405 }
406
1/1
✓ Branch 1 taken 22 times.
22 Mesh& mesh = m_meshes.at(prim);
407
1/1
✓ Branch 1 taken 22 times.
22 const ArrayRef points = store.Array(kPointBasedPoints, mesh.pointsRow);
408
4/6
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 10 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 12 times.
22 if (mesh.shape && points.address == mesh.key->points &&
409
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 points.count == mesh.key->pointCount) {
410 12 mesh.shape->refit = true;
411 } else {
412
1/1
✓ Branch 1 taken 10 times.
10 Release(mesh);
413
1/1
✓ Branch 1 taken 10 times.
10 rekeyed.push_back(prim);
414 }
415 }
416
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 272 times.
277 for (const uint32_t prim : reshaped) {
417
2/2
✓ Branch 1 taken 5 times.
✓ Branch 4 taken 5 times.
5 Release(m_meshes.at(prim));
418 }
419
1/1
✓ Branch 1 taken 272 times.
272 Prune();
420
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 272 times.
277 for (const uint32_t prim : reshaped) {
421
1/1
✓ Branch 1 taken 5 times.
5 Place(prim);
422 }
423
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 272 times.
282 for (const uint32_t prim : rekeyed) {
424
1/1
✓ Branch 1 taken 10 times.
10 Place(prim);
425 }
426 272 }
427
1/1
✓ Branch 1 taken 592 times.
592 Settle();
428
1/1
✓ Branch 1 taken 592 times.
592 Prune();
429 592 std::vector<uint32_t> updated;
430
1/1
✓ Branch 1 taken 592 times.
592 Update(updated);
431
2/2
✓ Branch 1 taken 592 times.
✓ Branch 4 taken 592 times.
592 m_records.Reserve(m_structure.GetColumnStore().Schema(SchemaId::Mesh).Capacity());
432
1/1
✓ Branch 1 taken 592 times.
592 const bool replaced = m_records.Upload(m_structure.GetColumnStore().GetDevice());
433
2/2
✓ Branch 0 taken 320 times.
✓ Branch 1 taken 272 times.
592 if (replaced) {
434
1/1
✓ Branch 1 taken 320 times.
320 m_relinked = {kGlobalWorld};
435 }
436
2/2
✓ Branch 0 taken 333 times.
✓ Branch 1 taken 259 times.
592 if (!m_relinked.empty()) {
437
1/1
✓ Branch 1 taken 333 times.
333 versions.Record(TableId::Meshes, m_relinked);
438 }
439
2/2
✓ Branch 0 taken 110 times.
✓ Branch 1 taken 482 times.
592 if (!updated.empty()) {
440
1/1
✓ Branch 1 taken 110 times.
110 versions.Record(TableId::MeshPoints, updated);
441 }
442 592 m_relinked.clear();
443 592 }
444
445 592 void MeshTable::Update(std::vector<uint32_t>& updated) {
446 592 std::vector<Ref<bvh::Blas>> built;
447 592 std::vector<Ref<bvh::Blas>> refit;
448 592 std::vector<NormalsShape> normals;
449 592 uint32_t total = 0;
450
4/4
✓ Branch 0 taken 49 times.
✓ Branch 1 taken 111 times.
✓ Branch 2 taken 160 times.
✓ Branch 3 taken 592 times.
752 for (auto& [key, shape] : m_shapes) {
451
4/4
✓ Branch 0 taken 49 times.
✓ Branch 1 taken 111 times.
✓ Branch 2 taken 37 times.
✓ Branch 3 taken 12 times.
160 if (!shape.build && !shape.refit) {
452 37 continue;
453 }
454
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 111 times.
123 shape.updated = Runs();
455
3/3
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 111 times.
✓ Branch 3 taken 123 times.
135 (shape.build ? built : refit).push_back(shape.blas);
456
1/1
✓ Branch 1 taken 123 times.
123 const Topology& topology = m_topologies.at(key.topology);
457 123 NormalsShape entry{};
458 123 entry.points.address = key.points;
459 123 entry.triangles.address = topology.triangles.address;
460 123 entry.starts.address = topology.starts.address;
461 123 entry.adjacency.address = topology.adjacency.address;
462 123 entry.normals.address = shape.normals.address;
463 123 entry.bounds.address = shape.bounds.address;
464 123 entry.first = total;
465 123 entry.count = shape.pointCount;
466 123 entry.covered = topology.covered;
467
1/1
✓ Branch 1 taken 123 times.
123 normals.push_back(entry);
468 123 total += shape.pointCount;
469 123 shape.build = false;
470 123 shape.refit = false;
471 }
472
2/2
✓ Branch 0 taken 482 times.
✓ Branch 1 taken 110 times.
592 m_stats.built = uint32_t(built.size());
473 592 m_stats.refit = uint32_t(refit.size());
474
2/2
✓ Branch 0 taken 482 times.
✓ Branch 1 taken 110 times.
592 m_stats.meshes = uint32_t(m_meshes.size());
475
2/2
✓ Branch 0 taken 482 times.
✓ Branch 1 taken 110 times.
592 m_stats.shapes = uint32_t(m_shapes.size());
476 592 m_stats.topologies = uint32_t(m_topologies.size());
477
2/2
✓ Branch 0 taken 482 times.
✓ Branch 1 taken 110 times.
592 if (normals.empty()) {
478 482 return;
479 }
480 110 const Prims& prims = m_structure.GetPrims();
481
3/4
✓ Branch 0 taken 173 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 173 times.
✓ Branch 3 taken 110 times.
456 for (const auto& [prim, mesh] : m_meshes) {
482
5/7
✓ Branch 0 taken 173 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 161 times.
✓ Branch 3 taken 12 times.
✓ Branch 5 taken 161 times.
✓ Branch 7 taken 161 times.
✗ Branch 8 not taken.
173 if (mesh.shape && mesh.shape->updated == Runs() && prims.Alive(prim)) {
483
2/2
✓ Branch 1 taken 161 times.
✓ Branch 4 taken 161 times.
161 updated.push_back(prims.Record(prim).world);
484 }
485 }
486
487
4/5
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 108 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 2 times.
110 static const profiler::ZoneId blasZone = profiler::ZoneId::Named("Geometry/Blas");
488
4/5
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 108 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 2 times.
110 static const profiler::ZoneId normalsZone = profiler::ZoneId::Named("Geometry/Normals");
489
1/1
✓ Branch 1 taken 110 times.
110 ColumnStore& store = m_structure.GetColumnStore();
490
1/1
✓ Branch 1 taken 110 times.
110 device::Device& device = store.GetDevice();
491
1/1
✓ Branch 1 taken 110 times.
110 store.Flush();
492
1/1
✓ Branch 1 taken 110 times.
110 const Ref<device::Buffer> table =
493
1/1
✓ Branch 1 taken 110 times.
110 device.CreateBuffer({.label = "normals shapes"}, std::span<const NormalsShape>(normals));
494 110 device::Queue& queue = device.GetQueue();
495
1/1
✓ Branch 1 taken 110 times.
110 device::CommandEncoder encoder = queue.CreateEncoder();
496
1/1
✓ Branch 1 taken 110 times.
110 encoder.BeginZone(blasZone);
497
1/1
✓ Branch 1 taken 110 times.
110 m_builder.Build(encoder, built);
498
1/1
✓ Branch 1 taken 110 times.
110 m_builder.Refit(encoder, refit);
499
1/1
✓ Branch 1 taken 110 times.
110 encoder.EndZone();
500
1/1
✓ Branch 1 taken 110 times.
110 encoder.BeginZone(normalsZone);
501
1/1
✓ Branch 1 taken 110 times.
110 const uint64_t extentWords = uint64_t(normals.size()) * 3;
502 110 const Ref<device::Buffer> extents = device.CreateBuffer(
503
1/1
✓ Branch 1 taken 110 times.
110 {.bytes = extentWords * 2 * sizeof(uint32_t), .label = "shape extents"});
504
1/1
✓ Branch 1 taken 110 times.
110 encoder.Fill(extents->Slice(0, extentWords * sizeof(uint32_t)), ~0u);
505
2/2
✓ Branch 1 taken 110 times.
✓ Branch 4 taken 110 times.
110 encoder.Fill(extents->Slice(extentWords * sizeof(uint32_t)), 0u);
506 110 NormalsParams params{};
507
1/1
✓ Branch 1 taken 110 times.
110 params.shapes.address = table->Address();
508 110 params.extents.address = extents->Address();
509
1/1
✓ Branch 1 taken 110 times.
110 params.shapeCount = uint32_t(normals.size());
510 110 params.total = total;
511
3/3
✓ Branch 1 taken 110 times.
✓ Branch 4 taken 110 times.
✓ Branch 7 taken 110 times.
110 encoder.Dispatch(Kernels().Entry("SmoothNormals"), params,
512
1/1
✓ Branch 1 taken 110 times.
110 {.x = device::GroupCount(total, kNormalsGroup)});
513
3/3
✓ Branch 1 taken 110 times.
✓ Branch 4 taken 110 times.
✓ Branch 7 taken 110 times.
110 encoder.Dispatch(Kernels().Entry("FinishShapeBounds"), params,
514
1/1
✓ Branch 1 taken 110 times.
110 {.x = device::GroupCount(params.shapeCount, kNormalsGroup)});
515
1/1
✓ Branch 1 taken 110 times.
110 encoder.EndZone();
516
3/6
✓ Branch 1 taken 110 times.
✓ Branch 4 taken 110 times.
✓ Branch 7 taken 110 times.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
110 queue.Submit(encoder.Finish());
517 1252 }
518
519 1013 void MeshTable::Hold(std::vector<Ref<const RefCounted>>& held) const {
520
4/8
✓ Branch 0 taken 1013 times.
✓ Branch 1 taken 1013 times.
✓ Branch 2 taken 1012 times.
✓ Branch 3 taken 1 time.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
2026 HoldBuffers(held, {m_records.Buffer()});
521
3/4
✓ Branch 0 taken 236 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 236 times.
✓ Branch 3 taken 1013 times.
1249 for (const auto& [key, shape] : m_shapes) {
522
1/2
✓ Branch 0 taken 236 times.
✗ Branch 1 not taken.
236 if (shape.blas) {
523 236 held.emplace_back(shape.blas);
524 }
525 }
526 2026 }
527
528 353 void MeshTable::Prepare() {
529 353 Kernels();
530
1/2
✓ Branch 0 taken 353 times.
✗ Branch 1 not taken.
353 if (!m_scan) {
531 353 m_scanCapacity = kPreparedScan;
532 353 m_scan = std::make_unique<algorithms::Scan>(m_structure.GetColumnStore().GetDevice(),
533 353 m_scanCapacity);
534 }
535 353 }
536
537 1139 device::Program& MeshTable::Kernels() {
538
2/2
✓ Branch 0 taken 392 times.
✓ Branch 1 taken 747 times.
1139 if (!m_program) {
539
2/2
✓ Branch 2 taken 392 times.
✓ Branch 5 taken 392 times.
1176 m_program = m_structure.GetColumnStore().GetDevice().CreateProgram(
540 {.modules = {"chrono_batch_render.scene.tables.kernels.meshes"},
541 .library = &kSceneKernels,
542 .entries = {"MarkHoles", "FaceSizes", "FaceTriangles", "TopologySizes",
543 "EmitTriangles", "WriteAdjacency", "WriteStarts", "SmoothNormals",
544 "FinishShapeBounds"},
545
2/3
✓ Branch 1 taken 392 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 392 times.
784 .label = "geometry"});
546 }
547 1139 return *m_program;
548 784 }
549
550 }
551