1use core::{iter, num::NonZero};
2
3use bevy_camera::Camera;
4use bevy_ecs::{entity::EntityHashMap, prelude::*};
5use bevy_light::{
6 cluster::{
7 ClusterableObjectCounts, ClusterableObjects, Clusters, GlobalClusterGpuSettings,
8 GlobalClusterSettings,
9 },
10 ClusteredDecal, EnvironmentMapLight, IrradianceVolume, PointLight, SpotLight,
11};
12use bevy_math::{uvec4, UVec3, UVec4, Vec4};
13use 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};
22use bytemuck::{Pod, Zeroable};
23use tracing::{error, info, trace, warn};
24
25use crate::{MeshPipeline, RenderViewLightProbes};
26
27pub(crate) mod gpu;
28
29pub const MAX_UNIFORM_BUFFER_CLUSTERABLE_OBJECTS: usize = 204;
32const _: () =
35 if !(size_of::<GpuClusteredLight>() * MAX_UNIFORM_BUFFER_CLUSTERABLE_OBJECTS
<= 16384) {
::core::panicking::panic("assertion failed: size_of::<GpuClusteredLight>() * MAX_UNIFORM_BUFFER_CLUSTERABLE_OBJECTS <=\n 16384")
}assert!(size_of::<GpuClusteredLight>() * MAX_UNIFORM_BUFFER_CLUSTERABLE_OBJECTS <= 16384);
36
37pub const CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT: u32 = 3;
40
41const CLUSTER_COUNT_SIZE: u32 = 9;
44
45const CLUSTER_OFFSET_MASK: u32 = (1 << (32 - (CLUSTER_COUNT_SIZE * 2))) - 1;
46const CLUSTER_COUNT_MASK: u32 = (1 << CLUSTER_COUNT_SIZE) - 1;
47
48pub const GPU_CLUSTERING_INITIAL_Z_SLICE_LIST_CAPACITY: usize = 1024;
53
54pub const GPU_CLUSTERING_INITIAL_INDEX_LIST_CAPACITY: usize = 65536;
59
60pub(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 = #[allow(non_exhaustive_omitted_patterns)] match device.get_supported_read_only_binding_type(CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT)
{
BufferBindingType::Storage { .. } => true,
_ => false,
}matches!(
67 device.get_supported_read_only_binding_type(CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT),
68 BufferBindingType::Storage { .. }
69 );
70
71 let gpu_clustering_supported = !(falsecfg!(target_os = "android") || falsecfg!(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 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event src/cluster/mod.rs:87",
"bevy_pbr::cluster", ::tracing::Level::INFO,
::tracing_core::__macro_support::Option::Some("src/cluster/mod.rs"),
::tracing_core::__macro_support::Option::Some(87u32),
::tracing_core::__macro_support::Option::Some("bevy_pbr::cluster"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::INFO <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::INFO <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("GPU clustering is supported on this device.")
as &dyn ::tracing::field::Value))])
});
} else { ; }
};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 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event src/cluster/mod.rs:93",
"bevy_pbr::cluster", ::tracing::Level::INFO,
::tracing_core::__macro_support::Option::Some("src/cluster/mod.rs"),
::tracing_core::__macro_support::Option::Some(93u32),
::tracing_core::__macro_support::Option::Some("bevy_pbr::cluster"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::INFO <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::INFO <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("GPU clustering isn\'t supported on this device; falling back to CPU clustering.")
as &dyn ::tracing::field::Value))])
});
} else { ; }
};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#[derive(#[automatically_derived]
impl ::core::marker::Copy for GpuClusteredLight { }Copy, #[automatically_derived]
impl ::core::clone::Clone for GpuClusteredLight {
#[inline]
fn clone(&self) -> GpuClusteredLight {
let _: ::core::clone::AssertParamIsClone<Vec4>;
let _: ::core::clone::AssertParamIsClone<u32>;
let _: ::core::clone::AssertParamIsClone<f32>;
*self
}
}Clone, impl bevy_render::render_resource::encase::private::ShaderSize for
GpuClusteredLight where
Vec4: bevy_render::render_resource::encase::private::ShaderSize,
Vec4: bevy_render::render_resource::encase::private::ShaderSize,
Vec4: bevy_render::render_resource::encase::private::ShaderSize,
u32: bevy_render::render_resource::encase::private::ShaderSize,
f32: bevy_render::render_resource::encase::private::ShaderSize,
f32: bevy_render::render_resource::encase::private::ShaderSize,
f32: bevy_render::render_resource::encase::private::ShaderSize,
f32: bevy_render::render_resource::encase::private::ShaderSize,
f32: bevy_render::render_resource::encase::private::ShaderSize,
u32: bevy_render::render_resource::encase::private::ShaderSize,
f32: bevy_render::render_resource::encase::private::ShaderSize {}ShaderType, #[automatically_derived]
impl ::core::default::Default for GpuClusteredLight {
#[inline]
fn default() -> GpuClusteredLight {
GpuClusteredLight {
light_custom_data: ::core::default::Default::default(),
color_inverse_square_range: ::core::default::Default::default(),
position_radius: ::core::default::Default::default(),
flags: ::core::default::Default::default(),
shadow_depth_bias: ::core::default::Default::default(),
shadow_normal_bias: ::core::default::Default::default(),
spot_light_tan_angle: ::core::default::Default::default(),
soft_shadow_size: ::core::default::Default::default(),
shadow_map_near_z: ::core::default::Default::default(),
decal_index: ::core::default::Default::default(),
range: ::core::default::Default::default(),
}
}
}Default, unsafe impl ::bytemuck::Pod for GpuClusteredLight {}Pod, unsafe impl ::bytemuck::Zeroable for GpuClusteredLight {}Zeroable, #[automatically_derived]
impl ::core::fmt::Debug for GpuClusteredLight {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
let names: &'static _ =
&["light_custom_data", "color_inverse_square_range",
"position_radius", "flags", "shadow_depth_bias",
"shadow_normal_bias", "spot_light_tan_angle",
"soft_shadow_size", "shadow_map_near_z", "decal_index",
"range"];
let values: &[&dyn ::core::fmt::Debug] =
&[&self.light_custom_data, &self.color_inverse_square_range,
&self.position_radius, &self.flags, &self.shadow_depth_bias,
&self.shadow_normal_bias, &self.spot_light_tan_angle,
&self.soft_shadow_size, &self.shadow_map_near_z,
&self.decal_index, &&self.range];
::core::fmt::Formatter::debug_struct_fields_finish(f,
"GpuClusteredLight", names, values)
}
}Debug)]
111#[repr(C)]
112pub struct GpuClusteredLight {
113 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 pub(crate) decal_index: u32,
129 pub(crate) range: f32,
131}
132
133#[derive(impl bevy_ecs::resource::Resource for GlobalClusterableObjectMeta where
Self: ::core::marker::Send + ::core::marker::Sync + 'static {}Resource)]
136pub struct GlobalClusterableObjectMeta {
137 pub gpu_clustered_lights: GpuClusteredLights,
142
143 pub entity_to_index: EntityHashMap<usize>,
149}
150
151pub struct GpuClusteredLights {
159 data: RawBufferVec<GpuClusteredLight>,
160 is_storage_buffer: bool,
161}
162
163#[derive(impl bevy_ecs::component::Component for ExtractedClusterConfig where
Self: ::core::marker::Send + ::core::marker::Sync + 'static {
const STORAGE_TYPE: bevy_ecs::component::StorageType =
bevy_ecs::component::StorageType::Table;
type Mutability = bevy_ecs::component::Mutable;
fn register_required_components(_requiree:
bevy_ecs::component::ComponentId,
required_components:
&mut bevy_ecs::component::RequiredComponentsRegistrator) {}
fn clone_behavior() -> bevy_ecs::component::ComponentCloneBehavior {
use bevy_ecs::component::{
DefaultCloneBehaviorBase, DefaultCloneBehaviorViaClone,
};
(&&&bevy_ecs::component::DefaultCloneBehaviorSpecialization::<Self>::default()).default_clone_behavior()
}
fn relationship_accessor()
->
::core::option::Option<bevy_ecs::relationship::ComponentRelationshipAccessor<Self>> {
::core::option::Option::None
}
}Component)]
164pub struct ExtractedClusterConfig {
165 pub(crate) near: f32,
167 pub(crate) far: f32,
168 pub(crate) dimensions: UVec3,
170}
171
172impl<'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
182enum ExtractedClusterableObjectElement {
185 ClusterHeader(ClusterableObjectCounts),
187 Light(Entity),
191 ReflectionProbe(MainEntity),
196 IrradianceVolume(MainEntity),
201 Decal(Entity),
205}
206
207#[derive(impl bevy_ecs::component::Component for ExtractedClusterableObjects where
Self: ::core::marker::Send + ::core::marker::Sync + 'static {
const STORAGE_TYPE: bevy_ecs::component::StorageType =
bevy_ecs::component::StorageType::Table;
type Mutability = bevy_ecs::component::Mutable;
fn register_required_components(_requiree:
bevy_ecs::component::ComponentId,
required_components:
&mut bevy_ecs::component::RequiredComponentsRegistrator) {}
fn clone_behavior() -> bevy_ecs::component::ComponentCloneBehavior {
use bevy_ecs::component::{
DefaultCloneBehaviorBase, DefaultCloneBehaviorViaClone,
};
(&&&bevy_ecs::component::DefaultCloneBehaviorSpecialization::<Self>::default()).default_clone_behavior()
}
fn relationship_accessor()
->
::core::option::Option<bevy_ecs::relationship::ComponentRelationshipAccessor<Self>> {
::core::option::Option::None
}
}Component)]
208pub struct ExtractedClusterableObjects {
209 data: Vec<ExtractedClusterableObjectElement>,
210}
211
212#[derive(impl bevy_render::render_resource::encase::private::ShaderSize for
GpuClusterOffsetsAndCountsUniform where
Box<[UVec4; ViewClusterBindings::MAX_UNIFORM_ITEMS]>: bevy_render::render_resource::encase::private::ShaderSize
{}ShaderType)]
213struct GpuClusterOffsetsAndCountsUniform {
214 data: Box<[UVec4; ViewClusterBindings::MAX_UNIFORM_ITEMS]>,
215}
216
217#[derive(impl bevy_render::render_resource::encase::private::CalculateSizeFor for
GpuClusterableObjectIndexListsStorage where
Self: bevy_render::render_resource::encase::private::ShaderType<ExtraMetadata
= bevy_render::render_resource::encase::private::StructMetadata<1usize>>,
Vec<u32>: bevy_render::render_resource::encase::private::CalculateSizeFor
{
fn calculate_size_for(nr_of_el: ::core::primitive::u64)
-> ::core::num::NonZeroU64 {
let mut offset =
<Self as
bevy_render::render_resource::encase::private::ShaderType>::METADATA.last_offset();
offset +=
<Vec<u32> as
bevy_render::render_resource::encase::private::CalculateSizeFor>::calculate_size_for(nr_of_el).get();
bevy_render::render_resource::encase::private::SizeValue::new(<Self as
bevy_render::render_resource::encase::private::ShaderType>::METADATA.alignment().round_up(offset)).0
}
}ShaderType, #[automatically_derived]
impl ::core::default::Default for GpuClusterableObjectIndexListsStorage {
#[inline]
fn default() -> GpuClusterableObjectIndexListsStorage {
GpuClusterableObjectIndexListsStorage {
data: ::core::default::Default::default(),
}
}
}Default)]
218struct GpuClusterableObjectIndexListsStorage {
219 #[shader(size(runtime))]
220 data: Vec<u32>,
221}
222
223#[derive(impl bevy_render::render_resource::encase::private::CalculateSizeFor for
GpuClusterOffsetsAndCountsStorage where
Self: bevy_render::render_resource::encase::private::ShaderType<ExtraMetadata
= bevy_render::render_resource::encase::private::StructMetadata<1usize>>,
Vec<GpuClusterOffsetAndCounts>: bevy_render::render_resource::encase::private::CalculateSizeFor
{
fn calculate_size_for(nr_of_el: ::core::primitive::u64)
-> ::core::num::NonZeroU64 {
let mut offset =
<Self as
bevy_render::render_resource::encase::private::ShaderType>::METADATA.last_offset();
offset +=
<Vec<GpuClusterOffsetAndCounts> as
bevy_render::render_resource::encase::private::CalculateSizeFor>::calculate_size_for(nr_of_el).get();
bevy_render::render_resource::encase::private::SizeValue::new(<Self as
bevy_render::render_resource::encase::private::ShaderType>::METADATA.alignment().round_up(offset)).0
}
}ShaderType, #[automatically_derived]
impl ::core::default::Default for GpuClusterOffsetsAndCountsStorage {
#[inline]
fn default() -> GpuClusterOffsetsAndCountsStorage {
GpuClusterOffsetsAndCountsStorage {
data: ::core::default::Default::default(),
}
}
}Default)]
224struct GpuClusterOffsetsAndCountsStorage {
225 #[shader(size(runtime))]
229 data: Vec<GpuClusterOffsetAndCounts>,
230}
231
232type GpuClusterOffsetAndCounts = [UVec4; 2];
234
235enum ViewClusterBuffers {
236 Uniform {
237 clusterable_object_index_lists: UniformBuffer<GpuClusterableObjectIndexListsUniform>,
239 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(impl bevy_ecs::component::Component for ViewClusterBindings where
Self: ::core::marker::Send + ::core::marker::Sync + 'static {
const STORAGE_TYPE: bevy_ecs::component::StorageType =
bevy_ecs::component::StorageType::Table;
type Mutability = bevy_ecs::component::Mutable;
fn register_required_components(_requiree:
bevy_ecs::component::ComponentId,
required_components:
&mut bevy_ecs::component::RequiredComponentsRegistrator) {}
fn clone_behavior() -> bevy_ecs::component::ComponentCloneBehavior {
use bevy_ecs::component::{
DefaultCloneBehaviorBase, DefaultCloneBehaviorViaClone,
};
(&&&bevy_ecs::component::DefaultCloneBehaviorSpecialization::<Self>::default()).default_clone_behavior()
}
fn relationship_accessor()
->
::core::option::Option<bevy_ecs::relationship::ComponentRelationshipAccessor<Self>> {
::core::option::Option::None
}
}Component)]
249pub struct ViewClusterBindings {
250 n_indices: usize,
251 n_offsets: usize,
252 buffers: ViewClusterBuffers,
253}
254
255pub 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
264impl 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
273impl 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
351type ClusterExtractionMapperQueryFlags = (
353 Has<PointLight>,
354 Has<SpotLight>,
355 Has<EnvironmentMapLight>,
356 Has<IrradianceVolume>,
357 Has<ClusteredDecal>,
358);
359type ClusterExtractionMapperQueryFilter = Or<(
361 With<PointLight>,
362 With<SpotLight>,
363 With<EnvironmentMapLight>,
364 With<IrradianceVolume>,
365 With<ClusteredDecal>,
366)>;
367
368pub 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
377pub fn gpu_clustering_is_enabled(global_cluster_settings: Res<GlobalClusterSettings>) -> bool {
381 global_cluster_settings.gpu_clustering.is_some()
382}
383
384pub 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 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event src/cluster/mod.rs:408",
"bevy_pbr::cluster", ::tracing::Level::ERROR,
::tracing_core::__macro_support::Option::Some("src/cluster/mod.rs"),
::tracing_core::__macro_support::Option::Some(408u32),
::tracing_core::__macro_support::Option::Some("bevy_pbr::cluster"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::ERROR <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::ERROR <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("Clusterable objects must have been in CPU mode if doing CPU clustering")
as &dyn ::tracing::field::Value))])
});
} else { ; }
};error!("Clusterable objects must have been in CPU mode if doing CPU clustering");
409 continue;
410 }
411 };
412
413 let mut data = ::alloc::vec::Vec::new()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 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event src/cluster/mod.rs:430",
"bevy_pbr::cluster", ::tracing::Level::ERROR,
::tracing_core::__macro_support::Option::Some("src/cluster/mod.rs"),
::tracing_core::__macro_support::Option::Some(430u32),
::tracing_core::__macro_support::Option::Some("bevy_pbr::cluster"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::ERROR <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::ERROR <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("Couldn\'t find clustered object {0:?} in the main world",
clusterable_entity) as &dyn ::tracing::field::Value))])
});
} else { ; }
};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
466pub 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 = #[allow(non_exhaustive_omitted_patterns)] match mesh_pipeline.clustered_forward_buffer_binding_type
{
BufferBindingType::Storage { .. } => true,
_ => false,
}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 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event src/cluster/mod.rs:506",
"bevy_pbr::cluster", ::tracing::Level::WARN,
::tracing_core::__macro_support::Option::Some("src/cluster/mod.rs"),
::tracing_core::__macro_support::Option::Some(506u32),
::tracing_core::__macro_support::Option::Some("bevy_pbr::cluster"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::WARN <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::WARN <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("Clusterable object index lists are full! The clusterable objects in the view are present in too many clusters.")
as &dyn ::tracing::field::Value))])
});
} else { ; }
};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 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event src/cluster/mod.rs:517",
"bevy_pbr::cluster", ::tracing::Level::ERROR,
::tracing_core::__macro_support::Option::Some("src/cluster/mod.rs"),
::tracing_core::__macro_support::Option::Some(517u32),
::tracing_core::__macro_support::Option::Some("bevy_pbr::cluster"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::ERROR <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::ERROR <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("Clustered light or decal {0:?} had no assigned index!",
entity) as &dyn ::tracing::field::Value))])
});
} else { ; }
};error!(
518 "Clustered light or decal {:?} had no assigned index!",
519 entity
520 );
521 if true {
if !false { ::core::panicking::panic("assertion failed: false") };
};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 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event src/cluster/mod.rs:538",
"bevy_pbr::cluster", ::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("src/cluster/mod.rs"),
::tracing_core::__macro_support::Option::Some(538u32),
::tracing_core::__macro_support::Option::Some("bevy_pbr::cluster"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("Clustered reflection probe {0:?} had no assigned index",
main_entity) as &dyn ::tracing::field::Value))])
});
} else { ; }
};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 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event src/cluster/mod.rs:557",
"bevy_pbr::cluster", ::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("src/cluster/mod.rs"),
::tracing_core::__macro_support::Option::Some(557u32),
::tracing_core::__macro_support::Option::Some("bevy_pbr::cluster"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("Clustered irradiance volume {0:?} had no assigned index",
main_entity) as &dyn ::tracing::field::Value))])
});
} else { ; }
};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
574impl 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; if array_index >= Self::MAX_UNIFORM_ITEMS {
616 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event src/cluster/mod.rs:616",
"bevy_pbr::cluster", ::tracing::Level::WARN,
::tracing_core::__macro_support::Option::Some("src/cluster/mod.rs"),
::tracing_core::__macro_support::Option::Some(616u32),
::tracing_core::__macro_support::Option::Some("bevy_pbr::cluster"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::WARN <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::WARN <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("cluster offset and count out of bounds!")
as &dyn ::tracing::field::Value))])
});
} else { ; }
};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 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; 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 pub fn push_index(&mut self, index: usize) {
676 self.push_raw_index(index as u32);
677 }
678
679 pub fn push_dummy_index(&mut self) {
684 self.push_raw_index(!0);
685 }
686
687 pub fn reserve_clusters(&mut self, clusters: usize) {
690 match &mut self.buffers {
691 ViewClusterBuffers::Uniform { .. } => {
692 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event src/cluster/mod.rs:692",
"bevy_pbr::cluster", ::tracing::Level::ERROR,
::tracing_core::__macro_support::Option::Some("src/cluster/mod.rs"),
::tracing_core::__macro_support::Option::Some(692u32),
::tracing_core::__macro_support::Option::Some("bevy_pbr::cluster"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::ERROR <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::ERROR <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("`reserve_clusters` should only be called in GPU clustering, which requires a storage buffer")
as &dyn ::tracing::field::Value))])
});
} else { ; }
};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 pub fn reserve_indices(&mut self, elements: usize) {
712 match &mut self.buffers {
713 ViewClusterBuffers::Uniform { .. } => {
714 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event src/cluster/mod.rs:714",
"bevy_pbr::cluster", ::tracing::Level::ERROR,
::tracing_core::__macro_support::Option::Some("src/cluster/mod.rs"),
::tracing_core::__macro_support::Option::Some(714u32),
::tracing_core::__macro_support::Option::Some("bevy_pbr::cluster"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::ERROR <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::ERROR <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("`reserve_indices` should only be called in GPU clustering, which requires a storage buffer")
as &dyn ::tracing::field::Value))])
});
} else { ; }
};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
793impl 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
816fn 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(impl bevy_render::render_resource::encase::private::ShaderSize for
GpuClusterableObjectIndexListsUniform where
Box<[UVec4; ViewClusterBindings::MAX_UNIFORM_ITEMS]>: bevy_render::render_resource::encase::private::ShaderSize
{}ShaderType)]
847struct GpuClusterableObjectIndexListsUniform {
848 data: Box<[UVec4; ViewClusterBindings::MAX_UNIFORM_ITEMS]>,
849}
850
851const _: () = if !(GpuClusterableObjectIndexListsUniform::SHADER_SIZE.get() <= 16384) {
::core::panicking::panic("assertion failed: GpuClusterableObjectIndexListsUniform::SHADER_SIZE.get() <= 16384")
}assert!(GpuClusterableObjectIndexListsUniform::SHADER_SIZE.get() <= 16384);
854
855impl Default for GpuClusterableObjectIndexListsUniform {
856 fn default() -> Self {
857 Self {
858 data: Box::new([UVec4::ZERO; ViewClusterBindings::MAX_UNIFORM_ITEMS]),
859 }
860 }
861}
862
863impl Default for GpuClusterOffsetsAndCountsUniform {
864 fn default() -> Self {
865 Self {
866 data: Box::new([UVec4::ZERO; ViewClusterBindings::MAX_UNIFORM_ITEMS]),
867 }
868 }
869}