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
This commit is contained in:
IceSentry 2024-05-07 02:46:41 -04:00 committed by GitHub
parent 1126b5a3d6
commit 4737106bdd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 80 additions and 27 deletions

View File

@ -33,15 +33,13 @@ use bevy_render::{
texture::{BevyDefault, DefaultImageSampler, ImageSampler, TextureFormatPixelInfo}, texture::{BevyDefault, DefaultImageSampler, ImageSampler, TextureFormatPixelInfo},
view::{ view::{
prepare_view_targets, GpuCulling, RenderVisibilityRanges, ViewTarget, ViewUniformOffset, prepare_view_targets, GpuCulling, RenderVisibilityRanges, ViewTarget, ViewUniformOffset,
ViewVisibility, VisibilityRange, VISIBILITY_RANGES_STORAGE_BUFFER_COUNT, ViewVisibility, VisibilityRange,
}, },
Extract, Extract,
}; };
use bevy_transform::components::GlobalTransform; use bevy_transform::components::GlobalTransform;
use bevy_utils::{tracing::error, tracing::warn, Entry, HashMap, Parallel}; use bevy_utils::{tracing::error, tracing::warn, Entry, HashMap, Parallel};
#[cfg(debug_assertions)]
use bevy_utils::warn_once;
use bytemuck::{Pod, Zeroable}; use bytemuck::{Pod, Zeroable};
use nonmax::{NonMaxU16, NonMaxU32}; use nonmax::{NonMaxU16, NonMaxU32};
use static_assertions::const_assert_eq; use static_assertions::const_assert_eq;
@ -234,6 +232,7 @@ impl Plugin for MeshRenderPlugin {
render_app render_app
.insert_resource(indirect_parameters_buffer) .insert_resource(indirect_parameters_buffer)
.init_resource::<MeshPipelineViewLayouts>()
.init_resource::<MeshPipeline>(); .init_resource::<MeshPipeline>();
} }
@ -1034,7 +1033,8 @@ fn collect_meshes_for_gpu_building(
#[derive(Resource, Clone)] #[derive(Resource, Clone)]
pub struct MeshPipeline { 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 // This dummy white texture is to be used in place of optional StandardMaterial textures
pub dummy_white_gpu_image: GpuImage, pub dummy_white_gpu_image: GpuImage,
pub clustered_forward_buffer_binding_type: BufferBindingType, pub clustered_forward_buffer_binding_type: BufferBindingType,
@ -1065,18 +1065,12 @@ impl FromWorld for MeshPipeline {
Res<RenderDevice>, Res<RenderDevice>,
Res<DefaultImageSampler>, Res<DefaultImageSampler>,
Res<RenderQueue>, Res<RenderQueue>,
Res<MeshPipelineViewLayouts>,
)> = SystemState::new(world); )> = 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 let clustered_forward_buffer_binding_type = render_device
.get_supported_read_only_binding_type(CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT); .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 // 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 = { let dummy_white_gpu_image = {
@ -1113,7 +1107,7 @@ impl FromWorld for MeshPipeline {
}; };
MeshPipeline { MeshPipeline {
view_layouts, view_layouts: view_layouts.clone(),
clustered_forward_buffer_binding_type, clustered_forward_buffer_binding_type,
dummy_white_gpu_image, dummy_white_gpu_image,
mesh_layouts: MeshLayouts::new(&render_device), mesh_layouts: MeshLayouts::new(&render_device),
@ -1141,16 +1135,7 @@ impl MeshPipeline {
} }
pub fn get_view_layout(&self, layout_key: MeshPipelineViewLayoutKey) -> &BindGroupLayout { pub fn get_view_layout(&self, layout_key: MeshPipelineViewLayoutKey) -> &BindGroupLayout {
let index = layout_key.bits() as usize; self.view_layouts.get_view_layout(layout_key)
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
} }
} }

View File

@ -1,4 +1,4 @@
use std::{array, num::NonZeroU64}; use std::{array, num::NonZeroU64, sync::Arc};
use bevy_core_pipeline::{ use bevy_core_pipeline::{
core_3d::ViewTransmissionTexture, core_3d::ViewTransmissionTexture,
@ -7,10 +7,12 @@ use bevy_core_pipeline::{
get_lut_bind_group_layout_entries, get_lut_bindings, Tonemapping, TonemappingLuts, get_lut_bind_group_layout_entries, get_lut_bindings, Tonemapping, TonemappingLuts,
}, },
}; };
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{ use bevy_ecs::{
component::Component, component::Component,
entity::Entity, entity::Entity,
system::{Commands, Query, Res}, system::{Commands, Query, Res, Resource},
world::{FromWorld, World},
}; };
use bevy_math::Vec4; use bevy_math::Vec4;
use bevy_render::{ use bevy_render::{
@ -19,13 +21,19 @@ use bevy_render::{
render_resource::{binding_types::*, *}, render_resource::{binding_types::*, *},
renderer::RenderDevice, renderer::RenderDevice,
texture::{BevyDefault, FallbackImage, FallbackImageMsaa, FallbackImageZero, GpuImage}, 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")))] #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
use bevy_render::render_resource::binding_types::texture_cube; use bevy_render::render_resource::binding_types::texture_cube;
use bevy_utils::warn_once;
use environment_map::EnvironmentMapLight; use environment_map::EnvironmentMapLight;
#[cfg(debug_assertions)]
use crate::MESH_PIPELINE_VIEW_LAYOUT_SAFE_MAX_TEXTURES;
use crate::{ use crate::{
environment_map::{self, RenderViewEnvironmentMapBindGroupEntries}, environment_map::{self, RenderViewEnvironmentMapBindGroupEntries},
irradiance_volume::{ irradiance_volume::{
@ -35,6 +43,7 @@ use crate::{
prepass, FogMeta, GlobalLightMeta, GpuFog, GpuLights, GpuPointLights, LightMeta, prepass, FogMeta, GlobalLightMeta, GpuFog, GpuLights, GpuPointLights, LightMeta,
LightProbesBuffer, LightProbesUniform, MeshPipeline, MeshPipelineKey, RenderViewLightProbes, LightProbesBuffer, LightProbesUniform, MeshPipeline, MeshPipelineKey, RenderViewLightProbes,
ScreenSpaceAmbientOcclusionTextures, ShadowSamplers, ViewClusterBindings, ViewShadowBindings, ScreenSpaceAmbientOcclusionTextures, ShadowSamplers, ViewClusterBindings, ViewShadowBindings,
CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT,
}; };
#[derive(Clone)] #[derive(Clone)]
@ -330,6 +339,65 @@ fn layout_entries(
entries.to_vec() 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::<RenderDevice>();
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 /// Generates all possible view layouts for the mesh pipeline, based on all combinations of
/// [`MeshPipelineViewLayoutKey`] flags. /// [`MeshPipelineViewLayoutKey`] flags.
pub fn generate_view_layouts( pub fn generate_view_layouts(