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.