The Pass
How a BWSL pass groups shader stages, attributes, helper functions, and stage composition rules.
Pressure-test the syntax
Take the concept from this page into the playground and deliberately break a pass, binding, or type signature to see how the compiler responds.
Try a Live EditA pass is the unit of compilation inside a pipeline. Each pass is either:
- a graphics pass with
vertexand optionalfragment - a compute pass with one
computeblock
Graphics Passes
pass "Main" {
use attributes { position, texcoord }
vertex {
output.position = float4(attributes.position, 1.0);
output.uv = attributes.texcoord;
}
fragment {
output.color = float4(input.uv, 0.0, 1.0);
}
}
Notes:
use attributes { ... }is only valid on graphics passes- the vertex stage must write
output.position - the fragment stage is optional
- fragment color outputs can be declared with an
outputs {}block
Compute Passes
pass "Simulate" {
compute "Main" [64, 1, 1] {
uint3 gid = input.global_id;
}
}
Rules enforced by the parser:
- a pass cannot combine
computewithvertexorfragment - only one
computeblock is allowed per pass - compute passes cannot use
use attributes
Pass-Scoped Functions
Helper functions can live inside a pass and are only visible there:
pass "Main" {
use attributes { position }
offsetPos :: (float3 p) -> float3 {
return p + float3(0.0, 0.1, 0.0);
}
vertex {
output.position = float4(offsetPos(attributes.position), 1.0);
}
}
Stage Assignment
Pass stages can be declared inline or assigned from expressions that resolve to stage-returning helper functions.
Inline:
pass "Main" {
use attributes { position }
vertex {
output.position = float4(attributes.position, 1.0);
}
}
Assigned:
vertexFunc :: () -> vertex_function {
vertex {
output.position = float4(0.0, 0.0, 1.0, 1.0);
}
}
pass "Main" {
use attributes { position }
vertex = vertexFunc()
}
Stage Reuse
You can reuse a stage from another pass:
pass "Base" {
use attributes { position }
vertex {
output.position = float4(attributes.position, 1.0);
}
}
pass "Reuse" {
use attributes { position }
vertex = "Base".vertex
}
Pass-Block Helpers
Functions can return pass_block to package a reusable pass body:
sprite :: () -> pass_block {
pass {
use attributes { position, texCoord }
vertex {
output.position = float4(attributes.position, 1.0);
output.uv = attributes.texCoord;
}
fragment {
output.color = float4(input.uv, 0.0, 1.0);
}
}
}
pass "Sprites" = sprite();
Module-defined pass blocks can be instantiated with attribute, resource, and variant mappings. See Pass Blocks for the full mapping rules.
Fragment Outputs
A graphics pass can declare fragment color outputs before its stage bodies:
pass "GBuffer" {
use attributes { position, normal, texcoord }
outputs {
color: float4
normal: float4
material: float4 @location(3)
}
vertex {
output.position = float4(attributes.position, 1.0);
output.normal = attributes.normal;
output.uv = attributes.texcoord;
}
fragment {
output.color = float4(1.0);
output.normal = float4(normalize(input.normal) * 0.5 + 0.5, 1.0);
output.material = float4(input.uv, 0.0, 1.0);
}
}
Declaration order assigns color attachment locations starting at 0. Use @location(n) when a pass needs a specific attachment index.
If the pass has no outputs block, the fragment stage keeps the default output surface: output.color at location 0 plus the built-in output.depth. Additional fragment output.* fields must be listed in outputs.
outputs declares color attachments only. Do not list output.depth there; write output.depth directly in the fragment stage when the pass needs custom depth.
Depth-Only Passes
Depth-only graphics passes can disable fragment output explicitly:
pass "Shadow" {
use attributes { position }
vertex {
output.position = float4(attributes.position, 1.0);
}
fragment = null
}