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}