bevy_pbr/render/
mesh_view_bindings.rs

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