Better error reporting, including validating WGSL file

This commit is contained in:
Matthew Gordon 2024-12-09 21:00:54 -04:00
parent 395e641250
commit 5226578593
2 changed files with 64 additions and 22 deletions

View File

@ -9,3 +9,4 @@ proc-macro = true
[dependencies] [dependencies]
quote = "1.0" quote = "1.0"
syn = "2.0.90" syn = "2.0.90"
naga = { version = "23.0.0", features = ["wgsl-in"] }

View File

@ -1,24 +1,68 @@
#![feature(proc_macro_span)] #![feature(proc_macro_span)]
use { use {
naga::front::wgsl,
proc_macro::TokenStream, proc_macro::TokenStream,
quote::quote, quote::quote,
std::{fs, path::PathBuf}, std::fs,
syn::{parse, LitStr}, syn::{parse, LitStr},
}; };
fn emit_create_shader_module(file_contents: &str) -> TokenStream { struct ShaderModule {
quote! { source_file: std::path::PathBuf,
wgpu::ShaderModuleDescriptor { source_text: String,
label: None, naga_module: naga::Module,
source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(#file_contents)), }
}
impl ShaderModule {
fn try_new(source_file: impl Into<std::path::PathBuf>) -> Result<Self, Error> {
let source_file = source_file.into();
let source_text = fs::read_to_string(&source_file).map_err(|err| {
Error::from_message(format!(
"Could not open \"{}\": {}",
&source_file.as_os_str().to_string_lossy(),
err
))
})?;
let naga_module = wgsl::parse_str(&source_text).map_err(|err| {
if let Some(location) = err.location(&source_text) {
Error::Message(format!(
"Error in {} at line {}, column {}:\n{}",
&source_file.as_os_str().to_string_lossy(),
location.line_number,
location.line_position,
err.message()
))
} else {
Error::Message(format!(
"Error in {}:\n{}",
&source_file.as_os_str().to_string_lossy(),
err.message()
))
}
})?;
Ok(Self {
source_file,
source_text,
naga_module,
})
}
fn emit_rust_code(&self) -> TokenStream {
let source_text = &self.source_text;
quote! {
wgpu::ShaderModuleDescriptor {
label: None,
source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(#source_text)),
}
}
.into()
} }
.into()
} }
struct WgslModuleMacroInput { struct WgslModuleMacroInput {
source_dir: PathBuf, source_dir: std::path::PathBuf,
filename: String, filename: String,
} }
@ -58,20 +102,17 @@ fn parse_input(token_stream: TokenStream) -> Result<WgslModuleMacroInput, Error>
fn wgsl_module_inner(input_token_stream: TokenStream) -> Result<TokenStream, Error> { fn wgsl_module_inner(input_token_stream: TokenStream) -> Result<TokenStream, Error> {
let input = parse_input(input_token_stream)?; let input = parse_input(input_token_stream)?;
let full_filename = input.source_dir.join(&input.filename); let full_filename = input.source_dir.join(&input.filename);
let contents = fs::read_to_string(&full_filename).map_err(|err| { let module = ShaderModule::try_new(full_filename)?;
Error::from_message(format!( Ok(module.emit_rust_code())
"Could not open \"{}\": {}",
&full_filename.as_os_str().to_string_lossy(),
err.to_string()
))
})?;
Ok(emit_create_shader_module(&contents))
} }
#[proc_macro] #[proc_macro]
pub fn wgsl_module(input: TokenStream) -> TokenStream { pub fn wgsl_module(input: TokenStream) -> TokenStream {
wgsl_module_inner(input).unwrap_or_else(|err| match err { wgsl_module_inner(input).unwrap_or_else(|err| {
Error::Message(message) => quote! { compile_error!(#message) }, match err {
Error::SynError(syn_err) => syn_err.into_compile_error(), Error::Message(message) => quote! { compile_error!(#message) },
}.into()) Error::SynError(syn_err) => syn_err.into_compile_error(),
}
.into()
})
} }