use naga::back::spv;
use naga::front::wgsl;
use naga::valid;
#[derive(Debug)]
pub enum CompileError {
Parse { name: String, message: String },
Validate { name: String, message: String },
SpirVWrite { name: String, message: String },
}
impl std::fmt::Display for CompileError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
CompileError::Parse { name, message } => {
write!(f, "WGSL parse error in `{name}`: {message}")
}
CompileError::Validate { name, message } => {
write!(f, "WGSL validation error in `{name}`: {message}")
}
CompileError::SpirVWrite { name, message } => {
write!(f, "SPIR-V write error in `{name}`: {message}")
}
}
}
}
impl std::error::Error for CompileError {}
pub fn wgsl_to_spirv(name: &str, source: &str) -> Result<Vec<u32>, CompileError> {
let module = wgsl::parse_str(source).map_err(|e| CompileError::Parse {
name: name.to_string(),
message: e.emit_to_string(source),
})?;
let info = valid::Validator::new(valid::ValidationFlags::all(), valid::Capabilities::all())
.validate(&module)
.map_err(|e| CompileError::Validate {
name: name.to_string(),
message: e.emit_to_string(source),
})?;
let options = spv::Options::default();
spv::write_vec(&module, &info, &options, None).map_err(|e| CompileError::SpirVWrite {
name: name.to_string(),
message: e.to_string(),
})
}
#[cfg(test)]
mod tests {
use super::*;
use aetna_core::shader::stock_wgsl;
fn assert_spirv_words(words: &[u32]) {
assert!(!words.is_empty(), "empty SPIR-V output");
assert_eq!(
words[0], 0x0723_0203,
"first word is not the SPIR-V magic number — got {:#x}",
words[0]
);
}
#[test]
fn rounded_rect_compiles() {
let words = wgsl_to_spirv("rounded_rect", stock_wgsl::ROUNDED_RECT)
.expect("rounded_rect.wgsl should compile cleanly");
assert_spirv_words(&words);
}
#[test]
fn text_compiles() {
let words =
wgsl_to_spirv("text", stock_wgsl::TEXT).expect("text.wgsl should compile cleanly");
assert_spirv_words(&words);
}
#[test]
fn parse_error_carries_name() {
let err = wgsl_to_spirv("broken", "not valid wgsl @@@")
.expect_err("garbage WGSL must not compile");
assert!(matches!(err, CompileError::Parse { .. }));
assert!(err.to_string().contains("broken"));
}
}