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},
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::<MeshPipelineViewLayouts>()
.init_resource::<MeshPipeline>();
}
@ -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<RenderDevice>,
Res<DefaultImageSampler>,
Res<RenderQueue>,
Res<MeshPipelineViewLayouts>,
)> = 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)
}
}

View File

@ -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::<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
/// [`MeshPipelineViewLayoutKey`] flags.
pub fn generate_view_layouts(