Skip to content

forward_mobile renderer effectively unusable with user shaders on WebGPU (only 1 user varying slot available) #1

@moodster321

Description

@moodster321

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)

  1. 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.
  2. Pack engine-internal varyings more aggressively (e.g. combine small scalars into shared vec4s) for the WebGPU backend, raising the effective ceiling.
  3. 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.
  4. 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

  1. scons platform=web target=template_release dlink_enabled=yes webgpu=yes opengl3=no threads=no
  2. 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; }
  3. Project Settings → rendering/renderer/rendering_method.web = "mobile"
  4. Export with the WebGPU template, open in Chrome 113+ → same error.

Minimal reproduction project (MRP)

n/a

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