1#![deny(unsafe_code)]
7#![warn(
8 clippy::all,
9 clippy::await_holding_lock,
10 clippy::char_lit_as_u8,
11 clippy::checked_conversions,
12 clippy::dbg_macro,
13 clippy::debug_assert_with_mut_call,
14 clippy::doc_markdown,
15 clippy::empty_enum,
16 clippy::enum_glob_use,
17 clippy::exit,
18 clippy::expl_impl_clone_on_copy,
19 clippy::explicit_deref_methods,
20 clippy::explicit_into_iter_loop,
21 clippy::fallible_impl_from,
22 clippy::filter_map_next,
23 clippy::float_cmp_const,
24 clippy::fn_params_excessive_bools,
25 clippy::if_let_mutex,
26 clippy::implicit_clone,
27 clippy::imprecise_flops,
28 clippy::inefficient_to_string,
29 clippy::invalid_upcast_comparisons,
30 clippy::large_types_passed_by_value,
31 clippy::let_unit_value,
32 clippy::linkedlist,
33 clippy::lossy_float_literal,
34 clippy::macro_use_imports,
35 clippy::manual_ok_or,
36 clippy::map_err_ignore,
37 clippy::map_flatten,
38 clippy::map_unwrap_or,
39 clippy::match_same_arms,
40 clippy::match_wildcard_for_single_variants,
41 clippy::mem_forget,
42 clippy::mut_mut,
43 clippy::mutex_integer,
44 clippy::needless_borrow,
45 clippy::needless_continue,
46 clippy::option_option,
47 clippy::path_buf_push_overwrite,
48 clippy::ptr_as_ptr,
49 clippy::ref_option_ref,
50 clippy::rest_pat_in_fully_bound_structs,
51 clippy::same_functions_in_if_condition,
52 clippy::semicolon_if_nothing_returned,
53 clippy::string_add_assign,
54 clippy::string_add,
55 clippy::string_lit_as_bytes,
56 clippy::string_to_string,
57 clippy::todo,
58 clippy::trait_duplication_in_bounds,
59 clippy::unimplemented,
60 clippy::unnested_or_patterns,
61 clippy::unused_self,
62 clippy::useless_transmute,
63 clippy::verbose_file_reads,
64 clippy::zero_sized_map_values,
65 future_incompatible,
66 nonstandard_style,
67 rust_2018_idioms
68)]
69#![doc = include_str!("../README.md")]
73
74mod debug_printf;
75mod image;
76mod sample_param_permutations;
77
78use crate::debug_printf::{DebugPrintfInput, debug_printf_inner};
79use proc_macro::TokenStream;
80use proc_macro2::{Delimiter, Group, Ident, TokenTree};
81use quote::{ToTokens, TokenStreamExt, format_ident, quote};
82use spirv_std_types::spirv_attr_version::spirv_attr_with_version;
83
84#[proc_macro]
134#[allow(nonstandard_style)]
137pub fn Image(item: TokenStream) -> TokenStream {
138 let output = syn::parse_macro_input!(item as image::ImageType).into_token_stream();
139
140 output.into()
141}
142
143#[proc_macro_attribute]
146pub fn spirv(attr: TokenStream, item: TokenStream) -> TokenStream {
147 let spirv = format_ident!("{}", &spirv_attr_with_version());
148
149 let attr: proc_macro2::TokenStream = attr.into();
151 let mut tokens = quote! { #[cfg_attr(target_arch="spirv", rust_gpu::#spirv(#attr))] };
152
153 let item: proc_macro2::TokenStream = item.into();
154 for tt in item {
155 match tt {
156 TokenTree::Group(group) if group.delimiter() == Delimiter::Parenthesis => {
157 let mut group_tokens = proc_macro2::TokenStream::new();
158 let mut last_token_hashtag = false;
159 for tt in group.stream() {
160 let is_token_hashtag =
161 matches!(&tt, TokenTree::Punct(punct) if punct.as_char() == '#');
162 match tt {
163 TokenTree::Group(group)
164 if group.delimiter() == Delimiter::Bracket
165 && last_token_hashtag
166 && matches!(group.stream().into_iter().next(), Some(TokenTree::Ident(ident)) if ident == "spirv") =>
167 {
168 let inner = group
171 .stream()
172 .into_iter()
173 .skip(1)
174 .collect::<proc_macro2::TokenStream>();
175 group_tokens.extend(
176 quote! { [cfg_attr(target_arch="spirv", rust_gpu::#spirv #inner)] },
177 );
178 }
179 _ => group_tokens.append(tt),
180 }
181 last_token_hashtag = is_token_hashtag;
182 }
183 let mut out = Group::new(Delimiter::Parenthesis, group_tokens);
184 out.set_span(group.span());
185 tokens.append(out);
186 }
187 _ => tokens.append(tt),
188 }
189 }
190 tokens.into()
191}
192
193#[proc_macro_attribute]
199pub fn spirv_recursive_for_testing(attr: TokenStream, item: TokenStream) -> TokenStream {
200 fn recurse(spirv: &Ident, stream: proc_macro2::TokenStream) -> proc_macro2::TokenStream {
201 let mut last_token_hashtag = false;
202 stream.into_iter().map(|tt| {
203 let mut is_token_hashtag = false;
204 let out = match tt {
205 TokenTree::Group(group)
206 if group.delimiter() == Delimiter::Bracket
207 && last_token_hashtag
208 && matches!(group.stream().into_iter().next(), Some(TokenTree::Ident(ident)) if ident == "spirv") =>
209 {
210 let inner = group
213 .stream()
214 .into_iter()
215 .skip(1)
216 .collect::<proc_macro2::TokenStream>();
217 quote! { [cfg_attr(target_arch="spirv", rust_gpu::#spirv #inner)] }
218 },
219 TokenTree::Group(group) => {
220 let mut out = Group::new(group.delimiter(), recurse(spirv, group.stream()));
221 out.set_span(group.span());
222 TokenTree::Group(out).into()
223 },
224 TokenTree::Punct(punct) => {
225 is_token_hashtag = punct.as_char() == '#';
226 TokenTree::Punct(punct).into()
227 }
228 tt => tt.into(),
229 };
230 last_token_hashtag = is_token_hashtag;
231 out
232 }).collect()
233 }
234
235 let attr: proc_macro2::TokenStream = attr.into();
236 let item: proc_macro2::TokenStream = item.into();
237
238 let spirv = format_ident!("{}", &spirv_attr_with_version());
240 let inner = recurse(&spirv, item);
241 quote! { #[cfg_attr(target_arch="spirv", rust_gpu::#spirv(#attr))] #inner }.into()
242}
243
244#[proc_macro_attribute]
247pub fn gpu_only(_attr: TokenStream, item: TokenStream) -> TokenStream {
248 let syn::ItemFn {
249 attrs,
250 vis,
251 sig,
252 block,
253 } = syn::parse_macro_input!(item as syn::ItemFn);
254
255 let fn_name = sig.ident.clone();
256
257 let sig_cpu = syn::Signature {
258 abi: None,
259 ..sig.clone()
260 };
261
262 let output = quote::quote! {
263 #[cfg(not(target_arch="spirv"))]
265 #[allow(unused_variables)]
266 #(#attrs)* #vis #sig_cpu {
267 unimplemented!(
268 concat!("`", stringify!(#fn_name), "` is only available on SPIR-V platforms.")
269 )
270 }
271
272 #[cfg(target_arch="spirv")]
273 #(#attrs)* #vis #sig {
274 #block
275 }
276 };
277
278 output.into()
279}
280
281#[proc_macro]
292pub fn debug_printf(input: TokenStream) -> TokenStream {
293 debug_printf_inner(syn::parse_macro_input!(input as DebugPrintfInput))
294}
295
296#[proc_macro]
298pub fn debug_printfln(input: TokenStream) -> TokenStream {
299 let mut input = syn::parse_macro_input!(input as DebugPrintfInput);
300 input.format_string.push('\n');
301 debug_printf_inner(input)
302}
303
304#[proc_macro_attribute]
310#[doc(hidden)]
311pub fn gen_sample_param_permutations(_attr: TokenStream, item: TokenStream) -> TokenStream {
312 sample_param_permutations::gen_sample_param_permutations(item)
313}