From 4737106bdd2f2087675981fcf82abad1f887caa1 Mon Sep 17 00:00:00 2001 From: IceSentry Date: Tue, 7 May 2024 02:46:41 -0400 Subject: [PATCH] Extract mesh view layouts logic (#13266) Copied almost verbatim from the volumetric fog PR # Objective - Managing mesh view layouts is complicated ## Solution - Extract it to it's own struct - This was done as part of #13057 and is copied almost verbatim. I wanted to keep this part of the PR it's own atomic commit in case we ever have to revert fog or run a bisect. This change is good whether or not we have volumetric fog. Co-Authored-By: @pcwalton --- crates/bevy_pbr/src/render/mesh.rs | 33 +++------ .../bevy_pbr/src/render/mesh_view_bindings.rs | 74 ++++++++++++++++++- 2 files changed, 80 insertions(+), 27 deletions(-) diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index 3c499be4410c3..aee311f082355 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -33,15 +33,13 @@ use bevy_render::{ texture::{BevyDefault, DefaultImageSampler, ImageSampler, TextureFormatPixelInfo}, view::{ prepare_view_targets, GpuCulling, RenderVisibilityRanges, ViewTarget, ViewUniformOffset, - ViewVisibility, VisibilityRange, VISIBILITY_RANGES_STORAGE_BUFFER_COUNT, + ViewVisibility, VisibilityRange, }, Extract, }; use bevy_transform::components::GlobalTransform; use bevy_utils::{tracing::error, tracing::warn, Entry, HashMap, Parallel}; -#[cfg(debug_assertions)] -use bevy_utils::warn_once; use bytemuck::{Pod, Zeroable}; use nonmax::{NonMaxU16, NonMaxU32}; use static_assertions::const_assert_eq; @@ -234,6 +232,7 @@ impl Plugin for MeshRenderPlugin { render_app .insert_resource(indirect_parameters_buffer) + .init_resource::() .init_resource::(); } @@ -1034,7 +1033,8 @@ fn collect_meshes_for_gpu_building( #[derive(Resource, Clone)] pub struct MeshPipeline { - view_layouts: [MeshPipelineViewLayout; MeshPipelineViewLayoutKey::COUNT], + /// A reference to all the mesh pipeline view layouts. + pub view_layouts: MeshPipelineViewLayouts, // 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, @@ -1065,18 +1065,12 @@ impl FromWorld for MeshPipeline { Res, Res, Res, + Res, )> = SystemState::new(world); - let (render_device, default_sampler, render_queue) = system_state.get_mut(world); + let (render_device, default_sampler, render_queue, view_layouts) = + system_state.get_mut(world); let clustered_forward_buffer_binding_type = render_device .get_supported_read_only_binding_type(CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT); - let visibility_ranges_buffer_binding_type = render_device - .get_supported_read_only_binding_type(VISIBILITY_RANGES_STORAGE_BUFFER_COUNT); - - let view_layouts = generate_view_layouts( - &render_device, - clustered_forward_buffer_binding_type, - visibility_ranges_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 = { @@ -1113,7 +1107,7 @@ impl FromWorld for MeshPipeline { }; MeshPipeline { - view_layouts, + view_layouts: view_layouts.clone(), clustered_forward_buffer_binding_type, dummy_white_gpu_image, mesh_layouts: MeshLayouts::new(&render_device), @@ -1141,16 +1135,7 @@ 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 { - // Issue our own warning here because Naga's error message is a bit cryptic in this situation - warn_once!("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 + self.view_layouts.get_view_layout(layout_key) } } diff --git a/crates/bevy_pbr/src/render/mesh_view_bindings.rs b/crates/bevy_pbr/src/render/mesh_view_bindings.rs index db2aa1831a6fa..e1f9ee573f1e6 100644 --- a/crates/bevy_pbr/src/render/mesh_view_bindings.rs +++ b/crates/bevy_pbr/src/render/mesh_view_bindings.rs @@ -1,4 +1,4 @@ -use std::{array, num::NonZeroU64}; +use std::{array, num::NonZeroU64, sync::Arc}; use bevy_core_pipeline::{ core_3d::ViewTransmissionTexture, @@ -7,10 +7,12 @@ use bevy_core_pipeline::{ get_lut_bind_group_layout_entries, get_lut_bindings, Tonemapping, TonemappingLuts, }, }; +use bevy_derive::{Deref, DerefMut}; use bevy_ecs::{ component::Component, entity::Entity, - system::{Commands, Query, Res}, + system::{Commands, Query, Res, Resource}, + world::{FromWorld, World}, }; use bevy_math::Vec4; use bevy_render::{ @@ -19,13 +21,19 @@ use bevy_render::{ render_resource::{binding_types::*, *}, renderer::RenderDevice, texture::{BevyDefault, FallbackImage, FallbackImageMsaa, FallbackImageZero, GpuImage}, - view::{Msaa, RenderVisibilityRanges, ViewUniform, ViewUniforms}, + view::{ + Msaa, RenderVisibilityRanges, ViewUniform, ViewUniforms, + VISIBILITY_RANGES_STORAGE_BUFFER_COUNT, + }, }; #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] use bevy_render::render_resource::binding_types::texture_cube; +use bevy_utils::warn_once; use environment_map::EnvironmentMapLight; +#[cfg(debug_assertions)] +use crate::MESH_PIPELINE_VIEW_LAYOUT_SAFE_MAX_TEXTURES; use crate::{ environment_map::{self, RenderViewEnvironmentMapBindGroupEntries}, irradiance_volume::{ @@ -35,6 +43,7 @@ use crate::{ prepass, FogMeta, GlobalLightMeta, GpuFog, GpuLights, GpuPointLights, LightMeta, LightProbesBuffer, LightProbesUniform, MeshPipeline, MeshPipelineKey, RenderViewLightProbes, ScreenSpaceAmbientOcclusionTextures, ShadowSamplers, ViewClusterBindings, ViewShadowBindings, + CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT, }; #[derive(Clone)] @@ -330,6 +339,65 @@ fn layout_entries( entries.to_vec() } +/// Stores the view layouts for every combination of pipeline keys. +/// +/// This is wrapped in an [`Arc`] so that it can be efficiently cloned and +/// placed inside specializable pipeline types. +#[derive(Resource, Clone, Deref, DerefMut)] +pub struct MeshPipelineViewLayouts( + pub Arc<[MeshPipelineViewLayout; MeshPipelineViewLayoutKey::COUNT]>, +); + +impl FromWorld for MeshPipelineViewLayouts { + fn from_world(world: &mut World) -> Self { + // Generates all possible view layouts for the mesh pipeline, based on all combinations of + // [`MeshPipelineViewLayoutKey`] flags. + + let render_device = world.resource::(); + + let clustered_forward_buffer_binding_type = render_device + .get_supported_read_only_binding_type(CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT); + let visibility_ranges_buffer_binding_type = render_device + .get_supported_read_only_binding_type(VISIBILITY_RANGES_STORAGE_BUFFER_COUNT); + + Self(Arc::new(array::from_fn(|i| { + let key = MeshPipelineViewLayoutKey::from_bits_truncate(i as u32); + let entries = layout_entries( + clustered_forward_buffer_binding_type, + visibility_ranges_buffer_binding_type, + key, + render_device, + ); + let texture_count: usize = entries + .iter() + .filter(|entry| matches!(entry.ty, BindingType::Texture { .. })) + .count(); + + MeshPipelineViewLayout { + bind_group_layout: render_device + .create_bind_group_layout(key.label().as_str(), &entries), + #[cfg(debug_assertions)] + texture_count, + } + }))) + } +} + +impl MeshPipelineViewLayouts { + pub fn get_view_layout(&self, layout_key: MeshPipelineViewLayoutKey) -> &BindGroupLayout { + let index = layout_key.bits() as usize; + let layout = &self[index]; + + #[cfg(debug_assertions)] + if layout.texture_count > MESH_PIPELINE_VIEW_LAYOUT_SAFE_MAX_TEXTURES { + // Issue our own warning here because Naga's error message is a bit cryptic in this situation + warn_once!("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 + } +} + /// Generates all possible view layouts for the mesh pipeline, based on all combinations of /// [`MeshPipelineViewLayoutKey`] flags. pub fn generate_view_layouts(