pub trait AsBindGroup {
    type Data: Send + Sync;

    // Required methods
    fn unprepared_bind_group(
        &self,
        layout: &BindGroupLayout,
        render_device: &RenderDevice,
        images: &RenderAssets<Image>,
        fallback_image: &FallbackImage
    ) -> Result<UnpreparedBindGroup<Self::Data>, AsBindGroupError>;
    fn bind_group_layout_entries(
        render_device: &RenderDevice
    ) -> Vec<BindGroupLayoutEntry>
       where Self: Sized;

    // Provided methods
    fn label() -> Option<&'static str> { ... }
    fn as_bind_group(
        &self,
        layout: &BindGroupLayout,
        render_device: &RenderDevice,
        images: &RenderAssets<Image>,
        fallback_image: &FallbackImage
    ) -> Result<PreparedBindGroup<Self::Data>, AsBindGroupError> { ... }
    fn bind_group_layout(render_device: &RenderDevice) -> BindGroupLayout
       where Self: Sized { ... }
}
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<Image> and 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: Color,
    #[texture(1)]
    #[sampler(2)]
    color_texture: Handle<Image>,
    #[storage(3, read_only)]
    values: Vec<f32>,
    #[storage(4, read_only, buffer)]
    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> values: 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 a Buffer, and bound as a uniform. ShaderType is implemented for most math types already, such as f32, Vec4, and Color. It can also be derived for custom structs.
  • texture(BINDING_INDEX, arguments)

ArgumentsValuesDefault
dimension = “…”"1d", "2d", "2d_array", "3d", "cube", "cube_array""2d"
sample_type = “…”"float", "depth", "s_int" or "u_int""float"
filterable = …true, falsetrue
multisampled = …true, falsefalse
visibility(...)all, none, or a list-combination of vertex, fragment, computevertex, fragment
ArgumentsValuesDefault
dimension = “…”"1d", "2d", "2d_array", "3d", "cube", "cube_array""2d"
image_format = …any member of TextureFormatRgba8Unorm
access = …any member of StorageTextureAccessReadWrite
visibility(...)all, none, or a list-combination of vertex, fragment, computecompute
ArgumentsValuesDefault
sampler_type = “…”"filtering", "non_filtering", "comparison"."filtering"
visibility(...)all, none, or a list-combination of vertex, fragment, computevertex, fragment
  • storage(BINDING_INDEX, arguments)
    • The field will be converted to a shader-compatible type using the ShaderType trait, written to a Buffer, and bound as a storage buffer.
    • It supports and optional read_only parameter. Defaults to false if not present.
ArgumentsValuesDefault
visibility(...)all, none, or a list-combination of vertex, fragment, computevertex, fragment
read_onlyif present then value is true, otherwise falsefalse

Note that fields without field-level binding attributes will be ignored.

#[derive(AsBindGroup)]
struct CoolMaterial {
    #[uniform(0)]
    color: Color,
    this_field_is_ignored: String,
}

As mentioned above, Option<Handle<Image>> is also supported:

#[derive(AsBindGroup)]
struct CoolMaterial {
    #[uniform(0)]
    color: Color,
    #[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 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: Color,
    #[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:

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: Color,
    roughness: f32,
}

#[derive(ShaderType)]
struct CoolMaterialUniform {
    color: Color,
    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: Color,
    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 Data: Send + Sync

Data that will be stored alongside the “prepared” bind group.

Required Methods§

fn unprepared_bind_group( &self, layout: &BindGroupLayout, render_device: &RenderDevice, images: &RenderAssets<Image>, fallback_image: &FallbackImage ) -> 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 define as_bind_group directly. This may prevent certain features from working correctly.

fn bind_group_layout_entries( render_device: &RenderDevice ) -> Vec<BindGroupLayoutEntry>
where Self: Sized,

Returns a vec of bind group layout entries

Provided Methods§

fn label() -> Option<&'static str>

label

fn as_bind_group( &self, layout: &BindGroupLayout, render_device: &RenderDevice, images: &RenderAssets<Image>, fallback_image: &FallbackImage ) -> Result<PreparedBindGroup<Self::Data>, AsBindGroupError>

Creates a bind group for self matching the layout defined in AsBindGroup::bind_group_layout.

fn bind_group_layout(render_device: &RenderDevice) -> BindGroupLayout
where Self: Sized,

Creates the bind group layout matching all bind groups returned by AsBindGroup::as_bind_group

Examples found in repository?
examples/shader/compute_shader_game_of_life.rs (line 133)
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
    fn from_world(world: &mut World) -> Self {
        let render_device = world.resource::<RenderDevice>();
        let texture_bind_group_layout = GameOfLifeImage::bind_group_layout(render_device);
        let shader = world
            .resource::<AssetServer>()
            .load("shaders/game_of_life.wgsl");
        let pipeline_cache = world.resource::<PipelineCache>();
        let init_pipeline = pipeline_cache.queue_compute_pipeline(ComputePipelineDescriptor {
            label: None,
            layout: vec![texture_bind_group_layout.clone()],
            push_constant_ranges: Vec::new(),
            shader: shader.clone(),
            shader_defs: vec![],
            entry_point: Cow::from("init"),
        });
        let update_pipeline = pipeline_cache.queue_compute_pipeline(ComputePipelineDescriptor {
            label: None,
            layout: vec![texture_bind_group_layout.clone()],
            push_constant_ranges: Vec::new(),
            shader,
            shader_defs: vec![],
            entry_point: Cow::from("update"),
        });

        GameOfLifePipeline {
            texture_bind_group_layout,
            init_pipeline,
            update_pipeline,
        }
    }

Object Safety§

This trait is not object safe.

Implementors§