Path: blob/main/crates/bevy_pbr/src/meshlet/material_pipeline_prepare.rs
6600 views
use super::{1instance_manager::InstanceManager, pipelines::MeshletPipelines,2resource_manager::ResourceManager,3};4use crate::*;5use bevy_camera::{Camera3d, Projection};6use bevy_core_pipeline::{7prepass::{DeferredPrepass, DepthPrepass, MotionVectorPrepass, NormalPrepass},8tonemapping::{DebandDither, Tonemapping},9};10use bevy_derive::{Deref, DerefMut};11use bevy_light::{EnvironmentMapLight, IrradianceVolume, ShadowFilteringMethod};12use bevy_mesh::VertexBufferLayout;13use bevy_mesh::{Mesh, MeshVertexBufferLayout, MeshVertexBufferLayoutRef, MeshVertexBufferLayouts};14use bevy_platform::collections::{HashMap, HashSet};15use bevy_render::erased_render_asset::ErasedRenderAssets;16use bevy_render::{camera::TemporalJitter, render_resource::*, view::ExtractedView};17use bevy_utils::default;18use core::any::{Any, TypeId};1920/// A list of `(Material ID, Pipeline, BindGroup)` for a view for use in [`super::MeshletMainOpaquePass3dNode`].21#[derive(Component, Deref, DerefMut, Default)]22pub struct MeshletViewMaterialsMainOpaquePass(pub Vec<(u32, CachedRenderPipelineId, BindGroup)>);2324/// Prepare [`Material`] pipelines for [`super::MeshletMesh`] entities for use in [`super::MeshletMainOpaquePass3dNode`],25/// and register the material with [`InstanceManager`].26pub fn prepare_material_meshlet_meshes_main_opaque_pass(27resource_manager: ResMut<ResourceManager>,28mut instance_manager: ResMut<InstanceManager>,29mut cache: Local<HashMap<(MeshPipelineKey, TypeId), CachedRenderPipelineId>>,30pipeline_cache: Res<PipelineCache>,31material_pipeline: Res<MaterialPipeline>,32mesh_pipeline: Res<MeshPipeline>,33render_materials: Res<ErasedRenderAssets<PreparedMaterial>>,34meshlet_pipelines: Res<MeshletPipelines>,35render_material_instances: Res<RenderMaterialInstances>,36material_bind_group_allocators: Res<MaterialBindGroupAllocators>,37mut mesh_vertex_buffer_layouts: ResMut<MeshVertexBufferLayouts>,38mut views: Query<39(40&mut MeshletViewMaterialsMainOpaquePass,41&ExtractedView,42Option<&Tonemapping>,43Option<&DebandDither>,44Option<&ShadowFilteringMethod>,45(Has<ScreenSpaceAmbientOcclusion>, Has<DistanceFog>),46(47Has<NormalPrepass>,48Has<DepthPrepass>,49Has<MotionVectorPrepass>,50Has<DeferredPrepass>,51),52Has<TemporalJitter>,53Option<&Projection>,54Has<RenderViewLightProbes<EnvironmentMapLight>>,55Has<RenderViewLightProbes<IrradianceVolume>>,56),57With<Camera3d>,58>,59) {60let fake_vertex_buffer_layout = &fake_vertex_buffer_layout(&mut mesh_vertex_buffer_layouts);6162for (63mut materials,64view,65tonemapping,66dither,67shadow_filter_method,68(ssao, distance_fog),69(normal_prepass, depth_prepass, motion_vector_prepass, deferred_prepass),70temporal_jitter,71projection,72has_environment_maps,73has_irradiance_volumes,74) in &mut views75{76let mut view_key =77MeshPipelineKey::from_msaa_samples(1) | MeshPipelineKey::from_hdr(view.hdr);7879if normal_prepass {80view_key |= MeshPipelineKey::NORMAL_PREPASS;81}82if depth_prepass {83view_key |= MeshPipelineKey::DEPTH_PREPASS;84}85if motion_vector_prepass {86view_key |= MeshPipelineKey::MOTION_VECTOR_PREPASS;87}88if deferred_prepass {89view_key |= MeshPipelineKey::DEFERRED_PREPASS;90}9192if temporal_jitter {93view_key |= MeshPipelineKey::TEMPORAL_JITTER;94}9596if has_environment_maps {97view_key |= MeshPipelineKey::ENVIRONMENT_MAP;98}99100if has_irradiance_volumes {101view_key |= MeshPipelineKey::IRRADIANCE_VOLUME;102}103104if let Some(projection) = projection {105view_key |= match projection {106Projection::Perspective(_) => MeshPipelineKey::VIEW_PROJECTION_PERSPECTIVE,107Projection::Orthographic(_) => MeshPipelineKey::VIEW_PROJECTION_ORTHOGRAPHIC,108Projection::Custom(_) => MeshPipelineKey::VIEW_PROJECTION_NONSTANDARD,109};110}111112match shadow_filter_method.unwrap_or(&ShadowFilteringMethod::default()) {113ShadowFilteringMethod::Hardware2x2 => {114view_key |= MeshPipelineKey::SHADOW_FILTER_METHOD_HARDWARE_2X2;115}116ShadowFilteringMethod::Gaussian => {117view_key |= MeshPipelineKey::SHADOW_FILTER_METHOD_GAUSSIAN;118}119ShadowFilteringMethod::Temporal => {120view_key |= MeshPipelineKey::SHADOW_FILTER_METHOD_TEMPORAL;121}122}123124if !view.hdr {125if let Some(tonemapping) = tonemapping {126view_key |= MeshPipelineKey::TONEMAP_IN_SHADER;127view_key |= tonemapping_pipeline_key(*tonemapping);128}129if let Some(DebandDither::Enabled) = dither {130view_key |= MeshPipelineKey::DEBAND_DITHER;131}132}133134if ssao {135view_key |= MeshPipelineKey::SCREEN_SPACE_AMBIENT_OCCLUSION;136}137if distance_fog {138view_key |= MeshPipelineKey::DISTANCE_FOG;139}140141view_key |= MeshPipelineKey::from_primitive_topology(PrimitiveTopology::TriangleList);142143for material_id in render_material_instances144.instances145.values()146.map(|instance| instance.asset_id)147.collect::<HashSet<_>>()148{149let Some(material) = render_materials.get(material_id) else {150continue;151};152153if material.properties.render_method != OpaqueRendererMethod::Forward154|| material.properties.alpha_mode != AlphaMode::Opaque155|| material.properties.reads_view_transmission_texture156{157continue;158}159160let erased_key = ErasedMaterialPipelineKey {161mesh_key: view_key,162material_key: material.properties.material_key.clone(),163type_id: material_id.type_id(),164};165let material_pipeline_specializer = MaterialPipelineSpecializer {166pipeline: material_pipeline.clone(),167properties: material.properties.clone(),168};169let Ok(material_pipeline_descriptor) =170material_pipeline_specializer.specialize(erased_key, fake_vertex_buffer_layout)171else {172continue;173};174let material_fragment = material_pipeline_descriptor.fragment.unwrap();175176let mut shader_defs = material_fragment.shader_defs;177shader_defs.push("MESHLET_MESH_MATERIAL_PASS".into());178179let layout = mesh_pipeline.get_view_layout(view_key.into());180let layout = vec![181layout.main_layout.clone(),182layout.binding_array_layout.clone(),183resource_manager.material_shade_bind_group_layout.clone(),184material185.properties186.material_layout187.as_ref()188.unwrap()189.clone(),190];191192let pipeline_descriptor = RenderPipelineDescriptor {193label: material_pipeline_descriptor.label,194layout,195push_constant_ranges: vec![],196vertex: VertexState {197shader: meshlet_pipelines.meshlet_mesh_material.clone(),198shader_defs: shader_defs.clone(),199entry_point: material_pipeline_descriptor.vertex.entry_point,200buffers: Vec::new(),201},202primitive: PrimitiveState::default(),203depth_stencil: Some(DepthStencilState {204format: TextureFormat::Depth16Unorm,205depth_write_enabled: false,206depth_compare: CompareFunction::Equal,207stencil: StencilState::default(),208bias: DepthBiasState::default(),209}),210multisample: MultisampleState::default(),211fragment: Some(FragmentState {212shader: match material.properties.get_shader(MeshletFragmentShader) {213Some(shader) => shader.clone(),214None => meshlet_pipelines.meshlet_mesh_material.clone(),215},216shader_defs,217entry_point: material_fragment.entry_point,218targets: material_fragment.targets,219}),220zero_initialize_workgroup_memory: false,221};222let type_id = material_id.type_id();223let Some(material_bind_group_allocator) = material_bind_group_allocators.get(&type_id)224else {225continue;226};227let material_id = instance_manager.get_material_id(material_id);228229let pipeline_id = *cache.entry((view_key, type_id)).or_insert_with(|| {230pipeline_cache.queue_render_pipeline(pipeline_descriptor.clone())231});232233let Some(material_bind_group) =234material_bind_group_allocator.get(material.binding.group)235else {236continue;237};238let Some(bind_group) = material_bind_group.bind_group() else {239continue;240};241242materials.push((material_id, pipeline_id, (*bind_group).clone()));243}244}245}246247/// A list of `(Material ID, Pipeline, BindGroup)` for a view for use in [`super::MeshletPrepassNode`].248#[derive(Component, Deref, DerefMut, Default)]249pub struct MeshletViewMaterialsPrepass(pub Vec<(u32, CachedRenderPipelineId, BindGroup)>);250251/// A list of `(Material ID, Pipeline, BindGroup)` for a view for use in [`super::MeshletDeferredGBufferPrepassNode`].252#[derive(Component, Deref, DerefMut, Default)]253pub struct MeshletViewMaterialsDeferredGBufferPrepass(254pub Vec<(u32, CachedRenderPipelineId, BindGroup)>,255);256257/// Prepare [`Material`] pipelines for [`super::MeshletMesh`] entities for use in [`super::MeshletPrepassNode`],258/// and [`super::MeshletDeferredGBufferPrepassNode`] and register the material with [`InstanceManager`].259pub fn prepare_material_meshlet_meshes_prepass(260resource_manager: ResMut<ResourceManager>,261mut instance_manager: ResMut<InstanceManager>,262mut cache: Local<HashMap<(MeshPipelineKey, TypeId), CachedRenderPipelineId>>,263pipeline_cache: Res<PipelineCache>,264prepass_pipeline: Res<PrepassPipeline>,265material_bind_group_allocators: Res<MaterialBindGroupAllocators>,266render_materials: Res<ErasedRenderAssets<PreparedMaterial>>,267meshlet_pipelines: Res<MeshletPipelines>,268render_material_instances: Res<RenderMaterialInstances>,269mut mesh_vertex_buffer_layouts: ResMut<MeshVertexBufferLayouts>,270mut views: Query<271(272&mut MeshletViewMaterialsPrepass,273&mut MeshletViewMaterialsDeferredGBufferPrepass,274&ExtractedView,275AnyOf<(&NormalPrepass, &MotionVectorPrepass, &DeferredPrepass)>,276),277With<Camera3d>,278>,279) {280let fake_vertex_buffer_layout = &fake_vertex_buffer_layout(&mut mesh_vertex_buffer_layouts);281282for (283mut materials,284mut deferred_materials,285view,286(normal_prepass, motion_vector_prepass, deferred_prepass),287) in &mut views288{289let mut view_key =290MeshPipelineKey::from_msaa_samples(1) | MeshPipelineKey::from_hdr(view.hdr);291292if normal_prepass.is_some() {293view_key |= MeshPipelineKey::NORMAL_PREPASS;294}295if motion_vector_prepass.is_some() {296view_key |= MeshPipelineKey::MOTION_VECTOR_PREPASS;297}298299view_key |= MeshPipelineKey::from_primitive_topology(PrimitiveTopology::TriangleList);300301for material_id in render_material_instances302.instances303.values()304.map(|instance| instance.asset_id)305.collect::<HashSet<_>>()306{307let Some(material) = render_materials.get(material_id) else {308continue;309};310let Some(material_bind_group_allocator) =311material_bind_group_allocators.get(&material_id.type_id())312else {313continue;314};315316if material.properties.alpha_mode != AlphaMode::Opaque317|| material.properties.reads_view_transmission_texture318{319continue;320}321322let material_wants_deferred = matches!(323material.properties.render_method,324OpaqueRendererMethod::Deferred325);326if deferred_prepass.is_some() && material_wants_deferred {327view_key |= MeshPipelineKey::DEFERRED_PREPASS;328} else if normal_prepass.is_none() && motion_vector_prepass.is_none() {329continue;330}331332let erased_key = ErasedMaterialPipelineKey {333mesh_key: view_key,334material_key: material.properties.material_key.clone(),335type_id: material_id.type_id(),336};337let material_pipeline_specializer = PrepassPipelineSpecializer {338pipeline: prepass_pipeline.clone(),339properties: material.properties.clone(),340};341let Ok(material_pipeline_descriptor) =342material_pipeline_specializer.specialize(erased_key, fake_vertex_buffer_layout)343else {344continue;345};346let material_fragment = material_pipeline_descriptor.fragment.unwrap();347348let mut shader_defs = material_fragment.shader_defs;349shader_defs.push("MESHLET_MESH_MATERIAL_PASS".into());350351let view_layout = if view_key.contains(MeshPipelineKey::MOTION_VECTOR_PREPASS) {352prepass_pipeline.view_layout_motion_vectors.clone()353} else {354prepass_pipeline.view_layout_no_motion_vectors.clone()355};356357let fragment_shader = if view_key.contains(MeshPipelineKey::DEFERRED_PREPASS) {358material359.properties360.get_shader(MeshletDeferredFragmentShader)361.unwrap_or(meshlet_pipelines.meshlet_mesh_material.clone())362} else {363material364.properties365.get_shader(MeshletPrepassFragmentShader)366.unwrap_or(meshlet_pipelines.meshlet_mesh_material.clone())367};368369let entry_point = if fragment_shader == meshlet_pipelines.meshlet_mesh_material {370material_fragment.entry_point.clone()371} else {372None373};374375let pipeline_descriptor = RenderPipelineDescriptor {376label: material_pipeline_descriptor.label,377layout: vec![378view_layout,379prepass_pipeline.empty_layout.clone(),380resource_manager.material_shade_bind_group_layout.clone(),381material382.properties383.material_layout384.as_ref()385.unwrap()386.clone(),387],388vertex: VertexState {389shader: meshlet_pipelines.meshlet_mesh_material.clone(),390shader_defs: shader_defs.clone(),391entry_point: material_pipeline_descriptor.vertex.entry_point,392..default()393},394primitive: PrimitiveState::default(),395depth_stencil: Some(DepthStencilState {396format: TextureFormat::Depth16Unorm,397depth_write_enabled: false,398depth_compare: CompareFunction::Equal,399stencil: StencilState::default(),400bias: DepthBiasState::default(),401}),402fragment: Some(FragmentState {403shader: fragment_shader,404shader_defs,405entry_point,406targets: material_fragment.targets,407}),408..default()409};410411let material_id = instance_manager.get_material_id(material_id);412413let pipeline_id = *cache414.entry((view_key, material_id.type_id()))415.or_insert_with(|| {416pipeline_cache.queue_render_pipeline(pipeline_descriptor.clone())417});418419let Some(material_bind_group) =420material_bind_group_allocator.get(material.binding.group)421else {422continue;423};424let Some(bind_group) = material_bind_group.bind_group() else {425continue;426};427428let item = (material_id, pipeline_id, (*bind_group).clone());429if view_key.contains(MeshPipelineKey::DEFERRED_PREPASS) {430deferred_materials.push(item);431} else {432materials.push(item);433}434}435}436}437438// Meshlet materials don't use a traditional vertex buffer, but the material specialization requires one.439fn fake_vertex_buffer_layout(layouts: &mut MeshVertexBufferLayouts) -> MeshVertexBufferLayoutRef {440layouts.insert(MeshVertexBufferLayout::new(441vec![442Mesh::ATTRIBUTE_POSITION.id,443Mesh::ATTRIBUTE_NORMAL.id,444Mesh::ATTRIBUTE_UV_0.id,445Mesh::ATTRIBUTE_TANGENT.id,446],447VertexBufferLayout {448array_stride: 48,449step_mode: VertexStepMode::Vertex,450attributes: vec![451VertexAttribute {452format: Mesh::ATTRIBUTE_POSITION.format,453offset: 0,454shader_location: 0,455},456VertexAttribute {457format: Mesh::ATTRIBUTE_NORMAL.format,458offset: 12,459shader_location: 1,460},461VertexAttribute {462format: Mesh::ATTRIBUTE_UV_0.format,463offset: 24,464shader_location: 2,465},466VertexAttribute {467format: Mesh::ATTRIBUTE_TANGENT.format,468offset: 32,469shader_location: 3,470},471],472},473))474}475476477