wrld/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//! ```
134use proc_macro::{TokenStream};
135
136mod converter;
137mod parser;
138mod 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::<f32>() = 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))]
263pub 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::<f32>() = 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))]
320pub 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<Vertex> 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<Vertex> 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<Vertex> for Vec<VertexBufferData> {
473/// fn from_iter<T: IntoIterator<Item = Vertex>>(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<VertexBufferData> {
485/// fn from_iter<T: IntoIterator<Item = &'static Vertex>>(iter: T) -> Self {
486/// let mut vec_data_from_ident_single_from_iterator : Vec<VertexBufferData> = 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<VertexBufferData>) -> &'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<VertexBufferData> {
511/// other_data_from_ident_to_transmute.into_iter().collect::<Vec<VertexBufferData>>()
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)]
652pub fn derive_wrld_buffer_data(item: TokenStream) -> TokenStream {
653 macros::derive_wrld_buffer_data(item)
654}