easy_wgpu/
bind_group_layout.rs

1use core::num::NonZeroU64;
2
3/// Convenience layout for the bind group. Can potentially be hashed and used in
4/// a arena-kind of way
5#[derive(Debug, Clone, Hash, PartialEq, Eq, Default)]
6pub struct BindGroupLayoutDesc {
7    label: Option<String>,
8    entries: Vec<wgpu::BindGroupLayoutEntry>,
9}
10impl BindGroupLayoutDesc {
11    pub fn into_bind_group_layout(self, device: &wgpu::Device) -> wgpu::BindGroupLayout {
12        let desc = wgpu::BindGroupLayoutDescriptor {
13            entries: self.entries.as_slice(),
14            label: self.label.as_deref(),
15        };
16        device.create_bind_group_layout(&desc)
17    }
18    pub fn empty() -> Self {
19        Self {
20            label: Some(String::from("emtpy_bgl")),
21            entries: Vec::new(),
22        }
23    }
24}
25
26/// Convenience builder to build the layout of a bind group
27/// # Example
28/// ```
29///  # use easy_wgpu::bind_group_layout::BindGroupLayoutBuilder;
30/// let desc = BindGroupLayoutBuilder::new()
31///     .add_entry_uniform(
32///         wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
33///         false,
34///         None,
35///     )
36///     .add_entry_sampler(
37///         wgpu::ShaderStages::FRAGMENT,
38///         wgpu::SamplerBindingType::NonFiltering,
39///     )
40///     .build();
41/// ```
42
43pub struct BindGroupLayoutBuilder {
44    layout_desc: Option<BindGroupLayoutDesc>,
45    last_binding_number: u32,
46}
47impl Default for BindGroupLayoutBuilder {
48    fn default() -> Self {
49        Self::new()
50    }
51}
52impl BindGroupLayoutBuilder {
53    pub fn new() -> Self {
54        Self {
55            layout_desc: Some(BindGroupLayoutDesc::empty()),
56            last_binding_number: 0,
57        }
58    }
59
60    /// # Panics
61    /// Will panic if the builder was not constructed with ``new()``
62    #[must_use]
63    pub fn label(mut self, label: &str) -> Self {
64        self.layout_desc.as_mut().unwrap().label = Some(String::from(label));
65        self
66    }
67
68    #[must_use]
69    pub fn add_entry_empty(mut self) -> Self {
70        self.last_binding_number += 1;
71        self
72    }
73
74    /// # Panics
75    /// Will panic if the builder was not constructed with ``new()``
76    #[must_use]
77    pub fn add_entry_tex(mut self, visibility: wgpu::ShaderStages, sample_type: wgpu::TextureSampleType) -> Self {
78        //each entry we add will have sequential binding_indices
79        //this should correspond with the binding in the shader
80        let binding_number = self.last_binding_number;
81        //entry and id
82        let entry = wgpu::BindGroupLayoutEntry {
83            binding: binding_number, //matches with the @binding in the shader
84            visibility,
85            ty: wgpu::BindingType::Texture {
86                multisampled: false,
87                view_dimension: wgpu::TextureViewDimension::D2,
88                sample_type,
89            },
90            count: None,
91        };
92        //add
93        self.layout_desc.as_mut().unwrap().entries.push(entry);
94        self.last_binding_number += 1;
95        self
96    }
97
98    /// # Panics
99    /// Will panic if the builder was not constructed with ``new()``
100    #[must_use]
101    pub fn add_entries_tex(self, visibility: wgpu::ShaderStages, sample_type: wgpu::TextureSampleType, num_textures: usize) -> Self {
102        let mut builder = self;
103        for _i in 0..num_textures {
104            builder = builder.add_entry_tex(visibility, sample_type);
105        }
106        builder
107    }
108
109    /// # Panics
110    /// Will panic if the builder was not constructed with ``new()``
111    #[must_use]
112    pub fn add_entry_cubemap(mut self, visibility: wgpu::ShaderStages, sample_type: wgpu::TextureSampleType) -> Self {
113        //each entry we add will have sequential binding_indices
114        //this should correspond with the binding in the shader
115        let binding_number = self.last_binding_number;
116        //entry and id
117        let entry = wgpu::BindGroupLayoutEntry {
118            binding: binding_number, //matches with the @binding in the shader
119            visibility,
120            ty: wgpu::BindingType::Texture {
121                multisampled: false,
122                view_dimension: wgpu::TextureViewDimension::Cube,
123                sample_type,
124            },
125            count: None,
126        };
127        //add
128        self.layout_desc.as_mut().unwrap().entries.push(entry);
129        self.last_binding_number += 1;
130        self
131    }
132
133    /// # Panics
134    /// Will panic if the builder was not constructed with ``new()``
135    #[must_use]
136    pub fn add_entry_sampler(mut self, visibility: wgpu::ShaderStages, sampler_type: wgpu::SamplerBindingType) -> Self {
137        //each entry we add will have sequential binding_indices
138        //this should correspond with the binding in the shader
139        let binding_number = self.last_binding_number;
140        //entry and id
141        let entry = wgpu::BindGroupLayoutEntry {
142            binding: binding_number, //matches with the @binding in the shader
143            visibility,
144            ty: wgpu::BindingType::Sampler(sampler_type),
145            count: None,
146        };
147        //add
148        self.layout_desc.as_mut().unwrap().entries.push(entry);
149        self.last_binding_number += 1;
150        self
151    }
152
153    /// # Panics
154    /// Will panic if the builder was not constructed with ``new()``
155    #[must_use]
156    pub fn add_entry_uniform(mut self, visibility: wgpu::ShaderStages, has_dynamic_offset: bool, _min_binding_size: Option<NonZeroU64>) -> Self {
157        //each entry we add will have sequential binding_indices
158        //this should correspond with the binding in the shader
159        let binding_number = self.last_binding_number;
160        // println!("BINDING NUM {binding_number}");
161        // println!("MIN SIZE {:?}", min_binding_size);
162        // println!("LABEL {:?}", self.layout_desc.clone().unwrap().label);
163
164        //entry and id
165        // TODO: Why does min_binding_size have an issue here?
166        let entry = wgpu::BindGroupLayoutEntry {
167            binding: binding_number, //----- keep in sync with the binding in create_bind_group and also the shader
168            visibility,
169            ty: wgpu::BindingType::Buffer {
170                ty: wgpu::BufferBindingType::Uniform,
171                has_dynamic_offset, //important because we will change the offset intot his buffer for each mesh
172                // min_binding_size,
173                min_binding_size: None,
174            },
175            count: None,
176        };
177        //add
178        self.layout_desc.as_mut().unwrap().entries.push(entry);
179        self.last_binding_number += 1;
180        self
181    }
182
183    /// # Panics
184    /// Will panic if the builder was not constructed with ``new()``
185    pub fn build(&mut self) -> BindGroupLayoutDesc {
186        self.layout_desc.take().unwrap()
187    }
188}