Path: blob/main/crates/bevy_render/src/render_resource/bind_group.rs
6596 views
use crate::{1define_atomic_id,2render_asset::RenderAssets,3render_resource::{BindGroupLayout, Buffer, Sampler, TextureView},4renderer::{RenderDevice, WgpuWrapper},5texture::GpuImage,6};7use bevy_derive::{Deref, DerefMut};8use bevy_ecs::system::{SystemParam, SystemParamItem};9pub use bevy_render_macros::AsBindGroup;10use core::ops::Deref;11use encase::ShaderType;12use thiserror::Error;13use wgpu::{14BindGroupEntry, BindGroupLayoutEntry, BindingResource, SamplerBindingType, TextureViewDimension,15};1617use super::{BindlessDescriptor, BindlessSlabResourceLimit};1819define_atomic_id!(BindGroupId);2021/// Bind groups are responsible for binding render resources (e.g. buffers, textures, samplers)22/// to a [`TrackedRenderPass`](crate::render_phase::TrackedRenderPass).23/// This makes them accessible in the pipeline (shaders) as uniforms.24///25/// This is a lightweight thread-safe wrapper around wgpu's own [`BindGroup`](wgpu::BindGroup),26/// which can be cloned as needed to workaround lifetime management issues. It may be converted27/// from and dereferences to wgpu's [`BindGroup`](wgpu::BindGroup).28///29/// Can be created via [`RenderDevice::create_bind_group`](RenderDevice::create_bind_group).30#[derive(Clone, Debug)]31pub struct BindGroup {32id: BindGroupId,33value: WgpuWrapper<wgpu::BindGroup>,34}3536impl BindGroup {37/// Returns the [`BindGroupId`] representing the unique ID of the bind group.38#[inline]39pub fn id(&self) -> BindGroupId {40self.id41}42}4344impl PartialEq for BindGroup {45fn eq(&self, other: &Self) -> bool {46self.id == other.id47}48}4950impl Eq for BindGroup {}5152impl core::hash::Hash for BindGroup {53fn hash<H: core::hash::Hasher>(&self, state: &mut H) {54self.id.0.hash(state);55}56}5758impl From<wgpu::BindGroup> for BindGroup {59fn from(value: wgpu::BindGroup) -> Self {60BindGroup {61id: BindGroupId::new(),62value: WgpuWrapper::new(value),63}64}65}6667impl<'a> From<&'a BindGroup> for Option<&'a wgpu::BindGroup> {68fn from(value: &'a BindGroup) -> Self {69Some(value.deref())70}71}7273impl<'a> From<&'a mut BindGroup> for Option<&'a wgpu::BindGroup> {74fn from(value: &'a mut BindGroup) -> Self {75Some(&*value)76}77}7879impl Deref for BindGroup {80type Target = wgpu::BindGroup;8182#[inline]83fn deref(&self) -> &Self::Target {84&self.value85}86}8788/// Converts a value to a [`BindGroup`] with a given [`BindGroupLayout`], which can then be used in Bevy shaders.89/// This trait can be derived (and generally should be). Read on for details and examples.90///91/// This is an opinionated trait that is intended to make it easy to generically92/// convert a type into a [`BindGroup`]. It provides access to specific render resources,93/// such as [`RenderAssets<GpuImage>`] and [`crate::texture::FallbackImage`]. If a type has a [`Handle<Image>`](bevy_asset::Handle),94/// these can be used to retrieve the corresponding [`Texture`](crate::render_resource::Texture) resource.95///96/// [`AsBindGroup::as_bind_group`] is intended to be called once, then the result cached somewhere. It is generally97/// ok to do "expensive" work here, such as creating a [`Buffer`] for a uniform.98///99/// If for some reason a [`BindGroup`] cannot be created yet (for example, the [`Texture`](crate::render_resource::Texture)100/// for an [`Image`](bevy_image::Image) hasn't loaded yet), just return [`AsBindGroupError::RetryNextUpdate`], which signals that the caller101/// should retry again later.102///103/// # Deriving104///105/// This trait can be derived. Field attributes like `uniform` and `texture` are used to define which fields should be bindings,106/// what their binding type is, and what index they should be bound at:107///108/// ```109/// # use bevy_render::render_resource::*;110/// # use bevy_image::Image;111/// # use bevy_color::LinearRgba;112/// # use bevy_asset::Handle;113/// # use bevy_render::storage::ShaderStorageBuffer;114///115/// #[derive(AsBindGroup)]116/// struct CoolMaterial {117/// #[uniform(0)]118/// color: LinearRgba,119/// #[texture(1)]120/// #[sampler(2)]121/// color_texture: Handle<Image>,122/// #[storage(3, read_only)]123/// storage_buffer: Handle<ShaderStorageBuffer>,124/// #[storage(4, read_only, buffer)]125/// raw_buffer: Buffer,126/// #[storage_texture(5)]127/// storage_texture: Handle<Image>,128/// }129/// ```130///131/// In WGSL shaders, the binding would look like this:132///133/// ```wgsl134/// @group(#{MATERIAL_BIND_GROUP}) @binding(0) var<uniform> color: vec4<f32>;135/// @group(#{MATERIAL_BIND_GROUP}) @binding(1) var color_texture: texture_2d<f32>;136/// @group(#{MATERIAL_BIND_GROUP}) @binding(2) var color_sampler: sampler;137/// @group(#{MATERIAL_BIND_GROUP}) @binding(3) var<storage> storage_buffer: array<f32>;138/// @group(#{MATERIAL_BIND_GROUP}) @binding(4) var<storage> raw_buffer: array<f32>;139/// @group(#{MATERIAL_BIND_GROUP}) @binding(5) var storage_texture: texture_storage_2d<rgba8unorm, read_write>;140/// ```141/// Note that the "group" index is determined by the usage context. It is not defined in [`AsBindGroup`]. For example, in Bevy material bind groups142/// are generally bound to group 2.143///144/// The following field-level attributes are supported:145///146/// ## `uniform(BINDING_INDEX)`147///148/// * The field will be converted to a shader-compatible type using the [`ShaderType`] trait, written to a [`Buffer`], and bound as a uniform.149/// [`ShaderType`] is implemented for most math types already, such as [`f32`], [`Vec4`](bevy_math::Vec4), and150/// [`LinearRgba`](bevy_color::LinearRgba). It can also be derived for custom structs.151///152/// ## `texture(BINDING_INDEX, arguments)`153///154/// * This field's [`Handle<Image>`](bevy_asset::Handle) will be used to look up the matching [`Texture`](crate::render_resource::Texture)155/// GPU resource, which will be bound as a texture in shaders. The field will be assumed to implement [`Into<Option<Handle<Image>>>`]. In practice,156/// most fields should be a [`Handle<Image>`](bevy_asset::Handle) or [`Option<Handle<Image>>`]. If the value of an [`Option<Handle<Image>>`] is157/// [`None`], the [`crate::texture::FallbackImage`] resource will be used instead. This attribute can be used in conjunction with a `sampler` binding attribute158/// (with a different binding index) if a binding of the sampler for the [`Image`](bevy_image::Image) is also required.159///160/// | Arguments | Values | Default |161/// |-----------------------|-------------------------------------------------------------------------|----------------------|162/// | `dimension` = "..." | `"1d"`, `"2d"`, `"2d_array"`, `"3d"`, `"cube"`, `"cube_array"` | `"2d"` |163/// | `sample_type` = "..." | `"float"`, `"depth"`, `"s_int"` or `"u_int"` | `"float"` |164/// | `filterable` = ... | `true`, `false` | `true` |165/// | `multisampled` = ... | `true`, `false` | `false` |166/// | `visibility(...)` | `all`, `none`, or a list-combination of `vertex`, `fragment`, `compute` | `vertex`, `fragment` |167///168/// ## `storage_texture(BINDING_INDEX, arguments)`169///170/// * This field's [`Handle<Image>`](bevy_asset::Handle) will be used to look up the matching [`Texture`](crate::render_resource::Texture)171/// 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,172/// most fields should be a [`Handle<Image>`](bevy_asset::Handle) or [`Option<Handle<Image>>`]. If the value of an [`Option<Handle<Image>>`] is173/// [`None`], the [`crate::texture::FallbackImage`] resource will be used instead.174///175/// | Arguments | Values | Default |176/// |------------------------|--------------------------------------------------------------------------------------------|---------------|177/// | `dimension` = "..." | `"1d"`, `"2d"`, `"2d_array"`, `"3d"`, `"cube"`, `"cube_array"` | `"2d"` |178/// | `image_format` = ... | any member of [`TextureFormat`](crate::render_resource::TextureFormat) | `Rgba8Unorm` |179/// | `access` = ... | any member of [`StorageTextureAccess`](crate::render_resource::StorageTextureAccess) | `ReadWrite` |180/// | `visibility(...)` | `all`, `none`, or a list-combination of `vertex`, `fragment`, `compute` | `compute` |181///182/// ## `sampler(BINDING_INDEX, arguments)`183///184/// * This field's [`Handle<Image>`](bevy_asset::Handle) will be used to look up the matching [`Sampler`] GPU185/// resource, which will be bound as a sampler in shaders. The field will be assumed to implement [`Into<Option<Handle<Image>>>`]. In practice,186/// most fields should be a [`Handle<Image>`](bevy_asset::Handle) or [`Option<Handle<Image>>`]. If the value of an [`Option<Handle<Image>>`] is187/// [`None`], the [`crate::texture::FallbackImage`] resource will be used instead. This attribute can be used in conjunction with a `texture` binding attribute188/// (with a different binding index) if a binding of the texture for the [`Image`](bevy_image::Image) is also required.189///190/// | Arguments | Values | Default |191/// |------------------------|-------------------------------------------------------------------------|------------------------|192/// | `sampler_type` = "..." | `"filtering"`, `"non_filtering"`, `"comparison"`. | `"filtering"` |193/// | `visibility(...)` | `all`, `none`, or a list-combination of `vertex`, `fragment`, `compute` | `vertex`, `fragment` |194///195/// ## `storage(BINDING_INDEX, arguments)`196///197/// * The field's [`Handle<Storage>`](bevy_asset::Handle) will be used to look198/// up the matching [`Buffer`] GPU resource, which will be bound as a storage199/// buffer in shaders. If the `storage` attribute is used, the field is expected200/// a raw buffer, and the buffer will be bound as a storage buffer in shaders.201/// In bindless mode, `binding_array()` argument that specifies the binding202/// number of the resulting storage buffer binding array must be present.203///204/// | Arguments | Values | Default |205/// |------------------------|-------------------------------------------------------------------------|------------------------|206/// | `visibility(...)` | `all`, `none`, or a list-combination of `vertex`, `fragment`, `compute` | `vertex`, `fragment` |207/// | `read_only` | if present then value is true, otherwise false | `false` |208/// | `buffer` | if present then the field will be assumed to be a raw wgpu buffer | |209/// | `binding_array(...)` | the binding number of the binding array, for bindless mode | bindless mode disabled |210///211/// Note that fields without field-level binding attributes will be ignored.212/// ```213/// # use bevy_render::{render_resource::AsBindGroup};214/// # use bevy_color::LinearRgba;215/// # use bevy_asset::Handle;216/// #[derive(AsBindGroup)]217/// struct CoolMaterial {218/// #[uniform(0)]219/// color: LinearRgba,220/// this_field_is_ignored: String,221/// }222/// ```223///224/// As mentioned above, [`Option<Handle<Image>>`] is also supported:225/// ```226/// # use bevy_asset::Handle;227/// # use bevy_color::LinearRgba;228/// # use bevy_image::Image;229/// # use bevy_render::render_resource::AsBindGroup;230/// #[derive(AsBindGroup)]231/// struct CoolMaterial {232/// #[uniform(0)]233/// color: LinearRgba,234/// #[texture(1)]235/// #[sampler(2)]236/// color_texture: Option<Handle<Image>>,237/// }238/// ```239/// 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 defaults240/// to "pure white".241///242/// Field uniforms with the same index will be combined into a single binding:243/// ```244/// # use bevy_render::{render_resource::AsBindGroup};245/// # use bevy_color::LinearRgba;246/// #[derive(AsBindGroup)]247/// struct CoolMaterial {248/// #[uniform(0)]249/// color: LinearRgba,250/// #[uniform(0)]251/// roughness: f32,252/// }253/// ```254///255/// In WGSL shaders, the binding would look like this:256/// ```wgsl257/// struct CoolMaterial {258/// color: vec4<f32>,259/// roughness: f32,260/// };261///262/// @group(#{MATERIAL_BIND_GROUP}) @binding(0) var<uniform> material: CoolMaterial;263/// ```264///265/// Some less common scenarios will require "struct-level" attributes. These are the currently supported struct-level attributes:266/// ## `uniform(BINDING_INDEX, ConvertedShaderType)`267///268/// * This also creates a [`Buffer`] using [`ShaderType`] and binds it as a269/// uniform, much like the field-level `uniform` attribute. The difference is270/// that the entire [`AsBindGroup`] value is converted to `ConvertedShaderType`,271/// which must implement [`ShaderType`], instead of a specific field272/// implementing [`ShaderType`]. This is useful if more complicated conversion273/// logic is required, or when using bindless mode (see below). The conversion274/// is done using the [`AsBindGroupShaderType<ConvertedShaderType>`] trait,275/// which is automatically implemented if `&Self` implements276/// [`Into<ConvertedShaderType>`]. Outside of bindless mode, only use277/// [`AsBindGroupShaderType`] if access to resources like278/// [`RenderAssets<GpuImage>`] is required.279///280/// * In bindless mode (see `bindless(COUNT)`), this attribute becomes281/// `uniform(BINDLESS_INDEX, ConvertedShaderType,282/// binding_array(BINDING_INDEX))`. The resulting uniform buffers will be283/// available in the shader as a binding array at the given `BINDING_INDEX`. The284/// `BINDLESS_INDEX` specifies the offset of the buffer in the bindless index285/// table.286///287/// For example, suppose that the material slot is stored in a variable named288/// `slot`, the bindless index table is named `material_indices`, and that the289/// first field (index 0) of the bindless index table type is named290/// `material`. Then specifying `#[uniform(0, StandardMaterialUniform,291/// binding_array(10)]` will create a binding array buffer declared in the292/// shader as `var<storage> material_array:293/// binding_array<StandardMaterialUniform>` and accessible as294/// `material_array[material_indices[slot].material]`.295///296/// ## `data(BINDING_INDEX, ConvertedShaderType, binding_array(BINDING_INDEX))`297///298/// * This is very similar to `uniform(BINDING_INDEX, ConvertedShaderType,299/// binding_array(BINDING_INDEX)` and in fact is identical if bindless mode300/// isn't being used. The difference is that, in bindless mode, the `data`301/// attribute produces a single buffer containing an array, not an array of302/// buffers. For example, suppose you had the following declaration:303///304/// ```ignore305/// #[uniform(0, StandardMaterialUniform, binding_array(10))]306/// struct StandardMaterial { ... }307/// ```308///309/// In bindless mode, this will produce a binding matching the following WGSL310/// declaration:311///312/// ```wgsl313/// @group(#{MATERIAL_BIND_GROUP}) @binding(10) var<storage> material_array: binding_array<StandardMaterial>;314/// ```315///316/// On the other hand, if you write this declaration:317///318/// ```ignore319/// #[data(0, StandardMaterialUniform, binding_array(10))]320/// struct StandardMaterial { ... }321/// ```322///323/// Then Bevy produces a binding that matches this WGSL declaration instead:324///325/// ```wgsl326/// @group(#{MATERIAL_BIND_GROUP}) @binding(10) var<storage> material_array: array<StandardMaterial>;327/// ```328///329/// * Just as with the structure-level `uniform` attribute, Bevy converts the330/// entire [`AsBindGroup`] to `ConvertedShaderType`, using the331/// [`AsBindGroupShaderType<ConvertedShaderType>`] trait.332///333/// * In non-bindless mode, the structure-level `data` attribute is the same as334/// the structure-level `uniform` attribute and produces a single uniform buffer335/// in the shader. The above example would result in a binding that looks like336/// this in WGSL in non-bindless mode:337///338/// ```wgsl339/// @group(#{MATERIAL_BIND_GROUP}) @binding(0) var<uniform> material: StandardMaterial;340/// ```341///342/// * For efficiency reasons, `data` is generally preferred over `uniform`343/// unless you need to place your data in individual buffers.344///345/// ## `bind_group_data(DataType)`346///347/// * The [`AsBindGroup`] type will be converted to some `DataType` using [`Into<DataType>`] and stored348/// as [`AsBindGroup::Data`] as part of the [`AsBindGroup::as_bind_group`] call. This is useful if data needs to be stored alongside349/// the generated bind group, such as a unique identifier for a material's bind group. The most common use case for this attribute350/// is "shader pipeline specialization". See [`SpecializedRenderPipeline`](crate::render_resource::SpecializedRenderPipeline).351///352/// ## `bindless`353///354/// * This switch enables *bindless resources*, which changes the way Bevy355/// supplies resources (textures, and samplers) to the shader. When bindless356/// resources are enabled, and the current platform supports them, Bevy will357/// allocate textures, and samplers into *binding arrays*, separated based on358/// type and will supply your shader with indices into those arrays.359/// * Bindless textures and samplers are placed into the appropriate global360/// array defined in `bevy_render::bindless` (`bindless.wgsl`).361/// * Bevy doesn't currently support bindless buffers, except for those created362/// with the `uniform(BINDLESS_INDEX, ConvertedShaderType,363/// binding_array(BINDING_INDEX))` attribute. If you need to include a buffer in364/// your object, and you can't create the data in that buffer with the `uniform`365/// attribute, consider a non-bindless object instead.366/// * If bindless mode is enabled, the `BINDLESS` definition will be367/// available. Because not all platforms support bindless resources, you368/// should check for the presence of this definition via `#ifdef` and fall369/// back to standard bindings if it isn't present.370/// * By default, in bindless mode, binding 0 becomes the *bindless index371/// table*, which is an array of structures, each of which contains as many372/// fields of type `u32` as the highest binding number in the structure373/// annotated with `#[derive(AsBindGroup)]`. Again by default, the *i*th field374/// of the bindless index table contains the index of the resource with binding375/// *i* within the appropriate binding array.376/// * In the case of materials, the index of the applicable table within the377/// bindless index table list corresponding to the mesh currently being drawn378/// can be retrieved with379/// `mesh[in.instance_index].material_and_lightmap_bind_group_slot & 0xffffu`.380/// * You can limit the size of the bindless slabs to N resources with the381/// `limit(N)` declaration. For example, `#[bindless(limit(16))]` ensures that382/// each slab will have no more than 16 total resources in it. If you don't383/// specify a limit, Bevy automatically picks a reasonable one for the current384/// platform.385/// * The `index_table(range(M..N), binding(B))` declaration allows you to386/// customize the layout of the bindless index table. This is useful for387/// materials that are composed of multiple bind groups, such as388/// `ExtendedMaterial`. In such cases, there will be multiple bindless index389/// tables, so they can't both be assigned to binding 0 or their bindings will390/// conflict.391/// - The `binding(B)` attribute of the `index_table` attribute allows you to392/// customize the binding (`@binding(B)`, in the shader) at which the index393/// table will be bound.394/// - The `range(M, N)` attribute of the `index_table` attribute allows you to395/// change the mapping from the field index in the bindless index table to the396/// bindless index. Instead of the field at index $i$ being mapped to the397/// bindless index $i$, with the `range(M, N)` attribute the field at index398/// $i$ in the bindless index table is mapped to the bindless index $i$ + M.399/// The size of the index table will be set to N - M. Note that this may400/// result in the table being too small to contain all the bindless bindings.401/// * The purpose of bindless mode is to improve performance by reducing402/// state changes. By grouping resources together into binding arrays, Bevy403/// doesn't have to modify GPU state as often, decreasing API and driver404/// overhead.405/// * See the `shaders/shader_material_bindless` example for an example of how406/// to use bindless mode. See the `shaders/extended_material_bindless` example407/// for a more exotic example of bindless mode that demonstrates the408/// `index_table` attribute.409/// * The following diagram illustrates how bindless mode works using a subset410/// of `StandardMaterial`:411///412/// ```text413/// Shader Bindings Sampler Binding Array414/// +----+-----------------------------+ +-----------+-----------+-----+415/// +---| 0 | material_indices | +->| sampler 0 | sampler 1 | ... |416/// | +----+-----------------------------+ | +-----------+-----------+-----+417/// | | 1 | bindless_samplers_filtering +--+ ^418/// | +----+-----------------------------+ +-------------------------------+419/// | | .. | ... | |420/// | +----+-----------------------------+ Texture Binding Array |421/// | | 5 | bindless_textures_2d +--+ +-----------+-----------+-----+ |422/// | +----+-----------------------------+ +->| texture 0 | texture 1 | ... | |423/// | | .. | ... | +-----------+-----------+-----+ |424/// | +----+-----------------------------+ ^ |425/// | + 10 | material_array +--+ +---------------------------+ |426/// | +----+-----------------------------+ | | |427/// | | Buffer Binding Array | |428/// | | +----------+----------+-----+ | |429/// | +->| buffer 0 | buffer 1 | ... | | |430/// | Material Bindless Indices +----------+----------+-----+ | |431/// | +----+-----------------------------+ ^ | |432/// +-->| 0 | material +----------+ | |433/// +----+-----------------------------+ | |434/// | 1 | base_color_texture +---------------------------------------+ |435/// +----+-----------------------------+ |436/// | 2 | base_color_sampler +-------------------------------------------+437/// +----+-----------------------------+438/// | .. | ... |439/// +----+-----------------------------+440/// ```441///442/// The previous `CoolMaterial` example illustrating "combining multiple field-level uniform attributes with the same binding index" can443/// also be equivalently represented with a single struct-level uniform attribute:444/// ```445/// # use bevy_render::{render_resource::{AsBindGroup, ShaderType}};446/// # use bevy_color::LinearRgba;447/// #[derive(AsBindGroup)]448/// #[uniform(0, CoolMaterialUniform)]449/// struct CoolMaterial {450/// color: LinearRgba,451/// roughness: f32,452/// }453///454/// #[derive(ShaderType)]455/// struct CoolMaterialUniform {456/// color: LinearRgba,457/// roughness: f32,458/// }459///460/// impl From<&CoolMaterial> for CoolMaterialUniform {461/// fn from(material: &CoolMaterial) -> CoolMaterialUniform {462/// CoolMaterialUniform {463/// color: material.color,464/// roughness: material.roughness,465/// }466/// }467/// }468/// ```469///470/// Setting `bind_group_data` looks like this:471/// ```472/// # use bevy_render::{render_resource::AsBindGroup};473/// # use bevy_color::LinearRgba;474/// #[derive(AsBindGroup)]475/// #[bind_group_data(CoolMaterialKey)]476/// struct CoolMaterial {477/// #[uniform(0)]478/// color: LinearRgba,479/// is_shaded: bool,480/// }481///482/// // Materials keys are intended to be small, cheap to hash, and483/// // uniquely identify a specific material permutation.484/// #[repr(C)]485/// #[derive(Copy, Clone, Hash, Eq, PartialEq)]486/// struct CoolMaterialKey {487/// is_shaded: bool,488/// }489///490/// impl From<&CoolMaterial> for CoolMaterialKey {491/// fn from(material: &CoolMaterial) -> CoolMaterialKey {492/// CoolMaterialKey {493/// is_shaded: material.is_shaded,494/// }495/// }496/// }497/// ```498pub trait AsBindGroup {499/// Data that will be stored alongside the "prepared" bind group.500type Data: Send + Sync;501502type Param: SystemParam + 'static;503504/// The number of slots per bind group, if bindless mode is enabled.505///506/// If this bind group doesn't use bindless, then this will be `None`.507///508/// Note that the *actual* slot count may be different from this value, due509/// to platform limitations. For example, if bindless resources aren't510/// supported on this platform, the actual slot count will be 1.511fn bindless_slot_count() -> Option<BindlessSlabResourceLimit> {512None513}514515/// True if the hardware *actually* supports bindless textures for this516/// type, taking the device and driver capabilities into account.517///518/// If this type doesn't use bindless textures, then the return value from519/// this function is meaningless.520fn bindless_supported(_: &RenderDevice) -> bool {521true522}523524/// label525fn label() -> Option<&'static str> {526None527}528529/// Creates a bind group for `self` matching the layout defined in [`AsBindGroup::bind_group_layout`].530fn as_bind_group(531&self,532layout: &BindGroupLayout,533render_device: &RenderDevice,534param: &mut SystemParamItem<'_, '_, Self::Param>,535) -> Result<PreparedBindGroup, AsBindGroupError> {536let UnpreparedBindGroup { bindings } =537Self::unprepared_bind_group(self, layout, render_device, param, false)?;538539let entries = bindings540.iter()541.map(|(index, binding)| BindGroupEntry {542binding: *index,543resource: binding.get_binding(),544})545.collect::<Vec<_>>();546547let bind_group = render_device.create_bind_group(Self::label(), layout, &entries);548549Ok(PreparedBindGroup {550bindings,551bind_group,552})553}554555fn bind_group_data(&self) -> Self::Data;556557/// Returns a vec of (binding index, `OwnedBindingResource`).558///559/// In cases where `OwnedBindingResource` is not available (as for bindless560/// texture arrays currently), an implementor may return561/// `AsBindGroupError::CreateBindGroupDirectly` from this function and562/// instead define `as_bind_group` directly. This may prevent certain563/// features, such as bindless mode, from working correctly.564///565/// Set `force_no_bindless` to true to require that bindless textures *not*566/// be used. `ExtendedMaterial` uses this in order to ensure that the base567/// material doesn't use bindless mode if the extension doesn't.568fn unprepared_bind_group(569&self,570layout: &BindGroupLayout,571render_device: &RenderDevice,572param: &mut SystemParamItem<'_, '_, Self::Param>,573force_no_bindless: bool,574) -> Result<UnpreparedBindGroup, AsBindGroupError>;575576/// Creates the bind group layout matching all bind groups returned by577/// [`AsBindGroup::as_bind_group`]578fn bind_group_layout(render_device: &RenderDevice) -> BindGroupLayout579where580Self: Sized,581{582render_device.create_bind_group_layout(583Self::label(),584&Self::bind_group_layout_entries(render_device, false),585)586}587588/// Returns a vec of bind group layout entries.589///590/// Set `force_no_bindless` to true to require that bindless textures *not*591/// be used. `ExtendedMaterial` uses this in order to ensure that the base592/// material doesn't use bindless mode if the extension doesn't.593fn bind_group_layout_entries(594render_device: &RenderDevice,595force_no_bindless: bool,596) -> Vec<BindGroupLayoutEntry>597where598Self: Sized;599600fn bindless_descriptor() -> Option<BindlessDescriptor> {601None602}603}604605/// An error that occurs during [`AsBindGroup::as_bind_group`] calls.606#[derive(Debug, Error)]607pub enum AsBindGroupError {608/// The bind group could not be generated. Try again next frame.609#[error("The bind group could not be generated")]610RetryNextUpdate,611#[error("Create the bind group via `as_bind_group()` instead")]612CreateBindGroupDirectly,613#[error("At binding index {0}, the provided image sampler `{1}` does not match the required sampler type(s) `{2}`.")]614InvalidSamplerType(u32, String, String),615}616617/// A prepared bind group returned as a result of [`AsBindGroup::as_bind_group`].618pub struct PreparedBindGroup {619pub bindings: BindingResources,620pub bind_group: BindGroup,621}622623/// a map containing `OwnedBindingResource`s, keyed by the target binding index624pub struct UnpreparedBindGroup {625pub bindings: BindingResources,626}627628/// A pair of binding index and binding resource, used as part of629/// [`PreparedBindGroup`] and [`UnpreparedBindGroup`].630#[derive(Deref, DerefMut)]631pub struct BindingResources(pub Vec<(u32, OwnedBindingResource)>);632633/// 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 all635/// render resources used by bindings.636#[derive(Debug)]637pub enum OwnedBindingResource {638Buffer(Buffer),639TextureView(TextureViewDimension, TextureView),640Sampler(SamplerBindingType, Sampler),641Data(OwnedData),642}643644/// 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>);649650impl OwnedBindingResource {651/// Creates a [`BindingResource`] reference to this652/// [`OwnedBindingResource`].653///654/// Note that this operation panics if passed a655/// [`OwnedBindingResource::Data`], because [`OwnedData`] doesn't itself656/// correspond to any binding and instead requires the657/// `MaterialBindGroupAllocator` to pack it into a buffer.658pub fn get_binding(&self) -> BindingResource<'_> {659match self {660OwnedBindingResource::Buffer(buffer) => buffer.as_entire_binding(),661OwnedBindingResource::TextureView(_, view) => BindingResource::TextureView(view),662OwnedBindingResource::Sampler(_, sampler) => BindingResource::Sampler(sampler),663OwnedBindingResource::Data(_) => panic!("`OwnedData` has no binding resource"),664}665}666}667668/// 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, but672/// 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.677fn as_bind_group_shader_type(&self, images: &RenderAssets<GpuImage>) -> T;678}679680impl<T, U: ShaderType> AsBindGroupShaderType<U> for T681where682for<'a> &'a T: Into<U>,683{684#[inline]685fn as_bind_group_shader_type(&self, _images: &RenderAssets<GpuImage>) -> U {686self.into()687}688}689690#[cfg(test)]691mod test {692use super::*;693use bevy_asset::Handle;694use bevy_image::Image;695696#[test]697fn texture_visibility() {698#[expect(699dead_code,700reason = "This is a derive macro compilation test. It will not be constructed."701)]702#[derive(AsBindGroup)]703pub struct TextureVisibilityTest {704#[texture(0, visibility(all))]705pub all: Handle<Image>,706#[texture(1, visibility(none))]707pub none: Handle<Image>,708#[texture(2, visibility(fragment))]709pub fragment: Handle<Image>,710#[texture(3, visibility(vertex))]711pub vertex: Handle<Image>,712#[texture(4, visibility(compute))]713pub compute: Handle<Image>,714#[texture(5, visibility(vertex, fragment))]715pub vertex_fragment: Handle<Image>,716#[texture(6, visibility(vertex, compute))]717pub vertex_compute: Handle<Image>,718#[texture(7, visibility(fragment, compute))]719pub fragment_compute: Handle<Image>,720#[texture(8, visibility(vertex, fragment, compute))]721pub vertex_fragment_compute: Handle<Image>,722}723}724}725726727