pebble-engine 2.7.1

A modular, ECS-style graphics/app framework for Rust.
Documentation
# Compute Pipelines


`Compute` is the compute-pipeline counterpart to [`Material`](./materials.md) — WGSL shader source plus its bind group layout, same asset pipeline pattern:

```rust,ignore
fn setup(backend: Read<Backend>, mut computes: Write<Assets<Compute>>) {
    let entries = OwnEntriesBuilder::new()
        .with_entry("data", BindingKind::storage_buffer_read_write(ShaderStages::COMPUTE))
        .build();

    Compute::new(SHADER_SOURCE)
        .with_entries(vec![entries])
        .build_asset("particles", &mut computes);
}
```

Every entry in a compute pipeline's own bind group must be visible to exactly `ShaderStages::COMPUTE` — mixing in `VERTEX`/`FRAGMENT` panics at upload time. `with_entry_point` overrides the default `cs_main`.

Supply the actual buffers/textures the same way as a material, via `ComputeInstance` (`BindingInstance<Compute>`):

```rust,ignore
ComputeInstance::new(compute_handle)
    .with_storage("data", initial_bytes)
    .build_asset("particles_instance", &mut instances);
```

`.with_storage`/`.with_uniform` build a fresh buffer from raw bytes. To bind a buffer you already have — e.g. chaining compute passes, where one pass's output storage buffer is the next pass's input — use `.with_buffer(name, existing_buffer)` instead: it binds `existing_buffer` as-is, no new buffer is created. `existing_buffer` must already carry usage flags matching how `name` was declared in `with_entries` (`BufferUsages::UNIFORM` or `::STORAGE`):

```rust,ignore
ComputeInstance::new(compute_handle)
    .with_buffer("data", previous_pass_output.clone())
    .build_asset("second_pass_instance", &mut instances);
```

## Dispatching


Unlike rendering, compute work isn't tied to the frame lifecycle — it doesn't need a swapchain frame to exist, so it dispatches immediately via `Backend::dispatch_compute`, in its own command encoder, submitted right away:

```rust,ignore
fn simulate(backend: Read<Backend>, compute: Read<Assets<Compute>>, instances: Read<Assets<ComputeInstance>>) {
    let Some(pipeline) = compute.get(compute_handle) else { return };
    let Some(instance) = instances.get(instance_handle) else { return };

    backend.dispatch_compute(|pass| {
        pass.set_pipeline(&pipeline.pipeline);
        pass.set_bind_group(0, &instance.bind_group, &[]);
        pass.dispatch_workgroups(64, 1, 1);
    });
}
```

`ComputePass` also has `dispatch_workgroups_indirect(buffer, offset)`. To read a result back afterward, use [`Buffer::read`](./buffers.md#reading-a-buffer-back) on the underlying storage buffer and poll the returned `Promise` — the GPU work itself is already submitted by the time `dispatch_compute` returns, but reading it back is still async.