chrono_batch_render/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
352 / 392
89.8%
Branches:
328 / 430
76.3%
Functions:
21 / 22
95.5%

scene/tables/curves.cpp
Line Branch Exec Source
1 // SPDX-License-Identifier: BSD-3-Clause
2 #include "chrono_batch_render/scene/tables/curves.h"
3
4 #include <algorithm>
5 #include <bit>
6 #include <optional>
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/curves.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 ColumnRef kLayout[] = {kCurvesWidthsInterpolation, kBasisCurvesType, kBasisCurvesBasis,
22 kBasisCurvesWrap, kPointBasedNormalsInterpolation};
23
24 constexpr ColumnRef kInputs[] = {
25 kPointBasedPoints, kCurvesCurveVertexCounts, kCurvesWidths,
26 kCurvesWidthsInterpolation, kBasisCurvesType, kBasisCurvesBasis,
27 kBasisCurvesWrap, kPointBasedNormals, kPointBasedNormalsInterpolation,
28 kHermiteCurvesTangents, kNurbsCurvesOrder, kNurbsCurvesKnots,
29 kNurbsCurvesPointWeights};
30
31 constexpr TableId kTableOutputs[] = {TableId::Curves, TableId::CurvesMoved};
32
33 constexpr uint32_t kNanWord = 0x7FC00000u;
34
35 96 uint32_t Grown(uint32_t count, uint32_t capacity) {
36
2/2
✓ Branch 0 taken 76 times.
✓ Branch 1 taken 20 times.
96 if (count > capacity) {
37
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 76 times.
76 return std::max(count, capacity + capacity / 2);
38 }
39
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
20 return count < capacity / 4 ? count : capacity;
40 }
41
42 396 Ref<device::Buffer> Words(device::Device& device, uint32_t count, const char* label) {
43 396 return count > 0
44
1/2
✓ Branch 0 taken 396 times.
✗ Branch 1 not taken.
396 ? device.CreateBuffer({.bytes = uint64_t(count) * sizeof(uint32_t), .label = label})
45
1/1
✓ Branch 1 taken 396 times.
792 : nullptr;
46 792 }
47
48 }
49
50 357 CurvesTable::~CurvesTable() {
51
2/2
✓ Branch 1 taken 18 times.
✓ Branch 2 taken 357 times.
375 for (const PendingTotals& pending : m_pending) {
52 18 pending.download.Wait();
53 }
54 922 }
55
56 2419 std::span<const ColumnRef> CurvesTable::Inputs() const {
57 2419 return kInputs;
58 }
59
60 357 std::span<const TableId> CurvesTable::TableOutputs() const {
61 357 return kTableOutputs;
62 }
63
64 545 void CurvesTable::Describe(RenderTables& tables) const {
65
1/2
✓ Branch 0 taken 545 times.
✗ Branch 1 not taken.
545 tables.curves.address = m_records.Buffer() ? m_records.Buffer()->Address() : 0;
66 545 }
67
68 38 void CurvesTable::Place(uint32_t prim) {
69
1/1
✓ Branch 1 taken 38 times.
38 const Prims& prims = m_structure.GetPrims();
70 38 Prim curves;
71 38 curves.prim = prim;
72
1/1
✓ Branch 1 taken 38 times.
38 curves.generation = prims.Generation(prim);
73
1/1
✓ Branch 1 taken 38 times.
38 curves.pointBasedRow = prims.Row(prim, SchemaId::PointBased);
74
1/1
✓ Branch 1 taken 38 times.
38 curves.curvesRow = prims.Row(prim, SchemaId::Curves);
75 38 curves.row = curves.curvesRow;
76
1/1
✓ Branch 1 taken 38 times.
38 const SchemaId schema = prims.Record(prim).schema;
77
2/3
✓ Branch 1 taken 38 times.
✓ Branch 3 taken 38 times.
✗ Branch 4 not taken.
38 if (IsA(schema, SchemaId::BasisCurves)) {
78
1/1
✓ Branch 1 taken 38 times.
38 curves.basisRow = prims.Row(prim, SchemaId::BasisCurves);
79
0/3
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ } else if (IsA(schema, SchemaId::HermiteCurves)) {
80 ✗ curves.kind = kCurveKindHermite;
81
0/1
✗ Branch 1 not taken.
✗ curves.kindRow = prims.Row(prim, SchemaId::HermiteCurves);
82
0/1
✗ Branch 1 not taken.
✗ m_byHermiteRow[curves.kindRow] = prim;
83
0/3
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ } else if (IsA(schema, SchemaId::NurbsCurves)) {
84 ✗ curves.kind = kCurveKindNurbs;
85
0/1
✗ Branch 1 not taken.
✗ curves.kindRow = prims.Row(prim, SchemaId::NurbsCurves);
86
0/1
✗ Branch 1 not taken.
✗ m_byNurbsRow[curves.kindRow] = prim;
87 }
88
1/1
✓ Branch 1 taken 38 times.
38 m_byPointBasedRow[curves.pointBasedRow] = prim;
89
1/1
✓ Branch 1 taken 38 times.
38 m_byCurvesRow[curves.curvesRow] = prim;
90
2/2
✓ Branch 1 taken 38 times.
✓ Branch 5 taken 38 times.
38 Link(m_prims[prim] = std::move(curves));
91 38 }
92
93 ✗ void CurvesTable::Forget(uint32_t prim) {
94 ✗ Prim& curves = m_prims.at(prim);
95 ✗ curves.bvh.Release(m_structure.GetColumnStore());
96 ✗ m_records.Set(curves.row, CurvesRecord{});
97 ✗ m_byPointBasedRow.erase(curves.pointBasedRow);
98 ✗ m_byCurvesRow.erase(curves.curvesRow);
99
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (curves.kind == kCurveKindHermite) {
100 ✗ m_byHermiteRow.erase(curves.kindRow);
101
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ } else if (curves.kind == kCurveKindNurbs) {
102 ✗ m_byNurbsRow.erase(curves.kindRow);
103 }
104 ✗ m_prims.erase(prim);
105 ✗ }
106
107 48 void CurvesTable::Link(Prim& curves) {
108 48 ColumnStore& store = m_structure.GetColumnStore();
109 48 device::Device& device = store.GetDevice();
110 48 const ArrayRef points = store.Array(kPointBasedPoints, curves.pointBasedRow);
111 48 const ArrayRef counts = store.Array(kCurvesCurveVertexCounts, curves.curvesRow);
112 48 const uint32_t pointCapacity = Grown(points.count, curves.pointCapacity);
113
2/2
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 10 times.
48 if (pointCapacity != curves.pointCapacity) {
114 38 curves.pointCapacity = pointCapacity;
115
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 38 times.
38 curves.spanFirst = Words(device, pointCapacity, "curve span firsts");
116
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 38 times.
38 curves.spanSteps = Words(device, pointCapacity, "curve span steps");
117 }
118 48 const uint32_t curveCapacity = Grown(counts.count, curves.curveCapacity);
119
2/2
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 10 times.
48 if (curveCapacity != curves.curveCapacity) {
120 38 curves.curveCapacity = curveCapacity;
121
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 38 times.
38 curves.starts = Words(device, curveCapacity, "curve starts");
122
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 38 times.
38 curves.varyingStarts = Words(device, curveCapacity, "curve varying starts");
123 38 curves.knotStarts = curves.kind == kCurveKindNurbs
124
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
76 ? Words(device, curveCapacity, "curve knot starts")
125
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
38 : nullptr;
126 }
127
3/4
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 10 times.
48 if (points.count != curves.count || counts.count != curves.curveCount) {
128 38 curves.count = points.count;
129 38 curves.curveCount = counts.count;
130 38 curves.bvh.fresh = true;
131 }
132 48 curves.bvh.Bound(store);
133
2/4
✓ Branch 0 taken 48 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 48 times.
48 curves.drawn = points.count > 0 && counts.count > 0;
134 48 curves.update = curves.drawn;
135 48 WriteRecord(curves);
136 48 }
137
138 40 void CurvesTable::Resize(Prim& curves, uint32_t segments) {
139
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 40 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
40 if (segments <= curves.bvh.capacity && segments >= curves.bvh.capacity / 4 && curves.bvh.blas) {
140 return;
141 }
142
1/2
✓ Branch 0 taken 40 times.
✗ Branch 1 not taken.
40 device::Device& device = m_structure.GetColumnStore().GetDevice();
143
1/2
✓ Branch 0 taken 40 times.
✗ Branch 1 not taken.
40 curves.bvh.Allocate(device, m_builder, segments > 0 ? segments + segments / 2 : 0,
144 "curve boxes");
145 40 curves.segments =
146 40 curves.bvh.capacity > 0
147
3/5
✓ Branch 0 taken 40 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 40 times.
✓ Branch 5 taken 40 times.
✗ Branch 6 not taken.
160 ? device.CreateBuffer({.bytes = uint64_t(curves.bvh.capacity) * sizeof(CurveSegment),
148 .label = "curve segments"})
149
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 38 times.
40 : nullptr;
150 40 WriteRecord(curves);
151 80 }
152
153 88 void CurvesTable::WriteRecord(Prim& curves) {
154 88 ColumnStore& store = m_structure.GetColumnStore();
155 88 const ArrayRef points = store.Array(kPointBasedPoints, curves.pointBasedRow);
156 88 const ArrayRef widths = store.Array(kCurvesWidths, curves.curvesRow);
157 88 const ArrayRef counts = store.Array(kCurvesCurveVertexCounts, curves.curvesRow);
158 88 const ArrayRef normals = store.Array(kPointBasedNormals, curves.pointBasedRow);
159 88 CurvesRecord record{};
160 88 record.kind = curves.kind;
161
1/2
✓ Branch 0 taken 88 times.
✗ Branch 1 not taken.
88 if (curves.drawn) {
162
2/2
✓ Branch 0 taken 50 times.
✓ Branch 1 taken 38 times.
88 record.blas = curves.bvh.blas ? curves.bvh.blas->Handle() : 0;
163 88 record.points = points.address;
164 88 record.widths = widths.address;
165 88 record.counts = counts.address;
166
2/2
✓ Branch 0 taken 50 times.
✓ Branch 1 taken 38 times.
88 record.starts = curves.starts->Address();
167 88 record.varyingStarts = curves.varyingStarts->Address();
168 88 record.spanFirst = curves.spanFirst->Address();
169 88 record.spanSteps = curves.spanSteps->Address();
170
2/2
✓ Branch 0 taken 50 times.
✓ Branch 1 taken 38 times.
88 record.segments = curves.segments ? curves.segments->Address() : 0;
171 88 record.normals = normals.address;
172 88 record.normalCount = normals.count;
173
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 88 times.
88 if (curves.kind == kCurveKindHermite) {
174 ✗ const ArrayRef tangents = store.Array(kHermiteCurvesTangents, curves.kindRow);
175 ✗ record.tangents = tangents.address;
176 ✗ record.tangentCount = tangents.count;
177
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 88 times.
88 } else if (curves.kind == kCurveKindNurbs) {
178 ✗ const ArrayRef orders = store.Array(kNurbsCurvesOrder, curves.kindRow);
179 ✗ const ArrayRef knots = store.Array(kNurbsCurvesKnots, curves.kindRow);
180 ✗ const ArrayRef weights = store.Array(kNurbsCurvesPointWeights, curves.kindRow);
181
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ record.orders = orders.count >= counts.count ? orders.address : 0;
182 ✗ record.knots = knots.address;
183 ✗ record.knotCount = knots.count;
184
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ record.knotStarts = curves.knotStarts ? curves.knotStarts->Address() : 0;
185 ✗ record.pointWeights = weights.address;
186 ✗ record.weightCount = weights.count;
187 }
188 88 record.pointCount = points.count;
189 88 record.widthCount = widths.count;
190 88 record.curveCount = counts.count;
191 88 record.segmentCount = curves.bvh.capacity;
192 88 record.bounds = curves.bvh.bounds;
193 }
194 88 const Prims& prims = m_structure.GetPrims();
195
3/4
✓ Branch 1 taken 79 times.
✓ Branch 2 taken 9 times.
✓ Branch 4 taken 79 times.
✗ Branch 5 not taken.
88 if (m_records.Set(curves.row, record) && prims.Alive(curves.prim)) {
196 79 m_relinked.push_back(prims.Record(curves.prim).world);
197 }
198 88 }
199
200 545 void CurvesTable::Run(const ChangeSet& changes, TableVersions& versions) {
201
4/5
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 540 times.
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 5 times.
545 CHRONO_BATCH_RENDER_ZONE("Curves/Run");
202
1/1
✓ Branch 1 taken 545 times.
545 const Prims& prims = m_structure.GetPrims();
203
1/1
✓ Branch 1 taken 545 times.
545 ColumnStore& store = m_structure.GetColumnStore();
204
1/1
✓ Branch 1 taken 545 times.
545 const uint64_t seen = store.WriteSequence();
205 545 TrackPrims(
206
1/1
✓ Branch 3 taken 545 times.
583 changes, prims, SchemaId::Curves, m_prims, [&](uint32_t prim) { Place(prim); },
207 ✗ [&](uint32_t prim) { Forget(prim); });
208
2/2
✓ Branch 0 taken 267 times.
✓ Branch 1 taken 278 times.
545 if (!changes.resync) {
209 2403 const auto relink = [&](ColumnRef column, const auto& owners, bool layout) {
210
2/2
✓ Branch 2 taken 121 times.
✓ Branch 3 taken 2136 times.
2257 for (const uint32_t row : changes.ArrayRows(column)) {
211
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 111 times.
121 const auto owner = owners.find(row);
212
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 111 times.
121 if (owner != owners.end()) {
213 10 Prim& curves = m_prims.at(owner->second);
214
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 1 time.
10 if (!curves.update) {
215 9 Link(curves);
216 }
217
3/4
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
✓ Branch 3 taken 1 time.
19 curves.bvh.fresh = curves.bvh.fresh || layout;
218 }
219 }
220 2403 };
221
1/1
✓ Branch 1 taken 267 times.
267 relink(kPointBasedPoints, m_byPointBasedRow, false);
222
1/1
✓ Branch 1 taken 267 times.
267 relink(kCurvesWidths, m_byCurvesRow, false);
223
1/1
✓ Branch 1 taken 267 times.
267 relink(kPointBasedNormals, m_byPointBasedRow, false);
224
1/1
✓ Branch 1 taken 267 times.
267 relink(kHermiteCurvesTangents, m_byHermiteRow, false);
225
1/1
✓ Branch 1 taken 267 times.
267 relink(kNurbsCurvesPointWeights, m_byNurbsRow, false);
226
1/1
✓ Branch 1 taken 267 times.
267 relink(kNurbsCurvesOrder, m_byNurbsRow, true);
227
1/1
✓ Branch 1 taken 267 times.
267 relink(kNurbsCurvesKnots, m_byNurbsRow, true);
228
1/1
✓ Branch 1 taken 267 times.
267 relink(kCurvesCurveVertexCounts, m_byCurvesRow, true);
229
2/3
✓ Branch 1 taken 267 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 267 times.
267 if (std::ranges::any_of(
230 1335 kLayout, [&](ColumnRef column) { return store.LastWrite(column) > m_seen; })) {
231
0/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ for (auto& [prim, curves] : m_prims) {
232
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (!curves.update) {
233
0/1
✗ Branch 1 not taken.
✗ Link(curves);
234 }
235 ✗ curves.bvh.fresh = true;
236 }
237 }
238 }
239 545 m_seen = seen;
240
1/1
✓ Branch 1 taken 545 times.
545 Collect();
241 545 std::vector<uint32_t> updated;
242
1/1
✓ Branch 1 taken 545 times.
545 Build(updated);
243
1/1
✓ Branch 1 taken 545 times.
545 PublishRecords(m_records, store, SchemaId::Curves, m_relinked, updated, versions,
244 TableId::Curves, TableId::CurvesMoved);
245 545 }
246
247 545 void CurvesTable::Collect() {
248 545 const Prims& prims = m_structure.GetPrims();
249
3/4
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 545 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 12 times.
557 while (!m_pending.empty() && m_pending.front().download.Ready()) {
250 12 const PendingTotals& pending = m_pending.front();
251 12 const Span<const std::byte> bytes = pending.download.Bytes();
252 12 const uint32_t* wanted = reinterpret_cast<const uint32_t*>(bytes.data());
253
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 12 times.
24 for (size_t i = 0; i < pending.prims.size(); ++i) {
254 12 const auto [prim, generation] = pending.prims[i];
255 12 const auto found = m_prims.find(prim);
256
3/6
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 12 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 12 times.
24 if (found == m_prims.end() || prims.Generation(prim) != generation ||
257 12 !pending.restepped[i]) {
258 ✗ continue;
259 }
260 12 Prim& curves = found->second;
261 12 const uint32_t segments = wanted[i * 2];
262 12 curves.wanted = segments;
263
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 if (curves.drawn &&
264
3/4
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 10 times.
12 (segments > curves.bvh.capacity || segments < curves.bvh.capacity / 4)) {
265
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
2 if (!curves.update) {
266 1 Link(curves);
267 }
268 2 Resize(curves, segments);
269 2 curves.bvh.refits = m_maxRefits;
270 }
271 }
272 12 m_pending.pop_front();
273 }
274 545 }
275
276 4220 std::string CurvesTable::ShapeOptions() const {
277
2/2
✓ Branch 1 taken 4216 times.
✓ Branch 2 taken 4 times.
4220 const bool curves = m_structure.GetColumnStore().Schema(SchemaId::Curves).Rows() > 0;
278
4/4
✓ Branch 0 taken 4173 times.
✓ Branch 1 taken 47 times.
✓ Branch 4 taken 4220 times.
✓ Branch 7 taken 4220 times.
21053 return std::string("static const bool kCurves = ") + (curves ? "true" : "false") +
279
3/3
✓ Branch 0 taken 4216 times.
✓ Branch 1 taken 4 times.
✓ Branch 3 taken 4220 times.
16880 ";\nstatic const bool kExactCurves = " + (m_exact ? "true" : "false") + ";\n";
280 }
281
282 10 std::vector<uint32_t> CurvesTable::SpanSteps(uint32_t curvesRow) const {
283
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10 const CurvesRecord& record = m_records.Row(curvesRow);
284 10 std::vector<uint32_t> steps(record.pointCount);
285
3/4
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
✓ Branch 3 taken 10 times.
30 for (const auto& [prim, curves] : m_prims) {
286
3/6
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
10 if (curves.row == curvesRow && curves.spanSteps && !steps.empty()) {
287
2/2
✓ Branch 1 taken 10 times.
✓ Branch 4 taken 10 times.
10 curves.spanSteps->GetDevice().GetQueue().Read(
288
1/1
✓ Branch 1 taken 10 times.
10 curves.spanSteps->Slice(0, steps.size() * sizeof(uint32_t)), steps.data());
289 }
290 }
291 10 return steps;
292 ✗ }
293
294 36 void CurvesTable::EnsureScratch(uint32_t items) {
295
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 10 times.
36 if (items <= m_scanCapacity) {
296 return;
297 }
298
1/2
✓ Branch 0 taken 26 times.
✗ Branch 1 not taken.
26 device::Device& device = m_structure.GetColumnStore().GetDevice();
299
1/2
✓ Branch 0 taken 26 times.
✗ Branch 1 not taken.
26 m_scanCapacity = std::bit_ceil(items);
300 26 m_scan = std::make_unique<algorithms::Scan>(device, m_scanCapacity);
301
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 26 times.
26 m_counts = Words(device, m_scanCapacity, "curve counts");
302
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 26 times.
26 m_scanned = Words(device, m_scanCapacity, "curve scanned counts");
303
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 26 times.
26 m_varying = Words(device, m_scanCapacity, "curve varying counts");
304
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 26 times.
26 m_scannedVarying = Words(device, m_scanCapacity, "curve scanned varying counts");
305
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 26 times.
26 m_steps = Words(device, m_scanCapacity, "curve steps");
306
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 26 times.
26 m_orders = Words(device, m_scanCapacity, "curve orders");
307
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 26 times.
26 m_scannedOrders = Words(device, m_scanCapacity, "curve scanned orders");
308
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 26 times.
26 m_stepFirst = Words(device, m_scanCapacity, "curve step firsts");
309 }
310
311 545 void CurvesTable::Build(std::vector<uint32_t>& updated) {
312 545 std::vector<CurvesJob> jobs;
313 545 std::vector<Prim*> owners;
314 545 bool reading = false;
315 545 uint32_t total = 0;
316 545 uint32_t countTotal = 0;
317 545 uint32_t active = 0;
318 545 const Prims& prims = m_structure.GetPrims();
319
4/4
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 48 times.
✓ Branch 2 taken 54 times.
✓ Branch 3 taken 545 times.
653 for (auto& [prim, curves] : m_prims) {
320
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 48 times.
54 if (!curves.update) {
321 6 continue;
322 }
323 48 curves.update = false;
324 48 CurvesJob job{};
325 48 job.first = total;
326 48 job.countFirst = countTotal;
327 48 job.curvesRow = curves.curvesRow;
328 48 job.basisRow = curves.basisRow;
329 48 job.pointBasedRow = curves.pointBasedRow;
330 96 job.restep =
331
5/6
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 41 times.
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 time.
✓ Branch 5 taken 6 times.
48 curves.bvh.fresh || !curves.bvh.blas || curves.bvh.refits >= m_maxRefits ? 1 : 0;
332
4/4
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 26 times.
✓ Branch 3 taken 10 times.
48 reading = reading || !curves.bvh.blas;
333
1/1
✓ Branch 1 taken 48 times.
48 jobs.push_back(job);
334
1/1
✓ Branch 1 taken 48 times.
48 owners.push_back(&curves);
335 48 total += curves.count;
336 48 countTotal += curves.curveCount;
337 48 active += curves.count;
338
2/3
✓ Branch 1 taken 48 times.
✓ Branch 3 taken 48 times.
✗ Branch 4 not taken.
48 if (prims.Alive(prim)) {
339
2/2
✓ Branch 1 taken 48 times.
✓ Branch 4 taken 48 times.
48 updated.push_back(prims.Record(prim).world);
340 }
341 }
342 545 m_stats = {};
343
2/2
✓ Branch 0 taken 509 times.
✓ Branch 1 taken 36 times.
545 m_stats.prims = uint32_t(m_prims.size());
344 545 m_stats.points = active;
345 545 m_stats.curves = countTotal;
346
2/2
✓ Branch 0 taken 509 times.
✓ Branch 1 taken 36 times.
545 if (jobs.empty()) {
347 509 return;
348 }
349
1/1
✓ Branch 1 taken 36 times.
36 ColumnStore& store = m_structure.GetColumnStore();
350
1/1
✓ Branch 1 taken 36 times.
36 device::Device& device = store.GetDevice();
351
1/1
✓ Branch 1 taken 36 times.
36 device::Queue& queue = device.GetQueue();
352
1/1
✓ Branch 1 taken 36 times.
36 store.Flush();
353
2/3
✗ Branch 0 not taken.
✓ Branch 1 taken 36 times.
✓ Branch 3 taken 36 times.
36 EnsureScratch(std::max(total, countTotal));
354 98 const auto describe = [&] {
355
2/2
✓ Branch 0 taken 86 times.
✓ Branch 1 taken 62 times.
148 for (size_t i = 0; i < jobs.size(); ++i) {
356
1/2
✓ Branch 0 taken 86 times.
✗ Branch 1 not taken.
86 const Prim& curves = *owners[i];
357
3/4
✓ Branch 0 taken 86 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 48 times.
✓ Branch 3 taken 38 times.
172 jobs[i].record = m_records.Row(curves.row);
358
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 38 times.
86 jobs[i].boxes.address = curves.bvh.boxes ? curves.bvh.boxes->Address() : 0;
359 86 jobs[i].bounds.address = curves.bvh.bounds.address;
360 }
361
1/1
✓ Branch 2 taken 62 times.
62 return device.CreateBuffer({.label = "curves jobs"}, std::span<const CurvesJob>(jobs));
362 124 };
363
1/1
✓ Branch 1 taken 36 times.
36 Ref<device::Buffer> table = describe();
364
1/1
✓ Branch 1 taken 36 times.
36 const Ref<device::Buffer> totals = Words(device, uint32_t(jobs.size()) * 2, "curves totals");
365
1/1
✓ Branch 1 taken 36 times.
36 const uint64_t extentWords = uint64_t(jobs.size()) * 3;
366 36 const Ref<device::Buffer> extents = device.CreateBuffer(
367
1/1
✓ Branch 1 taken 36 times.
36 {.bytes = extentWords * 2 * sizeof(uint32_t), .label = "curves extents"});
368 36 CurvesParams params{};
369 72 params.scene.address = store.DeviceData();
370
2/2
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 2 times.
36 params.jobs.address = table->Address();
371 36 params.extents.address = extents->Address();
372 36 params.counts.address = m_counts->Address();
373 36 params.scanned.address = m_scanned->Address();
374 36 params.varying.address = m_varying->Address();
375 36 params.scannedVarying.address = m_scannedVarying->Address();
376 36 params.orders.address = m_orders->Address();
377 36 params.scannedOrders.address = m_scannedOrders->Address();
378 36 params.steps.address = m_steps->Address();
379 36 params.stepFirst.address = m_stepFirst->Address();
380 36 params.totals.address = totals->Address();
381
2/2
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 2 times.
36 params.jobCount = uint32_t(jobs.size());
382 36 params.total = total;
383 36 params.countTotal = countTotal;
384 36 params.maxSteps = m_maxSteps;
385 36 params.tolerance = m_tolerance;
386
2/2
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 2 times.
36 params.exact = m_exact ? 1 : 0;
387 336 const auto words = [](const Ref<device::Buffer>& buffer, uint32_t count) {
388
3/3
✓ Branch 1 taken 30 times.
✓ Branch 4 taken 30 times.
✓ Branch 7 taken 30 times.
90 return buffer->Slice(0, uint64_t(count) * sizeof(uint32_t));
389 };
390
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 33 times.
36 const device::Size3 curveGroups{.x = device::GroupCount(countTotal, kCurvesGroup)};
391 36 const device::Size3 spanGroups{.x = device::GroupCount(total, kCurvesGroup)};
392
4/5
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 33 times.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 3 times.
36 static const profiler::ZoneId stepsZone = profiler::ZoneId::Named("Curves/Steps");
393
4/5
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 33 times.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 3 times.
36 static const profiler::ZoneId segmentsZone = profiler::ZoneId::Named("Curves/Segments");
394
4/5
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 33 times.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 3 times.
36 static const profiler::ZoneId blasZone = profiler::ZoneId::Named("Curves/Blas");
395
396
1/3
✓ Branch 1 taken 36 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
72 std::optional<device::CommandEncoder> encoder(queue.CreateEncoder());
397 36 const bool restepping =
398
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 30 times.
72 std::ranges::any_of(jobs, [](const CurvesJob& job) { return job.restep != 0; });
399
1/1
✓ Branch 1 taken 36 times.
36 encoder->BeginZone(stepsZone);
400
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 6 times.
36 if (restepping) {
401
2/2
✓ Branch 1 taken 30 times.
✓ Branch 4 taken 30 times.
30 encoder->Fill(totals->Slice(0), 0u);
402
3/3
✓ Branch 1 taken 30 times.
✓ Branch 4 taken 30 times.
✓ Branch 7 taken 30 times.
30 encoder->Dispatch(Kernels().Entry("GatherCurveCounts"), params, curveGroups);
403
3/3
✓ Branch 1 taken 30 times.
✓ Branch 4 taken 30 times.
✓ Branch 7 taken 30 times.
30 m_scan->ExclusiveSum(*encoder, words(m_counts, countTotal), words(m_scanned, countTotal),
404 countTotal);
405
3/3
✓ Branch 1 taken 30 times.
✓ Branch 4 taken 30 times.
✓ Branch 7 taken 30 times.
30 m_scan->ExclusiveSum(*encoder, words(m_varying, countTotal),
406
1/1
✓ Branch 1 taken 30 times.
30 words(m_scannedVarying, countTotal), countTotal);
407
3/3
✓ Branch 1 taken 30 times.
✓ Branch 4 taken 30 times.
✓ Branch 7 taken 30 times.
30 m_scan->ExclusiveSum(*encoder, words(m_orders, countTotal),
408
1/1
✓ Branch 1 taken 30 times.
30 words(m_scannedOrders, countTotal), countTotal);
409
3/3
✓ Branch 1 taken 30 times.
✓ Branch 4 taken 30 times.
✓ Branch 7 taken 30 times.
30 encoder->Dispatch(Kernels().Entry("WriteCurveStarts"), params, curveGroups);
410
3/3
✓ Branch 1 taken 30 times.
✓ Branch 4 taken 30 times.
✓ Branch 7 taken 30 times.
30 encoder->Dispatch(Kernels().Entry("CountCurveSteps"), params, spanGroups);
411
3/3
✓ Branch 1 taken 30 times.
✓ Branch 4 taken 30 times.
✓ Branch 7 taken 30 times.
30 m_scan->ExclusiveSum(*encoder, words(m_steps, total), words(m_stepFirst, total), total);
412
3/3
✓ Branch 1 taken 30 times.
✓ Branch 4 taken 30 times.
✓ Branch 7 taken 30 times.
60 encoder->Dispatch(Kernels().Entry("SumCurveSteps"), params,
413
1/1
✓ Branch 1 taken 30 times.
30 {.x = device::GroupCount(params.jobCount, kCurvesGroup)});
414 }
415
1/1
✓ Branch 1 taken 36 times.
36 encoder->EndZone();
416
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 10 times.
36 if (reading) {
417
2/2
✓ Branch 1 taken 26 times.
✓ Branch 4 taken 26 times.
26 queue.Submit(encoder->Finish());
418
1/3
✓ Branch 1 taken 26 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
26 std::vector<uint32_t> wanted(jobs.size() * 2);
419
2/2
✓ Branch 1 taken 26 times.
✓ Branch 4 taken 26 times.
26 queue.Read(totals->Slice(0), wanted.data());
420 26 ++m_stats.readBacks;
421
2/2
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 26 times.
64 for (size_t i = 0; i < jobs.size(); ++i) {
422
1/2
✓ Branch 0 taken 38 times.
✗ Branch 1 not taken.
38 Prim& curves = *owners[i];
423
1/2
✓ Branch 0 taken 38 times.
✗ Branch 1 not taken.
38 if (!curves.bvh.blas) {
424
1/1
✓ Branch 1 taken 38 times.
38 curves.wanted = wanted[i * 2];
425
1/1
✓ Branch 1 taken 38 times.
38 Resize(curves, wanted[i * 2]);
426 }
427 }
428
2/3
✓ Branch 1 taken 26 times.
✓ Branch 3 taken 26 times.
✗ Branch 4 not taken.
26 table = describe();
429
1/1
✓ Branch 1 taken 26 times.
26 params.jobs.address = table->Address();
430
1/1
✓ Branch 1 taken 26 times.
26 encoder.emplace(queue.CreateEncoder());
431 26 }
432
433 36 std::vector<Ref<bvh::Blas>> built;
434 36 std::vector<Ref<bvh::Blas>> refit;
435
1/1
✓ Branch 1 taken 36 times.
36 encoder->BeginZone(segmentsZone);
436
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 6 times.
36 if (restepping) {
437
3/3
✓ Branch 1 taken 30 times.
✓ Branch 4 taken 30 times.
✓ Branch 7 taken 30 times.
30 encoder->Dispatch(Kernels().Entry("FitCurveSteps"), params, spanGroups);
438
3/3
✓ Branch 1 taken 30 times.
✓ Branch 4 taken 30 times.
✓ Branch 7 taken 30 times.
30 m_scan->ExclusiveSum(*encoder, words(m_steps, total), words(m_stepFirst, total), total);
439
3/3
✓ Branch 1 taken 30 times.
✓ Branch 4 taken 30 times.
✓ Branch 7 taken 30 times.
60 encoder->Dispatch(Kernels().Entry("StoreCurveSteps"), params, spanGroups);
440 }
441
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 36 times.
84 for (size_t i = 0; i < jobs.size(); ++i) {
442
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 48 times.
48 Prim& curves = *owners[i];
443
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 48 times.
48 if (!curves.bvh.blas) {
444 ✗ continue;
445 }
446
4/5
✓ Branch 1 taken 48 times.
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 42 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 6 times.
48 if (curves.bvh.Rebuilds(m_maxRefits) || jobs[i].restep != 0) {
447
2/2
✓ Branch 1 taken 42 times.
✓ Branch 4 taken 42 times.
42 encoder->Fill(curves.bvh.boxes->Slice(0), kNanWord);
448
2/2
✓ Branch 1 taken 42 times.
✓ Branch 4 taken 42 times.
42 encoder->Fill(curves.segments->Slice(0), ~0u);
449
1/1
✓ Branch 1 taken 42 times.
42 built.push_back(curves.bvh.blas);
450 } else {
451
1/1
✓ Branch 1 taken 6 times.
6 refit.push_back(curves.bvh.blas);
452 }
453 48 m_stats.capacity += curves.bvh.capacity;
454
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 48 times.
48 m_stats.segments += std::min(curves.wanted, curves.bvh.capacity);
455 }
456
2/2
✓ Branch 1 taken 36 times.
✓ Branch 4 taken 36 times.
36 encoder->Fill(extents->Slice(0, extentWords * sizeof(uint32_t)), ~0u);
457
2/2
✓ Branch 1 taken 36 times.
✓ Branch 4 taken 36 times.
36 encoder->Fill(extents->Slice(extentWords * sizeof(uint32_t)), 0u);
458
3/3
✓ Branch 1 taken 36 times.
✓ Branch 4 taken 36 times.
✓ Branch 7 taken 36 times.
36 encoder->Dispatch(Kernels().Entry("WriteCurveSegments"), params, spanGroups);
459
3/3
✓ Branch 1 taken 36 times.
✓ Branch 4 taken 36 times.
✓ Branch 7 taken 36 times.
36 encoder->Dispatch(Kernels().Entry("FinishCurveBounds"), params,
460
1/1
✓ Branch 1 taken 36 times.
36 {.x = device::GroupCount(params.jobCount, kCurvesGroup)});
461
1/1
✓ Branch 1 taken 36 times.
36 encoder->EndZone();
462
1/1
✓ Branch 1 taken 36 times.
36 encoder->BeginZone(blasZone);
463
1/1
✓ Branch 1 taken 36 times.
36 m_builder.Build(*encoder, built);
464
1/1
✓ Branch 1 taken 36 times.
36 m_builder.Refit(*encoder, refit);
465
1/1
✓ Branch 1 taken 36 times.
36 encoder->EndZone();
466
2/2
✓ Branch 1 taken 36 times.
✓ Branch 4 taken 36 times.
36 queue.Submit(encoder->Finish());
467
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 6 times.
36 if (restepping) {
468
2/2
✓ Branch 1 taken 30 times.
✓ Branch 4 taken 30 times.
30 PendingTotals pending{.download = queue.ReadAsync(totals->Slice(0)), .totals = totals};
469
2/2
✓ Branch 1 taken 42 times.
✓ Branch 2 taken 30 times.
72 for (size_t i = 0; i < jobs.size(); ++i) {
470
1/1
✓ Branch 1 taken 42 times.
42 pending.prims.emplace_back(owners[i]->prim, owners[i]->generation);
471
1/1
✓ Branch 1 taken 42 times.
42 pending.restepped.push_back(jobs[i].restep != 0);
472 }
473
1/1
✓ Branch 1 taken 30 times.
30 m_pending.push_back(std::move(pending));
474 30 }
475 36 m_stats.built = uint32_t(built.size());
476 36 m_stats.refit = uint32_t(refit.size());
477 761 }
478
479 938 void CurvesTable::Hold(std::vector<Ref<const RefCounted>>& held) const {
480
4/8
✓ Branch 0 taken 8442 times.
✓ Branch 1 taken 938 times.
✓ Branch 2 taken 1361 times.
✓ Branch 3 taken 7081 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
9380 HoldBuffers(held, {m_records.Buffer(), m_counts, m_scanned, m_varying, m_scannedVarying,
481 3752 m_steps, m_stepFirst, m_orders, m_scannedOrders});
482
2/2
✓ Branch 1 taken 65 times.
✓ Branch 2 taken 938 times.
1068 for (const auto& [prim, curves] : m_prims) {
483 65 curves.bvh.Hold(held);
484 455 for (const Ref<device::Buffer>& buffer :
485 260 {curves.segments, curves.spanFirst, curves.spanSteps, curves.starts,
486
4/6
✓ Branch 6 taken 390 times.
✓ Branch 7 taken 65 times.
✓ Branch 8 taken 325 times.
✓ Branch 9 taken 65 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
1170 curves.varyingStarts, curves.knotStarts}) {
487
2/2
✓ Branch 0 taken 325 times.
✓ Branch 1 taken 65 times.
390 if (buffer) {
488
1/1
✓ Branch 2 taken 325 times.
325 held.emplace_back(buffer);
489 }
490 455 }
491 }
492 1876 }
493
494 353 void CurvesTable::Prepare() {
495 353 Kernels();
496 353 }
497
498 605 device::Program& CurvesTable::Kernels() {
499
2/2
✓ Branch 0 taken 357 times.
✓ Branch 1 taken 248 times.
605 if (!m_program) {
500
2/2
✓ Branch 2 taken 357 times.
✓ Branch 5 taken 357 times.
1071 m_program = m_structure.GetColumnStore().GetDevice().CreateProgram(
501 {.modules = {"chrono_batch_render.scene.tables.kernels.curves"},
502 .library = &kSceneKernels,
503 .entries = {"GatherCurveCounts", "WriteCurveStarts", "CountCurveSteps",
504 "SumCurveSteps", "FitCurveSteps", "StoreCurveSteps", "WriteCurveSegments",
505 "FinishCurveBounds"},
506
2/3
✓ Branch 1 taken 357 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 357 times.
714 .label = "curves"});
507 }
508 605 return *m_program;
509 714 }
510
511 }
512