Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_solari/src/scene/binder.rs
9395 views
1
use super::{blas::BlasManager, extract::StandardMaterialAssets, RaytracingMesh3d};
2
use bevy_asset::{AssetId, Handle};
3
use bevy_color::{ColorToComponents, LinearRgba};
4
use bevy_ecs::{
5
entity::{Entity, EntityHashMap},
6
resource::Resource,
7
system::{Query, Res, ResMut},
8
};
9
use bevy_math::{ops::cos, Mat4, Vec3};
10
use bevy_pbr::{
11
ExtractedDirectionalLight, MeshMaterial3d, PreviousGlobalTransform, StandardMaterial,
12
};
13
use bevy_platform::{collections::HashMap, hash::FixedHasher};
14
use bevy_render::{
15
mesh::allocator::MeshAllocator,
16
render_asset::RenderAssets,
17
render_resource::{binding_types::*, *},
18
renderer::{RenderDevice, RenderQueue},
19
texture::{FallbackImage, GpuImage},
20
};
21
use bevy_transform::components::GlobalTransform;
22
use core::{f32::consts::TAU, hash::Hash, num::NonZeroU32, ops::Deref};
23
24
const MAX_MESH_SLAB_COUNT: NonZeroU32 = NonZeroU32::new(500).unwrap();
25
const MAX_TEXTURE_COUNT: NonZeroU32 = NonZeroU32::new(5_000).unwrap();
26
27
const TEXTURE_MAP_NONE: u32 = u32::MAX;
28
const LIGHT_NOT_PRESENT_THIS_FRAME: u32 = u32::MAX;
29
30
#[derive(Resource)]
31
pub struct RaytracingSceneBindings {
32
pub bind_group: Option<BindGroup>,
33
pub bind_group_layout: BindGroupLayoutDescriptor,
34
previous_frame_light_entities: Vec<Entity>,
35
}
36
37
pub fn prepare_raytracing_scene_bindings(
38
instances_query: Query<(
39
Entity,
40
&RaytracingMesh3d,
41
&MeshMaterial3d<StandardMaterial>,
42
&GlobalTransform,
43
Option<&PreviousGlobalTransform>,
44
)>,
45
directional_lights_query: Query<(Entity, &ExtractedDirectionalLight)>,
46
mesh_allocator: Res<MeshAllocator>,
47
blas_manager: Res<BlasManager>,
48
material_assets: Res<StandardMaterialAssets>,
49
texture_assets: Res<RenderAssets<GpuImage>>,
50
fallback_texture: Res<FallbackImage>,
51
render_device: Res<RenderDevice>,
52
pipeline_cache: Res<PipelineCache>,
53
render_queue: Res<RenderQueue>,
54
mut raytracing_scene_bindings: ResMut<RaytracingSceneBindings>,
55
) {
56
raytracing_scene_bindings.bind_group = None;
57
58
let mut this_frame_entity_to_light_id = EntityHashMap::<u32>::default();
59
let previous_frame_light_entities: Vec<_> = raytracing_scene_bindings
60
.previous_frame_light_entities
61
.drain(..)
62
.collect();
63
64
if instances_query.iter().len() == 0 {
65
return;
66
}
67
68
let mut vertex_buffers = CachedBindingArray::new();
69
let mut index_buffers = CachedBindingArray::new();
70
let mut textures = CachedBindingArray::new();
71
let mut samplers = Vec::new();
72
let mut materials = StorageBufferList::<GpuMaterial>::default();
73
let mut tlas = render_device
74
.wgpu_device()
75
.create_tlas(&CreateTlasDescriptor {
76
label: Some("tlas"),
77
flags: AccelerationStructureFlags::PREFER_FAST_TRACE,
78
update_mode: AccelerationStructureUpdateMode::Build,
79
max_instances: instances_query.iter().len() as u32,
80
});
81
let mut transforms = StorageBufferList::<Mat4>::default();
82
let mut previous_frame_transforms = StorageBufferList::<Mat4>::default();
83
let mut geometry_ids = StorageBufferList::<GpuInstanceGeometryIds>::default();
84
let mut material_ids = StorageBufferList::<u32>::default();
85
let mut light_sources = StorageBufferList::<GpuLightSource>::default();
86
let mut directional_lights = StorageBufferList::<GpuDirectionalLight>::default();
87
let mut previous_frame_light_id_translations = StorageBufferList::<u32>::default();
88
89
let mut material_id_map: HashMap<AssetId<StandardMaterial>, u32, FixedHasher> =
90
HashMap::default();
91
let mut material_id = 0;
92
let mut process_texture = |texture_handle: &Option<Handle<_>>| -> Option<u32> {
93
match texture_handle {
94
Some(texture_handle) => match texture_assets.get(texture_handle.id()) {
95
Some(texture) => {
96
let (texture_id, is_new) =
97
textures.push_if_absent(texture.texture_view.deref(), texture_handle.id());
98
if is_new {
99
samplers.push(texture.sampler.deref());
100
}
101
Some(texture_id)
102
}
103
None => None,
104
},
105
None => Some(TEXTURE_MAP_NONE),
106
}
107
};
108
for (asset_id, material) in material_assets.iter() {
109
let Some(base_color_texture_id) = process_texture(&material.base_color_texture) else {
110
continue;
111
};
112
let Some(normal_map_texture_id) = process_texture(&material.normal_map_texture) else {
113
continue;
114
};
115
let Some(emissive_texture_id) = process_texture(&material.emissive_texture) else {
116
continue;
117
};
118
let Some(metallic_roughness_texture_id) =
119
process_texture(&material.metallic_roughness_texture)
120
else {
121
continue;
122
};
123
124
materials.get_mut().push(GpuMaterial {
125
normal_map_texture_id,
126
base_color_texture_id,
127
emissive_texture_id,
128
metallic_roughness_texture_id,
129
130
base_color: LinearRgba::from(material.base_color).to_vec3(),
131
perceptual_roughness: material.perceptual_roughness,
132
emissive: material.emissive.to_vec3(),
133
metallic: material.metallic,
134
reflectance: LinearRgba::from(material.specular_tint).to_vec3() * material.reflectance,
135
_padding: Default::default(),
136
});
137
138
material_id_map.insert(*asset_id, material_id);
139
material_id += 1;
140
}
141
142
if material_id == 0 {
143
return;
144
}
145
146
if textures.is_empty() {
147
textures.vec.push(fallback_texture.d2.texture_view.deref());
148
samplers.push(fallback_texture.d2.sampler.deref());
149
}
150
151
let mut instance_id = 0;
152
for (entity, mesh, material, transform, previous_frame_transform) in &instances_query {
153
let Some(blas) = blas_manager.get(&mesh.id()) else {
154
continue;
155
};
156
let Some(vertex_slice) = mesh_allocator.mesh_vertex_slice(&mesh.id()) else {
157
continue;
158
};
159
let Some(index_slice) = mesh_allocator.mesh_index_slice(&mesh.id()) else {
160
continue;
161
};
162
let Some(material_id) = material_id_map.get(&material.id()).copied() else {
163
continue;
164
};
165
let Some(material) = materials.get().get(material_id as usize) else {
166
continue;
167
};
168
169
let transform = transform.to_matrix();
170
*tlas.get_mut_single(instance_id).unwrap() = Some(TlasInstance::new(
171
blas,
172
tlas_transform(&transform),
173
Default::default(),
174
0xFF,
175
));
176
177
transforms.get_mut().push(transform);
178
previous_frame_transforms.get_mut().push(
179
previous_frame_transform
180
.map(|t| Mat4::from(t.0))
181
.unwrap_or(transform),
182
);
183
184
let (vertex_buffer_id, _) = vertex_buffers.push_if_absent(
185
vertex_slice.buffer.as_entire_buffer_binding(),
186
vertex_slice.buffer.id(),
187
);
188
let (index_buffer_id, _) = index_buffers.push_if_absent(
189
index_slice.buffer.as_entire_buffer_binding(),
190
index_slice.buffer.id(),
191
);
192
193
geometry_ids.get_mut().push(GpuInstanceGeometryIds {
194
vertex_buffer_id,
195
vertex_buffer_offset: vertex_slice.range.start,
196
index_buffer_id,
197
index_buffer_offset: index_slice.range.start,
198
triangle_count: (index_slice.range.len() / 3) as u32,
199
});
200
201
material_ids.get_mut().push(material_id);
202
203
if material.emissive != Vec3::ZERO {
204
light_sources
205
.get_mut()
206
.push(GpuLightSource::new_emissive_mesh_light(
207
instance_id as u32,
208
(index_slice.range.len() / 3) as u32,
209
));
210
211
this_frame_entity_to_light_id.insert(entity, light_sources.get().len() as u32 - 1);
212
raytracing_scene_bindings
213
.previous_frame_light_entities
214
.push(entity);
215
}
216
217
instance_id += 1;
218
}
219
220
if instance_id == 0 {
221
return;
222
}
223
224
for (entity, directional_light) in &directional_lights_query {
225
let directional_lights = directional_lights.get_mut();
226
let directional_light_id = directional_lights.len() as u32;
227
228
directional_lights.push(GpuDirectionalLight::new(directional_light));
229
230
light_sources
231
.get_mut()
232
.push(GpuLightSource::new_directional_light(directional_light_id));
233
234
this_frame_entity_to_light_id.insert(entity, light_sources.get().len() as u32 - 1);
235
raytracing_scene_bindings
236
.previous_frame_light_entities
237
.push(entity);
238
}
239
240
for previous_frame_light_entity in previous_frame_light_entities {
241
let current_frame_index = this_frame_entity_to_light_id
242
.get(&previous_frame_light_entity)
243
.copied()
244
.unwrap_or(LIGHT_NOT_PRESENT_THIS_FRAME);
245
previous_frame_light_id_translations
246
.get_mut()
247
.push(current_frame_index);
248
}
249
250
if light_sources.get().len() > u16::MAX as usize {
251
panic!("Too many light sources in the scene, maximum is 65536.");
252
}
253
254
materials.write_buffer(&render_device, &render_queue);
255
transforms.write_buffer(&render_device, &render_queue);
256
previous_frame_transforms.write_buffer(&render_device, &render_queue);
257
geometry_ids.write_buffer(&render_device, &render_queue);
258
material_ids.write_buffer(&render_device, &render_queue);
259
light_sources.write_buffer(&render_device, &render_queue);
260
directional_lights.write_buffer(&render_device, &render_queue);
261
previous_frame_light_id_translations.write_buffer(&render_device, &render_queue);
262
263
let mut command_encoder = render_device.create_command_encoder(&CommandEncoderDescriptor {
264
label: Some("build_tlas_command_encoder"),
265
});
266
command_encoder.build_acceleration_structures(&[], [&tlas]);
267
render_queue.submit([command_encoder.finish()]);
268
269
raytracing_scene_bindings.bind_group = Some(render_device.create_bind_group(
270
"raytracing_scene_bind_group",
271
&pipeline_cache.get_bind_group_layout(&raytracing_scene_bindings.bind_group_layout),
272
&BindGroupEntries::sequential((
273
vertex_buffers.as_slice(),
274
index_buffers.as_slice(),
275
textures.as_slice(),
276
samplers.as_slice(),
277
materials.binding().unwrap(),
278
tlas.as_binding(),
279
transforms.binding().unwrap(),
280
previous_frame_transforms.binding().unwrap(),
281
geometry_ids.binding().unwrap(),
282
material_ids.binding().unwrap(),
283
light_sources.binding().unwrap(),
284
directional_lights.binding().unwrap(),
285
previous_frame_light_id_translations.binding().unwrap(),
286
)),
287
));
288
}
289
290
impl RaytracingSceneBindings {
291
pub fn new() -> Self {
292
Self {
293
bind_group: None,
294
bind_group_layout: BindGroupLayoutDescriptor::new(
295
"raytracing_scene_bind_group_layout",
296
&BindGroupLayoutEntries::sequential(
297
ShaderStages::COMPUTE,
298
(
299
storage_buffer_read_only_sized(false, None).count(MAX_MESH_SLAB_COUNT),
300
storage_buffer_read_only_sized(false, None).count(MAX_MESH_SLAB_COUNT),
301
texture_2d(TextureSampleType::Float { filterable: true })
302
.count(MAX_TEXTURE_COUNT),
303
sampler(SamplerBindingType::Filtering).count(MAX_TEXTURE_COUNT),
304
storage_buffer_read_only_sized(false, None),
305
acceleration_structure(),
306
storage_buffer_read_only_sized(false, None),
307
storage_buffer_read_only_sized(false, None),
308
storage_buffer_read_only_sized(false, None),
309
storage_buffer_read_only_sized(false, None),
310
storage_buffer_read_only_sized(false, None),
311
storage_buffer_read_only_sized(false, None),
312
storage_buffer_read_only_sized(false, None),
313
),
314
),
315
),
316
previous_frame_light_entities: Vec::new(),
317
}
318
}
319
}
320
321
impl Default for RaytracingSceneBindings {
322
fn default() -> Self {
323
Self::new()
324
}
325
}
326
327
struct CachedBindingArray<T, I: Eq + Hash> {
328
map: HashMap<I, u32>,
329
vec: Vec<T>,
330
}
331
332
impl<T, I: Eq + Hash> CachedBindingArray<T, I> {
333
fn new() -> Self {
334
Self {
335
map: HashMap::default(),
336
vec: Vec::default(),
337
}
338
}
339
340
fn push_if_absent(&mut self, item: T, item_id: I) -> (u32, bool) {
341
let mut is_new = false;
342
let i = *self.map.entry(item_id).or_insert_with(|| {
343
is_new = true;
344
let i = self.vec.len() as u32;
345
self.vec.push(item);
346
i
347
});
348
(i, is_new)
349
}
350
351
fn is_empty(&self) -> bool {
352
self.vec.is_empty()
353
}
354
355
fn as_slice(&self) -> &[T] {
356
self.vec.as_slice()
357
}
358
}
359
360
type StorageBufferList<T> = StorageBuffer<Vec<T>>;
361
362
#[derive(ShaderType)]
363
struct GpuInstanceGeometryIds {
364
vertex_buffer_id: u32,
365
vertex_buffer_offset: u32,
366
index_buffer_id: u32,
367
index_buffer_offset: u32,
368
triangle_count: u32,
369
}
370
371
#[derive(ShaderType)]
372
struct GpuMaterial {
373
normal_map_texture_id: u32,
374
base_color_texture_id: u32,
375
emissive_texture_id: u32,
376
metallic_roughness_texture_id: u32,
377
378
base_color: Vec3,
379
perceptual_roughness: f32,
380
emissive: Vec3,
381
metallic: f32,
382
reflectance: Vec3,
383
_padding: f32,
384
}
385
386
#[derive(ShaderType)]
387
struct GpuLightSource {
388
kind: u32,
389
id: u32,
390
}
391
392
impl GpuLightSource {
393
fn new_emissive_mesh_light(instance_id: u32, triangle_count: u32) -> GpuLightSource {
394
if triangle_count > u16::MAX as u32 {
395
panic!("Too many triangles ({triangle_count}) in an emissive mesh, maximum is 65535.");
396
}
397
398
Self {
399
kind: triangle_count << 1,
400
id: instance_id,
401
}
402
}
403
404
fn new_directional_light(directional_light_id: u32) -> GpuLightSource {
405
Self {
406
kind: 1,
407
id: directional_light_id,
408
}
409
}
410
}
411
412
#[derive(ShaderType, Default)]
413
struct GpuDirectionalLight {
414
direction_to_light: Vec3,
415
cos_theta_max: f32,
416
luminance: Vec3,
417
inverse_pdf: f32,
418
}
419
420
impl GpuDirectionalLight {
421
fn new(directional_light: &ExtractedDirectionalLight) -> Self {
422
let cos_theta_max = cos(directional_light.sun_disk_angular_size / 2.0);
423
let solid_angle = TAU * (1.0 - cos_theta_max);
424
let luminance =
425
(directional_light.color.to_vec3() * directional_light.illuminance) / solid_angle;
426
427
Self {
428
direction_to_light: directional_light.transform.back().into(),
429
cos_theta_max,
430
luminance,
431
inverse_pdf: solid_angle,
432
}
433
}
434
}
435
436
fn tlas_transform(transform: &Mat4) -> [f32; 12] {
437
transform.transpose().to_cols_array()[..12]
438
.try_into()
439
.unwrap()
440
}
441
442