pub trait AsBindGroup {
type Data: Send + Sync;
type Param: SystemParam + 'static;
// Required methods
fn unprepared_bind_group(
&self,
layout: &BindGroupLayout,
render_device: &RenderDevice,
param: &mut SystemParamItem<'_, '_, Self::Param>,
force_no_bindless: bool,
) -> Result<UnpreparedBindGroup<Self::Data>, AsBindGroupError>;
fn bind_group_layout_entries(
render_device: &RenderDevice,
force_no_bindless: bool,
) -> Vec<BindGroupLayoutEntry>
where Self: Sized;
// Provided methods
fn bindless_slot_count() -> Option<BindlessSlabResourceLimit> { ... }
fn bindless_supported(_: &RenderDevice) -> bool { ... }
fn label() -> Option<&'static str> { ... }
fn as_bind_group(
&self,
layout: &BindGroupLayout,
render_device: &RenderDevice,
param: &mut SystemParamItem<'_, '_, Self::Param>,
) -> Result<PreparedBindGroup<Self::Data>, AsBindGroupError> { ... }
fn bind_group_layout(render_device: &RenderDevice) -> BindGroupLayout
where Self: Sized { ... }
fn bindless_descriptor() -> Option<BindlessDescriptor> { ... }
}
Expand description
Converts a value to a BindGroup
with a given BindGroupLayout
, which can then be used in Bevy shaders.
This trait can be derived (and generally should be). Read on for details and examples.
This is an opinionated trait that is intended to make it easy to generically
convert a type into a BindGroup
. It provides access to specific render resources,
such as RenderAssets<GpuImage>
and crate::texture::FallbackImage
. If a type has a Handle<Image>
,
these can be used to retrieve the corresponding Texture
resource.
AsBindGroup::as_bind_group
is intended to be called once, then the result cached somewhere. It is generally
ok to do “expensive” work here, such as creating a Buffer
for a uniform.
If for some reason a BindGroup
cannot be created yet (for example, the Texture
for an Image
hasn’t loaded yet), just return AsBindGroupError::RetryNextUpdate
, which signals that the caller
should retry again later.
§Deriving
This trait can be derived. Field attributes like uniform
and texture
are used to define which fields should be bindings,
what their binding type is, and what index they should be bound at:
#[derive(AsBindGroup)]
struct CoolMaterial {
#[uniform(0)]
color: LinearRgba,
#[texture(1)]
#[sampler(2)]
color_texture: Handle<Image>,
#[storage(3, read_only)]
storage_buffer: Handle<ShaderStorageBuffer>,
#[storage(4, read_only, buffer)]
raw_buffer: Buffer,
#[storage_texture(5)]
storage_texture: Handle<Image>,
}
In WGSL shaders, the binding would look like this:
@group(2) @binding(0) var<uniform> color: vec4<f32>;
@group(2) @binding(1) var color_texture: texture_2d<f32>;
@group(2) @binding(2) var color_sampler: sampler;
@group(2) @binding(3) var<storage> storage_buffer: array<f32>;
@group(2) @binding(4) var<storage> raw_buffer: array<f32>;
@group(2) @binding(5) var storage_texture: texture_storage_2d<rgba8unorm, read_write>;
Note that the “group” index is determined by the usage context. It is not defined in AsBindGroup
. For example, in Bevy material bind groups
are generally bound to group 2.
The following field-level attributes are supported:
§uniform(BINDING_INDEX)
- The field will be converted to a shader-compatible type using the
ShaderType
trait, written to aBuffer
, and bound as a uniform.ShaderType
is implemented for most math types already, such asf32
,Vec4
, andLinearRgba
. It can also be derived for custom structs.
§texture(BINDING_INDEX, arguments)
- This field’s
Handle<Image>
will be used to look up the matchingTexture
GPU resource, which will be bound as a texture in shaders. The field will be assumed to implementInto<Option<Handle<Image>>>
. In practice, most fields should be aHandle<Image>
orOption<Handle<Image>>
. If the value of anOption<Handle<Image>>
isNone
, thecrate::texture::FallbackImage
resource will be used instead. This attribute can be used in conjunction with asampler
binding attribute (with a different binding index) if a binding of the sampler for theImage
is also required.
Arguments | Values | Default |
---|---|---|
dimension = “…” | "1d" , "2d" , "2d_array" , "3d" , "cube" , "cube_array" | "2d" |
sample_type = “…” | "float" , "depth" , "s_int" or "u_int" | "float" |
filterable = … | true , false | true |
multisampled = … | true , false | false |
visibility(...) | all , none , or a list-combination of vertex , fragment , compute | vertex , fragment |
§storage_texture(BINDING_INDEX, arguments)
- This field’s
Handle<Image>
will be used to look up the matchingTexture
GPU resource, which will be bound as a storage texture in shaders. The field will be assumed to implementInto<Option<Handle<Image>>>
. In practice, most fields should be aHandle<Image>
orOption<Handle<Image>>
. If the value of anOption<Handle<Image>>
isNone
, thecrate::texture::FallbackImage
resource will be used instead.
Arguments | Values | Default |
---|---|---|
dimension = “…” | "1d" , "2d" , "2d_array" , "3d" , "cube" , "cube_array" | "2d" |
image_format = … | any member of TextureFormat | Rgba8Unorm |
access = … | any member of StorageTextureAccess | ReadWrite |
visibility(...) | all , none , or a list-combination of vertex , fragment , compute | compute |
§sampler(BINDING_INDEX, arguments)
- This field’s
Handle<Image>
will be used to look up the matchingSampler
GPU resource, which will be bound as a sampler in shaders. The field will be assumed to implementInto<Option<Handle<Image>>>
. In practice, most fields should be aHandle<Image>
orOption<Handle<Image>>
. If the value of anOption<Handle<Image>>
isNone
, thecrate::texture::FallbackImage
resource will be used instead. This attribute can be used in conjunction with atexture
binding attribute (with a different binding index) if a binding of the texture for theImage
is also required.
Arguments | Values | Default |
---|---|---|
sampler_type = “…” | "filtering" , "non_filtering" , "comparison" . | "filtering" |
visibility(...) | all , none , or a list-combination of vertex , fragment , compute | vertex , fragment |
§storage(BINDING_INDEX, arguments)
- The field’s
Handle<Storage>
will be used to look up the matchingBuffer
GPU resource, which will be bound as a storage buffer in shaders. If thestorage
attribute is used, the field is expected a raw buffer, and the buffer will be bound as a storage buffer in shaders. In bindless mode,binding_array()
argument that specifies the binding number of the resulting storage buffer binding array must be present.
Arguments | Values | Default |
---|---|---|
visibility(...) | all , none , or a list-combination of vertex , fragment , compute | vertex , fragment |
read_only | if present then value is true, otherwise false | false |
buffer | if present then the field will be assumed to be a raw wgpu buffer | |
binding_array(...) | the binding number of the binding array, for bindless mode | bindless mode disabled |
Note that fields without field-level binding attributes will be ignored.
#[derive(AsBindGroup)]
struct CoolMaterial {
#[uniform(0)]
color: LinearRgba,
this_field_is_ignored: String,
}
As mentioned above, Option<Handle<Image>>
is also supported:
#[derive(AsBindGroup)]
struct CoolMaterial {
#[uniform(0)]
color: LinearRgba,
#[texture(1)]
#[sampler(2)]
color_texture: Option<Handle<Image>>,
}
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
to “pure white”.
Field uniforms with the same index will be combined into a single binding:
#[derive(AsBindGroup)]
struct CoolMaterial {
#[uniform(0)]
color: LinearRgba,
#[uniform(0)]
roughness: f32,
}
In WGSL shaders, the binding would look like this:
struct CoolMaterial {
color: vec4<f32>,
roughness: f32,
};
@group(2) @binding(0) var<uniform> material: CoolMaterial;
Some less common scenarios will require “struct-level” attributes. These are the currently supported struct-level attributes:
§uniform(BINDING_INDEX, ConvertedShaderType)
-
This also creates a
Buffer
usingShaderType
and binds it as a uniform, much like the field-leveluniform
attribute. The difference is that the entireAsBindGroup
value is converted toConvertedShaderType
, which must implementShaderType
, instead of a specific field implementingShaderType
. This is useful if more complicated conversion logic is required, or when using bindless mode (see below). The conversion is done using theAsBindGroupShaderType<ConvertedShaderType>
trait, which is automatically implemented if&Self
implementsInto<ConvertedShaderType>
. Outside of bindless mode, only useAsBindGroupShaderType
if access to resources likeRenderAssets<GpuImage>
is required. -
In bindless mode (see
bindless(COUNT)
), this attribute becomesuniform(BINDLESS_INDEX, ConvertedShaderType, binding_array(BINDING_INDEX))
. The resulting uniform buffers will be available in the shader as a binding array at the givenBINDING_INDEX
. TheBINDLESS_INDEX
specifies the offset of the buffer in the bindless index table.For example, suppose that the material slot is stored in a variable named
slot
, the bindless index table is namedmaterial_indices
, and that the first field (index 0) of the bindless index table type is namedmaterial
. Then specifying#[uniform(0, StandardMaterialUniform, binding_array(10)]
will create a binding array buffer declared in the shader asvar<storage> material_array: binding_array<StandardMaterialUniform>
and accessible asmaterial_array[material_indices[slot].material]
.
§data(BINDING_INDEX, ConvertedShaderType, binding_array(BINDING_INDEX))
- This is very similar to
uniform(BINDING_INDEX, ConvertedShaderType, binding_array(BINDING_INDEX)
and in fact is identical if bindless mode isn’t being used. The difference is that, in bindless mode, thedata
attribute produces a single buffer containing an array, not an array of buffers. For example, suppose you had the following declaration:
#[uniform(0, StandardMaterialUniform, binding_array(10))]
struct StandardMaterial { ... }
In bindless mode, this will produce a binding matching the following WGSL declaration:
@group(2) @binding(10) var<storage> material_array: binding_array<StandardMaterial>;
On the other hand, if you write this declaration:
#[data(0, StandardMaterialUniform, binding_array(10))]
struct StandardMaterial { ... }
Then Bevy produces a binding that matches this WGSL declaration instead:
@group(2) @binding(10) var<storage> material_array: array<StandardMaterial>;
-
Just as with the structure-level
uniform
attribute, Bevy converts the entireAsBindGroup
toConvertedShaderType
, using theAsBindGroupShaderType<ConvertedShaderType>
trait. -
In non-bindless mode, the structure-level
data
attribute is the same as the structure-leveluniform
attribute and produces a single uniform buffer in the shader. The above example would result in a binding that looks like this in WGSL in non-bindless mode:
@group(2) @binding(0) var<uniform> material: StandardMaterial;
- For efficiency reasons,
data
is generally preferred overuniform
unless you need to place your data in individual buffers.
§bind_group_data(DataType)
- The
AsBindGroup
type will be converted to someDataType
usingInto<DataType>
and stored asAsBindGroup::Data
as part of theAsBindGroup::as_bind_group
call. This is useful if data needs to be stored alongside the generated bind group, such as a unique identifier for a material’s bind group. The most common use case for this attribute is “shader pipeline specialization”. SeeSpecializedRenderPipeline
.
§bindless
- This switch enables bindless resources, which changes the way Bevy supplies resources (textures, and samplers) to the shader. When bindless resources are enabled, and the current platform supports them, Bevy will allocate textures, and samplers into binding arrays, separated based on type and will supply your shader with indices into those arrays.
- Bindless textures and samplers are placed into the appropriate global
array defined in
bevy_render::bindless
(bindless.wgsl
). - Bevy doesn’t currently support bindless buffers, except for those created
with the
uniform(BINDLESS_INDEX, ConvertedShaderType, binding_array(BINDING_INDEX))
attribute. If you need to include a buffer in your object, and you can’t create the data in that buffer with theuniform
attribute, consider a non-bindless object instead. - If bindless mode is enabled, the
BINDLESS
definition will be available. Because not all platforms support bindless resources, you should check for the presence of this definition via#ifdef
and fall back to standard bindings if it isn’t present. - By default, in bindless mode, binding 0 becomes the bindless index
table, which is an array of structures, each of which contains as many
fields of type
u32
as the highest binding number in the structure annotated with#[derive(AsBindGroup)]
. Again by default, the ith field of the bindless index table contains the index of the resource with binding i within the appropriate binding array. - In the case of materials, the index of the applicable table within the
bindless index table list corresponding to the mesh currently being drawn
can be retrieved with
mesh[in.instance_index].material_and_lightmap_bind_group_slot & 0xffffu
. - You can limit the size of the bindless slabs to N resources with the
limit(N)
declaration. For example,#[bindless(limit(16))]
ensures that each slab will have no more than 16 total resources in it. If you don’t specify a limit, Bevy automatically picks a reasonable one for the current platform. - The
index_table(range(M..N), binding(B))
declaration allows you to customize the layout of the bindless index table. This is useful for materials that are composed of multiple bind groups, such asExtendedMaterial
. In such cases, there will be multiple bindless index tables, so they can’t both be assigned to binding 0 or their bindings will conflict.- The
binding(B)
attribute of theindex_table
attribute allows you to customize the binding (@binding(B)
, in the shader) at which the index table will be bound. - The
range(M, N)
attribute of theindex_table
attribute allows you to change the mapping from the field index in the bindless index table to the bindless index. Instead of the field at index $i$ being mapped to the bindless index $i$, with therange(M, N)
attribute the field at index $i$ in the bindless index table is mapped to the bindless index $i$ + M. The size of the index table will be set to N - M. Note that this may result in the table being too small to contain all the bindless bindings.
- The
- The purpose of bindless mode is to improve performance by reducing state changes. By grouping resources together into binding arrays, Bevy doesn’t have to modify GPU state as often, decreasing API and driver overhead.
- See the
shaders/shader_material_bindless
example for an example of how to use bindless mode. See theshaders/extended_material_bindless
example for a more exotic example of bindless mode that demonstrates theindex_table
attribute. - The following diagram illustrates how bindless mode works using a subset
of
StandardMaterial
:
Shader Bindings Sampler Binding Array
+----+-----------------------------+ +-----------+-----------+-----+
+---| 0 | material_indices | +->| sampler 0 | sampler 1 | ... |
| +----+-----------------------------+ | +-----------+-----------+-----+
| | 1 | bindless_samplers_filtering +--+ ^
| +----+-----------------------------+ +-------------------------------+
| | .. | ... | |
| +----+-----------------------------+ Texture Binding Array |
| | 5 | bindless_textures_2d +--+ +-----------+-----------+-----+ |
| +----+-----------------------------+ +->| texture 0 | texture 1 | ... | |
| | .. | ... | +-----------+-----------+-----+ |
| +----+-----------------------------+ ^ |
| + 10 | material_array +--+ +---------------------------+ |
| +----+-----------------------------+ | | |
| | Buffer Binding Array | |
| | +----------+----------+-----+ | |
| +->| buffer 0 | buffer 1 | ... | | |
| Material Bindless Indices +----------+----------+-----+ | |
| +----+-----------------------------+ ^ | |
+-->| 0 | material +----------+ | |
+----+-----------------------------+ | |
| 1 | base_color_texture +---------------------------------------+ |
+----+-----------------------------+ |
| 2 | base_color_sampler +-------------------------------------------+
+----+-----------------------------+
| .. | ... |
+----+-----------------------------+
The previous CoolMaterial
example illustrating “combining multiple field-level uniform attributes with the same binding index” can
also be equivalently represented with a single struct-level uniform attribute:
#[derive(AsBindGroup)]
#[uniform(0, CoolMaterialUniform)]
struct CoolMaterial {
color: LinearRgba,
roughness: f32,
}
#[derive(ShaderType)]
struct CoolMaterialUniform {
color: LinearRgba,
roughness: f32,
}
impl From<&CoolMaterial> for CoolMaterialUniform {
fn from(material: &CoolMaterial) -> CoolMaterialUniform {
CoolMaterialUniform {
color: material.color,
roughness: material.roughness,
}
}
}
Setting bind_group_data
looks like this:
#[derive(AsBindGroup)]
#[bind_group_data(CoolMaterialKey)]
struct CoolMaterial {
#[uniform(0)]
color: LinearRgba,
is_shaded: bool,
}
#[derive(Copy, Clone, Hash, Eq, PartialEq)]
struct CoolMaterialKey {
is_shaded: bool,
}
impl From<&CoolMaterial> for CoolMaterialKey {
fn from(material: &CoolMaterial) -> CoolMaterialKey {
CoolMaterialKey {
is_shaded: material.is_shaded,
}
}
}
Required Associated Types§
type Param: SystemParam + 'static
Required Methods§
Sourcefn unprepared_bind_group(
&self,
layout: &BindGroupLayout,
render_device: &RenderDevice,
param: &mut SystemParamItem<'_, '_, Self::Param>,
force_no_bindless: bool,
) -> Result<UnpreparedBindGroup<Self::Data>, AsBindGroupError>
fn unprepared_bind_group( &self, layout: &BindGroupLayout, render_device: &RenderDevice, param: &mut SystemParamItem<'_, '_, Self::Param>, force_no_bindless: bool, ) -> Result<UnpreparedBindGroup<Self::Data>, AsBindGroupError>
Returns a vec of (binding index, OwnedBindingResource
).
In cases where OwnedBindingResource
is not available (as for bindless
texture arrays currently), an implementor may return
AsBindGroupError::CreateBindGroupDirectly
from this function and
instead define as_bind_group
directly. This may prevent certain
features, such as bindless mode, from working correctly.
Set force_no_bindless
to true to require that bindless textures not
be used. ExtendedMaterial
uses this in order to ensure that the base
material doesn’t use bindless mode if the extension doesn’t.
Sourcefn bind_group_layout_entries(
render_device: &RenderDevice,
force_no_bindless: bool,
) -> Vec<BindGroupLayoutEntry>where
Self: Sized,
fn bind_group_layout_entries(
render_device: &RenderDevice,
force_no_bindless: bool,
) -> Vec<BindGroupLayoutEntry>where
Self: Sized,
Returns a vec of bind group layout entries.
Set force_no_bindless
to true to require that bindless textures not
be used. ExtendedMaterial
uses this in order to ensure that the base
material doesn’t use bindless mode if the extension doesn’t.
Provided Methods§
Sourcefn bindless_slot_count() -> Option<BindlessSlabResourceLimit>
fn bindless_slot_count() -> Option<BindlessSlabResourceLimit>
The number of slots per bind group, if bindless mode is enabled.
If this bind group doesn’t use bindless, then this will be None
.
Note that the actual slot count may be different from this value, due to platform limitations. For example, if bindless resources aren’t supported on this platform, the actual slot count will be 1.
Sourcefn bindless_supported(_: &RenderDevice) -> bool
fn bindless_supported(_: &RenderDevice) -> bool
True if the hardware actually supports bindless textures for this type, taking the device and driver capabilities into account.
If this type doesn’t use bindless textures, then the return value from this function is meaningless.
Sourcefn as_bind_group(
&self,
layout: &BindGroupLayout,
render_device: &RenderDevice,
param: &mut SystemParamItem<'_, '_, Self::Param>,
) -> Result<PreparedBindGroup<Self::Data>, AsBindGroupError>
fn as_bind_group( &self, layout: &BindGroupLayout, render_device: &RenderDevice, param: &mut SystemParamItem<'_, '_, Self::Param>, ) -> Result<PreparedBindGroup<Self::Data>, AsBindGroupError>
Creates a bind group for self
matching the layout defined in AsBindGroup::bind_group_layout
.
Sourcefn bind_group_layout(render_device: &RenderDevice) -> BindGroupLayoutwhere
Self: Sized,
fn bind_group_layout(render_device: &RenderDevice) -> BindGroupLayoutwhere
Self: Sized,
Creates the bind group layout matching all bind groups returned by
AsBindGroup::as_bind_group
fn bindless_descriptor() -> Option<BindlessDescriptor>
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.