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 };
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)?;
// vector of passes rendered in order (any iterable of Pass is renderable)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 } 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);
// vector of passes rendered in order (any iterable of Pass is renderable)renderer.render([pass, pass2], target);import os, sys# Skip this example in headless/CI environments that have no display surface.if os.environ.get('DISPLAY') is None and sys.platform != 'win32' and os.environ.get('FC_ALLOW_WINDOW') != '1': raise SystemExit(0)
from rendercanvas.auto import RenderCanvas, loop
from fragmentcolor import Shader, Pass, Renderer
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)
# vector of passes rendered in order (any iterable of Pass is renderable)renderer.render([rpass, pass2], target)import FragmentColor
let renderer = Renderer()// iOS: window/canvas provided by CAMetalLayer at runtimelet target = try await renderer.createTextureTarget([800, 600])let shader = Shader.default()
let pass = Pass("First Pass")pass.addShader(shader)
let pass2 = Pass("Second Pass")pass2.addShader(shader)
// standalonetry renderer.render(pass, target)
// vector of passes rendered in order (any iterable of Pass is renderable)try renderer.render([pass, pass2], target)import org.fragmentcolor.*
val renderer = Renderer()// HEADLESS: canvas creation not needed on Androidval target = renderer.createTextureTarget(800u, 600u)val shader = Shader.default()
val pass = Pass("First Pass")pass.addShader(shader)
val pass2 = Pass("Second Pass")pass2.addShader(shader)
// standalonerenderer.render(pass, target)
// vector of passes rendered in order (any iterable of Pass is renderable)renderer.render(listOf(pass, pass2), target)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")import FragmentColor
let pass = Pass("first pass")import org.fragmentcolor.*
val pass = Pass("first pass")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)import FragmentColor
let cs = try! Shader("@compute @workgroup_size(8,8,1) fn cs_main() {}")let pass = Pass("compute"); pass.addShader(cs)import org.fragmentcolor.*
val cs = Shader("@compute @workgroup_size(8,8,1) fn cs_main() {}")val pass = Pass("compute"); pass.addShader(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.
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)import FragmentColor
let shader = Shader.default()let pass = Pass("single"); pass.addShader(shader)import org.fragmentcolor.*
val shader = Shader.default()val pass = Pass("single"); pass.addShader(shader)Pass::name
Section titled “Pass::name”Read the pass’s name — the string passed to
Pass::new (or the label a
loader gave it). Names are how you address a pass by something other than its
index: pair this with
Scene::find_pass to
locate a pass in a scene’s graph, and it’s the label graphics debuggers
(RenderDoc, Xcode GPU capture) show for the pass.
Names aren’t required to be unique. If two passes share a name, name lookup returns the first one.
Example
Section titled “Example”1 collapsed line
fn main() -> Result<(), Box<dyn std::error::Error>> {use fragmentcolor::Pass;
let shadow = Pass::new("shadow");let label = shadow.name();3 collapsed lines
assert_eq!(label, "shadow");Ok(())}import { Pass } from "fragmentcolor";
const shadow = new Pass("shadow");const label = shadow.name();from fragmentcolor import Pass
shadow = Pass("shadow")label = shadow.name()import FragmentColor
let shadow = Pass("shadow")let label = shadow.name()import org.fragmentcolor.*
val shadow = Pass("shadow")val label = shadow.name()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)import FragmentColor
let renderer = Renderer()let target = try await renderer.createTextureTarget([64, 64])
let shader = Shader.default()let pass = Pass("blend with previous")pass.addShader(shader)pass.loadPrevious()
try renderer.render(pass, target)import org.fragmentcolor.*
val renderer = Renderer()val target = renderer.createTextureTarget(64u, 64u)
val shader = Shader.default()val pass = Pass("blend with previous")pass.addShader(shader)pass.loadPrevious()
renderer.render(pass, target)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 warningimport { Pass } from "fragmentcolor";
const pass = new Pass("example");const input = pass.getInput();from fragmentcolor import Pass
rpass = Pass("example")input = rpass.get_input()import FragmentColor
let pass = Pass("example")let input = pass.getInput()import org.fragmentcolor.*
val pass = Pass("example")val input = pass.getInput()Pass::add_shader(shader: Shader)
Section titled “Pass::add_shader(shader: Shader)”Add a Shader to the Pass. Shaders run in the order they were added, sharing the pass’s targets, viewport, clear color, and load policy.
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)import FragmentColor
let shader = Shader.default()let pass = Pass("p")pass.addShader(shader)import org.fragmentcolor.*
val shader = Shader.default()val pass = Pass("p")pass.addShader(shader)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)import FragmentColor
let mesh = Mesh()try mesh.addVertex([0.0, 0.0])
let shader = try 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.); }
""")
let pass = Pass("pass"); pass.addShader(shader)
try pass.addMesh(mesh)import org.fragmentcolor.*
val mesh = Mesh()mesh.addVertex(Vertex(listOf(0.0f, 0.0f)))
val 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.); }
""")
val pass = Pass("pass"); pass.addShader(shader)
pass.addMesh(mesh)Pass::add
Section titled “Pass::add”Absorb any SceneObject (a
Model,
Camera,
Light, or a user-defined
node that implements the trait) into the pass. Each kind brings its
own attach behaviour: a Model queues a draw with its own per-instance
transform; a Camera or Light wires its uniforms into every shader the
pass renders, both the ones already there and the ones added afterwards.
Camera and Light hold Arc-shared state, so subsequent mutations on the
same value (camera.look_at(...), light.set_color(...)) propagate to
every shader on the pass with no further add call.
Returns Result<&Pass, PassError>. Models can fail at attach time when
the Mesh layout doesn’t match the Material’s shader; Cameras and Lights
always succeed. Chain with ? between calls.
Example
Section titled “Example”1 collapsed line
async fn run() -> Result<(), Box<dyn std::error::Error>> {use fragmentcolor::{Camera, Light, Material, Mesh, Model, Pass, Renderer, Vertex};
let renderer = Renderer::new();
let mesh = Mesh::new();mesh.add_vertex( Vertex::pbr([0.0, 0.5, 0.0]).set(Vertex::UV0, [0.5, 1.0]),);let model = Model::new(mesh, Material::pbr());
let camera = Camera::perspective(1.047, 1.0, 0.1, 100.0) .look_at([0.0, 0.0, 2.0], [0.0, 0.0, 0.0], [0.0, 1.0, 0.0]);let sun = Light::directional([0.3, -1.0, -0.4], [1.0, 0.95, 0.9]);
let pass = Pass::new("scene");pass.add(&model)?;pass.add(&camera)?;pass.add(&sun)?;
// Updating the camera later is enough — every Model already on the pass// picks the new view_proj up at the next render.camera.look_at([3.0, 1.0, 5.0], [0.0, 0.0, 0.0], [0.0, 1.0, 0.0]);4 collapsed lines
let _ = renderer;Ok(())}fn main() -> Result<(), Box<dyn std::error::Error>> { pollster::block_on(run()) }import { Camera, Light, Material, Mesh, Model, Pass, Renderer, Vertex } from "fragmentcolor";
const renderer = new Renderer();
const mesh = new Mesh();mesh.addVertex( Vertex.pbr([0.0, 0.5, 0.0]).set("uv0", [0.5, 1.0]), );const model = new Model(mesh, Material.pbr());
const camera = Camera.perspective(1.047, 1.0, 0.1, 100.0).lookAt([0.0, 0.0, 2.0], [0.0, 0.0, 0.0], [0.0, 1.0, 0.0]);const sun = Light.directional([0.3, -1.0, -0.4], [1.0, 0.95, 0.9]);
const pass = new Pass("scene");pass.add(model);pass.add(camera);pass.add(sun);
// Updating the camera later is enough — every Model already on the pass// picks the new view_proj up at the next render.camera.lookAt([3.0, 1.0, 5.0], [0.0, 0.0, 0.0], [0.0, 1.0, 0.0]);from fragmentcolor import Camera, Light, Material, Mesh, Model, Pass, Renderer, Vertex
renderer = Renderer()
mesh = Mesh()mesh.add_vertex( Vertex.pbr([0.0, 0.5, 0.0]).set(Vertex.UV0, [0.5, 1.0]),)model = Model(mesh, Material.pbr())
camera = Camera.perspective(1.047, 1.0, 0.1, 100.0).look_at([0.0, 0.0, 2.0], [0.0, 0.0, 0.0], [0.0, 1.0, 0.0])sun = Light.directional([0.3, -1.0, -0.4], [1.0, 0.95, 0.9])
rpass = Pass("scene")rpass.add(model)rpass.add(camera)rpass.add(sun)
# Updating the camera later is enough — every Model already on the rpass# picks the new view_proj up at the next render.camera.look_at([3.0, 1.0, 5.0], [0.0, 0.0, 0.0], [0.0, 1.0, 0.0])import FragmentColor
let renderer = Renderer()
let mesh = Mesh()try mesh.addVertex( try Vertex.pbr([0.0, 0.5, 0.0]).set("uv0", [0.5, 1.0]))let model = Model(mesh, Material.pbr())
let camera = try Camera.perspective(1.047, 1.0, 0.1, 100.0).lookAt([0.0, 0.0, 2.0], [0.0, 0.0, 0.0], [0.0, 1.0, 0.0])let sun = try Light.directional([0.3, -1.0, -0.4], [1.0, 0.95, 0.9])
let pass = Pass("scene")try pass.add(model)try pass.add(camera)try pass.add(sun)
// Updating the camera later is enough — every Model already on the pass// picks the view_proj up at the next render.try camera.lookAt([3.0, 1.0, 5.0], [0.0, 0.0, 0.0], [0.0, 1.0, 0.0])import org.fragmentcolor.*
val renderer = Renderer()
val mesh = Mesh()mesh.addVertex( Vertex.pbr(listOf(0.0f, 0.5f, 0.0f)).set("uv0", floatArrayOf(0.5f, 1.0f)), )val model = Model(mesh, Material.pbr())
val camera = Camera.perspective(1.047f, 1.0f, 0.1f, 100.0f).lookAt(listOf(0.0f, 0.0f, 2.0f), listOf(0.0f, 0.0f, 0.0f), listOf(0.0f, 1.0f, 0.0f))val sun = Light.directional(listOf(0.3f, -1.0f, -0.4f), listOf(1.0f, 0.95f, 0.9f))
val pass = Pass("scene")pass.add(model)pass.add(camera)pass.add(sun)
// Updating the camera later is enough — every Model already on the pass// picks the view_proj up at the next render.camera.lookAt(listOf(3.0f, 1.0f, 5.0f), listOf(0.0f, 0.0f, 0.0f), listOf(0.0f, 1.0f, 0.0f))Pass::set_viewport(viewport)
Section titled “Pass::set_viewport(viewport)”Sets the viewport region for this Pass.
The viewport restricts drawing to a rectangular area of the Target.
The viewport argument accepts anything convertible into a ScreenRegion: an [x, y, w, h] array, a (w, h) tuple, or a ScreenRegion constructed explicitly.
Example
Section titled “Example”1 collapsed line
async fn run() -> Result<(), Box<dyn std::error::Error>> {
use fragmentcolor::{Renderer, Pass, Shader, ScreenRegion};
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(ScreenRegion::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, ScreenRegion } 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, ScreenRegion
renderer = Renderer()target = renderer.create_texture_target([64, 64])
shader = Shader.default()rpass = Pass("clipped")rpass.add_shader(shader)
rpass.set_viewport(ScreenRegion((0, 0), (32, 32)))
renderer.render(rpass, target)import FragmentColor
let renderer = Renderer()let target = try await renderer.createTextureTarget([64, 64])
let shader = Shader.default()let pass = Pass("clipped")pass.addShader(shader)
pass.setViewport([(0, 0), (32, 32)])
try renderer.render(pass, target)import org.fragmentcolor.*
val renderer = Renderer()val target = renderer.createTextureTarget(64u, 64u)
val shader = Shader.default()val pass = Pass("clipped")pass.addShader(shader)
pass.setViewport(ScreenRegion(minX=0u, minY=0u, maxX=32u, maxY=32u))
renderer.render(pass, target)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)import FragmentColor
let renderer = Renderer()let target = try await renderer.createTextureTarget([64, 64])
let shader = Shader.default()let pass = Pass("solid background")pass.addShader(shader)
try pass.setClearColor([0.1, 0.2, 0.3, 1.0])
try renderer.render(pass, target)import org.fragmentcolor.*
val renderer = Renderer()val target = renderer.createTextureTarget(64u, 64u)
val shader = Shader.default()val pass = Pass("solid background")pass.addShader(shader)
pass.setClearColor(listOf(0.1f, 0.2f, 0.3f, 1.0f))
renderer.render(pass, target)Pass::set_compute_dispatch
Section titled “Pass::set_compute_dispatch”Set the workgroup-grid size for a compute pass. The three numbers are the
number of workgroups to dispatch in each dimension; total invocations are
x * y * z * workgroup_size_x * workgroup_size_y * workgroup_size_z,
where the per-workgroup size comes from the WGSL @workgroup_size
attribute. Has no effect on render passes.
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)import FragmentColorlet cs = try! Shader("@compute @workgroup_size(8,8,1) fn cs_main() {}")let pass = Pass("compute"); pass.addShader(cs)pass.setComputeDispatch(64, 64, 1)import org.fragmentcolor.*val cs = Shader("@compute @workgroup_size(8,8,1) fn cs_main() {}")val pass = Pass("compute"); pass.addShader(cs)pass.setComputeDispatch(64u,64u,1u)Pass::set_target(target)
Section titled “Pass::set_target(target)”Attach a per-pass color render target. When set, this pass renders into the provided texture instead of the final Target.
A Pass has at most one color target. Call set_target again to swap it. 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.set_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.setTarget(tex_target);from fragmentcolor import Renderer, Pass, TextureFormat
r = Renderer()tex_target = r.create_texture_target([512, 512])
p = Pass("shadow")p.set_target(tex_target)import FragmentColor
let r = Renderer()let tex_target = try await r.createTextureTarget([512, 512])
let p = Pass("shadow")try p.setTarget(tex_target)import org.fragmentcolor.*
val r = Renderer()val tex_target = r.createTextureTarget(512u, 512u)
val p = Pass("shadow")p.setTarget(tex_target)Pass::set_depth_target
Section titled “Pass::set_depth_target”Attach a depth texture to this pass. Once attached, the renderer builds the pipeline with a matching depth-stencil state and depth-test is enabled: fragments behind the current contents of the depth buffer are discarded, and the pass writes to the depth buffer as it draws. That’s what you want for 3D meshes that occlude each other.
A Pass has at most one depth attachment. Call set_depth_target again to swap it.
The target must be a depth texture (Depth32Float is the canonical format) created by the same Renderer via create_depth_texture. The depth attachment’s sample count must match the color attachments’. Mixing 1× and 4× MSAA in the same pass returns RendererError::DepthSampleCountMismatch.
To opt out of depth testing, simply don’t call set_depth_target. The pass then renders without depth-state, painter’s-algorithm style (later draws win).
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?;
// One depth attachment shared across the 3D-content pass.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("blobs", &shader);
// Depth-test on — closer fragments win, the pass writes to the depth// buffer so subsequent draws within the same pass see the depth.pass.set_depth_target(&depth)?;
renderer.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]);
// One depth attachment shared across the 3D-content pass.const 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("blobs"); pass.addShader(shader);
// Depth-test on — closer fragments win, the pass writes to the depth// buffer so subsequent draws within the same pass see the depth.pass.setDepthTarget(depth);
renderer.render(pass, target);from fragmentcolor import Renderer, Pass, Shader, Mesh
renderer = Renderer()target = renderer.create_texture_target([64, 64])
# One depth attachment shared across the 3D-content pass.depth = 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("blobs"); rpass.add_shader(shader)
# Depth-test on — closer fragments win, the rpass writes to the depth# buffer so subsequent draws within the same rpass see the depth.rpass.set_depth_target(depth)
renderer.render(rpass, target)import FragmentColor
let renderer = Renderer()let target = try await renderer.createTextureTarget([64, 64])
// One depth attachment shared across the 3D-content pass.let depth = try await renderer.createDepthTexture([64, 64])
let mesh = Mesh()try mesh.addVertex([0.0, 0.0, 0.0])try mesh.addVertex([1.0, 0.0, 0.0])try mesh.addVertex([0.0, 1.0, 0.0])try mesh.addVertex([1.0, 1.0, 0.0])let shader = Shader.fromMesh(mesh)let pass = Pass("blobs"); pass.addShader(shader)
// Depth-test on — closer fragments win, the pass writes to the depth// buffer so subsequent draws within the same pass see the depth.try pass.setDepthTarget(depth)
try renderer.render(pass, target)import org.fragmentcolor.*
val renderer = Renderer()val target = renderer.createTextureTarget(64u, 64u)
// One depth attachment shared across the 3D-content pass.val depth = renderer.createDepthTexture(64u, 64u)
val mesh = Mesh()mesh.addVertex(Vertex(listOf(0.0f, 0.0f, 0.0f)))mesh.addVertex(Vertex(listOf(1.0f, 0.0f, 0.0f)))mesh.addVertex(Vertex(listOf(0.0f, 1.0f, 0.0f)))mesh.addVertex(Vertex(listOf(1.0f, 1.0f, 0.0f)))val shader = Shader.fromMesh(mesh)val pass = Pass("blobs"); pass.addShader(shader)
// Depth-test on — closer fragments win, the pass writes to the depth// buffer so subsequent draws within the same pass see the depth.pass.setDepthTarget(depth)
renderer.render(pass, target)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()import FragmentColor
let shader = try Shader("""@compute @workgroup_size(1)fn cs_main() { }
""")let pass = Pass("p"); pass.addShader(shader)
// Call the methodlet is_compute = pass.isCompute()import org.fragmentcolor.*
val shader = Shader("""@compute @workgroup_size(1)fn cs_main() { }
""")val pass = Pass("p"); pass.addShader(shader)
// Call the methodval is_compute = pass.isCompute()Pass::require(deps)
Section titled “Pass::require(deps)”Declare that this pass depends on one or more other renderables (Pass, Shader, 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 lastimport FragmentColorlet renderer = Renderer()let target = try await renderer.createTextureTarget([100,100])let color = Pass("color")let blurx = Pass("blur_x")try blurx.require(color); // color before blur_xlet blury = Pass("blur_y")try blury.require(blurx); // blur_x before blur_ylet compose = Pass("compose")try compose.require(color)try compose.require(blury); // fan-in; color and blur_y before composetry renderer.render(compose, target); // compose renders lastimport org.fragmentcolor.*val renderer = Renderer()val target = renderer.createTextureTarget(100u, 100u)val color = Pass("color")val blurx = Pass("blur_x")blurx.require(color); // color before blur_xval blury = Pass("blur_y")blury.require(blurx); // blur_x before blur_yval compose = Pass("compose")compose.require(color)compose.require(blury); // fan-in; color and blur_y before composerenderer.render(compose, target); // compose renders last