vello_sparse_shaders 0.0.6

Provide compilation of wgsl to glsl to support the WebGL `vello_hybrid` backend.
Documentation
// Copyright 2025 the Vello Authors
// SPDX-License-Identifier: Apache-2.0 OR MIT

//! Build.

use std::env;
use std::fmt::Write;
use std::fs;
use std::path::{Path, PathBuf};

#[allow(warnings)]
#[cfg(feature = "glsl")]
#[path = "src/compile.rs"]
mod compile;
#[allow(warnings)]
#[cfg(feature = "glsl")]
#[path = "src/types.rs"]
mod types;

#[cfg(feature = "glsl")]
use compile::compile_wgsl_shader;

// TODO: Format the generated code via `rustfmt`.
// TODO: Use `quote` instead of string concatenation to generate code.
fn main() {
    // Rerun build if the shaders directory changes
    println!("cargo:rerun-if-changed=shaders");
    let out_dir = env::var_os("OUT_DIR").unwrap();
    // Build outputs a `compiled_shaders.rs` module containing the GLSL source and reflection
    // metadata.
    let dest_path = Path::new(&out_dir).join("compiled_shaders.rs");

    // Get paths to WGSL files
    let shader_dir = PathBuf::from("shaders");
    let shader_paths = fs::read_dir(&shader_dir)
        .expect("Unable to read shaders directory")
        .filter_map(|entry| {
            let entry = entry.ok()?;
            let path = entry.path();
            if path.extension()?.to_str()? == "wgsl" {
                Some(path)
            } else {
                None
            }
        })
        .collect::<Vec<_>>();

    let mut shader_infos = Vec::new();
    for path in shader_paths {
        let file_stem = path.file_stem().unwrap().to_str().unwrap().to_owned();
        let source = fs::read_to_string(&path)
            .unwrap_or_else(|_| panic!("Failed to read shader {}", path.display()));
        shader_infos.push((file_stem, source));
    }
    shader_infos.sort_by(|a, b| a.0.cmp(&b.0));

    let mut buf = String::new();
    generate_compiled_shaders_module(&mut buf, &shader_infos);
    fs::write(dest_path, &buf).unwrap();
}

fn generate_compiled_shaders_module(buf: &mut String, shader_infos: &[(String, String)]) {
    writeln!(
        buf,
        "// Generated code by `vello_sparse_shaders` - DO NOT EDIT"
    )
    .unwrap();

    writeln!(buf, "/// Re-exporting wgsl shader source code.").unwrap();

    writeln!(buf, "pub mod wgsl {{").unwrap();
    for (shader_name, shader_source) in shader_infos {
        generate_wgsl_shader_module(buf, shader_name, shader_source).unwrap();
    }
    writeln!(buf, "}}").unwrap();

    // Implementation for creating a CompiledGlsl struct per shader assuming the standard entry
    // names of `vs_main` and `fs_main`.
    #[cfg(feature = "glsl")]
    {
        writeln!(
            buf,
            "/// Build time GLSL shaders derived from wgsl shaders."
        )
        .unwrap();

        for (shader_name, shader_source) in shader_infos {
            let compiled = compile_wgsl_shader(shader_source, "vs_main", "fs_main");

            let generated_code = compiled.to_generated_code(shader_name);
            writeln!(buf, "{generated_code}").unwrap();
        }
    }
}

fn generate_wgsl_shader_module<T: Write>(
    buf: &mut T,
    shader_name: &str,
    shader_source: &str,
) -> std::fmt::Result {
    let const_name = shader_name.to_uppercase();
    writeln!(buf, "    /// Source for `{shader_name}.wgsl`")?;
    writeln!(
        buf,
        "    pub const {const_name}: &str = r###\"{shader_source}\"###;"
    )?;

    Ok(())
}