Default Annotations
To keep simple shaders short, wgsl-rs applies a few default WGSL I/O annotations when an entry point returns a bare vector without an explicit struct.
Vertex Returning Vec4f
A vertex entry point that returns Vec4f directly (rather than a struct) is automatically annotated with @builtin(position) on the return value:
#![allow(unused)]
fn main() {
#[vertex]
pub fn vtx_main(#[builtin(vertex_index)] vertex_index: u32) -> Vec4f {
vec4f(0.0, 0.0, 0.0, 1.0)
}
}
@vertex
fn vtx_main(@builtin(vertex_index) vertex_index: u32) -> @builtin(position) vec4<f32> {
return vec4<f32>(0.0, 0.0, 0.0, 1.0);
}
This matches the most common vertex shader shape: produce a clip-space position and nothing else.
Fragment Returning Vec4f
A fragment entry point that returns Vec4f directly is automatically annotated with @location(0):
#![allow(unused)]
fn main() {
#[fragment]
pub fn frag_main() -> Vec4f {
vec4f(1.0, 0.0, 0.0, 1.0)
}
}
@fragment
fn frag_main() -> @location(0) vec4<f32> {
return vec4<f32>(1.0, 0.0, 0.0, 1.0);
}
This is the single-render-target case.
When to Use Explicit Annotations
Use a struct return type instead of the bare defaults whenever you need to emit more than one output value:
- A vertex shader that also writes inter-stage varyings (color, UV, normal, …).
- A fragment shader writing multiple render targets (MRT) — use
#[location(N)]per field. - A vertex shader whose position should be
@invariant— put#[builtin(position)]and#[invariant]on the field. - Dual-source blending — use
#[blend_src(0)]and#[blend_src(1)]on two fields.
For example, switching from the default to a struct:
#![allow(unused)]
fn main() {
pub struct VertexOutput {
#[builtin(position)]
pub clip_position: Vec4f,
#[location(0)]
pub color: Vec4f,
}
#[vertex]
pub fn vs_main(#[builtin(vertex_index)] vi: u32) -> VertexOutput {
VertexOutput {
clip_position: vec4f(0.0, 0.0, 0.0, 1.0),
color: vec4f(1.0, 0.0, 0.0, 1.0),
}
}
}
See Inter-stage IO for the full set of field attributes.
Compute Entry Points
Compute entry points return () (no value) and have no default annotations; their I/O is entirely through builtins on parameters and through binding macros. See Vertex / Fragment / Compute.
Custom IO via Struct Fields
For fragment inputs, accept a struct parameter whose fields mirror the vertex output struct. wgsl-rs allows the same struct to be used on both sides, which is the recommended shared-inter-stage pattern:
#![allow(unused)]
fn main() {
#[fragment]
pub fn fs_main(input: VertexOutput) -> Vec4f {
input.color
}
}
The defaults apply only to bare Vec4f returns; once you return or accept a struct, every IO attribute must be explicit on its fields.