agpu/graphics/buffer/
binding.rs

1use std::{ops::Deref, rc::Rc};
2
3use wgpu::BindGroupEntry;
4
5use crate::Gpu;
6
7impl crate::Buffer {
8    /// Create a uniform buffer binding.
9    ///
10    /// Example GLSL syntax:
11    /// ```cpp,ignore
12    /// layout(std140, binding = 0)
13    /// uniform Globals {
14    ///     vec2 aUniform;
15    ///     vec2 anotherUniform;
16    /// };
17    /// ```
18    #[must_use]
19    pub fn bind_uniform(&self) -> Binding {
20        Binding {
21            gpu: &self.gpu,
22            visibility: Binding::DEFAULT_VISIBILITY,
23            ty: wgpu::BindingType::Buffer {
24                ty: wgpu::BufferBindingType::Uniform,
25                has_dynamic_offset: false,
26                min_binding_size: None,
27            },
28            resource: self.as_entire_binding(),
29        }
30    }
31
32    /// Alias for `bind_uniform()`.
33    #[must_use]
34    pub fn bind(&self) -> Binding {
35        self.bind_uniform()
36    }
37
38    /// Create a storage buffer binding.
39    ///
40    /// Example GLSL syntax:
41    /// ```cpp,ignore
42    /// layout (set=0, binding=0) buffer myStorageBuffer {
43    ///     vec4 myElement[];
44    /// };
45    /// ```
46    #[must_use]
47    pub fn bind_storage(&self) -> Binding {
48        Binding {
49            gpu: &self.gpu,
50            visibility: Binding::DEFAULT_VISIBILITY,
51            ty: wgpu::BindingType::Buffer {
52                ty: wgpu::BufferBindingType::Storage { read_only: false },
53                has_dynamic_offset: false,
54                min_binding_size: None,
55            },
56            resource: self.as_entire_binding(),
57        }
58    }
59
60    /// Create a storage buffer binding. The buffer is read-only in shader,
61    /// and it must be annotated with `readonly`.
62    ///
63    /// Example GLSL syntax:
64    /// ```cpp,ignore
65    /// layout (set=0, binding=0) readonly buffer myStorageBuffer {
66    ///     vec4 myElement[];
67    /// };
68    /// ```
69    #[must_use]
70    pub fn bind_storage_readonly(&self) -> Binding {
71        Binding {
72            gpu: &self.gpu,
73            visibility: Binding::DEFAULT_VISIBILITY,
74            ty: wgpu::BindingType::Buffer {
75                ty: wgpu::BufferBindingType::Storage { read_only: true },
76                has_dynamic_offset: false,
77                min_binding_size: None,
78            },
79            resource: self.as_entire_binding(),
80        }
81    }
82}
83
84impl<D> crate::Texture<D>
85where
86    D: crate::TextureDimensions,
87{
88    /// Create a textureview binding.
89    // Can be const following RFC 2632
90    pub fn bind_texture(&self) -> Binding {
91        Binding {
92            gpu: &self.gpu,
93            visibility: Binding::DEFAULT_VISIBILITY,
94            ty: wgpu::BindingType::Texture {
95                sample_type: sample_type(self.format),
96                // TODO: different texture view reps?
97                view_dimension: match self.size.dim() {
98                    wgpu::TextureDimension::D1 => wgpu::TextureViewDimension::D1,
99                    wgpu::TextureDimension::D2 => wgpu::TextureViewDimension::D2,
100                    wgpu::TextureDimension::D3 => wgpu::TextureViewDimension::D3,
101                },
102                multisampled: false,
103            },
104            resource: wgpu::BindingResource::TextureView(&self.view),
105        }
106    }
107
108    /// Alias for `bind_texture()`.
109    // Can be const following RFC 2632
110    pub fn bind(&self) -> Binding {
111        self.bind_texture()
112    }
113
114    /// Create a storage texture binding.
115    // Can be const following RFC 2632
116    pub fn bind_storage_texture(&self) -> Binding {
117        Binding {
118            gpu: &self.gpu,
119            visibility: Binding::DEFAULT_VISIBILITY,
120            ty: wgpu::BindingType::StorageTexture {
121                // TODO: different texture view reps?
122                view_dimension: match self.size.dim() {
123                    wgpu::TextureDimension::D1 => wgpu::TextureViewDimension::D1,
124                    wgpu::TextureDimension::D2 => wgpu::TextureViewDimension::D2,
125                    wgpu::TextureDimension::D3 => wgpu::TextureViewDimension::D3,
126                },
127                access: wgpu::StorageTextureAccess::ReadWrite,
128                format: self.format,
129            },
130            resource: wgpu::BindingResource::TextureView(&self.view),
131        }
132    }
133}
134
135macro_rules! gen_binding_vis_fn {
136    ($($fn_name:ident => $stage:ident),*) => {
137        $(
138            pub const fn $fn_name(mut self) -> Self {
139                self.visibility = ::wgpu::ShaderStages::$stage;
140                self
141            }
142        )*
143    };
144}
145
146#[derive(Clone, Debug)]
147pub struct Binding<'a> {
148    pub gpu: &'a Gpu,
149    pub visibility: wgpu::ShaderStages,
150    pub ty: wgpu::BindingType,
151    pub resource: wgpu::BindingResource<'a>,
152}
153impl Binding<'_> {
154    pub(crate) const DEFAULT_VISIBILITY: wgpu::ShaderStages = wgpu::ShaderStages::VERTEX_FRAGMENT;
155
156    gen_binding_vis_fn!(
157        in_none => NONE,
158        in_vertex => VERTEX,
159        in_fragment => FRAGMENT,
160        in_compute => COMPUTE,
161        in_vertex_fragment => VERTEX_FRAGMENT
162    );
163
164    pub const fn buffer_dynamic_offset(mut self) -> Self {
165        if let wgpu::BindingType::Buffer {
166            ty,
167            min_binding_size,
168            ..
169        } = self.ty
170        {
171            self.ty = wgpu::BindingType::Buffer {
172                ty,
173                has_dynamic_offset: true,
174                min_binding_size,
175            };
176        } else {
177            #[cfg(feature = "const_panic")]
178            panic!("dynamic_offset is only supported for uniform buffers");
179        }
180        self
181    }
182
183    pub const fn sample_uint(mut self) -> Self {
184        if let wgpu::BindingType::Texture {
185            view_dimension,
186            multisampled,
187            ..
188        } = self.ty
189        {
190            self.ty = wgpu::BindingType::Texture {
191                sample_type: wgpu::TextureSampleType::Uint,
192                view_dimension,
193                multisampled,
194            };
195        } else {
196            #[cfg(feature = "const_panic")]
197            panic!("sample_uint is only supported for textures");
198        }
199        self
200    }
201}
202
203#[derive(Clone, Debug)]
204pub struct BindGroupLayout {
205    gpu: Gpu,
206    inner: Rc<wgpu::BindGroupLayout>,
207}
208impl Deref for BindGroupLayout {
209    type Target = wgpu::BindGroupLayout;
210    fn deref(&self) -> &Self::Target {
211        &self.inner
212    }
213}
214impl BindGroupLayout {
215    pub fn from_wgpu(gpu: Gpu, layout: wgpu::BindGroupLayout) -> Self {
216        Self {
217            gpu,
218            inner: Rc::new(layout),
219        }
220    }
221    pub fn inner(&self) -> &wgpu::BindGroupLayout {
222        &self.inner
223    }
224    pub fn create_bind_group(&self, bindings: &[Binding]) -> BindGroup {
225        let bind_group = self
226            .gpu
227            .device
228            .create_bind_group(&wgpu::BindGroupDescriptor {
229                label: None,
230                layout: self,
231                entries: bindings
232                    .iter()
233                    .enumerate()
234                    .map(|(i, b)| BindGroupEntry {
235                        binding: i as _,
236                        resource: b.resource.clone(),
237                    })
238                    .collect::<Vec<_>>()
239                    .as_slice(),
240            });
241
242        BindGroup {
243            gpu: self.gpu.clone(),
244            layout: self.clone(),
245            inner: bind_group,
246        }
247    }
248}
249
250#[derive(Debug)]
251pub struct BindGroup {
252    gpu: crate::Gpu,
253    pub layout: BindGroupLayout,
254    pub inner: wgpu::BindGroup,
255}
256crate::wgpu_inner_deref!(BindGroup);
257
258impl BindGroup {
259    pub fn new(gpu: crate::Gpu, bindings: &[Binding]) -> Self {
260        let bind_group_layout = gpu.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
261            label: None,
262            entries: bindings
263                .iter()
264                .enumerate()
265                .map(|(i, binding)| wgpu::BindGroupLayoutEntry {
266                    binding: i as _,
267                    visibility: binding.visibility,
268                    ty: binding.ty,
269                    count: None,
270                })
271                .collect::<Vec<_>>()
272                .as_slice(),
273        });
274
275        let bind_group = gpu.device.create_bind_group(&wgpu::BindGroupDescriptor {
276            label: None,
277            layout: &bind_group_layout,
278            entries: bindings
279                .iter()
280                .enumerate()
281                .map(|(i, b)| BindGroupEntry {
282                    binding: i as _,
283                    resource: b.resource.clone(),
284                })
285                .collect::<Vec<_>>()
286                .as_slice(),
287        });
288        let layout = BindGroupLayout::from_wgpu(gpu.clone(), bind_group_layout);
289
290        BindGroup {
291            gpu,
292            layout,
293            inner: bind_group,
294        }
295    }
296
297    // Creates a new bind group with the same layout as this one, but with the given bindings.
298    pub fn instance(&self, bindings: &[Binding]) -> Self {
299        let bind_group = self
300            .gpu
301            .device
302            .create_bind_group(&wgpu::BindGroupDescriptor {
303                label: None,
304                layout: &self.layout,
305                entries: bindings
306                    .iter()
307                    .enumerate()
308                    .map(|(i, b)| BindGroupEntry {
309                        binding: i as _,
310                        resource: b.resource.clone(),
311                    })
312                    .collect::<Vec<_>>()
313                    .as_slice(),
314            });
315
316        BindGroup {
317            gpu: self.gpu.clone(),
318            layout: self.layout.clone(),
319            inner: bind_group,
320        }
321    }
322
323    // Recreates the bind group with the same layout inplace, but with the given bindings.
324    pub fn rebind(&mut self, bindings: &[Binding]) -> &Self {
325        self.inner = self
326            .gpu
327            .device
328            .create_bind_group(&wgpu::BindGroupDescriptor {
329                label: None,
330                layout: &self.layout,
331                entries: bindings
332                    .iter()
333                    .enumerate()
334                    .map(|(i, b)| BindGroupEntry {
335                        binding: i as _,
336                        resource: b.resource.clone(),
337                    })
338                    .collect::<Vec<_>>()
339                    .as_slice(),
340            });
341        self
342    }
343}
344impl crate::Gpu {
345    pub fn create_bind_group(&self, bindings: &[Binding]) -> BindGroup {
346        BindGroup::new(self.clone(), bindings)
347    }
348}
349pub trait BindingsExt {
350    fn create_group(&self) -> BindGroup;
351}
352impl BindingsExt for [Binding<'_>] {
353    fn create_group(&self) -> BindGroup {
354        if let Some(Binding { gpu, .. }) = self.first() {
355            gpu.create_bind_group(self)
356        } else {
357            unreachable!("[Binding].group() array must not be empty")
358        }
359    }
360}
361
362/// Returns the binding sample type for a texture format.
363pub(crate) const fn sample_type(format: wgpu::TextureFormat) -> wgpu::TextureSampleType {
364    // Sample Types
365    const UINT: wgpu::TextureSampleType = wgpu::TextureSampleType::Uint;
366    const SINT: wgpu::TextureSampleType = wgpu::TextureSampleType::Sint;
367    const NEAREST: wgpu::TextureSampleType = wgpu::TextureSampleType::Float { filterable: false };
368    const FLOAT: wgpu::TextureSampleType = wgpu::TextureSampleType::Float { filterable: true };
369    const DEPTH: wgpu::TextureSampleType = wgpu::TextureSampleType::Depth;
370
371    match format {
372        // Normal 8 bit textures
373        wgpu::TextureFormat::R8Unorm => FLOAT,
374        wgpu::TextureFormat::R8Snorm => FLOAT,
375        wgpu::TextureFormat::R8Uint => UINT,
376        wgpu::TextureFormat::R8Sint => SINT,
377
378        // Normal 16 bit textures
379        wgpu::TextureFormat::R16Uint => UINT,
380        wgpu::TextureFormat::R16Sint => SINT,
381        wgpu::TextureFormat::R16Float => FLOAT,
382        wgpu::TextureFormat::Rg8Unorm => FLOAT,
383        wgpu::TextureFormat::Rg8Snorm => FLOAT,
384        wgpu::TextureFormat::Rg8Uint => UINT,
385        wgpu::TextureFormat::Rg8Sint => SINT,
386
387        // Normal 32 bit textures
388        wgpu::TextureFormat::R32Uint => UINT,
389        wgpu::TextureFormat::R32Sint => SINT,
390        wgpu::TextureFormat::R32Float => NEAREST,
391        wgpu::TextureFormat::Rg16Uint => UINT,
392        wgpu::TextureFormat::Rg16Sint => SINT,
393        wgpu::TextureFormat::Rg16Float => FLOAT,
394        wgpu::TextureFormat::Rgba8Unorm => FLOAT,
395        wgpu::TextureFormat::Rgba8UnormSrgb => FLOAT,
396        wgpu::TextureFormat::Rgba8Snorm => FLOAT,
397        wgpu::TextureFormat::Rgba8Uint => UINT,
398        wgpu::TextureFormat::Rgba8Sint => SINT,
399        wgpu::TextureFormat::Bgra8Unorm => FLOAT,
400        wgpu::TextureFormat::Bgra8UnormSrgb => FLOAT,
401
402        // Packed 32 bit textures
403        wgpu::TextureFormat::Rgb10a2Unorm => FLOAT,
404        wgpu::TextureFormat::Rg11b10Float => FLOAT,
405
406        // Packed 32 bit textures
407        wgpu::TextureFormat::Rg32Uint => UINT,
408        wgpu::TextureFormat::Rg32Sint => SINT,
409        wgpu::TextureFormat::Rg32Float => NEAREST,
410        wgpu::TextureFormat::Rgba16Uint => UINT,
411        wgpu::TextureFormat::Rgba16Sint => SINT,
412        wgpu::TextureFormat::Rgba16Float => FLOAT,
413
414        // Packed 32 bit textures
415        wgpu::TextureFormat::Rgba32Uint => UINT,
416        wgpu::TextureFormat::Rgba32Sint => SINT,
417        wgpu::TextureFormat::Rgba32Float => NEAREST,
418
419        // Depth-stencil textures
420        wgpu::TextureFormat::Depth32Float => DEPTH,
421        wgpu::TextureFormat::Depth24Plus => DEPTH,
422        wgpu::TextureFormat::Depth24PlusStencil8 => DEPTH,
423
424        // Packed uncompressed
425        wgpu::TextureFormat::Rgb9e5Ufloat => FLOAT,
426
427        // BCn compressed textures
428        wgpu::TextureFormat::Bc1RgbaUnorm
429        | wgpu::TextureFormat::Bc1RgbaUnormSrgb
430        | wgpu::TextureFormat::Bc2RgbaUnorm
431        | wgpu::TextureFormat::Bc2RgbaUnormSrgb
432        | wgpu::TextureFormat::Bc3RgbaUnorm
433        | wgpu::TextureFormat::Bc3RgbaUnormSrgb
434        | wgpu::TextureFormat::Bc4RUnorm
435        | wgpu::TextureFormat::Bc4RSnorm
436        | wgpu::TextureFormat::Bc5RgUnorm
437        | wgpu::TextureFormat::Bc5RgSnorm
438        | wgpu::TextureFormat::Bc6hRgbUfloat
439        | wgpu::TextureFormat::Bc6hRgbSfloat
440        | wgpu::TextureFormat::Bc7RgbaUnorm
441        | wgpu::TextureFormat::Bc7RgbaUnormSrgb => FLOAT,
442
443        // ETC compressed textures
444        wgpu::TextureFormat::Etc2Rgb8Unorm
445        | wgpu::TextureFormat::Etc2Rgb8UnormSrgb
446        | wgpu::TextureFormat::Etc2Rgb8A1Unorm
447        | wgpu::TextureFormat::Etc2Rgb8A1UnormSrgb
448        | wgpu::TextureFormat::Etc2Rgba8Unorm
449        | wgpu::TextureFormat::Etc2Rgba8UnormSrgb
450        | wgpu::TextureFormat::EacR11Unorm
451        | wgpu::TextureFormat::EacR11Snorm
452        | wgpu::TextureFormat::EacRg11Unorm
453        | wgpu::TextureFormat::EacRg11Snorm => FLOAT,
454
455        // ASTC compressed textures
456        wgpu::TextureFormat::Astc4x4RgbaUnorm
457        | wgpu::TextureFormat::Astc4x4RgbaUnormSrgb
458        | wgpu::TextureFormat::Astc5x4RgbaUnorm
459        | wgpu::TextureFormat::Astc5x4RgbaUnormSrgb
460        | wgpu::TextureFormat::Astc5x5RgbaUnorm
461        | wgpu::TextureFormat::Astc5x5RgbaUnormSrgb
462        | wgpu::TextureFormat::Astc6x5RgbaUnorm
463        | wgpu::TextureFormat::Astc6x5RgbaUnormSrgb
464        | wgpu::TextureFormat::Astc6x6RgbaUnorm
465        | wgpu::TextureFormat::Astc6x6RgbaUnormSrgb
466        | wgpu::TextureFormat::Astc8x5RgbaUnorm
467        | wgpu::TextureFormat::Astc8x5RgbaUnormSrgb
468        | wgpu::TextureFormat::Astc8x6RgbaUnorm
469        | wgpu::TextureFormat::Astc8x6RgbaUnormSrgb
470        | wgpu::TextureFormat::Astc10x5RgbaUnorm
471        | wgpu::TextureFormat::Astc10x5RgbaUnormSrgb
472        | wgpu::TextureFormat::Astc10x6RgbaUnorm
473        | wgpu::TextureFormat::Astc10x6RgbaUnormSrgb
474        | wgpu::TextureFormat::Astc8x8RgbaUnorm
475        | wgpu::TextureFormat::Astc8x8RgbaUnormSrgb
476        | wgpu::TextureFormat::Astc10x8RgbaUnorm
477        | wgpu::TextureFormat::Astc10x8RgbaUnormSrgb
478        | wgpu::TextureFormat::Astc10x10RgbaUnorm
479        | wgpu::TextureFormat::Astc10x10RgbaUnormSrgb
480        | wgpu::TextureFormat::Astc12x10RgbaUnorm
481        | wgpu::TextureFormat::Astc12x10RgbaUnormSrgb
482        | wgpu::TextureFormat::Astc12x12RgbaUnorm
483        | wgpu::TextureFormat::Astc12x12RgbaUnormSrgb => FLOAT,
484
485        // Optional normalized 16-bit-per-channel formats
486        wgpu::TextureFormat::R16Unorm
487        | wgpu::TextureFormat::R16Snorm
488        | wgpu::TextureFormat::Rg16Unorm
489        | wgpu::TextureFormat::Rg16Snorm
490        | wgpu::TextureFormat::Rgba16Unorm
491        | wgpu::TextureFormat::Rgba16Snorm => FLOAT,
492    }
493}