bevy_hanabi 0.19.0

Hanabi GPU particle system for the Bevy game engine
Documentation
#import bevy_hanabi::vfx_common::{EffectMetadata, Spawner}

/// Key-value pair for sorting, with optional second sort key.
struct KeyValuePair {
    /// Sorting key.
    key: u32,
    /// Secondary sorting key. Sorts value with the same primary key.
    key2: u32,
    /// Value associated with the sort key(s), generally an index to some other data.
    /// Copied as is and otherwise ignored by the sorting algorithm.
    value: u32,
}

struct SortBuffer {
    count: i32,
    pairs: array<KeyValuePair>,
}

struct IndirectIndexBuffer {
    data: array<u32>,
}

@group(0) @binding(0) var<storage, read_write> indirect_index_buffer : IndirectIndexBuffer;
@group(0) @binding(1) var<storage, read> sort_buffer : SortBuffer;
// Technically read-only, but the type contains atomic<> fields and wasm is strict about it
@group(0) @binding(2) var<storage, read_write> effect_metadata : EffectMetadata;
@group(0) @binding(3) var<storage, read> spawner : Spawner;

/// Copy the sorted particle indices back into the effect index buffer.
@compute @workgroup_size(64)
fn main(@builtin(global_invocation_id) global_invocation_id: vec3<u32>) {
    let row_index = global_invocation_id.x;
    let count = atomicLoad(&effect_metadata.alive_count); // TODO - atomic not needed
    if (row_index >= count) {
        return;
    }

    let base_particle = spawner.slab_offset;

    // Write the particle index back into the same ping-pong column index we read it from
    // in vfx_sort_fill. Sorting is optional and shouldn't influence that ping-pong logic.
    let write_index = effect_metadata.indirect_write_index;

    let particle_index = sort_buffer.pairs[row_index].value;
    indirect_index_buffer.data[(base_particle + row_index) * 3u + write_index] = particle_index;
}