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 #[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 pub const COUNT: usize = Self::all().bits() as usize + 1;
84
85 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
192fn 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 (
205 0,
206 uniform_buffer::<ViewUniform>(true).visibility(ShaderStages::VERTEX_FRAGMENT),
207 ),
208 (1, uniform_buffer::<GpuLights>(true)),
210 (
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 (3, sampler(SamplerBindingType::Comparison)),
230 #[cfg(feature = "experimental_pbr_pcss")]
232 (4, sampler(SamplerBindingType::Filtering)),
233 (
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 (6, sampler(SamplerBindingType::Comparison)),
247 #[cfg(feature = "experimental_pbr_pcss")]
249 (7, sampler(SamplerBindingType::Filtering)),
250 (
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 (
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 (
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 (
287 11,
288 uniform_buffer::<GlobalsUniform>(false).visibility(ShaderStages::VERTEX_FRAGMENT),
289 ),
290 (12, uniform_buffer::<GpuFog>(true)),
292 (13, uniform_buffer::<LightProbesUniform>(true)),
294 (
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 (15, uniform_buffer::<ScreenSpaceReflectionsUniform>(true)),
306 (
308 16,
309 texture_2d(TextureSampleType::Float { filterable: false }),
310 ),
311 ),
312 );
313
314 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 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 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 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 entries = entries.extend_with_indices((
357 (
358 29,
359 texture_2d(TextureSampleType::Float { filterable: true }),
360 ),
361 (30, sampler(SamplerBindingType::Filtering)),
362 ));
363
364 if layout_key.contains(MeshPipelineViewLayoutKey::OIT_ENABLED) {
366 if render_adapter
371 .get_downlevel_capabilities()
372 .flags
373 .contains(DownlevelFlags::FRAGMENT_WRITABLE_STORAGE)
374 {
375 entries = entries.extend_with_indices((
376 (31, storage_buffer_sized(false, None)),
378 (32, storage_buffer_sized(false, None)),
380 (
382 33,
383 uniform_buffer::<OrderIndependentTransparencySettings>(true),
384 ),
385 ));
386 }
387 }
388
389 entries.to_vec()
390}
391
392#[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 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 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
454pub 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 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}