Skip to content

Conversation

@RenaudRohlinger
Copy link
Collaborator

@RenaudRohlinger RenaudRohlinger commented Dec 9, 2025

WGSL specification does not allow ptr<storage, ...> (storage pointers) as function parameters (gpuweb/gpuweb#2268). The naga WGSL validator (used by WebGPU) now strictly enforces this, causing errors like:
Argument 'bvh_index' at index 0 is a pointer of space Storage, which can't be passed into functions.
The issue wasn't brought yet as it seems to be patched internally in Chrome since r123 https://developer.chrome.com/blog/new-in-webgpu-123#unrestricted_pointer_parameters_in_wgsl but crashes silently Safari and explicitly in Firefox:
image

When using wgslFn with storage buffers passed as ptr<storage, array<T>, access> parameters, Three.js was generating invalid WGSL code.
The Solution
Storage buffers must be declared as module-level variables with @group/@binding attributes and accessed directly, not passed as function parameters.

How It Works

  1. When a wgslFn is called with storage buffer parameters, FunctionCallNode builds those buffers (registering them as module-level bindings) and creates a mapping of parameter names to binding names
  2. This map is propagated to all included/nested functions
  3. When generating WGSL code, WGSLNodeFunction.getCode() transforms the code:
    • Removes ptr<storage, ...> parameters from signatures
    • Replaces variable references with the global binding names
    • Removes storage arguments from internal function calls

Alternative 1:

  1. Add the possibility in WGSL to create storage buffer as module-level variables vua wgsl() or code() and automatically bind them so we can access these buffers within the wgslFn without having to forward them as parameters.
  2. Add proper documentation and catch error if ptr<storage,...> and such are being used.

Alternative 2:
It's possible to bypass the issue using a mix of Fn and wgslFn but then requires very advanced knowledge and tricks with the NodeBuilder, and I don't think any developer coming from WebGPU will understand as it's pretty counterintuitive especially having to use TSL when trying to use plain wgsl().

This contribution is funded by Utsubo && Threejs Blocks

@RenaudRohlinger RenaudRohlinger requested a review from sunag December 9, 2025 06:22
@github-actions
Copy link

github-actions bot commented Dec 9, 2025

📦 Bundle size

Full ESM build, minified and gzipped.

Before After Diff
WebGL 350.93
83.27
350.93
83.27
+0 B
+0 B
WebGPU 616.43
171.09
618.27
171.72
+1.84 kB
+633 B
WebGPU Nodes 615.03
170.83
616.87
171.47
+1.84 kB
+641 B

🌳 Bundle size after tree-shaking

Minimal build including a renderer, camera, empty scene, and dependencies.

Before After Diff
WebGL 483.23
118.17
483.23
118.17
+0 B
+0 B
WebGPU 687.34
186.69
689.18
187.32
+1.84 kB
+640 B
WebGPU Nodes 637.18
173.9
639.02
174.53
+1.84 kB
+638 B

@@ -1,4 +1,5 @@
import CodeNode from './CodeNode.js';
import { nodeObject } from '../tsl/TSLBase.js';

Check notice

Code scanning / CodeQL

Unused variable, import, function or class Note

Unused import nodeObject.
@RenaudRohlinger RenaudRohlinger marked this pull request as draft December 9, 2025 06:27
@sunag
Copy link
Collaborator

sunag commented Dec 10, 2025

Do you think we could implement the keyword functions created in the past for string replacement?
#23766 Perhaps we need to orient her towards the function call.

Is the PR considering what happens if we have different buffers and function calls being executed within the same function? I believe that in some cases, under this principle, we will have more than one code for the same function since we are replacing its source code?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants