pub trait ShaderAppExt {
// Required methods
fn register_uniform_buffer<U>(
&mut self,
group_index: u32,
binding_index: u32,
) -> &mut Self
where U: ShaderType + WriteInto + Default + Resource + Clone + Send + Sync + 'static;
fn register_storage_buffer<S>(
&mut self,
group_index: u32,
binding_index: u32,
read_write: bool,
) -> &mut Self
where S: ShaderType + WriteInto + Default + Resource + Clone + Send + Sync + 'static;
fn register_array_buffer<Tag, T, const N: usize>(
&mut self,
group_index: u32,
binding_index: u32,
read_write: bool,
) -> &mut Self
where Tag: Send + Sync + 'static,
T: ShaderSize + WriteInto + Default + Send + Sync + 'static;
fn register_gpu_buffer<Tag, T>(
&mut self,
group_index: u32,
binding_index: u32,
len: u32,
) -> &mut Self
where Tag: Send + Sync + 'static,
T: ShaderSize + WriteInto + Default + Send + Sync + 'static;
fn read_back_gpu_buffer<Tag, T>(&mut self, mode: ReadbackMode) -> &mut Self
where Tag: Send + Sync + 'static,
T: ShaderSize + CreateFrom + Send + Sync + 'static;
fn read_back_array_buffer<Tag, T>(
&mut self,
mode: ReadbackMode,
) -> &mut Self
where Tag: Send + Sync + 'static,
T: ShaderSize + CreateFrom + Send + Sync + 'static;
fn read_back_storage_buffer<S>(&mut self, mode: ReadbackMode) -> &mut Self
where S: ShaderType + WriteInto + CreateFrom + Default + Resource + Clone + Send + Sync + 'static;
}Expand description
Extension methods on App for registering buffers shared by the fullscreen
fragment shader and the compute shader.
Every registered buffer is visible to both stages at the same @group/@binding.
Declare each binding with the same access mode (read or read_write) in every
shader that uses it — wgpu rejects a mismatch.
Required Methods§
Sourcefn register_uniform_buffer<U>(
&mut self,
group_index: u32,
binding_index: u32,
) -> &mut Self
fn register_uniform_buffer<U>( &mut self, group_index: u32, binding_index: u32, ) -> &mut Self
Register an auto-managed uniform buffer at @group(group_index) @binding(binding_index).
U must be inserted as a Resource in the main world. The library extracts
it to the render world and uploads it to a persistent uniform buffer whenever
the resource changes.
Multiple calls with the same group_index but different binding_index values
pack several uniform bindings into one bind group.
§Example
#[derive(Resource, Default, ShaderType, Clone)]
struct TimeUniforms { elapsed: f32 }
app.insert_resource(TimeUniforms::default())
.register_uniform_buffer::<TimeUniforms>(0, 0);
// WGSL: @group(0) @binding(0) var<uniform> time_uniforms: TimeUniforms;Sourcefn register_storage_buffer<S>(
&mut self,
group_index: u32,
binding_index: u32,
read_write: bool,
) -> &mut Self
fn register_storage_buffer<S>( &mut self, group_index: u32, binding_index: u32, read_write: bool, ) -> &mut Self
Register an auto-managed storage buffer at @group(group_index) @binding(binding_index).
S must be inserted as a Resource in the main world. The library extracts
it to the render world and uploads it to a persistent storage buffer whenever
the resource changes.
Multiple calls with the same group_index but different binding_index values
pack several storage bindings into one bind group.
read_write: false → var<storage, read>, true → var<storage, read_write>.
The CPU owns this buffer: shader writes persist until the resource next changes,
then the CPU upload replaces the whole buffer. For data the GPU owns, use
register_gpu_buffer.
§Example
#[derive(Resource, Default, ShaderType, Clone)]
struct Palette { colors: [Vec4; 16] }
app.insert_resource(Palette::default())
.register_storage_buffer::<Palette>(1, 0, false);
// WGSL: @group(1) @binding(0) var<storage, read> palette: Palette;Sourcefn register_array_buffer<Tag, T, const N: usize>(
&mut self,
group_index: u32,
binding_index: u32,
read_write: bool,
) -> &mut Self
fn register_array_buffer<Tag, T, const N: usize>( &mut self, group_index: u32, binding_index: u32, read_write: bool, ) -> &mut Self
Register a fixed-size array buffer at @group(group_index) @binding(binding_index).
Maps to WGSL array<T, N>. The buffer is initialized once with T::default() values
and persists across frames — only elements explicitly changed via ArrayBufferChanges
are uploaded each frame, batched into contiguous write_buffer runs.
Tag is a user-defined zero-sized marker type. Define one per registration so that
multiple buffers of the same element type and length can coexist:
struct Colors;
struct Positions;
app.register_array_buffer::<Colors, Vec4, 64>(1, 0, false);
app.register_array_buffer::<Positions, Vec4, 64>(2, 0, false);
// Each system names only its tag — no T or N required:
fn update_colors(mut changes: ResMut<ArrayBufferChanges<Colors>>) {
changes.set(0, Vec4::ONE);
}read_write: false → var<storage, read>, true → var<storage, read_write>.
Sourcefn register_gpu_buffer<Tag, T>(
&mut self,
group_index: u32,
binding_index: u32,
len: u32,
) -> &mut Self
fn register_gpu_buffer<Tag, T>( &mut self, group_index: u32, binding_index: u32, len: u32, ) -> &mut Self
Register a GPU-owned buffer of len elements at
@group(group_index) @binding(binding_index).
Maps to WGSL var<storage, read_write> name: array<T> (always read_write). The
buffer starts filled with T::default() and is never uploaded from the CPU again,
so shader writes persist across frames — use it for simulation state that compute
passes update. Its handle is available in the main world as GpuBuffer<Tag>.
Tag is a user-defined marker type naming the buffer, also used to size
dispatches with Workgroups::over:
#[derive(ShaderType, Clone, Copy, Default)]
struct Particle { pos: Vec2, vel: Vec2 }
struct Particles;
app.register_gpu_buffer::<Particles, Particle>(1, 0, 1024);
// WGSL: @group(1) @binding(0) var<storage, read_write> particles: array<Particle>;Sourcefn read_back_gpu_buffer<Tag, T>(&mut self, mode: ReadbackMode) -> &mut Self
fn read_back_gpu_buffer<Tag, T>(&mut self, mode: ReadbackMode) -> &mut Self
Enable reading the GPU buffer tagged Tag back to the CPU, decoded as T values
into the GpuReadback<Tag, T> resource. Call after
register_gpu_buffer, with the same T.
With ReadbackMode::OnRequest, call GpuReadback::request whenever you want a
copy. With ReadbackMode::EveryFrame, the buffer is copied every frame. Results
arrive 1–3 frames after the copy is made.
app.register_gpu_buffer::<Particles, Particle>(1, 0, 1024)
.read_back_gpu_buffer::<Particles, Particle>(ReadbackMode::OnRequest);
fn request(readback: Res<GpuReadback<Particles, Particle>>) {
readback.request();
}
fn print(readback: Res<GpuReadback<Particles, Particle>>) {
if !readback.is_changed() { return; }
let Some(particles) = readback.latest() else { return };
info!("{} particles", particles.len());
}Sourcefn read_back_array_buffer<Tag, T>(&mut self, mode: ReadbackMode) -> &mut Self
fn read_back_array_buffer<Tag, T>(&mut self, mode: ReadbackMode) -> &mut Self
Enable reading the array buffer tagged Tag back to the CPU, decoded as T values
into the GpuReadback<Tag, T> resource. Call after
register_array_buffer, with the same T.
Works exactly like read_back_gpu_buffer. Useful
when a shader writes to a read_write array buffer.
Sourcefn read_back_storage_buffer<S>(&mut self, mode: ReadbackMode) -> &mut Self
fn read_back_storage_buffer<S>(&mut self, mode: ReadbackMode) -> &mut Self
Enable reading the storage buffer of resource type S back to the CPU, decoded as
an S into the StorageReadback<S> resource. Call after
register_storage_buffer. If S is registered at
several bindings, the first storage registration is read.
Useful when a shader writes to a read_write storage buffer. With
ReadbackMode::OnRequest, call StorageReadback::request whenever you want a
copy. With ReadbackMode::EveryFrame, the buffer is copied every frame.
app.register_storage_buffer::<Stats>(1, 0, true)
.init_resource::<Stats>()
.read_back_storage_buffer::<Stats>(ReadbackMode::EveryFrame);
fn print(readback: Res<StorageReadback<Stats>>) {
let Some(stats) = readback.latest() else { return };
info!("{:?}", stats.total);
}Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".