Xenova HF Staff commited on
Commit
01356ce
·
verified ·
1 Parent(s): e9fc83c

sync 91d990483a17

Browse files
README.md CHANGED
@@ -18,16 +18,16 @@ See the [ONNX `GridSample` spec](https://onnx.ai/onnx/operators/onnx__GridSample
18
 
19
  ## Inputs
20
 
21
- | Name | Bind key | Logical dtype | Rank | Shape | Description | Presence |
22
  | --- | --- | --- | --- | --- | --- | --- |
23
- | `X` | `x` | `T` | — | — | Input tensor of shape `(N, C, D1, ..., Dr)` whose values are sampled. | required |
24
- | `grid` | `grid` | `T` | — | — | Flow-field of shape `(N, D1_out, ..., Dr_out, r)` with normalized sampling coordinates in `[-1, 1]`. | required |
25
 
26
  ## Outputs
27
 
28
- | Name | Bind key | Logical dtype | Rank | Shape | Description | Presence |
29
  | --- | --- | --- | --- | --- | --- | --- |
30
- | `Y` | `y` | `T` | same as `grid` | derived; see description | Output tensor of shape `(N, C, D1_out, ..., Dr_out)` containing the interpolated samples. | required |
31
 
32
  ## Attributes
33
 
@@ -35,9 +35,9 @@ Default values (overridable per request):
35
 
36
  | Attribute | Default | Description |
37
  | --- | --- | --- |
38
- | `mode` | `"linear"` | Interpolation method: `linear` (bilinear or trilinear, depending on rank), `nearest`, or `cubic`. Cubic interpolation is supported for rank-4 (2-D spatial) inputs. |
39
- | `padding_mode` | `"zeros"` | How out-of-bound grid positions are handled: `zeros` pads with 0, `border` clamps to the border value, or `reflection` reflects coordinates back into the valid range. |
40
  | `align_corners` | `0` | When 1, extrema values `-1` and `1` map to the center of the corner pixels; when 0 (default) they map to the outer edge of corner pixels, making sampling resolution-agnostic. |
 
 
41
 
42
  ## Type constraints
43
 
@@ -45,9 +45,16 @@ Default values (overridable per request):
45
  | --- | --- |
46
  | `T` | `float32`, `float16` |
47
 
 
 
 
 
 
 
 
48
  ## Files
49
 
50
- - [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, provenance)
51
  - [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
52
  - [`test.json`](build/webgpu/test.json) — correctness cases
53
  - [`bench.json`](build/webgpu/bench.json) — benchmark + tuning cases
@@ -56,10 +63,14 @@ Default values (overridable per request):
56
 
57
  ## Use with `@huggingface/kernels`
58
 
59
- The loader derives every required output's shape and logical dtype from the manifest contract and this call.
60
- It then allocates the result tensors automatically.
 
 
 
61
 
62
  The `version: 1` option selects the published kernel contract; it is independent of any operator opset, contrib `since_version`, or model version.
 
63
 
64
  Replace each `*Data` placeholder with a typed array containing the corresponding input data.
65
 
 
18
 
19
  ## Inputs
20
 
21
+ | Name | Upstream name | Logical dtype | Rank | Shape | Description | Presence |
22
  | --- | --- | --- | --- | --- | --- | --- |
23
+ | `x` | `X` | `T` | — | — | Input tensor of shape `(N, C, D1, ..., Dr)` whose values are sampled. | required |
24
+ | `grid` | | `T` | — | — | Flow-field of shape `(N, D1_out, ..., Dr_out, r)` with normalized sampling coordinates in `[-1, 1]`. | required |
25
 
26
  ## Outputs
27
 
28
+ | Name | Upstream name | Logical dtype | Rank | Shape | Description | Presence |
29
  | --- | --- | --- | --- | --- | --- | --- |
30
+ | `y` | `Y` | `T` | same as `grid` | derived | Output tensor of shape `(N, C, D1_out, ..., Dr_out)` containing the interpolated samples. | required |
31
 
32
  ## Attributes
33
 
 
35
 
36
  | Attribute | Default | Description |
37
  | --- | --- | --- |
 
 
38
  | `align_corners` | `0` | When 1, extrema values `-1` and `1` map to the center of the corner pixels; when 0 (default) they map to the outer edge of corner pixels, making sampling resolution-agnostic. |
39
+ | `mode` | `"linear"` | Interpolation method: `linear` (bilinear or trilinear, depending on rank), `nearest`, or `cubic` (bicubic for rank-4 inputs and tricubic for rank-5 inputs). |
40
+ | `padding_mode` | `"zeros"` | How out-of-bound grid positions are handled: `zeros` pads with 0, `border` clamps to the border value, or `reflection` reflects coordinates back into the valid range. |
41
 
42
  ## Type constraints
43
 
 
45
  | --- | --- |
46
  | `T` | `float32`, `float16` |
47
 
48
+ ## Implementation variants
49
+
50
+ One implementation is selected per call from the device capabilities, the request shapes and the dtypes; these notes say what each one covers.
51
+
52
+ - `ncdhw_rank5_channel_vector` — Shares volumetric coordinates, padding and interpolation across a vector of channels; uses two lanes for two channels and four lanes otherwise, with masked tails and device-capped workgroups.
53
+ - `ncdhw_rank5` — Portable scalar volumetric sampling for all interpolation and padding modes.
54
+
55
  ## Files
56
 
57
+ - [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, per-variant templates, provenance)
58
  - [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
59
  - [`test.json`](build/webgpu/test.json) — correctness cases
60
  - [`bench.json`](build/webgpu/bench.json) — benchmark + tuning cases
 
63
 
64
  ## Use with `@huggingface/kernels`
65
 
66
+ ```sh
67
+ npm install --save-exact @huggingface/kernels@0.0.1-preview.2
68
+ ```
69
+
70
+ Required output shapes and logical data types are inferred from the supplied inputs and attributes; result tensors are allocated automatically.
71
 
72
  The `version: 1` option selects the published kernel contract; it is independent of any operator opset, contrib `since_version`, or model version.
73
+ It follows the `v1` branch as fixes land. To pin exact artifact bytes, pass a 40-character commit `revision` instead of `version`.
74
 
75
  Replace each `*Data` placeholder with a typed array containing the corresponding input data.
76
 
build/webgpu/bench.json CHANGED
@@ -1,5 +1,4 @@
1
  {
2
- "op": "ai.onnx.GridSample",
3
  "cases": [
4
  {
5
  "name": "1x3x256x256_to_256_linear",
@@ -127,6 +126,292 @@
127
  "bench": {
128
  "metrics": [{ "type": "bandwidth", "value": "(numel(shapes.x) + numel(shapes.grid) + numel(shapes.y)) * 4" }]
129
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
  }
131
  ]
132
  }
 
1
  {
 
2
  "cases": [
3
  {
4
  "name": "1x3x256x256_to_256_linear",
 
126
  "bench": {
127
  "metrics": [{ "type": "bandwidth", "value": "(numel(shapes.x) + numel(shapes.grid) + numel(shapes.y)) * 4" }]
128
  }
129
+ },
130
+ {
131
+ "name": "volume_channels_linear_zeros_float32_c8",
132
+ "preset": "stress",
133
+ "attrs": { "mode": "linear", "padding_mode": "zeros", "align_corners": 0 },
134
+ "inputs": {
135
+ "x": { "dtype": "float32", "shape": [1, 8, 12, 16, 16], "dist": "normal", "scale": 0.3, "seed": 16300 },
136
+ "grid": { "dtype": "float32", "shape": [1, 24, 32, 32, 3], "dist": "normal", "scale": 0.9, "seed": 16400 }
137
+ },
138
+ "outputs": { "y": { "dtype": "float32", "shape": [1, 8, 24, 32, 32] } },
139
+ "bench": {
140
+ "metrics": [{ "type": "bandwidth", "value": "(numel(shapes.x) + numel(shapes.grid) + numel(shapes.y)) * 4" }]
141
+ }
142
+ },
143
+ {
144
+ "name": "volume_channels_linear_zeros_float16_c8",
145
+ "preset": "stress",
146
+ "attrs": { "mode": "linear", "padding_mode": "zeros", "align_corners": 1 },
147
+ "inputs": {
148
+ "x": { "dtype": "float16", "shape": [1, 8, 12, 16, 16], "dist": "normal", "scale": 0.3, "seed": 16301 },
149
+ "grid": { "dtype": "float16", "shape": [1, 24, 32, 32, 3], "dist": "normal", "scale": 0.9, "seed": 16401 }
150
+ },
151
+ "outputs": { "y": { "dtype": "float16", "shape": [1, 8, 24, 32, 32] } },
152
+ "bench": {
153
+ "metrics": [{ "type": "bandwidth", "value": "(numel(shapes.x) + numel(shapes.grid) + numel(shapes.y)) * 2" }]
154
+ }
155
+ },
156
+ {
157
+ "name": "volume_channels_linear_border_float32_c8",
158
+ "preset": "stress",
159
+ "attrs": { "mode": "linear", "padding_mode": "border", "align_corners": 0 },
160
+ "inputs": {
161
+ "x": { "dtype": "float32", "shape": [1, 8, 12, 16, 16], "dist": "normal", "scale": 0.3, "seed": 16302 },
162
+ "grid": { "dtype": "float32", "shape": [1, 24, 32, 32, 3], "dist": "normal", "scale": 1.5, "seed": 16402 }
163
+ },
164
+ "outputs": { "y": { "dtype": "float32", "shape": [1, 8, 24, 32, 32] } },
165
+ "bench": {
166
+ "metrics": [{ "type": "bandwidth", "value": "(numel(shapes.x) + numel(shapes.grid) + numel(shapes.y)) * 4" }]
167
+ }
168
+ },
169
+ {
170
+ "name": "volume_channels_linear_border_float16_c8",
171
+ "preset": "stress",
172
+ "attrs": { "mode": "linear", "padding_mode": "border", "align_corners": 1 },
173
+ "inputs": {
174
+ "x": { "dtype": "float16", "shape": [1, 8, 12, 16, 16], "dist": "normal", "scale": 0.3, "seed": 16303 },
175
+ "grid": { "dtype": "float16", "shape": [1, 24, 32, 32, 3], "dist": "normal", "scale": 1.5, "seed": 16403 }
176
+ },
177
+ "outputs": { "y": { "dtype": "float16", "shape": [1, 8, 24, 32, 32] } },
178
+ "bench": {
179
+ "metrics": [{ "type": "bandwidth", "value": "(numel(shapes.x) + numel(shapes.grid) + numel(shapes.y)) * 2" }]
180
+ }
181
+ },
182
+ {
183
+ "name": "volume_channels_linear_reflection_float32_c8",
184
+ "preset": "stress",
185
+ "attrs": { "mode": "linear", "padding_mode": "reflection", "align_corners": 0 },
186
+ "inputs": {
187
+ "x": { "dtype": "float32", "shape": [1, 8, 12, 16, 16], "dist": "normal", "scale": 0.3, "seed": 16304 },
188
+ "grid": { "dtype": "float32", "shape": [1, 24, 32, 32, 3], "dist": "normal", "scale": 1.5, "seed": 16404 }
189
+ },
190
+ "outputs": { "y": { "dtype": "float32", "shape": [1, 8, 24, 32, 32] } },
191
+ "bench": {
192
+ "metrics": [{ "type": "bandwidth", "value": "(numel(shapes.x) + numel(shapes.grid) + numel(shapes.y)) * 4" }]
193
+ }
194
+ },
195
+ {
196
+ "name": "volume_channels_linear_reflection_float16_c8",
197
+ "preset": "stress",
198
+ "attrs": { "mode": "linear", "padding_mode": "reflection", "align_corners": 1 },
199
+ "inputs": {
200
+ "x": { "dtype": "float16", "shape": [1, 8, 12, 16, 16], "dist": "normal", "scale": 0.3, "seed": 16305 },
201
+ "grid": { "dtype": "float16", "shape": [1, 24, 32, 32, 3], "dist": "normal", "scale": 1.5, "seed": 16405 }
202
+ },
203
+ "outputs": { "y": { "dtype": "float16", "shape": [1, 8, 24, 32, 32] } },
204
+ "bench": {
205
+ "metrics": [{ "type": "bandwidth", "value": "(numel(shapes.x) + numel(shapes.grid) + numel(shapes.y)) * 2" }]
206
+ }
207
+ },
208
+ {
209
+ "name": "volume_channels_nearest_zeros_float32_c8",
210
+ "preset": "stress",
211
+ "attrs": { "mode": "nearest", "padding_mode": "zeros", "align_corners": 0 },
212
+ "inputs": {
213
+ "x": { "dtype": "float32", "shape": [1, 8, 12, 16, 16], "dist": "normal", "scale": 0.3, "seed": 16306 },
214
+ "grid": { "dtype": "float32", "shape": [1, 24, 32, 32, 3], "dist": "normal", "scale": 0.9, "seed": 16406 }
215
+ },
216
+ "outputs": { "y": { "dtype": "float32", "shape": [1, 8, 24, 32, 32] } },
217
+ "bench": {
218
+ "metrics": [{ "type": "bandwidth", "value": "(numel(shapes.x) + numel(shapes.grid) + numel(shapes.y)) * 4" }]
219
+ }
220
+ },
221
+ {
222
+ "name": "volume_channels_nearest_zeros_float16_c8",
223
+ "preset": "stress",
224
+ "attrs": { "mode": "nearest", "padding_mode": "zeros", "align_corners": 1 },
225
+ "inputs": {
226
+ "x": { "dtype": "float16", "shape": [1, 8, 12, 16, 16], "dist": "normal", "scale": 0.3, "seed": 16307 },
227
+ "grid": { "dtype": "float16", "shape": [1, 24, 32, 32, 3], "dist": "normal", "scale": 0.9, "seed": 16407 }
228
+ },
229
+ "outputs": { "y": { "dtype": "float16", "shape": [1, 8, 24, 32, 32] } },
230
+ "bench": {
231
+ "metrics": [{ "type": "bandwidth", "value": "(numel(shapes.x) + numel(shapes.grid) + numel(shapes.y)) * 2" }]
232
+ }
233
+ },
234
+ {
235
+ "name": "volume_channels_nearest_border_float32_c8",
236
+ "preset": "stress",
237
+ "attrs": { "mode": "nearest", "padding_mode": "border", "align_corners": 0 },
238
+ "inputs": {
239
+ "x": { "dtype": "float32", "shape": [1, 8, 12, 16, 16], "dist": "normal", "scale": 0.3, "seed": 16308 },
240
+ "grid": { "dtype": "float32", "shape": [1, 24, 32, 32, 3], "dist": "normal", "scale": 1.5, "seed": 16408 }
241
+ },
242
+ "outputs": { "y": { "dtype": "float32", "shape": [1, 8, 24, 32, 32] } },
243
+ "bench": {
244
+ "metrics": [{ "type": "bandwidth", "value": "(numel(shapes.x) + numel(shapes.grid) + numel(shapes.y)) * 4" }]
245
+ }
246
+ },
247
+ {
248
+ "name": "volume_channels_nearest_border_float16_c8",
249
+ "preset": "stress",
250
+ "attrs": { "mode": "nearest", "padding_mode": "border", "align_corners": 1 },
251
+ "inputs": {
252
+ "x": { "dtype": "float16", "shape": [1, 8, 12, 16, 16], "dist": "normal", "scale": 0.3, "seed": 16309 },
253
+ "grid": { "dtype": "float16", "shape": [1, 24, 32, 32, 3], "dist": "normal", "scale": 1.5, "seed": 16409 }
254
+ },
255
+ "outputs": { "y": { "dtype": "float16", "shape": [1, 8, 24, 32, 32] } },
256
+ "bench": {
257
+ "metrics": [{ "type": "bandwidth", "value": "(numel(shapes.x) + numel(shapes.grid) + numel(shapes.y)) * 2" }]
258
+ }
259
+ },
260
+ {
261
+ "name": "volume_channels_nearest_reflection_float32_c8",
262
+ "preset": "stress",
263
+ "attrs": { "mode": "nearest", "padding_mode": "reflection", "align_corners": 0 },
264
+ "inputs": {
265
+ "x": { "dtype": "float32", "shape": [1, 8, 12, 16, 16], "dist": "normal", "scale": 0.3, "seed": 16310 },
266
+ "grid": { "dtype": "float32", "shape": [1, 24, 32, 32, 3], "dist": "normal", "scale": 1.5, "seed": 16410 }
267
+ },
268
+ "outputs": { "y": { "dtype": "float32", "shape": [1, 8, 24, 32, 32] } },
269
+ "bench": {
270
+ "metrics": [{ "type": "bandwidth", "value": "(numel(shapes.x) + numel(shapes.grid) + numel(shapes.y)) * 4" }]
271
+ }
272
+ },
273
+ {
274
+ "name": "volume_channels_nearest_reflection_float16_c8",
275
+ "preset": "stress",
276
+ "attrs": { "mode": "nearest", "padding_mode": "reflection", "align_corners": 1 },
277
+ "inputs": {
278
+ "x": { "dtype": "float16", "shape": [1, 8, 12, 16, 16], "dist": "normal", "scale": 0.3, "seed": 16311 },
279
+ "grid": { "dtype": "float16", "shape": [1, 24, 32, 32, 3], "dist": "normal", "scale": 1.5, "seed": 16411 }
280
+ },
281
+ "outputs": { "y": { "dtype": "float16", "shape": [1, 8, 24, 32, 32] } },
282
+ "bench": {
283
+ "metrics": [{ "type": "bandwidth", "value": "(numel(shapes.x) + numel(shapes.grid) + numel(shapes.y)) * 2" }]
284
+ }
285
+ },
286
+ {
287
+ "name": "volume_channels_cubic_zeros_float32_c8",
288
+ "preset": "stress",
289
+ "attrs": { "mode": "cubic", "padding_mode": "zeros", "align_corners": 0 },
290
+ "inputs": {
291
+ "x": { "dtype": "float32", "shape": [1, 8, 12, 16, 16], "dist": "normal", "scale": 0.3, "seed": 16312 },
292
+ "grid": { "dtype": "float32", "shape": [1, 24, 32, 32, 3], "dist": "normal", "scale": 0.9, "seed": 16412 }
293
+ },
294
+ "outputs": { "y": { "dtype": "float32", "shape": [1, 8, 24, 32, 32] } },
295
+ "bench": {
296
+ "metrics": [{ "type": "bandwidth", "value": "(numel(shapes.x) + numel(shapes.grid) + numel(shapes.y)) * 4" }]
297
+ }
298
+ },
299
+ {
300
+ "name": "volume_channels_cubic_zeros_float16_c8",
301
+ "preset": "stress",
302
+ "attrs": { "mode": "cubic", "padding_mode": "zeros", "align_corners": 1 },
303
+ "inputs": {
304
+ "x": { "dtype": "float16", "shape": [1, 8, 12, 16, 16], "dist": "normal", "scale": 0.3, "seed": 16313 },
305
+ "grid": { "dtype": "float16", "shape": [1, 24, 32, 32, 3], "dist": "normal", "scale": 0.9, "seed": 16413 }
306
+ },
307
+ "outputs": { "y": { "dtype": "float16", "shape": [1, 8, 24, 32, 32] } },
308
+ "bench": {
309
+ "metrics": [{ "type": "bandwidth", "value": "(numel(shapes.x) + numel(shapes.grid) + numel(shapes.y)) * 2" }]
310
+ }
311
+ },
312
+ {
313
+ "name": "volume_channels_cubic_border_float32_c8",
314
+ "preset": "stress",
315
+ "attrs": { "mode": "cubic", "padding_mode": "border", "align_corners": 0 },
316
+ "inputs": {
317
+ "x": { "dtype": "float32", "shape": [1, 8, 12, 16, 16], "dist": "normal", "scale": 0.3, "seed": 16314 },
318
+ "grid": { "dtype": "float32", "shape": [1, 24, 32, 32, 3], "dist": "normal", "scale": 1.5, "seed": 16414 }
319
+ },
320
+ "outputs": { "y": { "dtype": "float32", "shape": [1, 8, 24, 32, 32] } },
321
+ "bench": {
322
+ "metrics": [{ "type": "bandwidth", "value": "(numel(shapes.x) + numel(shapes.grid) + numel(shapes.y)) * 4" }]
323
+ }
324
+ },
325
+ {
326
+ "name": "volume_channels_cubic_border_float16_c8",
327
+ "preset": "stress",
328
+ "attrs": { "mode": "cubic", "padding_mode": "border", "align_corners": 1 },
329
+ "inputs": {
330
+ "x": { "dtype": "float16", "shape": [1, 8, 12, 16, 16], "dist": "normal", "scale": 0.3, "seed": 16315 },
331
+ "grid": { "dtype": "float16", "shape": [1, 24, 32, 32, 3], "dist": "normal", "scale": 1.5, "seed": 16415 }
332
+ },
333
+ "outputs": { "y": { "dtype": "float16", "shape": [1, 8, 24, 32, 32] } },
334
+ "bench": {
335
+ "metrics": [{ "type": "bandwidth", "value": "(numel(shapes.x) + numel(shapes.grid) + numel(shapes.y)) * 2" }]
336
+ }
337
+ },
338
+ {
339
+ "name": "volume_channels_cubic_reflection_float32_c8",
340
+ "preset": "stress",
341
+ "attrs": { "mode": "cubic", "padding_mode": "reflection", "align_corners": 0 },
342
+ "inputs": {
343
+ "x": { "dtype": "float32", "shape": [1, 8, 12, 16, 16], "dist": "normal", "scale": 0.3, "seed": 16316 },
344
+ "grid": { "dtype": "float32", "shape": [1, 24, 32, 32, 3], "dist": "normal", "scale": 1.5, "seed": 16416 }
345
+ },
346
+ "outputs": { "y": { "dtype": "float32", "shape": [1, 8, 24, 32, 32] } },
347
+ "bench": {
348
+ "metrics": [{ "type": "bandwidth", "value": "(numel(shapes.x) + numel(shapes.grid) + numel(shapes.y)) * 4" }]
349
+ }
350
+ },
351
+ {
352
+ "name": "volume_channels_cubic_reflection_float16_c8",
353
+ "preset": "stress",
354
+ "attrs": { "mode": "cubic", "padding_mode": "reflection", "align_corners": 1 },
355
+ "inputs": {
356
+ "x": { "dtype": "float16", "shape": [1, 8, 12, 16, 16], "dist": "normal", "scale": 0.3, "seed": 16317 },
357
+ "grid": { "dtype": "float16", "shape": [1, 24, 32, 32, 3], "dist": "normal", "scale": 1.5, "seed": 16417 }
358
+ },
359
+ "outputs": { "y": { "dtype": "float16", "shape": [1, 8, 24, 32, 32] } },
360
+ "bench": {
361
+ "metrics": [{ "type": "bandwidth", "value": "(numel(shapes.x) + numel(shapes.grid) + numel(shapes.y)) * 2" }]
362
+ }
363
+ },
364
+ {
365
+ "name": "volume_channels_linear_reflection_float32_c2",
366
+ "preset": "stress",
367
+ "attrs": { "mode": "linear", "padding_mode": "reflection", "align_corners": 0 },
368
+ "inputs": {
369
+ "x": { "dtype": "float32", "shape": [1, 2, 12, 16, 16], "dist": "normal", "scale": 0.3, "seed": 16318 },
370
+ "grid": { "dtype": "float32", "shape": [1, 24, 32, 32, 3], "dist": "normal", "scale": 1.5, "seed": 16418 }
371
+ },
372
+ "outputs": { "y": { "dtype": "float32", "shape": [1, 2, 24, 32, 32] } },
373
+ "bench": {
374
+ "metrics": [{ "type": "bandwidth", "value": "(numel(shapes.x) + numel(shapes.grid) + numel(shapes.y)) * 4" }]
375
+ }
376
+ },
377
+ {
378
+ "name": "volume_channels_linear_reflection_float32_c3",
379
+ "preset": "stress",
380
+ "attrs": { "mode": "linear", "padding_mode": "reflection", "align_corners": 1 },
381
+ "inputs": {
382
+ "x": { "dtype": "float32", "shape": [1, 3, 12, 16, 16], "dist": "normal", "scale": 0.3, "seed": 16319 },
383
+ "grid": { "dtype": "float32", "shape": [1, 24, 32, 32, 3], "dist": "normal", "scale": 1.5, "seed": 16419 }
384
+ },
385
+ "outputs": { "y": { "dtype": "float32", "shape": [1, 3, 24, 32, 32] } },
386
+ "bench": {
387
+ "metrics": [{ "type": "bandwidth", "value": "(numel(shapes.x) + numel(shapes.grid) + numel(shapes.y)) * 4" }]
388
+ }
389
+ },
390
+ {
391
+ "name": "volume_channels_linear_reflection_float32_c5",
392
+ "preset": "stress",
393
+ "attrs": { "mode": "linear", "padding_mode": "reflection", "align_corners": 0 },
394
+ "inputs": {
395
+ "x": { "dtype": "float32", "shape": [1, 5, 12, 16, 16], "dist": "normal", "scale": 0.3, "seed": 16320 },
396
+ "grid": { "dtype": "float32", "shape": [1, 24, 32, 32, 3], "dist": "normal", "scale": 1.5, "seed": 16420 }
397
+ },
398
+ "outputs": { "y": { "dtype": "float32", "shape": [1, 5, 24, 32, 32] } },
399
+ "bench": {
400
+ "metrics": [{ "type": "bandwidth", "value": "(numel(shapes.x) + numel(shapes.grid) + numel(shapes.y)) * 4" }]
401
+ }
402
+ },
403
+ {
404
+ "name": "volume_channels_linear_reflection_float32_c16",
405
+ "preset": "stress",
406
+ "attrs": { "mode": "linear", "padding_mode": "reflection", "align_corners": 1 },
407
+ "inputs": {
408
+ "x": { "dtype": "float32", "shape": [1, 16, 12, 16, 16], "dist": "normal", "scale": 0.3, "seed": 16321 },
409
+ "grid": { "dtype": "float32", "shape": [1, 24, 32, 32, 3], "dist": "normal", "scale": 1.5, "seed": 16421 }
410
+ },
411
+ "outputs": { "y": { "dtype": "float32", "shape": [1, 16, 24, 32, 32] } },
412
+ "bench": {
413
+ "metrics": [{ "type": "bandwidth", "value": "(numel(shapes.x) + numel(shapes.grid) + numel(shapes.y)) * 4" }]
414
+ }
415
  }
416
  ]
417
  }
build/webgpu/grid-sample.wgsl.jinja CHANGED
@@ -1,43 +1,40 @@
1
  {% macro flat_index_2d(name="i", bound="params.count", guardInline=false, note="dispatch-limit") %}
2
  {% if note == "dispatch-limit" %}
3
- // 2D-folded flat index: gid.y carries the high bits past the
4
- // maxComputeWorkgroupsPerDimension dispatch limit (outputs > 16.7M elements).
5
  {% elif note == "limit" %}
6
- // 2D-folded flat index: gid.y carries the high bits past the
7
- // maxComputeWorkgroupsPerDimension limit.
8
  {% elif note == "device-axis" %}
9
- // The flat dispatch is folded across x/y at the device's per-axis workgroup
10
- // limit; gid.y carries the high portion of the output index.
11
  {% elif note == "vec4-limit" %}
12
- // 2D-folded flat vec4 index: gid.y carries the high bits past the
13
- // maxComputeWorkgroupsPerDimension limit (the dispatch caps x and spills into y).
14
  {% elif note == "element-limit" %}
15
  // 2D-folded flat element index: gid.y carries the high bits past the
16
- // maxComputeWorkgroupsPerDimension limit.
17
  {% elif note == "dispatch" %}
18
- // 2D-folded flat index: gid.y carries the high bits past the
19
- // maxComputeWorkgroupsPerDimension dispatch limit.
20
  {% endif %}
21
  {% if bound == "" %}
22
- let {{ name }} = gid.x + gid.y * nwg.x * {{ tunables.WORKGROUP_SIZE }}u;
23
  {%- elif guardInline %}
24
- let {{ name }} = gid.x + gid.y * nwg.x * {{ tunables.WORKGROUP_SIZE }}u;
25
  if ({{ name }} >= {{ bound }}) { return; }
26
  {%- else %}
27
- let {{ name }} = gid.x + gid.y * nwg.x * {{ tunables.WORKGROUP_SIZE }}u;
28
  if ({{ name }} >= {{ bound }}) {
29
  return;
30
  }
31
  {%- endif %}
32
  {% endmacro %}
33
 
34
- {% if usesF16 %}
35
- enable f16;
36
- {% endif %}
37
  {{ env.wgsl.resourceDeclarations }}
38
 
39
  fn denormalize(n: f32, length: u32) -> f32 {
40
- {% if source.alignCorners %}
41
  return (n + 1.0) * 0.5 * f32(length - 1u);
42
  {% else %}
43
  return ((n + 1.0) * f32(length) - 1.0) * 0.5;
@@ -46,7 +43,7 @@ fn denormalize(n: f32, length: u32) -> f32 {
46
 
47
  fn sanitize_coord(v: f32) -> f32 {
48
  if (!(v >= -2147483000.0 && v <= 2147483000.0)) {
49
- {% if source.alignCorners %}
50
  return 0.0;
51
  {% else %}
52
  return -0.5;
@@ -54,7 +51,7 @@ fn sanitize_coord(v: f32) -> f32 {
54
  }
55
  return v;
56
  }
57
- {% if source.paddingMode == "reflection" %}
58
 
59
  fn reflect_coord(v: f32, lo: f32, hi: f32) -> f32 {
60
  let range = hi - lo;
@@ -77,17 +74,17 @@ fn reflect_coord(v: f32, lo: f32, hi: f32) -> f32 {
77
  }
78
 
79
  {% endif %}
80
- {% if source.mode != "nearest" %}
81
  fn sample_floor(v: f32) -> i32 {
82
  if (v > 2147483000.0) {
83
- {% if source.paddingMode == "reflection" %}
84
  return 0;
85
  {% else %}
86
  return 2147483000;
87
  {% endif %}
88
  }
89
  if (v < -2147483000.0) {
90
- {% if source.paddingMode == "reflection" %}
91
  return 0;
92
  {% else %}
93
  return -2147483000;
@@ -98,14 +95,14 @@ fn sample_floor(v: f32) -> i32 {
98
  {%- else %}
99
  fn sample_round(v: f32) -> i32 {
100
  if (v > 2147483000.0) {
101
- {% if source.paddingMode == "reflection" %}
102
  return 0;
103
  {% else %}
104
  return 2147483000;
105
  {% endif %}
106
  }
107
  if (v < -2147483000.0) {
108
- {% if source.paddingMode == "reflection" %}
109
  return 0;
110
  {% else %}
111
  return -2147483000;
@@ -116,7 +113,7 @@ fn sample_round(v: f32) -> i32 {
116
  {%- endif %}
117
 
118
 
119
- {% if source.mode == "cubic" %}
120
  fn cubic_coeffs(t: f32) -> vec4<f32> {
121
  let a = -0.75;
122
  let x0 = abs(t + 1.0);
@@ -133,17 +130,17 @@ fn cubic_one(x0: f32, a: f32) -> f32 {
133
  }
134
 
135
  {% endif -%}
136
- {% if source.channelWidth is not defined %}
137
  fn pixel(base: u32, h: i32, w: i32) -> f32 {
138
- {% if source.paddingMode == "zeros" %}
139
  if (h < 0 || h >= i32(params.inH) || w < 0 || w >= i32(params.inW)) { return 0.0; }
140
  let hh = u32(h);
141
  let ww = u32(w);
142
- {% elif source.paddingMode == "border" %}
143
  let hh = u32(clamp(h, 0, i32(params.inH) - 1));
144
  let ww = u32(clamp(w, 0, i32(params.inW) - 1));
145
  {% else %}
146
- {% if source.alignCorners %}
147
  let rh = i32(reflect_coord(f32(h), 0.0, f32(params.inH) - 1.0));
148
  let rw = i32(reflect_coord(f32(w), 0.0, f32(params.inW) - 1.0));
149
  {% else %}
@@ -158,22 +155,22 @@ fn pixel(base: u32, h: i32, w: i32) -> f32 {
158
 
159
 
160
  {% endif %}
161
- {% if source.channelWidth is defined %}
162
- {% set channelVec = "vec" ~ source.channelWidth ~ "<f32>" %}
163
  {% set components = ["x", "y", "z", "w"] %}
164
  // A block of NCHW channels shares one grid coordinate. Tail lanes alias the
165
  // final channel safely because main suppresses their stores.
166
- {% if source.paddingMode != "reflection" or source.mode == "nearest" %}
167
  fn pixel_channels(n: u32, c0: u32, h: i32, w: i32) -> {{ channelVec }} {
168
- {% if source.paddingMode == "zeros" %}
169
  if (h < 0 || h >= i32(params.inH) || w < 0 || w >= i32(params.inW)) { return {{ channelVec }}(0.0); }
170
  let hh = u32(h);
171
  let ww = u32(w);
172
- {% elif source.paddingMode == "border" %}
173
  let hh = u32(clamp(h, 0, i32(params.inH) - 1));
174
  let ww = u32(clamp(w, 0, i32(params.inW) - 1));
175
  {% else %}
176
- {% if source.alignCorners %}
177
  let rh = i32(reflect_coord(f32(h), 0.0, f32(params.inH) - 1.0));
178
  let rw = i32(reflect_coord(f32(w), 0.0, f32(params.inW) - 1.0));
179
  {% else %}
@@ -187,23 +184,23 @@ fn pixel_channels(n: u32, c0: u32, h: i32, w: i32) -> {{ channelVec }} {
187
  let offset = hh * params.inW + ww;
188
  let nb = n * params.C;
189
  return {{ channelVec }}(
190
- {% for lane in range(source.channelWidth) %}
191
- f32(x[(nb + {% if source.channelTail %}min(c0 + {{ lane }}u, params.C - 1u){% else %}c0 + {{ lane }}u{% endif %}) * plane + offset]){% if not loop.last %},{% else %});{% endif %}
192
  {% endfor %}
193
  }
194
 
195
  {% endif %}
196
- {% if source.paddingMode == "reflection" %}
197
  // Reflection gives every tap in one row or column the same resolved coordinate.
198
  // Resolve each row and column once in main and reuse it for all channel loads.
199
- {% if source.mode != "nearest" %}
200
  fn pixel_channels_resolved(n: u32, c0: u32, h: u32, w: u32) -> {{ channelVec }} {
201
  let plane = params.inH * params.inW;
202
  let offset = h * params.inW + w;
203
  let nb = n * params.C;
204
  return {{ channelVec }}(
205
- {% for lane in range(source.channelWidth) %}
206
- f32(x[(nb + {% if source.channelTail %}min(c0 + {{ lane }}u, params.C - 1u){% else %}c0 + {{ lane }}u{% endif %}) * plane + offset]){% if not loop.last %},{% else %});{% endif %}
207
  {% endfor %}
208
  }
209
 
@@ -213,34 +210,34 @@ fn pixel_channels_resolved(n: u32, c0: u32, h: u32, w: u32) -> {{ channelVec }}
213
  {% endif %}
214
  {% endif -%}
215
  @compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
216
- fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups) nwg: vec3<u32>) {
217
  {{ flat_index_2d("i", "") }}
218
- {% if source.channelWidth is defined %}
219
  let out_plane = params.outH * params.outW;
220
- let channel_blocks = (params.C + {{ source.channelWidth - 1 }}u) / {{ source.channelWidth }}u;
221
  let block_count = (params.count / params.C) * channel_blocks;
222
  if (i >= block_count) { return; }
223
  let spatial = i % out_plane;
224
  let cb = (i / out_plane) % channel_blocks;
225
  let n = i / (out_plane * channel_blocks);
226
- let c = cb * {{ source.channelWidth }}u;
227
  let grid_base = (n * out_plane + spatial) * 2u;
228
  let sx = sanitize_coord(denormalize(f32(grid[grid_base]), params.inW));
229
  let sy = sanitize_coord(denormalize(f32(grid[grid_base + 1u]), params.inH));
230
- {% if source.mode == "nearest" %}
231
  let result = pixel_channels(n, c, sample_round(sy), sample_round(sx));
232
- {% elif source.mode == "cubic" %}
233
  let x0 = sample_floor(sx) - 1;
234
  let y0 = sample_floor(sy) - 1;
235
  let dx = sx - f32(x0 + 1);
236
  let dy = sy - f32(y0 + 1);
237
  let cx = cubic_coeffs(dx);
238
  let cy = cubic_coeffs(dy);
239
- {% if source.paddingMode == "reflection" %}
240
  var reflected_y: array<u32, 4>;
241
  var reflected_x: array<u32, 4>;
242
  for (var k = 0u; k < 4u; k = k + 1u) {
243
- {% if source.alignCorners %}
244
  reflected_y[k] = u32(clamp(i32(reflect_coord(f32(y0 + i32(k)), 0.0, f32(params.inH) - 1.0)), 0, i32(params.inH) - 1));
245
  reflected_x[k] = u32(clamp(i32(reflect_coord(f32(x0 + i32(k)), 0.0, f32(params.inW) - 1.0)), 0, i32(params.inW) - 1));
246
  {% else %}
@@ -251,7 +248,7 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups)
251
  {% endif %}
252
  var result = {{ channelVec }}(0.0);
253
  for (var r = 0i; r < 4i; r = r + 1i) {
254
- {% if source.paddingMode == "reflection" %}
255
  let row = cx.x * pixel_channels_resolved(n, c, reflected_y[u32(r)], reflected_x[0])
256
  + cx.y * pixel_channels_resolved(n, c, reflected_y[u32(r)], reflected_x[1])
257
  + cx.z * pixel_channels_resolved(n, c, reflected_y[u32(r)], reflected_x[2])
@@ -269,8 +266,8 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups)
269
  let y0 = sample_floor(sy);
270
  let wx = sx - f32(x0);
271
  let wy = sy - f32(y0);
272
- {% if source.paddingMode == "reflection" %}
273
- {% if source.alignCorners %}
274
  let reflected_y0 = u32(clamp(i32(reflect_coord(f32(y0), 0.0, f32(params.inH) - 1.0)), 0, i32(params.inH) - 1));
275
  let reflected_y1 = u32(clamp(i32(reflect_coord(f32(y0 + 1), 0.0, f32(params.inH) - 1.0)), 0, i32(params.inH) - 1));
276
  let reflected_x0 = u32(clamp(i32(reflect_coord(f32(x0), 0.0, f32(params.inW) - 1.0)), 0, i32(params.inW) - 1));
@@ -295,8 +292,8 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups)
295
  + wy * ((1.0 - wx) * v10 + wx * v11);
296
  {% endif %}
297
  let out_base = (n * params.C + c) * out_plane + spatial;
298
- {% for lane in range(source.channelWidth) %}
299
- {% if source.channelTail and lane > 0 %}
300
  if (c + {{ lane }}u < params.C) { y[out_base + {{ lane }}u * out_plane] = {{ scalar }}(result.{{ components[lane] }}); }
301
  {% else %}
302
  y[out_base + {{ lane }}u * out_plane] = {{ scalar }}(result.{{ components[lane] }});
@@ -312,9 +309,9 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups)
312
  let sx = sanitize_coord(denormalize(f32(grid[grid_base]), params.inW));
313
  let sy = sanitize_coord(denormalize(f32(grid[grid_base + 1u]), params.inH));
314
  let img_base = (n * params.C + c) * params.inH * params.inW;
315
- {% if source.mode == "nearest" %}
316
  let result = pixel(img_base, sample_round(sy), sample_round(sx));
317
- {% elif source.mode == "cubic" %}
318
  let x0 = sample_floor(sx) - 1;
319
  let y0 = sample_floor(sy) - 1;
320
  let dx = sx - f32(x0 + 1);
 
1
  {% macro flat_index_2d(name="i", bound="params.count", guardInline=false, note="dispatch-limit") %}
2
  {% if note == "dispatch-limit" %}
3
+ // 2D-folded flat index: gid.y carries the high bits past the dispatch's
4
+ // per-axis workgroup fold width (outputs > 16.7M elements).
5
  {% elif note == "limit" %}
6
+ // 2D-folded flat index: gid.y carries the high bits past the dispatch's
7
+ // per-axis workgroup fold width.
8
  {% elif note == "device-axis" %}
9
+ // The flat dispatch is folded across x/y at a fixed per-axis workgroup
10
+ // width; gid.y carries the high portion of the output index.
11
  {% elif note == "vec4-limit" %}
12
+ // 2D-folded flat vec4 index: gid.y carries the high bits past the dispatch's
13
+ // per-axis workgroup fold width (the dispatch caps x and spills into y).
14
  {% elif note == "element-limit" %}
15
  // 2D-folded flat element index: gid.y carries the high bits past the
16
+ // dispatch's per-axis workgroup fold width.
17
  {% elif note == "dispatch" %}
18
+ // 2D-folded flat index: gid.y carries the high bits past the dispatch's
19
+ // per-axis workgroup fold width.
20
  {% endif %}
21
  {% if bound == "" %}
22
+ let {{ name }} = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ tunables.WORKGROUP_SIZE }}u;
23
  {%- elif guardInline %}
24
+ let {{ name }} = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ tunables.WORKGROUP_SIZE }}u;
25
  if ({{ name }} >= {{ bound }}) { return; }
26
  {%- else %}
27
+ let {{ name }} = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ tunables.WORKGROUP_SIZE }}u;
28
  if ({{ name }} >= {{ bound }}) {
29
  return;
30
  }
31
  {%- endif %}
32
  {% endmacro %}
33
 
 
 
 
34
  {{ env.wgsl.resourceDeclarations }}
35
 
36
  fn denormalize(n: f32, length: u32) -> f32 {
37
+ {% if alignCorners %}
38
  return (n + 1.0) * 0.5 * f32(length - 1u);
39
  {% else %}
40
  return ((n + 1.0) * f32(length) - 1.0) * 0.5;
 
43
 
44
  fn sanitize_coord(v: f32) -> f32 {
45
  if (!(v >= -2147483000.0 && v <= 2147483000.0)) {
46
+ {% if alignCorners %}
47
  return 0.0;
48
  {% else %}
49
  return -0.5;
 
51
  }
52
  return v;
53
  }
54
+ {% if paddingMode == "reflection" %}
55
 
56
  fn reflect_coord(v: f32, lo: f32, hi: f32) -> f32 {
57
  let range = hi - lo;
 
74
  }
75
 
76
  {% endif %}
77
+ {% if modeSpec != "nearest" %}
78
  fn sample_floor(v: f32) -> i32 {
79
  if (v > 2147483000.0) {
80
+ {% if paddingMode == "reflection" %}
81
  return 0;
82
  {% else %}
83
  return 2147483000;
84
  {% endif %}
85
  }
86
  if (v < -2147483000.0) {
87
+ {% if paddingMode == "reflection" %}
88
  return 0;
89
  {% else %}
90
  return -2147483000;
 
95
  {%- else %}
96
  fn sample_round(v: f32) -> i32 {
97
  if (v > 2147483000.0) {
98
+ {% if paddingMode == "reflection" %}
99
  return 0;
100
  {% else %}
101
  return 2147483000;
102
  {% endif %}
103
  }
104
  if (v < -2147483000.0) {
105
+ {% if paddingMode == "reflection" %}
106
  return 0;
107
  {% else %}
108
  return -2147483000;
 
113
  {%- endif %}
114
 
115
 
116
+ {% if modeSpec == "cubic" %}
117
  fn cubic_coeffs(t: f32) -> vec4<f32> {
118
  let a = -0.75;
119
  let x0 = abs(t + 1.0);
 
130
  }
131
 
132
  {% endif -%}
133
+ {% if channelWidthSpec is not defined %}
134
  fn pixel(base: u32, h: i32, w: i32) -> f32 {
135
+ {% if paddingMode == "zeros" %}
136
  if (h < 0 || h >= i32(params.inH) || w < 0 || w >= i32(params.inW)) { return 0.0; }
137
  let hh = u32(h);
138
  let ww = u32(w);
139
+ {% elif paddingMode == "border" %}
140
  let hh = u32(clamp(h, 0, i32(params.inH) - 1));
141
  let ww = u32(clamp(w, 0, i32(params.inW) - 1));
142
  {% else %}
143
+ {% if alignCorners %}
144
  let rh = i32(reflect_coord(f32(h), 0.0, f32(params.inH) - 1.0));
145
  let rw = i32(reflect_coord(f32(w), 0.0, f32(params.inW) - 1.0));
146
  {% else %}
 
155
 
156
 
157
  {% endif %}
158
+ {% if channelWidthSpec is defined %}
159
+ {% set channelVec = "vec" ~ channelWidthSpec ~ "<f32>" %}
160
  {% set components = ["x", "y", "z", "w"] %}
161
  // A block of NCHW channels shares one grid coordinate. Tail lanes alias the
162
  // final channel safely because main suppresses their stores.
163
+ {% if paddingMode != "reflection" or modeSpec == "nearest" %}
164
  fn pixel_channels(n: u32, c0: u32, h: i32, w: i32) -> {{ channelVec }} {
165
+ {% if paddingMode == "zeros" %}
166
  if (h < 0 || h >= i32(params.inH) || w < 0 || w >= i32(params.inW)) { return {{ channelVec }}(0.0); }
167
  let hh = u32(h);
168
  let ww = u32(w);
169
+ {% elif paddingMode == "border" %}
170
  let hh = u32(clamp(h, 0, i32(params.inH) - 1));
171
  let ww = u32(clamp(w, 0, i32(params.inW) - 1));
172
  {% else %}
173
+ {% if alignCorners %}
174
  let rh = i32(reflect_coord(f32(h), 0.0, f32(params.inH) - 1.0));
175
  let rw = i32(reflect_coord(f32(w), 0.0, f32(params.inW) - 1.0));
176
  {% else %}
 
184
  let offset = hh * params.inW + ww;
185
  let nb = n * params.C;
186
  return {{ channelVec }}(
187
+ {% for lane in range(channelWidthSpec) %}
188
+ f32(x[(nb + {% if channelTail %}min(c0 + {{ lane }}u, params.C - 1u){% else %}c0 + {{ lane }}u{% endif %}) * plane + offset]){% if not loop.last %},{% else %});{% endif %}
189
  {% endfor %}
190
  }
191
 
192
  {% endif %}
193
+ {% if paddingMode == "reflection" %}
194
  // Reflection gives every tap in one row or column the same resolved coordinate.
195
  // Resolve each row and column once in main and reuse it for all channel loads.
196
+ {% if modeSpec != "nearest" %}
197
  fn pixel_channels_resolved(n: u32, c0: u32, h: u32, w: u32) -> {{ channelVec }} {
198
  let plane = params.inH * params.inW;
199
  let offset = h * params.inW + w;
200
  let nb = n * params.C;
201
  return {{ channelVec }}(
202
+ {% for lane in range(channelWidthSpec) %}
203
+ f32(x[(nb + {% if channelTail %}min(c0 + {{ lane }}u, params.C - 1u){% else %}c0 + {{ lane }}u{% endif %}) * plane + offset]){% if not loop.last %},{% else %});{% endif %}
204
  {% endfor %}
205
  }
206
 
 
210
  {% endif %}
211
  {% endif -%}
212
  @compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
213
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
214
  {{ flat_index_2d("i", "") }}
215
+ {% if channelWidthSpec is defined %}
216
  let out_plane = params.outH * params.outW;
217
+ let channel_blocks = (params.C + {{ channelWidthSpec - 1 }}u) / {{ channelWidthSpec }}u;
218
  let block_count = (params.count / params.C) * channel_blocks;
219
  if (i >= block_count) { return; }
220
  let spatial = i % out_plane;
221
  let cb = (i / out_plane) % channel_blocks;
222
  let n = i / (out_plane * channel_blocks);
223
+ let c = cb * {{ channelWidthSpec }}u;
224
  let grid_base = (n * out_plane + spatial) * 2u;
225
  let sx = sanitize_coord(denormalize(f32(grid[grid_base]), params.inW));
226
  let sy = sanitize_coord(denormalize(f32(grid[grid_base + 1u]), params.inH));
227
+ {% if modeSpec == "nearest" %}
228
  let result = pixel_channels(n, c, sample_round(sy), sample_round(sx));
229
+ {% elif modeSpec == "cubic" %}
230
  let x0 = sample_floor(sx) - 1;
231
  let y0 = sample_floor(sy) - 1;
232
  let dx = sx - f32(x0 + 1);
233
  let dy = sy - f32(y0 + 1);
234
  let cx = cubic_coeffs(dx);
235
  let cy = cubic_coeffs(dy);
236
+ {% if paddingMode == "reflection" %}
237
  var reflected_y: array<u32, 4>;
238
  var reflected_x: array<u32, 4>;
239
  for (var k = 0u; k < 4u; k = k + 1u) {
240
+ {% if alignCorners %}
241
  reflected_y[k] = u32(clamp(i32(reflect_coord(f32(y0 + i32(k)), 0.0, f32(params.inH) - 1.0)), 0, i32(params.inH) - 1));
242
  reflected_x[k] = u32(clamp(i32(reflect_coord(f32(x0 + i32(k)), 0.0, f32(params.inW) - 1.0)), 0, i32(params.inW) - 1));
243
  {% else %}
 
248
  {% endif %}
249
  var result = {{ channelVec }}(0.0);
250
  for (var r = 0i; r < 4i; r = r + 1i) {
251
+ {% if paddingMode == "reflection" %}
252
  let row = cx.x * pixel_channels_resolved(n, c, reflected_y[u32(r)], reflected_x[0])
253
  + cx.y * pixel_channels_resolved(n, c, reflected_y[u32(r)], reflected_x[1])
254
  + cx.z * pixel_channels_resolved(n, c, reflected_y[u32(r)], reflected_x[2])
 
266
  let y0 = sample_floor(sy);
267
  let wx = sx - f32(x0);
268
  let wy = sy - f32(y0);
269
+ {% if paddingMode == "reflection" %}
270
+ {% if alignCorners %}
271
  let reflected_y0 = u32(clamp(i32(reflect_coord(f32(y0), 0.0, f32(params.inH) - 1.0)), 0, i32(params.inH) - 1));
272
  let reflected_y1 = u32(clamp(i32(reflect_coord(f32(y0 + 1), 0.0, f32(params.inH) - 1.0)), 0, i32(params.inH) - 1));
273
  let reflected_x0 = u32(clamp(i32(reflect_coord(f32(x0), 0.0, f32(params.inW) - 1.0)), 0, i32(params.inW) - 1));
 
292
  + wy * ((1.0 - wx) * v10 + wx * v11);
293
  {% endif %}
294
  let out_base = (n * params.C + c) * out_plane + spatial;
295
+ {% for lane in range(channelWidthSpec) %}
296
+ {% if channelTail and lane > 0 %}
297
  if (c + {{ lane }}u < params.C) { y[out_base + {{ lane }}u * out_plane] = {{ scalar }}(result.{{ components[lane] }}); }
298
  {% else %}
299
  y[out_base + {{ lane }}u * out_plane] = {{ scalar }}(result.{{ components[lane] }});
 
309
  let sx = sanitize_coord(denormalize(f32(grid[grid_base]), params.inW));
310
  let sy = sanitize_coord(denormalize(f32(grid[grid_base + 1u]), params.inH));
311
  let img_base = (n * params.C + c) * params.inH * params.inW;
312
+ {% if modeSpec == "nearest" %}
313
  let result = pixel(img_base, sample_round(sy), sample_round(sx));
314
+ {% elif modeSpec == "cubic" %}
315
  let x0 = sample_floor(sx) - 1;
316
  let y0 = sample_floor(sy) - 1;
317
  let dx = sx - f32(x0 + 1);
build/webgpu/grid-sample3d.wgsl.jinja CHANGED
@@ -1,43 +1,40 @@
1
  {% macro flat_index_2d(name="i", bound="params.count", guardInline=false, note="dispatch-limit") %}
2
  {% if note == "dispatch-limit" %}
3
- // 2D-folded flat index: gid.y carries the high bits past the
4
- // maxComputeWorkgroupsPerDimension dispatch limit (outputs > 16.7M elements).
5
  {% elif note == "limit" %}
6
- // 2D-folded flat index: gid.y carries the high bits past the
7
- // maxComputeWorkgroupsPerDimension limit.
8
  {% elif note == "device-axis" %}
9
- // The flat dispatch is folded across x/y at the device's per-axis workgroup
10
- // limit; gid.y carries the high portion of the output index.
11
  {% elif note == "vec4-limit" %}
12
- // 2D-folded flat vec4 index: gid.y carries the high bits past the
13
- // maxComputeWorkgroupsPerDimension limit (the dispatch caps x and spills into y).
14
  {% elif note == "element-limit" %}
15
  // 2D-folded flat element index: gid.y carries the high bits past the
16
- // maxComputeWorkgroupsPerDimension limit.
17
  {% elif note == "dispatch" %}
18
- // 2D-folded flat index: gid.y carries the high bits past the
19
- // maxComputeWorkgroupsPerDimension dispatch limit.
20
  {% endif %}
21
  {% if bound == "" %}
22
- let {{ name }} = gid.x + gid.y * nwg.x * {{ tunables.WORKGROUP_SIZE }}u;
23
  {%- elif guardInline %}
24
- let {{ name }} = gid.x + gid.y * nwg.x * {{ tunables.WORKGROUP_SIZE }}u;
25
  if ({{ name }} >= {{ bound }}) { return; }
26
  {%- else %}
27
- let {{ name }} = gid.x + gid.y * nwg.x * {{ tunables.WORKGROUP_SIZE }}u;
28
  if ({{ name }} >= {{ bound }}) {
29
  return;
30
  }
31
  {%- endif %}
32
  {% endmacro %}
33
 
34
- {% if usesF16 %}
35
- enable f16;
36
- {% endif %}
37
  {{ env.wgsl.resourceDeclarations }}
38
 
39
  fn denormalize(n: f32, length: u32) -> f32 {
40
- {% if source.alignCorners %}
41
  return (n + 1.0) * 0.5 * f32(length - 1u);
42
  {% else %}
43
  return ((n + 1.0) * f32(length) - 1.0) * 0.5;
@@ -46,7 +43,7 @@ fn denormalize(n: f32, length: u32) -> f32 {
46
 
47
  fn sanitize_coord(v: f32) -> f32 {
48
  if (!(v >= -2147483000.0 && v <= 2147483000.0)) {
49
- {% if source.alignCorners %}
50
  return 0.0;
51
  {% else %}
52
  return -0.5;
@@ -54,7 +51,7 @@ fn sanitize_coord(v: f32) -> f32 {
54
  }
55
  return v;
56
  }
57
- {% if source.paddingMode == "reflection" %}
58
 
59
  fn reflect_coord(v: f32, lo: f32, hi: f32) -> f32 {
60
  let range = hi - lo;
@@ -77,17 +74,17 @@ fn reflect_coord(v: f32, lo: f32, hi: f32) -> f32 {
77
  }
78
 
79
  {% endif %}
80
- {% if source.mode != "nearest" %}
81
  fn sample_floor(v: f32) -> i32 {
82
  if (v > 2147483000.0) {
83
- {% if source.paddingMode == "reflection" %}
84
  return 0;
85
  {% else %}
86
  return 2147483000;
87
  {% endif %}
88
  }
89
  if (v < -2147483000.0) {
90
- {% if source.paddingMode == "reflection" %}
91
  return 0;
92
  {% else %}
93
  return -2147483000;
@@ -98,14 +95,14 @@ fn sample_floor(v: f32) -> i32 {
98
  {%- else %}
99
  fn sample_round(v: f32) -> i32 {
100
  if (v > 2147483000.0) {
101
- {% if source.paddingMode == "reflection" %}
102
  return 0;
103
  {% else %}
104
  return 2147483000;
105
  {% endif %}
106
  }
107
  if (v < -2147483000.0) {
108
- {% if source.paddingMode == "reflection" %}
109
  return 0;
110
  {% else %}
111
  return -2147483000;
@@ -116,7 +113,7 @@ fn sample_round(v: f32) -> i32 {
116
  {%- endif %}
117
 
118
 
119
- {% if source.mode == "cubic" %}
120
  fn cubic_coeffs(t: f32) -> vec4<f32> {
121
  let a = -0.75;
122
  let x0 = abs(t + 1.0);
@@ -133,20 +130,23 @@ fn cubic_one(x0: f32, a: f32) -> f32 {
133
  }
134
 
135
  {% endif -%}
136
- fn voxel(base: u32, d: i32, h: i32, w: i32) -> f32 {
137
- {% if source.paddingMode == "zeros" %}
 
 
 
138
  if (d < 0 || d >= i32(params.inD) || h < 0 || h >= i32(params.inH) || w < 0 || w >= i32(params.inW)) {
139
- return 0.0;
140
  }
141
  let dd = u32(d);
142
  let hh = u32(h);
143
  let ww = u32(w);
144
- {% elif source.paddingMode == "border" %}
145
  let dd = u32(clamp(d, 0, i32(params.inD) - 1));
146
  let hh = u32(clamp(h, 0, i32(params.inH) - 1));
147
  let ww = u32(clamp(w, 0, i32(params.inW) - 1));
148
  {% else %}
149
- {% if source.alignCorners %}
150
  let rd = i32(reflect_coord(f32(d), 0.0, f32(params.inD) - 1.0));
151
  let rh = i32(reflect_coord(f32(h), 0.0, f32(params.inH) - 1.0));
152
  let rw = i32(reflect_coord(f32(w), 0.0, f32(params.inW) - 1.0));
@@ -159,11 +159,20 @@ fn voxel(base: u32, d: i32, h: i32, w: i32) -> f32 {
159
  let hh = u32(clamp(rh, 0, i32(params.inH) - 1));
160
  let ww = u32(clamp(rw, 0, i32(params.inW) - 1));
161
  {% endif %}
 
 
 
 
 
 
 
 
162
  return f32(x[((base + dd) * params.inH + hh) * params.inW + ww]);
 
163
  }
164
 
165
- @compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
166
- fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups) nwg: vec3<u32>) {
167
  {{ flat_index_2d(guardInline=true) }}
168
 
169
  let ow = i % params.outW;
@@ -172,8 +181,14 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups)
172
  t = t / params.outH;
173
  let od = t % params.outD;
174
  t = t / params.outD;
 
 
 
 
 
175
  let c = t % params.C;
176
  let n = t / params.C;
 
177
 
178
  let grid_base = (((n * params.outD + od) * params.outH + oh) * params.outW + ow) * 3u;
179
  let sx = sanitize_coord(denormalize(f32(grid[grid_base]), params.inW));
@@ -181,11 +196,11 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups)
181
  let sz = sanitize_coord(denormalize(f32(grid[grid_base + 2u]), params.inD));
182
  let img_base = (n * params.C + c) * params.inD;
183
 
184
- {% if source.mode == "nearest" %}
185
- let result = voxel(img_base, sample_round(sz), sample_round(sy), sample_round(sx));
186
- {% elif source.mode == "cubic" %}
187
- // Tricubic is the separable product of the same 4-tap Keys kernel the 2D path
188
- // uses, one axis at a time; voxel() already resolves every padding mode, so
189
  // the 64-tap window needs no boundary handling of its own.
190
  let x0 = sample_floor(sx) - 1;
191
  let y0 = sample_floor(sy) - 1;
@@ -193,14 +208,14 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups)
193
  let cx = cubic_coeffs(sx - f32(x0 + 1));
194
  let cy = cubic_coeffs(sy - f32(y0 + 1));
195
  let cz = cubic_coeffs(sz - f32(z0 + 1));
196
- var result = 0.0;
197
  for (var kz = 0i; kz < 4i; kz = kz + 1i) {
198
- var plane = 0.0;
199
  for (var ky = 0i; ky < 4i; ky = ky + 1i) {
200
- let row = cx.x * voxel(img_base, z0 + kz, y0 + ky, x0)
201
- + cx.y * voxel(img_base, z0 + kz, y0 + ky, x0 + 1)
202
- + cx.z * voxel(img_base, z0 + kz, y0 + ky, x0 + 2)
203
- + cx.w * voxel(img_base, z0 + kz, y0 + ky, x0 + 3);
204
  plane = plane + cy[u32(ky)] * row;
205
  }
206
  result = result + cz[u32(kz)] * plane;
@@ -212,17 +227,29 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups)
212
  let wx = sx - f32(x0);
213
  let wy = sy - f32(y0);
214
  let wz = sz - f32(z0);
215
- let v000 = voxel(img_base, z0, y0, x0);
216
- let v001 = voxel(img_base, z0, y0, x0 + 1);
217
- let v010 = voxel(img_base, z0, y0 + 1, x0);
218
- let v011 = voxel(img_base, z0, y0 + 1, x0 + 1);
219
- let v100 = voxel(img_base, z0 + 1, y0, x0);
220
- let v101 = voxel(img_base, z0 + 1, y0, x0 + 1);
221
- let v110 = voxel(img_base, z0 + 1, y0 + 1, x0);
222
- let v111 = voxel(img_base, z0 + 1, y0 + 1, x0 + 1);
223
  let front = (1.0 - wy) * ((1.0 - wx) * v000 + wx * v001) + wy * ((1.0 - wx) * v010 + wx * v011);
224
  let back = (1.0 - wy) * ((1.0 - wx) * v100 + wx * v101) + wy * ((1.0 - wx) * v110 + wx * v111);
225
  let result = (1.0 - wz) * front + wz * back;
226
  {% endif %}
 
 
 
 
 
 
 
 
 
 
 
227
  y[i] = {{ scalar }}(result);
 
228
  }
 
1
  {% macro flat_index_2d(name="i", bound="params.count", guardInline=false, note="dispatch-limit") %}
2
  {% if note == "dispatch-limit" %}
3
+ // 2D-folded flat index: gid.y carries the high bits past the dispatch's
4
+ // per-axis workgroup fold width (outputs > 16.7M elements).
5
  {% elif note == "limit" %}
6
+ // 2D-folded flat index: gid.y carries the high bits past the dispatch's
7
+ // per-axis workgroup fold width.
8
  {% elif note == "device-axis" %}
9
+ // The flat dispatch is folded across x/y at a fixed per-axis workgroup
10
+ // width; gid.y carries the high portion of the output index.
11
  {% elif note == "vec4-limit" %}
12
+ // 2D-folded flat vec4 index: gid.y carries the high bits past the dispatch's
13
+ // per-axis workgroup fold width (the dispatch caps x and spills into y).
14
  {% elif note == "element-limit" %}
15
  // 2D-folded flat element index: gid.y carries the high bits past the
16
+ // dispatch's per-axis workgroup fold width.
17
  {% elif note == "dispatch" %}
18
+ // 2D-folded flat index: gid.y carries the high bits past the dispatch's
19
+ // per-axis workgroup fold width.
20
  {% endif %}
21
  {% if bound == "" %}
22
+ let {{ name }} = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ tunables.WORKGROUP_SIZE }}u;
23
  {%- elif guardInline %}
24
+ let {{ name }} = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ tunables.WORKGROUP_SIZE }}u;
25
  if ({{ name }} >= {{ bound }}) { return; }
26
  {%- else %}
27
+ let {{ name }} = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ tunables.WORKGROUP_SIZE }}u;
28
  if ({{ name }} >= {{ bound }}) {
29
  return;
30
  }
31
  {%- endif %}
32
  {% endmacro %}
33
 
 
 
 
34
  {{ env.wgsl.resourceDeclarations }}
35
 
36
  fn denormalize(n: f32, length: u32) -> f32 {
37
+ {% if alignCorners %}
38
  return (n + 1.0) * 0.5 * f32(length - 1u);
39
  {% else %}
40
  return ((n + 1.0) * f32(length) - 1.0) * 0.5;
 
43
 
44
  fn sanitize_coord(v: f32) -> f32 {
45
  if (!(v >= -2147483000.0 && v <= 2147483000.0)) {
46
+ {% if alignCorners %}
47
  return 0.0;
48
  {% else %}
49
  return -0.5;
 
51
  }
52
  return v;
53
  }
54
+ {% if paddingMode == "reflection" %}
55
 
56
  fn reflect_coord(v: f32, lo: f32, hi: f32) -> f32 {
57
  let range = hi - lo;
 
74
  }
75
 
76
  {% endif %}
77
+ {% if modeSpec != "nearest" %}
78
  fn sample_floor(v: f32) -> i32 {
79
  if (v > 2147483000.0) {
80
+ {% if paddingMode == "reflection" %}
81
  return 0;
82
  {% else %}
83
  return 2147483000;
84
  {% endif %}
85
  }
86
  if (v < -2147483000.0) {
87
+ {% if paddingMode == "reflection" %}
88
  return 0;
89
  {% else %}
90
  return -2147483000;
 
95
  {%- else %}
96
  fn sample_round(v: f32) -> i32 {
97
  if (v > 2147483000.0) {
98
+ {% if paddingMode == "reflection" %}
99
  return 0;
100
  {% else %}
101
  return 2147483000;
102
  {% endif %}
103
  }
104
  if (v < -2147483000.0) {
105
+ {% if paddingMode == "reflection" %}
106
  return 0;
107
  {% else %}
108
  return -2147483000;
 
113
  {%- endif %}
114
 
115
 
116
+ {% if modeSpec == "cubic" %}
117
  fn cubic_coeffs(t: f32) -> vec4<f32> {
118
  let a = -0.75;
119
  let x0 = abs(t + 1.0);
 
130
  }
131
 
132
  {% endif -%}
133
+ {% set volumeVector = channelWidthSpec is defined %}
134
+ {% set volumeT = "vec" ~ channelWidthSpec ~ "<f32>" if volumeVector else "f32" %}
135
+ {% set voxelArgs = "img_base, c, " if volumeVector and channelTail else "img_base, " %}
136
+ fn voxel(base: u32,{% if volumeVector and channelTail %} c0: u32,{% endif %} d: i32, h: i32, w: i32) -> {{ volumeT }} {
137
+ {% if paddingMode == "zeros" %}
138
  if (d < 0 || d >= i32(params.inD) || h < 0 || h >= i32(params.inH) || w < 0 || w >= i32(params.inW)) {
139
+ return {% if volumeVector %}{{ volumeT }}(0.0){% else %}0.0{% endif %};
140
  }
141
  let dd = u32(d);
142
  let hh = u32(h);
143
  let ww = u32(w);
144
+ {% elif paddingMode == "border" %}
145
  let dd = u32(clamp(d, 0, i32(params.inD) - 1));
146
  let hh = u32(clamp(h, 0, i32(params.inH) - 1));
147
  let ww = u32(clamp(w, 0, i32(params.inW) - 1));
148
  {% else %}
149
+ {% if alignCorners %}
150
  let rd = i32(reflect_coord(f32(d), 0.0, f32(params.inD) - 1.0));
151
  let rh = i32(reflect_coord(f32(h), 0.0, f32(params.inH) - 1.0));
152
  let rw = i32(reflect_coord(f32(w), 0.0, f32(params.inW) - 1.0));
 
159
  let hh = u32(clamp(rh, 0, i32(params.inH) - 1));
160
  let ww = u32(clamp(rw, 0, i32(params.inW) - 1));
161
  {% endif %}
162
+ {% if volumeVector %}
163
+ // All channels share the resolved voxel coordinates. Incomplete vectors
164
+ // alias the final channel on loads; main masks their output stores.
165
+ return {{ volumeT }}(
166
+ {% for lane in range(channelWidthSpec) %}
167
+ f32(x[((base + dd{% if lane > 0 %} + {% if channelTail %}min({{ lane }}u, params.C - 1u - c0){% else %}{{ lane }}u{% endif %} * params.inD{% endif %}) * params.inH + hh) * params.inW + ww]){% if not loop.last %},{% else %});{% endif %}
168
+ {% endfor %}
169
+ {% else %}
170
  return f32(x[((base + dd) * params.inH + hh) * params.inW + ww]);
171
+ {% endif %}
172
  }
173
 
174
+ @compute @workgroup_size({{ volumeWorkgroup if volumeVector else tunables.WORKGROUP_SIZE }})
175
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
176
  {{ flat_index_2d(guardInline=true) }}
177
 
178
  let ow = i % params.outW;
 
181
  t = t / params.outH;
182
  let od = t % params.outD;
183
  t = t / params.outD;
184
+ {% if volumeVector %}
185
+ let channel_blocks = (params.C + {{ channelWidthSpec - 1 }}u) / {{ channelWidthSpec }}u;
186
+ let c = (t % channel_blocks) * {{ channelWidthSpec }}u;
187
+ let n = t / channel_blocks;
188
+ {% else %}
189
  let c = t % params.C;
190
  let n = t / params.C;
191
+ {% endif %}
192
 
193
  let grid_base = (((n * params.outD + od) * params.outH + oh) * params.outW + ow) * 3u;
194
  let sx = sanitize_coord(denormalize(f32(grid[grid_base]), params.inW));
 
196
  let sz = sanitize_coord(denormalize(f32(grid[grid_base + 2u]), params.inD));
197
  let img_base = (n * params.C + c) * params.inD;
198
 
199
+ {% if modeSpec == "nearest" %}
200
+ let result = voxel({{ voxelArgs }}sample_round(sz), sample_round(sy), sample_round(sx));
201
+ {% elif modeSpec == "cubic" %}
202
+ // Tricubic applies separable four-tap Keys interpolation along each spatial
203
+ // axis; voxel() already resolves every padding mode, so
204
  // the 64-tap window needs no boundary handling of its own.
205
  let x0 = sample_floor(sx) - 1;
206
  let y0 = sample_floor(sy) - 1;
 
208
  let cx = cubic_coeffs(sx - f32(x0 + 1));
209
  let cy = cubic_coeffs(sy - f32(y0 + 1));
210
  let cz = cubic_coeffs(sz - f32(z0 + 1));
211
+ var result = {% if volumeVector %}{{ volumeT }}(0.0){% else %}0.0{% endif %};
212
  for (var kz = 0i; kz < 4i; kz = kz + 1i) {
213
+ var plane = {% if volumeVector %}{{ volumeT }}(0.0){% else %}0.0{% endif %};
214
  for (var ky = 0i; ky < 4i; ky = ky + 1i) {
215
+ let row = cx.x * voxel({{ voxelArgs }}z0 + kz, y0 + ky, x0)
216
+ + cx.y * voxel({{ voxelArgs }}z0 + kz, y0 + ky, x0 + 1)
217
+ + cx.z * voxel({{ voxelArgs }}z0 + kz, y0 + ky, x0 + 2)
218
+ + cx.w * voxel({{ voxelArgs }}z0 + kz, y0 + ky, x0 + 3);
219
  plane = plane + cy[u32(ky)] * row;
220
  }
221
  result = result + cz[u32(kz)] * plane;
 
227
  let wx = sx - f32(x0);
228
  let wy = sy - f32(y0);
229
  let wz = sz - f32(z0);
230
+ let v000 = voxel({{ voxelArgs }}z0, y0, x0);
231
+ let v001 = voxel({{ voxelArgs }}z0, y0, x0 + 1);
232
+ let v010 = voxel({{ voxelArgs }}z0, y0 + 1, x0);
233
+ let v011 = voxel({{ voxelArgs }}z0, y0 + 1, x0 + 1);
234
+ let v100 = voxel({{ voxelArgs }}z0 + 1, y0, x0);
235
+ let v101 = voxel({{ voxelArgs }}z0 + 1, y0, x0 + 1);
236
+ let v110 = voxel({{ voxelArgs }}z0 + 1, y0 + 1, x0);
237
+ let v111 = voxel({{ voxelArgs }}z0 + 1, y0 + 1, x0 + 1);
238
  let front = (1.0 - wy) * ((1.0 - wx) * v000 + wx * v001) + wy * ((1.0 - wx) * v010 + wx * v011);
239
  let back = (1.0 - wy) * ((1.0 - wx) * v100 + wx * v101) + wy * ((1.0 - wx) * v110 + wx * v111);
240
  let result = (1.0 - wz) * front + wz * back;
241
  {% endif %}
242
+ {% if volumeVector %}
243
+ let out_plane = params.outD * params.outH * params.outW;
244
+ let out_base = (n * params.C + c) * out_plane + (od * params.outH + oh) * params.outW + ow;
245
+ {% for lane in range(channelWidthSpec) %}
246
+ {% if channelTail and lane > 0 %}
247
+ if (c + {{ lane }}u < params.C) { y[out_base + {{ lane }}u * out_plane] = {{ scalar }}(result[{{ lane }}u]); }
248
+ {% else %}
249
+ y[out_base + {{ lane }}u * out_plane] = {{ scalar }}(result[{{ lane }}u]);
250
+ {% endif %}
251
+ {% endfor %}
252
+ {% else %}
253
  y[i] = {{ scalar }}(result);
254
+ {% endif %}
255
  }
build/webgpu/manifest.json CHANGED
@@ -2,33 +2,19 @@
2
  "domain": "ai.onnx",
3
  "name": "GridSample",
4
  "sinceVersion": 20,
5
- "description": "Samples values from input tensor `X` at positions defined by a flow-field `grid`, producing output `Y` with spatial dimensions taken from `grid`. Grid coordinates are normalized to `[-1, 1]` over the input spatial extent; positions outside this range are handled according to `padding_mode`. Supports spatial `(rank-4, NCHW)` and volumetric `(rank-5, NCDHW)` inputs with `linear`, `nearest`, or `cubic` interpolation.",
6
- "inputs": [
7
- {
8
- "role": "X",
9
- "dtype": "T",
10
- "description": "Input tensor of shape `(N, C, D1, ..., Dr)` whose values are sampled."
11
- },
12
- {
13
- "role": "grid",
14
- "dtype": "T",
15
- "description": "Flow-field of shape `(N, D1_out, ..., Dr_out, r)` with normalized sampling coordinates in `[-1, 1]`."
16
- }
17
- ],
18
- "outputs": [
19
- {
20
- "role": "Y",
21
  "dtype": "T",
22
  "rank": "ranks.grid",
23
- "description": "Output tensor of shape `(N, C, D1_out, ..., Dr_out)` containing the interpolated samples.",
24
- "shape": "prefix(shapes.X, 2) + prefix(suffix(shapes.grid, 1), ranks.grid - 2)"
25
  }
26
- ],
27
- "attributes": { "mode": "linear", "padding_mode": "zeros", "align_corners": 0 },
28
- "attributeDescriptions": {
29
- "mode": "Interpolation method: `linear` (bilinear or trilinear, depending on rank), `nearest`, or `cubic`. Cubic interpolation is supported for rank-4 (2-D spatial) inputs.",
30
- "padding_mode": "How out-of-bound grid positions are handled: `zeros` pads with 0, `border` clamps to the border value, or `reflection` reflects coordinates back into the valid range.",
31
- "align_corners": "When 1, extrema values `-1` and `1` map to the center of the corner pixels; when 0 (default) they map to the outer edge of corner pixels, making sampling resolution-agnostic."
32
  },
33
  "attributeConstraints": {
34
  "mode": { "values": ["linear", "nearest", "cubic"] },
@@ -36,71 +22,52 @@
36
  "align_corners": { "values": [0, 1] }
37
  },
38
  "typeConstraints": { "T": ["float32", "float16"] },
39
- "args": {
40
- "x": { "kind": "tensor", "semantic": "X", "role": "input" },
41
- "grid": { "kind": "tensor", "semantic": "grid", "role": "input" },
42
- "y": { "kind": "tensor", "semantic": "Y", "role": "output" }
43
- },
44
- "tunables": { "WORKGROUP_SIZE": 256 },
45
  "derive": {
46
  "wave32Adapter": "has(device.adapterInfo, \"subgroupMinSize\") and has(device.adapterInfo, \"subgroupMaxSize\") and device.adapterInfo.subgroupMinSize == 32 and device.adapterInfo.subgroupMaxSize == 32",
47
  "reportedNonWave32Adapter": "not wave32Adapter and (has(device.adapterInfo, \"subgroupMinSize\") or has(device.adapterInfo, \"subgroupMaxSize\"))",
48
- "rank4Ok": "ranks.X == 4 and ranks.grid == 4 and ranks.Y == 4 and dim(shapes.grid, 0) == dim(shapes.X, 0) and dim(shapes.grid, 3) == 2 and dim(shapes.Y, 0) == dim(shapes.X, 0) and dim(shapes.Y, 1) == dim(shapes.X, 1) and dim(shapes.Y, 2) == dim(shapes.grid, 1) and dim(shapes.Y, 3) == dim(shapes.grid, 2) and f16Ok(dtypes.T)",
49
- "channelWidth": 4
 
 
 
 
50
  },
51
- "constants": { "scalar": "dtypes.T", "usesF16": "dtypes.T == \"f16\"" },
52
- "bindingSets": {
53
- "rank4": [
54
- { "name": "x", "arg": "x", "semantic": "X", "buffer": { "type": "read-only-storage" }, "elementType": "$scalar" },
55
- {
56
- "name": "grid",
57
- "arg": "grid",
58
- "semantic": "grid",
59
- "buffer": { "type": "read-only-storage" },
60
- "elementType": "$scalar"
61
- },
62
- { "name": "y", "arg": "y", "semantic": "Y", "buffer": { "type": "storage" }, "elementType": "$scalar" },
63
- {
64
- "name": "params",
65
- "semantic": "kernel.params",
66
- "buffer": { "type": "uniform" },
67
- "struct": {
68
- "name": "Params",
69
- "fields": [
70
- { "name": "count", "type": "u32", "value": "numel(shapes.Y)" },
71
- { "name": "C", "type": "u32", "value": "dim(shapes.X, 1)" },
72
- { "name": "inH", "type": "u32", "value": "dim(shapes.X, 2)" },
73
- { "name": "inW", "type": "u32", "value": "dim(shapes.X, 3)" },
74
- { "name": "outH", "type": "u32", "value": "dim(shapes.Y, 2)" },
75
- { "name": "outW", "type": "u32", "value": "dim(shapes.Y, 3)" }
76
- ]
77
- }
78
- }
79
- ]
80
  },
81
  "variants": [
82
  {
83
  "id": "nchw_rank4_channel_vector",
84
  "priority": 5,
85
- "when": ["rank4Ok", "not reportedNonWave32Adapter", "dim(shapes.X, 1) >= 2"],
86
  "passes": [
87
  {
88
  "id": "main",
89
  "name": "GridSample.ChannelX4",
90
- "source": {
91
- "shader": "grid-sample.wgsl.jinja",
92
- "inputs": {
93
- "mode": "attrs.mode",
94
- "paddingMode": "attrs.padding_mode",
95
- "alignCorners": "attrs.align_corners != 0",
96
- "channelWidth": "channelWidth",
97
- "channelTail": "dim(shapes.X, 1) % channelWidth != 0"
98
- }
99
  },
100
- "bindings": "rank4",
101
  "dispatch": {
102
- "threads": "dim(shapes.Y, 0) * ceilDiv(dim(shapes.Y, 1), channelWidth) * dim(shapes.Y, 2) * dim(shapes.Y, 3)",
103
- "workgroupSize": "tunables.WORKGROUP_SIZE"
 
104
  }
105
  }
106
  ]
@@ -112,71 +79,104 @@
112
  {
113
  "id": "main",
114
  "name": "GridSample",
115
- "source": {
116
- "shader": "grid-sample.wgsl.jinja",
117
- "inputs": {
118
- "mode": "attrs.mode",
119
- "paddingMode": "attrs.padding_mode",
120
- "alignCorners": "attrs.align_corners != 0"
121
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
  },
123
- "bindings": "rank4",
124
- "dispatch": { "threads": "numel(shapes.Y)", "workgroupSize": "tunables.WORKGROUP_SIZE" }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125
  }
126
  ]
127
  },
128
  {
129
  "id": "ncdhw_rank5",
130
  "priority": 10,
131
- "when": ["ranks.X == 5", "ranks.grid == 5", "ranks.Y == 5", "dim(shapes.grid, 0) == dim(shapes.X, 0)", "dim(shapes.grid, 4) == 3", "dim(shapes.Y, 0) == dim(shapes.X, 0)", "dim(shapes.Y, 1) == dim(shapes.X, 1)", "dim(shapes.Y, 2) == dim(shapes.grid, 1)", "dim(shapes.Y, 3) == dim(shapes.grid, 2)", "dim(shapes.Y, 4) == dim(shapes.grid, 3)", "(attrs.mode == \"linear\" or attrs.mode == \"nearest\" or attrs.mode == \"cubic\")", "(attrs.padding_mode == \"zeros\" or attrs.padding_mode == \"border\" or attrs.padding_mode == \"reflection\")", "f16Ok(dtypes.T)"],
132
  "passes": [
133
  {
134
  "id": "main",
135
  "name": "GridSample.Volumetric",
136
- "source": {
137
- "shader": "grid-sample3d.wgsl.jinja",
138
- "inputs": {
139
- "mode": "attrs.mode",
140
- "paddingMode": "attrs.padding_mode",
141
- "alignCorners": "attrs.align_corners != 0"
142
- }
143
  },
144
  "bindings": [
145
- {
146
- "name": "x",
147
- "arg": "x",
148
- "semantic": "X",
149
- "buffer": { "type": "read-only-storage" },
150
- "elementType": "$scalar"
151
- },
152
- {
153
- "name": "grid",
154
- "arg": "grid",
155
- "semantic": "grid",
156
- "buffer": { "type": "read-only-storage" },
157
- "elementType": "$scalar"
158
- },
159
- { "name": "y", "arg": "y", "semantic": "Y", "buffer": { "type": "storage" }, "elementType": "$scalar" },
160
  {
161
  "name": "params",
162
- "semantic": "kernel.params",
163
- "buffer": { "type": "uniform" },
164
- "struct": {
165
- "name": "Params",
166
- "fields": [
167
- { "name": "count", "type": "u32", "value": "numel(shapes.Y)" },
168
- { "name": "C", "type": "u32", "value": "dim(shapes.X, 1)" },
169
- { "name": "inD", "type": "u32", "value": "dim(shapes.X, 2)" },
170
- { "name": "inH", "type": "u32", "value": "dim(shapes.X, 3)" },
171
- { "name": "inW", "type": "u32", "value": "dim(shapes.X, 4)" },
172
- { "name": "outD", "type": "u32", "value": "dim(shapes.Y, 2)" },
173
- { "name": "outH", "type": "u32", "value": "dim(shapes.Y, 3)" },
174
- { "name": "outW", "type": "u32", "value": "dim(shapes.Y, 4)" }
175
- ]
176
- }
177
  }
178
  ],
179
- "dispatch": { "threads": "numel(shapes.Y)", "workgroupSize": "tunables.WORKGROUP_SIZE" }
 
 
 
 
180
  }
181
  ]
182
  }
 
2
  "domain": "ai.onnx",
3
  "name": "GridSample",
4
  "sinceVersion": 20,
5
+ "inputs": { "x": { "onnx": "X", "dtype": "T" }, "grid": { "dtype": "T" } },
6
+ "outputs": {
7
+ "y": {
8
+ "onnx": "Y",
 
 
 
 
 
 
 
 
 
 
 
 
9
  "dtype": "T",
10
  "rank": "ranks.grid",
11
+ "shape": "prefix(shapes.x, 2) + prefix(suffix(shapes.grid, 1), ranks.grid - 2)"
 
12
  }
13
+ },
14
+ "attributes": {
15
+ "mode": { "default": "linear" },
16
+ "padding_mode": { "default": "zeros" },
17
+ "align_corners": { "default": 0 }
 
18
  },
19
  "attributeConstraints": {
20
  "mode": { "values": ["linear", "nearest", "cubic"] },
 
22
  "align_corners": { "values": [0, 1] }
23
  },
24
  "typeConstraints": { "T": ["float32", "float16"] },
25
+ "tunables": { "WORKGROUP_SIZE": { "default": 256 } },
 
 
 
 
 
26
  "derive": {
27
  "wave32Adapter": "has(device.adapterInfo, \"subgroupMinSize\") and has(device.adapterInfo, \"subgroupMaxSize\") and device.adapterInfo.subgroupMinSize == 32 and device.adapterInfo.subgroupMaxSize == 32",
28
  "reportedNonWave32Adapter": "not wave32Adapter and (has(device.adapterInfo, \"subgroupMinSize\") or has(device.adapterInfo, \"subgroupMaxSize\"))",
29
+ "deviceWorkgroupCap": "min(device.limits.maxComputeInvocationsPerWorkgroup, device.limits.maxComputeWorkgroupSizeX)",
30
+ "rank4Ok": "ranks.x == 4 and ranks.grid == 4 and ranks.y == 4 and dim(shapes.grid, 0) == dim(shapes.x, 0) and dim(shapes.grid, 3) == 2 and dim(shapes.y, 0) == dim(shapes.x, 0) and dim(shapes.y, 1) == dim(shapes.x, 1) and dim(shapes.y, 2) == dim(shapes.grid, 1) and dim(shapes.y, 3) == dim(shapes.grid, 2) and f16Ok(dtypes.T)",
31
+ "channelWidth": 4,
32
+ "scalar": "dtypes.T",
33
+ "volumeWidth": "min(channelWidth, pow(2, log2ceil(max(1, dim(shapes.x, 1)))))",
34
+ "volumeWorkgroup": "min(tunables.WORKGROUP_SIZE, deviceWorkgroupCap)"
35
  },
36
+ "bindings": {
37
+ "params": {
38
+ "buffer": "uniform",
39
+ "struct": [
40
+ { "name": "count", "type": "u32", "value": "numel(shapes.y)" },
41
+ { "name": "C", "type": "u32", "value": "dim(shapes.x, 1)" },
42
+ { "name": "inH", "type": "u32", "value": "dim(shapes.x, 2)" },
43
+ { "name": "inW", "type": "u32", "value": "dim(shapes.x, 3)" },
44
+ { "name": "outH", "type": "u32", "value": "dim(shapes.y, 2)" },
45
+ { "name": "outW", "type": "u32", "value": "dim(shapes.y, 3)" }
46
+ ]
47
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  },
49
  "variants": [
50
  {
51
  "id": "nchw_rank4_channel_vector",
52
  "priority": 5,
53
+ "when": ["rank4Ok", "not reportedNonWave32Adapter", "dim(shapes.x, 1) >= 2"],
54
  "passes": [
55
  {
56
  "id": "main",
57
  "name": "GridSample.ChannelX4",
58
+ "shader": "grid-sample.wgsl.jinja",
59
+ "derive": {
60
+ "modeSpec": "attrs.mode",
61
+ "paddingMode": "attrs.padding_mode",
62
+ "alignCorners": "attrs.align_corners != 0",
63
+ "channelWidthSpec": "channelWidth",
64
+ "channelTail": "dim(shapes.x, 1) % channelWidth != 0"
 
 
65
  },
66
+ "bindings": ["x", "grid", "y", "params"],
67
  "dispatch": {
68
+ "x": "min(ceilDiv((dim(shapes.y, 0) * ceilDiv(dim(shapes.y, 1), channelWidth) * dim(shapes.y, 2) * dim(shapes.y, 3)), (tunables.WORKGROUP_SIZE)), 65535)",
69
+ "y": "ceilDiv(ceilDiv((dim(shapes.y, 0) * ceilDiv(dim(shapes.y, 1), channelWidth) * dim(shapes.y, 2) * dim(shapes.y, 3)), (tunables.WORKGROUP_SIZE)), 65535)",
70
+ "z": 1
71
  }
72
  }
73
  ]
 
79
  {
80
  "id": "main",
81
  "name": "GridSample",
82
+ "shader": "grid-sample.wgsl.jinja",
83
+ "derive": {
84
+ "modeSpec": "attrs.mode",
85
+ "paddingMode": "attrs.padding_mode",
86
+ "alignCorners": "attrs.align_corners != 0"
87
+ },
88
+ "bindings": ["x", "grid", "y", "params"],
89
+ "dispatch": {
90
+ "x": "min(ceilDiv((numel(shapes.y)), (tunables.WORKGROUP_SIZE)), 65535)",
91
+ "y": "ceilDiv(ceilDiv((numel(shapes.y)), (tunables.WORKGROUP_SIZE)), 65535)",
92
+ "z": 1
93
+ }
94
+ }
95
+ ]
96
+ },
97
+ {
98
+ "id": "ncdhw_rank5_channel_vector",
99
+ "priority": 15,
100
+ "when": ["ranks.x == 5", "ranks.grid == 5", "ranks.y == 5", "dim(shapes.grid, 0) == dim(shapes.x, 0)", "dim(shapes.grid, 4) == 3", "dim(shapes.y, 0) == dim(shapes.x, 0)", "dim(shapes.y, 1) == dim(shapes.x, 1)", "dim(shapes.y, 2) == dim(shapes.grid, 1)", "dim(shapes.y, 3) == dim(shapes.grid, 2)", "dim(shapes.y, 4) == dim(shapes.grid, 3)", "(attrs.mode == \"linear\" or attrs.mode == \"nearest\" or attrs.mode == \"cubic\")", "(attrs.padding_mode == \"zeros\" or attrs.padding_mode == \"border\" or attrs.padding_mode == \"reflection\")", "f16Ok(dtypes.T)", "not reportedNonWave32Adapter", "dim(shapes.x, 1) >= 2", "attrs.mode != \"cubic\" or attrs.padding_mode != \"border\""],
101
+ "passes": [
102
+ {
103
+ "id": "main",
104
+ "name": "GridSample.VolumetricChannels",
105
+ "shader": "grid-sample3d.wgsl.jinja",
106
+ "derive": {
107
+ "modeSpec": "attrs.mode",
108
+ "paddingMode": "attrs.padding_mode",
109
+ "alignCorners": "attrs.align_corners != 0",
110
+ "channelWidthSpec": "volumeWidth",
111
+ "channelTail": "dim(shapes.x, 1) % volumeWidth != 0"
112
  },
113
+ "bindings": [
114
+ "x",
115
+ "grid",
116
+ "y",
117
+ {
118
+ "name": "params",
119
+ "struct": [
120
+ {
121
+ "name": "count",
122
+ "type": "u32",
123
+ "value": "dim(shapes.y, 0) * ceilDiv(dim(shapes.y, 1), volumeWidth) * dim(shapes.y, 2) * dim(shapes.y, 3) * dim(shapes.y, 4)"
124
+ },
125
+ { "name": "C", "type": "u32", "value": "dim(shapes.x, 1)" },
126
+ { "name": "inD", "type": "u32", "value": "dim(shapes.x, 2)" },
127
+ { "name": "inH", "type": "u32", "value": "dim(shapes.x, 3)" },
128
+ { "name": "inW", "type": "u32", "value": "dim(shapes.x, 4)" },
129
+ { "name": "outD", "type": "u32", "value": "dim(shapes.y, 2)" },
130
+ { "name": "outH", "type": "u32", "value": "dim(shapes.y, 3)" },
131
+ { "name": "outW", "type": "u32", "value": "dim(shapes.y, 4)" }
132
+ ]
133
+ }
134
+ ],
135
+ "dispatch": {
136
+ "x": "min(ceilDiv((dim(shapes.y, 0) * ceilDiv(dim(shapes.y, 1), volumeWidth) * dim(shapes.y, 2) * dim(shapes.y, 3) * dim(shapes.y, 4)), (volumeWorkgroup)), 65535)",
137
+ "y": "ceilDiv(ceilDiv((dim(shapes.y, 0) * ceilDiv(dim(shapes.y, 1), volumeWidth) * dim(shapes.y, 2) * dim(shapes.y, 3) * dim(shapes.y, 4)), (volumeWorkgroup)), 65535)",
138
+ "z": 1
139
+ }
140
  }
141
  ]
142
  },
143
  {
144
  "id": "ncdhw_rank5",
145
  "priority": 10,
146
+ "when": ["ranks.x == 5", "ranks.grid == 5", "ranks.y == 5", "dim(shapes.grid, 0) == dim(shapes.x, 0)", "dim(shapes.grid, 4) == 3", "dim(shapes.y, 0) == dim(shapes.x, 0)", "dim(shapes.y, 1) == dim(shapes.x, 1)", "dim(shapes.y, 2) == dim(shapes.grid, 1)", "dim(shapes.y, 3) == dim(shapes.grid, 2)", "dim(shapes.y, 4) == dim(shapes.grid, 3)", "(attrs.mode == \"linear\" or attrs.mode == \"nearest\" or attrs.mode == \"cubic\")", "(attrs.padding_mode == \"zeros\" or attrs.padding_mode == \"border\" or attrs.padding_mode == \"reflection\")", "f16Ok(dtypes.T)"],
147
  "passes": [
148
  {
149
  "id": "main",
150
  "name": "GridSample.Volumetric",
151
+ "shader": "grid-sample3d.wgsl.jinja",
152
+ "derive": {
153
+ "modeSpec": "attrs.mode",
154
+ "paddingMode": "attrs.padding_mode",
155
+ "alignCorners": "attrs.align_corners != 0"
 
 
156
  },
157
  "bindings": [
158
+ "x",
159
+ "grid",
160
+ "y",
 
 
 
 
 
 
 
 
 
 
 
 
161
  {
162
  "name": "params",
163
+ "struct": [
164
+ { "name": "count", "type": "u32", "value": "numel(shapes.y)" },
165
+ { "name": "C", "type": "u32", "value": "dim(shapes.x, 1)" },
166
+ { "name": "inD", "type": "u32", "value": "dim(shapes.x, 2)" },
167
+ { "name": "inH", "type": "u32", "value": "dim(shapes.x, 3)" },
168
+ { "name": "inW", "type": "u32", "value": "dim(shapes.x, 4)" },
169
+ { "name": "outD", "type": "u32", "value": "dim(shapes.y, 2)" },
170
+ { "name": "outH", "type": "u32", "value": "dim(shapes.y, 3)" },
171
+ { "name": "outW", "type": "u32", "value": "dim(shapes.y, 4)" }
172
+ ]
 
 
 
 
 
173
  }
174
  ],
175
+ "dispatch": {
176
+ "x": "min(ceilDiv((numel(shapes.y)), (tunables.WORKGROUP_SIZE)), 65535)",
177
+ "y": "ceilDiv(ceilDiv((numel(shapes.y)), (tunables.WORKGROUP_SIZE)), 65535)",
178
+ "z": 1
179
+ }
180
  }
181
  ]
182
  }
build/webgpu/metadata.json CHANGED
@@ -1,19 +1,27 @@
1
  {
2
  "name": "ai.onnx.GridSample",
3
- "id": "_ai_onnx_gridsample_webgpu_3892192",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "backend": { "type": "webgpu" },
7
  "digest": {
8
  "algorithm": "sha256",
9
  "files": {
10
- "bench.json": "G6tTb9EsyvpKUZLsd4qAik9iHOhPJGsUBR3ah5o7L+w=",
11
- "grid-sample.wgsl.jinja": "vGFCLAzhkxtkeC071yc3MrcZa3C5mqPT4+ZbjxJn5Zo=",
12
- "grid-sample3d.wgsl.jinja": "cSEW1lJIrYaiamCfxoP1KBWbzqcRfe+00AtqpeXRUNA=",
13
- "manifest.json": "iZOT6Iz9u/5yFWpFbpO+1EgSPRoW9YPluK9w2unD2wk=",
14
- "test.json": "iD0xQONckmqQHJylOBRTC7z57/IvRn28fEdFgYX1TNI="
15
  }
16
  },
17
- "provenance": { "kernel": { "sha": "2e7068faf55e7f43df740015f6d1ee49391a41c5", "dirty": false } },
18
- "webgpu": { "manifestSpec": "1.0", "specialized": true, "opPath": "ops/ai.onnx.GridSample" }
 
 
 
 
 
 
 
 
19
  }
 
1
  {
2
  "name": "ai.onnx.GridSample",
3
+ "id": "_ai_onnx_gridsample_webgpu_f340775",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "backend": { "type": "webgpu" },
7
  "digest": {
8
  "algorithm": "sha256",
9
  "files": {
10
+ "bench.json": "GKt3XLyZG7PU01DKekKBLzn6wJaj5/4VX+430PWZLas=",
11
+ "grid-sample.wgsl.jinja": "azjm8+LAvCxuA3CAJtB0ElRwBbDCOy05cOL8rJzeeK0=",
12
+ "grid-sample3d.wgsl.jinja": "zLErkeaeWt4gEn6iAkcEYGDEhauXCj+czW4rO0kbv2k=",
13
+ "manifest.json": "PcNyEEhLUbrU5SY4T6mBxI/MFk1fnH4BQWcpSnleDHU=",
14
+ "test.json": "mLgrc4Eep+6yiX6JmU0Hpt/dMAjXnL6kxOFsL+/hO38="
15
  }
16
  },
17
+ "provenance": { "kernel": { "sha": "91d990483a174128daf7673f3f37a7c890493ae1", "dirty": false } },
18
+ "webgpu": {
19
+ "manifestSpec": "2.0",
20
+ "variants": {
21
+ "nchw_rank4_channel_vector": ["grid-sample.wgsl.jinja"],
22
+ "nchw_rank4": ["grid-sample.wgsl.jinja"],
23
+ "ncdhw_rank5_channel_vector": ["grid-sample3d.wgsl.jinja"],
24
+ "ncdhw_rank5": ["grid-sample3d.wgsl.jinja"]
25
+ }
26
+ }
27
  }
build/webgpu/test.json CHANGED
@@ -1,5 +1,4 @@
1
  {
2
- "op": "ai.onnx.GridSample",
3
  "fixtureArrays": {
4
  "onnx_backend_gridsample_volumetric_input_grid": [-1, -1, -1, -1, -0.5, 0.30000001192092896, -0.5, -0.5, -0.5, 1, -0.6000000238418579, -1, -0.20000000298023224, -0.20000000298023224, -0.20000000298023224, 0.4000000059604645, 0.20000000298023224, 0.6000000238418579, 0, 0, 0, -1, 0, 0, 0, 0, 0, -1, 1, 0, -0.20000000298023224, -0.20000000298023224, -0.20000000298023224, 1, 0.4000000059604645, -0.20000000298023224, 0.5, 0.5, 0.5, -1, -0.800000011920929, 0.800000011920929, 1, 1, 1, 0.4000000059604645, 0.6000000238418579, -0.30000001192092896],
5
  "grid_sample_grid_16": [-1, -0.800000011920929, -0.6000000238418579, -0.5, -0.10000000149011612, -0.20000000298023224, 0.699999988079071, 0, 0, 0.4000000059604645, 0.20000000298023224, -0.20000000298023224, -0.30000001192092896, 0.5, -1, 1],
@@ -94,7 +93,7 @@
94
  "reason": "Portable WGSL floating-point semantics do not guarantee preservation of the subnormal values required by this fixture. Backend evidence: Metal flushes denormals to zero in the ALU; bilinear/trilinear interpolation arithmetic flushes the subnormal pixel/voxel on GPU. Permanent FTZ limitation."
95
  },
96
  "provenance": {
97
- "notes": "Rank-5 companion: exact corner sampling should copy the positive subnormal source voxel through the trilinear path."
98
  },
99
  "attrs": { "mode": "linear", "padding_mode": "zeros", "align_corners": 1 },
100
  "inputs": {
@@ -1729,11 +1728,7 @@
1729
  "attrs": { "mode": "linear", "padding_mode": "zeros", "align_corners": 1 },
1730
  "inputs": {
1731
  "x": { "dtype": "float32", "shape": [1, 1, 0, 2], "data": { "kind": "values", "values": [] } },
1732
- "grid": {
1733
- "dtype": "float32",
1734
- "shape": [1, 2, 2, 2],
1735
- "data": { "kind": "values", "values": [-1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0] }
1736
- }
1737
  },
1738
  "outputs": {
1739
  "y": {
@@ -1890,7 +1885,7 @@
1890
  "name": "rank5_cubic_border_pinned_taps",
1891
  "provenance": {
1892
  "source": "ONNX GridSample cubic definition (Keys kernel, cubic_coeff_a = -0.75), evaluated independently",
1893
- "notes": "Ground truth computed independently from the separable 4-tap definition, so neither the kernel nor the reference is its own oracle. Sample points are dyadic and the field is in quarters, so the values are exact in binary; two of the four points push taps past the volume, which border clamps. Trilinear on the same points gives different answers, so a cubic path that silently ran as linear would fail here."
1894
  },
1895
  "attrs": { "mode": "cubic", "padding_mode": "border", "align_corners": 0 },
1896
  "inputs": {
@@ -1960,6 +1955,790 @@
1960
  }
1961
  },
1962
  "outputs": { "y": { "dtype": "float32", "shape": [1, 2, 2, 2, 2], "tolerance": 0.0001, "relTolerance": 0.0001 } }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1963
  }
1964
  ]
1965
  }
 
1
  {
 
2
  "fixtureArrays": {
3
  "onnx_backend_gridsample_volumetric_input_grid": [-1, -1, -1, -1, -0.5, 0.30000001192092896, -0.5, -0.5, -0.5, 1, -0.6000000238418579, -1, -0.20000000298023224, -0.20000000298023224, -0.20000000298023224, 0.4000000059604645, 0.20000000298023224, 0.6000000238418579, 0, 0, 0, -1, 0, 0, 0, 0, 0, -1, 1, 0, -0.20000000298023224, -0.20000000298023224, -0.20000000298023224, 1, 0.4000000059604645, -0.20000000298023224, 0.5, 0.5, 0.5, -1, -0.800000011920929, 0.800000011920929, 1, 1, 1, 0.4000000059604645, 0.6000000238418579, -0.30000001192092896],
4
  "grid_sample_grid_16": [-1, -0.800000011920929, -0.6000000238418579, -0.5, -0.10000000149011612, -0.20000000298023224, 0.699999988079071, 0, 0, 0.4000000059604645, 0.20000000298023224, -0.20000000298023224, -0.30000001192092896, 0.5, -1, 1],
 
93
  "reason": "Portable WGSL floating-point semantics do not guarantee preservation of the subnormal values required by this fixture. Backend evidence: Metal flushes denormals to zero in the ALU; bilinear/trilinear interpolation arithmetic flushes the subnormal pixel/voxel on GPU. Permanent FTZ limitation."
94
  },
95
  "provenance": {
96
+ "notes": "Exact corner sampling on a rank-5 input must copy the positive subnormal source voxel through the trilinear path."
97
  },
98
  "attrs": { "mode": "linear", "padding_mode": "zeros", "align_corners": 1 },
99
  "inputs": {
 
1728
  "attrs": { "mode": "linear", "padding_mode": "zeros", "align_corners": 1 },
1729
  "inputs": {
1730
  "x": { "dtype": "float32", "shape": [1, 1, 0, 2], "data": { "kind": "values", "values": [] } },
1731
+ "grid": { "dtype": "float32", "shape": [1, 2, 2, 2], "data": { "kind": "constant", "value": -1.0 } }
 
 
 
 
1732
  },
1733
  "outputs": {
1734
  "y": {
 
1885
  "name": "rank5_cubic_border_pinned_taps",
1886
  "provenance": {
1887
  "source": "ONNX GridSample cubic definition (Keys kernel, cubic_coeff_a = -0.75), evaluated independently",
1888
+ "notes": "Expected values are derived from the separable four-tap cubic definition. Dyadic sample points and quarter-valued voxels make the arithmetic exact; two points require border clamping, and trilinear interpolation gives different results."
1889
  },
1890
  "attrs": { "mode": "cubic", "padding_mode": "border", "align_corners": 0 },
1891
  "inputs": {
 
1955
  }
1956
  },
1957
  "outputs": { "y": { "dtype": "float32", "shape": [1, 2, 2, 2, 2], "tolerance": 0.0001, "relTolerance": 0.0001 } }
1958
+ },
1959
+ {
1960
+ "name": "volume_channels_tail_linear_zeros_ac0_float32",
1961
+ "provenance": {
1962
+ "notes": "Volumetric channel sharing across batches with a three-channel tail, all interpolation/padding modes and both coordinate conventions."
1963
+ },
1964
+ "attrs": { "mode": "linear", "padding_mode": "zeros", "align_corners": 0 },
1965
+ "inputs": {
1966
+ "x": {
1967
+ "dtype": "float32",
1968
+ "shape": [2, 3, 5, 7, 9],
1969
+ "data": { "kind": "fillFloat32", "scale": 0.3, "sinStep": 0.17, "cosStep": 0.09 }
1970
+ },
1971
+ "grid": {
1972
+ "dtype": "float32",
1973
+ "shape": [2, 7, 9, 11, 3],
1974
+ "data": { "kind": "linspace", "start": -3.5, "end": 3.5 }
1975
+ }
1976
+ },
1977
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 3, 7, 9, 11], "tolerance": 0.0001, "relTolerance": 0.0001 } }
1978
+ },
1979
+ {
1980
+ "name": "volume_channels_tail_linear_zeros_ac0_float16",
1981
+ "provenance": {
1982
+ "notes": "Volumetric channel sharing across batches with a three-channel tail, all interpolation/padding modes and both coordinate conventions."
1983
+ },
1984
+ "attrs": { "mode": "linear", "padding_mode": "zeros", "align_corners": 0 },
1985
+ "inputs": {
1986
+ "x": {
1987
+ "dtype": "float16",
1988
+ "shape": [2, 3, 5, 7, 9],
1989
+ "data": { "kind": "fillFloat32", "scale": 0.3, "sinStep": 0.17, "cosStep": 0.09 }
1990
+ },
1991
+ "grid": {
1992
+ "dtype": "float16",
1993
+ "shape": [2, 7, 9, 11, 3],
1994
+ "data": { "kind": "linspace", "start": -3.5, "end": 3.5 }
1995
+ }
1996
+ },
1997
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 3, 7, 9, 11], "tolerance": 0.001, "relTolerance": 0.001 } }
1998
+ },
1999
+ {
2000
+ "name": "volume_channels_tail_linear_zeros_ac1_float32",
2001
+ "provenance": {
2002
+ "notes": "Volumetric channel sharing across batches with a three-channel tail, all interpolation/padding modes and both coordinate conventions."
2003
+ },
2004
+ "attrs": { "mode": "linear", "padding_mode": "zeros", "align_corners": 1 },
2005
+ "inputs": {
2006
+ "x": {
2007
+ "dtype": "float32",
2008
+ "shape": [2, 3, 5, 7, 9],
2009
+ "data": { "kind": "fillFloat32", "scale": 0.3, "sinStep": 0.17, "cosStep": 0.09 }
2010
+ },
2011
+ "grid": {
2012
+ "dtype": "float32",
2013
+ "shape": [2, 7, 9, 11, 3],
2014
+ "data": { "kind": "linspace", "start": -3.5, "end": 3.5 }
2015
+ }
2016
+ },
2017
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 3, 7, 9, 11], "tolerance": 0.0001, "relTolerance": 0.0001 } }
2018
+ },
2019
+ {
2020
+ "name": "volume_channels_tail_linear_zeros_ac1_float16",
2021
+ "provenance": {
2022
+ "notes": "Volumetric channel sharing across batches with a three-channel tail, all interpolation/padding modes and both coordinate conventions."
2023
+ },
2024
+ "attrs": { "mode": "linear", "padding_mode": "zeros", "align_corners": 1 },
2025
+ "inputs": {
2026
+ "x": {
2027
+ "dtype": "float16",
2028
+ "shape": [2, 3, 5, 7, 9],
2029
+ "data": { "kind": "fillFloat32", "scale": 0.3, "sinStep": 0.17, "cosStep": 0.09 }
2030
+ },
2031
+ "grid": {
2032
+ "dtype": "float16",
2033
+ "shape": [2, 7, 9, 11, 3],
2034
+ "data": { "kind": "linspace", "start": -3.5, "end": 3.5 }
2035
+ }
2036
+ },
2037
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 3, 7, 9, 11], "tolerance": 0.001, "relTolerance": 0.001 } }
2038
+ },
2039
+ {
2040
+ "name": "volume_channels_tail_linear_border_ac0_float32",
2041
+ "provenance": {
2042
+ "notes": "Volumetric channel sharing across batches with a three-channel tail, all interpolation/padding modes and both coordinate conventions."
2043
+ },
2044
+ "attrs": { "mode": "linear", "padding_mode": "border", "align_corners": 0 },
2045
+ "inputs": {
2046
+ "x": {
2047
+ "dtype": "float32",
2048
+ "shape": [2, 3, 5, 7, 9],
2049
+ "data": { "kind": "fillFloat32", "scale": 0.3, "sinStep": 0.17, "cosStep": 0.09 }
2050
+ },
2051
+ "grid": {
2052
+ "dtype": "float32",
2053
+ "shape": [2, 7, 9, 11, 3],
2054
+ "data": { "kind": "linspace", "start": -3.5, "end": 3.5 }
2055
+ }
2056
+ },
2057
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 3, 7, 9, 11], "tolerance": 0.0001, "relTolerance": 0.0001 } }
2058
+ },
2059
+ {
2060
+ "name": "volume_channels_tail_linear_border_ac0_float16",
2061
+ "provenance": {
2062
+ "notes": "Volumetric channel sharing across batches with a three-channel tail, all interpolation/padding modes and both coordinate conventions."
2063
+ },
2064
+ "attrs": { "mode": "linear", "padding_mode": "border", "align_corners": 0 },
2065
+ "inputs": {
2066
+ "x": {
2067
+ "dtype": "float16",
2068
+ "shape": [2, 3, 5, 7, 9],
2069
+ "data": { "kind": "fillFloat32", "scale": 0.3, "sinStep": 0.17, "cosStep": 0.09 }
2070
+ },
2071
+ "grid": {
2072
+ "dtype": "float16",
2073
+ "shape": [2, 7, 9, 11, 3],
2074
+ "data": { "kind": "linspace", "start": -3.5, "end": 3.5 }
2075
+ }
2076
+ },
2077
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 3, 7, 9, 11], "tolerance": 0.001, "relTolerance": 0.001 } }
2078
+ },
2079
+ {
2080
+ "name": "volume_channels_tail_linear_border_ac1_float32",
2081
+ "provenance": {
2082
+ "notes": "Volumetric channel sharing across batches with a three-channel tail, all interpolation/padding modes and both coordinate conventions."
2083
+ },
2084
+ "attrs": { "mode": "linear", "padding_mode": "border", "align_corners": 1 },
2085
+ "inputs": {
2086
+ "x": {
2087
+ "dtype": "float32",
2088
+ "shape": [2, 3, 5, 7, 9],
2089
+ "data": { "kind": "fillFloat32", "scale": 0.3, "sinStep": 0.17, "cosStep": 0.09 }
2090
+ },
2091
+ "grid": {
2092
+ "dtype": "float32",
2093
+ "shape": [2, 7, 9, 11, 3],
2094
+ "data": { "kind": "linspace", "start": -3.5, "end": 3.5 }
2095
+ }
2096
+ },
2097
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 3, 7, 9, 11], "tolerance": 0.0001, "relTolerance": 0.0001 } }
2098
+ },
2099
+ {
2100
+ "name": "volume_channels_tail_linear_border_ac1_float16",
2101
+ "provenance": {
2102
+ "notes": "Volumetric channel sharing across batches with a three-channel tail, all interpolation/padding modes and both coordinate conventions."
2103
+ },
2104
+ "attrs": { "mode": "linear", "padding_mode": "border", "align_corners": 1 },
2105
+ "inputs": {
2106
+ "x": {
2107
+ "dtype": "float16",
2108
+ "shape": [2, 3, 5, 7, 9],
2109
+ "data": { "kind": "fillFloat32", "scale": 0.3, "sinStep": 0.17, "cosStep": 0.09 }
2110
+ },
2111
+ "grid": {
2112
+ "dtype": "float16",
2113
+ "shape": [2, 7, 9, 11, 3],
2114
+ "data": { "kind": "linspace", "start": -3.5, "end": 3.5 }
2115
+ }
2116
+ },
2117
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 3, 7, 9, 11], "tolerance": 0.001, "relTolerance": 0.001 } }
2118
+ },
2119
+ {
2120
+ "name": "volume_channels_tail_linear_reflection_ac0_float32",
2121
+ "provenance": {
2122
+ "notes": "Volumetric channel sharing across batches with a three-channel tail, all interpolation/padding modes and both coordinate conventions."
2123
+ },
2124
+ "attrs": { "mode": "linear", "padding_mode": "reflection", "align_corners": 0 },
2125
+ "inputs": {
2126
+ "x": {
2127
+ "dtype": "float32",
2128
+ "shape": [2, 3, 5, 7, 9],
2129
+ "data": { "kind": "fillFloat32", "scale": 0.3, "sinStep": 0.17, "cosStep": 0.09 }
2130
+ },
2131
+ "grid": {
2132
+ "dtype": "float32",
2133
+ "shape": [2, 7, 9, 11, 3],
2134
+ "data": { "kind": "linspace", "start": -3.5, "end": 3.5 }
2135
+ }
2136
+ },
2137
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 3, 7, 9, 11], "tolerance": 0.0001, "relTolerance": 0.0001 } }
2138
+ },
2139
+ {
2140
+ "name": "volume_channels_tail_linear_reflection_ac0_float16",
2141
+ "provenance": {
2142
+ "notes": "Volumetric channel sharing across batches with a three-channel tail, all interpolation/padding modes and both coordinate conventions."
2143
+ },
2144
+ "attrs": { "mode": "linear", "padding_mode": "reflection", "align_corners": 0 },
2145
+ "inputs": {
2146
+ "x": {
2147
+ "dtype": "float16",
2148
+ "shape": [2, 3, 5, 7, 9],
2149
+ "data": { "kind": "fillFloat32", "scale": 0.3, "sinStep": 0.17, "cosStep": 0.09 }
2150
+ },
2151
+ "grid": {
2152
+ "dtype": "float16",
2153
+ "shape": [2, 7, 9, 11, 3],
2154
+ "data": { "kind": "linspace", "start": -3.5, "end": 3.5 }
2155
+ }
2156
+ },
2157
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 3, 7, 9, 11], "tolerance": 0.001, "relTolerance": 0.001 } }
2158
+ },
2159
+ {
2160
+ "name": "volume_channels_tail_linear_reflection_ac1_float32",
2161
+ "provenance": {
2162
+ "notes": "Volumetric channel sharing across batches with a three-channel tail, all interpolation/padding modes and both coordinate conventions."
2163
+ },
2164
+ "attrs": { "mode": "linear", "padding_mode": "reflection", "align_corners": 1 },
2165
+ "inputs": {
2166
+ "x": {
2167
+ "dtype": "float32",
2168
+ "shape": [2, 3, 5, 7, 9],
2169
+ "data": { "kind": "fillFloat32", "scale": 0.3, "sinStep": 0.17, "cosStep": 0.09 }
2170
+ },
2171
+ "grid": {
2172
+ "dtype": "float32",
2173
+ "shape": [2, 7, 9, 11, 3],
2174
+ "data": { "kind": "linspace", "start": -3.5, "end": 3.5 }
2175
+ }
2176
+ },
2177
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 3, 7, 9, 11], "tolerance": 0.0001, "relTolerance": 0.0001 } }
2178
+ },
2179
+ {
2180
+ "name": "volume_channels_tail_linear_reflection_ac1_float16",
2181
+ "provenance": {
2182
+ "notes": "Volumetric channel sharing across batches with a three-channel tail, all interpolation/padding modes and both coordinate conventions."
2183
+ },
2184
+ "attrs": { "mode": "linear", "padding_mode": "reflection", "align_corners": 1 },
2185
+ "inputs": {
2186
+ "x": {
2187
+ "dtype": "float16",
2188
+ "shape": [2, 3, 5, 7, 9],
2189
+ "data": { "kind": "fillFloat32", "scale": 0.3, "sinStep": 0.17, "cosStep": 0.09 }
2190
+ },
2191
+ "grid": {
2192
+ "dtype": "float16",
2193
+ "shape": [2, 7, 9, 11, 3],
2194
+ "data": { "kind": "linspace", "start": -3.5, "end": 3.5 }
2195
+ }
2196
+ },
2197
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 3, 7, 9, 11], "tolerance": 0.001, "relTolerance": 0.001 } }
2198
+ },
2199
+ {
2200
+ "name": "volume_channels_tail_nearest_zeros_ac0_float32",
2201
+ "provenance": {
2202
+ "notes": "Volumetric channel sharing across batches with a three-channel tail, all interpolation/padding modes and both coordinate conventions."
2203
+ },
2204
+ "attrs": { "mode": "nearest", "padding_mode": "zeros", "align_corners": 0 },
2205
+ "inputs": {
2206
+ "x": {
2207
+ "dtype": "float32",
2208
+ "shape": [2, 3, 5, 7, 9],
2209
+ "data": { "kind": "fillFloat32", "scale": 0.3, "sinStep": 0.17, "cosStep": 0.09 }
2210
+ },
2211
+ "grid": {
2212
+ "dtype": "float32",
2213
+ "shape": [2, 7, 9, 11, 3],
2214
+ "data": { "kind": "linspace", "start": -3.5, "end": 3.5 }
2215
+ }
2216
+ },
2217
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 3, 7, 9, 11], "tolerance": 0.0001, "relTolerance": 0.0001 } }
2218
+ },
2219
+ {
2220
+ "name": "volume_channels_tail_nearest_zeros_ac0_float16",
2221
+ "provenance": {
2222
+ "notes": "Volumetric channel sharing across batches with a three-channel tail, all interpolation/padding modes and both coordinate conventions."
2223
+ },
2224
+ "attrs": { "mode": "nearest", "padding_mode": "zeros", "align_corners": 0 },
2225
+ "inputs": {
2226
+ "x": {
2227
+ "dtype": "float16",
2228
+ "shape": [2, 3, 5, 7, 9],
2229
+ "data": { "kind": "fillFloat32", "scale": 0.3, "sinStep": 0.17, "cosStep": 0.09 }
2230
+ },
2231
+ "grid": {
2232
+ "dtype": "float16",
2233
+ "shape": [2, 7, 9, 11, 3],
2234
+ "data": { "kind": "linspace", "start": -3.5, "end": 3.5 }
2235
+ }
2236
+ },
2237
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 3, 7, 9, 11], "tolerance": 0.001, "relTolerance": 0.001 } }
2238
+ },
2239
+ {
2240
+ "name": "volume_channels_tail_nearest_zeros_ac1_float32",
2241
+ "provenance": {
2242
+ "notes": "Volumetric channel sharing across batches with a three-channel tail, all interpolation/padding modes and both coordinate conventions."
2243
+ },
2244
+ "attrs": { "mode": "nearest", "padding_mode": "zeros", "align_corners": 1 },
2245
+ "inputs": {
2246
+ "x": {
2247
+ "dtype": "float32",
2248
+ "shape": [2, 3, 5, 7, 9],
2249
+ "data": { "kind": "fillFloat32", "scale": 0.3, "sinStep": 0.17, "cosStep": 0.09 }
2250
+ },
2251
+ "grid": {
2252
+ "dtype": "float32",
2253
+ "shape": [2, 7, 9, 11, 3],
2254
+ "data": { "kind": "linspace", "start": -3.5, "end": 3.5 }
2255
+ }
2256
+ },
2257
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 3, 7, 9, 11], "tolerance": 0.0001, "relTolerance": 0.0001 } }
2258
+ },
2259
+ {
2260
+ "name": "volume_channels_tail_nearest_zeros_ac1_float16",
2261
+ "provenance": {
2262
+ "notes": "Volumetric channel sharing across batches with a three-channel tail, all interpolation/padding modes and both coordinate conventions."
2263
+ },
2264
+ "attrs": { "mode": "nearest", "padding_mode": "zeros", "align_corners": 1 },
2265
+ "inputs": {
2266
+ "x": {
2267
+ "dtype": "float16",
2268
+ "shape": [2, 3, 5, 7, 9],
2269
+ "data": { "kind": "fillFloat32", "scale": 0.3, "sinStep": 0.17, "cosStep": 0.09 }
2270
+ },
2271
+ "grid": {
2272
+ "dtype": "float16",
2273
+ "shape": [2, 7, 9, 11, 3],
2274
+ "data": { "kind": "linspace", "start": -3.5, "end": 3.5 }
2275
+ }
2276
+ },
2277
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 3, 7, 9, 11], "tolerance": 0.001, "relTolerance": 0.001 } }
2278
+ },
2279
+ {
2280
+ "name": "volume_channels_tail_nearest_border_ac0_float32",
2281
+ "provenance": {
2282
+ "notes": "Volumetric channel sharing across batches with a three-channel tail, all interpolation/padding modes and both coordinate conventions."
2283
+ },
2284
+ "attrs": { "mode": "nearest", "padding_mode": "border", "align_corners": 0 },
2285
+ "inputs": {
2286
+ "x": {
2287
+ "dtype": "float32",
2288
+ "shape": [2, 3, 5, 7, 9],
2289
+ "data": { "kind": "fillFloat32", "scale": 0.3, "sinStep": 0.17, "cosStep": 0.09 }
2290
+ },
2291
+ "grid": {
2292
+ "dtype": "float32",
2293
+ "shape": [2, 7, 9, 11, 3],
2294
+ "data": { "kind": "linspace", "start": -3.5, "end": 3.5 }
2295
+ }
2296
+ },
2297
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 3, 7, 9, 11], "tolerance": 0.0001, "relTolerance": 0.0001 } }
2298
+ },
2299
+ {
2300
+ "name": "volume_channels_tail_nearest_border_ac0_float16",
2301
+ "provenance": {
2302
+ "notes": "Volumetric channel sharing across batches with a three-channel tail, all interpolation/padding modes and both coordinate conventions."
2303
+ },
2304
+ "attrs": { "mode": "nearest", "padding_mode": "border", "align_corners": 0 },
2305
+ "inputs": {
2306
+ "x": {
2307
+ "dtype": "float16",
2308
+ "shape": [2, 3, 5, 7, 9],
2309
+ "data": { "kind": "fillFloat32", "scale": 0.3, "sinStep": 0.17, "cosStep": 0.09 }
2310
+ },
2311
+ "grid": {
2312
+ "dtype": "float16",
2313
+ "shape": [2, 7, 9, 11, 3],
2314
+ "data": { "kind": "linspace", "start": -3.5, "end": 3.5 }
2315
+ }
2316
+ },
2317
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 3, 7, 9, 11], "tolerance": 0.001, "relTolerance": 0.001 } }
2318
+ },
2319
+ {
2320
+ "name": "volume_channels_tail_nearest_border_ac1_float32",
2321
+ "provenance": {
2322
+ "notes": "Volumetric channel sharing across batches with a three-channel tail, all interpolation/padding modes and both coordinate conventions."
2323
+ },
2324
+ "attrs": { "mode": "nearest", "padding_mode": "border", "align_corners": 1 },
2325
+ "inputs": {
2326
+ "x": {
2327
+ "dtype": "float32",
2328
+ "shape": [2, 3, 5, 7, 9],
2329
+ "data": { "kind": "fillFloat32", "scale": 0.3, "sinStep": 0.17, "cosStep": 0.09 }
2330
+ },
2331
+ "grid": {
2332
+ "dtype": "float32",
2333
+ "shape": [2, 7, 9, 11, 3],
2334
+ "data": { "kind": "linspace", "start": -3.5, "end": 3.5 }
2335
+ }
2336
+ },
2337
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 3, 7, 9, 11], "tolerance": 0.0001, "relTolerance": 0.0001 } }
2338
+ },
2339
+ {
2340
+ "name": "volume_channels_tail_nearest_border_ac1_float16",
2341
+ "provenance": {
2342
+ "notes": "Volumetric channel sharing across batches with a three-channel tail, all interpolation/padding modes and both coordinate conventions."
2343
+ },
2344
+ "attrs": { "mode": "nearest", "padding_mode": "border", "align_corners": 1 },
2345
+ "inputs": {
2346
+ "x": {
2347
+ "dtype": "float16",
2348
+ "shape": [2, 3, 5, 7, 9],
2349
+ "data": { "kind": "fillFloat32", "scale": 0.3, "sinStep": 0.17, "cosStep": 0.09 }
2350
+ },
2351
+ "grid": {
2352
+ "dtype": "float16",
2353
+ "shape": [2, 7, 9, 11, 3],
2354
+ "data": { "kind": "linspace", "start": -3.5, "end": 3.5 }
2355
+ }
2356
+ },
2357
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 3, 7, 9, 11], "tolerance": 0.001, "relTolerance": 0.001 } }
2358
+ },
2359
+ {
2360
+ "name": "volume_channels_tail_nearest_reflection_ac0_float32",
2361
+ "provenance": {
2362
+ "notes": "Volumetric channel sharing across batches with a three-channel tail, all interpolation/padding modes and both coordinate conventions."
2363
+ },
2364
+ "attrs": { "mode": "nearest", "padding_mode": "reflection", "align_corners": 0 },
2365
+ "inputs": {
2366
+ "x": {
2367
+ "dtype": "float32",
2368
+ "shape": [2, 3, 5, 7, 9],
2369
+ "data": { "kind": "fillFloat32", "scale": 0.3, "sinStep": 0.17, "cosStep": 0.09 }
2370
+ },
2371
+ "grid": {
2372
+ "dtype": "float32",
2373
+ "shape": [2, 7, 9, 11, 3],
2374
+ "data": { "kind": "linspace", "start": -3.5, "end": 3.5 }
2375
+ }
2376
+ },
2377
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 3, 7, 9, 11], "tolerance": 0.0001, "relTolerance": 0.0001 } }
2378
+ },
2379
+ {
2380
+ "name": "volume_channels_tail_nearest_reflection_ac0_float16",
2381
+ "provenance": {
2382
+ "notes": "Volumetric channel sharing across batches with a three-channel tail, all interpolation/padding modes and both coordinate conventions."
2383
+ },
2384
+ "attrs": { "mode": "nearest", "padding_mode": "reflection", "align_corners": 0 },
2385
+ "inputs": {
2386
+ "x": {
2387
+ "dtype": "float16",
2388
+ "shape": [2, 3, 5, 7, 9],
2389
+ "data": { "kind": "fillFloat32", "scale": 0.3, "sinStep": 0.17, "cosStep": 0.09 }
2390
+ },
2391
+ "grid": {
2392
+ "dtype": "float16",
2393
+ "shape": [2, 7, 9, 11, 3],
2394
+ "data": { "kind": "linspace", "start": -3.5, "end": 3.5 }
2395
+ }
2396
+ },
2397
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 3, 7, 9, 11], "tolerance": 0.001, "relTolerance": 0.001 } }
2398
+ },
2399
+ {
2400
+ "name": "volume_channels_tail_nearest_reflection_ac1_float32",
2401
+ "provenance": {
2402
+ "notes": "Volumetric channel sharing across batches with a three-channel tail, all interpolation/padding modes and both coordinate conventions."
2403
+ },
2404
+ "attrs": { "mode": "nearest", "padding_mode": "reflection", "align_corners": 1 },
2405
+ "inputs": {
2406
+ "x": {
2407
+ "dtype": "float32",
2408
+ "shape": [2, 3, 5, 7, 9],
2409
+ "data": { "kind": "fillFloat32", "scale": 0.3, "sinStep": 0.17, "cosStep": 0.09 }
2410
+ },
2411
+ "grid": {
2412
+ "dtype": "float32",
2413
+ "shape": [2, 7, 9, 11, 3],
2414
+ "data": { "kind": "linspace", "start": -3.5, "end": 3.5 }
2415
+ }
2416
+ },
2417
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 3, 7, 9, 11], "tolerance": 0.0001, "relTolerance": 0.0001 } }
2418
+ },
2419
+ {
2420
+ "name": "volume_channels_tail_nearest_reflection_ac1_float16",
2421
+ "provenance": {
2422
+ "notes": "Volumetric channel sharing across batches with a three-channel tail, all interpolation/padding modes and both coordinate conventions."
2423
+ },
2424
+ "attrs": { "mode": "nearest", "padding_mode": "reflection", "align_corners": 1 },
2425
+ "inputs": {
2426
+ "x": {
2427
+ "dtype": "float16",
2428
+ "shape": [2, 3, 5, 7, 9],
2429
+ "data": { "kind": "fillFloat32", "scale": 0.3, "sinStep": 0.17, "cosStep": 0.09 }
2430
+ },
2431
+ "grid": {
2432
+ "dtype": "float16",
2433
+ "shape": [2, 7, 9, 11, 3],
2434
+ "data": { "kind": "linspace", "start": -3.5, "end": 3.5 }
2435
+ }
2436
+ },
2437
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 3, 7, 9, 11], "tolerance": 0.001, "relTolerance": 0.001 } }
2438
+ },
2439
+ {
2440
+ "name": "volume_channels_tail_cubic_zeros_ac0_float32",
2441
+ "provenance": {
2442
+ "notes": "Volumetric channel sharing across batches with a three-channel tail, all interpolation/padding modes and both coordinate conventions."
2443
+ },
2444
+ "attrs": { "mode": "cubic", "padding_mode": "zeros", "align_corners": 0 },
2445
+ "inputs": {
2446
+ "x": {
2447
+ "dtype": "float32",
2448
+ "shape": [2, 3, 5, 7, 9],
2449
+ "data": { "kind": "fillFloat32", "scale": 0.3, "sinStep": 0.17, "cosStep": 0.09 }
2450
+ },
2451
+ "grid": {
2452
+ "dtype": "float32",
2453
+ "shape": [2, 7, 9, 11, 3],
2454
+ "data": { "kind": "linspace", "start": -3.5, "end": 3.5 }
2455
+ }
2456
+ },
2457
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 3, 7, 9, 11], "tolerance": 0.0001, "relTolerance": 0.0001 } }
2458
+ },
2459
+ {
2460
+ "name": "volume_channels_tail_cubic_zeros_ac0_float16",
2461
+ "provenance": {
2462
+ "notes": "Volumetric channel sharing across batches with a three-channel tail, all interpolation/padding modes and both coordinate conventions."
2463
+ },
2464
+ "attrs": { "mode": "cubic", "padding_mode": "zeros", "align_corners": 0 },
2465
+ "inputs": {
2466
+ "x": {
2467
+ "dtype": "float16",
2468
+ "shape": [2, 3, 5, 7, 9],
2469
+ "data": { "kind": "fillFloat32", "scale": 0.3, "sinStep": 0.17, "cosStep": 0.09 }
2470
+ },
2471
+ "grid": {
2472
+ "dtype": "float16",
2473
+ "shape": [2, 7, 9, 11, 3],
2474
+ "data": { "kind": "linspace", "start": -3.5, "end": 3.5 }
2475
+ }
2476
+ },
2477
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 3, 7, 9, 11], "tolerance": 0.001, "relTolerance": 0.001 } }
2478
+ },
2479
+ {
2480
+ "name": "volume_channels_tail_cubic_zeros_ac1_float32",
2481
+ "provenance": {
2482
+ "notes": "Volumetric channel sharing across batches with a three-channel tail, all interpolation/padding modes and both coordinate conventions."
2483
+ },
2484
+ "attrs": { "mode": "cubic", "padding_mode": "zeros", "align_corners": 1 },
2485
+ "inputs": {
2486
+ "x": {
2487
+ "dtype": "float32",
2488
+ "shape": [2, 3, 5, 7, 9],
2489
+ "data": { "kind": "fillFloat32", "scale": 0.3, "sinStep": 0.17, "cosStep": 0.09 }
2490
+ },
2491
+ "grid": {
2492
+ "dtype": "float32",
2493
+ "shape": [2, 7, 9, 11, 3],
2494
+ "data": { "kind": "linspace", "start": -3.5, "end": 3.5 }
2495
+ }
2496
+ },
2497
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 3, 7, 9, 11], "tolerance": 0.0001, "relTolerance": 0.0001 } }
2498
+ },
2499
+ {
2500
+ "name": "volume_channels_tail_cubic_zeros_ac1_float16",
2501
+ "provenance": {
2502
+ "notes": "Volumetric channel sharing across batches with a three-channel tail, all interpolation/padding modes and both coordinate conventions."
2503
+ },
2504
+ "attrs": { "mode": "cubic", "padding_mode": "zeros", "align_corners": 1 },
2505
+ "inputs": {
2506
+ "x": {
2507
+ "dtype": "float16",
2508
+ "shape": [2, 3, 5, 7, 9],
2509
+ "data": { "kind": "fillFloat32", "scale": 0.3, "sinStep": 0.17, "cosStep": 0.09 }
2510
+ },
2511
+ "grid": {
2512
+ "dtype": "float16",
2513
+ "shape": [2, 7, 9, 11, 3],
2514
+ "data": { "kind": "linspace", "start": -3.5, "end": 3.5 }
2515
+ }
2516
+ },
2517
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 3, 7, 9, 11], "tolerance": 0.001, "relTolerance": 0.001 } }
2518
+ },
2519
+ {
2520
+ "name": "volume_channels_tail_cubic_border_ac0_float32",
2521
+ "provenance": {
2522
+ "notes": "Volumetric channel sharing across batches with a three-channel tail, all interpolation/padding modes and both coordinate conventions."
2523
+ },
2524
+ "attrs": { "mode": "cubic", "padding_mode": "border", "align_corners": 0 },
2525
+ "inputs": {
2526
+ "x": {
2527
+ "dtype": "float32",
2528
+ "shape": [2, 3, 5, 7, 9],
2529
+ "data": { "kind": "fillFloat32", "scale": 0.3, "sinStep": 0.17, "cosStep": 0.09 }
2530
+ },
2531
+ "grid": {
2532
+ "dtype": "float32",
2533
+ "shape": [2, 7, 9, 11, 3],
2534
+ "data": { "kind": "linspace", "start": -3.5, "end": 3.5 }
2535
+ }
2536
+ },
2537
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 3, 7, 9, 11], "tolerance": 0.0001, "relTolerance": 0.0001 } }
2538
+ },
2539
+ {
2540
+ "name": "volume_channels_tail_cubic_border_ac0_float16",
2541
+ "provenance": {
2542
+ "notes": "Volumetric channel sharing across batches with a three-channel tail, all interpolation/padding modes and both coordinate conventions."
2543
+ },
2544
+ "attrs": { "mode": "cubic", "padding_mode": "border", "align_corners": 0 },
2545
+ "inputs": {
2546
+ "x": {
2547
+ "dtype": "float16",
2548
+ "shape": [2, 3, 5, 7, 9],
2549
+ "data": { "kind": "fillFloat32", "scale": 0.3, "sinStep": 0.17, "cosStep": 0.09 }
2550
+ },
2551
+ "grid": {
2552
+ "dtype": "float16",
2553
+ "shape": [2, 7, 9, 11, 3],
2554
+ "data": { "kind": "linspace", "start": -3.5, "end": 3.5 }
2555
+ }
2556
+ },
2557
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 3, 7, 9, 11], "tolerance": 0.001, "relTolerance": 0.001 } }
2558
+ },
2559
+ {
2560
+ "name": "volume_channels_tail_cubic_border_ac1_float32",
2561
+ "provenance": {
2562
+ "notes": "Volumetric channel sharing across batches with a three-channel tail, all interpolation/padding modes and both coordinate conventions."
2563
+ },
2564
+ "attrs": { "mode": "cubic", "padding_mode": "border", "align_corners": 1 },
2565
+ "inputs": {
2566
+ "x": {
2567
+ "dtype": "float32",
2568
+ "shape": [2, 3, 5, 7, 9],
2569
+ "data": { "kind": "fillFloat32", "scale": 0.3, "sinStep": 0.17, "cosStep": 0.09 }
2570
+ },
2571
+ "grid": {
2572
+ "dtype": "float32",
2573
+ "shape": [2, 7, 9, 11, 3],
2574
+ "data": { "kind": "linspace", "start": -3.5, "end": 3.5 }
2575
+ }
2576
+ },
2577
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 3, 7, 9, 11], "tolerance": 0.0001, "relTolerance": 0.0001 } }
2578
+ },
2579
+ {
2580
+ "name": "volume_channels_tail_cubic_border_ac1_float16",
2581
+ "provenance": {
2582
+ "notes": "Volumetric channel sharing across batches with a three-channel tail, all interpolation/padding modes and both coordinate conventions."
2583
+ },
2584
+ "attrs": { "mode": "cubic", "padding_mode": "border", "align_corners": 1 },
2585
+ "inputs": {
2586
+ "x": {
2587
+ "dtype": "float16",
2588
+ "shape": [2, 3, 5, 7, 9],
2589
+ "data": { "kind": "fillFloat32", "scale": 0.3, "sinStep": 0.17, "cosStep": 0.09 }
2590
+ },
2591
+ "grid": {
2592
+ "dtype": "float16",
2593
+ "shape": [2, 7, 9, 11, 3],
2594
+ "data": { "kind": "linspace", "start": -3.5, "end": 3.5 }
2595
+ }
2596
+ },
2597
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 3, 7, 9, 11], "tolerance": 0.001, "relTolerance": 0.001 } }
2598
+ },
2599
+ {
2600
+ "name": "volume_channels_tail_cubic_reflection_ac0_float32",
2601
+ "provenance": {
2602
+ "notes": "Volumetric channel sharing across batches with a three-channel tail, all interpolation/padding modes and both coordinate conventions."
2603
+ },
2604
+ "attrs": { "mode": "cubic", "padding_mode": "reflection", "align_corners": 0 },
2605
+ "inputs": {
2606
+ "x": {
2607
+ "dtype": "float32",
2608
+ "shape": [2, 3, 5, 7, 9],
2609
+ "data": { "kind": "fillFloat32", "scale": 0.3, "sinStep": 0.17, "cosStep": 0.09 }
2610
+ },
2611
+ "grid": {
2612
+ "dtype": "float32",
2613
+ "shape": [2, 7, 9, 11, 3],
2614
+ "data": { "kind": "linspace", "start": -3.5, "end": 3.5 }
2615
+ }
2616
+ },
2617
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 3, 7, 9, 11], "tolerance": 0.0001, "relTolerance": 0.0001 } }
2618
+ },
2619
+ {
2620
+ "name": "volume_channels_tail_cubic_reflection_ac0_float16",
2621
+ "provenance": {
2622
+ "notes": "Volumetric channel sharing across batches with a three-channel tail, all interpolation/padding modes and both coordinate conventions."
2623
+ },
2624
+ "attrs": { "mode": "cubic", "padding_mode": "reflection", "align_corners": 0 },
2625
+ "inputs": {
2626
+ "x": {
2627
+ "dtype": "float16",
2628
+ "shape": [2, 3, 5, 7, 9],
2629
+ "data": { "kind": "fillFloat32", "scale": 0.3, "sinStep": 0.17, "cosStep": 0.09 }
2630
+ },
2631
+ "grid": {
2632
+ "dtype": "float16",
2633
+ "shape": [2, 7, 9, 11, 3],
2634
+ "data": { "kind": "linspace", "start": -3.5, "end": 3.5 }
2635
+ }
2636
+ },
2637
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 3, 7, 9, 11], "tolerance": 0.001, "relTolerance": 0.001 } }
2638
+ },
2639
+ {
2640
+ "name": "volume_channels_tail_cubic_reflection_ac1_float32",
2641
+ "provenance": {
2642
+ "notes": "Volumetric channel sharing across batches with a three-channel tail, all interpolation/padding modes and both coordinate conventions."
2643
+ },
2644
+ "attrs": { "mode": "cubic", "padding_mode": "reflection", "align_corners": 1 },
2645
+ "inputs": {
2646
+ "x": {
2647
+ "dtype": "float32",
2648
+ "shape": [2, 3, 5, 7, 9],
2649
+ "data": { "kind": "fillFloat32", "scale": 0.3, "sinStep": 0.17, "cosStep": 0.09 }
2650
+ },
2651
+ "grid": {
2652
+ "dtype": "float32",
2653
+ "shape": [2, 7, 9, 11, 3],
2654
+ "data": { "kind": "linspace", "start": -3.5, "end": 3.5 }
2655
+ }
2656
+ },
2657
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 3, 7, 9, 11], "tolerance": 0.0001, "relTolerance": 0.0001 } }
2658
+ },
2659
+ {
2660
+ "name": "volume_channels_tail_cubic_reflection_ac1_float16",
2661
+ "provenance": {
2662
+ "notes": "Volumetric channel sharing across batches with a three-channel tail, all interpolation/padding modes and both coordinate conventions."
2663
+ },
2664
+ "attrs": { "mode": "cubic", "padding_mode": "reflection", "align_corners": 1 },
2665
+ "inputs": {
2666
+ "x": {
2667
+ "dtype": "float16",
2668
+ "shape": [2, 3, 5, 7, 9],
2669
+ "data": { "kind": "fillFloat32", "scale": 0.3, "sinStep": 0.17, "cosStep": 0.09 }
2670
+ },
2671
+ "grid": {
2672
+ "dtype": "float16",
2673
+ "shape": [2, 7, 9, 11, 3],
2674
+ "data": { "kind": "linspace", "start": -3.5, "end": 3.5 }
2675
+ }
2676
+ },
2677
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 3, 7, 9, 11], "tolerance": 0.001, "relTolerance": 0.001 } }
2678
+ },
2679
+ {
2680
+ "name": "volume_channels_linear_reflection_c1",
2681
+ "provenance": {
2682
+ "notes": "Checks scalar, two-lane, complete four-lane and masked four-lane volumetric channel selection."
2683
+ },
2684
+ "attrs": { "mode": "linear", "padding_mode": "reflection", "align_corners": 1 },
2685
+ "inputs": {
2686
+ "x": { "dtype": "float32", "shape": [2, 1, 3, 5, 7], "data": { "kind": "fillFloat32", "scale": 0.3 } },
2687
+ "grid": {
2688
+ "dtype": "float32",
2689
+ "shape": [2, 5, 7, 9, 3],
2690
+ "data": { "kind": "linspace", "start": -2.5, "end": 2.5 }
2691
+ }
2692
+ },
2693
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 1, 5, 7, 9], "tolerance": 0.0001, "relTolerance": 0.0001 } }
2694
+ },
2695
+ {
2696
+ "name": "volume_channels_linear_reflection_c2",
2697
+ "provenance": {
2698
+ "notes": "Checks scalar, two-lane, complete four-lane and masked four-lane volumetric channel selection."
2699
+ },
2700
+ "attrs": { "mode": "linear", "padding_mode": "reflection", "align_corners": 0 },
2701
+ "inputs": {
2702
+ "x": { "dtype": "float32", "shape": [2, 2, 3, 5, 7], "data": { "kind": "fillFloat32", "scale": 0.3 } },
2703
+ "grid": {
2704
+ "dtype": "float32",
2705
+ "shape": [2, 5, 7, 9, 3],
2706
+ "data": { "kind": "linspace", "start": -2.5, "end": 2.5 }
2707
+ }
2708
+ },
2709
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 2, 5, 7, 9], "tolerance": 0.0001, "relTolerance": 0.0001 } }
2710
+ },
2711
+ {
2712
+ "name": "volume_channels_linear_reflection_c4",
2713
+ "provenance": {
2714
+ "notes": "Checks scalar, two-lane, complete four-lane and masked four-lane volumetric channel selection."
2715
+ },
2716
+ "attrs": { "mode": "linear", "padding_mode": "reflection", "align_corners": 0 },
2717
+ "inputs": {
2718
+ "x": { "dtype": "float32", "shape": [2, 4, 3, 5, 7], "data": { "kind": "fillFloat32", "scale": 0.3 } },
2719
+ "grid": {
2720
+ "dtype": "float32",
2721
+ "shape": [2, 5, 7, 9, 3],
2722
+ "data": { "kind": "linspace", "start": -2.5, "end": 2.5 }
2723
+ }
2724
+ },
2725
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 4, 5, 7, 9], "tolerance": 0.0001, "relTolerance": 0.0001 } }
2726
+ },
2727
+ {
2728
+ "name": "volume_channels_linear_reflection_c5",
2729
+ "provenance": {
2730
+ "notes": "Checks scalar, two-lane, complete four-lane and masked four-lane volumetric channel selection."
2731
+ },
2732
+ "attrs": { "mode": "linear", "padding_mode": "reflection", "align_corners": 1 },
2733
+ "inputs": {
2734
+ "x": { "dtype": "float32", "shape": [2, 5, 3, 5, 7], "data": { "kind": "fillFloat32", "scale": 0.3 } },
2735
+ "grid": {
2736
+ "dtype": "float32",
2737
+ "shape": [2, 5, 7, 9, 3],
2738
+ "data": { "kind": "linspace", "start": -2.5, "end": 2.5 }
2739
+ }
2740
+ },
2741
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 5, 5, 7, 9], "tolerance": 0.0001, "relTolerance": 0.0001 } }
2742
  }
2743
  ]
2744
  }