agpu_macro/
lib.rs

1extern crate proc_macro;
2
3use proc_macro::TokenStream;
4use quote::quote;
5
6#[proc_macro_derive(VertexLayout)]
7pub fn vertex_layout(item: TokenStream) -> TokenStream {
8    // Construct a representation of Rust code as a syntax tree
9    // that we can manipulate
10    let ast: syn::DeriveInput = syn::parse(item).unwrap();
11
12    let name = &ast.ident;
13
14    let vertex_step_mode = quote! { ::agpu::wgpu::VertexStepMode::Vertex };
15
16    // Get fields/types
17    let vertex_attrs = match attrs_from_fields(&ast) {
18        Ok(value) => value,
19        Err(value) => return value,
20    };
21
22    let gen = quote! {
23       // TODO: impl TRAIT
24        impl ::agpu::buffer::VertexLayout for #name {
25            // Import trait
26            fn vertex_buffer_layout<const L: u32>() -> ::agpu::wgpu::VertexBufferLayout<'static> {
27                use ::agpu::VertexFormatType;
28                ::agpu::wgpu::VertexBufferLayout {
29                    array_stride: ::std::mem::size_of::<Self>() as ::agpu::wgpu::BufferAddress,
30                    step_mode: #vertex_step_mode,
31                    attributes: &[
32                        #vertex_attrs
33                    ],
34                }
35            }
36        }
37    };
38
39    gen.into()
40}
41
42#[proc_macro_derive(VertexLayoutInstance)]
43pub fn vertex_layout_instance(item: TokenStream) -> TokenStream {
44    // Construct a representation of Rust code as a syntax tree
45    // that we can manipulate
46    let ast: syn::DeriveInput = syn::parse(item).unwrap();
47
48    let name = &ast.ident;
49
50    let vertex_step_mode = quote! { ::agpu::wgpu::VertexStepMode::Instance };
51
52    // Get fields/types
53    let vertex_attrs = match attrs_from_fields(&ast) {
54        Ok(value) => value,
55        Err(value) => return value,
56    };
57
58    let gen = quote! {
59       // TODO: impl TRAIT
60        impl ::agpu::buffer::VertexLayout for #name {
61            // Import trait
62            fn vertex_buffer_layout<const L: u32>() -> ::agpu::wgpu::VertexBufferLayout<'static> {
63                use ::agpu::VertexFormatType;
64                ::agpu::wgpu::VertexBufferLayout {
65                    array_stride: ::std::mem::size_of::<Self>() as ::agpu::wgpu::BufferAddress,
66                    step_mode: #vertex_step_mode,
67                    attributes: &[
68                        #vertex_attrs
69                    ],
70                }
71            }
72        }
73    };
74
75    gen.into()
76}
77
78fn attrs_from_fields(ast: &syn::DeriveInput) -> Result<quote::__private::TokenStream, TokenStream> {
79    let mut vertex_attrs = quote! {};
80    let fields = match &ast.data {
81        syn::Data::Struct(data) => &data.fields,
82        // TODO: Maybe support enum? union?
83        _ => return Err(quote! { compile_error!("Vertex buffer must be struct")}.into()),
84    };
85    let mut offset = quote! { 0 };
86    for (i, field) in fields.iter().enumerate() {
87        let ty = &field.ty;
88
89        // TODO: Skip zero-sized fields
90        // ZSF do not affect layout so do not need an entry
91        // Somehow need to evaluate the size_of in this macro
92        // If this is not possible, then we can use a type blacklist (unpreferred)
93        //// if size_of<ty>() == 0 { continue; }
94
95        // Add the vertex attribute entry
96        vertex_attrs.extend(quote! {
97            ::agpu::wgpu::VertexAttribute {
98                offset: #offset,
99                shader_location: L + #i as u32,
100                format: <#ty>::VERTEX_FORMAT_TYPE,
101            },
102        });
103
104        // Add the size of this field to the cumulative offset
105        offset.extend(quote! {+ ::std::mem::size_of::<#ty>() as u64});
106    }
107    Ok(vertex_attrs)
108}
109
110// /// Determine step mode, if per_instance is set, then we will use instance, otherwise vertex
111// fn get_vertex_step_mode(ast: &syn::DeriveInput) -> quote::__private::TokenStream {
112//     let vertex_step_mode = {
113//         let step_per_instance = ast
114//             .attrs
115//             .iter()
116//             .any(|attr| attr.path.segments.last().unwrap().ident.to_string() == "per_instance");
117//         if step_per_instance {
118//             quote! {
119//                 ::wgpu::VertexStepMode::Instance
120//             }
121//         } else {
122//             quote! {
123
124//             }
125//         }
126//     };
127//     vertex_step_mode
128// }