Tested versions
reproducible in Godot 4.6.2.stable, custom build (commit f329e39, built 2026-05-13 03:14:44 UTC) — from the webgpu-4.6.2 tag of dwalter/godotwebgpu
System information
Godot v4.6.2.stable (f329e39) - Windows 11 (build 26200) - Multi-window, 2 monitors - OpenGL 3 (Compatibility) - NVIDIA GeForce RTX 2060 SUPER (NVIDIA; 32.0.15.9186) - AMD Ryzen 7 3700X 8-Core Processor (16 threads) - 31.93 GiB memory
Issue description
On the webgpu-4.6.2 branch, exporting a project that uses any non-trivial custom shader_type spatial shader with
rendering/renderer/rendering_method.web="mobile" fails at runtime with:
SHADER ERROR: Too many varyings used in shader (17 used, maximum supported is 16).
ERROR: Shader compilation failed.
at: set_code (servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp:176)
…followed by cascading GPUValidationError | [Invalid RenderPipeline "pipe#19:SceneForwardMobileShaderRD:0"] errors.
Root cause
servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp:827 sets:
actions.base_varying_index = 15;
The WebGPU backend correctly reports the spec-minimum hard limit:
// drivers/webgpu/rendering_device_driver_webgpu.cpp:8739
case LIMIT_MAX_SHADER_VARYINGS: return device_limits.maxInterStageShaderVariables;
…which is 16 on the vast majority of WebGPU implementations (Chrome 113+ on most GPUs reports exactly the spec floor).
With the base index at 15, that leaves a single inter-stage slot for user-declared varyings. Any spatial shader with
two or more varyings, or a single varying that occupies >1 slot (e.g. mat3), fails to compile.
In contrast, stock Godot on desktop Vulkan typically reports maxInterStageShaderVariables ≥ 31, so the 15-slot
reservation is invisible there.
(In our actual project: a parallax + MultiMesh ubershader using INSTANCE_CUSTOM with 6 user varyings — including mat3
tbn — for forward_mobile. Packing scalars and folding face_id into a vec4's .w reduced user-slot consumption but
couldn't get below 2 used slots, which still trips the cap.)
Forward+ is not a viable fallback
Switching to rendering_method.web="forward_plus" immediately hits a separate Tint bug:
thirdparty/tint/src/tint/lang/spirv/reader/parser/parser.cc:670
internal compiler error: TINT_UNIMPLEMENTED unhandled SPIR-V BuiltIn: HelperInvocation (val = 23)
So today there's no working renderer choice on web for projects that use any custom spatial shader.
Suggested fixes (not mutually exclusive)
- Reduce actions.base_varying_index on the WebGPU/mobile path. Audit which engine varyings are actually consumed by
forward_mobile/scene_forward_mobile.glsl after the WebGPU-specific defines are applied — several of the 15 reserved slots may be dead under WebGPU and could be reclaimed.
- Pack engine-internal varyings more aggressively (e.g. combine small scalars into shared vec4s) for the WebGPU backend, raising the effective ceiling.
- Implement HelperInvocation in thirdparty/tint/src/tint/lang/spirv/reader/parser/parser.cc so forward_plus becomes usable as a fallback for shader-heavy projects.
- Detect and report this earlier — at export time or at editor startup with WebGPU active — so users don't discover it as a cascade of WGSL validation errors at first frame.
Environment
- Branch / tag: webgpu-4.6.2
- Host: Windows 11 (build via MSVC v143; emsdk 4.0.10)
- Browser: Chrome 113+ (also reproduced in Edge)
- Project rendering method: gl_compatibility (web override: mobile → cap error; forward_plus → HelperInvocation)
Workaround we attempted
Stubbed drivers/webgpu/wgsl_precompiled.gen.h (_wgsl_precompiled_count = 0) and commented out the SCsub
wgsl_precompile step, because drivers/webgpu/tint_cli/build.sh requires bash + a POSIX C++ toolchain and doesn't run
on Windows MSVC hosts. Runtime falls back to Tint successfully — unrelated to the issue above but worth noting as a
separate Windows-build paper-cut.
Steps to reproduce
- scons platform=web target=template_release dlink_enabled=yes webgpu=yes opengl3=no threads=no
- New project, single MeshInstance3D with this minimal ShaderMaterial:
shader_type spatial;
varying vec3 a;
varying vec3 b;
void vertex() { a = VERTEX; b = NORMAL; }
void fragment() { ALBEDO = a + b; }
- Project Settings → rendering/renderer/rendering_method.web = "mobile"
- Export with the WebGPU template, open in Chrome 113+ → same error.
Minimal reproduction project (MRP)
n/a
Tested versions
reproducible in Godot 4.6.2.stable, custom build (commit f329e39, built 2026-05-13 03:14:44 UTC) — from the webgpu-4.6.2 tag of dwalter/godotwebgpu
System information
Godot v4.6.2.stable (f329e39) - Windows 11 (build 26200) - Multi-window, 2 monitors - OpenGL 3 (Compatibility) - NVIDIA GeForce RTX 2060 SUPER (NVIDIA; 32.0.15.9186) - AMD Ryzen 7 3700X 8-Core Processor (16 threads) - 31.93 GiB memory
Issue description
On the webgpu-4.6.2 branch, exporting a project that uses any non-trivial custom shader_type spatial shader with
rendering/renderer/rendering_method.web="mobile" fails at runtime with:
SHADER ERROR: Too many varyings used in shader (17 used, maximum supported is 16).
ERROR: Shader compilation failed.
at: set_code (servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp:176)
…followed by cascading GPUValidationError | [Invalid RenderPipeline "pipe#19:SceneForwardMobileShaderRD:0"] errors.
Root cause
servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp:827 sets:
actions.base_varying_index = 15;
The WebGPU backend correctly reports the spec-minimum hard limit:
// drivers/webgpu/rendering_device_driver_webgpu.cpp:8739
case LIMIT_MAX_SHADER_VARYINGS: return device_limits.maxInterStageShaderVariables;
…which is 16 on the vast majority of WebGPU implementations (Chrome 113+ on most GPUs reports exactly the spec floor).
With the base index at 15, that leaves a single inter-stage slot for user-declared varyings. Any spatial shader with
two or more varyings, or a single varying that occupies >1 slot (e.g. mat3), fails to compile.
In contrast, stock Godot on desktop Vulkan typically reports maxInterStageShaderVariables ≥ 31, so the 15-slot
reservation is invisible there.
(In our actual project: a parallax + MultiMesh ubershader using INSTANCE_CUSTOM with 6 user varyings — including mat3
tbn — for forward_mobile. Packing scalars and folding face_id into a vec4's .w reduced user-slot consumption but
couldn't get below 2 used slots, which still trips the cap.)
Forward+ is not a viable fallback
Switching to rendering_method.web="forward_plus" immediately hits a separate Tint bug:
thirdparty/tint/src/tint/lang/spirv/reader/parser/parser.cc:670
internal compiler error: TINT_UNIMPLEMENTED unhandled SPIR-V BuiltIn: HelperInvocation (val = 23)
So today there's no working renderer choice on web for projects that use any custom spatial shader.
Suggested fixes (not mutually exclusive)
forward_mobile/scene_forward_mobile.glsl after the WebGPU-specific defines are applied — several of the 15 reserved slots may be dead under WebGPU and could be reclaimed.
Environment
Workaround we attempted
Stubbed drivers/webgpu/wgsl_precompiled.gen.h (_wgsl_precompiled_count = 0) and commented out the SCsub
wgsl_precompile step, because drivers/webgpu/tint_cli/build.sh requires bash + a POSIX C++ toolchain and doesn't run
on Windows MSVC hosts. Runtime falls back to Tint successfully — unrelated to the issue above but worth noting as a
separate Windows-build paper-cut.
Steps to reproduce
shader_type spatial;
varying vec3 a;
varying vec3 b;
void vertex() { a = VERTEX; b = NORMAL; }
void fragment() { ALBEDO = a + b; }
Minimal reproduction project (MRP)
n/a