henad-compute 0.3.0

CPU and GPU engines that run Henad models, with their sim threads and parallel primitives.
Documentation
// Counting sort pass 2. Each agent claims a slot by bumping its cell's write cursor.
//
// The cursor starts as a copy of the scanned offsets, so a cell's agents land contiguously. Which
// slot within the cell is whatever order the atomics resolve in.

#import henad::dispatch::WORKGROUP

struct HashParams {
    grid_w: u32,
    grid_h: u32,
    num_agents: u32,
    groups_x: u32,
    cell_w_inv: f32,
    cell_h_inv: f32,
    _pad0: u32,
    _pad1: u32,
}

@group(0) @binding(0) var<storage, read> agent_cell: array<u32>;
@group(0) @binding(1) var<storage, read_write> cursor: array<atomic<u32>>;
@group(0) @binding(2) var<storage, read_write> sorted: array<u32>;
@group(0) @binding(3) var<uniform> params: HashParams;

@compute
@workgroup_size(256)
fn main(
    @builtin(local_invocation_id) lid: vec3<u32>,
    @builtin(workgroup_id) wid: vec3<u32>,
) {
    let i = (wid.y * params.groups_x + wid.x) * WORKGROUP + lid.x;
    if (i >= params.num_agents) {
        return;
    }

    let slot = atomicAdd(&cursor[agent_cell[i]], 1u);
    sorted[slot] = i;
}