Pass
Description
Section titled “Description”The Pass object is a collection of Shader objects that are rendered to a Target by the Renderer.
While the Shader represents a single Render Pipeline or a Compute Pipeline, the Pass can be used to draw multiple Shaders in sequence, for example when you have multiple objects in a scene with different materials.
The Pass represents a single RenderPass or a ComputePass in the WebGPU API.
The constructor creates a RenderPass by default. To create a ComputePass, call Pass::compute().
After creation, it will only accept a compatible Shader object. If you try to add a Compute Shader to a Render Pass or vice-versa, it won’t add the shader to its internal list and log a warning message in the console.
Example
Section titled “Example”1 collapsed line
async fn run() -> Result<(), Box<dyn std::error::Error>> {
use fragmentcolor::{ Shader, Pass, Renderer, Frame };
let renderer = Renderer::new();let window = fragmentcolor::headless_window([100, 100]);let target = renderer.create_target(window).await?;let shader = Shader::default();
let mut pass = Pass::new("First Pass");pass.add_shader(&shader);
let mut pass2 = Pass::new("Second Pass");pass2.add_shader(&shader);
// standalonerenderer.render(&pass, &target)?;
// using a Framelet mut frame = Frame::new();frame.add_pass(&pass);frame.add_pass(&pass2);renderer.render(&frame, &target)?;
// vector of passes (consume them)renderer.render(&vec![pass, pass2], &target)?;
3 collapsed lines
Ok(())}fn main() -> Result<(), Box<dyn std::error::Error>> { pollster::block_on(run()) }
import { Shader, Pass, Renderer, Frame } from "fragmentcolor";
const renderer = new Renderer();const canvas = document.createElement('canvas');const target = await renderer.createTarget(canvas);const shader = Shader.default();
const pass = new Pass("First Pass");pass.addShader(shader);
const pass2 = new Pass("Second Pass");pass2.addShader(shader);
// standalonerenderer.render(pass, target);
// using a Frameconst frame = new Frame();frame.addPass(pass);frame.addPass(pass2);renderer.render(frame, target);
// vector of passes (consume them)renderer.render([pass, pass2], target);
from rendercanvas.auto import RenderCanvas, loop
from fragmentcolor import Shader, Pass, Renderer, Frame
renderer = Renderer()canvas = RenderCanvas(size=(100, 100))target = renderer.create_target(canvas)shader = Shader.default()
rpass = Pass("First Pass")rpass.add_shader(shader)
pass2 = Pass("Second Pass")pass2.add_shader(shader)
# standalonerenderer.render(rpass, target)
# using a Frameframe = Frame()frame.add_pass(rpass)frame.add_pass(pass2)renderer.render(frame, target)
# vector of passes (consume them)renderer.render([rpass, pass2], target)
// Swift placeholder â bindings WIP
// Kotlin placeholder â bindings WIP
Methods
Section titled “Methods”Pass::new(name: &str) -> Self
Section titled “Pass::new(name: &str) -> Self”Creates a new Pass
Section titled “Creates a new Pass”The name property is optional and is used for debugging purposes.
Example
Section titled “Example”use fragmentcolor::Pass;
let pass = Pass::new("first pass");
1 collapsed line
_ = pass;
import { Pass } from "fragmentcolor";
const pass = new Pass("first pass");
from fragmentcolor import Pass
rpass = Pass("first rpass")
// Swift placeholder â bindings WIP
// Kotlin placeholder â bindings WIP
Pass::compute(name: &str) -> Pass
Section titled “Pass::compute(name: &str) -> Pass”Creates a new Pass configured for compute workloads.
Only Shader objects that compile to compute pipelines can be added.
Example
Section titled “Example”use fragmentcolor::{Pass, Shader};
let cs = Shader::new("@compute @workgroup_size(8,8,1) fn cs_main() {}").unwrap();let pass = Pass::from_shader("compute", &cs);
import { Pass, Shader } from "fragmentcolor";
const cs = new Shader("@compute @workgroup_size(8,8,1) fn cs_main() {}").unwrap();const pass = new Pass("compute"); pass.addShader(cs);
from fragmentcolor import Pass, Shader
cs = Shader("@compute @workgroup_size(8,8,1) fn cs_main() {}")rpass = Pass("compute"); rpass.add_shader(cs)
// Swift placeholder â bindings WIP
// Kotlin placeholder â bindings WIP
Pass::from_shader(name: &str, shader: Shader) -> Pass
Section titled “Pass::from_shader(name: &str, shader: Shader) -> Pass”Creates a new Pass from a single Shader.
The created Pass inherits the render/compute type from the provided Shader.
Example
Section titled “Example”use fragmentcolor::{Pass, Shader};
let shader = Shader::default();let pass = Pass::from_shader("single", &shader);
import { Pass, Shader } from "fragmentcolor";
const shader = Shader.default();const pass = new Pass("single"); pass.addShader(shader);
from fragmentcolor import Pass, Shader
shader = Shader.default()rpass = Pass("single"); rpass.add_shader(shader)
// Swift placeholder â bindings WIP
// Kotlin placeholder â bindings WIP
Pass::load_previous()
Section titled “Pass::load_previous()”Configures this Pass to load the previous contents of the Target instead of clearing it.
This is useful when layering multiple passes where the next pass should blend with the prior results.
Example
Section titled “Example”1 collapsed line
async fn run() -> Result<(), Box<dyn std::error::Error>> {
use fragmentcolor::{Renderer, Pass, Shader};
let renderer = Renderer::new();let target = renderer.create_texture_target([64, 64]).await?;
let shader = Shader::default();let mut pass = Pass::new("blend with previous");pass.add_shader(&shader);pass.load_previous();
renderer.render(&pass, &target)?;
3 collapsed lines
Ok(())}fn main() -> Result<(), Box<dyn std::error::Error>> { pollster::block_on(run()) }
import { Renderer, Pass, Shader } from "fragmentcolor";
const renderer = new Renderer();const target = await renderer.createTextureTarget([64, 64]);
const shader = Shader.default();const pass = new Pass("blend with previous");pass.addShader(shader);pass.loadPrevious();
renderer.render(pass, target);
from fragmentcolor import Renderer, Pass, Shader
renderer = Renderer()target = renderer.create_texture_target([64, 64])
shader = Shader.default()rpass = Pass("blend with previous")rpass.add_shader(shader)rpass.load_previous()
renderer.render(rpass, target)
// Swift placeholder â bindings WIP
// Kotlin placeholder â bindings WIP
Pass::get_input() -> PassInput
Section titled “Pass::get_input() -> PassInput”Returns a copy of the current input configuration for this Pass.
It includes the clear/load behavior and clear color.
Example
Section titled “Example”use fragmentcolor::Pass;
let pass = Pass::new("example");let input = pass.get_input();
1 collapsed line
_ = input; // Silence unused variable warning
import { Pass } from "fragmentcolor";
const pass = new Pass("example");const input = pass.getInput();
from fragmentcolor import Pass
rpass = Pass("example")input = rpass.get_input()
// Swift placeholder â bindings WIP
// Kotlin placeholder â bindings WIP
Pass::add_shader(shader: Shader)
Section titled “Pass::add_shader(shader: Shader)”Adds a Shader object to the Pass.
Example
Section titled “Example”use fragmentcolor::{Pass, Shader};
let shader = Shader::default();let pass = Pass::new("p");pass.add_shader(&shader);
import { Pass, Shader } from "fragmentcolor";
const shader = Shader.default();const pass = new Pass("p");pass.addShader(shader);
from fragmentcolor import Pass, Shader
shader = Shader.default()rpass = Pass("p")rpass.add_shader(shader)
// Swift placeholder â bindings WIP
// Kotlin placeholder â bindings WIP
Pass::add_mesh
Section titled “Pass::add_mesh”Attach a Mesh to this Pass.
- The mesh is attached to the last shader previously added to this Pass.
- Validates compatibility with that shaderâs vertex inputs.
- Returns Result
Result<(), ShaderError>
; on error, the mesh is not attached.
If a Shader wasn’t provided earlier, FragmentColor will create a default one.
Example
Section titled “Example”1 collapsed line
fn main() -> Result<(), Box<dyn std::error::Error>> {use fragmentcolor::{Pass, Shader, Mesh};
let mesh = Mesh::new();mesh.add_vertex([0.0, 0.0]);
let shader = Shader::new(r#"struct VOut { @builtin(position) pos: vec4<f32> };@vertexfn vs_main(@location(0) pos: vec2<f32>) -> VOut { var out: VOut; out.pos = vec4<f32>(pos, 0.0, 1.0); return out;}@fragmentfn fs_main(_v: VOut) -> @location(0) vec4<f32> { return vec4<f32>(1.,0.,0.,1.); }"#)?;
let pass = Pass::from_shader("pass", &shader);
pass.add_mesh(&mesh)?;
2 collapsed lines
Ok(())}
import { Pass, Shader, Mesh } from "fragmentcolor";
const mesh = new Mesh();mesh.addVertex([0.0, 0.0]);
const shader = new Shader(`struct VOut { @builtin(position) pos: vec4<f32> };@vertexfn vs_main(@location(0) pos: vec2<f32>) -> VOut { var out: VOut; out.pos = vec4<f32>(pos, 0.0, 1.0); return out;}@fragmentfn fs_main(_v: VOut) -> @location(0) vec4<f32> { return vec4<f32>(1.,0.,0.,1.); }
`);
const pass = new Pass("pass"); pass.addShader(shader);
pass.addMesh(mesh);
from fragmentcolor import Pass, Shader, Mesh
mesh = Mesh()mesh.add_vertex([0.0, 0.0])
shader = Shader("""struct VOut { @builtin(position) pos: vec4<f32> };@vertexfn vs_main(@location(0) pos: vec2<f32>) -> VOut { var out: VOut; out.pos = vec4<f32>(pos, 0.0, 1.0); return out;}@fragmentfn fs_main(_v: VOut) -> @location(0) vec4<f32> { return vec4<f32>(1.,0.,0.,1.); }
""")
rpass = Pass("rpass"); rpass.add_shader(shader)
rpass.add_mesh(mesh)
// Swift placeholder: bindings WIP
// Kotlin placeholder: bindings WIP
Pass::add_mesh_to_shader
Section titled “Pass::add_mesh_to_shader”Attach a Mesh to a specific Shader in this Pass. This forwards to shader.add_mesh(mesh)
and returns the same Result.
Example
Section titled “Example”1 collapsed line
fn main() -> Result<(), Box<dyn std::error::Error>> {use fragmentcolor::{Shader, Mesh, Vertex, Pass};
let mesh = Mesh::new();mesh.add_vertex(Vertex::new([0.0, 0.0]));let shader = Shader::new(r#"struct VOut { @builtin(position) pos: vec4<f32> };@vertexfn vs_main(@location(0) pos: vec2<f32>) -> VOut { var out: VOut; out.pos = vec4<f32>(pos, 0.0, 1.0); return out;}@fragmentfn fs_main(_v: VOut) -> @location(0) vec4<f32> { return vec4<f32>(1.,0.,0.,1.); }"#)?;
let pass = Pass::from_shader("pass", &shader);pass.add_mesh_to_shader(&mesh, &shader)?;
2 collapsed lines
Ok(())}
import { Shader, Mesh, Vertex, Pass } from "fragmentcolor";
const mesh = new Mesh();mesh.addVertex(Vertex.new([0.0, 0.0]));const shader = new Shader(`struct VOut { @builtin(position) pos: vec4<f32> };@vertexfn vs_main(@location(0) pos: vec2<f32>) -> VOut { var out: VOut; out.pos = vec4<f32>(pos, 0.0, 1.0); return out;}@fragmentfn fs_main(_v: VOut) -> @location(0) vec4<f32> { return vec4<f32>(1.,0.,0.,1.); }
`);
const pass = new Pass("pass"); pass.addShader(shader);pass.addMeshToShader(mesh, shader);
from fragmentcolor import Shader, Mesh, Vertex, Pass
mesh = Mesh()mesh.add_vertex(Vertex([0.0, 0.0]))shader = Shader("""struct VOut { @builtin(position) pos: vec4<f32> };@vertexfn vs_main(@location(0) pos: vec2<f32>) -> VOut { var out: VOut; out.pos = vec4<f32>(pos, 0.0, 1.0); return out;}@fragmentfn fs_main(_v: VOut) -> @location(0) vec4<f32> { return vec4<f32>(1.,0.,0.,1.); }
""")
rpass = Pass("rpass"); rpass.add_shader(shader)rpass.add_mesh_to_shader(mesh, shader)
// Swift placeholder: bindings WIP
// Kotlin placeholder: bindings WIP
Pass::set_viewport(viewport: Region)
Section titled “Pass::set_viewport(viewport: Region)”Sets the viewport region for this Pass.
The viewport restricts drawing to a rectangular area of the Target.
Example
Section titled “Example”1 collapsed line
async fn run() -> Result<(), Box<dyn std::error::Error>> {
use fragmentcolor::{Renderer, Pass, Shader, Region};
let renderer = Renderer::new();let target = renderer.create_texture_target([64, 64]).await?;
let shader = Shader::default();let mut pass = Pass::new("clipped");pass.add_shader(&shader);
pass.set_viewport(Region::new((0, 0), (32, 32)));
renderer.render(&pass, &target)?;
3 collapsed lines
Ok(())}fn main() -> Result<(), Box<dyn std::error::Error>> { pollster::block_on(run()) }
import { Renderer, Pass, Shader, Region } from "fragmentcolor";
const renderer = new Renderer();const target = await renderer.createTextureTarget([64, 64]);
const shader = Shader.default();const pass = new Pass("clipped");pass.addShader(shader);
pass.setViewport([(0, 0), (32, 32)]);
renderer.render(pass, target);
from fragmentcolor import Renderer, Pass, Shader, Region
renderer = Renderer()target = renderer.create_texture_target([64, 64])
shader = Shader.default()rpass = Pass("clipped")rpass.add_shader(shader)
rpass.set_viewport(Region((0, 0), (32, 32)))
renderer.render(rpass, target)
// Swift placeholder â bindings WIP
// Kotlin placeholder â bindings WIP
Pass::set_clear_color(color: [f32; 4])
Section titled “Pass::set_clear_color(color: [f32; 4])”Sets the clear color for this Pass.
When the pass is configured to clear, the render target is cleared to the given RGBA color before drawing.
Example
Section titled “Example”1 collapsed line
async fn run() -> Result<(), Box<dyn std::error::Error>> {
use fragmentcolor::{Renderer, Pass, Shader};
let renderer = Renderer::new();let target = renderer.create_texture_target([64, 64]).await?;
let shader = Shader::default();let mut pass = Pass::new("solid background");pass.add_shader(&shader);
pass.set_clear_color([0.1, 0.2, 0.3, 1.0]);
renderer.render(&pass, &target)?;
3 collapsed lines
Ok(())}fn main() -> Result<(), Box<dyn std::error::Error>> { pollster::block_on(run()) }
import { Renderer, Pass, Shader } from "fragmentcolor";
const renderer = new Renderer();const target = await renderer.createTextureTarget([64, 64]);
const shader = Shader.default();const pass = new Pass("solid background");pass.addShader(shader);
pass.setClearColor([0.1, 0.2, 0.3, 1.0]);
renderer.render(pass, target);
from fragmentcolor import Renderer, Pass, Shader
renderer = Renderer()target = renderer.create_texture_target([64, 64])
shader = Shader.default()rpass = Pass("solid background")rpass.add_shader(shader)
rpass.set_clear_color([0.1, 0.2, 0.3, 1.0])
renderer.render(rpass, target)
// Swift placeholder â bindings WIP
// Kotlin placeholder â bindings WIP
Pass::set_compute_dispatch
Section titled “Pass::set_compute_dispatch”Set the compute dispatch size for a compute pass.
Example
Section titled “Example”use fragmentcolor::{Pass, Shader};let cs = Shader::new("@compute @workgroup_size(8,8,1) fn cs_main() {}").unwrap();let pass = Pass::from_shader("compute", &cs);pass.set_compute_dispatch(64, 64, 1);
import { Pass, Shader } from "fragmentcolor";const cs = new Shader("@compute @workgroup_size(8,8,1) fn cs_main() {}").unwrap();const pass = new Pass("compute"); pass.addShader(cs);pass.setComputeDispatch(64, 64, 1);
from fragmentcolor import Pass, Shadercs = Shader("@compute @workgroup_size(8,8,1) fn cs_main() {}")rpass = Pass("compute"); rpass.add_shader(cs)rpass.set_compute_dispatch(64, 64, 1)
// Swift placeholder: bindings WIP
// Kotlin placeholder: bindings WIP
Pass::add_target(target)
Section titled “Pass::add_target(target)”Attach a per-pass color render target. When set, this pass renders into the provided texture instead of the final Target.
Use this to render intermediate results (e.g., a shadow map) that later passes can sample.
Example
Section titled “Example”1 collapsed line
async fn run() -> Result<(), Box<dyn std::error::Error>> {use fragmentcolor::{Renderer, Pass, TextureFormat};
let r = Renderer::new();let tex_target = r.create_texture_target([512, 512]).await?;
let p = Pass::new("shadow");p.add_target(&tex_target);
3 collapsed lines
Ok(())}fn main() -> Result<(), Box<dyn std::error::Error>> { pollster::block_on(run()) }
import { Renderer, Pass, TextureFormat } from "fragmentcolor";
const r = new Renderer();const tex_target = await r.createTextureTarget([512, 512]);
const p = new Pass("shadow");p.addTarget(tex_target);
from fragmentcolor import Renderer, Pass, TextureFormat
r = Renderer()tex_target = r.create_texture_target([512, 512])
p = Pass("shadow")p.add_target(tex_target)
// Swift placeholder: bindings WIP
// Kotlin placeholder: bindings WIP
Pass::add_depth_target
Section titled “Pass::add_depth_target”Select a depth attachment for this pass.
The target must be a stable texture created by the same Renderer with create_depth_texture().
When a depth target is attached, the renderer will create a render pipeline with a depth-stencil
matching the texture format (e.g., Depth32Float
) of the created texture.
Example
Section titled “Example”1 collapsed line
async fn run() -> Result<(), Box<dyn std::error::Error>> {use fragmentcolor::{Renderer, Pass, Shader, Mesh};
let renderer = Renderer::new();let target = renderer.create_texture_target([64, 64]).await?;
// Create a depth texture usable as a per-pass attachmentlet depth = renderer.create_depth_texture([64, 64]).await?;
let mut mesh = Mesh::new();mesh.add_vertex([0.0, 0.0, 0.0]);mesh.add_vertex([1.0, 0.0, 0.0]);mesh.add_vertex([0.0, 1.0, 0.0]);mesh.add_vertex([1.0, 1.0, 0.0]);let shader = Shader::from_mesh(&mesh);let pass = Pass::from_shader("scene", &shader);
// Attach depth texture to enable depth testing.// Pipeline will include a matching depth-stencil statepass.add_depth_target(&depth)?;
// Render as usualrenderer.render(&pass, &target)?;3 collapsed lines
Ok(())}fn main() -> Result<(), Box<dyn std::error::Error>> { pollster::block_on(run()) }
import { Renderer, Pass, Shader, Mesh } from "fragmentcolor";
const renderer = new Renderer();const target = await renderer.createTextureTarget([64, 64]);
// Create a depth texture usable as a per-pass attachmentconst depth = await renderer.createDepthTexture([64, 64]);
const mesh = new Mesh();mesh.addVertex([0.0, 0.0, 0.0]);mesh.addVertex([1.0, 0.0, 0.0]);mesh.addVertex([0.0, 1.0, 0.0]);mesh.addVertex([1.0, 1.0, 0.0]);const shader = Shader.fromMesh(mesh);const pass = new Pass("scene"); pass.addShader(shader);
// Attach depth texture to enable depth testing.// Pipeline will include a matching depth-stencil statepass.addDepthTarget(depth);
// Render as usualrenderer.render(pass, target);
from fragmentcolor import Renderer, Pass, Shader, Mesh
renderer = Renderer()target = renderer.create_texture_target([64, 64])
# Create a depth texture usable as a per-pass attachmentdepth = renderer.create_depth_texture([64, 64])
mesh = Mesh()mesh.add_vertex([0.0, 0.0, 0.0])mesh.add_vertex([1.0, 0.0, 0.0])mesh.add_vertex([0.0, 1.0, 0.0])mesh.add_vertex([1.0, 1.0, 0.0])shader = Shader.from_mesh(mesh)rpass = Pass("scene"); rpass.add_shader(shader)
# Attach depth texture to enable depth testing.# Pipeline will include a matching depth-stencil staterpass.add_depth_target(depth)
# Render as usualrenderer.render(rpass, target)
// Swift placeholder: bindings WIP
// Kotlin placeholder: bindings WIP
Pass::is_compute
Section titled “Pass::is_compute”Returns true if this Pass is a compute pass (has only compute shaders).
Example
Section titled “Example”1 collapsed line
fn main() -> Result<(), Box<dyn std::error::Error>> {use fragmentcolor::{Shader, Pass};
let shader = Shader::new(r#"@compute @workgroup_size(1)fn cs_main() { }"#)?;let pass = Pass::from_shader("p", &shader);
// Call the methodlet is_compute = pass.is_compute();
4 collapsed lines
_ = is_compute;assert!(pass.is_compute());Ok(())}
import { Shader, Pass } from "fragmentcolor";
const shader = new Shader(`@compute @workgroup_size(1)fn cs_main() { }
`);const pass = new Pass("p"); pass.addShader(shader);
// Call the methodconst is_compute = pass.isCompute();
from fragmentcolor import Shader, Pass
shader = Shader("""@compute @workgroup_size(1)fn cs_main() { }
""")rpass = Pass("p"); rpass.add_shader(shader)
# Call the methodis_compute = rpass.is_compute()
// Swift placeholder: bindings WIP
// Kotlin placeholder: bindings WIP
Pass::require(deps)
Section titled “Pass::require(deps)”Declare that this pass depends on one or more other renderables (Pass, Shader, Frame, Mesh). All dependencies will render before this Pass.
Return value
Section titled “Return value”- Ok(()) on success
- Err(PassError::SelfDependency) if a pass requires itself
- Err(PassError::DuplicateDependency(name)) if the dependency is already present
- Err(PassError::DependencyCycle { via }) if adding the dependency would create a cycle
Description
Section titled “Description”require
establishes a dependency: the given dependencies
must render before self
.
This allows you to build DAG render graphs directly from passes. The graph is validated at build time and does not perform cycle checks at render time.
- Dependencies are stored in insertion order.
- Traversal is dependencies-first, then the current pass, with deduplication.
- Creation order of passes does not matter.
Example
Section titled “Example”1 collapsed line
async fn run() -> Result<(), Box<dyn std::error::Error>> {use fragmentcolor::{Pass, Renderer};let renderer = Renderer::new();let target = renderer.create_texture_target([100,100]).await?;let color = Pass::new("color");let blurx = Pass::new("blur_x");blurx.require(&color)?; // color before blur_xlet blury = Pass::new("blur_y");blury.require(&blurx)?; // blur_x before blur_ylet compose = Pass::new("compose");compose.require(&color)?;compose.require(&blury)?; // fan-in; color and blur_y before composerenderer.render(&compose, &target)?; // compose renders last3 collapsed lines
Ok(())}fn main() -> Result<(), Box<dyn std::error::Error>> { pollster::block_on(run()) }
import { Pass, Renderer } from "fragmentcolor";const renderer = new Renderer();const target = await renderer.createTextureTarget([100,100]);const color = new Pass("color");const blurx = new Pass("blur_x");blurx.require(color); // color before blur_x;const blury = new Pass("blur_y");blury.require(blurx); // blur_x before blur_y;const compose = new Pass("compose");compose.require(color);compose.require(blury); // fan-in; color and blur_y before compose;renderer.render(compose, target); // compose renders last;
from fragmentcolor import Pass, Rendererrenderer = Renderer()target = renderer.create_texture_target([100,100])color = Pass("color")blurx = Pass("blur_x")blurx.require(color); # color before blur_xblury = Pass("blur_y")blury.require(blurx); # blur_x before blur_ycompose = Pass("compose")compose.require(color)compose.require(blury); # fan-in; color and blur_y before composerenderer.render(compose, target); # compose renders last
// Swift placeholder: bindings WIP
// Kotlin placeholder: bindings WIP