From 96dcbc5f8c37753dd854b0902758d0e2030e0df9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?charlotte=20=F0=9F=8C=B8?= Date: Thu, 26 Jun 2025 12:41:47 -0700 Subject: [PATCH] Ugrade to `wgpu` version `25.0` (#19563) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # 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 view: View; @group(0) @binding(1) var lights: types::Lights; #else @group(0) @binding(0) var view: array; @group(0) @binding(1) var lights: array; #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_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 Co-authored-by: Elabajaba --- assets/shaders/array_texture.wgsl | 4 +- assets/shaders/automatic_instancing.wgsl | 4 +- assets/shaders/bindless_material.wgsl | 10 +- assets/shaders/cubemap_unlit.wgsl | 6 +- assets/shaders/custom_material.frag | 6 +- assets/shaders/custom_material.vert | 4 +- assets/shaders/custom_material.wesl | 2 +- assets/shaders/custom_material.wgsl | 6 +- .../custom_material_screenspace_texture.wgsl | 4 +- assets/shaders/custom_vertex_attribute.wgsl | 2 +- assets/shaders/extended_material.wgsl | 2 +- .../shaders/extended_material_bindless.wgsl | 10 +- assets/shaders/fallback_image_test.wgsl | 24 +- ...irradiance_volume_voxel_visualization.wgsl | 2 +- assets/shaders/line_material.wgsl | 2 +- assets/shaders/shader_defs.wgsl | 2 +- assets/shaders/show_prepass.wgsl | 2 +- assets/shaders/storage_buffer.wgsl | 2 +- assets/shaders/texture_binding_array.wgsl | 4 +- assets/shaders/water_material.wgsl | 6 +- crates/bevy_color/Cargo.toml | 2 +- crates/bevy_gizmos/src/pipeline_3d.rs | 6 +- crates/bevy_image/Cargo.toml | 2 +- crates/bevy_mesh/Cargo.toml | 2 +- .../bevy_pbr/src/atmosphere/render_sky.wgsl | 8 +- crates/bevy_pbr/src/decal/forward_decal.wgsl | 2 +- .../src/deferred/deferred_lighting.wgsl | 2 +- crates/bevy_pbr/src/deferred/mod.rs | 21 +- crates/bevy_pbr/src/lib.rs | 4 +- crates/bevy_pbr/src/lightmap/lightmap.wgsl | 8 +- crates/bevy_pbr/src/material.rs | 13 +- .../src/meshlet/material_pipeline_prepare.rs | 14 +- .../src/meshlet/material_shade_nodes.rs | 2 +- .../src/meshlet/meshlet_bindings.wgsl | 18 +- .../bevy_pbr/src/meshlet/resource_manager.rs | 4 +- crates/bevy_pbr/src/prepass/mod.rs | 69 ++++- crates/bevy_pbr/src/render/mesh.rs | 53 +++- crates/bevy_pbr/src/render/mesh_bindings.wgsl | 4 +- .../bevy_pbr/src/render/mesh_view_bindings.rs | 280 ++++++++++-------- .../src/render/mesh_view_bindings.wgsl | 78 ++--- crates/bevy_pbr/src/render/morph.wgsl | 6 +- crates/bevy_pbr/src/render/pbr_bindings.wgsl | 66 ++--- crates/bevy_pbr/src/render/skinning.wgsl | 8 +- crates/bevy_pbr/src/ssr/mod.rs | 14 +- crates/bevy_pbr/src/ssr/raymarch.wgsl | 4 +- crates/bevy_pbr/src/ssr/ssr.wgsl | 4 +- crates/bevy_pbr/src/volumetric_fog/render.rs | 16 +- crates/bevy_pbr/src/wireframe.rs | 6 +- crates/bevy_reflect/Cargo.toml | 2 +- crates/bevy_render/Cargo.toml | 12 +- .../bevy_render/macros/src/as_bind_group.rs | 20 +- crates/bevy_render/src/bindless.wgsl | 18 +- .../bevy_render/src/diagnostic/tracy_gpu.rs | 8 +- crates/bevy_render/src/lib.rs | 2 + crates/bevy_render/src/render_phase/draw.rs | 6 +- .../src/render_resource/bind_group.rs | 20 +- .../src/render_resource/bind_group_entries.rs | 6 + .../bind_group_layout_entries.rs | 19 +- .../src/render_resource/bindless.rs | 50 +++- crates/bevy_render/src/render_resource/mod.rs | 22 +- .../src/render_resource/pipeline_cache.rs | 20 +- .../bevy_render/src/render_resource/shader.rs | 2 + crates/bevy_render/src/renderer/mod.rs | 29 +- .../bevy_render/src/renderer/render_device.rs | 17 +- crates/bevy_render/src/settings.rs | 6 +- crates/bevy_solari/src/lib.rs | 1 - .../src/scene/raytracing_scene_bindings.wgsl | 6 +- crates/bevy_winit/Cargo.toml | 2 +- examples/app/headless_renderer.rs | 9 +- examples/shader/custom_render_phase.rs | 19 +- examples/shader/custom_shader_instancing.rs | 4 +- examples/shader/specialized_mesh_pipeline.rs | 19 +- examples/shader/texture_binding_array.rs | 4 +- release-content/migration-guides/wgpu_25.md | 22 ++ 74 files changed, 719 insertions(+), 446 deletions(-) create mode 100644 release-content/migration-guides/wgpu_25.md diff --git a/assets/shaders/array_texture.wgsl b/assets/shaders/array_texture.wgsl index d49d492a06..293f331665 100644 --- a/assets/shaders/array_texture.wgsl +++ b/assets/shaders/array_texture.wgsl @@ -7,8 +7,8 @@ } #import bevy_core_pipeline::tonemapping::tone_mapping -@group(2) @binding(0) var my_array_texture: texture_2d_array; -@group(2) @binding(1) var my_array_texture_sampler: sampler; +@group(3) @binding(0) var my_array_texture: texture_2d_array; +@group(3) @binding(1) var my_array_texture_sampler: sampler; @fragment fn fragment( diff --git a/assets/shaders/automatic_instancing.wgsl b/assets/shaders/automatic_instancing.wgsl index 35276246b0..ca2195df0d 100644 --- a/assets/shaders/automatic_instancing.wgsl +++ b/assets/shaders/automatic_instancing.wgsl @@ -3,8 +3,8 @@ view_transformations::position_world_to_clip } -@group(2) @binding(0) var texture: texture_2d; -@group(2) @binding(1) var texture_sampler: sampler; +@group(3) @binding(0) var texture: texture_2d; +@group(3) @binding(1) var texture_sampler: sampler; struct Vertex { @builtin(instance_index) instance_index: u32, diff --git a/assets/shaders/bindless_material.wgsl b/assets/shaders/bindless_material.wgsl index 3de313b81a..f7fa795d94 100644 --- a/assets/shaders/bindless_material.wgsl +++ b/assets/shaders/bindless_material.wgsl @@ -15,12 +15,12 @@ struct MaterialBindings { } #ifdef BINDLESS -@group(2) @binding(0) var materials: array; -@group(2) @binding(10) var material_color: binding_array; +@group(3) @binding(0) var materials: array; +@group(3) @binding(10) var material_color: binding_array; #else // BINDLESS -@group(2) @binding(0) var material_color: Color; -@group(2) @binding(1) var material_color_texture: texture_2d; -@group(2) @binding(2) var material_color_sampler: sampler; +@group(3) @binding(0) var material_color: Color; +@group(3) @binding(1) var material_color_texture: texture_2d; +@group(3) @binding(2) var material_color_sampler: sampler; #endif // BINDLESS @fragment diff --git a/assets/shaders/cubemap_unlit.wgsl b/assets/shaders/cubemap_unlit.wgsl index 14e45a045b..81e153e408 100644 --- a/assets/shaders/cubemap_unlit.wgsl +++ b/assets/shaders/cubemap_unlit.wgsl @@ -1,12 +1,12 @@ #import bevy_pbr::forward_io::VertexOutput #ifdef CUBEMAP_ARRAY -@group(2) @binding(0) var base_color_texture: texture_cube_array; +@group(3) @binding(0) var base_color_texture: texture_cube_array; #else -@group(2) @binding(0) var base_color_texture: texture_cube; +@group(3) @binding(0) var base_color_texture: texture_cube; #endif -@group(2) @binding(1) var base_color_sampler: sampler; +@group(3) @binding(1) var base_color_sampler: sampler; @fragment fn fragment( diff --git a/assets/shaders/custom_material.frag b/assets/shaders/custom_material.frag index a6bc9af0d2..0617a08ae7 100644 --- a/assets/shaders/custom_material.frag +++ b/assets/shaders/custom_material.frag @@ -3,10 +3,10 @@ layout(location = 0) in vec2 v_Uv; 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 = 2, binding = 2) uniform sampler CustomMaterial_sampler; +layout(set = 3, binding = 1) uniform texture2D CustomMaterial_texture; +layout(set = 3, binding = 2) uniform sampler CustomMaterial_sampler; // wgsl modules can be imported and used in glsl // FIXME - this doesn't work any more ... diff --git a/assets/shaders/custom_material.vert b/assets/shaders/custom_material.vert index 86ca3629e2..f9a2813a37 100644 --- a/assets/shaders/custom_material.vert +++ b/assets/shaders/custom_material.vert @@ -25,9 +25,9 @@ struct Mesh { }; #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 -layout(set = 1, binding = 0) readonly buffer _Meshes { +layout(set = 2, binding = 0) readonly buffer _Meshes { Mesh Meshes[]; }; #endif // PER_OBJECT_BUFFER_BATCH_SIZE diff --git a/assets/shaders/custom_material.wesl b/assets/shaders/custom_material.wesl index 5113e1cbe0..ca94668784 100644 --- a/assets/shaders/custom_material.wesl +++ b/assets/shaders/custom_material.wesl @@ -10,7 +10,7 @@ struct CustomMaterial { time: vec4, } -@group(2) @binding(0) var material: CustomMaterial; +@group(3) @binding(0) var material: CustomMaterial; @fragment fn fragment( diff --git a/assets/shaders/custom_material.wgsl b/assets/shaders/custom_material.wgsl index 1b65627d45..7548d2223c 100644 --- a/assets/shaders/custom_material.wgsl +++ b/assets/shaders/custom_material.wgsl @@ -2,9 +2,9 @@ // we can import items from shader modules in the assets folder with a quoted path #import "shaders/custom_material_import.wgsl"::COLOR_MULTIPLIER -@group(2) @binding(0) var material_color: vec4; -@group(2) @binding(1) var material_color_texture: texture_2d; -@group(2) @binding(2) var material_color_sampler: sampler; +@group(3) @binding(0) var material_color: vec4; +@group(3) @binding(1) var material_color_texture: texture_2d; +@group(3) @binding(2) var material_color_sampler: sampler; @fragment fn fragment( diff --git a/assets/shaders/custom_material_screenspace_texture.wgsl b/assets/shaders/custom_material_screenspace_texture.wgsl index 36da2a7f8c..abad3cc15a 100644 --- a/assets/shaders/custom_material_screenspace_texture.wgsl +++ b/assets/shaders/custom_material_screenspace_texture.wgsl @@ -4,8 +4,8 @@ utils::coords_to_viewport_uv, } -@group(2) @binding(0) var texture: texture_2d; -@group(2) @binding(1) var texture_sampler: sampler; +@group(3) @binding(0) var texture: texture_2d; +@group(3) @binding(1) var texture_sampler: sampler; @fragment fn fragment( diff --git a/assets/shaders/custom_vertex_attribute.wgsl b/assets/shaders/custom_vertex_attribute.wgsl index f8062ab77b..6b7b93e4c7 100644 --- a/assets/shaders/custom_vertex_attribute.wgsl +++ b/assets/shaders/custom_vertex_attribute.wgsl @@ -3,7 +3,7 @@ struct CustomMaterial { color: vec4, }; -@group(2) @binding(0) var material: CustomMaterial; +@group(3) @binding(0) var material: CustomMaterial; struct Vertex { @builtin(instance_index) instance_index: u32, diff --git a/assets/shaders/extended_material.wgsl b/assets/shaders/extended_material.wgsl index fc69f30bb5..7bad24a331 100644 --- a/assets/shaders/extended_material.wgsl +++ b/assets/shaders/extended_material.wgsl @@ -19,7 +19,7 @@ struct MyExtendedMaterial { quantize_steps: u32, } -@group(2) @binding(100) +@group(3) @binding(100) var my_extended_material: MyExtendedMaterial; @fragment diff --git a/assets/shaders/extended_material_bindless.wgsl b/assets/shaders/extended_material_bindless.wgsl index f8650b0da7..c9cb07e0c7 100644 --- a/assets/shaders/extended_material_bindless.wgsl +++ b/assets/shaders/extended_material_bindless.wgsl @@ -42,19 +42,19 @@ struct ExampleBindlessExtendedMaterial { // The indices of the bindless resources in the bindless resource arrays, for // the `ExampleBindlessExtension` fields. -@group(2) @binding(100) var example_extended_material_indices: +@group(3) @binding(100) var example_extended_material_indices: array; // An array that holds the `ExampleBindlessExtendedMaterial` plain old data, // indexed by `ExampleBindlessExtendedMaterialIndices.material`. -@group(2) @binding(101) var example_extended_material: +@group(3) @binding(101) var example_extended_material: array; #else // BINDLESS // In non-bindless mode, we simply use a uniform for the plain old data. -@group(2) @binding(50) var example_extended_material: ExampleBindlessExtendedMaterial; -@group(2) @binding(51) var modulate_texture: texture_2d; -@group(2) @binding(52) var modulate_sampler: sampler; +@group(3) @binding(50) var example_extended_material: ExampleBindlessExtendedMaterial; +@group(3) @binding(51) var modulate_texture: texture_2d; +@group(3) @binding(52) var modulate_sampler: sampler; #endif // BINDLESS diff --git a/assets/shaders/fallback_image_test.wgsl b/assets/shaders/fallback_image_test.wgsl index c48f091bcc..c92cbd1577 100644 --- a/assets/shaders/fallback_image_test.wgsl +++ b/assets/shaders/fallback_image_test.wgsl @@ -1,22 +1,22 @@ #import bevy_pbr::forward_io::VertexOutput -@group(2) @binding(0) var test_texture_1d: texture_1d; -@group(2) @binding(1) var test_texture_1d_sampler: sampler; +@group(3) @binding(0) var test_texture_1d: texture_1d; +@group(3) @binding(1) var test_texture_1d_sampler: sampler; -@group(2) @binding(2) var test_texture_2d: texture_2d; -@group(2) @binding(3) var test_texture_2d_sampler: sampler; +@group(3) @binding(2) var test_texture_2d: texture_2d; +@group(3) @binding(3) var test_texture_2d_sampler: sampler; -@group(2) @binding(4) var test_texture_2d_array: texture_2d_array; -@group(2) @binding(5) var test_texture_2d_array_sampler: sampler; +@group(3) @binding(4) var test_texture_2d_array: texture_2d_array; +@group(3) @binding(5) var test_texture_2d_array_sampler: sampler; -@group(2) @binding(6) var test_texture_cube: texture_cube; -@group(2) @binding(7) var test_texture_cube_sampler: sampler; +@group(3) @binding(6) var test_texture_cube: texture_cube; +@group(3) @binding(7) var test_texture_cube_sampler: sampler; -@group(2) @binding(8) var test_texture_cube_array: texture_cube_array; -@group(2) @binding(9) var test_texture_cube_array_sampler: sampler; +@group(3) @binding(8) var test_texture_cube_array: texture_cube_array; +@group(3) @binding(9) var test_texture_cube_array_sampler: sampler; -@group(2) @binding(10) var test_texture_3d: texture_3d; -@group(2) @binding(11) var test_texture_3d_sampler: sampler; +@group(3) @binding(10) var test_texture_3d: texture_3d; +@group(3) @binding(11) var test_texture_3d_sampler: sampler; @fragment fn fragment(in: VertexOutput) {} diff --git a/assets/shaders/irradiance_volume_voxel_visualization.wgsl b/assets/shaders/irradiance_volume_voxel_visualization.wgsl index f34e6f8453..0e12110f3b 100644 --- a/assets/shaders/irradiance_volume_voxel_visualization.wgsl +++ b/assets/shaders/irradiance_volume_voxel_visualization.wgsl @@ -12,7 +12,7 @@ struct VoxelVisualizationIrradianceVolumeInfo { intensity: f32, } -@group(2) @binding(100) +@group(3) @binding(100) var irradiance_volume_info: VoxelVisualizationIrradianceVolumeInfo; @fragment diff --git a/assets/shaders/line_material.wgsl b/assets/shaders/line_material.wgsl index cc7ca047d5..639762a444 100644 --- a/assets/shaders/line_material.wgsl +++ b/assets/shaders/line_material.wgsl @@ -4,7 +4,7 @@ struct LineMaterial { color: vec4, }; -@group(2) @binding(0) var material: LineMaterial; +@group(3) @binding(0) var material: LineMaterial; @fragment fn fragment( diff --git a/assets/shaders/shader_defs.wgsl b/assets/shaders/shader_defs.wgsl index fdddc4caa1..776fc9ffe6 100644 --- a/assets/shaders/shader_defs.wgsl +++ b/assets/shaders/shader_defs.wgsl @@ -4,7 +4,7 @@ struct CustomMaterial { color: vec4, }; -@group(2) @binding(0) var material: CustomMaterial; +@group(3) @binding(0) var material: CustomMaterial; @fragment fn fragment( diff --git a/assets/shaders/show_prepass.wgsl b/assets/shaders/show_prepass.wgsl index c1b3a89750..b1a53677ac 100644 --- a/assets/shaders/show_prepass.wgsl +++ b/assets/shaders/show_prepass.wgsl @@ -11,7 +11,7 @@ struct ShowPrepassSettings { padding_1: u32, padding_2: u32, } -@group(2) @binding(0) var settings: ShowPrepassSettings; +@group(3) @binding(0) var settings: ShowPrepassSettings; @fragment fn fragment( diff --git a/assets/shaders/storage_buffer.wgsl b/assets/shaders/storage_buffer.wgsl index c27053b9a2..f447333cc9 100644 --- a/assets/shaders/storage_buffer.wgsl +++ b/assets/shaders/storage_buffer.wgsl @@ -3,7 +3,7 @@ view_transformations::position_world_to_clip } -@group(2) @binding(0) var colors: array, 5>; +@group(3) @binding(0) var colors: array, 5>; struct Vertex { @builtin(instance_index) instance_index: u32, diff --git a/assets/shaders/texture_binding_array.wgsl b/assets/shaders/texture_binding_array.wgsl index de7a4e1b96..17b94a74d3 100644 --- a/assets/shaders/texture_binding_array.wgsl +++ b/assets/shaders/texture_binding_array.wgsl @@ -1,7 +1,7 @@ #import bevy_pbr::forward_io::VertexOutput -@group(2) @binding(0) var textures: binding_array>; -@group(2) @binding(1) var nearest_sampler: sampler; +@group(3) @binding(0) var textures: binding_array>; +@group(3) @binding(1) var nearest_sampler: sampler; // We can also have array of samplers // var samplers: binding_array; diff --git a/assets/shaders/water_material.wgsl b/assets/shaders/water_material.wgsl index 31d04b5f11..a8a9e03df4 100644 --- a/assets/shaders/water_material.wgsl +++ b/assets/shaders/water_material.wgsl @@ -23,9 +23,9 @@ struct WaterSettings { @group(0) @binding(1) var globals: Globals; -@group(2) @binding(100) var water_normals_texture: texture_2d; -@group(2) @binding(101) var water_normals_sampler: sampler; -@group(2) @binding(102) var water_settings: WaterSettings; +@group(3) @binding(100) var water_normals_texture: texture_2d; +@group(3) @binding(101) var water_normals_sampler: sampler; +@group(3) @binding(102) var water_settings: WaterSettings; // Samples a single octave of noise and returns the resulting normal. fn sample_noise_octave(uv: vec2, strength: f32) -> vec3 { diff --git a/crates/bevy_color/Cargo.toml b/crates/bevy_color/Cargo.toml index 99ac8a70da..22ade12709 100644 --- a/crates/bevy_color/Cargo.toml +++ b/crates/bevy_color/Cargo.toml @@ -20,7 +20,7 @@ serde = { version = "1.0", features = [ ], default-features = false, optional = true } thiserror = { version = "2", default-features = false } 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 } [features] diff --git a/crates/bevy_gizmos/src/pipeline_3d.rs b/crates/bevy_gizmos/src/pipeline_3d.rs index 1cc70c67cb..69854f7de4 100644 --- a/crates/bevy_gizmos/src/pipeline_3d.rs +++ b/crates/bevy_gizmos/src/pipeline_3d.rs @@ -123,8 +123,7 @@ impl SpecializedRenderPipeline for LineGizmoPipeline { .mesh_pipeline .get_view_layout(key.view_key.into()) .clone(); - - let layout = vec![view_layout, self.uniform_layout.clone()]; + let layout = vec![view_layout.main_layout.clone(), self.uniform_layout.clone()]; let fragment_entry_point = match key.line_style { GizmoLineStyle::Solid => "fragment_solid", @@ -220,8 +219,7 @@ impl SpecializedRenderPipeline for LineJointGizmoPipeline { .mesh_pipeline .get_view_layout(key.view_key.into()) .clone(); - - let layout = vec![view_layout, self.uniform_layout.clone()]; + let layout = vec![view_layout.main_layout.clone(), self.uniform_layout.clone()]; 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."); diff --git a/crates/bevy_image/Cargo.toml b/crates/bevy_image/Cargo.toml index 54559d60d3..1ed4f29ca6 100644 --- a/crates/bevy_image/Cargo.toml +++ b/crates/bevy_image/Cargo.toml @@ -63,7 +63,7 @@ image = { version = "0.25.2", default-features = false } # misc bitflags = { version = "2.3", features = ["serde"] } bytemuck = { version = "1.5" } -wgpu-types = { version = "24", default-features = false } +wgpu-types = { version = "25", default-features = false } serde = { version = "1", features = ["derive"] } thiserror = { version = "2", default-features = false } futures-lite = "2.0.1" diff --git a/crates/bevy_mesh/Cargo.toml b/crates/bevy_mesh/Cargo.toml index 0b77bdb619..7807acbb9d 100644 --- a/crates/bevy_mesh/Cargo.toml +++ b/crates/bevy_mesh/Cargo.toml @@ -27,7 +27,7 @@ bevy_platform = { path = "../bevy_platform", version = "0.17.0-dev", default-fea # other bitflags = { version = "2.3", features = ["serde"] } 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 = [ "derive", ], optional = true } diff --git a/crates/bevy_pbr/src/atmosphere/render_sky.wgsl b/crates/bevy_pbr/src/atmosphere/render_sky.wgsl index e488656df4..f8298272ca 100644 --- a/crates/bevy_pbr/src/atmosphere/render_sky.wgsl +++ b/crates/bevy_pbr/src/atmosphere/render_sky.wgsl @@ -1,3 +1,5 @@ +enable dual_source_blending; + #import bevy_pbr::atmosphere::{ types::{Atmosphere, AtmosphereSettings}, bindings::{atmosphere, view, atmosphere_transforms}, @@ -19,9 +21,11 @@ #endif struct RenderSkyOutput { - @location(0) inscattering: vec4, #ifdef DUAL_SOURCE_BLENDING - @location(0) @second_blend_source transmittance: vec4, + @location(0) @blend_src(0) inscattering: vec4, + @location(0) @blend_src(1) transmittance: vec4, +#else + @location(0) inscattering: vec4, #endif } diff --git a/crates/bevy_pbr/src/decal/forward_decal.wgsl b/crates/bevy_pbr/src/decal/forward_decal.wgsl index ce24d57bf5..f0414bc807 100644 --- a/crates/bevy_pbr/src/decal/forward_decal.wgsl +++ b/crates/bevy_pbr/src/decal/forward_decal.wgsl @@ -10,7 +10,7 @@ } #import bevy_render::maths::project_onto -@group(2) @binding(200) +@group(3) @binding(200) var inv_depth_fade_factor: f32; struct ForwardDecalInformation { diff --git a/crates/bevy_pbr/src/deferred/deferred_lighting.wgsl b/crates/bevy_pbr/src/deferred/deferred_lighting.wgsl index 843ed2bbf6..7c14eea4ba 100644 --- a/crates/bevy_pbr/src/deferred/deferred_lighting.wgsl +++ b/crates/bevy_pbr/src/deferred/deferred_lighting.wgsl @@ -29,7 +29,7 @@ struct PbrDeferredLightingDepthId { _webgl2_padding_2: f32, #endif } -@group(1) @binding(0) +@group(2) @binding(0) var depth_id: PbrDeferredLightingDepthId; @vertex diff --git a/crates/bevy_pbr/src/deferred/mod.rs b/crates/bevy_pbr/src/deferred/mod.rs index 28edd38c52..b8f3a660c0 100644 --- a/crates/bevy_pbr/src/deferred/mod.rs +++ b/crates/bevy_pbr/src/deferred/mod.rs @@ -184,9 +184,9 @@ impl ViewNode for DeferredOpaquePass3dPbrLightingNode { return Ok(()); }; - let bind_group_1 = render_context.render_device().create_bind_group( - "deferred_lighting_layout_group_1", - &deferred_lighting_layout.bind_group_layout_1, + let bind_group_2 = render_context.render_device().create_bind_group( + "deferred_lighting_layout_group_2", + &deferred_lighting_layout.bind_group_layout_2, &BindGroupEntries::single(deferred_lighting_pass_id_binding), ); @@ -208,7 +208,7 @@ impl ViewNode for DeferredOpaquePass3dPbrLightingNode { render_pass.set_render_pipeline(pipeline); render_pass.set_bind_group( 0, - &mesh_view_bind_group.value, + &mesh_view_bind_group.main, &[ view_uniform_offset.offset, view_lights_offset.offset, @@ -218,7 +218,8 @@ impl ViewNode for DeferredOpaquePass3dPbrLightingNode { **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); Ok(()) @@ -228,7 +229,7 @@ impl ViewNode for DeferredOpaquePass3dPbrLightingNode { #[derive(Resource)] pub struct DeferredLightingLayout { mesh_pipeline: MeshPipeline, - bind_group_layout_1: BindGroupLayout, + bind_group_layout_2: BindGroupLayout, deferred_lighting_shader: Handle, } @@ -346,11 +347,13 @@ impl SpecializedRenderPipeline for DeferredLightingLayout { #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] shader_defs.push("SIXTEEN_BYTE_ALIGNMENT".into()); + let layout = self.mesh_pipeline.get_view_layout(key.into()); RenderPipelineDescriptor { label: Some("deferred_lighting_pipeline".into()), layout: vec![ - self.mesh_pipeline.get_view_layout(key.into()).clone(), - self.bind_group_layout_1.clone(), + layout.main_layout.clone(), + layout.binding_array_layout.clone(), + self.bind_group_layout_2.clone(), ], vertex: VertexState { shader: self.deferred_lighting_shader.clone(), @@ -408,7 +411,7 @@ impl FromWorld for DeferredLightingLayout { ); Self { mesh_pipeline: world.resource::().clone(), - bind_group_layout_1: layout, + bind_group_layout_2: layout, deferred_lighting_shader: load_embedded_asset!(world, "deferred_lighting.wgsl"), } } diff --git a/crates/bevy_pbr/src/lib.rs b/crates/bevy_pbr/src/lib.rs index 945bc9c55b..517ef0eb5c 100644 --- a/crates/bevy_pbr/src/lib.rs +++ b/crates/bevy_pbr/src/lib.rs @@ -152,8 +152,8 @@ fn shader_ref(path: PathBuf) -> ShaderRef { const MESHLET_VISIBILITY_BUFFER_RESOLVE_SHADER_HANDLE: Handle = weak_handle!("69187376-3dea-4d0f-b3f5-185bde63d6a2"); -pub const TONEMAPPING_LUT_TEXTURE_BINDING_INDEX: u32 = 26; -pub const TONEMAPPING_LUT_SAMPLER_BINDING_INDEX: u32 = 27; +pub const TONEMAPPING_LUT_TEXTURE_BINDING_INDEX: u32 = 18; +pub const TONEMAPPING_LUT_SAMPLER_BINDING_INDEX: u32 = 19; /// Sets up the entire PBR infrastructure of bevy. pub struct PbrPlugin { diff --git a/crates/bevy_pbr/src/lightmap/lightmap.wgsl b/crates/bevy_pbr/src/lightmap/lightmap.wgsl index da10ece9b1..4ba6f51bc9 100644 --- a/crates/bevy_pbr/src/lightmap/lightmap.wgsl +++ b/crates/bevy_pbr/src/lightmap/lightmap.wgsl @@ -3,11 +3,11 @@ #import bevy_pbr::mesh_bindings::mesh #ifdef MULTIPLE_LIGHTMAPS_IN_ARRAY -@group(1) @binding(4) var lightmaps_textures: binding_array, 4>; -@group(1) @binding(5) var lightmaps_samplers: binding_array; +@group(2) @binding(4) var lightmaps_textures: binding_array, 4>; +@group(2) @binding(5) var lightmaps_samplers: binding_array; #else // MULTIPLE_LIGHTMAPS_IN_ARRAY -@group(1) @binding(4) var lightmaps_texture: texture_2d; -@group(1) @binding(5) var lightmaps_sampler: sampler; +@group(2) @binding(4) var lightmaps_texture: texture_2d; +@group(2) @binding(5) var lightmaps_sampler: sampler; #endif // MULTIPLE_LIGHTMAPS_IN_ARRAY // Samples the lightmap, if any, and returns indirect illumination from it. diff --git a/crates/bevy_pbr/src/material.rs b/crates/bevy_pbr/src/material.rs index d17599c106..f73f1392bb 100644 --- a/crates/bevy_pbr/src/material.rs +++ b/crates/bevy_pbr/src/material.rs @@ -120,9 +120,9 @@ use tracing::error; /// In WGSL shaders, the material's binding would look like this: /// /// ```wgsl -/// @group(2) @binding(0) var color: vec4; -/// @group(2) @binding(1) var color_texture: texture_2d; -/// @group(2) @binding(2) var color_sampler: sampler; +/// @group(3) @binding(0) var color: vec4; +/// @group(3) @binding(1) var color_texture: texture_2d; +/// @group(3) @binding(2) var color_sampler: sampler; /// ``` pub trait Material: Asset + AsBindGroup + Clone + Sized { /// Returns this material's vertex shader. If [`ShaderRef::Default`] is returned, the default mesh vertex shader @@ -501,7 +501,7 @@ where 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)?; @@ -544,8 +544,9 @@ impl FromWorld for MaterialPipeline { type DrawMaterial = ( SetItemPipeline, SetMeshViewBindGroup<0>, - SetMeshBindGroup<1>, - SetMaterialBindGroup, + SetMeshViewBindingArrayBindGroup<1>, + SetMeshBindGroup<2>, + SetMaterialBindGroup, DrawMesh, ); diff --git a/crates/bevy_pbr/src/meshlet/material_pipeline_prepare.rs b/crates/bevy_pbr/src/meshlet/material_pipeline_prepare.rs index 57762bfc8a..90d35d0514 100644 --- a/crates/bevy_pbr/src/meshlet/material_pipeline_prepare.rs +++ b/crates/bevy_pbr/src/meshlet/material_pipeline_prepare.rs @@ -186,13 +186,17 @@ pub fn prepare_material_meshlet_meshes_main_opaque_pass( let mut shader_defs = material_fragment.shader_defs; 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 { label: material_pipeline_descriptor.label, - layout: vec![ - mesh_pipeline.get_view_layout(view_key.into()).clone(), - resource_manager.material_shade_bind_group_layout.clone(), - material_pipeline.material_layout.clone(), - ], + layout, push_constant_ranges: vec![], vertex: VertexState { shader: MESHLET_MESH_MATERIAL_SHADER_HANDLE, diff --git a/crates/bevy_pbr/src/meshlet/material_shade_nodes.rs b/crates/bevy_pbr/src/meshlet/material_shade_nodes.rs index 9c2d432d88..cb05de38fb 100644 --- a/crates/bevy_pbr/src/meshlet/material_shade_nodes.rs +++ b/crates/bevy_pbr/src/meshlet/material_shade_nodes.rs @@ -106,7 +106,7 @@ impl ViewNode for MeshletMainOpaquePass3dNode { render_pass.set_bind_group( 0, - &mesh_view_bind_group.value, + &mesh_view_bind_group.main, &[ view_uniform_offset.offset, view_lights_offset.offset, diff --git a/crates/bevy_pbr/src/meshlet/meshlet_bindings.wgsl b/crates/bevy_pbr/src/meshlet/meshlet_bindings.wgsl index e179e78b7a..63e92f15e6 100644 --- a/crates/bevy_pbr/src/meshlet/meshlet_bindings.wgsl +++ b/crates/bevy_pbr/src/meshlet/meshlet_bindings.wgsl @@ -149,15 +149,15 @@ fn get_meshlet_vertex_position(meshlet: ptr, vertex_id: u32) #endif #ifdef MESHLET_MESH_MATERIAL_PASS -@group(1) @binding(0) var meshlet_visibility_buffer: texture_storage_2d; -@group(1) @binding(1) var meshlet_cluster_meshlet_ids: array; // Per cluster -@group(1) @binding(2) var meshlets: array; // Per meshlet -@group(1) @binding(3) var meshlet_indices: array; // Many per meshlet -@group(1) @binding(4) var meshlet_vertex_positions: array; // Many per meshlet -@group(1) @binding(5) var meshlet_vertex_normals: array; // Many per meshlet -@group(1) @binding(6) var meshlet_vertex_uvs: array>; // Many per meshlet -@group(1) @binding(7) var meshlet_cluster_instance_ids: array; // Per cluster -@group(1) @binding(8) var meshlet_instance_uniforms: array; // Per entity instance +@group(2) @binding(0) var meshlet_visibility_buffer: texture_storage_2d; +@group(2) @binding(1) var meshlet_cluster_meshlet_ids: array; // Per cluster +@group(2) @binding(2) var meshlets: array; // Per meshlet +@group(2) @binding(3) var meshlet_indices: array; // Many per meshlet +@group(2) @binding(4) var meshlet_vertex_positions: array; // Many per meshlet +@group(2) @binding(5) var meshlet_vertex_normals: array; // Many per meshlet +@group(2) @binding(6) var meshlet_vertex_uvs: array>; // Many per meshlet +@group(2) @binding(7) var meshlet_cluster_instance_ids: array; // Per cluster +@group(2) @binding(8) var meshlet_instance_uniforms: array; // Per entity instance // 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 { diff --git a/crates/bevy_pbr/src/meshlet/resource_manager.rs b/crates/bevy_pbr/src/meshlet/resource_manager.rs index 9b45d7676a..d33752d426 100644 --- a/crates/bevy_pbr/src/meshlet/resource_manager.rs +++ b/crates/bevy_pbr/src/meshlet/resource_manager.rs @@ -206,7 +206,7 @@ impl ResourceManager { visibility_buffer_raster_bind_group_layout: render_device.create_bind_group_layout( "meshlet_visibility_buffer_raster_bind_group_layout", &BindGroupLayoutEntries::sequential( - ShaderStages::all(), + ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE, ( storage_buffer_read_only_sized(false, None), storage_buffer_read_only_sized(false, None), @@ -225,7 +225,7 @@ impl ResourceManager { .create_bind_group_layout( "meshlet_visibility_buffer_raster_shadow_view_bind_group_layout", &BindGroupLayoutEntries::sequential( - ShaderStages::all(), + ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE, ( storage_buffer_read_only_sized(false, None), storage_buffer_read_only_sized(false, None), diff --git a/crates/bevy_pbr/src/prepass/mod.rs b/crates/bevy_pbr/src/prepass/mod.rs index aef2b74177..8732f92e82 100644 --- a/crates/bevy_pbr/src/prepass/mod.rs +++ b/crates/bevy_pbr/src/prepass/mod.rs @@ -95,7 +95,6 @@ where Render, prepare_prepass_view_bind_group::.in_set(RenderSystems::PrepareBindGroups), ) - .init_resource::() .init_resource::>>() .allow_ambiguous_resource::>>(); } @@ -105,7 +104,9 @@ where return; }; - render_app.init_resource::>(); + render_app + .init_resource::>() + .init_resource::(); } } @@ -272,6 +273,7 @@ pub struct PrepassPipelineInternal { pub view_layout_motion_vectors: BindGroupLayout, pub view_layout_no_motion_vectors: BindGroupLayout, pub mesh_layouts: MeshLayouts, + pub empty_layout: BindGroupLayout, pub material_layout: BindGroupLayout, pub prepass_material_vertex_shader: Option>, pub prepass_material_fragment_shader: Option>, @@ -381,6 +383,7 @@ impl FromWorld for PrepassPipeline { skins_use_uniform_buffers: skin::skins_use_uniform_buffers(render_device), depth_clip_control_supported, binding_arrays_are_usable: binding_arrays_are_usable(render_device, render_adapter), + empty_layout: render_device.create_bind_group_layout("prepass_empty_layout", &[]), }; PrepassPipeline { internal, @@ -425,13 +428,14 @@ impl PrepassPipelineInternal { layout: &MeshVertexBufferLayoutRef, ) -> Result { let mut shader_defs = shader_defs; - let mut bind_group_layouts = vec![if mesh_key - .contains(MeshPipelineKey::MOTION_VECTOR_PREPASS) - { - self.view_layout_motion_vectors.clone() - } else { - self.view_layout_no_motion_vectors.clone() - }]; + let mut bind_group_layouts = vec![ + if mesh_key.contains(MeshPipelineKey::MOTION_VECTOR_PREPASS) { + self.view_layout_motion_vectors.clone() + } else { + self.view_layout_no_motion_vectors.clone() + }, + self.empty_layout.clone(), + ]; let mut vertex_attributes = Vec::new(); // Let the shader code know that it's running in a prepass pipeline. @@ -556,7 +560,7 @@ impl PrepassPipelineInternal { &mut vertex_attributes, 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)?; // Setup prepass fragment targets - normals in slot 0 (or None if not needed), motion vectors in slot 1 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 motion_vectors: Option, pub no_motion_vectors: Option, + pub empty_bind_group: BindGroup, +} + +impl FromWorld for PrepassViewBindGroup { + fn from_world(world: &mut World) -> Self { + let pipeline = world.resource::>(); + + let render_device = world.resource::(); + 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( @@ -1284,7 +1307,26 @@ impl RenderCommand

for SetPrepassViewBindGroup< ); } } + RenderCommandResult::Success + } +} +pub struct SetPrepassViewEmptyBindGroup; +impl RenderCommand

for SetPrepassViewEmptyBindGroup { + type Param = SRes; + 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 } } @@ -1292,7 +1334,8 @@ impl RenderCommand

for SetPrepassViewBindGroup< pub type DrawPrepass = ( SetItemPipeline, SetPrepassViewBindGroup<0>, - SetMeshBindGroup<1>, - SetMaterialBindGroup, + SetPrepassViewEmptyBindGroup<1>, + SetMeshBindGroup<2>, + SetMaterialBindGroup, DrawMesh, ); diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index d8a3256b13..50d7e98a48 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -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) } } @@ -2320,7 +2323,11 @@ impl SpecializedMeshPipeline for MeshPipeline { 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 { shader_defs.push("MULTISAMPLED".into()); @@ -2890,7 +2897,47 @@ impl RenderCommand

for SetMeshViewBindGroup if let Some(layers_count_offset) = maybe_oit_layers_count_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; +impl RenderCommand

for SetMeshViewBindingArrayBindGroup { + type Param = (); + type ViewQuery = (Read,); + 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; +impl RenderCommand

for SetMeshViewEmptyBindGroup { + type Param = (); + type ViewQuery = (Read,); + 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 } diff --git a/crates/bevy_pbr/src/render/mesh_bindings.wgsl b/crates/bevy_pbr/src/render/mesh_bindings.wgsl index 62b967c56f..6e78dc4337 100644 --- a/crates/bevy_pbr/src/render/mesh_bindings.wgsl +++ b/crates/bevy_pbr/src/render/mesh_bindings.wgsl @@ -4,8 +4,8 @@ #ifndef MESHLET_MESH_MATERIAL_PASS #ifdef PER_OBJECT_BUFFER_BATCH_SIZE -@group(1) @binding(0) var mesh: array; +@group(2) @binding(0) var mesh: array; #else -@group(1) @binding(0) var mesh: array; +@group(2) @binding(0) var mesh: array; #endif // PER_OBJECT_BUFFER_BATCH_SIZE #endif // MESHLET_MESH_MATERIAL_PASS diff --git a/crates/bevy_pbr/src/render/mesh_view_bindings.rs b/crates/bevy_pbr/src/render/mesh_view_bindings.rs index b995224b6b..03165f1bc5 100644 --- a/crates/bevy_pbr/src/render/mesh_view_bindings.rs +++ b/crates/bevy_pbr/src/render/mesh_view_bindings.rs @@ -59,7 +59,9 @@ use {crate::MESH_PIPELINE_VIEW_LAYOUT_SAFE_MAX_TEXTURES, bevy_utils::once, traci #[derive(Clone)] pub struct MeshPipelineViewLayout { - pub bind_group_layout: BindGroupLayout, + pub main_layout: BindGroupLayout, + pub binding_array_layout: BindGroupLayout, + pub empty_layout: BindGroupLayout, #[cfg(debug_assertions)] pub texture_count: usize, @@ -213,7 +215,11 @@ fn layout_entries( layout_key: MeshPipelineViewLayoutKey, render_device: &RenderDevice, render_adapter: &RenderAdapter, -) -> Vec { +) -> [Vec; 2] { + // EnvironmentMapLight + let environment_map_entries = + environment_map::get_bind_group_layout_entries(render_device, render_adapter); + let mut entries = DynamicBindGroupLayoutEntries::new_with_indices( ShaderStages::FRAGMENT, ( @@ -325,45 +331,15 @@ fn layout_entries( 16, 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 let tonemapping_lut_entries = get_lut_bind_group_layout_entries(); entries = entries.extend_with_indices(( - (26, tonemapping_lut_entries[0]), - (27, tonemapping_lut_entries[1]), + (18, tonemapping_lut_entries[0]), + (19, tonemapping_lut_entries[1]), )); // Prepass @@ -373,7 +349,7 @@ fn layout_entries( { for (entry, binding) in prepass::get_bind_group_layout_entries(layout_key) .iter() - .zip([28, 29, 30, 31]) + .zip([20, 21, 22, 23]) { if let Some(entry) = entry { entries = entries.extend_with_indices(((binding as u32, *entry),)); @@ -384,10 +360,10 @@ fn layout_entries( // View Transmission Texture entries = entries.extend_with_indices(( ( - 32, + 24, texture_2d(TextureSampleType::Float { filterable: true }), ), - (33, sampler(SamplerBindingType::Filtering)), + (25, sampler(SamplerBindingType::Filtering)), )); // OIT @@ -398,19 +374,47 @@ fn layout_entries( if is_oit_supported(render_adapter, render_device, false) { entries = entries.extend_with_indices(( // oit_layers - (34, storage_buffer_sized(false, None)), + (26, storage_buffer_sized(false, None)), // oit_layer_ids, - (35, storage_buffer_sized(false, None)), + (27, storage_buffer_sized(false, None)), // oit_layer_count ( - 36, + 28, uniform_buffer::(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. @@ -447,12 +451,21 @@ impl FromWorld for MeshPipelineViewLayouts { #[cfg(debug_assertions)] let texture_count: usize = entries .iter() - .filter(|entry| matches!(entry.ty, BindingType::Texture { .. })) + .flat_map(|e| { + e.iter() + .filter(|entry| matches!(entry.ty, BindingType::Texture { .. })) + }) .count(); MeshPipelineViewLayout { - bind_group_layout: render_device - .create_bind_group_layout(key.label().as_str(), &entries), + main_layout: render_device + .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)] texture_count, } @@ -461,7 +474,10 @@ impl FromWorld for 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 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.")); } - &layout.bind_group_layout + layout } } @@ -496,12 +512,20 @@ pub fn generate_view_layouts( #[cfg(debug_assertions)] let texture_count: usize = entries .iter() - .filter(|entry| matches!(entry.ty, BindingType::Texture { .. })) + .flat_map(|e| { + e.iter() + .filter(|entry| matches!(entry.ty, BindingType::Texture { .. })) + }) .count(); MeshPipelineViewLayout { - bind_group_layout: render_device - .create_bind_group_layout(key.label().as_str(), &entries), + main_layout: render_device.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)] texture_count, } @@ -510,7 +534,9 @@ pub fn generate_view_layouts( #[derive(Component)] pub struct MeshViewBindGroup { - pub value: BindGroup, + pub main: BindGroup, + pub binding_array: BindGroup, + pub empty: BindGroup, } pub fn prepare_mesh_view_bind_groups( @@ -597,7 +623,7 @@ pub fn prepare_mesh_view_bind_groups( 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(( (0, view_binding.clone()), @@ -626,6 +652,58 @@ pub fn prepare_mesh_view_bind_groups( (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( render_view_environment_maps, &images, @@ -633,18 +711,16 @@ pub fn prepare_mesh_view_bind_groups( &render_device, &render_adapter, ); - match environment_map_bind_group_entries { RenderViewEnvironmentMapBindGroupEntries::Single { diffuse_texture_view, specular_texture_view, sampler, } => { - entries = entries.extend_with_indices(( - (17, diffuse_texture_view), - (18, specular_texture_view), - (19, sampler), - (20, environment_map_binding.clone()), + entries_binding_array = entries_binding_array.extend_with_indices(( + (0, diffuse_texture_view), + (1, specular_texture_view), + (2, sampler), )); } RenderViewEnvironmentMapBindGroupEntries::Multiple { @@ -652,11 +728,10 @@ pub fn prepare_mesh_view_bind_groups( ref specular_texture_views, sampler, } => { - entries = entries.extend_with_indices(( - (17, diffuse_texture_views.as_slice()), - (18, specular_texture_views.as_slice()), - (19, sampler), - (20, environment_map_binding.clone()), + entries_binding_array = entries_binding_array.extend_with_indices(( + (0, diffuse_texture_views.as_slice()), + (1, specular_texture_views.as_slice()), + (2, sampler), )); } } @@ -678,14 +753,15 @@ pub fn prepare_mesh_view_bind_groups( texture_view, 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 { ref texture_views, sampler, }) => { - entries = entries - .extend_with_indices(((21, texture_views.as_slice()), (22, sampler))); + entries_binding_array = entries_binding_array + .extend_with_indices(((3, texture_views.as_slice()), (4, sampler))); } None => {} } @@ -701,76 +777,42 @@ pub fn prepare_mesh_view_bind_groups( // Add the 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` ( - 23, + 5, render_view_decal_bind_group_entries .decals .as_entire_binding(), ), // `clustered_decal_textures` ( - 24, + 6, render_view_decal_bind_group_entries .texture_views .as_slice(), ), // `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 { - 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, + &[], + ), }); } } diff --git a/crates/bevy_pbr/src/render/mesh_view_bindings.wgsl b/crates/bevy_pbr/src/render/mesh_view_bindings.wgsl index 2fb34d8466..0f650e6e54 100644 --- a/crates/bevy_pbr/src/render/mesh_view_bindings.wgsl +++ b/crates/bevy_pbr/src/render/mesh_view_bindings.wgsl @@ -50,70 +50,70 @@ const VISIBILITY_RANGE_UNIFORM_BUFFER_SIZE: u32 = 64u; @group(0) @binding(15) var ssr_settings: types::ScreenSpaceReflectionsSettings; @group(0) @binding(16) var screen_space_ambient_occlusion_texture: texture_2d; - -#ifdef MULTIPLE_LIGHT_PROBES_IN_ARRAY -@group(0) @binding(17) var diffuse_environment_maps: binding_array, 8u>; -@group(0) @binding(18) var specular_environment_maps: binding_array, 8u>; -#else -@group(0) @binding(17) var diffuse_environment_map: texture_cube; -@group(0) @binding(18) var specular_environment_map: texture_cube; -#endif -@group(0) @binding(19) var environment_map_sampler: sampler; -@group(0) @binding(20) var 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, 8u>; -#else -@group(0) @binding(21) var irradiance_volume: texture_3d; -#endif -@group(0) @binding(22) var irradiance_volume_sampler: sampler; -#endif - -#ifdef CLUSTERED_DECALS_ARE_USABLE -@group(0) @binding(23) var clustered_decals: types::ClusteredDecals; -@group(0) @binding(24) var clustered_decal_textures: binding_array, 8u>; -@group(0) @binding(25) var clustered_decal_sampler: sampler; -#endif // CLUSTERED_DECALS_ARE_USABLE +@group(0) @binding(17) var environment_map_uniform: types::EnvironmentMapUniform; // NB: If you change these, make sure to update `tonemapping_shared.wgsl` too. -@group(0) @binding(26) var dt_lut_texture: texture_3d; -@group(0) @binding(27) var dt_lut_sampler: sampler; +@group(0) @binding(18) var dt_lut_texture: texture_3d; +@group(0) @binding(19) var dt_lut_sampler: sampler; #ifdef MULTISAMPLED #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 #ifdef NORMAL_PREPASS -@group(0) @binding(29) var normal_prepass_texture: texture_multisampled_2d; +@group(0) @binding(21) var normal_prepass_texture: texture_multisampled_2d; #endif // NORMAL_PREPASS #ifdef MOTION_VECTOR_PREPASS -@group(0) @binding(30) var motion_vector_prepass_texture: texture_multisampled_2d; +@group(0) @binding(22) var motion_vector_prepass_texture: texture_multisampled_2d; #endif // MOTION_VECTOR_PREPASS #else // MULTISAMPLED #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 #ifdef NORMAL_PREPASS -@group(0) @binding(29) var normal_prepass_texture: texture_2d; +@group(0) @binding(21) var normal_prepass_texture: texture_2d; #endif // NORMAL_PREPASS #ifdef MOTION_VECTOR_PREPASS -@group(0) @binding(30) var motion_vector_prepass_texture: texture_2d; +@group(0) @binding(22) var motion_vector_prepass_texture: texture_2d; #endif // MOTION_VECTOR_PREPASS #endif // MULTISAMPLED #ifdef DEFERRED_PREPASS -@group(0) @binding(31) var deferred_prepass_texture: texture_2d; +@group(0) @binding(23) var deferred_prepass_texture: texture_2d; #endif // DEFERRED_PREPASS -@group(0) @binding(32) var view_transmission_texture: texture_2d; -@group(0) @binding(33) var view_transmission_sampler: sampler; +@group(0) @binding(24) var view_transmission_texture: texture_2d; +@group(0) @binding(25) var view_transmission_sampler: sampler; #ifdef OIT_ENABLED -@group(0) @binding(34) var oit_layers: array>; -@group(0) @binding(35) var oit_layer_ids: array>; -@group(0) @binding(36) var oit_settings: types::OrderIndependentTransparencySettings; +@group(0) @binding(26) var oit_layers: array>; +@group(0) @binding(27) var oit_layer_ids: array>; +@group(0) @binding(28) var oit_settings: types::OrderIndependentTransparencySettings; #endif // OIT_ENABLED + +#ifdef MULTIPLE_LIGHT_PROBES_IN_ARRAY +@group(1) @binding(0) var diffuse_environment_maps: binding_array, 8u>; +@group(1) @binding(1) var specular_environment_maps: binding_array, 8u>; +#else +@group(1) @binding(0) var diffuse_environment_map: texture_cube; +@group(1) @binding(1) var specular_environment_map: texture_cube; +#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, 8u>; +#else +@group(1) @binding(3) var irradiance_volume: texture_3d; +#endif +@group(1) @binding(4) var irradiance_volume_sampler: sampler; +#endif + +#ifdef CLUSTERED_DECALS_ARE_USABLE +@group(1) @binding(5) var clustered_decals: types::ClusteredDecals; +@group(1) @binding(6) var clustered_decal_textures: binding_array, 8u>; +@group(1) @binding(7) var clustered_decal_sampler: sampler; +#endif // CLUSTERED_DECALS_ARE_USABLE diff --git a/crates/bevy_pbr/src/render/morph.wgsl b/crates/bevy_pbr/src/render/morph.wgsl index 939b714c77..6689d68cc6 100644 --- a/crates/bevy_pbr/src/render/morph.wgsl +++ b/crates/bevy_pbr/src/render/morph.wgsl @@ -4,9 +4,9 @@ #import bevy_pbr::mesh_types::MorphWeights; -@group(1) @binding(2) var morph_weights: MorphWeights; -@group(1) @binding(3) var morph_targets: texture_3d; -@group(1) @binding(7) var prev_morph_weights: MorphWeights; +@group(2) @binding(2) var morph_weights: MorphWeights; +@group(2) @binding(3) var morph_targets: texture_3d; +@group(2) @binding(7) var prev_morph_weights: MorphWeights; // NOTE: Those are the "hardcoded" values found in `MorphAttributes` struct // in crates/bevy_render/src/mesh/morph/visitors.rs diff --git a/crates/bevy_pbr/src/render/pbr_bindings.wgsl b/crates/bevy_pbr/src/render/pbr_bindings.wgsl index fac7b97265..373d0bdd54 100644 --- a/crates/bevy_pbr/src/render/pbr_bindings.wgsl +++ b/crates/bevy_pbr/src/render/pbr_bindings.wgsl @@ -37,53 +37,53 @@ struct StandardMaterialBindings { specular_tint_sampler: u32, // 30 } -@group(2) @binding(0) var material_indices: array; -@group(2) @binding(10) var material_array: array; +@group(3) @binding(0) var material_indices: array; +@group(3) @binding(10) var material_array: array; #else // BINDLESS -@group(2) @binding(0) var material: StandardMaterial; -@group(2) @binding(1) var base_color_texture: texture_2d; -@group(2) @binding(2) var base_color_sampler: sampler; -@group(2) @binding(3) var emissive_texture: texture_2d; -@group(2) @binding(4) var emissive_sampler: sampler; -@group(2) @binding(5) var metallic_roughness_texture: texture_2d; -@group(2) @binding(6) var metallic_roughness_sampler: sampler; -@group(2) @binding(7) var occlusion_texture: texture_2d; -@group(2) @binding(8) var occlusion_sampler: sampler; -@group(2) @binding(9) var normal_map_texture: texture_2d; -@group(2) @binding(10) var normal_map_sampler: sampler; -@group(2) @binding(11) var depth_map_texture: texture_2d; -@group(2) @binding(12) var depth_map_sampler: sampler; +@group(3) @binding(0) var material: StandardMaterial; +@group(3) @binding(1) var base_color_texture: texture_2d; +@group(3) @binding(2) var base_color_sampler: sampler; +@group(3) @binding(3) var emissive_texture: texture_2d; +@group(3) @binding(4) var emissive_sampler: sampler; +@group(3) @binding(5) var metallic_roughness_texture: texture_2d; +@group(3) @binding(6) var metallic_roughness_sampler: sampler; +@group(3) @binding(7) var occlusion_texture: texture_2d; +@group(3) @binding(8) var occlusion_sampler: sampler; +@group(3) @binding(9) var normal_map_texture: texture_2d; +@group(3) @binding(10) var normal_map_sampler: sampler; +@group(3) @binding(11) var depth_map_texture: texture_2d; +@group(3) @binding(12) var depth_map_sampler: sampler; #ifdef PBR_ANISOTROPY_TEXTURE_SUPPORTED -@group(2) @binding(13) var anisotropy_texture: texture_2d; -@group(2) @binding(14) var anisotropy_sampler: sampler; +@group(3) @binding(13) var anisotropy_texture: texture_2d; +@group(3) @binding(14) var anisotropy_sampler: sampler; #endif // PBR_ANISOTROPY_TEXTURE_SUPPORTED #ifdef PBR_TRANSMISSION_TEXTURES_SUPPORTED -@group(2) @binding(15) var specular_transmission_texture: texture_2d; -@group(2) @binding(16) var specular_transmission_sampler: sampler; -@group(2) @binding(17) var thickness_texture: texture_2d; -@group(2) @binding(18) var thickness_sampler: sampler; -@group(2) @binding(19) var diffuse_transmission_texture: texture_2d; -@group(2) @binding(20) var diffuse_transmission_sampler: sampler; +@group(3) @binding(15) var specular_transmission_texture: texture_2d; +@group(3) @binding(16) var specular_transmission_sampler: sampler; +@group(3) @binding(17) var thickness_texture: texture_2d; +@group(3) @binding(18) var thickness_sampler: sampler; +@group(3) @binding(19) var diffuse_transmission_texture: texture_2d; +@group(3) @binding(20) var diffuse_transmission_sampler: sampler; #endif // PBR_TRANSMISSION_TEXTURES_SUPPORTED #ifdef PBR_MULTI_LAYER_MATERIAL_TEXTURES_SUPPORTED -@group(2) @binding(21) var clearcoat_texture: texture_2d; -@group(2) @binding(22) var clearcoat_sampler: sampler; -@group(2) @binding(23) var clearcoat_roughness_texture: texture_2d; -@group(2) @binding(24) var clearcoat_roughness_sampler: sampler; -@group(2) @binding(25) var clearcoat_normal_texture: texture_2d; -@group(2) @binding(26) var clearcoat_normal_sampler: sampler; +@group(3) @binding(21) var clearcoat_texture: texture_2d; +@group(3) @binding(22) var clearcoat_sampler: sampler; +@group(3) @binding(23) var clearcoat_roughness_texture: texture_2d; +@group(3) @binding(24) var clearcoat_roughness_sampler: sampler; +@group(3) @binding(25) var clearcoat_normal_texture: texture_2d; +@group(3) @binding(26) var clearcoat_normal_sampler: sampler; #endif // PBR_MULTI_LAYER_MATERIAL_TEXTURES_SUPPORTED #ifdef PBR_SPECULAR_TEXTURES_SUPPORTED -@group(2) @binding(27) var specular_texture: texture_2d; -@group(2) @binding(28) var specular_sampler: sampler; -@group(2) @binding(29) var specular_tint_texture: texture_2d; -@group(2) @binding(30) var specular_tint_sampler: sampler; +@group(3) @binding(27) var specular_texture: texture_2d; +@group(3) @binding(28) var specular_sampler: sampler; +@group(3) @binding(29) var specular_tint_texture: texture_2d; +@group(3) @binding(30) var specular_tint_sampler: sampler; #endif // PBR_SPECULAR_TEXTURES_SUPPORTED #endif // BINDLESS diff --git a/crates/bevy_pbr/src/render/skinning.wgsl b/crates/bevy_pbr/src/render/skinning.wgsl index 1762a73887..6c4da0754a 100644 --- a/crates/bevy_pbr/src/render/skinning.wgsl +++ b/crates/bevy_pbr/src/render/skinning.wgsl @@ -6,9 +6,9 @@ #ifdef SKINNED #ifdef SKINS_USE_UNIFORM_BUFFERS -@group(1) @binding(1) var joint_matrices: SkinnedMesh; +@group(2) @binding(1) var joint_matrices: SkinnedMesh; #else // SKINS_USE_UNIFORM_BUFFERS -@group(1) @binding(1) var joint_matrices: array>; +@group(2) @binding(1) var joint_matrices: array>; #endif // SKINS_USE_UNIFORM_BUFFERS // 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 // the previous frame, this is simply the same as `joint_matrices` above. #ifdef SKINS_USE_UNIFORM_BUFFERS -@group(1) @binding(6) var prev_joint_matrices: SkinnedMesh; +@group(2) @binding(6) var prev_joint_matrices: SkinnedMesh; #else // SKINS_USE_UNIFORM_BUFFERS -@group(1) @binding(6) var prev_joint_matrices: array>; +@group(2) @binding(6) var prev_joint_matrices: array>; #endif // SKINS_USE_UNIFORM_BUFFERS fn skin_model( diff --git a/crates/bevy_pbr/src/ssr/mod.rs b/crates/bevy_pbr/src/ssr/mod.rs index 67ca025ce7..f3d876ffde 100644 --- a/crates/bevy_pbr/src/ssr/mod.rs +++ b/crates/bevy_pbr/src/ssr/mod.rs @@ -323,7 +323,7 @@ impl ViewNode for ScreenSpaceReflectionsNode { render_pass.set_render_pipeline(render_pipeline); render_pass.set_bind_group( 0, - &view_bind_group.value, + &view_bind_group.main, &[ view_uniform_offset.offset, view_lights_offset.offset, @@ -333,9 +333,10 @@ impl ViewNode for ScreenSpaceReflectionsNode { **view_environment_map_offset, ], ); + render_pass.set_bind_group(1, &view_bind_group.binding_array, &[]); // 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); Ok(()) @@ -517,9 +518,14 @@ impl SpecializedRenderPipeline for ScreenSpaceReflectionsPipeline { type Key = ScreenSpaceReflectionsPipelineKey; fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor { - let mesh_view_layout = self + let layout = self .mesh_view_layouts .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![ "DEPTH_PREPASS".into(), @@ -537,7 +543,7 @@ impl SpecializedRenderPipeline for ScreenSpaceReflectionsPipeline { RenderPipelineDescriptor { label: Some("SSR pipeline".into()), - layout: vec![mesh_view_layout.clone(), self.bind_group_layout.clone()], + layout, vertex: self.fullscreen_shader.to_vertex_state(), fragment: Some(FragmentState { shader: self.fragment_shader.clone(), diff --git a/crates/bevy_pbr/src/ssr/raymarch.wgsl b/crates/bevy_pbr/src/ssr/raymarch.wgsl index e149edfbbc..12140c91e3 100644 --- a/crates/bevy_pbr/src/ssr/raymarch.wgsl +++ b/crates/bevy_pbr/src/ssr/raymarch.wgsl @@ -25,10 +25,10 @@ } // 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. -@group(1) @binding(3) var depth_nearest_sampler: sampler; +@group(2) @binding(3) var depth_nearest_sampler: sampler; // Main code diff --git a/crates/bevy_pbr/src/ssr/ssr.wgsl b/crates/bevy_pbr/src/ssr/ssr.wgsl index 3dddfa1ba3..d646ac69fe 100644 --- a/crates/bevy_pbr/src/ssr/ssr.wgsl +++ b/crates/bevy_pbr/src/ssr/ssr.wgsl @@ -36,10 +36,10 @@ #endif // The texture representing the color framebuffer. -@group(1) @binding(0) var color_texture: texture_2d; +@group(2) @binding(0) var color_texture: texture_2d; // 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`. diff --git a/crates/bevy_pbr/src/volumetric_fog/render.rs b/crates/bevy_pbr/src/volumetric_fog/render.rs index cf2989a980..a5cd8e56f3 100644 --- a/crates/bevy_pbr/src/volumetric_fog/render.rs +++ b/crates/bevy_pbr/src/volumetric_fog/render.rs @@ -461,7 +461,7 @@ impl ViewNode for VolumetricFogNode { render_pass.set_pipeline(pipeline); render_pass.set_bind_group( 0, - &view_bind_group.value, + &view_bind_group.main, &[ view_uniform_offset.offset, view_lights_offset.offset, @@ -511,10 +511,6 @@ impl SpecializedRenderPipeline for VolumetricFogPipeline { type Key = VolumetricFogPipelineKey; 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 // more accurate versions with percentage-closer filtering aren't worth // the overhead. @@ -559,9 +555,17 @@ impl SpecializedRenderPipeline for VolumetricFogPipeline { 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 { label: Some("volumetric lighting pipeline".into()), - layout: vec![mesh_view_layout.clone(), volumetric_view_bind_group_layout], + layout, push_constant_ranges: vec![], vertex: VertexState { shader: self.shader.clone(), diff --git a/crates/bevy_pbr/src/wireframe.rs b/crates/bevy_pbr/src/wireframe.rs index b710141ea3..f7c193161d 100644 --- a/crates/bevy_pbr/src/wireframe.rs +++ b/crates/bevy_pbr/src/wireframe.rs @@ -1,6 +1,7 @@ use crate::{ DrawMesh, MeshPipeline, MeshPipelineKey, RenderMeshInstanceFlags, RenderMeshInstances, - SetMeshBindGroup, SetMeshViewBindGroup, ViewKeyCache, ViewSpecializationTicks, + SetMeshBindGroup, SetMeshViewBindGroup, SetMeshViewBindingArrayBindGroup, ViewKeyCache, + ViewSpecializationTicks, }; use bevy_app::{App, Plugin, PostUpdate, Startup, Update}; use bevy_asset::{ @@ -318,7 +319,8 @@ impl RenderCommand

for SetWireframe3dPushConstants { pub type DrawWireframe3d = ( SetItemPipeline, SetMeshViewBindGroup<0>, - SetMeshBindGroup<1>, + SetMeshViewBindingArrayBindGroup<1>, + SetMeshBindGroup<2>, SetWireframe3dPushConstants, DrawMesh, ); diff --git a/crates/bevy_reflect/Cargo.toml b/crates/bevy_reflect/Cargo.toml index 60f8478bac..ae3a3a856e 100644 --- a/crates/bevy_reflect/Cargo.toml +++ b/crates/bevy_reflect/Cargo.toml @@ -109,7 +109,7 @@ uuid = { version = "1.13.1", default-features = false, optional = true, features "serde", ] } variadics_please = "1.1" -wgpu-types = { version = "24", features = [ +wgpu-types = { version = "25", features = [ "serde", ], optional = true, default-features = false } diff --git a/crates/bevy_render/Cargo.toml b/crates/bevy_render/Cargo.toml index 9ecbbfc744..a657da4f0e 100644 --- a/crates/bevy_render/Cargo.toml +++ b/crates/bevy_render/Cargo.toml @@ -85,19 +85,21 @@ bevy_platform = { path = "../bevy_platform", version = "0.17.0-dev", default-fea image = { version = "0.25.2", default-features = false } # 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 # 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 # 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", "dx12", "metal", + "vulkan", + "gles", "naga-ir", "fragile-send-sync-non-atomic-wasm", ] } -naga = { version = "24", features = ["wgsl-in"] } +naga = { version = "25", features = ["wgsl-in"] } serde = { version = "1", features = ["derive"] } bytemuck = { version = "1.5", features = ["derive", "must_cast"] } 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] # 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", ] } @@ -131,7 +133,7 @@ naga_oil = { version = "0.17.1", default-features = false, features = [ proptest = "1" [target.'cfg(target_arch = "wasm32")'.dependencies] -naga_oil = "0.17.1" +naga_oil = { version = "0.18" } js-sys = "0.3" web-sys = { version = "0.3.67", features = [ 'Blob', diff --git a/crates/bevy_render/macros/src/as_bind_group.rs b/crates/bevy_render/macros/src/as_bind_group.rs index 4252929170..7bac6796ac 100644 --- a/crates/bevy_render/macros/src/as_bind_group.rs +++ b/crates/bevy_render/macros/src/as_bind_group.rs @@ -204,7 +204,7 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result { #bind_group_layout_entries.push( #render_path::render_resource::BindGroupLayoutEntry { 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: #uniform_binding_type, has_dynamic_offset: false, @@ -253,7 +253,7 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result { #bind_group_layout_entries.push( #render_path::render_resource::BindGroupLayoutEntry { 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: #uniform_binding_type, has_dynamic_offset: false, @@ -279,7 +279,7 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result { #bind_group_layout_entries.push( #render_path::render_resource::BindGroupLayoutEntry { 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: #uniform_binding_type, has_dynamic_offset: false, @@ -519,7 +519,7 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result { #bind_group_layout_entries.push( #render_path::render_resource::BindGroupLayoutEntry { 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::BufferBindingType::Storage { read_only: #read_only @@ -834,7 +834,7 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result { #bind_group_layout_entries.push( #render_path::render_resource::BindGroupLayoutEntry { 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: #uniform_binding_type, has_dynamic_offset: false, @@ -881,7 +881,7 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result { non_bindless_binding_layouts.push(quote!{ #bind_group_layout_entries.push(#render_path::render_resource::BindGroupLayoutEntry { 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: #uniform_binding_type, has_dynamic_offset: false, @@ -1337,7 +1337,13 @@ impl VisibilityFlags { impl ShaderStageVisibility { fn hygienic_quote(&self, path: &proc_macro2::TokenStream) -> proc_macro2::TokenStream { 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::Flags(flags) => { let mut quoted = Vec::new(); diff --git a/crates/bevy_render/src/bindless.wgsl b/crates/bevy_render/src/bindless.wgsl index 05517a1746..717e9c1047 100644 --- a/crates/bevy_render/src/bindless.wgsl +++ b/crates/bevy_render/src/bindless.wgsl @@ -16,22 +16,22 @@ // Binding 0 is the bindless index table. // Filtering samplers. -@group(2) @binding(1) var bindless_samplers_filtering: binding_array; +@group(3) @binding(1) var bindless_samplers_filtering: binding_array; // Non-filtering samplers (nearest neighbor). -@group(2) @binding(2) var bindless_samplers_non_filtering: binding_array; +@group(3) @binding(2) var bindless_samplers_non_filtering: binding_array; // Comparison samplers (typically for shadow mapping). -@group(2) @binding(3) var bindless_samplers_comparison: binding_array; +@group(3) @binding(3) var bindless_samplers_comparison: binding_array; // 1D textures. -@group(2) @binding(4) var bindless_textures_1d: binding_array>; +@group(3) @binding(4) var bindless_textures_1d: binding_array>; // 2D textures. -@group(2) @binding(5) var bindless_textures_2d: binding_array>; +@group(3) @binding(5) var bindless_textures_2d: binding_array>; // 2D array textures. -@group(2) @binding(6) var bindless_textures_2d_array: binding_array>; +@group(3) @binding(6) var bindless_textures_2d_array: binding_array>; // 3D textures. -@group(2) @binding(7) var bindless_textures_3d: binding_array>; +@group(3) @binding(7) var bindless_textures_3d: binding_array>; // Cubemap textures. -@group(2) @binding(8) var bindless_textures_cube: binding_array>; +@group(3) @binding(8) var bindless_textures_cube: binding_array>; // Cubemap array textures. -@group(2) @binding(9) var bindless_textures_cube_array: binding_array>; +@group(3) @binding(9) var bindless_textures_cube_array: binding_array>; #endif // BINDLESS diff --git a/crates/bevy_render/src/diagnostic/tracy_gpu.rs b/crates/bevy_render/src/diagnostic/tracy_gpu.rs index c059b8baa5..7a66db4ea6 100644 --- a/crates/bevy_render/src/diagnostic/tracy_gpu.rs +++ b/crates/bevy_render/src/diagnostic/tracy_gpu.rs @@ -1,7 +1,7 @@ use crate::renderer::{RenderAdapterInfo, RenderDevice, RenderQueue}; use tracy_client::{Client, GpuContext, GpuContextType}; use wgpu::{ - Backend, BufferDescriptor, BufferUsages, CommandEncoderDescriptor, Maintain, MapMode, + Backend, BufferDescriptor, BufferUsages, CommandEncoderDescriptor, MapMode, PollType, QuerySetDescriptor, QueryType, QUERY_SIZE, }; @@ -14,7 +14,7 @@ pub fn new_tracy_gpu_context( Backend::Vulkan => GpuContextType::Vulkan, Backend::Dx12 => GpuContextType::Direct3D12, 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(); @@ -60,7 +60,9 @@ fn initial_timestamp(device: &RenderDevice, queue: &RenderQueue) -> i64 { queue.submit([timestamp_encoder.finish(), copy_encoder.finish()]); 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(); i64::from_le_bytes((*view).try_into().unwrap()) diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index a20315c099..aa1e2da676 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -352,10 +352,12 @@ impl Plugin for RenderPlugin { backend_options: wgpu::BackendOptions { gl: wgpu::GlBackendOptions { gles_minor_version: settings.gles3_minor_version, + fence_behavior: wgpu::GlFenceBehavior::Normal, }, dx12: wgpu::Dx12BackendOptions { shader_compiler: settings.dx12_shader_compiler.clone(), }, + noop: wgpu::NoopBackendOptions { enable: false }, }, }); diff --git a/crates/bevy_render/src/render_phase/draw.rs b/crates/bevy_render/src/render_phase/draw.rs index 42a6725c8c..39c6a074e6 100644 --- a/crates/bevy_render/src/render_phase/draw.rs +++ b/crates/bevy_render/src/render_phase/draw.rs @@ -168,14 +168,16 @@ impl DrawFunctions

{ /// ``` /// # use bevy_render::render_phase::SetItemPipeline; /// # struct SetMeshViewBindGroup; +/// # struct SetMeshViewBindingArrayBindGroup; /// # struct SetMeshBindGroup; /// # struct SetMaterialBindGroup(std::marker::PhantomData); /// # struct DrawMesh; /// pub type DrawMaterial = ( /// SetItemPipeline, /// SetMeshViewBindGroup<0>, -/// SetMeshBindGroup<1>, -/// SetMaterialBindGroup, +/// SetMeshViewBindingArrayBindGroup<1>, +/// SetMeshBindGroup<2>, +/// SetMaterialBindGroup, /// DrawMesh, /// ); /// ``` diff --git a/crates/bevy_render/src/render_resource/bind_group.rs b/crates/bevy_render/src/render_resource/bind_group.rs index cff88bd355..17de8455da 100644 --- a/crates/bevy_render/src/render_resource/bind_group.rs +++ b/crates/bevy_render/src/render_resource/bind_group.rs @@ -133,12 +133,12 @@ impl Deref for BindGroup { /// In WGSL shaders, the binding would look like this: /// /// ```wgsl -/// @group(2) @binding(0) var color: vec4; -/// @group(2) @binding(1) var color_texture: texture_2d; -/// @group(2) @binding(2) var color_sampler: sampler; -/// @group(2) @binding(3) var storage_buffer: array; -/// @group(2) @binding(4) var raw_buffer: array; -/// @group(2) @binding(5) var storage_texture: texture_storage_2d; +/// @group(3) @binding(0) var color: vec4; +/// @group(3) @binding(1) var color_texture: texture_2d; +/// @group(3) @binding(2) var color_sampler: sampler; +/// @group(3) @binding(3) var storage_buffer: array; +/// @group(3) @binding(4) var raw_buffer: array; +/// @group(3) @binding(5) var storage_texture: texture_storage_2d; /// ``` /// 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. @@ -261,7 +261,7 @@ impl Deref for BindGroup { /// roughness: f32, /// }; /// -/// @group(2) @binding(0) var material: CoolMaterial; +/// @group(3) @binding(0) var material: CoolMaterial; /// ``` /// /// 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: /// /// ```wgsl -/// @group(2) @binding(10) var material_array: binding_array; +/// @group(3) @binding(10) var material_array: binding_array; /// ``` /// /// 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: /// /// ```wgsl -/// @group(2) @binding(10) var material_array: array; +/// @group(3) @binding(10) var material_array: array; /// ``` /// /// * 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: /// /// ```wgsl -/// @group(2) @binding(0) var material: StandardMaterial; +/// @group(3) @binding(0) var material: StandardMaterial; /// ``` /// /// * For efficiency reasons, `data` is generally preferred over `uniform` diff --git a/crates/bevy_render/src/render_resource/bind_group_entries.rs b/crates/bevy_render/src/render_resource/bind_group_entries.rs index cc8eb188de..847bb46f49 100644 --- a/crates/bevy_render/src/render_resource/bind_group_entries.rs +++ b/crates/bevy_render/src/render_resource/bind_group_entries.rs @@ -287,6 +287,12 @@ impl<'b> DynamicBindGroupEntries<'b> { } } + pub fn new() -> Self { + Self { + entries: Vec::new(), + } + } + pub fn extend_with_indices( mut self, entries: impl IntoIndexedBindingArray<'b, N>, diff --git a/crates/bevy_render/src/render_resource/bind_group_layout_entries.rs b/crates/bevy_render/src/render_resource/bind_group_layout_entries.rs index 41affa4349..17630b7dae 100644 --- a/crates/bevy_render/src/render_resource/bind_group_layout_entries.rs +++ b/crates/bevy_render/src/render_resource/bind_group_layout_entries.rs @@ -334,6 +334,13 @@ impl DynamicBindGroupLayoutEntries { } } + pub fn new(default_visibility: ShaderStages) -> Self { + Self { + default_visibility, + entries: Vec::new(), + } + } + pub fn extend_with_indices( mut self, entries: impl IntoIndexedBindGroupLayoutEntryBuilderArray, @@ -570,6 +577,16 @@ pub mod binding_types { } 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() } } diff --git a/crates/bevy_render/src/render_resource/bindless.rs b/crates/bevy_render/src/render_resource/bindless.rs index 64a0fa2c1f..dc3bf00eee 100644 --- a/crates/bevy_render/src/render_resource/bindless.rs +++ b/crates/bevy_render/src/render_resource/bindless.rs @@ -243,35 +243,65 @@ pub fn create_bindless_bind_group_layout_entries( false, NonZeroU64::new(bindless_index_table_length as u64 * size_of::() 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. sampler(SamplerBindingType::Filtering) .count(bindless_slab_resource_limit) - .build(1, ShaderStages::all()), + .build( + 1, + ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE, + ), sampler(SamplerBindingType::NonFiltering) .count(bindless_slab_resource_limit) - .build(2, ShaderStages::all()), + .build( + 2, + ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE, + ), sampler(SamplerBindingType::Comparison) .count(bindless_slab_resource_limit) - .build(3, ShaderStages::all()), + .build( + 3, + ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE, + ), texture_1d(TextureSampleType::Float { filterable: true }) .count(bindless_slab_resource_limit) - .build(4, ShaderStages::all()), + .build( + 4, + ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE, + ), texture_2d(TextureSampleType::Float { filterable: true }) .count(bindless_slab_resource_limit) - .build(5, ShaderStages::all()), + .build( + 5, + ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE, + ), texture_2d_array(TextureSampleType::Float { filterable: true }) .count(bindless_slab_resource_limit) - .build(6, ShaderStages::all()), + .build( + 6, + ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE, + ), texture_3d(TextureSampleType::Float { filterable: true }) .count(bindless_slab_resource_limit) - .build(7, ShaderStages::all()), + .build( + 7, + ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE, + ), texture_cube(TextureSampleType::Float { filterable: true }) .count(bindless_slab_resource_limit) - .build(8, ShaderStages::all()), + .build( + 8, + ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE, + ), texture_cube_array(TextureSampleType::Float { filterable: true }) .count(bindless_slab_resource_limit) - .build(9, ShaderStages::all()), + .build( + 9, + ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE, + ), ] } diff --git a/crates/bevy_render/src/render_resource/mod.rs b/crates/bevy_render/src/render_resource/mod.rs index aecf27173d..09be66e840 100644 --- a/crates/bevy_render/src/render_resource/mod.rs +++ b/crates/bevy_render/src/render_resource/mod.rs @@ -49,18 +49,18 @@ pub use wgpu::{ ComputePassDescriptor, ComputePipelineDescriptor as RawComputePipelineDescriptor, CreateBlasDescriptor, CreateTlasDescriptor, DepthBiasState, DepthStencilState, DownlevelFlags, 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, - PipelineLayoutDescriptor, PolygonMode, PrimitiveState, PrimitiveTopology, PushConstantRange, - RenderPassColorAttachment, RenderPassDepthStencilAttachment, RenderPassDescriptor, - RenderPipelineDescriptor as RawRenderPipelineDescriptor, Sampler as WgpuSampler, - SamplerBindingType, SamplerBindingType as WgpuSamplerBindingType, SamplerDescriptor, - ShaderModule, ShaderModuleDescriptor, ShaderSource, ShaderStages, StencilFaceState, - StencilOperation, StencilState, StorageTextureAccess, StoreOp, TexelCopyBufferInfo, - TexelCopyBufferLayout, TexelCopyTextureInfo, TextureAspect, TextureDescriptor, - TextureDimension, TextureFormat, TextureFormatFeatureFlags, TextureFormatFeatures, - TextureSampleType, TextureUsages, TextureView as WgpuTextureView, TextureViewDescriptor, - TextureViewDimension, Tlas, TlasInstance, TlasPackage, VertexAttribute, + PipelineLayoutDescriptor, PollType, PolygonMode, PrimitiveState, PrimitiveTopology, + PushConstantRange, RenderPassColorAttachment, RenderPassDepthStencilAttachment, + RenderPassDescriptor, RenderPipelineDescriptor as RawRenderPipelineDescriptor, + Sampler as WgpuSampler, SamplerBindingType, SamplerBindingType as WgpuSamplerBindingType, + SamplerDescriptor, ShaderModule, ShaderModuleDescriptor, ShaderSource, ShaderStages, + StencilFaceState, StencilOperation, StencilState, StorageTextureAccess, StoreOp, + TexelCopyBufferInfo, TexelCopyBufferLayout, TexelCopyTextureInfo, TextureAspect, + TextureDescriptor, TextureDimension, TextureFormat, TextureFormatFeatureFlags, + TextureFormatFeatures, TextureSampleType, TextureUsages, TextureView as WgpuTextureView, + TextureViewDescriptor, TextureViewDimension, Tlas, TlasInstance, TlasPackage, VertexAttribute, VertexBufferLayout as RawVertexBufferLayout, VertexFormat, VertexState as RawVertexState, VertexStepMode, COPY_BUFFER_ALIGNMENT, }; diff --git a/crates/bevy_render/src/render_resource/pipeline_cache.rs b/crates/bevy_render/src/render_resource/pipeline_cache.rs index 9d658cf361..ebd3229636 100644 --- a/crates/bevy_render/src/render_resource/pipeline_cache.rs +++ b/crates/bevy_render/src/render_resource/pipeline_cache.rs @@ -873,7 +873,7 @@ impl PipelineCache { // TODO: Expose the rest of this somehow let compilation_options = PipelineCompilationOptions { - constants: &default(), + constants: &[], zero_initialize_workgroup_memory: descriptor.zero_initialize_workgroup_memory, }; @@ -955,7 +955,7 @@ impl PipelineCache { entry_point: Some(&descriptor.entry_point), // TODO: Expose the rest of this somehow compilation_options: PipelineCompilationOptions { - constants: &default(), + constants: &[], zero_initialize_workgroup_memory: descriptor .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), ); capabilities.set( - Capabilities::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING, - features.contains(Features::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING), + Capabilities::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 capabilities.set( @@ -1229,6 +1233,14 @@ fn get_capabilities(features: Features, downlevel: DownlevelFlags) -> Capabiliti Capabilities::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 } diff --git a/crates/bevy_render/src/render_resource/shader.rs b/crates/bevy_render/src/render_resource/shader.rs index ff8430b951..1be9fd7427 100644 --- a/crates/bevy_render/src/render_resource/shader.rs +++ b/crates/bevy_render/src/render_resource/shader.rs @@ -301,6 +301,8 @@ impl From<&Source> for naga_oil::compose::ShaderType { naga::ShaderStage::Vertex => naga_oil::compose::ShaderType::GlslVertex, naga::ShaderStage::Fragment => naga_oil::compose::ShaderType::GlslFragment, 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")))] Source::Glsl(_, _) => panic!( diff --git a/crates/bevy_render/src/renderer/mod.rs b/crates/bevy_render/src/renderer/mod.rs index bec30200a8..5df4967757 100644 --- a/crates/bevy_render/src/renderer/mod.rs +++ b/crates/bevy_render/src/renderer/mod.rs @@ -23,7 +23,7 @@ use bevy_platform::time::Instant; use bevy_time::TimeSender; use wgpu::{ 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. @@ -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 // should not be automatically enabled in this case. It is however beneficial for // integrated GPUs. - features -= wgpu::Features::MAPPABLE_PRIMARY_BUFFERS; + features.remove(wgpu::Features::MAPPABLE_PRIMARY_BUFFERS); } limits = adapter.limits(); @@ -185,7 +185,7 @@ pub async fn initialize_renderer( // Enforce the 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. 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 .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 .min(constrained_limits.max_uniform_buffer_binding_size), @@ -304,15 +310,14 @@ pub async fn initialize_renderer( } let (device, queue) = adapter - .request_device( - &wgpu::DeviceDescriptor { - label: options.device_label.as_ref().map(AsRef::as_ref), - required_features: features, - required_limits: limits, - memory_hints: options.memory_hints.clone(), - }, - options.trace_path.as_deref(), - ) + .request_device(&wgpu::DeviceDescriptor { + label: options.device_label.as_ref().map(AsRef::as_ref), + required_features: features, + required_limits: limits, + memory_hints: options.memory_hints.clone(), + // See https://github.com/gfx-rs/wgpu/issues/5974 + trace: Trace::Off, + }) .await .unwrap(); let queue = Arc::new(WgpuWrapper::new(queue)); diff --git a/crates/bevy_render/src/renderer/render_device.rs b/crates/bevy_render/src/renderer/render_device.rs index 31f47e5740..b1a20d2ace 100644 --- a/crates/bevy_render/src/renderer/render_device.rs +++ b/crates/bevy_render/src/renderer/render_device.rs @@ -7,7 +7,7 @@ use bevy_ecs::resource::Resource; use bevy_utils::WgpuWrapper; use wgpu::{ 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. @@ -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. // No attempt is made to ensure that data is valid SPIR-V. unsafe { - self.device - .create_shader_module_spirv(&wgpu::ShaderModuleDescriptorSpirV { - label: desc.label, - source: source.clone(), - }) + self.device.create_shader_module_passthrough( + wgpu::ShaderModuleDescriptorPassthrough::SpirV( + wgpu::ShaderModuleDescriptorSpirV { + label: desc.label, + source: source.clone(), + }, + ), + ) } } // SAFETY: @@ -118,7 +121,7 @@ impl RenderDevice { /// /// no-op on the web, device is automatically polled. #[inline] - pub fn poll(&self, maintain: wgpu::Maintain) -> MaintainResult { + pub fn poll(&self, maintain: wgpu::PollType) -> Result { self.device.poll(maintain) } diff --git a/crates/bevy_render/src/settings.rs b/crates/bevy_render/src/settings.rs index ab50fc81b1..715bbb35f8 100644 --- a/crates/bevy_render/src/settings.rs +++ b/crates/bevy_render/src/settings.rs @@ -2,8 +2,8 @@ use crate::renderer::{ RenderAdapter, RenderAdapterInfo, RenderDevice, RenderInstance, RenderQueue, }; use alloc::borrow::Cow; -use std::path::PathBuf; +use wgpu::DxcShaderModel; pub use wgpu::{ Backends, Dx12Compiler, Features as WgpuFeatures, Gles3MinorVersion, InstanceFlags, Limits as WgpuLimits, MemoryHints, PowerPreference, @@ -53,8 +53,6 @@ pub struct WgpuSettings { pub instance_flags: InstanceFlags, /// This hints to the WGPU device about the preferred memory allocation strategy. 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, } impl Default for WgpuSettings { @@ -114,6 +112,7 @@ impl Default for WgpuSettings { Dx12Compiler::DynamicDxc { dxc_path: String::from(dxc), dxil_path: String::from(dxil), + max_shader_model: DxcShaderModel::V6_7, } } else { Dx12Compiler::Fxc @@ -137,7 +136,6 @@ impl Default for WgpuSettings { gles3_minor_version, instance_flags, memory_hints: MemoryHints::default(), - trace_path: None, } } } diff --git a/crates/bevy_solari/src/lib.rs b/crates/bevy_solari/src/lib.rs index 416c850b04..d5a22e014b 100644 --- a/crates/bevy_solari/src/lib.rs +++ b/crates/bevy_solari/src/lib.rs @@ -46,7 +46,6 @@ impl SolariPlugin { | WgpuFeatures::EXPERIMENTAL_RAY_QUERY | WgpuFeatures::BUFFER_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::PARTIALLY_BOUND_BINDING_ARRAY } diff --git a/crates/bevy_solari/src/scene/raytracing_scene_bindings.wgsl b/crates/bevy_solari/src/scene/raytracing_scene_bindings.wgsl index aad064590f..974ee50d7d 100644 --- a/crates/bevy_solari/src/scene/raytracing_scene_bindings.wgsl +++ b/crates/bevy_solari/src/scene/raytracing_scene_bindings.wgsl @@ -71,8 +71,8 @@ struct DirectionalLight { @group(0) @binding(9) var light_sources: array; @group(0) @binding(10) var directional_lights: array; -const RAY_T_MIN = 0.01; -const RAY_T_MAX = 100000.0; +const RAY_T_MIN = 0.01f; +const RAY_T_MAX = 100000.0f; const RAY_NO_CULL = 0xFFu; @@ -120,7 +120,7 @@ fn resolve_material(material: Material, uv: vec2) -> ResolvedMaterial { 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); - 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) -> ResolvedRayHitFull { diff --git a/crates/bevy_winit/Cargo.toml b/crates/bevy_winit/Cargo.toml index bb28b84fb0..43dcc0506b 100644 --- a/crates/bevy_winit/Cargo.toml +++ b/crates/bevy_winit/Cargo.toml @@ -59,7 +59,7 @@ cfg-if = "1.0" raw-window-handle = "0.6" serde = { version = "1.0", features = ["derive"], optional = true } bytemuck = { version = "1.5", optional = true } -wgpu-types = { version = "24", optional = true } +wgpu-types = { version = "25", optional = true } accesskit = "0.19" tracing = { version = "0.1", default-features = false, features = ["std"] } diff --git a/examples/app/headless_renderer.rs b/examples/app/headless_renderer.rs index 2038c85a29..5d027b2115 100644 --- a/examples/app/headless_renderer.rs +++ b/examples/app/headless_renderer.rs @@ -22,8 +22,8 @@ use bevy::{ render_asset::{RenderAssetUsages, RenderAssets}, render_graph::{self, NodeRunError, RenderGraph, RenderGraphContext, RenderLabel}, render_resource::{ - Buffer, BufferDescriptor, BufferUsages, CommandEncoderDescriptor, Extent3d, Maintain, - MapMode, TexelCopyBufferInfo, TexelCopyBufferLayout, TextureDimension, TextureFormat, + Buffer, BufferDescriptor, BufferUsages, CommandEncoderDescriptor, Extent3d, MapMode, + PollType, TexelCopyBufferInfo, TexelCopyBufferLayout, TextureDimension, TextureFormat, TextureUsages, }, renderer::{RenderContext, RenderDevice, RenderQueue}, @@ -41,7 +41,6 @@ use std::{ }, time::Duration, }; - // 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 // 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. // 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 r.recv().expect("Failed to receive the map_async message"); diff --git a/examples/shader/custom_render_phase.rs b/examples/shader/custom_render_phase.rs index 0f834e893a..309954bbe3 100644 --- a/examples/shader/custom_render_phase.rs +++ b/examples/shader/custom_render_phase.rs @@ -12,6 +12,7 @@ use std::ops::Range; +use bevy::pbr::SetMeshViewEmptyBindGroup; use bevy::{ core_pipeline::core_3d::graph::{Core3d, Node3d}, ecs::{ @@ -193,17 +194,19 @@ impl SpecializedMeshPipeline for StencilPipeline { } // This will automatically generate the correct `VertexBufferLayout` based on the 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 { label: Some("Specialized Mesh Pipeline".into()), // We want to reuse the data from bevy so we use the same bind groups as the default // mesh pipeline layout: vec![ // Bind group 0 is the view uniform - self.mesh_pipeline - .get_view_layout(MeshPipelineViewLayoutKey::from(key)) - .clone(), - // Bind group 1 is the mesh uniform + view_layout.main_layout.clone(), + // Bind group 1 is empty + view_layout.empty_layout.clone(), + // Bind group 2 is the mesh uniform self.mesh_pipeline.mesh_layouts.model_only.clone(), ], push_constant_ranges: vec![], @@ -244,8 +247,10 @@ type DrawMesh3dStencil = ( SetItemPipeline, // This will set the view bindings in group 0 SetMeshViewBindGroup<0>, - // This will set the mesh bindings in group 1 - SetMeshBindGroup<1>, + // This will set an empty bind group in group 1 + SetMeshViewEmptyBindGroup<1>, + // This will set the mesh bindings in group 2 + SetMeshBindGroup<2>, // This will draw the mesh DrawMesh, ); diff --git a/examples/shader/custom_shader_instancing.rs b/examples/shader/custom_shader_instancing.rs index 06845b0617..358a2beac7 100644 --- a/examples/shader/custom_shader_instancing.rs +++ b/examples/shader/custom_shader_instancing.rs @@ -7,6 +7,7 @@ //! implementation using bevy's low level rendering api. //! It's generally recommended to try the built-in instancing before going with this approach. +use bevy::pbr::SetMeshViewBindingArrayBindGroup; use bevy::{ core_pipeline::core_3d::Transparent3d, ecs::{ @@ -248,7 +249,8 @@ impl SpecializedMeshPipeline for CustomPipeline { type DrawCustom = ( SetItemPipeline, SetMeshViewBindGroup<0>, - SetMeshBindGroup<1>, + SetMeshViewBindingArrayBindGroup<1>, + SetMeshBindGroup<2>, DrawMeshInstanced, ); diff --git a/examples/shader/specialized_mesh_pipeline.rs b/examples/shader/specialized_mesh_pipeline.rs index 9a7a3fce96..eeeeeb4a1e 100644 --- a/examples/shader/specialized_mesh_pipeline.rs +++ b/examples/shader/specialized_mesh_pipeline.rs @@ -12,7 +12,7 @@ use bevy::{ math::{vec3, vec4}, pbr::{ DrawMesh, MeshPipeline, MeshPipelineKey, MeshPipelineViewLayoutKey, RenderMeshInstances, - SetMeshBindGroup, SetMeshViewBindGroup, + SetMeshBindGroup, SetMeshViewBindGroup, SetMeshViewEmptyBindGroup, }, prelude::*, render::{ @@ -153,8 +153,10 @@ type DrawSpecializedPipelineCommands = ( SetItemPipeline, // Set the view uniform at bind group 0 SetMeshViewBindGroup<0>, - // Set the mesh uniform at bind group 1 - SetMeshBindGroup<1>, + // Set an empty material bind group at bind group 1 + SetMeshViewEmptyBindGroup<1>, + // Set the mesh uniform at bind group 2 + SetMeshBindGroup<2>, // Draw the mesh DrawMesh, ); @@ -210,14 +212,15 @@ impl SpecializedMeshPipeline for CustomMeshPipeline { // This will automatically generate the correct `VertexBufferLayout` based on the 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 { label: Some("Specialized Mesh Pipeline".into()), layout: vec![ - // Bind group 0 is the view uniform - self.mesh_pipeline - .get_view_layout(MeshPipelineViewLayoutKey::from(mesh_key)) - .clone(), - // Bind group 1 is the mesh uniform + view_layout.main_layout.clone(), + view_layout.empty_layout.clone(), self.mesh_pipeline.mesh_layouts.model_only.clone(), ], push_constant_ranges: vec![], diff --git a/examples/shader/texture_binding_array.rs b/examples/shader/texture_binding_array.rs index bf5dc3da9d..e4939edf6a 100644 --- a/examples/shader/texture_binding_array.rs +++ b/examples/shader/texture_binding_array.rs @@ -163,7 +163,7 @@ impl AsBindGroup for BindlessMaterial { ( // Screen texture // - // @group(2) @binding(0) var textures: binding_array>; + // @group(3) @binding(0) var textures: binding_array>; ( 0, texture_2d(TextureSampleType::Float { filterable: true }) @@ -171,7 +171,7 @@ impl AsBindGroup for BindlessMaterial { ), // 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 // onto one binding slot: diff --git a/release-content/migration-guides/wgpu_25.md b/release-content/migration-guides/wgpu_25.md new file mode 100644 index 0000000000..dfbead7e28 --- /dev/null +++ b/release-content/migration-guides/wgpu_25.md @@ -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).