Path: blob/main/crates/bevy_render/src/render_resource/bind_group.rs
9330 views
use crate::{1render_asset::RenderAssets,2render_resource::{BindGroupLayout, Buffer, PipelineCache, Sampler, TextureView},3renderer::{RenderDevice, WgpuWrapper},4texture::GpuImage,5};6use bevy_derive::{Deref, DerefMut};7use bevy_ecs::system::{SystemParam, SystemParamItem};8use bevy_material::descriptor::BindGroupLayoutDescriptor;9pub use bevy_render_macros::AsBindGroup;10use bevy_utils::define_atomic_id;11use core::ops::Deref;12use encase::ShaderType;13use thiserror::Error;14use wgpu::{15BindGroupEntry, BindGroupLayoutEntry, BindingResource, SamplerBindingType, TextureViewDimension,16};1718use super::{BindlessDescriptor, BindlessSlabResourceLimit};1920define_atomic_id!(BindGroupId);2122/// 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 converted28/// 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 {33id: BindGroupId,34value: WgpuWrapper<wgpu::BindGroup>,35}3637impl BindGroup {38/// Returns the [`BindGroupId`] representing the unique ID of the bind group.39#[inline]40pub fn id(&self) -> BindGroupId {41self.id42}43}4445impl PartialEq for BindGroup {46fn eq(&self, other: &Self) -> bool {47self.id == other.id48}49}5051impl Eq for BindGroup {}5253impl core::hash::Hash for BindGroup {54fn hash<H: core::hash::Hasher>(&self, state: &mut H) {55self.id.0.hash(state);56}57}5859impl From<wgpu::BindGroup> for BindGroup {60fn from(value: wgpu::BindGroup) -> Self {61BindGroup {62id: BindGroupId::new(),63value: WgpuWrapper::new(value),64}65}66}6768impl<'a> From<&'a BindGroup> for Option<&'a wgpu::BindGroup> {69fn from(value: &'a BindGroup) -> Self {70Some(value.deref())71}72}7374impl<'a> From<&'a mut BindGroup> for Option<&'a wgpu::BindGroup> {75fn from(value: &'a mut BindGroup) -> Self {76Some(&*value)77}78}7980impl Deref for BindGroup {81type Target = wgpu::BindGroup;8283#[inline]84fn deref(&self) -> &Self::Target {85&self.value86}87}8889/// 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 generically93/// 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 generally98/// 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 caller102/// should retry again later.103///104/// # Deriving105///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::ShaderBuffer;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<ShaderBuffer>,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/// ```wgsl135/// @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 groups143/// 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), and151/// [`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>>`] is158/// [`None`], the [`crate::texture::FallbackImage`] resource will be used instead. This attribute can be used in conjunction with a `sampler` binding attribute159/// (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>>`] is174/// [`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`] GPU186/// 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>>`] is188/// [`None`], the [`crate::texture::FallbackImage`] resource will be used instead. This attribute can be used in conjunction with a `texture` binding attribute189/// (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 look199/// up the matching [`Buffer`] GPU resource, which will be bound as a storage200/// buffer in shaders. If the `storage` attribute is used, the field is expected201/// 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 binding203/// 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 defaults241/// 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/// ```wgsl258/// 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 a270/// uniform, much like the field-level `uniform` attribute. The difference is271/// that the entire [`AsBindGroup`] value is converted to `ConvertedShaderType`,272/// which must implement [`ShaderType`], instead of a specific field273/// implementing [`ShaderType`]. This is useful if more complicated conversion274/// logic is required, or when using bindless mode (see below). The conversion275/// is done using the [`AsBindGroupShaderType<ConvertedShaderType>`] trait,276/// which is automatically implemented if `&Self` implements277/// [`Into<ConvertedShaderType>`]. Outside of bindless mode, only use278/// [`AsBindGroupShaderType`] if access to resources like279/// [`RenderAssets<GpuImage>`] is required.280///281/// * In bindless mode (see `bindless(COUNT)`), this attribute becomes282/// `uniform(BINDLESS_INDEX, ConvertedShaderType,283/// binding_array(BINDING_INDEX))`. The resulting uniform buffers will be284/// available in the shader as a binding array at the given `BINDING_INDEX`. The285/// `BINDLESS_INDEX` specifies the offset of the buffer in the bindless index286/// table.287///288/// For example, suppose that the material slot is stored in a variable named289/// `slot`, the bindless index table is named `material_indices`, and that the290/// first field (index 0) of the bindless index table type is named291/// `material`. Then specifying `#[uniform(0, StandardMaterialUniform,292/// binding_array(10)]` will create a binding array buffer declared in the293/// shader as `var<storage> material_array:294/// binding_array<StandardMaterialUniform>` and accessible as295/// `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 mode301/// 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 of303/// buffers. For example, suppose you had the following declaration:304///305/// ```ignore306/// #[uniform(0, StandardMaterialUniform, binding_array(10))]307/// struct StandardMaterial { ... }308/// ```309///310/// In bindless mode, this will produce a binding matching the following WGSL311/// declaration:312///313/// ```wgsl314/// @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/// ```ignore320/// #[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/// ```wgsl327/// @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 the331/// entire [`AsBindGroup`] to `ConvertedShaderType`, using the332/// [`AsBindGroupShaderType<ConvertedShaderType>`] trait.333///334/// * In non-bindless mode, the structure-level `data` attribute is the same as335/// the structure-level `uniform` attribute and produces a single uniform buffer336/// in the shader. The above example would result in a binding that looks like337/// this in WGSL in non-bindless mode:338///339/// ```wgsl340/// @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 stored349/// as [`AsBindGroup::Data`] as part of the [`AsBindGroup::as_bind_group`] call. This is useful if data needs to be stored alongside350/// the generated bind group, such as a unique identifier for a material's bind group. The most common use case for this attribute351/// is "shader pipeline specialization". See [`SpecializedRenderPipeline`](crate::render_resource::SpecializedRenderPipeline).352///353/// ## `bindless`354///355/// * This switch enables *bindless resources*, which changes the way Bevy356/// supplies resources (textures, and samplers) to the shader. When bindless357/// resources are enabled, and the current platform supports them, Bevy will358/// allocate textures, and samplers into *binding arrays*, separated based on359/// type and will supply your shader with indices into those arrays.360/// * Bindless textures and samplers are placed into the appropriate global361/// array defined in `bevy_render::bindless` (`bindless.wgsl`).362/// * Bevy doesn't currently support bindless buffers, except for those created363/// with the `uniform(BINDLESS_INDEX, ConvertedShaderType,364/// binding_array(BINDING_INDEX))` attribute. If you need to include a buffer in365/// 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 be368/// available. Because not all platforms support bindless resources, you369/// should check for the presence of this definition via `#ifdef` and fall370/// back to standard bindings if it isn't present.371/// * By default, in bindless mode, binding 0 becomes the *bindless index372/// table*, which is an array of structures, each of which contains as many373/// fields of type `u32` as the highest binding number in the structure374/// annotated with `#[derive(AsBindGroup)]`. Again by default, the *i*th field375/// of the bindless index table contains the index of the resource with binding376/// *i* within the appropriate binding array.377/// * In the case of materials, the index of the applicable table within the378/// bindless index table list corresponding to the mesh currently being drawn379/// can be retrieved with380/// `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 the382/// `limit(N)` declaration. For example, `#[bindless(limit(16))]` ensures that383/// each slab will have no more than 16 total resources in it. If you don't384/// specify a limit, Bevy automatically picks a reasonable one for the current385/// platform.386/// * The `index_table(range(M..N), binding(B))` declaration allows you to387/// customize the layout of the bindless index table. This is useful for388/// materials that are composed of multiple bind groups, such as389/// `ExtendedMaterial`. In such cases, there will be multiple bindless index390/// tables, so they can't both be assigned to binding 0 or their bindings will391/// conflict.392/// - The `binding(B)` attribute of the `index_table` attribute allows you to393/// customize the binding (`@binding(B)`, in the shader) at which the index394/// table will be bound.395/// - The `range(M, N)` attribute of the `index_table` attribute allows you to396/// change the mapping from the field index in the bindless index table to the397/// bindless index. Instead of the field at index $i$ being mapped to the398/// bindless index $i$, with the `range(M, N)` attribute the field at index399/// $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 may401/// result in the table being too small to contain all the bindless bindings.402/// * The purpose of bindless mode is to improve performance by reducing403/// state changes. By grouping resources together into binding arrays, Bevy404/// doesn't have to modify GPU state as often, decreasing API and driver405/// overhead.406/// * See the `shaders/shader_material_bindless` example for an example of how407/// to use bindless mode. See the `shaders/extended_material_bindless` example408/// for a more exotic example of bindless mode that demonstrates the409/// `index_table` attribute.410/// * The following diagram illustrates how bindless mode works using a subset411/// of `StandardMaterial`:412///413/// ```text414/// Shader Bindings Sampler Binding Array415/// +----+-----------------------------+ +-----------+-----------+-----+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" can444/// 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, and484/// // 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.501type Data: Send + Sync;502503type Param: SystemParam + 'static;504505/// 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, due510/// to platform limitations. For example, if bindless resources aren't511/// supported on this platform, the actual slot count will be 1.512fn bindless_slot_count() -> Option<BindlessSlabResourceLimit> {513None514}515516/// True if the hardware *actually* supports bindless textures for this517/// type, taking the device and driver capabilities into account.518///519/// If this type doesn't use bindless textures, then the return value from520/// this function is meaningless.521fn bindless_supported(_: &RenderDevice) -> bool {522true523}524525/// label526fn label() -> &'static str;527528/// Creates a bind group for `self` matching the layout defined in [`AsBindGroup::bind_group_layout`].529fn as_bind_group(530&self,531layout_descriptor: &BindGroupLayoutDescriptor,532render_device: &RenderDevice,533pipeline_cache: &PipelineCache,534param: &mut SystemParamItem<'_, '_, Self::Param>,535) -> Result<PreparedBindGroup, AsBindGroupError> {536let layout = &pipeline_cache.get_bind_group_layout(layout_descriptor);537538let UnpreparedBindGroup { bindings } =539Self::unprepared_bind_group(self, layout, render_device, param, false)?;540541let entries = bindings542.iter()543.map(|(index, binding)| BindGroupEntry {544binding: *index,545resource: binding.get_binding(),546})547.collect::<Vec<_>>();548549let bind_group = render_device.create_bind_group(Self::label(), layout, &entries);550551Ok(PreparedBindGroup {552bindings,553bind_group,554})555}556557fn bind_group_data(&self) -> Self::Data;558559/// Returns a vec of (binding index, `OwnedBindingResource`).560///561/// In cases where `OwnedBindingResource` is not available (as for bindless562/// texture arrays currently), an implementor may return563/// `AsBindGroupError::CreateBindGroupDirectly` from this function and564/// instead define `as_bind_group` directly. This may prevent certain565/// features, such as bindless mode, from working correctly.566///567/// Set `force_no_bindless` to true to require that bindless textures *not*568/// be used. `ExtendedMaterial` uses this in order to ensure that the base569/// material doesn't use bindless mode if the extension doesn't.570fn unprepared_bind_group(571&self,572layout: &BindGroupLayout,573render_device: &RenderDevice,574param: &mut SystemParamItem<'_, '_, Self::Param>,575force_no_bindless: bool,576) -> Result<UnpreparedBindGroup, AsBindGroupError>;577578/// Creates the bind group layout matching all bind groups returned by579/// [`AsBindGroup::as_bind_group`]580fn bind_group_layout(render_device: &RenderDevice) -> BindGroupLayout581where582Self: Sized,583{584render_device.create_bind_group_layout(585Self::label(),586&Self::bind_group_layout_entries(render_device, false),587)588}589590/// Creates the bind group layout descriptor matching all bind groups returned by591/// [`AsBindGroup::as_bind_group`]592/// TODO: we only need `RenderDevice` to determine if bindless is supported593fn bind_group_layout_descriptor(render_device: &RenderDevice) -> BindGroupLayoutDescriptor594where595Self: Sized,596{597BindGroupLayoutDescriptor {598label: Self::label().into(),599entries: Self::bind_group_layout_entries(render_device, false),600}601}602603/// Returns a vec of bind group layout entries.604///605/// Set `force_no_bindless` to true to require that bindless textures *not*606/// be used. `ExtendedMaterial` uses this in order to ensure that the base607/// material doesn't use bindless mode if the extension doesn't.608fn bind_group_layout_entries(609render_device: &RenderDevice,610force_no_bindless: bool,611) -> Vec<BindGroupLayoutEntry>612where613Self: Sized;614615fn bindless_descriptor() -> Option<BindlessDescriptor> {616None617}618}619620/// An error that occurs during [`AsBindGroup::as_bind_group`] calls.621#[derive(Debug, Error)]622pub enum AsBindGroupError {623/// The bind group could not be generated. Try again next frame.624#[error("The bind group could not be generated")]625RetryNextUpdate,626#[error("Create the bind group via `as_bind_group()` instead")]627CreateBindGroupDirectly,628#[error("At binding index {0}, the provided image sampler `{1}` does not match the required sampler type(s) `{2}`.")]629InvalidSamplerType(u32, String, String),630}631632/// A prepared bind group returned as a result of [`AsBindGroup::as_bind_group`].633pub struct PreparedBindGroup {634pub bindings: BindingResources,635pub bind_group: BindGroup,636}637638/// a map containing `OwnedBindingResource`s, keyed by the target binding index639pub struct UnpreparedBindGroup {640pub bindings: BindingResources,641}642643/// A pair of binding index and binding resource, used as part of644/// [`PreparedBindGroup`] and [`UnpreparedBindGroup`].645#[derive(Deref, DerefMut)]646pub struct BindingResources(pub Vec<(u32, OwnedBindingResource)>);647648/// An owned binding resource of any type (ex: a [`Buffer`], [`TextureView`], etc).649/// This is used by types like [`PreparedBindGroup`] to hold a single list of all650/// render resources used by bindings.651#[derive(Debug)]652pub enum OwnedBindingResource {653Buffer(Buffer),654TextureView(TextureViewDimension, TextureView),655Sampler(SamplerBindingType, Sampler),656Data(OwnedData),657}658659/// Data that will be copied into a GPU buffer.660///661/// This corresponds to the `#[data]` attribute in `AsBindGroup`.662#[derive(Debug, Deref, DerefMut)]663pub struct OwnedData(pub Vec<u8>);664665impl OwnedBindingResource {666/// Creates a [`BindingResource`] reference to this667/// [`OwnedBindingResource`].668///669/// Note that this operation panics if passed a670/// [`OwnedBindingResource::Data`], because [`OwnedData`] doesn't itself671/// correspond to any binding and instead requires the672/// `MaterialBindGroupAllocator` to pack it into a buffer.673pub fn get_binding(&self) -> BindingResource<'_> {674match self {675OwnedBindingResource::Buffer(buffer) => buffer.as_entire_binding(),676OwnedBindingResource::TextureView(_, view) => BindingResource::TextureView(view),677OwnedBindingResource::Sampler(_, sampler) => BindingResource::Sampler(sampler),678OwnedBindingResource::Data(_) => panic!("`OwnedData` has no binding resource"),679}680}681}682683/// Converts a value to a [`ShaderType`] for use in a bind group.684///685/// This is automatically implemented for references that implement [`Into`].686/// Generally normal [`Into`] / [`From`] impls should be preferred, but687/// sometimes additional runtime metadata is required.688/// This exists largely to make some [`AsBindGroup`] use cases easier.689pub trait AsBindGroupShaderType<T: ShaderType> {690/// Return the `T` [`ShaderType`] for `self`. When used in [`AsBindGroup`]691/// derives, it is safe to assume that all images in `self` exist.692fn as_bind_group_shader_type(&self, images: &RenderAssets<GpuImage>) -> T;693}694695impl<T, U: ShaderType> AsBindGroupShaderType<U> for T696where697for<'a> &'a T: Into<U>,698{699#[inline]700fn as_bind_group_shader_type(&self, _images: &RenderAssets<GpuImage>) -> U {701self.into()702}703}704705#[cfg(test)]706mod test {707use super::*;708use bevy_asset::Handle;709use bevy_image::Image;710711#[test]712fn texture_visibility() {713#[expect(714dead_code,715reason = "This is a derive macro compilation test. It will not be constructed."716)]717#[derive(AsBindGroup)]718pub struct TextureVisibilityTest {719#[texture(0, visibility(all))]720pub all: Handle<Image>,721#[texture(1, visibility(none))]722pub none: Handle<Image>,723#[texture(2, visibility(fragment))]724pub fragment: Handle<Image>,725#[texture(3, visibility(vertex))]726pub vertex: Handle<Image>,727#[texture(4, visibility(compute))]728pub compute: Handle<Image>,729#[texture(5, visibility(vertex, fragment))]730pub vertex_fragment: Handle<Image>,731#[texture(6, visibility(vertex, compute))]732pub vertex_compute: Handle<Image>,733#[texture(7, visibility(fragment, compute))]734pub fragment_compute: Handle<Image>,735#[texture(8, visibility(vertex, fragment, compute))]736pub vertex_fragment_compute: Handle<Image>,737}738}739}740741742