Open3D (C++ API)  0.20.0
Loading...
Searching...
No Matches
InterpolatePointsSYCL.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 three_nn / three_interpolate(_grad) — ports
9// InterpolatePoints.cu.
10//
11// ThreeNNSYCL: one work-group per (batch, query); work-items grid-stride over
12// candidates, merge per-lane top-3 in SLM, then work-item 0 merges to the
13// final top-3. Equal-distance tie-breaking differs from a single-work-item
14// serial scan (CUDA reference); python/test/ml_ops tests check distances and
15// the three neighbor indices, not CUDA's exact tie order. A serial-scan port
16// would match CUDA tie order but is not required today.
17//
18// three_interpolate / _grad: order-independent gather and atomic scatter
19// (InvertNeighborsList pattern), launched via core::ParallelFor.
20
21#pragma once
22
23#include <sycl/sycl.hpp>
24
26
27namespace open3d {
28namespace ml {
29namespace contrib {
30
31namespace {
32// Work-group size for ThreeNNSYCL: one work-group per query, work-items
33// grid-stride over the m candidate points. Best-guess default (matches the
34// work-group-per-output-point size used elsewhere in this codebase, e.g.
35// BallQuerySYCL.h/the conv FillColumn kernels); not yet tuned on target HW.
36constexpr size_t kThreeNNWGSize = 32;
37} // namespace
38
48inline void ThreeNNSYCL(sycl::queue& queue,
49 int b,
50 int n,
51 int m,
52 const float* const unknown,
53 const float* const known,
54 float* const dist2,
55 int* const idx) {
56 if (b <= 0 || n <= 0) return;
57 const size_t wg = kThreeNNWGSize;
58
59 queue.submit([&](sycl::handler& cgh) {
60 // Per-work-item local top-3 (value, index), staged here so work-item
61 // 0 can merge all work-items' candidates after the barrier below.
62 sycl::local_accessor<double, 1> local_best(3 * wg, cgh);
63 sycl::local_accessor<int, 1> local_besti(3 * wg, cgh);
64
65 cgh.parallel_for(
66 sycl::nd_range<1>(
67 sycl::range<1>(static_cast<size_t>(b) * n * wg),
68 sycl::range<1>(wg)),
69 // Distinct buffers — safe for [[intel::kernel_args_restrict]].
70 [=](sycl::nd_item<1> item) [[intel::kernel_args_restrict]] {
71 const size_t group_id = item.get_group(0);
72 const int bs_idx = static_cast<int>(group_id / n);
73 const int pt_idx = static_cast<int>(group_id % n);
74 const size_t lid = item.get_local_id(0);
75 auto group = item.get_group();
76
77 const float* const u =
78 unknown + bs_idx * n * 3 + pt_idx * 3;
79 const float* const kn = known + bs_idx * m * 3;
80
81 const float ux = u[0];
82 const float uy = u[1];
83 const float uz = u[2];
84
85 double best1 = 1e40, best2 = 1e40, best3 = 1e40;
86 int besti1 = 0, besti2 = 0, besti3 = 0;
87 for (int k = static_cast<int>(lid); k < m;
88 k += static_cast<int>(wg)) {
89 const float x = kn[k * 3 + 0];
90 const float y = kn[k * 3 + 1];
91 const float z = kn[k * 3 + 2];
92 const double d = double((ux - x) * (ux - x) +
93 (uy - y) * (uy - y) +
94 (uz - z) * (uz - z));
95 if (d < best1) {
96 best3 = best2;
97 besti3 = besti2;
98 best2 = best1;
99 besti2 = besti1;
100 best1 = d;
101 besti1 = k;
102 } else if (d < best2) {
103 best3 = best2;
104 besti3 = besti2;
105 best2 = d;
106 besti2 = k;
107 } else if (d < best3) {
108 best3 = d;
109 besti3 = k;
110 }
111 }
112
113 local_best[3 * lid + 0] = best1;
114 local_best[3 * lid + 1] = best2;
115 local_best[3 * lid + 2] = best3;
116 local_besti[3 * lid + 0] = besti1;
117 local_besti[3 * lid + 1] = besti2;
118 local_besti[3 * lid + 2] = besti3;
119 sycl::group_barrier(group);
120
121 if (lid != 0) return;
122
123 // Merge up to 3*wg per-work-item candidates (some may be
124 // the 1e40 sentinel if that work-item's slice had fewer
125 // than 3 candidates, e.g. when m < wg) into the final
126 // top-3, using the same insertion-style update as the
127 // per-work-item scan above.
128 float* const d2_out = dist2 + bs_idx * n * 3 + pt_idx * 3;
129 int* const idx_out = idx + bs_idx * n * 3 + pt_idx * 3;
130
131 double mbest1 = 1e40, mbest2 = 1e40, mbest3 = 1e40;
132 int mbesti1 = 0, mbesti2 = 0, mbesti3 = 0;
133 for (size_t c = 0; c < 3 * wg; ++c) {
134 const double d = local_best[c];
135 const int di = local_besti[c];
136 if (d < mbest1) {
137 mbest3 = mbest2;
138 mbesti3 = mbesti2;
139 mbest2 = mbest1;
140 mbesti2 = mbesti1;
141 mbest1 = d;
142 mbesti1 = di;
143 } else if (d < mbest2) {
144 mbest3 = mbest2;
145 mbesti3 = mbesti2;
146 mbest2 = d;
147 mbesti2 = di;
148 } else if (d < mbest3) {
149 mbest3 = d;
150 mbesti3 = di;
151 }
152 }
153 d2_out[0] = static_cast<float>(mbest1);
154 d2_out[1] = static_cast<float>(mbest2);
155 d2_out[2] = static_cast<float>(mbest3);
156 idx_out[0] = mbesti1;
157 idx_out[1] = mbesti2;
158 idx_out[2] = mbesti3;
159 });
160 });
161 queue.wait_and_throw();
162}
163
171inline void ThreeInterpolateSYCL(sycl::queue& queue,
172 int b,
173 int c,
174 int m,
175 int n,
176 const float* const points,
177 const int* const idx,
178 const float* const weight,
179 float* const out) {
180 if (b <= 0 || c <= 0 || n <= 0) return;
181
183 queue, static_cast<int64_t>(b) * c * n, [=](int64_t idx64) {
184 const int bs_idx = static_cast<int>(idx64 / (c * n));
185 const int rem = static_cast<int>(idx64 % (c * n));
186 const int c_idx = rem / n;
187 const int pt_idx = rem % n;
188
189 const float* const w = weight + bs_idx * n * 3 + pt_idx * 3;
190 const float* const pts = points + bs_idx * c * m + c_idx * m;
191 const int* const id = idx + bs_idx * n * 3 + pt_idx * 3;
192 float* const o = out + bs_idx * c * n + c_idx * n;
193
194 o[pt_idx] = w[0] * pts[id[0]] + w[1] * pts[id[1]] +
195 w[2] * pts[id[2]];
196 });
197}
198
203inline void ThreeInterpolateGradSYCL(sycl::queue& queue,
204 int b,
205 int c,
206 int n,
207 int m,
208 const float* const grad_out,
209 const int* const idx,
210 const float* const weight,
211 float* const grad_points) {
212 if (b <= 0 || c <= 0 || n <= 0) return;
213
215 queue, static_cast<int64_t>(b) * c * n, [=](int64_t idx64) {
216 const int bs_idx = static_cast<int>(idx64 / (c * n));
217 const int rem = static_cast<int>(idx64 % (c * n));
218 const int c_idx = rem / n;
219 const int pt_idx = rem % n;
220
221 const float g = grad_out[bs_idx * c * n + c_idx * n + pt_idx];
222 const float* const w = weight + bs_idx * n * 3 + pt_idx * 3;
223 const int* const id = idx + bs_idx * n * 3 + pt_idx * 3;
224 float* const gp = grad_points + bs_idx * c * m + c_idx * m;
225
226 for (int l = 0; l < 3; ++l) {
227 sycl::atomic_ref<float, sycl::memory_order::relaxed,
228 sycl::memory_scope::device,
229 sycl::access::address_space::global_space>
230 ref(gp[id[l]]);
231 ref.fetch_add(g * w[l]);
232 }
233 });
234}
235
236} // namespace contrib
237} // namespace ml
238} // namespace open3d
std::int64_t y
Definition NormalDistributionsTransform.cpp:43
std::int64_t x
Definition NormalDistributionsTransform.cpp:42
std::int64_t z
Definition NormalDistributionsTransform.cpp:44
sycl::queue queue
Definition SYCLContext.cpp:88
Real weight
Definition SurfaceReconstructionPoisson.cpp:270
int points
Definition FilePCD.cpp:55
void ParallelFor(const Device &device, int64_t n, const func_t &func)
Definition ParallelFor.h:190
void ThreeInterpolateGradSYCL(sycl::queue &queue, int b, int c, int n, int m, const float *const grad_out, const int *const idx, const float *const weight, float *const grad_points)
Definition InterpolatePointsSYCL.h:203
void ThreeInterpolateSYCL(sycl::queue &queue, int b, int c, int m, int n, const float *const points, const int *const idx, const float *const weight, float *const out)
Definition InterpolatePointsSYCL.h:171
void ThreeNNSYCL(sycl::queue &queue, int b, int n, int m, const float *const unknown, const float *const known, float *const dist2, int *const idx)
Definition InterpolatePointsSYCL.h:48
Definition PinholeCameraIntrinsic.cpp:16