easy_wgpu/
bind_group.rs

1use smallvec::SmallVec;
2use wgpu;
3
4use crate::texture::Texture;
5
6pub fn align(size: usize, alignment: usize) -> usize {
7    size.div_ceil(alignment) * alignment
8}
9
10/// Since we want the `BindGroupWrapper` to keep a vector of the types of resources that are bound,
11/// we use an enum to deal with heterogeneous types
12#[derive(PartialEq, Clone)]
13pub enum BgResource {
14    TexView(wgpu::TextureView),
15    Buf(wgpu::Buffer),
16    Sampler(wgpu::Sampler),
17}
18
19/// Wrapper for a bind group that also keep a handle on the resources in the bingroup. This helps
20/// with keeping track if the textures in the entries have changed and the bind
21/// group needs to be recreated
22pub struct BindGroupWrapper {
23    bind_group: wgpu::BindGroup,
24    resources: SmallVec<[BgResource; 16]>,
25}
26impl BindGroupWrapper {
27    fn new(bind_group: wgpu::BindGroup, resources: SmallVec<[BgResource; 16]>) -> Self {
28        Self { bind_group, resources }
29    }
30    pub fn bg(&self) -> &wgpu::BindGroup {
31        &self.bind_group
32    }
33    pub fn is_stale(&self, entries: &SmallVec<[wgpu::BindGroupEntry; 16]>) -> bool {
34        if self.resources.len() != entries.len() {
35            return true;
36        }
37
38        for it in entries.iter().zip(self.resources.iter()) {
39            let (entry, res) = it;
40            let is_different = match &entry.resource {
41                wgpu::BindingResource::Buffer(buffer_binding) => BgResource::Buf(buffer_binding.buffer.clone()) != *res,
42                wgpu::BindingResource::Sampler(sampler) => BgResource::Sampler((*sampler).clone()) != *res,
43                wgpu::BindingResource::TextureView(view) => BgResource::TexView((*view).clone()) != *res,
44                _ => unimplemented!("other binding types"),
45            };
46
47            if is_different {
48                return true;
49            }
50        }
51
52        false
53    }
54}
55
56/// Describes a bind group a series of entries
57pub struct BindGroupDesc<'a> {
58    pub label: Option<String>,
59    pub bind_group_entries: SmallVec<[wgpu::BindGroupEntry<'a>; 16]>,
60    last_binding_number: u32,
61}
62impl Default for BindGroupDesc<'_> {
63    fn default() -> Self {
64        Self {
65            label: None,
66            bind_group_entries: SmallVec::new(),
67            last_binding_number: 0,
68        }
69    }
70}
71impl<'a> BindGroupDesc<'a> {
72    pub fn new(label: &str, entries: SmallVec<[wgpu::BindGroupEntry<'a>; 16]>) -> Self {
73        Self {
74            label: Some(String::from(label)),
75            bind_group_entries: entries,
76            last_binding_number: 0,
77        }
78    }
79    pub fn into_bind_group_wrapper(self, device: &wgpu::Device, layout: &wgpu::BindGroupLayout) -> BindGroupWrapper {
80        //create bg
81        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
82            layout,
83            entries: self.bind_group_entries.as_slice(),
84            label: self.label.as_deref(),
85        });
86        //create some owned resources by cloning the texture,buffers,etc. Since its all Arc<> it's actually just a reference count
87        let mut bg_resources = SmallVec::new();
88        for res in self.bind_group_entries {
89            match res.resource {
90                wgpu::BindingResource::Buffer(buffer_binding) => bg_resources.push(BgResource::Buf(buffer_binding.buffer.clone())),
91                wgpu::BindingResource::Sampler(sampler) => bg_resources.push(BgResource::Sampler(sampler.clone())),
92                wgpu::BindingResource::TextureView(texture_view) => bg_resources.push(BgResource::TexView(texture_view.clone())),
93                _ => todo!(),
94            }
95        }
96        BindGroupWrapper::new(bind_group, bg_resources)
97        // texture entries
98    }
99}
100
101/// Builder for bind groups
102/// IMPORTANT: order of adding entries sets also the binding index that should
103/// correspond with the layout and the shader
104pub struct BindGroupBuilder<'a> {
105    bind_group_desc: Option<BindGroupDesc<'a>>,
106}
107impl Default for BindGroupBuilder<'_> {
108    fn default() -> Self {
109        Self::new()
110    }
111}
112impl<'a> BindGroupBuilder<'a> {
113    pub fn new() -> Self {
114        Self {
115            bind_group_desc: Some(BindGroupDesc::default()),
116        }
117    }
118
119    /// # Panics
120    /// Will panic if the builder was not constructed with ``new()``
121    #[must_use]
122    pub fn label(mut self, label: &str) -> Self {
123        self.bind_group_desc.as_mut().unwrap().label = Some(String::from(label));
124        self
125    }
126
127    /// # Panics
128    /// Will panic if the builder was not constructed with ``new()``
129    #[must_use]
130    pub fn add_entry_empty(mut self) -> Self {
131        self.bind_group_desc.as_mut().unwrap().last_binding_number += 1;
132        self
133    }
134
135    /// # Panics
136    /// Will panic if the builder was not constructed with ``new()``
137    #[must_use]
138    pub fn add_entry_tex(mut self, tex: &'a Texture) -> Self {
139        //each entry we add will have sequential binding_indices
140        //this should correspond with the binding in the shader
141        let binding_number = self.bind_group_desc.as_ref().unwrap().last_binding_number;
142        //entry
143        let entry = wgpu::BindGroupEntry {
144            binding: binding_number,
145            resource: wgpu::BindingResource::TextureView(&tex.view),
146        };
147        //add
148        self.bind_group_desc.as_mut().unwrap().bind_group_entries.push(entry);
149        self.bind_group_desc.as_mut().unwrap().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_buf(mut self, buffer: &'a wgpu::Buffer) -> 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.bind_group_desc.as_ref().unwrap().last_binding_number;
160        //entry
161        let entry = wgpu::BindGroupEntry {
162            binding: binding_number,
163            resource: buffer.as_entire_binding(),
164        };
165        //add
166        self.bind_group_desc.as_mut().unwrap().bind_group_entries.push(entry);
167        self.bind_group_desc.as_mut().unwrap().last_binding_number += 1;
168        self
169    }
170
171    /// # Panics
172    /// Will panic if the builder was not constructed with ``new()``
173    #[must_use]
174    pub fn add_entry_buf_chunk<T>(mut self, buffer: &'a wgpu::Buffer) -> Self {
175        //each entry we add will have sequential binding_indices
176        //this should correspond with the binding in the shader
177        let binding_number = self.bind_group_desc.as_ref().unwrap().last_binding_number;
178        //chunk of the buffer
179        let binding = wgpu::BufferBinding {
180            buffer,
181            offset: 0,
182            size: wgpu::BufferSize::new(u64::try_from(align(std::mem::size_of::<T>(), 256)).unwrap()),
183        };
184        //entry
185        let entry = wgpu::BindGroupEntry {
186            binding: binding_number,
187            resource: wgpu::BindingResource::Buffer(binding),
188        };
189        //add
190        self.bind_group_desc.as_mut().unwrap().bind_group_entries.push(entry);
191        self.bind_group_desc.as_mut().unwrap().last_binding_number += 1;
192        self
193    }
194
195    /// # Panics
196    /// Will panic if the builder was not constructed with ``new()``
197    #[must_use]
198    pub fn add_entry_sampler(mut self, sampler: &'a wgpu::Sampler) -> Self {
199        //each entry we add will have sequential binding_indices
200        //this should correspond with the binding in the shader
201        let binding_number = self.bind_group_desc.as_ref().unwrap().last_binding_number;
202        //entry
203        let entry = wgpu::BindGroupEntry {
204            binding: binding_number,
205            resource: wgpu::BindingResource::Sampler(sampler),
206        };
207        // add
208        self.bind_group_desc.as_mut().unwrap().bind_group_entries.push(entry);
209        self.bind_group_desc.as_mut().unwrap().last_binding_number += 1;
210        self
211    }
212
213    // pub fn get_or_build(
214    //     &mut self,
215    //     device: &wgpu::Device,
216    //     layout: &wgpu::BindGroupLayout,
217    //     old_bind_group: Option<BindGroupWrapper>,
218    // ) -> BindGroupWrapper {
219    //     //check if it's stale by comparing if the id's match
220    //     let stale = true;
221    //     let ret = if stale {
222    //         let desc = self.bind_group_desc.take().unwrap();
223    //         desc.into_bind_group_wrapper(device, layout)
224    //     } else {
225    //         old_bind_group.unwrap()
226    //     };
227    //     ret
228    // }
229
230    /// # Panics
231    /// Will panic if the builder was not constructed with ``new()``
232    pub fn build(&mut self, device: &wgpu::Device, layout: &wgpu::BindGroupLayout) -> BindGroupWrapper {
233        let desc = self.bind_group_desc.take().unwrap();
234        desc.into_bind_group_wrapper(device, layout)
235    }
236
237    /// # Panics
238    /// Will panic if the builder was not constructed with ``new()``
239    pub fn build_bind_group(&mut self, device: &wgpu::Device, layout: &wgpu::BindGroupLayout) -> wgpu::BindGroup {
240        let desc = self.bind_group_desc.take().unwrap();
241        desc.into_bind_group_wrapper(device, layout).bind_group
242    }
243
244    /// # Panics
245    /// Will panic if the builder was not constructed with ``new()``
246    pub fn build_entries(&mut self) -> SmallVec<[wgpu::BindGroupEntry<'a>; 16]> {
247        self.bind_group_desc.take().unwrap().bind_group_entries
248    }
249}