bevy_pbr/render/
mesh_view_bindings.rs

1use alloc::sync::Arc;
2use bevy_core_pipeline::{
3    core_3d::ViewTransmissionTexture,
4    oit::{resolve::is_oit_supported, OitBuffers, OrderIndependentTransparencySettings},
5    prepass::ViewPrepassTextures,
6    tonemapping::{
7        get_lut_bind_group_layout_entries, get_lut_bindings, Tonemapping, TonemappingLuts,
8    },
9};
10use bevy_derive::{Deref, DerefMut};
11use bevy_ecs::{
12    component::Component,
13    entity::Entity,
14    query::Has,
15    resource::Resource,
16    system::{Commands, Query, Res},
17    world::{FromWorld, World},
18};
19use bevy_image::BevyDefault as _;
20use bevy_light::{EnvironmentMapLight, IrradianceVolume};
21use bevy_math::Vec4;
22use bevy_render::{
23    globals::{GlobalsBuffer, GlobalsUniform},
24    render_asset::RenderAssets,
25    render_resource::{binding_types::*, *},
26    renderer::{RenderAdapter, RenderDevice},
27    texture::{FallbackImage, FallbackImageMsaa, FallbackImageZero, GpuImage},
28    view::{
29        Msaa, RenderVisibilityRanges, ViewUniform, ViewUniforms,
30        VISIBILITY_RANGES_STORAGE_BUFFER_COUNT,
31    },
32};
33use core::{array, num::NonZero};
34
35use crate::{
36    decal::{
37        self,
38        clustered::{
39            DecalsBuffer, RenderClusteredDecals, RenderViewClusteredDecalBindGroupEntries,
40        },
41    },
42    environment_map::{self, RenderViewEnvironmentMapBindGroupEntries},
43    irradiance_volume::{
44        self, RenderViewIrradianceVolumeBindGroupEntries, IRRADIANCE_VOLUMES_ARE_USABLE,
45    },
46    prepass,
47    resources::{AtmosphereBuffer, AtmosphereData, AtmosphereSampler, AtmosphereTextures},
48    EnvironmentMapUniformBuffer, ExtractedAtmosphere, FogMeta, GlobalClusterableObjectMeta,
49    GpuClusterableObjects, GpuFog, GpuLights, LightMeta, LightProbesBuffer, LightProbesUniform,
50    MeshPipeline, MeshPipelineKey, RenderViewLightProbes, ScreenSpaceAmbientOcclusionResources,
51    ScreenSpaceReflectionsBuffer, ScreenSpaceReflectionsUniform, ShadowSamplers,
52    ViewClusterBindings, ViewShadowBindings, CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT,
53};
54
55#[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
56use bevy_render::render_resource::binding_types::texture_cube;
57
58#[cfg(debug_assertions)]
59use {crate::MESH_PIPELINE_VIEW_LAYOUT_SAFE_MAX_TEXTURES, bevy_utils::once, tracing::warn};
60
61#[derive(Clone)]
62pub struct MeshPipelineViewLayout {
63    pub main_layout: BindGroupLayoutDescriptor,
64    pub binding_array_layout: BindGroupLayoutDescriptor,
65    pub empty_layout: BindGroupLayoutDescriptor,
66
67    #[cfg(debug_assertions)]
68    pub texture_count: usize,
69}
70
71bitflags::bitflags! {
72    /// A key that uniquely identifies a [`MeshPipelineViewLayout`].
73    ///
74    /// Used to generate all possible layouts for the mesh pipeline in [`generate_view_layouts`],
75    /// so special care must be taken to not add too many flags, as the number of possible layouts
76    /// will grow exponentially.
77    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
78    #[repr(transparent)]
79    pub struct MeshPipelineViewLayoutKey: u32 {
80        const MULTISAMPLED                = 1 << 0;
81        const DEPTH_PREPASS               = 1 << 1;
82        const NORMAL_PREPASS              = 1 << 2;
83        const MOTION_VECTOR_PREPASS       = 1 << 3;
84        const DEFERRED_PREPASS            = 1 << 4;
85        const OIT_ENABLED                 = 1 << 5;
86        const ATMOSPHERE                  = 1 << 6;
87    }
88}
89
90impl MeshPipelineViewLayoutKey {
91    // The number of possible layouts
92    pub const COUNT: usize = Self::all().bits() as usize + 1;
93
94    /// Builds a unique label for each layout based on the flags
95    pub fn label(&self) -> String {
96        use MeshPipelineViewLayoutKey as Key;
97
98        format!(
99            "mesh_view_layout{}{}{}{}{}{}{}",
100            if self.contains(Key::MULTISAMPLED) {
101                "_multisampled"
102            } else {
103                Default::default()
104            },
105            if self.contains(Key::DEPTH_PREPASS) {
106                "_depth"
107            } else {
108                Default::default()
109            },
110            if self.contains(Key::NORMAL_PREPASS) {
111                "_normal"
112            } else {
113                Default::default()
114            },
115            if self.contains(Key::MOTION_VECTOR_PREPASS) {
116                "_motion"
117            } else {
118                Default::default()
119            },
120            if self.contains(Key::DEFERRED_PREPASS) {
121                "_deferred"
122            } else {
123                Default::default()
124            },
125            if self.contains(Key::OIT_ENABLED) {
126                "_oit"
127            } else {
128                Default::default()
129            },
130            if self.contains(Key::ATMOSPHERE) {
131                "_atmosphere"
132            } else {
133                Default::default()
134            },
135        )
136    }
137}
138
139impl From<MeshPipelineKey> for MeshPipelineViewLayoutKey {
140    fn from(value: MeshPipelineKey) -> Self {
141        let mut result = MeshPipelineViewLayoutKey::empty();
142
143        if value.msaa_samples() > 1 {
144            result |= MeshPipelineViewLayoutKey::MULTISAMPLED;
145        }
146        if value.contains(MeshPipelineKey::DEPTH_PREPASS) {
147            result |= MeshPipelineViewLayoutKey::DEPTH_PREPASS;
148        }
149        if value.contains(MeshPipelineKey::NORMAL_PREPASS) {
150            result |= MeshPipelineViewLayoutKey::NORMAL_PREPASS;
151        }
152        if value.contains(MeshPipelineKey::MOTION_VECTOR_PREPASS) {
153            result |= MeshPipelineViewLayoutKey::MOTION_VECTOR_PREPASS;
154        }
155        if value.contains(MeshPipelineKey::DEFERRED_PREPASS) {
156            result |= MeshPipelineViewLayoutKey::DEFERRED_PREPASS;
157        }
158        if value.contains(MeshPipelineKey::OIT_ENABLED) {
159            result |= MeshPipelineViewLayoutKey::OIT_ENABLED;
160        }
161        if value.contains(MeshPipelineKey::ATMOSPHERE) {
162            result |= MeshPipelineViewLayoutKey::ATMOSPHERE;
163        }
164
165        result
166    }
167}
168
169impl From<Msaa> for MeshPipelineViewLayoutKey {
170    fn from(value: Msaa) -> Self {
171        let mut result = MeshPipelineViewLayoutKey::empty();
172
173        if value.samples() > 1 {
174            result |= MeshPipelineViewLayoutKey::MULTISAMPLED;
175        }
176
177        result
178    }
179}
180
181impl From<Option<&ViewPrepassTextures>> for MeshPipelineViewLayoutKey {
182    fn from(value: Option<&ViewPrepassTextures>) -> Self {
183        let mut result = MeshPipelineViewLayoutKey::empty();
184
185        if let Some(prepass_textures) = value {
186            if prepass_textures.depth.is_some() {
187                result |= MeshPipelineViewLayoutKey::DEPTH_PREPASS;
188            }
189            if prepass_textures.normal.is_some() {
190                result |= MeshPipelineViewLayoutKey::NORMAL_PREPASS;
191            }
192            if prepass_textures.motion_vectors.is_some() {
193                result |= MeshPipelineViewLayoutKey::MOTION_VECTOR_PREPASS;
194            }
195            if prepass_textures.deferred.is_some() {
196                result |= MeshPipelineViewLayoutKey::DEFERRED_PREPASS;
197            }
198        }
199
200        result
201    }
202}
203
204pub(crate) fn buffer_layout(
205    buffer_binding_type: BufferBindingType,
206    has_dynamic_offset: bool,
207    min_binding_size: Option<NonZero<u64>>,
208) -> BindGroupLayoutEntryBuilder {
209    match buffer_binding_type {
210        BufferBindingType::Uniform => uniform_buffer_sized(has_dynamic_offset, min_binding_size),
211        BufferBindingType::Storage { read_only } => {
212            if read_only {
213                storage_buffer_read_only_sized(has_dynamic_offset, min_binding_size)
214            } else {
215                storage_buffer_sized(has_dynamic_offset, min_binding_size)
216            }
217        }
218    }
219}
220
221/// Returns the appropriate bind group layout vec based on the parameters
222fn layout_entries(
223    clustered_forward_buffer_binding_type: BufferBindingType,
224    visibility_ranges_buffer_binding_type: BufferBindingType,
225    layout_key: MeshPipelineViewLayoutKey,
226    render_device: &RenderDevice,
227    render_adapter: &RenderAdapter,
228) -> [Vec<BindGroupLayoutEntry>; 2] {
229    // EnvironmentMapLight
230    let environment_map_entries =
231        environment_map::get_bind_group_layout_entries(render_device, render_adapter);
232
233    let mut entries = DynamicBindGroupLayoutEntries::new_with_indices(
234        ShaderStages::FRAGMENT,
235        (
236            // View
237            (
238                0,
239                uniform_buffer::<ViewUniform>(true).visibility(ShaderStages::VERTEX_FRAGMENT),
240            ),
241            // Lights
242            (1, uniform_buffer::<GpuLights>(true)),
243            // Point Shadow Texture Cube Array
244            (
245                2,
246                #[cfg(all(
247                    not(target_abi = "sim"),
248                    any(
249                        not(feature = "webgl"),
250                        not(target_arch = "wasm32"),
251                        feature = "webgpu"
252                    )
253                ))]
254                texture_cube_array(TextureSampleType::Depth),
255                #[cfg(any(
256                    target_abi = "sim",
257                    all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu"))
258                ))]
259                texture_cube(TextureSampleType::Depth),
260            ),
261            // Point Shadow Texture Array Comparison Sampler
262            (3, sampler(SamplerBindingType::Comparison)),
263            // Point Shadow Texture Array Linear Sampler
264            #[cfg(feature = "experimental_pbr_pcss")]
265            (4, sampler(SamplerBindingType::Filtering)),
266            // Directional Shadow Texture Array
267            (
268                5,
269                #[cfg(any(
270                    not(feature = "webgl"),
271                    not(target_arch = "wasm32"),
272                    feature = "webgpu"
273                ))]
274                texture_2d_array(TextureSampleType::Depth),
275                #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
276                texture_2d(TextureSampleType::Depth),
277            ),
278            // Directional Shadow Texture Array Comparison Sampler
279            (6, sampler(SamplerBindingType::Comparison)),
280            // Directional Shadow Texture Array Linear Sampler
281            #[cfg(feature = "experimental_pbr_pcss")]
282            (7, sampler(SamplerBindingType::Filtering)),
283            // PointLights
284            (
285                8,
286                buffer_layout(
287                    clustered_forward_buffer_binding_type,
288                    false,
289                    Some(GpuClusterableObjects::min_size(
290                        clustered_forward_buffer_binding_type,
291                    )),
292                ),
293            ),
294            // ClusteredLightIndexLists
295            (
296                9,
297                buffer_layout(
298                    clustered_forward_buffer_binding_type,
299                    false,
300                    Some(
301                        ViewClusterBindings::min_size_clusterable_object_index_lists(
302                            clustered_forward_buffer_binding_type,
303                        ),
304                    ),
305                ),
306            ),
307            // ClusterOffsetsAndCounts
308            (
309                10,
310                buffer_layout(
311                    clustered_forward_buffer_binding_type,
312                    false,
313                    Some(ViewClusterBindings::min_size_cluster_offsets_and_counts(
314                        clustered_forward_buffer_binding_type,
315                    )),
316                ),
317            ),
318            // Globals
319            (
320                11,
321                uniform_buffer::<GlobalsUniform>(false).visibility(ShaderStages::VERTEX_FRAGMENT),
322            ),
323            // Fog
324            (12, uniform_buffer::<GpuFog>(true)),
325            // Light probes
326            (13, uniform_buffer::<LightProbesUniform>(true)),
327            // Visibility ranges
328            (
329                14,
330                buffer_layout(
331                    visibility_ranges_buffer_binding_type,
332                    false,
333                    Some(Vec4::min_size()),
334                )
335                .visibility(ShaderStages::VERTEX),
336            ),
337            // Screen space reflection settings
338            (15, uniform_buffer::<ScreenSpaceReflectionsUniform>(true)),
339            // Screen space ambient occlusion texture
340            (
341                16,
342                texture_2d(TextureSampleType::Float { filterable: false }),
343            ),
344            (17, environment_map_entries[3]),
345        ),
346    );
347
348    // Tonemapping
349    let tonemapping_lut_entries = get_lut_bind_group_layout_entries();
350    entries = entries.extend_with_indices((
351        (18, tonemapping_lut_entries[0]),
352        (19, tonemapping_lut_entries[1]),
353    ));
354
355    // Prepass
356    if cfg!(any(not(feature = "webgl"), not(target_arch = "wasm32")))
357        || (cfg!(all(feature = "webgl", target_arch = "wasm32"))
358            && !layout_key.contains(MeshPipelineViewLayoutKey::MULTISAMPLED))
359    {
360        for (entry, binding) in prepass::get_bind_group_layout_entries(layout_key)
361            .iter()
362            .zip([20, 21, 22, 23])
363        {
364            if let Some(entry) = entry {
365                entries = entries.extend_with_indices(((binding as u32, *entry),));
366            }
367        }
368    }
369
370    // View Transmission Texture
371    entries = entries.extend_with_indices((
372        (
373            24,
374            texture_2d(TextureSampleType::Float { filterable: true }),
375        ),
376        (25, sampler(SamplerBindingType::Filtering)),
377    ));
378
379    // OIT
380    if layout_key.contains(MeshPipelineViewLayoutKey::OIT_ENABLED) {
381        // Check if we can use OIT. This is a hack to avoid errors on webgl --
382        // the OIT plugin will warn the user that OIT is not supported on their
383        // platform, so we don't need to do it here.
384        if is_oit_supported(render_adapter, render_device, false) {
385            entries = entries.extend_with_indices((
386                // oit_layers
387                (26, storage_buffer_sized(false, None)),
388                // oit_layer_ids,
389                (27, storage_buffer_sized(false, None)),
390                // oit_layer_count
391                (
392                    28,
393                    uniform_buffer::<OrderIndependentTransparencySettings>(true),
394                ),
395            ));
396        }
397    }
398
399    // Atmosphere
400    if layout_key.contains(MeshPipelineViewLayoutKey::ATMOSPHERE) {
401        entries = entries.extend_with_indices((
402            // transmittance LUT
403            (
404                29,
405                texture_2d(TextureSampleType::Float { filterable: true }),
406            ),
407            (30, sampler(SamplerBindingType::Filtering)),
408            // atmosphere data buffer
409            (31, storage_buffer_read_only::<AtmosphereData>(false)),
410        ));
411    }
412
413    let mut binding_array_entries = DynamicBindGroupLayoutEntries::new(ShaderStages::FRAGMENT);
414    binding_array_entries = binding_array_entries.extend_with_indices((
415        (0, environment_map_entries[0]),
416        (1, environment_map_entries[1]),
417        (2, environment_map_entries[2]),
418    ));
419
420    // Irradiance volumes
421    if IRRADIANCE_VOLUMES_ARE_USABLE {
422        let irradiance_volume_entries =
423            irradiance_volume::get_bind_group_layout_entries(render_device, render_adapter);
424        binding_array_entries = binding_array_entries.extend_with_indices((
425            (3, irradiance_volume_entries[0]),
426            (4, irradiance_volume_entries[1]),
427        ));
428    }
429
430    // Clustered decals
431    if let Some(clustered_decal_entries) =
432        decal::clustered::get_bind_group_layout_entries(render_device, render_adapter)
433    {
434        binding_array_entries = binding_array_entries.extend_with_indices((
435            (5, clustered_decal_entries[0]),
436            (6, clustered_decal_entries[1]),
437            (7, clustered_decal_entries[2]),
438        ));
439    }
440
441    [entries.to_vec(), binding_array_entries.to_vec()]
442}
443
444/// Stores the view layouts for every combination of pipeline keys.
445///
446/// This is wrapped in an [`Arc`] so that it can be efficiently cloned and
447/// placed inside specializable pipeline types.
448#[derive(Resource, Clone, Deref, DerefMut)]
449pub struct MeshPipelineViewLayouts(
450    pub Arc<[MeshPipelineViewLayout; MeshPipelineViewLayoutKey::COUNT]>,
451);
452
453impl FromWorld for MeshPipelineViewLayouts {
454    fn from_world(world: &mut World) -> Self {
455        // Generates all possible view layouts for the mesh pipeline, based on all combinations of
456        // [`MeshPipelineViewLayoutKey`] flags.
457
458        let render_device = world.resource::<RenderDevice>();
459        let render_adapter = world.resource::<RenderAdapter>();
460
461        let clustered_forward_buffer_binding_type = render_device
462            .get_supported_read_only_binding_type(CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT);
463        let visibility_ranges_buffer_binding_type = render_device
464            .get_supported_read_only_binding_type(VISIBILITY_RANGES_STORAGE_BUFFER_COUNT);
465
466        Self(Arc::new(array::from_fn(|i| {
467            let key = MeshPipelineViewLayoutKey::from_bits_truncate(i as u32);
468            let entries = layout_entries(
469                clustered_forward_buffer_binding_type,
470                visibility_ranges_buffer_binding_type,
471                key,
472                render_device,
473                render_adapter,
474            );
475            #[cfg(debug_assertions)]
476            let texture_count: usize = entries
477                .iter()
478                .flat_map(|e| {
479                    e.iter()
480                        .filter(|entry| matches!(entry.ty, BindingType::Texture { .. }))
481                })
482                .count();
483
484            MeshPipelineViewLayout {
485                main_layout: BindGroupLayoutDescriptor::new(key.label(), &entries[0]),
486                binding_array_layout: BindGroupLayoutDescriptor::new(
487                    format!("{}_binding_array", key.label()),
488                    &entries[1],
489                ),
490                empty_layout: BindGroupLayoutDescriptor::new(format!("{}_empty", key.label()), &[]),
491                #[cfg(debug_assertions)]
492                texture_count,
493            }
494        })))
495    }
496}
497
498impl MeshPipelineViewLayouts {
499    pub fn get_view_layout(
500        &self,
501        layout_key: MeshPipelineViewLayoutKey,
502    ) -> &MeshPipelineViewLayout {
503        let index = layout_key.bits() as usize;
504        let layout = &self[index];
505
506        #[cfg(debug_assertions)]
507        if layout.texture_count > MESH_PIPELINE_VIEW_LAYOUT_SAFE_MAX_TEXTURES {
508            // Issue our own warning here because Naga's error message is a bit cryptic in this situation
509            once!(warn!("Too many textures in mesh pipeline view layout, this might cause us to hit `wgpu::Limits::max_sampled_textures_per_shader_stage` in some environments."));
510        }
511
512        layout
513    }
514}
515
516/// Generates all possible view layouts for the mesh pipeline, based on all combinations of
517/// [`MeshPipelineViewLayoutKey`] flags.
518pub fn generate_view_layouts(
519    render_device: &RenderDevice,
520    render_adapter: &RenderAdapter,
521    clustered_forward_buffer_binding_type: BufferBindingType,
522    visibility_ranges_buffer_binding_type: BufferBindingType,
523) -> [MeshPipelineViewLayout; MeshPipelineViewLayoutKey::COUNT] {
524    array::from_fn(|i| {
525        let key = MeshPipelineViewLayoutKey::from_bits_truncate(i as u32);
526        let entries = layout_entries(
527            clustered_forward_buffer_binding_type,
528            visibility_ranges_buffer_binding_type,
529            key,
530            render_device,
531            render_adapter,
532        );
533
534        #[cfg(debug_assertions)]
535        let texture_count: usize = entries
536            .iter()
537            .flat_map(|e| {
538                e.iter()
539                    .filter(|entry| matches!(entry.ty, BindingType::Texture { .. }))
540            })
541            .count();
542
543        MeshPipelineViewLayout {
544            main_layout: BindGroupLayoutDescriptor::new(key.label(), &entries[0]),
545            binding_array_layout: BindGroupLayoutDescriptor::new(
546                format!("{}_binding_array", key.label()),
547                &entries[1],
548            ),
549            empty_layout: BindGroupLayoutDescriptor::new(format!("{}_empty", key.label()), &[]),
550            #[cfg(debug_assertions)]
551            texture_count,
552        }
553    })
554}
555
556#[derive(Component)]
557pub struct MeshViewBindGroup {
558    pub main: BindGroup,
559    pub binding_array: BindGroup,
560    pub empty: BindGroup,
561}
562
563pub fn prepare_mesh_view_bind_groups(
564    mut commands: Commands,
565    (render_device, pipeline_cache, render_adapter): (
566        Res<RenderDevice>,
567        Res<PipelineCache>,
568        Res<RenderAdapter>,
569    ),
570    mesh_pipeline: Res<MeshPipeline>,
571    shadow_samplers: Res<ShadowSamplers>,
572    (light_meta, global_light_meta): (Res<LightMeta>, Res<GlobalClusterableObjectMeta>),
573    fog_meta: Res<FogMeta>,
574    (view_uniforms, environment_map_uniform): (Res<ViewUniforms>, Res<EnvironmentMapUniformBuffer>),
575    views: Query<(
576        Entity,
577        &ViewShadowBindings,
578        &ViewClusterBindings,
579        &Msaa,
580        Option<&ScreenSpaceAmbientOcclusionResources>,
581        Option<&ViewPrepassTextures>,
582        Option<&ViewTransmissionTexture>,
583        &Tonemapping,
584        Option<&RenderViewLightProbes<EnvironmentMapLight>>,
585        Option<&RenderViewLightProbes<IrradianceVolume>>,
586        Has<OrderIndependentTransparencySettings>,
587        Option<&AtmosphereTextures>,
588        Has<ExtractedAtmosphere>,
589    )>,
590    (images, mut fallback_images, fallback_image, fallback_image_zero): (
591        Res<RenderAssets<GpuImage>>,
592        FallbackImageMsaa,
593        Res<FallbackImage>,
594        Res<FallbackImageZero>,
595    ),
596    globals_buffer: Res<GlobalsBuffer>,
597    tonemapping_luts: Res<TonemappingLuts>,
598    light_probes_buffer: Res<LightProbesBuffer>,
599    visibility_ranges: Res<RenderVisibilityRanges>,
600    ssr_buffer: Res<ScreenSpaceReflectionsBuffer>,
601    oit_buffers: Res<OitBuffers>,
602    (decals_buffer, render_decals, atmosphere_buffer, atmosphere_sampler): (
603        Res<DecalsBuffer>,
604        Res<RenderClusteredDecals>,
605        Option<Res<AtmosphereBuffer>>,
606        Option<Res<AtmosphereSampler>>,
607    ),
608) {
609    if let (
610        Some(view_binding),
611        Some(light_binding),
612        Some(clusterable_objects_binding),
613        Some(globals),
614        Some(fog_binding),
615        Some(light_probes_binding),
616        Some(visibility_ranges_buffer),
617        Some(ssr_binding),
618        Some(environment_map_binding),
619    ) = (
620        view_uniforms.uniforms.binding(),
621        light_meta.view_gpu_lights.binding(),
622        global_light_meta.gpu_clusterable_objects.binding(),
623        globals_buffer.buffer.binding(),
624        fog_meta.gpu_fogs.binding(),
625        light_probes_buffer.binding(),
626        visibility_ranges.buffer().buffer(),
627        ssr_buffer.binding(),
628        environment_map_uniform.binding(),
629    ) {
630        for (
631            entity,
632            shadow_bindings,
633            cluster_bindings,
634            msaa,
635            ssao_resources,
636            prepass_textures,
637            transmission_texture,
638            tonemapping,
639            render_view_environment_maps,
640            render_view_irradiance_volumes,
641            has_oit,
642            atmosphere_textures,
643            has_atmosphere,
644        ) in &views
645        {
646            let fallback_ssao = fallback_images
647                .image_for_samplecount(1, TextureFormat::bevy_default())
648                .texture_view
649                .clone();
650            let ssao_view = ssao_resources
651                .map(|t| &t.screen_space_ambient_occlusion_texture.default_view)
652                .unwrap_or(&fallback_ssao);
653
654            let mut layout_key = MeshPipelineViewLayoutKey::from(*msaa)
655                | MeshPipelineViewLayoutKey::from(prepass_textures);
656            if has_oit {
657                layout_key |= MeshPipelineViewLayoutKey::OIT_ENABLED;
658            }
659            if has_atmosphere {
660                layout_key |= MeshPipelineViewLayoutKey::ATMOSPHERE;
661            }
662
663            let layout = mesh_pipeline.get_view_layout(layout_key);
664
665            let mut entries = DynamicBindGroupEntries::new_with_indices((
666                (0, view_binding.clone()),
667                (1, light_binding.clone()),
668                (2, &shadow_bindings.point_light_depth_texture_view),
669                (3, &shadow_samplers.point_light_comparison_sampler),
670                #[cfg(feature = "experimental_pbr_pcss")]
671                (4, &shadow_samplers.point_light_linear_sampler),
672                (5, &shadow_bindings.directional_light_depth_texture_view),
673                (6, &shadow_samplers.directional_light_comparison_sampler),
674                #[cfg(feature = "experimental_pbr_pcss")]
675                (7, &shadow_samplers.directional_light_linear_sampler),
676                (8, clusterable_objects_binding.clone()),
677                (
678                    9,
679                    cluster_bindings
680                        .clusterable_object_index_lists_binding()
681                        .unwrap(),
682                ),
683                (10, cluster_bindings.offsets_and_counts_binding().unwrap()),
684                (11, globals.clone()),
685                (12, fog_binding.clone()),
686                (13, light_probes_binding.clone()),
687                (14, visibility_ranges_buffer.as_entire_binding()),
688                (15, ssr_binding.clone()),
689                (16, ssao_view),
690            ));
691
692            entries = entries.extend_with_indices(((17, environment_map_binding.clone()),));
693
694            let lut_bindings =
695                get_lut_bindings(&images, &tonemapping_luts, tonemapping, &fallback_image);
696            entries = entries.extend_with_indices(((18, lut_bindings.0), (19, lut_bindings.1)));
697
698            // When using WebGL, we can't have a depth texture with multisampling
699            let prepass_bindings;
700            if cfg!(any(not(feature = "webgl"), not(target_arch = "wasm32"))) || msaa.samples() == 1
701            {
702                prepass_bindings = prepass::get_bindings(prepass_textures);
703                for (binding, index) in prepass_bindings
704                    .iter()
705                    .map(Option::as_ref)
706                    .zip([20, 21, 22, 23])
707                    .flat_map(|(b, i)| b.map(|b| (b, i)))
708                {
709                    entries = entries.extend_with_indices(((index, binding),));
710                }
711            };
712
713            let transmission_view = transmission_texture
714                .map(|transmission| &transmission.view)
715                .unwrap_or(&fallback_image_zero.texture_view);
716
717            let transmission_sampler = transmission_texture
718                .map(|transmission| &transmission.sampler)
719                .unwrap_or(&fallback_image_zero.sampler);
720
721            entries =
722                entries.extend_with_indices(((24, transmission_view), (25, transmission_sampler)));
723
724            if has_oit
725                && let (
726                    Some(oit_layers_binding),
727                    Some(oit_layer_ids_binding),
728                    Some(oit_settings_binding),
729                ) = (
730                    oit_buffers.layers.binding(),
731                    oit_buffers.layer_ids.binding(),
732                    oit_buffers.settings.binding(),
733                )
734            {
735                entries = entries.extend_with_indices((
736                    (26, oit_layers_binding.clone()),
737                    (27, oit_layer_ids_binding.clone()),
738                    (28, oit_settings_binding.clone()),
739                ));
740            }
741
742            if has_atmosphere
743                && let Some(atmosphere_textures) = atmosphere_textures
744                && let Some(atmosphere_buffer) = atmosphere_buffer.as_ref()
745                && let Some(atmosphere_sampler) = atmosphere_sampler.as_ref()
746                && let Some(atmosphere_buffer_binding) = atmosphere_buffer.buffer.binding()
747            {
748                entries = entries.extend_with_indices((
749                    (29, &atmosphere_textures.transmittance_lut.default_view),
750                    (30, &***atmosphere_sampler),
751                    (31, atmosphere_buffer_binding),
752                ));
753            }
754
755            let mut entries_binding_array = DynamicBindGroupEntries::new();
756
757            let environment_map_bind_group_entries = RenderViewEnvironmentMapBindGroupEntries::get(
758                render_view_environment_maps,
759                &images,
760                &fallback_image,
761                &render_device,
762                &render_adapter,
763            );
764            match environment_map_bind_group_entries {
765                RenderViewEnvironmentMapBindGroupEntries::Single {
766                    diffuse_texture_view,
767                    specular_texture_view,
768                    sampler,
769                } => {
770                    entries_binding_array = entries_binding_array.extend_with_indices((
771                        (0, diffuse_texture_view),
772                        (1, specular_texture_view),
773                        (2, sampler),
774                    ));
775                }
776                RenderViewEnvironmentMapBindGroupEntries::Multiple {
777                    ref diffuse_texture_views,
778                    ref specular_texture_views,
779                    sampler,
780                } => {
781                    entries_binding_array = entries_binding_array.extend_with_indices((
782                        (0, diffuse_texture_views.as_slice()),
783                        (1, specular_texture_views.as_slice()),
784                        (2, sampler),
785                    ));
786                }
787            }
788
789            let irradiance_volume_bind_group_entries = if IRRADIANCE_VOLUMES_ARE_USABLE {
790                Some(RenderViewIrradianceVolumeBindGroupEntries::get(
791                    render_view_irradiance_volumes,
792                    &images,
793                    &fallback_image,
794                    &render_device,
795                    &render_adapter,
796                ))
797            } else {
798                None
799            };
800
801            match irradiance_volume_bind_group_entries {
802                Some(RenderViewIrradianceVolumeBindGroupEntries::Single {
803                    texture_view,
804                    sampler,
805                }) => {
806                    entries_binding_array = entries_binding_array
807                        .extend_with_indices(((3, texture_view), (4, sampler)));
808                }
809                Some(RenderViewIrradianceVolumeBindGroupEntries::Multiple {
810                    ref texture_views,
811                    sampler,
812                }) => {
813                    entries_binding_array = entries_binding_array
814                        .extend_with_indices(((3, texture_views.as_slice()), (4, sampler)));
815                }
816                None => {}
817            }
818
819            let decal_bind_group_entries = RenderViewClusteredDecalBindGroupEntries::get(
820                &render_decals,
821                &decals_buffer,
822                &images,
823                &fallback_image,
824                &render_device,
825                &render_adapter,
826            );
827
828            // Add the decal bind group entries.
829            if let Some(ref render_view_decal_bind_group_entries) = decal_bind_group_entries {
830                entries_binding_array = entries_binding_array.extend_with_indices((
831                    // `clustered_decals`
832                    (
833                        5,
834                        render_view_decal_bind_group_entries
835                            .decals
836                            .as_entire_binding(),
837                    ),
838                    // `clustered_decal_textures`
839                    (
840                        6,
841                        render_view_decal_bind_group_entries
842                            .texture_views
843                            .as_slice(),
844                    ),
845                    // `clustered_decal_sampler`
846                    (7, render_view_decal_bind_group_entries.sampler),
847                ));
848            }
849
850            commands.entity(entity).insert(MeshViewBindGroup {
851                main: render_device.create_bind_group(
852                    "mesh_view_bind_group",
853                    &pipeline_cache.get_bind_group_layout(&layout.main_layout),
854                    &entries,
855                ),
856                binding_array: render_device.create_bind_group(
857                    "mesh_view_bind_group_binding_array",
858                    &pipeline_cache.get_bind_group_layout(&layout.binding_array_layout),
859                    &entries_binding_array,
860                ),
861                empty: render_device.create_bind_group(
862                    "mesh_view_bind_group_empty",
863                    &pipeline_cache.get_bind_group_layout(&layout.empty_layout),
864                    &[],
865                ),
866            });
867        }
868    }
869}