Skip to main content

bevy_pbr/render/
mesh_view_bindings.rs

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