Skip to main content

bevy_render/render_resource/
bind_group.rs

1use crate::{
2    material_bind_groups::FallbackBuffer,
3    render_asset::RenderAssets,
4    render_resource::{BindGroupLayout, Buffer, PipelineCache, Sampler, TextureView},
5    renderer::{wgpu_wrapper, RenderDevice},
6    storage::{GpuShaderBuffer, ShaderBuffer},
7    texture::GpuImage,
8};
9use bevy_asset::Handle;
10use bevy_derive::{Deref, DerefMut};
11use bevy_ecs::system::{SystemParam, SystemParamItem};
12use bevy_material::descriptor::BindGroupLayoutDescriptor;
13pub use bevy_render_macros::AsBindGroup;
14use bevy_utils::define_atomic_id;
15use core::ops::Deref;
16use encase::ShaderType;
17use std::ops::Range;
18use thiserror::Error;
19use wgpu::{
20    BindGroupEntry, BindGroupLayoutEntry, BindingResource, BufferBinding, SamplerBindingType,
21    TextureViewDimension,
22};
23
24use super::{BindlessDescriptor, BindlessSlabResourceLimit};
25
26define_atomic_id!(BindGroupId);
27
28wgpu_wrapper! {
29    #[derive(Clone, Debug)]
30    struct WgpuBindGroup(wgpu::BindGroup);
31}
32
33/// Bind groups are responsible for binding render resources (e.g. buffers, textures, samplers)
34/// to a [`TrackedRenderPass`](crate::render_phase::TrackedRenderPass).
35/// This makes them accessible in the pipeline (shaders) as uniforms.
36///
37/// This is a lightweight thread-safe wrapper around wgpu's own [`BindGroup`](wgpu::BindGroup),
38/// which can be cloned as needed to workaround lifetime management issues. It may be converted
39/// from and dereferences to wgpu's [`BindGroup`](wgpu::BindGroup).
40///
41/// Can be created via [`RenderDevice::create_bind_group`](RenderDevice::create_bind_group).
42#[derive(Clone, Debug)]
43pub struct BindGroup {
44    id: BindGroupId,
45    value: WgpuBindGroup,
46}
47
48impl BindGroup {
49    /// Returns the [`BindGroupId`] representing the unique ID of the bind group.
50    #[inline]
51    pub fn id(&self) -> BindGroupId {
52        self.id
53    }
54}
55
56impl PartialEq for BindGroup {
57    fn eq(&self, other: &Self) -> bool {
58        self.id == other.id
59    }
60}
61
62impl Eq for BindGroup {}
63
64impl core::hash::Hash for BindGroup {
65    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
66        self.id.0.hash(state);
67    }
68}
69
70impl From<wgpu::BindGroup> for BindGroup {
71    fn from(value: wgpu::BindGroup) -> Self {
72        BindGroup {
73            id: BindGroupId::new(),
74            value: WgpuBindGroup::new(value),
75        }
76    }
77}
78
79impl<'a> From<&'a BindGroup> for Option<&'a wgpu::BindGroup> {
80    fn from(value: &'a BindGroup) -> Self {
81        Some(value.deref())
82    }
83}
84
85impl<'a> From<&'a mut BindGroup> for Option<&'a wgpu::BindGroup> {
86    fn from(value: &'a mut BindGroup) -> Self {
87        Some(&*value)
88    }
89}
90
91impl Deref for BindGroup {
92    type Target = wgpu::BindGroup;
93
94    #[inline]
95    fn deref(&self) -> &Self::Target {
96        &self.value
97    }
98}
99
100/// Converts a value to a [`BindGroup`] with a given [`BindGroupLayout`], which can then be used in Bevy shaders.
101/// This trait can be derived (and generally should be). Read on for details and examples.
102///
103/// This is an opinionated trait that is intended to make it easy to generically
104/// convert a type into a [`BindGroup`]. It provides access to specific render resources,
105/// such as [`RenderAssets<GpuImage>`] and [`crate::texture::FallbackImage`]. If a type has a [`Handle<Image>`](bevy_asset::Handle),
106/// these can be used to retrieve the corresponding [`Texture`](crate::render_resource::Texture) resource.
107///
108/// [`AsBindGroup::as_bind_group`] is intended to be called once, then the result cached somewhere. It is generally
109/// ok to do "expensive" work here, such as creating a [`Buffer`] for a uniform.
110///
111/// If for some reason a [`BindGroup`] cannot be created yet (for example, the [`Texture`](crate::render_resource::Texture)
112/// for an [`Image`](bevy_image::Image) hasn't loaded yet), just return [`AsBindGroupError::RetryNextUpdate`], which signals that the caller
113/// should retry again later.
114///
115/// # Deriving
116///
117/// This trait can be derived. Field attributes like `uniform` and `texture` are used to define which fields should be bindings,
118/// what their binding type is, and what index they should be bound at:
119///
120/// ```
121/// # use bevy_render::render_resource::*;
122/// # use bevy_image::Image;
123/// # use bevy_color::LinearRgba;
124/// # use bevy_asset::Handle;
125/// # use bevy_render::storage::ShaderBuffer;
126///
127/// #[derive(AsBindGroup)]
128/// struct CoolMaterial {
129///     #[uniform(0)]
130///     color: LinearRgba,
131///     #[texture(1)]
132///     #[sampler(2)]
133///     color_texture: Handle<Image>,
134///     #[storage(3, read_only)]
135///     storage_buffer: Handle<ShaderBuffer>,
136///     #[storage(4, read_only, buffer)]
137///     raw_buffer: Buffer,
138///     #[storage_texture(5)]
139///     storage_texture: Handle<Image>,
140/// }
141/// ```
142///
143/// In WGSL shaders, the binding would look like this:
144///
145/// ```wgsl
146/// @group(#{MATERIAL_BIND_GROUP}) @binding(0) var<uniform> color: vec4<f32>;
147/// @group(#{MATERIAL_BIND_GROUP}) @binding(1) var color_texture: texture_2d<f32>;
148/// @group(#{MATERIAL_BIND_GROUP}) @binding(2) var color_sampler: sampler;
149/// @group(#{MATERIAL_BIND_GROUP}) @binding(3) var<storage> storage_buffer: array<f32>;
150/// @group(#{MATERIAL_BIND_GROUP}) @binding(4) var<storage> raw_buffer: array<f32>;
151/// @group(#{MATERIAL_BIND_GROUP}) @binding(5) var storage_texture: texture_storage_2d<rgba8unorm, read_write>;
152/// ```
153/// Note that the "group" index is determined by the usage context. It is not defined in [`AsBindGroup`]. For example, in Bevy material bind groups
154/// are generally bound to group 2.
155///
156/// The following field-level attributes are supported:
157///
158/// ## `uniform(BINDING_INDEX)`
159///
160///  * The field will be converted to a shader-compatible type using the [`ShaderType`] trait, written to a [`Buffer`], and bound as a uniform.
161///    [`ShaderType`] is implemented for most math types already, such as [`f32`], [`Vec4`](bevy_math::Vec4), and
162///    [`LinearRgba`](bevy_color::LinearRgba). It can also be derived for custom structs.
163///
164/// ## `texture(BINDING_INDEX, arguments)`
165///
166///  * This field's [`Handle<Image>`](bevy_asset::Handle) will be used to look up the matching [`Texture`](crate::render_resource::Texture)
167///    GPU resource, which will be bound as a texture in shaders. The field will be assumed to implement [`Into<Option<Handle<Image>>>`]. In practice,
168///    most fields should be a [`Handle<Image>`](bevy_asset::Handle) or [`Option<Handle<Image>>`]. If the value of an [`Option<Handle<Image>>`] is
169///    [`None`], the [`crate::texture::FallbackImage`] resource will be used instead. This attribute can be used in conjunction with a `sampler` binding attribute
170///    (with a different binding index) if a binding of the sampler for the [`Image`](bevy_image::Image) is also required.
171///
172/// | Arguments             | Values                                                                  | Default              |
173/// |-----------------------|-------------------------------------------------------------------------|----------------------|
174/// | `dimension` = "..."   | `"1d"`, `"2d"`, `"2d_array"`, `"3d"`, `"cube"`, `"cube_array"`          | `"2d"`               |
175/// | `sample_type` = "..." | `"float"`, `"depth"`, `"s_int"` or `"u_int"`                            | `"float"`            |
176/// | `filterable` = ...    | `true`, `false`                                                         | `true`               |
177/// | `multisampled` = ...  | `true`, `false`                                                         | `false`              |
178/// | `visibility(...)`     | `all`, `none`, or a list-combination of `vertex`, `fragment`, `compute` | `vertex`, `fragment` |
179///
180/// ## `storage_texture(BINDING_INDEX, arguments)`
181///
182/// * This field's [`Handle<Image>`](bevy_asset::Handle) will be used to look up the matching [`Texture`](crate::render_resource::Texture)
183///   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,
184///   most fields should be a [`Handle<Image>`](bevy_asset::Handle) or [`Option<Handle<Image>>`]. If the value of an [`Option<Handle<Image>>`] is
185///   [`None`], the [`crate::texture::FallbackImage`] resource will be used instead.
186///
187/// | Arguments              | Values                                                                                     | Default       |
188/// |------------------------|--------------------------------------------------------------------------------------------|---------------|
189/// | `dimension` = "..."    | `"1d"`, `"2d"`, `"2d_array"`, `"3d"`, `"cube"`, `"cube_array"`                             | `"2d"`        |
190/// | `image_format` = ...   | any member of [`TextureFormat`](crate::render_resource::TextureFormat)                     | `Rgba8Unorm`  |
191/// | `access` = ...         | any member of [`StorageTextureAccess`](crate::render_resource::StorageTextureAccess)       | `ReadWrite`   |
192/// | `visibility(...)`      | `all`, `none`, or a list-combination of `vertex`, `fragment`, `compute`                    | `compute`     |
193///
194/// ## `sampler(BINDING_INDEX, arguments)`
195///
196/// * This field's [`Handle<Image>`](bevy_asset::Handle) will be used to look up the matching [`Sampler`] GPU
197///   resource, which will be bound as a sampler in shaders. The field will be assumed to implement [`Into<Option<Handle<Image>>>`]. In practice,
198///   most fields should be a [`Handle<Image>`](bevy_asset::Handle) or [`Option<Handle<Image>>`]. If the value of an [`Option<Handle<Image>>`] is
199///   [`None`], the [`crate::texture::FallbackImage`] resource will be used instead. This attribute can be used in conjunction with a `texture` binding attribute
200///   (with a different binding index) if a binding of the texture for the [`Image`](bevy_image::Image) is also required.
201///
202/// | Arguments              | Values                                                                  | Default                |
203/// |------------------------|-------------------------------------------------------------------------|------------------------|
204/// | `sampler_type` = "..." | `"filtering"`, `"non_filtering"`, `"comparison"`.                       |  `"filtering"`         |
205/// | `visibility(...)`      | `all`, `none`, or a list-combination of `vertex`, `fragment`, `compute` |   `vertex`, `fragment` |
206///
207/// ## `storage(BINDING_INDEX, arguments)`
208///
209/// * The field's [`Handle<Storage>`](bevy_asset::Handle) will be used to look
210///   up the matching [`Buffer`] GPU resource, which will be bound as a storage
211///   buffer in shaders. If the `storage` attribute is used, the field is expected
212///   a raw buffer, and the buffer will be bound as a storage buffer in shaders.
213///   In bindless mode, `binding_array()` argument that specifies the binding
214///   number of the resulting storage buffer binding array must be present.
215///
216/// | Arguments              | Values                                                                  | Default                |
217/// |------------------------|-------------------------------------------------------------------------|------------------------|
218/// | `visibility(...)`      | `all`, `none`, or a list-combination of `vertex`, `fragment`, `compute` | `vertex`, `fragment`   |
219/// | `read_only`            | if present then value is true, otherwise false                          | `false`                |
220/// | `buffer`               | if present then the field will be assumed to be a raw wgpu buffer       |                        |
221/// | `binding_array(...)`   | the binding number of the binding array, for bindless mode              | bindless mode disabled |
222///
223/// Note that fields without field-level binding attributes will be ignored.
224/// ```
225/// # use bevy_render::{render_resource::AsBindGroup};
226/// # use bevy_color::LinearRgba;
227/// # use bevy_asset::Handle;
228/// #[derive(AsBindGroup)]
229/// struct CoolMaterial {
230///     #[uniform(0)]
231///     color: LinearRgba,
232///     this_field_is_ignored: String,
233/// }
234/// ```
235///
236///  As mentioned above, [`Option<Handle<Image>>`] is also supported:
237/// ```
238/// # use bevy_asset::Handle;
239/// # use bevy_color::LinearRgba;
240/// # use bevy_image::Image;
241/// # use bevy_render::render_resource::AsBindGroup;
242/// #[derive(AsBindGroup)]
243/// struct CoolMaterial {
244///     #[uniform(0)]
245///     color: LinearRgba,
246///     #[texture(1)]
247///     #[sampler(2)]
248///     color_texture: Option<Handle<Image>>,
249/// }
250/// ```
251/// This is useful if you want a texture to be optional. When the value is [`None`], the [`crate::texture::FallbackImage`] will be used for the binding instead, which defaults
252/// to "pure white".
253///
254/// Field uniforms with the same index will be combined into a single binding:
255/// ```
256/// # use bevy_render::{render_resource::AsBindGroup};
257/// # use bevy_color::LinearRgba;
258/// #[derive(AsBindGroup)]
259/// struct CoolMaterial {
260///     #[uniform(0)]
261///     color: LinearRgba,
262///     #[uniform(0)]
263///     roughness: f32,
264/// }
265/// ```
266///
267/// In WGSL shaders, the binding would look like this:
268/// ```wgsl
269/// struct CoolMaterial {
270///     color: vec4<f32>,
271///     roughness: f32,
272/// };
273///
274/// @group(#{MATERIAL_BIND_GROUP}) @binding(0) var<uniform> material: CoolMaterial;
275/// ```
276///
277/// Some less common scenarios will require "struct-level" attributes. These are the currently supported struct-level attributes:
278/// ## `uniform(BINDING_INDEX, ConvertedShaderType)`
279///
280/// * This also creates a [`Buffer`] using [`ShaderType`] and binds it as a
281///   uniform, much like the field-level `uniform` attribute. The difference is
282///   that the entire [`AsBindGroup`] value is converted to `ConvertedShaderType`,
283///   which must implement [`ShaderType`], instead of a specific field
284///   implementing [`ShaderType`]. This is useful if more complicated conversion
285///   logic is required, or when using bindless mode (see below). The conversion
286///   is done using the [`AsBindGroupShaderType<ConvertedShaderType>`] trait,
287///   which is automatically implemented if `&Self` implements
288///   [`Into<ConvertedShaderType>`]. Outside of bindless mode, only use
289///   [`AsBindGroupShaderType`] if access to resources like
290///   [`RenderAssets<GpuImage>`] is required.
291///
292/// * In bindless mode (see `bindless(COUNT)`), this attribute becomes
293///   `uniform(BINDLESS_INDEX, ConvertedShaderType,
294///   binding_array(BINDING_INDEX))`. The resulting uniform buffers will be
295///   available in the shader as a binding array at the given `BINDING_INDEX`. The
296///   `BINDLESS_INDEX` specifies the offset of the buffer in the bindless index
297///   table.
298///
299///   For example, suppose that the material slot is stored in a variable named
300///   `slot`, the bindless index table is named `material_indices`, and that the
301///   first field (index 0) of the bindless index table type is named
302///   `material`. Then specifying `#[uniform(0, StandardMaterialUniform,
303///   binding_array(10)]` will create a binding array buffer declared in the
304///   shader as `var<storage> material_array:
305///   binding_array<StandardMaterialUniform>` and accessible as
306///   `material_array[material_indices[slot].material]`.
307///
308/// ## `data(BINDING_INDEX, ConvertedShaderType, binding_array(BINDING_INDEX))`
309///
310/// * This is very similar to `uniform(BINDING_INDEX, ConvertedShaderType,
311///   binding_array(BINDING_INDEX)` and in fact is identical if bindless mode
312///   isn't being used. The difference is that, in bindless mode, the `data`
313///   attribute produces a single buffer containing an array, not an array of
314///   buffers. For example, suppose you had the following declaration:
315///
316/// ```ignore
317/// #[uniform(0, StandardMaterialUniform, binding_array(10))]
318/// struct StandardMaterial { ... }
319/// ```
320///
321/// In bindless mode, this will produce a binding matching the following WGSL
322/// declaration:
323///
324/// ```wgsl
325/// @group(#{MATERIAL_BIND_GROUP}) @binding(10) var<storage> material_array: binding_array<StandardMaterial>;
326/// ```
327///
328/// On the other hand, if you write this declaration:
329///
330/// ```ignore
331/// #[data(0, StandardMaterialUniform, binding_array(10))]
332/// struct StandardMaterial { ... }
333/// ```
334///
335/// Then Bevy produces a binding that matches this WGSL declaration instead:
336///
337/// ```wgsl
338/// @group(#{MATERIAL_BIND_GROUP}) @binding(10) var<storage> material_array: array<StandardMaterial>;
339/// ```
340///
341/// * Just as with the structure-level `uniform` attribute, Bevy converts the
342///   entire [`AsBindGroup`] to `ConvertedShaderType`, using the
343///   [`AsBindGroupShaderType<ConvertedShaderType>`] trait.
344///
345/// * In non-bindless mode, the structure-level `data` attribute is the same as
346///   the structure-level `uniform` attribute and produces a single uniform buffer
347///   in the shader. The above example would result in a binding that looks like
348///   this in WGSL in non-bindless mode:
349///
350/// ```wgsl
351/// @group(#{MATERIAL_BIND_GROUP}) @binding(0) var<uniform> material: StandardMaterial;
352/// ```
353///
354/// * For efficiency reasons, `data` is generally preferred over `uniform`
355///   unless you need to place your data in individual buffers.
356///
357/// ## `bind_group_data(DataType)`
358///
359/// * The [`AsBindGroup`] type will be converted to some `DataType` using [`Into<DataType>`] and stored
360///   as [`AsBindGroup::Data`] as part of the [`AsBindGroup::as_bind_group`] call. This is useful if data needs to be stored alongside
361///   the generated bind group, such as a unique identifier for a material's bind group. The most common use case for this attribute
362///   is "shader pipeline specialization". See [`SpecializedRenderPipeline`](crate::render_resource::SpecializedRenderPipeline).
363///
364/// ## `bindless`
365///
366/// * This switch enables *bindless resources*, which changes the way Bevy
367///   supplies resources (textures, and samplers) to the shader.  When bindless
368///   resources are enabled, and the current platform supports them, Bevy will
369///   allocate textures, and samplers into *binding arrays*, separated based on
370///   type and will supply your shader with indices into those arrays.
371/// * Bindless textures and samplers are placed into the appropriate global
372///   array defined in `bevy_render::bindless` (`bindless.wesl`).
373/// * Bevy doesn't currently support bindless buffers, except for those created
374///   with the `uniform(BINDLESS_INDEX, ConvertedShaderType,
375///   binding_array(BINDING_INDEX))` attribute. If you need to include a buffer in
376///   your object, and you can't create the data in that buffer with the `uniform`
377///   attribute, consider a non-bindless object instead.
378/// * If bindless mode is enabled, the `BINDLESS` definition will be
379///   available. Because not all platforms support bindless resources, you
380///   should check for the presence of this definition via `@if` and fall
381///   back to standard bindings if it isn't present.
382/// * By default, in bindless mode, binding 0 becomes the *bindless index
383///   table*, which is an array of structures, each of which contains as many
384///   fields of type `u32` as the highest binding number in the structure
385///   annotated with `#[derive(AsBindGroup)]`. Again by default, the *i*th field
386///   of the bindless index table contains the index of the resource with binding
387///   *i* within the appropriate binding array.
388/// * In the case of materials, the index of the applicable table within the
389///   bindless index table list corresponding to the mesh currently being drawn
390///   can be retrieved with
391///   `mesh[in.instance_index].material_and_lightmap_bind_group_slot & 0xffffu`.
392/// * You can limit the size of the bindless slabs to N resources with the
393///   `limit(N)` declaration. For example, `#[bindless(limit(16))]` ensures that
394///   each slab will have no more than 16 total resources in it. If you don't
395///   specify a limit, Bevy automatically picks a reasonable one for the current
396///   platform.
397/// * The `index_table(range(M..N), binding(B))` declaration allows you to
398///   customize the layout of the bindless index table. This is useful for
399///   materials that are composed of multiple bind groups, such as
400///   `ExtendedMaterial`. In such cases, there will be multiple bindless index
401///   tables, so they can't both be assigned to binding 0 or their bindings will
402///   conflict.
403///   - The `binding(B)` attribute of the `index_table` attribute allows you to
404///     customize the binding (`@binding(B)`, in the shader) at which the index
405///     table will be bound.
406///   - The `range(M, N)` attribute of the `index_table` attribute allows you to
407///     change the mapping from the field index in the bindless index table to the
408///     bindless index. Instead of the field at index $i$ being mapped to the
409///     bindless index $i$, with the `range(M, N)` attribute the field at index
410///     $i$ in the bindless index table is mapped to the bindless index $i$ + M.
411///     The size of the index table will be set to N - M. Note that this may
412///     result in the table being too small to contain all the bindless bindings.
413/// * The purpose of bindless mode is to improve performance by reducing
414///   state changes. By grouping resources together into binding arrays, Bevy
415///   doesn't have to modify GPU state as often, decreasing API and driver
416///   overhead.
417/// * See the `shaders/shader_material_bindless` example for an example of how
418///   to use bindless mode. See the `shaders/extended_material_bindless` example
419///   for a more exotic example of bindless mode that demonstrates the
420///   `index_table` attribute.
421/// * The following diagram illustrates how bindless mode works using a subset
422///   of `StandardMaterial`:
423///
424/// ```text
425///      Shader Bindings                          Sampler Binding Array
426///     +----+-----------------------------+     +-----------+-----------+-----+
427/// +---|  0 | material_indices            |  +->| sampler 0 | sampler 1 | ... |
428/// |   +----+-----------------------------+  |  +-----------+-----------+-----+
429/// |   |  1 | bindless_samplers_filtering +--+        ^
430/// |   +----+-----------------------------+           +-------------------------------+
431/// |   | .. |            ...              |                                           |
432/// |   +----+-----------------------------+      Texture Binding Array                |
433/// |   |  5 | bindless_textures_2d        +--+  +-----------+-----------+-----+       |
434/// |   +----+-----------------------------+  +->| texture 0 | texture 1 | ... |       |
435/// |   | .. |            ...              |     +-----------+-----------+-----+       |
436/// |   +----+-----------------------------+           ^                               |
437/// |   + 10 | material_array              +--+        +---------------------------+   |
438/// |   +----+-----------------------------+  |                                    |   |
439/// |                                         |   Buffer Binding Array             |   |
440/// |                                         |  +----------+----------+-----+     |   |
441/// |                                         +->| buffer 0 | buffer 1 | ... |     |   |
442/// |    Material Bindless Indices               +----------+----------+-----+     |   |
443/// |   +----+-----------------------------+          ^                            |   |
444/// +-->|  0 | material                    +----------+                            |   |
445///     +----+-----------------------------+                                       |   |
446///     |  1 | base_color_texture          +---------------------------------------+   |
447///     +----+-----------------------------+                                           |
448///     |  2 | base_color_sampler          +-------------------------------------------+
449///     +----+-----------------------------+
450///     | .. |            ...              |
451///     +----+-----------------------------+
452/// ```
453///
454/// The previous `CoolMaterial` example illustrating "combining multiple field-level uniform attributes with the same binding index" can
455/// also be equivalently represented with a single struct-level uniform attribute:
456/// ```
457/// # use bevy_render::{render_resource::{AsBindGroup, ShaderType}};
458/// # use bevy_color::LinearRgba;
459/// #[derive(AsBindGroup)]
460/// #[uniform(0, CoolMaterialUniform)]
461/// struct CoolMaterial {
462///     color: LinearRgba,
463///     roughness: f32,
464/// }
465///
466/// #[derive(ShaderType)]
467/// struct CoolMaterialUniform {
468///     color: LinearRgba,
469///     roughness: f32,
470/// }
471///
472/// impl From<&CoolMaterial> for CoolMaterialUniform {
473///     fn from(material: &CoolMaterial) -> CoolMaterialUniform {
474///         CoolMaterialUniform {
475///             color: material.color,
476///             roughness: material.roughness,
477///         }
478///     }
479/// }
480/// ```
481///
482/// Setting `bind_group_data` looks like this:
483/// ```
484/// # use bevy_render::{render_resource::AsBindGroup};
485/// # use bevy_color::LinearRgba;
486/// #[derive(AsBindGroup)]
487/// #[bind_group_data(CoolMaterialKey)]
488/// struct CoolMaterial {
489///     #[uniform(0)]
490///     color: LinearRgba,
491///     is_shaded: bool,
492/// }
493///
494/// // Materials keys are intended to be small, cheap to hash, and
495/// // uniquely identify a specific material permutation.
496/// #[repr(C)]
497/// #[derive(Copy, Clone, Hash, Eq, PartialEq)]
498/// struct CoolMaterialKey {
499///     is_shaded: bool,
500/// }
501///
502/// impl From<&CoolMaterial> for CoolMaterialKey {
503///     fn from(material: &CoolMaterial) -> CoolMaterialKey {
504///         CoolMaterialKey {
505///             is_shaded: material.is_shaded,
506///         }
507///     }
508/// }
509/// ```
510pub trait AsBindGroup {
511    /// Data that will be stored alongside the "prepared" bind group.
512    type Data: Send + Sync;
513
514    type Param: SystemParam + 'static;
515
516    /// The number of slots per bind group, if bindless mode is enabled.
517    ///
518    /// If this bind group doesn't use bindless, then this will be `None`.
519    ///
520    /// Note that the *actual* slot count may be different from this value, due
521    /// to platform limitations. For example, if bindless resources aren't
522    /// supported on this platform, the actual slot count will be 1.
523    fn bindless_slot_count() -> Option<BindlessSlabResourceLimit> {
524        None
525    }
526
527    /// True if the hardware *actually* supports bindless textures for this
528    /// type, taking the device and driver capabilities into account.
529    ///
530    /// If this type doesn't use bindless textures, then the return value from
531    /// this function is meaningless.
532    fn bindless_supported(_: &RenderDevice) -> bool {
533        true
534    }
535
536    /// label
537    fn label() -> &'static str;
538
539    /// Creates a bind group for `self` matching the layout defined in [`AsBindGroup::bind_group_layout`].
540    fn as_bind_group(
541        &self,
542        layout_descriptor: &BindGroupLayoutDescriptor,
543        render_device: &RenderDevice,
544        pipeline_cache: &PipelineCache,
545        fallback_buffer: &FallbackBuffer,
546        shader_buffer_assets: &RenderAssets<GpuShaderBuffer>,
547        param: &mut SystemParamItem<'_, '_, Self::Param>,
548    ) -> Result<PreparedBindGroup, AsBindGroupError> {
549        let layout = &pipeline_cache.get_bind_group_layout(layout_descriptor);
550
551        let mut bind_group_builder = BindGroupBuilder::default();
552        Self::build_bind_group(
553            self,
554            layout,
555            render_device,
556            param,
557            false,
558            &mut bind_group_builder,
559        )?;
560
561        let entries = bind_group_builder
562            .binding_resources
563            .iter()
564            .map(|(index, binding)| BindGroupEntry {
565                binding: *index,
566                resource: binding.get_binding(fallback_buffer, shader_buffer_assets),
567            })
568            .collect::<Vec<_>>();
569
570        let bind_group = render_device.create_bind_group(Self::label(), layout, &entries);
571
572        Ok(PreparedBindGroup {
573            bindings: bind_group_builder.into_binding_resources(),
574            bind_group,
575        })
576    }
577
578    fn bind_group_data(&self) -> Self::Data;
579
580    /// Populates a [`BindGroupBuilder`] with all bindings that this type
581    /// exposes.
582    ///
583    /// If the bind group builder API isn't sufficient to produce bind groups
584    /// for this type (e.g. if the type uses its own bindless texture arrays),
585    /// an implementor may return `AsBindGroupError::CreateBindGroupDirectly`
586    /// from this function and instead define `as_bind_group` directly. This may
587    /// prevent certain features, such as bindless mode, from working correctly.
588    ///
589    /// Set `force_no_bindless` to true to require that bindless textures *not*
590    /// be used. `ExtendedMaterial` uses this in order to ensure that the base
591    /// material doesn't use bindless mode if the extension doesn't.
592    fn build_bind_group(
593        &self,
594        layout: &BindGroupLayout,
595        render_device: &RenderDevice,
596        param: &mut SystemParamItem<'_, '_, Self::Param>,
597        force_no_bindless: bool,
598        output: &mut BindGroupBuilder,
599    ) -> Result<(), AsBindGroupError>;
600
601    /// Creates the bind group layout matching all bind groups returned by
602    /// [`AsBindGroup::as_bind_group`]
603    fn bind_group_layout(render_device: &RenderDevice) -> BindGroupLayout
604    where
605        Self: Sized,
606    {
607        render_device.create_bind_group_layout(
608            Self::label(),
609            &Self::bind_group_layout_entries(render_device, false),
610        )
611    }
612
613    /// Creates the bind group layout descriptor matching all bind groups returned by
614    /// [`AsBindGroup::as_bind_group`]
615    /// TODO: we only need `RenderDevice` to determine if bindless is supported
616    fn bind_group_layout_descriptor(render_device: &RenderDevice) -> BindGroupLayoutDescriptor
617    where
618        Self: Sized,
619    {
620        BindGroupLayoutDescriptor {
621            label: Self::label().into(),
622            entries: Self::bind_group_layout_entries(render_device, false),
623        }
624    }
625
626    /// Returns a vec of bind group layout entries.
627    ///
628    /// Set `force_no_bindless` to true to require that bindless textures *not*
629    /// be used. `ExtendedMaterial` uses this in order to ensure that the base
630    /// material doesn't use bindless mode if the extension doesn't.
631    fn bind_group_layout_entries(
632        render_device: &RenderDevice,
633        force_no_bindless: bool,
634    ) -> Vec<BindGroupLayoutEntry>
635    where
636        Self: Sized;
637
638    fn bindless_descriptor() -> Option<BindlessDescriptor> {
639        None
640    }
641}
642
643/// An error that occurs during [`AsBindGroup::as_bind_group`] calls.
644#[derive(Debug, Error)]
645pub enum AsBindGroupError {
646    /// The bind group could not be generated. Try again next frame.
647    #[error("The bind group could not be generated")]
648    RetryNextUpdate,
649    #[error("Create the bind group via `as_bind_group()` instead")]
650    CreateBindGroupDirectly,
651    #[error("At binding index {0}, the provided image sampler `{1}` does not match the required sampler type(s) `{2}`.")]
652    InvalidSamplerType(u32, String, String),
653}
654
655/// A prepared bind group returned as a result of [`AsBindGroup::as_bind_group`].
656pub struct PreparedBindGroup {
657    pub bindings: BindingResources,
658    pub bind_group: BindGroup,
659}
660
661impl PreparedBindGroup {
662    pub(crate) fn unprepare(&self) -> BindGroupBuilder {
663        let mut data_buffer = vec![];
664        BindGroupBuilder {
665            binding_resources: UnpreparedBindingResources(
666                self.bindings
667                    .iter()
668                    .map(|(binding, owned_binding_resource)| {
669                        let unprepared_binding_resource = match owned_binding_resource {
670                            OwnedBindingResource::Buffer(buffer) => {
671                                UnpreparedBindingResource::Buffer(buffer.clone())
672                            }
673                            OwnedBindingResource::ShaderBuffer(handle) => {
674                                UnpreparedBindingResource::ShaderBuffer(handle.clone())
675                            }
676                            OwnedBindingResource::TextureView(
677                                texture_view_dimension,
678                                texture_view,
679                            ) => UnpreparedBindingResource::TextureView(
680                                *texture_view_dimension,
681                                texture_view.clone(),
682                            ),
683                            OwnedBindingResource::Sampler(sampler_binding_type, sampler) => {
684                                UnpreparedBindingResource::Sampler(
685                                    *sampler_binding_type,
686                                    sampler.clone(),
687                                )
688                            }
689                            OwnedBindingResource::Data(owned_data) => {
690                                let start_offset = data_buffer.len() as u32;
691                                data_buffer.extend_from_slice(&owned_data.0);
692                                let end_offset = data_buffer.len() as u32;
693                                UnpreparedBindingResource::Data(start_offset..end_offset)
694                            }
695                        };
696                        (*binding, unprepared_binding_resource)
697                    })
698                    .collect(),
699            ),
700            data_buffer,
701        }
702    }
703}
704
705/// A raw list of binding resources, suitable for either preparing into a bind
706/// group or for combining with other bind groups (perhaps via bindless).
707///
708/// This can be cleared and reused for different bind groups, in order to reduce
709/// allocations.
710#[derive(Default, Deref, DerefMut)]
711pub struct BindGroupBuilder {
712    /// The resources that this bind group builder holds.
713    #[deref]
714    pub binding_resources: UnpreparedBindingResources,
715
716    /// A shared buffer that holds all the POD that this bind group uses.
717    ///
718    /// Data binding resources contain ranges into this buffer.
719    pub data_buffer: Vec<u8>,
720}
721
722/// A pair of binding index and binding resource, used as part of
723/// [`PreparedBindGroup`].
724#[derive(Default, Deref, DerefMut)]
725pub struct BindingResources(pub Vec<(u32, OwnedBindingResource)>);
726
727/// A pair of binding index and binding resource, used as part of
728/// [`BindGroupBuilder`].
729#[derive(Default, Deref, DerefMut)]
730pub struct UnpreparedBindingResources(pub Vec<(u32, UnpreparedBindingResource)>);
731
732impl BindGroupBuilder {
733    /// Turns this [`BindGroupBuilder`] into a set of [`BindingResources`]
734    /// suitable for embedding into a [`PreparedBindGroup`].
735    pub(crate) fn into_binding_resources(self) -> BindingResources {
736        BindingResources(
737            self.binding_resources
738                .0
739                .into_iter()
740                .map(|(binding_index, unprepared_binding_resource)| {
741                    let owned_binding_resource = match unprepared_binding_resource {
742                        UnpreparedBindingResource::Buffer(buffer) => {
743                            OwnedBindingResource::Buffer(buffer)
744                        }
745                        UnpreparedBindingResource::ShaderBuffer(handle) => {
746                            OwnedBindingResource::ShaderBuffer(handle)
747                        }
748                        UnpreparedBindingResource::TextureView(
749                            texture_view_dimension,
750                            texture_view,
751                        ) => {
752                            OwnedBindingResource::TextureView(texture_view_dimension, texture_view)
753                        }
754                        UnpreparedBindingResource::Sampler(sampler_binding_type, sampler) => {
755                            OwnedBindingResource::Sampler(sampler_binding_type, sampler)
756                        }
757                        UnpreparedBindingResource::Data(range) => {
758                            OwnedBindingResource::Data(OwnedData(
759                                self.data_buffer[(range.start as usize)..(range.end as usize)]
760                                    .to_vec(),
761                            ))
762                        }
763                    };
764                    (binding_index, owned_binding_resource)
765                })
766                .collect(),
767        )
768    }
769
770    /// Clears out this [`BindGroupBuilder`] so that it can be used to construct
771    /// a new bind group, while preserving the allocations.
772    pub fn clear(&mut self) {
773        self.binding_resources.clear();
774        self.data_buffer.clear();
775    }
776}
777
778/// An owned binding resource of any type (ex: a [`Buffer`], [`TextureView`], etc).
779/// This is used by types like [`PreparedBindGroup`] to hold a single list of all
780/// render resources used by bindings.
781#[derive(Debug)]
782pub enum OwnedBindingResource {
783    Buffer(Buffer),
784    ShaderBuffer(Handle<ShaderBuffer>),
785    TextureView(TextureViewDimension, TextureView),
786    Sampler(SamplerBindingType, Sampler),
787    Data(OwnedData),
788}
789
790/// A raw binding resource inside a [`BindGroupBuilder`].
791///
792/// This is the same as [`OwnedBindingResource`], except it references the
793/// [`BindGroupBuilder::data_buffer`] instead of requiring separate allocations
794/// for POD.
795///
796/// The name "unprepared" refers to the fact that this represents an individual
797/// binding resource that hasn't been added to a bind group (i.e. "prepared")
798/// yet.
799#[derive(Debug)]
800pub enum UnpreparedBindingResource {
801    Buffer(Buffer),
802    ShaderBuffer(Handle<ShaderBuffer>),
803    TextureView(TextureViewDimension, TextureView),
804    Sampler(SamplerBindingType, Sampler),
805    /// Plain old data (POD).
806    ///
807    /// The given range represents the byte range within the
808    /// [`BindGroupBuilder::data_buffer`].
809    Data(Range<u32>),
810}
811
812/// Data that will be copied into a GPU buffer.
813///
814/// This corresponds to the `#[data]` attribute in `AsBindGroup`.
815#[derive(Debug, Deref, DerefMut)]
816pub struct OwnedData(pub Vec<u8>);
817
818impl OwnedBindingResource {
819    /// Creates a [`BindingResource`] reference to this
820    /// [`OwnedBindingResource`].
821    ///
822    /// Note that this operation panics if passed a
823    /// [`OwnedBindingResource::Data`], because [`OwnedData`] doesn't itself
824    /// correspond to any binding and instead requires the
825    /// `MaterialBindGroupAllocator` to pack it into a buffer.
826    pub fn get_binding(&self) -> BindingResource<'_> {
827        match self {
828            OwnedBindingResource::Buffer(buffer) => buffer.as_entire_binding(),
829            OwnedBindingResource::ShaderBuffer(_) => {
830                panic!(
831                    "You can't use `get_binding` with a `ShaderBuffer`; fetch the buffer from \
832                     the `RenderAssets<GpuShaderBuffer>` instead"
833                )
834            }
835            OwnedBindingResource::TextureView(_, view) => BindingResource::TextureView(view),
836            OwnedBindingResource::Sampler(_, sampler) => BindingResource::Sampler(sampler),
837            OwnedBindingResource::Data(_) => panic!("`OwnedData` has no binding resource"),
838        }
839    }
840}
841
842impl UnpreparedBindingResource {
843    /// Creates a [`BindingResource`] reference to this
844    /// [`UnpreparedBindingResource`].
845    ///
846    /// Note that this operation panics if passed a
847    /// [`UnpreparedBindingResource::Data`], because the range doesn't itself
848    /// correspond to any binding and instead requires the
849    /// `MaterialBindGroupAllocator` to pack it into a buffer.
850    pub fn get_binding<'a>(
851        &'a self,
852        fallback_buffer: &'a FallbackBuffer,
853        shader_buffer_assets: &'a RenderAssets<GpuShaderBuffer>,
854    ) -> BindingResource<'a> {
855        match self {
856            UnpreparedBindingResource::Buffer(buffer) => buffer.as_entire_binding(),
857            UnpreparedBindingResource::TextureView(_, view) => BindingResource::TextureView(view),
858            UnpreparedBindingResource::Sampler(_, sampler) => BindingResource::Sampler(sampler),
859            UnpreparedBindingResource::ShaderBuffer(shader_buffer) => {
860                // Fetch the raw buffer from the
861                // `shader_buffer_assets`. If it's not there,
862                // use the fallback buffer.
863                match shader_buffer_assets.get(shader_buffer.id()) {
864                    Some(shader_buffer) => BindingResource::Buffer(BufferBinding {
865                        buffer: &shader_buffer.buffer,
866                        offset: 0,
867                        size: None,
868                    }),
869                    None => BindingResource::Buffer(BufferBinding {
870                        buffer: fallback_buffer,
871                        offset: 0,
872                        size: None,
873                    }),
874                }
875            }
876            UnpreparedBindingResource::Data(_) => panic!("Data ranges have no binding resource"),
877        }
878    }
879}
880
881/// Converts a value to a [`ShaderType`] for use in a bind group.
882///
883/// This is automatically implemented for references that implement [`Into`].
884/// Generally normal [`Into`] / [`From`] impls should be preferred, but
885/// sometimes additional runtime metadata is required.
886/// This exists largely to make some [`AsBindGroup`] use cases easier.
887pub trait AsBindGroupShaderType<T: ShaderType> {
888    /// Return the `T` [`ShaderType`] for `self`. When used in [`AsBindGroup`]
889    /// derives, it is safe to assume that all images in `self` exist.
890    fn as_bind_group_shader_type(&self, images: &RenderAssets<GpuImage>) -> T;
891}
892
893impl<T, U: ShaderType> AsBindGroupShaderType<U> for T
894where
895    for<'a> &'a T: Into<U>,
896{
897    #[inline]
898    fn as_bind_group_shader_type(&self, _images: &RenderAssets<GpuImage>) -> U {
899        self.into()
900    }
901}
902
903#[cfg(test)]
904mod test {
905    use super::*;
906    use bevy_asset::Handle;
907    use bevy_image::Image;
908
909    #[test]
910    fn texture_visibility() {
911        #[expect(
912            dead_code,
913            reason = "This is a derive macro compilation test. It will not be constructed."
914        )]
915        #[derive(AsBindGroup)]
916        pub struct TextureVisibilityTest {
917            #[texture(0, visibility(all))]
918            pub all: Handle<Image>,
919            #[texture(1, visibility(none))]
920            pub none: Handle<Image>,
921            #[texture(2, visibility(fragment))]
922            pub fragment: Handle<Image>,
923            #[texture(3, visibility(vertex))]
924            pub vertex: Handle<Image>,
925            #[texture(4, visibility(compute))]
926            pub compute: Handle<Image>,
927            #[texture(5, visibility(vertex, fragment))]
928            pub vertex_fragment: Handle<Image>,
929            #[texture(6, visibility(vertex, compute))]
930            pub vertex_compute: Handle<Image>,
931            #[texture(7, visibility(fragment, compute))]
932            pub fragment_compute: Handle<Image>,
933            #[texture(8, visibility(vertex, fragment, compute))]
934            pub vertex_fragment_compute: Handle<Image>,
935        }
936    }
937}