Shader
Description
Section titled “Description”The Shader object is the main building block in FragmentColor.
It takes a WGSL or GLSL shader source as input, parses it, validates it, and exposes the uniforms as keys.
To draw your shader, you must use your Shader instance as input to a Renderer.
You can compose Shader instances into a Pass object to create more complex rendering pipelines.
You can also create renderings with multiple Render Passes by using multiple Pass instances to a Frame object.
Uniforms
Section titled “Uniforms”Classic uniforms are declared with var<uniform>
and can be nested structs/arrays.
FragmentColor exposes every root and nested field as addressable keys using dot and index notation:
- Set a field:
shader.set("u.color", [r, g, b, a])
- Index arrays:
shader.set("u.arr[1]", value)
WGSL example
Section titled “WGSL example”struct MyUniform { color: vec4<f32>, arr: array<vec4<f32>, 2>};
@group(0) @binding(0) var<uniform> u: MyUniform;
- Binding sizes are aligned to 16 bytes for layout correctness; this is handled automatically.
- Large uniform blobs are uploaded via an internal buffer pool.
Textures and Samplers
Section titled “Textures and Samplers”Sampled textures and samplers are supported via texture_*
and sampler
declarations.
You can bind a Texture object created by the Renderer directly to a texture uniform (e.g., shader.set("tex", &texture)
);
samplers are provided automatically:
- If a texture is bound in the same group, the sampler defaults to that texture’s sampler.
- Otherwise, a reasonable default sampler is used.
WGSL example
Section titled “WGSL example”@group(0) @binding(0) var tex: texture_2d<f32>;@group(0) @binding(1) var samp: sampler;
- 2D/3D/Cube and array variants are supported; the correct view dimension is inferred.
- Integer textures map to Sint/Uint sample types; float textures use filterable float when possible.
Storage Textures
Section titled “Storage Textures”Writeable/readable image surfaces are supported via storage textures (texture_storage_*
).
Access flags are preserved from WGSL and mapped to the device:
read
-> read-only storage accesswrite
-> write-only storage accessread_write
-> read+write (when supported)
WGSL example
Section titled “WGSL example”@group(0) @binding(0) var img: texture_storage_2d<rgba8unorm, write>;
- The declared storage format is respected when creating the binding layout.
- User must ensure the adapter supports the chosen format/access mode.
Storage Buffers
Section titled “Storage Buffers”Structured buffers are supported via var<storage, read>
or var<storage, read_write>
and can contain nested structs/arrays. FragmentColor preserves and applies the WGSL access flags
when creating binding layouts and setting visibility.
WGSL example
Section titled “WGSL example”struct Buf { a: vec4<f32> };@group(0) @binding(0) var<storage, read> ssbo: Buf;
- Read-only buffers are bound with
read-only
storage access;read_write
allows writes when supported. - Buffer byte spans are computed from WGSL shapes; arrays/structs honor stride and alignment.
- CPU-side updates use the same set(“path”, value) and get_bytes(“path”) APIs as uniforms, with array indexing supported (e.g.,
buf.items[2].v
). - Large buffers are uploaded via a dedicated storage buffer pool.
Push Constants
Section titled “Push Constants”Push constants are supported with var<push_constants>
in all platforms.
They will fallback to regular uniform buffers when:
- push_constants are not natively supported (ex. on Web),
- multiple push-constant roots are declared, or
- the total push-constant size exceeds the device limit.
In fallback mode, FragmentColor rewrites push constants into classic uniform buffers placed in a newly allocated bind group. In this case:
- A bind group slot will be used by this fallback group (allocated as max existing group + 1).
- There is no check for the max bind groups supported.
- If you use push constants and many bind groups, very high group indices can exceed device limits.
- Each push-constant root becomes one uniform buffer binding in the fallback group.
- Currently, the fallback is applied for render pipelines; compute pipeline fallback may be added later.
Example
Section titled “Example”1 collapsed line
async fn run() -> Result<(), Box<dyn std::error::Error>> {
use fragmentcolor::{Shader, Renderer};
let shader = Shader::new(r#" @vertex fn vs_main(@builtin(vertex_index) index: u32) -> @builtin(position) vec4<f32> { var pos = array<vec2<f32>, 3>( vec2<f32>(-1.0, -1.0), vec2<f32>( 3.0, -1.0), vec2<f32>(-1.0, 3.0) ); return vec4<f32>(pos[index], 0.0, 1.0); }
@group(0) @binding(0) var<uniform> resolution: vec2<f32>;
@fragment fn fs_main() -> @location(0) vec4<f32> { return vec4<f32>(1.0, 0.0, 0.0, 1.0); // Red }"#)?;
// Set the "resolution" uniformshader.set("resolution", [800.0, 600.0])?;let res: [f32; 2] = shader.get("resolution")?;
let renderer = Renderer::new();let target = renderer.create_texture_target([16, 16]).await?;renderer.render(&shader, &target)?;
5 collapsed lines
assert_eq!(res, [800.0, 600.0]);assert!(shader.list_uniforms().len() >= 1);Ok(())}fn main() -> Result<(), Box<dyn std::error::Error>> { pollster::block_on(run()) }
import { Shader, Renderer } from "fragmentcolor";
const shader = new Shader(` @vertex fn vs_main(@builtin(vertex_index) index: u32) -> @builtin(position) vec4<f32> { var pos = array<vec2<f32>, 3>( vec2<f32>(-1.0, -1.0), vec2<f32>( 3.0, -1.0), vec2<f32>(-1.0, 3.0) ); return vec4<f32>(pos[index], 0.0, 1.0); }
@group(0) @binding(0) var<uniform> resolution: vec2<f32>;
@fragment fn fs_main() -> @location(0) vec4<f32> { return vec4<f32>(1.0, 0.0, 0.0, 1.0); // Red }
`);
// Set the "resolution" uniformshader.set("resolution", [800.0, 600.0]);const res = shader.get("resolution");
const renderer = new Renderer();const target = await renderer.createTextureTarget([16, 16]);renderer.render(shader, target);
from fragmentcolor import Shader, Renderer
shader = Shader(""" @vertex fn vs_main(@builtin(vertex_index) index: u32) -> @builtin(position) vec4<f32> { var pos = array<vec2<f32>, 3>( vec2<f32>(-1.0, -1.0), vec2<f32>( 3.0, -1.0), vec2<f32>(-1.0, 3.0) ); return vec4<f32>(pos[index], 0.0, 1.0); }
@group(0) @binding(0) var<uniform> resolution: vec2<f32>;
@fragment fn fs_main() -> @location(0) vec4<f32> { return vec4<f32>(1.0, 0.0, 0.0, 1.0); // Red }
""")
# Set the "resolution" uniformshader.set("resolution", [800.0, 600.0])res = shader.get("resolution")
renderer = Renderer()target = renderer.create_texture_target([16, 16])renderer.render(shader, target)
// Swift placeholder â bindings WIP
// Kotlin placeholder â bindings WIP
Methods
Section titled “Methods”Shader::new(source: string)
Section titled “Shader::new(source: string)”Creates a new Shader instance from the given WGSL source string, file path, or URL.
GLSL is also supported if you enable the glsl
feature.
Shadertoy-flavored GLSL is supported if the shadertoy
feature is enabled.
If the optional features are enabled, the constructor will try to automatically detect the shader type and parse it accordingly.
If an exception occurs during parsing, the error message will indicate the location of the error.
If the initial source validation passes, the shader is guaranteed to work on the GPU. All uniforms are initialized to their default zero values.
Example
Section titled “Example”1 collapsed line
fn main() -> Result<(), Box<dyn std::error::Error>> {
use fragmentcolor::Shader;
let shader = Shader::new(r#" @vertex fn vs_main(@builtin(vertex_index) index: u32) -> @builtin(position) vec4<f32> { var pos = array<vec2<f32>, 3>( vec2<f32>(-1.0, -1.0), vec2<f32>( 3.0, -1.0), vec2<f32>(-1.0, 3.0) ); return vec4<f32>(pos[index], 0.0, 1.0); }
@group(0) @binding(0) var<uniform> resolution: vec2<f32>;
@fragment fn fs_main() -> @location(0) vec4<f32> { return vec4<f32>(1.0, 0.0, 0.0, 1.0); // Red }"#)?;
3 collapsed lines
assert!(shader.list_keys().len() >= 1);Ok(())}
import { Shader } from "fragmentcolor";
const shader = new Shader(` @vertex fn vs_main(@builtin(vertex_index) index: u32) -> @builtin(position) vec4<f32> { var pos = array<vec2<f32>, 3>( vec2<f32>(-1.0, -1.0), vec2<f32>( 3.0, -1.0), vec2<f32>(-1.0, 3.0) ); return vec4<f32>(pos[index], 0.0, 1.0); }
@group(0) @binding(0) var<uniform> resolution: vec2<f32>;
@fragment fn fs_main() -> @location(0) vec4<f32> { return vec4<f32>(1.0, 0.0, 0.0, 1.0); // Red }
`);
from fragmentcolor import Shader
shader = Shader(""" @vertex fn vs_main(@builtin(vertex_index) index: u32) -> @builtin(position) vec4<f32> { var pos = array<vec2<f32>, 3>( vec2<f32>(-1.0, -1.0), vec2<f32>( 3.0, -1.0), vec2<f32>(-1.0, 3.0) ); return vec4<f32>(pos[index], 0.0, 1.0); }
@group(0) @binding(0) var<uniform> resolution: vec2<f32>;
@fragment fn fs_main() -> @location(0) vec4<f32> { return vec4<f32>(1.0, 0.0, 0.0, 1.0); // Red }
""")
// Swift placeholder â bindings WIP
// Kotlin placeholder â bindings WIP
Shader::set(key: string, value: any)
Section titled “Shader::set(key: string, value: any)”Sets the value of the uniform identified by the given key.
If the key does not exist or the value format is incorrect, the set
method throws an exception. The shader remains valid, and if the exception is caught, the shader can still be used with the renderer.
Example
Section titled “Example”1 collapsed line
fn main() -> Result<(), Box<dyn std::error::Error>> {use fragmentcolor::{Renderer, Shader};let r = Renderer::new();let shader = Shader::new(r#"@group(0) @binding(0) var<uniform> resolution: vec2<f32>;
struct VOut { @builtin(position) pos: vec4<f32> };@vertex fn vs_main(@builtin(vertex_index) i: u32) -> VOut {var p = array<vec2<f32>, 3>(vec2<f32>(-1.,-1.), vec2<f32>(3.,-1.), vec2<f32>(-1.,3.));var out: VOut;out.pos = vec4<f32>(p[i], 0., 1.);return out;}@fragment fn main() -> @location(0) vec4<f32> { return vec4<f32>(1.,0.,0.,1.); }"#)?;
// Set scalars/vectors on declared uniformsshader.set("resolution", [800.0, 600.0])?;2 collapsed lines
Ok(())}
import { Renderer, Shader } from "fragmentcolor";const r = new Renderer();const shader = new Shader(`@group(0) @binding(0) var<uniform> resolution: vec2<f32>;
struct VOut { @builtin(position) pos: vec4<f32> };@vertex fn vs_main(@builtin(vertex_index) i: u32) -> VOut {var p = array<vec2<f32>, 3>(vec2<f32>(-1.,-1.), vec2<f32>(3.,-1.), vec2<f32>(-1.,3.));var out: VOut;out.pos = vec4<f32>(p[i], 0., 1.);return out;}@fragment fn main() -> @location(0) vec4<f32> { return vec4<f32>(1.,0.,0.,1.); }
`);
// Set scalars/vectors on declared uniformsshader.set("resolution", [800.0, 600.0]);
from fragmentcolor import Renderer, Shaderr = Renderer()shader = Shader("""@group(0) @binding(0) var<uniform> resolution: vec2<f32>;
struct VOut { @builtin(position) pos: vec4<f32> };@vertex fn vs_main(@builtin(vertex_index) i: u32) -> VOut {var p = array<vec2<f32>, 3>(vec2<f32>(-1.,-1.), vec2<f32>(3.,-1.), vec2<f32>(-1.,3.));var out: VOut;out.pos = vec4<f32>(p[i], 0., 1.);return out;}@fragment fn main() -> @location(0) vec4<f32> { return vec4<f32>(1.,0.,0.,1.); }
""")
# Set scalars/vectors on declared uniformsshader.set("resolution", [800.0, 600.0])
// Swift placeholder â bindings WIP
// Kotlin placeholder â bindings WIP
Shader::get(key: string) -> any
Section titled “Shader::get(key: string) -> any”Returns the current value of the uniform identified by the given key.
Example
Section titled “Example”1 collapsed line
fn main() -> Result<(), Box<dyn std::error::Error>> {use fragmentcolor::Shader;
let shader = Shader::default();shader.set("resolution", [800.0, 600.0])?;let res: [f32; 2] = shader.get("resolution")?;
3 collapsed lines
assert_eq!(res, [800.0, 600.0]);Ok(())}
import { Shader } from "fragmentcolor";
const shader = Shader.default();shader.set("resolution", [800.0, 600.0]);const res = shader.get("resolution");
from fragmentcolor import Shader
shader = Shader.default()shader.set("resolution", [800.0, 600.0])res = shader.get("resolution")
// Swift placeholder â bindings WIP
// Kotlin placeholder â bindings WIP
Shader::list_uniforms() -> [string]
Section titled “Shader::list_uniforms() -> [string]”Returns a list of all uniform names in the Shader (excluding struct fields).
Example
Section titled “Example”1 collapsed line
fn main() -> Result<(), Box<dyn std::error::Error>> {use fragmentcolor::Shader;
let shader = Shader::default();let list = shader.list_uniforms();
3 collapsed lines
assert!(list.contains(&"resolution".to_string()));Ok(())}
import { Shader } from "fragmentcolor";
const shader = Shader.default();const list = shader.listUniforms();
from fragmentcolor import Shader
shader = Shader.default()list = shader.list_uniforms()
// Swift placeholder â bindings WIP
// Kotlin placeholder â bindings WIP
Shader::list_keys() -> [string]
Section titled “Shader::list_keys() -> [string]”Returns a list of all keys in the Shader, including uniform names and struct fields using the dot notation.
Example
Section titled “Example”1 collapsed line
fn main() -> Result<(), Box<dyn std::error::Error>> {
use fragmentcolor::Shader;
let shader = Shader::default();let keys = shader.list_keys();
3 collapsed lines
assert!(keys.contains(&"resolution".to_string()));Ok(())}
import { Shader } from "fragmentcolor";
const shader = Shader.default();const keys = shader.listKeys();
from fragmentcolor import Shader
shader = Shader.default()keys = shader.list_keys()
// Swift placeholder â bindings WIP
// Kotlin placeholder â bindings WIP
Shader::from_vertex
Section titled “Shader::from_vertex”Build a basic WGSL shader source from a single Vertex layout.
This inspects the vertex position dimensionality (2D or 3D) and optional properties.
It generates a minimal vertex shader that consumes @location(0)
position and a fragment shader that returns a flat color by default.
If a color: vec4<f32>
property exists, it is passed through to the fragment stage and used as output.
This is intended as a fallback and for quick debugging. Canonical usage is the opposite: write your own shader and then build Meshes that match it.
Example
Section titled “Example”use fragmentcolor::{Shader, Vertex};
let vertex = Vertex::new([0.0, 0.0, 0.0]);let shader = Shader::from_vertex(&vertex);
1 collapsed line
let _ = shader;
import { Shader, Vertex } from "fragmentcolor";
const vertex = new Vertex([0.0, 0.0, 0.0]);const shader = Shader.fromVertex(vertex);
from fragmentcolor import Shader, Vertex
vertex = Vertex([0.0, 0.0, 0.0])shader = Shader.from_vertex(vertex)
// Swift placeholder: bindings WIP
// Kotlin placeholder: bindings WIP
Shader::from_mesh
Section titled “Shader::from_mesh”Build a basic WGSL shader source from the first vertex in a Mesh.
The resulting shader automatically adds the provided Mesh to its internal list of Meshes to render,
so the user doesn’t need to call Shader::add_mesh
manually.
This function uses the first Vertex to infer position dimensionality and optional properties.
It generates a minimal vertex shader that consumes @location(0)
position and a fragment shader that returns a flat color by default. If a color: vec4<f32>
property exists, it is passed through to the fragment stage and used as output.
Empty Mesh Handling
Section titled “Empty Mesh Handling”If the Mesh has no vertices, a default shader is returned and a warning is logged. Because the default shader does not take any vertex inputs, it is compatible with any Mesh.
Example
Section titled “Example”use fragmentcolor::{Mesh, Shader};
let mut mesh = Mesh::new();mesh.add_vertex([0.0, 0.0, 0.0]);let shader = Shader::from_mesh(&mesh);
1 collapsed line
let _ = shader;
import { Mesh, Shader } from "fragmentcolor";
const mesh = new Mesh();mesh.addVertex([0.0, 0.0, 0.0]);const shader = Shader.fromMesh(mesh);
from fragmentcolor import Mesh, Shader
mesh = Mesh()mesh.add_vertex([0.0, 0.0, 0.0])shader = Shader.from_mesh(mesh)
// Swift placeholder: bindings WIP
// Kotlin placeholder: bindings WIP
Shader::add_mesh
Section titled “Shader::add_mesh”Attach a Mesh to this Shader. The Renderer will draw all meshes attached to it (one draw call per mesh, same pipeline).
This method now validates that the mesh’s vertex/instance layout is compatible with the shader’s @location inputs and returns ResultResult<(), ShaderError>
.
- On success, the mesh is attached and will be drawn when this shader is rendered.
- On mismatch (missing attribute or type mismatch), returns an error and does not attach.
See also
Section titled “See also”Use Shader::validate_mesh for performing a compatibility check without attaching.
Example
Section titled “Example”1 collapsed line
fn main() -> Result<(), Box<dyn std::error::Error>> {use fragmentcolor::{Shader, Mesh};
let shader = Shader::new(r#"@vertex fn vs_main(@location(0) pos: vec3<f32>) -> @builtin(position) vec4<f32> { return vec4<f32>(pos, 1.0);}@fragment fn fs_main() -> @location(0) vec4<f32> { return vec4<f32>(1.,0.,0.,1.); }"#)?;
let mesh = Mesh::new();mesh.add_vertex([0.0, 0.0, 0.0]);
// Attach mesh to this shader (errors if incompatible)shader.add_mesh(&mesh)?;
// Renderer will draw the mesh when rendering this pass.// Each Shader represents a RenderPipeline or ComputePipeline// in the GPU. Adding multiple meshes to it will draw all meshes// and all its instances in the same Pipeline.
2 collapsed lines
Ok(())}
import { Shader, Mesh } from "fragmentcolor";
const shader = new Shader(`@vertex fn vs_main(@location(0) pos: vec3<f32>) -> @builtin(position) vec4<f32> { return vec4<f32>(pos, 1.0);}@fragment fn fs_main() -> @location(0) vec4<f32> { return vec4<f32>(1.,0.,0.,1.); }
`);
const mesh = new Mesh();mesh.addVertex([0.0, 0.0, 0.0]);
// Attach mesh to this shader (errors if incompatible)shader.addMesh(mesh);
// Renderer will draw the mesh when rendering this pass.// Each Shader represents a RenderPipeline or ComputePipeline// in the GPU. Adding multiple meshes to it will draw all meshes// and all its instances in the same Pipeline.
from fragmentcolor import Shader, Mesh
shader = Shader("""@vertex fn vs_main(@location(0) pos: vec3<f32>) -> @builtin(position) vec4<f32> { return vec4<f32>(pos, 1.0);}@fragment fn fs_main() -> @location(0) vec4<f32> { return vec4<f32>(1.,0.,0.,1.); }
""")
mesh = Mesh()mesh.add_vertex([0.0, 0.0, 0.0])
# Attach mesh to this shader (errors if incompatible)shader.add_mesh(mesh)
# Renderer will draw the mesh when rendering this pass.# Each Shader represents a RenderPipeline or ComputePipeline# in the GPU. Adding multiple meshes to it will draw all meshes# and all its instances in the same Pipeline.
// Swift placeholder: bindings WIP
// Kotlin placeholder: bindings WIP
Shader::remove_mesh
Section titled “Shader::remove_mesh”Remove a single Mesh previously attached to this Shader. If the Mesh is attached multiple times, removes the first match.
Example
Section titled “Example”1 collapsed line
fn main() -> Result<(), Box<dyn std::error::Error>> {use fragmentcolor::{Shader, Mesh};
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.0,0.0,1.0); }"#)?;
let mesh = Mesh::new();mesh.add_vertex([0.0, 0.0]);shader.add_mesh(&mesh)?;
// Detach the meshshader.remove_mesh(&mesh);2 collapsed lines
Ok(())}
import { Shader, Mesh } from "fragmentcolor";
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.0,0.0,1.0); }
`);
const mesh = new Mesh();mesh.addVertex([0.0, 0.0]);shader.addMesh(mesh);
// Detach the meshshader.removeMesh(mesh);
from fragmentcolor import Shader, Mesh
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.0,0.0,1.0); }
""")
mesh = Mesh()mesh.add_vertex([0.0, 0.0])shader.add_mesh(mesh)
# Detach the meshshader.remove_mesh(mesh)
// Swift placeholder: bindings WIP
// Kotlin placeholder: bindings WIP
Shader::remove_meshes
Section titled “Shader::remove_meshes”Remove multiple meshes from this Shader.
Example
Section titled “Example”1 collapsed line
fn main() -> Result<(), Box<dyn std::error::Error>> {use fragmentcolor::{Shader, Mesh};
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.0,0.0,1.0); }"#)?;
let m1 = Mesh::new();m1.add_vertex([0.0, 0.0]);let m2 = Mesh::new();m2.add_vertex([0.5, 0.0]);
shader.add_mesh(&m1)?;shader.add_mesh(&m2)?;
shader.remove_meshes([&m1, &m2]);2 collapsed lines
Ok(())}
import { Shader, Mesh } from "fragmentcolor";
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.0,0.0,1.0); }
`);
const m1 = new Mesh();m1.addVertex([0.0, 0.0]);const m2 = new Mesh();m2.addVertex([0.5, 0.0]);
shader.addMesh(m1);shader.addMesh(m2);
shader.removeMeshes([m1, m2]);
from fragmentcolor import Shader, Mesh
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.0,0.0,1.0); }
""")
m1 = Mesh()m1.add_vertex([0.0, 0.0])m2 = Mesh()m2.add_vertex([0.5, 0.0])
shader.add_mesh(m1)shader.add_mesh(m2)
shader.remove_meshes([m1, m2])
// Swift placeholder: bindings WIP
// Kotlin placeholder: bindings WIP
Shader::clear_meshes
Section titled “Shader::clear_meshes”Remove all meshes attached to this Shader.
Example
Section titled “Example”1 collapsed line
fn main() -> Result<(), Box<dyn std::error::Error>> {use fragmentcolor::{Shader, Mesh};
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.0,0.0,1.0); }"#)?;
let mesh = Mesh::new();mesh.add_vertex([0.0, 0.0]);shader.add_mesh(&mesh)?;
// Clear allshader.clear_meshes();2 collapsed lines
Ok(())}
import { Shader, Mesh } from "fragmentcolor";
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.0,0.0,1.0); }
`);
const mesh = new Mesh();mesh.addVertex([0.0, 0.0]);shader.addMesh(mesh);
// Clear allshader.clearMeshes();
from fragmentcolor import Shader, Mesh
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.0,0.0,1.0); }
""")
mesh = Mesh()mesh.add_vertex([0.0, 0.0])shader.add_mesh(mesh)
# Clear allshader.clear_meshes()
// Swift placeholder: bindings WIP
// Kotlin placeholder: bindings WIP
Shader::validate_mesh
Section titled “Shader::validate_mesh”Validate that a Mesh is compatible with this Shader’s vertex inputs.
- Checks presence and type for all @location(…) inputs of the vertex entry point.
- Matches attributes in the following order:
- Instance attributes by explicit @location index (if the mesh has instances)
- Vertex attributes by explicit @location index (position is assumed at @location(0))
- Fallback by name (tries instance first, then vertex)
- Returns Ok(()) when all inputs are matched with a compatible wgpu::VertexFormat; returns an error otherwise.
- This method is called automatically when adding a Mesh to a Shader or Pass, so you usually don’t need to call it manually.
- If the Shader has no @location inputs (fullscreen/builtin-only), attaching a Mesh is rejected.
- This method does not allocate GPU buffers; it inspects CPU-side vertex/instance data only.
Example
Section titled “Example”1 collapsed line
fn main() -> Result<(), Box<dyn std::error::Error>> {use fragmentcolor::{Shader, Pass, Mesh};
let shader = Shader::new(r#"struct VOut { @builtin(position) pos: vec4<f32> };@vertex fn vs_main(@location(0) pos: vec3<f32>) -> VOut {var out: VOut;out.pos = vec4<f32>(pos, 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("p", &shader);
let mesh = Mesh::new();mesh.add_vertices([[-0.5, -0.5, 0.0],[ 0.5, -0.5, 0.0],[ 0.0, 0.5, 0.0],]);
shader.validate_mesh(&mesh)?; // Okpass.add_mesh(&mesh)?;
2 collapsed lines
Ok(())}
import { Shader, Pass, Mesh } from "fragmentcolor";
const shader = new Shader(`struct VOut { @builtin(position) pos: vec4<f32> };@vertex fn vs_main(@location(0) pos: vec3<f32>) -> VOut {var out: VOut;out.pos = vec4<f32>(pos, 1.0);return out;}@fragment fn fs_main(_v: VOut) -> @location(0) vec4<f32> { return vec4<f32>(1.,0.,0.,1.); }
`);const pass = new Pass("p"); pass.addShader(shader);
const mesh = new Mesh();mesh.addVertices([[-0.5, -0.5, 0.0],[ 0.5, -0.5, 0.0],[ 0.0, 0.5, 0.0],]);
shader.validateMesh(mesh); // Ok;pass.addMesh(mesh);
from fragmentcolor import Shader, Pass, Mesh
shader = Shader("""struct VOut { @builtin(position) pos: vec4<f32> };@vertex fn vs_main(@location(0) pos: vec3<f32>) -> VOut {var out: VOut;out.pos = vec4<f32>(pos, 1.0);return out;}@fragment fn fs_main(_v: VOut) -> @location(0) vec4<f32> { return vec4<f32>(1.,0.,0.,1.); }
""")rpass = Pass("p"); rpass.add_shader(shader)
mesh = Mesh()mesh.add_vertices([[-0.5, -0.5, 0.0],[ 0.5, -0.5, 0.0],[ 0.0, 0.5, 0.0],])
shader.validate_mesh(mesh); # Okrpass.add_mesh(mesh)
// Swift placeholder: bindings WIP
// Kotlin placeholder: bindings WIP
Shader::is_compute
Section titled “Shader::is_compute”Returns true if this Shader is a compute shader (has a compute entry point).
Example
Section titled “Example”1 collapsed line
fn main() -> Result<(), Box<dyn std::error::Error>> {use fragmentcolor::Shader;
let shader = Shader::new(r#"@compute @workgroup_size(1)fn cs_main() { }"#)?;
// Call the methodlet is_compute = shader.is_compute();
4 collapsed lines
let _ = is_compute;assert!(shader.is_compute());Ok(())}
import { Shader } from "fragmentcolor";
const shader = new Shader(`@compute @workgroup_size(1)fn cs_main() { }
`);
// Call the methodconst is_compute = shader.isCompute();
from fragmentcolor import Shader
shader = Shader("""@compute @workgroup_size(1)fn cs_main() { }
""")
# Call the methodis_compute = shader.is_compute()
// Swift placeholder: bindings WIP
// Kotlin placeholder: bindings WIP