bevy_render/render_resource/
bind_group_layout_entries.rs

1use core::num::NonZero;
2use variadics_please::all_tuples_with_size;
3use wgpu::{BindGroupLayoutEntry, BindingType, ShaderStages};
4
5/// Helper for constructing bind group layouts.
6///
7/// Allows constructing the layout's entries as:
8/// ```ignore (render_device cannot be easily accessed)
9/// let layout = render_device.create_bind_group_layout(
10///     "my_bind_group_layout",
11///     &BindGroupLayoutEntries::with_indices(
12///         // The layout entries will only be visible in the fragment stage
13///         ShaderStages::FRAGMENT,
14///         (
15///             // Screen texture
16///             (2, texture_2d(TextureSampleType::Float { filterable: true })),
17///             // Sampler
18///             (3, sampler(SamplerBindingType::Filtering)),
19///         ),
20///     ),
21/// );
22/// ```
23///
24/// instead of
25///
26/// ```ignore (render_device cannot be easily accessed)
27/// let layout = render_device.create_bind_group_layout(
28///     "my_bind_group_layout",
29///     &[
30///         // Screen texture
31///         BindGroupLayoutEntry {
32///             binding: 2,
33///             visibility: ShaderStages::FRAGMENT,
34///             ty: BindingType::Texture {
35///                 sample_type: TextureSampleType::Float { filterable: true },
36///                 view_dimension: TextureViewDimension::D2,
37///                 multisampled: false,
38///             },
39///             count: None,
40///         },
41///         // Sampler
42///         BindGroupLayoutEntry {
43///             binding: 3,
44///             visibility: ShaderStages::FRAGMENT,
45///             ty: BindingType::Sampler(SamplerBindingType::Filtering),
46///             count: None,
47///         },
48///     ],
49/// );
50/// ```
51///
52/// or
53///
54/// ```ignore (render_device cannot be easily accessed)
55/// render_device.create_bind_group_layout(
56///     "my_bind_group_layout",
57///     &BindGroupLayoutEntries::sequential(
58///         ShaderStages::FRAGMENT,
59///         (
60///             // Screen texture
61///             texture_2d(TextureSampleType::Float { filterable: true }),
62///             // Sampler
63///             sampler(SamplerBindingType::Filtering),
64///         ),
65///     ),
66/// );
67/// ```
68///
69/// instead of
70///
71/// ```ignore (render_device cannot be easily accessed)
72/// let layout = render_device.create_bind_group_layout(
73///     "my_bind_group_layout",
74///     &[
75///         // Screen texture
76///         BindGroupLayoutEntry {
77///             binding: 0,
78///             visibility: ShaderStages::FRAGMENT,
79///             ty: BindingType::Texture {
80///                 sample_type: TextureSampleType::Float { filterable: true },
81///                 view_dimension: TextureViewDimension::D2,
82///                 multisampled: false,
83///             },
84///             count: None,
85///         },
86///         // Sampler
87///         BindGroupLayoutEntry {
88///             binding: 1,
89///             visibility: ShaderStages::FRAGMENT,
90///             ty: BindingType::Sampler(SamplerBindingType::Filtering),
91///             count: None,
92///         },
93///     ],
94/// );
95/// ```
96///
97/// or
98///
99/// ```ignore (render_device cannot be easily accessed)
100/// render_device.create_bind_group_layout(
101///     "my_bind_group_layout",
102///     &BindGroupLayoutEntries::single(
103///         ShaderStages::FRAGMENT,
104///         texture_2d(TextureSampleType::Float { filterable: true }),
105///     ),
106/// );
107/// ```
108///
109/// instead of
110///
111/// ```ignore (render_device cannot be easily accessed)
112/// let layout = render_device.create_bind_group_layout(
113///     "my_bind_group_layout",
114///     &[
115///         BindGroupLayoutEntry {
116///             binding: 0,
117///             visibility: ShaderStages::FRAGMENT,
118///             ty: BindingType::Texture {
119///                 sample_type: TextureSampleType::Float { filterable: true },
120///                 view_dimension: TextureViewDimension::D2,
121///                 multisampled: false,
122///             },
123///             count: None,
124///         },
125///     ],
126/// );
127/// ```
128
129#[derive(Clone, Copy)]
130pub struct BindGroupLayoutEntryBuilder {
131    ty: BindingType,
132    visibility: Option<ShaderStages>,
133    count: Option<NonZero<u32>>,
134}
135
136impl BindGroupLayoutEntryBuilder {
137    pub fn visibility(mut self, visibility: ShaderStages) -> Self {
138        self.visibility = Some(visibility);
139        self
140    }
141
142    pub fn count(mut self, count: NonZero<u32>) -> Self {
143        self.count = Some(count);
144        self
145    }
146
147    pub fn build(&self, binding: u32, default_visibility: ShaderStages) -> BindGroupLayoutEntry {
148        BindGroupLayoutEntry {
149            binding,
150            ty: self.ty,
151            visibility: self.visibility.unwrap_or(default_visibility),
152            count: self.count,
153        }
154    }
155}
156
157pub struct BindGroupLayoutEntries<const N: usize> {
158    entries: [BindGroupLayoutEntry; N],
159}
160
161impl<const N: usize> BindGroupLayoutEntries<N> {
162    #[inline]
163    pub fn sequential(
164        default_visibility: ShaderStages,
165        entries_ext: impl IntoBindGroupLayoutEntryBuilderArray<N>,
166    ) -> Self {
167        let mut i = 0;
168        Self {
169            entries: entries_ext.into_array().map(|entry| {
170                let binding = i;
171                i += 1;
172                entry.build(binding, default_visibility)
173            }),
174        }
175    }
176
177    #[inline]
178    pub fn with_indices(
179        default_visibility: ShaderStages,
180        indexed_entries: impl IntoIndexedBindGroupLayoutEntryBuilderArray<N>,
181    ) -> Self {
182        Self {
183            entries: indexed_entries
184                .into_array()
185                .map(|(binding, entry)| entry.build(binding, default_visibility)),
186        }
187    }
188}
189
190impl BindGroupLayoutEntries<1> {
191    pub fn single(
192        visibility: ShaderStages,
193        resource: impl IntoBindGroupLayoutEntryBuilder,
194    ) -> [BindGroupLayoutEntry; 1] {
195        [resource
196            .into_bind_group_layout_entry_builder()
197            .build(0, visibility)]
198    }
199}
200
201impl<const N: usize> core::ops::Deref for BindGroupLayoutEntries<N> {
202    type Target = [BindGroupLayoutEntry];
203    fn deref(&self) -> &[BindGroupLayoutEntry] {
204        &self.entries
205    }
206}
207
208pub trait IntoBindGroupLayoutEntryBuilder {
209    fn into_bind_group_layout_entry_builder(self) -> BindGroupLayoutEntryBuilder;
210}
211
212impl IntoBindGroupLayoutEntryBuilder for BindingType {
213    fn into_bind_group_layout_entry_builder(self) -> BindGroupLayoutEntryBuilder {
214        BindGroupLayoutEntryBuilder {
215            ty: self,
216            visibility: None,
217            count: None,
218        }
219    }
220}
221
222impl IntoBindGroupLayoutEntryBuilder for BindGroupLayoutEntry {
223    fn into_bind_group_layout_entry_builder(self) -> BindGroupLayoutEntryBuilder {
224        if self.binding != u32::MAX {
225            tracing::warn!("The BindGroupLayoutEntries api ignores the binding index when converting a raw wgpu::BindGroupLayoutEntry. You can ignore this warning by setting it to u32::MAX.");
226        }
227        BindGroupLayoutEntryBuilder {
228            ty: self.ty,
229            visibility: Some(self.visibility),
230            count: self.count,
231        }
232    }
233}
234
235impl IntoBindGroupLayoutEntryBuilder for BindGroupLayoutEntryBuilder {
236    fn into_bind_group_layout_entry_builder(self) -> BindGroupLayoutEntryBuilder {
237        self
238    }
239}
240
241pub trait IntoBindGroupLayoutEntryBuilderArray<const N: usize> {
242    fn into_array(self) -> [BindGroupLayoutEntryBuilder; N];
243}
244macro_rules! impl_to_binding_type_slice {
245    ($N: expr, $(#[$meta:meta])* $(($T: ident, $I: ident)),*) => {
246        $(#[$meta])*
247        impl<$($T: IntoBindGroupLayoutEntryBuilder),*> IntoBindGroupLayoutEntryBuilderArray<$N> for ($($T,)*) {
248            #[inline]
249            fn into_array(self) -> [BindGroupLayoutEntryBuilder; $N] {
250                let ($($I,)*) = self;
251                [$($I.into_bind_group_layout_entry_builder(), )*]
252            }
253        }
254    }
255}
256all_tuples_with_size!(
257    #[doc(fake_variadic)]
258    impl_to_binding_type_slice,
259    1,
260    32,
261    T,
262    s
263);
264
265pub trait IntoIndexedBindGroupLayoutEntryBuilderArray<const N: usize> {
266    fn into_array(self) -> [(u32, BindGroupLayoutEntryBuilder); N];
267}
268macro_rules! impl_to_indexed_binding_type_slice {
269    ($N: expr, $(($T: ident, $S: ident, $I: ident)),*) => {
270        impl<$($T: IntoBindGroupLayoutEntryBuilder),*> IntoIndexedBindGroupLayoutEntryBuilderArray<$N> for ($((u32, $T),)*) {
271            #[inline]
272            fn into_array(self) -> [(u32, BindGroupLayoutEntryBuilder); $N] {
273                let ($(($S, $I),)*) = self;
274                [$(($S, $I.into_bind_group_layout_entry_builder())), *]
275            }
276        }
277    }
278}
279all_tuples_with_size!(impl_to_indexed_binding_type_slice, 1, 32, T, n, s);
280
281impl<const N: usize> IntoBindGroupLayoutEntryBuilderArray<N> for [BindGroupLayoutEntry; N] {
282    fn into_array(self) -> [BindGroupLayoutEntryBuilder; N] {
283        self.map(IntoBindGroupLayoutEntryBuilder::into_bind_group_layout_entry_builder)
284    }
285}
286
287pub struct DynamicBindGroupLayoutEntries {
288    default_visibility: ShaderStages,
289    entries: Vec<BindGroupLayoutEntry>,
290}
291
292impl DynamicBindGroupLayoutEntries {
293    pub fn sequential<const N: usize>(
294        default_visibility: ShaderStages,
295        entries: impl IntoBindGroupLayoutEntryBuilderArray<N>,
296    ) -> Self {
297        Self {
298            default_visibility,
299            entries: entries
300                .into_array()
301                .into_iter()
302                .enumerate()
303                .map(|(ix, resource)| resource.build(ix as u32, default_visibility))
304                .collect(),
305        }
306    }
307
308    pub fn extend_sequential<const N: usize>(
309        mut self,
310        entries: impl IntoBindGroupLayoutEntryBuilderArray<N>,
311    ) -> Self {
312        let start = self.entries.last().unwrap().binding + 1;
313        self.entries.extend(
314            entries
315                .into_array()
316                .into_iter()
317                .enumerate()
318                .map(|(ix, resource)| resource.build(start + ix as u32, self.default_visibility)),
319        );
320        self
321    }
322
323    pub fn new_with_indices<const N: usize>(
324        default_visibility: ShaderStages,
325        entries: impl IntoIndexedBindGroupLayoutEntryBuilderArray<N>,
326    ) -> Self {
327        Self {
328            default_visibility,
329            entries: entries
330                .into_array()
331                .into_iter()
332                .map(|(binding, resource)| resource.build(binding, default_visibility))
333                .collect(),
334        }
335    }
336
337    pub fn new(default_visibility: ShaderStages) -> Self {
338        Self {
339            default_visibility,
340            entries: Vec::new(),
341        }
342    }
343
344    pub fn extend_with_indices<const N: usize>(
345        mut self,
346        entries: impl IntoIndexedBindGroupLayoutEntryBuilderArray<N>,
347    ) -> Self {
348        self.entries.extend(
349            entries
350                .into_array()
351                .into_iter()
352                .map(|(binding, resource)| resource.build(binding, self.default_visibility)),
353        );
354        self
355    }
356}
357
358impl core::ops::Deref for DynamicBindGroupLayoutEntries {
359    type Target = [BindGroupLayoutEntry];
360
361    fn deref(&self) -> &[BindGroupLayoutEntry] {
362        &self.entries
363    }
364}
365
366pub mod binding_types {
367    use crate::render_resource::{
368        BufferBindingType, SamplerBindingType, TextureSampleType, TextureViewDimension,
369    };
370    use core::num::NonZero;
371    use encase::ShaderType;
372    use wgpu::{StorageTextureAccess, TextureFormat};
373
374    use super::*;
375
376    pub fn storage_buffer<T: ShaderType>(has_dynamic_offset: bool) -> BindGroupLayoutEntryBuilder {
377        storage_buffer_sized(has_dynamic_offset, Some(T::min_size()))
378    }
379
380    pub fn storage_buffer_sized(
381        has_dynamic_offset: bool,
382        min_binding_size: Option<NonZero<u64>>,
383    ) -> BindGroupLayoutEntryBuilder {
384        BindingType::Buffer {
385            ty: BufferBindingType::Storage { read_only: false },
386            has_dynamic_offset,
387            min_binding_size,
388        }
389        .into_bind_group_layout_entry_builder()
390    }
391
392    pub fn storage_buffer_read_only<T: ShaderType>(
393        has_dynamic_offset: bool,
394    ) -> BindGroupLayoutEntryBuilder {
395        storage_buffer_read_only_sized(has_dynamic_offset, Some(T::min_size()))
396    }
397
398    pub fn storage_buffer_read_only_sized(
399        has_dynamic_offset: bool,
400        min_binding_size: Option<NonZero<u64>>,
401    ) -> BindGroupLayoutEntryBuilder {
402        BindingType::Buffer {
403            ty: BufferBindingType::Storage { read_only: true },
404            has_dynamic_offset,
405            min_binding_size,
406        }
407        .into_bind_group_layout_entry_builder()
408    }
409
410    pub fn uniform_buffer<T: ShaderType>(has_dynamic_offset: bool) -> BindGroupLayoutEntryBuilder {
411        uniform_buffer_sized(has_dynamic_offset, Some(T::min_size()))
412    }
413
414    pub fn uniform_buffer_sized(
415        has_dynamic_offset: bool,
416        min_binding_size: Option<NonZero<u64>>,
417    ) -> BindGroupLayoutEntryBuilder {
418        BindingType::Buffer {
419            ty: BufferBindingType::Uniform,
420            has_dynamic_offset,
421            min_binding_size,
422        }
423        .into_bind_group_layout_entry_builder()
424    }
425
426    pub fn texture_1d(sample_type: TextureSampleType) -> BindGroupLayoutEntryBuilder {
427        BindingType::Texture {
428            sample_type,
429            view_dimension: TextureViewDimension::D1,
430            multisampled: false,
431        }
432        .into_bind_group_layout_entry_builder()
433    }
434
435    pub fn texture_2d(sample_type: TextureSampleType) -> BindGroupLayoutEntryBuilder {
436        BindingType::Texture {
437            sample_type,
438            view_dimension: TextureViewDimension::D2,
439            multisampled: false,
440        }
441        .into_bind_group_layout_entry_builder()
442    }
443
444    pub fn texture_2d_multisampled(sample_type: TextureSampleType) -> BindGroupLayoutEntryBuilder {
445        BindingType::Texture {
446            sample_type,
447            view_dimension: TextureViewDimension::D2,
448            multisampled: true,
449        }
450        .into_bind_group_layout_entry_builder()
451    }
452
453    pub fn texture_2d_array(sample_type: TextureSampleType) -> BindGroupLayoutEntryBuilder {
454        BindingType::Texture {
455            sample_type,
456            view_dimension: TextureViewDimension::D2Array,
457            multisampled: false,
458        }
459        .into_bind_group_layout_entry_builder()
460    }
461
462    pub fn texture_2d_array_multisampled(
463        sample_type: TextureSampleType,
464    ) -> BindGroupLayoutEntryBuilder {
465        BindingType::Texture {
466            sample_type,
467            view_dimension: TextureViewDimension::D2Array,
468            multisampled: true,
469        }
470        .into_bind_group_layout_entry_builder()
471    }
472
473    pub fn texture_depth_2d() -> BindGroupLayoutEntryBuilder {
474        texture_2d(TextureSampleType::Depth).into_bind_group_layout_entry_builder()
475    }
476
477    pub fn texture_depth_2d_multisampled() -> BindGroupLayoutEntryBuilder {
478        texture_2d_multisampled(TextureSampleType::Depth).into_bind_group_layout_entry_builder()
479    }
480
481    pub fn texture_cube(sample_type: TextureSampleType) -> BindGroupLayoutEntryBuilder {
482        BindingType::Texture {
483            sample_type,
484            view_dimension: TextureViewDimension::Cube,
485            multisampled: false,
486        }
487        .into_bind_group_layout_entry_builder()
488    }
489
490    pub fn texture_cube_multisampled(
491        sample_type: TextureSampleType,
492    ) -> BindGroupLayoutEntryBuilder {
493        BindingType::Texture {
494            sample_type,
495            view_dimension: TextureViewDimension::Cube,
496            multisampled: true,
497        }
498        .into_bind_group_layout_entry_builder()
499    }
500
501    pub fn texture_cube_array(sample_type: TextureSampleType) -> BindGroupLayoutEntryBuilder {
502        BindingType::Texture {
503            sample_type,
504            view_dimension: TextureViewDimension::CubeArray,
505            multisampled: false,
506        }
507        .into_bind_group_layout_entry_builder()
508    }
509
510    pub fn texture_cube_array_multisampled(
511        sample_type: TextureSampleType,
512    ) -> BindGroupLayoutEntryBuilder {
513        BindingType::Texture {
514            sample_type,
515            view_dimension: TextureViewDimension::CubeArray,
516            multisampled: true,
517        }
518        .into_bind_group_layout_entry_builder()
519    }
520
521    pub fn texture_3d(sample_type: TextureSampleType) -> BindGroupLayoutEntryBuilder {
522        BindingType::Texture {
523            sample_type,
524            view_dimension: TextureViewDimension::D3,
525            multisampled: false,
526        }
527        .into_bind_group_layout_entry_builder()
528    }
529
530    pub fn texture_3d_multisampled(sample_type: TextureSampleType) -> BindGroupLayoutEntryBuilder {
531        BindingType::Texture {
532            sample_type,
533            view_dimension: TextureViewDimension::D3,
534            multisampled: true,
535        }
536        .into_bind_group_layout_entry_builder()
537    }
538
539    pub fn sampler(sampler_binding_type: SamplerBindingType) -> BindGroupLayoutEntryBuilder {
540        BindingType::Sampler(sampler_binding_type).into_bind_group_layout_entry_builder()
541    }
542
543    pub fn texture_storage_2d(
544        format: TextureFormat,
545        access: StorageTextureAccess,
546    ) -> BindGroupLayoutEntryBuilder {
547        BindingType::StorageTexture {
548            access,
549            format,
550            view_dimension: TextureViewDimension::D2,
551        }
552        .into_bind_group_layout_entry_builder()
553    }
554
555    pub fn texture_storage_2d_array(
556        format: TextureFormat,
557        access: StorageTextureAccess,
558    ) -> BindGroupLayoutEntryBuilder {
559        BindingType::StorageTexture {
560            access,
561            format,
562            view_dimension: TextureViewDimension::D2Array,
563        }
564        .into_bind_group_layout_entry_builder()
565    }
566
567    pub fn texture_storage_3d(
568        format: TextureFormat,
569        access: StorageTextureAccess,
570    ) -> BindGroupLayoutEntryBuilder {
571        BindingType::StorageTexture {
572            access,
573            format,
574            view_dimension: TextureViewDimension::D3,
575        }
576        .into_bind_group_layout_entry_builder()
577    }
578
579    pub fn acceleration_structure() -> BindGroupLayoutEntryBuilder {
580        BindingType::AccelerationStructure {
581            vertex_return: false,
582        }
583        .into_bind_group_layout_entry_builder()
584    }
585
586    pub fn acceleration_structure_vertex_return() -> BindGroupLayoutEntryBuilder {
587        BindingType::AccelerationStructure {
588            vertex_return: true,
589        }
590        .into_bind_group_layout_entry_builder()
591    }
592}