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