wgcore 0.2.2

Utilities and abstractions for composable WGSL shaders.
Documentation
use std::ops::Div;
use nalgebra::{DVector, Vector4};
use wgcore::composer::ComposerExt;
use wgcore::gpu::GpuInstance;
use wgcore::kernel::{KernelInvocationBuilder, KernelInvocationQueue};
use wgcore::tensor::{GpuScalar, GpuVector};
use wgcore::Shader;
use wgpu::{BufferUsages, ComputePipeline};
use wgcore::hot_reloading::HotReloadState;
use wgcore::timestamps::GpuTimestamps;

#[derive(Shader)]
#[shader(
    src = "timestamp_queries.wgsl",
    composable = false
)]
struct ShaderTimestampQueries {
    main: ComputePipeline
}

#[async_std::main]
async fn main() -> anyhow::Result<()> {
    // Initialize the gpu device and its queue.
    //
    // Note that `GpuInstance` is just a simple helper struct for initializing the gpu resources.
    // You are free to initialize them independently if more control is needed, or reuse the ones
    // that were already created/owned by e.g., a game engine.
    let gpu = GpuInstance::new().await?;

    // Load and compile our kernel. The `from_device` function was generated by the `Shader` derive.
    // Note that its dependency to `Composable` is automatically resolved by the `Shader` derive
    // too.
    let mut kernel = ShaderTimestampQueries::from_device(gpu.device())?;

    // Create the buffers.
    const LEN: u32 = 2_000_000;
    let buffer = GpuVector::init(gpu.device(), vec![0u32; LEN as usize], BufferUsages::STORAGE | BufferUsages::COPY_SRC);

    // Init hot-reloading.
    // We are setting up hot-reloading so that we can change somme elements in the shader
    // (like the iteration count) and see how that affects performances live.
    let mut hot_reload = HotReloadState::new()?;
    ShaderTimestampQueries::watch_sources(&mut hot_reload)?;

    // Init timestamp queries.
    // To measure the time of one kernel, we need two timestamps (one for when it starts and one for
    // when it stopped).
    let mut timestamps = GpuTimestamps::new(gpu.device(), 2);

    // Queue the operation.
    println!("#############################");
    println!("Edit the file `timestamp_queries.wgsl` (for example by multiplying or dividing NUM_ITERS by 10).\nThe updated runtime will be printed below whenever a change is detected.");
    println!("#############################");

    for loop_id in 0.. {
        // Detect & apply changes.
        hot_reload.update_changes();
        match kernel.reload_if_changed(gpu.device(), &hot_reload) {
            Ok(changed) => {
                if changed {
                    // Clear the timestamps to reuse in the next loop.
                    timestamps.clear();
                    // We detected a change (or this is the first loop).
                    // Read the result.
                    let mut queue = KernelInvocationQueue::new(gpu.device());
                    // Declare a compute pass with timestamps enabled.
                    queue.compute_pass("timestamp_queries_test", true);
                    KernelInvocationBuilder::new(&mut queue, &kernel.main)
                        .bind0([buffer.buffer()])
                        .queue(LEN.div_ceil(64));

                    // Encode & submit the operation to the gpu.
                    let mut encoder = gpu.device().create_command_encoder(&Default::default());
                    // Run our kernel.
                    queue.encode(&mut encoder, Some(&mut timestamps));
                    // Resolve the timestamp queries.
                    timestamps.resolve(&mut encoder);
                    gpu.queue().submit(Some(encoder.finish()));

                    // Read and print the kernel’s runtime.
                    let timestamps_read = timestamps.wait_for_results_ms(gpu.device(), gpu.queue());
                    println!("Current run time: {}ms", timestamps_read[1] - timestamps_read[0]);
                }
            }
            Err(e) => {
                // Hot-reloading failed, likely due to a syntax error in the shader.
                println!("Hot reloading error: {:?}", e);
            }
        }
    }

    Ok(())
}