1use crate::{
2 AreaLightLuts, DfgLut, ViewFogUniformOffset, ViewLightProbesUniformOffset,
3 ViewLightsUniformOffset, ViewScreenSpaceReflectionsUniformOffset,
4};
5use arrayvec::ArrayVec;
6use bevy_core_pipeline::{
7 oit::{
8 OitBuffers, OrderIndependentTransparencySettings,
9 OrderIndependentTransparencySettingsOffset,
10 },
11 prepass::ViewPrepassTextures,
12 tonemapping::{
13 get_lut_bind_group_layout_entries, get_lut_bindings, Tonemapping, TonemappingLuts,
14 },
15};
16use bevy_ecs::{
17 component::Component,
18 entity::Entity,
19 query::Has,
20 resource::Resource,
21 system::{Commands, Local, Query, Res},
22};
23use bevy_light::{EnvironmentMapLight, IrradianceVolume};
24use bevy_math::Vec4;
25use bevy_platform::sync::Arc;
26use bevy_render::{
27 camera::ExtractedCamera,
28 globals::{GlobalsBuffer, GlobalsUniform},
29 render_asset::RenderAssets,
30 render_resource::{binding_types::*, *},
31 renderer::{RenderAdapter, RenderDevice},
32 texture::{FallbackImage, FallbackImageZero, GpuImage},
33 view::{
34 Msaa, RenderVisibilityRanges, ViewUniform, ViewUniformOffset, ViewUniforms,
35 VISIBILITY_RANGES_STORAGE_BUFFER_COUNT,
36 },
37};
38use core::fmt::Write;
39use core::num::NonZero;
40
41use crate::{
42 contact_shadows::{
43 ContactShadowsBuffer, ContactShadowsUniform, ViewContactShadowsUniformOffset,
44 },
45 decal::{
46 self,
47 clustered::{
48 DecalsBuffer, RenderClusteredDecals, RenderViewClusteredDecalBindGroupEntries,
49 },
50 },
51 environment_map::{self, RenderViewEnvironmentMapBindGroupEntries},
52 irradiance_volume::{
53 self, RenderViewIrradianceVolumeBindGroupEntries, IRRADIANCE_VOLUMES_ARE_USABLE,
54 },
55 prepass,
56 resources::{AtmosphereBuffer, AtmosphereSampler, AtmosphereTextures, GpuAtmosphere},
57 Bluenoise, ExtractedAtmosphere, FogMeta, GlobalClusterableObjectMeta, GpuClusteredLights,
58 GpuFog, GpuLights, LightMeta, LightProbesBuffer, LightProbesUniform, MeshPipeline,
59 MeshPipelineKey, RenderViewLightProbes, ScreenSpaceAmbientOcclusionResources,
60 ScreenSpaceReflectionsBuffer, ScreenSpaceReflectionsUniform, ShadowSamplers,
61 ViewClusterBindings, ViewShadowBindings, ViewTransmissionTexture,
62 CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT,
63};
64
65#[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
66use bevy_render::render_resource::binding_types::texture_cube;
67
68#[cfg(debug_assertions)]
69use {crate::MESH_PIPELINE_VIEW_LAYOUT_SAFE_MAX_TEXTURES, bevy_utils::once, tracing::warn};
70
71pub const TONEMAPPING_LUT_TEXTURE_BINDING_INDEX: u32 = 18;
72pub const TONEMAPPING_LUT_SAMPLER_BINDING_INDEX: u32 = 19;
73
74#[derive(Clone)]
75pub struct MeshPipelineViewLayout {
76 pub main_layout: BindGroupLayoutDescriptor,
77 pub binding_array_layout: BindGroupLayoutDescriptor,
78 pub empty_layout: BindGroupLayoutDescriptor,
79}
80
81bitflags::bitflags! {
82 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
84 #[repr(transparent)]
85 pub struct MeshPipelineViewLayoutKey: u32 {
86 const MULTISAMPLED = 1 << 0;
87 const DEPTH_PREPASS = 1 << 1;
88 const NORMAL_PREPASS = 1 << 2;
89 const MOTION_VECTOR_PREPASS = 1 << 3;
90 const DEFERRED_PREPASS = 1 << 4;
91 const OIT_ENABLED = 1 << 5;
92 const ATMOSPHERE = 1 << 6;
93 const STBN = 1 << 7;
94 const TONEMAP_IN_SHADER = 1 << 8;
95 const ENVIRONMENT_MAP = 1 << 9;
96 const SCREEN_SPACE_AMBIENT_OCCLUSION = 1 << 10;
97 const IRRADIANCE_VOLUME = 1 << 11;
98 const SCREEN_SPACE_REFLECTIONS = 1 << 12;
99 const CONTACT_SHADOWS = 1 << 13;
100 const DISTANCE_FOG = 1 << 14;
101 const AREA_LIGHT_LUTS = 1 << 15;
102 }
103}
104
105impl MeshPipelineViewLayoutKey {
106 pub fn label(&self) -> String {
108 let iter = self
109 .iter_names()
110 .filter(|(_, key)| self.contains(*key))
111 .map(|(name, _)| name.to_lowercase());
112 let (lower, _) = iter.size_hint();
113 let sep = ",";
114 let mut result = String::with_capacity(sep.len() * lower);
115
116 for (i, name) in iter.enumerate() {
117 if i != 0 {
118 result.push_str(sep);
119 }
120 write!(&mut result, "{}", name).unwrap();
121 }
122
123 format!("mesh_view_layout:{}", result)
124 }
125}
126
127impl From<MeshPipelineKey> for MeshPipelineViewLayoutKey {
128 fn from(value: MeshPipelineKey) -> Self {
129 let mut result = MeshPipelineViewLayoutKey::empty();
130
131 if value.msaa_samples() > 1 {
132 result |= MeshPipelineViewLayoutKey::MULTISAMPLED;
133 }
134 if value.contains(MeshPipelineKey::DEPTH_PREPASS) {
135 result |= MeshPipelineViewLayoutKey::DEPTH_PREPASS;
136 }
137 if value.contains(MeshPipelineKey::NORMAL_PREPASS) {
138 result |= MeshPipelineViewLayoutKey::NORMAL_PREPASS;
139 }
140 if value.contains(MeshPipelineKey::MOTION_VECTOR_PREPASS) {
141 result |= MeshPipelineViewLayoutKey::MOTION_VECTOR_PREPASS;
142 }
143 if value.contains(MeshPipelineKey::DEFERRED_PREPASS) {
144 result |= MeshPipelineViewLayoutKey::DEFERRED_PREPASS;
145 }
146 if value.contains(MeshPipelineKey::OIT_ENABLED) {
147 result |= MeshPipelineViewLayoutKey::OIT_ENABLED;
148 }
149 if value.contains(MeshPipelineKey::ATMOSPHERE) {
150 result |= MeshPipelineViewLayoutKey::ATMOSPHERE;
151 }
152
153 if cfg!(feature = "bluenoise_texture") {
154 result |= MeshPipelineViewLayoutKey::STBN;
155 }
156
157 if cfg!(feature = "area_light_luts") {
158 result |= MeshPipelineViewLayoutKey::AREA_LIGHT_LUTS;
159 }
160
161 if value.contains(MeshPipelineKey::TONEMAP_IN_SHADER) {
162 result |= MeshPipelineViewLayoutKey::TONEMAP_IN_SHADER;
163 }
164 if value.contains(MeshPipelineKey::ENVIRONMENT_MAP) {
165 result |= MeshPipelineViewLayoutKey::ENVIRONMENT_MAP;
166 }
167 if value.contains(MeshPipelineKey::SCREEN_SPACE_AMBIENT_OCCLUSION) {
168 result |= MeshPipelineViewLayoutKey::SCREEN_SPACE_AMBIENT_OCCLUSION;
169 }
170 if value.contains(MeshPipelineKey::IRRADIANCE_VOLUME) {
171 result |= MeshPipelineViewLayoutKey::IRRADIANCE_VOLUME;
172 }
173 if value.contains(MeshPipelineKey::SCREEN_SPACE_REFLECTIONS) {
174 result |= MeshPipelineViewLayoutKey::SCREEN_SPACE_REFLECTIONS;
175 }
176 if value.contains(MeshPipelineKey::CONTACT_SHADOWS) {
177 result |= MeshPipelineViewLayoutKey::CONTACT_SHADOWS;
178 }
179 if value.contains(MeshPipelineKey::DISTANCE_FOG) {
180 result |= MeshPipelineViewLayoutKey::DISTANCE_FOG;
181 }
182
183 result
184 }
185}
186
187impl From<Msaa> for MeshPipelineViewLayoutKey {
188 fn from(value: Msaa) -> Self {
189 let mut result = MeshPipelineViewLayoutKey::empty();
190
191 if value.samples() > 1 {
192 result |= MeshPipelineViewLayoutKey::MULTISAMPLED;
193 }
194
195 result
196 }
197}
198
199impl From<Option<&ViewPrepassTextures>> for MeshPipelineViewLayoutKey {
200 fn from(value: Option<&ViewPrepassTextures>) -> Self {
201 let mut result = MeshPipelineViewLayoutKey::empty();
202
203 if let Some(prepass_textures) = value {
204 if prepass_textures.depth.is_some() {
205 result |= MeshPipelineViewLayoutKey::DEPTH_PREPASS;
206 }
207 if prepass_textures.normal.is_some() {
208 result |= MeshPipelineViewLayoutKey::NORMAL_PREPASS;
209 }
210 if prepass_textures.motion_vectors.is_some() {
211 result |= MeshPipelineViewLayoutKey::MOTION_VECTOR_PREPASS;
212 }
213 if prepass_textures.deferred.is_some() {
214 result |= MeshPipelineViewLayoutKey::DEFERRED_PREPASS;
215 }
216 }
217
218 result
219 }
220}
221
222pub(crate) fn buffer_layout(
223 buffer_binding_type: BufferBindingType,
224 has_dynamic_offset: bool,
225 min_binding_size: Option<NonZero<u64>>,
226) -> BindGroupLayoutEntryBuilder {
227 match buffer_binding_type {
228 BufferBindingType::Uniform => uniform_buffer_sized(has_dynamic_offset, min_binding_size),
229 BufferBindingType::Storage { read_only } => {
230 if read_only {
231 storage_buffer_read_only_sized(has_dynamic_offset, min_binding_size)
232 } else {
233 storage_buffer_sized(has_dynamic_offset, min_binding_size)
234 }
235 }
236 }
237}
238
239fn layout_entries(
241 layout_key: MeshPipelineViewLayoutKey,
242 &MeshPipelineViewLayoutParams {
243 clustered_forward_buffer_binding_type,
244 visibility_ranges_buffer_binding_type,
245 environment_map_entries,
246 irradiance_volume_entries,
247 clustered_decal_entries,
248 is_oit_supported,
249 }: &MeshPipelineViewLayoutParams,
250) -> [Vec<BindGroupLayoutEntry>; 2] {
251 let mut entries = DynamicBindGroupLayoutEntries::new_with_indices(
252 ShaderStages::FRAGMENT,
253 (
254 (
256 0,
257 uniform_buffer::<ViewUniform>(true).visibility(ShaderStages::VERTEX_FRAGMENT),
258 ),
259 (1, uniform_buffer::<GpuLights>(true)),
261 (
263 2,
264 #[cfg(all(
265 not(target_abi = "sim"),
266 any(
267 not(feature = "webgl"),
268 not(target_arch = "wasm32"),
269 feature = "webgpu"
270 )
271 ))]
272 texture_cube_array(TextureSampleType::Depth),
273 #[cfg(any(
274 target_abi = "sim",
275 all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu"))
276 ))]
277 texture_cube(TextureSampleType::Depth),
278 ),
279 (3, sampler(SamplerBindingType::Comparison)),
281 #[cfg(feature = "experimental_pbr_pcss")]
283 (4, sampler(SamplerBindingType::Filtering)),
284 (
286 5,
287 #[cfg(any(
288 not(feature = "webgl"),
289 not(target_arch = "wasm32"),
290 feature = "webgpu"
291 ))]
292 texture_2d_array(TextureSampleType::Depth),
293 #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
294 texture_2d(TextureSampleType::Depth),
295 ),
296 (6, sampler(SamplerBindingType::Comparison)),
298 #[cfg(feature = "experimental_pbr_pcss")]
300 (7, sampler(SamplerBindingType::Filtering)),
301 (
303 8,
304 buffer_layout(
305 clustered_forward_buffer_binding_type,
306 false,
307 Some(GpuClusteredLights::min_size(
308 clustered_forward_buffer_binding_type,
309 )),
310 ),
311 ),
312 (
314 9,
315 buffer_layout(
316 clustered_forward_buffer_binding_type,
317 false,
318 Some(
319 ViewClusterBindings::min_size_clusterable_object_index_lists(
320 clustered_forward_buffer_binding_type,
321 ),
322 ),
323 ),
324 ),
325 (
327 10,
328 buffer_layout(
329 clustered_forward_buffer_binding_type,
330 false,
331 Some(ViewClusterBindings::min_size_cluster_offsets_and_counts(
332 clustered_forward_buffer_binding_type,
333 )),
334 ),
335 ),
336 (
338 11,
339 uniform_buffer::<GlobalsUniform>(false).visibility(ShaderStages::VERTEX_FRAGMENT),
340 ),
341 (12, uniform_buffer::<LightProbesUniform>(true)),
343 (
345 14,
346 buffer_layout(
347 visibility_ranges_buffer_binding_type,
348 false,
349 Some(Vec4::min_size()),
350 )
351 .visibility(ShaderStages::VERTEX),
352 ),
353 ),
354 );
355
356 if layout_key.contains(MeshPipelineViewLayoutKey::DISTANCE_FOG) {
357 entries = entries.extend_with_indices((
358 (13, uniform_buffer::<GpuFog>(true)),
360 ));
361 }
362 if layout_key.contains(MeshPipelineViewLayoutKey::SCREEN_SPACE_REFLECTIONS) {
363 entries = entries.extend_with_indices((
364 (15, uniform_buffer::<ScreenSpaceReflectionsUniform>(true)),
366 ));
367 }
368 if layout_key.contains(MeshPipelineViewLayoutKey::CONTACT_SHADOWS) {
369 entries = entries.extend_with_indices((
370 (16, uniform_buffer::<ContactShadowsUniform>(true)),
372 ));
373 }
374 if layout_key.contains(MeshPipelineViewLayoutKey::SCREEN_SPACE_AMBIENT_OCCLUSION) {
375 entries = entries.extend_with_indices((
376 (
378 17,
379 texture_2d(TextureSampleType::Float { filterable: false }),
380 ),
381 ));
382 }
383
384 if layout_key.contains(MeshPipelineViewLayoutKey::TONEMAP_IN_SHADER) {
385 let tonemapping_lut_entries = get_lut_bind_group_layout_entries();
387 entries = entries.extend_with_indices((
388 (
389 TONEMAPPING_LUT_TEXTURE_BINDING_INDEX,
390 tonemapping_lut_entries[0],
391 ),
392 (
393 TONEMAPPING_LUT_SAMPLER_BINDING_INDEX,
394 tonemapping_lut_entries[1],
395 ),
396 ));
397 }
398
399 if cfg!(any(feature = "webgpu", not(target_arch = "wasm32")))
401 || !layout_key.contains(MeshPipelineViewLayoutKey::MULTISAMPLED)
402 {
403 for (entry, binding) in prepass::get_bind_group_layout_entries(layout_key)
404 .iter()
405 .zip([20, 21, 22, 23])
406 {
407 if let Some(entry) = entry {
408 entries = entries.extend_with_indices(((binding as u32, *entry),));
409 }
410 }
411 }
412
413 entries = entries.extend_with_indices((
415 (
416 24,
417 texture_2d(TextureSampleType::Float { filterable: true }),
418 ),
419 (25, sampler(SamplerBindingType::Filtering)),
420 ));
421
422 if layout_key.contains(MeshPipelineViewLayoutKey::OIT_ENABLED) {
424 if is_oit_supported {
428 entries = entries.extend_with_indices((
429 (
430 26,
431 uniform_buffer::<OrderIndependentTransparencySettings>(true),
432 ),
433 (27, uniform_buffer::<u32>(false)),
435 (28, storage_buffer_sized(false, None)),
437 (29, storage_buffer_sized(false, None)),
439 (
441 30,
442 storage_buffer_sized(false, NonZero::<u64>::new(size_of::<u32>() as u64)),
443 ),
444 ));
445 }
446 }
447
448 if layout_key.contains(MeshPipelineViewLayoutKey::ATMOSPHERE) {
450 entries = entries.extend_with_indices((
451 (
453 31,
454 texture_2d(TextureSampleType::Float { filterable: true }),
455 ),
456 (32, sampler(SamplerBindingType::Filtering)),
457 (33, storage_buffer_read_only::<GpuAtmosphere>(false)),
459 ));
460 }
461
462 if layout_key.contains(MeshPipelineViewLayoutKey::STBN) {
464 entries = entries.extend_with_indices(((
465 34,
466 texture_2d_array(TextureSampleType::Float { filterable: false }),
467 ),));
468 }
469 if cfg!(feature = "area_light_luts") {
471 entries = entries.extend_with_indices((
472 (
473 35,
474 texture_2d_array(TextureSampleType::Float { filterable: true }),
475 ),
476 (36, sampler(SamplerBindingType::Filtering)),
477 ));
478 }
479 if cfg!(feature = "dfg_lut") {
481 entries = entries.extend_with_indices((
482 (
483 37,
484 texture_2d(TextureSampleType::Float { filterable: true }),
485 ),
486 (38, sampler(SamplerBindingType::Filtering)),
487 ));
488 }
489
490 let mut binding_array_entries = DynamicBindGroupLayoutEntries::new(ShaderStages::FRAGMENT);
491 if layout_key.contains(MeshPipelineViewLayoutKey::ENVIRONMENT_MAP) {
492 binding_array_entries = binding_array_entries.extend_with_indices((
493 (0, environment_map_entries[0]),
494 (1, environment_map_entries[1]),
495 (2, environment_map_entries[2]),
496 ));
497 }
498
499 if layout_key.contains(MeshPipelineViewLayoutKey::IRRADIANCE_VOLUME) {
500 if IRRADIANCE_VOLUMES_ARE_USABLE {
502 binding_array_entries = binding_array_entries.extend_with_indices((
503 (3, irradiance_volume_entries[0]),
504 (4, irradiance_volume_entries[1]),
505 ));
506 }
507 }
508
509 if let Some(clustered_decal_entries) = clustered_decal_entries {
511 binding_array_entries = binding_array_entries.extend_with_indices((
512 (5, clustered_decal_entries[0]),
513 (6, clustered_decal_entries[1]),
514 (7, clustered_decal_entries[2]),
515 ));
516 }
517
518 [entries.to_vec(), binding_array_entries.to_vec()]
519}
520
521#[derive(Clone, Copy)]
523struct MeshPipelineViewLayoutParams {
524 clustered_forward_buffer_binding_type: BufferBindingType,
525 visibility_ranges_buffer_binding_type: BufferBindingType,
526 environment_map_entries: [BindGroupLayoutEntryBuilder; 3],
527 irradiance_volume_entries: [BindGroupLayoutEntryBuilder; 2],
528 clustered_decal_entries: Option<[BindGroupLayoutEntryBuilder; 3]>,
529 is_oit_supported: bool,
530}
531
532#[derive(Resource, Clone)]
534pub struct MeshPipelineViewLayouts {
535 params: Arc<MeshPipelineViewLayoutParams>,
536}
537
538pub fn init_mesh_pipeline_view_layouts(
539 mut commands: Commands,
540 render_device: Res<RenderDevice>,
541 render_adapter: Res<RenderAdapter>,
542) {
543 let clustered_forward_buffer_binding_type =
544 render_device.get_supported_read_only_binding_type(CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT);
545 let visibility_ranges_buffer_binding_type =
546 render_device.get_supported_read_only_binding_type(VISIBILITY_RANGES_STORAGE_BUFFER_COUNT);
547
548 let res = MeshPipelineViewLayouts {
549 params: Arc::new(MeshPipelineViewLayoutParams {
550 clustered_forward_buffer_binding_type,
551 visibility_ranges_buffer_binding_type,
552 environment_map_entries: environment_map::get_bind_group_layout_entries(
553 &render_device,
554 &render_adapter,
555 ),
556 irradiance_volume_entries: irradiance_volume::get_bind_group_layout_entries(
557 &render_device,
558 &render_adapter,
559 ),
560 clustered_decal_entries: decal::clustered::get_bind_group_layout_entries(
561 &render_device,
562 &render_adapter,
563 ),
564 is_oit_supported: bevy_core_pipeline::oit::resolve::is_oit_supported(
565 &render_adapter,
566 &render_device,
567 false,
568 ),
569 }),
570 };
571
572 commands.insert_resource(res);
573}
574
575impl MeshPipelineViewLayouts {
576 pub fn get_view_layout(&self, layout_key: MeshPipelineViewLayoutKey) -> MeshPipelineViewLayout {
578 let mut entries = layout_entries(layout_key, &self.params);
579
580 #[cfg(debug_assertions)]
581 let texture_count: usize = entries
582 .iter()
583 .flat_map(|e| {
584 e.iter()
585 .filter(|entry| matches!(entry.ty, BindingType::Texture { .. }))
586 })
587 .count();
588
589 #[cfg(debug_assertions)]
590 if texture_count > MESH_PIPELINE_VIEW_LAYOUT_SAFE_MAX_TEXTURES {
591 once!(warn!(
593 "Too many textures in mesh pipeline view layout, this might cause us \
594 to hit `wgpu::Limits::max_sampled_textures_per_shader_stage` in some environments."
595 ));
596 }
597
598 MeshPipelineViewLayout {
599 main_layout: BindGroupLayoutDescriptor {
600 label: layout_key.label().into(),
601 entries: core::mem::take(&mut entries[0]),
602 },
603 binding_array_layout: BindGroupLayoutDescriptor {
604 label: layout_key
605 .label()
606 .replace("mesh_view_layout:", "mesh_view_layout_binding_array:")
607 .into(),
608 entries: core::mem::take(&mut entries[1]),
609 },
610 empty_layout: BindGroupLayoutDescriptor {
611 label: "mesh_view_layout_empty".into(),
612 entries: vec![],
613 },
614 }
615 }
616}
617
618#[derive(Component)]
619pub struct MeshViewBindGroup {
620 pub main: BindGroup,
621 pub main_offsets: ArrayVec<u32, 7>,
622 pub binding_array: BindGroup,
623 pub empty: BindGroup,
624}
625
626pub fn prepare_mesh_view_bind_groups(
627 mut commands: Commands,
628 (render_device, pipeline_cache, render_adapter): (
629 Res<RenderDevice>,
630 Res<PipelineCache>,
631 Res<RenderAdapter>,
632 ),
633 mesh_pipeline: Res<MeshPipeline>,
634 shadow_samplers: Res<ShadowSamplers>,
635 (light_meta, global_clusterable_object_meta, fog_meta, view_uniforms): (
636 Res<LightMeta>,
637 Res<GlobalClusterableObjectMeta>,
638 Res<FogMeta>,
639 Res<ViewUniforms>,
640 ),
641 views: Query<(
642 Entity,
643 Option<&ExtractedCamera>,
644 &ViewShadowBindings,
645 &ViewClusterBindings,
646 &Msaa,
647 Option<&ScreenSpaceAmbientOcclusionResources>,
648 Option<&ViewPrepassTextures>,
649 Option<&ViewTransmissionTexture>,
650 Option<&AtmosphereTextures>,
651 &Tonemapping,
652 (
653 Option<&RenderViewLightProbes<EnvironmentMapLight>>,
654 Option<&RenderViewLightProbes<IrradianceVolume>>,
655 ),
656 Has<ExtractedAtmosphere>,
657 (
658 &ViewUniformOffset,
659 &ViewLightsUniformOffset,
660 &ViewLightProbesUniformOffset,
661 Option<&ViewFogUniformOffset>,
662 Option<&ViewScreenSpaceReflectionsUniformOffset>,
663 Option<&ViewContactShadowsUniformOffset>,
664 Option<&OrderIndependentTransparencySettingsOffset>,
665 ),
666 )>,
667 (images, fallback_image, fallback_image_zero): (
668 Res<RenderAssets<GpuImage>>,
669 Res<FallbackImage>,
670 Res<FallbackImageZero>,
671 ),
672 globals_buffer: Res<GlobalsBuffer>,
673 tonemapping_luts: Res<TonemappingLuts>,
674 light_probes_buffer: Res<LightProbesBuffer>,
675 visibility_ranges: Res<RenderVisibilityRanges>,
676 (ssr_buffer, contact_shadows_buffer, oit_buffers): (
677 Res<ScreenSpaceReflectionsBuffer>,
678 Res<ContactShadowsBuffer>,
679 Res<OitBuffers>,
680 ),
681 (
682 decals_buffer,
683 render_decals,
684 atmosphere_buffer,
685 atmosphere_sampler,
686 blue_noise,
687 area_light_luts,
688 dfg_lut,
689 ): (
690 Res<DecalsBuffer>,
691 Res<RenderClusteredDecals>,
692 Option<Res<AtmosphereBuffer>>,
693 Option<Res<AtmosphereSampler>>,
694 Res<Bluenoise>,
695 Res<AreaLightLuts>,
696 Res<DfgLut>,
697 ),
698 #[cfg(not(all(target_arch = "wasm32", target_feature = "atomics")))] mut entries_cache: Local<
700 Vec<BindGroupEntry>,
701 >,
702 #[cfg(not(all(target_arch = "wasm32", target_feature = "atomics")))]
703 mut entries_binding_array_cache: Local<Vec<BindGroupEntry>>,
704) {
705 if let (
706 Some(view_binding),
707 Some(light_binding),
708 Some(clusterable_objects_binding),
709 Some(globals),
710 Some(light_probes_binding),
711 Some(visibility_ranges_buffer),
712 ) = (
713 view_uniforms.uniforms.binding(),
714 light_meta.view_gpu_lights.binding(),
715 global_clusterable_object_meta
716 .gpu_clustered_lights
717 .binding(),
718 globals_buffer.buffer.binding(),
719 light_probes_buffer.binding(),
720 visibility_ranges.buffer().buffer(),
721 ) {
722 for (
723 entity,
724 camera,
725 shadow_bindings,
726 cluster_bindings,
727 msaa,
728 ssao_resources,
729 prepass_textures,
730 transmission_texture,
731 atmosphere_textures,
732 tonemapping,
733 (render_view_environment_maps, render_view_irradiance_volumes),
734 has_atmosphere,
735 (
736 view_uniform_offset,
737 view_lights_offset,
738 view_light_probes_offset,
739 view_fog_offset,
740 view_ssr_offset,
741 view_contact_shadows_offset,
742 view_oit_settings_offset,
743 ),
744 ) in &views
745 {
746 let mut entries = DynamicBindGroupEntries::new();
747 let mut entries_binding_array = DynamicBindGroupEntries::new();
748 #[cfg(not(all(target_arch = "wasm32", target_feature = "atomics")))]
749 {
750 entries.entries = core::mem::take(&mut *entries_cache)
753 .into_iter()
754 .map(|_| -> BindGroupEntry { unreachable!() })
755 .collect();
756 entries_binding_array.entries = core::mem::take(&mut *entries_binding_array_cache)
757 .into_iter()
758 .map(|_| -> BindGroupEntry { unreachable!() })
759 .collect();
760 }
761
762 let tonemap_in_shader = camera.is_none_or(|camera| !camera.hdr);
763 let mut layout_key = MeshPipelineViewLayoutKey::from(*msaa)
764 | MeshPipelineViewLayoutKey::from(prepass_textures);
765 let mut offsets = ArrayVec::from_iter([
766 view_uniform_offset.offset,
767 view_lights_offset.offset,
768 **view_light_probes_offset,
769 ]);
770
771 entries = entries.extend_with_indices((
772 (0, view_binding.clone()),
773 (1, light_binding.clone()),
774 (2, &shadow_bindings.point_light_depth_texture_view),
775 (3, &shadow_samplers.point_light_comparison_sampler),
776 #[cfg(feature = "experimental_pbr_pcss")]
777 (4, &shadow_samplers.point_light_linear_sampler),
778 (5, &shadow_bindings.directional_light_depth_texture_view),
779 (6, &shadow_samplers.directional_light_comparison_sampler),
780 #[cfg(feature = "experimental_pbr_pcss")]
781 (7, &shadow_samplers.directional_light_linear_sampler),
782 (8, clusterable_objects_binding.clone()),
783 (
784 9,
785 cluster_bindings
786 .clusterable_object_index_lists_binding()
787 .unwrap(),
788 ),
789 (10, cluster_bindings.offsets_and_counts_binding().unwrap()),
790 (11, globals.clone()),
791 (12, light_probes_binding.clone()),
792 (14, visibility_ranges_buffer.as_entire_binding()),
793 ));
794
795 if let Some(view_fog_offset) = view_fog_offset {
796 layout_key |= MeshPipelineViewLayoutKey::DISTANCE_FOG;
797 offsets.push(view_fog_offset.offset);
798 entries =
799 entries.extend_with_indices(((13, fog_meta.gpu_fogs.binding().unwrap()),));
800 }
801
802 if let Some(view_ssr_offset) = view_ssr_offset {
803 layout_key |= MeshPipelineViewLayoutKey::SCREEN_SPACE_REFLECTIONS;
804 offsets.push(**view_ssr_offset);
805 entries = entries.extend_with_indices(((15, ssr_buffer.binding().unwrap()),));
806 }
807
808 if let Some(view_contact_shadows_offset) = view_contact_shadows_offset {
809 layout_key |= MeshPipelineViewLayoutKey::CONTACT_SHADOWS;
810 offsets.push(**view_contact_shadows_offset);
811 entries = entries
812 .extend_with_indices(((16, contact_shadows_buffer.0.binding().unwrap()),));
813 }
814
815 if let Some(view_oit_settings_offset) = view_oit_settings_offset {
816 layout_key |= MeshPipelineViewLayoutKey::OIT_ENABLED;
817 offsets.push(view_oit_settings_offset.offset);
818 entries = entries.extend_with_indices((
819 (26, oit_buffers.settings.binding().unwrap()),
820 (27, oit_buffers.nodes_capacity.binding().unwrap()),
821 (28, oit_buffers.nodes.binding().unwrap()),
822 (29, oit_buffers.heads.binding().unwrap()),
823 (30, oit_buffers.atomic_counter.binding().unwrap()),
824 ));
825 }
826
827 if has_atmosphere
828 && let Some(atmosphere_textures) = atmosphere_textures
829 && let Some(atmosphere_buffer) = atmosphere_buffer.as_ref()
830 && let Some(atmosphere_sampler) = atmosphere_sampler.as_ref()
831 && let Some(atmosphere_buffer_binding) = atmosphere_buffer.buffer.binding()
832 {
833 layout_key |= MeshPipelineViewLayoutKey::ATMOSPHERE;
834 entries = entries.extend_with_indices((
835 (31, &atmosphere_textures.transmittance_lut.default_view),
836 (32, &***atmosphere_sampler),
837 (33, atmosphere_buffer_binding),
838 ));
839 }
840
841 if cfg!(feature = "bluenoise_texture") {
842 layout_key |= MeshPipelineViewLayoutKey::STBN;
843 let stbn_view = &images
844 .get(&blue_noise.texture)
845 .expect("STBN texture is added unconditionally with at least a placeholder")
846 .texture_view;
847 entries = entries.extend_with_indices(((34, stbn_view),));
848 }
849
850 if tonemap_in_shader {
851 layout_key |= MeshPipelineViewLayoutKey::TONEMAP_IN_SHADER;
852 let lut_bindings =
853 get_lut_bindings(&images, &tonemapping_luts, tonemapping, &fallback_image);
854 entries = entries.extend_with_indices((
855 (TONEMAPPING_LUT_TEXTURE_BINDING_INDEX, lut_bindings.0),
856 (TONEMAPPING_LUT_SAMPLER_BINDING_INDEX, lut_bindings.1),
857 ));
858 }
859
860 if let Some(ssao_resources) = ssao_resources {
861 layout_key |= MeshPipelineViewLayoutKey::SCREEN_SPACE_AMBIENT_OCCLUSION;
862 let ssao_view = &ssao_resources
863 .screen_space_ambient_occlusion_texture
864 .default_view;
865 entries = entries.extend_with_indices(((17, ssao_view),));
866 }
867
868 let transmission_view = transmission_texture
869 .map(|transmission| &transmission.view)
870 .unwrap_or(&fallback_image_zero.texture_view);
871
872 let transmission_sampler = transmission_texture
873 .map(|transmission| &transmission.sampler)
874 .unwrap_or(&fallback_image_zero.sampler);
875
876 entries =
877 entries.extend_with_indices(((24, transmission_view), (25, transmission_sampler)));
878
879 let prepass_bindings;
882 if cfg!(any(feature = "webgpu", not(target_arch = "wasm32"))) || msaa.samples() == 1 {
883 prepass_bindings = prepass::get_bindings(prepass_textures);
884 for (binding, index) in prepass_bindings
885 .iter()
886 .map(Option::as_ref)
887 .zip([20, 21, 22, 23])
888 .flat_map(|(b, i)| b.map(|b| (b, i)))
889 {
890 entries = entries.extend_with_indices(((index, binding),));
891 }
892 };
893
894 if cfg!(feature = "area_light_luts") {
896 let (ltc_view, ltc_sampler) = images
897 .get(&area_light_luts.image)
898 .map(|img| (&img.texture_view, &img.sampler))
899 .unwrap_or((
900 &fallback_image.d2_array.texture_view,
901 &fallback_image.d2_array.sampler,
902 ));
903 entries = entries.extend_with_indices(((35, ltc_view), (36, ltc_sampler)));
904 }
905
906 if cfg!(feature = "dfg_lut") {
908 let (dfg_view, dfg_sampler) = images
909 .get(&dfg_lut.texture)
910 .map(|img| (&img.texture_view, &img.sampler))
911 .unwrap_or((&fallback_image.d2.texture_view, &fallback_image.d2.sampler));
912 entries = entries.extend_with_indices(((37, dfg_view), (38, dfg_sampler)));
913 }
914
915 let environment_map_bind_group_entries =
916 render_view_environment_maps.map(|render_view_environment_maps| {
917 layout_key |= MeshPipelineViewLayoutKey::ENVIRONMENT_MAP;
918
919 RenderViewEnvironmentMapBindGroupEntries::get(
920 Some(render_view_environment_maps),
921 &images,
922 &fallback_image,
923 &render_device,
924 &render_adapter,
925 )
926 });
927 match environment_map_bind_group_entries {
928 Some(RenderViewEnvironmentMapBindGroupEntries::Single {
929 diffuse_texture_view,
930 specular_texture_view,
931 sampler,
932 }) => {
933 entries_binding_array = entries_binding_array.extend_with_indices((
934 (0, diffuse_texture_view),
935 (1, specular_texture_view),
936 (2, sampler),
937 ));
938 }
939 Some(RenderViewEnvironmentMapBindGroupEntries::Multiple {
940 ref diffuse_texture_views,
941 ref specular_texture_views,
942 sampler,
943 }) => {
944 entries_binding_array = entries_binding_array.extend_with_indices((
945 (0, diffuse_texture_views.as_slice()),
946 (1, specular_texture_views.as_slice()),
947 (2, sampler),
948 ));
949 }
950 None => {}
951 }
952
953 let irradiance_volume_bind_group_entries =
954 if render_view_irradiance_volumes.is_some() && IRRADIANCE_VOLUMES_ARE_USABLE {
955 layout_key |= MeshPipelineViewLayoutKey::IRRADIANCE_VOLUME;
956
957 Some(RenderViewIrradianceVolumeBindGroupEntries::get(
958 render_view_irradiance_volumes,
959 &images,
960 &fallback_image,
961 &render_device,
962 &render_adapter,
963 ))
964 } else {
965 None
966 };
967
968 match irradiance_volume_bind_group_entries {
969 Some(RenderViewIrradianceVolumeBindGroupEntries::Single {
970 texture_view,
971 sampler,
972 }) => {
973 entries_binding_array = entries_binding_array
974 .extend_with_indices(((3, texture_view), (4, sampler)));
975 }
976 Some(RenderViewIrradianceVolumeBindGroupEntries::Multiple {
977 ref texture_views,
978 sampler,
979 }) => {
980 entries_binding_array = entries_binding_array
981 .extend_with_indices(((3, texture_views.as_slice()), (4, sampler)));
982 }
983 None => {}
984 }
985
986 let decal_bind_group_entries = RenderViewClusteredDecalBindGroupEntries::get(
987 &render_decals,
988 &decals_buffer,
989 &images,
990 &fallback_image,
991 &render_device,
992 &render_adapter,
993 );
994
995 if let Some(ref render_view_decal_bind_group_entries) = decal_bind_group_entries {
997 entries_binding_array = entries_binding_array.extend_with_indices((
998 (
1000 5,
1001 render_view_decal_bind_group_entries
1002 .decals
1003 .as_entire_binding(),
1004 ),
1005 (
1007 6,
1008 render_view_decal_bind_group_entries
1009 .texture_views
1010 .as_slice(),
1011 ),
1012 (7, render_view_decal_bind_group_entries.sampler),
1014 ));
1015 }
1016
1017 let layout = mesh_pipeline.get_view_layout(layout_key);
1018 commands.entity(entity).insert((MeshViewBindGroup {
1019 main_offsets: offsets,
1020 main: render_device.create_bind_group(
1021 "mesh_view_bind_group",
1022 &pipeline_cache.get_bind_group_layout(&layout.main_layout),
1023 &entries,
1024 ),
1025 binding_array: render_device.create_bind_group(
1026 "mesh_view_bind_group_binding_array",
1027 &pipeline_cache.get_bind_group_layout(&layout.binding_array_layout),
1028 &entries_binding_array,
1029 ),
1030 empty: render_device.create_bind_group(
1031 "mesh_view_bind_group_empty",
1032 &pipeline_cache.get_bind_group_layout(&layout.empty_layout),
1033 &[],
1034 ),
1035 },));
1036
1037 #[cfg(not(all(target_arch = "wasm32", target_feature = "atomics")))]
1038 {
1039 entries.entries.clear();
1040 entries_binding_array.entries.clear();
1041 *entries_cache = entries
1042 .entries
1043 .into_iter()
1044 .map(|_| -> BindGroupEntry<'static> { unreachable!() })
1045 .collect();
1046 *entries_binding_array_cache = entries_binding_array
1047 .entries
1048 .into_iter()
1049 .map(|_| -> BindGroupEntry<'static> { unreachable!() })
1050 .collect();
1051 }
1052 }
1053 }
1054}