dynamis-gpu 0.7.0

wgpu device, buffer, stream, pipeline, and readback runtime
Documentation
mod common;

use common::shared;
use dynamis_gpu::{
    Contents, GpuContext, Stream, StreamDesc, StreamElement, SubmissionEncoder, read_regions,
};
use wgpu::BufferUsages;

const STREAM_USAGE: BufferUsages = BufferUsages::STORAGE
    .union(BufferUsages::COPY_DST)
    .union(BufferUsages::COPY_SRC);

fn stream(
    context: &GpuContext,
    label: &'static str,
    slots: u32,
    element: StreamElement,
    elements_per_slot: u64,
    contents: Contents,
) -> Stream {
    Stream::new(
        context.device(),
        context.queue(),
        StreamDesc {
            label,
            slots,
            element,
            elements_per_slot,
            usage: STREAM_USAGE,
            contents,
        },
    )
}

fn seed(stream: &Stream, values: &[u32]) {
    stream.write(shared().queue(), bytemuck::cast_slice(values));
}

fn read(stream: &Stream, words: u64) -> Vec<u32> {
    let context = shared();
    let bytes = read_regions(
        context.device(),
        context.queue(),
        "stream probe",
        &[(stream.buffer(), 0, words * 4)],
    );
    bytemuck::cast_slice(&bytes).to_vec()
}

fn resized(stream: &mut Stream, slots: u32) -> bool {
    let context = shared();
    let mut encoder = SubmissionEncoder::new(context.device(), "stream resize");
    let changed = stream.reserve(context.device(), &mut encoder, slots);
    if changed {
        encoder.submit(context.queue());
    }
    changed
}

#[test]
fn a_preserving_stream_keeps_its_leading_slots_across_resizes() {
    let context = shared();
    let mut stream = stream(
        context,
        "preserving stream",
        4,
        StreamElement::new("u32", 4),
        1,
        Contents::Durable,
    );
    seed(&stream, &[7, 8, 9, 10]);
    assert!(!resized(&mut stream, 4));
    assert!(resized(&mut stream, 6));
    assert_eq!(stream.slots(), 6);
    assert_eq!(read(&stream, 4), vec![7, 8, 9, 10]);
    assert!(resized(&mut stream, 2));
    assert_eq!(read(&stream, 2), vec![7, 8]);
}

#[test]
fn a_reset_stream_drops_its_contents_across_resizes() {
    let context = shared();
    let mut stream = stream(
        context,
        "reset stream",
        4,
        StreamElement::new("u32", 4),
        1,
        Contents::Scratch,
    );
    seed(&stream, &[7, 8, 9, 10]);
    assert!(resized(&mut stream, 8));
    assert_eq!(read(&stream, 4), vec![0, 0, 0, 0]);
}

#[test]
fn a_seeded_stream_starts_with_its_head_word() {
    let context = shared();
    let stream = stream(
        context,
        "seeded stream",
        1,
        StreamElement::new("u32", 4),
        1,
        Contents::Seeded(u32::MAX),
    );
    assert_eq!(read(&stream, 1), vec![u32::MAX]);
}

#[test]
fn a_stream_reports_its_stride_and_bytes() {
    let context = shared();
    let stream = stream(
        context,
        "strided stream",
        3,
        StreamElement::new("vec4f", 16),
        4,
        Contents::Scratch,
    );
    assert_eq!(stream.slots(), 3);
    assert_eq!(stream.element(), StreamElement::new("vec4f", 16));
    assert_eq!(stream.stride(), 64);
    assert_eq!(stream.size(), 192);
}

#[test]
#[should_panic(expected = "requires at least one slot")]
fn a_stream_refuses_an_empty_capacity() {
    let context = shared();
    let _ = stream(
        context,
        "empty stream",
        0,
        StreamElement::new("u32", 4),
        1,
        Contents::Scratch,
    );
}

#[test]
#[should_panic(expected = "requires at least one element per slot")]
fn a_stream_refuses_an_empty_slot() {
    let context = shared();
    let _ = stream(
        context,
        "dense stream",
        4,
        StreamElement::new("u32", 4),
        0,
        Contents::Scratch,
    );
}

#[test]
#[should_panic(expected = "word aligned record")]
fn a_stream_refuses_an_unaligned_record() {
    let context = shared();
    let _ = stream(
        context,
        "unaligned stream",
        4,
        StreamElement::new("u32", 6),
        1,
        Contents::Scratch,
    );
}

#[test]
#[should_panic(expected = "storage binding limit")]
fn a_stream_refuses_to_exceed_the_binding_limit() {
    let context = shared();
    let _ = stream(
        context,
        "oversized stream",
        u32::MAX,
        StreamElement::new("vec4f", 16),
        4,
        Contents::Scratch,
    );
}