Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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

CommandTypical use
dispatchLaunch workgroups directly from CPU-provided dimensions
dispatch_baseLaunch workgroups with a non-zero base workgroup ID
dispatch_indirectRead dispatch dimensions from a buffer on the device
push_constantsUpdate 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

  • dispatch and dispatch_base are the simplest and cheapest commands to drive from CPU code.
  • dispatch_indirect is 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.