zenfg 0.1.0

A FrameGraph compiler and transient-resource executor for wgpu
Documentation
use std::time::{Duration, Instant};

use zenfg::{BufferDesc, BufferRange, CompileOptions, FrameGraph, FrameGraphError, WriteContents};

const BUFFER_SIZE: u64 = 4096;

fn execute_aliasing_frame(
    graph: &mut FrameGraph,
    queue: &wgpu::Queue,
    logical_resources: usize,
) -> Result<(), FrameGraphError> {
    let mut frame = graph.begin_frame();
    for index in 0..logical_resources {
        let buffer = frame.create_buffer(BufferDesc::new(
            format!("alias-buffer-{index}"),
            BUFFER_SIZE,
        ))?;
        let mut pass = frame.command_pass(format!("alias-write-{index}"));
        let token =
            pass.storage_buffer_write(buffer, BufferRange::whole(), WriteContents::Overwrite)?;
        pass.finish_command(move |ctx| {
            let _ = ctx.resources.buffer(token)?;
            Ok(())
        })?;
    }
    frame.compile(CompileOptions::default())?.execute(queue)
}

fn execute_overlapping_frame(
    graph: &mut FrameGraph,
    queue: &wgpu::Queue,
    logical_resources: usize,
) -> Result<(), FrameGraphError> {
    let mut frame = graph.begin_frame();
    let mut buffers = Vec::with_capacity(logical_resources);
    for index in 0..logical_resources {
        let buffer = frame.create_buffer(BufferDesc::new(
            format!("overlap-buffer-{index}"),
            BUFFER_SIZE,
        ))?;
        let mut write = frame.compute_pass(format!("overlap-write-{index}"));
        let token =
            write.storage_buffer_write(buffer, BufferRange::whole(), WriteContents::Overwrite)?;
        write.finish_compute(move |ctx| {
            let _ = ctx.resources.buffer(token)?;
            Ok(())
        })?;
        buffers.push(buffer);
    }

    let mut consume = frame.command_pass("consume-overlapping-buffers");
    let mut tokens = Vec::with_capacity(buffers.len());
    for buffer in buffers {
        tokens.push(consume.storage_buffer_read(buffer, BufferRange::whole())?);
    }
    consume.finish_command(move |ctx| {
        for token in tokens {
            let _ = ctx.resources.buffer(token)?;
        }
        Ok(())
    })?;
    frame.compile(CompileOptions::default())?.execute(queue)
}

fn elapsed_per_frame(elapsed: Duration, frames: usize) -> Duration {
    elapsed / u32::try_from(frames).expect("frame count fits u32")
}

fn main() -> Result<(), FrameGraphError> {
    let (device, queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());

    let mut alias_graph = FrameGraph::with_device(&device);
    let cold_start = Instant::now();
    execute_aliasing_frame(&mut alias_graph, &queue, 256)?;
    let cold_elapsed = cold_start.elapsed();

    const WARM_FRAMES: usize = 200;
    let warm_start = Instant::now();
    for _ in 0..WARM_FRAMES {
        execute_aliasing_frame(&mut alias_graph, &queue, 64)?;
    }
    let warm_elapsed = warm_start.elapsed();
    let alias_stats = alias_graph.resource_pool_stats();

    let mut overlap_graph = FrameGraph::with_device(&device);
    let overlap_start = Instant::now();
    execute_overlapping_frame(&mut overlap_graph, &queue, 128)?;
    let overlap_elapsed = overlap_start.elapsed();
    let overlap_stats = overlap_graph.resource_pool_stats();

    println!("cold aliasing frame: {cold_elapsed:?} (256 logical / 1 physical)");
    println!(
        "warm aliasing frames: {:?} total, {:?} per frame",
        warm_elapsed,
        elapsed_per_frame(warm_elapsed, WARM_FRAMES)
    );
    println!("warm pool stats: {alias_stats:?}");
    println!("overlapping frame: {overlap_elapsed:?} (128 physical)");
    println!("overlapping pool stats: {overlap_stats:?}");
    Ok(())
}