Slang shader compilation (SPIR-V + WGSL)#

Two behaviourally identical drivers compile a Slang shader tree:

Lane

File

Linux / POSIX

linux/scripts/lib/slang-compile.sh

Windows

windows/scripts/modules/WindowsSlang.Common.psm1

They must be kept in step. Both are project-agnostic — nothing about a specific project is hard-coded. A thin wrapper sets the path variables, sources the library, and calls slang_compile_main. Everything else is data, read from a manifest JSON the wrapper points at.

The shell library deliberately does not set -e/-u/-o pipefail, so sourcing it cannot change the caller’s shell options; wrappers run under set -euo pipefail themselves.

Caller variables#

Required:

Variable

Meaning

SLANG_COMPILE_MANIFEST

the manifest JSON (schema below)

SLANG_COMPILE_SOURCE_ROOT

root of the .slang tree; every manifest path and every -I include path resolves against it

Optional (default in the right column):

Variable

Meaning

Default

SLANG_COMPILE_SPIRV_OUTPUT_ROOT

where spirv targets are written

<source root>/build/spirv

SLANG_COMPILE_WGSL_OUTPUT_ROOT

where wgsl targets are written

<source root>/build/wgsl

SLANG_COMPILE_COMBINED_OUTPUT_DIR

staging dir for the combined WGSL emit, before validation and copy

<source root>/build

SLANG_COMPILE_DEST_ROOT

root the manifest’s wgslMap dst paths resolve against, i.e. the consuming repo root

$PWD

Manifest schema#

Keys starting with _ are documentation and ignored.

Key

Shape

Meaning

manifest[]

{ file, entry, stage, targets[], disabled? }

one row per (entry point, target). file is relative to the source root; targets is any mix of "spirv" and "wgsl"; disabled rows stay as documentation without being compiled

wgslMap[]

{ src, out, dst }

the combined (whole-module) WGSL emit

depthTexturePatches

{ "<out>": [ { pattern, replacement } ] }

post-emit regex patches applied to that combined emit

minSlangcVersionForWgsl

"MAJOR.MINOR"

toolchain floor for the combined emit

Return codes from slang_compile_main#

Code

Meaning

0

success — also when the source root is absent and there is nothing to do

1

a slangc invocation failed, or an emit was rejected by the WGSL validator

2

a prerequisite is missing (python3, the manifest, or slangc itself)

Code 2 is deliberately not a silent skip: a missing slangc once let CI pass green with no compiled shaders at all.

The combined-emit outcomes#

_slang_emit_one_wgsl handles one wgslMap row and reports which of four things happened; slang_compile_combined_wgsl owns the counters and turns them into the summary line and the exit code.

Return

Meaning

What the caller does

0

emitted, patched, validated, copied to dst

counts it into SLANG_COMPILE_WGSL_EMITTED_COUNT

1

slangc itself failed

records the source in the emit-failed WARN block; the run still exits 0

2

the emit has a varying struct member with neither @builtin nor @location

records it in SLANG_COMPILE_INVALID_EMITS, which makes slang_compile_main exit 1

3

the row names a source file that is not in the tree

nothing — a row can point at a shader another repo owns

The asymmetry between 1 and 2 is deliberate. A failed slangc is a build problem the log already shows; an emit that succeeded and is structurally wrong is the regression this guard exists for, because the only thing standing between it and a committed, naga-rejected shader is that exit code. In both cases the destination file is left exactly as it was: nothing is copied until the validator has passed.

The guard exists because slangc 2026.1 (the Vulkan SDK build in the Linux image) drops @location(N) from varying struct members in the combined emit while emitting it per entry point, so a regeneration yields WGSL naga rejects. Two defences are both needed: below minSlangcVersionForWgsl nothing is emitted, so the checked-in WGSL is never overwritten, and at or above it every emit is validated. The manifest is read with python3, not jq, because the image has no jq.

A depthTexturePatches pattern that matches nothing is a WARNING, not a refusal — it means slangc’s output moved under the pattern, which is worth seeing but is not itself proof the emit is wrong.

Staleness#

An output is reused only when it is newer than its source and every .slang file under the source tree and the manifest file itself. That is conservative on purpose — an import or a manifest edit rebuilds every dependent.