vk_shader_macros/lib.rs
1extern crate proc_macro;
2
3mod build;
4
5use std::path::Path;
6use std::{env, fs};
7
8use proc_macro::TokenStream;
9use syn::parse::{Parse, ParseStream, Result};
10use syn::{parse_macro_input, LitStr, Token};
11
12use self::build::{BuildOptions, Builder, Output};
13
14struct IncludeGlsl(Output);
15
16impl Parse for IncludeGlsl {
17 fn parse(input: ParseStream) -> Result<Self> {
18 let path_lit = input.parse::<LitStr>()?;
19 let path = Path::new(&env::var("CARGO_MANIFEST_DIR").unwrap()).join(&path_lit.value());
20 let path_str = path.to_string_lossy();
21
22 let src = fs::read_to_string(&path).map_err(|e| syn::Error::new(path_lit.span(), e))?;
23
24 let options = if input.peek(Token![,]) {
25 input.parse::<Token![,]>()?;
26 input.parse::<BuildOptions>()?
27 } else {
28 BuildOptions::default()
29 };
30
31 let builder = Builder {
32 src,
33 name: path_str.into_owned(),
34 path: Some(path),
35 span: path_lit.span(),
36 options,
37 };
38 builder.build().map(Self)
39 }
40}
41
42struct Glsl(Output);
43
44impl Parse for Glsl {
45 fn parse(input: ParseStream) -> Result<Self> {
46 let options = input.parse::<BuildOptions>()?;
47
48 if options.unterminated {
49 input.parse::<Token![,]>()?;
50 }
51
52 let src_lit = input.parse::<LitStr>()?;
53 let src = src_lit.value();
54
55 if input.peek(Token![,]) {
56 input.parse::<Token![,]>()?;
57 }
58
59 let builder = Builder {
60 src,
61 name: "inline".to_owned(),
62 path: None,
63 span: src_lit.span(),
64 options,
65 };
66 builder.build().map(Self)
67 }
68}
69
70/// Compile a GLSL source file into a binary SPIR-V constant
71///
72/// ```
73/// use vk_shader_macros::include_glsl;
74/// const VERT: &[u32] = include_glsl!("example.vert");
75/// ```
76///
77/// Due to limitations of proc macros, paths are resolved relative to the crate root.
78///
79/// # Options
80///
81/// Compile options may be specified as additional arguments. Supported options include:
82/// - `kind: <kind>` - Specify shader kind. Valid kinds are the same as the recognized file
83/// extensions: `vert`, `frag`, `comp`, `geom`, `tesc`, `tese`, `spvasm`, `rgen`, `rahit`,
84/// `rchit`, `rmiss`, `rint`, `rcall`, `task`, and `mesh`. If omitted, kind is inferred from the
85/// file's extension, or a pragma in the source.
86/// - `version: <version>` - Specify GLSL version. If omitted, version must be specified in the
87/// source with `#version`
88/// - `strip` - Omit debug info (set as default by enabling the `strip` feature)
89/// - `debug` - Force debug info, even with the `strip` feature enabled
90/// - `define: <name> ["value"]` - Define the preprocessor macro `<name>` as `value`
91/// - `optimize: <level>` - Specify optimization level. Supported values are: `zero`, `size`, and
92/// `performance`. If omitted, will default to `performance`.
93/// - `target: <target>` - Specify target environment. Supported values: `vulkan1_0`, `vulkan1_1`,
94/// `vulkan1_2`, `vulkan1_3`. Defaults to `vulkan1_0`.
95#[proc_macro]
96pub fn include_glsl(tokens: TokenStream) -> TokenStream {
97 let IncludeGlsl(output) = parse_macro_input!(tokens as IncludeGlsl);
98 output.expand()
99}
100
101/// Compile inline GLSL source
102///
103/// ```
104/// use vk_shader_macros::glsl;
105/// const VERT: &[u32] = glsl! {
106/// version: 450, kind: vert, optimize: size, target: vulkan1_1,
107/// r#"
108/// #version 450
109///
110/// void main() {
111/// gl_Position = vec4(0);
112/// }
113/// "#
114/// };
115/// ```
116///
117/// Because the shader kind cannot be inferred from a file extension,
118/// you may need to specify it manually as the above example does or
119/// add it to the source code, e.g. `#pragma shader_stage(vertex)`.
120///
121/// Due to limitations of proc macros, includes are resolved relative to the crate root.
122///
123/// # Options
124///
125/// See the [`include_glsl!`] macro for a list of compile options.
126#[proc_macro]
127pub fn glsl(tokens: TokenStream) -> TokenStream {
128 let Glsl(output) = parse_macro_input!(tokens as Glsl);
129 output.expand()
130}