Computing
Compute commands are recorded after binding a ComputePipeline. They are typically paired with
shader_resource_access for storage buffers or images, and resource_access for indirect argument
buffers.
API docs: ComputeCommandRef::dispatch,
ComputeCommandRef::dispatch_base,
ComputeCommandRef::dispatch_indirect,
ComputeCommandRef::push_constants.
Available Commands
| Command | Typical use |
|---|---|
dispatch | Launch workgroups directly from CPU-provided dimensions |
dispatch_base | Launch workgroups with a non-zero base workgroup ID |
dispatch_indirect | Read dispatch dimensions from a buffer on the device |
push_constants | Update small pipeline constants without a buffer upload |
Direct Dispatch
dispatch is the default option. Use it when the CPU already knows the workgroup count.
let mut graph = Graph::default();
let output = graph.bind_resource(Buffer::create(
&device,
BufferInfo::device_mem(
4096,
vk::BufferUsageFlags::STORAGE_BUFFER,
),
)?);
let pipeline = ComputePipeline::create(
&device,
ComputePipelineInfo::default(),
Shader::new_compute([0u8; 4].as_slice()),
)?;
graph
.begin_cmd()
.debug_name("prefix sum")
.bind_pipeline(&pipeline)
.shader_resource_access(0, output, AccessType::ComputeShaderWrite)
.record_cmd(move |cmd| {
cmd.dispatch(64, 1, 1);
});
Offset Dispatches
dispatch_base is useful when a pipeline processes a tiled domain and each invocation needs a
non-zero gl_WorkGroupID origin.
let mut graph = Graph::default();
let output = graph.bind_resource(Buffer::create(
&device,
BufferInfo::device_mem(4096, vk::BufferUsageFlags::STORAGE_BUFFER),
)?);
let pipeline = ComputePipeline::create(
&device,
ComputePipelineInfo::default(),
Shader::new_compute([0u8; 4].as_slice()),
)?;
graph
.begin_cmd()
.bind_pipeline(&pipeline)
.shader_resource_access(0, output, AccessType::ComputeShaderWrite)
.record_cmd(move |cmd| {
cmd.dispatch_base(4, 2, 0, 16, 8, 1);
});
GPU-Driven Dispatch
dispatch_indirect lets an earlier pass write the group counts into a buffer. The compute pass
then consumes those parameters without CPU intervention.
let mut graph = Graph::default();
let output = graph.bind_resource(Buffer::create(
&device,
BufferInfo::device_mem(4096, vk::BufferUsageFlags::STORAGE_BUFFER),
)?);
let args = vk::DispatchIndirectCommand { x: 32, y: 8, z: 1 };
let args_buffer = graph.bind_resource(Buffer::create_from_slice(
&device,
vk::BufferUsageFlags::INDIRECT_BUFFER | vk::BufferUsageFlags::TRANSFER_DST,
bytemuck::cast_slice::<u32, u8>(&[args.x, args.y, args.z]),
)?);
let pipeline = ComputePipeline::create(
&device,
ComputePipelineInfo::default(),
Shader::new_compute([0u8; 4].as_slice()),
)?;
graph
.begin_cmd()
.bind_pipeline(&pipeline)
.resource_access(args_buffer, AccessType::IndirectBuffer)
.shader_resource_access(0, output, AccessType::ComputeShaderWrite)
.record_cmd(move |cmd| {
cmd.dispatch_indirect(args_buffer, 0);
});
Push Constants
Use ComputeCommandRef::push_constants
for small values that change often, such as frame indices, dispatch parameters,
or other compact state.
graph
.begin_cmd()
.bind_pipeline(&pipeline)
.record_cmd(move |cmd| {
cmd.push_constants(0, &[42])
.dispatch(1, 1, 1);
});
Notes
dispatchanddispatch_baseare the simplest and cheapest commands to drive from CPU code.dispatch_indirectis the usual choice for GPU-generated work queues or culling results.- The bound pipeline and declared resource access determine the synchronization requirements around the dispatch.