Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_pbr/src/cluster/mod.rs
30636 views
1
use core::{iter, num::NonZero};
2
3
use bevy_camera::Camera;
4
use bevy_ecs::{entity::EntityHashMap, prelude::*};
5
use bevy_light::{
6
cluster::{
7
ClusterableObjectCounts, ClusterableObjects, Clusters, GlobalClusterGpuSettings,
8
GlobalClusterSettings,
9
},
10
ClusteredDecal, EnvironmentMapLight, IrradianceVolume, PointLight, SpotLight,
11
};
12
use bevy_math::{uvec4, UVec3, UVec4, Vec4};
13
use bevy_render::{
14
render_resource::{
15
BindingResource, BufferBindingType, BufferUsages, DownlevelFlags, RawBufferVec, ShaderSize,
16
ShaderType, StorageBuffer, UniformBuffer,
17
},
18
renderer::{RenderAdapter, RenderDevice, RenderQueue},
19
sync_world::{MainEntity, RenderEntity},
20
Extract,
21
};
22
use bytemuck::{Pod, Zeroable};
23
use tracing::{error, info, trace, warn};
24
25
use crate::{MeshPipeline, RenderViewLightProbes};
26
27
pub(crate) mod gpu;
28
29
// NOTE: this must be kept in sync with the same constants in
30
// `mesh_view_types.wgsl`.
31
pub const MAX_UNIFORM_BUFFER_CLUSTERABLE_OBJECTS: usize = 204;
32
// Make sure that the clusterable object buffer doesn't overflow the maximum
33
// size of a UBO on WebGL 2.
34
const _: () =
35
assert!(size_of::<GpuClusteredLight>() * MAX_UNIFORM_BUFFER_CLUSTERABLE_OBJECTS <= 16384);
36
37
// NOTE: Clustered-forward rendering requires 3 storage buffer bindings so check that
38
// at least that many are supported using this constant and SupportedBindingType::from_device()
39
pub const CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT: u32 = 3;
40
41
// this must match CLUSTER_COUNT_SIZE in pbr.wgsl
42
// and must be large enough to contain MAX_UNIFORM_BUFFER_CLUSTERABLE_OBJECTS
43
const CLUSTER_COUNT_SIZE: u32 = 9;
44
45
const CLUSTER_OFFSET_MASK: u32 = (1 << (32 - (CLUSTER_COUNT_SIZE * 2))) - 1;
46
const CLUSTER_COUNT_MASK: u32 = (1 << CLUSTER_COUNT_SIZE) - 1;
47
48
/// The initial capacity of the Z slice list.
49
///
50
/// The application can override this by setting
51
/// [`GlobalClusterGpuSettings::initial_z_slice_list_capacity`].
52
pub const GPU_CLUSTERING_INITIAL_Z_SLICE_LIST_CAPACITY: usize = 1024;
53
54
/// The initial capacity of the clustered object index list.
55
///
56
/// The application can override this by setting
57
/// [`GlobalClusterGpuSettings::initial_index_list_capacity`].
58
pub const GPU_CLUSTERING_INITIAL_INDEX_LIST_CAPACITY: usize = 65536;
59
60
/// Creates the default [`GlobalClusterSettings`] resource.
61
pub(crate) fn make_global_cluster_settings(world: &World) -> GlobalClusterSettings {
62
let device = world.resource::<RenderDevice>();
63
let adapter = world.resource::<RenderAdapter>();
64
let clustered_decals_are_usable =
65
crate::decal::clustered::clustered_decals_are_usable(device, adapter);
66
let supports_storage_buffers = matches!(
67
device.get_supported_read_only_binding_type(CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT),
68
BufferBindingType::Storage { .. }
69
);
70
71
// We need to support compute shaders to use GPU clustering. To deal with
72
// the `WGPU_SETTINGS_PRIO="webgl2"` environment setting, we check the
73
// `RenderDevice` limits in addition to the `RenderAdapter`.
74
//
75
// Some android devices report the capabilities and limits wrong, so we can't rely on them.
76
// See <https://github.com/bevyengine/bevy/issues/23208> for Android issues
77
//
78
// GPU clustering doesn't work properly on iOS simulator. See https://github.com/bevyengine/bevy/issues/23428
79
let gpu_clustering_supported = !(cfg!(target_os = "android") || cfg!(target_abi = "sim"))
80
&& adapter
81
.get_downlevel_capabilities()
82
.flags
83
.contains(DownlevelFlags::COMPUTE_SHADERS)
84
&& device.limits().max_storage_buffers_per_shader_stage > 0;
85
86
let gpu_clustering = if gpu_clustering_supported {
87
info!("GPU clustering is supported on this device.");
88
Some(GlobalClusterGpuSettings {
89
initial_z_slice_list_capacity: GPU_CLUSTERING_INITIAL_Z_SLICE_LIST_CAPACITY,
90
initial_index_list_capacity: GPU_CLUSTERING_INITIAL_INDEX_LIST_CAPACITY,
91
})
92
} else {
93
info!("GPU clustering isn't supported on this device; falling back to CPU clustering.");
94
None
95
};
96
97
GlobalClusterSettings {
98
supports_storage_buffers,
99
clustered_decals_are_usable,
100
gpu_clustering,
101
max_uniform_buffer_clusterable_objects: MAX_UNIFORM_BUFFER_CLUSTERABLE_OBJECTS,
102
view_cluster_bindings_max_indices: ViewClusterBindings::MAX_INDICES,
103
}
104
}
105
106
/// The GPU-side structure that stores information about a clustered light
107
/// (point or spot).
108
///
109
/// This is *not* used for other clustered objects, such as light probes.
110
#[derive(Copy, Clone, ShaderType, Default, Pod, Zeroable, Debug)]
111
#[repr(C)]
112
pub struct GpuClusteredLight {
113
// For point lights: the lower-right 2x2 values of the projection matrix [2][2] [2][3] [3][2] [3][3]
114
// For spot lights: 2 components of the direction (x,z), spot_scale and spot_offset
115
pub(crate) light_custom_data: Vec4,
116
pub(crate) color_inverse_square_range: Vec4,
117
pub(crate) position_radius: Vec4,
118
pub(crate) flags: u32,
119
pub(crate) shadow_depth_bias: f32,
120
pub(crate) shadow_normal_bias: f32,
121
pub(crate) spot_light_tan_angle: f32,
122
pub(crate) soft_shadow_size: f32,
123
pub(crate) shadow_map_near_z: f32,
124
/// The decal applied to this light.
125
///
126
/// Note that this is separate from clustered decals. Clustered decals have
127
/// their own structures and don't use [`GpuClusteredLight`].
128
pub(crate) decal_index: u32,
129
/// The radius of the range that the light affects, used for clustering.
130
pub(crate) range: f32,
131
}
132
133
/// Contains information about clusterable objects in the scene that's global:
134
/// i.e. not specific to any view.
135
#[derive(Resource)]
136
pub struct GlobalClusterableObjectMeta {
137
/// GPU buffers that hold data about the clustered lights.
138
///
139
/// This is only for lights. Data about other clusterable objects are stored
140
/// in other buffers.
141
pub gpu_clustered_lights: GpuClusteredLights,
142
143
/// Maps a *render-world* entity to the index in the appropriate list.
144
///
145
/// Only clusterable objects that have render-world entities are in this
146
/// list! In particular, light probes (reflection probes and irradiance
147
/// volumes) are not.
148
pub entity_to_index: EntityHashMap<usize>,
149
}
150
151
/// GPU buffers that hold data about the clustered lights.
152
///
153
/// This is only for lights. Data about other clusterable objects are stored in
154
/// other buffers.
155
///
156
/// This has two variants in order to handle platforms in which storage buffers
157
/// aren't available.
158
pub struct GpuClusteredLights {
159
data: RawBufferVec<GpuClusteredLight>,
160
is_storage_buffer: bool,
161
}
162
163
#[derive(Component)]
164
pub struct ExtractedClusterConfig {
165
/// Special near value for cluster calculations
166
pub(crate) near: f32,
167
pub(crate) far: f32,
168
/// Number of clusters in `X` / `Y` / `Z` in the view frustum
169
pub(crate) dimensions: UVec3,
170
}
171
172
impl<'a> From<&'a Clusters> for ExtractedClusterConfig {
173
fn from(clusters: &'a Clusters) -> Self {
174
Self {
175
near: clusters.near,
176
far: clusters.far,
177
dimensions: clusters.dimensions,
178
}
179
}
180
}
181
182
/// A single command in the stream that [`extract_clusters_for_cpu_clustering`]
183
/// produces.
184
enum ExtractedClusterableObjectElement {
185
/// Marks the beginning of a new cluster.
186
ClusterHeader(ClusterableObjectCounts),
187
/// Represents a light.
188
///
189
/// The given entity is the render-world entity.
190
Light(Entity),
191
/// Represents a reflection probe.
192
///
193
/// The given entity is the main-world entity of the light probe, as light
194
/// probes don't have render world entities.
195
ReflectionProbe(MainEntity),
196
/// Represents an irradiance volume.
197
///
198
/// The given entity is the main-world entity of the light probe, as light
199
/// probes don't have render world entities.
200
IrradianceVolume(MainEntity),
201
/// Represents a clustered decal.
202
///
203
/// The given entity is the render-world entity.
204
Decal(Entity),
205
}
206
207
#[derive(Component)]
208
pub struct ExtractedClusterableObjects {
209
data: Vec<ExtractedClusterableObjectElement>,
210
}
211
212
#[derive(ShaderType)]
213
struct GpuClusterOffsetsAndCountsUniform {
214
data: Box<[UVec4; ViewClusterBindings::MAX_UNIFORM_ITEMS]>,
215
}
216
217
#[derive(ShaderType, Default)]
218
struct GpuClusterableObjectIndexListsStorage {
219
#[shader(size(runtime))]
220
data: Vec<u32>,
221
}
222
223
#[derive(ShaderType, Default)]
224
struct GpuClusterOffsetsAndCountsStorage {
225
/// The starting offset, followed by the number of point lights, spot
226
/// lights, reflection probes, and irradiance volumes in each cluster, in
227
/// that order. The remaining fields are filled with zeroes.
228
#[shader(size(runtime))]
229
data: Vec<GpuClusterOffsetAndCounts>,
230
}
231
232
/// The type we use for the offset and counts for each cluster.
233
type GpuClusterOffsetAndCounts = [UVec4; 2];
234
235
enum ViewClusterBuffers {
236
Uniform {
237
// NOTE: UVec4 is because all arrays in Std140 layout have 16-byte alignment
238
clusterable_object_index_lists: UniformBuffer<GpuClusterableObjectIndexListsUniform>,
239
// NOTE: UVec4 is because all arrays in Std140 layout have 16-byte alignment
240
cluster_offsets_and_counts: UniformBuffer<GpuClusterOffsetsAndCountsUniform>,
241
},
242
Storage {
243
clusterable_object_index_lists: StorageBuffer<GpuClusterableObjectIndexListsStorage>,
244
cluster_offsets_and_counts: StorageBuffer<GpuClusterOffsetsAndCountsStorage>,
245
},
246
}
247
248
#[derive(Component)]
249
pub struct ViewClusterBindings {
250
n_indices: usize,
251
n_offsets: usize,
252
buffers: ViewClusterBuffers,
253
}
254
255
pub fn init_global_clusterable_object_meta(
256
mut commands: Commands,
257
render_device: Res<RenderDevice>,
258
) {
259
commands.insert_resource(GlobalClusterableObjectMeta::new(
260
render_device.get_supported_read_only_binding_type(CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT),
261
));
262
}
263
264
impl GlobalClusterableObjectMeta {
265
pub fn new(buffer_binding_type: BufferBindingType) -> Self {
266
Self {
267
gpu_clustered_lights: GpuClusteredLights::new(buffer_binding_type),
268
entity_to_index: EntityHashMap::default(),
269
}
270
}
271
}
272
273
impl GpuClusteredLights {
274
fn new(buffer_binding_type: BufferBindingType) -> Self {
275
match buffer_binding_type {
276
BufferBindingType::Storage { .. } => Self::storage(),
277
BufferBindingType::Uniform => Self::uniform(),
278
}
279
}
280
281
fn uniform() -> Self {
282
GpuClusteredLights {
283
data: RawBufferVec::new(BufferUsages::UNIFORM),
284
is_storage_buffer: false,
285
}
286
}
287
288
fn storage() -> Self {
289
GpuClusteredLights {
290
data: RawBufferVec::new(BufferUsages::STORAGE),
291
is_storage_buffer: true,
292
}
293
}
294
295
pub(crate) fn clear(&mut self) {
296
self.data.clear();
297
}
298
299
pub(crate) fn len(&self) -> usize {
300
self.data.len()
301
}
302
303
pub(crate) fn add(&mut self, light: GpuClusteredLight) {
304
if self.is_storage_buffer || self.data.len() < MAX_UNIFORM_BUFFER_CLUSTERABLE_OBJECTS {
305
self.data.push(light);
306
}
307
}
308
309
pub(crate) fn write_buffer(
310
&mut self,
311
render_device: &RenderDevice,
312
render_queue: &RenderQueue,
313
) {
314
if self.is_storage_buffer {
315
if self.data.is_empty() {
316
self.data.push(GpuClusteredLight::default());
317
}
318
} else {
319
while self.data.len() < MAX_UNIFORM_BUFFER_CLUSTERABLE_OBJECTS {
320
self.data.push(GpuClusteredLight::default());
321
}
322
}
323
324
self.data.write_buffer(render_device, render_queue);
325
}
326
327
pub fn binding(&self) -> Option<BindingResource<'_>> {
328
self.data.binding()
329
}
330
331
pub fn min_size(buffer_binding_type: BufferBindingType) -> NonZero<u64> {
332
match buffer_binding_type {
333
BufferBindingType::Storage { .. } => GpuClusteredLight::min_size(),
334
BufferBindingType::Uniform => NonZero::try_from(
335
u64::from(GpuClusteredLight::min_size())
336
* MAX_UNIFORM_BUFFER_CLUSTERABLE_OBJECTS as u64,
337
)
338
.unwrap(),
339
}
340
}
341
342
pub fn max_clustered_lights(&self) -> Option<usize> {
343
if self.is_storage_buffer {
344
None
345
} else {
346
Some(MAX_UNIFORM_BUFFER_CLUSTERABLE_OBJECTS)
347
}
348
}
349
}
350
351
/// A shortcut for testing the type of a clusterable object.
352
type ClusterExtractionMapperQueryFlags = (
353
Has<PointLight>,
354
Has<SpotLight>,
355
Has<EnvironmentMapLight>,
356
Has<IrradianceVolume>,
357
Has<ClusteredDecal>,
358
);
359
/// A shortcut for testing whether an entity is any type of clusterable object.
360
type ClusterExtractionMapperQueryFilter = Or<(
361
With<PointLight>,
362
With<SpotLight>,
363
With<EnvironmentMapLight>,
364
With<IrradianceVolume>,
365
With<ClusteredDecal>,
366
)>;
367
368
/// A run condition that tests whether GPU clustering is enabled.
369
///
370
/// This is the version for use in extraction systems.
371
pub fn gpu_clustering_is_enabled_during_extraction(
372
global_cluster_settings: Extract<Res<GlobalClusterSettings>>,
373
) -> bool {
374
global_cluster_settings.gpu_clustering.is_some()
375
}
376
377
/// A run condition that tests whether GPU clustering is enabled.
378
///
379
/// This is the version for use in non-extraction systems.
380
pub fn gpu_clustering_is_enabled(global_cluster_settings: Res<GlobalClusterSettings>) -> bool {
381
global_cluster_settings.gpu_clustering.is_some()
382
}
383
384
/// Extracts the clusters that the CPU produced into the render world.
385
pub fn extract_clusters_for_cpu_clustering(
386
mut commands: Commands,
387
views: Extract<Query<(RenderEntity, &Clusters, &Camera)>>,
388
mapper: Extract<
389
Query<
390
(Option<&RenderEntity>, ClusterExtractionMapperQueryFlags),
391
ClusterExtractionMapperQueryFilter,
392
>,
393
>,
394
global_cluster_settings: Extract<Res<GlobalClusterSettings>>,
395
) {
396
for (entity, clusters, camera) in &views {
397
let mut entity_commands = commands
398
.get_entity(entity)
399
.expect("Clusters entity wasn't synced.");
400
if !camera.is_active {
401
entity_commands.remove::<(ExtractedClusterableObjects, ExtractedClusterConfig)>();
402
continue;
403
}
404
405
let clusterable_objects = match clusters.clusterable_objects {
406
ClusterableObjects::Cpu(ref cpu_clusterable_objects) => cpu_clusterable_objects,
407
ClusterableObjects::Gpu => {
408
error!("Clusterable objects must have been in CPU mode if doing CPU clustering");
409
continue;
410
}
411
};
412
413
let mut data = vec![];
414
for cluster_objects in clusterable_objects {
415
data.push(ExtractedClusterableObjectElement::ClusterHeader(
416
cluster_objects.counts,
417
));
418
for clusterable_entity in cluster_objects.iter() {
419
let Ok((
420
maybe_render_entity,
421
(
422
is_point_light,
423
is_spot_light,
424
is_reflection_probe,
425
is_irradiance_volume,
426
is_clustered_decal,
427
),
428
)) = mapper.get(*clusterable_entity)
429
else {
430
error!(
431
"Couldn't find clustered object {:?} in the main world",
432
clusterable_entity
433
);
434
continue;
435
};
436
437
if let Some(render_entity) = maybe_render_entity {
438
if is_clustered_decal {
439
data.push(ExtractedClusterableObjectElement::Decal(**render_entity));
440
} else if is_point_light || is_spot_light {
441
data.push(ExtractedClusterableObjectElement::Light(**render_entity));
442
}
443
}
444
if is_reflection_probe {
445
data.push(ExtractedClusterableObjectElement::ReflectionProbe(
446
MainEntity::from(*clusterable_entity),
447
));
448
}
449
if is_irradiance_volume {
450
data.push(ExtractedClusterableObjectElement::IrradianceVolume(
451
MainEntity::from(*clusterable_entity),
452
));
453
}
454
}
455
}
456
457
entity_commands.insert((
458
ExtractedClusterableObjects { data },
459
ExtractedClusterConfig::from(clusters),
460
));
461
}
462
463
commands.insert_resource(global_cluster_settings.clone());
464
}
465
466
/// Creates and populates the GPU buffers that store clusters when CPU
467
/// clustering is being used.
468
pub fn prepare_clusters_for_cpu_clustering(
469
mut commands: Commands,
470
render_device: Res<RenderDevice>,
471
render_queue: Res<RenderQueue>,
472
mesh_pipeline: Res<MeshPipeline>,
473
global_clusterable_object_meta: Res<GlobalClusterableObjectMeta>,
474
views: Query<(
475
Entity,
476
&ExtractedClusterableObjects,
477
Option<&RenderViewLightProbes<EnvironmentMapLight>>,
478
Option<&RenderViewLightProbes<IrradianceVolume>>,
479
)>,
480
) {
481
let render_device = render_device.into_inner();
482
let supports_storage_buffers = matches!(
483
mesh_pipeline.clustered_forward_buffer_binding_type,
484
BufferBindingType::Storage { .. }
485
);
486
for (entity, extracted_clusters, maybe_environment_maps, maybe_irradiance_volumes) in &views {
487
let mut view_clusters_bindings =
488
ViewClusterBindings::new(mesh_pipeline.clustered_forward_buffer_binding_type);
489
view_clusters_bindings.clear();
490
491
for record in &extracted_clusters.data {
492
match record {
493
ExtractedClusterableObjectElement::ClusterHeader(counts) => {
494
let offset = view_clusters_bindings.n_indices();
495
view_clusters_bindings.push_offset_and_counts(offset, counts);
496
}
497
498
ExtractedClusterableObjectElement::Light(entity)
499
| ExtractedClusterableObjectElement::Decal(entity) => {
500
if let Some(clusterable_object_index) =
501
global_clusterable_object_meta.entity_to_index.get(entity)
502
{
503
if view_clusters_bindings.n_indices() >= ViewClusterBindings::MAX_INDICES
504
&& !supports_storage_buffers
505
{
506
warn!(
507
"Clusterable object index lists are full! The clusterable \
508
objects in the view are present in too many clusters."
509
);
510
break;
511
}
512
view_clusters_bindings.push_index(*clusterable_object_index);
513
} else {
514
// This should never happen. The appropriate systems
515
// should have populated
516
// `global_clusterable_object_meta` by now.
517
error!(
518
"Clustered light or decal {:?} had no assigned index!",
519
entity
520
);
521
// Things that should never happen won't happen in debug mode.
522
debug_assert!(false);
523
view_clusters_bindings.push_dummy_index();
524
}
525
}
526
527
ExtractedClusterableObjectElement::ReflectionProbe(main_entity) => {
528
match maybe_environment_maps.and_then(|environment_maps| {
529
environment_maps
530
.main_entity_to_render_light_probe_index
531
.get(main_entity)
532
}) {
533
Some(render_light_probe_index) => {
534
view_clusters_bindings.push_index(*render_light_probe_index as usize);
535
}
536
None => {
537
// This can happen while the reflection probe is loading.
538
trace!(
539
"Clustered reflection probe {:?} had no assigned index",
540
main_entity,
541
);
542
view_clusters_bindings.push_dummy_index();
543
}
544
}
545
}
546
547
ExtractedClusterableObjectElement::IrradianceVolume(main_entity) => {
548
match maybe_irradiance_volumes.and_then(|irradiance_volumes| {
549
irradiance_volumes
550
.main_entity_to_render_light_probe_index
551
.get(main_entity)
552
}) {
553
Some(render_light_probe_index) => {
554
view_clusters_bindings.push_index(*render_light_probe_index as usize);
555
}
556
None => {
557
trace!(
558
"Clustered irradiance volume {:?} had no assigned index",
559
main_entity
560
);
561
view_clusters_bindings.push_dummy_index();
562
}
563
}
564
}
565
}
566
}
567
568
view_clusters_bindings.write_buffers(render_device, &render_queue);
569
570
commands.entity(entity).insert(view_clusters_bindings);
571
}
572
}
573
574
impl ViewClusterBindings {
575
pub const MAX_OFFSETS: usize = 16384 / 4;
576
const MAX_UNIFORM_ITEMS: usize = Self::MAX_OFFSETS / 4;
577
pub const MAX_INDICES: usize = 16384;
578
579
pub fn new(buffer_binding_type: BufferBindingType) -> Self {
580
Self {
581
n_indices: 0,
582
n_offsets: 0,
583
buffers: ViewClusterBuffers::new(buffer_binding_type),
584
}
585
}
586
587
pub fn clear(&mut self) {
588
match &mut self.buffers {
589
ViewClusterBuffers::Uniform {
590
clusterable_object_index_lists,
591
cluster_offsets_and_counts,
592
} => {
593
*clusterable_object_index_lists.get_mut().data =
594
[UVec4::ZERO; Self::MAX_UNIFORM_ITEMS];
595
*cluster_offsets_and_counts.get_mut().data = [UVec4::ZERO; Self::MAX_UNIFORM_ITEMS];
596
}
597
ViewClusterBuffers::Storage {
598
clusterable_object_index_lists,
599
cluster_offsets_and_counts,
600
..
601
} => {
602
clusterable_object_index_lists.get_mut().data.clear();
603
cluster_offsets_and_counts.get_mut().data.clear();
604
}
605
}
606
}
607
608
fn push_offset_and_counts(&mut self, offset: usize, counts: &ClusterableObjectCounts) {
609
match &mut self.buffers {
610
ViewClusterBuffers::Uniform {
611
cluster_offsets_and_counts,
612
..
613
} => {
614
let array_index = self.n_offsets >> 2; // >> 2 is equivalent to / 4
615
if array_index >= Self::MAX_UNIFORM_ITEMS {
616
warn!("cluster offset and count out of bounds!");
617
return;
618
}
619
let component = self.n_offsets & ((1 << 2) - 1);
620
let packed =
621
pack_offset_and_counts(offset, counts.point_lights, counts.spot_lights);
622
623
cluster_offsets_and_counts.get_mut().data[array_index][component] = packed;
624
}
625
ViewClusterBuffers::Storage {
626
cluster_offsets_and_counts,
627
..
628
} => {
629
cluster_offsets_and_counts.get_mut().data.push([
630
uvec4(
631
offset as u32,
632
counts.point_lights,
633
counts.spot_lights,
634
counts.reflection_probes,
635
),
636
uvec4(counts.irradiance_volumes, counts.decals, 0, 0),
637
]);
638
}
639
}
640
641
self.n_offsets += 1;
642
}
643
644
pub fn n_indices(&self) -> usize {
645
self.n_indices
646
}
647
648
// An internal helper method that pushes a raw clustered object index to the
649
// GPU buffer.
650
fn push_raw_index(&mut self, index: u32) {
651
match &mut self.buffers {
652
ViewClusterBuffers::Uniform {
653
clusterable_object_index_lists,
654
..
655
} => {
656
let array_index = self.n_indices >> 4; // >> 4 is equivalent to / 16
657
let component = (self.n_indices >> 2) & ((1 << 2) - 1);
658
let sub_index = self.n_indices & ((1 << 2) - 1);
659
660
clusterable_object_index_lists.get_mut().data[array_index][component] |=
661
index << (8 * sub_index);
662
}
663
ViewClusterBuffers::Storage {
664
clusterable_object_index_lists,
665
..
666
} => {
667
clusterable_object_index_lists.get_mut().data.push(index);
668
}
669
}
670
671
self.n_indices += 1;
672
}
673
674
/// Pushes the index of a clustered object to the GPU buffer.
675
pub fn push_index(&mut self, index: usize) {
676
self.push_raw_index(index as u32);
677
}
678
679
/// Pushes a placeholder -1 index to the GPU buffer.
680
///
681
/// This is used when processing reflection probes and irradiance volumes
682
/// that haven't loaded yet.
683
pub fn push_dummy_index(&mut self) {
684
self.push_raw_index(!0);
685
}
686
687
/// Reserves space in the cluster offsets-and-counts list for `clusters`
688
/// clusters.
689
pub fn reserve_clusters(&mut self, clusters: usize) {
690
match &mut self.buffers {
691
ViewClusterBuffers::Uniform { .. } => {
692
error!("`reserve_clusters` should only be called in GPU clustering, which requires a storage buffer");
693
}
694
ViewClusterBuffers::Storage {
695
cluster_offsets_and_counts,
696
..
697
} => {
698
cluster_offsets_and_counts
699
.get_mut()
700
.data
701
.extend(iter::repeat_n(
702
GpuClusterOffsetAndCounts::default(),
703
clusters,
704
));
705
self.n_offsets += clusters;
706
}
707
}
708
}
709
710
/// Reserves space in the index lists for `elements` indices.
711
pub fn reserve_indices(&mut self, elements: usize) {
712
match &mut self.buffers {
713
ViewClusterBuffers::Uniform { .. } => {
714
error!("`reserve_indices` should only be called in GPU clustering, which requires a storage buffer");
715
}
716
ViewClusterBuffers::Storage {
717
clusterable_object_index_lists,
718
..
719
} => {
720
clusterable_object_index_lists
721
.get_mut()
722
.data
723
.extend(iter::repeat_n(0, elements));
724
self.n_indices += elements;
725
}
726
}
727
}
728
729
pub fn write_buffers(&mut self, render_device: &RenderDevice, render_queue: &RenderQueue) {
730
match &mut self.buffers {
731
ViewClusterBuffers::Uniform {
732
clusterable_object_index_lists,
733
cluster_offsets_and_counts,
734
} => {
735
clusterable_object_index_lists.write_buffer(render_device, render_queue);
736
cluster_offsets_and_counts.write_buffer(render_device, render_queue);
737
}
738
ViewClusterBuffers::Storage {
739
clusterable_object_index_lists,
740
cluster_offsets_and_counts,
741
} => {
742
clusterable_object_index_lists.write_buffer(render_device, render_queue);
743
cluster_offsets_and_counts.write_buffer(render_device, render_queue);
744
}
745
}
746
}
747
748
pub fn clusterable_object_index_lists_binding(&self) -> Option<BindingResource<'_>> {
749
match &self.buffers {
750
ViewClusterBuffers::Uniform {
751
clusterable_object_index_lists,
752
..
753
} => clusterable_object_index_lists.binding(),
754
ViewClusterBuffers::Storage {
755
clusterable_object_index_lists,
756
..
757
} => clusterable_object_index_lists.binding(),
758
}
759
}
760
761
pub fn offsets_and_counts_binding(&self) -> Option<BindingResource<'_>> {
762
match &self.buffers {
763
ViewClusterBuffers::Uniform {
764
cluster_offsets_and_counts,
765
..
766
} => cluster_offsets_and_counts.binding(),
767
ViewClusterBuffers::Storage {
768
cluster_offsets_and_counts,
769
..
770
} => cluster_offsets_and_counts.binding(),
771
}
772
}
773
774
pub fn min_size_clusterable_object_index_lists(
775
buffer_binding_type: BufferBindingType,
776
) -> NonZero<u64> {
777
match buffer_binding_type {
778
BufferBindingType::Storage { .. } => GpuClusterableObjectIndexListsStorage::min_size(),
779
BufferBindingType::Uniform => GpuClusterableObjectIndexListsUniform::min_size(),
780
}
781
}
782
783
pub fn min_size_cluster_offsets_and_counts(
784
buffer_binding_type: BufferBindingType,
785
) -> NonZero<u64> {
786
match buffer_binding_type {
787
BufferBindingType::Storage { .. } => GpuClusterOffsetsAndCountsStorage::min_size(),
788
BufferBindingType::Uniform => GpuClusterOffsetsAndCountsUniform::min_size(),
789
}
790
}
791
}
792
793
impl ViewClusterBuffers {
794
fn new(buffer_binding_type: BufferBindingType) -> Self {
795
match buffer_binding_type {
796
BufferBindingType::Storage { .. } => Self::storage(),
797
BufferBindingType::Uniform => Self::uniform(),
798
}
799
}
800
801
fn uniform() -> Self {
802
ViewClusterBuffers::Uniform {
803
clusterable_object_index_lists: UniformBuffer::default(),
804
cluster_offsets_and_counts: UniformBuffer::default(),
805
}
806
}
807
808
fn storage() -> Self {
809
ViewClusterBuffers::Storage {
810
clusterable_object_index_lists: StorageBuffer::default(),
811
cluster_offsets_and_counts: StorageBuffer::default(),
812
}
813
}
814
}
815
816
// Compresses the offset and counts of point and spot lights so that they fit in
817
// a UBO.
818
//
819
// This function is only used if storage buffers are unavailable on this
820
// platform: typically, on WebGL 2.
821
//
822
// NOTE: With uniform buffer max binding size as 16384 bytes
823
// that means we can fit 204 clusterable objects in one uniform
824
// buffer, which means the count can be at most 204 so it
825
// needs 9 bits.
826
// The array of indices can also use u8 and that means the
827
// offset in to the array of indices needs to be able to address
828
// 16384 values. log2(16384) = 14 bits.
829
// We use 32 bits to store the offset and counts so
830
// we pack the offset into the upper 14 bits of a u32,
831
// the point light count into bits 9-17, and the spot light count into bits 0-8.
832
// [ 31 .. 18 | 17 .. 9 | 8 .. 0 ]
833
// [ offset | point light count | spot light count ]
834
//
835
// NOTE: This assumes CPU and GPU endianness are the same which is true
836
// for all common and tested x86/ARM CPUs and AMD/NVIDIA/Intel/Apple/etc GPUs
837
//
838
// NOTE: On platforms that use this function, we don't cluster light probes, so
839
// the number of light probes is irrelevant.
840
fn pack_offset_and_counts(offset: usize, point_count: u32, spot_count: u32) -> u32 {
841
((offset as u32 & CLUSTER_OFFSET_MASK) << (CLUSTER_COUNT_SIZE * 2))
842
| ((point_count & CLUSTER_COUNT_MASK) << CLUSTER_COUNT_SIZE)
843
| (spot_count & CLUSTER_COUNT_MASK)
844
}
845
846
#[derive(ShaderType)]
847
struct GpuClusterableObjectIndexListsUniform {
848
data: Box<[UVec4; ViewClusterBindings::MAX_UNIFORM_ITEMS]>,
849
}
850
851
// NOTE: Assert at compile time that GpuClusterableObjectIndexListsUniform
852
// fits within the maximum uniform buffer binding size
853
const _: () = assert!(GpuClusterableObjectIndexListsUniform::SHADER_SIZE.get() <= 16384);
854
855
impl Default for GpuClusterableObjectIndexListsUniform {
856
fn default() -> Self {
857
Self {
858
data: Box::new([UVec4::ZERO; ViewClusterBindings::MAX_UNIFORM_ITEMS]),
859
}
860
}
861
}
862
863
impl Default for GpuClusterOffsetsAndCountsUniform {
864
fn default() -> Self {
865
Self {
866
data: Box::new([UVec4::ZERO; ViewClusterBindings::MAX_UNIFORM_ITEMS]),
867
}
868
}
869
}
870
871