agpu_macro/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
extern crate proc_macro;

use proc_macro::TokenStream;
use quote::quote;

#[proc_macro_derive(VertexLayout)]
pub fn vertex_layout(item: TokenStream) -> TokenStream {
    // Construct a representation of Rust code as a syntax tree
    // that we can manipulate
    let ast: syn::DeriveInput = syn::parse(item).unwrap();

    let name = &ast.ident;

    let vertex_step_mode = quote! { ::agpu::wgpu::VertexStepMode::Vertex };

    // Get fields/types
    let vertex_attrs = match attrs_from_fields(&ast) {
        Ok(value) => value,
        Err(value) => return value,
    };

    let gen = quote! {
       // TODO: impl TRAIT
        impl ::agpu::buffer::VertexLayout for #name {
            // Import trait
            fn vertex_buffer_layout<const L: u32>() -> ::agpu::wgpu::VertexBufferLayout<'static> {
                use ::agpu::VertexFormatType;
                ::agpu::wgpu::VertexBufferLayout {
                    array_stride: ::std::mem::size_of::<Self>() as ::agpu::wgpu::BufferAddress,
                    step_mode: #vertex_step_mode,
                    attributes: &[
                        #vertex_attrs
                    ],
                }
            }
        }
    };

    gen.into()
}

#[proc_macro_derive(VertexLayoutInstance)]
pub fn vertex_layout_instance(item: TokenStream) -> TokenStream {
    // Construct a representation of Rust code as a syntax tree
    // that we can manipulate
    let ast: syn::DeriveInput = syn::parse(item).unwrap();

    let name = &ast.ident;

    let vertex_step_mode = quote! { ::agpu::wgpu::VertexStepMode::Instance };

    // Get fields/types
    let vertex_attrs = match attrs_from_fields(&ast) {
        Ok(value) => value,
        Err(value) => return value,
    };

    let gen = quote! {
       // TODO: impl TRAIT
        impl ::agpu::buffer::VertexLayout for #name {
            // Import trait
            fn vertex_buffer_layout<const L: u32>() -> ::agpu::wgpu::VertexBufferLayout<'static> {
                use ::agpu::VertexFormatType;
                ::agpu::wgpu::VertexBufferLayout {
                    array_stride: ::std::mem::size_of::<Self>() as ::agpu::wgpu::BufferAddress,
                    step_mode: #vertex_step_mode,
                    attributes: &[
                        #vertex_attrs
                    ],
                }
            }
        }
    };

    gen.into()
}

fn attrs_from_fields(ast: &syn::DeriveInput) -> Result<quote::__private::TokenStream, TokenStream> {
    let mut vertex_attrs = quote! {};
    let fields = match &ast.data {
        syn::Data::Struct(data) => &data.fields,
        // TODO: Maybe support enum? union?
        _ => return Err(quote! { compile_error!("Vertex buffer must be struct")}.into()),
    };
    let mut offset = quote! { 0 };
    for (i, field) in fields.iter().enumerate() {
        let ty = &field.ty;

        // TODO: Skip zero-sized fields
        // ZSF do not affect layout so do not need an entry
        // Somehow need to evaluate the size_of in this macro
        // If this is not possible, then we can use a type blacklist (unpreferred)
        //// if size_of<ty>() == 0 { continue; }

        // Add the vertex attribute entry
        vertex_attrs.extend(quote! {
            ::agpu::wgpu::VertexAttribute {
                offset: #offset,
                shader_location: L + #i as u32,
                format: <#ty>::VERTEX_FORMAT_TYPE,
            },
        });

        // Add the size of this field to the cumulative offset
        offset.extend(quote! {+ ::std::mem::size_of::<#ty>() as u64});
    }
    Ok(vertex_attrs)
}

// /// Determine step mode, if per_instance is set, then we will use instance, otherwise vertex
// fn get_vertex_step_mode(ast: &syn::DeriveInput) -> quote::__private::TokenStream {
//     let vertex_step_mode = {
//         let step_per_instance = ast
//             .attrs
//             .iter()
//             .any(|attr| attr.path.segments.last().unwrap().ident.to_string() == "per_instance");
//         if step_per_instance {
//             quote! {
//                 ::wgpu::VertexStepMode::Instance
//             }
//         } else {
//             quote! {

//             }
//         }
//     };
//     vertex_step_mode
// }