Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_pbr/src/meshlet/instance_manager.rs
6600 views
1
use super::{meshlet_mesh_manager::MeshletMeshManager, MeshletMesh, MeshletMesh3d};
2
use crate::DUMMY_MESH_MATERIAL;
3
use crate::{
4
meshlet::asset::MeshletAabb, MaterialBindingId, MeshFlags, MeshTransforms, MeshUniform,
5
PreviousGlobalTransform, RenderMaterialBindings, RenderMaterialInstances,
6
};
7
use bevy_asset::{AssetEvent, AssetServer, Assets, UntypedAssetId};
8
use bevy_camera::visibility::RenderLayers;
9
use bevy_ecs::{
10
entity::{Entities, Entity, EntityHashMap},
11
event::EventReader,
12
query::Has,
13
resource::Resource,
14
system::{Local, Query, Res, ResMut, SystemState},
15
};
16
use bevy_light::{NotShadowCaster, NotShadowReceiver};
17
use bevy_platform::collections::{HashMap, HashSet};
18
use bevy_render::{render_resource::StorageBuffer, sync_world::MainEntity, MainWorld};
19
use bevy_transform::components::GlobalTransform;
20
use core::ops::DerefMut;
21
22
/// Manages data for each entity with a [`MeshletMesh`].
23
#[derive(Resource)]
24
pub struct InstanceManager {
25
/// Amount of instances in the scene.
26
pub scene_instance_count: u32,
27
/// The max BVH depth of any instance in the scene. This is used to control the number of
28
/// dependent dispatches emitted for BVH traversal.
29
pub max_bvh_depth: u32,
30
31
/// Per-instance [`MainEntity`], [`RenderLayers`], and [`NotShadowCaster`].
32
pub instances: Vec<(MainEntity, RenderLayers, bool)>,
33
/// Per-instance [`MeshUniform`].
34
pub instance_uniforms: StorageBuffer<Vec<MeshUniform>>,
35
/// Per-instance model-space AABB.
36
pub instance_aabbs: StorageBuffer<Vec<MeshletAabb>>,
37
/// Per-instance material ID.
38
pub instance_material_ids: StorageBuffer<Vec<u32>>,
39
/// Per-instance index to the root node of the instance's BVH.
40
pub instance_bvh_root_nodes: StorageBuffer<Vec<u32>>,
41
/// Per-view per-instance visibility bit. Used for [`RenderLayers`] and [`NotShadowCaster`] support.
42
pub view_instance_visibility: EntityHashMap<StorageBuffer<Vec<u32>>>,
43
44
/// Next material ID available.
45
next_material_id: u32,
46
/// Map of material asset to material ID.
47
material_id_lookup: HashMap<UntypedAssetId, u32>,
48
/// Set of material IDs used in the scene.
49
material_ids_present_in_scene: HashSet<u32>,
50
}
51
52
impl InstanceManager {
53
pub fn new() -> Self {
54
Self {
55
scene_instance_count: 0,
56
max_bvh_depth: 0,
57
58
instances: Vec::new(),
59
instance_uniforms: {
60
let mut buffer = StorageBuffer::default();
61
buffer.set_label(Some("meshlet_instance_uniforms"));
62
buffer
63
},
64
instance_aabbs: {
65
let mut buffer = StorageBuffer::default();
66
buffer.set_label(Some("meshlet_instance_aabbs"));
67
buffer
68
},
69
instance_material_ids: {
70
let mut buffer = StorageBuffer::default();
71
buffer.set_label(Some("meshlet_instance_material_ids"));
72
buffer
73
},
74
instance_bvh_root_nodes: {
75
let mut buffer = StorageBuffer::default();
76
buffer.set_label(Some("meshlet_instance_bvh_root_nodes"));
77
buffer
78
},
79
view_instance_visibility: EntityHashMap::default(),
80
81
next_material_id: 0,
82
material_id_lookup: HashMap::default(),
83
material_ids_present_in_scene: HashSet::default(),
84
}
85
}
86
87
pub fn add_instance(
88
&mut self,
89
instance: MainEntity,
90
root_bvh_node: u32,
91
aabb: MeshletAabb,
92
bvh_depth: u32,
93
transform: &GlobalTransform,
94
previous_transform: Option<&PreviousGlobalTransform>,
95
render_layers: Option<&RenderLayers>,
96
mesh_material_ids: &RenderMaterialInstances,
97
render_material_bindings: &RenderMaterialBindings,
98
not_shadow_receiver: bool,
99
not_shadow_caster: bool,
100
) {
101
// Build a MeshUniform for the instance
102
let transform = transform.affine();
103
let previous_transform = previous_transform.map(|t| t.0).unwrap_or(transform);
104
let mut flags = if not_shadow_receiver {
105
MeshFlags::empty()
106
} else {
107
MeshFlags::SHADOW_RECEIVER
108
};
109
if transform.matrix3.determinant().is_sign_positive() {
110
flags |= MeshFlags::SIGN_DETERMINANT_MODEL_3X3;
111
}
112
let transforms = MeshTransforms {
113
world_from_local: (&transform).into(),
114
previous_world_from_local: (&previous_transform).into(),
115
flags: flags.bits(),
116
};
117
118
let mesh_material = mesh_material_ids.mesh_material(instance);
119
let mesh_material_binding_id = if mesh_material != DUMMY_MESH_MATERIAL.untyped() {
120
render_material_bindings
121
.get(&mesh_material)
122
.cloned()
123
.unwrap_or_default()
124
} else {
125
// Use a dummy binding ID if the mesh has no material
126
MaterialBindingId::default()
127
};
128
129
let mesh_uniform = MeshUniform::new(
130
&transforms,
131
0,
132
mesh_material_binding_id.slot,
133
None,
134
None,
135
None,
136
);
137
138
// Append instance data
139
self.instances.push((
140
instance,
141
render_layers.cloned().unwrap_or(RenderLayers::default()),
142
not_shadow_caster,
143
));
144
self.instance_uniforms.get_mut().push(mesh_uniform);
145
self.instance_aabbs.get_mut().push(aabb);
146
self.instance_material_ids.get_mut().push(0);
147
self.instance_bvh_root_nodes.get_mut().push(root_bvh_node);
148
149
self.scene_instance_count += 1;
150
self.max_bvh_depth = self.max_bvh_depth.max(bvh_depth);
151
}
152
153
/// Get the material ID for a [`crate::Material`].
154
pub fn get_material_id(&mut self, material_asset_id: UntypedAssetId) -> u32 {
155
*self
156
.material_id_lookup
157
.entry(material_asset_id)
158
.or_insert_with(|| {
159
self.next_material_id += 1;
160
self.next_material_id
161
})
162
}
163
164
pub fn material_present_in_scene(&self, material_id: &u32) -> bool {
165
self.material_ids_present_in_scene.contains(material_id)
166
}
167
168
pub fn reset(&mut self, entities: &Entities) {
169
self.scene_instance_count = 0;
170
self.max_bvh_depth = 0;
171
172
self.instances.clear();
173
self.instance_uniforms.get_mut().clear();
174
self.instance_aabbs.get_mut().clear();
175
self.instance_material_ids.get_mut().clear();
176
self.instance_bvh_root_nodes.get_mut().clear();
177
self.view_instance_visibility
178
.retain(|view_entity, _| entities.contains(*view_entity));
179
self.view_instance_visibility
180
.values_mut()
181
.for_each(|b| b.get_mut().clear());
182
183
self.next_material_id = 0;
184
self.material_id_lookup.clear();
185
self.material_ids_present_in_scene.clear();
186
}
187
}
188
189
pub fn extract_meshlet_mesh_entities(
190
mut meshlet_mesh_manager: ResMut<MeshletMeshManager>,
191
mut instance_manager: ResMut<InstanceManager>,
192
// TODO: Replace main_world and system_state when Extract<ResMut<Assets<MeshletMesh>>> is possible
193
mut main_world: ResMut<MainWorld>,
194
mesh_material_ids: Res<RenderMaterialInstances>,
195
render_material_bindings: Res<RenderMaterialBindings>,
196
mut system_state: Local<
197
Option<
198
SystemState<(
199
Query<(
200
Entity,
201
&MeshletMesh3d,
202
&GlobalTransform,
203
Option<&PreviousGlobalTransform>,
204
Option<&RenderLayers>,
205
Has<NotShadowReceiver>,
206
Has<NotShadowCaster>,
207
)>,
208
Res<AssetServer>,
209
ResMut<Assets<MeshletMesh>>,
210
EventReader<AssetEvent<MeshletMesh>>,
211
)>,
212
>,
213
>,
214
render_entities: &Entities,
215
) {
216
// Get instances query
217
if system_state.is_none() {
218
*system_state = Some(SystemState::new(&mut main_world));
219
}
220
let system_state = system_state.as_mut().unwrap();
221
let (instances_query, asset_server, mut assets, mut asset_events) =
222
system_state.get_mut(&mut main_world);
223
224
// Reset per-frame data
225
instance_manager.reset(render_entities);
226
227
// Free GPU buffer space for any modified or dropped MeshletMesh assets
228
for asset_event in asset_events.read() {
229
if let AssetEvent::Unused { id } | AssetEvent::Modified { id } = asset_event {
230
meshlet_mesh_manager.remove(id);
231
}
232
}
233
234
// Iterate over every instance
235
// TODO: Switch to change events to not upload every instance every frame.
236
for (
237
instance,
238
meshlet_mesh,
239
transform,
240
previous_transform,
241
render_layers,
242
not_shadow_receiver,
243
not_shadow_caster,
244
) in &instances_query
245
{
246
// Skip instances with an unloaded MeshletMesh asset
247
// TODO: This is a semi-expensive check
248
if asset_server.is_managed(meshlet_mesh.id())
249
&& !asset_server.is_loaded_with_dependencies(meshlet_mesh.id())
250
{
251
continue;
252
}
253
254
// Upload the instance's MeshletMesh asset data if not done already done
255
let (root_bvh_node, aabb, bvh_depth) =
256
meshlet_mesh_manager.queue_upload_if_needed(meshlet_mesh.id(), &mut assets);
257
258
// Add the instance's data to the instance manager
259
instance_manager.add_instance(
260
instance.into(),
261
root_bvh_node,
262
aabb,
263
bvh_depth,
264
transform,
265
previous_transform,
266
render_layers,
267
&mesh_material_ids,
268
&render_material_bindings,
269
not_shadow_receiver,
270
not_shadow_caster,
271
);
272
}
273
}
274
275
/// For each entity in the scene, record what material ID its material was assigned in the `prepare_material_meshlet_meshes` systems,
276
/// and note that the material is used by at least one entity in the scene.
277
pub fn queue_material_meshlet_meshes(
278
mut instance_manager: ResMut<InstanceManager>,
279
render_material_instances: Res<RenderMaterialInstances>,
280
) {
281
let instance_manager = instance_manager.deref_mut();
282
283
for (i, (instance, _, _)) in instance_manager.instances.iter().enumerate() {
284
if let Some(material_instance) = render_material_instances.instances.get(instance)
285
&& let Some(material_id) = instance_manager
286
.material_id_lookup
287
.get(&material_instance.asset_id)
288
{
289
instance_manager
290
.material_ids_present_in_scene
291
.insert(*material_id);
292
instance_manager.instance_material_ids.get_mut()[i] = *material_id;
293
}
294
}
295
}
296
297