Vertex / Fragment / Compute
WGSL has three shader stages, each with its own entry-point attribute. wgsl-rs exposes them as Rust attributes that the macro translates to @vertex, @fragment, and @compute.
| Attribute | WGSL | Notes |
|---|---|---|
#[vertex] | @vertex | One per pipeline's vertex stage. |
#[fragment] | @fragment | One per pipeline's fragment stage. |
#[compute] | @compute | Requires #[workgroup_size(...)]. |
A module may contain any combination of entry points. Functions without these attributes transpile to ordinary WGSL functions.
Vertex
A vertex entry point takes per-vertex/per-instance inputs (builtins and location-tagged values) and returns a position. Returning a bare Vec4f is automatically annotated with @builtin(position); see Default Annotations.
#![allow(unused)] fn main() { #[vertex] pub fn vtx_main(#[builtin(vertex_index)] vertex_index: u32) -> Vec4f { const POS: [Vec2f; 3] = [ vec2f(0.0, 0.5), vec2f(-0.5, -0.5), vec2f(0.5, -0.5), ]; let position = POS[vertex_index as usize]; vec4f(position.x, position.y, 0.0, 1.0) } }
@vertex
fn vtx_main(@builtin(vertex_index) vertex_index: u32) -> vec4<f32> {
/* ... */
}
Fragment
A fragment entry point takes inter-stage inputs (locations and builtins such as @builtin(front_facing)) and returns a color. Returning a bare Vec4f 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() -> vec4<f32> {
return vec4<f32>(1.0, 0.0, 0.0, 1.0);
}
Compute
A compute entry point must declare a workgroup size. Use a single integer for a 1D dispatch or three integers for a 3D dispatch:
#[compute] #[workgroup_size(64)] pub fn main(#[builtin(global_invocation_id)] global_id: Vec3u) { let idx = global_id.x() as usize; /* ... */ } #[compute] #[workgroup_size(8, 8, 1)] pub fn tiled(#[builtin(global_invocation_id)] global_id: Vec3u) { let x = global_id.x(); let y = global_id.y(); /* ... */ }
@compute @workgroup_size(64)
fn main(@builtin(global_invocation_id) global_id: vec3<u32>) { /* ... */ }
@compute @workgroup_size(8, 8, 1)
fn tiled(@builtin(global_invocation_id) global_id: vec3<u32>) { /* ... */ }
Compute entry points frequently take no return value (a () return type) and access storage or workgroup resources via the binding macros (see Binding Macros).
Inputs
Entry-point inputs are declared as ordinary function parameters. Each parameter may carry an I/O attribute:
#[builtin(NAME)]— a WGSL builtin value.#[location(N)]— a per-vertex attribute (vertex stage) or inter-stage value (fragment stage).
For complex inter-stage IO, use a struct input/output; see Inter-stage IO.
All Three Stages Together
A single module may declare all three stages. The example module below shows vertex, fragment, and compute entry points coexisting (see Binding Macros for storage! and get_mut!):
#![allow(unused)] fn main() { #[wgsl] pub mod pipeline { use wgsl_rs::std::*; storage!(group(0), binding(0), read_write, COUNTER: u32); #[vertex] pub fn vs_main(#[builtin(vertex_index)] vi: u32) -> Vec4f { vec4f(0.0, 0.0, 0.0, 1.0) } #[fragment] pub fn fs_main() -> Vec4f { vec4f(1.0, 1.0, 1.0, 1.0) } #[compute] #[workgroup_size(64)] pub fn cs_main(#[builtin(global_invocation_id)] gid: Vec3u) { let i = gid.x() as usize; get_mut!(COUNTER); } } }