use anyhow::*; use glob::glob; use std::fs::{read_to_string, write}; use std::path::PathBuf; use rayon::prelude::*; // Adapted from https://github.com/sotrh/learn-wgpu // by Benjamin Hansen, licensed under the MIT license // Rename this file to build.rs and uncomment the build-dependencies in Cargo.toml to compile // the shaders into .spv files struct ShaderData { src: String, src_path: PathBuf, spv_path: PathBuf, kind: shaderc::ShaderKind, } impl ShaderData { pub fn load(src_path: PathBuf) -> Result { let extension = src_path .extension() .context("File has no extension")? .to_str() .context("Extension cannot be converted to &str")?; let kind = match extension { "vert" => shaderc::ShaderKind::Vertex, "frag" => shaderc::ShaderKind::Fragment, "comp" => shaderc::ShaderKind::Compute, _ => bail!("Unsupported shader: {}", src_path.display()), }; let src = read_to_string(src_path.clone())?; let spv_path = src_path.with_extension(format!("{}.spv", extension)); Ok(Self { src, src_path, spv_path, kind, }) } } fn main() -> Result<()> { // Collect all shaders recursively within /src/ let mut shader_paths = Vec::new(); shader_paths.extend(glob("./src/**/*.vert")?); shader_paths.extend(glob("./src/**/*.frag")?); shader_paths.extend(glob("./src/**/*.comp")?); let shaders = shader_paths.into_par_iter() .map(|glob_result| { ShaderData::load(glob_result?) }) .collect::>>() .into_iter() .collect::>>()?; let mut compiler = shaderc::Compiler::new().context("Unable to create shader compiler")?; // This can't be parallelized. The [shaderc::Compiler] is not // thread safe. Also, it creates a lot of resources. You could // spawn multiple processes to handle this, but it would probably // be better just to only compile shaders that have been changed // recently. for shader in shaders { let compiled = compiler.compile_into_spirv( &shader.src, shader.kind, &shader.src_path.to_str().unwrap(), "main", None, )?; write(shader.spv_path, compiled.as_binary_u8())?; } Ok(()) }