Open-Source Wikis

/

wgpu

/

Features

/

Shader translation

gfx-rs/wgpu

Shader translation

Shader translation is one of the most distinctive parts of wgpu. The user supplies a shader in WGSL, GLSL, SPIR-V, or pre-built naga IR; the runtime needs the shader in whatever language the active backend's driver wants. This page traces the path.

End-to-end flow

graph LR
    UserSrc[User shader source<br/>WGSL / GLSL / SPIR-V / naga IR] --> Wgpu[wgpu::Device::create_shader_module]
    Wgpu --> Bridge[wgpu-naga-bridge]
    Bridge --> Front[naga::front::*]
    Front --> Mod[naga::Module]
    Mod --> Valid[naga::valid::Validator]
    Valid --> Info[ModuleInfo]
    Info --> Bridge2[wgpu-naga-bridge]
    Bridge2 -->|backend = vulkan| SpvOut[naga::back::spv]
    Bridge2 -->|backend = metal| MslOut[naga::back::msl]
    Bridge2 -->|backend = dx12| HlslOut[naga::back::hlsl]
    Bridge2 -->|backend = gles| GlslOut[naga::back::glsl]
    SpvOut -->|SPIR-V| Vk[Vulkan driver]
    MslOut -->|MSL source| Mtl[Metal compiler]
    HlslOut -->|HLSL source| Dxc[DXC / FXC]
    Dxc -->|DXIL / DXBC| Dx12[D3D12 driver]
    GlslOut -->|GLSL source| Gl[GL driver]

On the WebGPU backend (browser), the flow is much shorter: the WGSL string is passed unchanged to navigator.gpu, and the browser's WebGPU implementation handles validation and translation.

At each step

1. User-supplied source

wgpu::ShaderModuleDescriptor (wgpu/src/api/shader_module.rs) accepts:

  • Wgsl(Cow<'_, str>) — WGSL source. Default and most common.
  • Glsl { shader, stage, defines } — GLSL source. Requires the glsl feature.
  • SpirV(Cow<'_, [u32]>) — SPIR-V binary. Requires the spirv feature.
  • Naga(Cow<'_, naga::Module>) — pre-parsed IR. Requires the naga-ir feature.
  • Dummy(PhantomData) — for builds that omit shader compilation.
  • Plus a Passthrough(...) variant for backends that take backend-native shaders directly.

2. Frontend

If the input is source, the relevant naga::front::* parser runs. WGSL has the most polished diagnostics; GLSL and SPIR-V are functional. See crates/naga/frontends.

3. Validator

naga::valid::Validator::validate runs the IR through every checked rule. The Capabilities set is determined by wgpu-naga-bridge from the device's Features. Failed validation surfaces as a WithSpan<ValidationError>; the WGSL frontend's diagnostic formatter renders it with source-position info.

See crates/naga/validation.

4. Backend code generation

wgpu-naga-bridge picks the right backend based on the active wgpu-hal backend:

  • Vulkan → naga::back::spv.
  • Metal → naga::back::msl.
  • DX12 → naga::back::hlsl (then DXC compiles HLSL to DXIL inside wgpu-hal/src/dx12/shader_compilation.rs).
  • GLES → naga::back::glsl.

Each backend carries a knob like Version::Embedded { version, is_webgl } (GLSL) or ShaderModel::V5_1 (HLSL) so the output matches what the driver actually expects.

5. Pipeline-constant substitution

naga/src/back/pipeline_constants.rs replaces @override values with concrete values just before code emission. This happens once per pipeline (when the RenderPipeline or ComputePipeline is created), not once per shader module. Multiple pipelines built from the same shader can therefore have different override values.

6. Driver compilation

The driver takes over:

  • Vulkan: vkCreateShaderModule validates SPIR-V and pre-compiles for the GPU.
  • Metal: MTLDevice::newLibraryWithSource: compiles MSL.
  • DX12: DXC produces DXIL from HLSL; D3D12 then accepts DXIL or DXBC into the pipeline state.
  • GL/GLES: glCompileShader + glLinkProgram, deferred until the program is first used.

What can go wrong

  • WGSL not in the spec yetnaga's WGSL frontend is constantly catching up to the WebGPU/WGSL working draft. README.md is candid: "the wgpu crate and the version of WGSL it implements will likely differ from what is specified." Bug reports against naga are tracked with the naga label on the wgpu issue tracker.
  • GLSL → SPIR-V handoff — the GLSL frontend is more limited than the WGSL one. Compute shaders, subgroup ops, and some atomics are not fully supported.
  • HLSL semantics quirksnaga::back::hlsl emits HLSL that DXC then re-compiles. There are edge cases where a user's WGSL is valid, the IR is valid, the HLSL is valid syntactically, but DXC's later checks reject the output. These tend to surface as "shader compilation failed" errors during pipeline creation. WGPU_DX12_COMPILER=fxc may behave differently than dxc here.
  • Driver bugs — vendors occasionally miscompile shaders. Workarounds live in the relevant wgpu-hal backend or in naga's backend (emitting a slightly different but equivalent shader).

Bypassing translation

If you have a backend-native shader you'd rather use directly:

  • The Passthrough variant of ShaderModuleDescriptor bypasses naga and sends a string straight to the driver. Used when integrating with engines that have their own shader pipelines.
  • Naga(...) skips frontend parsing.

Diagnostics

  • RUST_LOG=naga=debug,wgpu_core::pipeline=trace shows each translation step.
  • naga (the CLI) parses, validates, and emits in any direction. Useful for reproducing issues without the rest of the stack.
  • Instance::AUTOMATIC_SHADER_FALLBACK (InstanceFlags) lets naga apply automatic conversions when a user shader is borderline (e.g. interpolation defaults).

See also

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Shader translation – wgpu wiki | Factory