diff --git a/clippy.toml b/clippy.toml index 81951d92ec4d9..085da641febcb 100644 --- a/clippy.toml +++ b/clippy.toml @@ -1 +1 @@ -doc-valid-idents = ["sRGB", "NaN", "iOS", "glTF", "GitHub", "WebGPU", "GilRs"] +doc-valid-idents = ["sRGB", "NaN", "iOS", "glTF", "GitHub", "WebGL", "WebGPU", "GilRs"] diff --git a/crates/bevy_gizmos/src/pipeline_3d.rs b/crates/bevy_gizmos/src/pipeline_3d.rs index e63125a0fbb2f..416d7e9a9992f 100644 --- a/crates/bevy_gizmos/src/pipeline_3d.rs +++ b/crates/bevy_gizmos/src/pipeline_3d.rs @@ -93,11 +93,10 @@ impl SpecializedRenderPipeline for LineGizmoPipeline { TextureFormat::bevy_default() }; - let view_layout = if key.mesh_key.msaa_samples() == 1 { - self.mesh_pipeline.view_layout.clone() - } else { - self.mesh_pipeline.view_layout_multisampled.clone() - }; + let view_layout = self + .mesh_pipeline + .get_view_layout(key.mesh_key.into()) + .clone(); let layout = vec![view_layout, self.uniform_layout.clone()]; diff --git a/crates/bevy_pbr/src/deferred/mod.rs b/crates/bevy_pbr/src/deferred/mod.rs index 568be134be18a..b8b61d15573da 100644 --- a/crates/bevy_pbr/src/deferred/mod.rs +++ b/crates/bevy_pbr/src/deferred/mod.rs @@ -243,7 +243,7 @@ impl ViewNode for DeferredOpaquePass3dPbrLightingNode { #[derive(Resource)] pub struct DeferredLightingLayout { - bind_group_layout_0: BindGroupLayout, + mesh_pipeline: MeshPipeline, bind_group_layout_1: BindGroupLayout, } @@ -332,7 +332,7 @@ impl SpecializedRenderPipeline for DeferredLightingLayout { RenderPipelineDescriptor { label: Some("deferred_lighting_pipeline".into()), layout: vec![ - self.bind_group_layout_0.clone(), + self.mesh_pipeline.get_view_layout(key.into()).clone(), self.bind_group_layout_1.clone(), ], vertex: VertexState { @@ -395,7 +395,7 @@ impl FromWorld for DeferredLightingLayout { }], }); Self { - bind_group_layout_0: world.resource::().view_layout.clone(), + mesh_pipeline: world.resource::().clone(), bind_group_layout_1: layout, } } diff --git a/crates/bevy_pbr/src/prepass/mod.rs b/crates/bevy_pbr/src/prepass/mod.rs index 61afa1d0e459c..6c5b4559d045c 100644 --- a/crates/bevy_pbr/src/prepass/mod.rs +++ b/crates/bevy_pbr/src/prepass/mod.rs @@ -1,3 +1,7 @@ +mod prepass_bindings; + +pub use prepass_bindings::*; + use bevy_app::{Plugin, PreUpdate}; use bevy_asset::{load_internal_asset, AssetServer, Handle}; use bevy_core_pipeline::{ @@ -9,7 +13,7 @@ use bevy_core_pipeline::{ prelude::Camera3d, prepass::{ AlphaMask3dPrepass, DeferredPrepass, DepthPrepass, MotionVectorPrepass, NormalPrepass, - Opaque3dPrepass, ViewPrepassTextures, MOTION_VECTOR_PREPASS_FORMAT, NORMAL_PREPASS_FORMAT, + Opaque3dPrepass, MOTION_VECTOR_PREPASS_FORMAT, NORMAL_PREPASS_FORMAT, }, }; use bevy_ecs::{ @@ -32,21 +36,18 @@ use bevy_render::{ }, render_resource::{ BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindGroupLayoutDescriptor, - BindGroupLayoutEntry, BindingResource, BindingType, BufferBindingType, ColorTargetState, - ColorWrites, CompareFunction, DepthBiasState, DepthStencilState, DynamicUniformBuffer, - FragmentState, FrontFace, MultisampleState, PipelineCache, PolygonMode, PrimitiveState, - PushConstantRange, RenderPipelineDescriptor, Shader, ShaderRef, ShaderStages, ShaderType, + BindGroupLayoutEntry, BindingType, BufferBindingType, ColorTargetState, ColorWrites, + CompareFunction, DepthBiasState, DepthStencilState, DynamicUniformBuffer, FragmentState, + FrontFace, MultisampleState, PipelineCache, PolygonMode, PrimitiveState, PushConstantRange, + RenderPipelineDescriptor, Shader, ShaderRef, ShaderStages, ShaderType, SpecializedMeshPipeline, SpecializedMeshPipelineError, SpecializedMeshPipelines, - StencilFaceState, StencilState, TextureAspect, TextureFormat, TextureSampleType, - TextureView, TextureViewDescriptor, TextureViewDimension, VertexState, + StencilFaceState, StencilState, VertexState, }, renderer::{RenderDevice, RenderQueue}, - texture::{BevyDefault, FallbackImageMsaa}, view::{ExtractedView, Msaa, ViewUniform, ViewUniformOffset, ViewUniforms, VisibleEntities}, Extract, ExtractSchedule, Render, RenderApp, RenderSet, }; use bevy_transform::prelude::GlobalTransform; -use bevy_utils::default; use bevy_utils::tracing::error; use crate::{ @@ -634,131 +635,6 @@ where } } -pub fn get_bind_group_layout_entries( - bindings: [u32; 4], - multisampled: bool, -) -> [BindGroupLayoutEntry; 4] { - [ - // Depth texture - BindGroupLayoutEntry { - binding: bindings[0], - visibility: ShaderStages::FRAGMENT, - ty: BindingType::Texture { - multisampled, - sample_type: TextureSampleType::Depth, - view_dimension: TextureViewDimension::D2, - }, - count: None, - }, - // Normal texture - BindGroupLayoutEntry { - binding: bindings[1], - visibility: ShaderStages::FRAGMENT, - ty: BindingType::Texture { - multisampled, - sample_type: TextureSampleType::Float { filterable: false }, - view_dimension: TextureViewDimension::D2, - }, - count: None, - }, - // Motion Vectors texture - BindGroupLayoutEntry { - binding: bindings[2], - visibility: ShaderStages::FRAGMENT, - ty: BindingType::Texture { - multisampled, - sample_type: TextureSampleType::Float { filterable: false }, - view_dimension: TextureViewDimension::D2, - }, - count: None, - }, - // Deferred texture - BindGroupLayoutEntry { - binding: bindings[3], - visibility: ShaderStages::FRAGMENT, - ty: BindingType::Texture { - multisampled: false, - sample_type: TextureSampleType::Uint, - view_dimension: TextureViewDimension::D2, - }, - count: None, - }, - ] -} - -// Needed so the texture views can live long enough. -pub struct PrepassBindingsSet([TextureView; 4]); - -impl PrepassBindingsSet { - pub fn get_entries(&self, bindings: [u32; 4]) -> [BindGroupEntry; 4] { - [ - BindGroupEntry { - binding: bindings[0], - resource: BindingResource::TextureView(&self.0[0]), - }, - BindGroupEntry { - binding: bindings[1], - resource: BindingResource::TextureView(&self.0[1]), - }, - BindGroupEntry { - binding: bindings[2], - resource: BindingResource::TextureView(&self.0[2]), - }, - BindGroupEntry { - binding: bindings[3], - resource: BindingResource::TextureView(&self.0[3]), - }, - ] - } -} - -pub fn get_bindings( - prepass_textures: Option<&ViewPrepassTextures>, - fallback_images: &mut FallbackImageMsaa, - msaa: &Msaa, -) -> PrepassBindingsSet { - let depth_desc = TextureViewDescriptor { - label: Some("prepass_depth"), - aspect: TextureAspect::DepthOnly, - ..default() - }; - let depth_view = match prepass_textures.and_then(|x| x.depth.as_ref()) { - Some(texture) => texture.texture.create_view(&depth_desc), - None => fallback_images - .image_for_samplecount(msaa.samples(), CORE_3D_DEPTH_FORMAT) - .texture - .create_view(&depth_desc), - }; - - let normal_motion_vectors_fallback = &fallback_images - .image_for_samplecount(msaa.samples(), TextureFormat::bevy_default()) - .texture_view; - - let normal_view = match prepass_textures.and_then(|x| x.normal.as_ref()) { - Some(texture) => &texture.default_view, - None => normal_motion_vectors_fallback, - } - .clone(); - - let motion_vectors_view = match prepass_textures.and_then(|x| x.motion_vectors.as_ref()) { - Some(texture) => &texture.default_view, - None => normal_motion_vectors_fallback, - } - .clone(); - - let deferred_fallback = &fallback_images - .image_for_samplecount(1, TextureFormat::Rgba32Uint) - .texture_view; - - let deferred_view = match prepass_textures.and_then(|x| x.deferred.as_ref()) { - Some(texture) => &texture.default_view, - None => deferred_fallback, - } - .clone(); - - PrepassBindingsSet([depth_view, normal_view, motion_vectors_view, deferred_view]) -} - // Extract the render phases for the prepass pub fn extract_camera_previous_view_projection( mut commands: Commands, diff --git a/crates/bevy_pbr/src/prepass/prepass_bindings.rs b/crates/bevy_pbr/src/prepass/prepass_bindings.rs new file mode 100644 index 0000000000000..acbf80ceabde1 --- /dev/null +++ b/crates/bevy_pbr/src/prepass/prepass_bindings.rs @@ -0,0 +1,158 @@ +use bevy_core_pipeline::prepass::ViewPrepassTextures; +use bevy_render::render_resource::{ + BindGroupEntry, BindGroupLayoutEntry, BindingResource, BindingType, ShaderStages, + TextureAspect, TextureSampleType, TextureView, TextureViewDescriptor, TextureViewDimension, +}; +use bevy_utils::default; +use smallvec::SmallVec; + +use crate::MeshPipelineViewLayoutKey; + +pub fn get_bind_group_layout_entries( + bindings: [u32; 4], + layout_key: MeshPipelineViewLayoutKey, +) -> SmallVec<[BindGroupLayoutEntry; 4]> { + let mut result = SmallVec::<[BindGroupLayoutEntry; 4]>::new(); + + let multisampled = layout_key.contains(MeshPipelineViewLayoutKey::MULTISAMPLED); + + if layout_key.contains(MeshPipelineViewLayoutKey::DEPTH_PREPASS) { + result.push( + // Depth texture + BindGroupLayoutEntry { + binding: bindings[0], + visibility: ShaderStages::FRAGMENT, + ty: BindingType::Texture { + multisampled, + sample_type: TextureSampleType::Depth, + view_dimension: TextureViewDimension::D2, + }, + count: None, + }, + ); + } + + if layout_key.contains(MeshPipelineViewLayoutKey::NORMAL_PREPASS) { + result.push( + // Normal texture + BindGroupLayoutEntry { + binding: bindings[1], + visibility: ShaderStages::FRAGMENT, + ty: BindingType::Texture { + multisampled, + sample_type: TextureSampleType::Float { filterable: false }, + view_dimension: TextureViewDimension::D2, + }, + count: None, + }, + ); + } + + if layout_key.contains(MeshPipelineViewLayoutKey::MOTION_VECTOR_PREPASS) { + result.push( + // Motion Vectors texture + BindGroupLayoutEntry { + binding: bindings[2], + visibility: ShaderStages::FRAGMENT, + ty: BindingType::Texture { + multisampled, + sample_type: TextureSampleType::Float { filterable: false }, + view_dimension: TextureViewDimension::D2, + }, + count: None, + }, + ); + } + + if layout_key.contains(MeshPipelineViewLayoutKey::DEFERRED_PREPASS) { + result.push( + // Deferred texture + BindGroupLayoutEntry { + binding: bindings[3], + visibility: ShaderStages::FRAGMENT, + ty: BindingType::Texture { + multisampled: false, + sample_type: TextureSampleType::Uint, + view_dimension: TextureViewDimension::D2, + }, + count: None, + }, + ); + } + + result +} + +// Needed so the texture views can live long enough. +pub struct PrepassBindingsSet { + depth_view: Option, + normal_view: Option, + motion_vectors_view: Option, + deferred_view: Option, +} + +impl PrepassBindingsSet { + pub fn get_entries(&self, bindings: [u32; 4]) -> SmallVec<[BindGroupEntry; 4]> { + let mut result = SmallVec::<[BindGroupEntry; 4]>::new(); + + if let Some(ref depth_view) = self.depth_view { + result.push(BindGroupEntry { + binding: bindings[0], + resource: BindingResource::TextureView(depth_view), + }); + } + + if let Some(ref normal_view) = self.normal_view { + result.push(BindGroupEntry { + binding: bindings[1], + resource: BindingResource::TextureView(normal_view), + }); + } + + if let Some(ref motion_vectors_view) = self.motion_vectors_view { + result.push(BindGroupEntry { + binding: bindings[2], + resource: BindingResource::TextureView(motion_vectors_view), + }); + } + + if let Some(ref deferred_view) = self.deferred_view { + result.push(BindGroupEntry { + binding: bindings[3], + resource: BindingResource::TextureView(deferred_view), + }); + } + + result + } +} + +pub fn get_bindings(prepass_textures: Option<&ViewPrepassTextures>) -> PrepassBindingsSet { + let depth_desc = TextureViewDescriptor { + label: Some("prepass_depth"), + aspect: TextureAspect::DepthOnly, + ..default() + }; + let depth_view = prepass_textures + .and_then(|x| x.depth.as_ref()) + .map(|texture| texture.texture.create_view(&depth_desc)); + + let normal_view = prepass_textures + .and_then(|x| x.normal.as_ref()) + .map(|texture| texture.default_view.clone()); + + let motion_vectors_view = prepass_textures + .and_then(|x| x.motion_vectors.as_ref()) + .map(|texture| texture.default_view.clone()); + + let deferred_view = prepass_textures + .and_then(|x| x.deferred.as_ref()) + .map(|texture| texture.default_view.clone()); + + PrepassBindingsSet { + depth_view, + normal_view, + motion_vectors_view, + deferred_view, + } +} diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index 8d4fbb7b2f545..8f884e6bb980e 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -1,19 +1,15 @@ use crate::{ - environment_map, prepass, EnvironmentMapLight, FogMeta, GlobalLightMeta, GpuFog, GpuLights, - GpuPointLights, LightMeta, MaterialBindGroupId, NotShadowCaster, NotShadowReceiver, - PreviousGlobalTransform, ScreenSpaceAmbientOcclusionTextures, Shadow, ShadowSamplers, - ViewClusterBindings, ViewFogUniformOffset, ViewLightsUniformOffset, ViewShadowBindings, - CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT, MAX_CASCADES_PER_LIGHT, MAX_DIRECTIONAL_LIGHTS, + generate_view_layouts, prepare_mesh_view_bind_groups, MaterialBindGroupId, + MeshPipelineViewLayout, MeshPipelineViewLayoutKey, MeshViewBindGroup, NotShadowCaster, + NotShadowReceiver, PreviousGlobalTransform, Shadow, ViewFogUniformOffset, + ViewLightsUniformOffset, CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT, MAX_CASCADES_PER_LIGHT, + MAX_DIRECTIONAL_LIGHTS, }; use bevy_app::{Plugin, PostUpdate}; use bevy_asset::{load_internal_asset, AssetId, Handle}; use bevy_core_pipeline::{ core_3d::{AlphaMask3d, Opaque3d, Transparent3d, CORE_3D_DEPTH_FORMAT}, deferred::{AlphaMask3dDeferred, Opaque3dDeferred}, - prepass::ViewPrepassTextures, - tonemapping::{ - get_lut_bind_group_layout_entries, get_lut_bindings, Tonemapping, TonemappingLuts, - }, }; use bevy_derive::{Deref, DerefMut}; use bevy_ecs::{ @@ -27,21 +23,18 @@ use bevy_render::{ batch_and_prepare_render_phase, write_batched_instance_buffer, GetBatchData, NoAutomaticBatching, }, - globals::{GlobalsBuffer, GlobalsUniform}, mesh::{ GpuBufferInfo, InnerMeshVertexBufferLayout, Mesh, MeshVertexBufferLayout, VertexAttributeDescriptor, }, - prelude::Msaa, render_asset::RenderAssets, render_phase::{PhaseItem, RenderCommand, RenderCommandResult, TrackedRenderPass}, render_resource::*, renderer::{RenderDevice, RenderQueue}, texture::{ - BevyDefault, DefaultImageSampler, FallbackImageCubemap, FallbackImageMsaa, GpuImage, Image, - ImageSampler, TextureFormatPixelInfo, + BevyDefault, DefaultImageSampler, GpuImage, Image, ImageSampler, TextureFormatPixelInfo, }, - view::{ViewTarget, ViewUniform, ViewUniformOffset, ViewUniforms, ViewVisibility}, + view::{ViewTarget, ViewUniformOffset, ViewVisibility}, Extract, ExtractSchedule, Render, RenderApp, RenderSet, }; use bevy_transform::components::GlobalTransform; @@ -49,6 +42,15 @@ use bevy_utils::{tracing::error, EntityHashMap, HashMap, Hashed}; use std::cell::Cell; use thread_local::ThreadLocal; +#[cfg(debug_assertions)] +use bevy_utils::tracing::warn; + +#[cfg(debug_assertions)] +use std::sync::{ + atomic::{AtomicBool, Ordering}, + Arc, +}; + use crate::render::{ morph::{ extract_morphs, no_automatic_morph_batching, prepare_morphs, MorphIndices, MorphUniform, @@ -72,6 +74,17 @@ pub const MESH_SHADER_HANDLE: Handle = Handle::weak_from_u128(3252377289 pub const SKINNING_HANDLE: Handle = Handle::weak_from_u128(13215291596265391738); pub const MORPH_HANDLE: Handle = Handle::weak_from_u128(970982813587607345); +/// How many textures are allowed in the view bind group layout (`@group(0)`) before +/// broader compatibility with WebGL and WebGPU is at risk, due to the minimum guaranteed +/// values for `MAX_TEXTURE_IMAGE_UNITS` (in WebGL) and `maxSampledTexturesPerShaderStage` (in WebGPU), +/// currently both at 16. +/// +/// We use 10 here because it still leaves us, in a worst case scenario, with 6 textures for the other bind groups. +/// +/// See: +#[cfg(debug_assertions)] +pub const MESH_PIPELINE_VIEW_LAYOUT_SAFE_MAX_TEXTURES: usize = 10; + impl Plugin for MeshRenderPlugin { fn build(&self, app: &mut bevy_app::App) { load_internal_asset!(app, FORWARD_IO_HANDLE, "forward_io.wgsl", Shader::from_wgsl); @@ -319,8 +332,7 @@ pub fn extract_meshes( #[derive(Resource, Clone)] pub struct MeshPipeline { - pub view_layout: BindGroupLayout, - pub view_layout_multisampled: BindGroupLayout, + view_layouts: [MeshPipelineViewLayout; MeshPipelineViewLayoutKey::COUNT], // This dummy white texture is to be used in place of optional StandardMaterial textures pub dummy_white_gpu_image: GpuImage, pub clustered_forward_buffer_binding_type: BufferBindingType, @@ -337,6 +349,9 @@ pub struct MeshPipeline { /// ##endif // PER_OBJECT_BUFFER_BATCH_SIZE /// ``` pub per_object_buffer_batch_size: Option, + + #[cfg(debug_assertions)] + pub did_warn_about_too_many_textures: Arc, } impl FromWorld for MeshPipeline { @@ -350,185 +365,8 @@ impl FromWorld for MeshPipeline { let clustered_forward_buffer_binding_type = render_device .get_supported_read_only_binding_type(CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT); - /// Returns the appropriate bind group layout vec based on the parameters - fn layout_entries( - clustered_forward_buffer_binding_type: BufferBindingType, - multisampled: bool, - ) -> Vec { - let mut entries = vec![ - // View - BindGroupLayoutEntry { - binding: 0, - visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT, - ty: BindingType::Buffer { - ty: BufferBindingType::Uniform, - has_dynamic_offset: true, - min_binding_size: Some(ViewUniform::min_size()), - }, - count: None, - }, - // Lights - BindGroupLayoutEntry { - binding: 1, - visibility: ShaderStages::FRAGMENT, - ty: BindingType::Buffer { - ty: BufferBindingType::Uniform, - has_dynamic_offset: true, - min_binding_size: Some(GpuLights::min_size()), - }, - count: None, - }, - // Point Shadow Texture Cube Array - BindGroupLayoutEntry { - binding: 2, - visibility: ShaderStages::FRAGMENT, - ty: BindingType::Texture { - multisampled: false, - sample_type: TextureSampleType::Depth, - #[cfg(any(not(feature = "webgl"), not(target_arch = "wasm32")))] - view_dimension: TextureViewDimension::CubeArray, - #[cfg(all(feature = "webgl", target_arch = "wasm32"))] - view_dimension: TextureViewDimension::Cube, - }, - count: None, - }, - // Point Shadow Texture Array Sampler - BindGroupLayoutEntry { - binding: 3, - visibility: ShaderStages::FRAGMENT, - ty: BindingType::Sampler(SamplerBindingType::Comparison), - count: None, - }, - // Directional Shadow Texture Array - BindGroupLayoutEntry { - binding: 4, - visibility: ShaderStages::FRAGMENT, - ty: BindingType::Texture { - multisampled: false, - sample_type: TextureSampleType::Depth, - #[cfg(any(not(feature = "webgl"), not(target_arch = "wasm32")))] - view_dimension: TextureViewDimension::D2Array, - #[cfg(all(feature = "webgl", target_arch = "wasm32"))] - view_dimension: TextureViewDimension::D2, - }, - count: None, - }, - // Directional Shadow Texture Array Sampler - BindGroupLayoutEntry { - binding: 5, - visibility: ShaderStages::FRAGMENT, - ty: BindingType::Sampler(SamplerBindingType::Comparison), - count: None, - }, - // PointLights - BindGroupLayoutEntry { - binding: 6, - visibility: ShaderStages::FRAGMENT, - ty: BindingType::Buffer { - ty: clustered_forward_buffer_binding_type, - has_dynamic_offset: false, - min_binding_size: Some(GpuPointLights::min_size( - clustered_forward_buffer_binding_type, - )), - }, - count: None, - }, - // ClusteredLightIndexLists - BindGroupLayoutEntry { - binding: 7, - visibility: ShaderStages::FRAGMENT, - ty: BindingType::Buffer { - ty: clustered_forward_buffer_binding_type, - has_dynamic_offset: false, - min_binding_size: Some( - ViewClusterBindings::min_size_cluster_light_index_lists( - clustered_forward_buffer_binding_type, - ), - ), - }, - count: None, - }, - // ClusterOffsetsAndCounts - BindGroupLayoutEntry { - binding: 8, - visibility: ShaderStages::FRAGMENT, - ty: BindingType::Buffer { - ty: clustered_forward_buffer_binding_type, - has_dynamic_offset: false, - min_binding_size: Some( - ViewClusterBindings::min_size_cluster_offsets_and_counts( - clustered_forward_buffer_binding_type, - ), - ), - }, - count: None, - }, - // Globals - BindGroupLayoutEntry { - binding: 9, - visibility: ShaderStages::VERTEX_FRAGMENT, - ty: BindingType::Buffer { - ty: BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: Some(GlobalsUniform::min_size()), - }, - count: None, - }, - // Fog - BindGroupLayoutEntry { - binding: 10, - visibility: ShaderStages::FRAGMENT, - ty: BindingType::Buffer { - ty: BufferBindingType::Uniform, - has_dynamic_offset: true, - min_binding_size: Some(GpuFog::min_size()), - }, - count: None, - }, - // Screen space ambient occlusion texture - BindGroupLayoutEntry { - binding: 11, - visibility: ShaderStages::FRAGMENT, - ty: BindingType::Texture { - multisampled: false, - sample_type: TextureSampleType::Float { filterable: false }, - view_dimension: TextureViewDimension::D2, - }, - count: None, - }, - ]; - - // EnvironmentMapLight - let environment_map_entries = - environment_map::get_bind_group_layout_entries([12, 13, 14]); - entries.extend_from_slice(&environment_map_entries); - - // Tonemapping - let tonemapping_lut_entries = get_lut_bind_group_layout_entries([15, 16]); - entries.extend_from_slice(&tonemapping_lut_entries); - - if cfg!(any(not(feature = "webgl"), not(target_arch = "wasm32"))) - || (cfg!(all(feature = "webgl", target_arch = "wasm32")) && !multisampled) - { - entries.extend_from_slice(&prepass::get_bind_group_layout_entries( - [17, 18, 19, 20], - multisampled, - )); - } - - entries - } - - let view_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor { - label: Some("mesh_view_layout"), - entries: &layout_entries(clustered_forward_buffer_binding_type, false), - }); - - let view_layout_multisampled = - render_device.create_bind_group_layout(&BindGroupLayoutDescriptor { - label: Some("mesh_view_layout_multisampled"), - entries: &layout_entries(clustered_forward_buffer_binding_type, true), - }); + let view_layouts = + generate_view_layouts(&render_device, clustered_forward_buffer_binding_type); // A 1x1x1 'all 1.0' texture to use as a dummy texture to use in place of optional StandardMaterial textures let dummy_white_gpu_image = { @@ -571,12 +409,13 @@ impl FromWorld for MeshPipeline { }; MeshPipeline { - view_layout, - view_layout_multisampled, + view_layouts, clustered_forward_buffer_binding_type, dummy_white_gpu_image, mesh_layouts: MeshLayouts::new(&render_device), per_object_buffer_batch_size: GpuArrayBuffer::::batch_size(&render_device), + #[cfg(debug_assertions)] + did_warn_about_too_many_textures: Arc::new(AtomicBool::new(false)), } } } @@ -597,6 +436,24 @@ impl MeshPipeline { )) } } + + pub fn get_view_layout(&self, layout_key: MeshPipelineViewLayoutKey) -> &BindGroupLayout { + let index = layout_key.bits() as usize; + let layout = &self.view_layouts[index]; + + #[cfg(debug_assertions)] + if layout.texture_count > MESH_PIPELINE_VIEW_LAYOUT_SAFE_MAX_TEXTURES + && !self.did_warn_about_too_many_textures.load(Ordering::SeqCst) + { + self.did_warn_about_too_many_textures + .store(true, Ordering::SeqCst); + + // Issue our own warning here because Naga's error message is a bit cryptic in this situation + 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 + } } impl GetBatchData for MeshPipeline { @@ -807,12 +664,10 @@ impl SpecializedMeshPipeline for MeshPipeline { vertex_attributes.push(Mesh::ATTRIBUTE_COLOR.at_shader_location(5)); } - let mut bind_group_layout = match key.msaa_samples() { - 1 => vec![self.view_layout.clone()], - _ => { - shader_defs.push("MULTISAMPLED".into()); - vec![self.view_layout_multisampled.clone()] - } + let mut bind_group_layout = vec![self.get_view_layout(key.into()).clone()]; + + if key.msaa_samples() > 1 { + shader_defs.push("MULTISAMPLED".into()); }; bind_group_layout.push(setup_morph_and_skinning_defs( @@ -1088,173 +943,6 @@ pub fn prepare_mesh_bind_group( } } -#[derive(Component)] -pub struct MeshViewBindGroup { - pub value: BindGroup, -} - -#[allow(clippy::too_many_arguments)] -pub fn prepare_mesh_view_bind_groups( - mut commands: Commands, - render_device: Res, - mesh_pipeline: Res, - shadow_samplers: Res, - light_meta: Res, - global_light_meta: Res, - fog_meta: Res, - view_uniforms: Res, - views: Query<( - Entity, - &ViewShadowBindings, - &ViewClusterBindings, - Option<&ScreenSpaceAmbientOcclusionTextures>, - Option<&ViewPrepassTextures>, - Option<&EnvironmentMapLight>, - &Tonemapping, - )>, - (images, mut fallback_images, fallback_cubemap): ( - Res>, - FallbackImageMsaa, - Res, - ), - msaa: Res, - globals_buffer: Res, - tonemapping_luts: Res, -) { - if let ( - Some(view_binding), - Some(light_binding), - Some(point_light_binding), - Some(globals), - Some(fog_binding), - ) = ( - view_uniforms.uniforms.binding(), - light_meta.view_gpu_lights.binding(), - global_light_meta.gpu_point_lights.binding(), - globals_buffer.buffer.binding(), - fog_meta.gpu_fogs.binding(), - ) { - for ( - entity, - view_shadow_bindings, - view_cluster_bindings, - ssao_textures, - prepass_textures, - environment_map, - tonemapping, - ) in &views - { - let fallback_ssao = fallback_images - .image_for_samplecount(1, TextureFormat::bevy_default()) - .texture_view - .clone(); - - let layout = if msaa.samples() > 1 { - &mesh_pipeline.view_layout_multisampled - } else { - &mesh_pipeline.view_layout - }; - - let mut entries = vec![ - BindGroupEntry { - binding: 0, - resource: view_binding.clone(), - }, - BindGroupEntry { - binding: 1, - resource: light_binding.clone(), - }, - BindGroupEntry { - binding: 2, - resource: BindingResource::TextureView( - &view_shadow_bindings.point_light_depth_texture_view, - ), - }, - BindGroupEntry { - binding: 3, - resource: BindingResource::Sampler(&shadow_samplers.point_light_sampler), - }, - BindGroupEntry { - binding: 4, - resource: BindingResource::TextureView( - &view_shadow_bindings.directional_light_depth_texture_view, - ), - }, - BindGroupEntry { - binding: 5, - resource: BindingResource::Sampler(&shadow_samplers.directional_light_sampler), - }, - BindGroupEntry { - binding: 6, - resource: point_light_binding.clone(), - }, - BindGroupEntry { - binding: 7, - resource: view_cluster_bindings.light_index_lists_binding().unwrap(), - }, - BindGroupEntry { - binding: 8, - resource: view_cluster_bindings.offsets_and_counts_binding().unwrap(), - }, - BindGroupEntry { - binding: 9, - resource: globals.clone(), - }, - BindGroupEntry { - binding: 10, - resource: fog_binding.clone(), - }, - BindGroupEntry { - binding: 11, - resource: BindingResource::TextureView( - ssao_textures - .map(|t| &t.screen_space_ambient_occlusion_texture.default_view) - .unwrap_or(&fallback_ssao), - ), - }, - ]; - - let env_map = environment_map::get_bindings( - environment_map, - &images, - &fallback_cubemap, - [12, 13, 14], - ); - entries.extend_from_slice(&env_map); - - let tonemapping_luts = - get_lut_bindings(&images, &tonemapping_luts, tonemapping, [15, 16]); - entries.extend_from_slice(&tonemapping_luts); - - let label = Some("mesh_view_bind_group"); - - // When using WebGL, we can't have a depth texture with multisampling - if cfg!(any(not(feature = "webgl"), not(target_arch = "wasm32"))) - || (cfg!(all(feature = "webgl", target_arch = "wasm32")) && msaa.samples() == 1) - { - let prepass_bindings = - prepass::get_bindings(prepass_textures, &mut fallback_images, &msaa); - entries.extend_from_slice(&prepass_bindings.get_entries([17, 18, 19, 20])); - commands.entity(entity).insert(MeshViewBindGroup { - value: render_device.create_bind_group(&BindGroupDescriptor { - entries: &entries, - label, - layout, - }), - }); - } else { - commands.entity(entity).insert(MeshViewBindGroup { - value: render_device.create_bind_group(&BindGroupDescriptor { - entries: &entries, - label, - layout, - }), - }); - } - } - } -} - pub struct SetMeshViewBindGroup; impl RenderCommand

for SetMeshViewBindGroup { type Param = (); diff --git a/crates/bevy_pbr/src/render/mesh_view_bindings.rs b/crates/bevy_pbr/src/render/mesh_view_bindings.rs new file mode 100644 index 0000000000000..8ed474769525f --- /dev/null +++ b/crates/bevy_pbr/src/render/mesh_view_bindings.rs @@ -0,0 +1,503 @@ +use std::array; + +use bevy_core_pipeline::{ + prepass::ViewPrepassTextures, + tonemapping::{ + get_lut_bind_group_layout_entries, get_lut_bindings, Tonemapping, TonemappingLuts, + }, +}; +use bevy_ecs::{ + component::Component, + entity::Entity, + system::{Commands, Query, Res}, +}; +use bevy_render::{ + globals::{GlobalsBuffer, GlobalsUniform}, + render_asset::RenderAssets, + render_resource::{ + BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindGroupLayoutDescriptor, + BindGroupLayoutEntry, BindingResource, BindingType, BufferBindingType, SamplerBindingType, + ShaderStages, ShaderType, TextureFormat, TextureSampleType, TextureViewDimension, + }, + renderer::RenderDevice, + texture::{BevyDefault, FallbackImageCubemap, FallbackImageMsaa, Image}, + view::{Msaa, ViewUniform, ViewUniforms}, +}; + +use crate::{ + environment_map, prepass, EnvironmentMapLight, FogMeta, GlobalLightMeta, GpuFog, GpuLights, + GpuPointLights, LightMeta, MeshPipeline, MeshPipelineKey, ScreenSpaceAmbientOcclusionTextures, + ShadowSamplers, ViewClusterBindings, ViewShadowBindings, +}; + +#[derive(Clone)] +pub struct MeshPipelineViewLayout { + pub bind_group_layout: BindGroupLayout, + + #[cfg(debug_assertions)] + pub texture_count: usize, +} + +bitflags::bitflags! { + /// A key that uniquely identifies a [`MeshPipelineViewLayout`]. + /// + /// Used to generate all possible layouts for the mesh pipeline in [`generate_view_layouts`], + /// so special care must be taken to not add too many flags, as the number of possible layouts + /// will grow exponentially. + #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] + #[repr(transparent)] + pub struct MeshPipelineViewLayoutKey: u32 { + const MULTISAMPLED = (1 << 0); + const DEPTH_PREPASS = (1 << 1); + const NORMAL_PREPASS = (1 << 2); + const MOTION_VECTOR_PREPASS = (1 << 3); + const DEFERRED_PREPASS = (1 << 4); + } +} + +impl MeshPipelineViewLayoutKey { + // The number of possible layouts + pub const COUNT: usize = Self::all().bits() as usize + 1; + + /// Builds a unique label for each layout based on the flags + pub fn label(&self) -> String { + use MeshPipelineViewLayoutKey as Key; + + format!( + "mesh_view_layout{}{}{}{}{}", + self.contains(Key::MULTISAMPLED) + .then_some("_multisampled") + .unwrap_or_default(), + self.contains(Key::DEPTH_PREPASS) + .then_some("_depth") + .unwrap_or_default(), + self.contains(Key::NORMAL_PREPASS) + .then_some("_normal") + .unwrap_or_default(), + self.contains(Key::MOTION_VECTOR_PREPASS) + .then_some("_motion") + .unwrap_or_default(), + self.contains(Key::DEFERRED_PREPASS) + .then_some("_deferred") + .unwrap_or_default(), + ) + } +} + +impl From for MeshPipelineViewLayoutKey { + fn from(value: MeshPipelineKey) -> Self { + let mut result = MeshPipelineViewLayoutKey::empty(); + + if value.msaa_samples() > 1 { + result |= MeshPipelineViewLayoutKey::MULTISAMPLED; + } + if value.contains(MeshPipelineKey::DEPTH_PREPASS) { + result |= MeshPipelineViewLayoutKey::DEPTH_PREPASS; + } + if value.contains(MeshPipelineKey::NORMAL_PREPASS) { + result |= MeshPipelineViewLayoutKey::NORMAL_PREPASS; + } + if value.contains(MeshPipelineKey::MOTION_VECTOR_PREPASS) { + result |= MeshPipelineViewLayoutKey::MOTION_VECTOR_PREPASS; + } + if value.contains(MeshPipelineKey::DEFERRED_PREPASS) { + result |= MeshPipelineViewLayoutKey::DEFERRED_PREPASS; + } + + result + } +} + +impl From for MeshPipelineViewLayoutKey { + fn from(value: Msaa) -> Self { + let mut result = MeshPipelineViewLayoutKey::empty(); + + if value.samples() > 1 { + result |= MeshPipelineViewLayoutKey::MULTISAMPLED; + } + + result + } +} + +impl From> for MeshPipelineViewLayoutKey { + fn from(value: Option<&ViewPrepassTextures>) -> Self { + let mut result = MeshPipelineViewLayoutKey::empty(); + + if let Some(prepass_textures) = value { + if prepass_textures.depth.is_some() { + result |= MeshPipelineViewLayoutKey::DEPTH_PREPASS; + } + if prepass_textures.normal.is_some() { + result |= MeshPipelineViewLayoutKey::NORMAL_PREPASS; + } + if prepass_textures.motion_vectors.is_some() { + result |= MeshPipelineViewLayoutKey::MOTION_VECTOR_PREPASS; + } + if prepass_textures.deferred.is_some() { + result |= MeshPipelineViewLayoutKey::DEFERRED_PREPASS; + } + } + + result + } +} + +/// Returns the appropriate bind group layout vec based on the parameters +fn layout_entries( + clustered_forward_buffer_binding_type: BufferBindingType, + layout_key: MeshPipelineViewLayoutKey, +) -> Vec { + let mut entries = vec![ + // View + BindGroupLayoutEntry { + binding: 0, + visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT, + ty: BindingType::Buffer { + ty: BufferBindingType::Uniform, + has_dynamic_offset: true, + min_binding_size: Some(ViewUniform::min_size()), + }, + count: None, + }, + // Lights + BindGroupLayoutEntry { + binding: 1, + visibility: ShaderStages::FRAGMENT, + ty: BindingType::Buffer { + ty: BufferBindingType::Uniform, + has_dynamic_offset: true, + min_binding_size: Some(GpuLights::min_size()), + }, + count: None, + }, + // Point Shadow Texture Cube Array + BindGroupLayoutEntry { + binding: 2, + visibility: ShaderStages::FRAGMENT, + ty: BindingType::Texture { + multisampled: false, + sample_type: TextureSampleType::Depth, + #[cfg(any(not(feature = "webgl"), not(target_arch = "wasm32")))] + view_dimension: TextureViewDimension::CubeArray, + #[cfg(all(feature = "webgl", target_arch = "wasm32"))] + view_dimension: TextureViewDimension::Cube, + }, + count: None, + }, + // Point Shadow Texture Array Sampler + BindGroupLayoutEntry { + binding: 3, + visibility: ShaderStages::FRAGMENT, + ty: BindingType::Sampler(SamplerBindingType::Comparison), + count: None, + }, + // Directional Shadow Texture Array + BindGroupLayoutEntry { + binding: 4, + visibility: ShaderStages::FRAGMENT, + ty: BindingType::Texture { + multisampled: false, + sample_type: TextureSampleType::Depth, + #[cfg(any(not(feature = "webgl"), not(target_arch = "wasm32")))] + view_dimension: TextureViewDimension::D2Array, + #[cfg(all(feature = "webgl", target_arch = "wasm32"))] + view_dimension: TextureViewDimension::D2, + }, + count: None, + }, + // Directional Shadow Texture Array Sampler + BindGroupLayoutEntry { + binding: 5, + visibility: ShaderStages::FRAGMENT, + ty: BindingType::Sampler(SamplerBindingType::Comparison), + count: None, + }, + // PointLights + BindGroupLayoutEntry { + binding: 6, + visibility: ShaderStages::FRAGMENT, + ty: BindingType::Buffer { + ty: clustered_forward_buffer_binding_type, + has_dynamic_offset: false, + min_binding_size: Some(GpuPointLights::min_size( + clustered_forward_buffer_binding_type, + )), + }, + count: None, + }, + // ClusteredLightIndexLists + BindGroupLayoutEntry { + binding: 7, + visibility: ShaderStages::FRAGMENT, + ty: BindingType::Buffer { + ty: clustered_forward_buffer_binding_type, + has_dynamic_offset: false, + min_binding_size: Some(ViewClusterBindings::min_size_cluster_light_index_lists( + clustered_forward_buffer_binding_type, + )), + }, + count: None, + }, + // ClusterOffsetsAndCounts + BindGroupLayoutEntry { + binding: 8, + visibility: ShaderStages::FRAGMENT, + ty: BindingType::Buffer { + ty: clustered_forward_buffer_binding_type, + has_dynamic_offset: false, + min_binding_size: Some(ViewClusterBindings::min_size_cluster_offsets_and_counts( + clustered_forward_buffer_binding_type, + )), + }, + count: None, + }, + // Globals + BindGroupLayoutEntry { + binding: 9, + visibility: ShaderStages::VERTEX_FRAGMENT, + ty: BindingType::Buffer { + ty: BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: Some(GlobalsUniform::min_size()), + }, + count: None, + }, + // Fog + BindGroupLayoutEntry { + binding: 10, + visibility: ShaderStages::FRAGMENT, + ty: BindingType::Buffer { + ty: BufferBindingType::Uniform, + has_dynamic_offset: true, + min_binding_size: Some(GpuFog::min_size()), + }, + count: None, + }, + // Screen space ambient occlusion texture + BindGroupLayoutEntry { + binding: 11, + visibility: ShaderStages::FRAGMENT, + ty: BindingType::Texture { + multisampled: false, + sample_type: TextureSampleType::Float { filterable: false }, + view_dimension: TextureViewDimension::D2, + }, + count: None, + }, + ]; + + // EnvironmentMapLight + let environment_map_entries = environment_map::get_bind_group_layout_entries([12, 13, 14]); + entries.extend_from_slice(&environment_map_entries); + + // Tonemapping + let tonemapping_lut_entries = get_lut_bind_group_layout_entries([15, 16]); + entries.extend_from_slice(&tonemapping_lut_entries); + + if cfg!(any(not(feature = "webgl"), not(target_arch = "wasm32"))) + || (cfg!(all(feature = "webgl", target_arch = "wasm32")) + && !layout_key.contains(MeshPipelineViewLayoutKey::MULTISAMPLED)) + { + entries.extend_from_slice(&prepass::get_bind_group_layout_entries( + [17, 18, 19, 20], + layout_key, + )); + } + + entries +} + +/// Generates all possible view layouts for the mesh pipeline, based on all combinations of +/// [`MeshPipelineViewLayoutKey`] flags. +pub fn generate_view_layouts( + render_device: &RenderDevice, + clustered_forward_buffer_binding_type: BufferBindingType, +) -> [MeshPipelineViewLayout; MeshPipelineViewLayoutKey::COUNT] { + array::from_fn(|i| { + let key = MeshPipelineViewLayoutKey::from_bits_truncate(i as u32); + let entries = layout_entries(clustered_forward_buffer_binding_type, key); + + #[cfg(debug_assertions)] + let texture_count: usize = entries + .iter() + .filter(|entry| matches!(entry.ty, BindingType::Texture { .. })) + .count(); + + MeshPipelineViewLayout { + bind_group_layout: render_device.create_bind_group_layout(&BindGroupLayoutDescriptor { + label: Some(key.label().as_str()), + entries: &entries, + }), + #[cfg(debug_assertions)] + texture_count, + } + }) +} + +#[derive(Component)] +pub struct MeshViewBindGroup { + pub value: BindGroup, +} + +#[allow(clippy::too_many_arguments)] +pub fn prepare_mesh_view_bind_groups( + mut commands: Commands, + render_device: Res, + mesh_pipeline: Res, + shadow_samplers: Res, + light_meta: Res, + global_light_meta: Res, + fog_meta: Res, + view_uniforms: Res, + views: Query<( + Entity, + &ViewShadowBindings, + &ViewClusterBindings, + Option<&ScreenSpaceAmbientOcclusionTextures>, + Option<&ViewPrepassTextures>, + Option<&EnvironmentMapLight>, + &Tonemapping, + )>, + (images, mut fallback_images, fallback_cubemap): ( + Res>, + FallbackImageMsaa, + Res, + ), + msaa: Res, + globals_buffer: Res, + tonemapping_luts: Res, +) { + if let ( + Some(view_binding), + Some(light_binding), + Some(point_light_binding), + Some(globals), + Some(fog_binding), + ) = ( + view_uniforms.uniforms.binding(), + light_meta.view_gpu_lights.binding(), + global_light_meta.gpu_point_lights.binding(), + globals_buffer.buffer.binding(), + fog_meta.gpu_fogs.binding(), + ) { + for ( + entity, + view_shadow_bindings, + view_cluster_bindings, + ssao_textures, + prepass_textures, + environment_map, + tonemapping, + ) in &views + { + let fallback_ssao = fallback_images + .image_for_samplecount(1, TextureFormat::bevy_default()) + .texture_view + .clone(); + + let layout = &mesh_pipeline.get_view_layout( + MeshPipelineViewLayoutKey::from(*msaa) + | MeshPipelineViewLayoutKey::from(prepass_textures), + ); + + let mut entries = vec![ + BindGroupEntry { + binding: 0, + resource: view_binding.clone(), + }, + BindGroupEntry { + binding: 1, + resource: light_binding.clone(), + }, + BindGroupEntry { + binding: 2, + resource: BindingResource::TextureView( + &view_shadow_bindings.point_light_depth_texture_view, + ), + }, + BindGroupEntry { + binding: 3, + resource: BindingResource::Sampler(&shadow_samplers.point_light_sampler), + }, + BindGroupEntry { + binding: 4, + resource: BindingResource::TextureView( + &view_shadow_bindings.directional_light_depth_texture_view, + ), + }, + BindGroupEntry { + binding: 5, + resource: BindingResource::Sampler(&shadow_samplers.directional_light_sampler), + }, + BindGroupEntry { + binding: 6, + resource: point_light_binding.clone(), + }, + BindGroupEntry { + binding: 7, + resource: view_cluster_bindings.light_index_lists_binding().unwrap(), + }, + BindGroupEntry { + binding: 8, + resource: view_cluster_bindings.offsets_and_counts_binding().unwrap(), + }, + BindGroupEntry { + binding: 9, + resource: globals.clone(), + }, + BindGroupEntry { + binding: 10, + resource: fog_binding.clone(), + }, + BindGroupEntry { + binding: 11, + resource: BindingResource::TextureView( + ssao_textures + .map(|t| &t.screen_space_ambient_occlusion_texture.default_view) + .unwrap_or(&fallback_ssao), + ), + }, + ]; + + let env_map = environment_map::get_bindings( + environment_map, + &images, + &fallback_cubemap, + [12, 13, 14], + ); + entries.extend_from_slice(&env_map); + + let tonemapping_luts = + get_lut_bindings(&images, &tonemapping_luts, tonemapping, [15, 16]); + entries.extend_from_slice(&tonemapping_luts); + + let label = Some("mesh_view_bind_group"); + + // 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"))) + || (cfg!(all(feature = "webgl", target_arch = "wasm32")) && msaa.samples() == 1) + { + Some(prepass::get_bindings(prepass_textures)) + } else { + None + }; + + // This if statement is here to make the borrow checker happy. + // Ideally we could just have `entries.extend_from_slice(&prepass_bindings.get_entries([17, 18, 19, 20]));` + // in the existing if statement above, but that either doesn't allow `prepass_bindings` to live long enough, + // as its used when creating the bind group at the end of the function, or causes a `cannot move out of` error. + if let Some(prepass_bindings) = &prepass_bindings { + entries.extend_from_slice(&prepass_bindings.get_entries([17, 18, 19, 20])); + } + + commands.entity(entity).insert(MeshViewBindGroup { + value: render_device.create_bind_group(&BindGroupDescriptor { + entries: &entries, + label, + layout, + }), + }); + } + } +} diff --git a/crates/bevy_pbr/src/render/mesh_view_bindings.wgsl b/crates/bevy_pbr/src/render/mesh_view_bindings.wgsl index f3b51ddc50abb..6d68aa305a091 100644 --- a/crates/bevy_pbr/src/render/mesh_view_bindings.wgsl +++ b/crates/bevy_pbr/src/render/mesh_view_bindings.wgsl @@ -42,12 +42,31 @@ @group(0) @binding(16) var dt_lut_sampler: sampler; #ifdef MULTISAMPLED + +#ifdef DEPTH_PREPASS @group(0) @binding(17) var depth_prepass_texture: texture_depth_multisampled_2d; +#endif // DEPTH_PREPASS +#ifdef NORMAL_PREPASS @group(0) @binding(18) var normal_prepass_texture: texture_multisampled_2d; +#endif // NORMAL_PREPASS +#ifdef MOTION_VECTOR_PREPASS @group(0) @binding(19) var motion_vector_prepass_texture: texture_multisampled_2d; -#else +#endif // MOTION_VECTOR_PREPASS + +#else // MULTISAMPLED + +#ifdef DEPTH_PREPASS @group(0) @binding(17) var depth_prepass_texture: texture_depth_2d; +#endif // DEPTH_PREPASS +#ifdef NORMAL_PREPASS @group(0) @binding(18) var normal_prepass_texture: texture_2d; +#endif // NORMAL_PREPASS +#ifdef MOTION_VECTOR_PREPASS @group(0) @binding(19) var motion_vector_prepass_texture: texture_2d; +#endif // MOTION_VECTOR_PREPASS + +#endif // MULTISAMPLED + +#ifdef DEFERRED_PREPASS @group(0) @binding(20) var deferred_prepass_texture: texture_2d; -#endif +#endif // DEFERRED_PREPASS diff --git a/crates/bevy_pbr/src/render/mod.rs b/crates/bevy_pbr/src/render/mod.rs index b9d0d239c3874..7efffc05681f8 100644 --- a/crates/bevy_pbr/src/render/mod.rs +++ b/crates/bevy_pbr/src/render/mod.rs @@ -2,6 +2,7 @@ mod fog; mod light; pub(crate) mod mesh; mod mesh_bindings; +mod mesh_view_bindings; mod morph; mod skin; @@ -9,4 +10,5 @@ pub use fog::*; pub use light::*; pub use mesh::*; pub use mesh_bindings::MeshLayouts; +pub use mesh_view_bindings::*; pub use skin::{extract_skins, prepare_skins, SkinIndex, SkinUniform, MAX_JOINTS};