Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_solari/src/scene/blas.rs
6596 views
1
use alloc::collections::VecDeque;
2
use bevy_asset::AssetId;
3
use bevy_ecs::{
4
resource::Resource,
5
system::{Res, ResMut},
6
};
7
use bevy_mesh::{Indices, Mesh};
8
use bevy_platform::collections::HashMap;
9
use bevy_render::{
10
mesh::{
11
allocator::{MeshAllocator, MeshBufferSlice},
12
RenderMesh,
13
},
14
render_asset::ExtractedAssets,
15
render_resource::*,
16
renderer::{RenderDevice, RenderQueue},
17
};
18
19
/// After compacting this many vertices worth of meshes per frame, no further BLAS will be compacted.
20
/// Lower this number to distribute the work across more frames.
21
const MAX_COMPACTION_VERTICES_PER_FRAME: u32 = 400_000;
22
23
#[derive(Resource, Default)]
24
pub struct BlasManager {
25
blas: HashMap<AssetId<Mesh>, Blas>,
26
compaction_queue: VecDeque<(AssetId<Mesh>, u32, bool)>,
27
}
28
29
impl BlasManager {
30
pub fn get(&self, mesh: &AssetId<Mesh>) -> Option<&Blas> {
31
self.blas.get(mesh)
32
}
33
}
34
35
pub fn prepare_raytracing_blas(
36
mut blas_manager: ResMut<BlasManager>,
37
extracted_meshes: Res<ExtractedAssets<RenderMesh>>,
38
mesh_allocator: Res<MeshAllocator>,
39
render_device: Res<RenderDevice>,
40
render_queue: Res<RenderQueue>,
41
) {
42
// Delete BLAS for deleted or modified meshes
43
for asset_id in extracted_meshes
44
.removed
45
.iter()
46
.chain(extracted_meshes.modified.iter())
47
{
48
blas_manager.blas.remove(asset_id);
49
}
50
51
if extracted_meshes.extracted.is_empty() {
52
return;
53
}
54
55
// Create new BLAS for added or changed meshes
56
let blas_resources = extracted_meshes
57
.extracted
58
.iter()
59
.filter(|(_, mesh)| is_mesh_raytracing_compatible(mesh))
60
.map(|(asset_id, _)| {
61
let vertex_slice = mesh_allocator.mesh_vertex_slice(asset_id).unwrap();
62
let index_slice = mesh_allocator.mesh_index_slice(asset_id).unwrap();
63
64
let (blas, blas_size) =
65
allocate_blas(&vertex_slice, &index_slice, asset_id, &render_device);
66
67
blas_manager.blas.insert(*asset_id, blas);
68
blas_manager
69
.compaction_queue
70
.push_back((*asset_id, blas_size.vertex_count, false));
71
72
(*asset_id, vertex_slice, index_slice, blas_size)
73
})
74
.collect::<Vec<_>>();
75
76
// Build geometry into each BLAS
77
let build_entries = blas_resources
78
.iter()
79
.map(|(asset_id, vertex_slice, index_slice, blas_size)| {
80
let geometry = BlasTriangleGeometry {
81
size: blas_size,
82
vertex_buffer: vertex_slice.buffer,
83
first_vertex: vertex_slice.range.start,
84
vertex_stride: 48,
85
index_buffer: Some(index_slice.buffer),
86
first_index: Some(index_slice.range.start),
87
transform_buffer: None,
88
transform_buffer_offset: None,
89
};
90
BlasBuildEntry {
91
blas: &blas_manager.blas[asset_id],
92
geometry: BlasGeometries::TriangleGeometries(vec![geometry]),
93
}
94
})
95
.collect::<Vec<_>>();
96
97
let mut command_encoder = render_device.create_command_encoder(&CommandEncoderDescriptor {
98
label: Some("build_blas_command_encoder"),
99
});
100
command_encoder.build_acceleration_structures(&build_entries, &[]);
101
render_queue.submit([command_encoder.finish()]);
102
}
103
104
pub fn compact_raytracing_blas(
105
mut blas_manager: ResMut<BlasManager>,
106
render_queue: Res<RenderQueue>,
107
) {
108
let queue_size = blas_manager.compaction_queue.len();
109
let mut meshes_processed = 0;
110
let mut vertices_compacted = 0;
111
112
while !blas_manager.compaction_queue.is_empty()
113
&& vertices_compacted < MAX_COMPACTION_VERTICES_PER_FRAME
114
&& meshes_processed < queue_size
115
{
116
meshes_processed += 1;
117
118
let (mesh, vertex_count, compaction_started) =
119
blas_manager.compaction_queue.pop_front().unwrap();
120
121
let Some(blas) = blas_manager.get(&mesh) else {
122
continue;
123
};
124
125
if !compaction_started {
126
blas.prepare_compaction_async(|_| {});
127
}
128
129
if blas.ready_for_compaction() {
130
let compacted_blas = render_queue.compact_blas(blas);
131
blas_manager.blas.insert(mesh, compacted_blas);
132
133
vertices_compacted += vertex_count;
134
continue;
135
}
136
137
// BLAS not ready for compaction, put back in queue
138
blas_manager
139
.compaction_queue
140
.push_back((mesh, vertex_count, true));
141
}
142
}
143
144
fn allocate_blas(
145
vertex_slice: &MeshBufferSlice,
146
index_slice: &MeshBufferSlice,
147
asset_id: &AssetId<Mesh>,
148
render_device: &RenderDevice,
149
) -> (Blas, BlasTriangleGeometrySizeDescriptor) {
150
let blas_size = BlasTriangleGeometrySizeDescriptor {
151
vertex_format: Mesh::ATTRIBUTE_POSITION.format,
152
vertex_count: vertex_slice.range.len() as u32,
153
index_format: Some(IndexFormat::Uint32),
154
index_count: Some(index_slice.range.len() as u32),
155
flags: AccelerationStructureGeometryFlags::OPAQUE,
156
};
157
158
let blas = render_device.wgpu_device().create_blas(
159
&CreateBlasDescriptor {
160
label: Some(&asset_id.to_string()),
161
flags: AccelerationStructureFlags::PREFER_FAST_TRACE
162
| AccelerationStructureFlags::ALLOW_COMPACTION,
163
update_mode: AccelerationStructureUpdateMode::Build,
164
},
165
BlasGeometrySizeDescriptors::Triangles {
166
descriptors: vec![blas_size.clone()],
167
},
168
);
169
170
(blas, blas_size)
171
}
172
173
fn is_mesh_raytracing_compatible(mesh: &Mesh) -> bool {
174
let triangle_list = mesh.primitive_topology() == PrimitiveTopology::TriangleList;
175
let vertex_attributes = mesh.attributes().map(|(attribute, _)| attribute.id).eq([
176
Mesh::ATTRIBUTE_POSITION.id,
177
Mesh::ATTRIBUTE_NORMAL.id,
178
Mesh::ATTRIBUTE_UV_0.id,
179
Mesh::ATTRIBUTE_TANGENT.id,
180
]);
181
let indexed_32 = matches!(mesh.indices(), Some(Indices::U32(..)));
182
mesh.enable_raytracing && triangle_list && vertex_attributes && indexed_32
183
}
184
185