Path: blob/main/crates/bevy_pbr/src/meshlet/instance_manager.rs
6600 views
use super::{meshlet_mesh_manager::MeshletMeshManager, MeshletMesh, MeshletMesh3d};1use crate::DUMMY_MESH_MATERIAL;2use crate::{3meshlet::asset::MeshletAabb, MaterialBindingId, MeshFlags, MeshTransforms, MeshUniform,4PreviousGlobalTransform, RenderMaterialBindings, RenderMaterialInstances,5};6use bevy_asset::{AssetEvent, AssetServer, Assets, UntypedAssetId};7use bevy_camera::visibility::RenderLayers;8use bevy_ecs::{9entity::{Entities, Entity, EntityHashMap},10event::EventReader,11query::Has,12resource::Resource,13system::{Local, Query, Res, ResMut, SystemState},14};15use bevy_light::{NotShadowCaster, NotShadowReceiver};16use bevy_platform::collections::{HashMap, HashSet};17use bevy_render::{render_resource::StorageBuffer, sync_world::MainEntity, MainWorld};18use bevy_transform::components::GlobalTransform;19use core::ops::DerefMut;2021/// Manages data for each entity with a [`MeshletMesh`].22#[derive(Resource)]23pub struct InstanceManager {24/// Amount of instances in the scene.25pub scene_instance_count: u32,26/// The max BVH depth of any instance in the scene. This is used to control the number of27/// dependent dispatches emitted for BVH traversal.28pub max_bvh_depth: u32,2930/// Per-instance [`MainEntity`], [`RenderLayers`], and [`NotShadowCaster`].31pub instances: Vec<(MainEntity, RenderLayers, bool)>,32/// Per-instance [`MeshUniform`].33pub instance_uniforms: StorageBuffer<Vec<MeshUniform>>,34/// Per-instance model-space AABB.35pub instance_aabbs: StorageBuffer<Vec<MeshletAabb>>,36/// Per-instance material ID.37pub instance_material_ids: StorageBuffer<Vec<u32>>,38/// Per-instance index to the root node of the instance's BVH.39pub instance_bvh_root_nodes: StorageBuffer<Vec<u32>>,40/// Per-view per-instance visibility bit. Used for [`RenderLayers`] and [`NotShadowCaster`] support.41pub view_instance_visibility: EntityHashMap<StorageBuffer<Vec<u32>>>,4243/// Next material ID available.44next_material_id: u32,45/// Map of material asset to material ID.46material_id_lookup: HashMap<UntypedAssetId, u32>,47/// Set of material IDs used in the scene.48material_ids_present_in_scene: HashSet<u32>,49}5051impl InstanceManager {52pub fn new() -> Self {53Self {54scene_instance_count: 0,55max_bvh_depth: 0,5657instances: Vec::new(),58instance_uniforms: {59let mut buffer = StorageBuffer::default();60buffer.set_label(Some("meshlet_instance_uniforms"));61buffer62},63instance_aabbs: {64let mut buffer = StorageBuffer::default();65buffer.set_label(Some("meshlet_instance_aabbs"));66buffer67},68instance_material_ids: {69let mut buffer = StorageBuffer::default();70buffer.set_label(Some("meshlet_instance_material_ids"));71buffer72},73instance_bvh_root_nodes: {74let mut buffer = StorageBuffer::default();75buffer.set_label(Some("meshlet_instance_bvh_root_nodes"));76buffer77},78view_instance_visibility: EntityHashMap::default(),7980next_material_id: 0,81material_id_lookup: HashMap::default(),82material_ids_present_in_scene: HashSet::default(),83}84}8586pub fn add_instance(87&mut self,88instance: MainEntity,89root_bvh_node: u32,90aabb: MeshletAabb,91bvh_depth: u32,92transform: &GlobalTransform,93previous_transform: Option<&PreviousGlobalTransform>,94render_layers: Option<&RenderLayers>,95mesh_material_ids: &RenderMaterialInstances,96render_material_bindings: &RenderMaterialBindings,97not_shadow_receiver: bool,98not_shadow_caster: bool,99) {100// Build a MeshUniform for the instance101let transform = transform.affine();102let previous_transform = previous_transform.map(|t| t.0).unwrap_or(transform);103let mut flags = if not_shadow_receiver {104MeshFlags::empty()105} else {106MeshFlags::SHADOW_RECEIVER107};108if transform.matrix3.determinant().is_sign_positive() {109flags |= MeshFlags::SIGN_DETERMINANT_MODEL_3X3;110}111let transforms = MeshTransforms {112world_from_local: (&transform).into(),113previous_world_from_local: (&previous_transform).into(),114flags: flags.bits(),115};116117let mesh_material = mesh_material_ids.mesh_material(instance);118let mesh_material_binding_id = if mesh_material != DUMMY_MESH_MATERIAL.untyped() {119render_material_bindings120.get(&mesh_material)121.cloned()122.unwrap_or_default()123} else {124// Use a dummy binding ID if the mesh has no material125MaterialBindingId::default()126};127128let mesh_uniform = MeshUniform::new(129&transforms,1300,131mesh_material_binding_id.slot,132None,133None,134None,135);136137// Append instance data138self.instances.push((139instance,140render_layers.cloned().unwrap_or(RenderLayers::default()),141not_shadow_caster,142));143self.instance_uniforms.get_mut().push(mesh_uniform);144self.instance_aabbs.get_mut().push(aabb);145self.instance_material_ids.get_mut().push(0);146self.instance_bvh_root_nodes.get_mut().push(root_bvh_node);147148self.scene_instance_count += 1;149self.max_bvh_depth = self.max_bvh_depth.max(bvh_depth);150}151152/// Get the material ID for a [`crate::Material`].153pub fn get_material_id(&mut self, material_asset_id: UntypedAssetId) -> u32 {154*self155.material_id_lookup156.entry(material_asset_id)157.or_insert_with(|| {158self.next_material_id += 1;159self.next_material_id160})161}162163pub fn material_present_in_scene(&self, material_id: &u32) -> bool {164self.material_ids_present_in_scene.contains(material_id)165}166167pub fn reset(&mut self, entities: &Entities) {168self.scene_instance_count = 0;169self.max_bvh_depth = 0;170171self.instances.clear();172self.instance_uniforms.get_mut().clear();173self.instance_aabbs.get_mut().clear();174self.instance_material_ids.get_mut().clear();175self.instance_bvh_root_nodes.get_mut().clear();176self.view_instance_visibility177.retain(|view_entity, _| entities.contains(*view_entity));178self.view_instance_visibility179.values_mut()180.for_each(|b| b.get_mut().clear());181182self.next_material_id = 0;183self.material_id_lookup.clear();184self.material_ids_present_in_scene.clear();185}186}187188pub fn extract_meshlet_mesh_entities(189mut meshlet_mesh_manager: ResMut<MeshletMeshManager>,190mut instance_manager: ResMut<InstanceManager>,191// TODO: Replace main_world and system_state when Extract<ResMut<Assets<MeshletMesh>>> is possible192mut main_world: ResMut<MainWorld>,193mesh_material_ids: Res<RenderMaterialInstances>,194render_material_bindings: Res<RenderMaterialBindings>,195mut system_state: Local<196Option<197SystemState<(198Query<(199Entity,200&MeshletMesh3d,201&GlobalTransform,202Option<&PreviousGlobalTransform>,203Option<&RenderLayers>,204Has<NotShadowReceiver>,205Has<NotShadowCaster>,206)>,207Res<AssetServer>,208ResMut<Assets<MeshletMesh>>,209EventReader<AssetEvent<MeshletMesh>>,210)>,211>,212>,213render_entities: &Entities,214) {215// Get instances query216if system_state.is_none() {217*system_state = Some(SystemState::new(&mut main_world));218}219let system_state = system_state.as_mut().unwrap();220let (instances_query, asset_server, mut assets, mut asset_events) =221system_state.get_mut(&mut main_world);222223// Reset per-frame data224instance_manager.reset(render_entities);225226// Free GPU buffer space for any modified or dropped MeshletMesh assets227for asset_event in asset_events.read() {228if let AssetEvent::Unused { id } | AssetEvent::Modified { id } = asset_event {229meshlet_mesh_manager.remove(id);230}231}232233// Iterate over every instance234// TODO: Switch to change events to not upload every instance every frame.235for (236instance,237meshlet_mesh,238transform,239previous_transform,240render_layers,241not_shadow_receiver,242not_shadow_caster,243) in &instances_query244{245// Skip instances with an unloaded MeshletMesh asset246// TODO: This is a semi-expensive check247if asset_server.is_managed(meshlet_mesh.id())248&& !asset_server.is_loaded_with_dependencies(meshlet_mesh.id())249{250continue;251}252253// Upload the instance's MeshletMesh asset data if not done already done254let (root_bvh_node, aabb, bvh_depth) =255meshlet_mesh_manager.queue_upload_if_needed(meshlet_mesh.id(), &mut assets);256257// Add the instance's data to the instance manager258instance_manager.add_instance(259instance.into(),260root_bvh_node,261aabb,262bvh_depth,263transform,264previous_transform,265render_layers,266&mesh_material_ids,267&render_material_bindings,268not_shadow_receiver,269not_shadow_caster,270);271}272}273274/// For each entity in the scene, record what material ID its material was assigned in the `prepare_material_meshlet_meshes` systems,275/// and note that the material is used by at least one entity in the scene.276pub fn queue_material_meshlet_meshes(277mut instance_manager: ResMut<InstanceManager>,278render_material_instances: Res<RenderMaterialInstances>,279) {280let instance_manager = instance_manager.deref_mut();281282for (i, (instance, _, _)) in instance_manager.instances.iter().enumerate() {283if let Some(material_instance) = render_material_instances.instances.get(instance)284&& let Some(material_id) = instance_manager285.material_id_lookup286.get(&material_instance.asset_id)287{288instance_manager289.material_ids_present_in_scene290.insert(*material_id);291instance_manager.instance_material_ids.get_mut()[i] = *material_id;292}293}294}295296297