chrono_batch_render/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
483 / 496
97.4%
Branches:
578 / 662
87.3%
Functions:
26 / 27
96.3%

scene/tables/transforms.cpp
Line Branch Exec Source
1 // SPDX-License-Identifier: BSD-3-Clause
2 #include "chrono_batch_render/scene/tables/transforms.h"
3
4 #include <algorithm>
5 #include <cstring>
6 #include <functional>
7 #include <iterator>
8 #include <map>
9 #include <numeric>
10 #include <set>
11 #include <string>
12 #include <unordered_map>
13
14 #include "chrono_batch_render/device/commands.h"
15 #include "chrono_batch_render/device/queue.h"
16 #include "chrono_batch_render/scene/kernels.h"
17 #include "chrono_batch_render/scene/records/render_tables.h"
18 #include "chrono_batch_render/scene/schema/columns.h"
19 #include "chrono_batch_render/scene/tables/captures.h"
20 #include "chrono_batch_render/scene/tables/kernels/transforms.h"
21 #include "chrono_batch_render/stage/columns/kernels/cells.h"
22 #include "chrono_batch_render/stage/publish/change_set.h"
23 #include "chrono_batch_render/stage/views/attribute.h"
24 #include "chrono_batch_render/stage/views/view.h"
25 #include "chrono_batch_render/util/log.h"
26 #include "chrono_batch_render/util/profiler.h"
27
28 namespace chrono::batch_render {
29
30 namespace {
31
32 constexpr ColumnRef kFollowInputs[] = {kFollowAPIFollowTarget,
33 kFollowAPIFollowPath,
34 kFollowAPIFollowPathMode,
35 kFollowAPIFollowPathPosition,
36 kFollowAPIFollowPathSpeed,
37 kFollowAPIFollowPathCurve,
38 kFollowAPIFollowLookAt,
39 kFollowAPIFollowTranslate,
40 kFollowAPIFollowRotate,
41 kFollowAPIFollowUp,
42 kFollowAPIFollowInheritScale,
43 kFollowAPIFollowOffsetTranslate,
44 kFollowAPIFollowOffsetOrient,
45 kFollowAPIFollowOffsetScale,
46 kFollowAPIFollowLookAtOffsetTranslate,
47 kFollowAPIFollowPositionLag,
48 kFollowAPIFollowRotationLag,
49 kFollowAPIFollowLagMaxDistance,
50 kFollowAPIFollowWeight};
51
52 constexpr ColumnRef kInputs[] = {kXformableTranslate,
53 kXformableOrient,
54 kXformableScale,
55 kXformableResetXformStack,
56 kImageableVisibility,
57 kImageablePurpose,
58 kMaterialBindingAPIMaterialBinding,
59 kMotionAPIMotionVelocity,
60 kMotionAPIMotionAngularVelocity,
61 kFollowAPIFollowTarget,
62 kFollowAPIFollowPath,
63 kFollowAPIFollowPathMode,
64 kFollowAPIFollowPathPosition,
65 kFollowAPIFollowPathSpeed,
66 kFollowAPIFollowPathCurve,
67 kFollowAPIFollowLookAt,
68 kFollowAPIFollowTranslate,
69 kFollowAPIFollowRotate,
70 kFollowAPIFollowUp,
71 kFollowAPIFollowInheritScale,
72 kFollowAPIFollowOffsetTranslate,
73 kFollowAPIFollowOffsetOrient,
74 kFollowAPIFollowOffsetScale,
75 kFollowAPIFollowLookAtOffsetTranslate,
76 kFollowAPIFollowPositionLag,
77 kFollowAPIFollowRotationLag,
78 kFollowAPIFollowLagMaxDistance,
79 kFollowAPIFollowWeight};
80
81 constexpr ColumnRef kPathInputs[] = {kPointBasedPoints, kCurvesCurveVertexCounts, kBasisCurvesType,
82 kBasisCurvesBasis, kBasisCurvesWrap};
83
84 constexpr ColumnRef kStamped[] = {kXformableTranslate,
85 kXformableOrient,
86 kXformableScale,
87 kXformableResetXformStack,
88 kImageableVisibility,
89 kImageablePurpose,
90 kMaterialBindingAPIMaterialBinding};
91
92 constexpr TableId kTableOutputs[] = {TableId::Transforms, TableId::Imageables};
93
94 constexpr uint32_t kUnresolved = kNoPrim - 1;
95
96 }
97
98 390 TransformTable::TransformTable(Structure& structure, const SimulationClock* clock)
99
6/6
✓ Branch 1 taken 390 times.
✓ Branch 6 taken 390 times.
✓ Branch 11 taken 390 times.
✓ Branch 15 taken 390 times.
✓ Branch 19 taken 390 times.
✓ Branch 24 taken 390 times.
1950 : m_structure(structure), m_clock(clock) {
100
2/2
✓ Branch 0 taken 2730 times.
✓ Branch 1 taken 390 times.
3120 for (const ColumnRef column : kStamped) {
101
1/1
✓ Branch 1 taken 2730 times.
2730 m_structure.GetColumnStore().TrackRows(column);
102 }
103 390 }
104
105 2841 std::span<const ColumnRef> TransformTable::Inputs() const {
106 5 static const std::vector<ColumnRef> inputs = [] {
107 5 std::vector<ColumnRef> columns(std::begin(kInputs), std::end(kInputs));
108
1/1
✓ Branch 1 taken 5 times.
5 columns.insert(columns.end(), std::begin(kPathInputs), std::end(kPathInputs));
109 5 return columns;
110
4/5
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 2836 times.
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 5 times.
2841 }();
111 2841 return inputs;
112 }
113
114 390 std::span<const TableId> TransformTable::TableOutputs() const {
115 390 return kTableOutputs;
116 }
117
118 808 void TransformTable::Describe(RenderTables& tables) const {
119
1/2
✓ Branch 0 taken 808 times.
✗ Branch 1 not taken.
808 tables.transforms.address = m_transforms.Address();
120
1/2
✓ Branch 0 taken 808 times.
✗ Branch 1 not taken.
808 tables.imageables.address = m_imageables.Address();
121
1/2
✓ Branch 0 taken 808 times.
✗ Branch 1 not taken.
808 tables.motions.address = m_motions.Address();
122 808 }
123
124 999 void TransformTable::Hold(std::vector<Ref<const RefCounted>>& held) const {
125
4/8
✓ Branch 0 taken 2997 times.
✓ Branch 1 taken 999 times.
✓ Branch 2 taken 2994 times.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
3996 HoldBuffers(held, {m_transforms.Buffer(), m_imageables.Buffer(), m_motions.Buffer()});
126 1998 }
127
128 6878 uint32_t TransformTable::ParentRow(uint32_t prim) const {
129 6878 const Prims& prims = m_structure.GetPrims();
130 6878 uint32_t up = prims.Record(prim).parent;
131
3/4
✓ Branch 0 taken 344 times.
✓ Branch 1 taken 6534 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 344 times.
6878 while (up != kNoPrim && !IsA(prims.Record(up).schema, SchemaId::Imageable)) {
132 ✗ up = prims.Record(up).parent;
133 }
134
2/2
✓ Branch 0 taken 344 times.
✓ Branch 1 taken 6534 times.
6878 return up == kNoPrim ? kNoRow : prims.Row(up, SchemaId::Imageable);
135 }
136
137 331 void TransformTable::Rebuild() {
138
4/5
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 326 times.
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 5 times.
331 CHRONO_BATCH_RENDER_ZONE("Hierarchy/Rebuild");
139
1/1
✓ Branch 1 taken 331 times.
331 const Prims& prims = m_structure.GetPrims();
140
1/1
✓ Branch 1 taken 331 times.
331 ColumnStore& store = m_structure.GetColumnStore();
141
1/1
✓ Branch 1 taken 331 times.
331 device::Device& device = store.GetDevice();
142
3/3
✓ Branch 1 taken 331 times.
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 325 times.
331 const uint32_t capacity = std::max(store.Schema(SchemaId::Imageable).Capacity(), 1u);
143
2/2
✓ Branch 1 taken 331 times.
✓ Branch 4 taken 331 times.
331 m_transforms.Allocate(device, store.Schema(SchemaId::Xformable).Capacity());
144
1/1
✓ Branch 1 taken 331 times.
331 m_imageables.Allocate(device, capacity);
145
1/1
✓ Branch 1 taken 331 times.
331 m_parents.Assign(capacity, kNoRow);
146
1/1
✓ Branch 1 taken 331 times.
331 m_xforms.Assign(capacity, kNoRow);
147 331 m_rows.clear();
148
1/1
✓ Branch 1 taken 331 times.
331 std::vector<std::vector<uint32_t>> segments(prims.WorldCount() + 1);
149
2/2
✓ Branch 0 taken 7624 times.
✓ Branch 1 taken 331 times.
7955 for (uint32_t prim = 0; prim < prims.Size(); ++prim) {
150
7/7
✓ Branch 1 taken 7624 times.
✓ Branch 3 taken 7369 times.
✓ Branch 4 taken 255 times.
✓ Branch 6 taken 7369 times.
✓ Branch 9 taken 7369 times.
✓ Branch 11 taken 749 times.
✓ Branch 12 taken 6620 times.
7624 if (!prims.Alive(prim) || !IsA(prims.Record(prim).schema, SchemaId::Imageable)) {
151 1004 continue;
152 }
153
1/1
✓ Branch 1 taken 6620 times.
6620 const PrimRecord& record = prims.Record(prim);
154
1/1
✓ Branch 1 taken 6620 times.
6620 const uint32_t row = prims.Row(prim, SchemaId::Imageable);
155
2/2
✓ Branch 1 taken 6620 times.
✓ Branch 4 taken 6620 times.
6620 m_parents.Set(row, ParentRow(prim));
156
3/3
✓ Branch 1 taken 6620 times.
✓ Branch 3 taken 6403 times.
✓ Branch 4 taken 217 times.
6620 if (IsA(record.schema, SchemaId::Xformable)) {
157
2/2
✓ Branch 1 taken 6403 times.
✓ Branch 4 taken 6403 times.
6403 m_xforms.Set(row, prims.Row(prim, SchemaId::Xformable));
158 }
159
3/3
✓ Branch 0 taken 308 times.
✓ Branch 1 taken 6312 times.
✓ Branch 3 taken 6620 times.
6928 segments[WorldSlot(record.world)].push_back(row);
160
1/1
✓ Branch 1 taken 6620 times.
6620 m_rows[prim] = row;
161 }
162
1/1
✓ Branch 1 taken 331 times.
331 m_segments.Assign(std::move(segments));
163
164
1/1
✓ Branch 1 taken 331 times.
331 m_parents.Upload(device);
165
1/1
✓ Branch 1 taken 331 times.
331 m_xforms.Upload(device);
166
3/3
✓ Branch 1 taken 331 times.
✓ Branch 4 taken 331 times.
✓ Branch 7 taken 331 times.
662 m_segmentRows = device.CreateBuffer(
167 {.label = "hierarchy segment rows"},
168
4/4
✓ Branch 1 taken 331 times.
✓ Branch 3 taken 9 times.
✓ Branch 4 taken 322 times.
✓ Branch 6 taken 331 times.
993 std::vector<uint32_t>(m_segments.Items().begin(), m_segments.Items().end()));
169
2/2
✓ Branch 1 taken 331 times.
✓ Branch 4 taken 331 times.
993 m_segmentStart = device.CreateBuffer(
170
1/1
✓ Branch 1 taken 331 times.
331 {.label = "hierarchy segment starts"},
171
4/4
✓ Branch 1 taken 331 times.
✓ Branch 3 taken 9 times.
✓ Branch 4 taken 322 times.
✓ Branch 6 taken 331 times.
993 std::vector<uint32_t>(m_segments.Starts().begin(), m_segments.Starts().end()));
172
2/2
✓ Branch 1 taken 331 times.
✓ Branch 4 taken 331 times.
993 m_segmentCount = device.CreateBuffer(
173
1/1
✓ Branch 1 taken 331 times.
331 {.label = "hierarchy segment counts"},
174
4/4
✓ Branch 1 taken 331 times.
✓ Branch 3 taken 9 times.
✓ Branch 4 taken 322 times.
✓ Branch 6 taken 331 times.
993 std::vector<uint32_t>(m_segments.Counts().begin(), m_segments.Counts().end()));
175 331 m_built = true;
176 331 ++m_rebuilds;
177 662 Log(LogLevel::Debug, "rebuilt the hierarchy: {} rows in {} segments", m_rows.size(),
178
1/1
✓ Branch 1 taken 331 times.
331 m_segments.Size());
179 2317 }
180
181 186 bool TransformTable::Apply(const ChangeSet& changes) {
182
4/5
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 183 times.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 3 times.
186 CHRONO_BATCH_RENDER_ZONE("Hierarchy/Apply");
183
2/2
✓ Branch 0 taken 181 times.
✓ Branch 1 taken 5 times.
186 const Prims& prims = m_structure.GetPrims();
184
2/2
✓ Branch 0 taken 181 times.
✓ Branch 1 taken 5 times.
186 if (m_segments.Size() != prims.WorldCount() + 1) {
185 return false;
186 }
187 181 std::map<uint32_t, std::vector<uint32_t>> touched;
188 539 const auto segment = [&](uint32_t index) -> std::vector<uint32_t>& {
189 358 auto [entry, fresh] = touched.try_emplace(index);
190
2/2
✓ Branch 0 taken 143 times.
✓ Branch 1 taken 215 times.
358 if (fresh) {
191 143 const std::span<const uint32_t> items = m_segments.Items(index);
192 143 entry->second.assign(items.begin(), items.end());
193 }
194 358 return entry->second;
195 181 };
196
2/2
✓ Branch 0 taken 147 times.
✓ Branch 1 taken 181 times.
328 for (const StructureEntry& entry : changes.removed) {
197 147 const auto found = m_rows.find(entry.prim);
198
2/2
✓ Branch 0 taken 47 times.
✓ Branch 1 taken 100 times.
147 if (found == m_rows.end()) {
199 47 continue;
200 }
201 100 const uint32_t row = found->second;
202
3/3
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 98 times.
✓ Branch 3 taken 100 times.
102 std::erase(segment(WorldSlot(entry.world)), row);
203
1/1
✓ Branch 1 taken 100 times.
100 m_parents.Set(row, kNoRow);
204
1/1
✓ Branch 1 taken 100 times.
100 m_xforms.Set(row, kNoRow);
205 100 m_rows.erase(found);
206 }
207
2/2
✓ Branch 0 taken 317 times.
✓ Branch 1 taken 180 times.
497 for (const StructureEntry& entry : changes.added) {
208
8/8
✓ Branch 1 taken 317 times.
✓ Branch 3 taken 301 times.
✓ Branch 4 taken 16 times.
✓ Branch 6 taken 301 times.
✓ Branch 8 taken 291 times.
✓ Branch 9 taken 10 times.
✓ Branch 10 taken 32 times.
✓ Branch 11 taken 259 times.
608 if (!prims.Alive(entry.prim) || prims.Generation(entry.prim) != entry.generation ||
209
2/2
✓ Branch 1 taken 291 times.
✓ Branch 4 taken 291 times.
291 !IsA(prims.Record(entry.prim).schema, SchemaId::Imageable)) {
210 58 continue;
211 }
212
1/1
✓ Branch 1 taken 259 times.
259 const uint32_t row = prims.Row(entry.prim, SchemaId::Imageable);
213
2/2
✓ Branch 0 taken 258 times.
✓ Branch 1 taken 1 time.
259 if (row >= m_parents.Rows()) {
214 1 return false;
215 }
216
4/4
✓ Branch 1 taken 258 times.
✓ Branch 4 taken 258 times.
✓ Branch 6 taken 256 times.
✓ Branch 7 taken 2 times.
258 if (IsA(prims.Record(entry.prim).schema, SchemaId::Xformable) &&
217
3/5
✓ Branch 1 taken 256 times.
✓ Branch 3 taken 256 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 256 times.
✗ Branch 6 not taken.
257 prims.Row(entry.prim, SchemaId::Xformable) >= m_transforms.Rows()) {
218 return false;
219 }
220
2/2
✓ Branch 1 taken 258 times.
✓ Branch 4 taken 258 times.
258 m_parents.Set(row, ParentRow(entry.prim));
221
2/2
✓ Branch 1 taken 258 times.
✓ Branch 4 taken 258 times.
258 m_xforms.Set(row, IsA(prims.Record(entry.prim).schema, SchemaId::Xformable)
222
4/4
✓ Branch 0 taken 256 times.
✓ Branch 1 taken 2 times.
✓ Branch 3 taken 256 times.
✓ Branch 6 taken 258 times.
258 ? prims.Row(entry.prim, SchemaId::Xformable)
223 : kNoRow);
224
4/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 256 times.
✓ Branch 3 taken 258 times.
✓ Branch 6 taken 258 times.
260 segment(WorldSlot(entry.world)).push_back(row);
225
1/1
✓ Branch 1 taken 258 times.
258 m_rows[entry.prim] = row;
226 }
227
3/3
✓ Branch 1 taken 143 times.
✓ Branch 3 taken 143 times.
✓ Branch 4 taken 177 times.
320 for (const auto& [index, items] : touched) {
228
3/3
✓ Branch 1 taken 143 times.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 140 times.
143 if (!m_segments.Replace(index, items)) {
229 3 return false;
230 }
231 }
232
233
1/1
✓ Branch 1 taken 177 times.
177 device::Device& device = m_structure.GetColumnStore().GetDevice();
234
1/1
✓ Branch 1 taken 177 times.
177 m_parents.Upload(device);
235
1/1
✓ Branch 1 taken 177 times.
177 m_xforms.Upload(device);
236 177 device::Queue& queue = device.GetQueue();
237
3/3
✓ Branch 1 taken 140 times.
✓ Branch 3 taken 140 times.
✓ Branch 4 taken 177 times.
317 for (const auto& [index, items] : touched) {
238
1/1
✓ Branch 1 taken 140 times.
140 const uint32_t start = m_segments.Starts()[index];
239
1/1
✓ Branch 1 taken 140 times.
140 queue.Write(m_segmentRows->Slice(start * sizeof(uint32_t),
240
1/1
✓ Branch 1 taken 140 times.
140 m_segments.Capacity(index) * sizeof(uint32_t)),
241
1/1
✓ Branch 1 taken 140 times.
280 m_segments.Items().data() + start);
242 }
243
2/2
✓ Branch 0 taken 140 times.
✓ Branch 1 taken 37 times.
177 if (!touched.empty()) {
244
1/1
✓ Branch 1 taken 140 times.
140 queue.Write(*m_segmentCount, m_segments.Counts().data());
245 }
246 return true;
247 186 }
248
249 808 void TransformTable::Run(const ChangeSet& changes, TableVersions& versions) {
250
4/5
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 803 times.
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 5 times.
808 CHRONO_BATCH_RENDER_ZONE("Hierarchy/Run");
251
2/2
✓ Branch 0 taken 486 times.
✓ Branch 1 taken 322 times.
808 ColumnStore& store = m_structure.GetColumnStore();
252
8/9
✓ Branch 0 taken 486 times.
✓ Branch 1 taken 322 times.
✓ Branch 2 taken 486 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 186 times.
✓ Branch 5 taken 300 times.
✓ Branch 7 taken 186 times.
✓ Branch 9 taken 9 times.
✓ Branch 10 taken 177 times.
808 const bool rebuild = changes.resync || !m_built || (changes.structure && !Apply(changes));
253
2/2
✓ Branch 0 taken 331 times.
✓ Branch 1 taken 477 times.
808 if (rebuild) {
254
1/1
✓ Branch 1 taken 331 times.
331 Rebuild();
255 }
256
5/5
✓ Branch 0 taken 477 times.
✓ Branch 1 taken 331 times.
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 465 times.
✓ Branch 5 taken 808 times.
820 WriteMotions(rebuild || !changes.apis.empty());
257
258 808 std::vector<uint32_t> segments;
259
5/5
✓ Branch 0 taken 477 times.
✓ Branch 1 taken 331 times.
✓ Branch 3 taken 477 times.
✓ Branch 5 taken 443 times.
✓ Branch 6 taken 34 times.
808 const bool global = rebuild || changes.Global();
260 808 if (global) {
261
1/1
✓ Branch 1 taken 774 times.
774 segments.resize(m_segments.Size());
262 1548 std::iota(segments.begin(), segments.end(), 0u);
263 } else {
264
2/2
✓ Branch 0 taken 25 times.
✓ Branch 1 taken 34 times.
59 for (const uint32_t world : changes.worlds) {
265
2/3
✓ Branch 0 taken 25 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 25 times.
50 segments.push_back(WorldSlot(world));
266 }
267 }
268 808 std::erase_if(segments, [&](uint32_t segment) {
269
3/4
✓ Branch 0 taken 928 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 22 times.
✓ Branch 3 taken 906 times.
928 return segment >= m_segments.Size() || m_segments.Counts()[segment] == 0;
270 });
271 808 m_dispatched = 0;
272
8/8
✓ Branch 0 taken 477 times.
✓ Branch 1 taken 331 times.
✓ Branch 2 taken 300 times.
✓ Branch 3 taken 177 times.
✓ Branch 4 taken 289 times.
✓ Branch 5 taken 11 times.
✓ Branch 6 taken 11 times.
✓ Branch 7 taken 278 times.
1097 const bool replanned = rebuild || changes.structure || !changes.apis.empty() ||
273
1/1
✓ Branch 1 taken 289 times.
289 std::ranges::any_of(kFollowInputs, [&](ColumnRef input) {
274 5344 return store.LastWrite(input) > m_followSeen;
275 1338 });
276 530 bool refollowed = false;
277 530 if (replanned) {
278 530 refollowed = !m_follows.frames.empty();
279
1/1
✓ Branch 1 taken 530 times.
530 PlanFollows();
280 530 m_followSeen = store.WriteSequence();
281 }
282 3221 const auto written = [&](std::span<const ColumnRef> inputs) {
283
4/4
✓ Branch 0 taken 1426 times.
✓ Branch 1 taken 987 times.
✓ Branch 3 taken 501 times.
✓ Branch 4 taken 925 times.
2413 return rebuild || std::ranges::any_of(inputs, [&](ColumnRef input) {
284 4411 return store.LastWrite(input) > m_seen;
285 2413 });
286 808 };
287
1/1
✓ Branch 1 taken 808 times.
808 const bool pathsWritten = written(kPathInputs);
288
4/4
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 788 times.
✓ Branch 2 taken 14 times.
✓ Branch 3 taken 6 times.
808 const bool pathsMoved = m_follows.pathed && pathsWritten;
289
6/9
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 802 times.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 6 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 6 times.
808 if (pathsWritten && !global && !replanned && changes.apis.empty() && !written(kInputs)) {
290 6 segments.clear();
291 }
292
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 808 times.
808 if (refollowed && !global) {
293
0/1
✗ Branch 1 not taken.
✗ segments.resize(m_segments.Size());
294 ✗ std::iota(segments.begin(), segments.end(), 0u);
295 ✗ std::erase_if(segments,
296 ✗ [&](uint32_t segment) { return m_segments.Counts()[segment] == 0; });
297 }
298 808 const std::span<const ColumnRef> stamped = kStamped;
299
1/1
✓ Branch 1 taken 808 times.
808 const bool moved = written(stamped.first(4));
300
6/6
✓ Branch 0 taken 37 times.
✓ Branch 1 taken 771 times.
✓ Branch 2 taken 8 times.
✓ Branch 3 taken 29 times.
✓ Branch 4 taken 6 times.
✓ Branch 5 taken 2 times.
808 const bool following = !m_follows.frames.empty() && (moved || replanned || m_follows.lagged ||
301
3/4
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 time.
6 m_follows.riding || pathsMoved);
302
4/4
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 784 times.
✓ Branch 2 taken 7 times.
✓ Branch 3 taken 17 times.
808 if (segments.empty() && !following) {
303 17 return;
304 }
305 791 uint32_t widest = 0;
306
2/2
✓ Branch 0 taken 903 times.
✓ Branch 1 taken 791 times.
1694 for (const uint32_t segment : segments) {
307
2/2
✓ Branch 0 taken 833 times.
✓ Branch 1 taken 70 times.
903 widest = std::max(widest, m_segments.Counts()[segment]);
308 903 m_dispatched += m_segments.Counts()[segment];
309 }
310
311 791 ResolveParams params{};
312
1/1
✓ Branch 1 taken 791 times.
791 params.tree.parent.address = m_parents.Buffer()->Address();
313 791 params.tree.xform.address = m_xforms.Buffer()->Address();
314 791 params.tree.segmentRows.address = m_segmentRows->Address();
315 791 params.tree.segmentStart.address = m_segmentStart->Address();
316 791 params.tree.segmentCount.address = m_segmentCount->Address();
317
1/1
✓ Branch 1 taken 791 times.
791 params.scene.address = store.DeviceData();
318
1/2
✓ Branch 0 taken 791 times.
✗ Branch 1 not taken.
791 params.transforms.address = m_transforms.Address();
319
1/2
✓ Branch 0 taken 791 times.
✗ Branch 1 not taken.
791 params.imageables.address = m_imageables.Address();
320 791 Paged<uint64_t>* stamps[] = {&params.translateStamps, &params.orientStamps,
321 &params.scaleStamps, &params.resetXformStackStamps,
322 &params.visibilityStamps, &params.purposeStamps,
323 791 &params.materialBindingStamps};
324
2/2
✓ Branch 0 taken 5537 times.
✓ Branch 1 taken 791 times.
6328 for (size_t at = 0; at < std::size(kStamped); ++at) {
325 5537 const ColumnRef column = kStamped[at];
326
3/3
✓ Branch 1 taken 5537 times.
✓ Branch 4 taken 5537 times.
✓ Branch 7 taken 5537 times.
5537 stamps[at]->pages.address = store.Schema(column.schema).Pages(store.StampColumn(column));
327 }
328 791 params.since = changes.sinceStamp;
329 791 params.until = changes.untilStamp;
330
4/4
✓ Branch 0 taken 466 times.
✓ Branch 1 taken 325 times.
✓ Branch 2 taken 452 times.
✓ Branch 3 taken 14 times.
791 params.all = rebuild || refollowed ? 1 : 0;
331
4/4
✓ Branch 0 taken 681 times.
✓ Branch 1 taken 110 times.
✓ Branch 2 taken 22 times.
✓ Branch 3 taken 659 times.
791 params.inherit = changes.removed.empty() && changes.apis.empty() ? 0 : 1;
332
333
1/1
✓ Branch 1 taken 791 times.
791 const bool inherited =
334
7/7
✓ Branch 1 taken 791 times.
✓ Branch 3 taken 352 times.
✓ Branch 4 taken 439 times.
✓ Branch 5 taken 296 times.
✓ Branch 6 taken 56 times.
✓ Branch 7 taken 11 times.
✓ Branch 8 taken 285 times.
791 written(stamped.subspan(4)) || !changes.removed.empty() || !changes.apis.empty();
335
2/2
✓ Branch 0 taken 763 times.
✓ Branch 1 taken 28 times.
791 m_seen = store.WriteSequence();
336 791 std::vector<uint32_t> worlds;
337
2/2
✓ Branch 0 taken 763 times.
✓ Branch 1 taken 28 times.
791 if (global || refollowed) {
338
1/1
✓ Branch 1 taken 763 times.
763 worlds.push_back(kGlobalWorld);
339 } else {
340
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 28 times.
50 for (const uint32_t segment : segments) {
341
2/3
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 22 times.
44 worlds.push_back(SlotWorld(segment));
342 }
343 }
344
3/4
✓ Branch 0 taken 654 times.
✓ Branch 1 taken 137 times.
✓ Branch 2 taken 654 times.
✗ Branch 3 not taken.
791 if ((moved || refollowed) && !segments.empty()) {
345
1/1
✓ Branch 1 taken 654 times.
654 versions.Record(TableId::Transforms, worlds);
346 }
347
3/4
✓ Branch 0 taken 506 times.
✓ Branch 1 taken 285 times.
✓ Branch 2 taken 506 times.
✗ Branch 3 not taken.
791 if (inherited && !segments.empty()) {
348
1/1
✓ Branch 1 taken 506 times.
506 versions.Record(TableId::Imageables, worlds);
349 }
350
2/2
✓ Branch 0 taken 37 times.
✓ Branch 1 taken 754 times.
791 if (following) {
351
1/1
✓ Branch 1 taken 37 times.
37 versions.Record(TableId::Transforms, m_follows.worlds);
352 }
353
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 786 times.
791 device::Queue& queue = store.GetDevice().GetQueue();
354
4/5
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 786 times.
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 5 times.
791 static const profiler::ZoneId zone = profiler::ZoneId::Named("Hierarchy/Resolve");
355
1/1
✓ Branch 1 taken 791 times.
791 device::CommandEncoder encoder = queue.CreateEncoder();
356 791 Ref<device::Buffer> dirty;
357
2/2
✓ Branch 0 taken 784 times.
✓ Branch 1 taken 7 times.
791 if (!segments.empty()) {
358
2/2
✓ Branch 1 taken 784 times.
✓ Branch 4 taken 784 times.
784 dirty = store.GetDevice().CreateBuffer({.label = "hierarchy dirty segments"}, segments);
359 784 params.segments.address = dirty->Address();
360
1/1
✓ Branch 1 taken 784 times.
784 const device::ZoneScope scope(encoder, zone);
361
3/3
✓ Branch 1 taken 784 times.
✓ Branch 4 taken 784 times.
✓ Branch 7 taken 784 times.
784 encoder.Dispatch(
362
2/2
✓ Branch 1 taken 784 times.
✓ Branch 4 taken 784 times.
784 Kernels().Entry("ResolveRows"), params,
363
1/1
✓ Branch 1 taken 784 times.
784 {.x = device::GroupCount(widest, kHierarchyGroup), .y = uint32_t(segments.size())});
364 784 }
365
2/2
✓ Branch 0 taken 37 times.
✓ Branch 1 taken 754 times.
791 if (following) {
366
1/1
✓ Branch 1 taken 37 times.
37 RecordFollows(encoder, params);
367 }
368
4/4
✓ Branch 1 taken 791 times.
✓ Branch 4 taken 791 times.
✓ Branch 7 taken 784 times.
✓ Branch 8 taken 7 times.
791 queue.Submit(encoder.Finish());
369 3167 }
370
371 530 void TransformTable::PlanFollows() {
372
4/5
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 525 times.
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 5 times.
530 CHRONO_BATCH_RENDER_ZONE("Hierarchy/PlanFollows");
373 530 const Prims& prims = m_structure.GetPrims();
374 530 m_follows = {};
375 530 std::vector<uint32_t> followers;
376
2/2
✓ Branch 0 taken 11939 times.
✓ Branch 1 taken 530 times.
12469 for (uint32_t prim = 0; prim < prims.Size(); ++prim) {
377
9/9
✓ Branch 1 taken 11939 times.
✓ Branch 3 taken 11126 times.
✓ Branch 4 taken 813 times.
✓ Branch 6 taken 11126 times.
✓ Branch 9 taken 11126 times.
✓ Branch 11 taken 9529 times.
✓ Branch 12 taken 1597 times.
✓ Branch 13 taken 31 times.
✓ Branch 14 taken 9498 times.
21468 if (prims.Alive(prim) && IsA(prims.Record(prim).schema, SchemaId::Xformable) &&
378
1/1
✓ Branch 1 taken 9529 times.
9529 prims.HasApi(prim, SchemaId::FollowAPI)) {
379
1/1
✓ Branch 1 taken 31 times.
31 followers.push_back(prim);
380 }
381 }
382
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 504 times.
530 if (followers.empty()) {
383 return;
384 }
385
386 106 const auto resolve = [&](const std::vector<std::string>& paths) {
387
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 48 times.
80 if (paths.empty()) {
388 return kNoPrim;
389 }
390 32 const uint32_t prim = m_structure.Find(paths.front());
391
3/6
✓ Branch 0 taken 32 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 32 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 32 times.
✗ Branch 6 not taken.
64 const bool valid = prim != kNoPrim && prims.Alive(prim) &&
392 32 IsA(prims.Record(prim).schema, SchemaId::Xformable);
393 return valid ? prim : kNoPrim;
394 26 };
395
396 57 struct Link {
397 uint32_t target = kNoPrim;
398 uint32_t lookAt = kNoPrim;
399 };
400
401
1/1
✓ Branch 1 taken 26 times.
26 std::unordered_map<uint32_t, Link> links;
402 26 {
403
2/2
✓ Branch 1 taken 26 times.
✓ Branch 4 taken 26 times.
26 const View view(m_structure, SchemaId::FollowAPI, followers);
404
1/1
✓ Branch 1 taken 26 times.
26 const UntypedAttribute targets = view.Get(kFollowAPIFollowTarget);
405
1/1
✓ Branch 1 taken 26 times.
26 const UntypedAttribute paths = view.Get(kFollowAPIFollowPath);
406
1/1
✓ Branch 1 taken 26 times.
26 const UntypedAttribute looks = view.Get(kFollowAPIFollowLookAt);
407
2/4
✓ Branch 1 taken 26 times.
✓ Branch 4 taken 26 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
26 const std::vector<Float3> lags = view.Get<Float3>(kFollowAPIFollowPositionLag).Get();
408
2/2
✓ Branch 1 taken 26 times.
✓ Branch 4 taken 26 times.
26 const std::vector<float> turns = view.Get<float>(kFollowAPIFollowRotationLag).Get();
409
2/2
✓ Branch 1 taken 26 times.
✓ Branch 4 taken 26 times.
26 const std::vector<float> speeds = view.Get<float>(kFollowAPIFollowPathSpeed).Get();
410
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 26 times.
57 for (uint32_t index = 0; index < followers.size(); ++index) {
411
2/2
✓ Branch 1 taken 31 times.
✓ Branch 4 taken 31 times.
31 uint32_t target = resolve(paths.GetTargets(index));
412
5/6
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 18 times.
✓ Branch 3 taken 13 times.
✓ Branch 6 taken 13 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 13 times.
31 if (target != kNoPrim && IsA(prims.Record(target).schema, SchemaId::BasisCurves)) {
413 13 m_follows.pathed = true;
414
3/4
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 7 times.
19 m_follows.riding = m_follows.riding || speeds[index] != 0;
415 } else {
416
2/2
✓ Branch 1 taken 18 times.
✓ Branch 4 taken 18 times.
18 target = resolve(targets.GetTargets(index));
417 }
418
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 31 times.
31 if (target == kNoPrim) {
419 ✗ continue;
420 }
421
3/3
✓ Branch 1 taken 31 times.
✓ Branch 4 taken 31 times.
✓ Branch 7 taken 31 times.
31 links[followers[index]] = {target, resolve(looks.GetTargets(index))};
422
1/2
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
31 const Float3 lag = lags[index];
423 31 m_follows.lagged =
424
6/10
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 27 times.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 27 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 27 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 27 times.
31 m_follows.lagged || lag.x > 0 || lag.y > 0 || lag.z > 0 || turns[index] > 0;
425 }
426 104 }
427
428 26 std::vector<uint32_t> owner;
429
1/1
✓ Branch 1 taken 26 times.
26 std::unordered_map<uint32_t, uint32_t> levels;
430 26 std::unordered_map<uint32_t, uint8_t> marks;
431 26 std::vector<uint32_t> cyclic;
432 26 std::vector<uint32_t> resolving;
433 125 const auto findOwner = [&](uint32_t prim) {
434 99 std::vector<uint32_t> chain;
435 99 uint32_t found = kNoPrim;
436
3/3
✓ Branch 1 taken 36 times.
✓ Branch 3 taken 102 times.
✓ Branch 4 taken 33 times.
135 for (uint32_t at = prim; at != kNoPrim; at = prims.Record(at).parent) {
437
2/2
✓ Branch 0 taken 68 times.
✓ Branch 1 taken 34 times.
102 if (owner[at] != kUnresolved) {
438 found = owner[at];
439 break;
440 }
441
1/1
✓ Branch 1 taken 68 times.
68 chain.push_back(at);
442
2/2
✓ Branch 1 taken 32 times.
✓ Branch 2 taken 36 times.
68 if (links.contains(at)) {
443 32 found = at;
444 32 break;
445 }
446 }
447
2/2
✓ Branch 0 taken 68 times.
✓ Branch 1 taken 99 times.
167 for (const uint32_t walked : chain) {
448 68 owner[walked] = found;
449 }
450 99 return found;
451 99 };
452
1/1
✓ Branch 1 taken 26 times.
26 std::function<uint32_t(uint32_t)> findLevel;
453 ✗ const std::function<uint32_t(uint32_t)> follow = [&](uint32_t follower) -> uint32_t {
454 38 const uint8_t mark = marks[follower];
455
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 34 times.
38 if (mark == 2) {
456 4 return levels[follower];
457 }
458
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 33 times.
34 if (mark == 1) {
459 1 const auto loop = std::ranges::find(resolving, follower);
460 1 cyclic.insert(cyclic.end(), loop, resolving.end());
461 1 return 0;
462 }
463 33 marks[follower] = 1;
464 33 resolving.push_back(follower);
465 33 const Link link = links.at(follower);
466 33 const uint32_t deepest = std::max({findLevel(prims.Record(follower).parent),
467 33 findLevel(link.target), findLevel(link.lookAt)});
468 33 resolving.pop_back();
469 33 marks[follower] = 2;
470 33 levels[follower] = deepest + 1;
471 33 return deepest + 1;
472
1/1
✓ Branch 1 taken 26 times.
26 };
473 26 findLevel = [&](uint32_t prim) -> uint32_t {
474
2/2
✓ Branch 0 taken 35 times.
✓ Branch 1 taken 64 times.
99 if (prim == kNoPrim) {
475 return 0;
476 }
477 35 const uint32_t found = findOwner(prim);
478
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 30 times.
35 return found == kNoPrim ? 0 : follow(found);
479 26 };
480 28 while (true) {
481
1/1
✓ Branch 1 taken 27 times.
27 owner.assign(prims.Size(), kUnresolved);
482 27 levels.clear();
483 27 marks.clear();
484 27 cyclic.clear();
485
2/2
✓ Branch 0 taken 33 times.
✓ Branch 1 taken 27 times.
60 for (const auto& entry : links) {
486
1/1
✓ Branch 1 taken 33 times.
33 follow(entry.first);
487 }
488
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 26 times.
27 if (cyclic.empty()) {
489 break;
490 }
491 1 std::ranges::sort(cyclic);
492 1 cyclic.erase(std::unique(cyclic.begin(), cyclic.end()), cyclic.end());
493
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 time.
3 for (const uint32_t follower : cyclic) {
494
1/1
✓ Branch 1 taken 2 times.
2 Log(LogLevel::Warning, "{} follows itself through its targets, so it follows nothing",
495
1/1
✓ Branch 1 taken 2 times.
2 m_structure.GetPaths().Path(follower));
496 2 links.erase(follower);
497 }
498 1 }
499
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 26 times.
26 if (links.empty()) {
500 ✗ m_follows.lagged = false;
501 ✗ m_follows.pathed = false;
502 ✗ m_follows.riding = false;
503 ✗ return;
504 }
505
506 26 uint32_t deepest = 0;
507
2/2
✓ Branch 0 taken 29 times.
✓ Branch 1 taken 26 times.
55 for (const auto& entry : levels) {
508
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 3 times.
55 deepest = std::max(deepest, entry.second);
509 }
510
1/1
✓ Branch 1 taken 26 times.
26 std::vector<std::vector<uint32_t>> frames(deepest + 1);
511
1/1
✓ Branch 1 taken 26 times.
26 std::vector<std::vector<uint32_t>> owned(deepest + 1);
512
1/1
✓ Branch 1 taken 26 times.
26 std::vector<std::vector<uint32_t>> owners(deepest + 1);
513 26 std::set<uint32_t> worlds;
514
2/2
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 26 times.
90 for (uint32_t prim = 0; prim < prims.Size(); ++prim) {
515
5/7
✓ Branch 1 taken 64 times.
✓ Branch 3 taken 64 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 64 times.
✓ Branch 9 taken 64 times.
✓ Branch 11 taken 64 times.
✗ Branch 12 not taken.
64 if (!prims.Alive(prim) || !IsA(prims.Record(prim).schema, SchemaId::Xformable)) {
516 32 continue;
517 }
518
1/1
✓ Branch 1 taken 64 times.
64 const uint32_t found = findOwner(prim);
519
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 32 times.
64 if (found == kNoPrim) {
520 32 continue;
521 }
522
1/1
✓ Branch 1 taken 32 times.
32 const uint32_t level = levels.at(found);
523
1/1
✓ Branch 1 taken 32 times.
32 const uint32_t row = prims.Row(prim, SchemaId::Imageable);
524
2/2
✓ Branch 0 taken 29 times.
✓ Branch 1 taken 3 times.
32 if (found == prim) {
525
1/1
✓ Branch 1 taken 29 times.
29 frames[level].push_back(row);
526 } else {
527
1/1
✓ Branch 1 taken 3 times.
3 owned[level].push_back(row);
528
2/2
✓ Branch 1 taken 3 times.
✓ Branch 4 taken 3 times.
3 owners[level].push_back(prims.Row(found, SchemaId::Imageable));
529 }
530
2/2
✓ Branch 1 taken 32 times.
✓ Branch 4 taken 32 times.
32 worlds.insert(prims.Record(prim).world);
531 }
532 26 std::vector<uint32_t> rows;
533 26 std::vector<uint32_t> rowOwners;
534
2/2
✓ Branch 0 taken 54 times.
✓ Branch 1 taken 26 times.
80 for (uint32_t level = 0; level <= deepest; ++level) {
535
1/1
✓ Branch 1 taken 54 times.
54 m_follows.frameStarts.push_back(uint32_t(rows.size()));
536
1/1
✓ Branch 1 taken 54 times.
54 rows.insert(rows.end(), frames[level].begin(), frames[level].end());
537
1/1
✓ Branch 1 taken 54 times.
54 rowOwners.insert(rowOwners.end(), frames[level].size(), kNoRow);
538
1/1
✓ Branch 1 taken 54 times.
54 m_follows.frames.insert(m_follows.frames.end(), frames[level].begin(), frames[level].end());
539 }
540
1/1
✓ Branch 1 taken 26 times.
26 m_follows.frameStarts.push_back(uint32_t(rows.size()));
541
2/2
✓ Branch 0 taken 54 times.
✓ Branch 1 taken 26 times.
80 for (uint32_t level = 0; level <= deepest; ++level) {
542
1/1
✓ Branch 1 taken 54 times.
54 m_follows.ownedStarts.push_back(uint32_t(rows.size()));
543
1/1
✓ Branch 1 taken 54 times.
54 rows.insert(rows.end(), owned[level].begin(), owned[level].end());
544
1/1
✓ Branch 1 taken 54 times.
54 rowOwners.insert(rowOwners.end(), owners[level].begin(), owners[level].end());
545
1/1
✓ Branch 1 taken 54 times.
54 m_follows.owned.insert(m_follows.owned.end(), owned[level].begin(), owned[level].end());
546 }
547
1/1
✓ Branch 1 taken 26 times.
26 m_follows.ownedStarts.push_back(uint32_t(rows.size()));
548
1/1
✓ Branch 1 taken 26 times.
26 m_follows.worlds.assign(worlds.begin(), worlds.end());
549
1/1
✓ Branch 1 taken 26 times.
26 device::Device& device = m_structure.GetColumnStore().GetDevice();
550
3/3
✓ Branch 1 taken 26 times.
✓ Branch 3 taken 13 times.
✓ Branch 4 taken 13 times.
39 m_followRows = device.CreateBuffer({.label = "follow rows"}, rows);
551
3/3
✓ Branch 1 taken 26 times.
✓ Branch 3 taken 13 times.
✓ Branch 4 taken 13 times.
39 m_followOwners = device.CreateBuffer({.label = "follow owners"}, rowOwners);
552 582 }
553
554 37 void TransformTable::RecordFollows(device::CommandEncoder& encoder, const ResolveParams& resolved) {
555 37 ColumnStore& store = m_structure.GetColumnStore();
556 37 device::Device& device = store.GetDevice();
557 111 if (m_followStates.Reserve(device,
558
3/4
✗ Branch 1 not taken.
✓ Branch 2 taken 37 times.
✓ Branch 4 taken 13 times.
✓ Branch 5 taken 24 times.
37 std::max(store.Schema(SchemaId::FollowAPI).Capacity(), 1u))) {
559
1/2
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
26 encoder.Fill(m_followStates.Buffer()->Slice(0, uint64_t(m_followStates.Rows()) *
560 sizeof(FollowState)));
561 }
562 37 FollowParams params{};
563 37 params.tree = resolved.tree;
564 37 params.scene = resolved.scene;
565 37 params.transforms = resolved.transforms;
566
1/2
✓ Branch 0 taken 37 times.
✗ Branch 1 not taken.
37 params.rows.address = m_followRows->Address();
567 37 params.owners.address = m_followOwners->Address();
568
1/2
✓ Branch 0 taken 37 times.
✗ Branch 1 not taken.
37 params.states.address = m_followStates.Address();
569 37 params.zero = resolved.zero;
570
1/2
✓ Branch 0 taken 37 times.
✗ Branch 1 not taken.
37 if (m_clock != nullptr) {
571
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 37 times.
37 if (m_clock->resets != m_clockResets) {
572 ✗ params.snap = 1;
573
2/2
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 3 times.
37 } else if (m_clock->steps != m_clockSteps) {
574
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 34 times.
34 params.dt = float(std::max(m_clock->end - m_clockEnd, 0.0));
575 }
576 37 m_clockResets = m_clock->resets;
577 37 m_clockSteps = m_clock->steps;
578 37 m_clockEnd = m_clock->end;
579 }
580
4/5
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 35 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 2 times.
37 static const profiler::ZoneId zone = profiler::ZoneId::Named("Hierarchy/Follow");
581 37 const device::ZoneScope scope(encoder, zone);
582 37 const uint32_t levels = uint32_t(m_follows.frameStarts.size()) - 1;
583
2/2
✓ Branch 0 taken 76 times.
✓ Branch 1 taken 37 times.
113 for (uint32_t level = 0; level < levels; ++level) {
584 76 const std::pair<const char*, const std::vector<uint32_t>*> passes[] = {
585 76 {"FollowFrames", &m_follows.frameStarts}, {"FollowOwned", &m_follows.ownedStarts}};
586
4/4
✓ Branch 0 taken 109 times.
✓ Branch 1 taken 43 times.
✓ Branch 2 taken 152 times.
✓ Branch 3 taken 76 times.
228 for (const auto& [entry, starts] : passes) {
587
2/2
✓ Branch 0 taken 109 times.
✓ Branch 1 taken 43 times.
152 params.first = (*starts)[level];
588 152 params.count = (*starts)[level + 1] - params.first;
589
2/2
✓ Branch 0 taken 109 times.
✓ Branch 1 taken 43 times.
152 if (params.count == 0) {
590 109 continue;
591 }
592
1/1
✓ Branch 1 taken 43 times.
43 encoder.Barrier();
593
3/3
✓ Branch 1 taken 43 times.
✓ Branch 4 taken 43 times.
✓ Branch 7 taken 43 times.
86 encoder.Dispatch(Kernels().Entry(entry), params,
594
1/1
✓ Branch 1 taken 43 times.
43 {.x = device::GroupCount(params.count, kHierarchyGroup)});
595 }
596 }
597 37 }
598
599 808 void TransformTable::WriteMotions(bool restructured) {
600 808 ColumnStore& store = m_structure.GetColumnStore();
601
2/2
✓ Branch 1 taken 797 times.
✓ Branch 2 taken 11 times.
808 const bool written = store.LastWrite(kMotionAPIMotionVelocity) > m_motionSeen ||
602
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 797 times.
797 store.LastWrite(kMotionAPIMotionAngularVelocity) > m_motionSeen;
603 808 m_motionSeen = store.WriteSequence();
604 808 device::Device& device = store.GetDevice();
605 808 const bool grown = m_motions.Reserve(device, store.Schema(SchemaId::MotionAPI).Capacity());
606
2/2
✓ Branch 1 taken 11 times.
✓ Branch 2 taken 797 times.
808 const uint32_t rows = store.Schema(SchemaId::MotionAPI).Rows();
607
3/6
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 797 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 11 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
808 if (rows == 0 || !(written || restructured || grown)) {
608 797 return;
609 }
610 11 MotionParams params{};
611 11 params.scene.address = store.DeviceData();
612
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 params.motions.address = m_motions.Address();
613 11 params.rows = rows;
614 11 device::Queue& queue = device.GetQueue();
615 11 device::CommandEncoder encoder = queue.CreateEncoder();
616
3/3
✓ Branch 1 taken 11 times.
✓ Branch 4 taken 11 times.
✓ Branch 7 taken 11 times.
11 encoder.Dispatch(Kernels().Entry("WriteMotions"), params,
617
1/1
✓ Branch 1 taken 11 times.
11 {.x = device::GroupCount(rows, kHierarchyGroup)});
618
2/2
✓ Branch 1 taken 11 times.
✓ Branch 4 taken 11 times.
11 queue.Submit(encoder.Finish());
619 11 }
620
621 template <class Params>
622 238 device::Operation TransformTable::Dispatch(const char* entry, const Params& params,
623 uint32_t threads) {
624
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 238 times.
238 device::Queue& queue = m_structure.GetColumnStore().GetDevice().GetQueue();
625
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 238 times.
238 if (threads == 0) {
626 ✗ return queue.Mark();
627 }
628 238 device::CommandEncoder encoder = queue.CreateEncoder();
629 {
630
1/1
✓ Branch 1 taken 238 times.
476 const device::ZoneScope zone(encoder,
631
2/2
✓ Branch 1 taken 238 times.
✓ Branch 4 taken 238 times.
476 profiler::ZoneId::Named(std::string("Hierarchy/") + entry));
632
2/2
✓ Branch 1 taken 238 times.
✓ Branch 4 taken 238 times.
238 encoder.Dispatch(Kernels().Entry(entry), params,
633
1/1
✓ Branch 1 taken 238 times.
238 {.x = device::GroupCount(threads, kHierarchyGroup)});
634 238 }
635
2/2
✓ Branch 1 taken 238 times.
✓ Branch 4 taken 238 times.
238 return queue.Submit(encoder.Finish());
636 238 }
637
638 230 device::Tensor TransformTable::ReadWorldPoses(ViewState& view, device::DType type, Stream consumer,
639 QuaternionOrder order) {
640
4/5
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 227 times.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 3 times.
230 CHRONO_BATCH_RENDER_ZONE("Hierarchy/ReadWorldPoses");
641
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 228 times.
230 device::Device& device = m_structure.GetColumnStore().GetDevice();
642
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 228 times.
230 const uint32_t count = uint32_t(view.Prims().size());
643 230 const bool doubles = type == device::DType::Float64;
644 230 const Ref<device::Buffer> values = device.CreateBuffer(
645
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 230 times.
230 {.bytes = std::max<uint64_t>(uint64_t(count) * kPoseWords * (doubles ? 8 : 4), 4),
646 .usage = device::BufferUsage::Interop,
647
2/2
✓ Branch 1 taken 230 times.
✓ Branch 4 taken 230 times.
230 .label = "world poses"});
648 230 WorldPoseParams params{};
649 230 params.transforms.address = Transforms()->Address();
650
1/1
✓ Branch 1 taken 230 times.
230 const RowSource rows = view.Source(SchemaId::Xformable);
651 230 params.rows.address = rows.rows;
652 230 params.firstRow = rows.firstRow;
653
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 228 times.
230 params.out.address = values->Address();
654 230 params.count = count;
655
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 228 times.
230 params.doubles = doubles ? 1 : 0;
656 230 params.order = uint32_t(order);
657
2/2
✓ Branch 1 taken 230 times.
✓ Branch 4 taken 230 times.
230 Dispatch("ReadWorldPoses", params, count).Wait(consumer);
658
3/6
✓ Branch 1 taken 230 times.
✓ Branch 4 taken 230 times.
✓ Branch 8 taken 230 times.
✗ Branch 9 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
920 return device::Tensor(values, 0, type, {int64_t(count), int64_t(kPoseWords)});
659 920 }
660
661 1 device::Tensor TransformTable::ReadWorldScales(ViewState& view, device::DType type,
662 Stream consumer) {
663
3/5
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
✓ Branch 3 taken 1 time.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 time.
1 CHRONO_BATCH_RENDER_ZONE("Hierarchy/ReadWorldScales");
664
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 device::Device& device = m_structure.GetColumnStore().GetDevice();
665
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 const uint32_t count = uint32_t(view.Prims().size());
666 1 const bool doubles = type == device::DType::Float64;
667 1 const Ref<device::Buffer> values = device.CreateBuffer(
668
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 {.bytes = std::max<uint64_t>(uint64_t(count) * 3 * (doubles ? 8 : 4), 4),
669 .usage = device::BufferUsage::Interop,
670
2/2
✓ Branch 1 taken 1 time.
✓ Branch 4 taken 1 time.
1 .label = "world scales"});
671 1 WorldPoseParams params{};
672 1 params.transforms.address = Transforms()->Address();
673
1/1
✓ Branch 1 taken 1 time.
1 const RowSource rows = view.Source(SchemaId::Xformable);
674 1 params.rows.address = rows.rows;
675 1 params.firstRow = rows.firstRow;
676
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 params.out.address = values->Address();
677 1 params.count = count;
678
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 params.doubles = doubles ? 1 : 0;
679
2/2
✓ Branch 1 taken 1 time.
✓ Branch 4 taken 1 time.
1 Dispatch("ReadWorldScales", params, count).Wait(consumer);
680
3/6
✓ Branch 1 taken 1 time.
✓ Branch 4 taken 1 time.
✓ Branch 8 taken 1 time.
✗ Branch 9 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
4 return device::Tensor(values, 0, type, {int64_t(count), 3});
681 4 }
682
683 7 device::Tensor TransformTable::Localize(ViewState& view, const device::Tensor& values,
684 const device::Tensor* rotations, QuaternionOrder order,
685 const device::Tensor& sources) {
686
4/5
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 5 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 2 times.
7 CHRONO_BATCH_RENDER_ZONE("Hierarchy/Localize");
687
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 ColumnStore& store = m_structure.GetColumnStore();
688
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 device::Device& device = store.GetDevice();
689
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 const uint32_t count = uint32_t(view.Prims().size());
690 7 const Ref<device::Buffer> local = device.CreateBuffer(
691
1/1
✓ Branch 1 taken 7 times.
7 {.bytes = std::max<uint64_t>(uint64_t(count) * kPoseWords * 8, 4), .label = "local poses"});
692 14 const Ref<device::Buffer>& parents = view.XformParents();
693 7 WorldPoseParams params{};
694
1/1
✓ Branch 1 taken 7 times.
7 params.scene.address = store.DeviceData();
695
1/1
✓ Branch 1 taken 7 times.
7 params.transforms.address = Transforms()->Address();
696
1/1
✓ Branch 1 taken 7 times.
7 const RowSource rows = view.Source(SchemaId::Xformable);
697 7 params.rows.address = rows.rows;
698 7 params.firstRow = rows.firstRow;
699
1/1
✓ Branch 1 taken 7 times.
7 params.parents.address = parents->Address();
700
1/1
✓ Branch 1 taken 7 times.
7 params.values.address = values.Address();
701
3/3
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 6 times.
✓ Branch 3 taken 1 time.
7 params.rotations.address = rotations ? rotations->Address() : 0;
702
3/3
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 6 times.
✓ Branch 3 taken 1 time.
7 params.sources.address = sources.Empty() ? 0 : sources.Address();
703
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 5 times.
7 params.out.address = local->Address();
704 7 params.count = count;
705
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 5 times.
7 params.doubles = values.Type() == device::DType::Float64 ? 1 : 0;
706 7 params.order = uint32_t(order);
707
1/1
✓ Branch 1 taken 7 times.
7 Dispatch("WorldToLocalPoses", params, count);
708
3/6
✓ Branch 1 taken 7 times.
✓ Branch 4 taken 7 times.
✓ Branch 8 taken 7 times.
✗ Branch 9 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
28 return device::Tensor(local, 0, device::DType::Float64, {int64_t(count), int64_t(kPoseWords)});
709 21 }
710
711 353 void TransformTable::Prepare() {
712 353 Kernels();
713 353 }
714
715 1429 device::Program& TransformTable::Kernels() {
716
2/2
✓ Branch 0 taken 389 times.
✓ Branch 1 taken 1040 times.
1429 if (!m_program) {
717
2/2
✓ Branch 2 taken 389 times.
✓ Branch 5 taken 389 times.
1167 m_program = m_structure.GetColumnStore().GetDevice().CreateProgram(
718 {.modules = {"chrono_batch_render.scene.tables.kernels.transforms"},
719 .library = &kSceneKernels,
720 .entries = {"ResolveRows", "WriteMotions", "FollowFrames", "FollowOwned",
721 "ReadWorldPoses", "ReadWorldScales", "WorldToLocalPoses"},
722
2/3
✓ Branch 1 taken 389 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 389 times.
778 .label = "hierarchy"});
723 }
724 1429 return *m_program;
725 778 }
726
727 }
728