chrono_batch_render/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
348 / 390
89.2%
Branches:
279 / 355
78.6%
Functions:
34 / 35
97.1%

stage/structure/prims.cpp
Line Branch Exec Source
1 // SPDX-License-Identifier: BSD-3-Clause
2 #include "chrono_batch_render/stage/structure/prims.h"
3
4 #include <algorithm>
5 #include <set>
6
7 #include "chrono_batch_render/util/error.h"
8
9 namespace chrono::batch_render {
10
11 namespace {
12
13 242875 std::span<const std::byte> Word(const uint32_t& value) {
14 242875 return std::as_bytes(std::span(&value, 1));
15 }
16
17 91298 uint64_t BlockApis(const BlockShape& shape, uint32_t index) {
18
1/2
✓ Branch 0 taken 91298 times.
✗ Branch 1 not taken.
91298 const uint64_t applied = shape.apis.empty() ? 0 : shape.apis[index];
19
2/2
✓ Branch 0 taken 550 times.
✓ Branch 1 taken 90748 times.
91298 return (Info(shape.schemas[index]).apis | applied) & ~kMultipleApis;
20 }
21
22 7629 uint64_t InstanceKey(uint32_t prim, SchemaId api) {
23
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7629 times.
7629 return (uint64_t(prim) << 6) | Info(api).api;
24 }
25
26 }
27
28 1053446 bool Prims::Alive(uint32_t prim) const {
29
4/4
✓ Branch 0 taken 1053444 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 6176 times.
✓ Branch 3 taken 1047268 times.
1053446 return prim < m_records.size() && (m_records[prim].flags & kPrimAlive) != 0;
30 }
31
32 329583 uint32_t Prims::Generation(uint32_t prim) const {
33
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 329583 times.
329583 if (prim >= m_records.size()) {
34
0/1
✗ Branch 2 not taken.
✗ throw Error("No prim {}", prim);
35 }
36 329583 return m_records[prim].generation;
37 }
38
39 606929 const PrimRecord& Prims::Record(uint32_t prim) const {
40
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 606929 times.
606929 if (!Alive(prim)) {
41
0/1
✗ Branch 2 not taken.
✗ throw Error("No prim {}", prim);
42 }
43 606929 return m_records[prim];
44 }
45
46 88414 uint32_t Prims::FindRow(uint32_t prim, SchemaId table) const {
47 88414 const PrimRecord& record = Record(prim);
48 88414 const uint32_t slot = PrimSlot(record.schema, record.apis, table);
49
2/2
✓ Branch 0 taken 87177 times.
✓ Branch 1 taken 1237 times.
88414 return slot == kNoSlot ? kNoRow : m_slotRows[record.slots + slot];
50 }
51
52 63 std::vector<uint32_t> Prims::Rows(std::span<const uint32_t> prims, SchemaId table) const {
53 63 std::vector<uint32_t> rows;
54
1/1
✓ Branch 1 taken 63 times.
63 rows.reserve(prims.size());
55
2/2
✓ Branch 0 taken 67 times.
✓ Branch 1 taken 63 times.
130 for (const uint32_t prim : prims) {
56
4/4
✓ Branch 0 taken 66 times.
✓ Branch 1 taken 1 time.
✓ Branch 3 taken 66 times.
✓ Branch 6 taken 67 times.
67 rows.push_back(Alive(prim) ? FindRow(prim, table) : kNoRow);
57 }
58 63 return rows;
59 ✗ }
60
61 85857 uint32_t Prims::Row(uint32_t prim, SchemaId table) const {
62 85857 const uint32_t row = FindRow(prim, table);
63
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 85857 times.
85857 if (row == kNoRow) {
64
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ throw Error("Prim {}, a {}, has no {} data", prim, Info(Record(prim).schema).name,
65
0/4
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 7 not taken.
✗ Info(table).name);
66 }
67 85857 return row;
68 }
69
70 22728 bool Prims::HasApi(uint32_t prim, SchemaId api) const {
71 22728 return chrono::batch_render::HasApi(Record(prim).apis, api);
72 }
73
74 7441 std::span<const ApiInstance> Prims::Instances(uint32_t prim, SchemaId api) const {
75 7441 const auto found = m_instances.find(InstanceKey(prim, api));
76
2/2
✓ Branch 0 taken 7186 times.
✓ Branch 1 taken 255 times.
7696 return found == m_instances.end() ? std::span<const ApiInstance>() : found->second;
77 }
78
79 95 uint32_t Prims::InstanceRow(uint32_t prim, SchemaId api, uint32_t instance) const {
80
2/2
✓ Branch 1 taken 40 times.
✓ Branch 2 taken 56 times.
96 for (const ApiInstance& entry : Instances(prim, api)) {
81
2/2
✓ Branch 0 taken 39 times.
✓ Branch 1 taken 1 time.
40 if (entry.instance == instance) {
82 39 return entry.row;
83 }
84 }
85 56 return kNoRow;
86 }
87
88 2722 void Prims::Check(const BlockShape& shape, std::span<const uint32_t> parents) const {
89
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2722 times.
2722 if (shape.parents.size() != shape.schemas.size()) {
90
0/1
✗ Branch 1 not taken.
✗ throw Error("A block of {} prims with {} parents", shape.schemas.size(),
91
0/1
✗ Branch 2 not taken.
✗ shape.parents.size());
92 }
93
2/4
✓ Branch 0 taken 2722 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2722 times.
2722 if (!shape.apis.empty() && shape.apis.size() != shape.schemas.size()) {
94
0/1
✗ Branch 1 not taken.
✗ throw Error("A block of {} prims with {} API masks", shape.schemas.size(),
95
0/1
✗ Branch 2 not taken.
✗ shape.apis.size());
96 }
97 2722 bool worlds = false;
98
2/2
✓ Branch 0 taken 2799 times.
✓ Branch 1 taken 2721 times.
5520 for (uint32_t i = 0; i < shape.schemas.size(); ++i) {
99 2799 const SchemaId schema = shape.schemas[i];
100
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2799 times.
2799 if (uint32_t(schema) >= SchemaCount()) {
101
0/1
✗ Branch 2 not taken.
✗ throw Error("No schema {}", uint32_t(schema));
102 }
103
4/4
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 2777 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 2798 times.
5598 if (Info(schema).kind != SchemaKind::Concrete) {
104 2 throw Error("{} is {}, so no prim is one", Info(schema).name,
105
4/7
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 1 time.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 time.
✓ Branch 8 taken 1 time.
4 Info(schema).kind == SchemaKind::Api ? "an API schema" : "abstract");
106 }
107
2/2
✓ Branch 0 taken 61556 times.
✓ Branch 1 taken 2798 times.
64354 for (const SchemaId api : kApiSchemas) {
108
5/6
✓ Branch 2 taken 3388 times.
✓ Branch 3 taken 58168 times.
✓ Branch 5 taken 33 times.
✓ Branch 6 taken 3355 times.
✓ Branch 7 taken 33 times.
✗ Branch 8 not taken.
61589 if (chrono::batch_render::HasApi(BlockApis(shape, i), api) && !Includes(schema, api) &&
109 33 !AppliesTo(api, schema)) {
110
0/5
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ throw Error("{} doesn't apply to {}", Info(api).name, Info(schema).name);
111 }
112 }
113
2/2
✓ Branch 0 taken 77 times.
✓ Branch 1 taken 2721 times.
2798 const uint32_t parent = shape.parents[i];
114
3/4
✓ Branch 0 taken 77 times.
✓ Branch 1 taken 2721 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 77 times.
2798 if (parent != kNoPrim && parent >= i) {
115 ✗ throw Error("Prim {} of a block has parent {}, which doesn't come before it", i,
116
0/1
✗ Branch 2 not taken.
✗ parent);
117 }
118
2/2
✓ Branch 1 taken 139 times.
✓ Branch 2 taken 2659 times.
2798 if (IsA(schema, SchemaId::World)) {
119 139 worlds = true;
120
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 139 times.
139 for (uint32_t up = parent; up != kNoPrim; up = shape.parents[up]) {
121
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ if (IsA(shape.schemas[up], SchemaId::World)) {
122
0/2
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✗ throw Error("A World inside a World; worlds don't nest");
123 }
124 }
125 }
126 }
127
2/2
✓ Branch 0 taken 24068 times.
✓ Branch 1 taken 2718 times.
26786 for (const uint32_t parent : parents) {
128
2/2
✓ Branch 0 taken 22901 times.
✓ Branch 1 taken 1167 times.
24068 if (parent == kNoPrim) {
129 22901 continue;
130 }
131 1167 const PrimRecord& record = Record(parent);
132
3/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1164 times.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
1167 if (worlds && record.world != kGlobalWorld) {
133
1/1
✓ Branch 2 taken 3 times.
3 throw Error("Worlds added inside world {}; worlds don't nest", record.world);
134 }
135 }
136 2718 }
137
138 2737 uint32_t Prims::AcquireSlots(uint32_t count) {
139
1/2
✓ Branch 0 taken 2737 times.
✗ Branch 1 not taken.
2737 if (count == 0) {
140 return kNoRow;
141 }
142 2737 std::vector<uint32_t>& free = m_freeSlots[count];
143
2/2
✓ Branch 0 taken 164 times.
✓ Branch 1 taken 2573 times.
2737 if (!free.empty()) {
144 164 const uint32_t first = free.back();
145 164 free.pop_back();
146 164 return first;
147 }
148 2573 const uint32_t first = m_store.Slots().Allocate(count);
149 2573 m_slotRows.resize(m_store.Slots().Rows(), kNoRow);
150 2573 return first;
151 }
152
153 2591 PrimBlock Prims::AllocateOne(SchemaId schema, uint64_t apis) {
154 2591 PrimBlock block;
155 2591 block.count = 1;
156 2591 block.copies = 1;
157
2/2
✓ Branch 0 taken 2477 times.
✓ Branch 1 taken 114 times.
2591 if (m_freePrims.empty()) {
158
1/1
✓ Branch 1 taken 2477 times.
2477 block.firstPrim = m_store.Prims().Allocate(1);
159
1/1
✓ Branch 1 taken 2477 times.
2477 m_records.resize(m_store.Prims().Rows());
160 } else {
161 114 block.firstPrim = m_freePrims.back();
162 114 m_freePrims.pop_back();
163 }
164
1/1
✓ Branch 1 taken 2591 times.
2591 const std::vector<SchemaId> tables = PrimTables(schema, apis);
165
1/1
✓ Branch 1 taken 2591 times.
2591 block.slotsPerCopy = uint32_t(tables.size());
166
1/1
✓ Branch 1 taken 2591 times.
2591 block.firstSlot = AcquireSlots(block.slotsPerCopy);
167
2/2
✓ Branch 0 taken 10367 times.
✓ Branch 1 taken 2591 times.
12958 for (const SchemaId table : tables) {
168
3/3
✓ Branch 1 taken 10367 times.
✓ Branch 4 taken 10367 times.
✓ Branch 7 taken 10367 times.
10367 block.tables.push_back({table, m_store.Schema(table).Acquire(), 1});
169 }
170 2591 return block;
171 2591 }
172
173 127 PrimBlock Prims::AllocateBlock(const BlockShape& shape, uint32_t copies, bool pageAligned) {
174 127 PrimBlock block;
175
1/1
✓ Branch 1 taken 127 times.
127 block.count = uint32_t(shape.schemas.size());
176 127 block.copies = copies;
177
1/1
✓ Branch 1 taken 127 times.
127 block.firstPrim = m_store.Prims().Allocate(block.count * copies);
178
1/1
✓ Branch 1 taken 127 times.
127 m_records.resize(m_store.Prims().Rows());
179 127 std::map<SchemaId, uint32_t> perCopy;
180
2/2
✓ Branch 0 taken 202 times.
✓ Branch 1 taken 127 times.
329 for (uint32_t i = 0; i < block.count; ++i) {
181
2/2
✓ Branch 1 taken 202 times.
✓ Branch 4 taken 202 times.
202 const std::vector<SchemaId> tables = PrimTables(shape.schemas[i], BlockApis(shape, i));
182
2/2
✓ Branch 0 taken 953 times.
✓ Branch 1 taken 202 times.
1155 for (const SchemaId table : tables) {
183
1/1
✓ Branch 1 taken 953 times.
953 ++perCopy[table];
184 }
185 202 block.slotsPerCopy += uint32_t(tables.size());
186 202 }
187
1/2
✓ Branch 0 taken 127 times.
✗ Branch 1 not taken.
127 if (block.slotsPerCopy > 0) {
188
1/1
✓ Branch 1 taken 127 times.
127 block.firstSlot = m_store.Slots().Allocate(block.slotsPerCopy * copies);
189 }
190
3/3
✓ Branch 1 taken 718 times.
✓ Branch 3 taken 718 times.
✓ Branch 4 taken 127 times.
845 for (const auto& [table, rows] : perCopy) {
191 718 block.tables.push_back(
192
3/3
✓ Branch 1 taken 718 times.
✓ Branch 4 taken 718 times.
✓ Branch 7 taken 718 times.
718 {table, m_store.Schema(table).Allocate(rows * copies, pageAligned), rows});
193 }
194 127 return block;
195 127 }
196
197 2722 PrimBlock Prims::Add(const BlockShape& shape, std::span<const uint32_t> parents,
198 uint32_t reuseWorld, bool pageAligned) {
199 2722 Check(shape, parents);
200
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2718 times.
2718 const uint32_t count = uint32_t(shape.schemas.size());
201
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2718 times.
2718 const uint32_t copies = uint32_t(parents.size());
202
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2718 times.
2718 if (count == 0 || copies == 0) {
203 ✗ return {};
204 }
205
1/2
✓ Branch 0 taken 2591 times.
✗ Branch 1 not taken.
2591 PrimBlock block = count == 1 && copies == 1 && !pageAligned
206
2/2
✓ Branch 0 taken 2591 times.
✓ Branch 1 taken 127 times.
5309 ? AllocateOne(shape.schemas[0], BlockApis(shape, 0))
207 2718 : AllocateBlock(shape, copies, pageAligned);
208
1/1
✓ Branch 1 taken 2718 times.
2718 m_slotRows.resize(m_store.Slots().Rows(), kNoRow);
209
210
2/2
✓ Branch 0 taken 2793 times.
✓ Branch 1 taken 2718 times.
5511 for (const SchemaId schema : shape.schemas) {
211
3/3
✓ Branch 1 taken 2793 times.
✓ Branch 3 taken 2657 times.
✓ Branch 4 taken 136 times.
5450 block.worldsPerCopy += IsA(schema, SchemaId::World) ? 1 : 0;
212 }
213
2/2
✓ Branch 0 taken 136 times.
✓ Branch 1 taken 2582 times.
2718 if (block.worldsPerCopy > 0) {
214
2/2
✓ Branch 0 taken 135 times.
✓ Branch 1 taken 1 time.
136 block.firstWorld = reuseWorld == kGlobalWorld ? m_worldCount : reuseWorld;
215
1/2
✓ Branch 0 taken 136 times.
✗ Branch 1 not taken.
272 m_worldCount = std::max(m_worldCount, block.firstWorld + block.worldsPerCopy * copies);
216 }
217
1/1
✓ Branch 1 taken 2718 times.
2718 m_store.SetCounts(Size(), m_worldCount);
218
219
1/1
✓ Branch 1 taken 2718 times.
2718 std::vector<std::vector<uint32_t>> ordinals(count);
220
1/1
✓ Branch 1 taken 2718 times.
2718 std::vector<uint32_t> slotOffsets(count);
221
1/1
✓ Branch 1 taken 2718 times.
2718 std::map<SchemaId, uint32_t> used;
222 2718 uint32_t offset = 0;
223
1/1
✓ Branch 1 taken 2718 times.
2718 std::vector<std::vector<SchemaId>> primTables(count);
224
2/2
✓ Branch 0 taken 2793 times.
✓ Branch 1 taken 2718 times.
5511 for (uint32_t i = 0; i < count; ++i) {
225
1/1
✓ Branch 1 taken 2793 times.
2793 slotOffsets[i] = offset;
226
2/2
✓ Branch 1 taken 2793 times.
✓ Branch 4 taken 2793 times.
2793 primTables[i] = PrimTables(shape.schemas[i], BlockApis(shape, i));
227
2/2
✓ Branch 0 taken 11320 times.
✓ Branch 1 taken 2793 times.
14113 for (const SchemaId table : primTables[i]) {
228
2/2
✓ Branch 1 taken 11320 times.
✓ Branch 4 taken 11320 times.
11320 ordinals[i].push_back(used[table]++);
229 11320 ++offset;
230 }
231 }
232 2718 std::map<SchemaId, const BlockTable*> tables;
233
2/2
✓ Branch 0 taken 11085 times.
✓ Branch 1 taken 2718 times.
13803 for (const BlockTable& table : block.tables) {
234
1/1
✓ Branch 1 taken 11085 times.
11085 tables[table.table] = &table;
235 }
236
237
1/1
✓ Branch 1 taken 2718 times.
2718 std::set<uint32_t> worlds;
238
1/1
✓ Branch 1 taken 2718 times.
2718 std::vector<uint32_t> copyWorlds(count);
239
2/2
✓ Branch 0 taken 24064 times.
✓ Branch 1 taken 2718 times.
26782 for (uint32_t copy = 0; copy < copies; ++copy) {
240 uint32_t worldIndex = 0;
241
2/2
✓ Branch 0 taken 24156 times.
✓ Branch 1 taken 24064 times.
48220 for (uint32_t i = 0; i < count; ++i) {
242
2/2
✓ Branch 0 taken 24064 times.
✓ Branch 1 taken 92 times.
24156 const uint32_t up = shape.parents[i];
243 24156 PrimRecord record;
244 24156 record.schema = shape.schemas[i];
245
2/2
✓ Branch 0 taken 24064 times.
✓ Branch 1 taken 92 times.
24156 record.parent = up == kNoPrim ? parents[copy] : block.Prim(copy, up);
246
4/4
✓ Branch 0 taken 92 times.
✓ Branch 1 taken 24064 times.
✓ Branch 2 taken 1164 times.
✓ Branch 3 taken 22900 times.
24156 record.world = up != kNoPrim ? copyWorlds[up]
247
2/2
✓ Branch 0 taken 1164 times.
✓ Branch 1 taken 22900 times.
24064 : parents[copy] == kNoPrim ? kGlobalWorld
248 1164 : m_records[parents[copy]].world;
249
3/3
✓ Branch 1 taken 24156 times.
✓ Branch 3 taken 152 times.
✓ Branch 4 taken 24004 times.
24156 if (IsA(record.schema, SchemaId::World)) {
250 152 record.world = block.firstWorld + copy * block.worldsPerCopy + worldIndex++;
251 }
252
1/1
✓ Branch 1 taken 24156 times.
24156 copyWorlds[i] = record.world;
253
1/1
✓ Branch 1 taken 24156 times.
24156 worlds.insert(record.world);
254 24156 record.flags = kPrimAlive;
255
1/1
✓ Branch 1 taken 24156 times.
24156 record.apis = BlockApis(shape, i);
256
1/2
✓ Branch 0 taken 24156 times.
✗ Branch 1 not taken.
24156 const std::vector<SchemaId>& slots = primTables[i];
257 48312 record.slots = slots.empty()
258
1/2
✓ Branch 0 taken 24156 times.
✗ Branch 1 not taken.
24156 ? kNoRow
259 24156 : block.firstSlot + copy * block.slotsPerCopy + slotOffsets[i];
260 24156 std::vector<uint32_t> rows;
261
2/2
✓ Branch 0 taken 118128 times.
✓ Branch 1 taken 24156 times.
142284 for (uint32_t slot = 0; slot < slots.size(); ++slot) {
262
1/1
✓ Branch 1 taken 118128 times.
118128 const BlockTable& table = *tables.at(slots[slot]);
263
1/1
✓ Branch 1 taken 118128 times.
118128 rows.push_back(table.first + copy * table.perCopy + ordinals[i][slot]);
264 }
265
1/1
✓ Branch 1 taken 24156 times.
24156 Write(block.Prim(copy, i), record, rows);
266 24156 }
267 }
268
1/2
✓ Branch 0 taken 2718 times.
✗ Branch 1 not taken.
2718 if (block.slotsPerCopy > 0) {
269 2718 const uint32_t slots = block.slotsPerCopy * copies;
270
1/1
✓ Branch 1 taken 2718 times.
2718 m_store.Slots().Stage(0, block.firstSlot,
271
1/1
✓ Branch 1 taken 2718 times.
2718 std::as_bytes(std::span(m_slotRows.data() + block.firstSlot, slots)));
272 }
273
2/2
✓ Branch 0 taken 2734 times.
✓ Branch 1 taken 2718 times.
5452 for (const uint32_t world : worlds) {
274
1/1
✓ Branch 1 taken 2734 times.
2734 m_store.StampWorld(world);
275 }
276 2718 m_live += count * copies;
277 2718 return block;
278 2718 }
279
280 24156 void Prims::Write(uint32_t prim, const PrimRecord& record, std::span<const uint32_t> rows) {
281 24156 const uint32_t generation = m_records[prim].generation;
282 24156 m_records[prim] = record;
283 24156 m_records[prim].generation = generation;
284 24156 const std::vector<SchemaId> tables = PrimTables(record.schema, record.apis);
285
2/2
✓ Branch 0 taken 118128 times.
✓ Branch 1 taken 24156 times.
142284 for (uint32_t slot = 0; slot < tables.size(); ++slot) {
286
1/1
✓ Branch 1 taken 118128 times.
118128 m_slotRows[record.slots + slot] = rows[slot];
287
1/1
✓ Branch 1 taken 118128 times.
118128 m_store.Schema(tables[slot])
288
2/2
✓ Branch 1 taken 118128 times.
✓ Branch 4 taken 118128 times.
118128 .Stage(m_store.PrimsColumn(tables[slot]), rows[slot], Word(prim));
289 }
290
1/1
✓ Branch 1 taken 24156 times.
24156 StageRecord(prim);
291 24156 }
292
293 24554 void Prims::StageRecord(uint32_t prim) {
294 24554 const PrimRecord& record = m_records[prim];
295 24554 const uint32_t values[] = {uint32_t(record.schema), record.parent, record.world, record.flags,
296 24554 record.slots};
297
2/2
✓ Branch 0 taken 122770 times.
✓ Branch 1 taken 24554 times.
147324 for (uint32_t column = kPrimSchema; column <= kPrimSlots; ++column) {
298 122770 m_store.Prims().Stage(column, prim, Word(values[column]));
299 }
300 24554 m_store.Prims().Stage(kPrimApis, prim, std::as_bytes(std::span(&record.apis, 1)));
301 24554 }
302
303 485 void Prims::CheckRemovable(uint32_t prim) const {
304
2/2
✓ Branch 3 taken 2378 times.
✓ Branch 4 taken 482 times.
2860 for (const SchemaId table : PrimTables(Record(prim).schema, Record(prim).apis)) {
305
3/3
✓ Branch 1 taken 2378 times.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 2375 times.
2378 if (m_store.Pinned(table)) {
306 3 throw Error("{} data is mapped or attached, so prims with it can't be removed until "
307 "the mapping closes or the attachment is released",
308
2/3
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✓ Branch 4 taken 3 times.
6 Info(table).name);
309 }
310 485 }
311 482 }
312
313 20 std::vector<BlockTable> Prims::Retype(uint32_t prim, SchemaId schema, uint64_t apis) {
314 20 const PrimRecord old = Record(prim);
315 20 const std::vector<SchemaId> before = PrimTables(old.schema, old.apis);
316
1/1
✓ Branch 1 taken 20 times.
20 const std::vector<SchemaId> after = PrimTables(schema, apis);
317 239 const auto kept = [](const std::vector<SchemaId>& tables, SchemaId table) {
318 219 return std::ranges::find(tables, table) != tables.end();
319 };
320
2/2
✓ Branch 0 taken 74 times.
✓ Branch 1 taken 20 times.
94 for (const SchemaId table : before) {
321
4/5
✓ Branch 1 taken 24 times.
✓ Branch 2 taken 50 times.
✓ Branch 4 taken 24 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 24 times.
74 if (!kept(after, table) && m_store.Pinned(table)) {
322 ✗ throw Error("{} data is mapped or attached, so prims with it can't change type until "
323 "the mapping closes or the attachment is released",
324
0/3
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Info(table).name);
325 }
326 }
327
1/1
✓ Branch 1 taken 20 times.
20 const uint32_t first = AcquireSlots(uint32_t(after.size()));
328 20 std::vector<BlockTable> added;
329
2/2
✓ Branch 0 taken 71 times.
✓ Branch 1 taken 20 times.
91 for (uint32_t slot = 0; slot < after.size(); ++slot) {
330 71 const SchemaId table = after[slot];
331 71 uint32_t row = kNoRow;
332
2/2
✓ Branch 1 taken 50 times.
✓ Branch 2 taken 21 times.
71 if (kept(before, table)) {
333
1/1
✓ Branch 1 taken 50 times.
50 row = m_slotRows[old.slots + PrimSlot(old.schema, old.apis, table)];
334 } else {
335
2/2
✓ Branch 1 taken 21 times.
✓ Branch 4 taken 21 times.
21 row = m_store.Schema(table).Acquire();
336
3/3
✓ Branch 1 taken 21 times.
✓ Branch 4 taken 21 times.
✓ Branch 7 taken 21 times.
21 m_store.Schema(table).Stage(m_store.PrimsColumn(table), row, Word(prim));
337
1/1
✓ Branch 1 taken 21 times.
21 added.push_back({table, row, 1, 1});
338 }
339 71 m_slotRows[first + slot] = row;
340 }
341
2/2
✓ Branch 0 taken 74 times.
✓ Branch 1 taken 20 times.
94 for (uint32_t slot = 0; slot < before.size(); ++slot) {
342 74 const SchemaId table = before[slot];
343
2/2
✓ Branch 1 taken 50 times.
✓ Branch 2 taken 24 times.
74 if (kept(after, table)) {
344 74 continue;
345 }
346
2/3
✓ Branch 1 taken 24 times.
✓ Branch 3 taken 24 times.
✗ Branch 4 not taken.
24 if (!IsMultipleApi(table)) {
347
1/1
✓ Branch 1 taken 24 times.
24 ReleaseRow(table, m_slotRows[old.slots + slot]);
348 24 continue;
349 }
350
0/1
✗ Branch 1 not taken.
✗ const auto found = m_instances.find(InstanceKey(prim, table));
351
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ for (const ApiInstance& entry : found->second) {
352
0/1
✗ Branch 1 not taken.
✗ ReleaseRow(table, entry.row);
353 }
354 ✗ m_instances.erase(found);
355 }
356
1/2
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
20 if (!before.empty()) {
357
2/2
✓ Branch 1 taken 20 times.
✓ Branch 4 taken 20 times.
20 m_freeSlots[uint32_t(before.size())].push_back(old.slots);
358 }
359
1/2
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
20 PrimRecord& record = m_records[prim];
360 20 record.schema = schema;
361 20 record.apis = apis;
362
1/2
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
20 record.slots = after.empty() ? kNoRow : first;
363
1/2
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
20 if (!after.empty()) {
364
1/1
✓ Branch 1 taken 20 times.
20 m_store.Slots().Stage(0, first,
365
1/1
✓ Branch 1 taken 20 times.
20 std::as_bytes(std::span(m_slotRows.data() + first, after.size())));
366 }
367
1/1
✓ Branch 1 taken 20 times.
20 StageRecord(prim);
368
1/1
✓ Branch 1 taken 20 times.
20 m_store.StampWorld(record.world);
369 20 return added;
370 20 }
371
372 8 void Prims::Reparent(uint32_t prim, uint32_t parent) {
373 8 m_records[prim].parent = parent;
374 8 StageRecord(prim);
375 8 m_store.StampWorld(m_records[prim].world);
376 8 }
377
378 13 void Prims::Renew(uint32_t prim) {
379 13 ++m_records[prim].generation;
380 13 }
381
382 241 void Prims::Remove(uint32_t prim) {
383 241 CheckRemovable(prim);
384 241 const PrimRecord record = Record(prim);
385 241 const std::vector<SchemaId> tables = PrimTables(record.schema, record.apis);
386
2/2
✓ Branch 0 taken 1183 times.
✓ Branch 1 taken 241 times.
1424 for (uint32_t slot = 0; slot < tables.size(); ++slot) {
387
3/3
✓ Branch 1 taken 1183 times.
✓ Branch 3 taken 1177 times.
✓ Branch 4 taken 6 times.
1183 if (!IsMultipleApi(tables[slot])) {
388
1/1
✓ Branch 1 taken 1177 times.
1177 ReleaseRow(tables[slot], m_slotRows[record.slots + slot]);
389 1177 continue;
390 }
391
1/1
✓ Branch 1 taken 6 times.
6 const auto found = m_instances.find(InstanceKey(prim, tables[slot]));
392
2/2
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 6 times.
15 for (const ApiInstance& entry : found->second) {
393
1/1
✓ Branch 1 taken 9 times.
9 ReleaseRow(tables[slot], entry.row);
394 }
395 6 m_instances.erase(found);
396 }
397
1/2
✓ Branch 0 taken 241 times.
✗ Branch 1 not taken.
241 if (!tables.empty()) {
398
2/2
✓ Branch 1 taken 241 times.
✓ Branch 4 taken 241 times.
241 m_freeSlots[uint32_t(tables.size())].push_back(record.slots);
399 }
400
1/1
✓ Branch 1 taken 241 times.
241 PrimRecord& removed = m_records[prim];
401 241 removed = {};
402 241 removed.generation = record.generation + 1;
403
1/1
✓ Branch 1 taken 241 times.
241 StageRecord(prim);
404
1/1
✓ Branch 1 taken 241 times.
241 m_freePrims.push_back(prim);
405
1/1
✓ Branch 1 taken 241 times.
241 m_store.StampWorld(record.world);
406 241 --m_live;
407 241 }
408
409 22 BlockTable Prims::Apply(std::span<const uint32_t> prims, SchemaId api) {
410 22 const BlockTable rows{api, m_store.Schema(api).Allocate(uint32_t(prims.size())), 1};
411
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 22 times.
44 for (uint32_t i = 0; i < prims.size(); ++i) {
412 22 const uint32_t row = rows.first + i;
413 22 m_store.Schema(api).Stage(m_store.PrimsColumn(api), row, Word(prims[i]));
414 22 Regroup(prims[i], m_records[prims[i]].apis | ApiBit(api), api, row);
415 }
416 22 return rows;
417 }
418
419 4 void Prims::Unapply(std::span<const uint32_t> prims, SchemaId api) {
420
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
8 for (const uint32_t prim : prims) {
421 4 ReleaseRow(api, Row(prim, api));
422 4 Regroup(prim, m_records[prim].apis & ~ApiBit(api), SchemaId::None, kNoRow);
423 }
424 4 }
425
426 126 void Prims::Regroup(uint32_t prim, uint64_t apis, SchemaId api, uint32_t row) {
427 126 const std::vector<SchemaId> before = PrimTables(m_records[prim].schema, m_records[prim].apis);
428
1/1
✓ Branch 1 taken 126 times.
126 const std::vector<SchemaId> after = PrimTables(m_records[prim].schema, apis);
429
1/1
✓ Branch 1 taken 126 times.
126 const uint32_t first = AcquireSlots(uint32_t(after.size()));
430 126 PrimRecord& record = m_records[prim];
431
2/2
✓ Branch 0 taken 792 times.
✓ Branch 1 taken 126 times.
918 for (uint32_t slot = 0; slot < after.size(); ++slot) {
432 792 m_slotRows[first + slot] =
433
2/2
✓ Branch 0 taken 670 times.
✓ Branch 1 taken 122 times.
792 after[slot] == api
434
2/2
✓ Branch 0 taken 670 times.
✓ Branch 1 taken 122 times.
1462 ? row
435
1/1
✓ Branch 1 taken 670 times.
670 : m_slotRows[record.slots + PrimSlot(record.schema, record.apis, after[slot])];
436 }
437
1/2
✓ Branch 0 taken 126 times.
✗ Branch 1 not taken.
126 if (!before.empty()) {
438
2/2
✓ Branch 1 taken 126 times.
✓ Branch 4 taken 126 times.
126 m_freeSlots[uint32_t(before.size())].push_back(record.slots);
439 }
440 126 record.slots = first;
441 126 record.apis = apis;
442
1/2
✓ Branch 0 taken 126 times.
✗ Branch 1 not taken.
126 if (!after.empty()) {
443
1/1
✓ Branch 1 taken 126 times.
126 m_store.Slots().Stage(0, first,
444
1/1
✓ Branch 1 taken 126 times.
126 std::as_bytes(std::span(m_slotRows.data() + first, after.size())));
445 }
446
1/1
✓ Branch 1 taken 126 times.
126 StageRecord(prim);
447
1/1
✓ Branch 1 taken 126 times.
126 m_store.StampWorld(record.world);
448 126 }
449
450 3 void Prims::MarkSession(uint32_t prim) {
451 3 m_records[prim].flags |= kPrimSession;
452 3 StageRecord(prim);
453 3 }
454
455 5 bool Prims::Session(uint32_t prim) const {
456
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 2 times.
9 for (uint32_t at = prim; at != kNoPrim; at = m_records[at].parent) {
457
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 3 times.
7 if ((m_records[at].flags & kPrimSession) != 0) {
458 return true;
459 }
460 }
461 return false;
462 }
463
464 1214 void Prims::ReleaseRow(SchemaId table, uint32_t row) {
465
4/4
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 4647 times.
✓ Branch 2 taken 3439 times.
✓ Branch 3 taken 1214 times.
9306 for (uint32_t index = 0; index < Info(table).columnCount; ++index) {
466
2/2
✓ Branch 1 taken 571 times.
✓ Branch 2 taken 2868 times.
3439 const ColumnRef column{table, &Columns(table)[index], index};
467
4/4
✓ Branch 0 taken 571 times.
✓ Branch 1 taken 2868 times.
✓ Branch 3 taken 40 times.
✓ Branch 4 taken 531 times.
3439 if (IsArray(column.column->type) && m_store.Array(column, row).address != 0) {
468 40 m_store.SetArray(column, row, {});
469 }
470 }
471 1214 m_store.DropOverrides(table, row);
472 1214 m_store.Schema(table).Stage(m_store.PrimsColumn(table), row, Word(kNoPrim));
473 1214 m_store.Schema(table).Release(row);
474 1214 }
475
476 182 BlockTable Prims::ApplyInstance(std::span<const uint32_t> prims, SchemaId api, uint32_t instance) {
477 182 const BlockTable rows{api, m_store.Schema(api).Allocate(uint32_t(prims.size())), 1};
478 182 const ColumnRef name = FindOwnColumn(api, "instance");
479
2/2
✓ Branch 0 taken 182 times.
✓ Branch 1 taken 182 times.
364 for (uint32_t i = 0; i < prims.size(); ++i) {
480 182 const uint32_t prim = prims[i];
481 182 const uint32_t row = rows.first + i;
482 182 m_store.Schema(api).Stage(m_store.PrimsColumn(api), row, Word(prim));
483 182 m_store.Stage(name, row, Word(instance));
484 182 std::vector<ApiInstance>& listed = m_instances[InstanceKey(prim, api)];
485 182 listed.insert(listed.begin(), {instance, row});
486
2/2
✓ Branch 0 taken 100 times.
✓ Branch 1 taken 82 times.
182 if (listed.size() == 1) {
487 100 Regroup(prim, m_records[prim].apis | ApiBit(api), api, row);
488 } else {
489 82 SetHead(prim, api, row);
490 }
491 }
492 182 StageLinks(prims, api);
493 182 return rows;
494 }
495
496 ✗ void Prims::UnapplyInstance(std::span<const uint32_t> prims, SchemaId api, uint32_t instance) {
497
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ for (const uint32_t prim : prims) {
498 ✗ const auto found = m_instances.find(InstanceKey(prim, api));
499
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (found == m_instances.end()) {
500 ✗ continue;
501 }
502 ✗ std::vector<ApiInstance>& listed = found->second;
503 ✗ const auto entry = std::ranges::find(listed, instance, &ApiInstance::instance);
504
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (entry == listed.end()) {
505 ✗ continue;
506 }
507 ✗ const bool head = entry == listed.begin();
508 ✗ ReleaseRow(api, entry->row);
509 ✗ listed.erase(entry);
510
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (listed.empty()) {
511 ✗ m_instances.erase(found);
512 ✗ Regroup(prim, m_records[prim].apis & ~ApiBit(api), SchemaId::None, kNoRow);
513 ✗ continue;
514 }
515
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (head) {
516 ✗ SetHead(prim, api, listed.front().row);
517 }
518 ✗ StageLinks(std::span(&prim, 1), api);
519 }
520 ✗ }
521
522 189 void Prims::StageLinks(std::span<const uint32_t> prims, SchemaId api) {
523 189 const ColumnRef next = FindOwnColumn(api, "next");
524
2/2
✓ Branch 0 taken 189 times.
✓ Branch 1 taken 189 times.
378 for (const uint32_t prim : prims) {
525 189 const std::span<const ApiInstance> listed = Instances(prim, api);
526
2/2
✓ Branch 0 taken 274 times.
✓ Branch 1 taken 189 times.
463 for (size_t i = 0; i < listed.size(); ++i) {
527
2/2
✓ Branch 0 taken 85 times.
✓ Branch 1 taken 189 times.
274 const uint32_t following = i + 1 < listed.size() ? listed[i + 1].row : kNoRow;
528 274 m_store.Stage(next, listed[i].row, Word(following));
529 }
530 }
531 189 }
532
533 82 void Prims::SetHead(uint32_t prim, SchemaId api, uint32_t row) {
534 82 const PrimRecord& record = m_records[prim];
535 82 const uint32_t slot = record.slots + ApiSlot(record.schema, record.apis, api);
536 82 m_slotRows[slot] = row;
537 82 m_store.Slots().Stage(0, slot, Word(row));
538 82 m_store.StampWorld(record.world);
539 82 }
540
541 }
542