├── .gitignore ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE ├── README.md ├── src ├── converter.rs ├── lib.rs ├── macros.rs └── parser.rs └── tests └── integration_test.rs /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.0.0 - 2022-09-08 2 | ## Added 3 | - DescInstance macro 4 | - Matrix attribute 5 | - Parser for matrix attribute 6 | ## 0.3.0 - 2022-08-30 (Merge of 0.21.31 and 0.21.3 due to semver violation) 7 | ## Added 8 | - Copy derive macro on TypeToWGPU type (backend) 9 | - Look 0.21.31 and 0.21.3 10 | ## Fix 11 | - Renamed function derive implemention from wrsl to wrld 12 | - Look 0.21.31 and 0.21.3 13 | ## 0.21.31 - 2022-08-29 (Yanked due to semver violation) 14 | ### Added 15 | - Added more info on macro name definition for mutate_#structname and #structname_const_into 16 | ### Fix 17 | - Fixed #[warn(unused_macros)] on mutate_#structname and #structname_const_into 18 | - Fixed wrld::Desc macro example in doc 19 | - Fixed exmaple on readme to add #[repr(C)] or #[repr(transparent)] macro 20 | - Fixed error handing for attribute parameter of wrld::Desc macro 21 | ## 0.21.3 - 2022-08-29 (Yanked due to semver violation) 22 | ### Add 23 | - Added macro conversion 24 | ### Fix 25 | - Fixed typo on BufferData macro 26 | - Fixed old example on Desc macro 27 | - Fixed old example on readme 28 | ## 0.2.2 - 2022-08-28 29 | ### Add 30 | - Added parse_args function helper for the backend 31 | ### Change 32 | - wrld::Desc now require #[repr(C)] or #[repr(transparent)] attribute. This is to make sure to have a fixed layout and avoid safety problem 33 | (more info [here](https://github.com/CorentinDeblock/wrld/issues/1)) 34 | ### Fix 35 | - Fixed typo BufferData example in doc 36 | ## 0.2.1 - 2022-08-26 37 | ### Fix 38 | - Fixed "constant functions cannot evaluate destructors" error 39 | ## 0.2.0 - 2022-08-26 40 | ### Add 41 | - Added a new macro "BufferData" (see [doc](https://docs.rs/wrld/0.21.3/wrld/derive.BufferData.html) for more details) 42 | ### Fix 43 | - Fixed array_stride data to take only the size of provided attributes 44 | 45 | ## 0.1.2 - 2022-08-20 46 | ### Add 47 | - Added a changelog.md 48 | ### Fix 49 | - Fixed errornous shader location 50 | ## 0.1.1 - 2022-08-18 51 | ### Fix 52 | - Removed debug printing information 53 | - Removed useless file 54 | 55 | # Summary (all patch since 0.1.0) 56 | ## Add 57 | - Added DescInstance macro 58 | - Added matrix attribute 59 | - Added parser for matrix attribute 60 | - Added more info on macro name definition for mutate_#structname and #structname_const_into 61 | - Added macro conversion 62 | - Added parse_args function helper for the backend 63 | - Added a new macro "BufferData" (see [doc](https://docs.rs/wrld/0.21.3/wrld/derive.BufferData.html) for more details) 64 | - Added a changelog.md 65 | ## Fix 66 | - Fixed #[warn(unused_macros)] on mutate_#structname and #structname_const_into 67 | - Fixed wrld::Desc macro example in doc 68 | - Fixed exmaple on readme to add #[repr(C)] or #[repr(transparent)] macro 69 | - Fixed error handing for attribute parameter of wrld::Desc macro 70 | - Fixed typo on BufferData macro 71 | - Fixed old example on Desc macro 72 | - Fixed old example on readme 73 | - Fixed "constant functions cannot evaluate destructors" error 74 | - Fixed array_stride data to take only the size of provided attributes 75 | - Fixed errornous shader location 76 | - Removed debug printing information 77 | - Removed useless file 78 | ## Change 79 | - wrld::Desc now require #[repr(C)] or #[repr(transparent)] attribute. This is to make sure to have a fixed layout and avoid safety problem -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wrld" 3 | version = "1.0.0" 4 | edition = "2021" 5 | authors = ["Corentin "] 6 | license = "MIT" 7 | keywords = ["wgpu", "macro", "derive-macro", "wgpu-helper"] 8 | categories = ["accessibility"] 9 | description = "A wgpu derive macro to help with buffer description" 10 | readme = "README.md" 11 | repository = "https://github.com/CorentinDeblock/wrld" 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [lib] 16 | proc_macro = true 17 | 18 | [dev_dependencies] 19 | trybuild = {version = "1.0", features = ["diff"]} 20 | 21 | [dependencies] 22 | const_format = "0.2.26" 23 | syn = {version = "1.0.99", features = ["extra-traits"]} 24 | quote = "1.0.21" 25 | proc-macro2 = "1.0.43" 26 | wgpu = "0" 27 | bytemuck = { version = "1.4", features = [ "derive" ] } 28 | regex = "1.6.0" 29 | phf = {version = "0.11", features = ["macros"]} -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 BrindilleDeLaForet 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WRLD (Wgpu Rust Language Descriptor) 2 | 3 | WRLD is a set of derive macro to make easy, pleasent and more safe wgpu code. 4 | 5 | WRLD description is based on Learn wgpu tutorial. 6 | 7 | ## Motivation 8 | 9 | The main reason of wrld was to create VertexBufferLayout with only one macro that support all of the type that wgpu support natively. 10 | 11 | However the more i develop it. The more i see features that could be implemented that wgpu dosen't support out of the box. 12 | 13 | ## Getting started 14 | 15 | To get started with wrld, just put wrld in your cargo.toml dependency 16 | ```toml 17 | wrld = "Your version" 18 | ``` 19 | And that's it. 20 | 21 | ## Example 22 | 23 | basic rust structure. 24 | ```rust 25 | struct Test { 26 | position: [f32; 2], 27 | color: [f32; 4] 28 | } 29 | ``` 30 | With WRLD : 31 | ```rust 32 | use wrld::Desc; 33 | 34 | #[repr(C)] 35 | #[derive(Desc)] 36 | struct Test { 37 | #[f32x2(0)] position: [f32; 2], 38 | #[f32x4(1)] color: [f32; 4] 39 | } 40 | 41 | // or 42 | #[repr(transparent)] 43 | #[derive(Desc)] 44 | struct TestTransparent { 45 | #[f32x2(0)] position: f32, 46 | #[f32x4(1)] color: f32 47 | } 48 | 49 | ``` 50 | Will produce 51 | ```rust 52 | impl Test { 53 | pub fn desc<'a>() -> wgpu::VertexBufferLayout<'a> { 54 | wgpu::VertexBufferLayout { 55 | // let size_f32 = size_of::() = 4 56 | // let f32x2 = size_f32 * 2 = 8; 57 | // let f32x4 = size_f32 * 4 = 16; 58 | // let array_stride = 8 + 16 = 24; 59 | 60 | array_stride: 24 as wgpu::BufferAddress // array_stride variable, 61 | step_mode: wgpu::VertexStepMode::Vertex, 62 | attributes: &[ 63 | wgpu::VertexAttribute { 64 | offset: 0u64, 65 | format: wgpu::VertexFormat::Float32x2, 66 | shader_location: 0u32, 67 | }, 68 | wgpu::VertexAttribute { 69 | offset: 8u64, 70 | format: wgpu::VertexFormat::Float32x4, 71 | shader_location: 1u32, 72 | }, 73 | ], 74 | } 75 | } 76 | } 77 | ``` 78 | 79 | ## Running test 80 | 81 | WRLD has now some basic test, like basic desc structure testing, basic desc instance testing and buffer data testing. It's not totally complete but it will do for now. Feel free to add test if needed and do a pull request. 82 | 83 | To run test 84 | 85 | ```bash 86 | cargo test --test integration_test -- --nocapture 87 | ``` 88 | 89 | ## Changelog 90 | 91 | [Changelog](CHANGELOG.md) -------------------------------------------------------------------------------- /src/converter.rs: -------------------------------------------------------------------------------- 1 | use phf::phf_map; 2 | 3 | const F16 : usize = 2; 4 | 5 | #[derive(Copy, Clone, Debug)] 6 | pub struct TypeToWGPU { 7 | pub offset: u64, 8 | pub ty: wgpu::VertexFormat, 9 | } 10 | 11 | #[derive(Copy, Clone)] 12 | pub struct WGPUData { 13 | pub wgpu_type: TypeToWGPU, 14 | pub shader_location: u32 15 | } 16 | 17 | static TYPE_MAPPER : phf::Map<&'static str, TypeToWGPU> = phf_map! { 18 | "u32" => TypeToWGPU { offset: std::mem::size_of::() as u64, ty: wgpu::VertexFormat::Uint32 }, 19 | "f32" => TypeToWGPU { offset: std::mem::size_of::() as u64, ty: wgpu::VertexFormat::Float32 }, 20 | "s32" => TypeToWGPU { offset: std::mem::size_of::() as u64, ty: wgpu::VertexFormat::Sint32 }, 21 | "f64" => TypeToWGPU { offset: std::mem::size_of::() as u64, ty: wgpu::VertexFormat::Float64 }, 22 | "u8x2" => TypeToWGPU { offset: std::mem::size_of::<[u8; 2]>() as u64, ty: wgpu::VertexFormat::Uint8x2 }, 23 | "u8x4" => TypeToWGPU { offset: std::mem::size_of::<[u8; 4]>() as u64, ty: wgpu::VertexFormat::Uint8x4 }, 24 | "s8x2" => TypeToWGPU { offset: std::mem::size_of::<[i8; 2]>() as u64, ty: wgpu::VertexFormat::Sint8x2 }, 25 | "s8x4" => TypeToWGPU { offset: std::mem::size_of::<[i8; 4]>() as u64, ty: wgpu::VertexFormat::Sint8x4 }, 26 | "un8x2" => TypeToWGPU { offset: std::mem::size_of::<[u8; 2]>() as u64, ty: wgpu::VertexFormat::Unorm8x2 }, 27 | "un8x4" => TypeToWGPU { offset: std::mem::size_of::<[u8; 4]>() as u64, ty: wgpu::VertexFormat::Unorm8x4 }, 28 | "sn8x2" => TypeToWGPU { offset: std::mem::size_of::<[i8; 2]>() as u64, ty: wgpu::VertexFormat::Snorm8x2 }, 29 | "sn8x4" => TypeToWGPU { offset: std::mem::size_of::<[i8; 4]>() as u64, ty: wgpu::VertexFormat::Snorm8x4 }, 30 | "u16x2" => TypeToWGPU { offset: std::mem::size_of::<[u16; 2]>() as u64, ty: wgpu::VertexFormat::Uint16x2 }, 31 | "u16x4" => TypeToWGPU { offset: std::mem::size_of::<[u16; 4]>() as u64, ty: wgpu::VertexFormat::Uint16x4 }, 32 | "s16x2" => TypeToWGPU { offset: std::mem::size_of::<[i16; 2]>() as u64, ty: wgpu::VertexFormat::Sint16x2 }, 33 | "s16x4" => TypeToWGPU { offset: std::mem::size_of::<[i16; 4]>() as u64, ty: wgpu::VertexFormat::Sint16x4 }, 34 | "un16x2" => TypeToWGPU { offset: std::mem::size_of::<[u16; 2]>() as u64, ty: wgpu::VertexFormat::Unorm16x2 }, 35 | "un16x4" => TypeToWGPU { offset: std::mem::size_of::<[u16; 4]>() as u64, ty: wgpu::VertexFormat::Unorm16x4 }, 36 | "sn16x2" => TypeToWGPU { offset: std::mem::size_of::<[i16; 2]>() as u64, ty: wgpu::VertexFormat::Snorm16x2 }, 37 | "sn16x4" => TypeToWGPU { offset: std::mem::size_of::<[i16; 4]>() as u64, ty: wgpu::VertexFormat::Snorm16x4 }, 38 | "f16x2" => TypeToWGPU { offset: (F16 * 2) as u64, ty: wgpu::VertexFormat::Float16x2 }, 39 | "f16x4" => TypeToWGPU { offset: (F16 * 4) as u64, ty: wgpu::VertexFormat::Float16x4 }, 40 | "f32x2" => TypeToWGPU { offset: std::mem::size_of::<[f32; 2]>() as u64, ty: wgpu::VertexFormat::Float32x2 }, 41 | "f32x3" => TypeToWGPU { offset: std::mem::size_of::<[f32; 3]>() as u64, ty: wgpu::VertexFormat::Float32x3 }, 42 | "f32x4" => TypeToWGPU { offset: std::mem::size_of::<[f32; 4]>() as u64, ty: wgpu::VertexFormat::Float32x4 }, 43 | "u32x2" => TypeToWGPU { offset: std::mem::size_of::<[u32; 2]>() as u64, ty: wgpu::VertexFormat::Uint32x2 }, 44 | "u32x3" => TypeToWGPU { offset: std::mem::size_of::<[u32; 3]>() as u64, ty: wgpu::VertexFormat::Uint32x3 }, 45 | "u32x4" => TypeToWGPU { offset: std::mem::size_of::<[u32; 4]>() as u64, ty: wgpu::VertexFormat::Uint32x4 }, 46 | "s32x2" => TypeToWGPU { offset: std::mem::size_of::<[i32; 2]>() as u64, ty: wgpu::VertexFormat::Sint32x2 }, 47 | "s32x3" => TypeToWGPU { offset: std::mem::size_of::<[i32; 3]>() as u64, ty: wgpu::VertexFormat::Sint32x3 }, 48 | "s32x4" => TypeToWGPU { offset: std::mem::size_of::<[i32; 4]>() as u64, ty: wgpu::VertexFormat::Sint32x4 }, 49 | "f64x2" => TypeToWGPU { offset: std::mem::size_of::<[f64; 2]>() as u64, ty: wgpu::VertexFormat::Float64x2 }, 50 | "f64x3" => TypeToWGPU { offset: std::mem::size_of::<[f32; 3]>() as u64, ty: wgpu::VertexFormat::Float64x3 }, 51 | "f64x4" => TypeToWGPU { offset: std::mem::size_of::<[f64; 4]>() as u64, ty: wgpu::VertexFormat::Float64x4 } 52 | }; 53 | 54 | fn get_type(name: &str) -> Result { 55 | if let Some(typ) = TYPE_MAPPER.get(name) { 56 | return Ok(*typ) 57 | } 58 | 59 | Err(format!("Cannot get type for {:?}", name)) 60 | } 61 | 62 | pub fn get_allowed_type(name: &str) -> std::vec::Vec<&str> { 63 | let mut vec: std::vec::Vec::<&str> = std::vec::Vec::new(); 64 | for i in TYPE_MAPPER.keys() { 65 | if i.contains(name) { 66 | vec.push(i); 67 | } 68 | } 69 | vec 70 | } 71 | 72 | pub fn convert_mat_type_to_wgou(name: &String, shader_location: u32, ty: &mut String) -> std::vec::Vec { 73 | let mut wgpu_vec: Vec = std::vec::Vec::new(); 74 | let mut row = ty.clone(); 75 | row.push_str(&name[name.len() - 2..name.len()]); 76 | 77 | let column: &u32 = &name[name.len() - 3..name.len() - 2].parse().unwrap(); 78 | let mut final_ty = convert_type_to_wgpu(&row, shader_location).unwrap_or_else(|_| { 79 | 80 | let allowed_types = get_allowed_type(ty) 81 | .join(" or ") 82 | .replace(ty.as_str(), format!("mat{:?}", column).as_str()); 83 | 84 | panic!("Matrix {} cannot be use with {} ! Available matrix are {} for {}", name, ty, allowed_types, ty); 85 | }); 86 | 87 | for i in 0..*column { 88 | final_ty.shader_location += if i == 0 { 0 } else { 1 }; 89 | wgpu_vec.push(final_ty); 90 | } 91 | 92 | wgpu_vec 93 | } 94 | 95 | pub fn convert_type_to_wgpu(name: &String, shader_location: u32) -> Result { 96 | let wgpu_type = get_type(name.as_str())?; 97 | 98 | Ok(WGPUData { wgpu_type, shader_location }) 99 | } 100 | 101 | pub fn has_type(name: &str) -> bool { 102 | TYPE_MAPPER.contains_key(name) 103 | } -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // MIT License 2 | 3 | // Copyright (c) 2022 BrindilleDeLaForet 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | //! ## Description 24 | //! 25 | //! wrld is a easy, fast, and more secure way of writing buffer descriptor for wgpu renderpipeline 26 | //! 27 | //! ## How it works ? 28 | //! 29 | //! Wrld take derived structure and crate a VertexBufferLayout according to the attribute passed. 30 | //! 31 | //! Wrld come with 3 macros : 32 | //! - Desc 33 | //! - DescInstance 34 | //! - BufferData 35 | //! 36 | //! ### Desc 37 | //! 38 | //! Desc derive macro allow to create a VertexBufferLayout from a structure. 39 | //! 40 | //! #### Example 41 | //! ``` 42 | //! #[repr(C)] 43 | //! #[derive(wrld::Desc)] 44 | //! struct Vertex { 45 | //! #[f32x2(0)] position: [f32; 2], 46 | //! #[f32x4(1)] color: [f32; 4] 47 | //! } 48 | //! ``` 49 | //! 50 | //! #### Thing to know 51 | //! - Desc will not handle data transformation. (bytemuck slice) 52 | //! - Desc does not handle chaotic structure. 53 | //! 54 | //! ### DescInstance 55 | //! 56 | //! DescInstance is the same as the Desc macro. The only difference with DescInstance is that it change the vertex step mode but the result is the same. 57 | //! 58 | //! #### Example 59 | //! ``` 60 | //! #[derive(wrld::DescInstance)] 61 | //! struct Vertex { 62 | //! #[f32x2(0)] position: [f32; 2], 63 | //! #[f32x4(1)] color: [f32; 4] 64 | //! } 65 | //! ``` 66 | //! 67 | //! ### Chaotic and ordered structure. 68 | //! 69 | //! Before aboarding the next macro. We need to know what is the difference between chaotic and ordered structure type. 70 | //! 71 | //! ### Chaotic structure 72 | //! 73 | //! A chaotic structure is a structure that have attributes put anywhere in the struct 74 | //! 75 | //! #### Example 76 | //! ``` 77 | //! #[derive(wrld::Desc)] 78 | //! struct Vertex { 79 | //! #[f32x2(0)] position: [f32; 2], 80 | //! data: String, 81 | //! #[f32x4(1)] color: [f32; 4] 82 | //! } 83 | //! ``` 84 | //! If you try to cast slice with bytemuck on this structure. It will result in this. 85 | //! ``` 86 | //! struct Vertex { 87 | //! position: [f32; 2], 88 | //! data: String 89 | //! } 90 | //! ``` 91 | //! And this is not good, because this is not the data that we are expecting to get from bytemuck. 92 | //! 93 | //! A simple fix to that is to create a ordered structure and have one or multiple function that convert this structure to a ordered one or to sort this one. 94 | //! 95 | //! ### Ordered structure 96 | //! 97 | //! A ordered structure is a structure that put the attribute field on top of the structure. 98 | //! 99 | //! #### Example 100 | //! ``` 101 | //! #[derive(wrld::Desc)] 102 | //! struct Vertex { 103 | //! #[f32x2(0)] position: [f32; 2], 104 | //! #[f32x4(1)] color: [f32; 4], 105 | //! data: String 106 | //! } 107 | //! ``` 108 | //! If you try to cast slice with bytemuck on this structure. It will result in this. 109 | //! ``` 110 | //! struct Vertex { 111 | //! position: [f32; 2], 112 | //! color: [f32; 4] 113 | //! } 114 | //! ``` 115 | //! 116 | //! While this technique work. It could be annoying to rewrite thousand and thousand of structure just to fix this. 117 | //! This is why the next macro was created for. 118 | //! 119 | //! ### BufferData 120 | //! 121 | //! BufferData create a ordered structure from a chaotic structure. It come with bytemuck derive macro out of the box. 122 | //! 123 | //! #### Example 124 | //! ``` 125 | //! #[repr(C)] 126 | //! #[derive(wrld::Desc, wrld::BufferData)] 127 | //! struct Vertex { 128 | //! uv: [f32; 2], 129 | //! #[f32x2(0)] position: [f32; 2], 130 | //! data: String, 131 | //! #[f32x4(1)] color: [f32; 4] 132 | //! } 133 | //! ``` 134 | use proc_macro::{TokenStream}; 135 | 136 | mod converter; 137 | mod parser; 138 | mod macros; 139 | 140 | /// Desc is a proc derive macro that allow you to describe a structure as a description to pass to a renderpipeline. 141 | /// 142 | /// ## Example 143 | /// ``` 144 | /// use wrld::Desc; 145 | /// 146 | /// #[repr(C)] 147 | /// #[derive(Desc)] 148 | /// struct Test { 149 | /// #[f32x3(0)] position: Vector3 150 | /// #[f32x4(1)] color: Vector4 151 | /// } 152 | /// ``` 153 | /// into 154 | /// ``` 155 | /// impl Test { 156 | /// pub fn desc<'a>() -> wgpu::VertexBufferLayout<'a> { 157 | /// // let size_f32 = size_of::() = 4 158 | /// // let f32x3 = size_f32 * 3 = 12; 159 | /// // let f32x4 = size_f32 * 4 = 16; 160 | /// // let array_stride = 12 + 16 = 28; 161 | /// 162 | /// wgpu::VertexBufferLayout { 163 | /// array_stride: 28 as wgpu::BufferAddress // array_stride variable, 164 | /// step_mode: wgpu::VertexStepMode::Vertex, 165 | /// attributes: &[ 166 | /// wgpu::VertexAttribute { 167 | /// offset: 0u64, 168 | /// format: wgpu::VertexFormat::Float32x3, 169 | /// shader_location: 0u32, 170 | /// }, 171 | /// wgpu::VertexAttribute { 172 | /// offset: 12u64, 173 | /// format: wgpu::VertexFormat::Float32x4, 174 | /// shader_location: 1u32, 175 | /// }, 176 | /// ], 177 | /// } 178 | /// } 179 | /// } 180 | /// ``` 181 | /// 182 | /// ## Matrice attributes 183 | /// 184 | /// Matrices attributes are kind of special, because matrices are the only attributes that can take multiple location. 185 | /// 186 | /// Matrices need two argument : 187 | /// - The type of the matrice (u8, f32, f64, ect...) 188 | /// - And the starting location 189 | /// 190 | /// Matrices dimension start from 2x2 to 4x4 191 | /// 192 | /// ### Example 193 | /// ``` 194 | /// #[repr(C)] 195 | /// #[derive(wrld::Desc)] 196 | /// struct Actor { 197 | /// #[mat4x2(u8, 0)] transform: [[f32; 4]; 4] 198 | /// } 199 | /// ``` 200 | /// Will result to 201 | /// ``` 202 | /// impl Actor { 203 | /// pub fn desc<'a>() -> wgpu::VertexBufferLayout<'a> { 204 | /// wgpu::VertexBufferLayout { 205 | /// array_stride: 8u64 as wgpu::BufferAddress, 206 | /// step_mode: wgpu::VertexStepMode::Instance, 207 | /// attributes: &[ 208 | /// wgpu::VertexAttribute { 209 | /// offset: 0u64, 210 | /// format: wgpu::VertexFormat::Uint8x2, 211 | /// shader_location: 0u32, 212 | /// }, 213 | /// wgpu::VertexAttribute { 214 | /// offset: 2u64, 215 | /// format: wgpu::VertexFormat::Uint8x2, 216 | /// shader_location: 1u32, 217 | /// }, 218 | /// wgpu::VertexAttribute { 219 | /// offset: 4u64, 220 | /// format: wgpu::VertexFormat::Uint8x2, 221 | /// shader_location: 2u32, 222 | /// }, 223 | /// wgpu::VertexAttribute { 224 | /// offset: 6u64, 225 | /// format: wgpu::VertexFormat::Uint8x2, 226 | /// shader_location: 3u32, 227 | /// }, 228 | /// ], 229 | /// } 230 | /// } 231 | /// } 232 | /// ``` 233 | /// So take care while using it. 234 | /// 235 | /// Also matrix type handle only wgpu VertexFormat type for row. 236 | /// That does mean that matrix like that. 237 | /// ``` 238 | /// #[repr(C)] 239 | /// #[derive(wrld::DescInstance)] 240 | /// struct Vertex { 241 | /// #[mat4x3(u8, 0)] transform: [[f32; 4]; 4] 242 | /// } 243 | /// ``` 244 | /// Will throw an error : 245 | /// 246 | /// "Matrix mat4x3 cannot be use with u8 ! Available matrix are mat4x2 or mat4x4 for u8" 247 | /// 248 | /// 249 | /// ## Thing to know 250 | /// - Desc will not handle data transformation 251 | /// - Desc does not handle chaotic structure 252 | #[proc_macro_derive(Desc, attributes( 253 | u8x2, u8x4, s8x2, s8x4, un8x2, un8x4, sn8x2, sn8x4, 254 | u16x2, u16x4, s16x2, s16x4, un16x2, un16x4, sn16x2, sn16x4, f16x2, f16x4, 255 | f32, f32x2, f32x3, f32x4, 256 | u32, u32x2, u32x3, u32x4, 257 | s32, s32x2, s32x3, s32x4, 258 | f64, f64x2, f64x3, f64x4, 259 | mat2x2, mat2x3, mat2x4, 260 | mat3x2, mat3x3, mat3x4, 261 | mat4x2, mat4x3, mat4x4 262 | ))] 263 | pub fn derive_wrld_desc(item: TokenStream) -> TokenStream { 264 | macros::derive_wrld_desc(item, wgpu::VertexStepMode::Vertex) 265 | } 266 | 267 | /// DescInstance is the same as Desc. The only difference is that it change the step mode to Instance instead of Vertex 268 | /// 269 | /// ## Example 270 | 271 | /// ``` 272 | /// use wrld::DescInstance; 273 | /// 274 | /// #[repr(C)] 275 | /// #[derive(DescInstance)] 276 | /// struct Test { 277 | /// #[f32x3(0)] position: Vector3 278 | /// #[f32x4(1)] color: Vector4 279 | /// } 280 | /// ``` 281 | /// into 282 | /// ``` 283 | /// impl Test { 284 | /// pub fn desc<'a>() -> wgpu::VertexBufferLayout<'a> { 285 | /// // let size_f32 = size_of::() = 4 286 | /// // let f32x3 = size_f32 * 3 = 12; 287 | /// // let f32x4 = size_f32 * 4 = 16; 288 | /// // let array_stride = 12 + 16 = 28; 289 | /// 290 | /// wgpu::VertexBufferLayout { 291 | /// array_stride: 28 as wgpu::BufferAddress // array_stride variable, 292 | /// step_mode: wgpu::VertexStepMode::Instance, 293 | /// attributes: &[ 294 | /// wgpu::VertexAttribute { 295 | /// offset: 0u64, 296 | /// format: wgpu::VertexFormat::Float32x3, 297 | /// shader_location: 0u32, 298 | /// }, 299 | /// wgpu::VertexAttribute { 300 | /// offset: 12u64, 301 | /// format: wgpu::VertexFormat::Float32x4, 302 | /// shader_location: 1u32, 303 | /// }, 304 | /// ], 305 | /// } 306 | /// } 307 | /// } 308 | /// ``` 309 | #[proc_macro_derive(DescInstance, attributes( 310 | u8x2, u8x4, s8x2, s8x4, un8x2, un8x4, sn8x2, sn8x4, 311 | u16x2, u16x4, s16x2, s16x4, un16x2, un16x4, sn16x2, sn16x4, f16x2, f16x4, 312 | f32, f32x2, f32x3, f32x4, 313 | u32, u32x2, u32x3, u32x4, 314 | s32, s32x2, s32x3, s32x4, 315 | f64, f64x2, f64x3, f64x4, 316 | mat2x2, mat2x3, mat2x4, 317 | mat3x2, mat3x3, mat3x4, 318 | mat4x2, mat4x3, mat4x4 319 | ))] 320 | pub fn derive_wrld_desc_instance(item: TokenStream) -> TokenStream { 321 | macros::derive_wrld_desc(item, wgpu::VertexStepMode::Instance) 322 | } 323 | 324 | /// A macro to handle any type of chaotic structure. 325 | /// 326 | /// ## What is a chaotic structure ? And what are the structure different type ? 327 | /// 328 | /// - Chaotic structure : 329 | /// 330 | /// structure that have attribute but the fields are not ordered (basically put everywhere and not on the top of the structure) 331 | /// 332 | /// for example 333 | /// ``` 334 | /// #[repr(C)] 335 | /// #[derive(wgpu::Desc)] 336 | /// struct Vertex { 337 | /// some_data: String, 338 | /// #[f32x2(0)] position: [f32; 2], 339 | /// some_other_data: TypeDefinedByUser, 340 | /// #[f32x4(1)] color: [f32; 4] 341 | /// } 342 | /// ``` 343 | /// 344 | /// is a chaotic structure because crates like bytemuck will interpret this structure like this. 345 | /// 346 | /// ``` 347 | /// struct Vertex { 348 | /// some_data: String, 349 | /// position: [f32; 2] 350 | /// } 351 | /// ``` 352 | /// 353 | /// - Ordered structure 354 | /// 355 | /// is a structure that does put attribute field on the top of the structure. 356 | /// 357 | /// for example 358 | /// ``` 359 | /// #[repr(C)] 360 | /// #[derive(wgpu::Desc)] 361 | /// struct Vertex { 362 | /// #[f32x2(0)] position: [f32; 2], 363 | /// #[f32x4(1)] color: [f32; 4], 364 | /// some_data: String, 365 | /// some_other_data: TypeDefinedByUser 366 | /// } 367 | /// ``` 368 | /// 369 | /// is a ordered structure and bytemuck will interpret this structure like this. 370 | /// 371 | /// ``` 372 | /// struct Vertex { 373 | /// position: [f32; 2], 374 | /// color: [f32; 4] 375 | /// } 376 | /// ``` 377 | /// 378 | /// before that macro, structure like this (chaotic structure) 379 | /// ``` 380 | /// #[repr(C)] 381 | /// #[derive(wgpu::Desc)] 382 | /// struct Vertex { 383 | /// uv: [f32; 2], 384 | /// #[f32x2(0)] position: [f32; 2], 385 | /// data: String, 386 | /// #[f32x4(1)] color: [f32; 4] 387 | /// } 388 | /// ``` 389 | /// Where not very well handled by wrld, because bytemuck will not look for attribute data. 390 | /// Which create undefined behaviour on structure data and will not correspond to what we expect to receive. 391 | /// 392 | /// A solution to that was to reorder structure data fields (ordered structure) 393 | /// ``` 394 | /// #[repr(C)] 395 | /// #[derive(wrld::Desc)] 396 | /// struct Vertex { 397 | /// #[f32x2(0)] position: [f32; 2], 398 | /// #[f32x4(1)] color: [f32; 4], 399 | /// 400 | /// uv: [f32; 4], 401 | /// data: String 402 | /// } 403 | /// ``` 404 | /// But now with BufferData this is not a problem anymore. 405 | /// BufferData handle any type of chaotic structure so that does mean that this structure for example 406 | /// ``` 407 | /// #[repr(C)] 408 | /// #[derive(wrld::Desc)] 409 | /// struct Vertex { 410 | /// uv: [f32; 4], 411 | /// #[f32x2(0)] position: [f32; 2], 412 | /// data: String, 413 | /// #[f32x4(1)] color: [f32; 4] 414 | /// } 415 | /// ``` 416 | /// Is handled via this macro and will have the result of what we expect it from. 417 | /// 418 | /// ## How it's working ? 419 | /// 420 | /// BufferData create a ordered structure from a chaotic structure. 421 | /// It take any array or variable and transform it to is correponding ordered structure 422 | /// it also provide function and trait converter accordingly. 423 | /// 424 | /// ## Example 425 | /// 426 | /// Take this structure 427 | /// ``` 428 | /// #[repr(C)] 429 | /// #[derive(wrld::Desc, wrld::BufferData)] 430 | /// struct Vertex { 431 | /// texture: SomeTextureType, 432 | /// #[f32x3(0)] position: [f32; 3], 433 | /// message: String, 434 | /// #[f21x3(1)] scale: [f32; 3] 435 | /// } 436 | /// ``` 437 | /// 438 | /// This structure will result in this implementation 439 | /// 440 | /// ``` 441 | /// #[repr(C)] 442 | /// #[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)] 443 | /// struct VertexBufferData { 444 | /// position: [f32; 3], 445 | /// scale: [f32; 3] 446 | /// } 447 | /// 448 | /// impl From for VertexBufferData { 449 | /// fn from(other_data_from_ident_to_into: Vertex) -> Self { 450 | /// Self { 451 | /// position: other_data_from_ident_to_into.position, 452 | /// scale: other_data_from_ident_to_into.scale 453 | /// } 454 | /// } 455 | /// } 456 | /// 457 | /// impl From<&'static Vertex> for VertexBufferData { 458 | /// fn from(other_data_from_ident_to_into: &'static Vertex) -> Self { 459 | /// Self { 460 | /// position: other_data_from_ident_to_into.position, 461 | /// scale: other_data_from_ident_to_into.scale 462 | /// } 463 | /// } 464 | /// } 465 | /// 466 | /// impl PartialEq for VertexBufferData { 467 | /// fn eq(&self, other_ident_data_boolean_condition: &Vertex) -> bool { 468 | /// position == other_ident_data_boolean_condition.position && scale: other_ident_data_boolean_condition.scale 469 | /// } 470 | /// } 471 | /// 472 | /// impl FromIterator for Vec { 473 | /// fn from_iter>(iter: T) -> Self { 474 | /// let mut vec_data_from_ident_from_iterator = Vec::new(); 475 | /// 476 | /// for c in iter { 477 | /// vec_data_from_ident_from_iterator.push(c.into()); 478 | /// } 479 | /// 480 | /// vec_data_from_ident_from_iterator 481 | /// } 482 | /// } 483 | /// 484 | /// impl FromIterator<&'static Vertex> for Vec { 485 | /// fn from_iter>(iter: T) -> Self { 486 | /// let mut vec_data_from_ident_single_from_iterator : Vec = Vec::new(); 487 | /// 488 | /// for c in iter { 489 | /// vec_data_from_ident_single_from_iterator.push(c.into()); 490 | /// } 491 | /// 492 | /// vec_data_from_ident_single_from_iterator 493 | /// } 494 | /// } 495 | /// 496 | /// impl VertexBufferData { 497 | /// pub const fn const_into(other_ident_data_to_into_const: &Vertex) -> Self { 498 | /// Self { 499 | /// position: other_ident_data_to_into_const.position, 500 | /// scale: other_ident_data_to_into_const.scale 501 | /// } 502 | /// } 503 | /// } 504 | /// 505 | /// impl Vertex { 506 | /// pub fn mutate<'a>(other_data_from_ident_to_mutate: &'a Vec) -> &'a [u8] { 507 | /// bytemuck::cast_slice(other_data_from_ident_to_mutate.as_slice()) 508 | /// } 509 | /// 510 | /// pub fn transmute(other_data_from_ident_to_transmute: &'static [Self]) -> Vec { 511 | /// other_data_from_ident_to_transmute.into_iter().collect::>() 512 | /// } 513 | /// } 514 | /// 515 | /// macro_rules! vertex_const_into { 516 | /// ($data: expr) => { 517 | /// VertexBufferData::const_into(&$data) 518 | /// }; 519 | /// } 520 | /// 521 | /// macro_rules! mutate_vertex { 522 | /// ($data: expr) => { 523 | /// Vertex::mutate(&Vertex::transmute($data)) 524 | /// }; 525 | /// } 526 | /// ``` 527 | /// Also bytemuck is used for converting structure data to wgpu 528 | /// 529 | /// ## How to use it ? 530 | /// 531 | /// When you create any chaotic structure for wrld. Just put wrld::BufferData derive macro at the top 532 | /// 533 | /// ``` 534 | /// #[repr(C)] 535 | /// #[derive(wrld::Desc, wrld::BufferData)] 536 | /// struct Vertex { 537 | /// texture: SomeTextureType, 538 | /// #[f32x3(0)] position: [f32; 3], 539 | /// message: String, 540 | /// #[f21x3(1)] scale: [f32; 3] 541 | /// } 542 | /// ``` 543 | /// 544 | /// ### Single variable conversion. 545 | /// 546 | /// If you only need to convert a single variable. You can do that. 547 | /// 548 | /// ``` 549 | /// let data : VertexBufferData = Vertex { 550 | /// texture: SomeTextureType::new(), 551 | /// position: [0.0, 0.0, 0.0], 552 | /// message: String::from("something"), 553 | /// scale: [1.0, 1.0, 1.0] 554 | /// }.into() 555 | /// ``` 556 | /// 557 | /// If you however want to convert a constant vertex variable. 558 | /// 559 | /// ``` 560 | /// const data : Vertex = Vertex { 561 | /// texture: SomeTextureType::new(), 562 | /// position: [0.0, 0.0, 0.0], 563 | /// message: String::from("something"), 564 | /// scale: [1.0, 1.0, 1.0] 565 | /// } 566 | /// const vertex_buffer_data = VertexBufferData::const_into(&data); 567 | /// // or 568 | /// const vertex_buffer_data_new = vertex_const_into!(data); 569 | /// ``` 570 | /// 571 | /// ### Array conversion 572 | /// 573 | /// Array conversion is a little bit more complex. We can't use the .into() because rust will not allow that. 574 | /// This is why you will need to transmute the const array first and then mutate it. 575 | /// 576 | /// ``` 577 | /// const data : [Vertex] = [Vertex { 578 | /// texture: SomeTextureType::new(), 579 | /// position: [0.0, 0.0, 0.0], 580 | /// message: String::from("something"), 581 | /// scale: [1.0, 1.0, 1.0] 582 | /// }, Vertex { 583 | /// texture: SomeTextureType::new(), 584 | /// position: [0.0, 1.0, 0.0], 585 | /// message: String::from("something 2"), 586 | /// scale: [1.0, 1.0, 1.0] 587 | /// }] 588 | /// 589 | /// fn main() { 590 | /// let arr : &[u8] = Vertex::mutate(&Vertex::transmute(data)); 591 | /// // or 592 | /// let arr_new : &[u8] = mutate_vertex!(data); 593 | /// 594 | /// // With wgpu create_buffer_init 595 | /// let device = wgpu::Device::new() 596 | /// 597 | /// let vertex_buffer = device.create_buffer_init( 598 | /// &wgpu::utils::BufferInitDescriptor { 599 | /// label: Some("Buffer init"), 600 | /// contents: Vertex::mutate(&Vertex::transmute(data)), 601 | /// usage: wgpu::BufferUsages::VERTEX 602 | /// }) 603 | /// 604 | /// // or 605 | /// 606 | /// let vertex_buffer_new = device.create_buffer_init( 607 | /// &wgpu::utils::BufferInitDescriptor { 608 | /// label: Some("Buffer init"), 609 | /// contents: mutate_vertex!(data), 610 | /// usage: wgpu::BufferUsages::VERTEX 611 | /// }) 612 | /// } 613 | /// ``` 614 | /// 615 | /// macro name are formated like this. 616 | /// - struct name will be all lowercase 617 | /// - struct that have uppercase letter in his name are prefix with _ and the letter in question except for the starting letter. 618 | /// 619 | /// ### Example 620 | /// ``` 621 | /// #[repr(C)] 622 | /// #[derive(wrld::Desc, wrld::BufferData)] 623 | /// struct VertexData { 624 | /// #[f32x2(0)] position: [f32; 2] 625 | /// #[f32x4(1)] color: [f32; 4] 626 | /// } 627 | /// 628 | /// // is equal to 629 | /// 630 | /// macro_rules! vertex_data_const_into { 631 | /// ($data: expr) => { 632 | /// VertexDataBufferData::const_into(&$data) 633 | /// }; 634 | /// } 635 | /// macro_rules! mutate_vertex_data { 636 | /// ($data: expr) => { 637 | /// VertexData::mutate(&VertexData::transmute($data)) 638 | /// }; 639 | /// } 640 | /// ``` 641 | /// 642 | /// ## Why you have created a another macro instead of putting it in wrld::Desc ? 643 | /// 644 | /// 1. Prevent wrld to be too much invasive. 645 | /// 2. BufferData is not always needed. 646 | /// 3. BufferData is made to handle chaotic structure and not ordered one. (related to 2.) 647 | /// 648 | /// There is also know problem about naming const variable the same as the quote generated code variable. 649 | /// There is a simple workaround that is to name const variable all uppercase or just change name of the const variable. 650 | /// However this problem only occurs on const variable 651 | #[proc_macro_derive(BufferData)] 652 | pub fn derive_wrld_buffer_data(item: TokenStream) -> TokenStream { 653 | macros::derive_wrld_buffer_data(item) 654 | } -------------------------------------------------------------------------------- /src/macros.rs: -------------------------------------------------------------------------------- 1 | use crate::converter::{convert_type_to_wgpu, has_type, convert_mat_type_to_wgou}; 2 | use crate::parser::TokenVertexFormat; 3 | use crate::parser::parse_attrs; 4 | 5 | #[derive(Debug)] 6 | struct Entity { 7 | fields: Vec 8 | } 9 | 10 | #[derive(Debug)] 11 | struct EntityFieldsAttrs { 12 | name: String, 13 | data: u32, 14 | ty: Option 15 | } 16 | 17 | #[derive(Debug)] 18 | struct EntityFields { 19 | attrs: Vec, 20 | name: proc_macro2::Ident, 21 | ty: syn::Type 22 | } 23 | 24 | fn get_entity_field(field: &syn::Field) -> Option { 25 | let mut attrs: Vec = Vec::new(); 26 | 27 | parse_attrs(&field.attrs,Box::new(|attr| { 28 | let name = attr.segment.ident.to_string(); 29 | if name.starts_with("mat") { 30 | let mat : crate::parser::AttrMat = attr.attribute.parse_args().unwrap(); 31 | 32 | attrs.push(EntityFieldsAttrs { 33 | name, 34 | data: mat.data, 35 | ty: Some(mat.ident.to_string()), 36 | }); 37 | 38 | return 39 | } 40 | 41 | let lint : syn::LitInt = attr.attribute.parse_args().expect("Only integer is authorize for shader location data"); 42 | 43 | attrs.push(EntityFieldsAttrs { 44 | name: attr.segment.ident.to_string(), 45 | data: lint.base10_parse().unwrap(), 46 | ty: None 47 | }); 48 | })); 49 | 50 | let entity_fields = EntityFields { 51 | attrs, 52 | name: field.ident.clone().unwrap(), 53 | ty: field.ty.clone() 54 | }; 55 | 56 | Some(entity_fields) 57 | } 58 | 59 | fn process_wgpu_type( 60 | format: &crate::converter::WGPUData, 61 | shader_locations: &mut Vec, 62 | attrs: &mut Vec, 63 | offset: &u64 64 | ) { 65 | let tty = TokenVertexFormat { attribute: format.wgpu_type.ty}; 66 | let shader_location = format.shader_location; 67 | 68 | if shader_locations.contains(&shader_location) { 69 | panic!("Cannot have two time the same location in the same struct"); 70 | } 71 | 72 | shader_locations.push(shader_location); 73 | 74 | attrs.push(quote::quote! { 75 | wgpu::VertexAttribute { 76 | offset: #offset, 77 | format: #tty, 78 | shader_location: #shader_location 79 | } 80 | }); 81 | } 82 | 83 | fn require_repr_c(attrs : &std::vec::Vec) { 84 | let mut valid = false; 85 | 86 | parse_attrs(&attrs, Box::new(|attr| { 87 | let repr_attr = attr.attribute.parse_args::().unwrap().to_string(); 88 | if attr.segment.ident.to_string() == "repr" && (repr_attr == "C" || repr_attr == "transparent") { 89 | valid = true; 90 | } 91 | })); 92 | 93 | if !valid { 94 | panic!("wrld::Desc derive macro require #[repr(C)] or #[repr(transparent)] attribute for safety measure"); 95 | } 96 | } 97 | 98 | pub fn derive_wrld_desc(item: proc_macro::TokenStream, step_mode: wgpu::VertexStepMode) -> proc_macro::TokenStream { 99 | let syn::DeriveInput {ident, data, attrs, ..} = syn::parse_macro_input!(item as syn::DeriveInput); 100 | let fields = if let syn::Data::Struct(syn::DataStruct { 101 | fields: syn::Fields::Named(syn::FieldsNamed { ref named, ..}), 102 | .. 103 | }) = data 104 | { 105 | named 106 | } else { 107 | panic!("Only struct are supported by wrld::Desc supported"); 108 | }; 109 | 110 | require_repr_c(&attrs); 111 | 112 | let entity = Entity { 113 | fields: fields.iter().filter_map(|field| {get_entity_field(field)}).collect() 114 | }; 115 | 116 | let mut attrs : Vec = Vec::new(); 117 | 118 | let mut offset:u64 = 0; 119 | let mut shader_locations: Vec = Vec::new(); 120 | 121 | for i in entity.fields { 122 | for attr in i.attrs { 123 | if attr.ty == None { 124 | let format = convert_type_to_wgpu(&attr.name, attr.data).unwrap(); 125 | process_wgpu_type(&format, &mut shader_locations, &mut attrs, &offset); 126 | offset += format.wgpu_type.offset; 127 | } else { 128 | let mat_format = convert_mat_type_to_wgou( 129 | &attr.name, 130 | attr.data, 131 | &mut attr.ty.unwrap() 132 | ); 133 | 134 | for format in mat_format { 135 | process_wgpu_type(&format, &mut shader_locations, &mut attrs, &offset); 136 | offset += format.wgpu_type.offset; 137 | } 138 | } 139 | } 140 | } 141 | 142 | let step_mode = crate::parser::TokenVertexStepMode {step_mode}; 143 | 144 | quote::quote! { 145 | impl #ident { 146 | pub fn desc<'a>() -> wgpu::VertexBufferLayout<'a> { 147 | wgpu::VertexBufferLayout { 148 | array_stride: #offset as wgpu::BufferAddress, 149 | step_mode: #step_mode, 150 | attributes: &[#(#attrs),*] 151 | } 152 | } 153 | } 154 | }.into() 155 | } 156 | 157 | pub fn derive_wrld_buffer_data(item: proc_macro::TokenStream) -> proc_macro::TokenStream { 158 | let syn::DeriveInput {ident, data, ..} = syn::parse_macro_input!(item as syn::DeriveInput); 159 | let fields = if let syn::Data::Struct(syn::DataStruct { 160 | fields: syn::Fields::Named(syn::FieldsNamed { ref named, ..}), 161 | .. 162 | }) = data 163 | { 164 | named 165 | } else { 166 | panic!("This is not supported"); 167 | }; 168 | 169 | let entity = Entity { 170 | fields: fields.iter().filter_map(|field| {get_entity_field(field)}).collect() 171 | }; 172 | 173 | let subclass_name = quote::format_ident!("{}{}", ident, "BufferData"); 174 | 175 | let mut struct_fields : Vec = Vec::new(); 176 | let mut equal_fields : Vec = Vec::new(); 177 | let mut partial_eq_fields : Vec = Vec::new(); 178 | let mut into_fields : Vec = Vec::new(); 179 | 180 | entity.fields.iter().for_each(|f| { 181 | let mut process = false; 182 | 183 | for i in &f.attrs { 184 | process = has_type(&i.name); 185 | } 186 | 187 | if process 188 | { 189 | let name = f.name.clone(); 190 | let ty = f.ty.clone(); 191 | 192 | struct_fields.push(quote::quote! { 193 | #name: #ty 194 | }); 195 | 196 | equal_fields.push(quote::quote! { 197 | #name: other_data_from_ident_to_into.#name 198 | }); 199 | 200 | partial_eq_fields.push(quote::quote! { 201 | self.#name == other_ident_data_boolean_condition.#name 202 | }); 203 | 204 | into_fields.push(quote::quote! { 205 | #name: other_ident_data_to_into_const.#name 206 | }); 207 | } 208 | }); 209 | 210 | let ident_regex_lowercase = regex::Regex::new(r"(?P[A-Z])").unwrap(); 211 | let ident_string = ident.to_string(); 212 | let replace_all = ident_regex_lowercase.replace_all(ident_string.as_str(), "_$M"); 213 | let mut result = replace_all.to_ascii_lowercase(); 214 | 215 | if result.chars().nth(0).unwrap() == '_' { 216 | result.remove(0); 217 | } 218 | 219 | let const_into_macro = quote::format_ident!("{}_const_into", result); 220 | let mutate_data_macro = quote::format_ident!("mutate_{}", result); 221 | 222 | quote::quote! { 223 | #[repr(C)] 224 | #[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)] 225 | struct #subclass_name { 226 | #(#struct_fields),* 227 | } 228 | 229 | impl From<#ident> for #subclass_name { 230 | fn from(other_data_from_ident_to_into: #ident) -> Self { 231 | Self { 232 | #(#equal_fields),* 233 | } 234 | } 235 | } 236 | 237 | impl From<&'static #ident> for #subclass_name { 238 | fn from(other_data_from_ident_to_into: &'static #ident) -> Self { 239 | Self { 240 | #(#equal_fields),* 241 | } 242 | } 243 | } 244 | 245 | impl PartialEq<#ident> for #subclass_name { 246 | fn eq(&self, other_ident_data_boolean_condition: &#ident) -> bool { 247 | #(#partial_eq_fields)&&* 248 | } 249 | } 250 | 251 | impl FromIterator<#ident> for Vec<#subclass_name> { 252 | fn from_iter>(iter: T) -> Self { 253 | let mut vec_data_from_ident_from_iterator = Vec::new(); 254 | 255 | for c in iter { 256 | vec_data_from_ident_from_iterator.push(c.into()); 257 | } 258 | 259 | vec_data_from_ident_from_iterator 260 | } 261 | } 262 | 263 | impl FromIterator<&'static #ident> for Vec<#subclass_name> { 264 | fn from_iter>(iter: T) -> Self { 265 | let mut vec_data_from_ident_single_from_iterator : Vec<#subclass_name> = Vec::new(); 266 | 267 | for c in iter { 268 | vec_data_from_ident_single_from_iterator.push(c.into()); 269 | } 270 | 271 | vec_data_from_ident_single_from_iterator 272 | } 273 | } 274 | 275 | impl #subclass_name { 276 | pub const fn const_into(other_ident_data_to_into_const: &#ident) -> Self { 277 | Self { 278 | #(#into_fields),* 279 | } 280 | } 281 | } 282 | 283 | impl #ident { 284 | pub fn mutate<'a>(other_data_from_ident_to_mutate: &'a Vec<#subclass_name>) -> &'a [u8] { 285 | bytemuck::cast_slice(other_data_from_ident_to_mutate.as_slice()) 286 | } 287 | 288 | pub fn transmute(other_data_from_ident_to_transmute: &'static [Self]) -> Vec<#subclass_name> { 289 | other_data_from_ident_to_transmute.into_iter().collect::>() 290 | } 291 | } 292 | 293 | #[allow(unused_macros)] 294 | macro_rules! #const_into_macro { 295 | ($data: expr) => { 296 | #subclass_name::const_into(&$data) 297 | }; 298 | } 299 | 300 | #[allow(unused_macros)] 301 | macro_rules! #mutate_data_macro { 302 | ($data: expr) => { 303 | #ident::mutate(&#ident::transmute($data)) 304 | }; 305 | } 306 | }.into() 307 | } -------------------------------------------------------------------------------- /src/parser.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::{Ident, Punct, Span, Spacing}; 2 | use quote::TokenStreamExt; 3 | 4 | pub struct TokenVertexFormat { 5 | pub attribute: wgpu::VertexFormat 6 | } 7 | 8 | impl quote::ToTokens for TokenVertexFormat { 9 | fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { 10 | //tokens.append_separated(&["wgpu", "VertexFormat", "Float32x2"], "::"); 11 | tokens.append(Ident::new("wgpu", Span::call_site())); 12 | tokens.append(Punct::new(':', Spacing::Joint)); 13 | tokens.append(Punct::new(':', Spacing::Alone)); 14 | tokens.append(Ident::new("VertexFormat", Span::call_site())); 15 | tokens.append(Punct::new(':', Spacing::Joint)); 16 | tokens.append(Punct::new(':', Spacing::Alone)); 17 | tokens.append(Ident::new(format!("{:?}", self.attribute).as_str(), Span::call_site())); 18 | } 19 | } 20 | 21 | pub struct TokenVertexStepMode { 22 | pub step_mode: wgpu::VertexStepMode 23 | } 24 | 25 | impl quote::ToTokens for TokenVertexStepMode { 26 | fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { 27 | //tokens.append_separated(&["wgpu", "VertexFormat", "Float32x2"], "::"); 28 | tokens.append(Ident::new("wgpu", Span::call_site())); 29 | tokens.append(Punct::new(':', Spacing::Joint)); 30 | tokens.append(Punct::new(':', Spacing::Alone)); 31 | tokens.append(Ident::new("VertexStepMode", Span::call_site())); 32 | tokens.append(Punct::new(':', Spacing::Joint)); 33 | tokens.append(Punct::new(':', Spacing::Alone)); 34 | tokens.append(Ident::new(format!("{:?}", self.step_mode).as_str(), Span::call_site())); 35 | } 36 | } 37 | 38 | pub struct AttrData<'a> { 39 | pub attribute: &'a syn::Attribute, 40 | pub segment: &'a syn::PathSegment 41 | } 42 | 43 | pub fn parse_attrs<'a>(attrs : &'a std::vec::Vec, mut callback: Box) { 44 | attrs.iter().for_each(|a| { 45 | a.path.segments.iter().for_each(|ps| { 46 | callback(AttrData { 47 | attribute: &a, 48 | segment: &ps, 49 | }); 50 | }); 51 | }); 52 | } 53 | 54 | #[derive(Debug, Clone)] 55 | pub struct AttrMat { 56 | pub ident: syn::Ident, 57 | pub data: u32 58 | } 59 | 60 | impl syn::parse::Parse for AttrMat { 61 | fn parse(tokens: syn::parse::ParseStream) -> syn::Result { 62 | let ident : syn::Ident = tokens.parse()?; 63 | tokens.parse::()?; 64 | let data : syn::LitInt = tokens.parse()?; 65 | 66 | Ok(AttrMat { ident, data: data.base10_parse().unwrap() }) 67 | } 68 | } -------------------------------------------------------------------------------- /tests/integration_test.rs: -------------------------------------------------------------------------------- 1 | use wrld::{Desc, DescInstance, BufferData}; 2 | 3 | #[repr(C)] 4 | #[derive(Desc, Debug)] 5 | struct Vertex { 6 | #[f32x2(0)] position: [f32; 2], 7 | #[f32x4(1)] color: [f32; 4] 8 | } 9 | 10 | #[repr(C)] 11 | #[derive(DescInstance)] 12 | struct VertexInstance { 13 | #[f32x2(0)] position: [f32; 2], 14 | #[f32x4(1)] color: [f32; 4] 15 | } 16 | 17 | #[repr(C)] 18 | #[derive(Desc, BufferData, Debug)] 19 | struct VertexDBD { 20 | #[f32x2(0)] position: [f32; 2], 21 | data: &'static str, 22 | #[f32x4(1)] color: [f32; 4] 23 | } 24 | 25 | #[repr(C)] 26 | #[derive(DescInstance, BufferData)] 27 | struct VertexDIBD { 28 | #[f32x2(0)] position: [f32; 2], 29 | data: &'static str, 30 | #[f32x4(1)] color: [f32; 4] 31 | } 32 | 33 | const DESC_DATA : [VertexDBD; 2] = [ 34 | VertexDBD { position: [0.0, 0.0], data: "hello", color: [1.0, 0.5, 0.5, 1.0]}, 35 | VertexDBD { position: [1.0, 0.0], data: "hello", color: [1.0, 0.5, 0.5, 1.0]} 36 | ]; 37 | 38 | const DESC_INSTANCE_DATA : [VertexDIBD; 2] = [ 39 | VertexDIBD { position: [0.0, 0.0], data: "hello", color: [1.0, 0.5, 0.5, 1.0]}, 40 | VertexDIBD { position: [1.0, 0.0], data: "hello", color: [1.0, 0.5, 0.5, 1.0]} 41 | ]; 42 | 43 | #[test] 44 | fn desc() { 45 | println!("Vertex data struct \n{:?}\n", Vertex { position: [0.0, 0.0], color: [1.0, 0.5, 0.5, 1.0]}); 46 | println!("Description of Vertex struct \n{:?}\n", Vertex::desc()); 47 | } 48 | 49 | #[test] 50 | fn desc_instance() { 51 | println!("Instance description of Vertex struct \n{:?}\n", VertexInstance::desc()); 52 | } 53 | 54 | #[test] 55 | fn desc_buffer_data() { 56 | println!("Vertex data struct array \n{:?}\n", DESC_DATA); 57 | println!("Result of mutate vertex desc buffer data : \n{:?}\n", mutate_vertex_d_b_d!(&DESC_DATA)); 58 | } 59 | 60 | #[test] 61 | fn desc_instance_buffer_data() { 62 | println!("Result of mutate vertex desc instance buffer data : \n{:?}\n", mutate_vertex_d_i_b_d!(&DESC_INSTANCE_DATA)); 63 | } --------------------------------------------------------------------------------