bevy_render/render_resource/
bind_group.rs

1use crate::{
2    define_atomic_id,
3    render_asset::RenderAssets,
4    render_resource::{resource_macros::*, BindGroupLayout, Buffer, Sampler, TextureView},
5    renderer::RenderDevice,
6    texture::{FallbackImage, GpuImage},
7};
8pub use bevy_render_macros::AsBindGroup;
9use encase::ShaderType;
10use std::ops::Deref;
11use thiserror::Error;
12use wgpu::{BindGroupEntry, BindGroupLayoutEntry, BindingResource};
13
14define_atomic_id!(BindGroupId);
15render_resource_wrapper!(ErasedBindGroup, wgpu::BindGroup);
16
17/// Bind groups are responsible for binding render resources (e.g. buffers, textures, samplers)
18/// to a [`TrackedRenderPass`](crate::render_phase::TrackedRenderPass).
19/// This makes them accessible in the pipeline (shaders) as uniforms.
20///
21/// May be converted from and dereferences to a wgpu [`BindGroup`](wgpu::BindGroup).
22/// Can be created via [`RenderDevice::create_bind_group`](RenderDevice::create_bind_group).
23#[derive(Clone, Debug)]
24pub struct BindGroup {
25    id: BindGroupId,
26    value: ErasedBindGroup,
27}
28
29impl BindGroup {
30    /// Returns the [`BindGroupId`].
31    #[inline]
32    pub fn id(&self) -> BindGroupId {
33        self.id
34    }
35}
36
37impl From<wgpu::BindGroup> for BindGroup {
38    fn from(value: wgpu::BindGroup) -> Self {
39        BindGroup {
40            id: BindGroupId::new(),
41            value: ErasedBindGroup::new(value),
42        }
43    }
44}
45
46impl Deref for BindGroup {
47    type Target = wgpu::BindGroup;
48
49    #[inline]
50    fn deref(&self) -> &Self::Target {
51        &self.value
52    }
53}
54
55/// Converts a value to a [`BindGroup`] with a given [`BindGroupLayout`], which can then be used in Bevy shaders.
56/// This trait can be derived (and generally should be). Read on for details and examples.
57///
58/// This is an opinionated trait that is intended to make it easy to generically
59/// convert a type into a [`BindGroup`]. It provides access to specific render resources,
60/// such as [`RenderAssets<GpuImage>`] and [`FallbackImage`]. If a type has a [`Handle<Image>`](bevy_asset::Handle),
61/// these can be used to retrieve the corresponding [`Texture`](crate::render_resource::Texture) resource.
62///
63/// [`AsBindGroup::as_bind_group`] is intended to be called once, then the result cached somewhere. It is generally
64/// ok to do "expensive" work here, such as creating a [`Buffer`] for a uniform.
65///
66/// If for some reason a [`BindGroup`] cannot be created yet (for example, the [`Texture`](crate::render_resource::Texture)
67/// for an [`Image`](crate::texture::Image) hasn't loaded yet), just return [`AsBindGroupError::RetryNextUpdate`], which signals that the caller
68/// should retry again later.
69///
70/// # Deriving
71///
72/// This trait can be derived. Field attributes like `uniform` and `texture` are used to define which fields should be bindings,
73/// what their binding type is, and what index they should be bound at:
74///
75/// ```
76/// # use bevy_render::{render_resource::*, texture::Image};
77/// # use bevy_color::LinearRgba;
78/// # use bevy_asset::Handle;
79/// #[derive(AsBindGroup)]
80/// struct CoolMaterial {
81///     #[uniform(0)]
82///     color: LinearRgba,
83///     #[texture(1)]
84///     #[sampler(2)]
85///     color_texture: Handle<Image>,
86///     #[storage(3, read_only)]
87///     values: Vec<f32>,
88///     #[storage(4, read_only, buffer)]
89///     buffer: Buffer,
90///     #[storage_texture(5)]
91///     storage_texture: Handle<Image>,
92/// }
93/// ```
94///
95/// In WGSL shaders, the binding would look like this:
96///
97/// ```wgsl
98/// @group(2) @binding(0) var<uniform> color: vec4<f32>;
99/// @group(2) @binding(1) var color_texture: texture_2d<f32>;
100/// @group(2) @binding(2) var color_sampler: sampler;
101/// @group(2) @binding(3) var<storage> values: array<f32>;
102/// @group(2) @binding(5) var storage_texture: texture_storage_2d<rgba8unorm, read_write>;
103/// ```
104/// Note that the "group" index is determined by the usage context. It is not defined in [`AsBindGroup`]. For example, in Bevy material bind groups
105/// are generally bound to group 2.
106///
107/// The following field-level attributes are supported:
108///
109/// * `uniform(BINDING_INDEX)`
110///     * The field will be converted to a shader-compatible type using the [`ShaderType`] trait, written to a [`Buffer`], and bound as a uniform.
111///     [`ShaderType`] is implemented for most math types already, such as [`f32`], [`Vec4`](bevy_math::Vec4), and
112///   [`LinearRgba`](bevy_color::LinearRgba). It can also be derived for custom structs.
113///
114/// * `texture(BINDING_INDEX, arguments)`
115///     * This field's [`Handle<Image>`](bevy_asset::Handle) will be used to look up the matching [`Texture`](crate::render_resource::Texture)
116///     GPU resource, which will be bound as a texture in shaders. The field will be assumed to implement [`Into<Option<Handle<Image>>>`]. In practice,
117///     most fields should be a [`Handle<Image>`](bevy_asset::Handle) or [`Option<Handle<Image>>`]. If the value of an [`Option<Handle<Image>>`] is
118///     [`None`], the [`FallbackImage`] resource will be used instead. This attribute can be used in conjunction with a `sampler` binding attribute
119///    (with a different binding index) if a binding of the sampler for the [`Image`](crate::texture::Image) is also required.
120///
121/// | Arguments             | Values                                                                  | Default              |
122/// |-----------------------|-------------------------------------------------------------------------|----------------------|
123/// | `dimension` = "..."   | `"1d"`, `"2d"`, `"2d_array"`, `"3d"`, `"cube"`, `"cube_array"`          | `"2d"`               |
124/// | `sample_type` = "..." | `"float"`, `"depth"`, `"s_int"` or `"u_int"`                            | `"float"`            |
125/// | `filterable` = ...    | `true`, `false`                                                         | `true`               |
126/// | `multisampled` = ...  | `true`, `false`                                                         | `false`              |
127/// | `visibility(...)`     | `all`, `none`, or a list-combination of `vertex`, `fragment`, `compute` | `vertex`, `fragment` |
128///
129/// * `storage_texture(BINDING_INDEX, arguments)`
130///     * This field's [`Handle<Image>`](bevy_asset::Handle) will be used to look up the matching [`Texture`](crate::render_resource::Texture)
131///     GPU resource, which will be bound as a storage texture in shaders. The field will be assumed to implement [`Into<Option<Handle<Image>>>`]. In practice,
132///     most fields should be a [`Handle<Image>`](bevy_asset::Handle) or [`Option<Handle<Image>>`]. If the value of an [`Option<Handle<Image>>`] is
133///     [`None`], the [`FallbackImage`] resource will be used instead.
134///
135/// | Arguments              | Values                                                                                     | Default       |
136/// |------------------------|--------------------------------------------------------------------------------------------|---------------|
137/// | `dimension` = "..."    | `"1d"`, `"2d"`, `"2d_array"`, `"3d"`, `"cube"`, `"cube_array"`                             | `"2d"`        |
138/// | `image_format` = ...   | any member of [`TextureFormat`](crate::render_resource::TextureFormat)                     | `Rgba8Unorm`  |
139/// | `access` = ...         | any member of [`StorageTextureAccess`](crate::render_resource::StorageTextureAccess)       | `ReadWrite`   |
140/// | `visibility(...)`      | `all`, `none`, or a list-combination of `vertex`, `fragment`, `compute`                    | `compute`     |
141///
142/// * `sampler(BINDING_INDEX, arguments)`
143///     * This field's [`Handle<Image>`](bevy_asset::Handle) will be used to look up the matching [`Sampler`] GPU
144///     resource, which will be bound as a sampler in shaders. The field will be assumed to implement [`Into<Option<Handle<Image>>>`]. In practice,
145///     most fields should be a [`Handle<Image>`](bevy_asset::Handle) or [`Option<Handle<Image>>`]. If the value of an [`Option<Handle<Image>>`] is
146///     [`None`], the [`FallbackImage`] resource will be used instead. This attribute can be used in conjunction with a `texture` binding attribute
147///     (with a different binding index) if a binding of the texture for the [`Image`](crate::texture::Image) is also required.
148///
149/// | Arguments              | Values                                                                  | Default                |
150/// |------------------------|-------------------------------------------------------------------------|------------------------|
151/// | `sampler_type` = "..." | `"filtering"`, `"non_filtering"`, `"comparison"`.                       |  `"filtering"`         |
152/// | `visibility(...)`      | `all`, `none`, or a list-combination of `vertex`, `fragment`, `compute` |   `vertex`, `fragment` |
153///
154/// * `storage(BINDING_INDEX, arguments)`
155///     * The field will be converted to a shader-compatible type using the [`ShaderType`] trait, written to a [`Buffer`], and bound as a storage buffer.
156///     * It supports and optional `read_only` parameter. Defaults to false if not present.
157///
158/// | Arguments              | Values                                                                  | Default              |
159/// |------------------------|-------------------------------------------------------------------------|----------------------|
160/// | `visibility(...)`      | `all`, `none`, or a list-combination of `vertex`, `fragment`, `compute` | `vertex`, `fragment` |
161/// | `read_only`            | if present then value is true, otherwise false                          | `false`              |
162///
163/// Note that fields without field-level binding attributes will be ignored.
164/// ```
165/// # use bevy_render::{render_resource::AsBindGroup};
166/// # use bevy_color::LinearRgba;
167/// # use bevy_asset::Handle;
168/// #[derive(AsBindGroup)]
169/// struct CoolMaterial {
170///     #[uniform(0)]
171///     color: LinearRgba,
172///     this_field_is_ignored: String,
173/// }
174/// ```
175///
176///  As mentioned above, [`Option<Handle<Image>>`] is also supported:
177/// ```
178/// # use bevy_render::{render_resource::AsBindGroup, texture::Image};
179/// # use bevy_color::LinearRgba;
180/// # use bevy_asset::Handle;
181/// #[derive(AsBindGroup)]
182/// struct CoolMaterial {
183///     #[uniform(0)]
184///     color: LinearRgba,
185///     #[texture(1)]
186///     #[sampler(2)]
187///     color_texture: Option<Handle<Image>>,
188/// }
189/// ```
190/// This is useful if you want a texture to be optional. When the value is [`None`], the [`FallbackImage`] will be used for the binding instead, which defaults
191/// to "pure white".
192///
193/// Field uniforms with the same index will be combined into a single binding:
194/// ```
195/// # use bevy_render::{render_resource::AsBindGroup};
196/// # use bevy_color::LinearRgba;
197/// #[derive(AsBindGroup)]
198/// struct CoolMaterial {
199///     #[uniform(0)]
200///     color: LinearRgba,
201///     #[uniform(0)]
202///     roughness: f32,
203/// }
204/// ```
205///
206/// In WGSL shaders, the binding would look like this:
207/// ```wgsl
208/// struct CoolMaterial {
209///     color: vec4<f32>,
210///     roughness: f32,
211/// };
212///
213/// @group(2) @binding(0) var<uniform> material: CoolMaterial;
214/// ```
215///
216/// Some less common scenarios will require "struct-level" attributes. These are the currently supported struct-level attributes:
217/// * `uniform(BINDING_INDEX, ConvertedShaderType)`
218///     * This also creates a [`Buffer`] using [`ShaderType`] and binds it as a uniform, much
219///     much like the field-level `uniform` attribute. The difference is that the entire [`AsBindGroup`] value is converted to `ConvertedShaderType`,
220///     which must implement [`ShaderType`], instead of a specific field implementing [`ShaderType`]. This is useful if more complicated conversion
221///     logic is required. The conversion is done using the [`AsBindGroupShaderType<ConvertedShaderType>`] trait, which is automatically implemented
222///     if `&Self` implements [`Into<ConvertedShaderType>`]. Only use [`AsBindGroupShaderType`] if access to resources like [`RenderAssets<GpuImage>`] is
223///     required.
224/// * `bind_group_data(DataType)`
225///     * The [`AsBindGroup`] type will be converted to some `DataType` using [`Into<DataType>`] and stored
226///     as [`AsBindGroup::Data`] as part of the [`AsBindGroup::as_bind_group`] call. This is useful if data needs to be stored alongside
227///     the generated bind group, such as a unique identifier for a material's bind group. The most common use case for this attribute
228///     is "shader pipeline specialization". See [`SpecializedRenderPipeline`](crate::render_resource::SpecializedRenderPipeline).
229///
230/// The previous `CoolMaterial` example illustrating "combining multiple field-level uniform attributes with the same binding index" can
231/// also be equivalently represented with a single struct-level uniform attribute:
232/// ```
233/// # use bevy_render::{render_resource::{AsBindGroup, ShaderType}};
234/// # use bevy_color::LinearRgba;
235/// #[derive(AsBindGroup)]
236/// #[uniform(0, CoolMaterialUniform)]
237/// struct CoolMaterial {
238///     color: LinearRgba,
239///     roughness: f32,
240/// }
241///
242/// #[derive(ShaderType)]
243/// struct CoolMaterialUniform {
244///     color: LinearRgba,
245///     roughness: f32,
246/// }
247///
248/// impl From<&CoolMaterial> for CoolMaterialUniform {
249///     fn from(material: &CoolMaterial) -> CoolMaterialUniform {
250///         CoolMaterialUniform {
251///             color: material.color,
252///             roughness: material.roughness,
253///         }
254///     }
255/// }
256/// ```
257///
258/// Setting `bind_group_data` looks like this:
259/// ```
260/// # use bevy_render::{render_resource::AsBindGroup};
261/// # use bevy_color::LinearRgba;
262/// #[derive(AsBindGroup)]
263/// #[bind_group_data(CoolMaterialKey)]
264/// struct CoolMaterial {
265///     #[uniform(0)]
266///     color: LinearRgba,
267///     is_shaded: bool,
268/// }
269///
270/// #[derive(Copy, Clone, Hash, Eq, PartialEq)]
271/// struct CoolMaterialKey {
272///     is_shaded: bool,
273/// }
274///
275/// impl From<&CoolMaterial> for CoolMaterialKey {
276///     fn from(material: &CoolMaterial) -> CoolMaterialKey {
277///         CoolMaterialKey {
278///             is_shaded: material.is_shaded,
279///         }
280///     }
281/// }
282/// ```
283pub trait AsBindGroup {
284    /// Data that will be stored alongside the "prepared" bind group.
285    type Data: Send + Sync;
286
287    /// label
288    fn label() -> Option<&'static str> {
289        None
290    }
291
292    /// Creates a bind group for `self` matching the layout defined in [`AsBindGroup::bind_group_layout`].
293    fn as_bind_group(
294        &self,
295        layout: &BindGroupLayout,
296        render_device: &RenderDevice,
297        images: &RenderAssets<GpuImage>,
298        fallback_image: &FallbackImage,
299    ) -> Result<PreparedBindGroup<Self::Data>, AsBindGroupError> {
300        let UnpreparedBindGroup { bindings, data } =
301            Self::unprepared_bind_group(self, layout, render_device, images, fallback_image)?;
302
303        let entries = bindings
304            .iter()
305            .map(|(index, binding)| BindGroupEntry {
306                binding: *index,
307                resource: binding.get_binding(),
308            })
309            .collect::<Vec<_>>();
310
311        let bind_group = render_device.create_bind_group(Self::label(), layout, &entries);
312
313        Ok(PreparedBindGroup {
314            bindings,
315            bind_group,
316            data,
317        })
318    }
319
320    /// Returns a vec of (binding index, `OwnedBindingResource`).
321    /// In cases where `OwnedBindingResource` is not available (as for bindless texture arrays currently),
322    /// an implementor may define `as_bind_group` directly. This may prevent certain features
323    /// from working correctly.
324    fn unprepared_bind_group(
325        &self,
326        layout: &BindGroupLayout,
327        render_device: &RenderDevice,
328        images: &RenderAssets<GpuImage>,
329        fallback_image: &FallbackImage,
330    ) -> Result<UnpreparedBindGroup<Self::Data>, AsBindGroupError>;
331
332    /// Creates the bind group layout matching all bind groups returned by [`AsBindGroup::as_bind_group`]
333    fn bind_group_layout(render_device: &RenderDevice) -> BindGroupLayout
334    where
335        Self: Sized,
336    {
337        render_device.create_bind_group_layout(
338            Self::label(),
339            &Self::bind_group_layout_entries(render_device),
340        )
341    }
342
343    /// Returns a vec of bind group layout entries
344    fn bind_group_layout_entries(render_device: &RenderDevice) -> Vec<BindGroupLayoutEntry>
345    where
346        Self: Sized;
347}
348
349/// An error that occurs during [`AsBindGroup::as_bind_group`] calls.
350#[derive(Debug, Error)]
351pub enum AsBindGroupError {
352    /// The bind group could not be generated. Try again next frame.
353    #[error("The bind group could not be generated")]
354    RetryNextUpdate,
355}
356
357/// A prepared bind group returned as a result of [`AsBindGroup::as_bind_group`].
358pub struct PreparedBindGroup<T> {
359    pub bindings: Vec<(u32, OwnedBindingResource)>,
360    pub bind_group: BindGroup,
361    pub data: T,
362}
363
364/// a map containing `OwnedBindingResource`s, keyed by the target binding index
365pub struct UnpreparedBindGroup<T> {
366    pub bindings: Vec<(u32, OwnedBindingResource)>,
367    pub data: T,
368}
369
370/// An owned binding resource of any type (ex: a [`Buffer`], [`TextureView`], etc).
371/// This is used by types like [`PreparedBindGroup`] to hold a single list of all
372/// render resources used by bindings.
373#[derive(Debug)]
374pub enum OwnedBindingResource {
375    Buffer(Buffer),
376    TextureView(TextureView),
377    Sampler(Sampler),
378}
379
380impl OwnedBindingResource {
381    pub fn get_binding(&self) -> BindingResource {
382        match self {
383            OwnedBindingResource::Buffer(buffer) => buffer.as_entire_binding(),
384            OwnedBindingResource::TextureView(view) => BindingResource::TextureView(view),
385            OwnedBindingResource::Sampler(sampler) => BindingResource::Sampler(sampler),
386        }
387    }
388}
389
390/// Converts a value to a [`ShaderType`] for use in a bind group.
391/// This is automatically implemented for references that implement [`Into`].
392/// Generally normal [`Into`] / [`From`] impls should be preferred, but
393/// sometimes additional runtime metadata is required.
394/// This exists largely to make some [`AsBindGroup`] use cases easier.
395pub trait AsBindGroupShaderType<T: ShaderType> {
396    /// Return the `T` [`ShaderType`] for `self`. When used in [`AsBindGroup`]
397    /// derives, it is safe to assume that all images in `self` exist.
398    fn as_bind_group_shader_type(&self, images: &RenderAssets<GpuImage>) -> T;
399}
400
401impl<T, U: ShaderType> AsBindGroupShaderType<U> for T
402where
403    for<'a> &'a T: Into<U>,
404{
405    #[inline]
406    fn as_bind_group_shader_type(&self, _images: &RenderAssets<GpuImage>) -> U {
407        self.into()
408    }
409}
410
411#[cfg(test)]
412mod test {
413    use super::*;
414    use crate::{self as bevy_render, prelude::Image};
415    use bevy_asset::Handle;
416
417    #[test]
418    fn texture_visibility() {
419        #[derive(AsBindGroup)]
420        pub struct TextureVisibilityTest {
421            #[texture(0, visibility(all))]
422            pub all: Handle<Image>,
423            #[texture(1, visibility(none))]
424            pub none: Handle<Image>,
425            #[texture(2, visibility(fragment))]
426            pub fragment: Handle<Image>,
427            #[texture(3, visibility(vertex))]
428            pub vertex: Handle<Image>,
429            #[texture(4, visibility(compute))]
430            pub compute: Handle<Image>,
431            #[texture(5, visibility(vertex, fragment))]
432            pub vertex_fragment: Handle<Image>,
433            #[texture(6, visibility(vertex, compute))]
434            pub vertex_compute: Handle<Image>,
435            #[texture(7, visibility(fragment, compute))]
436            pub fragment_compute: Handle<Image>,
437            #[texture(8, visibility(vertex, fragment, compute))]
438            pub vertex_fragment_compute: Handle<Image>,
439        }
440    }
441}