chrono_batch_render/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
209 / 218
95.9%
Branches:
195 / 213
91.5%
Functions:
11 / 12
91.7%

scene/tables/points.cpp
Line Branch Exec Source
1 // SPDX-License-Identifier: BSD-3-Clause
2 #include "chrono_batch_render/scene/tables/points.h"
3
4 #include <algorithm>
5 #include <bit>
6
7 #include "chrono_batch_render/device/commands.h"
8 #include "chrono_batch_render/device/queue.h"
9 #include "chrono_batch_render/scene/kernels.h"
10 #include "chrono_batch_render/scene/records/render_tables.h"
11 #include "chrono_batch_render/scene/schema/columns.h"
12 #include "chrono_batch_render/scene/tables/kernels/points.h"
13 #include "chrono_batch_render/stage/publish/change_set.h"
14 #include "chrono_batch_render/util/profiler.h"
15
16 namespace chrono::batch_render {
17
18 namespace {
19
20 constexpr ColumnRef kInputs[] = {kPointBasedPoints, kPointBasedNormals, kPointsWidths, kPointsIds};
21
22 constexpr TableId kTableOutputs[] = {TableId::Points, TableId::PointsMoved};
23
24 }
25
26 2456 std::span<const ColumnRef> PointsTable::Inputs() const {
27 2456 return kInputs;
28 }
29
30 358 std::span<const TableId> PointsTable::TableOutputs() const {
31 358 return kTableOutputs;
32 }
33
34 556 void PointsTable::Describe(RenderTables& tables) const {
35
1/2
✓ Branch 0 taken 556 times.
✗ Branch 1 not taken.
556 tables.points.address = m_records.Buffer() ? m_records.Buffer()->Address() : 0;
36 556 }
37
38 48 void PointsTable::Place(uint32_t prim) {
39
1/1
✓ Branch 1 taken 48 times.
48 const Prims& prims = m_structure.GetPrims();
40 48 Cloud cloud;
41 48 cloud.prim = prim;
42
1/1
✓ Branch 1 taken 48 times.
48 cloud.generation = prims.Generation(prim);
43
1/1
✓ Branch 1 taken 48 times.
48 cloud.pointBasedRow = prims.Row(prim, SchemaId::PointBased);
44
1/1
✓ Branch 1 taken 48 times.
48 cloud.row = prims.Row(prim, SchemaId::Points);
45
1/1
✓ Branch 1 taken 48 times.
48 m_byPointBasedRow[cloud.pointBasedRow] = prim;
46
1/1
✓ Branch 1 taken 48 times.
48 m_byRow[cloud.row] = prim;
47
2/2
✓ Branch 1 taken 48 times.
✓ Branch 5 taken 48 times.
48 Link(m_clouds[prim] = std::move(cloud));
48 48 }
49
50 ✗ void PointsTable::Forget(uint32_t prim) {
51 ✗ Cloud& cloud = m_clouds.at(prim);
52 ✗ cloud.bvh.Release(m_structure.GetColumnStore());
53 ✗ m_records.Set(cloud.row, PointsRecord{});
54 ✗ m_byPointBasedRow.erase(cloud.pointBasedRow);
55 ✗ m_byRow.erase(cloud.row);
56 ✗ m_clouds.erase(prim);
57 ✗ }
58
59 167 void PointsTable::Link(Cloud& cloud) {
60 167 ColumnStore& store = m_structure.GetColumnStore();
61 167 const ArrayRef points = store.Array(kPointBasedPoints, cloud.pointBasedRow);
62 167 const ArrayRef widths = store.Array(kPointsWidths, cloud.row);
63 167 const ArrayRef normals = store.Array(kPointBasedNormals, cloud.pointBasedRow);
64 167 const ArrayRef ids = store.Array(kPointsIds, cloud.row);
65
4/4
✓ Branch 0 taken 95 times.
✓ Branch 1 taken 72 times.
✓ Branch 2 taken 17 times.
✓ Branch 3 taken 78 times.
167 if (points.count > cloud.capacity || points.count < cloud.capacity / 4) {
66 178 cloud.capacity = points.count > cloud.capacity
67
4/4
✓ Branch 0 taken 72 times.
✓ Branch 1 taken 17 times.
✓ Branch 2 taken 9 times.
✓ Branch 3 taken 63 times.
98 ? std::max(points.count, cloud.capacity + cloud.capacity / 2)
68 : points.count;
69
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 75 times.
89 cloud.order = nullptr;
70
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 75 times.
89 cloud.packed = nullptr;
71 89 device::Device& device = store.GetDevice();
72 89 cloud.bvh.Allocate(device, m_builder,
73 89 (cloud.capacity + m_pointsPerBox - 1) / m_pointsPerBox, "point boxes");
74
2/2
✓ Branch 0 taken 81 times.
✓ Branch 1 taken 8 times.
89 if (cloud.capacity > 0) {
75
2/2
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 54 times.
81 if (m_pointsPerBox > 1) {
76
1/1
✓ Branch 1 taken 27 times.
54 cloud.order = device.CreateBuffer(
77
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 27 times.
27 {.bytes = uint64_t(cloud.capacity) * sizeof(uint32_t), .label = "point order"});
78 27 cloud.packed =
79
1/1
✓ Branch 1 taken 27 times.
54 device.CreateBuffer({.bytes = uint64_t(cloud.capacity) * 4 * sizeof(float),
80
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 27 times.
54 .label = "packed points"});
81 }
82 }
83 }
84
2/2
✓ Branch 0 taken 126 times.
✓ Branch 1 taken 41 times.
167 if (points.count != cloud.count) {
85 126 cloud.count = points.count;
86 126 cloud.bvh.fresh = true;
87 }
88 167 cloud.bvh.Bound(store);
89 167 cloud.update = points.count > 0;
90 167 PointsRecord record{};
91
4/4
✓ Branch 0 taken 159 times.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 157 times.
✓ Branch 3 taken 2 times.
167 if (cloud.bvh.blas && points.count > 0) {
92 157 record.blas = cloud.bvh.blas->Handle();
93 157 record.points = points.address;
94 157 record.widths = widths.address;
95 157 record.pointCount = points.count;
96 157 record.widthCount = widths.count;
97 157 record.normals = normals.address;
98 157 record.normalCount = normals.count;
99 157 record.ids = ids.address;
100 157 record.idCount = ids.count;
101
2/2
✓ Branch 0 taken 57 times.
✓ Branch 1 taken 100 times.
157 record.order = cloud.order ? cloud.order->Address() : 0;
102
2/2
✓ Branch 0 taken 57 times.
✓ Branch 1 taken 100 times.
157 record.packed = cloud.packed ? cloud.packed->Address() : 0;
103 157 record.perBox = m_pointsPerBox;
104 157 record.bounds = cloud.bvh.bounds;
105 }
106 167 const Prims& prims = m_structure.GetPrims();
107
3/4
✓ Branch 1 taken 131 times.
✓ Branch 2 taken 36 times.
✓ Branch 4 taken 131 times.
✗ Branch 5 not taken.
167 if (m_records.Set(cloud.row, record) && prims.Alive(cloud.prim)) {
108 131 m_relinked.push_back(prims.Record(cloud.prim).world);
109 }
110 248 }
111
112 556 void PointsTable::Run(const ChangeSet& changes, TableVersions& versions) {
113
4/5
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 551 times.
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 5 times.
556 CHRONO_BATCH_RENDER_ZONE("Points/Run");
114
1/1
✓ Branch 1 taken 556 times.
556 const Prims& prims = m_structure.GetPrims();
115 556 TrackPrims(
116
1/1
✓ Branch 3 taken 556 times.
604 changes, prims, SchemaId::Points, m_clouds, [&](uint32_t prim) { Place(prim); },
117 ✗ [&](uint32_t prim) { Forget(prim); });
118
2/2
✓ Branch 0 taken 277 times.
✓ Branch 1 taken 279 times.
556 if (!changes.resync) {
119 1385 const auto relink = [&](ColumnRef column, const auto& owners) {
120
2/2
✓ Branch 2 taken 138 times.
✓ Branch 3 taken 1108 times.
1246 for (const uint32_t row : changes.ArrayRows(column)) {
121
2/2
✓ Branch 0 taken 120 times.
✓ Branch 1 taken 18 times.
138 const auto owner = owners.find(row);
122
2/2
✓ Branch 0 taken 120 times.
✓ Branch 1 taken 18 times.
138 if (owner != owners.end()) {
123 120 Cloud& cloud = m_clouds.at(owner->second);
124
2/2
✓ Branch 0 taken 119 times.
✓ Branch 1 taken 1 time.
120 if (!cloud.update) {
125 119 Link(cloud);
126 }
127 }
128 }
129 1385 };
130
1/1
✓ Branch 1 taken 277 times.
277 relink(kPointBasedPoints, m_byPointBasedRow);
131
1/1
✓ Branch 1 taken 277 times.
277 relink(kPointBasedNormals, m_byPointBasedRow);
132
1/1
✓ Branch 1 taken 277 times.
277 relink(kPointsWidths, m_byRow);
133
1/1
✓ Branch 1 taken 277 times.
277 relink(kPointsIds, m_byRow);
134 }
135 556 std::vector<uint32_t> updated;
136
1/1
✓ Branch 1 taken 556 times.
556 Build(updated);
137
1/1
✓ Branch 1 taken 556 times.
556 PublishRecords(m_records, m_structure.GetColumnStore(), SchemaId::Points, m_relinked, updated,
138 versions, TableId::Points, TableId::PointsMoved);
139 556 }
140
141 556 void PointsTable::Build(std::vector<uint32_t>& updated) {
142 556 std::vector<PointsJob> jobs;
143 556 std::vector<PointsJob> sorts;
144 556 std::vector<Ref<bvh::Blas>> built;
145 556 std::vector<Ref<bvh::Blas>> refit;
146 556 uint32_t total = 0;
147 556 uint32_t keys = 0;
148 556 uint32_t active = 0;
149 556 const Prims& prims = m_structure.GetPrims();
150
4/4
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 157 times.
✓ Branch 2 taken 178 times.
✓ Branch 3 taken 556 times.
912 for (auto& [prim, cloud] : m_clouds) {
151
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 157 times.
178 if (!cloud.update) {
152 21 continue;
153 }
154 157 cloud.update = false;
155 157 PointsJob job{};
156
1/2
✓ Branch 0 taken 157 times.
✗ Branch 1 not taken.
157 job.record = m_records.Row(cloud.row);
157
1/1
✓ Branch 1 taken 157 times.
157 job.boxes.address = cloud.bvh.boxes->Address();
158 157 job.bounds.address = cloud.bvh.bounds.address;
159
3/3
✓ Branch 1 taken 157 times.
✓ Branch 3 taken 118 times.
✓ Branch 4 taken 39 times.
157 if (cloud.bvh.Rebuilds(m_maxRefits)) {
160
1/1
✓ Branch 1 taken 118 times.
118 built.push_back(cloud.bvh.blas);
161
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 76 times.
118 if (cloud.order) {
162 42 PointsJob sort = job;
163 42 sort.first = keys;
164 42 sort.capacity = cloud.capacity;
165
1/1
✓ Branch 1 taken 42 times.
42 sorts.push_back(sort);
166 42 keys += cloud.capacity;
167 }
168 } else {
169
1/1
✓ Branch 1 taken 39 times.
39 refit.push_back(cloud.bvh.blas);
170 }
171 157 job.first = total;
172 157 job.capacity = (cloud.capacity + m_pointsPerBox - 1) / m_pointsPerBox;
173
1/1
✓ Branch 1 taken 157 times.
157 jobs.push_back(job);
174 157 total += job.capacity;
175 157 active += cloud.count;
176
2/3
✓ Branch 1 taken 157 times.
✓ Branch 3 taken 157 times.
✗ Branch 4 not taken.
157 if (prims.Alive(prim)) {
177
2/2
✓ Branch 1 taken 157 times.
✓ Branch 4 taken 157 times.
157 updated.push_back(prims.Record(prim).world);
178 }
179 }
180
2/2
✓ Branch 0 taken 455 times.
✓ Branch 1 taken 101 times.
556 m_stats.clouds = uint32_t(m_clouds.size());
181
2/2
✓ Branch 0 taken 455 times.
✓ Branch 1 taken 101 times.
556 m_stats.built = uint32_t(built.size());
182 556 m_stats.refit = uint32_t(refit.size());
183 556 m_stats.sorted = uint32_t(sorts.size());
184 556 m_stats.capacity = total;
185 556 m_stats.points = active;
186
2/2
✓ Branch 0 taken 455 times.
✓ Branch 1 taken 101 times.
556 if (jobs.empty()) {
187 455 return;
188 }
189
1/1
✓ Branch 1 taken 101 times.
101 ColumnStore& store = m_structure.GetColumnStore();
190
1/1
✓ Branch 1 taken 101 times.
101 device::Device& device = store.GetDevice();
191
1/1
✓ Branch 1 taken 101 times.
101 store.Flush();
192
1/1
✓ Branch 1 taken 101 times.
101 const Ref<device::Buffer> table =
193
1/1
✓ Branch 1 taken 101 times.
101 device.CreateBuffer({.label = "points jobs"}, std::span<const PointsJob>(jobs));
194 101 const uint64_t extentWords = uint64_t(jobs.size()) * 3;
195 101 const Ref<device::Buffer> extents = device.CreateBuffer(
196
2/2
✓ Branch 1 taken 101 times.
✓ Branch 4 taken 101 times.
101 {.bytes = extentWords * 2 * sizeof(uint32_t), .label = "points extents"});
197 101 PointsParams params{};
198 101 params.jobs.address = table->Address();
199 101 params.extents.address = extents->Address();
200
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 98 times.
101 params.jobCount = uint32_t(jobs.size());
201 101 params.total = total;
202
4/5
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 98 times.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 3 times.
101 static const profiler::ZoneId boxesZone = profiler::ZoneId::Named("Points/Boxes");
203
4/5
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 98 times.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 3 times.
101 static const profiler::ZoneId blasZone = profiler::ZoneId::Named("Points/Blas");
204
1/1
✓ Branch 1 taken 101 times.
101 device::Queue& queue = device.GetQueue();
205
1/1
✓ Branch 1 taken 101 times.
101 device::CommandEncoder encoder = queue.CreateEncoder();
206
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 77 times.
101 if (!sorts.empty()) {
207
4/5
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 21 times.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 3 times.
24 static const profiler::ZoneId sortZone = profiler::ZoneId::Named("Points/Sort");
208
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 13 times.
24 if (keys > m_sortCapacity) {
209
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 m_sortCapacity = std::bit_ceil(keys);
210
1/1
✓ Branch 1 taken 11 times.
11 m_sort = std::make_unique<algorithms::RadixSort>(device, m_sortCapacity, 8);
211
1/1
✓ Branch 1 taken 11 times.
22 m_keys = device.CreateBuffer(
212
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 9 times.
11 {.bytes = uint64_t(m_sortCapacity) * 2 * sizeof(uint32_t), .label = "point keys"});
213
1/1
✓ Branch 1 taken 11 times.
22 m_values = device.CreateBuffer(
214
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 9 times.
11 {.bytes = uint64_t(m_sortCapacity) * sizeof(uint32_t), .label = "point values"});
215 }
216
1/1
✓ Branch 1 taken 24 times.
24 const Ref<device::Buffer> sortTable =
217
1/1
✓ Branch 1 taken 24 times.
24 device.CreateBuffer({.label = "points sorts"}, std::span<const PointsJob>(sorts));
218 24 const uint64_t sortWords = uint64_t(sorts.size()) * 3;
219 24 const Ref<device::Buffer> sortExtents = device.CreateBuffer(
220
2/2
✓ Branch 1 taken 24 times.
✓ Branch 4 taken 24 times.
24 {.bytes = sortWords * 2 * sizeof(uint32_t), .label = "points sort extents"});
221 24 PointsParams sortParams{};
222 24 sortParams.jobs.address = sortTable->Address();
223 24 sortParams.extents.address = sortExtents->Address();
224 24 sortParams.keys.address = m_keys->Address();
225 24 sortParams.values.address = m_values->Address();
226
1/1
✓ Branch 1 taken 24 times.
24 sortParams.jobCount = uint32_t(sorts.size());
227 24 sortParams.total = keys;
228
1/1
✓ Branch 1 taken 24 times.
24 const device::Size3 groups{.x = device::GroupCount(keys, kPointsGroup)};
229
1/1
✓ Branch 1 taken 24 times.
24 encoder.BeginZone(sortZone);
230
2/2
✓ Branch 1 taken 24 times.
✓ Branch 4 taken 24 times.
24 encoder.Fill(sortExtents->Slice(0, sortWords * sizeof(uint32_t)), ~0u);
231
2/2
✓ Branch 1 taken 24 times.
✓ Branch 4 taken 24 times.
24 encoder.Fill(sortExtents->Slice(sortWords * sizeof(uint32_t)), 0u);
232
3/3
✓ Branch 1 taken 24 times.
✓ Branch 4 taken 24 times.
✓ Branch 7 taken 24 times.
24 encoder.Dispatch(Kernels().Entry("ReducePointExtents"), sortParams, groups);
233
3/3
✓ Branch 1 taken 24 times.
✓ Branch 4 taken 24 times.
✓ Branch 7 taken 24 times.
24 encoder.Dispatch(Kernels().Entry("FinishPointBounds"), sortParams,
234
1/1
✓ Branch 1 taken 24 times.
24 {.x = device::GroupCount(sortParams.jobCount, kPointsGroup)});
235
3/3
✓ Branch 1 taken 24 times.
✓ Branch 4 taken 24 times.
✓ Branch 7 taken 24 times.
24 encoder.Dispatch(Kernels().Entry("WritePointKeys"), sortParams, groups);
236
5/5
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 6 times.
✓ Branch 3 taken 24 times.
✓ Branch 6 taken 24 times.
✓ Branch 9 taken 24 times.
42 m_sort->Sort(encoder, m_keys->Slice(0, uint64_t(keys) * 2 * sizeof(uint32_t)),
237 m_values->Slice(0, uint64_t(keys) * sizeof(uint32_t)), keys,
238
3/3
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 6 times.
✓ Branch 3 taken 24 times.
42 32 + std::bit_width(sortParams.jobCount - 1));
239
3/5
✓ Branch 1 taken 24 times.
✓ Branch 4 taken 24 times.
✓ Branch 7 taken 24 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
24 encoder.Dispatch(Kernels().Entry("StorePointOrder"), sortParams, groups);
240
1/1
✓ Branch 1 taken 24 times.
24 encoder.EndZone();
241 48 }
242
1/1
✓ Branch 1 taken 101 times.
101 encoder.BeginZone(boxesZone);
243
2/2
✓ Branch 1 taken 101 times.
✓ Branch 4 taken 101 times.
101 encoder.Fill(extents->Slice(0, extentWords * sizeof(uint32_t)), ~0u);
244
2/2
✓ Branch 1 taken 101 times.
✓ Branch 4 taken 101 times.
101 encoder.Fill(extents->Slice(extentWords * sizeof(uint32_t)), 0u);
245
3/3
✓ Branch 1 taken 101 times.
✓ Branch 4 taken 101 times.
✓ Branch 7 taken 101 times.
101 encoder.Dispatch(Kernels().Entry("WritePointBoxes"), params,
246
1/1
✓ Branch 1 taken 101 times.
101 {.x = device::GroupCount(total, kPointsGroup)});
247
3/3
✓ Branch 1 taken 101 times.
✓ Branch 4 taken 101 times.
✓ Branch 7 taken 101 times.
101 encoder.Dispatch(Kernels().Entry("FinishPointBounds"), params,
248
1/1
✓ Branch 1 taken 101 times.
101 {.x = device::GroupCount(params.jobCount, kPointsGroup)});
249
1/1
✓ Branch 1 taken 101 times.
101 encoder.EndZone();
250
1/1
✓ Branch 1 taken 101 times.
101 encoder.BeginZone(blasZone);
251
1/1
✓ Branch 1 taken 101 times.
101 m_builder.Build(encoder, built);
252
1/1
✓ Branch 1 taken 101 times.
101 m_builder.Refit(encoder, refit);
253
1/1
✓ Branch 1 taken 101 times.
101 encoder.EndZone();
254
2/2
✓ Branch 1 taken 101 times.
✓ Branch 4 taken 101 times.
101 queue.Submit(encoder.Finish());
255 1155 }
256
257 951 void PointsTable::Hold(std::vector<Ref<const RefCounted>>& held) const {
258
4/8
✓ Branch 0 taken 2853 times.
✓ Branch 1 taken 951 times.
✓ Branch 2 taken 1036 times.
✓ Branch 3 taken 1817 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
3804 HoldBuffers(held, {m_records.Buffer(), m_keys, m_values});
259
2/2
✓ Branch 1 taken 218 times.
✓ Branch 2 taken 951 times.
1387 for (const auto& [prim, cloud] : m_clouds) {
260 218 cloud.bvh.Hold(held);
261
2/2
✓ Branch 0 taken 210 times.
✓ Branch 1 taken 8 times.
218 if (cloud.bvh.blas) {
262
2/2
✓ Branch 0 taken 73 times.
✓ Branch 1 taken 137 times.
210 if (cloud.order) {
263 73 held.emplace_back(cloud.order);
264 73 held.emplace_back(cloud.packed);
265 }
266 }
267 }
268 1902 }
269
270 353 void PointsTable::Prepare() {
271 353 Kernels();
272 353 }
273
274 651 device::Program& PointsTable::Kernels() {
275
2/2
✓ Branch 0 taken 358 times.
✓ Branch 1 taken 293 times.
651 if (!m_program) {
276
2/2
✓ Branch 2 taken 358 times.
✓ Branch 5 taken 358 times.
1074 m_program = m_structure.GetColumnStore().GetDevice().CreateProgram(
277 {.modules = {"chrono_batch_render.scene.tables.kernels.points"},
278 .library = &kSceneKernels,
279 .entries = {"ReducePointExtents", "WritePointKeys", "StorePointOrder",
280 "WritePointBoxes", "FinishPointBounds"},
281
2/3
✓ Branch 1 taken 358 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 358 times.
716 .label = "points"});
282 }
283 651 return *m_program;
284 716 }
285
286 }
287