glsl_layout_derive/
lib.rs1#![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}