Skip to content

09 · GPU Effects

The !gpu effect marks a function as GPU-resident. Functions with !gpu cannot be called directly from CPU code (!io) — the only legal crossing point is a Pipeline object, which the compiler type-checks end-to-end. This eliminates an entire class of runtime GPU errors by turning them into compile errors.

gpu_effects.cssl
use cssl::gpu::{Pipeline, Buffer, Surface};
use cssl::math::{Vec2, Vec3, Vec4};
use cssl::space::{Clip, Color};
struct Vertex {
pos: Vec2,
color: Vec3,
}
// Vertex shader — GPU-resident, returns clip-space position.
#[shader(vertex)]
fn vs_main(v: Vertex) -> Vec4<Clip> !gpu {
Vec4::new(v.pos.x, v.pos.y, 0.0, 1.0)
}
// Fragment shader — GPU-resident, returns RGBA color.
#[shader(fragment)]
fn fs_main(color: Vec3) -> Vec4<Color> !gpu {
Vec4::new(color.r, color.g, color.b, 1.0)
}
fn main() !io {
let verts: Buffer<Vertex> = Buffer::from([
Vertex { pos: Vec2::new( 0.0, 0.5), color: Vec3::RED },
Vertex { pos: Vec2::new(-0.5, -0.5), color: Vec3::GREEN },
Vertex { pos: Vec2::new( 0.5, -0.5), color: Vec3::BLUE },
]);
// Pipeline::new checks that vs_main output matches fs_main input.
let pipeline = Pipeline::new(vs_main, fs_main);
let surface = Surface::new(800, 600, "GPU Triangle") !io;
surface.run(|frame| !io {
frame.clear(Vec4::BLACK);
pipeline.draw(&verts, frame);
});
}

A window opens showing a single RGB triangle against a black background. No terminal output — the result is visual.

vs_main and fs_main carry !gpu — calling either from main directly is a compile error. Pipeline::new(vs_main, fs_main) is the one place where the compiler checks that the interpolated output of the vertex stage matches the input of the fragment stage; mismatched shader interfaces fail here, not at driver link time. See §B.3 Effect System for the full !gpu / !io incompatibility rules.


Next → 10 · Autodiff