-
-
Notifications
You must be signed in to change notification settings - Fork 4.1k
Solari BLAS compaction #20457
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Solari BLAS compaction #20457
Changes from all commits
076a659
4cce7c9
7d8cbca
41fae09
da1baf4
53df83a
9d42ade
a815ce5
1aeeceb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
use alloc::collections::VecDeque; | ||
use bevy_asset::AssetId; | ||
use bevy_ecs::{ | ||
resource::Resource, | ||
|
@@ -15,12 +16,19 @@ use bevy_render::{ | |
renderer::{RenderDevice, RenderQueue}, | ||
}; | ||
|
||
/// After compacting this many vertices worth of meshes per frame, no further BLAS will be compacted. | ||
/// Lower this number to distribute the work across more frames. | ||
const MAX_COMPACTION_VERTICES_PER_FRAME: u32 = 400_000; | ||
|
||
#[derive(Resource, Default)] | ||
pub struct BlasManager(HashMap<AssetId<Mesh>, Blas>); | ||
pub struct BlasManager { | ||
blas: HashMap<AssetId<Mesh>, Blas>, | ||
compaction_queue: VecDeque<(AssetId<Mesh>, u32, bool)>, | ||
} | ||
|
||
impl BlasManager { | ||
pub fn get(&self, mesh: &AssetId<Mesh>) -> Option<&Blas> { | ||
self.0.get(mesh) | ||
self.blas.get(mesh) | ||
} | ||
} | ||
|
||
|
@@ -31,15 +39,13 @@ pub fn prepare_raytracing_blas( | |
render_device: Res<RenderDevice>, | ||
render_queue: Res<RenderQueue>, | ||
) { | ||
let blas_manager = &mut blas_manager.0; | ||
|
||
// Delete BLAS for deleted or modified meshes | ||
for asset_id in extracted_meshes | ||
.removed | ||
.iter() | ||
.chain(extracted_meshes.modified.iter()) | ||
{ | ||
blas_manager.remove(asset_id); | ||
blas_manager.blas.remove(asset_id); | ||
} | ||
|
||
if extracted_meshes.extracted.is_empty() { | ||
|
@@ -58,7 +64,10 @@ pub fn prepare_raytracing_blas( | |
let (blas, blas_size) = | ||
allocate_blas(&vertex_slice, &index_slice, asset_id, &render_device); | ||
|
||
blas_manager.insert(*asset_id, blas); | ||
blas_manager.blas.insert(*asset_id, blas); | ||
blas_manager | ||
.compaction_queue | ||
.push_back((*asset_id, blas_size.vertex_count, false)); | ||
|
||
(*asset_id, vertex_slice, index_slice, blas_size) | ||
}) | ||
|
@@ -79,7 +88,7 @@ pub fn prepare_raytracing_blas( | |
transform_buffer_offset: None, | ||
}; | ||
BlasBuildEntry { | ||
blas: &blas_manager[asset_id], | ||
blas: &blas_manager.blas[asset_id], | ||
geometry: BlasGeometries::TriangleGeometries(vec![geometry]), | ||
} | ||
}) | ||
|
@@ -92,6 +101,48 @@ pub fn prepare_raytracing_blas( | |
render_queue.submit([command_encoder.finish()]); | ||
} | ||
|
||
pub fn compact_raytracing_blas( | ||
mut blas_manager: ResMut<BlasManager>, | ||
render_queue: Res<RenderQueue>, | ||
) { | ||
let mut first_mesh_processed = None; | ||
|
||
let mut vertices_compacted = 0; | ||
while vertices_compacted < MAX_COMPACTION_VERTICES_PER_FRAME | ||
&& let Some((mesh, vertex_count, compaction_started)) = | ||
blas_manager.compaction_queue.pop_front() | ||
{ | ||
// Stop iterating once we loop back around to the start of the list | ||
if Some(mesh) == first_mesh_processed { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if everything is already maximally compacted, this will iterate all entities every frame right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Once something is compacted, it stops getting added back to the compaction queue. So the list would be empty. |
||
break; | ||
} | ||
if first_mesh_processed.is_none() { | ||
first_mesh_processed = Some(mesh); | ||
} | ||
|
||
let Some(blas) = blas_manager.get(&mesh) else { | ||
continue; | ||
}; | ||
|
||
if !compaction_started { | ||
blas.prepare_compaction_async(|_| {}); | ||
} | ||
|
||
if blas.ready_for_compaction() { | ||
let compacted_blas = render_queue.compact_blas(blas); | ||
blas_manager.blas.insert(mesh, compacted_blas); | ||
|
||
vertices_compacted += vertex_count; | ||
continue; | ||
} | ||
|
||
// BLAS not ready for compaction, put back in queue | ||
blas_manager | ||
.compaction_queue | ||
.push_back((mesh, vertex_count, true)); | ||
} | ||
} | ||
|
||
fn allocate_blas( | ||
vertex_slice: &MeshBufferSlice, | ||
index_slice: &MeshBufferSlice, | ||
|
@@ -109,7 +160,8 @@ fn allocate_blas( | |
let blas = render_device.wgpu_device().create_blas( | ||
&CreateBlasDescriptor { | ||
label: Some(&asset_id.to_string()), | ||
flags: AccelerationStructureFlags::PREFER_FAST_TRACE, | ||
flags: AccelerationStructureFlags::PREFER_FAST_TRACE | ||
| AccelerationStructureFlags::ALLOW_COMPACTION, | ||
update_mode: AccelerationStructureUpdateMode::Build, | ||
}, | ||
BlasGeometrySizeDescriptors::Triangles { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this be a plugin setting or resource?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Eventually yes, for now nothing in solari is configurable. It's in the tracking issue.