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