Ugrade to wgpu version 25.0 (#19563)

# Objective

Upgrade to `wgpu` version `25.0`.

Depends on https://github.com/bevyengine/naga_oil/pull/121

## Solution

### Problem

The biggest issue we face upgrading is the following requirement:
> To facilitate this change, there was an additional validation rule put
in place: if there is a binding array in a bind group, you may not use
dynamic offset buffers or uniform buffers in that bind group. This
requirement comes from vulkan rules on UpdateAfterBind descriptors.

This is a major difficulty for us, as there are a number of binding
arrays that are used in the view bind group. Note, this requirement does
not affect merely uniform buffors that use dynamic offset but the use of
*any* uniform in a bind group that also has a binding array.

### Attempted fixes

The easiest fix would be to change uniforms to be storage buffers
whenever binding arrays are in use:
```wgsl
#ifdef BINDING_ARRAYS_ARE_USED
@group(0) @binding(0) var<uniform> view: View;
@group(0) @binding(1) var<uniform> lights: types::Lights;
#else
@group(0) @binding(0) var<storage> view: array<View>;
@group(0) @binding(1) var<storage> lights: array<types::Lights>;
#endif
```

This requires passing the view index to the shader so that we know where
to index into the buffer:

```wgsl
struct PushConstants {
    view_index: u32,
}

var<push_constant> push_constants: PushConstants;
```

Using push constants is no problem because binding arrays are only
usable on native anyway.

However, this greatly complicates the ability to access `view` in
shaders. For example:
```wgsl
#ifdef BINDING_ARRAYS_ARE_USED
mesh_view_bindings::view.view_from_world[0].z
#else
mesh_view_bindings::view[mesh_view_bindings::view_index].view_from_world[0].z
#endif
```

Using this approach would work but would have the effect of polluting
our shaders with ifdef spam basically *everywhere*.

Why not use a function? Unfortunately, the following is not valid wgsl
as it returns a binding directly from a function in the uniform path.

```wgsl
fn get_view() -> View {
#if BINDING_ARRAYS_ARE_USED
    let view_index = push_constants.view_index;
    let view = views[view_index];
#endif
    return view;
}
```

This also poses problems for things like lights where we want to return
a ptr to the light data. Returning ptrs from wgsl functions isn't
allowed even if both bindings were buffers.

The next attempt was to simply use indexed buffers everywhere, in both
the binding array and non binding array path. This would be viable if
push constants were available everywhere to pass the view index, but
unfortunately they are not available on webgpu. This means either
passing the view index in a storage buffer (not ideal for such a small
amount of state) or using push constants sometimes and uniform buffers
only on webgpu. However, this kind of conditional layout infects
absolutely everything.

Even if we were to accept just using storage buffer for the view index,
there's also the additional problem that some dynamic offsets aren't
actually per-view but per-use of a setting on a camera, which would
require passing that uniform data on *every* camera regardless of
whether that rendering feature is being used, which is also gross.

As such, although it's gross, the simplest solution just to bump binding
arrays into `@group(1)` and all other bindings up one bind group. This
should still bring us under the device limit of 4 for most users.

### Next steps / looking towards the future

I'd like to avoid needing split our view bind group into multiple parts.
In the future, if `wgpu` were to add `@builtin(draw_index)`, we could
build a list of draw state in gpu processing and avoid the need for any
kind of state change at all (see
https://github.com/gfx-rs/wgpu/issues/6823). This would also provide
significantly more flexibility to handle things like offsets into other
arrays that may not be per-view.

### Testing

Tested a number of examples, there are probably more that are still
broken.

---------

Co-authored-by: François Mockers <mockersf@gmail.com>
Co-authored-by: Elabajaba <Elabajaba@users.noreply.github.com>
This commit is contained in:
charlotte 🌸 2025-06-26 12:41:47 -07:00 committed by GitHub
parent 92e65d5eb1
commit 96dcbc5f8c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
74 changed files with 719 additions and 446 deletions

View File

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

View File

@ -3,8 +3,8 @@
view_transformations::position_world_to_clip view_transformations::position_world_to_clip
} }
@group(2) @binding(0) var texture: texture_2d<f32>; @group(3) @binding(0) var texture: texture_2d<f32>;
@group(2) @binding(1) var texture_sampler: sampler; @group(3) @binding(1) var texture_sampler: sampler;
struct Vertex { struct Vertex {
@builtin(instance_index) instance_index: u32, @builtin(instance_index) instance_index: u32,

View File

@ -15,12 +15,12 @@ struct MaterialBindings {
} }
#ifdef BINDLESS #ifdef BINDLESS
@group(2) @binding(0) var<storage> materials: array<MaterialBindings>; @group(3) @binding(0) var<storage> materials: array<MaterialBindings>;
@group(2) @binding(10) var<storage> material_color: binding_array<Color>; @group(3) @binding(10) var<storage> material_color: binding_array<Color>;
#else // BINDLESS #else // BINDLESS
@group(2) @binding(0) var<uniform> material_color: Color; @group(3) @binding(0) var<uniform> material_color: Color;
@group(2) @binding(1) var material_color_texture: texture_2d<f32>; @group(3) @binding(1) var material_color_texture: texture_2d<f32>;
@group(2) @binding(2) var material_color_sampler: sampler; @group(3) @binding(2) var material_color_sampler: sampler;
#endif // BINDLESS #endif // BINDLESS
@fragment @fragment

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -42,19 +42,19 @@ struct ExampleBindlessExtendedMaterial {
// The indices of the bindless resources in the bindless resource arrays, for // The indices of the bindless resources in the bindless resource arrays, for
// the `ExampleBindlessExtension` fields. // the `ExampleBindlessExtension` fields.
@group(2) @binding(100) var<storage> example_extended_material_indices: @group(3) @binding(100) var<storage> example_extended_material_indices:
array<ExampleBindlessExtendedMaterialIndices>; array<ExampleBindlessExtendedMaterialIndices>;
// An array that holds the `ExampleBindlessExtendedMaterial` plain old data, // An array that holds the `ExampleBindlessExtendedMaterial` plain old data,
// indexed by `ExampleBindlessExtendedMaterialIndices.material`. // indexed by `ExampleBindlessExtendedMaterialIndices.material`.
@group(2) @binding(101) var<storage> example_extended_material: @group(3) @binding(101) var<storage> example_extended_material:
array<ExampleBindlessExtendedMaterial>; array<ExampleBindlessExtendedMaterial>;
#else // BINDLESS #else // BINDLESS
// In non-bindless mode, we simply use a uniform for the plain old data. // In non-bindless mode, we simply use a uniform for the plain old data.
@group(2) @binding(50) var<uniform> example_extended_material: ExampleBindlessExtendedMaterial; @group(3) @binding(50) var<uniform> example_extended_material: ExampleBindlessExtendedMaterial;
@group(2) @binding(51) var modulate_texture: texture_2d<f32>; @group(3) @binding(51) var modulate_texture: texture_2d<f32>;
@group(2) @binding(52) var modulate_sampler: sampler; @group(3) @binding(52) var modulate_sampler: sampler;
#endif // BINDLESS #endif // BINDLESS

View File

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

View File

@ -12,7 +12,7 @@ struct VoxelVisualizationIrradianceVolumeInfo {
intensity: f32, intensity: f32,
} }
@group(2) @binding(100) @group(3) @binding(100)
var<uniform> irradiance_volume_info: VoxelVisualizationIrradianceVolumeInfo; var<uniform> irradiance_volume_info: VoxelVisualizationIrradianceVolumeInfo;
@fragment @fragment

View File

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

View File

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

View File

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

View File

@ -3,7 +3,7 @@
view_transformations::position_world_to_clip view_transformations::position_world_to_clip
} }
@group(2) @binding(0) var<storage, read> colors: array<vec4<f32>, 5>; @group(3) @binding(0) var<storage, read> colors: array<vec4<f32>, 5>;
struct Vertex { struct Vertex {
@builtin(instance_index) instance_index: u32, @builtin(instance_index) instance_index: u32,

View File

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

View File

@ -23,9 +23,9 @@ struct WaterSettings {
@group(0) @binding(1) var<uniform> globals: Globals; @group(0) @binding(1) var<uniform> globals: Globals;
@group(2) @binding(100) var water_normals_texture: texture_2d<f32>; @group(3) @binding(100) var water_normals_texture: texture_2d<f32>;
@group(2) @binding(101) var water_normals_sampler: sampler; @group(3) @binding(101) var water_normals_sampler: sampler;
@group(2) @binding(102) var<uniform> water_settings: WaterSettings; @group(3) @binding(102) var<uniform> water_settings: WaterSettings;
// Samples a single octave of noise and returns the resulting normal. // Samples a single octave of noise and returns the resulting normal.
fn sample_noise_octave(uv: vec2<f32>, strength: f32) -> vec3<f32> { fn sample_noise_octave(uv: vec2<f32>, strength: f32) -> vec3<f32> {

View File

@ -20,7 +20,7 @@ serde = { version = "1.0", features = [
], default-features = false, optional = true } ], default-features = false, optional = true }
thiserror = { version = "2", default-features = false } thiserror = { version = "2", default-features = false }
derive_more = { version = "2", default-features = false, features = ["from"] } derive_more = { version = "2", default-features = false, features = ["from"] }
wgpu-types = { version = "24", default-features = false, optional = true } wgpu-types = { version = "25", default-features = false, optional = true }
encase = { version = "0.10", default-features = false, optional = true } encase = { version = "0.10", default-features = false, optional = true }
[features] [features]

View File

@ -123,8 +123,7 @@ impl SpecializedRenderPipeline for LineGizmoPipeline {
.mesh_pipeline .mesh_pipeline
.get_view_layout(key.view_key.into()) .get_view_layout(key.view_key.into())
.clone(); .clone();
let layout = vec![view_layout.main_layout.clone(), self.uniform_layout.clone()];
let layout = vec![view_layout, self.uniform_layout.clone()];
let fragment_entry_point = match key.line_style { let fragment_entry_point = match key.line_style {
GizmoLineStyle::Solid => "fragment_solid", GizmoLineStyle::Solid => "fragment_solid",
@ -220,8 +219,7 @@ impl SpecializedRenderPipeline for LineJointGizmoPipeline {
.mesh_pipeline .mesh_pipeline
.get_view_layout(key.view_key.into()) .get_view_layout(key.view_key.into())
.clone(); .clone();
let layout = vec![view_layout.main_layout.clone(), self.uniform_layout.clone()];
let layout = vec![view_layout, self.uniform_layout.clone()];
if key.joints == GizmoLineJoint::None { if key.joints == GizmoLineJoint::None {
error!("There is no entry point for line joints with GizmoLineJoints::None. Please consider aborting the drawing process before reaching this stage."); error!("There is no entry point for line joints with GizmoLineJoints::None. Please consider aborting the drawing process before reaching this stage.");

View File

@ -63,7 +63,7 @@ image = { version = "0.25.2", default-features = false }
# misc # misc
bitflags = { version = "2.3", features = ["serde"] } bitflags = { version = "2.3", features = ["serde"] }
bytemuck = { version = "1.5" } bytemuck = { version = "1.5" }
wgpu-types = { version = "24", default-features = false } wgpu-types = { version = "25", default-features = false }
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
thiserror = { version = "2", default-features = false } thiserror = { version = "2", default-features = false }
futures-lite = "2.0.1" futures-lite = "2.0.1"

View File

@ -27,7 +27,7 @@ bevy_platform = { path = "../bevy_platform", version = "0.17.0-dev", default-fea
# other # other
bitflags = { version = "2.3", features = ["serde"] } bitflags = { version = "2.3", features = ["serde"] }
bytemuck = { version = "1.5" } bytemuck = { version = "1.5" }
wgpu-types = { version = "24", default-features = false } wgpu-types = { version = "25", default-features = false }
serde = { version = "1", default-features = false, features = [ serde = { version = "1", default-features = false, features = [
"derive", "derive",
], optional = true } ], optional = true }

View File

@ -1,3 +1,5 @@
enable dual_source_blending;
#import bevy_pbr::atmosphere::{ #import bevy_pbr::atmosphere::{
types::{Atmosphere, AtmosphereSettings}, types::{Atmosphere, AtmosphereSettings},
bindings::{atmosphere, view, atmosphere_transforms}, bindings::{atmosphere, view, atmosphere_transforms},
@ -19,9 +21,11 @@
#endif #endif
struct RenderSkyOutput { struct RenderSkyOutput {
@location(0) inscattering: vec4<f32>,
#ifdef DUAL_SOURCE_BLENDING #ifdef DUAL_SOURCE_BLENDING
@location(0) @second_blend_source transmittance: vec4<f32>, @location(0) @blend_src(0) inscattering: vec4<f32>,
@location(0) @blend_src(1) transmittance: vec4<f32>,
#else
@location(0) inscattering: vec4<f32>,
#endif #endif
} }

View File

@ -10,7 +10,7 @@
} }
#import bevy_render::maths::project_onto #import bevy_render::maths::project_onto
@group(2) @binding(200) @group(3) @binding(200)
var<uniform> inv_depth_fade_factor: f32; var<uniform> inv_depth_fade_factor: f32;
struct ForwardDecalInformation { struct ForwardDecalInformation {

View File

@ -29,7 +29,7 @@ struct PbrDeferredLightingDepthId {
_webgl2_padding_2: f32, _webgl2_padding_2: f32,
#endif #endif
} }
@group(1) @binding(0) @group(2) @binding(0)
var<uniform> depth_id: PbrDeferredLightingDepthId; var<uniform> depth_id: PbrDeferredLightingDepthId;
@vertex @vertex

View File

@ -184,9 +184,9 @@ impl ViewNode for DeferredOpaquePass3dPbrLightingNode {
return Ok(()); return Ok(());
}; };
let bind_group_1 = render_context.render_device().create_bind_group( let bind_group_2 = render_context.render_device().create_bind_group(
"deferred_lighting_layout_group_1", "deferred_lighting_layout_group_2",
&deferred_lighting_layout.bind_group_layout_1, &deferred_lighting_layout.bind_group_layout_2,
&BindGroupEntries::single(deferred_lighting_pass_id_binding), &BindGroupEntries::single(deferred_lighting_pass_id_binding),
); );
@ -208,7 +208,7 @@ impl ViewNode for DeferredOpaquePass3dPbrLightingNode {
render_pass.set_render_pipeline(pipeline); render_pass.set_render_pipeline(pipeline);
render_pass.set_bind_group( render_pass.set_bind_group(
0, 0,
&mesh_view_bind_group.value, &mesh_view_bind_group.main,
&[ &[
view_uniform_offset.offset, view_uniform_offset.offset,
view_lights_offset.offset, view_lights_offset.offset,
@ -218,7 +218,8 @@ impl ViewNode for DeferredOpaquePass3dPbrLightingNode {
**view_environment_map_offset, **view_environment_map_offset,
], ],
); );
render_pass.set_bind_group(1, &bind_group_1, &[]); render_pass.set_bind_group(1, &mesh_view_bind_group.binding_array, &[]);
render_pass.set_bind_group(2, &bind_group_2, &[]);
render_pass.draw(0..3, 0..1); render_pass.draw(0..3, 0..1);
Ok(()) Ok(())
@ -228,7 +229,7 @@ impl ViewNode for DeferredOpaquePass3dPbrLightingNode {
#[derive(Resource)] #[derive(Resource)]
pub struct DeferredLightingLayout { pub struct DeferredLightingLayout {
mesh_pipeline: MeshPipeline, mesh_pipeline: MeshPipeline,
bind_group_layout_1: BindGroupLayout, bind_group_layout_2: BindGroupLayout,
deferred_lighting_shader: Handle<Shader>, deferred_lighting_shader: Handle<Shader>,
} }
@ -346,11 +347,13 @@ impl SpecializedRenderPipeline for DeferredLightingLayout {
#[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
shader_defs.push("SIXTEEN_BYTE_ALIGNMENT".into()); shader_defs.push("SIXTEEN_BYTE_ALIGNMENT".into());
let layout = self.mesh_pipeline.get_view_layout(key.into());
RenderPipelineDescriptor { RenderPipelineDescriptor {
label: Some("deferred_lighting_pipeline".into()), label: Some("deferred_lighting_pipeline".into()),
layout: vec![ layout: vec![
self.mesh_pipeline.get_view_layout(key.into()).clone(), layout.main_layout.clone(),
self.bind_group_layout_1.clone(), layout.binding_array_layout.clone(),
self.bind_group_layout_2.clone(),
], ],
vertex: VertexState { vertex: VertexState {
shader: self.deferred_lighting_shader.clone(), shader: self.deferred_lighting_shader.clone(),
@ -408,7 +411,7 @@ impl FromWorld for DeferredLightingLayout {
); );
Self { Self {
mesh_pipeline: world.resource::<MeshPipeline>().clone(), mesh_pipeline: world.resource::<MeshPipeline>().clone(),
bind_group_layout_1: layout, bind_group_layout_2: layout,
deferred_lighting_shader: load_embedded_asset!(world, "deferred_lighting.wgsl"), deferred_lighting_shader: load_embedded_asset!(world, "deferred_lighting.wgsl"),
} }
} }

View File

@ -152,8 +152,8 @@ fn shader_ref(path: PathBuf) -> ShaderRef {
const MESHLET_VISIBILITY_BUFFER_RESOLVE_SHADER_HANDLE: Handle<Shader> = const MESHLET_VISIBILITY_BUFFER_RESOLVE_SHADER_HANDLE: Handle<Shader> =
weak_handle!("69187376-3dea-4d0f-b3f5-185bde63d6a2"); weak_handle!("69187376-3dea-4d0f-b3f5-185bde63d6a2");
pub const TONEMAPPING_LUT_TEXTURE_BINDING_INDEX: u32 = 26; pub const TONEMAPPING_LUT_TEXTURE_BINDING_INDEX: u32 = 18;
pub const TONEMAPPING_LUT_SAMPLER_BINDING_INDEX: u32 = 27; pub const TONEMAPPING_LUT_SAMPLER_BINDING_INDEX: u32 = 19;
/// Sets up the entire PBR infrastructure of bevy. /// Sets up the entire PBR infrastructure of bevy.
pub struct PbrPlugin { pub struct PbrPlugin {

View File

@ -3,11 +3,11 @@
#import bevy_pbr::mesh_bindings::mesh #import bevy_pbr::mesh_bindings::mesh
#ifdef MULTIPLE_LIGHTMAPS_IN_ARRAY #ifdef MULTIPLE_LIGHTMAPS_IN_ARRAY
@group(1) @binding(4) var lightmaps_textures: binding_array<texture_2d<f32>, 4>; @group(2) @binding(4) var lightmaps_textures: binding_array<texture_2d<f32>, 4>;
@group(1) @binding(5) var lightmaps_samplers: binding_array<sampler, 4>; @group(2) @binding(5) var lightmaps_samplers: binding_array<sampler, 4>;
#else // MULTIPLE_LIGHTMAPS_IN_ARRAY #else // MULTIPLE_LIGHTMAPS_IN_ARRAY
@group(1) @binding(4) var lightmaps_texture: texture_2d<f32>; @group(2) @binding(4) var lightmaps_texture: texture_2d<f32>;
@group(1) @binding(5) var lightmaps_sampler: sampler; @group(2) @binding(5) var lightmaps_sampler: sampler;
#endif // MULTIPLE_LIGHTMAPS_IN_ARRAY #endif // MULTIPLE_LIGHTMAPS_IN_ARRAY
// Samples the lightmap, if any, and returns indirect illumination from it. // Samples the lightmap, if any, and returns indirect illumination from it.

View File

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

View File

@ -186,13 +186,17 @@ pub fn prepare_material_meshlet_meshes_main_opaque_pass<M: Material>(
let mut shader_defs = material_fragment.shader_defs; let mut shader_defs = material_fragment.shader_defs;
shader_defs.push("MESHLET_MESH_MATERIAL_PASS".into()); shader_defs.push("MESHLET_MESH_MATERIAL_PASS".into());
let layout = mesh_pipeline.get_view_layout(view_key.into());
let layout = vec![
layout.main_layout.clone(),
layout.binding_array_layout.clone(),
resource_manager.material_shade_bind_group_layout.clone(),
material_pipeline.material_layout.clone(),
];
let pipeline_descriptor = RenderPipelineDescriptor { let pipeline_descriptor = RenderPipelineDescriptor {
label: material_pipeline_descriptor.label, label: material_pipeline_descriptor.label,
layout: vec![ layout,
mesh_pipeline.get_view_layout(view_key.into()).clone(),
resource_manager.material_shade_bind_group_layout.clone(),
material_pipeline.material_layout.clone(),
],
push_constant_ranges: vec![], push_constant_ranges: vec![],
vertex: VertexState { vertex: VertexState {
shader: MESHLET_MESH_MATERIAL_SHADER_HANDLE, shader: MESHLET_MESH_MATERIAL_SHADER_HANDLE,

View File

@ -106,7 +106,7 @@ impl ViewNode for MeshletMainOpaquePass3dNode {
render_pass.set_bind_group( render_pass.set_bind_group(
0, 0,
&mesh_view_bind_group.value, &mesh_view_bind_group.main,
&[ &[
view_uniform_offset.offset, view_uniform_offset.offset,
view_lights_offset.offset, view_lights_offset.offset,

View File

@ -149,15 +149,15 @@ fn get_meshlet_vertex_position(meshlet: ptr<function, Meshlet>, vertex_id: u32)
#endif #endif
#ifdef MESHLET_MESH_MATERIAL_PASS #ifdef MESHLET_MESH_MATERIAL_PASS
@group(1) @binding(0) var meshlet_visibility_buffer: texture_storage_2d<r64uint, read>; @group(2) @binding(0) var meshlet_visibility_buffer: texture_storage_2d<r64uint, read>;
@group(1) @binding(1) var<storage, read> meshlet_cluster_meshlet_ids: array<u32>; // Per cluster @group(2) @binding(1) var<storage, read> meshlet_cluster_meshlet_ids: array<u32>; // Per cluster
@group(1) @binding(2) var<storage, read> meshlets: array<Meshlet>; // Per meshlet @group(2) @binding(2) var<storage, read> meshlets: array<Meshlet>; // Per meshlet
@group(1) @binding(3) var<storage, read> meshlet_indices: array<u32>; // Many per meshlet @group(2) @binding(3) var<storage, read> meshlet_indices: array<u32>; // Many per meshlet
@group(1) @binding(4) var<storage, read> meshlet_vertex_positions: array<u32>; // Many per meshlet @group(2) @binding(4) var<storage, read> meshlet_vertex_positions: array<u32>; // Many per meshlet
@group(1) @binding(5) var<storage, read> meshlet_vertex_normals: array<u32>; // Many per meshlet @group(2) @binding(5) var<storage, read> meshlet_vertex_normals: array<u32>; // Many per meshlet
@group(1) @binding(6) var<storage, read> meshlet_vertex_uvs: array<vec2<f32>>; // Many per meshlet @group(2) @binding(6) var<storage, read> meshlet_vertex_uvs: array<vec2<f32>>; // Many per meshlet
@group(1) @binding(7) var<storage, read> meshlet_cluster_instance_ids: array<u32>; // Per cluster @group(2) @binding(7) var<storage, read> meshlet_cluster_instance_ids: array<u32>; // Per cluster
@group(1) @binding(8) var<storage, read> meshlet_instance_uniforms: array<Mesh>; // Per entity instance @group(2) @binding(8) var<storage, read> meshlet_instance_uniforms: array<Mesh>; // Per entity instance
// TODO: Load only twice, instead of 3x in cases where you load 3 indices per thread? // TODO: Load only twice, instead of 3x in cases where you load 3 indices per thread?
fn get_meshlet_vertex_id(index_id: u32) -> u32 { fn get_meshlet_vertex_id(index_id: u32) -> u32 {

View File

@ -206,7 +206,7 @@ impl ResourceManager {
visibility_buffer_raster_bind_group_layout: render_device.create_bind_group_layout( visibility_buffer_raster_bind_group_layout: render_device.create_bind_group_layout(
"meshlet_visibility_buffer_raster_bind_group_layout", "meshlet_visibility_buffer_raster_bind_group_layout",
&BindGroupLayoutEntries::sequential( &BindGroupLayoutEntries::sequential(
ShaderStages::all(), ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE,
( (
storage_buffer_read_only_sized(false, None), storage_buffer_read_only_sized(false, None),
storage_buffer_read_only_sized(false, None), storage_buffer_read_only_sized(false, None),
@ -225,7 +225,7 @@ impl ResourceManager {
.create_bind_group_layout( .create_bind_group_layout(
"meshlet_visibility_buffer_raster_shadow_view_bind_group_layout", "meshlet_visibility_buffer_raster_shadow_view_bind_group_layout",
&BindGroupLayoutEntries::sequential( &BindGroupLayoutEntries::sequential(
ShaderStages::all(), ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE,
( (
storage_buffer_read_only_sized(false, None), storage_buffer_read_only_sized(false, None),
storage_buffer_read_only_sized(false, None), storage_buffer_read_only_sized(false, None),

View File

@ -95,7 +95,6 @@ where
Render, Render,
prepare_prepass_view_bind_group::<M>.in_set(RenderSystems::PrepareBindGroups), prepare_prepass_view_bind_group::<M>.in_set(RenderSystems::PrepareBindGroups),
) )
.init_resource::<PrepassViewBindGroup>()
.init_resource::<SpecializedMeshPipelines<PrepassPipeline<M>>>() .init_resource::<SpecializedMeshPipelines<PrepassPipeline<M>>>()
.allow_ambiguous_resource::<SpecializedMeshPipelines<PrepassPipeline<M>>>(); .allow_ambiguous_resource::<SpecializedMeshPipelines<PrepassPipeline<M>>>();
} }
@ -105,7 +104,9 @@ where
return; return;
}; };
render_app.init_resource::<PrepassPipeline<M>>(); render_app
.init_resource::<PrepassPipeline<M>>()
.init_resource::<PrepassViewBindGroup>();
} }
} }
@ -272,6 +273,7 @@ pub struct PrepassPipelineInternal {
pub view_layout_motion_vectors: BindGroupLayout, pub view_layout_motion_vectors: BindGroupLayout,
pub view_layout_no_motion_vectors: BindGroupLayout, pub view_layout_no_motion_vectors: BindGroupLayout,
pub mesh_layouts: MeshLayouts, pub mesh_layouts: MeshLayouts,
pub empty_layout: BindGroupLayout,
pub material_layout: BindGroupLayout, pub material_layout: BindGroupLayout,
pub prepass_material_vertex_shader: Option<Handle<Shader>>, pub prepass_material_vertex_shader: Option<Handle<Shader>>,
pub prepass_material_fragment_shader: Option<Handle<Shader>>, pub prepass_material_fragment_shader: Option<Handle<Shader>>,
@ -381,6 +383,7 @@ impl<M: Material> FromWorld for PrepassPipeline<M> {
skins_use_uniform_buffers: skin::skins_use_uniform_buffers(render_device), skins_use_uniform_buffers: skin::skins_use_uniform_buffers(render_device),
depth_clip_control_supported, depth_clip_control_supported,
binding_arrays_are_usable: binding_arrays_are_usable(render_device, render_adapter), binding_arrays_are_usable: binding_arrays_are_usable(render_device, render_adapter),
empty_layout: render_device.create_bind_group_layout("prepass_empty_layout", &[]),
}; };
PrepassPipeline { PrepassPipeline {
internal, internal,
@ -425,13 +428,14 @@ impl PrepassPipelineInternal {
layout: &MeshVertexBufferLayoutRef, layout: &MeshVertexBufferLayoutRef,
) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> { ) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
let mut shader_defs = shader_defs; let mut shader_defs = shader_defs;
let mut bind_group_layouts = vec![if mesh_key let mut bind_group_layouts = vec![
.contains(MeshPipelineKey::MOTION_VECTOR_PREPASS) if mesh_key.contains(MeshPipelineKey::MOTION_VECTOR_PREPASS) {
{ self.view_layout_motion_vectors.clone()
self.view_layout_motion_vectors.clone() } else {
} else { self.view_layout_no_motion_vectors.clone()
self.view_layout_no_motion_vectors.clone() },
}]; self.empty_layout.clone(),
];
let mut vertex_attributes = Vec::new(); let mut vertex_attributes = Vec::new();
// Let the shader code know that it's running in a prepass pipeline. // Let the shader code know that it's running in a prepass pipeline.
@ -556,7 +560,7 @@ impl PrepassPipelineInternal {
&mut vertex_attributes, &mut vertex_attributes,
self.skins_use_uniform_buffers, self.skins_use_uniform_buffers,
); );
bind_group_layouts.insert(1, bind_group); bind_group_layouts.insert(2, bind_group);
let vertex_buffer_layout = layout.0.get_layout(&vertex_attributes)?; let vertex_buffer_layout = layout.0.get_layout(&vertex_attributes)?;
// Setup prepass fragment targets - normals in slot 0 (or None if not needed), motion vectors in slot 1 // Setup prepass fragment targets - normals in slot 0 (or None if not needed), motion vectors in slot 1
let mut targets = prepass_target_descriptors( let mut targets = prepass_target_descriptors(
@ -723,10 +727,29 @@ pub fn prepare_previous_view_uniforms(
} }
} }
#[derive(Default, Resource)] #[derive(Resource)]
pub struct PrepassViewBindGroup { pub struct PrepassViewBindGroup {
pub motion_vectors: Option<BindGroup>, pub motion_vectors: Option<BindGroup>,
pub no_motion_vectors: Option<BindGroup>, pub no_motion_vectors: Option<BindGroup>,
pub empty_bind_group: BindGroup,
}
impl FromWorld for PrepassViewBindGroup {
fn from_world(world: &mut World) -> Self {
let pipeline = world.resource::<PrepassPipeline<StandardMaterial>>();
let render_device = world.resource::<RenderDevice>();
let empty_bind_group = render_device.create_bind_group(
"prepass_view_empty_bind_group",
&pipeline.internal.empty_layout,
&[],
);
PrepassViewBindGroup {
motion_vectors: None,
no_motion_vectors: None,
empty_bind_group,
}
}
} }
pub fn prepare_prepass_view_bind_group<M: Material>( pub fn prepare_prepass_view_bind_group<M: Material>(
@ -1284,7 +1307,26 @@ impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetPrepassViewBindGroup<
); );
} }
} }
RenderCommandResult::Success
}
}
pub struct SetPrepassViewEmptyBindGroup<const I: usize>;
impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetPrepassViewEmptyBindGroup<I> {
type Param = SRes<PrepassViewBindGroup>;
type ViewQuery = ();
type ItemQuery = ();
#[inline]
fn render<'w>(
_item: &P,
_view: (),
_entity: Option<()>,
prepass_view_bind_group: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {
let prepass_view_bind_group = prepass_view_bind_group.into_inner();
pass.set_bind_group(I, &prepass_view_bind_group.empty_bind_group, &[]);
RenderCommandResult::Success RenderCommandResult::Success
} }
} }
@ -1292,7 +1334,8 @@ impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetPrepassViewBindGroup<
pub type DrawPrepass<M> = ( pub type DrawPrepass<M> = (
SetItemPipeline, SetItemPipeline,
SetPrepassViewBindGroup<0>, SetPrepassViewBindGroup<0>,
SetMeshBindGroup<1>, SetPrepassViewEmptyBindGroup<1>,
SetMaterialBindGroup<M, 2>, SetMeshBindGroup<2>,
SetMaterialBindGroup<M, 3>,
DrawMesh, DrawMesh,
); );

View File

@ -1864,7 +1864,10 @@ impl MeshPipeline {
} }
} }
pub fn get_view_layout(&self, layout_key: MeshPipelineViewLayoutKey) -> &BindGroupLayout { pub fn get_view_layout(
&self,
layout_key: MeshPipelineViewLayoutKey,
) -> &MeshPipelineViewLayout {
self.view_layouts.get_view_layout(layout_key) self.view_layouts.get_view_layout(layout_key)
} }
} }
@ -2320,7 +2323,11 @@ impl SpecializedMeshPipeline for MeshPipeline {
shader_defs.push("PBR_SPECULAR_TEXTURES_SUPPORTED".into()); shader_defs.push("PBR_SPECULAR_TEXTURES_SUPPORTED".into());
} }
let mut bind_group_layout = vec![self.get_view_layout(key.into()).clone()]; let bind_group_layout = self.get_view_layout(key.into());
let mut bind_group_layout = vec![
bind_group_layout.main_layout.clone(),
bind_group_layout.binding_array_layout.clone(),
];
if key.msaa_samples() > 1 { if key.msaa_samples() > 1 {
shader_defs.push("MULTISAMPLED".into()); shader_defs.push("MULTISAMPLED".into());
@ -2890,7 +2897,47 @@ impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetMeshViewBindGroup<I>
if let Some(layers_count_offset) = maybe_oit_layers_count_offset { if let Some(layers_count_offset) = maybe_oit_layers_count_offset {
offsets.push(layers_count_offset.offset); offsets.push(layers_count_offset.offset);
} }
pass.set_bind_group(I, &mesh_view_bind_group.value, &offsets); pass.set_bind_group(I, &mesh_view_bind_group.main, &offsets);
RenderCommandResult::Success
}
}
pub struct SetMeshViewBindingArrayBindGroup<const I: usize>;
impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetMeshViewBindingArrayBindGroup<I> {
type Param = ();
type ViewQuery = (Read<MeshViewBindGroup>,);
type ItemQuery = ();
#[inline]
fn render<'w>(
_item: &P,
(mesh_view_bind_group,): ROQueryItem<'w, '_, Self::ViewQuery>,
_entity: Option<()>,
_: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {
pass.set_bind_group(I, &mesh_view_bind_group.binding_array, &[]);
RenderCommandResult::Success
}
}
pub struct SetMeshViewEmptyBindGroup<const I: usize>;
impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetMeshViewEmptyBindGroup<I> {
type Param = ();
type ViewQuery = (Read<MeshViewBindGroup>,);
type ItemQuery = ();
#[inline]
fn render<'w>(
_item: &P,
(mesh_view_bind_group,): ROQueryItem<'w, '_, Self::ViewQuery>,
_entity: Option<()>,
_: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {
pass.set_bind_group(I, &mesh_view_bind_group.empty, &[]);
RenderCommandResult::Success RenderCommandResult::Success
} }

View File

@ -4,8 +4,8 @@
#ifndef MESHLET_MESH_MATERIAL_PASS #ifndef MESHLET_MESH_MATERIAL_PASS
#ifdef PER_OBJECT_BUFFER_BATCH_SIZE #ifdef PER_OBJECT_BUFFER_BATCH_SIZE
@group(1) @binding(0) var<uniform> mesh: array<Mesh, #{PER_OBJECT_BUFFER_BATCH_SIZE}u>; @group(2) @binding(0) var<uniform> mesh: array<Mesh, #{PER_OBJECT_BUFFER_BATCH_SIZE}u>;
#else #else
@group(1) @binding(0) var<storage> mesh: array<Mesh>; @group(2) @binding(0) var<storage> mesh: array<Mesh>;
#endif // PER_OBJECT_BUFFER_BATCH_SIZE #endif // PER_OBJECT_BUFFER_BATCH_SIZE
#endif // MESHLET_MESH_MATERIAL_PASS #endif // MESHLET_MESH_MATERIAL_PASS

View File

@ -59,7 +59,9 @@ use {crate::MESH_PIPELINE_VIEW_LAYOUT_SAFE_MAX_TEXTURES, bevy_utils::once, traci
#[derive(Clone)] #[derive(Clone)]
pub struct MeshPipelineViewLayout { pub struct MeshPipelineViewLayout {
pub bind_group_layout: BindGroupLayout, pub main_layout: BindGroupLayout,
pub binding_array_layout: BindGroupLayout,
pub empty_layout: BindGroupLayout,
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
pub texture_count: usize, pub texture_count: usize,
@ -213,7 +215,11 @@ fn layout_entries(
layout_key: MeshPipelineViewLayoutKey, layout_key: MeshPipelineViewLayoutKey,
render_device: &RenderDevice, render_device: &RenderDevice,
render_adapter: &RenderAdapter, render_adapter: &RenderAdapter,
) -> Vec<BindGroupLayoutEntry> { ) -> [Vec<BindGroupLayoutEntry>; 2] {
// EnvironmentMapLight
let environment_map_entries =
environment_map::get_bind_group_layout_entries(render_device, render_adapter);
let mut entries = DynamicBindGroupLayoutEntries::new_with_indices( let mut entries = DynamicBindGroupLayoutEntries::new_with_indices(
ShaderStages::FRAGMENT, ShaderStages::FRAGMENT,
( (
@ -325,45 +331,15 @@ fn layout_entries(
16, 16,
texture_2d(TextureSampleType::Float { filterable: false }), texture_2d(TextureSampleType::Float { filterable: false }),
), ),
(17, environment_map_entries[3]),
), ),
); );
// EnvironmentMapLight
let environment_map_entries =
environment_map::get_bind_group_layout_entries(render_device, render_adapter);
entries = entries.extend_with_indices((
(17, environment_map_entries[0]),
(18, environment_map_entries[1]),
(19, environment_map_entries[2]),
(20, environment_map_entries[3]),
));
// Irradiance volumes
if IRRADIANCE_VOLUMES_ARE_USABLE {
let irradiance_volume_entries =
irradiance_volume::get_bind_group_layout_entries(render_device, render_adapter);
entries = entries.extend_with_indices((
(21, irradiance_volume_entries[0]),
(22, irradiance_volume_entries[1]),
));
}
// Clustered decals
if let Some(clustered_decal_entries) =
decal::clustered::get_bind_group_layout_entries(render_device, render_adapter)
{
entries = entries.extend_with_indices((
(23, clustered_decal_entries[0]),
(24, clustered_decal_entries[1]),
(25, clustered_decal_entries[2]),
));
}
// Tonemapping // Tonemapping
let tonemapping_lut_entries = get_lut_bind_group_layout_entries(); let tonemapping_lut_entries = get_lut_bind_group_layout_entries();
entries = entries.extend_with_indices(( entries = entries.extend_with_indices((
(26, tonemapping_lut_entries[0]), (18, tonemapping_lut_entries[0]),
(27, tonemapping_lut_entries[1]), (19, tonemapping_lut_entries[1]),
)); ));
// Prepass // Prepass
@ -373,7 +349,7 @@ fn layout_entries(
{ {
for (entry, binding) in prepass::get_bind_group_layout_entries(layout_key) for (entry, binding) in prepass::get_bind_group_layout_entries(layout_key)
.iter() .iter()
.zip([28, 29, 30, 31]) .zip([20, 21, 22, 23])
{ {
if let Some(entry) = entry { if let Some(entry) = entry {
entries = entries.extend_with_indices(((binding as u32, *entry),)); entries = entries.extend_with_indices(((binding as u32, *entry),));
@ -384,10 +360,10 @@ fn layout_entries(
// View Transmission Texture // View Transmission Texture
entries = entries.extend_with_indices(( entries = entries.extend_with_indices((
( (
32, 24,
texture_2d(TextureSampleType::Float { filterable: true }), texture_2d(TextureSampleType::Float { filterable: true }),
), ),
(33, sampler(SamplerBindingType::Filtering)), (25, sampler(SamplerBindingType::Filtering)),
)); ));
// OIT // OIT
@ -398,19 +374,47 @@ fn layout_entries(
if is_oit_supported(render_adapter, render_device, false) { if is_oit_supported(render_adapter, render_device, false) {
entries = entries.extend_with_indices(( entries = entries.extend_with_indices((
// oit_layers // oit_layers
(34, storage_buffer_sized(false, None)), (26, storage_buffer_sized(false, None)),
// oit_layer_ids, // oit_layer_ids,
(35, storage_buffer_sized(false, None)), (27, storage_buffer_sized(false, None)),
// oit_layer_count // oit_layer_count
( (
36, 28,
uniform_buffer::<OrderIndependentTransparencySettings>(true), uniform_buffer::<OrderIndependentTransparencySettings>(true),
), ),
)); ));
} }
} }
entries.to_vec() let mut binding_array_entries = DynamicBindGroupLayoutEntries::new(ShaderStages::FRAGMENT);
binding_array_entries = binding_array_entries.extend_with_indices((
(0, environment_map_entries[0]),
(1, environment_map_entries[1]),
(2, environment_map_entries[2]),
));
// Irradiance volumes
if IRRADIANCE_VOLUMES_ARE_USABLE {
let irradiance_volume_entries =
irradiance_volume::get_bind_group_layout_entries(render_device, render_adapter);
binding_array_entries = binding_array_entries.extend_with_indices((
(3, irradiance_volume_entries[0]),
(4, irradiance_volume_entries[1]),
));
}
// Clustered decals
if let Some(clustered_decal_entries) =
decal::clustered::get_bind_group_layout_entries(render_device, render_adapter)
{
binding_array_entries = binding_array_entries.extend_with_indices((
(5, clustered_decal_entries[0]),
(6, clustered_decal_entries[1]),
(7, clustered_decal_entries[2]),
));
}
[entries.to_vec(), binding_array_entries.to_vec()]
} }
/// Stores the view layouts for every combination of pipeline keys. /// Stores the view layouts for every combination of pipeline keys.
@ -447,12 +451,21 @@ impl FromWorld for MeshPipelineViewLayouts {
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
let texture_count: usize = entries let texture_count: usize = entries
.iter() .iter()
.filter(|entry| matches!(entry.ty, BindingType::Texture { .. })) .flat_map(|e| {
e.iter()
.filter(|entry| matches!(entry.ty, BindingType::Texture { .. }))
})
.count(); .count();
MeshPipelineViewLayout { MeshPipelineViewLayout {
bind_group_layout: render_device main_layout: render_device
.create_bind_group_layout(key.label().as_str(), &entries), .create_bind_group_layout(key.label().as_str(), &entries[0]),
binding_array_layout: render_device.create_bind_group_layout(
format!("{}_binding_array", key.label()).as_str(),
&entries[1],
),
empty_layout: render_device
.create_bind_group_layout(format!("{}_empty", key.label()).as_str(), &[]),
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
texture_count, texture_count,
} }
@ -461,7 +474,10 @@ impl FromWorld for MeshPipelineViewLayouts {
} }
impl MeshPipelineViewLayouts { impl MeshPipelineViewLayouts {
pub fn get_view_layout(&self, layout_key: MeshPipelineViewLayoutKey) -> &BindGroupLayout { pub fn get_view_layout(
&self,
layout_key: MeshPipelineViewLayoutKey,
) -> &MeshPipelineViewLayout {
let index = layout_key.bits() as usize; let index = layout_key.bits() as usize;
let layout = &self[index]; let layout = &self[index];
@ -471,7 +487,7 @@ impl MeshPipelineViewLayouts {
once!(warn!("Too many textures in mesh pipeline view layout, this might cause us to hit `wgpu::Limits::max_sampled_textures_per_shader_stage` in some environments.")); once!(warn!("Too many textures in mesh pipeline view layout, this might cause us to hit `wgpu::Limits::max_sampled_textures_per_shader_stage` in some environments."));
} }
&layout.bind_group_layout layout
} }
} }
@ -496,12 +512,20 @@ pub fn generate_view_layouts(
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
let texture_count: usize = entries let texture_count: usize = entries
.iter() .iter()
.filter(|entry| matches!(entry.ty, BindingType::Texture { .. })) .flat_map(|e| {
e.iter()
.filter(|entry| matches!(entry.ty, BindingType::Texture { .. }))
})
.count(); .count();
MeshPipelineViewLayout { MeshPipelineViewLayout {
bind_group_layout: render_device main_layout: render_device.create_bind_group_layout(key.label().as_str(), &entries[0]),
.create_bind_group_layout(key.label().as_str(), &entries), binding_array_layout: render_device.create_bind_group_layout(
format!("{}_binding_array", key.label()).as_str(),
&entries[1],
),
empty_layout: render_device
.create_bind_group_layout(format!("{}_empty", key.label()).as_str(), &[]),
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
texture_count, texture_count,
} }
@ -510,7 +534,9 @@ pub fn generate_view_layouts(
#[derive(Component)] #[derive(Component)]
pub struct MeshViewBindGroup { pub struct MeshViewBindGroup {
pub value: BindGroup, pub main: BindGroup,
pub binding_array: BindGroup,
pub empty: BindGroup,
} }
pub fn prepare_mesh_view_bind_groups( pub fn prepare_mesh_view_bind_groups(
@ -597,7 +623,7 @@ pub fn prepare_mesh_view_bind_groups(
layout_key |= MeshPipelineViewLayoutKey::OIT_ENABLED; layout_key |= MeshPipelineViewLayoutKey::OIT_ENABLED;
} }
let layout = &mesh_pipeline.get_view_layout(layout_key); let layout = mesh_pipeline.get_view_layout(layout_key);
let mut entries = DynamicBindGroupEntries::new_with_indices(( let mut entries = DynamicBindGroupEntries::new_with_indices((
(0, view_binding.clone()), (0, view_binding.clone()),
@ -626,6 +652,58 @@ pub fn prepare_mesh_view_bind_groups(
(16, ssao_view), (16, ssao_view),
)); ));
entries = entries.extend_with_indices(((17, environment_map_binding.clone()),));
let lut_bindings =
get_lut_bindings(&images, &tonemapping_luts, tonemapping, &fallback_image);
entries = entries.extend_with_indices(((18, lut_bindings.0), (19, lut_bindings.1)));
// When using WebGL, we can't have a depth texture with multisampling
let prepass_bindings;
if cfg!(any(not(feature = "webgl"), not(target_arch = "wasm32"))) || msaa.samples() == 1
{
prepass_bindings = prepass::get_bindings(prepass_textures);
for (binding, index) in prepass_bindings
.iter()
.map(Option::as_ref)
.zip([20, 21, 22, 23])
.flat_map(|(b, i)| b.map(|b| (b, i)))
{
entries = entries.extend_with_indices(((index, binding),));
}
};
let transmission_view = transmission_texture
.map(|transmission| &transmission.view)
.unwrap_or(&fallback_image_zero.texture_view);
let transmission_sampler = transmission_texture
.map(|transmission| &transmission.sampler)
.unwrap_or(&fallback_image_zero.sampler);
entries =
entries.extend_with_indices(((24, transmission_view), (25, transmission_sampler)));
if has_oit {
if let (
Some(oit_layers_binding),
Some(oit_layer_ids_binding),
Some(oit_settings_binding),
) = (
oit_buffers.layers.binding(),
oit_buffers.layer_ids.binding(),
oit_buffers.settings.binding(),
) {
entries = entries.extend_with_indices((
(26, oit_layers_binding.clone()),
(27, oit_layer_ids_binding.clone()),
(28, oit_settings_binding.clone()),
));
}
}
let mut entries_binding_array = DynamicBindGroupEntries::new();
let environment_map_bind_group_entries = RenderViewEnvironmentMapBindGroupEntries::get( let environment_map_bind_group_entries = RenderViewEnvironmentMapBindGroupEntries::get(
render_view_environment_maps, render_view_environment_maps,
&images, &images,
@ -633,18 +711,16 @@ pub fn prepare_mesh_view_bind_groups(
&render_device, &render_device,
&render_adapter, &render_adapter,
); );
match environment_map_bind_group_entries { match environment_map_bind_group_entries {
RenderViewEnvironmentMapBindGroupEntries::Single { RenderViewEnvironmentMapBindGroupEntries::Single {
diffuse_texture_view, diffuse_texture_view,
specular_texture_view, specular_texture_view,
sampler, sampler,
} => { } => {
entries = entries.extend_with_indices(( entries_binding_array = entries_binding_array.extend_with_indices((
(17, diffuse_texture_view), (0, diffuse_texture_view),
(18, specular_texture_view), (1, specular_texture_view),
(19, sampler), (2, sampler),
(20, environment_map_binding.clone()),
)); ));
} }
RenderViewEnvironmentMapBindGroupEntries::Multiple { RenderViewEnvironmentMapBindGroupEntries::Multiple {
@ -652,11 +728,10 @@ pub fn prepare_mesh_view_bind_groups(
ref specular_texture_views, ref specular_texture_views,
sampler, sampler,
} => { } => {
entries = entries.extend_with_indices(( entries_binding_array = entries_binding_array.extend_with_indices((
(17, diffuse_texture_views.as_slice()), (0, diffuse_texture_views.as_slice()),
(18, specular_texture_views.as_slice()), (1, specular_texture_views.as_slice()),
(19, sampler), (2, sampler),
(20, environment_map_binding.clone()),
)); ));
} }
} }
@ -678,14 +753,15 @@ pub fn prepare_mesh_view_bind_groups(
texture_view, texture_view,
sampler, sampler,
}) => { }) => {
entries = entries.extend_with_indices(((21, texture_view), (22, sampler))); entries_binding_array = entries_binding_array
.extend_with_indices(((3, texture_view), (4, sampler)));
} }
Some(RenderViewIrradianceVolumeBindGroupEntries::Multiple { Some(RenderViewIrradianceVolumeBindGroupEntries::Multiple {
ref texture_views, ref texture_views,
sampler, sampler,
}) => { }) => {
entries = entries entries_binding_array = entries_binding_array
.extend_with_indices(((21, texture_views.as_slice()), (22, sampler))); .extend_with_indices(((3, texture_views.as_slice()), (4, sampler)));
} }
None => {} None => {}
} }
@ -701,76 +777,42 @@ pub fn prepare_mesh_view_bind_groups(
// Add the decal bind group entries. // Add the decal bind group entries.
if let Some(ref render_view_decal_bind_group_entries) = decal_bind_group_entries { if let Some(ref render_view_decal_bind_group_entries) = decal_bind_group_entries {
entries = entries.extend_with_indices(( entries_binding_array = entries_binding_array.extend_with_indices((
// `clustered_decals` // `clustered_decals`
( (
23, 5,
render_view_decal_bind_group_entries render_view_decal_bind_group_entries
.decals .decals
.as_entire_binding(), .as_entire_binding(),
), ),
// `clustered_decal_textures` // `clustered_decal_textures`
( (
24, 6,
render_view_decal_bind_group_entries render_view_decal_bind_group_entries
.texture_views .texture_views
.as_slice(), .as_slice(),
), ),
// `clustered_decal_sampler` // `clustered_decal_sampler`
(25, render_view_decal_bind_group_entries.sampler), (7, render_view_decal_bind_group_entries.sampler),
)); ));
} }
let lut_bindings =
get_lut_bindings(&images, &tonemapping_luts, tonemapping, &fallback_image);
entries = entries.extend_with_indices(((26, lut_bindings.0), (27, lut_bindings.1)));
// When using WebGL, we can't have a depth texture with multisampling
let prepass_bindings;
if cfg!(any(not(feature = "webgl"), not(target_arch = "wasm32"))) || msaa.samples() == 1
{
prepass_bindings = prepass::get_bindings(prepass_textures);
for (binding, index) in prepass_bindings
.iter()
.map(Option::as_ref)
.zip([28, 29, 30, 31])
.flat_map(|(b, i)| b.map(|b| (b, i)))
{
entries = entries.extend_with_indices(((index, binding),));
}
};
let transmission_view = transmission_texture
.map(|transmission| &transmission.view)
.unwrap_or(&fallback_image_zero.texture_view);
let transmission_sampler = transmission_texture
.map(|transmission| &transmission.sampler)
.unwrap_or(&fallback_image_zero.sampler);
entries =
entries.extend_with_indices(((32, transmission_view), (33, transmission_sampler)));
if has_oit {
if let (
Some(oit_layers_binding),
Some(oit_layer_ids_binding),
Some(oit_settings_binding),
) = (
oit_buffers.layers.binding(),
oit_buffers.layer_ids.binding(),
oit_buffers.settings.binding(),
) {
entries = entries.extend_with_indices((
(34, oit_layers_binding.clone()),
(35, oit_layer_ids_binding.clone()),
(36, oit_settings_binding.clone()),
));
}
}
commands.entity(entity).insert(MeshViewBindGroup { commands.entity(entity).insert(MeshViewBindGroup {
value: render_device.create_bind_group("mesh_view_bind_group", layout, &entries), main: render_device.create_bind_group(
"mesh_view_bind_group",
&layout.main_layout,
&entries,
),
binding_array: render_device.create_bind_group(
"mesh_view_bind_group_binding_array",
&layout.binding_array_layout,
&entries_binding_array,
),
empty: render_device.create_bind_group(
"mesh_view_bind_group_empty",
&layout.empty_layout,
&[],
),
}); });
} }
} }

View File

@ -50,70 +50,70 @@ const VISIBILITY_RANGE_UNIFORM_BUFFER_SIZE: u32 = 64u;
@group(0) @binding(15) var<uniform> ssr_settings: types::ScreenSpaceReflectionsSettings; @group(0) @binding(15) var<uniform> ssr_settings: types::ScreenSpaceReflectionsSettings;
@group(0) @binding(16) var screen_space_ambient_occlusion_texture: texture_2d<f32>; @group(0) @binding(16) var screen_space_ambient_occlusion_texture: texture_2d<f32>;
@group(0) @binding(17) var<uniform> environment_map_uniform: types::EnvironmentMapUniform;
#ifdef MULTIPLE_LIGHT_PROBES_IN_ARRAY
@group(0) @binding(17) var diffuse_environment_maps: binding_array<texture_cube<f32>, 8u>;
@group(0) @binding(18) var specular_environment_maps: binding_array<texture_cube<f32>, 8u>;
#else
@group(0) @binding(17) var diffuse_environment_map: texture_cube<f32>;
@group(0) @binding(18) var specular_environment_map: texture_cube<f32>;
#endif
@group(0) @binding(19) var environment_map_sampler: sampler;
@group(0) @binding(20) var<uniform> environment_map_uniform: types::EnvironmentMapUniform;
#ifdef IRRADIANCE_VOLUMES_ARE_USABLE
#ifdef MULTIPLE_LIGHT_PROBES_IN_ARRAY
@group(0) @binding(21) var irradiance_volumes: binding_array<texture_3d<f32>, 8u>;
#else
@group(0) @binding(21) var irradiance_volume: texture_3d<f32>;
#endif
@group(0) @binding(22) var irradiance_volume_sampler: sampler;
#endif
#ifdef CLUSTERED_DECALS_ARE_USABLE
@group(0) @binding(23) var<storage> clustered_decals: types::ClusteredDecals;
@group(0) @binding(24) var clustered_decal_textures: binding_array<texture_2d<f32>, 8u>;
@group(0) @binding(25) var clustered_decal_sampler: sampler;
#endif // CLUSTERED_DECALS_ARE_USABLE
// NB: If you change these, make sure to update `tonemapping_shared.wgsl` too. // NB: If you change these, make sure to update `tonemapping_shared.wgsl` too.
@group(0) @binding(26) var dt_lut_texture: texture_3d<f32>; @group(0) @binding(18) var dt_lut_texture: texture_3d<f32>;
@group(0) @binding(27) var dt_lut_sampler: sampler; @group(0) @binding(19) var dt_lut_sampler: sampler;
#ifdef MULTISAMPLED #ifdef MULTISAMPLED
#ifdef DEPTH_PREPASS #ifdef DEPTH_PREPASS
@group(0) @binding(28) var depth_prepass_texture: texture_depth_multisampled_2d; @group(0) @binding(20) var depth_prepass_texture: texture_depth_multisampled_2d;
#endif // DEPTH_PREPASS #endif // DEPTH_PREPASS
#ifdef NORMAL_PREPASS #ifdef NORMAL_PREPASS
@group(0) @binding(29) var normal_prepass_texture: texture_multisampled_2d<f32>; @group(0) @binding(21) var normal_prepass_texture: texture_multisampled_2d<f32>;
#endif // NORMAL_PREPASS #endif // NORMAL_PREPASS
#ifdef MOTION_VECTOR_PREPASS #ifdef MOTION_VECTOR_PREPASS
@group(0) @binding(30) var motion_vector_prepass_texture: texture_multisampled_2d<f32>; @group(0) @binding(22) var motion_vector_prepass_texture: texture_multisampled_2d<f32>;
#endif // MOTION_VECTOR_PREPASS #endif // MOTION_VECTOR_PREPASS
#else // MULTISAMPLED #else // MULTISAMPLED
#ifdef DEPTH_PREPASS #ifdef DEPTH_PREPASS
@group(0) @binding(28) var depth_prepass_texture: texture_depth_2d; @group(0) @binding(20) var depth_prepass_texture: texture_depth_2d;
#endif // DEPTH_PREPASS #endif // DEPTH_PREPASS
#ifdef NORMAL_PREPASS #ifdef NORMAL_PREPASS
@group(0) @binding(29) var normal_prepass_texture: texture_2d<f32>; @group(0) @binding(21) var normal_prepass_texture: texture_2d<f32>;
#endif // NORMAL_PREPASS #endif // NORMAL_PREPASS
#ifdef MOTION_VECTOR_PREPASS #ifdef MOTION_VECTOR_PREPASS
@group(0) @binding(30) var motion_vector_prepass_texture: texture_2d<f32>; @group(0) @binding(22) var motion_vector_prepass_texture: texture_2d<f32>;
#endif // MOTION_VECTOR_PREPASS #endif // MOTION_VECTOR_PREPASS
#endif // MULTISAMPLED #endif // MULTISAMPLED
#ifdef DEFERRED_PREPASS #ifdef DEFERRED_PREPASS
@group(0) @binding(31) var deferred_prepass_texture: texture_2d<u32>; @group(0) @binding(23) var deferred_prepass_texture: texture_2d<u32>;
#endif // DEFERRED_PREPASS #endif // DEFERRED_PREPASS
@group(0) @binding(32) var view_transmission_texture: texture_2d<f32>; @group(0) @binding(24) var view_transmission_texture: texture_2d<f32>;
@group(0) @binding(33) var view_transmission_sampler: sampler; @group(0) @binding(25) var view_transmission_sampler: sampler;
#ifdef OIT_ENABLED #ifdef OIT_ENABLED
@group(0) @binding(34) var<storage, read_write> oit_layers: array<vec2<u32>>; @group(0) @binding(26) var<storage, read_write> oit_layers: array<vec2<u32>>;
@group(0) @binding(35) var<storage, read_write> oit_layer_ids: array<atomic<i32>>; @group(0) @binding(27) var<storage, read_write> oit_layer_ids: array<atomic<i32>>;
@group(0) @binding(36) var<uniform> oit_settings: types::OrderIndependentTransparencySettings; @group(0) @binding(28) var<uniform> oit_settings: types::OrderIndependentTransparencySettings;
#endif // OIT_ENABLED #endif // OIT_ENABLED
#ifdef MULTIPLE_LIGHT_PROBES_IN_ARRAY
@group(1) @binding(0) var diffuse_environment_maps: binding_array<texture_cube<f32>, 8u>;
@group(1) @binding(1) var specular_environment_maps: binding_array<texture_cube<f32>, 8u>;
#else
@group(1) @binding(0) var diffuse_environment_map: texture_cube<f32>;
@group(1) @binding(1) var specular_environment_map: texture_cube<f32>;
#endif
@group(1) @binding(2) var environment_map_sampler: sampler;
#ifdef IRRADIANCE_VOLUMES_ARE_USABLE
#ifdef MULTIPLE_LIGHT_PROBES_IN_ARRAY
@group(1) @binding(3) var irradiance_volumes: binding_array<texture_3d<f32>, 8u>;
#else
@group(1) @binding(3) var irradiance_volume: texture_3d<f32>;
#endif
@group(1) @binding(4) var irradiance_volume_sampler: sampler;
#endif
#ifdef CLUSTERED_DECALS_ARE_USABLE
@group(1) @binding(5) var<storage> clustered_decals: types::ClusteredDecals;
@group(1) @binding(6) var clustered_decal_textures: binding_array<texture_2d<f32>, 8u>;
@group(1) @binding(7) var clustered_decal_sampler: sampler;
#endif // CLUSTERED_DECALS_ARE_USABLE

View File

@ -4,9 +4,9 @@
#import bevy_pbr::mesh_types::MorphWeights; #import bevy_pbr::mesh_types::MorphWeights;
@group(1) @binding(2) var<uniform> morph_weights: MorphWeights; @group(2) @binding(2) var<uniform> morph_weights: MorphWeights;
@group(1) @binding(3) var morph_targets: texture_3d<f32>; @group(2) @binding(3) var morph_targets: texture_3d<f32>;
@group(1) @binding(7) var<uniform> prev_morph_weights: MorphWeights; @group(2) @binding(7) var<uniform> prev_morph_weights: MorphWeights;
// NOTE: Those are the "hardcoded" values found in `MorphAttributes` struct // NOTE: Those are the "hardcoded" values found in `MorphAttributes` struct
// in crates/bevy_render/src/mesh/morph/visitors.rs // in crates/bevy_render/src/mesh/morph/visitors.rs

View File

@ -37,53 +37,53 @@ struct StandardMaterialBindings {
specular_tint_sampler: u32, // 30 specular_tint_sampler: u32, // 30
} }
@group(2) @binding(0) var<storage> material_indices: array<StandardMaterialBindings>; @group(3) @binding(0) var<storage> material_indices: array<StandardMaterialBindings>;
@group(2) @binding(10) var<storage> material_array: array<StandardMaterial>; @group(3) @binding(10) var<storage> material_array: array<StandardMaterial>;
#else // BINDLESS #else // BINDLESS
@group(2) @binding(0) var<uniform> material: StandardMaterial; @group(3) @binding(0) var<uniform> material: StandardMaterial;
@group(2) @binding(1) var base_color_texture: texture_2d<f32>; @group(3) @binding(1) var base_color_texture: texture_2d<f32>;
@group(2) @binding(2) var base_color_sampler: sampler; @group(3) @binding(2) var base_color_sampler: sampler;
@group(2) @binding(3) var emissive_texture: texture_2d<f32>; @group(3) @binding(3) var emissive_texture: texture_2d<f32>;
@group(2) @binding(4) var emissive_sampler: sampler; @group(3) @binding(4) var emissive_sampler: sampler;
@group(2) @binding(5) var metallic_roughness_texture: texture_2d<f32>; @group(3) @binding(5) var metallic_roughness_texture: texture_2d<f32>;
@group(2) @binding(6) var metallic_roughness_sampler: sampler; @group(3) @binding(6) var metallic_roughness_sampler: sampler;
@group(2) @binding(7) var occlusion_texture: texture_2d<f32>; @group(3) @binding(7) var occlusion_texture: texture_2d<f32>;
@group(2) @binding(8) var occlusion_sampler: sampler; @group(3) @binding(8) var occlusion_sampler: sampler;
@group(2) @binding(9) var normal_map_texture: texture_2d<f32>; @group(3) @binding(9) var normal_map_texture: texture_2d<f32>;
@group(2) @binding(10) var normal_map_sampler: sampler; @group(3) @binding(10) var normal_map_sampler: sampler;
@group(2) @binding(11) var depth_map_texture: texture_2d<f32>; @group(3) @binding(11) var depth_map_texture: texture_2d<f32>;
@group(2) @binding(12) var depth_map_sampler: sampler; @group(3) @binding(12) var depth_map_sampler: sampler;
#ifdef PBR_ANISOTROPY_TEXTURE_SUPPORTED #ifdef PBR_ANISOTROPY_TEXTURE_SUPPORTED
@group(2) @binding(13) var anisotropy_texture: texture_2d<f32>; @group(3) @binding(13) var anisotropy_texture: texture_2d<f32>;
@group(2) @binding(14) var anisotropy_sampler: sampler; @group(3) @binding(14) var anisotropy_sampler: sampler;
#endif // PBR_ANISOTROPY_TEXTURE_SUPPORTED #endif // PBR_ANISOTROPY_TEXTURE_SUPPORTED
#ifdef PBR_TRANSMISSION_TEXTURES_SUPPORTED #ifdef PBR_TRANSMISSION_TEXTURES_SUPPORTED
@group(2) @binding(15) var specular_transmission_texture: texture_2d<f32>; @group(3) @binding(15) var specular_transmission_texture: texture_2d<f32>;
@group(2) @binding(16) var specular_transmission_sampler: sampler; @group(3) @binding(16) var specular_transmission_sampler: sampler;
@group(2) @binding(17) var thickness_texture: texture_2d<f32>; @group(3) @binding(17) var thickness_texture: texture_2d<f32>;
@group(2) @binding(18) var thickness_sampler: sampler; @group(3) @binding(18) var thickness_sampler: sampler;
@group(2) @binding(19) var diffuse_transmission_texture: texture_2d<f32>; @group(3) @binding(19) var diffuse_transmission_texture: texture_2d<f32>;
@group(2) @binding(20) var diffuse_transmission_sampler: sampler; @group(3) @binding(20) var diffuse_transmission_sampler: sampler;
#endif // PBR_TRANSMISSION_TEXTURES_SUPPORTED #endif // PBR_TRANSMISSION_TEXTURES_SUPPORTED
#ifdef PBR_MULTI_LAYER_MATERIAL_TEXTURES_SUPPORTED #ifdef PBR_MULTI_LAYER_MATERIAL_TEXTURES_SUPPORTED
@group(2) @binding(21) var clearcoat_texture: texture_2d<f32>; @group(3) @binding(21) var clearcoat_texture: texture_2d<f32>;
@group(2) @binding(22) var clearcoat_sampler: sampler; @group(3) @binding(22) var clearcoat_sampler: sampler;
@group(2) @binding(23) var clearcoat_roughness_texture: texture_2d<f32>; @group(3) @binding(23) var clearcoat_roughness_texture: texture_2d<f32>;
@group(2) @binding(24) var clearcoat_roughness_sampler: sampler; @group(3) @binding(24) var clearcoat_roughness_sampler: sampler;
@group(2) @binding(25) var clearcoat_normal_texture: texture_2d<f32>; @group(3) @binding(25) var clearcoat_normal_texture: texture_2d<f32>;
@group(2) @binding(26) var clearcoat_normal_sampler: sampler; @group(3) @binding(26) var clearcoat_normal_sampler: sampler;
#endif // PBR_MULTI_LAYER_MATERIAL_TEXTURES_SUPPORTED #endif // PBR_MULTI_LAYER_MATERIAL_TEXTURES_SUPPORTED
#ifdef PBR_SPECULAR_TEXTURES_SUPPORTED #ifdef PBR_SPECULAR_TEXTURES_SUPPORTED
@group(2) @binding(27) var specular_texture: texture_2d<f32>; @group(3) @binding(27) var specular_texture: texture_2d<f32>;
@group(2) @binding(28) var specular_sampler: sampler; @group(3) @binding(28) var specular_sampler: sampler;
@group(2) @binding(29) var specular_tint_texture: texture_2d<f32>; @group(3) @binding(29) var specular_tint_texture: texture_2d<f32>;
@group(2) @binding(30) var specular_tint_sampler: sampler; @group(3) @binding(30) var specular_tint_sampler: sampler;
#endif // PBR_SPECULAR_TEXTURES_SUPPORTED #endif // PBR_SPECULAR_TEXTURES_SUPPORTED
#endif // BINDLESS #endif // BINDLESS

View File

@ -6,9 +6,9 @@
#ifdef SKINNED #ifdef SKINNED
#ifdef SKINS_USE_UNIFORM_BUFFERS #ifdef SKINS_USE_UNIFORM_BUFFERS
@group(1) @binding(1) var<uniform> joint_matrices: SkinnedMesh; @group(2) @binding(1) var<uniform> joint_matrices: SkinnedMesh;
#else // SKINS_USE_UNIFORM_BUFFERS #else // SKINS_USE_UNIFORM_BUFFERS
@group(1) @binding(1) var<storage> joint_matrices: array<mat4x4<f32>>; @group(2) @binding(1) var<storage> joint_matrices: array<mat4x4<f32>>;
#endif // SKINS_USE_UNIFORM_BUFFERS #endif // SKINS_USE_UNIFORM_BUFFERS
// An array of matrices specifying the joint positions from the previous frame. // An array of matrices specifying the joint positions from the previous frame.
@ -18,9 +18,9 @@
// If this is the first frame, or we're otherwise prevented from using data from // If this is the first frame, or we're otherwise prevented from using data from
// the previous frame, this is simply the same as `joint_matrices` above. // the previous frame, this is simply the same as `joint_matrices` above.
#ifdef SKINS_USE_UNIFORM_BUFFERS #ifdef SKINS_USE_UNIFORM_BUFFERS
@group(1) @binding(6) var<uniform> prev_joint_matrices: SkinnedMesh; @group(2) @binding(6) var<uniform> prev_joint_matrices: SkinnedMesh;
#else // SKINS_USE_UNIFORM_BUFFERS #else // SKINS_USE_UNIFORM_BUFFERS
@group(1) @binding(6) var<storage> prev_joint_matrices: array<mat4x4<f32>>; @group(2) @binding(6) var<storage> prev_joint_matrices: array<mat4x4<f32>>;
#endif // SKINS_USE_UNIFORM_BUFFERS #endif // SKINS_USE_UNIFORM_BUFFERS
fn skin_model( fn skin_model(

View File

@ -323,7 +323,7 @@ impl ViewNode for ScreenSpaceReflectionsNode {
render_pass.set_render_pipeline(render_pipeline); render_pass.set_render_pipeline(render_pipeline);
render_pass.set_bind_group( render_pass.set_bind_group(
0, 0,
&view_bind_group.value, &view_bind_group.main,
&[ &[
view_uniform_offset.offset, view_uniform_offset.offset,
view_lights_offset.offset, view_lights_offset.offset,
@ -333,9 +333,10 @@ impl ViewNode for ScreenSpaceReflectionsNode {
**view_environment_map_offset, **view_environment_map_offset,
], ],
); );
render_pass.set_bind_group(1, &view_bind_group.binding_array, &[]);
// Perform the SSR render pass. // Perform the SSR render pass.
render_pass.set_bind_group(1, &ssr_bind_group, &[]); render_pass.set_bind_group(2, &ssr_bind_group, &[]);
render_pass.draw(0..3, 0..1); render_pass.draw(0..3, 0..1);
Ok(()) Ok(())
@ -517,9 +518,14 @@ impl SpecializedRenderPipeline for ScreenSpaceReflectionsPipeline {
type Key = ScreenSpaceReflectionsPipelineKey; type Key = ScreenSpaceReflectionsPipelineKey;
fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor { fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
let mesh_view_layout = self let layout = self
.mesh_view_layouts .mesh_view_layouts
.get_view_layout(key.mesh_pipeline_view_key); .get_view_layout(key.mesh_pipeline_view_key);
let layout = vec![
layout.main_layout.clone(),
layout.binding_array_layout.clone(),
self.bind_group_layout.clone(),
];
let mut shader_defs = vec![ let mut shader_defs = vec![
"DEPTH_PREPASS".into(), "DEPTH_PREPASS".into(),
@ -537,7 +543,7 @@ impl SpecializedRenderPipeline for ScreenSpaceReflectionsPipeline {
RenderPipelineDescriptor { RenderPipelineDescriptor {
label: Some("SSR pipeline".into()), label: Some("SSR pipeline".into()),
layout: vec![mesh_view_layout.clone(), self.bind_group_layout.clone()], layout,
vertex: self.fullscreen_shader.to_vertex_state(), vertex: self.fullscreen_shader.to_vertex_state(),
fragment: Some(FragmentState { fragment: Some(FragmentState {
shader: self.fragment_shader.clone(), shader: self.fragment_shader.clone(),

View File

@ -25,10 +25,10 @@
} }
// Allows us to sample from the depth buffer with bilinear filtering. // Allows us to sample from the depth buffer with bilinear filtering.
@group(1) @binding(2) var depth_linear_sampler: sampler; @group(2) @binding(2) var depth_linear_sampler: sampler;
// Allows us to sample from the depth buffer with nearest-neighbor filtering. // Allows us to sample from the depth buffer with nearest-neighbor filtering.
@group(1) @binding(3) var depth_nearest_sampler: sampler; @group(2) @binding(3) var depth_nearest_sampler: sampler;
// Main code // Main code

View File

@ -36,10 +36,10 @@
#endif #endif
// The texture representing the color framebuffer. // The texture representing the color framebuffer.
@group(1) @binding(0) var color_texture: texture_2d<f32>; @group(2) @binding(0) var color_texture: texture_2d<f32>;
// The sampler that lets us sample from the color framebuffer. // The sampler that lets us sample from the color framebuffer.
@group(1) @binding(1) var color_sampler: sampler; @group(2) @binding(1) var color_sampler: sampler;
// Group 1, bindings 2 and 3 are in `raymarch.wgsl`. // Group 1, bindings 2 and 3 are in `raymarch.wgsl`.

View File

@ -461,7 +461,7 @@ impl ViewNode for VolumetricFogNode {
render_pass.set_pipeline(pipeline); render_pass.set_pipeline(pipeline);
render_pass.set_bind_group( render_pass.set_bind_group(
0, 0,
&view_bind_group.value, &view_bind_group.main,
&[ &[
view_uniform_offset.offset, view_uniform_offset.offset,
view_lights_offset.offset, view_lights_offset.offset,
@ -511,10 +511,6 @@ impl SpecializedRenderPipeline for VolumetricFogPipeline {
type Key = VolumetricFogPipelineKey; type Key = VolumetricFogPipelineKey;
fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor { fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
let mesh_view_layout = self
.mesh_view_layouts
.get_view_layout(key.mesh_pipeline_view_key);
// We always use hardware 2x2 filtering for sampling the shadow map; the // We always use hardware 2x2 filtering for sampling the shadow map; the
// more accurate versions with percentage-closer filtering aren't worth // more accurate versions with percentage-closer filtering aren't worth
// the overhead. // the overhead.
@ -559,9 +555,17 @@ impl SpecializedRenderPipeline for VolumetricFogPipeline {
shader_defs.push("DENSITY_TEXTURE".into()); shader_defs.push("DENSITY_TEXTURE".into());
} }
let layout = self
.mesh_view_layouts
.get_view_layout(key.mesh_pipeline_view_key);
let layout = vec![
layout.main_layout.clone(),
volumetric_view_bind_group_layout.clone(),
];
RenderPipelineDescriptor { RenderPipelineDescriptor {
label: Some("volumetric lighting pipeline".into()), label: Some("volumetric lighting pipeline".into()),
layout: vec![mesh_view_layout.clone(), volumetric_view_bind_group_layout], layout,
push_constant_ranges: vec![], push_constant_ranges: vec![],
vertex: VertexState { vertex: VertexState {
shader: self.shader.clone(), shader: self.shader.clone(),

View File

@ -1,6 +1,7 @@
use crate::{ use crate::{
DrawMesh, MeshPipeline, MeshPipelineKey, RenderMeshInstanceFlags, RenderMeshInstances, DrawMesh, MeshPipeline, MeshPipelineKey, RenderMeshInstanceFlags, RenderMeshInstances,
SetMeshBindGroup, SetMeshViewBindGroup, ViewKeyCache, ViewSpecializationTicks, SetMeshBindGroup, SetMeshViewBindGroup, SetMeshViewBindingArrayBindGroup, ViewKeyCache,
ViewSpecializationTicks,
}; };
use bevy_app::{App, Plugin, PostUpdate, Startup, Update}; use bevy_app::{App, Plugin, PostUpdate, Startup, Update};
use bevy_asset::{ use bevy_asset::{
@ -318,7 +319,8 @@ impl<P: PhaseItem> RenderCommand<P> for SetWireframe3dPushConstants {
pub type DrawWireframe3d = ( pub type DrawWireframe3d = (
SetItemPipeline, SetItemPipeline,
SetMeshViewBindGroup<0>, SetMeshViewBindGroup<0>,
SetMeshBindGroup<1>, SetMeshViewBindingArrayBindGroup<1>,
SetMeshBindGroup<2>,
SetWireframe3dPushConstants, SetWireframe3dPushConstants,
DrawMesh, DrawMesh,
); );

View File

@ -109,7 +109,7 @@ uuid = { version = "1.13.1", default-features = false, optional = true, features
"serde", "serde",
] } ] }
variadics_please = "1.1" variadics_please = "1.1"
wgpu-types = { version = "24", features = [ wgpu-types = { version = "25", features = [
"serde", "serde",
], optional = true, default-features = false } ], optional = true, default-features = false }

View File

@ -85,19 +85,21 @@ bevy_platform = { path = "../bevy_platform", version = "0.17.0-dev", default-fea
image = { version = "0.25.2", default-features = false } image = { version = "0.25.2", default-features = false }
# misc # misc
codespan-reporting = "0.11.0" codespan-reporting = "0.12.0"
# `fragile-send-sync-non-atomic-wasm` feature means we can't use Wasm threads for rendering # `fragile-send-sync-non-atomic-wasm` feature means we can't use Wasm threads for rendering
# It is enabled for now to avoid having to do a significant overhaul of the renderer just for wasm. # It is enabled for now to avoid having to do a significant overhaul of the renderer just for wasm.
# When the 'atomics' feature is enabled `fragile-send-sync-non-atomic` does nothing # When the 'atomics' feature is enabled `fragile-send-sync-non-atomic` does nothing
# and Bevy instead wraps `wgpu` types to verify they are not used off their origin thread. # and Bevy instead wraps `wgpu` types to verify they are not used off their origin thread.
wgpu = { version = "24", default-features = false, features = [ wgpu = { version = "25", default-features = false, features = [
"wgsl", "wgsl",
"dx12", "dx12",
"metal", "metal",
"vulkan",
"gles",
"naga-ir", "naga-ir",
"fragile-send-sync-non-atomic-wasm", "fragile-send-sync-non-atomic-wasm",
] } ] }
naga = { version = "24", features = ["wgsl-in"] } naga = { version = "25", features = ["wgsl-in"] }
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
bytemuck = { version = "1.5", features = ["derive", "must_cast"] } bytemuck = { version = "1.5", features = ["derive", "must_cast"] }
downcast-rs = { version = "2", default-features = false, features = ["std"] } downcast-rs = { version = "2", default-features = false, features = ["std"] }
@ -123,7 +125,7 @@ wesl = { version = "0.1.2", optional = true }
[target.'cfg(not(target_arch = "wasm32"))'.dependencies] [target.'cfg(not(target_arch = "wasm32"))'.dependencies]
# Omit the `glsl` feature in non-WebAssembly by default. # Omit the `glsl` feature in non-WebAssembly by default.
naga_oil = { version = "0.17.1", default-features = false, features = [ naga_oil = { version = "0.18", default-features = false, features = [
"test_shader", "test_shader",
] } ] }
@ -131,7 +133,7 @@ naga_oil = { version = "0.17.1", default-features = false, features = [
proptest = "1" proptest = "1"
[target.'cfg(target_arch = "wasm32")'.dependencies] [target.'cfg(target_arch = "wasm32")'.dependencies]
naga_oil = "0.17.1" naga_oil = { version = "0.18" }
js-sys = "0.3" js-sys = "0.3"
web-sys = { version = "0.3.67", features = [ web-sys = { version = "0.3.67", features = [
'Blob', 'Blob',

View File

@ -204,7 +204,7 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result<TokenStream> {
#bind_group_layout_entries.push( #bind_group_layout_entries.push(
#render_path::render_resource::BindGroupLayoutEntry { #render_path::render_resource::BindGroupLayoutEntry {
binding: #binding_array_binding, binding: #binding_array_binding,
visibility: #render_path::render_resource::ShaderStages::all(), visibility: #render_path::render_resource::ShaderStages::FRAGMENT | #render_path::render_resource::ShaderStages::VERTEX | #render_path::render_resource::ShaderStages::COMPUTE,
ty: #render_path::render_resource::BindingType::Buffer { ty: #render_path::render_resource::BindingType::Buffer {
ty: #uniform_binding_type, ty: #uniform_binding_type,
has_dynamic_offset: false, has_dynamic_offset: false,
@ -253,7 +253,7 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result<TokenStream> {
#bind_group_layout_entries.push( #bind_group_layout_entries.push(
#render_path::render_resource::BindGroupLayoutEntry { #render_path::render_resource::BindGroupLayoutEntry {
binding: #binding_array_binding, binding: #binding_array_binding,
visibility: #render_path::render_resource::ShaderStages::all(), visibility: #render_path::render_resource::ShaderStages::FRAGMENT | #render_path::render_resource::ShaderStages::VERTEX | #render_path::render_resource::ShaderStages::COMPUTE,
ty: #render_path::render_resource::BindingType::Buffer { ty: #render_path::render_resource::BindingType::Buffer {
ty: #uniform_binding_type, ty: #uniform_binding_type,
has_dynamic_offset: false, has_dynamic_offset: false,
@ -279,7 +279,7 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result<TokenStream> {
#bind_group_layout_entries.push( #bind_group_layout_entries.push(
#render_path::render_resource::BindGroupLayoutEntry { #render_path::render_resource::BindGroupLayoutEntry {
binding: #binding_index, binding: #binding_index,
visibility: #render_path::render_resource::ShaderStages::all(), visibility: #render_path::render_resource::ShaderStages::FRAGMENT | #render_path::render_resource::ShaderStages::VERTEX | #render_path::render_resource::ShaderStages::COMPUTE,
ty: #render_path::render_resource::BindingType::Buffer { ty: #render_path::render_resource::BindingType::Buffer {
ty: #uniform_binding_type, ty: #uniform_binding_type,
has_dynamic_offset: false, has_dynamic_offset: false,
@ -519,7 +519,7 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result<TokenStream> {
#bind_group_layout_entries.push( #bind_group_layout_entries.push(
#render_path::render_resource::BindGroupLayoutEntry { #render_path::render_resource::BindGroupLayoutEntry {
binding: #binding_array_binding, binding: #binding_array_binding,
visibility: #render_path::render_resource::ShaderStages::all(), visibility: #render_path::render_resource::ShaderStages::FRAGMENT | #render_path::render_resource::ShaderStages::VERTEX | #render_path::render_resource::ShaderStages::COMPUTE,
ty: #render_path::render_resource::BindingType::Buffer { ty: #render_path::render_resource::BindingType::Buffer {
ty: #render_path::render_resource::BufferBindingType::Storage { ty: #render_path::render_resource::BufferBindingType::Storage {
read_only: #read_only read_only: #read_only
@ -834,7 +834,7 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result<TokenStream> {
#bind_group_layout_entries.push( #bind_group_layout_entries.push(
#render_path::render_resource::BindGroupLayoutEntry { #render_path::render_resource::BindGroupLayoutEntry {
binding: #binding_index, binding: #binding_index,
visibility: #render_path::render_resource::ShaderStages::all(), visibility: #render_path::render_resource::ShaderStages::FRAGMENT | #render_path::render_resource::ShaderStages::VERTEX | #render_path::render_resource::ShaderStages::COMPUTE,
ty: #render_path::render_resource::BindingType::Buffer { ty: #render_path::render_resource::BindingType::Buffer {
ty: #uniform_binding_type, ty: #uniform_binding_type,
has_dynamic_offset: false, has_dynamic_offset: false,
@ -881,7 +881,7 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result<TokenStream> {
non_bindless_binding_layouts.push(quote!{ non_bindless_binding_layouts.push(quote!{
#bind_group_layout_entries.push(#render_path::render_resource::BindGroupLayoutEntry { #bind_group_layout_entries.push(#render_path::render_resource::BindGroupLayoutEntry {
binding: #binding_index, binding: #binding_index,
visibility: #render_path::render_resource::ShaderStages::all(), visibility: #render_path::render_resource::ShaderStages::FRAGMENT | #render_path::render_resource::ShaderStages::VERTEX | #render_path::render_resource::ShaderStages::COMPUTE,
ty: #render_path::render_resource::BindingType::Buffer { ty: #render_path::render_resource::BindingType::Buffer {
ty: #uniform_binding_type, ty: #uniform_binding_type,
has_dynamic_offset: false, has_dynamic_offset: false,
@ -1337,7 +1337,13 @@ impl VisibilityFlags {
impl ShaderStageVisibility { impl ShaderStageVisibility {
fn hygienic_quote(&self, path: &proc_macro2::TokenStream) -> proc_macro2::TokenStream { fn hygienic_quote(&self, path: &proc_macro2::TokenStream) -> proc_macro2::TokenStream {
match self { match self {
ShaderStageVisibility::All => quote! { #path::ShaderStages::all() }, ShaderStageVisibility::All => quote! {
if cfg!(feature = "webgpu") {
todo!("Please use a more specific shader stage: https://github.com/gfx-rs/wgpu/issues/7708")
} else {
#path::ShaderStages::all()
}
},
ShaderStageVisibility::None => quote! { #path::ShaderStages::NONE }, ShaderStageVisibility::None => quote! { #path::ShaderStages::NONE },
ShaderStageVisibility::Flags(flags) => { ShaderStageVisibility::Flags(flags) => {
let mut quoted = Vec::new(); let mut quoted = Vec::new();

View File

@ -16,22 +16,22 @@
// Binding 0 is the bindless index table. // Binding 0 is the bindless index table.
// Filtering samplers. // Filtering samplers.
@group(2) @binding(1) var bindless_samplers_filtering: binding_array<sampler>; @group(3) @binding(1) var bindless_samplers_filtering: binding_array<sampler>;
// Non-filtering samplers (nearest neighbor). // Non-filtering samplers (nearest neighbor).
@group(2) @binding(2) var bindless_samplers_non_filtering: binding_array<sampler>; @group(3) @binding(2) var bindless_samplers_non_filtering: binding_array<sampler>;
// Comparison samplers (typically for shadow mapping). // Comparison samplers (typically for shadow mapping).
@group(2) @binding(3) var bindless_samplers_comparison: binding_array<sampler>; @group(3) @binding(3) var bindless_samplers_comparison: binding_array<sampler>;
// 1D textures. // 1D textures.
@group(2) @binding(4) var bindless_textures_1d: binding_array<texture_1d<f32>>; @group(3) @binding(4) var bindless_textures_1d: binding_array<texture_1d<f32>>;
// 2D textures. // 2D textures.
@group(2) @binding(5) var bindless_textures_2d: binding_array<texture_2d<f32>>; @group(3) @binding(5) var bindless_textures_2d: binding_array<texture_2d<f32>>;
// 2D array textures. // 2D array textures.
@group(2) @binding(6) var bindless_textures_2d_array: binding_array<texture_2d_array<f32>>; @group(3) @binding(6) var bindless_textures_2d_array: binding_array<texture_2d_array<f32>>;
// 3D textures. // 3D textures.
@group(2) @binding(7) var bindless_textures_3d: binding_array<texture_3d<f32>>; @group(3) @binding(7) var bindless_textures_3d: binding_array<texture_3d<f32>>;
// Cubemap textures. // Cubemap textures.
@group(2) @binding(8) var bindless_textures_cube: binding_array<texture_cube<f32>>; @group(3) @binding(8) var bindless_textures_cube: binding_array<texture_cube<f32>>;
// Cubemap array textures. // Cubemap array textures.
@group(2) @binding(9) var bindless_textures_cube_array: binding_array<texture_cube_array<f32>>; @group(3) @binding(9) var bindless_textures_cube_array: binding_array<texture_cube_array<f32>>;
#endif // BINDLESS #endif // BINDLESS

View File

@ -1,7 +1,7 @@
use crate::renderer::{RenderAdapterInfo, RenderDevice, RenderQueue}; use crate::renderer::{RenderAdapterInfo, RenderDevice, RenderQueue};
use tracy_client::{Client, GpuContext, GpuContextType}; use tracy_client::{Client, GpuContext, GpuContextType};
use wgpu::{ use wgpu::{
Backend, BufferDescriptor, BufferUsages, CommandEncoderDescriptor, Maintain, MapMode, Backend, BufferDescriptor, BufferUsages, CommandEncoderDescriptor, MapMode, PollType,
QuerySetDescriptor, QueryType, QUERY_SIZE, QuerySetDescriptor, QueryType, QUERY_SIZE,
}; };
@ -14,7 +14,7 @@ pub fn new_tracy_gpu_context(
Backend::Vulkan => GpuContextType::Vulkan, Backend::Vulkan => GpuContextType::Vulkan,
Backend::Dx12 => GpuContextType::Direct3D12, Backend::Dx12 => GpuContextType::Direct3D12,
Backend::Gl => GpuContextType::OpenGL, Backend::Gl => GpuContextType::OpenGL,
Backend::Metal | Backend::BrowserWebGpu | Backend::Empty => GpuContextType::Invalid, Backend::Metal | Backend::BrowserWebGpu | Backend::Noop => GpuContextType::Invalid,
}; };
let tracy_client = Client::running().unwrap(); let tracy_client = Client::running().unwrap();
@ -60,7 +60,9 @@ fn initial_timestamp(device: &RenderDevice, queue: &RenderQueue) -> i64 {
queue.submit([timestamp_encoder.finish(), copy_encoder.finish()]); queue.submit([timestamp_encoder.finish(), copy_encoder.finish()]);
map_buffer.slice(..).map_async(MapMode::Read, |_| ()); map_buffer.slice(..).map_async(MapMode::Read, |_| ());
device.poll(Maintain::Wait); device
.poll(PollType::Wait)
.expect("Failed to poll device for map async");
let view = map_buffer.slice(..).get_mapped_range(); let view = map_buffer.slice(..).get_mapped_range();
i64::from_le_bytes((*view).try_into().unwrap()) i64::from_le_bytes((*view).try_into().unwrap())

View File

@ -352,10 +352,12 @@ impl Plugin for RenderPlugin {
backend_options: wgpu::BackendOptions { backend_options: wgpu::BackendOptions {
gl: wgpu::GlBackendOptions { gl: wgpu::GlBackendOptions {
gles_minor_version: settings.gles3_minor_version, gles_minor_version: settings.gles3_minor_version,
fence_behavior: wgpu::GlFenceBehavior::Normal,
}, },
dx12: wgpu::Dx12BackendOptions { dx12: wgpu::Dx12BackendOptions {
shader_compiler: settings.dx12_shader_compiler.clone(), shader_compiler: settings.dx12_shader_compiler.clone(),
}, },
noop: wgpu::NoopBackendOptions { enable: false },
}, },
}); });

View File

@ -168,14 +168,16 @@ impl<P: PhaseItem> DrawFunctions<P> {
/// ``` /// ```
/// # use bevy_render::render_phase::SetItemPipeline; /// # use bevy_render::render_phase::SetItemPipeline;
/// # struct SetMeshViewBindGroup<const N: usize>; /// # struct SetMeshViewBindGroup<const N: usize>;
/// # struct SetMeshViewBindingArrayBindGroup<const N: usize>;
/// # struct SetMeshBindGroup<const N: usize>; /// # struct SetMeshBindGroup<const N: usize>;
/// # struct SetMaterialBindGroup<M, const N: usize>(std::marker::PhantomData<M>); /// # struct SetMaterialBindGroup<M, const N: usize>(std::marker::PhantomData<M>);
/// # struct DrawMesh; /// # struct DrawMesh;
/// pub type DrawMaterial<M> = ( /// pub type DrawMaterial<M> = (
/// SetItemPipeline, /// SetItemPipeline,
/// SetMeshViewBindGroup<0>, /// SetMeshViewBindGroup<0>,
/// SetMeshBindGroup<1>, /// SetMeshViewBindingArrayBindGroup<1>,
/// SetMaterialBindGroup<M, 2>, /// SetMeshBindGroup<2>,
/// SetMaterialBindGroup<M, 3>,
/// DrawMesh, /// DrawMesh,
/// ); /// );
/// ``` /// ```

View File

@ -133,12 +133,12 @@ impl Deref for BindGroup {
/// In WGSL shaders, the binding would look like this: /// In WGSL shaders, the binding would look like this:
/// ///
/// ```wgsl /// ```wgsl
/// @group(2) @binding(0) var<uniform> color: vec4<f32>; /// @group(3) @binding(0) var<uniform> color: vec4<f32>;
/// @group(2) @binding(1) var color_texture: texture_2d<f32>; /// @group(3) @binding(1) var color_texture: texture_2d<f32>;
/// @group(2) @binding(2) var color_sampler: sampler; /// @group(3) @binding(2) var color_sampler: sampler;
/// @group(2) @binding(3) var<storage> storage_buffer: array<f32>; /// @group(3) @binding(3) var<storage> storage_buffer: array<f32>;
/// @group(2) @binding(4) var<storage> raw_buffer: array<f32>; /// @group(3) @binding(4) var<storage> raw_buffer: array<f32>;
/// @group(2) @binding(5) var storage_texture: texture_storage_2d<rgba8unorm, read_write>; /// @group(3) @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 /// 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. /// are generally bound to group 2.
@ -261,7 +261,7 @@ impl Deref for BindGroup {
/// roughness: f32, /// roughness: f32,
/// }; /// };
/// ///
/// @group(2) @binding(0) var<uniform> material: CoolMaterial; /// @group(3) @binding(0) var<uniform> material: CoolMaterial;
/// ``` /// ```
/// ///
/// Some less common scenarios will require "struct-level" attributes. These are the currently supported struct-level attributes: /// Some less common scenarios will require "struct-level" attributes. These are the currently supported struct-level attributes:
@ -312,7 +312,7 @@ impl Deref for BindGroup {
/// declaration: /// declaration:
/// ///
/// ```wgsl /// ```wgsl
/// @group(2) @binding(10) var<storage> material_array: binding_array<StandardMaterial>; /// @group(3) @binding(10) var<storage> material_array: binding_array<StandardMaterial>;
/// ``` /// ```
/// ///
/// On the other hand, if you write this declaration: /// On the other hand, if you write this declaration:
@ -325,7 +325,7 @@ impl Deref for BindGroup {
/// Then Bevy produces a binding that matches this WGSL declaration instead: /// Then Bevy produces a binding that matches this WGSL declaration instead:
/// ///
/// ```wgsl /// ```wgsl
/// @group(2) @binding(10) var<storage> material_array: array<StandardMaterial>; /// @group(3) @binding(10) var<storage> material_array: array<StandardMaterial>;
/// ``` /// ```
/// ///
/// * Just as with the structure-level `uniform` attribute, Bevy converts the /// * Just as with the structure-level `uniform` attribute, Bevy converts the
@ -338,7 +338,7 @@ impl Deref for BindGroup {
/// this in WGSL in non-bindless mode: /// this in WGSL in non-bindless mode:
/// ///
/// ```wgsl /// ```wgsl
/// @group(2) @binding(0) var<uniform> material: StandardMaterial; /// @group(3) @binding(0) var<uniform> material: StandardMaterial;
/// ``` /// ```
/// ///
/// * For efficiency reasons, `data` is generally preferred over `uniform` /// * For efficiency reasons, `data` is generally preferred over `uniform`

View File

@ -287,6 +287,12 @@ impl<'b> DynamicBindGroupEntries<'b> {
} }
} }
pub fn new() -> Self {
Self {
entries: Vec::new(),
}
}
pub fn extend_with_indices<const N: usize>( pub fn extend_with_indices<const N: usize>(
mut self, mut self,
entries: impl IntoIndexedBindingArray<'b, N>, entries: impl IntoIndexedBindingArray<'b, N>,

View File

@ -334,6 +334,13 @@ impl DynamicBindGroupLayoutEntries {
} }
} }
pub fn new(default_visibility: ShaderStages) -> Self {
Self {
default_visibility,
entries: Vec::new(),
}
}
pub fn extend_with_indices<const N: usize>( pub fn extend_with_indices<const N: usize>(
mut self, mut self,
entries: impl IntoIndexedBindGroupLayoutEntryBuilderArray<N>, entries: impl IntoIndexedBindGroupLayoutEntryBuilderArray<N>,
@ -570,6 +577,16 @@ pub mod binding_types {
} }
pub fn acceleration_structure() -> BindGroupLayoutEntryBuilder { pub fn acceleration_structure() -> BindGroupLayoutEntryBuilder {
BindingType::AccelerationStructure.into_bind_group_layout_entry_builder() BindingType::AccelerationStructure {
vertex_return: false,
}
.into_bind_group_layout_entry_builder()
}
pub fn acceleration_structure_vertex_return() -> BindGroupLayoutEntryBuilder {
BindingType::AccelerationStructure {
vertex_return: true,
}
.into_bind_group_layout_entry_builder()
} }
} }

View File

@ -243,35 +243,65 @@ pub fn create_bindless_bind_group_layout_entries(
false, false,
NonZeroU64::new(bindless_index_table_length as u64 * size_of::<u32>() as u64), NonZeroU64::new(bindless_index_table_length as u64 * size_of::<u32>() as u64),
) )
.build(*bindless_index_table_binding_number, ShaderStages::all()), .build(
*bindless_index_table_binding_number,
ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE,
),
// Continue with the common bindless resource arrays. // Continue with the common bindless resource arrays.
sampler(SamplerBindingType::Filtering) sampler(SamplerBindingType::Filtering)
.count(bindless_slab_resource_limit) .count(bindless_slab_resource_limit)
.build(1, ShaderStages::all()), .build(
1,
ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE,
),
sampler(SamplerBindingType::NonFiltering) sampler(SamplerBindingType::NonFiltering)
.count(bindless_slab_resource_limit) .count(bindless_slab_resource_limit)
.build(2, ShaderStages::all()), .build(
2,
ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE,
),
sampler(SamplerBindingType::Comparison) sampler(SamplerBindingType::Comparison)
.count(bindless_slab_resource_limit) .count(bindless_slab_resource_limit)
.build(3, ShaderStages::all()), .build(
3,
ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE,
),
texture_1d(TextureSampleType::Float { filterable: true }) texture_1d(TextureSampleType::Float { filterable: true })
.count(bindless_slab_resource_limit) .count(bindless_slab_resource_limit)
.build(4, ShaderStages::all()), .build(
4,
ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE,
),
texture_2d(TextureSampleType::Float { filterable: true }) texture_2d(TextureSampleType::Float { filterable: true })
.count(bindless_slab_resource_limit) .count(bindless_slab_resource_limit)
.build(5, ShaderStages::all()), .build(
5,
ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE,
),
texture_2d_array(TextureSampleType::Float { filterable: true }) texture_2d_array(TextureSampleType::Float { filterable: true })
.count(bindless_slab_resource_limit) .count(bindless_slab_resource_limit)
.build(6, ShaderStages::all()), .build(
6,
ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE,
),
texture_3d(TextureSampleType::Float { filterable: true }) texture_3d(TextureSampleType::Float { filterable: true })
.count(bindless_slab_resource_limit) .count(bindless_slab_resource_limit)
.build(7, ShaderStages::all()), .build(
7,
ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE,
),
texture_cube(TextureSampleType::Float { filterable: true }) texture_cube(TextureSampleType::Float { filterable: true })
.count(bindless_slab_resource_limit) .count(bindless_slab_resource_limit)
.build(8, ShaderStages::all()), .build(
8,
ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE,
),
texture_cube_array(TextureSampleType::Float { filterable: true }) texture_cube_array(TextureSampleType::Float { filterable: true })
.count(bindless_slab_resource_limit) .count(bindless_slab_resource_limit)
.build(9, ShaderStages::all()), .build(
9,
ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE,
),
] ]
} }

View File

@ -49,18 +49,18 @@ pub use wgpu::{
ComputePassDescriptor, ComputePipelineDescriptor as RawComputePipelineDescriptor, ComputePassDescriptor, ComputePipelineDescriptor as RawComputePipelineDescriptor,
CreateBlasDescriptor, CreateTlasDescriptor, DepthBiasState, DepthStencilState, DownlevelFlags, CreateBlasDescriptor, CreateTlasDescriptor, DepthBiasState, DepthStencilState, DownlevelFlags,
Extent3d, Face, Features as WgpuFeatures, FilterMode, FragmentState as RawFragmentState, Extent3d, Face, Features as WgpuFeatures, FilterMode, FragmentState as RawFragmentState,
FrontFace, ImageSubresourceRange, IndexFormat, Limits as WgpuLimits, LoadOp, Maintain, MapMode, FrontFace, ImageSubresourceRange, IndexFormat, Limits as WgpuLimits, LoadOp, MapMode,
MultisampleState, Operations, Origin3d, PipelineCompilationOptions, PipelineLayout, MultisampleState, Operations, Origin3d, PipelineCompilationOptions, PipelineLayout,
PipelineLayoutDescriptor, PolygonMode, PrimitiveState, PrimitiveTopology, PushConstantRange, PipelineLayoutDescriptor, PollType, PolygonMode, PrimitiveState, PrimitiveTopology,
RenderPassColorAttachment, RenderPassDepthStencilAttachment, RenderPassDescriptor, PushConstantRange, RenderPassColorAttachment, RenderPassDepthStencilAttachment,
RenderPipelineDescriptor as RawRenderPipelineDescriptor, Sampler as WgpuSampler, RenderPassDescriptor, RenderPipelineDescriptor as RawRenderPipelineDescriptor,
SamplerBindingType, SamplerBindingType as WgpuSamplerBindingType, SamplerDescriptor, Sampler as WgpuSampler, SamplerBindingType, SamplerBindingType as WgpuSamplerBindingType,
ShaderModule, ShaderModuleDescriptor, ShaderSource, ShaderStages, StencilFaceState, SamplerDescriptor, ShaderModule, ShaderModuleDescriptor, ShaderSource, ShaderStages,
StencilOperation, StencilState, StorageTextureAccess, StoreOp, TexelCopyBufferInfo, StencilFaceState, StencilOperation, StencilState, StorageTextureAccess, StoreOp,
TexelCopyBufferLayout, TexelCopyTextureInfo, TextureAspect, TextureDescriptor, TexelCopyBufferInfo, TexelCopyBufferLayout, TexelCopyTextureInfo, TextureAspect,
TextureDimension, TextureFormat, TextureFormatFeatureFlags, TextureFormatFeatures, TextureDescriptor, TextureDimension, TextureFormat, TextureFormatFeatureFlags,
TextureSampleType, TextureUsages, TextureView as WgpuTextureView, TextureViewDescriptor, TextureFormatFeatures, TextureSampleType, TextureUsages, TextureView as WgpuTextureView,
TextureViewDimension, Tlas, TlasInstance, TlasPackage, VertexAttribute, TextureViewDescriptor, TextureViewDimension, Tlas, TlasInstance, TlasPackage, VertexAttribute,
VertexBufferLayout as RawVertexBufferLayout, VertexFormat, VertexState as RawVertexState, VertexBufferLayout as RawVertexBufferLayout, VertexFormat, VertexState as RawVertexState,
VertexStepMode, COPY_BUFFER_ALIGNMENT, VertexStepMode, COPY_BUFFER_ALIGNMENT,
}; };

View File

@ -873,7 +873,7 @@ impl PipelineCache {
// TODO: Expose the rest of this somehow // TODO: Expose the rest of this somehow
let compilation_options = PipelineCompilationOptions { let compilation_options = PipelineCompilationOptions {
constants: &default(), constants: &[],
zero_initialize_workgroup_memory: descriptor.zero_initialize_workgroup_memory, zero_initialize_workgroup_memory: descriptor.zero_initialize_workgroup_memory,
}; };
@ -955,7 +955,7 @@ impl PipelineCache {
entry_point: Some(&descriptor.entry_point), entry_point: Some(&descriptor.entry_point),
// TODO: Expose the rest of this somehow // TODO: Expose the rest of this somehow
compilation_options: PipelineCompilationOptions { compilation_options: PipelineCompilationOptions {
constants: &default(), constants: &[],
zero_initialize_workgroup_memory: descriptor zero_initialize_workgroup_memory: descriptor
.zero_initialize_workgroup_memory, .zero_initialize_workgroup_memory,
}, },
@ -1155,8 +1155,12 @@ fn get_capabilities(features: Features, downlevel: DownlevelFlags) -> Capabiliti
features.contains(Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING), features.contains(Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING),
); );
capabilities.set( capabilities.set(
Capabilities::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING, Capabilities::STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING,
features.contains(Features::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING), features.contains(Features::STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING),
);
capabilities.set(
Capabilities::UNIFORM_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
features.contains(Features::UNIFORM_BUFFER_BINDING_ARRAYS),
); );
// TODO: This needs a proper wgpu feature // TODO: This needs a proper wgpu feature
capabilities.set( capabilities.set(
@ -1229,6 +1233,14 @@ fn get_capabilities(features: Features, downlevel: DownlevelFlags) -> Capabiliti
Capabilities::TEXTURE_INT64_ATOMIC, Capabilities::TEXTURE_INT64_ATOMIC,
features.contains(Features::TEXTURE_INT64_ATOMIC), features.contains(Features::TEXTURE_INT64_ATOMIC),
); );
capabilities.set(
Capabilities::SHADER_FLOAT16,
features.contains(Features::SHADER_F16),
);
capabilities.set(
Capabilities::RAY_HIT_VERTEX_POSITION,
features.intersects(Features::EXPERIMENTAL_RAY_HIT_VERTEX_RETURN),
);
capabilities capabilities
} }

View File

@ -301,6 +301,8 @@ impl From<&Source> for naga_oil::compose::ShaderType {
naga::ShaderStage::Vertex => naga_oil::compose::ShaderType::GlslVertex, naga::ShaderStage::Vertex => naga_oil::compose::ShaderType::GlslVertex,
naga::ShaderStage::Fragment => naga_oil::compose::ShaderType::GlslFragment, naga::ShaderStage::Fragment => naga_oil::compose::ShaderType::GlslFragment,
naga::ShaderStage::Compute => panic!("glsl compute not yet implemented"), naga::ShaderStage::Compute => panic!("glsl compute not yet implemented"),
naga::ShaderStage::Task => panic!("task shaders not yet implemented"),
naga::ShaderStage::Mesh => panic!("mesh shaders not yet implemented"),
}, },
#[cfg(all(not(feature = "shader_format_glsl"), not(target_arch = "wasm32")))] #[cfg(all(not(feature = "shader_format_glsl"), not(target_arch = "wasm32")))]
Source::Glsl(_, _) => panic!( Source::Glsl(_, _) => panic!(

View File

@ -23,7 +23,7 @@ use bevy_platform::time::Instant;
use bevy_time::TimeSender; use bevy_time::TimeSender;
use wgpu::{ use wgpu::{
Adapter, AdapterInfo, CommandBuffer, CommandEncoder, DeviceType, Instance, Queue, Adapter, AdapterInfo, CommandBuffer, CommandEncoder, DeviceType, Instance, Queue,
RequestAdapterOptions, RequestAdapterOptions, Trace,
}; };
/// Updates the [`RenderGraph`] with all of its nodes and then runs it to render the entire frame. /// Updates the [`RenderGraph`] with all of its nodes and then runs it to render the entire frame.
@ -177,7 +177,7 @@ pub async fn initialize_renderer(
// discrete GPUs due to having to transfer data across the PCI-E bus and so it // discrete GPUs due to having to transfer data across the PCI-E bus and so it
// should not be automatically enabled in this case. It is however beneficial for // should not be automatically enabled in this case. It is however beneficial for
// integrated GPUs. // integrated GPUs.
features -= wgpu::Features::MAPPABLE_PRIMARY_BUFFERS; features.remove(wgpu::Features::MAPPABLE_PRIMARY_BUFFERS);
} }
limits = adapter.limits(); limits = adapter.limits();
@ -185,7 +185,7 @@ pub async fn initialize_renderer(
// Enforce the disabled features // Enforce the disabled features
if let Some(disabled_features) = options.disabled_features { if let Some(disabled_features) = options.disabled_features {
features -= disabled_features; features.remove(disabled_features);
} }
// NOTE: |= is used here to ensure that any explicitly-enabled features are respected. // NOTE: |= is used here to ensure that any explicitly-enabled features are respected.
features |= options.features; features |= options.features;
@ -234,6 +234,12 @@ pub async fn initialize_renderer(
max_uniform_buffers_per_shader_stage: limits max_uniform_buffers_per_shader_stage: limits
.max_uniform_buffers_per_shader_stage .max_uniform_buffers_per_shader_stage
.min(constrained_limits.max_uniform_buffers_per_shader_stage), .min(constrained_limits.max_uniform_buffers_per_shader_stage),
max_binding_array_elements_per_shader_stage: limits
.max_binding_array_elements_per_shader_stage
.min(constrained_limits.max_binding_array_elements_per_shader_stage),
max_binding_array_sampler_elements_per_shader_stage: limits
.max_binding_array_sampler_elements_per_shader_stage
.min(constrained_limits.max_binding_array_sampler_elements_per_shader_stage),
max_uniform_buffer_binding_size: limits max_uniform_buffer_binding_size: limits
.max_uniform_buffer_binding_size .max_uniform_buffer_binding_size
.min(constrained_limits.max_uniform_buffer_binding_size), .min(constrained_limits.max_uniform_buffer_binding_size),
@ -304,15 +310,14 @@ pub async fn initialize_renderer(
} }
let (device, queue) = adapter let (device, queue) = adapter
.request_device( .request_device(&wgpu::DeviceDescriptor {
&wgpu::DeviceDescriptor { label: options.device_label.as_ref().map(AsRef::as_ref),
label: options.device_label.as_ref().map(AsRef::as_ref), required_features: features,
required_features: features, required_limits: limits,
required_limits: limits, memory_hints: options.memory_hints.clone(),
memory_hints: options.memory_hints.clone(), // See https://github.com/gfx-rs/wgpu/issues/5974
}, trace: Trace::Off,
options.trace_path.as_deref(), })
)
.await .await
.unwrap(); .unwrap();
let queue = Arc::new(WgpuWrapper::new(queue)); let queue = Arc::new(WgpuWrapper::new(queue));

View File

@ -7,7 +7,7 @@ use bevy_ecs::resource::Resource;
use bevy_utils::WgpuWrapper; use bevy_utils::WgpuWrapper;
use wgpu::{ use wgpu::{
util::DeviceExt, BindGroupDescriptor, BindGroupEntry, BindGroupLayoutDescriptor, util::DeviceExt, BindGroupDescriptor, BindGroupEntry, BindGroupLayoutDescriptor,
BindGroupLayoutEntry, BufferAsyncError, BufferBindingType, MaintainResult, BindGroupLayoutEntry, BufferAsyncError, BufferBindingType, PollError, PollStatus,
}; };
/// This GPU device is responsible for the creation of most rendering and compute resources. /// This GPU device is responsible for the creation of most rendering and compute resources.
@ -67,11 +67,14 @@ impl RenderDevice {
// This call passes binary data to the backend as-is and can potentially result in a driver crash or bogus behavior. // This call passes binary data to the backend as-is and can potentially result in a driver crash or bogus behavior.
// No attempt is made to ensure that data is valid SPIR-V. // No attempt is made to ensure that data is valid SPIR-V.
unsafe { unsafe {
self.device self.device.create_shader_module_passthrough(
.create_shader_module_spirv(&wgpu::ShaderModuleDescriptorSpirV { wgpu::ShaderModuleDescriptorPassthrough::SpirV(
label: desc.label, wgpu::ShaderModuleDescriptorSpirV {
source: source.clone(), label: desc.label,
}) source: source.clone(),
},
),
)
} }
} }
// SAFETY: // SAFETY:
@ -118,7 +121,7 @@ impl RenderDevice {
/// ///
/// no-op on the web, device is automatically polled. /// no-op on the web, device is automatically polled.
#[inline] #[inline]
pub fn poll(&self, maintain: wgpu::Maintain) -> MaintainResult { pub fn poll(&self, maintain: wgpu::PollType) -> Result<PollStatus, PollError> {
self.device.poll(maintain) self.device.poll(maintain)
} }

View File

@ -2,8 +2,8 @@ use crate::renderer::{
RenderAdapter, RenderAdapterInfo, RenderDevice, RenderInstance, RenderQueue, RenderAdapter, RenderAdapterInfo, RenderDevice, RenderInstance, RenderQueue,
}; };
use alloc::borrow::Cow; use alloc::borrow::Cow;
use std::path::PathBuf;
use wgpu::DxcShaderModel;
pub use wgpu::{ pub use wgpu::{
Backends, Dx12Compiler, Features as WgpuFeatures, Gles3MinorVersion, InstanceFlags, Backends, Dx12Compiler, Features as WgpuFeatures, Gles3MinorVersion, InstanceFlags,
Limits as WgpuLimits, MemoryHints, PowerPreference, Limits as WgpuLimits, MemoryHints, PowerPreference,
@ -53,8 +53,6 @@ pub struct WgpuSettings {
pub instance_flags: InstanceFlags, pub instance_flags: InstanceFlags,
/// This hints to the WGPU device about the preferred memory allocation strategy. /// This hints to the WGPU device about the preferred memory allocation strategy.
pub memory_hints: MemoryHints, pub memory_hints: MemoryHints,
/// The path to pass to wgpu for API call tracing. This only has an effect if wgpu's tracing functionality is enabled.
pub trace_path: Option<PathBuf>,
} }
impl Default for WgpuSettings { impl Default for WgpuSettings {
@ -114,6 +112,7 @@ impl Default for WgpuSettings {
Dx12Compiler::DynamicDxc { Dx12Compiler::DynamicDxc {
dxc_path: String::from(dxc), dxc_path: String::from(dxc),
dxil_path: String::from(dxil), dxil_path: String::from(dxil),
max_shader_model: DxcShaderModel::V6_7,
} }
} else { } else {
Dx12Compiler::Fxc Dx12Compiler::Fxc
@ -137,7 +136,6 @@ impl Default for WgpuSettings {
gles3_minor_version, gles3_minor_version,
instance_flags, instance_flags,
memory_hints: MemoryHints::default(), memory_hints: MemoryHints::default(),
trace_path: None,
} }
} }
} }

View File

@ -46,7 +46,6 @@ impl SolariPlugin {
| WgpuFeatures::EXPERIMENTAL_RAY_QUERY | WgpuFeatures::EXPERIMENTAL_RAY_QUERY
| WgpuFeatures::BUFFER_BINDING_ARRAY | WgpuFeatures::BUFFER_BINDING_ARRAY
| WgpuFeatures::TEXTURE_BINDING_ARRAY | WgpuFeatures::TEXTURE_BINDING_ARRAY
| WgpuFeatures::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING
| WgpuFeatures::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING | WgpuFeatures::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING
| WgpuFeatures::PARTIALLY_BOUND_BINDING_ARRAY | WgpuFeatures::PARTIALLY_BOUND_BINDING_ARRAY
} }

View File

@ -71,8 +71,8 @@ struct DirectionalLight {
@group(0) @binding(9) var<storage> light_sources: array<LightSource>; @group(0) @binding(9) var<storage> light_sources: array<LightSource>;
@group(0) @binding(10) var<storage> directional_lights: array<DirectionalLight>; @group(0) @binding(10) var<storage> directional_lights: array<DirectionalLight>;
const RAY_T_MIN = 0.01; const RAY_T_MIN = 0.01f;
const RAY_T_MAX = 100000.0; const RAY_T_MAX = 100000.0f;
const RAY_NO_CULL = 0xFFu; const RAY_NO_CULL = 0xFFu;
@ -120,7 +120,7 @@ fn resolve_material(material: Material, uv: vec2<f32>) -> ResolvedMaterial {
fn resolve_ray_hit_full(ray_hit: RayIntersection) -> ResolvedRayHitFull { fn resolve_ray_hit_full(ray_hit: RayIntersection) -> ResolvedRayHitFull {
let barycentrics = vec3(1.0 - ray_hit.barycentrics.x - ray_hit.barycentrics.y, ray_hit.barycentrics); let barycentrics = vec3(1.0 - ray_hit.barycentrics.x - ray_hit.barycentrics.y, ray_hit.barycentrics);
return resolve_triangle_data_full(ray_hit.instance_id, ray_hit.primitive_index, barycentrics); return resolve_triangle_data_full(ray_hit.instance_index, ray_hit.primitive_index, barycentrics);
} }
fn resolve_triangle_data_full(instance_id: u32, triangle_id: u32, barycentrics: vec3<f32>) -> ResolvedRayHitFull { fn resolve_triangle_data_full(instance_id: u32, triangle_id: u32, barycentrics: vec3<f32>) -> ResolvedRayHitFull {

View File

@ -59,7 +59,7 @@ cfg-if = "1.0"
raw-window-handle = "0.6" raw-window-handle = "0.6"
serde = { version = "1.0", features = ["derive"], optional = true } serde = { version = "1.0", features = ["derive"], optional = true }
bytemuck = { version = "1.5", optional = true } bytemuck = { version = "1.5", optional = true }
wgpu-types = { version = "24", optional = true } wgpu-types = { version = "25", optional = true }
accesskit = "0.19" accesskit = "0.19"
tracing = { version = "0.1", default-features = false, features = ["std"] } tracing = { version = "0.1", default-features = false, features = ["std"] }

View File

@ -22,8 +22,8 @@ use bevy::{
render_asset::{RenderAssetUsages, RenderAssets}, render_asset::{RenderAssetUsages, RenderAssets},
render_graph::{self, NodeRunError, RenderGraph, RenderGraphContext, RenderLabel}, render_graph::{self, NodeRunError, RenderGraph, RenderGraphContext, RenderLabel},
render_resource::{ render_resource::{
Buffer, BufferDescriptor, BufferUsages, CommandEncoderDescriptor, Extent3d, Maintain, Buffer, BufferDescriptor, BufferUsages, CommandEncoderDescriptor, Extent3d, MapMode,
MapMode, TexelCopyBufferInfo, TexelCopyBufferLayout, TextureDimension, TextureFormat, PollType, TexelCopyBufferInfo, TexelCopyBufferLayout, TextureDimension, TextureFormat,
TextureUsages, TextureUsages,
}, },
renderer::{RenderContext, RenderDevice, RenderQueue}, renderer::{RenderContext, RenderDevice, RenderQueue},
@ -41,7 +41,6 @@ use std::{
}, },
time::Duration, time::Duration,
}; };
// To communicate between the main world and the render world we need a channel. // To communicate between the main world and the render world we need a channel.
// Since the main world and render world run in parallel, there will always be a frame of latency // Since the main world and render world run in parallel, there will always be a frame of latency
// between the data sent from the render world and the data received in the main world // between the data sent from the render world and the data received in the main world
@ -460,7 +459,9 @@ fn receive_image_from_buffer(
// `Maintain::Wait` will cause the thread to wait on native but not on WebGpu. // `Maintain::Wait` will cause the thread to wait on native but not on WebGpu.
// This blocks until the gpu is done executing everything // This blocks until the gpu is done executing everything
render_device.poll(Maintain::wait()).panic_on_timeout(); render_device
.poll(PollType::Wait)
.expect("Failed to poll device for map async");
// This blocks until the buffer is mapped // This blocks until the buffer is mapped
r.recv().expect("Failed to receive the map_async message"); r.recv().expect("Failed to receive the map_async message");

View File

@ -12,6 +12,7 @@
use std::ops::Range; use std::ops::Range;
use bevy::pbr::SetMeshViewEmptyBindGroup;
use bevy::{ use bevy::{
core_pipeline::core_3d::graph::{Core3d, Node3d}, core_pipeline::core_3d::graph::{Core3d, Node3d},
ecs::{ ecs::{
@ -193,17 +194,19 @@ impl SpecializedMeshPipeline for StencilPipeline {
} }
// This will automatically generate the correct `VertexBufferLayout` based on the vertex attributes // This will automatically generate the correct `VertexBufferLayout` based on the vertex attributes
let vertex_buffer_layout = layout.0.get_layout(&vertex_attributes)?; let vertex_buffer_layout = layout.0.get_layout(&vertex_attributes)?;
let view_layout = self
.mesh_pipeline
.get_view_layout(MeshPipelineViewLayoutKey::from(key));
Ok(RenderPipelineDescriptor { Ok(RenderPipelineDescriptor {
label: Some("Specialized Mesh Pipeline".into()), label: Some("Specialized Mesh Pipeline".into()),
// We want to reuse the data from bevy so we use the same bind groups as the default // We want to reuse the data from bevy so we use the same bind groups as the default
// mesh pipeline // mesh pipeline
layout: vec![ layout: vec![
// Bind group 0 is the view uniform // Bind group 0 is the view uniform
self.mesh_pipeline view_layout.main_layout.clone(),
.get_view_layout(MeshPipelineViewLayoutKey::from(key)) // Bind group 1 is empty
.clone(), view_layout.empty_layout.clone(),
// Bind group 1 is the mesh uniform // Bind group 2 is the mesh uniform
self.mesh_pipeline.mesh_layouts.model_only.clone(), self.mesh_pipeline.mesh_layouts.model_only.clone(),
], ],
push_constant_ranges: vec![], push_constant_ranges: vec![],
@ -244,8 +247,10 @@ type DrawMesh3dStencil = (
SetItemPipeline, SetItemPipeline,
// This will set the view bindings in group 0 // This will set the view bindings in group 0
SetMeshViewBindGroup<0>, SetMeshViewBindGroup<0>,
// This will set the mesh bindings in group 1 // This will set an empty bind group in group 1
SetMeshBindGroup<1>, SetMeshViewEmptyBindGroup<1>,
// This will set the mesh bindings in group 2
SetMeshBindGroup<2>,
// This will draw the mesh // This will draw the mesh
DrawMesh, DrawMesh,
); );

View File

@ -7,6 +7,7 @@
//! implementation using bevy's low level rendering api. //! implementation using bevy's low level rendering api.
//! It's generally recommended to try the built-in instancing before going with this approach. //! It's generally recommended to try the built-in instancing before going with this approach.
use bevy::pbr::SetMeshViewBindingArrayBindGroup;
use bevy::{ use bevy::{
core_pipeline::core_3d::Transparent3d, core_pipeline::core_3d::Transparent3d,
ecs::{ ecs::{
@ -248,7 +249,8 @@ impl SpecializedMeshPipeline for CustomPipeline {
type DrawCustom = ( type DrawCustom = (
SetItemPipeline, SetItemPipeline,
SetMeshViewBindGroup<0>, SetMeshViewBindGroup<0>,
SetMeshBindGroup<1>, SetMeshViewBindingArrayBindGroup<1>,
SetMeshBindGroup<2>,
DrawMeshInstanced, DrawMeshInstanced,
); );

View File

@ -12,7 +12,7 @@ use bevy::{
math::{vec3, vec4}, math::{vec3, vec4},
pbr::{ pbr::{
DrawMesh, MeshPipeline, MeshPipelineKey, MeshPipelineViewLayoutKey, RenderMeshInstances, DrawMesh, MeshPipeline, MeshPipelineKey, MeshPipelineViewLayoutKey, RenderMeshInstances,
SetMeshBindGroup, SetMeshViewBindGroup, SetMeshBindGroup, SetMeshViewBindGroup, SetMeshViewEmptyBindGroup,
}, },
prelude::*, prelude::*,
render::{ render::{
@ -153,8 +153,10 @@ type DrawSpecializedPipelineCommands = (
SetItemPipeline, SetItemPipeline,
// Set the view uniform at bind group 0 // Set the view uniform at bind group 0
SetMeshViewBindGroup<0>, SetMeshViewBindGroup<0>,
// Set the mesh uniform at bind group 1 // Set an empty material bind group at bind group 1
SetMeshBindGroup<1>, SetMeshViewEmptyBindGroup<1>,
// Set the mesh uniform at bind group 2
SetMeshBindGroup<2>,
// Draw the mesh // Draw the mesh
DrawMesh, DrawMesh,
); );
@ -210,14 +212,15 @@ impl SpecializedMeshPipeline for CustomMeshPipeline {
// This will automatically generate the correct `VertexBufferLayout` based on the vertex attributes // This will automatically generate the correct `VertexBufferLayout` based on the vertex attributes
let vertex_buffer_layout = layout.0.get_layout(&vertex_attributes)?; let vertex_buffer_layout = layout.0.get_layout(&vertex_attributes)?;
let view_layout = self
.mesh_pipeline
.get_view_layout(MeshPipelineViewLayoutKey::from(mesh_key));
Ok(RenderPipelineDescriptor { Ok(RenderPipelineDescriptor {
label: Some("Specialized Mesh Pipeline".into()), label: Some("Specialized Mesh Pipeline".into()),
layout: vec![ layout: vec![
// Bind group 0 is the view uniform view_layout.main_layout.clone(),
self.mesh_pipeline view_layout.empty_layout.clone(),
.get_view_layout(MeshPipelineViewLayoutKey::from(mesh_key))
.clone(),
// Bind group 1 is the mesh uniform
self.mesh_pipeline.mesh_layouts.model_only.clone(), self.mesh_pipeline.mesh_layouts.model_only.clone(),
], ],
push_constant_ranges: vec![], push_constant_ranges: vec![],

View File

@ -163,7 +163,7 @@ impl AsBindGroup for BindlessMaterial {
( (
// Screen texture // Screen texture
// //
// @group(2) @binding(0) var textures: binding_array<texture_2d<f32>>; // @group(3) @binding(0) var textures: binding_array<texture_2d<f32>>;
( (
0, 0,
texture_2d(TextureSampleType::Float { filterable: true }) texture_2d(TextureSampleType::Float { filterable: true })
@ -171,7 +171,7 @@ impl AsBindGroup for BindlessMaterial {
), ),
// Sampler // Sampler
// //
// @group(2) @binding(1) var nearest_sampler: sampler; // @group(3) @binding(1) var nearest_sampler: sampler;
// //
// Note: as with textures, multiple samplers can also be bound // Note: as with textures, multiple samplers can also be bound
// onto one binding slot: // onto one binding slot:

View File

@ -0,0 +1,22 @@
---
title: `wgpu` 25
pull_requests: [ 19563 ]
---
`wgpu` 25 introduces a number of breaking changes, most notably in the way Bevy is required to handle
uniforms with dynamic offsets which are used pervasively in the renderer. Dynamic offsets and uniforms
of any kind are no longer allowed to be used in the same bind group as binding arrays. As such, the
following changes to the default bind group numbering have been made in 3d:
- `@group(0)` view binding resources
- `@group(1)` view resources requiring binding arrays
- `@group(2)` mesh binding resources
- `@group(3)` material binding resources
Most users who are not using mid-level render APIs will simply need to switch their material bind groups
from `@group(2)` to `@group(3)`.
Exported float constants from shaders without an explicit type declaration like `const FOO = 1.0;` are no
longer supported and must be explicitly typed like `const FOO: f32 = 1.0;`.
See the full changelog [here](https://github.com/gfx-rs/wgpu/blob/trunk/CHANGELOG.md#v2500-2025-04-10).