chrono_batch_render/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
133 / 146
91.1%
Branches:
147 / 182
80.8%
Functions:
12 / 14
85.7%

render/ray_cast/ray_cast.cpp
Line Branch Exec Source
1 // SPDX-License-Identifier: BSD-3-Clause
2 #include "chrono_batch_render/render/ray_cast/ray_cast.h"
3
4 #include <algorithm>
5 #include <memory>
6 #include <string>
7 #include <utility>
8 #include <vector>
9
10 #include "chrono_batch_render/device/device.h"
11 #include "chrono_batch_render/device/program.h"
12 #include "chrono_batch_render/device/queue.h"
13 #include "chrono_batch_render/render/kernels.h"
14 #include "chrono_batch_render/render/kernels/query/ray_cast.h"
15 #include "chrono_batch_render/scene/scene.h"
16 #include "chrono_batch_render/scene/scene_impl.h"
17 #include "chrono_batch_render/util/error.h"
18 #include "chrono_batch_render/util/profiler.h"
19 #include "chrono_batch_render/util/span.h"
20
21 namespace chrono::batch_render {
22 namespace {
23
24 1183 int64_t CountRays(device::Device& device, const device::Tensor& tensor, int64_t width,
25 device::DType type, const char* what) {
26 1183 const Span<const int64_t> shape = tensor.Shape();
27
4/6
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1178 times.
✓ Branch 2 taken 1178 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1178 times.
1183 const bool shaped = width == 1 ? shape.size() == 1 : shape.size() == 2 && shape[1] == width;
28
3/4
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1182 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 time.
1183 const bool typed = tensor.Type() == type ||
29
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ (type == device::DType::UInt32 && tensor.Type() == device::DType::Int32);
30
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1182 times.
1183 if (!shaped || !typed) {
31 1 throw Error("Ray {} are a [N{}] {} tensor", what,
32
3/7
✗ Branch 1 not taken.
✓ Branch 4 taken 1 time.
✓ Branch 7 taken 1 time.
✓ Branch 9 taken 1 time.
✗ Branch 10 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
3 width == 1 ? "" : ", " + std::to_string(width),
33
3/5
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 time.
✓ Branch 6 taken 1 time.
3 type == device::DType::Float32 ? "float32" : "uint32");
34 }
35
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1182 times.
1182 if (&tensor.GetDevice() != &device) {
36 ✗ throw Error("Ray {} are on {}, not the scene's {}; Device::Copy or Device::Import moves "
37 "them",
38
0/2
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✗ what, tensor.GetDevice().Adapter().alias, device.Adapter().alias);
39 }
40 1182 const Span<const int64_t> strides = tensor.Strides();
41
6/8
✓ Branch 0 taken 1182 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1181 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 1181 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1177 times.
✓ Branch 7 taken 5 times.
1182 if (!strides.empty() && ((shape.back() > 1 && strides.back() != 1) ||
42
3/4
✓ Branch 0 taken 440 times.
✓ Branch 1 taken 737 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 440 times.
1177 (width > 1 && shape[0] > 1 && strides[0] != width))) {
43
0/1
✗ Branch 2 not taken.
✗ throw UnsupportedError("Ray {} are cast from contiguous tensors only", what);
44 }
45 1182 return shape[0];
46 }
47
48 1463 uint64_t InputAddress(device::Device& device, const device::Tensor& tensor, int64_t width,
49 device::DType type, int64_t count, const char* what) {
50
2/2
✓ Branch 0 taken 591 times.
✓ Branch 1 taken 872 times.
1463 if (tensor.Empty()) {
51 return 0;
52 }
53
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 590 times.
591 if (CountRays(device, tensor, width, type, what) != count) {
54
1/1
✓ Branch 3 taken 1 time.
1 throw Error("Ray {} hold {} rows for {} rays", what, tensor.Shape()[0], count);
55 }
56 590 return tensor.Address();
57 }
58
59 11 void Collect(const Prim& prim, std::vector<uint32_t>& prims) {
60 11 prims.push_back(prim.Index());
61
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 11 times.
15 for (const Prim& child : prim.Children()) {
62
1/1
✓ Branch 1 taken 4 times.
4 Collect(child, prims);
63 11 }
64 11 }
65
66 291 std::vector<uint32_t> IgnoredPrims(const std::vector<Prim>& ignore) {
67 291 std::vector<uint32_t> prims;
68
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 291 times.
298 for (const Prim& prim : ignore) {
69
1/1
✓ Branch 1 taken 7 times.
7 Collect(prim, prims);
70 }
71 291 std::ranges::sort(prims);
72 291 prims.erase(std::unique(prims.begin(), prims.end()), prims.end());
73 291 return prims;
74 ✗ }
75
76 1752 uint64_t Output(device::Device& device, device::Tensor& tensor, bool wanted, device::DType type,
77 std::vector<int64_t> shape) {
78
2/2
✓ Branch 0 taken 883 times.
✓ Branch 1 taken 869 times.
1752 if (!wanted) {
79 883 tensor = device::Tensor();
80 883 return 0;
81 }
82
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 869 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
869 const bool fits = !tensor.Empty() && &tensor.GetDevice() == &device && tensor.Type() == type &&
83
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ std::ranges::equal(tensor.Shape(), shape);
84 ✗ if (!fits) {
85
1/1
✓ Branch 2 taken 869 times.
869 tensor = device.CreateTensor(type, std::move(shape), "ray hits");
86 }
87 869 return tensor.Address();
88 }
89
90 }
91
92 51 struct RayCaster::State {
93 Ref<Scene> scene;
94 Ref<device::Program> program;
95 std::string options;
96
97 50 void Link() {
98 50 options = SceneImpl::Get(*scene).GetCurves().ShapeOptions();
99
1/1
✓ Branch 3 taken 50 times.
300 program = scene->GetDevice().CreateProgram(
100 {.modules = {"chrono_batch_render.render.kernels.query.ray_cast"},
101 .library = &kRenderKernels,
102 .sources = {{kShapeOptionsFile, options}},
103 .entries = {"CastClosest", "CastAny"},
104
1/1
✓ Branch 1 taken 50 times.
50 .label = "ray cast",
105
2/3
✓ Branch 1 taken 50 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 50 times.
100 .traversal = SceneImpl::Get(*scene).GetBuilder().Format()});
106 200 }
107 };
108
109 51 RayCaster::RayCaster(Ref<Scene> scene) : m_state(std::make_unique<State>()) {
110
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 50 times.
51 if (!scene) {
111
2/2
✓ Branch 2 taken 1 time.
✓ Branch 5 taken 1 time.
2 throw Error("A ray caster of no scene");
112 }
113
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 50 times.
50 m_state->scene = std::move(scene);
114
1/1
✓ Branch 1 taken 50 times.
50 m_state->Link();
115 51 }
116
117 50 RayCaster::~RayCaster() = default;
118 ✗ RayCaster::RayCaster(RayCaster&&) noexcept = default;
119 ✗ RayCaster& RayCaster::operator=(RayCaster&&) noexcept = default;
120
121 297 device::Operation RayCaster::Cast(const Rays& rays, RayHits& hits, const RayCastDesc& desc,
122 Stream stream) {
123
4/5
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 295 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 2 times.
297 CHRONO_BATCH_RENDER_ZONE("Query/RayCast");
124
1/1
✓ Branch 1 taken 297 times.
297 Scene& scene = *m_state->scene;
125
1/1
✓ Branch 1 taken 297 times.
297 SceneUpdate update = SceneImpl::Get(scene).Update({}, stream);
126
2/3
✓ Branch 1 taken 297 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 297 times.
297 if (SceneImpl::Get(scene).GetCurves().ShapeOptions() != m_state->options) {
127
0/1
✗ Branch 1 not taken.
✗ m_state->Link();
128 }
129
1/1
✓ Branch 1 taken 297 times.
297 device::Device& device = scene.GetDevice();
130
3/4
✓ Branch 0 taken 296 times.
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
✓ Branch 3 taken 296 times.
297 if (rays.origins.Empty() || rays.directions.Empty()) {
131
2/2
✓ Branch 2 taken 1 time.
✓ Branch 5 taken 1 time.
2 throw Error("A cast needs ray origins and directions");
132 }
133 296 constexpr device::DType kFloat = device::DType::Float32;
134
1/1
✓ Branch 1 taken 296 times.
296 const int64_t count = CountRays(device, rays.origins, 3, kFloat, "origins");
135
3/3
✓ Branch 1 taken 295 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 293 times.
296 if (CountRays(device, rays.directions, 3, kFloat, "directions") != count) {
136
1/1
✓ Branch 3 taken 2 times.
2 throw Error("{} ray origins with {} directions", count, rays.directions.Shape()[0]);
137 }
138
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 293 times.
293 if (count > int64_t(~uint32_t(0))) {
139
0/1
✗ Branch 2 not taken.
✗ throw Error("A cast of {} rays; at most {} fit one cast", count, ~uint32_t(0));
140 }
141 293 RayCastParams params{};
142
1/1
✓ Branch 1 taken 293 times.
293 params.origins.address = InputAddress(device, rays.origins, 3, kFloat, count, "origins");
143 586 params.directions.address =
144
1/1
✓ Branch 1 taken 293 times.
293 InputAddress(device, rays.directions, 3, kFloat, count, "directions");
145
1/1
✓ Branch 1 taken 292 times.
293 params.tMins.address = InputAddress(device, rays.tMins, 1, kFloat, count, "minimum distances");
146 584 params.tMaxes.address =
147
1/1
✓ Branch 1 taken 292 times.
292 InputAddress(device, rays.tMaxes, 1, kFloat, count, "maximum distances");
148 584 params.worlds.address =
149
1/1
✓ Branch 1 taken 292 times.
292 InputAddress(device, rays.worlds, 1, device::DType::UInt32, count, "worlds");
150 292 params.minDistance = rays.minDistance;
151 292 params.maxDistance = rays.maxDistance;
152 292 params.world = rays.world;
153 292 params.count = uint32_t(count);
154
155 292 const bool closest = desc.mode == RayCastMode::Closest;
156 2044 const auto rows = [&](int64_t width) {
157
2/2
✓ Branch 0 taken 1168 times.
✓ Branch 1 taken 584 times.
1752 return width == 1 ? std::vector<int64_t>{count} : std::vector<int64_t>{count, width};
158 292 };
159
2/2
✓ Branch 1 taken 292 times.
✓ Branch 4 taken 292 times.
292 params.hits.address = Output(device, hits.hit, true, device::DType::UInt32, rows(1));
160 584 params.distances.address =
161
6/6
✓ Branch 1 taken 292 times.
✓ Branch 3 taken 289 times.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 1 time.
✓ Branch 6 taken 288 times.
✓ Branch 8 taken 292 times.
293 Output(device, hits.distance, closest && desc.outputs & RayOutput::Distance,
162 292 device::DType::Float32, rows(1));
163
6/6
✓ Branch 1 taken 292 times.
✓ Branch 3 taken 289 times.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 24 times.
✓ Branch 6 taken 265 times.
✓ Branch 8 taken 292 times.
316 params.prims.address = Output(device, hits.prim, closest && desc.outputs & RayOutput::Prim,
164 292 device::DType::UInt32, rows(1));
165 584 params.instances.address =
166
6/6
✓ Branch 1 taken 292 times.
✓ Branch 3 taken 289 times.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 288 times.
✓ Branch 6 taken 1 time.
✓ Branch 8 taken 292 times.
580 Output(device, hits.instance, closest && desc.outputs & RayOutput::InstanceId,
167 292 device::DType::Int64, rows(1));
168 584 params.normals.address =
169
6/6
✓ Branch 1 taken 292 times.
✓ Branch 3 taken 289 times.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 275 times.
✓ Branch 6 taken 14 times.
✓ Branch 8 taken 292 times.
567 Output(device, hits.normal, closest && desc.outputs & RayOutput::Normal,
170 292 device::DType::Float32, rows(3));
171 584 params.positions.address =
172
6/6
✓ Branch 1 taken 292 times.
✓ Branch 3 taken 289 times.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 280 times.
✓ Branch 6 taken 9 times.
✓ Branch 8 taken 292 times.
572 Output(device, hits.position, closest && desc.outputs & RayOutput::Position,
173 292 device::DType::Float32, rows(3));
174
175 292 params.spaceRow = kNoRow;
176
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 289 times.
292 if (rays.space) {
177
3/3
✓ Branch 1 taken 3 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 2 times.
3 if (!rays.space->IsA<Xformable>()) {
178 1 throw Error("rays are cast in the space of {}, which has no transform",
179
2/2
✓ Branch 2 taken 1 time.
✓ Branch 5 taken 1 time.
2 rays.space->Path());
180 }
181 }
182
1/1
✓ Branch 1 taken 291 times.
291 const std::vector<uint32_t> ignored = IgnoredPrims(desc.ignore);
183
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 289 times.
291 params.ignoredCount = uint32_t(ignored.size());
184
185
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 289 times.
291 if (rays.space) {
186
1/1
✓ Branch 1 taken 2 times.
2 const uint32_t prim = rays.space->Index();
187
1/1
✓ Branch 1 taken 2 times.
4 params.spaceRow = SceneImpl::Get(scene).GetStructure().GetPrims().Rows(
188 2 std::span<const uint32_t>(&prim, 1), SchemaId::Xformable)[0];
189 }
190
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 291 times.
291 if (count == 0) {
191 ✗ hits.done = update.Published();
192 ✗ return hits.done;
193 }
194
1/1
✓ Branch 1 taken 291 times.
291 const Snapshot& snapshot = update.Tables();
195 291 params.tables.address = snapshot.tables;
196
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 286 times.
291 device::Queue& queue = device.GetQueue();
197
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 286 times.
291 if (!ignored.empty()) {
198
1/1
✓ Branch 1 taken 5 times.
5 params.ignored.address =
199
1/1
✓ Branch 1 taken 5 times.
5 queue.Upload(ignored.data(), ignored.size() * sizeof(uint32_t)).Address();
200 }
201
1/1
✓ Branch 1 taken 291 times.
291 queue.Follow(stream);
202
1/1
✓ Branch 1 taken 291 times.
291 device::CommandEncoder encoder = queue.CreateEncoder();
203
2/2
✓ Branch 1 taken 291 times.
✓ Branch 4 taken 291 times.
291 encoder.BeginZone(profiler::ZoneId::Named(desc.label));
204
4/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 288 times.
✓ Branch 3 taken 291 times.
✓ Branch 6 taken 291 times.
294 encoder.Dispatch(m_state->program->Entry(closest ? "CastClosest" : "CastAny"), params,
205
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 288 times.
291 {.x = device::GroupCount(params.count, kRayCastGroup)});
206
1/1
✓ Branch 1 taken 291 times.
291 encoder.EndZone();
207
2/2
✓ Branch 1 taken 291 times.
✓ Branch 4 taken 291 times.
291 hits.done = queue.Submit(encoder.Finish());
208
1/2
✓ Branch 0 taken 291 times.
✗ Branch 1 not taken.
291 if (!stream.IsNone()) {
209
1/1
✓ Branch 1 taken 291 times.
291 hits.done.Wait(stream);
210 }
211 291 return hits.done;
212 303 }
213
214 18 Scene& RayCaster::GetScene() const {
215 18 return *m_state->scene;
216 }
217
218 288 RayHits RayCaster::Cast(const Rays& rays, const RayCastDesc& desc) {
219 288 RayHits hits;
220
1/1
✓ Branch 1 taken 283 times.
288 Cast(rays, hits, desc, Stream::Host());
221 283 return hits;
222 5 }
223
224 }
225