Open3D (C++ API)  0.20.0
Loading...
Searching...
No Matches
VoxelizeSYCL.h
Go to the documentation of this file.
1// ----------------------------------------------------------------------------
2// - Open3D: www.open3d.org -
3// ----------------------------------------------------------------------------
4// Copyright (c) 2018-2026 www.open3d.org
5// SPDX-License-Identifier: MIT
6// ----------------------------------------------------------------------------
7
8// SYCL implementation of Voxelize — ports Voxelize.cuh.
9//
10// Design differences from the CUDA version (documented deviations):
11// - The CUDA version uses cub with a two-pass "query size, then allocate"
12// convention (MemoryAllocation.h) because cub device algorithms require a
13// caller-managed scratch buffer of a specific size. oneDPL algorithms
14// manage their own scratch space internally, so this SYCL port allocates
15// scratch buffers directly with sycl::malloc_device/sycl::free and calls
16// the algorithm once (no size-query pass).
17// - cub::DeviceRunLengthEncode::Encode -> oneapi::dpl::reduce_by_key (plan
18// §6.4): reducing a constant-1 "value" sequence grouped by the sorted key
19// sequence yields the unique keys plus their run lengths in one call.
20// reduce_by_key has no non-blocking oneDPL async equivalent, so it remains
21// a genuine synchronization point (see RunLengthEncodeSYCL).
22// - cub::DeviceRadixSort::SortPairs -> oneapi::dpl::stable_sort_by_key (must
23// be the stable variant to match cub's stable ordering of points sharing a
24// hash). Also has no async equivalent; a genuine synchronization point.
25// - cub::DeviceScan::InclusiveSum ->
26// oneapi::dpl::experimental::inclusive_scan_async
27// (non-blocking; same pattern as InvertNeighborsListSYCL.h).
28//
29// Each stage takes an optional `deps` event-vector and returns its
30// completion event; callers thread these through instead of blocking waits,
31// except at genuine host-synchronization points (ReadScalar, used where a
32// value must be read back to make a host-side branch/sizing decision) and
33// the two oneDPL algorithms above (which have no async form).
34//
35// MiniVec (open3d/utility/MiniVec.h) is reused as-is: its FN_SPECIFIERS macro
36// expands to plain `inline` when not compiled by nvcc, so it is safe to use
37// unmodified inside SYCL device kernels.
38
39#pragma once
40
41#include <oneapi/dpl/algorithm>
42#include <oneapi/dpl/async>
43#include <oneapi/dpl/execution>
44#include <sycl/sycl.hpp>
45
49
50namespace open3d {
51namespace ml {
52namespace impl {
53
54namespace sycl_voxelize_detail {
55
57
61template <class T>
62inline T ReadScalar(sycl::queue& queue,
63 const T* device_ptr,
64 const std::vector<sycl::event>& deps = {}) {
65 T value{};
66 queue.memcpy(&value, device_ptr, sizeof(T), deps).wait();
67 return value;
68}
69
72inline sycl::event ComputeIndicesBatchesSYCL(sycl::queue& queue,
73 int64_t* indices_batches,
74 const int64_t* row_splits,
75 int64_t batch_size) {
76 if (batch_size == 0) return sycl::event();
77 return core::ParallelFor(
78 queue, batch_size,
79 [=](int64_t b) {
80 for (int64_t i = row_splits[b]; i < row_splits[b + 1]; ++i) {
81 indices_batches[i] = b;
82 }
83 },
84 std::vector<sycl::event>{});
85}
86
91template <class T, int NDIM>
92inline sycl::event ComputeHashSYCL(sycl::queue& queue,
93 int64_t* hashes,
94 int64_t num_points,
95 const T* const points,
96 const int64_t* const indices_batches,
97 const MiniVec<T, NDIM> points_range_min_vec,
98 const MiniVec<T, NDIM> points_range_max_vec,
100 const MiniVec<int64_t, NDIM> strides,
101 int64_t batch_hash,
102 int64_t invalid_hash,
103 const std::vector<sycl::event>& deps = {}) {
104 if (num_points == 0) return sycl::event();
105 typedef MiniVec<T, NDIM> Vec_t;
106 return core::ParallelFor(
107 queue, num_points,
108 [=](int64_t i) {
109 Vec_t point(points + i * NDIM);
110 if ((point >= points_range_min_vec &&
111 point <= points_range_max_vec)
112 .all()) {
113 auto coords =
114 ((point - points_range_min_vec) * inv_voxel_size)
115 .template cast<int64_t>();
116 int64_t h = coords.dot(strides);
117 h += indices_batches[i] * batch_hash;
118 hashes[i] = h;
119 } else {
120 hashes[i] = invalid_hash;
121 }
122 },
123 deps);
124}
125
128inline sycl::event LimitCountsSYCL(sycl::queue& queue,
129 int64_t* counts,
130 int64_t num,
131 int64_t limit,
132 const std::vector<sycl::event>& deps = {}) {
133 if (num == 0) return sycl::event();
134 return core::ParallelFor(
135 queue, num,
136 [=](int64_t i) {
137 if (counts[i] > limit) counts[i] = limit;
138 },
139 deps);
140}
141
156inline int64_t RunLengthEncodeSYCL(sycl::queue& queue,
157 const int64_t* const keys,
158 int64_t num_keys,
159 int64_t* unique_keys_out,
160 int64_t* unique_counts_out,
161 const std::vector<sycl::event>& deps = {}) {
162 if (num_keys == 0) return 0;
163
164 // reduce_by_key sums a "value" sequence per run of equal keys; a
165 // constant-1 value sequence turns that sum into the run length, i.e.
166 // the RLE count cub::DeviceRunLengthEncode::Encode would produce.
167 int64_t* ones = sycl::malloc_device<int64_t>(num_keys, queue);
168 // reduce_by_key has no event-dependency parameter, so `deps` and the
169 // fill must be explicitly waited on before it (a genuine hazard on an
170 // out-of-order queue, not a lazy default).
171 queue.fill(ones, int64_t(1), num_keys, deps).wait();
172
173 auto dpl_policy = oneapi::dpl::execution::make_device_policy(queue);
174 auto result =
175 oneapi::dpl::reduce_by_key(dpl_policy, keys, keys + num_keys, ones,
176 unique_keys_out, unique_counts_out);
177 sycl::free(ones, queue);
178
179 return static_cast<int64_t>(result.first - unique_keys_out);
180}
181
184inline void ComputeBatchIdSYCL(sycl::queue& queue,
185 int64_t* hashes,
186 int64_t num_voxels,
187 int64_t batch_hash) {
188 if (num_voxels == 0) return;
189 core::ParallelFor(queue, num_voxels,
190 [=](int64_t i) { hashes[i] /= batch_hash; });
191}
192
196inline void ComputeVoxelPerBatchSYCL(sycl::queue& queue,
197 int64_t* num_voxels_per_batch,
198 const int64_t* unique_batches_count,
199 const int64_t* unique_batches,
200 int64_t num_batches) {
201 if (num_batches == 0) return;
202 core::ParallelFor(queue, num_batches, [=](int64_t i) {
203 num_voxels_per_batch[unique_batches[i]] = unique_batches_count[i];
204 });
205}
206
207// Kernels here use core::ParallelFor (int64_t lambda), not nd_item kernels;
208// [[intel::kernel_args_restrict]] would need to live in ParallelFor.h to
209// apply globally. ComputeStartIdxSYCL may alias points_count with
210// unique_hashes_count at some call sites — audit before adding no-alias hints.
211
216inline sycl::event ComputeStartIdxSYCL(
217 sycl::queue& queue,
218 int64_t* start_idx,
219 int64_t* points_count,
220 const int64_t* num_voxels_prefix_sum,
221 const int64_t* unique_hashes_count_prefix_sum,
222 const int64_t* out_batch_splits,
223 int64_t batch_size,
224 int64_t max_points_per_voxel,
225 const std::vector<sycl::event>& deps = {}) {
226 if (batch_size == 0) return sycl::event();
227 return core::ParallelFor(
228 queue, batch_size,
229 [=](int64_t b) {
230 int64_t voxel_idx = (b == 0) ? 0 : num_voxels_prefix_sum[b - 1];
231 const int64_t begin_out = out_batch_splits[b];
232 const int64_t end_out = out_batch_splits[b + 1];
233 for (int64_t out_idx = begin_out; out_idx < end_out;
234 ++out_idx, ++voxel_idx) {
235 if (voxel_idx == 0) {
236 start_idx[out_idx] = 0;
237 points_count[out_idx] =
238 sycl::min(max_points_per_voxel,
239 unique_hashes_count_prefix_sum[0]);
240 } else {
241 start_idx[out_idx] =
242 unique_hashes_count_prefix_sum[voxel_idx - 1];
243 points_count[out_idx] = sycl::min(
244 max_points_per_voxel,
245 unique_hashes_count_prefix_sum[voxel_idx] -
246 unique_hashes_count_prefix_sum
247 [voxel_idx - 1]);
248 }
249 }
250 },
251 deps);
252}
253
256template <class T, int NDIM>
257inline void ComputeVoxelCoordsSYCL(sycl::queue& queue,
258 int32_t* voxel_coords,
259 const T* const points,
260 const int64_t* const point_indices,
261 const int64_t* const prefix_sum,
262 const MiniVec<T, NDIM> points_range_min_vec,
264 int64_t num_voxels) {
265 if (num_voxels == 0) return;
266 typedef MiniVec<T, NDIM> Vec_t;
267 core::ParallelFor(queue, num_voxels, [=](int64_t i) {
268 const int64_t point_idx = point_indices[prefix_sum[i]];
269 Vec_t point(points + point_idx * NDIM);
270 auto coords = ((point - points_range_min_vec) * inv_voxel_size)
271 .template cast<int32_t>();
272 for (int d = 0; d < NDIM; ++d) {
273 voxel_coords[i * NDIM + d] = coords[d];
274 }
275 });
276}
277
280inline void CopyPointIndicesSYCL(sycl::queue& queue,
281 int64_t* out,
282 const int64_t* const point_indices,
283 const int64_t* const prefix_sum_in,
284 const int64_t* const prefix_sum_out,
285 int64_t num_voxels) {
286 if (num_voxels == 0) return;
287 core::ParallelFor(queue, num_voxels, [=](int64_t i) {
288 const int64_t begin_out = (i == 0) ? 0 : prefix_sum_out[i - 1];
289 const int64_t end_out = prefix_sum_out[i];
290 int64_t in_idx = prefix_sum_in[i];
291 for (int64_t out_idx = begin_out; out_idx < end_out;
292 ++out_idx, ++in_idx) {
293 out[out_idx] = point_indices[in_idx];
294 }
295 });
296}
297
298} // namespace sycl_voxelize_detail
299
309template <class T, int NDIM, class OUTPUT_ALLOCATOR>
310void VoxelizeSYCL(sycl::queue& queue,
311 size_t num_points,
312 const T* const points,
313 const size_t batch_size,
314 const int64_t* const row_splits,
315 const T* const voxel_size,
316 const T* const points_range_min,
317 const T* const points_range_max,
318 const int64_t max_points_per_voxel,
319 const int64_t max_voxels,
320 OUTPUT_ALLOCATOR& output_allocator) {
321 using namespace sycl_voxelize_detail;
322 using namespace open3d::utility;
323 typedef MiniVec<T, NDIM> Vec_t;
324
325 const Vec_t inv_voxel_size = T(1) / Vec_t(voxel_size);
326 const Vec_t points_range_min_vec(points_range_min);
327 const Vec_t points_range_max_vec(points_range_max);
328 MiniVec<int32_t, NDIM> extents =
329 ceil((points_range_max_vec - points_range_min_vec) * inv_voxel_size)
330 .template cast<int32_t>();
332 for (int i = 0; i < NDIM; ++i) {
333 strides[i] = 1;
334 for (int j = 0; j < i; ++j) strides[i] *= extents[j];
335 }
336 const int64_t batch_hash = strides[NDIM - 1] * extents[NDIM - 1];
337 const int64_t invalid_hash = batch_hash * int64_t(batch_size);
338
339 // Degenerate case: no input points. Still emit correctly-shaped (empty)
340 // outputs and all-zero batch splits.
341 if (num_points == 0) {
342 int64_t* out_batch_splits = nullptr;
343 output_allocator.AllocVoxelBatchSplits(&out_batch_splits,
344 batch_size + 1);
345 if (batch_size)
346 queue.fill(out_batch_splits, int64_t(0), batch_size + 1).wait();
347 int32_t* out_voxel_coords = nullptr;
348 output_allocator.AllocVoxelCoords(&out_voxel_coords, 0, NDIM);
349 int64_t* out_voxel_row_splits = nullptr;
350 output_allocator.AllocVoxelPointRowSplits(&out_voxel_row_splits, 1);
351 queue.fill(out_voxel_row_splits, int64_t(0), 1).wait();
352 int64_t* out_point_indices = nullptr;
353 output_allocator.AllocVoxelPointIndices(&out_point_indices, 0);
354 return;
355 }
356
357 // --- Step 1: hash each point (voxel index + batch offset) ------------
358 int64_t* indices_batches = sycl::malloc_device<int64_t>(num_points, queue);
359 int64_t* point_indices = sycl::malloc_device<int64_t>(num_points, queue);
360 int64_t* hashes = sycl::malloc_device<int64_t>(num_points, queue);
361
362 sycl::event indices_batches_event = ComputeIndicesBatchesSYCL(
363 queue, indices_batches, row_splits, int64_t(batch_size));
364
365 auto dpl_policy = oneapi::dpl::execution::make_device_policy(queue);
366 core::ParallelFor(queue, int64_t(num_points),
367 [=](int64_t i) { point_indices[i] = i; });
368
369 // Depends on indices_batches_event: ComputeHashSYCL reads
370 // indices_batches, which ComputeIndicesBatchesSYCL wrote asynchronously.
371 sycl::event hashes_event = ComputeHashSYCL<T, NDIM>(
372 queue, hashes, int64_t(num_points), points, indices_batches,
373 points_range_min_vec, points_range_max_vec, inv_voxel_size, strides,
374 batch_hash, invalid_hash, {indices_batches_event});
375 // indices_batches is freed here, so its last reader (ComputeHashSYCL)
376 // must have completed first; sycl::free is not queue-ordered.
377 hashes_event.wait();
378 sycl::free(indices_batches, queue);
379
380 // --- Step 2: sort points by hash (groups points into voxels) ---------
381 // stable_sort_by_key has no async oneDPL equivalent, so this is a
382 // genuine synchronization point (blocks internally before returning).
383 // Must be stable to match the CUDA path's cub::DeviceRadixSort::SortPairs
384 // (see the file header comment above), which is a stable sort: points
385 // that share a hash (i.e. land in the same voxel) must keep their
386 // original relative order.
387 oneapi::dpl::stable_sort_by_key(dpl_policy, hashes, hashes + num_points,
389
390 // --- Step 3: run-length-encode the sorted hashes -> unique voxels ----
391 int64_t* unique_hashes = sycl::malloc_device<int64_t>(num_points, queue);
392 int64_t* unique_hashes_count =
393 sycl::malloc_device<int64_t>(num_points, queue);
394
395 int64_t num_voxels =
396 RunLengthEncodeSYCL(queue, hashes, int64_t(num_points),
397 unique_hashes, unique_hashes_count);
398 sycl::free(hashes, queue);
399
400 const int64_t last_hash =
401 ReadScalar(queue, unique_hashes + (num_voxels - 1));
402 if (last_hash == invalid_hash) {
403 // Points outside the valid range were hashed to invalid_hash and
404 // sort last; drop that trailing "voxel".
405 --num_voxels;
406 }
407
408 // --- Step 4: prefix sum of (unlimited) per-voxel counts --------------
409 int64_t* unique_hashes_count_prefix_sum = sycl::malloc_device<int64_t>(
410 num_voxels > 0 ? num_voxels : 1, queue);
411 sycl::event scan1_event;
412 if (num_voxels > 0) {
413 scan1_event = oneapi::dpl::experimental::inclusive_scan_async(
414 dpl_policy, unique_hashes_count,
415 unique_hashes_count + num_voxels,
416 unique_hashes_count_prefix_sum)
417 .event();
418 }
419
420 // Clamp per-voxel point counts to max_points_per_voxel (applied after
421 // the prefix sum above, matching the CUDA ordering: the prefix sum uses
422 // the true point ranges, while the clamped counts become the final
423 // per-voxel output sizes). LimitCountsSYCL writes unique_hashes_count in
424 // place while scan1_event may still be reading it, so it depends on
425 // scan1_event (a genuine hazard, not a lazy default) via the
426 // event-accepting overload instead of a blocking wait. Its own
427 // completion event is captured below (limit1_event) since
428 // unique_hashes_count is read again later (aliased as points_count).
429 sycl::event limit1_event;
430 if (max_points_per_voxel < int64_t(num_points)) {
431 limit1_event = LimitCountsSYCL(queue, unique_hashes_count, num_voxels,
432 max_points_per_voxel, {scan1_event});
433 }
434
435 // --- Step 5: group voxels by batch -------------------------------
436 ComputeBatchIdSYCL(queue, unique_hashes, num_voxels, batch_hash);
437
438 int64_t* unique_batches = sycl::malloc_device<int64_t>(
439 batch_size > 0 ? batch_size : 1, queue);
440 int64_t* unique_batches_count = sycl::malloc_device<int64_t>(
441 batch_size > 0 ? batch_size : 1, queue);
442 int64_t num_batches =
443 RunLengthEncodeSYCL(queue, unique_hashes, num_voxels,
444 unique_batches, unique_batches_count);
445 sycl::free(unique_hashes, queue);
446
447 int64_t* num_voxels_per_batch = sycl::malloc_device<int64_t>(
448 batch_size > 0 ? batch_size : 1, queue);
449 queue.fill(num_voxels_per_batch, int64_t(0),
450 batch_size > 0 ? batch_size : 1)
451 .wait();
452 ComputeVoxelPerBatchSYCL(queue, num_voxels_per_batch, unique_batches_count,
453 unique_batches, num_batches);
454 sycl::free(unique_batches, queue);
455 sycl::free(unique_batches_count, queue);
456
457 // Prefix sum of the *unlimited* per-batch voxel counts: gives the index
458 // of the first (unlimited) voxel of each batch within the global list.
459 // Only used by ComputeStartIdxSYCL when num_voxels > max_voxels.
460 int64_t* num_voxels_prefix_sum = sycl::malloc_device<int64_t>(
461 batch_size > 0 ? batch_size : 1, queue);
462 sycl::event scan2_event;
463 if (batch_size > 0) {
464 scan2_event = oneapi::dpl::experimental::inclusive_scan_async(
465 dpl_policy, num_voxels_per_batch,
466 num_voxels_per_batch + batch_size,
467 num_voxels_prefix_sum)
468 .event();
469 }
470
471 // LimitCountsSYCL writes num_voxels_per_batch in place while scan2_event
472 // may still be reading it, so it depends on scan2_event (a genuine
473 // hazard, not a lazy default). num_voxels_per_batch_ready tracks
474 // whichever of {scan2_event, limit2_event} last touched the buffer, so
475 // the Step 6 scan below depends on the right one.
476 sycl::event num_voxels_per_batch_ready = scan2_event;
477 if (num_voxels >= max_voxels) {
478 num_voxels_per_batch_ready =
479 LimitCountsSYCL(queue, num_voxels_per_batch,
480 int64_t(batch_size), max_voxels, {scan2_event});
481 }
482
483 // --- Step 6: batch splits over the (possibly limited) voxel counts ---
484 int64_t* out_batch_splits = nullptr;
485 output_allocator.AllocVoxelBatchSplits(&out_batch_splits, batch_size + 1);
486 queue.fill(out_batch_splits, int64_t(0), 1).wait();
487 sycl::event scan3_event;
488 if (batch_size > 0) {
489 scan3_event = oneapi::dpl::experimental::inclusive_scan_async(
490 dpl_policy, num_voxels_per_batch,
491 num_voxels_per_batch + batch_size,
492 out_batch_splits + 1, num_voxels_per_batch_ready)
493 .event();
494 }
495 queue.ext_oneapi_submit_barrier({scan3_event}).wait();
496 sycl::free(num_voxels_per_batch, queue);
497
498 const int64_t num_valid_voxels =
499 ReadScalar(queue, out_batch_splits + batch_size);
500
501 // --- Step 7: per-voxel start index + clamped point count --------------
502 int64_t* start_idx = sycl::malloc_device<int64_t>(
503 num_valid_voxels > 0 ? num_valid_voxels : 1, queue);
504 int64_t* points_count = nullptr;
505 bool points_count_is_alias = false;
506
507 sycl::event start_idx_ready_event;
508 if (num_voxels <= max_voxels) {
509 // All voxels kept: start_idx/points_count come directly from the
510 // (unlimited-then-clamped) global arrays computed above.
511 queue.fill(start_idx, int64_t(0), 1).wait();
512 if (num_voxels > 1) {
513 // Depends on scan1_event (unique_hashes_count_prefix_sum).
514 start_idx_ready_event = queue.memcpy(
515 start_idx + 1, unique_hashes_count_prefix_sum,
516 (num_voxels - 1) * sizeof(int64_t), scan1_event);
517 }
518 points_count = unique_hashes_count;
519 points_count_is_alias = true;
520 } else {
521 points_count = sycl::malloc_device<int64_t>(num_valid_voxels, queue);
522 // ComputeStartIdxSYCL reads num_voxels_prefix_sum (scan2_event) and
523 // unique_hashes_count_prefix_sum (scan1_event); depends on both via
524 // the event-accepting overload (a genuine hazard, not a lazy
525 // default) instead of a blocking wait.
526 start_idx_ready_event = ComputeStartIdxSYCL(
527 queue, start_idx, points_count, num_voxels_prefix_sum,
528 unique_hashes_count_prefix_sum, out_batch_splits,
529 int64_t(batch_size), max_points_per_voxel,
530 {scan1_event, scan2_event});
531 }
532 // num_voxels_prefix_sum/unique_hashes_count_prefix_sum are read by
533 // start_idx_ready_event's op (whichever branch above); sycl::free is not
534 // queue-ordered, so wait for it before freeing.
535 start_idx_ready_event.wait();
536 sycl::free(num_voxels_prefix_sum, queue);
537 sycl::free(unique_hashes_count_prefix_sum, queue);
538
539 // --- Step 8: row splits over output points per voxel ------------------
540 int64_t* out_voxel_row_splits = nullptr;
541 output_allocator.AllocVoxelPointRowSplits(&out_voxel_row_splits,
542 num_valid_voxels + 1);
543 queue.fill(out_voxel_row_splits, int64_t(0), 1).wait();
544 sycl::event scan4_event;
545 if (num_valid_voxels > 0) {
546 // When points_count_is_alias, points_count is unique_hashes_count,
547 // which limit1_event may still be writing (a genuine hazard, not a
548 // lazy default); depend on it in that case. Otherwise points_count
549 // was written by ComputeStartIdxSYCL, already awaited via
550 // start_idx_ready_event.wait() above.
551 scan4_event =
552 oneapi::dpl::experimental::inclusive_scan_async(
553 dpl_policy, points_count,
554 points_count + num_valid_voxels,
555 out_voxel_row_splits + 1,
556 points_count_is_alias ? limit1_event : sycl::event())
557 .event();
558 }
559
560 // --- Step 9: voxel coordinates + compacted point indices --------------
561 int32_t* out_voxel_coords = nullptr;
562 output_allocator.AllocVoxelCoords(&out_voxel_coords, num_valid_voxels,
563 NDIM);
564 // ComputeVoxelCoordsSYCL reads start_idx (already complete: see Step 7's
565 // ComputeStartIdxSYCL/queue.memcpy, both awaited via core::ParallelFor's
566 // or queue.memcpy's own blocking) -- not points_count/out_voxel_row_splits
567 // (scan4_event), so it may run concurrently with the Step 8 scan on an
568 // out-of-order queue; no dependency needed.
569 ComputeVoxelCoordsSYCL<T, NDIM>(
570 queue, out_voxel_coords, points, point_indices, start_idx,
571 points_range_min_vec, inv_voxel_size, num_valid_voxels);
572
573 const int64_t num_valid_points =
574 num_valid_voxels > 0
575 ? ReadScalar(queue, out_voxel_row_splits + num_valid_voxels,
576 {scan4_event})
577 : 0;
578 int64_t* out_point_indices = nullptr;
579 output_allocator.AllocVoxelPointIndices(&out_point_indices,
580 num_valid_points);
581 // CopyPointIndicesSYCL reads out_voxel_row_splits (scan4_event, already
582 // awaited above via ReadScalar's blocking memcpy) and start_idx (already
583 // complete, see above).
584 CopyPointIndicesSYCL(queue, out_point_indices, point_indices, start_idx,
585 out_voxel_row_splits + 1, num_valid_voxels);
586
587 sycl::free(start_idx, queue);
588 if (!points_count_is_alias) sycl::free(points_count, queue);
589 sycl::free(unique_hashes_count, queue);
590 sycl::free(point_indices, queue);
591}
592
593} // namespace impl
594} // namespace ml
595} // namespace open3d
MouseEvent event
Definition BitmapWindowSystem.cpp:70
double inv_voxel_size
Definition NormalDistributionsTransform.cpp:256
std::vector< int > point_indices
Definition NormalDistributionsTransform.cpp:72
std::vector< int > counts
Definition PointCloudSmoothing.cpp:132
sycl::queue queue
Definition SYCLContext.cpp:88
Point< Real, 3 > point
Definition SurfaceReconstructionPoisson.cpp:166
core::Tensor result
Definition VtkUtils.cpp:76
int points
Definition FilePCD.cpp:55
void ParallelFor(const Device &device, int64_t n, const func_t &func)
Definition ParallelFor.h:190
sycl::event ComputeStartIdxSYCL(sycl::queue &queue, int64_t *start_idx, int64_t *points_count, const int64_t *num_voxels_prefix_sum, const int64_t *unique_hashes_count_prefix_sum, const int64_t *out_batch_splits, int64_t batch_size, int64_t max_points_per_voxel, const std::vector< sycl::event > &deps={})
Definition VoxelizeSYCL.h:216
void ComputeVoxelCoordsSYCL(sycl::queue &queue, int32_t *voxel_coords, const T *const points, const int64_t *const point_indices, const int64_t *const prefix_sum, const MiniVec< T, NDIM > points_range_min_vec, const MiniVec< T, NDIM > inv_voxel_size, int64_t num_voxels)
Definition VoxelizeSYCL.h:257
int64_t RunLengthEncodeSYCL(sycl::queue &queue, const int64_t *const keys, int64_t num_keys, int64_t *unique_keys_out, int64_t *unique_counts_out, const std::vector< sycl::event > &deps={})
Definition VoxelizeSYCL.h:156
sycl::event ComputeIndicesBatchesSYCL(sycl::queue &queue, int64_t *indices_batches, const int64_t *row_splits, int64_t batch_size)
Definition VoxelizeSYCL.h:72
sycl::event ComputeHashSYCL(sycl::queue &queue, int64_t *hashes, int64_t num_points, const T *const points, const int64_t *const indices_batches, const MiniVec< T, NDIM > points_range_min_vec, const MiniVec< T, NDIM > points_range_max_vec, const MiniVec< T, NDIM > inv_voxel_size, const MiniVec< int64_t, NDIM > strides, int64_t batch_hash, int64_t invalid_hash, const std::vector< sycl::event > &deps={})
Definition VoxelizeSYCL.h:92
void CopyPointIndicesSYCL(sycl::queue &queue, int64_t *out, const int64_t *const point_indices, const int64_t *const prefix_sum_in, const int64_t *const prefix_sum_out, int64_t num_voxels)
Definition VoxelizeSYCL.h:280
sycl::event LimitCountsSYCL(sycl::queue &queue, int64_t *counts, int64_t num, int64_t limit, const std::vector< sycl::event > &deps={})
Definition VoxelizeSYCL.h:128
T ReadScalar(sycl::queue &queue, const T *device_ptr, const std::vector< sycl::event > &deps={})
Definition VoxelizeSYCL.h:62
void ComputeVoxelPerBatchSYCL(sycl::queue &queue, int64_t *num_voxels_per_batch, const int64_t *unique_batches_count, const int64_t *unique_batches, int64_t num_batches)
Definition VoxelizeSYCL.h:196
void ComputeBatchIdSYCL(sycl::queue &queue, int64_t *hashes, int64_t num_voxels, int64_t batch_hash)
Definition VoxelizeSYCL.h:184
void VoxelizeSYCL(sycl::queue &queue, size_t num_points, const T *const points, const size_t batch_size, const int64_t *const row_splits, const T *const voxel_size, const T *const points_range_min, const T *const points_range_max, const int64_t max_points_per_voxel, const int64_t max_voxels, OUTPUT_ALLOCATOR &output_allocator)
Definition VoxelizeSYCL.h:310
Definition Dispatch.h:65
Definition PinholeCameraIntrinsic.cpp:16
Definition MiniVec.h:24