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