use build_print::{error, info, note, warn};
use shaderc::ShaderKind;
use std::{
env,
ffi::OsString,
fs,
io::Write,
path::{Path, PathBuf},
};
fn main() -> Result<(), terminator::Terminator> {
println!("cargo::rerun-if-changed=shaders");
let out_dir: PathBuf = env::var("OUT_DIR")?.into();
let shader_compiler = ShaderCompiler::new()?;
for entry in fs::read_dir("shaders")? {
let path = entry?.path();
if !path.is_file() {
note!(
"shader-compile: skipping `{}` as it is a directory",
path.display()
);
continue;
}
if let Err(e) = shader_compiler.compile_shader(&path, &out_dir) {
if matches!(e, BuildError::UnsupportedExt(_) | BuildError::NotFile) {
warn!("{e}");
continue;
}
Err(e)?;
}
}
Ok(())
}
pub struct ShaderCompiler {
compiler: shaderc::Compiler,
}
impl ShaderCompiler {
pub fn new() -> Result<Self, BuildError> {
Ok(ShaderCompiler {
compiler: shaderc::Compiler::new().map_err(BuildError::FailedInit)?,
})
}
pub fn compile_shader(&self, path: &Path, out_dir: &Path) -> Result<(), BuildError> {
let file_name = path
.file_name()
.ok_or(BuildError::NotFile)?
.to_str()
.expect("utf8 file name for shader");
let ext = path
.extension()
.ok_or_else(|| BuildError::UnsupportedExt(path.to_owned().into_os_string()))?;
let shader_kind = match ext.as_encoded_bytes() {
b"vert" => ShaderKind::Vertex,
b"frag" => ShaderKind::Fragment,
_ => return Err(BuildError::UnsupportedExt(ext.to_owned())),
};
let src = fs::read_to_string(path)?;
let compiled =
self.compiler
.compile_into_spirv(&src, shader_kind, file_name, "main", None)?;
let output_path = out_dir.join(format!("{file_name}.spv"));
let mut output_file = fs::File::create(output_path)?;
output_file.write_all(compiled.as_binary_u8())?;
info!("Compiled {} -> {}", file_name, format!("{file_name}.spv"));
Ok(())
}
}
#[derive(Debug, thiserror::Error)]
pub enum BuildError {
#[error("Failed to initialise shader compiler")]
FailedInit(#[source] shaderc::Error),
#[error("I/O error when compiling shader")]
Io(
#[from]
#[source]
std::io::Error,
),
#[error("unsupported shader extension: {}", _0.to_str().unwrap())]
UnsupportedExt(OsString),
#[error("source file is not a file")]
NotFile,
#[error("failed to compile shader")]
CompileError(
#[from]
#[source]
shaderc::Error,
),
}