Skip to content

setBindGroup without dynamic offsets crashes on Safari #56

@miloszmaki

Description

@miloszmaki

Safari WebGPU crashes: GPUComputePassEncoder.setBindGroup called with wrong dynamic offsets overload/type (Uint32Array required)

Environment

  • App: C++/Wasm (Emscripten) + WebGPU

  • Browser:

    • ✅ Chrome: works
    • ❌ Safari: fails at first compute pass bind
  • Safari: WebGPU enabled via feature flag

  • Repro: any compute dispatch that calls wgpu_encoder_set_bind_group(..., dynamicOffsets=nullptr, numDynamicOffsets=0) through this library’s JS glue

What happens

On Safari, the app fails with:

Unhandled Promise Rejection: TypeError:
Argument 3 ('dynamicOffsetsData') to GPUComputePassEncoder.setBindGroup
must be an instance of Uint32Array

Stack points into the generated WebGPU shim and the call to setBindGroup.

Root cause (suspected)

The library’s glue code always calls the “dynamic offsets” overload of WebGPU setBindGroup, even when no dynamic offsets are used.

Current glue implementation (excerpt):

wgpu_encoder_set_bind_group: function(encoder, index, bindGroup, dynamicOffsets, numDynamicOffsets) {
  // ...
  wgpu[encoder]['setBindGroup'](index, wgpu[bindGroup], HEAPU32, dynamicOffsets >> 2, numDynamicOffsets);
},

When numDynamicOffsets == 0 (the common case), Safari appears to treat argument 3 as dynamicOffsetsData and enforces that it’s a Uint32Array, rejecting the call path. Chrome accepts this.

Expected behavior

If numDynamicOffsets == 0, the glue should call the 2-argument overload:

encoder.setBindGroup(index, bindGroupOrNull);

If numDynamicOffsets > 0, the glue should pass a real Uint32Array (e.g. via subarray) to satisfy Safari’s type checks.

Proposed fix

Branch on numDynamicOffsets and use Safari-friendly typed array handling:

wgpu_encoder_set_bind_group: function(encoder, index, bindGroup, dynamicOffsets, numDynamicOffsets) {
  const bg = (bindGroup === 0) ? null : wgpu[bindGroup];

  if (numDynamicOffsets === 0) {
    wgpu[encoder]['setBindGroup'](index, bg);
    return;
  }

  const start = (dynamicOffsets >>> 2);
  const offsetsView = HEAPU32.subarray(start, start + numDynamicOffsets);
  wgpu[encoder]['setBindGroup'](index, bg, offsetsView);
},

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions