Swap material and mesh bind groups (#10485)

# Objective
- Materials should be a more frequent rebind then meshes (due to being
able to use a single vertex buffer, such as in #10164) and therefore
should be in a higher bind group.

---

## Changelog
- For 2d and 3d mesh/material setups (but not UI materials, or other
rendering setups such as gizmos, sprites, or text), mesh data is now in
bind group 1, and material data is now in bind group 2, which is swapped
from how they were before.

## Migration Guide
- Custom 2d and 3d mesh/material shaders should now use bind group 2
`@group(2) @binding(x)` for their bound resources, instead of bind group
1.
- Many internal pieces of rendering code have changed so that mesh data
is now in bind group 1, and material data is now in bind group 2.
Semi-custom rendering setups (that don't use the Material or Material2d
APIs) should adapt to these changes.
This commit is contained in:
JMS55 2023-11-28 17:26:22 -05:00 committed by GitHub
parent a902ea6f85
commit 4bf20e7d27
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 88 additions and 141 deletions

View file

@ -6,8 +6,8 @@
}
#import bevy_core_pipeline::tonemapping::tone_mapping
@group(1) @binding(0) var my_array_texture: texture_2d_array<f32>;
@group(1) @binding(1) var my_array_texture_sampler: sampler;
@group(2) @binding(0) var my_array_texture: texture_2d_array<f32>;
@group(2) @binding(1) var my_array_texture_sampler: sampler;
@fragment
fn fragment(

View file

@ -12,10 +12,9 @@ var<uniform> input: CustomUiMaterial;
fn fragment(in: UiVertexOutput) -> @location(0) vec4<f32> {
// the UVs are now adjusted around the middle of the rect.
let uv = in.uv * 2.0 - 1.0;
// circle alpha, the higher the power the harsher the falloff.
let alpha = 1.0 - pow(sqrt(dot(uv, uv)), 100.0);
return vec4<f32>(input.color.rgb, alpha);
}

View file

@ -1,12 +1,12 @@
#import bevy_pbr::forward_io::VertexOutput
#ifdef CUBEMAP_ARRAY
@group(1) @binding(0) var base_color_texture: texture_cube_array<f32>;
@group(2) @binding(0) var base_color_texture: texture_cube_array<f32>;
#else
@group(1) @binding(0) var base_color_texture: texture_cube<f32>;
@group(2) @binding(0) var base_color_texture: texture_cube<f32>;
#endif
@group(1) @binding(1) var base_color_sampler: sampler;
@group(2) @binding(1) var base_color_sampler: sampler;
@fragment
fn fragment(

View file

@ -3,10 +3,10 @@ layout(location = 0) in vec2 v_Uv;
layout(location = 0) out vec4 o_Target;
layout(set = 1, binding = 0) uniform vec4 CustomMaterial_color;
layout(set = 2, binding = 0) uniform vec4 CustomMaterial_color;
layout(set = 1, binding = 1) uniform texture2D CustomMaterial_texture;
layout(set = 1, binding = 2) uniform sampler CustomMaterial_sampler;
layout(set = 2, binding = 1) uniform texture2D CustomMaterial_texture;
layout(set = 2, binding = 2) uniform sampler CustomMaterial_sampler;
// wgsl modules can be imported and used in glsl
// FIXME - this doesn't work any more ...

View file

@ -23,9 +23,9 @@ struct Mesh {
};
#ifdef PER_OBJECT_BUFFER_BATCH_SIZE
layout(set = 2, binding = 0) uniform Mesh Meshes[#{PER_OBJECT_BUFFER_BATCH_SIZE}];
layout(set = 1, binding = 0) uniform Mesh Meshes[#{PER_OBJECT_BUFFER_BATCH_SIZE}];
#else
layout(set = 2, binding = 0) readonly buffer _Meshes {
layout(set = 1, binding = 0) readonly buffer _Meshes {
Mesh Meshes[];
};
#endif // PER_OBJECT_BUFFER_BATCH_SIZE

View file

@ -2,9 +2,9 @@
// we can import items from shader modules in the assets folder with a quoted path
#import "shaders/custom_material_import.wgsl"::COLOR_MULTIPLIER
@group(1) @binding(0) var<uniform> material_color: vec4<f32>;
@group(1) @binding(1) var material_color_texture: texture_2d<f32>;
@group(1) @binding(2) var material_color_sampler: sampler;
@group(2) @binding(0) var<uniform> material_color: vec4<f32>;
@group(2) @binding(1) var material_color_texture: texture_2d<f32>;
@group(2) @binding(2) var material_color_sampler: sampler;
@fragment
fn fragment(

View file

@ -4,8 +4,8 @@
utils::coords_to_viewport_uv,
}
@group(1) @binding(0) var texture: texture_2d<f32>;
@group(1) @binding(1) var texture_sampler: sampler;
@group(2) @binding(0) var texture: texture_2d<f32>;
@group(2) @binding(1) var texture_sampler: sampler;
@fragment
fn fragment(

View file

@ -3,7 +3,7 @@
struct CustomMaterial {
color: vec4<f32>,
};
@group(1) @binding(0) var<uniform> material: CustomMaterial;
@group(2) @binding(0) var<uniform> material: CustomMaterial;
struct Vertex {
@builtin(instance_index) instance_index: u32,

View file

@ -19,7 +19,7 @@ struct MyExtendedMaterial {
quantize_steps: u32,
}
@group(1) @binding(100)
@group(2) @binding(100)
var<uniform> my_extended_material: MyExtendedMaterial;
@fragment

View file

@ -1,22 +1,22 @@
#import bevy_pbr::forward_io::VertexOutput
@group(1) @binding(0) var test_texture_1d: texture_1d<f32>;
@group(1) @binding(1) var test_texture_1d_sampler: sampler;
@group(2) @binding(0) var test_texture_1d: texture_1d<f32>;
@group(2) @binding(1) var test_texture_1d_sampler: sampler;
@group(1) @binding(2) var test_texture_2d: texture_2d<f32>;
@group(1) @binding(3) var test_texture_2d_sampler: sampler;
@group(2) @binding(2) var test_texture_2d: texture_2d<f32>;
@group(2) @binding(3) var test_texture_2d_sampler: sampler;
@group(1) @binding(4) var test_texture_2d_array: texture_2d_array<f32>;
@group(1) @binding(5) var test_texture_2d_array_sampler: sampler;
@group(2) @binding(4) var test_texture_2d_array: texture_2d_array<f32>;
@group(2) @binding(5) var test_texture_2d_array_sampler: sampler;
@group(1) @binding(6) var test_texture_cube: texture_cube<f32>;
@group(1) @binding(7) var test_texture_cube_sampler: sampler;
@group(2) @binding(6) var test_texture_cube: texture_cube<f32>;
@group(2) @binding(7) var test_texture_cube_sampler: sampler;
@group(1) @binding(8) var test_texture_cube_array: texture_cube_array<f32>;
@group(1) @binding(9) var test_texture_cube_array_sampler: sampler;
@group(2) @binding(8) var test_texture_cube_array: texture_cube_array<f32>;
@group(2) @binding(9) var test_texture_cube_array_sampler: sampler;
@group(1) @binding(10) var test_texture_3d: texture_3d<f32>;
@group(1) @binding(11) var test_texture_3d_sampler: sampler;
@group(2) @binding(10) var test_texture_3d: texture_3d<f32>;
@group(2) @binding(11) var test_texture_3d_sampler: sampler;
@fragment
fn fragment(in: VertexOutput) {}

View file

@ -4,7 +4,7 @@ struct LineMaterial {
color: vec4<f32>,
};
@group(1) @binding(0) var<uniform> material: LineMaterial;
@group(2) @binding(0) var<uniform> material: LineMaterial;
@fragment
fn fragment(

View file

@ -4,7 +4,7 @@ struct CustomMaterial {
color: vec4<f32>,
};
@group(1) @binding(0) var<uniform> material: CustomMaterial;
@group(2) @binding(0) var<uniform> material: CustomMaterial;
@fragment
fn fragment(

View file

@ -11,7 +11,7 @@ struct ShowPrepassSettings {
padding_1: u32,
padding_2: u32,
}
@group(1) @binding(0) var<uniform> settings: ShowPrepassSettings;
@group(2) @binding(0) var<uniform> settings: ShowPrepassSettings;
@fragment
fn fragment(

View file

@ -1,7 +1,7 @@
#import bevy_pbr::forward_io::VertexOutput
@group(1) @binding(0) var textures: binding_array<texture_2d<f32>>;
@group(1) @binding(1) var nearest_sampler: sampler;
@group(2) @binding(0) var textures: binding_array<texture_2d<f32>>;
@group(2) @binding(1) var nearest_sampler: sampler;
// We can also have array of samplers
// var samplers: binding_array<sampler>;

View file

@ -87,9 +87,9 @@ use std::marker::PhantomData;
/// In WGSL shaders, the material's binding would look like this:
///
/// ```wgsl
/// @group(1) @binding(0) var<uniform> color: vec4<f32>;
/// @group(1) @binding(1) var color_texture: texture_2d<f32>;
/// @group(1) @binding(2) var color_sampler: sampler;
/// @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;
/// ```
pub trait Material: Asset + AsBindGroup + Clone + Sized {
/// Returns this material's vertex shader. If [`ShaderRef::Default`] is returned, the default mesh vertex shader
@ -335,7 +335,7 @@ where
descriptor.fragment.as_mut().unwrap().shader = fragment_shader.clone();
}
descriptor.layout.insert(1, self.material_layout.clone());
descriptor.layout.insert(2, self.material_layout.clone());
M::specialize(self, &mut descriptor, layout, key)?;
Ok(descriptor)
@ -368,8 +368,8 @@ impl<M: Material> FromWorld for MaterialPipeline<M> {
type DrawMaterial<M> = (
SetItemPipeline,
SetMeshViewBindGroup<0>,
SetMaterialBindGroup<M, 1>,
SetMeshBindGroup<2>,
SetMeshBindGroup<1>,
SetMaterialBindGroup<M, 2>,
DrawMesh,
);

View file

@ -320,7 +320,7 @@ where
// NOTE: Eventually, it would be nice to only add this when the shaders are overloaded by the Material.
// The main limitation right now is that bind group order is hardcoded in shaders.
bind_group_layouts.insert(1, self.material_layout.clone());
bind_group_layouts.push(self.material_layout.clone());
#[cfg(all(feature = "webgl", target_arch = "wasm32"))]
shader_defs.push("WEBGL2".into());
@ -421,7 +421,7 @@ where
&mut shader_defs,
&mut vertex_attributes,
);
bind_group_layouts.insert(2, bind_group);
bind_group_layouts.insert(1, bind_group);
let vertex_buffer_layout = layout.get_layout(&vertex_attributes)?;
@ -919,8 +919,8 @@ impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetPrepassViewBindGroup<
pub type DrawPrepass<M> = (
SetItemPipeline,
SetPrepassViewBindGroup<0>,
SetMaterialBindGroup<M, 1>,
SetMeshBindGroup<2>,
SetMeshBindGroup<1>,
SetMaterialBindGroup<M, 2>,
DrawMesh,
);

View file

@ -4,4 +4,4 @@
@group(0) @binding(2) var<uniform> previous_view_proj: mat4x4<f32>;
#endif // MOTION_VECTOR_PREPASS
// Material bindings will be in @group(1)
// Material bindings will be in @group(2)

View file

@ -339,9 +339,9 @@ pub struct MeshPipeline {
/// Use code like this in custom shaders:
/// ```wgsl
/// ##ifdef PER_OBJECT_BUFFER_BATCH_SIZE
/// @group(2) @binding(0) var<uniform> mesh: array<Mesh, #{PER_OBJECT_BUFFER_BATCH_SIZE}u>;
/// @group(1) @binding(0) var<uniform> mesh: array<Mesh, #{PER_OBJECT_BUFFER_BATCH_SIZE}u>;
/// ##else
/// @group(2) @binding(0) var<storage> mesh: array<Mesh>;
/// @group(1) @binding(0) var<storage> mesh: array<Mesh>;
/// ##endif // PER_OBJECT_BUFFER_BATCH_SIZE
/// ```
pub per_object_buffer_batch_size: Option<u32>,

View file

@ -2,20 +2,8 @@
#import bevy_pbr::mesh_types::Mesh
#ifdef MESH_BINDGROUP_1
#ifdef PER_OBJECT_BUFFER_BATCH_SIZE
@group(1) @binding(0) var<uniform> mesh: array<Mesh, #{PER_OBJECT_BUFFER_BATCH_SIZE}u>;
#else
@group(1) @binding(0) var<storage> mesh: array<Mesh>;
#endif // PER_OBJECT_BUFFER_BATCH_SIZE
#else // MESH_BINDGROUP_1
#ifdef PER_OBJECT_BUFFER_BATCH_SIZE
@group(2) @binding(0) var<uniform> mesh: array<Mesh, #{PER_OBJECT_BUFFER_BATCH_SIZE}u>;
#else
@group(2) @binding(0) var<storage> mesh: array<Mesh>;
#endif // PER_OBJECT_BUFFER_BATCH_SIZE
#endif // MESH_BINDGROUP_1

View file

@ -4,19 +4,9 @@
#import bevy_pbr::mesh_types::MorphWeights;
#ifdef MESH_BINDGROUP_1
@group(1) @binding(2) var<uniform> morph_weights: MorphWeights;
@group(1) @binding(3) var morph_targets: texture_3d<f32>;
#else
@group(2) @binding(2) var<uniform> morph_weights: MorphWeights;
@group(2) @binding(3) var morph_targets: texture_3d<f32>;
#endif
// NOTE: Those are the "hardcoded" values found in `MorphAttributes` struct
// in crates/bevy_render/src/mesh/morph/visitors.rs
// In an ideal world, the offsets are established dynamically and passed as #defines

View file

@ -2,24 +2,24 @@
#import bevy_pbr::pbr_types::StandardMaterial
@group(1) @binding(0) var<uniform> material: StandardMaterial;
@group(1) @binding(1) var base_color_texture: texture_2d<f32>;
@group(1) @binding(2) var base_color_sampler: sampler;
@group(1) @binding(3) var emissive_texture: texture_2d<f32>;
@group(1) @binding(4) var emissive_sampler: sampler;
@group(1) @binding(5) var metallic_roughness_texture: texture_2d<f32>;
@group(1) @binding(6) var metallic_roughness_sampler: sampler;
@group(1) @binding(7) var occlusion_texture: texture_2d<f32>;
@group(1) @binding(8) var occlusion_sampler: sampler;
@group(1) @binding(9) var normal_map_texture: texture_2d<f32>;
@group(1) @binding(10) var normal_map_sampler: sampler;
@group(1) @binding(11) var depth_map_texture: texture_2d<f32>;
@group(1) @binding(12) var depth_map_sampler: sampler;
@group(2) @binding(0) var<uniform> material: StandardMaterial;
@group(2) @binding(1) var base_color_texture: texture_2d<f32>;
@group(2) @binding(2) var base_color_sampler: sampler;
@group(2) @binding(3) var emissive_texture: texture_2d<f32>;
@group(2) @binding(4) var emissive_sampler: sampler;
@group(2) @binding(5) var metallic_roughness_texture: texture_2d<f32>;
@group(2) @binding(6) var metallic_roughness_sampler: sampler;
@group(2) @binding(7) var occlusion_texture: texture_2d<f32>;
@group(2) @binding(8) var occlusion_sampler: sampler;
@group(2) @binding(9) var normal_map_texture: texture_2d<f32>;
@group(2) @binding(10) var normal_map_sampler: sampler;
@group(2) @binding(11) var depth_map_texture: texture_2d<f32>;
@group(2) @binding(12) var depth_map_sampler: sampler;
#ifdef PBR_TRANSMISSION_TEXTURES_SUPPORTED
@group(1) @binding(13) var specular_transmission_texture: texture_2d<f32>;
@group(1) @binding(14) var specular_transmission_sampler: sampler;
@group(1) @binding(15) var thickness_texture: texture_2d<f32>;
@group(1) @binding(16) var thickness_sampler: sampler;
@group(1) @binding(17) var diffuse_transmission_texture: texture_2d<f32>;
@group(1) @binding(18) var diffuse_transmission_sampler: sampler;
@group(2) @binding(13) var specular_transmission_texture: texture_2d<f32>;
@group(2) @binding(14) var specular_transmission_sampler: sampler;
@group(2) @binding(15) var thickness_texture: texture_2d<f32>;
@group(2) @binding(16) var thickness_sampler: sampler;
@group(2) @binding(17) var diffuse_transmission_texture: texture_2d<f32>;
@group(2) @binding(18) var diffuse_transmission_sampler: sampler;
#endif

View file

@ -4,12 +4,7 @@
#ifdef SKINNED
#ifdef MESH_BINDGROUP_1
@group(1) @binding(1) var<uniform> joint_matrices: SkinnedMesh;
#else
@group(2) @binding(1) var<uniform> joint_matrices: SkinnedMesh;
#endif
@group(1) @binding(1) var<uniform> joint_matrices: SkinnedMesh;
fn skin_model(
indexes: vec4<u32>,

View file

@ -4,7 +4,7 @@ struct WireframeMaterial {
color: vec4<f32>,
};
@group(1) @binding(0)
@group(2) @binding(0)
var<uniform> material: WireframeMaterial;
@fragment
fn fragment(in: VertexOutput) -> @location(0) vec4<f32> {

View file

@ -93,13 +93,13 @@ impl Deref for BindGroup {
/// In WGSL shaders, the binding would look like this:
///
/// ```wgsl
/// @group(1) @binding(0) var<uniform> color: vec4<f32>;
/// @group(1) @binding(1) var color_texture: texture_2d<f32>;
/// @group(1) @binding(2) var color_sampler: sampler;
/// @group(1) @binding(3) var<storage> values: array<f32>;
/// @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>;
/// ```
/// 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 1.
/// are generally bound to group 2.
///
/// The following field-level attributes are supported:
///
@ -191,7 +191,7 @@ impl Deref for BindGroup {
/// roughness: f32,
/// };
///
/// @group(1) @binding(0) var<uniform> material: CoolMaterial;
/// @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:

View file

@ -14,9 +14,9 @@ struct ColorMaterial {
};
const COLOR_MATERIAL_FLAGS_TEXTURE_BIT: u32 = 1u;
@group(1) @binding(0) var<uniform> material: ColorMaterial;
@group(1) @binding(1) var texture: texture_2d<f32>;
@group(1) @binding(2) var texture_sampler: sampler;
@group(2) @binding(0) var<uniform> material: ColorMaterial;
@group(2) @binding(1) var texture: texture_2d<f32>;
@group(2) @binding(2) var texture_sampler: sampler;
@fragment
fn fragment(

View file

@ -95,9 +95,9 @@ use crate::{
/// color: vec4<f32>,
/// }
///
/// @group(1) @binding(0) var<uniform> material: CustomMaterial;
/// @group(1) @binding(1) var color_texture: texture_2d<f32>;
/// @group(1) @binding(2) var color_sampler: sampler;
/// @group(2) @binding(0) var<uniform> material: CustomMaterial;
/// @group(2) @binding(1) var color_texture: texture_2d<f32>;
/// @group(2) @binding(2) var color_sampler: sampler;
/// ```
pub trait Material2d: AsBindGroup + Asset + Clone + Sized {
/// Returns this material's vertex shader. If [`ShaderRef::Default`] is returned, the default mesh vertex shader
@ -281,8 +281,8 @@ where
}
descriptor.layout = vec![
self.mesh2d_pipeline.view_layout.clone(),
self.material2d_layout.clone(),
self.mesh2d_pipeline.mesh_layout.clone(),
self.material2d_layout.clone(),
];
M::specialize(&mut descriptor, layout, key)?;
@ -317,8 +317,8 @@ impl<M: Material2d> FromWorld for Material2dPipeline<M> {
type DrawMaterial2d<M> = (
SetItemPipeline,
SetMesh2dViewBindGroup<0>,
SetMaterial2dBindGroup<M, 1>,
SetMesh2dBindGroup<2>,
SetMesh2dBindGroup<1>,
SetMaterial2dBindGroup<M, 2>,
DrawMesh2d,
);

View file

@ -2,20 +2,8 @@
#import bevy_sprite::mesh2d_types::Mesh2d
#ifdef MESH_BINDGROUP_1
#ifdef PER_OBJECT_BUFFER_BATCH_SIZE
@group(1) @binding(0) var<uniform> mesh: array<Mesh2d, #{PER_OBJECT_BUFFER_BATCH_SIZE}u>;
#else
@group(1) @binding(0) var<storage> mesh: array<Mesh2d>;
#endif // PER_OBJECT_BUFFER_BATCH_SIZE
#else // MESH_BINDGROUP_1
#ifdef PER_OBJECT_BUFFER_BATCH_SIZE
@group(2) @binding(0) var<uniform> mesh: array<Mesh2d, #{PER_OBJECT_BUFFER_BATCH_SIZE}u>;
#else
@group(2) @binding(0) var<storage> mesh: array<Mesh2d>;
#endif // PER_OBJECT_BUFFER_BATCH_SIZE
#endif // MESH_BINDGROUP_1

View file

@ -151,24 +151,19 @@ impl SpecializedRenderPipeline for ColoredMesh2dPipeline {
false => TextureFormat::bevy_default(),
};
// Meshes typically live in bind group 2. Because we are using bind group 1
// we need to add the MESH_BINDGROUP_1 shader def so that the bindings are correctly
// linked in the shader.
let shader_defs = vec!["MESH_BINDGROUP_1".into()];
RenderPipelineDescriptor {
vertex: VertexState {
// Use our custom shader
shader: COLORED_MESH2D_SHADER_HANDLE,
entry_point: "vertex".into(),
shader_defs: shader_defs.clone(),
shader_defs: vec![],
// Use our custom vertex buffer
buffers: vec![vertex_layout],
},
fragment: Some(FragmentState {
// Use our custom shader
shader: COLORED_MESH2D_SHADER_HANDLE,
shader_defs,
shader_defs: vec![],
entry_point: "fragment".into(),
targets: vec![Some(ColorTargetState {
format,

View file

@ -204,14 +204,6 @@ impl SpecializedMeshPipeline for CustomPipeline {
) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
let mut descriptor = self.mesh_pipeline.specialize(key, layout)?;
// meshes typically live in bind group 2. because we are using bindgroup 1
// we need to add MESH_BINDGROUP_1 shader def so that the bindings are correctly
// linked in the shader
descriptor
.vertex
.shader_defs
.push("MESH_BINDGROUP_1".into());
descriptor.vertex.shader = self.shader.clone();
descriptor.vertex.buffers.push(VertexBufferLayout {
array_stride: std::mem::size_of::<InstanceData>() as u64,

View file

@ -146,7 +146,7 @@ impl AsBindGroup for BindlessMaterial {
Self: Sized,
{
vec![
// @group(1) @binding(0) var textures: binding_array<texture_2d<f32>>;
// @group(2) @binding(0) var textures: binding_array<texture_2d<f32>>;
BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStages::FRAGMENT,
@ -157,7 +157,7 @@ impl AsBindGroup for BindlessMaterial {
},
count: NonZeroU32::new(MAX_TEXTURE_COUNT as u32),
},
// @group(1) @binding(1) var nearest_sampler: sampler;
// @group(2) @binding(1) var nearest_sampler: sampler;
BindGroupLayoutEntry {
binding: 1,
visibility: ShaderStages::FRAGMENT,