[Web] [WebGPU] GatherND `computeSliceOffsets` emits `i32(vec4<u32>)` for data rank >= 5 — Tint rejects the ShaderModule
Maintainers usually reply within 2 days
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 74/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- javascript
- Domain
- machine-learning, web-dev
Research direction
Start at the JSEP WebGPU GatherND computeSliceOffsets program builder and inspect its generated input_dims and sizes_from_slice_dims_data indexing. Compare the dynamic packed-array indexing with Slice's calculateInputIndices helper, then verify that rank-5 GatherND shaders compile and produce values matching the CPU EP.
Written by the indexing model from the issue text.
Description
Describe the issue
Running any model containing a GatherND node whose data input has rank >= 5 on the WebGPU execution provider fails at shader-compilation time. The generated computeSliceOffsets helper kernel contains:
index += i32(uniforms.input_dims[input_dim_idx]);
input_dim_idx is a dynamic index (uniforms.batch_dims + dim_idx), and for rank >= 5 the input_dims uniform array is vec4-packed, so the expression yields vec4<u32>. Tint rejects it:
:28:18 error: no matching constructor for 'i32(vec4<u32>)'
[Invalid ShaderModule "computeSliceOffsets"] is invalid.
followed by the usual Invalid ComputePipeline / BindGroup / CommandBuffer cascade. The same graph runs fine on the WASM/CPU EP, and the equivalent rank-4 GatherND compiles and runs fine on WebGPU.
To reproduce
Minimal model (opset 17): GatherND, data float16 [1,1,4,4,8], indices int64 [1,1,8,2] (values in [0,4)), batch_dims = 2 → output float16 [1,1,8,8].
import numpy as np, onnx
from onnx import helper, TensorProto
data = helper.make_tensor_value_info('data', TensorProto.FLOAT16, [1,1,4,4,8])
idx = helper.make_tensor_value_info('indices', TensorProto.INT64, [1,1,8,2])
out = helper.make_tensor_value_info('out', TensorProto.FLOAT16, [1,1,8,8])
node = helper.make_node('GatherND', ['data','indices'], ['out'], batch_dims=2)
g = helper.make_graph([node], 'h1', [data, idx], [out], [])
m = helper.make_model(g, opset_imports=[helper.make_opsetid('', 17)])
m.ir_version = 8
onnx.save(m, 'h1_gathernd_r5.onnx')
const sess = await ort.InferenceSession.create(bytes, { executionProviders: ['webgpu'] });
await sess.run({ data, indices }); // WGSL validation errors, no usable output
Control: the same node with rank-4 data ([1,4,4,8], indices [1,8,2], batch_dims=1) works on WebGPU and is bit-identical to WASM.
Root cause (from the generated template)
In the JSEP WebGPU GatherND implementation (computeSliceOffsets program builder), the uniforms are declared as
{ name: 'input_dims', type: 'u32', length: o.length } // o = data dims
and the shader source is generated as
${o.length === 1
? 'index += i32(uniforms.input_dims);'
: 'index += i32(uniforms.input_dims[input_dim_idx]);'}
input_dim_idx is dynamic, so once the array is vec4-packed (length > 4, i.e. data rank >= 5) the subscript returns a whole vec4<u32> and i32(...) has no matching constructor. The sibling expression uniforms.sizes_from_slice_dims_data[dim_idx] has the same latent hazard for slice-dim counts > 4 (our graphs have 2, so only the first one fires). Other ops (e.g. Slice's calculateInputIndices) index packed uniform arrays through a helper that decomposes the dynamic index — GatherND's template does raw indexing instead. The one-line fix should be the same decomposition (vec-index + component swizzle) or keeping input_dims unpacked.
Expected behavior
Rank >= 5 GatherND compiles and returns the same values as the CPU EP (verified: the gather itself is well-defined; an exact Reshape+Gather rewrite of the node produces bit-identical results on CPU).
Environment
onnxruntime-web: 1.29.0, 1.30.0, and 1.31.0-dev.20260918-bc8e7ed75 (all three emit the identical broken shader)- Browser: Chrome (current), real GPU (AMD RDNA3,
maxStorageBuffersPerShaderStage: 10) - OS: Windows 11
Real-world impact
The BiRefNet Lite 512 matting graph contains 80 such GatherNDs (all rank-5 fp16 data, batch_dims=2, in the deformable-attention path), so the model is unusable on WebGPU without graph surgery. Happy to provide the full-graph repro or test a fix.
Urgency
No response
ONNX Runtime Installation
Built from Source
ONNX Runtime Version or Commit ID
1.29.0, 1.30.0, and 1.31.0-dev.20260918-bc8e7ed75
Execution Provider
'webgpu' (WebGPU)
- Dominant language
- C++
- Stars
- 22.2k
- Forks
- 4.4k
- Avg merge
- 3d 5h
- Merged PRs (30d)
- 312
Getting set up
Starts the project's dev container in your browser, under your own GitHub account.
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from microsoft/onnxruntime
-
platform:web
Difficulty 1/5 Under an hour Newbie friendliness 82/100
microsoft/onnxruntime#33570 ·
Maintainers usually reply within 2 days
-
C++ tests fail to compilePossibly taken @winstonwen727 claimed this 8 days ago. Open
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
microsoft/onnxruntime#33430 ·
Maintainers usually reply within 2 days
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
microsoft/onnxruntime#33300 ·
Maintainers usually reply within 2 days
-
LayerNormFusionL2 removes a public duplicate Sub outputPossibly taken @Yuhx141 claimed this 28 days ago. Open
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
microsoft/onnxruntime#32953 ·
Maintainers usually reply within 2 days
-
IsInitializerWithExpectedValue(int64_t) dereferences a null constant initializerPossibly taken @Yuhx141 claimed this 28 days ago. Open
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
microsoft/onnxruntime#32907 ·
Maintainers usually reply within 2 days
All issues in microsoft/onnxruntime
Similar issues
-
backlog documentation
Difficulty 2/5 1-3 hours Newbie friendliness 73/100
EchoTools/nevr-runtime#1328 ·
Maintainers usually reply within 1 day
-
Component - Build
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
HarbourMasters/Lighthouse#655 ·
Maintainers usually reply within 1 day
-
bug-unconfirmed
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
wazuh/wazuh#40870 · 1 comment ·
Maintainers usually reply within 1 day