Skip to content

Pass

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.

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);
// standalone
renderer.render(&pass, &target)?;
// using a Frame
let 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()) }

The name property is optional and is used for debugging purposes.

use fragmentcolor::Pass;
let pass = Pass::new("first pass");
1 collapsed line
_ = pass;

Creates a new Pass configured for compute workloads.

Only Shader objects that compile to compute pipelines can be added.

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::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.

use fragmentcolor::{Pass, Shader};
let shader = Shader::default();
let pass = Pass::from_shader("single", &shader);

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.

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()) }

Returns a copy of the current input configuration for this Pass.

It includes the clear/load behavior and clear color.

use fragmentcolor::Pass;
let pass = Pass::new("example");
let input = pass.get_input();
1 collapsed line
_ = input; // Silence unused variable warning

Adds a Shader object to the Pass.

use fragmentcolor::{Pass, Shader};
let shader = Shader::default();
let pass = Pass::new("p");
pass.add_shader(&shader);

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 ResultResult<(), ShaderError>; on error, the mesh is not attached.

If a Shader wasn’t provided earlier, FragmentColor will create a default one.

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> };
@vertex
fn vs_main(@location(0) pos: vec2<f32>) -> VOut {
var out: VOut;
out.pos = vec4<f32>(pos, 0.0, 1.0);
return out;
}
@fragment
fn 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(())
}

Attach a Mesh to a specific Shader in this Pass. This forwards to shader.add_mesh(mesh) and returns the same Result.

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> };
@vertex
fn vs_main(@location(0) pos: vec2<f32>) -> VOut {
var out: VOut;
out.pos = vec4<f32>(pos, 0.0, 1.0);
return out;
}
@fragment
fn 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(())
}

Sets the viewport region for this Pass.

The viewport restricts drawing to a rectangular area of the Target.

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()) }

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.

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()) }

Set the compute dispatch size for a compute pass.

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);

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.

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()) }

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.

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 attachment
let 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 state
pass.add_depth_target(&depth)?;
// Render as usual
renderer.render(&pass, &target)?;
3 collapsed lines
Ok(())
}
fn main() -> Result<(), Box<dyn std::error::Error>> { pollster::block_on(run()) }

Returns true if this Pass is a compute pass (has only compute shaders).

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 method
let is_compute = pass.is_compute();
4 collapsed lines
_ = is_compute;
assert!(pass.is_compute());
Ok(())
}

Declare that this pass depends on one or more other renderables (Pass, Shader, Frame, Mesh). All dependencies will render before this Pass.

  • 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

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.
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_x
let blury = Pass::new("blur_y");
blury.require(&blurx)?; // blur_x before blur_y
let compose = Pass::new("compose");
compose.require(&color)?;
compose.require(&blury)?; // fan-in; color and blur_y before compose
renderer.render(&compose, &target)?; // compose renders last
3 collapsed lines
Ok(())
}
fn main() -> Result<(), Box<dyn std::error::Error>> { pollster::block_on(run()) }