forked from nonsensical-dev/zenyx-engine
100 lines
3 KiB
Rust
100 lines
3 KiB
Rust
use build_print::{info, warn};
|
|
use naga::{
|
|
ShaderStage,
|
|
back::spv::{self, WriterFlags},
|
|
front::glsl::{self, Options as GlslOptions, ParseErrors},
|
|
valid::{ValidationError, ValidationFlags, Validator},
|
|
};
|
|
use std::{
|
|
env, fs,
|
|
path::{Path, PathBuf},
|
|
};
|
|
use thiserror::Error;
|
|
|
|
#[derive(Debug, Error)]
|
|
pub enum BuildError {
|
|
#[error("I/O error: {0}")]
|
|
Io(#[from] std::io::Error),
|
|
#[error("environment variable error: {0}")]
|
|
EnvVar(#[from] env::VarError),
|
|
#[error("unsupported shader extension: {0}")]
|
|
UnsupportedExt(String),
|
|
#[error("GLSL parse errors in `{0}`:\n{1}")]
|
|
ParseErrors(String, ParseErrors),
|
|
#[error("validation errors in `{0}`: {1}")]
|
|
ValidateErrors(String, ValidationError),
|
|
#[error("SPIR-V write error for `{0}`: {1}")]
|
|
Spv(String, spv::Error),
|
|
}
|
|
|
|
impl From<(String, ParseErrors)> for BuildError {
|
|
fn from((s, e): (String, ParseErrors)) -> Self {
|
|
BuildError::ParseErrors(s, e)
|
|
}
|
|
}
|
|
|
|
impl From<(String, ValidationError)> for BuildError {
|
|
fn from((s, e): (String, ValidationError)) -> Self {
|
|
BuildError::ValidateErrors(s, e)
|
|
}
|
|
}
|
|
|
|
impl From<(String, spv::Error)> for BuildError {
|
|
fn from((s, e): (String, spv::Error)) -> Self {
|
|
BuildError::Spv(s, e)
|
|
}
|
|
}
|
|
|
|
fn compile_shader(path: &Path, out_dir: &Path) -> Result<(), BuildError> {
|
|
let ext = path
|
|
.extension()
|
|
.and_then(|e| e.to_str())
|
|
.map(str::to_string)
|
|
.ok_or_else(|| BuildError::UnsupportedExt(path.display().to_string()))?;
|
|
let stage = match ext.as_str() {
|
|
"vert" => ShaderStage::Vertex,
|
|
"frag" => ShaderStage::Fragment,
|
|
"comp" => ShaderStage::Compute,
|
|
_ => return Err(BuildError::UnsupportedExt(ext)),
|
|
};
|
|
let src = fs::read_to_string(path)?;
|
|
|
|
let module = glsl::Frontend::default()
|
|
.parse(&GlslOptions::from(stage), &src)
|
|
.map_err(|e| (ext.clone(), e))?;
|
|
let info = Validator::new(ValidationFlags::all(), Default::default())
|
|
.validate(&module)
|
|
.map_err(|e| (ext.clone(), e.into_inner()))?;
|
|
let mut writer = spv::Writer::new(&spv::Options {
|
|
flags: WriterFlags::empty(),
|
|
..Default::default()
|
|
})
|
|
.map_err(|e| (ext.clone(), e))?;
|
|
let mut spirv = Vec::new();
|
|
writer
|
|
.write(&module, &info, None, &None, &mut spirv)
|
|
.map_err(|e| (ext.clone(), e))?;
|
|
let out_path = out_dir.join(format!("{}.spv", ext));
|
|
fs::write(&out_path, bytemuck::cast_slice(&spirv))?;
|
|
info!("Compiled {} → {}", path.display(), out_path.display());
|
|
Ok(())
|
|
}
|
|
|
|
fn main() -> Result<(), BuildError> {
|
|
println!("cargo:rerun-if-changed=shaders");
|
|
let out_dir = PathBuf::from(env::var("OUT_DIR")?);
|
|
for entry in fs::read_dir("shaders")? {
|
|
let path = entry?.path();
|
|
if !path.is_file() {
|
|
continue;
|
|
}
|
|
if let Err(e) = compile_shader(&path, &out_dir) {
|
|
if matches!(e, BuildError::UnsupportedExt(_)) {
|
|
warn!("{}", e);
|
|
continue;
|
|
}
|
|
return Err(e);
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|