chrono_batch_render/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
202 / 210
96.2%
Branches:
143 / 153
93.5%
Functions:
17 / 17
100.0%

sensor_pipeline/resolve.cpp
Line Branch Exec Source
1 // SPDX-License-Identifier: BSD-3-Clause
2 #include "chrono_batch_render/sensor_pipeline/resolve.h"
3
4 #include <algorithm>
5 #include <unordered_map>
6
7 #include "chrono_batch_render/util/error.h"
8 #include "chrono_batch_render/util/text.h"
9
10 namespace chrono::batch_render::sensor_pipeline {
11
12 namespace {
13
14 693 uint32_t Port(std::span<const PortDef> ports, std::string_view name) {
15 693 const auto found = std::ranges::find(ports, name, &PortDef::name);
16
1/2
✓ Branch 0 taken 693 times.
✗ Branch 1 not taken.
693 return found == ports.end() ? kNoValue : uint32_t(found - ports.begin());
17 }
18
19 274 uint32_t ChooseUniform(const NodeDesc& node, const UniformDef& uniform) {
20 274 const auto set =
21 274 std::ranges::find(node.uniforms, uniform.name, &std::pair<std::string, std::string>::first);
22
2/2
✓ Branch 0 taken 124 times.
✓ Branch 1 taken 150 times.
274 const std::string& value = set == node.uniforms.end() ? uniform.fallback : set->second;
23 274 const auto found = std::ranges::find(uniform.allowed, value);
24
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 273 times.
274 if (found == uniform.allowed.end()) {
25 2 throw Error("{}'s {} is {}, and it takes {}", node.path, uniform.name, value,
26
2/2
✓ Branch 2 taken 1 time.
✓ Branch 5 taken 1 time.
2 Join(uniform.allowed));
27 }
28 273 return uint32_t(found - uniform.allowed.begin());
29 }
30
31 232 std::vector<float> ParameterValues(const NodeDesc& node, const ParameterDef& parameter) {
32 232 const auto set = std::ranges::find(node.values, parameter.name,
33 &std::pair<std::string, std::vector<float>>::first);
34
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 227 times.
232 std::vector<float> values = set != node.values.end() ? set->second
35 227 : parameter.fallback.empty()
36
2/2
✓ Branch 0 taken 47 times.
✓ Branch 1 taken 180 times.
227 ? std::vector<float>(parameter.words, 0)
37
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 232 times.
232 : parameter.fallback;
38
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 232 times.
232 if (values.size() != parameter.words) {
39
0/1
✗ Branch 1 not taken.
✗ throw Error("{}'s {} has {} values, and it takes {}", node.path, parameter.name,
40
0/1
✗ Branch 2 not taken.
✗ values.size(), parameter.words);
41 }
42 232 return values;
43 ✗ }
44
45 123 std::string_view UniformValue(const NodeDef& def, const ResolvedNode& node, std::string_view name) {
46
1/2
✓ Branch 0 taken 235 times.
✗ Branch 1 not taken.
235 for (uint32_t index = 0; index < def.uniforms.size(); ++index) {
47
2/2
✓ Branch 0 taken 123 times.
✓ Branch 1 taken 112 times.
235 if (def.uniforms[index].name == name) {
48 123 return def.uniforms[index].allowed[node.uniforms[index]];
49 }
50 }
51 ✗ return {};
52 }
53
54 class Resolver {
55 public:
56 101 Resolver(const SensorPipelineDesc& desc, const NodeRegistry& registry)
57 101 : m_desc(desc), m_registry(registry) {
58
2/2
✓ Branch 0 taken 391 times.
✓ Branch 1 taken 100 times.
491 for (uint32_t index = 0; index < desc.nodes.size(); ++index) {
59
1/1
✓ Branch 1 taken 391 times.
391 const NodeDesc& node = desc.nodes[index];
60
1/1
✓ Branch 1 taken 391 times.
391 const uint32_t def = registry.Find(node.id);
61
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 390 times.
391 if (def == kNoDef) {
62
1/1
✓ Branch 2 taken 1 time.
1 throw Error("{} runs {}, which no pipeline node is", node.path, node.id);
63 }
64
1/1
✓ Branch 1 taken 390 times.
390 m_byPrim.emplace(node.prim, index);
65
1/1
✓ Branch 1 taken 390 times.
390 m_defs.push_back(def);
66 }
67 103 }
68
69 100 ResolvedSensorPipeline Resolve() {
70
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 99 times.
100 if (m_desc.outputs.empty()) {
71
1/1
✓ Branch 2 taken 1 time.
1 throw Error("{} has no outputs", m_desc.path);
72 }
73 99 std::vector<Source> outputs;
74
2/2
✓ Branch 0 taken 118 times.
✓ Branch 1 taken 99 times.
217 for (const ConnectionDesc& output : m_desc.outputs) {
75
1/1
✓ Branch 1 taken 118 times.
236 outputs.push_back(FindSource(output, m_desc.path, output.input));
76 }
77
1/1
✓ Branch 1 taken 99 times.
99 const std::vector<uint32_t> nodes = ReachedFrom(outputs);
78
1/1
✓ Branch 1 taken 95 times.
99 const graph::Graph graph = Connect(nodes);
79
1/1
✓ Branch 1 taken 94 times.
96 const std::vector<uint32_t> order = Order(graph, nodes);
80
81
1/1
✓ Branch 1 taken 94 times.
94 ResolvedSensorPipeline pipeline;
82
1/1
✓ Branch 1 taken 94 times.
94 std::vector<uint32_t> position(graph.Nodes().size());
83
2/2
✓ Branch 0 taken 370 times.
✓ Branch 1 taken 93 times.
463 for (const uint32_t node : order) {
84
1/1
✓ Branch 1 taken 369 times.
370 position[node] = uint32_t(pipeline.nodes.size());
85
1/1
✓ Branch 1 taken 369 times.
370 AddNode(pipeline, nodes[node], graph[node], position);
86 }
87
2/2
✓ Branch 0 taken 111 times.
✓ Branch 1 taken 93 times.
204 for (uint32_t index = 0; index < outputs.size(); ++index) {
88
1/1
✓ Branch 1 taken 111 times.
111 const ResolvedNode& made = pipeline.nodes[position[m_graphNode[outputs[index].node]]];
89 111 pipeline.outputs.push_back(
90
2/2
✓ Branch 1 taken 111 times.
✓ Branch 4 taken 111 times.
111 {m_desc.outputs[index].input, made.outputs[outputs[index].port]});
91 }
92 93 std::ranges::sort(pipeline.outputs, {}, &ResolvedOutput::name);
93
1/1
✓ Branch 1 taken 93 times.
93 pipeline.key = Key(graph, order, pipeline, position);
94 93 return pipeline;
95 219 }
96
97 private:
98 struct Source {
99 uint32_t node;
100 uint32_t port;
101 };
102
103 1444 const NodeDef& Def(uint32_t node) const {
104 1444 return m_registry[m_defs[node]];
105 }
106
107 406 Source FindSource(const ConnectionDesc& connection, std::string_view reader,
108 std::string_view input) const {
109 406 const auto found = m_byPrim.find(connection.source);
110
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 405 times.
406 if (found == m_byPrim.end()) {
111 1 throw Error("{}'s {} connects to {}, which isn't a node of {}", reader, input,
112
1/1
✓ Branch 2 taken 1 time.
1 connection.sourcePath, m_desc.path);
113 }
114 405 const NodeDef& def = Def(found->second);
115 405 const uint32_t port = Port(def.outputs, connection.output);
116
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 405 times.
405 if (port == kNoValue) {
117 ✗ throw Error("{}'s {} reads {}'s {}, which {} doesn't output", reader, input,
118
0/1
✗ Branch 2 not taken.
✗ connection.sourcePath, connection.output, def.id);
119 }
120 405 return {found->second, port};
121 }
122
123 99 std::vector<uint32_t> ReachedFrom(std::span<const Source> outputs) const {
124 99 std::vector<bool> reached(m_desc.nodes.size(), false);
125 99 std::vector<uint32_t> pending;
126 504 const auto reach = [&](uint32_t node) {
127
2/2
✓ Branch 1 taken 383 times.
✓ Branch 2 taken 22 times.
405 if (!reached[node]) {
128 383 reached[node] = true;
129 383 pending.push_back(node);
130 }
131 504 };
132
2/2
✓ Branch 0 taken 118 times.
✓ Branch 1 taken 99 times.
217 for (const Source& output : outputs) {
133
1/1
✓ Branch 1 taken 118 times.
118 reach(output.node);
134 }
135
2/2
✓ Branch 0 taken 383 times.
✓ Branch 1 taken 99 times.
482 while (!pending.empty()) {
136 383 const uint32_t node = pending.back();
137 383 pending.pop_back();
138
2/2
✓ Branch 0 taken 288 times.
✓ Branch 1 taken 383 times.
671 for (const ConnectionDesc& connection : m_desc.nodes[node].inputs) {
139
2/2
✓ Branch 1 taken 287 times.
✓ Branch 2 taken 1 time.
288 if (const auto found = m_byPrim.find(connection.source); found != m_byPrim.end()) {
140
1/1
✓ Branch 2 taken 287 times.
287 reach(found->second);
141 }
142 }
143 }
144 99 std::vector<uint32_t> nodes;
145
2/2
✓ Branch 0 taken 389 times.
✓ Branch 1 taken 99 times.
488 for (uint32_t node = 0; node < reached.size(); ++node) {
146
2/2
✓ Branch 1 taken 383 times.
✓ Branch 2 taken 6 times.
389 if (reached[node]) {
147
1/1
✓ Branch 1 taken 383 times.
383 nodes.push_back(node);
148 }
149 }
150 198 return nodes;
151 99 }
152
153 99 graph::Graph Connect(std::span<const uint32_t> nodes) {
154 99 graph::Graph graph;
155
1/1
✓ Branch 1 taken 99 times.
99 m_graphNode.assign(m_desc.nodes.size(), graph::kNoNode);
156
2/2
✓ Branch 0 taken 383 times.
✓ Branch 1 taken 99 times.
482 for (const uint32_t node : nodes) {
157
1/1
✓ Branch 1 taken 383 times.
383 m_graphNode[node] = graph.Add(m_defs[node]);
158 }
159
2/2
✓ Branch 0 taken 382 times.
✓ Branch 1 taken 95 times.
477 for (const uint32_t node : nodes) {
160 382 const NodeDesc& desc = m_desc.nodes[node];
161 382 const NodeDef& def = Def(node);
162
1/1
✓ Branch 1 taken 382 times.
386 std::vector<bool> connected(def.inputs.size(), false);
163
2/2
✓ Branch 0 taken 288 times.
✓ Branch 1 taken 379 times.
667 for (const ConnectionDesc& connection : desc.inputs) {
164 288 const uint32_t input = Port(def.inputs, connection.input);
165
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 288 times.
288 if (input == kNoValue) {
166 ✗ throw Error("{}'s {} is connected, but {} has no such signal input", desc.path,
167
0/1
✗ Branch 2 not taken.
✗ connection.input, def.id);
168 }
169
1/1
✓ Branch 1 taken 287 times.
288 const Source source = FindSource(connection, desc.path, connection.input);
170
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 285 times.
287 const SignalType made = Def(source.node).outputs[source.port].type;
171 287 const SignalType taken = def.inputs[input].type;
172
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 285 times.
287 if (made != taken) {
173 4 throw Error("{}'s {} takes {}, but {}'s {} is {}", desc.path, connection.input,
174
1/1
✓ Branch 1 taken 2 times.
2 Signal(taken).name, connection.sourcePath, connection.output,
175
1/1
✓ Branch 2 taken 2 times.
2 Signal(made).name);
176 }
177
1/1
✓ Branch 1 taken 285 times.
285 graph.Connect(m_graphNode[node], input, m_graphNode[source.node], source.port);
178 285 connected[input] = true;
179 }
180
2/2
✓ Branch 0 taken 287 times.
✓ Branch 1 taken 378 times.
665 for (uint32_t input = 0; input < def.inputs.size(); ++input) {
181
4/4
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 285 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 1 time.
287 if (!connected[input] && !def.inputs[input].optional) {
182
1/1
✓ Branch 1 taken 1 time.
1 throw Error("{}'s {} isn't connected, and {} needs it", desc.path,
183
1/1
✓ Branch 2 taken 1 time.
1 def.inputs[input].name, def.id);
184 }
185 }
186 382 }
187 95 return graph;
188 4 }
189
190 95 std::vector<uint32_t> Order(const graph::Graph& graph, std::span<const uint32_t> nodes) const {
191 95 graph::Ordering ordering = graph.Order();
192
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 94 times.
95 if (!ordering.cycle.empty()) {
193 1 std::vector<std::string> paths;
194
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 time.
3 for (const uint32_t node : ordering.cycle) {
195
1/1
✓ Branch 1 taken 2 times.
2 paths.push_back(m_desc.nodes[nodes[node]].path);
196 }
197
2/2
✓ Branch 2 taken 1 time.
✓ Branch 5 taken 1 time.
2 throw Error("{} loops through {}", m_desc.path, Join(paths));
198 1 }
199 94 return std::move(ordering.order);
200 94 }
201
202 370 void AddNode(ResolvedSensorPipeline& pipeline, uint32_t node, const graph::Node& connected,
203 std::span<const uint32_t> position) const {
204 370 const NodeDesc& desc = m_desc.nodes[node];
205 370 const NodeDef& def = Def(node);
206 370 const auto at = uint32_t(pipeline.nodes.size());
207 370 ResolvedNode& resolved = pipeline.nodes.emplace_back();
208 370 resolved.def = m_defs[node];
209 370 resolved.prim = desc.prim;
210 370 resolved.path = desc.path;
211
2/2
✓ Branch 0 taken 274 times.
✓ Branch 1 taken 369 times.
643 for (const UniformDef& uniform : def.uniforms) {
212 274 resolved.uniforms.push_back(ChooseUniform(desc, uniform));
213 }
214
2/2
✓ Branch 0 taken 232 times.
✓ Branch 1 taken 369 times.
601 for (const ParameterDef& parameter : def.parameters) {
215 232 const std::vector<float> values = ParameterValues(desc, parameter);
216
1/1
✓ Branch 1 taken 232 times.
232 resolved.parameters.insert(resolved.parameters.end(), values.begin(), values.end());
217 232 }
218
2/2
✓ Branch 0 taken 369 times.
✓ Branch 1 taken 369 times.
738 for (uint32_t port = 0; port < def.outputs.size(); ++port) {
219 369 resolved.outputs.push_back(uint32_t(pipeline.values.size()));
220 369 pipeline.values.push_back({at, port, def.outputs[port].type});
221 }
222
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 363 times.
369 if (def.kind == NodeKind::Global) {
223
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 6 times.
14 for (uint32_t pass = 0; pass + 1 < def.passes; ++pass) {
224 8 resolved.scratch.push_back(uint32_t(pipeline.values.size()));
225 8 pipeline.values.push_back(
226 8 {at, uint32_t(def.outputs.size()) + pass, def.outputs[0].type});
227 }
228 }
229 369 resolved.inputs.assign(def.inputs.size(), kNoValue);
230
2/2
✓ Branch 0 taken 277 times.
✓ Branch 1 taken 369 times.
646 for (const graph::Edge& edge : connected.inputs) {
231 277 resolved.inputs[edge.input] =
232 277 pipeline.nodes[position[edge.source]].outputs[edge.output];
233 }
234 369 }
235
236 93 graph::ShapeKey Key(const graph::Graph& graph, std::span<const uint32_t> order,
237 const ResolvedSensorPipeline& pipeline,
238 std::span<const uint32_t> position) const {
239 553 return graph::GraphShape(graph, order, [&](uint32_t node, graph::ShapeWriter& writer) {
240 367 const ResolvedNode& resolved = pipeline.nodes[position[node]];
241 367 writer.Add(m_registry[resolved.def].code.version);
242
2/2
✓ Branch 0 taken 271 times.
✓ Branch 1 taken 367 times.
638 for (const uint32_t uniform : resolved.uniforms) {
243 271 writer.Add(uniform);
244 }
245
2/2
✓ Branch 0 taken 93 times.
✓ Branch 1 taken 274 times.
367 if (node == order.back()) {
246 93 writer.Add(uint32_t(pipeline.outputs.size()));
247
2/2
✓ Branch 0 taken 111 times.
✓ Branch 1 taken 93 times.
204 for (const ResolvedOutput& output : pipeline.outputs) {
248 111 writer.Add(output.name);
249 111 writer.Add(output.value);
250 }
251 }
252
1/1
✓ Branch 2 taken 93 times.
186 });
253 }
254
255 const SensorPipelineDesc& m_desc;
256 const NodeRegistry& m_registry;
257 std::unordered_map<uint32_t, uint32_t> m_byPrim;
258 std::vector<uint32_t> m_defs;
259 std::vector<uint32_t> m_graphNode;
260 };
261
262 }
263
264 101 ResolvedSensorPipeline ResolveSensorPipeline(const SensorPipelineDesc& desc,
265 const NodeRegistry& registry) {
266
1/1
✓ Branch 2 taken 93 times.
101 return Resolver(desc, registry).Resolve();
267 }
268
269 124 std::vector<SignalLayout> PropagateLayouts(const ResolvedSensorPipeline& pipeline,
270 const NodeRegistry& registry, DetectorLayout detector) {
271 124 std::vector<SignalLayout> layouts(pipeline.values.size());
272
2/2
✓ Branch 0 taken 534 times.
✓ Branch 1 taken 124 times.
658 for (const ResolvedNode& node : pipeline.nodes) {
273 534 const NodeDef& def = registry[node.def];
274 534 const auto first =
275 534 std::ranges::find_if(node.inputs, [](uint32_t value) { return value != kNoValue; });
276
2/2
✓ Branch 0 taken 408 times.
✓ Branch 1 taken 126 times.
534 SignalLayout layout = first != node.inputs.end() ? layouts[*first] : SignalLayout{};
277
6/6
✓ Branch 0 taken 112 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 38 times.
✓ Branch 3 taken 18 times.
✓ Branch 4 taken 12 times.
✓ Branch 5 taken 350 times.
534 switch (def.layout) {
278 case LayoutRule::FirstInput:
279 break;
280 112 case LayoutRule::Detector:
281 112 layout = SignalLayout{.channels = detector.channels, .pattern = detector.pattern};
282
4/4
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 101 times.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 8 times.
112 layout.mosaic = detector.mosaics && UniformValue(def, node, "layout") == "mosaic";
283
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 104 times.
112 if (layout.mosaic) {
284 8 layout.channels = 1;
285 }
286
2/2
✓ Branch 1 taken 110 times.
✓ Branch 2 taken 2 times.
112 layout.element = UniformValue(def, node, "precision") == "half" ? ElementType::F16
287 : ElementType::F32;
288 112 break;
289 4 case LayoutRule::FullChannels:
290 4 layout.channels = detector.channels;
291 4 layout.pattern = detector.pattern;
292 4 layout.mosaic = false;
293 4 break;
294 38 case LayoutRule::Color:
295 38 layout = SignalLayout{.channels = 3};
296 38 break;
297 18 case LayoutRule::Stat:
298 18 layout = SignalLayout{.channels = kStatStorageWords, .perCapture = true};
299 18 break;
300 12 case LayoutRule::Returns:
301 12 layout = SignalLayout{.channels = kReturnsChannels};
302 12 break;
303 }
304
2/2
✓ Branch 0 taken 534 times.
✓ Branch 1 taken 534 times.
1068 for (const uint32_t value : node.outputs) {
305 534 layouts[value] = layout;
306 }
307
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 534 times.
544 for (const uint32_t value : node.scratch) {
308 10 layouts[value] = layout;
309 }
310 }
311 124 return layouts;
312 }
313
314 }
315