chrono_batch_render/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
360 / 411
87.6%
Branches:
387 / 504
76.8%
Functions:
19 / 19
100.0%

scene/tables/products.cpp
Line Branch Exec Source
1 // SPDX-License-Identifier: BSD-3-Clause
2 #include "chrono_batch_render/scene/tables/products.h"
3
4 #include <algorithm>
5 #include <cmath>
6 #include <initializer_list>
7 #include <optional>
8
9 #include <fmt/format.h>
10
11 #include "chrono_batch_render/scene/records/render_tables.h"
12 #include "chrono_batch_render/scene/scene.h"
13 #include "chrono_batch_render/scene/schema/columns.h"
14 #include "chrono_batch_render/stage/publish/change_set.h"
15 #include "chrono_batch_render/stage/views/view.h"
16 #include "chrono_batch_render/util/error.h"
17 #include "chrono_batch_render/util/log.h"
18 #include "chrono_batch_render/util/profiler.h"
19
20 namespace chrono::batch_render {
21
22 namespace {
23
24 5 std::vector<ColumnRef> ProductInputs() {
25 5 std::vector<ColumnRef> inputs = {
26 kLidarFiringsPerScan, kLidarEmitterElevations, kLidarMaxReturns,
27 5 kRadarRays, kDetectorAPIDetectorPattern, kSensorPipelineBinding};
28 40 for (const SchemaId schema :
29 {SchemaId::RenderProduct, SchemaId::RenderVar, SchemaId::MinimalAPI,
30 SchemaId::BackgroundAPI, SchemaId::RealTimeAPI, SchemaId::SensorAPI,
31
2/2
✓ Branch 0 taken 40 times.
✓ Branch 1 taken 5 times.
45 SchemaId::ColorBackground, SchemaId::GradientBackground}) {
32
1/1
✓ Branch 1 taken 40 times.
40 const Span<const ColumnInfo> columns = Columns(schema);
33
2/2
✓ Branch 0 taken 175 times.
✓ Branch 1 taken 40 times.
215 for (uint32_t index = 0; index < columns.size(); ++index) {
34
1/1
✓ Branch 1 taken 175 times.
175 inputs.push_back({schema, &columns[index], index});
35 }
36 }
37 5 return inputs;
38 ✗ }
39
40 struct Shape {
41 441 Uint2 resolution;
42 441 uint32_t returns = 0;
43
44
2/4
✓ Branch 0 taken 441 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 441 times.
882 bool operator==(const Shape&) const = default;
45 };
46
47 844 Shape SensorShape(const Prim& sensor, Uint2 resolution) {
48
2/2
✓ Branch 1 taken 30 times.
✓ Branch 2 taken 814 times.
844 if (sensor.IsA<Lidar>()) {
49 30 const Shape shape{{sensor.Get(Lidar::kFiringsPerScan),
50 ✗ uint32_t(sensor.Get(Lidar::kEmitterElevations).size())},
51
1/1
✓ Branch 2 taken 30 times.
30 sensor.Get(Lidar::kMaxReturns)};
52
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30 times.
30 if (shape.returns == 0 || shape.returns > kMostLidarReturns) {
53
0/1
✗ Branch 1 not taken.
✗ throw Error("{} reports {} returns per beam, and lidars report 1 to {}", sensor.Path(),
54
0/1
✗ Branch 2 not taken.
✗ shape.returns, kMostLidarReturns);
55 }
56
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30 times.
30 if (shape.resolution.y == 0) {
57
0/2
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✗ throw Error("{} has no emitters", sensor.Path());
58 }
59 30 return shape;
60 }
61
2/2
✓ Branch 1 taken 34 times.
✓ Branch 2 taken 780 times.
814 if (sensor.IsA<Radar>()) {
62 34 return {sensor.Get(Radar::kRays)};
63 }
64 780 return {resolution};
65 }
66
67 780 int64_t DetectorChannels(const Prim& camera) {
68
3/4
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 773 times.
✓ Branch 4 taken 5 times.
780 switch (camera.Get(DetectorAPI::kDetectorPattern)) {
69 case DetectorPattern::Mono:
70 return 1;
71 ✗ case DetectorPattern::Rccc:
72 ✗ return 2;
73 2 case DetectorPattern::Bands:
74 2 return kDetectorBands;
75 773 default:
76 773 return 3;
77 }
78 }
79
80 struct VarFormat {
81 device::DType type;
82 int64_t channels;
83 };
84
85 407 VarFormat SourceFormat(RenderVarSource source) {
86
7/8
✓ Branch 0 taken 37 times.
✓ Branch 1 taken 81 times.
✓ Branch 2 taken 237 times.
✓ Branch 3 taken 17 times.
✓ Branch 4 taken 17 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 3 times.
✓ Branch 7 taken 15 times.
407 switch (source) {
87 37 case RenderVarSource::LdrColor:
88 37 case RenderVarSource::DiffuseAlbedo:
89 37 return {device::DType::UInt8, 4};
90 81 case RenderVarSource::HdrColor:
91 81 return {device::DType::Float32, 4};
92 237 case RenderVarSource::DistanceToCamera:
93 237 case RenderVarSource::DistanceToImagePlane:
94 237 case RenderVarSource::NormalizedDepth:
95 237 case RenderVarSource::Intensity:
96 237 case RenderVarSource::RadialVelocity:
97 237 return {device::DType::Float32, 1};
98 17 case RenderVarSource::NonStableInstanceSegmentation:
99 17 case RenderVarSource::SemanticSegmentation:
100 17 return {device::DType::UInt32, 1};
101 17 case RenderVarSource::Normal:
102 17 return {device::DType::Float32, 3};
103 ✗ case RenderVarSource::SemanticInstanceSegmentation:
104 ✗ case RenderVarSource::StableIdSegmentation:
105 ✗ case RenderVarSource::InstancerStableId:
106 ✗ return {device::DType::UInt32, 4};
107 3 case RenderVarSource::InstanceId:
108 3 return {device::DType::Int64, 1};
109 15 default:
110 15 return {device::DType::UInt8, 0};
111 }
112 }
113
114 constexpr uint64_t Sources(std::initializer_list<RenderVarSource> sources) {
115 uint64_t mask = 0;
116 for (const RenderVarSource source : sources) {
117 mask |= uint64_t(1) << uint32_t(source);
118 }
119 return mask;
120 }
121
122 428 constexpr bool Includes(uint64_t sources, RenderVarSource source) {
123 428 return (sources >> uint32_t(source) & 1) != 0;
124 }
125
126 constexpr uint64_t kSemanticSources =
127 Sources({RenderVarSource::NonStableInstanceSegmentation, RenderVarSource::SemanticSegmentation,
128 RenderVarSource::SemanticIdMap, RenderVarSource::SemanticInstanceSegmentation,
129 RenderVarSource::StableIdSegmentation, RenderVarSource::StableIdMap});
130
131 constexpr uint64_t kRangeSources =
132 kSemanticSources | Sources({RenderVarSource::DistanceToCamera, RenderVarSource::Normal,
133 RenderVarSource::Intensity, RenderVarSource::PointCloud});
134
135 struct SensorKind {
136 SchemaId schema;
137 std::optional<RenderEntry> entry;
138 uint64_t outputs;
139 };
140
141 const SensorKind kSensorKinds[] = {
142 {SchemaId::Camera, std::nullopt,
143 ((uint64_t(1) << kRenderVarSourceCount) - 1) &
144 ~Sources({RenderVarSource::Intensity, RenderVarSource::PointCloud,
145 RenderVarSource::RadialVelocity})},
146 {SchemaId::Lidar, RenderEntry::Lidar, kRangeSources},
147 {SchemaId::Radar, RenderEntry::Radar,
148 kRangeSources | Sources({RenderVarSource::RadialVelocity})},
149 };
150
151 403 const SensorKind& FindSensorKind(SchemaId schema) {
152
1/2
✓ Branch 0 taken 452 times.
✗ Branch 1 not taken.
452 for (const SensorKind& kind : kSensorKinds) {
153
2/2
✓ Branch 1 taken 403 times.
✓ Branch 2 taken 49 times.
452 if (IsA(schema, kind.schema)) {
154 403 return kind;
155 }
156 }
157
0/3
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ throw Error("A {} isn't a sensor the renderer renders", Info(schema).name);
158 }
159
160
2/2
✓ Branch 0 taken 54 times.
✓ Branch 1 taken 340 times.
394 struct BoundSensorPipeline {
161 std::shared_ptr<const SensorPipelineInstance> instance;
162 };
163
164 32 std::vector<PointChannel> CloudChannels(const Prim& prim, const SensorKind& kind, bool& semantic) {
165 32 std::vector<std::string> names = prim.Get(RenderVar::kChannels);
166
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
32 if (names.empty()) {
167
0/5
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ names = {"Coordinates", "Intensity"};
168 }
169 32 std::vector<PointChannel> channels;
170
2/2
✓ Branch 0 taken 89 times.
✓ Branch 1 taken 32 times.
121 for (const std::string& channel : names) {
171 89 const auto found = std::ranges::find(kPointChannels, channel, &PointChannelInfo::name);
172
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 89 times.
89 if (found == std::end(kPointChannels)) {
173
0/1
✗ Branch 1 not taken.
✗ throw Error("{} asks for {}, which a PointCloud has no channel for", prim.Path(),
174
0/1
✗ Branch 2 not taken.
✗ channel);
175 }
176 89 const auto point = PointChannel(found - std::begin(kPointChannels));
177 89 if ((point == PointChannel::RadialVelocity ||
178
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 79 times.
89 point == PointChannel::RadialVelocityVector) &&
179
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 !Includes(kind.outputs, RenderVarSource::RadialVelocity)) {
180 ✗ throw Error("{} asks for {}, which a {} doesn't measure", prim.Path(), channel,
181
0/4
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 7 not taken.
✗ Info(kind.schema).name);
182 }
183 89 semantic |= point == PointChannel::StableId;
184
1/1
✓ Branch 1 taken 89 times.
89 channels.push_back(point);
185 }
186 32 return channels;
187 32 }
188
189 68 ResolvedVar SensorPipelineVar(const Prim& prim, const ResolvedProduct& product,
190 const SensorKind& kind, const BoundSensorPipeline& bound,
191 const std::string& name) {
192
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 61 times.
68 const bool tap = name == kTapSource;
193
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 61 times.
68 const std::string output = tap ? prim.Path() : name;
194 68 const auto found = std::ranges::find(bound.instance->resolved.outputs, output,
195 &sensor_pipeline::ResolvedOutput::name);
196
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 66 times.
68 if (found == bound.instance->resolved.outputs.end()) {
197
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
2 if (tap) {
198
2/2
✓ Branch 2 taken 1 time.
✓ Branch 5 taken 1 time.
2 throw Error("{} taps no node of {}", prim.Path(), bound.instance->path);
199 }
200 1 throw Error("{} reads {}, which isn't an output the renderer writes or {} makes",
201
2/2
✓ Branch 2 taken 1 time.
✓ Branch 5 taken 1 time.
2 prim.Path(), name, bound.instance->path);
202 }
203
1/1
✓ Branch 1 taken 66 times.
66 const sensor_pipeline::SignalLayout& layout = product.layouts[found->value];
204
1/1
✓ Branch 1 taken 66 times.
66 ResolvedVar var{.prim = prim.Index(),
205 .path = prim.Path(),
206
1/1
✓ Branch 1 taken 66 times.
66 .frame = prim.Get(RenderVar::kFrameOfReference),
207 .type = device::DType::Float32,
208 .output = output,
209
2/2
✓ Branch 1 taken 66 times.
✓ Branch 4 taken 66 times.
132 .tap = tap};
210
2/2
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 10 times.
66 const sensor_pipeline::SignalType type = bound.instance->resolved.values[found->value].type;
211
4/4
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 54 times.
66 if (type == sensor_pipeline::SignalType::PointCloud ||
212 2 (type == sensor_pipeline::SignalType::Returns &&
213
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 (kind.schema == SchemaId::Lidar || kind.schema == SchemaId::Radar))) {
214
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if (kind.schema != SchemaId::Lidar && kind.schema != SchemaId::Radar) {
215 ✗ throw Error("{} reads the point cloud {} makes, and only lidars and radars make them "
216 "in a pipeline",
217
0/2
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✗ prim.Path(), bound.instance->path);
218 }
219 12 var.source = RenderVarSource::PointCloud;
220
1/1
✓ Branch 1 taken 12 times.
12 var.channels = CloudChannels(prim, kind, var.semantic);
221 12 return var;
222 }
223
1/1
✓ Branch 1 taken 54 times.
54 var.shape = {int64_t(product.sensors.size())};
224
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 53 times.
54 if (layout.perCapture) {
225
1/1
✓ Branch 1 taken 1 time.
1 var.shape.push_back(sensor_pipeline::kStatWords);
226 1 return var;
227 }
228
1/1
✓ Branch 1 taken 53 times.
53 var.shape.insert(var.shape.end(), {int64_t(product.height), int64_t(product.width)});
229
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 53 times.
53 if (product.returns > 0) {
230
0/1
✗ Branch 1 not taken.
✗ var.shape.push_back(product.returns);
231 }
232
2/2
✓ Branch 0 taken 49 times.
✓ Branch 1 taken 4 times.
53 if (layout.channels > 1) {
233
1/1
✓ Branch 1 taken 49 times.
49 var.shape.push_back(layout.channels);
234 }
235 return var;
236 66 }
237
238 497 ResolvedVar ResolveVar(const Prim& prim, const ResolvedProduct& product, const SensorKind& kind,
239 int64_t channels, const BoundSensorPipeline& bound) {
240 497 const std::string name = prim.Get(RenderVar::kSourceName);
241
2/2
✓ Branch 0 taken 69 times.
✓ Branch 1 taken 428 times.
497 const std::optional<RenderVarSource> builtIn = FindRenderVarSource(name);
242
2/2
✓ Branch 0 taken 69 times.
✓ Branch 1 taken 428 times.
497 if (!builtIn) {
243
3/4
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 68 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 time.
69 if (!bound.instance && name == kTapSource) {
244
0/2
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✗ throw Error("{} taps a pipeline node, but its product binds no pipeline", prim.Path());
245 }
246
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 68 times.
69 if (!bound.instance) {
247
1/1
✓ Branch 1 taken 1 time.
1 throw Error("{} reads {}, which isn't an output the renderer writes", prim.Path(),
248
1/1
✓ Branch 2 taken 1 time.
3 name);
249 }
250
1/1
✓ Branch 1 taken 66 times.
68 return SensorPipelineVar(prim, product, kind, bound, name);
251 }
252
1/1
✓ Branch 1 taken 428 times.
428 const RenderVarSource source = *builtIn;
253
1/1
✓ Branch 1 taken 428 times.
428 ResolvedVar var{.prim = prim.Index(),
254 .path = prim.Path(),
255 .source = source,
256 428 .frame = prim.Get(RenderVar::kFrameOfReference),
257
4/4
✓ Branch 1 taken 428 times.
✓ Branch 4 taken 428 times.
✓ Branch 6 taken 1 time.
✓ Branch 7 taken 427 times.
428 .semantic = Includes(kSemanticSources, source)};
258
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 427 times.
428 if (!Includes(kind.outputs, source)) {
259 ✗ throw Error("{} writes {}, which a {} doesn't output", prim.Path(), name,
260
3/4
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
✓ Branch 4 taken 1 time.
✓ Branch 7 taken 1 time.
3 Info(kind.schema).name);
261 }
262
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 407 times.
427 if (source == RenderVarSource::PointCloud) {
263
1/1
✓ Branch 1 taken 20 times.
40 var.channels = CloudChannels(prim, kind, var.semantic);
264 20 return var;
265 }
266 407 VarFormat format = SourceFormat(source);
267
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 392 times.
407 if (source == RenderVarSource::ElectronRate) {
268
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 14 times.
15 if (channels == 0) {
269 1 throw Error("{} writes ElectronRate from cameras whose detector patterns have "
270 "different channel counts",
271
1/1
✓ Branch 2 taken 1 time.
1 var.path);
272 }
273 14 format = {device::DType::Float32, channels};
274 }
275
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 406 times.
406 if (format.channels == 0) {
276 ✗ return var;
277 }
278 406 var.type = format.type;
279
1/1
✓ Branch 1 taken 406 times.
406 var.shape = {int64_t(product.sensors.size()), product.height, product.width};
280
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 406 times.
406 if (product.returns > 0) {
281
0/1
✗ Branch 1 not taken.
✗ var.shape.push_back(product.returns);
282 }
283
2/2
✓ Branch 0 taken 147 times.
✓ Branch 1 taken 259 times.
406 if (format.channels > 1) {
284
1/1
✓ Branch 1 taken 147 times.
147 var.shape.push_back(format.channels);
285 }
286 406 return var;
287 920 }
288
289 395 RenderEntry SelectEntry(const ResolvedProduct& product, const SensorKind& kind) {
290
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 363 times.
395 if (kind.entry) {
291 32 return *kind.entry;
292 }
293 363 const bool shaded = std::ranges::any_of(product.vars, [](const ResolvedVar& var) {
294
2/2
✓ Branch 0 taken 213 times.
✓ Branch 1 taken 161 times.
374 return var.source == RenderVarSource::LdrColor || var.source == RenderVarSource::HdrColor ||
295 var.source == RenderVarSource::DiffuseAlbedo ||
296
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 213 times.
213 var.source == RenderVarSource::ElectronRate || !var.output.empty();
297 });
298
2/2
✓ Branch 0 taken 161 times.
✓ Branch 1 taken 202 times.
363 if (!shaded) {
299 return RenderEntry::MinimalData;
300 }
301 161 const ProductRecord& record = product.record;
302
2/2
✓ Branch 0 taken 75 times.
✓ Branch 1 taken 86 times.
161 if (RenderMode(record.renderMode) == RenderMode::RealTime) {
303
2/2
✓ Branch 0 taken 65 times.
✓ Branch 1 taken 10 times.
75 return SpectralMode(record.spectralMode) == SpectralMode::Bands ? RenderEntry::RealTimeBands
304 75 : RenderEntry::RealTime;
305 }
306
3/4
✓ Branch 0 taken 57 times.
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
86 switch (MinimalMode(record.minimalMode)) {
307 case MinimalMode::DisplayColor:
308 return RenderEntry::MinimalDisplayColor;
309 57 case MinimalMode::TextureDiffuse:
310 57 return RenderEntry::MinimalTextureDiffuse;
311 21 case MinimalMode::Full:
312 21 return RenderEntry::MinimalFull;
313 ✗ default:
314 ✗ return RenderEntry::MinimalData;
315 }
316 }
317
318 }
319
320 2394 std::span<const ColumnRef> ProductTable::Inputs() const {
321
4/5
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 2389 times.
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 5 times.
2394 static const std::vector<ColumnRef> inputs = ProductInputs();
322 2394 return inputs;
323 }
324
325 501 void ProductTable::Describe(RenderTables& tables) const {
326
1/2
✓ Branch 0 taken 501 times.
✗ Branch 1 not taken.
501 tables.products.address = m_records.Buffer() ? m_records.Buffer()->Address() : 0;
327 501 }
328
329 3982 std::shared_ptr<const ResolvedProduct> ProductTable::Find(uint32_t prim) const {
330 3982 const std::lock_guard lock(m_mutex);
331 3982 const auto found = m_products.find(prim);
332
3/4
✓ Branch 0 taken 185 times.
✓ Branch 1 taken 3797 times.
✓ Branch 2 taken 3797 times.
✗ Branch 3 not taken.
7779 return found == m_products.end() ? nullptr : found->second;
333 3982 }
334
335 527 std::vector<std::shared_ptr<const ResolvedProduct>> ProductTable::Products() const {
336 527 const std::lock_guard lock(m_mutex);
337 527 std::vector<std::shared_ptr<const ResolvedProduct>> products;
338
3/3
✓ Branch 1 taken 433 times.
✓ Branch 3 taken 433 times.
✓ Branch 4 taken 527 times.
960 for (const auto& [prim, product] : m_products) {
339
1/1
✓ Branch 1 taken 433 times.
433 products.push_back(product);
340 }
341 527 return products;
342 527 }
343
344 404 ResolvedProduct ProductTable::Resolve(uint32_t index) const {
345 404 const Prims& prims = m_structure.GetPrims();
346 404 ResolvedProduct product;
347 404 product.prim = index;
348
1/1
✓ Branch 1 taken 404 times.
404 const Prim prim = m_scene.ByIndex(index);
349
1/1
✓ Branch 1 taken 404 times.
404 const std::string path = prim.Path();
350
3/3
✓ Branch 1 taken 404 times.
✓ Branch 4 taken 404 times.
✓ Branch 8 taken 404 times.
404 const View view(m_structure, SchemaId::RenderProduct, std::vector<uint32_t>{index});
351 1209 const auto targets = [&](ColumnRef column, SchemaId schema) {
352
1/1
✓ Branch 2 taken 805 times.
805 const std::vector<uint32_t> found = view.GetArray<uint32_t>(column).Get(0);
353
2/2
✓ Branch 0 taken 939 times.
✓ Branch 1 taken 805 times.
1744 for (const uint32_t target : found) {
354
5/7
✓ Branch 1 taken 939 times.
✓ Branch 3 taken 939 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 939 times.
✓ Branch 9 taken 939 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 939 times.
939 if (!prims.Alive(target) || !IsA(prims.Record(target).schema, schema)) {
355 ✗ throw Error("{}'s {} targets prim {}, which isn't a {}", path, column.column->field,
356
0/3
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ target, Info(schema).name);
357 }
358 }
359 805 return found;
360 ✗ };
361 404 ProductRecord& record = product.record;
362
1/1
✓ Branch 1 taken 404 times.
404 record.textureLodBias = prim.Get(RenderProduct::kTextureLodBias);
363
2/3
✓ Branch 1 taken 404 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 404 times.
404 record.maxAnisotropy = uint32_t(std::max(prim.Get(RenderProduct::kMaxAnisotropy), 1));
364
2/3
✓ Branch 1 taken 404 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 404 times.
404 record.maxBounces = uint32_t(std::max(prim.Get(RealTimeAPI::kMaxBounces), 0));
365
2/3
✓ Branch 1 taken 404 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 404 times.
404 record.pixelSamples = uint32_t(std::max(prim.Get(RealTimeAPI::kPixelSamples), 1));
366
1/1
✓ Branch 1 taken 404 times.
404 record.conform = uint32_t(prim.Get(RenderProduct::kAspectRatioConformPolicy));
367
1/1
✓ Branch 1 taken 404 times.
404 record.pixelAspect = prim.Get(RenderProduct::kPixelAspectRatio);
368
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 404 times.
404 if (!(record.pixelAspect > 0)) {
369 ✗ record.pixelAspect = 1;
370 }
371
2/3
✓ Branch 1 taken 404 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 404 times.
404 record.castShadows = prim.Get(MinimalAPI::kCastShadows) ? 1 : 0;
372
1/1
✓ Branch 1 taken 404 times.
404 record.shadowVisibility = prim.Get(MinimalAPI::kShadowVisibility);
373
2/3
✓ Branch 1 taken 404 times.
✓ Branch 3 taken 404 times.
✗ Branch 4 not taken.
404 record.hemispheric = prim.Get(MinimalAPI::kAmbientHemispheric) ? 1 : 0;
374
1/1
✓ Branch 1 taken 404 times.
404 const float intensity = prim.Get(MinimalAPI::kAmbientLightIntensity);
375
1/1
✓ Branch 1 taken 404 times.
404 const Float3 ambient = prim.Get(MinimalAPI::kAmbientLightColor);
376
1/1
✓ Branch 1 taken 404 times.
404 const Float3 ground = prim.Get(MinimalAPI::kAmbientGroundColor);
377 404 record.ambient = {ambient.x * intensity, ambient.y * intensity, ambient.z * intensity};
378 404 record.ground = {ground.x * intensity, ground.y * intensity, ground.z * intensity};
379
1/1
✓ Branch 1 taken 404 times.
404 record.backgroundColor = prim.Get(BackgroundAPI::kBackgroundColor);
380 404 const std::vector<uint32_t> backgrounds =
381
2/4
✓ Branch 1 taken 404 times.
✓ Branch 4 taken 404 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
404 view.GetArray<uint32_t>(kBackgroundAPIBackground).Get(0);
382
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 402 times.
404 record.background = backgrounds.empty() ? kNoPrim : backgrounds.front();
383 404 record.backgroundKind = uint32_t(SchemaId::None);
384
4/5
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 402 times.
✓ Branch 3 taken 2 times.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
404 if (record.background != kNoPrim && prims.Alive(record.background)) {
385
1/1
✓ Branch 1 taken 2 times.
2 const Prim background = m_scene.ByIndex(record.background);
386
1/1
✓ Branch 1 taken 2 times.
2 record.backgroundKind = uint32_t(prims.Record(record.background).schema);
387
2/3
✓ Branch 1 taken 2 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
2 if (background.IsA<ColorBackground>()) {
388
1/1
✓ Branch 1 taken 2 times.
2 record.backgroundLow = background.Get(ColorBackground::kColor);
389
0/3
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ } else if (background.IsA<GradientBackground>()) {
390
0/1
✗ Branch 1 not taken.
✗ record.backgroundLow = background.Get(GradientBackground::kHorizonColor);
391
0/1
✗ Branch 1 not taken.
✗ record.backgroundHigh = background.Get(GradientBackground::kZenithColor);
392 }
393 }
394
1/1
✓ Branch 1 taken 404 times.
404 record.renderMode = uint32_t(prim.Get(RenderProduct::kRenderMode));
395
1/1
✓ Branch 1 taken 404 times.
404 record.minimalMode = uint32_t(prim.Get(MinimalAPI::kMode));
396
1/1
✓ Branch 1 taken 404 times.
404 record.spectralMode = uint32_t(prim.Get(RenderProduct::kSpectralMode));
397
1/1
✓ Branch 1 taken 404 times.
404 product.missDistance = prim.Get(RenderProduct::kMissDistance);
398
2/2
✓ Branch 1 taken 404 times.
✓ Branch 4 taken 404 times.
404 record.sensors = m_structure.GetColumnStore().Array(kRenderProductCamera,
399 prims.Row(index, SchemaId::RenderProduct));
400
2/2
✓ Branch 1 taken 404 times.
✓ Branch 4 taken 404 times.
404 record.vars = m_structure.GetColumnStore().Array(kRenderProductOrderedVars,
401 prims.Row(index, SchemaId::RenderProduct));
402
3/4
✓ Branch 1 taken 404 times.
✓ Branch 4 taken 404 times.
✓ Branch 7 taken 404 times.
✗ Branch 8 not taken.
404 product.taxonomy = view.Get<Token>(kRenderProductSemanticTaxonomy).Get().front();
403 404 try {
404
1/1
✓ Branch 1 taken 404 times.
404 product.sensors = targets(kRenderProductCamera, SchemaId::Sensor);
405
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 403 times.
404 if (product.sensors.empty()) {
406
1/1
✓ Branch 2 taken 1 time.
1 throw Error("{} renders no sensor", path);
407 }
408
1/1
✓ Branch 1 taken 403 times.
403 const Prim lead = m_scene.ByIndex(product.sensors.front());
409
1/1
✓ Branch 1 taken 403 times.
403 const SchemaId schema = prims.Record(lead.Index()).schema;
410
1/1
✓ Branch 1 taken 403 times.
403 const Uint2 resolution = prim.Get(RenderProduct::kResolution);
411
1/1
✓ Branch 1 taken 403 times.
403 const Shape shape = SensorShape(lead, resolution);
412 403 product.sensor = schema;
413
1/1
✓ Branch 1 taken 403 times.
403 product.frameRate = lead.Get(SensorAPI::kSensorFrameRate);
414
1/1
✓ Branch 1 taken 403 times.
403 product.startTime = lead.Get(SensorAPI::kSensorStartTime);
415
2/2
✓ Branch 0 taken 441 times.
✓ Branch 1 taken 403 times.
844 for (const uint32_t sensor : product.sensors) {
416
1/1
✓ Branch 1 taken 441 times.
441 const Prim each = m_scene.ByIndex(sensor);
417
2/3
✓ Branch 1 taken 441 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 441 times.
441 if (prims.Record(sensor).schema != schema) {
418 ✗ throw Error("{} stacks {} and {}, which are different kinds of sensor", path,
419
0/3
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✗ Branch 8 not taken.
✗ lead.Path(), each.Path());
420 }
421
2/3
✓ Branch 1 taken 441 times.
✓ Branch 3 taken 441 times.
✗ Branch 4 not taken.
441 if (each.Get(SensorAPI::kSensorFrameRate) != product.frameRate ||
422
2/3
✓ Branch 1 taken 441 times.
✓ Branch 3 taken 441 times.
✗ Branch 4 not taken.
441 each.Get(SensorAPI::kSensorStartTime) != product.startTime) {
423 ✗ throw Error("{} stacks {} and {}, which capture at different times", path,
424
0/3
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✗ Branch 8 not taken.
✗ lead.Path(), each.Path());
425 }
426
1/1
✓ Branch 1 taken 441 times.
882 if (SensorShape(each, resolution) != shape) {
427
0/1
✗ Branch 1 not taken.
✗ throw Error("{} stacks {} and {}, which have different shapes", path, lead.Path(),
428
0/2
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✗ each.Path());
429 }
430 }
431 403 product.width = shape.resolution.x;
432 403 product.height = shape.resolution.y;
433 403 product.returns = shape.returns;
434
2/4
✓ Branch 0 taken 403 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 403 times.
403 if (product.width == 0 || product.height == 0) {
435
0/1
✗ Branch 2 not taken.
✗ throw Error("{} has a resolution of {} x {}", path, product.width, product.height);
436 }
437
1/1
✓ Branch 1 taken 403 times.
403 const SensorKind& kind = FindSensorKind(schema);
438 403 int64_t channels = 0;
439
3/3
✓ Branch 1 taken 403 times.
✓ Branch 3 taken 371 times.
✓ Branch 4 taken 32 times.
403 if (lead.IsA<Camera>()) {
440
1/1
✓ Branch 1 taken 371 times.
371 channels = DetectorChannels(lead);
441
2/2
✓ Branch 0 taken 409 times.
✓ Branch 1 taken 371 times.
780 for (const uint32_t sensor : product.sensors) {
442
4/4
✓ Branch 1 taken 409 times.
✓ Branch 4 taken 409 times.
✓ Branch 6 taken 408 times.
✓ Branch 7 taken 1 time.
817 channels = DetectorChannels(m_scene.ByIndex(sensor)) == channels ? channels : 0;
443 }
444 }
445 403 product.channels = uint32_t(channels);
446 1626 const auto binding = [&](ColumnRef column, uint32_t at) {
447
3/3
✓ Branch 2 taken 1223 times.
✓ Branch 5 taken 1223 times.
✓ Branch 9 taken 1223 times.
1223 const View bound(m_structure, prims.Record(at).schema, std::vector<uint32_t>{at});
448
2/4
✓ Branch 1 taken 1223 times.
✓ Branch 4 taken 1223 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
1223 const std::vector<uint32_t> found = bound.GetArray<uint32_t>(column).Get(0);
449
4/5
✓ Branch 0 taken 107 times.
✓ Branch 1 taken 1116 times.
✓ Branch 3 taken 107 times.
✓ Branch 5 taken 107 times.
✗ Branch 6 not taken.
1223 return found.empty() || !prims.Alive(found.front()) ? kNoPrim : found.front();
450
1/2
✓ Branch 1 taken 1223 times.
✗ Branch 2 not taken.
2446 };
451
1/1
✓ Branch 1 taken 403 times.
403 product.pipeline = binding(kRenderProductPipelineBinding, index);
452
2/2
✓ Branch 0 taken 391 times.
✓ Branch 1 taken 12 times.
403 if (product.pipeline == kNoPrim) {
453
1/1
✓ Branch 1 taken 391 times.
391 product.pipeline = binding(kSensorPipelineBinding, lead.Index());
454
2/2
✓ Branch 0 taken 429 times.
✓ Branch 1 taken 391 times.
820 for (const uint32_t sensor : product.sensors) {
455
2/3
✓ Branch 1 taken 429 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 429 times.
429 if (binding(kSensorPipelineBinding, sensor) != product.pipeline) {
456 ✗ throw Error("{} stacks {} and {}, which bind different pipelines", path,
457
0/4
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✗ Branch 8 not taken.
✗ Branch 11 not taken.
✗ lead.Path(), m_scene.ByIndex(sensor).Path());
458 }
459 }
460 }
461 403 BoundSensorPipeline bound;
462
2/2
✓ Branch 0 taken 59 times.
✓ Branch 1 taken 344 times.
403 if (product.pipeline != kNoPrim) {
463
3/4
✓ Branch 1 taken 59 times.
✓ Branch 4 taken 59 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 59 times.
59 if (!IsA(prims.Record(product.pipeline).schema, SchemaId::SensorPipeline)) {
464 ✗ throw Error("{} binds {}, which isn't a SensorPipeline", path,
465
0/3
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✗ Branch 8 not taken.
✗ m_scene.ByIndex(product.pipeline).Path());
466 }
467
2/3
✓ Branch 1 taken 59 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 59 times.
118 bound.instance = m_pipelines.Find(product.pipeline);
468
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 59 times.
59 if (!bound.instance) {
469 ✗ throw Error("{} binds {}, which isn't resolved yet", path,
470
0/3
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✗ Branch 8 not taken.
✗ m_scene.ByIndex(product.pipeline).Path());
471 }
472
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 57 times.
59 if (!bound.instance->error.empty()) {
473
1/1
✓ Branch 2 taken 2 times.
2 throw Error("{}", bound.instance->error);
474 }
475
1/1
✓ Branch 1 taken 57 times.
57 const DetectorPattern pattern = lead.IsA<Camera>()
476
3/3
✓ Branch 0 taken 46 times.
✓ Branch 1 taken 11 times.
✓ Branch 3 taken 46 times.
57 ? lead.Get(DetectorAPI::kDetectorPattern)
477 57 : DetectorPattern::Mono;
478 57 const bool mosaics = pattern != DetectorPattern::Mono &&
479 pattern != DetectorPattern::Rgb &&
480 57 pattern != DetectorPattern::Bands;
481
2/3
✓ Branch 1 taken 57 times.
✓ Branch 3 taken 57 times.
✗ Branch 4 not taken.
114 product.layouts = sensor_pipeline::PropagateLayouts(
482
1/1
✓ Branch 1 taken 57 times.
57 bound.instance->resolved, *m_pipelines.Registry(),
483
3/3
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 46 times.
✓ Branch 3 taken 57 times.
125 {.channels = uint32_t(std::max<int64_t>(channels, 1)),
484 .pattern = uint32_t(pattern),
485 57 .mosaics = mosaics});
486 }
487
3/3
✓ Branch 1 taken 401 times.
✓ Branch 3 taken 497 times.
✓ Branch 4 taken 396 times.
893 for (const uint32_t var : targets(kRenderProductOrderedVars, SchemaId::RenderVar)) {
488
1/1
✓ Branch 1 taken 492 times.
492 const ResolvedVar& added = product.vars.emplace_back(
489
2/2
✓ Branch 1 taken 497 times.
✓ Branch 4 taken 492 times.
989 ResolveVar(m_scene.ByIndex(var), product, kind, channels, bound));
490 492 product.semantic |= added.semantic;
491 401 }
492 396 const bool rated =
493 396 std::ranges::any_of(product.vars,
494 474 [](const ResolvedVar& var) {
495
2/2
✓ Branch 0 taken 423 times.
✓ Branch 1 taken 51 times.
474 return var.output.empty() &&
496
2/2
✓ Branch 0 taken 409 times.
✓ Branch 1 taken 14 times.
423 var.source == RenderVarSource::ElectronRate;
497
2/2
✓ Branch 0 taken 382 times.
✓ Branch 1 taken 14 times.
396 }) ||
498
2/2
✓ Branch 0 taken 33 times.
✓ Branch 1 taken 11 times.
44 (bound.instance &&
499 44 std::ranges::any_of(bound.instance->resolved.values,
500 64 [](const sensor_pipeline::Value& value) {
501
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 33 times.
64 return value.type == sensor_pipeline::SignalType::ElectronRate;
502
2/2
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 338 times.
382 }));
503
4/4
✓ Branch 0 taken 55 times.
✓ Branch 1 taken 341 times.
✓ Branch 3 taken 11 times.
✓ Branch 4 taken 44 times.
396 if (bound.instance && std::ranges::any_of(bound.instance->resolved.values,
504 218 [](const sensor_pipeline::Value& value) {
505
2/2
✓ Branch 0 taken 207 times.
✓ Branch 1 taken 11 times.
218 return value.type ==
506 sensor_pipeline::SignalType::Returns;
507
2/2
✓ Branch 0 taken 55 times.
✓ Branch 1 taken 341 times.
396 })) {
508 11 std::optional<FrameOfReference> frame;
509
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 11 times.
34 for (const ResolvedVar& var : product.vars) {
510
3/4
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 11 times.
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
23 if (var.output.empty() || var.source != RenderVarSource::PointCloud) {
511 11 continue;
512 }
513
3/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
12 if (frame && *frame != var.frame) {
514 ✗ throw Error("{} reads point clouds from {} in two frames of reference, and a "
515 "pipeline's clouds share one",
516
0/1
✗ Branch 2 not taken.
✗ path, bound.instance->path);
517 }
518 12 frame = var.frame;
519 }
520
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 if (kind.schema != SchemaId::Lidar && kind.schema != SchemaId::Radar) {
521 ✗ throw Error("{} runs {}, which reads returns, so its sensor must be a Lidar or a "
522 "Radar",
523
0/1
✗ Branch 2 not taken.
✗ path, bound.instance->path);
524 }
525 }
526
4/4
✓ Branch 0 taken 47 times.
✓ Branch 1 taken 349 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 46 times.
396 if (rated && RenderMode(record.renderMode) != RenderMode::RealTime) {
527 1 throw Error("{} measures electron rates, which only a RealTime render traces, so set "
528 "its renderMode to RealTime",
529
1/1
✓ Branch 2 taken 1 time.
1 path);
530 }
531 395 product.entry = SelectEntry(product, kind);
532
3/3
✓ Branch 1 taken 395 times.
✓ Branch 5 taken 5 times.
✓ Branch 6 taken 4 times.
404 product.overlays = prim.Get(RenderProduct::kOverlays);
533
4/4
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 384 times.
✓ Branch 2 taken 10 times.
✓ Branch 3 taken 1 time.
395 if (!product.overlays.empty() && kind.schema != SchemaId::Camera) {
534 1 throw Error("{} draws overlays over a {}, and only cameras draw them", path,
535
2/3
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
✓ Branch 4 taken 1 time.
2 Info(kind.schema).name);
536 }
537
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
404 } catch (const Error& error) {
538
1/1
✓ Branch 2 taken 10 times.
10 product.error = error.what();
539 10 }
540 404 record.sensorSchema = uint32_t(product.sensor);
541 404 record.width = product.width;
542 404 record.height = product.height;
543 404 record.returns = product.returns;
544 404 record.frameRate = product.frameRate;
545 404 record.startTime = product.startTime;
546 404 record.missDistance = product.missDistance;
547 808 return product;
548 808 }
549
550 501 void ProductTable::Run(const ChangeSet& changes, TableVersions& versions) {
551
4/5
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 496 times.
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 5 times.
501 CHRONO_BATCH_RENDER_ZONE("Products/Run");
552
2/2
✓ Branch 0 taken 227 times.
✓ Branch 1 taken 274 times.
501 ColumnStore& store = m_structure.GetColumnStore();
553
5/6
✓ Branch 0 taken 50 times.
✓ Branch 1 taken 177 times.
✓ Branch 2 taken 39 times.
✓ Branch 3 taken 11 times.
✓ Branch 4 taken 39 times.
✗ Branch 5 not taken.
227 const bool edited = changes.resync || changes.structure || !changes.apis.empty() ||
554
4/6
✓ Branch 0 taken 227 times.
✓ Branch 1 taken 274 times.
✓ Branch 2 taken 39 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 39 times.
✗ Branch 5 not taken.
579 m_pipelines.Version() != m_pipelineVersion ||
555
2/2
✓ Branch 1 taken 39 times.
✓ Branch 4 taken 39 times.
39 std::ranges::any_of(Inputs(), [&](ColumnRef input) {
556 556 return store.LastWrite(input) > m_seen;
557 501 });
558
1/2
✓ Branch 0 taken 501 times.
✗ Branch 1 not taken.
501 m_seen = store.WriteSequence();
559
1/2
✓ Branch 0 taken 501 times.
✗ Branch 1 not taken.
501 m_pipelineVersion = m_pipelines.Version();
560
1/2
✓ Branch 0 taken 501 times.
✗ Branch 1 not taken.
501 if (edited) {
561
1/1
✓ Branch 1 taken 501 times.
501 Resolve();
562
2/2
✓ Branch 1 taken 501 times.
✓ Branch 4 taken 501 times.
501 versions.Record(TableId::Products, std::vector<uint32_t>{kGlobalWorld});
563 }
564 501 }
565
566 501 void ProductTable::Resolve() {
567 501 const Prims& prims = m_structure.GetPrims();
568 501 std::map<uint32_t, std::shared_ptr<const ResolvedProduct>> resolved;
569
2/2
✓ Branch 0 taken 11559 times.
✓ Branch 1 taken 501 times.
12060 for (uint32_t prim = 0; prim < prims.Size(); ++prim) {
570
6/6
✓ Branch 1 taken 11559 times.
✓ Branch 3 taken 10748 times.
✓ Branch 4 taken 811 times.
✓ Branch 6 taken 10748 times.
✓ Branch 8 taken 10344 times.
✓ Branch 9 taken 404 times.
11559 if (!prims.Alive(prim) || prims.Record(prim).schema != SchemaId::RenderProduct) {
571 11155 continue;
572 }
573
1/1
✓ Branch 1 taken 404 times.
404 ResolvedProduct product = Resolve(prim);
574
1/1
✓ Branch 1 taken 404 times.
404 const std::shared_ptr<const ResolvedProduct> previous = Find(prim);
575
4/4
✓ Branch 0 taken 219 times.
✓ Branch 1 taken 185 times.
✓ Branch 3 taken 32 times.
✓ Branch 4 taken 187 times.
404 const bool same = previous && static_cast<const ProductShape&>(*previous) == product;
576 404 product.version = same ? previous->version : ++m_versions;
577
4/4
✓ Branch 0 taken 217 times.
✓ Branch 1 taken 187 times.
✓ Branch 2 taken 207 times.
✓ Branch 3 taken 10 times.
404 if (!same && product.error.empty()) {
578 207 std::string sources;
579
2/2
✓ Branch 0 taken 295 times.
✓ Branch 1 taken 207 times.
502 for (const ResolvedVar& var : product.vars) {
580 ✗ sources += fmt::format(
581
3/3
✓ Branch 0 taken 95 times.
✓ Branch 1 taken 200 times.
✓ Branch 3 taken 295 times.
685 "{}{}", sources.empty() ? "" : ", ",
582
3/3
✓ Branch 0 taken 242 times.
✓ Branch 1 taken 53 times.
✓ Branch 3 taken 242 times.
590 var.output.empty() ? std::string(kRenderVarSourceTokens[uint32_t(var.source)])
583
1/1
✓ Branch 1 taken 53 times.
348 : var.output);
584 }
585
1/1
✓ Branch 1 taken 207 times.
207 Log(LogLevel::Debug, "resolved {}: {} at {} x {} writing {}{}",
586
3/3
✓ Branch 1 taken 207 times.
✓ Branch 4 taken 207 times.
✓ Branch 7 taken 207 times.
414 m_scene.ByIndex(prim).Path(), Counted(product.sensors.size(), "sensor"),
587
4/4
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 200 times.
✓ Branch 3 taken 7 times.
✓ Branch 6 taken 200 times.
414 product.width, product.height, sources.empty() ? "nothing" : sources,
588
3/3
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 202 times.
✓ Branch 3 taken 5 times.
414 product.frameRate > 0 ? fmt::format(", capturing at {} Hz", product.frameRate)
589
1/1
✓ Branch 1 taken 202 times.
202 : "");
590 207 }
591
2/2
✓ Branch 1 taken 404 times.
✓ Branch 4 taken 404 times.
404 m_records.Set(prims.Row(prim, SchemaId::RenderProduct), product.record);
592
4/6
✓ Branch 1 taken 404 times.
✓ Branch 4 taken 404 times.
✓ Branch 6 taken 219 times.
✓ Branch 7 taken 185 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
808 resolved.emplace(prim, std::make_shared<const ResolvedProduct>(std::move(product)));
593 404 }
594 501 {
595
1/1
✓ Branch 1 taken 501 times.
501 const std::lock_guard lock(m_mutex);
596 501 m_products = std::move(resolved);
597 501 }
598
2/2
✓ Branch 1 taken 501 times.
✓ Branch 4 taken 501 times.
501 m_records.Reserve(m_structure.GetColumnStore().Schema(SchemaId::RenderProduct).Capacity());
599
1/1
✓ Branch 1 taken 501 times.
501 m_records.Upload(m_structure.GetColumnStore().GetDevice());
600 501 }
601
602 }
603