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 #[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 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
243fn 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 (
260 0,
261 uniform_buffer::<ViewUniform>(true).visibility(ShaderStages::VERTEX_FRAGMENT),
262 ),
263 (1, uniform_buffer::<GpuLights>(true)),
265 (
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 (3, sampler(SamplerBindingType::Comparison)),
285 #[cfg(feature = "experimental_pbr_pcss")]
287 (4, sampler(SamplerBindingType::Filtering)),
288 (
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 (6, sampler(SamplerBindingType::Comparison)),
302 #[cfg(feature = "experimental_pbr_pcss")]
304 (7, sampler(SamplerBindingType::Filtering)),
305 (
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 (
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 (
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 (
342 11,
343 uniform_buffer::<GlobalsUniform>(false).visibility(ShaderStages::VERTEX_FRAGMENT),
344 ),
345 (12, uniform_buffer::<LightProbesUniform>(true)),
347 (
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 (13, uniform_buffer::<GpuFog>(true)),
364 ));
365 }
366 if layout_key.contains(MeshPipelineViewLayoutKey::SCREEN_SPACE_REFLECTIONS) {
367 entries = entries.extend_with_indices((
368 (15, uniform_buffer::<ScreenSpaceReflectionsUniform>(true)),
370 ));
371 }
372 if layout_key.contains(MeshPipelineViewLayoutKey::CONTACT_SHADOWS) {
373 entries = entries.extend_with_indices((
374 (16, uniform_buffer::<ContactShadowsUniform>(true)),
376 ));
377 }
378 if layout_key.contains(MeshPipelineViewLayoutKey::SCREEN_SPACE_AMBIENT_OCCLUSION) {
379 entries = entries.extend_with_indices((
380 (
382 17,
383 texture_2d(TextureSampleType::Float { filterable: false }),
384 ),
385 ));
386 }
387
388 if layout_key.contains(MeshPipelineViewLayoutKey::TONEMAP_IN_SHADER) {
389 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 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 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 if layout_key.contains(MeshPipelineViewLayoutKey::OIT_ENABLED) {
430 if is_oit_supported {
434 entries = entries.extend_with_indices((
435 (
436 26,
437 uniform_buffer::<OrderIndependentTransparencySettings>(true),
438 ),
439 (27, uniform_buffer::<u32>(false)),
441 (28, storage_buffer_sized(false, None)),
443 (29, storage_buffer_sized(false, None)),
445 (
447 30,
448 storage_buffer_sized(false, NonZero::<u64>::new(size_of::<u32>() as u64)),
449 ),
450 ));
451 }
452 }
453
454 if layout_key.contains(MeshPipelineViewLayoutKey::ATMOSPHERE) {
456 entries = entries.extend_with_indices((
457 (
459 31,
460 texture_2d(TextureSampleType::Float { filterable: true }),
461 ),
462 (32, sampler(SamplerBindingType::Filtering)),
463 (33, storage_buffer_read_only::<GpuAtmosphere>(false)),
465 ));
466 }
467
468 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 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 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 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 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#[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#[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 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 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 #[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 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 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 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 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 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 (
1002 5,
1003 render_view_decal_bind_group_entries
1004 .decals
1005 .as_entire_binding(),
1006 ),
1007 (
1009 6,
1010 render_view_decal_bind_group_entries
1011 .texture_views
1012 .as_slice(),
1013 ),
1014 (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}