chrono_batch_render/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
707 / 794
89.0%
Branches:
533 / 746
71.4%
Functions:
101 / 113
89.4%

scene/scene.cpp
Line Branch Exec Source
1 // SPDX-License-Identifier: BSD-3-Clause
2 #include "chrono_batch_render/scene/scene.h"
3
4 #include <algorithm>
5 #include <atomic>
6 #include <charconv>
7 #include <cmath>
8 #include <cstdint>
9 #include <cstring>
10 #include <initializer_list>
11 #include <memory>
12 #include <string>
13 #include <type_traits>
14 #include <utility>
15 #include <vector>
16
17 #include <fmt/format.h>
18
19 #include "chrono_batch_render/device/program.h"
20 #include "chrono_batch_render/scene/scene_impl.h"
21 #include "chrono_batch_render/stage/primvar.h"
22 #include "chrono_batch_render/stage/views/view_state.h"
23 #include "chrono_batch_render/texture/texels.h"
24 #include "chrono_batch_render/util/error.h"
25 #include "chrono_batch_render/util/log.h"
26 #include "chrono_batch_render/util/profiler.h"
27 #include "chrono_batch_render/util/span.h"
28
29 namespace chrono::batch_render {
30
31 namespace {
32
33 struct CopiedValue {
34 SchemaId owner = SchemaId::None;
35 uint32_t index = 0;
36 std::vector<std::byte> bytes;
37 std::string instance;
38 std::vector<std::string> strings;
39 std::vector<Primvar> primvars;
40
41 312 ColumnRef Column() const {
42 312 return {owner, &Columns(owner)[index], index};
43 }
44 };
45
46 10 struct CopiedPrim {
47 std::string relative;
48 SchemaId schema = SchemaId::None;
49 uint64_t apis = 0;
50 std::vector<std::pair<SchemaId, std::string>> instances;
51 std::vector<CopiedValue> values;
52 std::vector<std::pair<std::string, FileBytes>> files;
53 };
54
55 116 bool IsToken(ColumnType type) {
56 116 return type == ColumnType::Token || type == ColumnType::Asset;
57 }
58
59 3 std::string Rebased(const std::string& target, const std::string& from, std::string_view to) {
60
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (target == from) {
61 ✗ return std::string(to);
62 }
63
2/2
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 2 times.
3 if (target.starts_with(from + "/")) {
64
2/2
✓ Branch 2 taken 1 time.
✓ Branch 5 taken 1 time.
2 return std::string(to) + target.substr(from.size());
65 }
66 2 return target;
67 }
68
69 }
70
71 353 SceneImpl::SceneImpl(Scene& scene, device::Device& device, const SceneDesc& desc)
72
1/1
✓ Branch 2 taken 353 times.
353 : m_scene(scene), m_device(&device), m_waitForKernels(desc.waitForKernels), m_store(device),
73
1/1
✓ Branch 1 taken 353 times.
353 m_structure(m_store), m_publisher(m_structure, &m_header),
74
2/2
✓ Branch 1 taken 353 times.
✓ Branch 4 taken 353 times.
353 m_builder(bvh::Builder::Create(device, desc.traversal)), m_transforms(m_structure, &m_clock),
75
1/1
✓ Branch 1 taken 353 times.
353 m_meshes(m_structure, *m_builder),
76
1/1
✓ Branch 1 taken 353 times.
353 m_points(m_structure, *m_builder, desc.maxRefits, desc.pointsPerBox),
77 353 m_curves(m_structure, *m_builder, desc.maxRefits, desc.curveTolerance, desc.maxCurveSteps,
78
1/1
✓ Branch 1 taken 353 times.
353 desc.exactCurves),
79
2/2
✓ Branch 1 taken 353 times.
✓ Branch 4 taken 353 times.
353 m_bounds(m_structure, m_header.Tables(), m_instances), m_tangents(m_structure, m_meshes),
80
1/1
✓ Branch 1 taken 353 times.
353 m_textures(m_structure, desc.textureThreads), m_semantics(m_structure),
81
3/3
✓ Branch 1 taken 353 times.
✓ Branch 4 taken 353 times.
✓ Branch 7 taken 353 times.
353 m_subsets(m_structure), m_sensors(m_structure), m_pipelines(m_structure),
82
2/2
✓ Branch 1 taken 353 times.
✓ Branch 4 taken 353 times.
353 m_products(scene, m_structure, m_pipelines), m_plans(scene, m_products),
83
2/2
✓ Branch 1 taken 353 times.
✓ Branch 4 taken 353 times.
353 m_materials(m_structure, m_textures), m_spectra(m_structure),
84
1/1
✓ Branch 1 taken 353 times.
353 m_instances(m_structure, m_header.Tables(), *m_builder, desc.maxRefits),
85
1/1
✓ Branch 1 taken 353 times.
353 m_surfaces(m_structure, m_header.Tables(), m_instances),
86
1/1
✓ Branch 1 taken 353 times.
353 m_lights(m_structure, m_header.Tables(), m_instances),
87
2/2
✓ Branch 3 taken 353 times.
✓ Branch 6 taken 353 times.
1412 m_views(m_structure, m_header.Tables(), m_products) {
88
1/1
✓ Branch 1 taken 353 times.
353 m_publisher.Add(m_transforms);
89
1/1
✓ Branch 1 taken 353 times.
353 m_publisher.Add(m_meshes);
90
1/1
✓ Branch 1 taken 353 times.
353 m_publisher.Add(m_points);
91
1/1
✓ Branch 1 taken 353 times.
353 m_publisher.Add(m_curves);
92
1/1
✓ Branch 1 taken 353 times.
353 m_publisher.Add(m_tangents);
93
1/1
✓ Branch 1 taken 353 times.
353 m_publisher.Add(m_textures);
94
1/1
✓ Branch 1 taken 353 times.
353 m_publisher.Add(m_semantics);
95
1/1
✓ Branch 1 taken 353 times.
353 m_publisher.Add(m_subsets);
96
1/1
✓ Branch 1 taken 353 times.
353 m_publisher.Add(m_sensors);
97
1/1
✓ Branch 1 taken 353 times.
353 m_publisher.Add(m_pipelines);
98
1/1
✓ Branch 1 taken 353 times.
353 m_publisher.Add(m_products);
99
1/1
✓ Branch 1 taken 353 times.
353 m_publisher.Add(m_spectra);
100
1/1
✓ Branch 1 taken 353 times.
353 m_publisher.Add(m_materials);
101
1/1
✓ Branch 1 taken 353 times.
353 m_publisher.Add(m_instances);
102
1/1
✓ Branch 1 taken 353 times.
353 m_publisher.Add(m_surfaces);
103
1/1
✓ Branch 1 taken 353 times.
353 m_publisher.Add(m_bounds);
104
1/1
✓ Branch 1 taken 353 times.
353 m_publisher.Add(m_lights);
105
1/1
✓ Branch 1 taken 353 times.
353 m_publisher.Add(m_views);
106
1/1
✓ Branch 1 taken 353 times.
353 m_store.Prepare();
107
1/1
✓ Branch 1 taken 353 times.
353 m_builder->Prepare();
108 5295 for (Table* system : std::initializer_list<Table*>{
109 &m_transforms, &m_meshes, &m_points, &m_curves, &m_bounds, &m_tangents, &m_textures,
110 &m_semantics, &m_subsets, &m_sensors, &m_materials, &m_instances, &m_surfaces,
111
2/2
✓ Branch 0 taken 5295 times.
✓ Branch 1 taken 353 times.
5648 &m_lights, &m_views}) {
112
1/1
✓ Branch 1 taken 5295 times.
5295 system->Prepare();
113 }
114
2/2
✓ Branch 0 taken 352 times.
✓ Branch 1 taken 1 time.
353 if (desc.waitForKernels) {
115
1/1
✓ Branch 1 taken 352 times.
352 device::WaitForBuilds();
116 }
117 353 m_store.SetLock(&m_lock);
118 353 m_structure.SetWorldPoses(this);
119 353 }
120
121 268338 StoreLock::Guard LockScene(const Scene* scene) {
122
2/2
✓ Branch 0 taken 268335 times.
✓ Branch 1 taken 3 times.
268338 return scene ? SceneImpl::Get(*scene).Lock() : StoreLock::Guard();
123 }
124
125 706 SceneImpl::~SceneImpl() {
126 353 m_structure.SetWorldPoses(nullptr);
127 353 m_lock.DropDeferred();
128 353 m_store.SetLock(nullptr);
129 1059 }
130
131 238 void SceneImpl::RefreshTransforms() {
132 238 RunCommitSteps();
133
2/2
✓ Branch 1 taken 202 times.
✓ Branch 2 taken 36 times.
238 if (!Pending()) {
134 202 return;
135 }
136 36 Table* const tables[] = {&m_transforms};
137 36 m_publisher.Refresh(tables);
138 }
139
140 230 device::Tensor SceneImpl::Read(ViewState& view, device::DType type, Stream consumer,
141 QuaternionOrder order) {
142 230 RefreshTransforms();
143 230 return m_transforms.ReadWorldPoses(view, type, consumer, order);
144 }
145
146 1 device::Tensor SceneImpl::ReadScales(ViewState& view, device::DType type, Stream consumer) {
147 1 RefreshTransforms();
148 1 return m_transforms.ReadWorldScales(view, type, consumer);
149 }
150
151 7 device::Tensor SceneImpl::Localize(ViewState& view, const device::Tensor& values,
152 const device::Tensor* rotations, QuaternionOrder order,
153 const device::Tensor& sources) {
154 7 RefreshTransforms();
155 7 return m_transforms.Localize(view, values, rotations, order, sources);
156 }
157
158 48076 Prim SceneImpl::Handle(uint32_t prim) const {
159 48076 return Prim(m_scene, prim, m_structure.GetPrims().Generation(prim));
160 }
161
162 26 PrimRange SceneImpl::Range(const PrimBlock& block) const {
163 26 return {block.firstPrim, block.count, block.copies, block.firstWorld, block.worldsPerCopy};
164 }
165
166 33 std::vector<uint32_t> SceneImpl::Indices(Span<const Prim> prims) const {
167 33 std::vector<uint32_t> indices;
168
1/1
✓ Branch 1 taken 33 times.
33 indices.reserve(prims.size());
169
2/2
✓ Branch 0 taken 33 times.
✓ Branch 1 taken 33 times.
66 for (const Prim& prim : prims) {
170
1/1
✓ Branch 1 taken 33 times.
33 Check(prim);
171
1/1
✓ Branch 1 taken 33 times.
33 indices.push_back(prim.Index());
172 }
173 33 return indices;
174 ✗ }
175
176 55957 void SceneImpl::Check(const Prim& prim) const {
177
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 55957 times.
55957 if (&prim.GetScene() != &m_scene) {
178
0/1
✗ Branch 2 not taken.
✗ throw Error("Prim {} belongs to another scene", prim.Index());
179 }
180
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 55955 times.
55957 if (!prim.Valid()) {
181
1/1
✓ Branch 2 taken 2 times.
2 throw Error("Prim {} was removed", prim.Index());
182 }
183 55955 }
184
185 namespace {
186
187 11 ShaderLoader& Loader() {
188
3/4
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 6 times.
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
11 static ShaderLoader loader;
189 11 return loader;
190 }
191
192 219 SchemaId DefinedShader(Scene& scene, const Shader::Desc& desc) {
193
2/2
✓ Branch 0 taken 216 times.
✓ Branch 1 taken 3 times.
219 if (desc.implementationSource != ImplementationSource::SourceAsset) {
194 216 const SchemaId schema = ShaderSchema(desc.id);
195
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 214 times.
216 if (schema == SchemaId::None) {
196
1/1
✓ Branch 2 taken 2 times.
2 throw Error("no shader has info:id \"{}\"", desc.id);
197 }
198 return schema;
199 }
200 3 const std::string& file = desc.slangSourceAsset;
201 3 const std::string& name = desc.slangSourceAssetSubIdentifier;
202
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
3 if (file.empty() || name.empty()) {
203 ✗ throw Error("A shader loaded from a source asset needs info:slang:sourceAsset and "
204
0/2
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✗ "info:slang:sourceAsset:subIdentifier");
205 }
206
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 if (const SchemaId known = ShaderSchema(name); known != SchemaId::None) {
207 return known;
208 }
209
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
3 if (!Loader()) {
210
0/1
✗ Branch 2 not taken.
✗ throw Error("{} can't load {}, because nothing has registered a shader loader", name, file);
211 }
212
1/1
✓ Branch 3 taken 2 times.
4 Loader()(scene, file);
213 2 const SchemaId loaded = ShaderSchema(name);
214
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
2 if (loaded == SchemaId::None) {
215
1/1
✓ Branch 2 taken 1 time.
1 throw Error("{} has no material node called {}", file, name);
216 }
217 return loaded;
218 }
219
220 215 void WriteShader(RowWriter& writer, SchemaId schema, const Shader::Desc& desc) {
221 215 WriteFallbacks(schema, writer);
222 215 writer.Set(SchemaId::Connectable, Shader::kConnectionsSources.index, desc.connectionsSources);
223 215 writer.Set(SchemaId::Connectable, Shader::kConnectionsInputs.index, desc.connectionsInputs);
224 215 writer.Set(SchemaId::Connectable, Shader::kConnectionsOutputs.index, desc.connectionsOutputs);
225
3/3
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 198 times.
✓ Branch 4 taken 215 times.
430 writer.Set(SchemaId::Shader, Shader::kId.index, std::string(Info(schema).name));
226
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 214 times.
215 if (desc.implementationSource == ImplementationSource::SourceAsset) {
227 1 writer.Set(SchemaId::Shader, Shader::kImplementationSource.index,
228 1 desc.implementationSource);
229 1 writer.Set(SchemaId::Shader, Shader::kSlangSourceAsset.index, desc.slangSourceAsset);
230 1 writer.Set(SchemaId::Shader, Shader::kSlangSourceAssetSubIdentifier.index,
231 1 desc.slangSourceAssetSubIdentifier);
232 }
233 215 }
234
235 }
236
237 5 void SetShaderLoader(ShaderLoader loader) {
238 5 Loader() = std::move(loader);
239 5 }
240
241 2 uint32_t RetypePrims(Scene& scene, SchemaId from, SchemaId to) {
242 2 SceneImpl& impl = SceneImpl::Get(scene);
243 2 Structure& structure = impl.GetStructure();
244 2 ColumnStore& store = structure.GetColumnStore();
245 2 const Prims& prims = structure.GetPrims();
246 2 const Span<const ColumnInfo> before = Columns(from);
247 2 const Span<const ColumnInfo> after = Columns(to);
248 uint32_t moved = 0;
249
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 2 times.
10 for (uint32_t prim = 0; prim < prims.Size(); ++prim) {
250
3/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✓ Branch 5 taken 1 time.
8 if (!prims.Alive(prim) || prims.Record(prim).schema != from) {
251 7 continue;
252 }
253 1 std::vector<std::pair<uint32_t, std::vector<std::byte>>> kept;
254
1/1
✓ Branch 1 taken 1 time.
1 const uint32_t row = prims.Row(prim, from);
255
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 time.
3 for (uint32_t column = 0; column < after.size(); ++column) {
256
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 for (uint32_t old = 0; old < before.size(); ++old) {
257
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
2 if (before[old].field == after[column].field &&
258
4/6
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 time.
✗ Branch 5 not taken.
2 before[old].type == after[column].type && !IsArray(before[old].type)) {
259
3/3
✓ Branch 1 taken 1 time.
✓ Branch 4 taken 1 time.
✓ Branch 7 taken 1 time.
2 kept.emplace_back(column, store.Schema(from).Read(old, row, 1));
260 }
261 }
262 }
263
1/1
✓ Branch 1 taken 1 time.
1 structure.Retype(prim, to);
264
1/1
✓ Branch 1 taken 1 time.
1 const uint32_t retyped = prims.Row(prim, to);
265
3/3
✓ Branch 1 taken 1 time.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 1 time.
2 for (const auto& [column, cell] : kept) {
266
1/1
✓ Branch 1 taken 1 time.
1 store.Stage({to, &after[column], column}, retyped, cell);
267 }
268 1 ++moved;
269 1 }
270
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
2 if (moved > 0) {
271
2/3
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
✓ Branch 4 taken 1 time.
3 Log(LogLevel::Debug, "moved {} from the old {} to its new inputs", Counted(moved, "prim"),
272 1 Info(to).name);
273 }
274 2 return moved;
275 }
276
277 218 ShaderPrim Define(Scene& scene, std::string_view path, const Shader::Desc& desc) {
278 218 const auto lock = SceneImpl::Get(scene).Lock();
279
1/1
✓ Branch 1 taken 214 times.
218 const SchemaId schema = DefinedShader(scene, desc);
280
3/3
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 197 times.
✓ Branch 3 taken 214 times.
428 Log(LogLevel::Debug, "{} is a {} shader", path, Info(schema).name);
281
2/2
✓ Branch 1 taken 214 times.
✓ Branch 4 taken 214 times.
214 return ShaderPrim(SceneImpl::Get(scene).Define(
282
2/2
✓ Branch 1 taken 214 times.
✓ Branch 4 taken 214 times.
642 path, schema, [&](RowWriter& writer) { WriteShader(writer, schema, desc); }));
283 218 }
284
285 1 PrimRange Define(Scene& scene, Span<const std::string> paths, const Shader::Desc& desc,
286 const BatchDesc& batch) {
287 1 const auto lock = SceneImpl::Get(scene).Lock();
288
1/1
✓ Branch 1 taken 1 time.
1 const SchemaId schema = DefinedShader(scene, desc);
289
1/1
✓ Branch 1 taken 1 time.
1 return SceneImpl::Get(scene).Define(
290
2/2
✓ Branch 1 taken 1 time.
✓ Branch 4 taken 1 time.
3 paths, schema, [&](RowWriter& writer) { WriteShader(writer, schema, desc); }, batch);
291 1 }
292
293 ✗ Ref<Scene> Scene::Create(const SceneDesc& desc) {
294 ✗ const Ref<device::Device> device = device::Device::Create(desc.device);
295
0/1
✗ Branch 1 not taken.
✗ return Create(*device, desc);
296 ✗ }
297
298 353 Ref<Scene> Scene::Create(device::Device& device, const SceneDesc& desc) {
299
1/1
✓ Branch 2 taken 353 times.
353 return Ref<Scene>(new Scene(device, desc));
300 }
301
302 353 Scene::Scene(device::Device& device, const SceneDesc& desc)
303
1/1
✓ Branch 1 taken 353 times.
353 : m_impl(std::make_unique<SceneImpl>(*this, device, desc)) {}
304
305 706 Scene::~Scene() = default;
306
307 12 Prim Scene::Define(std::string_view path, SchemaId schema) {
308 12 const auto lock = m_impl->Lock();
309
2/2
✓ Branch 1 taken 11 times.
✓ Branch 4 taken 11 times.
34 return m_impl->Define(path, schema, [&](RowWriter& writer) { WriteFallbacks(schema, writer); });
310 12 }
311
312 5 PrimRange Scene::Define(Span<const std::string> paths, SchemaId schema, const BatchDesc& batch) {
313 5 const auto lock = m_impl->Lock();
314
1/1
✓ Branch 1 taken 5 times.
5 return m_impl->Define(
315
2/2
✓ Branch 1 taken 5 times.
✓ Branch 4 taken 5 times.
15 paths, schema, [&](RowWriter& writer) { WriteFallbacks(schema, writer); }, batch);
316 5 }
317
318 15 Prim Scene::Move(Prim prim, std::string_view path) {
319 15 const auto lock = m_impl->Lock();
320
1/1
✓ Branch 1 taken 15 times.
15 m_impl->Check(prim);
321
1/1
✓ Branch 1 taken 8 times.
15 Structure& structure = m_impl->GetStructure();
322
1/1
✓ Branch 1 taken 8 times.
15 structure.Move(prim.Index(), path);
323 8 std::vector<std::string> xformable;
324
3/3
✓ Branch 1 taken 8 times.
✓ Branch 3 taken 13 times.
✓ Branch 4 taken 8 times.
21 for (const uint32_t moved : structure.GetPaths().Subtree(prim.Index())) {
325
4/4
✓ Branch 1 taken 13 times.
✓ Branch 4 taken 13 times.
✓ Branch 6 taken 12 times.
✓ Branch 7 taken 1 time.
13 if (IsA(structure.GetPrims().Record(moved).schema, SchemaId::Xformable)) {
326
1/1
✓ Branch 1 taken 12 times.
24 xformable.push_back(structure.GetPaths().Path(moved));
327 }
328 8 }
329
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 1 time.
8 if (!xformable.empty()) {
330
4/5
✓ Branch 2 taken 7 times.
✓ Branch 5 taken 7 times.
✓ Branch 8 taken 7 times.
✓ Branch 10 taken 7 times.
✗ Branch 11 not taken.
21 Select(xformable, SchemaId::Xformable).Get(Xformable::kTranslate.Column()).MarkChanged();
331 }
332
1/1
✓ Branch 1 taken 8 times.
16 return ByIndex(prim.Index());
333 15 }
334
335 4 Prim Scene::Rename(Prim prim, std::string_view name) {
336 4 const auto lock = m_impl->Lock();
337
1/1
✓ Branch 1 taken 3 times.
4 m_impl->Check(prim);
338
1/1
✓ Branch 1 taken 3 times.
3 const std::optional<Prim> parent = prim.Parent();
339
6/6
✓ Branch 1 taken 3 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 1 time.
✓ Branch 6 taken 2 times.
✓ Branch 9 taken 3 times.
✓ Branch 12 taken 3 times.
12 return Move(prim, (parent ? parent->Path() : std::string()) + "/" + std::string(name));
340 4 }
341
342 132 void Scene::Remove(Prim prim) {
343 132 const auto lock = m_impl->Lock();
344
1/1
✓ Branch 1 taken 132 times.
132 m_impl->Check(prim);
345
1/1
✓ Branch 1 taken 132 times.
132 m_impl->GetStructure().Remove(prim.Index());
346 132 }
347
348 namespace {
349
350 3 bool SameColumn(ColumnRef a, ColumnRef b) {
351
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
3 return a.schema == b.schema && a.index == b.index;
352 }
353
354 }
355
356 ✗ Overrides::Overrides() = default;
357
358 3 Overrides::Overrides(Ref<Scene> scene, uint64_t owner)
359 3 : m_scene(std::move(scene)), m_owner(owner) {}
360
361 4 Overrides::~Overrides() {
362
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 time.
4 if (m_scene) {
363 3 try {
364
1/1
✓ Branch 1 taken 3 times.
3 Clear();
365
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ } catch (const std::exception& error) {
366 ✗ Log(LogLevel::Warning, "Clearing overrides: {}", error.what());
367 ✗ }
368 }
369 7 }
370
371 1 Overrides::Overrides(Overrides&& other) noexcept
372 1 : m_scene(std::move(other.m_scene)), m_owner(other.m_owner), m_cells(std::move(other.m_cells)) {
373 1 }
374
375 ✗ Overrides& Overrides::operator=(Overrides&& other) noexcept {
376
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (this != &other) {
377
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (m_scene) {
378 ✗ try {
379
0/1
✗ Branch 1 not taken.
✗ Clear();
380
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ } catch (const std::exception& error) {
381 ✗ Log(LogLevel::Warning, "Clearing overrides: {}", error.what());
382 ✗ }
383 }
384
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ m_scene = std::move(other.m_scene);
385 ✗ m_owner = other.m_owner;
386 ✗ m_cells = std::move(other.m_cells);
387 }
388 ✗ return *this;
389 }
390
391 3 void Overrides::Set(Prim prim, ColumnRef column, Span<const std::byte> cell) {
392 3 const auto lock = LockScene(m_scene.Get());
393
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (!m_scene) {
394
0/1
✗ Branch 2 not taken.
✗ throw Error("Overrides from no scene can't set {}", column.column->field);
395 }
396
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (IsArray(column.column->type)) {
397
0/1
✗ Branch 2 not taken.
✗ throw Error("{} holds arrays, which overrides don't", column.column->field);
398 }
399
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
3 if (column.schema == SchemaId::Shader && column.index == Shader::kId.index) {
400
0/2
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✗ throw Error("A shader's info:id picks its node, so overrides can't change it");
401 }
402
1/1
✓ Branch 1 taken 3 times.
3 SceneImpl& impl = SceneImpl::Get(*m_scene);
403
1/1
✓ Branch 1 taken 3 times.
3 impl.Check(prim);
404
1/1
✓ Branch 1 taken 3 times.
3 const uint32_t row = impl.GetStructure().GetPrims().Row(prim.Index(), column.schema);
405
1/1
✓ Branch 1 taken 3 times.
3 ColumnStore& store = impl.GetColumnStore();
406 3 std::vector<std::byte> authored;
407
3/3
✓ Branch 1 taken 3 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 1 time.
3 if (!store.Overridden(column, row)) {
408
5/7
✓ Branch 1 taken 2 times.
✓ Branch 4 taken 2 times.
✓ Branch 7 taken 2 times.
✓ Branch 10 taken 2 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 2 times.
✗ Branch 13 not taken.
8 authored = m_scene->Select(prim).Get(column).ReadHost();
409 }
410
1/1
✓ Branch 1 taken 3 times.
3 store.Override(column, row, m_owner, authored, cell);
411
2/2
✓ Branch 1 taken 3 times.
✓ Branch 4 taken 3 times.
3 store.StampWorld(prim.World());
412 3 const bool held = std::ranges::any_of(m_cells, [&](const auto& entry) {
413
0/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ return entry.first.Index() == prim.Index() && SameColumn(entry.second, column);
414 });
415
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if (!held) {
416
1/1
✓ Branch 1 taken 3 times.
3 m_cells.emplace_back(prim, column);
417 }
418 3 }
419
420 3 void Overrides::Clear(Prim prim, ColumnRef column) {
421 3 const auto lock = LockScene(m_scene.Get());
422 3 const auto found = std::ranges::find_if(m_cells, [&](const auto& entry) {
423
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
3 return entry.first.Index() == prim.Index() && SameColumn(entry.second, column);
424 });
425
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (found == m_cells.end()) {
426 ✗ return;
427 }
428
3/3
✓ Branch 1 taken 3 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 1 time.
3 if (prim.Valid()) {
429
1/1
✓ Branch 1 taken 2 times.
2 SceneImpl& impl = SceneImpl::Get(*m_scene);
430
1/1
✓ Branch 1 taken 2 times.
2 const uint32_t row = impl.GetStructure().GetPrims().FindRow(prim.Index(), column.schema);
431
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (row != kNoRow) {
432
1/1
✓ Branch 1 taken 2 times.
2 impl.GetColumnStore().ClearOverride(column, row, m_owner);
433
2/2
✓ Branch 1 taken 2 times.
✓ Branch 4 taken 2 times.
2 impl.GetColumnStore().StampWorld(prim.World());
434 }
435 }
436 3 m_cells.erase(found);
437 3 }
438
439 5 void Overrides::Clear() {
440 5 const auto lock = LockScene(m_scene.Get());
441
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 5 times.
8 while (!m_cells.empty()) {
442
1/1
✓ Branch 1 taken 3 times.
3 const auto [prim, column] = m_cells.back();
443
1/1
✓ Branch 1 taken 3 times.
3 Clear(prim, column);
444 }
445 5 }
446
447 3 Overrides Scene::CreateOverrides() {
448 3 const auto lock = m_impl->Lock();
449 3 static std::atomic<uint64_t> next{1};
450
1/1
✓ Branch 2 taken 3 times.
6 return Overrides(Ref<Scene>(this), next++);
451 3 }
452
453 5 bool Scene::Overridden(Prim prim, ColumnRef column) const {
454 5 const auto lock = m_impl->Lock();
455
1/1
✓ Branch 1 taken 5 times.
5 m_impl->Check(prim);
456
1/1
✓ Branch 1 taken 5 times.
5 const uint32_t row = m_impl->GetStructure().GetPrims().FindRow(prim.Index(), column.schema);
457
4/5
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 5 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 3 times.
10 return row != kNoRow && m_impl->GetColumnStore().Overridden(column, row);
458 5 }
459
460 2 std::vector<std::byte> Scene::ReadAuthored(Prim prim, ColumnRef column) const {
461 2 const auto lock = m_impl->Lock();
462
2/3
✓ Branch 1 taken 2 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
2 if (Overridden(prim, column)) {
463
1/1
✓ Branch 1 taken 2 times.
2 const uint32_t row = m_impl->GetStructure().GetPrims().Row(prim.Index(), column.schema);
464
1/1
✓ Branch 1 taken 2 times.
2 return m_impl->GetColumnStore().Authored(column, row);
465 }
466
0/5
✗ Branch 1 not taken.
✗ Branch 4 not taken.
✗ Branch 7 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ return Select(prim).Get(column).ReadHost();
467 2 }
468
469 2 ComposedWorld::ComposedWorld() = default;
470
471 1 ComposedWorld::ComposedWorld(Ref<Scene> scene, uint32_t world)
472 1 : m_scene(std::move(scene)), m_world(world) {}
473
474 3 ComposedWorld::~ComposedWorld() {
475 3 Release();
476 3 }
477
478 ✗ ComposedWorld::ComposedWorld(ComposedWorld&& other) noexcept
479 ✗ : m_scene(std::move(other.m_scene)), m_world(std::exchange(other.m_world, kGlobalWorld)) {}
480
481 1 ComposedWorld& ComposedWorld::operator=(ComposedWorld&& other) noexcept {
482
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 if (this != &other) {
483 1 Release();
484
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 m_scene = std::move(other.m_scene);
485 1 m_world = std::exchange(other.m_world, kGlobalWorld);
486 }
487 1 return *this;
488 }
489
490 4 void ComposedWorld::Release() {
491 4 const auto lock = LockScene(m_scene.Get());
492
3/4
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
4 if (m_scene && m_world != kGlobalWorld) {
493 1 SceneImpl::Get(*m_scene).GetStructure().Decompose(m_world);
494
1/1
✓ Branch 1 taken 1 time.
1 SceneImpl::Get(*m_scene).GetColumnStore().StampWorld(m_world);
495 }
496 4 m_world = kGlobalWorld;
497 4 }
498
499 1 void ComposedWorld::Set(const ComposedWorldDesc& desc) {
500 1 const auto lock = LockScene(m_scene.Get());
501
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 if (!m_scene) {
502
0/2
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✗ throw Error("An empty world view shows nothing");
503 }
504
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 Structure& structure = SceneImpl::Get(*m_scene).GetStructure();
505
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 const uint32_t worlds = structure.GetPrims().WorldCount();
506
2/4
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 time.
1 if (!desc.offsets.empty() && desc.offsets.size() != desc.worlds.size()) {
507
0/1
✗ Branch 1 not taken.
✗ throw Error("A world view has {} offsets for {} worlds", desc.offsets.size(),
508
0/1
✗ Branch 2 not taken.
✗ desc.worlds.size());
509 }
510
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
2 for (const uint32_t world : desc.worlds) {
511
2/4
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 time.
1 if (world >= worlds || world == m_world) {
512
0/1
✗ Branch 2 not taken.
✗ throw Error("A world view can't show world {} of {}", world, worlds);
513 }
514 }
515
3/4
✓ Branch 1 taken 1 time.
✓ Branch 4 taken 1 time.
✓ Branch 7 taken 1 time.
✗ Branch 8 not taken.
2 if (structure.Compose(m_world, desc)) {
516
1/1
✓ Branch 1 taken 1 time.
1 SceneImpl::Get(*m_scene).GetColumnStore().StampWorld(m_world);
517 }
518 1 }
519
520 1 ComposedWorld Scene::ComposeWorld(Prim world, const ComposedWorldDesc& desc) {
521 1 const auto lock = m_impl->Lock();
522
1/1
✓ Branch 1 taken 1 time.
1 m_impl->Check(world);
523
4/6
✓ Branch 1 taken 1 time.
✓ Branch 3 taken 1 time.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 time.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 time.
1 if (!world.IsA(SchemaId::World) || !world.IsSession()) {
524
0/2
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✗ throw Error("{} isn't a session World, which only can show other worlds", world.Path());
525 }
526
2/3
✓ Branch 1 taken 1 time.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 time.
1 if (m_impl->GetStructure().Compositions().contains(world.World())) {
527
0/2
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✗ throw Error("{} already shows other worlds", world.Path());
528 }
529
2/2
✓ Branch 1 taken 1 time.
✓ Branch 5 taken 1 time.
1 ComposedWorld view(Ref<Scene>(this), world.World());
530
1/1
✓ Branch 1 taken 1 time.
1 view.Set(desc);
531 1 return view;
532 1 }
533
534 3 void Scene::MarkSession(Prim prim) {
535 3 const auto lock = m_impl->Lock();
536
1/1
✓ Branch 1 taken 3 times.
3 m_impl->Check(prim);
537
1/1
✓ Branch 1 taken 3 times.
3 m_impl->GetStructure().MarkSession(prim.Index());
538 3 }
539
540 ✗ void Scene::Apply(Span<const Prim> prims, SchemaId api) {
541 ✗ const auto lock = m_impl->Lock();
542
0/2
✗ Branch 1 not taken.
✗ Branch 4 not taken.
✗ m_impl->Apply(prims, api, [&](RowWriter& writer) { WriteFallbacks(api, writer); });
543 ✗ }
544
545 1 void Scene::Apply(Span<const Prim> prims, SchemaId api, std::string_view instance) {
546 1 const auto lock = m_impl->Lock();
547
2/2
✓ Branch 1 taken 1 time.
✓ Branch 4 taken 1 time.
2 m_impl->Apply(prims, api, instance, [&](RowWriter& writer) { WriteFallbacks(api, writer); });
548 1 }
549
550 6 void Scene::Unapply(Span<const Prim> prims, SchemaId api) {
551 6 const auto lock = m_impl->Lock();
552
2/2
✓ Branch 1 taken 6 times.
✓ Branch 4 taken 4 times.
6 m_impl->GetStructure().Unapply(m_impl->Indices(prims), api);
553 6 }
554
555 ✗ void Scene::Unapply(Span<const Prim> prims, SchemaId api, std::string_view instance) {
556 ✗ const auto lock = m_impl->Lock();
557
0/2
✗ Branch 1 not taken.
✗ Branch 4 not taken.
✗ m_impl->GetStructure().Unapply(m_impl->Indices(prims), api, instance);
558 ✗ }
559
560 2 std::vector<Prim> Scene::Handles(const PrimRange& range) const {
561 2 const auto lock = m_impl->Lock();
562 2 std::vector<Prim> prims;
563
1/1
✓ Branch 1 taken 2 times.
2 prims.reserve(range.Size());
564
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
5 for (uint32_t prim = range.firstPrim; prim < range.firstPrim + range.Size(); ++prim) {
565
1/1
✓ Branch 1 taken 3 times.
6 prims.push_back(m_impl->Handle(prim));
566 }
567 2 return prims;
568 2 }
569
570 25 PrimRange Scene::Clone(Prim source, Span<const std::string> targets, const CloneDesc& desc) {
571 25 const auto lock = m_impl->Lock();
572
1/1
✓ Branch 1 taken 25 times.
25 m_impl->Check(source);
573
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 19 times.
25 if (targets.empty()) {
574 6 return {};
575 }
576
1/1
✓ Branch 1 taken 14 times.
19 return m_impl->Range(m_impl->GetStructure().Clone(
577 source.Index(), std::span(targets.data(), targets.size()), desc));
578 25 }
579
580 9 PrimRange Scene::Clone(Prim source, uint32_t count, const CloneDesc& desc) {
581 9 const auto lock = m_impl->Lock();
582
1/1
✓ Branch 1 taken 9 times.
9 m_impl->Check(source);
583
1/1
✓ Branch 1 taken 9 times.
9 const std::string path = source.Path();
584 9 const size_t slash = path.rfind('/');
585
1/1
✓ Branch 1 taken 9 times.
9 const std::string_view name = std::string_view(path).substr(slash + 1);
586 9 const size_t digits = name.find_last_not_of("0123456789") + 1;
587 9 std::string_view stem = name;
588 9 uint64_t first = 1;
589
1/1
✓ Branch 1 taken 9 times.
9 std::string separator = "_";
590
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 7 times.
9 if (digits < name.size() &&
591
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 std::from_chars(name.data() + digits, name.data() + name.size(), first).ec == std::errc()) {
592
1/1
✓ Branch 1 taken 2 times.
2 stem = name.substr(0, digits);
593 2 separator.clear();
594 2 ++first;
595 }
596 9 std::vector<std::string> targets;
597
1/1
✓ Branch 1 taken 9 times.
9 targets.reserve(count);
598
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 9 times.
15 for (uint64_t copy = first; copy < first + count; ++copy) {
599
2/2
✓ Branch 1 taken 6 times.
✓ Branch 4 taken 6 times.
12 targets.push_back(fmt::format("{}{}{}{}", std::string_view(path).substr(0, slash + 1), stem,
600 separator, copy));
601 }
602
1/1
✓ Branch 2 taken 9 times.
18 return Clone(source, targets, desc);
603 9 }
604
605 42936 std::optional<Prim> Scene::Find(std::string_view path) const {
606 42936 const auto lock = m_impl->Lock();
607
1/1
✓ Branch 1 taken 42936 times.
42936 const uint32_t prim = m_impl->GetStructure().Find(path);
608
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 42917 times.
42936 if (prim == kNoPrim) {
609 19 return std::nullopt;
610 }
611
1/1
✓ Branch 1 taken 42917 times.
42917 return m_impl->Handle(prim);
612 42936 }
613
614 2567 Prim Scene::ByIndex(uint32_t index) const {
615 2567 const auto lock = m_impl->Lock();
616
2/3
✓ Branch 1 taken 2567 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2567 times.
2567 if (!m_impl->GetStructure().GetPrims().Alive(index)) {
617
0/1
✗ Branch 2 not taken.
✗ throw Error("No prim {}", index);
618 }
619
1/1
✓ Branch 1 taken 2567 times.
5134 return m_impl->Handle(index);
620 2567 }
621
622 232 View Scene::Select(std::string_view pattern, SchemaId schema) const {
623 232 const auto lock = m_impl->Lock();
624
1/1
✓ Branch 1 taken 232 times.
232 Structure& structure = const_cast<SceneImpl&>(*m_impl).GetStructure();
625
2/2
✓ Branch 1 taken 232 times.
✓ Branch 4 taken 232 times.
464 return View(structure, schema, structure.Select(pattern, schema));
626 232 }
627
628 362 View Scene::Select(Span<const std::string> paths, SchemaId schema) const {
629 362 const auto lock = m_impl->Lock();
630 362 Structure& structure = const_cast<SceneImpl&>(*m_impl).GetStructure();
631 362 std::vector<uint32_t> prims;
632
2/2
✓ Branch 0 taken 703 times.
✓ Branch 1 taken 362 times.
1065 for (const std::string& path : paths) {
633
1/1
✓ Branch 1 taken 703 times.
703 const uint32_t prim = structure.Find(path);
634
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 703 times.
703 if (prim == kNoPrim) {
635
0/1
✗ Branch 2 not taken.
✗ throw Error("No prim at \"{}\"", path);
636 }
637
1/1
✓ Branch 1 taken 703 times.
703 prims.push_back(prim);
638 }
639
1/1
✓ Branch 1 taken 362 times.
724 return View(structure, schema, std::move(prims));
640 362 }
641
642 8 View Scene::Select(const PrimRange& range, SchemaId schema, uint32_t index) const {
643 8 const auto lock = m_impl->Lock();
644 8 Structure& structure = const_cast<SceneImpl&>(*m_impl).GetStructure();
645 8 std::vector<uint32_t> prims;
646
2/2
✓ Branch 0 taken 510 times.
✓ Branch 1 taken 8 times.
518 for (uint32_t copy = 0; copy < range.copies; ++copy) {
647
1/1
✓ Branch 1 taken 510 times.
510 prims.push_back(range.Index(copy, index));
648 }
649
1/1
✓ Branch 1 taken 8 times.
16 return View(structure, schema, std::move(prims));
650 8 }
651
652 55713 View Scene::Select(Prim prim) const {
653 55713 const auto lock = m_impl->Lock();
654
1/1
✓ Branch 1 taken 55712 times.
55713 m_impl->Check(prim);
655
1/1
✓ Branch 1 taken 55712 times.
55712 Structure& structure = const_cast<SceneImpl&>(*m_impl).GetStructure();
656
3/3
✓ Branch 1 taken 55712 times.
✓ Branch 4 taken 55712 times.
✓ Branch 7 taken 55712 times.
55712 return View(structure, prim.Schema(), std::vector<uint32_t>{prim.Index()});
657 55713 }
658
659 1 View Scene::Select(std::string_view pattern, SchemaId api, std::string_view instance) const {
660 1 const auto lock = m_impl->Lock();
661
1/1
✓ Branch 1 taken 1 time.
1 Structure& structure = const_cast<SceneImpl&>(*m_impl).GetStructure();
662
2/3
✓ Branch 1 taken 1 time.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 time.
1 if (!IsMultipleApi(api)) {
663
0/3
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ throw Error("{} isn't applied many times, so it has no instances", Info(api).name);
664 }
665
1/1
✓ Branch 1 taken 1 time.
1 const uint32_t token = structure.GetTokens().Intern(instance).id;
666
1/1
✓ Branch 1 taken 1 time.
1 std::vector<uint32_t> prims = structure.Select(pattern, api);
667
1/1
✓ Branch 1 taken 1 time.
1 std::erase_if(prims, [&](uint32_t prim) {
668 1 return structure.GetPrims().InstanceRow(prim, api, token) == kNoRow;
669 });
670
1/1
✓ Branch 1 taken 1 time.
2 return View(structure, api, std::move(prims), instance);
671 1 }
672
673 6 View Scene::Select(Prim prim, SchemaId api, std::string_view instance) const {
674 6 const auto lock = m_impl->Lock();
675
1/1
✓ Branch 1 taken 6 times.
6 m_impl->Check(prim);
676
1/1
✓ Branch 1 taken 6 times.
6 Structure& structure = const_cast<SceneImpl&>(*m_impl).GetStructure();
677
2/3
✓ Branch 1 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
6 if (!IsMultipleApi(api)) {
678
0/3
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ throw Error("{} isn't applied many times, so it has no instances", Info(api).name);
679 }
680
3/4
✓ Branch 1 taken 6 times.
✓ Branch 4 taken 6 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 6 times.
6 if (structure.GetPrims().InstanceRow(prim.Index(), api,
681
1/1
✓ Branch 1 taken 6 times.
6 structure.GetTokens().Intern(instance).id) == kNoRow) {
682
0/4
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 7 not taken.
✗ throw Error("{} has no {} {}", prim.Path(), Info(api).name, instance);
683 }
684
2/2
✓ Branch 1 taken 6 times.
✓ Branch 4 taken 6 times.
6 return View(structure, api, std::vector<uint32_t>{prim.Index()}, instance);
685 6 }
686
687 112 uint32_t Scene::PrimCount() const {
688 112 const auto lock = m_impl->Lock();
689 112 return m_impl->GetStructure().GetPrims().LiveCount();
690 112 }
691
692 23 uint32_t Scene::WorldCount() const {
693 23 const auto lock = m_impl->Lock();
694 23 return m_impl->GetStructure().GetPrims().WorldCount();
695 23 }
696
697 4307 device::Operation SceneImpl::Publish(const UpdateDesc& desc, Stream producer) {
698 4307 {
699
4/5
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 4302 times.
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 5 times.
4307 CHRONO_BATCH_RENDER_ZONE("Scene/CommitSteps");
700
1/1
✓ Branch 1 taken 4307 times.
4307 RunCommitSteps();
701 4307 }
702 4307 device::Operation done =
703
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 4306 times.
4308 m_publisher.Publish(desc.ordinal.value_or(m_publisher.Published() + 1), producer);
704
3/4
✓ Branch 0 taken 682 times.
✓ Branch 1 taken 3625 times.
✓ Branch 2 taken 682 times.
✗ Branch 3 not taken.
4307 if (desc.finish && !desc.wait) {
705 682 m_publisher.Finish();
706
2/2
✓ Branch 1 taken 15 times.
✓ Branch 2 taken 667 times.
682 if (m_publisher.Pending()) {
707 15 done = m_publisher.Publish(m_publisher.Published() + 1, producer);
708 }
709 }
710
4/4
✓ Branch 0 taken 66 times.
✓ Branch 1 taken 4242 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 65 times.
4308 while (desc.wait && Pending()) {
711 1 m_publisher.Finish();
712 1 RunCommitSteps();
713 1 done = m_publisher.Publish(m_publisher.Published() + 1, producer);
714 }
715 4307 return done;
716 }
717
718 4306 SceneUpdate SceneImpl::Update(const UpdateDesc& desc, Stream producer) {
719 4306 StoreLock::Guard lock = Lock();
720
1/1
✓ Branch 1 taken 4306 times.
4306 const device::Operation published = Publish(desc, producer);
721 8612 return SceneUpdate(*this, std::move(lock), published);
722 4306 }
723
724 4306 SceneUpdate::SceneUpdate(SceneImpl& scene, StoreLock::Guard lock, device::Operation published)
725 4306 : m_lock(std::move(lock)), m_scene(scene), m_published(published) {}
726
727 4306 SceneUpdate::~SceneUpdate() {
728
2/2
✓ Branch 0 taken 3804 times.
✓ Branch 1 taken 502 times.
4306 if (m_snapshot) {
729 3804 m_scene.GetPublisher().Release(*m_snapshot);
730 }
731 4306 }
732
733 3513 void SceneUpdate::Refresh(Stream producer) {
734
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3513 times.
3513 if (m_snapshot) {
735
0/2
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✗ throw Error("A scene update refreshed after its tables were read");
736 }
737
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 3512 times.
3513 if (m_scene.Pending()) {
738 1 m_published = m_scene.Publish({}, producer);
739 }
740 3513 }
741
742 3804 const Snapshot& SceneUpdate::Tables() {
743
1/2
✓ Branch 0 taken 3804 times.
✗ Branch 1 not taken.
3804 if (!m_snapshot) {
744
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 3804 times.
3804 m_snapshot = m_scene.GetPublisher().Acquire();
745 }
746 3804 return *m_snapshot;
747 }
748
749 3526 const SimulationClock& SceneUpdate::Clock() const {
750 3526 return m_scene.GetClock();
751 }
752
753 327 device::Operation Scene::Publish(const PublishDesc& desc, Stream producer) {
754 327 return m_impl->Update({.ordinal = desc.ordinal, .wait = desc.wait}, producer).Published();
755 }
756
757 376 void Scene::Advance(double dt) {
758 376 const auto lock = m_impl->Lock();
759
4/4
✓ Branch 0 taken 375 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 373 times.
✓ Branch 3 taken 2 times.
376 if (!std::isfinite(dt) || dt < 0) {
760 3 throw Error("Advancing the scene by {} s; the time step must be finite and not negative",
761
1/1
✓ Branch 2 taken 3 times.
3 dt);
762 }
763 373 SimulationClock& clock = m_impl->GetClock();
764 373 clock.start = clock.end;
765 373 clock.end += dt;
766 373 ++clock.steps;
767 376 }
768
769 13 double Scene::Time() const {
770 13 const auto lock = m_impl->Lock();
771 13 return m_impl->GetClock().end;
772 13 }
773
774 5 void Scene::SetTime(double time) {
775 5 const auto lock = m_impl->Lock();
776
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 4 times.
5 if (!std::isfinite(time)) {
777
1/1
✓ Branch 2 taken 1 time.
1 throw Error("Setting the scene's time to {} s; it must be finite", time);
778 }
779
1/1
✓ Branch 1 taken 4 times.
4 SimulationClock& clock = m_impl->GetClock();
780
1/1
✓ Branch 1 taken 4 times.
4 Log(LogLevel::Debug, "simulation time set from {} to {}", clock.end, time);
781 4 clock.start = time;
782 4 clock.end = time;
783 4 clock.resetTime = time;
784 4 ++clock.steps;
785 4 ++clock.resets;
786 5 }
787
788 namespace {
789
790 213 std::vector<uint32_t> PathPrims(const Scene& scene, Span<const std::string> paths) {
791 213 std::vector<uint32_t> prims;
792
2/2
✓ Branch 0 taken 214 times.
✓ Branch 1 taken 213 times.
427 for (const std::string& path : paths) {
793
1/1
✓ Branch 1 taken 214 times.
214 const std::optional<Prim> prim = scene.Find(path);
794
3/3
✓ Branch 0 taken 213 times.
✓ Branch 1 taken 1 time.
✓ Branch 3 taken 214 times.
214 prims.push_back(prim ? prim->Index() : kNoPrim);
795 }
796 213 return prims;
797 ✗ }
798
799 213 std::vector<uint32_t> BoundableRows(const Prims& prims, std::vector<uint32_t> indices) {
800
2/2
✓ Branch 0 taken 214 times.
✓ Branch 1 taken 213 times.
427 for (uint32_t& prim : indices) {
801
3/4
✓ Branch 1 taken 213 times.
✓ Branch 2 taken 1 time.
✓ Branch 5 taken 213 times.
✗ Branch 6 not taken.
214 prim = prims.Alive(prim) && IsA(prims.Record(prim).schema, SchemaId::Boundable) ? prim
802 : kNoRow;
803 }
804 213 return indices;
805 }
806
807 }
808
809 static_assert(sizeof(Box) == sizeof(BoundableBox));
810
811 100 device::Download Scene::ReadPrimBoundsAsync(Span<const std::string> paths) const {
812 100 const auto lock = m_impl->Lock();
813
1/1
✓ Branch 1 taken 100 times.
100 m_impl->UpdateIfPending();
814 100 const std::vector<uint32_t> rows =
815
2/2
✓ Branch 1 taken 100 times.
✓ Branch 4 taken 100 times.
100 BoundableRows(m_impl->GetStructure().GetPrims(), PathPrims(*this, paths));
816
1/1
✓ Branch 1 taken 100 times.
100 return m_impl->GetColumnStore().ReadRowsAsync(m_impl->GetBounds().PrimBounds(),
817
1/1
✓ Branch 1 taken 100 times.
200 sizeof(BoundableBox), rows);
818 100 }
819
820 113 std::vector<Box> Scene::PrimBounds(Span<const std::string> paths) const {
821 113 const auto lock = m_impl->Lock();
822
1/1
✓ Branch 1 taken 113 times.
113 m_impl->UpdateIfPending();
823 113 const std::vector<uint32_t> rows =
824
2/2
✓ Branch 1 taken 113 times.
✓ Branch 4 taken 113 times.
113 BoundableRows(m_impl->GetStructure().GetPrims(), PathPrims(*this, paths));
825
1/1
✓ Branch 1 taken 113 times.
226 return m_impl->GetColumnStore().ReadRows<Box>(m_impl->GetBounds().PrimBounds(), rows);
826 113 }
827
828 114 std::optional<Box> Scene::Bounds(uint32_t world) const {
829 114 const auto lock = m_impl->Lock();
830
1/1
✓ Branch 1 taken 114 times.
114 m_impl->UpdateIfPending();
831
1/1
✓ Branch 1 taken 113 times.
114 const std::optional<BvhBox> box = m_impl->GetBounds().World(world);
832
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 112 times.
113 if (!box) {
833 1 return std::nullopt;
834 }
835 112 return Box{{box->lower[0], box->lower[1], box->lower[2]},
836 112 {box->upper[0], box->upper[1], box->upper[2]}};
837 114 }
838
839 ✗ device::Download Scene::ReadBoundsAsync(uint32_t world) const {
840 ✗ const auto lock = m_impl->Lock();
841
0/1
✗ Branch 1 not taken.
✗ m_impl->UpdateIfPending();
842
0/1
✗ Branch 1 not taken.
✗ return m_impl->GetBounds().ReadWorldAsync(world);
843 ✗ }
844
845 100 device::Download Scene::ReadAllBoundsAsync() const {
846 100 const auto lock = m_impl->Lock();
847
1/1
✓ Branch 1 taken 100 times.
100 m_impl->UpdateIfPending();
848
1/1
✓ Branch 1 taken 100 times.
200 return m_impl->GetBounds().ReadWorldsAsync();
849 100 }
850
851 4 bool Scene::Pending() const {
852 4 const auto lock = m_impl->Lock();
853
1/1
✓ Branch 1 taken 4 times.
8 return m_impl->Pending();
854 4 }
855
856 10 uint64_t Scene::StructureVersion() const {
857 10 const auto lock = m_impl->Lock();
858 10 return m_impl->GetStructure().Epoch();
859 10 }
860
861 2 Ordinal Scene::Published() const {
862 2 const auto lock = m_impl->Lock();
863 2 return const_cast<SceneImpl&>(*m_impl).GetPublisher().Published();
864 2 }
865
866 60 Snapshot Scene::Acquire(Stream consumer) {
867 60 const auto lock = m_impl->Lock();
868
1/1
✓ Branch 1 taken 59 times.
119 return m_impl->GetPublisher().Acquire(consumer);
869 60 }
870
871 59 void Scene::Release(const Snapshot& snapshot, Stream consumer) {
872 59 const auto lock = m_impl->Lock();
873
1/1
✓ Branch 1 taken 59 times.
59 m_impl->GetPublisher().Release(snapshot, consumer);
874 59 }
875
876 4 void Scene::Require(Span<const chrono::batch_render::Token> taxonomies) {
877 4 const auto lock = m_impl->Lock();
878
1/1
✓ Branch 1 taken 4 times.
4 m_impl->GetSemantics().Require(std::span(taxonomies.data(), taxonomies.size()));
879 4 }
880
881 6 void Scene::AddFile(std::string path, FileBytes bytes, std::optional<Prim> owner) {
882 6 const auto lock = m_impl->Lock();
883 6 std::optional<uint32_t> index;
884
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 if (owner) {
885
1/1
✓ Branch 1 taken 3 times.
3 m_impl->Check(*owner);
886 3 index = owner->Index();
887 }
888
2/5
✓ Branch 2 taken 6 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 6 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
12 m_impl->GetStructure().AddFile(std::move(path), std::move(bytes), index);
889 6 }
890
891 5 void Scene::AddTexture(std::string path, const TextureData& data, std::optional<Prim> owner) {
892
5/8
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 time.
✓ Branch 7 taken 4 times.
5 if (data.width == 0 || data.height == 0 || data.channels == 0 || data.channels > 4) {
893 1 throw Error("Adding texture \"{}\": it is {}x{} with {} channels, but textures need a "
894 "width, a height and 1 to 4 channels",
895
1/1
✓ Branch 2 taken 1 time.
1 path, data.width, data.height, data.channels);
896 }
897
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 3 times.
4 const size_t texel = data.type == TexelType::Float32 ? sizeof(float) : 1;
898 4 const size_t expected = size_t(data.width) * data.height * data.channels * texel;
899
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 3 times.
4 if (data.texels.size() != expected) {
900 1 throw Error("Adding texture \"{}\": {}x{} with {} channels needs {} bytes, not {}", path,
901
1/1
✓ Branch 2 taken 1 time.
1 data.width, data.height, data.channels, expected, data.texels.size());
902 }
903 3 const TexelHeader header{.width = data.width,
904 3 .height = data.height,
905 3 .channels = data.channels,
906
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 2 times.
3 .floats = data.type == TexelType::Float32 ? 1u : 0u};
907 3 auto bytes = std::make_shared<std::vector<std::byte>>(sizeof(header) + expected);
908 3 std::memcpy(bytes->data(), &header, sizeof(header));
909 3 std::memcpy(bytes->data() + sizeof(header), data.texels.data(), expected);
910
3/9
✓ Branch 2 taken 3 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
6 AddFile(std::move(path), std::move(bytes), owner);
911 3 }
912
913 ✗ void Scene::RemoveFile(std::string_view path) {
914 ✗ const auto lock = m_impl->Lock();
915
0/1
✗ Branch 1 not taken.
✗ m_impl->GetStructure().RemoveFile(path);
916 ✗ }
917
918 45 chrono::batch_render::Token Scene::Token(std::string_view text) {
919 45 const auto lock = m_impl->Lock();
920
1/1
✓ Branch 1 taken 45 times.
90 return m_impl->GetStructure().GetTokens().Intern(text);
921 45 }
922
923 543 std::string_view Scene::Text(chrono::batch_render::Token token) const {
924 543 const auto lock = m_impl->Lock();
925
1/1
✓ Branch 1 taken 543 times.
1086 return m_impl->GetStructure().GetTokens().Text(token);
926 543 }
927
928 5768 device::Device& Scene::GetDevice() const {
929 5768 return m_impl->GetDevice();
930 }
931
932 10 struct PrimCopy::State {
933 std::string root;
934 std::vector<CopiedPrim> prims;
935 };
936
937 6 PrimCopy::PrimCopy() = default;
938
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 2 times.
7 PrimCopy::~PrimCopy() = default;
939
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ PrimCopy::PrimCopy(const PrimCopy& other) = default;
940 1 PrimCopy::PrimCopy(PrimCopy&& other) noexcept = default;
941 ✗ PrimCopy& PrimCopy::operator=(const PrimCopy& other) = default;
942 ✗ PrimCopy& PrimCopy::operator=(PrimCopy&& other) noexcept = default;
943
944 8 bool PrimCopy::Empty() const {
945
3/4
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
✓ Branch 3 taken 7 times.
8 return !m_state || m_state->prims.empty();
946 }
947
948 1 std::string_view PrimCopy::Name() const {
949
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
1 if (Empty()) {
950 ✗ return {};
951 }
952 1 return std::string_view(m_state->root).substr(m_state->root.rfind('/') + 1);
953 }
954
955 1 uint32_t PrimCopy::PrimCount() const {
956
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 return m_state ? uint32_t(m_state->prims.size()) : 0;
957 }
958
959 5 PrimCopy Scene::Copy(Prim prim) {
960 5 const auto lock = m_impl->Lock();
961
5/6
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 2 times.
✓ Branch 10 taken 5 times.
5 CHRONO_BATCH_RENDER_ZONE("Scene/Copy");
962
1/1
✓ Branch 1 taken 5 times.
5 m_impl->Check(prim);
963
1/1
✓ Branch 1 taken 5 times.
5 Structure& structure = m_impl->GetStructure();
964
1/1
✓ Branch 1 taken 5 times.
5 auto state = std::make_shared<PrimCopy::State>();
965
1/3
✓ Branch 1 taken 5 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
5 state->root = prim.Path();
966
967 272 struct Pending {
968 CopiedValue* value;
969 ColumnType type;
970 device::Download read;
971 };
972
973 5 std::vector<Pending> reads;
974
3/3
✓ Branch 1 taken 5 times.
✓ Branch 3 taken 10 times.
✓ Branch 4 taken 5 times.
15 for (const uint32_t index : structure.GetPaths().Subtree(prim.Index())) {
975
1/1
✓ Branch 1 taken 10 times.
10 const PrimRecord& record = structure.GetPrims().Record(index);
976
1/1
✓ Branch 1 taken 10 times.
10 const std::string path = structure.GetPaths().Path(index);
977
1/1
✓ Branch 1 taken 10 times.
10 CopiedPrim& copied = state->prims.emplace_back();
978
1/1
✓ Branch 1 taken 10 times.
10 copied.relative = path.substr(state->root.size());
979
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 5 times.
10 if (copied.relative.starts_with('/')) {
980
1/1
✓ Branch 1 taken 5 times.
5 copied.relative.erase(0, 1);
981 }
982 10 copied.schema = record.schema;
983 10 copied.apis = record.apis;
984
1/1
✓ Branch 1 taken 10 times.
10 copied.files = structure.OwnedFiles(index);
985
3/3
✓ Branch 1 taken 10 times.
✓ Branch 3 taken 31 times.
✓ Branch 4 taken 10 times.
41 for (const SchemaId table : PrimTables(record.schema, record.apis)) {
986
1/1
✓ Branch 1 taken 31 times.
31 std::vector<std::string> names = {""};
987
2/3
✓ Branch 1 taken 31 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 31 times.
31 if (IsMultipleApi(table)) {
988
0/2
✗ Branch 1 not taken.
✗ Branch 4 not taken.
✗ names = ByIndex(index).Instances(table);
989
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ for (const std::string& name : names) {
990
0/1
✗ Branch 1 not taken.
✗ copied.instances.emplace_back(table, name);
991 }
992 }
993
1/1
✓ Branch 1 taken 31 times.
31 const Span<const ColumnInfo> columns = Columns(table);
994
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 31 times.
62 for (const std::string& name : names) {
995
2/2
✓ Branch 0 taken 78 times.
✓ Branch 1 taken 31 times.
109 for (uint32_t column = 0; column < columns.size(); ++column) {
996
1/2
✓ Branch 0 taken 78 times.
✗ Branch 1 not taken.
78 if (!Has(columns[column].flags, ColumnFlags::Internal)) {
997 78 copied.values.push_back({table, column, {}, name});
998 }
999 }
1000 }
1001 31 }
1002
2/2
✓ Branch 1 taken 10 times.
✓ Branch 4 taken 10 times.
10 const View view = Select(ByIndex(index));
1003
2/2
✓ Branch 0 taken 78 times.
✓ Branch 1 taken 10 times.
88 for (CopiedValue& value : copied.values) {
1004
1/1
✓ Branch 1 taken 78 times.
78 const ColumnInfo& info = *value.Column().column;
1005 78 const UntypedAttribute attribute =
1006
1/2
✓ Branch 0 taken 78 times.
✗ Branch 1 not taken.
78 value.instance.empty()
1007
1/2
✓ Branch 0 taken 78 times.
✗ Branch 1 not taken.
78 ? view.Get(value.Column())
1008
2/10
✓ Branch 1 taken 78 times.
✓ Branch 4 taken 78 times.
✗ Branch 7 not taken.
✗ Branch 10 not taken.
✗ Branch 13 not taken.
✗ Branch 16 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
78 : Select(ByIndex(index), value.owner, value.instance).Get(value.Column());
1009
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 68 times.
78 if (info.type == ColumnType::PrimArray) {
1010
1/1
✓ Branch 1 taken 10 times.
10 value.strings = attribute.GetTargets(0);
1011
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 62 times.
68 } else if (info.type == ColumnType::TokenArray) {
1012
1/1
✓ Branch 1 taken 6 times.
6 value.strings = attribute.GetTokens(0);
1013
3/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 58 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
62 } else if (info.type == ColumnType::IntArray && info.field == "primvars") {
1014
1/1
✓ Branch 1 taken 4 times.
4 value.primvars = attribute.GetPrimvars(0);
1015
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 58 times.
58 } else if (IsArray(info.type)) {
1016
0/1
✗ Branch 1 not taken.
✗ value.bytes = attribute.GetArray(0);
1017 } else {
1018
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
58 reads.push_back({&value, info.type, attribute.ReadHostAsync()});
1019 }
1020 78 }
1021 15 }
1022
2/2
✓ Branch 0 taken 58 times.
✓ Branch 1 taken 5 times.
63 for (Pending& pending : reads) {
1023
1/1
✓ Branch 1 taken 58 times.
58 pending.read.Wait();
1024
1/1
✓ Branch 1 taken 58 times.
58 const Span<const std::byte> bytes = pending.read.Bytes();
1025
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 58 times.
58 if (IsToken(pending.type)) {
1026 ✗ uint32_t id = 0;
1027
0/3
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✗ std::memcpy(&id, bytes.data(), std::min<size_t>(bytes.size(), sizeof(id)));
1028
0/5
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ pending.value->strings = {std::string(Text(chrono::batch_render::Token{id}))};
1029 } else {
1030
1/1
✓ Branch 1 taken 58 times.
58 pending.value->bytes.assign(bytes.begin(), bytes.end());
1031 }
1032 }
1033
1/1
✓ Branch 1 taken 5 times.
5 PrimCopy copy;
1034 5 copy.m_state = std::move(state);
1035 5 return copy;
1036 141 }
1037
1038 7 Prim Scene::Paste(const PrimCopy& copy, std::string_view path) {
1039 7 const auto lock = m_impl->Lock();
1040
5/6
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 5 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 2 times.
✓ Branch 10 taken 7 times.
7 CHRONO_BATCH_RENDER_ZONE("Scene/Paste");
1041
3/3
✓ Branch 1 taken 7 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 6 times.
7 if (copy.Empty()) {
1042
1/1
✓ Branch 2 taken 1 time.
1 throw Error("Pasting an empty copy at \"{}\"", path);
1043 }
1044 6 const PrimCopy::State& state = *copy.m_state;
1045 17 const auto pastedPath = [&](const CopiedPrim& copied) {
1046
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 5 times.
11 return copied.relative.empty() ? std::string(path)
1047
8/12
✓ Branch 1 taken 6 times.
✓ Branch 4 taken 5 times.
✓ Branch 7 taken 5 times.
✓ Branch 10 taken 5 times.
✓ Branch 12 taken 5 times.
✓ Branch 13 taken 6 times.
✓ Branch 14 taken 5 times.
✓ Branch 15 taken 6 times.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
26 : std::string(path) + "/" + copied.relative;
1048 6 };
1049
1050 6 std::vector<Prim> pasted;
1051 6 try {
1052
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 5 times.
16 for (const CopiedPrim& copied : state.prims) {
1053
2/2
✓ Branch 1 taken 11 times.
✓ Branch 4 taken 10 times.
12 const Prim prim = Define(pastedPath(copied), copied.schema);
1054
1/1
✓ Branch 1 taken 10 times.
10 pasted.push_back(prim);
1055
2/2
✓ Branch 0 taken 220 times.
✓ Branch 1 taken 10 times.
230 for (const SchemaId api : kApiSchemas) {
1056
7/9
✓ Branch 1 taken 220 times.
✓ Branch 3 taken 7 times.
✓ Branch 4 taken 213 times.
✓ Branch 6 taken 7 times.
✓ Branch 8 taken 7 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 7 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 7 times.
220 if (HasApi(copied.apis, api) && !IsMultipleApi(api) && !prim.HasApi(api)) {
1057
0/1
✗ Branch 1 not taken.
✗ Apply(Span<const Prim>(&prim, 1), api);
1058 }
1059 }
1060
1/3
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 10 times.
10 for (const auto& [api, name] : copied.instances) {
1061
0/1
✗ Branch 1 not taken.
✗ const std::vector<std::string> present = prim.Instances(api);
1062
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ if (std::ranges::find(present, name) == present.end()) {
1063
0/1
✗ Branch 1 not taken.
✗ Apply(Span<const Prim>(&prim, 1), api, name);
1064 }
1065 ✗ }
1066 }
1067 1 } catch (...) {
1068
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 if (!pasted.empty()) {
1069
0/1
✗ Branch 1 not taken.
✗ Remove(pasted.front());
1070 }
1071 1 throw;
1072 1 }
1073
1074
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 5 times.
15 for (size_t at = 0; at < state.prims.size(); ++at) {
1075
1/1
✓ Branch 1 taken 10 times.
10 const CopiedPrim& copied = state.prims[at];
1076
1/1
✓ Branch 1 taken 10 times.
10 const Prim prim = ByIndex(pasted[at].Index());
1077
1/1
✓ Branch 1 taken 10 times.
10 const View view = Select(prim);
1078
2/2
✓ Branch 0 taken 78 times.
✓ Branch 1 taken 10 times.
88 for (const CopiedValue& value : copied.values) {
1079
1/1
✓ Branch 1 taken 78 times.
78 const ColumnInfo& info = *value.Column().column;
1080 78 UntypedAttribute attribute =
1081
1/2
✓ Branch 0 taken 78 times.
✗ Branch 1 not taken.
78 value.instance.empty()
1082
1/2
✓ Branch 0 taken 78 times.
✗ Branch 1 not taken.
78 ? view.Get(value.Column())
1083
2/7
✓ Branch 1 taken 78 times.
✓ Branch 4 taken 78 times.
✗ Branch 7 not taken.
✗ Branch 10 not taken.
✗ Branch 13 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
78 : Select(prim, value.owner, value.instance).Get(value.Column());
1084
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 68 times.
78 if (info.type == ColumnType::PrimArray) {
1085 10 std::vector<std::string> targets;
1086
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 10 times.
13 for (const std::string& target : value.strings) {
1087
1/1
✓ Branch 1 taken 3 times.
6 targets.push_back(Rebased(target, state.root, path));
1088 }
1089
1/1
✓ Branch 1 taken 10 times.
10 attribute.SetTargets(0, targets);
1090
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 62 times.
78 } else if (info.type == ColumnType::TokenArray) {
1091
1/1
✓ Branch 1 taken 6 times.
6 attribute.SetTokens(0, value.strings);
1092
3/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 58 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
62 } else if (info.type == ColumnType::IntArray && info.field == "primvars") {
1093
1/1
✓ Branch 1 taken 4 times.
4 attribute.SetPrimvars(0, value.primvars);
1094
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 58 times.
58 } else if (IsArray(info.type)) {
1095
0/1
✗ Branch 1 not taken.
✗ attribute.SetArray(0, value.bytes);
1096
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 58 times.
58 } else if (IsToken(info.type)) {
1097
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ const uint32_t id = value.strings.empty() || value.strings.front().empty()
1098
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ ? 0
1099
0/1
✗ Branch 1 not taken.
✗ : Token(value.strings.front()).id;
1100
0/1
✗ Branch 1 not taken.
✗ attribute.Write(AsBytes(Span<const uint32_t>(&id, 1)));
1101
1/2
✓ Branch 0 taken 58 times.
✗ Branch 1 not taken.
58 } else if (!value.bytes.empty()) {
1102
1/3
✓ Branch 1 taken 58 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
58 attribute.Write(value.bytes);
1103 }
1104 78 }
1105
1/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 10 times.
10 for (const auto& [file, bytes] : copied.files) {
1106
0/10
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✗ Branch 6 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ AddFile(file, bytes, prim);
1107 }
1108 10 }
1109
1/1
✓ Branch 1 taken 5 times.
10 return ByIndex(pasted.front().Index());
1110 10 }
1111
1112 }
1113