glsl_layout_derive/
lib.rs

1#![recursion_limit = "128"]
2
3extern crate proc_macro;
4extern crate proc_macro2;
5extern crate syn;
6#[macro_use]
7extern crate quote;
8
9use proc_macro2::Span;
10
11#[proc_macro_derive(Uniform)]
12pub fn uniform(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
13    let input = proc_macro2::TokenStream::from(input);
14
15    let ast = syn::parse2(input).unwrap();
16
17    proc_macro::TokenStream::from(impl_uniform(&ast))
18}
19
20fn impl_uniform(ast: &syn::DeriveInput) -> proc_macro2::TokenStream {
21    let name = &ast.ident;
22
23    let rname = format_ident!("LayoutStd140{}", name);
24
25    let fields = match &ast.data {
26        syn::Data::Struct(syn::DataStruct {
27            fields: syn::Fields::Named(syn::FieldsNamed { named, .. }),
28            ..
29        }) => named,
30        _ => panic!(),
31    };
32
33    let aligned_fields = fields.iter().flat_map(|field| {
34        let (a, f) = aligned_field(field);
35        vec![a, f]
36    });
37
38    let field_names = fields.iter().map(|field| field.ident.as_ref().unwrap());
39    let field_names2 = fields.iter().map(|field| field.ident.as_ref().unwrap());
40
41    let dummy = format_ident!("_GLSL_LAYOUT_{}", name);
42
43    quote! {
44        #[allow(bad_style)]
45        const #dummy: () = {
46            #[repr(C, align(16))]
47            #[derive(Clone, Copy, Debug, Default)]
48            pub struct #rname {#(
49                #aligned_fields,
50            )*}
51
52            unsafe impl glsl_layout::Std140 for #rname {}
53
54            impl glsl_layout::Uniform for #rname {
55                type Align = glsl_layout::align::Align16;
56                type Std140 = #rname;
57
58                fn std140(&self) -> #rname {
59                    self.clone()
60                }
61            }
62
63            impl glsl_layout::Uniform for #name {
64                type Align = glsl_layout::align::Align16;
65                type Std140 = #rname;
66
67                fn std140(&self) -> #rname {
68                    #rname {
69                        #(#field_names: self.#field_names2.std140(),)*
70                        ..Default::default()
71                    }
72                }
73            }
74        };
75    }
76}
77
78fn aligned_field(field: &syn::Field) -> (syn::Field, syn::Field) {
79    let name = field.ident.as_ref().unwrap();
80    let align = syn::Field {
81        ty: syn::Type::Path(align_type_for(&field.ty)),
82        ident: Some(format_ident!("_align_{}", name)),
83        attrs: Vec::new(),
84        vis: syn::Visibility::Inherited,
85        colon_token: Some(Default::default()),
86    };
87
88    let std140 = syn::Field {
89        ty: syn::Type::Path(std140_type_for(&field.ty)),
90        ..field.clone()
91    };
92
93    (align, std140)
94}
95
96fn align_type_for(aligned: &syn::Type) -> syn::TypePath {
97    use std::iter::once;
98    syn::TypePath {
99        qself: Some(syn::QSelf {
100            lt_token: Default::default(),
101            ty: Box::new(aligned.clone()),
102            position: 2,
103            as_token: Some(Default::default()),
104            gt_token: Default::default(),
105        }),
106        path: syn::Path {
107            leading_colon: None,
108            segments: once(syn::PathSegment::from(syn::Ident::new(
109                "glsl_layout",
110                Span::call_site(),
111            )))
112            .chain(once(syn::Ident::new("Uniform", Span::call_site()).into()))
113            .chain(once(syn::Ident::new("Align", Span::call_site()).into()))
114            .collect(),
115        },
116    }
117}
118
119fn std140_type_for(aligned: &syn::Type) -> syn::TypePath {
120    use std::iter::once;
121    syn::TypePath {
122        qself: Some(syn::QSelf {
123            lt_token: Default::default(),
124            ty: Box::new(aligned.clone()),
125            position: 2,
126            as_token: Some(Default::default()),
127            gt_token: Default::default(),
128        }),
129        path: syn::Path {
130            leading_colon: None,
131            segments: once(syn::PathSegment::from(syn::Ident::new(
132                "glsl_layout",
133                Span::call_site(),
134            )))
135            .chain(once(
136                syn::Ident::new("Uniform".into(), Span::call_site()).into(),
137            ))
138            .chain(once(
139                syn::Ident::new("Std140".into(), Span::call_site()).into(),
140            ))
141            .collect(),
142        },
143    }
144}