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