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