From 1b9233d0e79e51457ec0d24ee17ace7cf42ff720 Mon Sep 17 00:00:00 2001 From: nickwilcox <44078858+nickwilcox@users.noreply.github.com> Date: Sun, 27 Oct 2019 17:05:45 +1100 Subject: [PATCH] add support for shader macro predefines (#1257) --- CHANGELOG_VULKANO.md | 1 + vulkano-shaders/src/codegen.rs | 47 +++++++++++++++++++++++++++------- vulkano-shaders/src/lib.rs | 29 +++++++++++++++++++-- 3 files changed, 66 insertions(+), 11 deletions(-) diff --git a/CHANGELOG_VULKANO.md b/CHANGELOG_VULKANO.md index c1de9a98..a67b9a35 100644 --- a/CHANGELOG_VULKANO.md +++ b/CHANGELOG_VULKANO.md @@ -11,6 +11,7 @@ - Update Winit to 0.20.0 - Update dependencies: lazy_static, half, syn, quote & proc-macro2 - Swapchain can now be recreated with dimensions of corresponding surface using `recreate()`. +- Add support for GLSL macro defines to the `shader!` macro. # Version 0.14.0 (2019-08-17) diff --git a/vulkano-shaders/src/codegen.rs b/vulkano-shaders/src/codegen.rs index 819515c7..5f173295 100644 --- a/vulkano-shaders/src/codegen.rs +++ b/vulkano-shaders/src/codegen.rs @@ -110,7 +110,7 @@ fn include_callback(requested_source_path_raw: &str, directive_type: IncludeType }) } -pub fn compile(path: Option, base_path: &impl AsRef, code: &str, ty: ShaderKind, include_directories: &[impl AsRef]) -> Result { +pub fn compile(path: Option, base_path: &impl AsRef, code: &str, ty: ShaderKind, include_directories: &[impl AsRef], macro_defines: &[(impl AsRef, impl AsRef)]) -> Result { let mut compiler = Compiler::new().ok_or("failed to create GLSL compiler")?; let mut compile_options = CompileOptions::new() .ok_or("failed to initialize compile option")?; @@ -128,6 +128,10 @@ pub fn compile(path: Option, base_path: &impl AsRef, code: &str, t recursion_depth, include_directories, path.is_some(), base_path) }); + for (macro_name, macro_value) in macro_defines.iter() { + compile_options.add_macro_definition(macro_name.as_ref(), Some(macro_value.as_ref())); + } + let content = compiler .compile_into_spirv(&code, ty, root_source_path, "main", Some(&compile_options)) .map_err(|e| e.to_string())?; @@ -437,6 +441,7 @@ mod tests { // 12th byte. Since we can't generate code for these types, we should // create an error instead of generating incorrect code. let includes: [PathBuf;0] = []; + let defines: [(String, String);0] = []; let comp = compile(None, &Path::new(""), " #version 450 struct MyStruct { @@ -446,7 +451,7 @@ mod tests { MyStruct s; }; void main() {} - ", ShaderKind::Vertex, &includes).unwrap(); + ", ShaderKind::Vertex, &includes, &defines).unwrap(); let doc = parse::parse_spirv(comp.as_binary()).unwrap(); let res = std::panic::catch_unwind(|| structs::write_structs(&doc)); assert!(res.is_err()); @@ -454,6 +459,7 @@ mod tests { #[test] fn test_trivial_alignment() { let includes: [PathBuf;0] = []; + let defines: [(String, String);0] = []; let comp = compile(None, &Path::new(""), " #version 450 struct MyStruct { @@ -463,7 +469,7 @@ mod tests { MyStruct s; }; void main() {} - ", ShaderKind::Vertex, &includes).unwrap(); + ", ShaderKind::Vertex, &includes, &defines).unwrap(); let doc = parse::parse_spirv(comp.as_binary()).unwrap(); structs::write_structs(&doc); } @@ -472,6 +478,7 @@ mod tests { // This is a workaround suggested in the case of test_bad_alignment, // so we should make sure it works. let includes: [PathBuf;0] = []; + let defines: [(String, String);0] = []; let comp = compile(None, &Path::new(""), " #version 450 struct Vec3Wrap { @@ -484,7 +491,7 @@ mod tests { MyStruct s; }; void main() {} - ", ShaderKind::Vertex, &includes).unwrap(); + ", ShaderKind::Vertex, &includes, &defines).unwrap(); let doc = parse::parse_spirv(comp.as_binary()).unwrap(); structs::write_structs(&doc); } @@ -493,26 +500,29 @@ mod tests { fn test_include_resolution() { let root_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); let empty_includes: [PathBuf;0] = []; + let defines: [(String, String);0] = []; let _compile_relative = compile(Some(String::from("tests/include_test.glsl")), &root_path, " #version 450 #include \"include_dir_a/target_a.glsl\" #include \"include_dir_b/target_b.glsl\" void main() {} - ", ShaderKind::Vertex, &empty_includes).expect("Cannot resolve include files"); + ", ShaderKind::Vertex, &empty_includes, &defines).expect("Cannot resolve include files"); let _compile_include_paths = compile(Some(String::from("tests/include_test.glsl")), &root_path, " #version 450 #include #include void main() {} - ", ShaderKind::Vertex,&[root_path.join("tests/include_dir_a"), root_path.join("tests/include_dir_b")]).expect("Cannot resolve include files"); + ", ShaderKind::Vertex,&[root_path.join("tests/include_dir_a"), root_path.join("tests/include_dir_b")], + &defines).expect("Cannot resolve include files"); let _compile_include_paths_with_relative = compile(Some(String::from("tests/include_test.glsl")), &root_path, " #version 450 #include #include <../include_dir_b/target_b.glsl> void main() {} - ", ShaderKind::Vertex,&[root_path.join("tests/include_dir_a")]).expect("Cannot resolve include files"); + ", ShaderKind::Vertex,&[root_path.join("tests/include_dir_a")], + &defines).expect("Cannot resolve include files"); let absolute_path = root_path.join("tests/include_dir_a/target_a.glsl"); let absolute_path_str = absolute_path.to_str().expect("Cannot run tests in a folder with non unicode characters"); @@ -520,12 +530,31 @@ mod tests { #version 450 #include \"{}\" void main() {{}} - ", absolute_path_str), ShaderKind::Vertex, &empty_includes).expect("Cannot resolve include files"); + ", absolute_path_str), ShaderKind::Vertex, &empty_includes, &defines).expect("Cannot resolve include files"); let _compile_recursive = compile(Some(String::from("tests/include_test.glsl")), &root_path, " #version 450 #include void main() {} - ", ShaderKind::Vertex,&[root_path.join("tests/include_dir_b"), root_path.join("tests/include_dir_c")]).expect("Cannot resolve include files"); + ", ShaderKind::Vertex,&[root_path.join("tests/include_dir_b"), root_path.join("tests/include_dir_c")], + &defines).expect("Cannot resolve include files"); + } + + #[test] + fn test_macros() { + let empty_includes: [PathBuf;0] = []; + let defines= vec![("NAME1", ""), ("NAME2", "58")]; + let no_defines: [(String, String);0] = []; + let need_defines = " + #version 450 + #if defined(NAME1) && NAME2 > 29 + void main() {} + #endif + "; + let compile_no_defines = compile(None, &Path::new(""), need_defines, ShaderKind::Vertex, &empty_includes, &no_defines); + assert!(compile_no_defines.is_err()); + + let compile_defines = compile(None, &Path::new(""), need_defines, ShaderKind::Vertex, &empty_includes, &defines); + compile_defines.expect("Setting shader macros did not work"); } } diff --git a/vulkano-shaders/src/lib.rs b/vulkano-shaders/src/lib.rs index af8b5be8..0189fa5e 100644 --- a/vulkano-shaders/src/lib.rs +++ b/vulkano-shaders/src/lib.rs @@ -135,6 +135,11 @@ //! to specify one or more standard include directories. Relative paths are relative to the //! directory, which contains the source file the `#include "..."` directive is declared in. //! +//! ## `define: [("NAME", "VALUE"), ...] +//! +//! Adds the given macro definitions to the pre-processor. This is equivalent to passing `-DNAME=VALUE` +//! on the command line. +//! //! ## `dump: true` //! //! The crate fails to compile but prints the generated rust code to stdout. @@ -185,6 +190,7 @@ struct MacroInput { shader_kind: ShaderKind, source_kind: SourceKind, include_directories: Vec, + macro_defines: Vec<(String, String)>, dump: bool, } @@ -194,6 +200,7 @@ impl Parse for MacroInput { let mut shader_kind = None; let mut source_kind = None; let mut include_directories = Vec::new(); + let mut macro_defines = Vec::new(); while !input.is_empty() { let name: Ident = input.parse()?; @@ -233,6 +240,24 @@ impl Parse for MacroInput { let path: LitStr = input.parse()?; source_kind = Some(SourceKind::Path(path.value())); } + "define" => { + let array_input; + bracketed!(array_input in input); + + while !array_input.is_empty() { + let tuple_input; + parenthesized!(tuple_input in array_input); + + let name: LitStr = tuple_input.parse()?; + tuple_input.parse::()?; + let value: LitStr = tuple_input.parse()?; + macro_defines.push((name.value(), value.value())); + + if !array_input.is_empty() { + array_input.parse::()?; + } + } + } "include" => { let in_brackets; bracketed!(in_brackets in input); @@ -274,7 +299,7 @@ impl Parse for MacroInput { let dump = dump.unwrap_or(false); - Ok(MacroInput { shader_kind, source_kind, include_directories, dump }) + Ok(MacroInput { shader_kind, source_kind, include_directories, dump, macro_defines }) } } @@ -312,6 +337,6 @@ pub fn shader(input: proc_macro::TokenStream) -> proc_macro::TokenStream { full_include_path }).collect::>(); - let content = codegen::compile(path, &root_path, &source_code, input.shader_kind, &include_paths).unwrap(); + let content = codegen::compile(path, &root_path, &source_code, input.shader_kind, &include_paths, &input.macro_defines).unwrap(); codegen::reflect("Shader", content.as_binary(), input.dump).unwrap().into() }