add support for shader macro predefines (#1257)

This commit is contained in:
nickwilcox 2019-10-27 17:05:45 +11:00 committed by Austin Johnson
parent 761fb4e633
commit 1b9233d0e7
3 changed files with 66 additions and 11 deletions

View File

@ -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)

View File

@ -110,7 +110,7 @@ fn include_callback(requested_source_path_raw: &str, directive_type: IncludeType
})
}
pub fn compile(path: Option<String>, base_path: &impl AsRef<Path>, code: &str, ty: ShaderKind, include_directories: &[impl AsRef<Path>]) -> Result<CompilationArtifact, String> {
pub fn compile(path: Option<String>, base_path: &impl AsRef<Path>, code: &str, ty: ShaderKind, include_directories: &[impl AsRef<Path>], macro_defines: &[(impl AsRef<str>, impl AsRef<str>)]) -> Result<CompilationArtifact, String> {
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<String>, base_path: &impl AsRef<Path>, 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 <target_a.glsl>
#include <target_b.glsl>
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 <target_a.glsl>
#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 <target_c.glsl>
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");
}
}

View File

@ -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<String>,
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::<Token![,]>()?;
let value: LitStr = tuple_input.parse()?;
macro_defines.push((name.value(), value.value()));
if !array_input.is_empty() {
array_input.parse::<Token![,]>()?;
}
}
}
"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::<Vec<_>>();
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()
}