From cef10799e1de268459ad11898b08631703580749 Mon Sep 17 00:00:00 2001 From: Martin Valigursky Date: Wed, 27 May 2026 09:36:15 +0100 Subject: [PATCH] fix(gsplat): make writeSortIndirectArgs WGSL helper portable for Firefox WebGPU The helper took the indirect dispatch buffer as a ptr parameter. Passing storage-space pointers across function boundaries requires the 'unrestricted_pointer_parameters' WGSL language feature, which Chrome (Dawn) enables by default but Firefox (naga) rejects with a shader validation error. Drop the pointer parameter and reference the global indirectDispatchArgs binding directly. Both call sites already used that exact binding name. --- .../chunks/common/comp/sort-indirect-args.js | 27 +++++++++++-------- ...te-gsplat-projector-write-indirect-args.js | 2 +- .../compute-gsplat-write-indirect-args.js | 2 +- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/scene/shader-lib/wgsl/chunks/common/comp/sort-indirect-args.js b/src/scene/shader-lib/wgsl/chunks/common/comp/sort-indirect-args.js index 40c00c7a716..3bf4e4e5d7a 100644 --- a/src/scene/shader-lib/wgsl/chunks/common/comp/sort-indirect-args.js +++ b/src/scene/shader-lib/wgsl/chunks/common/comp/sort-indirect-args.js @@ -5,8 +5,14 @@ export default /* wgsl */` // dispatch of ceil(count / granularity[i]) workgroups into slots // (baseSlot + i) for every active slot i. // +// Contract: the calling shader must declare a storage binding named +// 'indirectDispatchArgs' of type array with read_write access. The helper +// references that global directly rather than taking it as a pointer +// parameter, because passing storage pointers across function boundaries +// requires the 'unrestricted_pointer_parameters' WGSL language feature, which +// is not portable (Firefox/naga rejects it). +// // Parameters: -// buf - indirect dispatch buffer (plain array, not atomic). // baseSlot - index of the first slot to write (u32 offset = baseSlot * 3). // count - number of elements the sort will process. // slotInfo - sorter metadata, obtained verbatim from @@ -16,7 +22,6 @@ export default /* wgsl */` // .y/.z/.w = per-slot elements-per-workgroup (granularity); // unused trailing entries are 0. fn writeSortIndirectArgs( - buf: ptr, read_write>, baseSlot: u32, count: u32, slotInfo: vec4 @@ -27,25 +32,25 @@ fn writeSortIndirectArgs( let g = slotInfo.y; let wc = (count + g - 1u) / g; let off = baseSlot * 3u; - (*buf)[off + 0u] = wc; - (*buf)[off + 1u] = 1u; - (*buf)[off + 2u] = 1u; + indirectDispatchArgs[off + 0u] = wc; + indirectDispatchArgs[off + 1u] = 1u; + indirectDispatchArgs[off + 2u] = 1u; } if (n >= 2u) { let g = slotInfo.z; let wc = (count + g - 1u) / g; let off = (baseSlot + 1u) * 3u; - (*buf)[off + 0u] = wc; - (*buf)[off + 1u] = 1u; - (*buf)[off + 2u] = 1u; + indirectDispatchArgs[off + 0u] = wc; + indirectDispatchArgs[off + 1u] = 1u; + indirectDispatchArgs[off + 2u] = 1u; } if (n >= 3u) { let g = slotInfo.w; let wc = (count + g - 1u) / g; let off = (baseSlot + 2u) * 3u; - (*buf)[off + 0u] = wc; - (*buf)[off + 1u] = 1u; - (*buf)[off + 2u] = 1u; + indirectDispatchArgs[off + 0u] = wc; + indirectDispatchArgs[off + 1u] = 1u; + indirectDispatchArgs[off + 2u] = 1u; } } `; diff --git a/src/scene/shader-lib/wgsl/chunks/gsplat/compute-gsplat-projector-write-indirect-args.js b/src/scene/shader-lib/wgsl/chunks/gsplat/compute-gsplat-projector-write-indirect-args.js index 1333de4c065..6391d6b2c08 100644 --- a/src/scene/shader-lib/wgsl/chunks/gsplat/compute-gsplat-projector-write-indirect-args.js +++ b/src/scene/shader-lib/wgsl/chunks/gsplat/compute-gsplat-projector-write-indirect-args.js @@ -53,7 +53,7 @@ fn main() { // Sort dispatch args at sortSlotBase (no keygen slot — sort keys come from the // projector pass directly). - writeSortIndirectArgs(&indirectDispatchArgs, uniforms.sortSlotBase, count, uniforms.sortIndirectInfo); + writeSortIndirectArgs(uniforms.sortSlotBase, count, uniforms.sortIndirectInfo); } `; diff --git a/src/scene/shader-lib/wgsl/chunks/gsplat/compute-gsplat-write-indirect-args.js b/src/scene/shader-lib/wgsl/chunks/gsplat/compute-gsplat-write-indirect-args.js index fced9b09fd6..039aa9d925e 100644 --- a/src/scene/shader-lib/wgsl/chunks/gsplat/compute-gsplat-write-indirect-args.js +++ b/src/scene/shader-lib/wgsl/chunks/gsplat/compute-gsplat-write-indirect-args.js @@ -75,7 +75,7 @@ fn main(@builtin(global_invocation_id) gid: vec3u) { // Sort dispatch slots — delegated to the sorter-agnostic helper so this // shader doesn't need to know how many slots or what granularity the // active radix sort backend uses. - writeSortIndirectArgs(&indirectDispatchArgs, keygenSlot + 1u, count, uniforms.sortIndirectInfo); + writeSortIndirectArgs(keygenSlot + 1u, count, uniforms.sortIndirectInfo); // Write sortElementCount for sort shaders (= visibleCount) sortElementCountBuf[0] = count;