simple_wgpu/
bind_group.rs

1use std::{collections::HashMap, hash::Hash, num::NonZeroU64};
2
3use crate::{buffer::BufferBinding, context::Context, sampler::Sampler, texture::TextureBinding};
4
5#[derive(Hash, PartialEq, Clone, Eq, Debug)]
6pub(crate) struct Binding {
7    /// The binding index. Must be unique within a single bind group
8    pub binding: usize,
9    /// What shader stages the binding will be visible to
10    pub visibility: wgpu::ShaderStages,
11    /// The resource to bind
12    pub resource: BindingResource,
13}
14
15#[derive(Hash, PartialEq, Eq, Clone, Debug)]
16pub(crate) enum BindingResource {
17    Buffer(BufferBinding, Option<usize>),
18    Texture(TextureBinding),
19    Sampler(Sampler),
20}
21
22#[derive(Debug, Clone, Hash, PartialEq, Eq)]
23pub(crate) struct BindGroupLayout {
24    layout: Vec<wgpu::BindGroupLayoutEntry>,
25}
26
27impl BindGroupLayout {
28    pub(crate) fn get_or_build(&self, context: &Context) -> wgpu::BindGroupLayout {
29        let mut bind_group_layout_cache = context.caches.bind_group_layout_cache.borrow_mut();
30
31        bind_group_layout_cache
32            .get_or_insert_with(self.clone(), || {
33                context
34                    .device()
35                    .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
36                        label: None,
37                        entries: &self.layout,
38                    })
39            })
40            .clone()
41    }
42}
43
44/// A handle to a binding group
45///
46/// Binding groups let you bind GPU resources to a [DrawCall](crate::DrawCall).
47///  
48/// The equivalent to [wgpu::BindGroup]
49
50#[derive(Clone, Hash, PartialEq, Eq, Debug)]
51pub struct BindGroup {
52    bindings: Vec<Binding>,
53    name: Option<String>,
54}
55
56impl BindGroup {
57    pub(crate) fn build_layout(&self) -> BindGroupLayout {
58        let layout = self
59            .bindings
60            .iter()
61            .map(|b| match &b.resource {
62                BindingResource::Sampler(sampler) => wgpu::BindGroupLayoutEntry {
63                    binding: b.binding as u32,
64                    visibility: b.visibility,
65                    ty: wgpu::BindingType::Sampler(sampler.sampler_type()),
66                    count: None,
67                },
68                BindingResource::Texture(texture) => wgpu::BindGroupLayoutEntry {
69                    binding: b.binding as u32,
70                    visibility: b.visibility,
71                    ty: texture.binding_type,
72                    count: None,
73                },
74                BindingResource::Buffer(buffer, _) => wgpu::BindGroupLayoutEntry {
75                    binding: b.binding as u32,
76                    visibility: b.visibility,
77                    ty: wgpu::BindingType::Buffer {
78                        ty: buffer.binding_type,
79                        has_dynamic_offset: buffer.has_dynamic_offset,
80                        min_binding_size: buffer.min_binding_size,
81                    },
82                    count: None,
83                },
84            })
85            .collect();
86
87        BindGroupLayout { layout }
88    }
89
90    pub(crate) fn get_or_build(&self, context: &Context) -> wgpu::BindGroup {
91        let mut bind_group_cache = context.caches.bind_group_cache.borrow_mut();
92
93        bind_group_cache
94            .get_or_insert_with(self.clone(), || {
95                let gpu_layout = self.build_layout().get_or_build(context);
96
97                let mut texture_views = HashMap::new();
98                let mut samplers = HashMap::new();
99
100                for b in &self.bindings {
101                    match &b.resource {
102                        BindingResource::Texture(texture) => {
103                            texture_views
104                                .insert(&texture.texture, texture.texture.get_or_build(context));
105                        }
106                        BindingResource::Sampler(sampler) => {
107                            samplers.insert(sampler, sampler.get_or_build(context));
108                        }
109                        _ => {}
110                    }
111                }
112
113                let gpu_bindings = self
114                    .bindings
115                    .iter()
116                    .map(|b| match &b.resource {
117                        BindingResource::Buffer(buffer, size) => wgpu::BindGroupEntry {
118                            binding: b.binding as u32,
119                            resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
120                                buffer: buffer.buffer.buffer(),
121                                offset: 0,
122                                size: size.and_then(|s| NonZeroU64::new(s as u64)),
123                            }),
124                        },
125                        BindingResource::Texture(texture) => wgpu::BindGroupEntry {
126                            binding: b.binding as u32,
127                            resource: wgpu::BindingResource::TextureView(
128                                &texture_views.get(&texture.texture).unwrap(),
129                            ),
130                        },
131                        BindingResource::Sampler(sampler) => wgpu::BindGroupEntry {
132                            binding: b.binding as u32,
133                            resource: wgpu::BindingResource::Sampler(
134                                &samplers.get(sampler).unwrap(),
135                            ),
136                        },
137                    })
138                    .collect::<Vec<_>>();
139
140                context
141                    .device()
142                    .create_bind_group(&wgpu::BindGroupDescriptor {
143                        label: self.name.as_deref(),
144                        layout: &gpu_layout,
145                        entries: &gpu_bindings,
146                    })
147            })
148            .clone()
149    }
150}
151
152/// Builds a [BindGroup]
153pub struct BindGroupBuilder {
154    bindings: Vec<Binding>,
155    name: Option<String>,
156}
157
158impl BindGroupBuilder {
159    /// Create a new builder
160    pub fn new() -> Self {
161        Self {
162            bindings: vec![],
163            name: None,
164        }
165    }
166
167    /// Set the optional debug name. This may appear in error messages and GPU profiler traces
168    pub fn name(mut self, name: &str) -> Self {
169        self.name = Some(name.to_string());
170        self
171    }
172
173    /// Bind a [Buffer](crate::Buffer) to this bind group
174    pub fn buffer(
175        mut self,
176        binding: usize,
177        visibility: wgpu::ShaderStages,
178        buffer: &BufferBinding,
179        size: Option<usize>,
180    ) -> Self {
181        self.bindings.push(Binding {
182            binding,
183            visibility,
184            resource: BindingResource::Buffer(buffer.clone(), size),
185        });
186        self
187    }
188
189    /// Bind a [Texture](crate::Texture) to this bind group
190    pub fn texture(
191        mut self,
192        binding: usize,
193        visibility: wgpu::ShaderStages,
194        texture: &TextureBinding,
195    ) -> Self {
196        self.bindings.push(Binding {
197            binding,
198            visibility,
199            resource: BindingResource::Texture(texture.clone()),
200        });
201        self
202    }
203
204    /// Bind a [Sampler](crate::Sampler) to this bind group
205    pub fn sampler(
206        mut self,
207        binding: usize,
208        visibility: wgpu::ShaderStages,
209        sampler: &Sampler,
210    ) -> Self {
211        self.bindings.push(Binding {
212            binding,
213            visibility,
214            resource: BindingResource::Sampler(sampler.clone()),
215        });
216        self
217    }
218
219    /// Consume this builder and return a [BindGroup]
220    pub fn build(self) -> BindGroup {
221        BindGroup {
222            bindings: self.bindings,
223            name: self.name,
224        }
225    }
226}