bevy_pbr/render/
mesh.rs

1use crate::{
2    material_bind_groups::{MaterialBindGroupIndex, MaterialBindGroupSlot},
3    resources::write_atmosphere_buffer,
4};
5use bevy_asset::{embedded_asset, load_embedded_asset, AssetId};
6use bevy_camera::{
7    primitives::Aabb,
8    visibility::{NoFrustumCulling, RenderLayers, ViewVisibility, VisibilityRange},
9    Camera, Camera3d, Projection,
10};
11use bevy_core_pipeline::{
12    core_3d::{AlphaMask3d, Opaque3d, Transmissive3d, Transparent3d, CORE_3D_DEPTH_FORMAT},
13    deferred::{AlphaMask3dDeferred, Opaque3dDeferred},
14    oit::{prepare_oit_buffers, OrderIndependentTransparencySettingsOffset},
15    prepass::MotionVectorPrepass,
16};
17use bevy_derive::{Deref, DerefMut};
18use bevy_diagnostic::FrameCount;
19use bevy_ecs::{
20    entity::EntityHashSet,
21    prelude::*,
22    query::{QueryData, ROQueryItem},
23    relationship::RelationshipSourceCollection,
24    system::{lifetimeless::*, SystemParamItem, SystemState},
25};
26use bevy_image::{BevyDefault, ImageSampler, TextureFormatPixelInfo};
27use bevy_light::{
28    EnvironmentMapLight, IrradianceVolume, NotShadowCaster, NotShadowReceiver,
29    ShadowFilteringMethod, TransmittedShadowReceiver,
30};
31use bevy_math::{Affine3, Rect, UVec2, Vec3, Vec4};
32use bevy_mesh::{
33    skinning::SkinnedMesh, BaseMeshPipelineKey, Mesh, Mesh3d, MeshTag, MeshVertexBufferLayoutRef,
34    VertexAttributeDescriptor,
35};
36use bevy_platform::collections::{hash_map::Entry, HashMap};
37use bevy_render::{
38    batching::{
39        gpu_preprocessing::{
40            self, GpuPreprocessingSupport, IndirectBatchSet, IndirectParametersBuffers,
41            IndirectParametersCpuMetadata, IndirectParametersIndexed, IndirectParametersNonIndexed,
42            InstanceInputUniformBuffer, UntypedPhaseIndirectParametersBuffers,
43        },
44        no_gpu_preprocessing, GetBatchData, GetFullBatchData, NoAutomaticBatching,
45    },
46    mesh::{allocator::MeshAllocator, RenderMesh, RenderMeshBufferInfo},
47    render_asset::RenderAssets,
48    render_phase::{
49        BinnedRenderPhasePlugin, InputUniformIndex, PhaseItem, PhaseItemExtraIndex, RenderCommand,
50        RenderCommandResult, SortedRenderPhasePlugin, TrackedRenderPass,
51    },
52    render_resource::*,
53    renderer::{RenderAdapter, RenderDevice, RenderQueue},
54    sync_world::MainEntityHashSet,
55    texture::{DefaultImageSampler, GpuImage},
56    view::{
57        self, NoIndirectDrawing, RenderVisibilityRanges, RetainedViewEntity, ViewTarget,
58        ViewUniformOffset,
59    },
60    Extract,
61};
62use bevy_shader::{load_shader_library, Shader, ShaderDefVal, ShaderSettings};
63use bevy_transform::components::GlobalTransform;
64use bevy_utils::{default, Parallel, TypeIdMap};
65use core::any::TypeId;
66use core::mem::size_of;
67use material_bind_groups::MaterialBindingId;
68use tracing::{error, warn};
69
70use self::irradiance_volume::IRRADIANCE_VOLUMES_ARE_USABLE;
71use crate::{
72    render::{
73        morph::{
74            extract_morphs, no_automatic_morph_batching, prepare_morphs, MorphIndices,
75            MorphUniforms,
76        },
77        skin::no_automatic_skin_batching,
78    },
79    *,
80};
81use bevy_core_pipeline::oit::OrderIndependentTransparencySettings;
82use bevy_core_pipeline::prepass::{DeferredPrepass, DepthPrepass, NormalPrepass};
83use bevy_core_pipeline::tonemapping::{DebandDither, Tonemapping};
84use bevy_ecs::change_detection::Tick;
85use bevy_ecs::system::SystemChangeTick;
86use bevy_render::camera::TemporalJitter;
87use bevy_render::prelude::Msaa;
88use bevy_render::sync_world::{MainEntity, MainEntityHashMap};
89use bevy_render::view::ExtractedView;
90use bevy_render::RenderSystems::PrepareAssets;
91
92use bytemuck::{Pod, Zeroable};
93use nonmax::{NonMaxU16, NonMaxU32};
94use smallvec::{smallvec, SmallVec};
95use static_assertions::const_assert_eq;
96
97/// Provides support for rendering 3D meshes.
98pub struct MeshRenderPlugin {
99    /// Whether we're building [`MeshUniform`]s on GPU.
100    ///
101    /// This requires compute shader support and so will be forcibly disabled if
102    /// the platform doesn't support those.
103    pub use_gpu_instance_buffer_builder: bool,
104    /// Debugging flags that can optionally be set when constructing the renderer.
105    pub debug_flags: RenderDebugFlags,
106}
107
108impl MeshRenderPlugin {
109    /// Creates a new [`MeshRenderPlugin`] with the given debug flags.
110    pub fn new(debug_flags: RenderDebugFlags) -> MeshRenderPlugin {
111        MeshRenderPlugin {
112            use_gpu_instance_buffer_builder: false,
113            debug_flags,
114        }
115    }
116}
117
118/// How many textures are allowed in the view bind group layout (`@group(0)`) before
119/// broader compatibility with WebGL and WebGPU is at risk, due to the minimum guaranteed
120/// values for `MAX_TEXTURE_IMAGE_UNITS` (in WebGL) and `maxSampledTexturesPerShaderStage` (in WebGPU),
121/// currently both at 16.
122///
123/// We use 10 here because it still leaves us, in a worst case scenario, with 6 textures for the other bind groups.
124///
125/// See: <https://gpuweb.github.io/gpuweb/#limits>
126#[cfg(debug_assertions)]
127pub const MESH_PIPELINE_VIEW_LAYOUT_SAFE_MAX_TEXTURES: usize = 10;
128
129impl Plugin for MeshRenderPlugin {
130    fn build(&self, app: &mut App) {
131        load_shader_library!(app, "forward_io.wgsl");
132        load_shader_library!(app, "mesh_view_types.wgsl", |settings| *settings =
133            ShaderSettings {
134                shader_defs: vec![
135                    ShaderDefVal::UInt(
136                        "MAX_DIRECTIONAL_LIGHTS".into(),
137                        MAX_DIRECTIONAL_LIGHTS as u32
138                    ),
139                    ShaderDefVal::UInt(
140                        "MAX_CASCADES_PER_LIGHT".into(),
141                        MAX_CASCADES_PER_LIGHT as u32,
142                    )
143                ]
144            });
145        load_shader_library!(app, "mesh_view_bindings.wgsl");
146        load_shader_library!(app, "mesh_types.wgsl");
147        load_shader_library!(app, "mesh_functions.wgsl");
148        load_shader_library!(app, "skinning.wgsl");
149        load_shader_library!(app, "morph.wgsl");
150        load_shader_library!(app, "occlusion_culling.wgsl");
151
152        embedded_asset!(app, "mesh.wgsl");
153
154        if app.get_sub_app(RenderApp).is_none() {
155            return;
156        }
157
158        app.add_systems(
159            PostUpdate,
160            (no_automatic_skin_batching, no_automatic_morph_batching),
161        )
162        .add_plugins((
163            BinnedRenderPhasePlugin::<Opaque3d, MeshPipeline>::new(self.debug_flags),
164            BinnedRenderPhasePlugin::<AlphaMask3d, MeshPipeline>::new(self.debug_flags),
165            BinnedRenderPhasePlugin::<Shadow, MeshPipeline>::new(self.debug_flags),
166            BinnedRenderPhasePlugin::<Opaque3dDeferred, MeshPipeline>::new(self.debug_flags),
167            BinnedRenderPhasePlugin::<AlphaMask3dDeferred, MeshPipeline>::new(self.debug_flags),
168            SortedRenderPhasePlugin::<Transmissive3d, MeshPipeline>::new(self.debug_flags),
169            SortedRenderPhasePlugin::<Transparent3d, MeshPipeline>::new(self.debug_flags),
170        ));
171
172        if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
173            render_app
174                .init_resource::<MorphUniforms>()
175                .init_resource::<MorphIndices>()
176                .init_resource::<MeshCullingDataBuffer>()
177                .init_resource::<RenderMaterialInstances>()
178                .configure_sets(
179                    ExtractSchedule,
180                    MeshExtractionSystems
181                        .after(view::extract_visibility_ranges)
182                        .after(late_sweep_material_instances),
183                )
184                .add_systems(
185                    ExtractSchedule,
186                    (
187                        extract_skins,
188                        extract_morphs,
189                        gpu_preprocessing::clear_batched_gpu_instance_buffers::<MeshPipeline>
190                            .before(MeshExtractionSystems),
191                    ),
192                )
193                .add_systems(
194                    Render,
195                    (
196                        set_mesh_motion_vector_flags.in_set(RenderSystems::PrepareMeshes),
197                        prepare_skins.in_set(RenderSystems::PrepareResources),
198                        prepare_morphs.in_set(RenderSystems::PrepareResources),
199                        prepare_mesh_bind_groups.in_set(RenderSystems::PrepareBindGroups),
200                        prepare_mesh_view_bind_groups
201                            .in_set(RenderSystems::PrepareBindGroups)
202                            .after(prepare_oit_buffers)
203                            .after(write_atmosphere_buffer),
204                        no_gpu_preprocessing::clear_batched_cpu_instance_buffers::<MeshPipeline>
205                            .in_set(RenderSystems::Cleanup)
206                            .after(RenderSystems::Render),
207                    ),
208                );
209        }
210    }
211
212    fn finish(&self, app: &mut App) {
213        let mut mesh_bindings_shader_defs = Vec::with_capacity(1);
214
215        if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
216            render_app
217                .init_resource::<ViewKeyCache>()
218                .init_resource::<ViewSpecializationTicks>()
219                .init_resource::<GpuPreprocessingSupport>()
220                .init_resource::<SkinUniforms>()
221                .add_systems(
222                    Render,
223                    check_views_need_specialization.in_set(PrepareAssets),
224                );
225
226            let gpu_preprocessing_support =
227                render_app.world().resource::<GpuPreprocessingSupport>();
228            let use_gpu_instance_buffer_builder =
229                self.use_gpu_instance_buffer_builder && gpu_preprocessing_support.is_available();
230
231            let render_mesh_instances = RenderMeshInstances::new(use_gpu_instance_buffer_builder);
232            render_app.insert_resource(render_mesh_instances);
233
234            if use_gpu_instance_buffer_builder {
235                render_app
236                    .init_resource::<gpu_preprocessing::BatchedInstanceBuffers<
237                        MeshUniform,
238                        MeshInputUniform
239                    >>()
240                    .init_resource::<RenderMeshInstanceGpuQueues>()
241                    .init_resource::<MeshesToReextractNextFrame>()
242                    .add_systems(
243                        ExtractSchedule,
244                        extract_meshes_for_gpu_building.in_set(MeshExtractionSystems),
245                    )
246                    .add_systems(
247                        Render,
248                        (
249                            gpu_preprocessing::write_batched_instance_buffers::<MeshPipeline>
250                                .in_set(RenderSystems::PrepareResourcesFlush),
251                            gpu_preprocessing::delete_old_work_item_buffers::<MeshPipeline>
252                                .in_set(RenderSystems::PrepareResources),
253                            collect_meshes_for_gpu_building
254                                .in_set(RenderSystems::PrepareMeshes)
255                                // This must be before
256                                // `set_mesh_motion_vector_flags` so it doesn't
257                                // overwrite those flags.
258                                .before(set_mesh_motion_vector_flags),
259                        ),
260                    );
261            } else {
262                let render_device = render_app.world().resource::<RenderDevice>();
263                let cpu_batched_instance_buffer = no_gpu_preprocessing::BatchedInstanceBuffer::<
264                    MeshUniform,
265                >::new(&render_device.limits());
266                render_app
267                    .insert_resource(cpu_batched_instance_buffer)
268                    .add_systems(
269                        ExtractSchedule,
270                        extract_meshes_for_cpu_building.in_set(MeshExtractionSystems),
271                    )
272                    .add_systems(
273                        Render,
274                        no_gpu_preprocessing::write_batched_instance_buffer::<MeshPipeline>
275                            .in_set(RenderSystems::PrepareResourcesFlush),
276                    );
277            };
278
279            let render_device = render_app.world().resource::<RenderDevice>();
280            if let Some(per_object_buffer_batch_size) =
281                GpuArrayBuffer::<MeshUniform>::batch_size(&render_device.limits())
282            {
283                mesh_bindings_shader_defs.push(ShaderDefVal::UInt(
284                    "PER_OBJECT_BUFFER_BATCH_SIZE".into(),
285                    per_object_buffer_batch_size,
286                ));
287            }
288
289            render_app
290                .init_resource::<MeshPipelineViewLayouts>()
291                .init_resource::<MeshPipeline>();
292        }
293
294        // Load the mesh_bindings shader module here as it depends on runtime information about
295        // whether storage buffers are supported, or the maximum uniform buffer binding size.
296        load_shader_library!(app, "mesh_bindings.wgsl", move |settings| *settings =
297            ShaderSettings {
298                shader_defs: mesh_bindings_shader_defs.clone(),
299            });
300    }
301}
302
303#[derive(Resource, Deref, DerefMut, Default, Debug, Clone)]
304pub struct ViewKeyCache(HashMap<RetainedViewEntity, MeshPipelineKey>);
305
306#[derive(Resource, Deref, DerefMut, Default, Debug, Clone)]
307pub struct ViewSpecializationTicks(HashMap<RetainedViewEntity, Tick>);
308
309pub fn check_views_need_specialization(
310    mut view_key_cache: ResMut<ViewKeyCache>,
311    mut view_specialization_ticks: ResMut<ViewSpecializationTicks>,
312    mut views: Query<(
313        &ExtractedView,
314        &Msaa,
315        Option<&Tonemapping>,
316        Option<&DebandDither>,
317        Option<&ShadowFilteringMethod>,
318        Has<ScreenSpaceAmbientOcclusion>,
319        (
320            Has<NormalPrepass>,
321            Has<DepthPrepass>,
322            Has<MotionVectorPrepass>,
323            Has<DeferredPrepass>,
324        ),
325        Option<&Camera3d>,
326        Has<TemporalJitter>,
327        Option<&Projection>,
328        Has<DistanceFog>,
329        (
330            Has<RenderViewLightProbes<EnvironmentMapLight>>,
331            Has<RenderViewLightProbes<IrradianceVolume>>,
332        ),
333        Has<OrderIndependentTransparencySettings>,
334        Has<ExtractedAtmosphere>,
335    )>,
336    ticks: SystemChangeTick,
337) {
338    for (
339        view,
340        msaa,
341        tonemapping,
342        dither,
343        shadow_filter_method,
344        ssao,
345        (normal_prepass, depth_prepass, motion_vector_prepass, deferred_prepass),
346        camera_3d,
347        temporal_jitter,
348        projection,
349        distance_fog,
350        (has_environment_maps, has_irradiance_volumes),
351        has_oit,
352        has_atmosphere,
353    ) in views.iter_mut()
354    {
355        let mut view_key = MeshPipelineKey::from_msaa_samples(msaa.samples())
356            | MeshPipelineKey::from_hdr(view.hdr);
357
358        if normal_prepass {
359            view_key |= MeshPipelineKey::NORMAL_PREPASS;
360        }
361
362        if depth_prepass {
363            view_key |= MeshPipelineKey::DEPTH_PREPASS;
364        }
365
366        if motion_vector_prepass {
367            view_key |= MeshPipelineKey::MOTION_VECTOR_PREPASS;
368        }
369
370        if deferred_prepass {
371            view_key |= MeshPipelineKey::DEFERRED_PREPASS;
372        }
373
374        if temporal_jitter {
375            view_key |= MeshPipelineKey::TEMPORAL_JITTER;
376        }
377
378        if has_environment_maps {
379            view_key |= MeshPipelineKey::ENVIRONMENT_MAP;
380        }
381
382        if has_irradiance_volumes {
383            view_key |= MeshPipelineKey::IRRADIANCE_VOLUME;
384        }
385
386        if has_oit {
387            view_key |= MeshPipelineKey::OIT_ENABLED;
388        }
389
390        if has_atmosphere {
391            view_key |= MeshPipelineKey::ATMOSPHERE;
392        }
393
394        if view.invert_culling {
395            view_key |= MeshPipelineKey::INVERT_CULLING;
396        }
397
398        if let Some(projection) = projection {
399            view_key |= match projection {
400                Projection::Perspective(_) => MeshPipelineKey::VIEW_PROJECTION_PERSPECTIVE,
401                Projection::Orthographic(_) => MeshPipelineKey::VIEW_PROJECTION_ORTHOGRAPHIC,
402                Projection::Custom(_) => MeshPipelineKey::VIEW_PROJECTION_NONSTANDARD,
403            };
404        }
405
406        match shadow_filter_method.unwrap_or(&ShadowFilteringMethod::default()) {
407            ShadowFilteringMethod::Hardware2x2 => {
408                view_key |= MeshPipelineKey::SHADOW_FILTER_METHOD_HARDWARE_2X2;
409            }
410            ShadowFilteringMethod::Gaussian => {
411                view_key |= MeshPipelineKey::SHADOW_FILTER_METHOD_GAUSSIAN;
412            }
413            ShadowFilteringMethod::Temporal => {
414                view_key |= MeshPipelineKey::SHADOW_FILTER_METHOD_TEMPORAL;
415            }
416        }
417
418        if !view.hdr {
419            if let Some(tonemapping) = tonemapping {
420                view_key |= MeshPipelineKey::TONEMAP_IN_SHADER;
421                view_key |= tonemapping_pipeline_key(*tonemapping);
422            }
423            if let Some(DebandDither::Enabled) = dither {
424                view_key |= MeshPipelineKey::DEBAND_DITHER;
425            }
426        }
427        if ssao {
428            view_key |= MeshPipelineKey::SCREEN_SPACE_AMBIENT_OCCLUSION;
429        }
430        if distance_fog {
431            view_key |= MeshPipelineKey::DISTANCE_FOG;
432        }
433        if let Some(camera_3d) = camera_3d {
434            view_key |= screen_space_specular_transmission_pipeline_key(
435                camera_3d.screen_space_specular_transmission_quality,
436            );
437        }
438        if !view_key_cache
439            .get_mut(&view.retained_view_entity)
440            .is_some_and(|current_key| *current_key == view_key)
441        {
442            view_key_cache.insert(view.retained_view_entity, view_key);
443            view_specialization_ticks.insert(view.retained_view_entity, ticks.this_run());
444        }
445    }
446}
447
448#[derive(Component)]
449pub struct MeshTransforms {
450    pub world_from_local: Affine3,
451    pub previous_world_from_local: Affine3,
452    pub flags: u32,
453}
454
455#[derive(ShaderType, Clone)]
456pub struct MeshUniform {
457    // Affine 4x3 matrices transposed to 3x4
458    pub world_from_local: [Vec4; 3],
459    pub previous_world_from_local: [Vec4; 3],
460    // 3x3 matrix packed in mat2x4 and f32 as:
461    //   [0].xyz, [1].x,
462    //   [1].yz, [2].xy
463    //   [2].z
464    pub local_from_world_transpose_a: [Vec4; 2],
465    pub local_from_world_transpose_b: f32,
466    pub flags: u32,
467    // Four 16-bit unsigned normalized UV values packed into a `UVec2`:
468    //
469    //                         <--- MSB                   LSB --->
470    //                         +---- min v ----+ +---- min u ----+
471    //     lightmap_uv_rect.x: vvvvvvvv vvvvvvvv uuuuuuuu uuuuuuuu,
472    //                         +---- max v ----+ +---- max u ----+
473    //     lightmap_uv_rect.y: VVVVVVVV VVVVVVVV UUUUUUUU UUUUUUUU,
474    //
475    // (MSB: most significant bit; LSB: least significant bit.)
476    pub lightmap_uv_rect: UVec2,
477    /// The index of this mesh's first vertex in the vertex buffer.
478    ///
479    /// Multiple meshes can be packed into a single vertex buffer (see
480    /// [`MeshAllocator`]). This value stores the offset of the first vertex in
481    /// this mesh in that buffer.
482    pub first_vertex_index: u32,
483    /// The current skin index, or `u32::MAX` if there's no skin.
484    pub current_skin_index: u32,
485    /// The material and lightmap indices, packed into 32 bits.
486    ///
487    /// Low 16 bits: index of the material inside the bind group data.
488    /// High 16 bits: index of the lightmap in the binding array.
489    pub material_and_lightmap_bind_group_slot: u32,
490    /// User supplied tag to identify this mesh instance.
491    pub tag: u32,
492    /// Padding.
493    pub pad: u32,
494}
495
496/// Information that has to be transferred from CPU to GPU in order to produce
497/// the full [`MeshUniform`].
498///
499/// This is essentially a subset of the fields in [`MeshUniform`] above.
500#[derive(ShaderType, Pod, Zeroable, Clone, Copy, Default, Debug)]
501#[repr(C)]
502pub struct MeshInputUniform {
503    /// Affine 4x3 matrix transposed to 3x4.
504    pub world_from_local: [Vec4; 3],
505    /// Four 16-bit unsigned normalized UV values packed into a `UVec2`:
506    ///
507    /// ```text
508    ///                         <--- MSB                   LSB --->
509    ///                         +---- min v ----+ +---- min u ----+
510    ///     lightmap_uv_rect.x: vvvvvvvv vvvvvvvv uuuuuuuu uuuuuuuu,
511    ///                         +---- max v ----+ +---- max u ----+
512    ///     lightmap_uv_rect.y: VVVVVVVV VVVVVVVV UUUUUUUU UUUUUUUU,
513    ///
514    /// (MSB: most significant bit; LSB: least significant bit.)
515    /// ```
516    pub lightmap_uv_rect: UVec2,
517    /// Various [`MeshFlags`].
518    pub flags: u32,
519    /// The index of this mesh's [`MeshInputUniform`] in the previous frame's
520    /// buffer, if applicable.
521    ///
522    /// This is used for TAA. If not present, this will be `u32::MAX`.
523    pub previous_input_index: u32,
524    /// The index of this mesh's first vertex in the vertex buffer.
525    ///
526    /// Multiple meshes can be packed into a single vertex buffer (see
527    /// [`MeshAllocator`]). This value stores the offset of the first vertex in
528    /// this mesh in that buffer.
529    pub first_vertex_index: u32,
530    /// The index of this mesh's first index in the index buffer, if any.
531    ///
532    /// Multiple meshes can be packed into a single index buffer (see
533    /// [`MeshAllocator`]). This value stores the offset of the first index in
534    /// this mesh in that buffer.
535    ///
536    /// If this mesh isn't indexed, this value is ignored.
537    pub first_index_index: u32,
538    /// For an indexed mesh, the number of indices that make it up; for a
539    /// non-indexed mesh, the number of vertices in it.
540    pub index_count: u32,
541    /// The current skin index, or `u32::MAX` if there's no skin.
542    pub current_skin_index: u32,
543    /// The material and lightmap indices, packed into 32 bits.
544    ///
545    /// Low 16 bits: index of the material inside the bind group data.
546    /// High 16 bits: index of the lightmap in the binding array.
547    pub material_and_lightmap_bind_group_slot: u32,
548    /// The number of the frame on which this [`MeshInputUniform`] was built.
549    ///
550    /// This is used to validate the previous transform and skin. If this
551    /// [`MeshInputUniform`] wasn't updated on this frame, then we know that
552    /// neither this mesh's transform nor that of its joints have been updated
553    /// on this frame, and therefore the transforms of both this mesh and its
554    /// joints must be identical to those for the previous frame.
555    pub timestamp: u32,
556    /// User supplied tag to identify this mesh instance.
557    pub tag: u32,
558    /// Padding.
559    pub pad: u32,
560}
561
562/// Information about each mesh instance needed to cull it on GPU.
563///
564/// This consists of its axis-aligned bounding box (AABB).
565#[derive(ShaderType, Pod, Zeroable, Clone, Copy, Default)]
566#[repr(C)]
567pub struct MeshCullingData {
568    /// The 3D center of the AABB in model space, padded with an extra unused
569    /// float value.
570    pub aabb_center: Vec4,
571    /// The 3D extents of the AABB in model space, divided by two, padded with
572    /// an extra unused float value.
573    pub aabb_half_extents: Vec4,
574}
575
576/// A GPU buffer that holds the information needed to cull meshes on GPU.
577///
578/// At the moment, this simply holds each mesh's AABB.
579///
580/// To avoid wasting CPU time in the CPU culling case, this buffer will be empty
581/// if GPU culling isn't in use.
582#[derive(Resource, Deref, DerefMut)]
583pub struct MeshCullingDataBuffer(RawBufferVec<MeshCullingData>);
584
585impl MeshUniform {
586    pub fn new(
587        mesh_transforms: &MeshTransforms,
588        first_vertex_index: u32,
589        material_bind_group_slot: MaterialBindGroupSlot,
590        maybe_lightmap: Option<(LightmapSlotIndex, Rect)>,
591        current_skin_index: Option<u32>,
592        tag: Option<u32>,
593    ) -> Self {
594        let (local_from_world_transpose_a, local_from_world_transpose_b) =
595            mesh_transforms.world_from_local.inverse_transpose_3x3();
596        let lightmap_bind_group_slot = match maybe_lightmap {
597            None => u16::MAX,
598            Some((slot_index, _)) => slot_index.into(),
599        };
600
601        Self {
602            world_from_local: mesh_transforms.world_from_local.to_transpose(),
603            previous_world_from_local: mesh_transforms.previous_world_from_local.to_transpose(),
604            lightmap_uv_rect: pack_lightmap_uv_rect(maybe_lightmap.map(|(_, uv_rect)| uv_rect)),
605            local_from_world_transpose_a,
606            local_from_world_transpose_b,
607            flags: mesh_transforms.flags,
608            first_vertex_index,
609            current_skin_index: current_skin_index.unwrap_or(u32::MAX),
610            material_and_lightmap_bind_group_slot: u32::from(material_bind_group_slot)
611                | ((lightmap_bind_group_slot as u32) << 16),
612            tag: tag.unwrap_or(0),
613            pad: 0,
614        }
615    }
616}
617
618// NOTE: These must match the bit flags in bevy_pbr/src/render/mesh_types.wgsl!
619bitflags::bitflags! {
620    /// Various flags and tightly-packed values on a mesh.
621    ///
622    /// Flags grow from the top bit down; other values grow from the bottom bit
623    /// up.
624    #[repr(transparent)]
625    pub struct MeshFlags: u32 {
626        /// Bitmask for the 16-bit index into the LOD array.
627        ///
628        /// This will be `u16::MAX` if this mesh has no LOD.
629        const LOD_INDEX_MASK              = (1 << 16) - 1;
630        /// Disables frustum culling for this mesh.
631        ///
632        /// This corresponds to the
633        /// [`bevy_render::view::visibility::NoFrustumCulling`] component.
634        const NO_FRUSTUM_CULLING          = 1 << 28;
635        const SHADOW_RECEIVER             = 1 << 29;
636        const TRANSMITTED_SHADOW_RECEIVER = 1 << 30;
637        // Indicates the sign of the determinant of the 3x3 model matrix. If the sign is positive,
638        // then the flag should be set, else it should not be set.
639        const SIGN_DETERMINANT_MODEL_3X3  = 1 << 31;
640        const NONE                        = 0;
641        const UNINITIALIZED               = 0xFFFFFFFF;
642    }
643}
644
645impl MeshFlags {
646    fn from_components(
647        transform: &GlobalTransform,
648        lod_index: Option<NonMaxU16>,
649        no_frustum_culling: bool,
650        not_shadow_receiver: bool,
651        transmitted_receiver: bool,
652    ) -> MeshFlags {
653        let mut mesh_flags = if not_shadow_receiver {
654            MeshFlags::empty()
655        } else {
656            MeshFlags::SHADOW_RECEIVER
657        };
658        if no_frustum_culling {
659            mesh_flags |= MeshFlags::NO_FRUSTUM_CULLING;
660        }
661        if transmitted_receiver {
662            mesh_flags |= MeshFlags::TRANSMITTED_SHADOW_RECEIVER;
663        }
664        if transform.affine().matrix3.determinant().is_sign_positive() {
665            mesh_flags |= MeshFlags::SIGN_DETERMINANT_MODEL_3X3;
666        }
667
668        let lod_index_bits = match lod_index {
669            None => u16::MAX,
670            Some(lod_index) => u16::from(lod_index),
671        };
672        mesh_flags |=
673            MeshFlags::from_bits_retain((lod_index_bits as u32) << MeshFlags::LOD_INDEX_SHIFT);
674
675        mesh_flags
676    }
677
678    /// The first bit of the LOD index.
679    pub const LOD_INDEX_SHIFT: u32 = 0;
680}
681
682bitflags::bitflags! {
683    /// Various useful flags for [`RenderMeshInstance`]s.
684    #[derive(Clone, Copy)]
685    pub struct RenderMeshInstanceFlags: u8 {
686        /// The mesh casts shadows.
687        const SHADOW_CASTER           = 1 << 0;
688        /// The mesh can participate in automatic batching.
689        const AUTOMATIC_BATCHING      = 1 << 1;
690        /// The mesh had a transform last frame and so is eligible for motion
691        /// vector computation.
692        const HAS_PREVIOUS_TRANSFORM  = 1 << 2;
693        /// The mesh had a skin last frame and so that skin should be taken into
694        /// account for motion vector computation.
695        const HAS_PREVIOUS_SKIN       = 1 << 3;
696        /// The mesh had morph targets last frame and so they should be taken
697        /// into account for motion vector computation.
698        const HAS_PREVIOUS_MORPH      = 1 << 4;
699    }
700}
701
702/// CPU data that the render world keeps for each entity, when *not* using GPU
703/// mesh uniform building.
704#[derive(Deref, DerefMut)]
705pub struct RenderMeshInstanceCpu {
706    /// Data shared between both the CPU mesh uniform building and the GPU mesh
707    /// uniform building paths.
708    #[deref]
709    pub shared: RenderMeshInstanceShared,
710    /// The transform of the mesh.
711    ///
712    /// This will be written into the [`MeshUniform`] at the appropriate time.
713    pub transforms: MeshTransforms,
714}
715
716/// CPU data that the render world needs to keep for each entity that contains a
717/// mesh when using GPU mesh uniform building.
718#[derive(Deref, DerefMut)]
719pub struct RenderMeshInstanceGpu {
720    /// Data shared between both the CPU mesh uniform building and the GPU mesh
721    /// uniform building paths.
722    #[deref]
723    pub shared: RenderMeshInstanceShared,
724    /// The representative position of the mesh instance in world-space.
725    ///
726    /// This world-space center is used as a spatial proxy for view-dependent
727    /// operations such as distance computation and render-order sorting.
728    pub center: Vec3,
729    /// The index of the [`MeshInputUniform`] in the buffer.
730    pub current_uniform_index: NonMaxU32,
731}
732
733/// CPU data that the render world needs to keep about each entity that contains
734/// a mesh.
735pub struct RenderMeshInstanceShared {
736    /// The [`AssetId`] of the mesh.
737    pub mesh_asset_id: AssetId<Mesh>,
738    /// A slot for the material bind group index.
739    pub material_bindings_index: MaterialBindingId,
740    /// Various flags.
741    pub flags: RenderMeshInstanceFlags,
742    /// Index of the slab that the lightmap resides in, if a lightmap is
743    /// present.
744    pub lightmap_slab_index: Option<LightmapSlabIndex>,
745    /// User supplied tag to identify this mesh instance.
746    pub tag: u32,
747    /// Render layers that this mesh instance belongs to.
748    pub render_layers: Option<RenderLayers>,
749    /// A representative position of the mesh instance in local space,
750    /// derived from its axis-aligned bounding box.
751    ///
752    /// This value is typically used as a spatial proxy for operations such as
753    /// view-dependent sorting (e.g., transparent object ordering).
754    pub center: Vec3,
755}
756
757/// Information that is gathered during the parallel portion of mesh extraction
758/// when GPU mesh uniform building is enabled.
759///
760/// From this, the [`MeshInputUniform`] and [`RenderMeshInstanceGpu`] are
761/// prepared.
762pub struct RenderMeshInstanceGpuBuilder {
763    /// Data that will be placed on the [`RenderMeshInstanceGpu`].
764    pub shared: RenderMeshInstanceShared,
765    /// The current transform.
766    pub world_from_local: Affine3,
767    /// Four 16-bit unsigned normalized UV values packed into a [`UVec2`]:
768    ///
769    /// ```text
770    ///                         <--- MSB                   LSB --->
771    ///                         +---- min v ----+ +---- min u ----+
772    ///     lightmap_uv_rect.x: vvvvvvvv vvvvvvvv uuuuuuuu uuuuuuuu,
773    ///                         +---- max v ----+ +---- max u ----+
774    ///     lightmap_uv_rect.y: VVVVVVVV VVVVVVVV UUUUUUUU UUUUUUUU,
775    ///
776    /// (MSB: most significant bit; LSB: least significant bit.)
777    /// ```
778    pub lightmap_uv_rect: UVec2,
779    /// The index of the previous mesh input.
780    pub previous_input_index: Option<NonMaxU32>,
781    /// Various flags.
782    pub mesh_flags: MeshFlags,
783}
784
785/// The per-thread queues used during [`extract_meshes_for_gpu_building`].
786///
787/// There are two varieties of these: one for when culling happens on CPU and
788/// one for when culling happens on GPU. Having the two varieties avoids wasting
789/// space if GPU culling is disabled.
790#[derive(Default)]
791pub enum RenderMeshInstanceGpuQueue {
792    /// The default value.
793    ///
794    /// This becomes [`RenderMeshInstanceGpuQueue::CpuCulling`] or
795    /// [`RenderMeshInstanceGpuQueue::GpuCulling`] once extraction starts.
796    #[default]
797    None,
798    /// The version of [`RenderMeshInstanceGpuQueue`] that omits the
799    /// [`MeshCullingData`], so that we don't waste space when GPU
800    /// culling is disabled.
801    CpuCulling {
802        /// Stores GPU data for each entity that became visible or changed in
803        /// such a way that necessitates updating the [`MeshInputUniform`] (e.g.
804        /// changed transform).
805        changed: Vec<(MainEntity, RenderMeshInstanceGpuBuilder)>,
806        /// Stores the IDs of entities that became invisible this frame.
807        removed: Vec<MainEntity>,
808    },
809    /// The version of [`RenderMeshInstanceGpuQueue`] that contains the
810    /// [`MeshCullingData`], used when any view has GPU culling
811    /// enabled.
812    GpuCulling {
813        /// Stores GPU data for each entity that became visible or changed in
814        /// such a way that necessitates updating the [`MeshInputUniform`] (e.g.
815        /// changed transform).
816        changed: Vec<(MainEntity, RenderMeshInstanceGpuBuilder, MeshCullingData)>,
817        /// Stores the IDs of entities that became invisible this frame.
818        removed: Vec<MainEntity>,
819    },
820}
821
822/// The per-thread queues containing mesh instances, populated during the
823/// extract phase.
824///
825/// These are filled in [`extract_meshes_for_gpu_building`] and consumed in
826/// [`collect_meshes_for_gpu_building`].
827#[derive(Resource, Default, Deref, DerefMut)]
828pub struct RenderMeshInstanceGpuQueues(Parallel<RenderMeshInstanceGpuQueue>);
829
830/// Holds a list of meshes that couldn't be extracted this frame because their
831/// materials weren't prepared yet.
832///
833/// On subsequent frames, we try to reextract those meshes.
834#[derive(Resource, Default, Deref, DerefMut)]
835pub struct MeshesToReextractNextFrame(MainEntityHashSet);
836
837impl RenderMeshInstanceShared {
838    /// A gpu builder will provide the mesh instance id
839    /// during [`RenderMeshInstanceGpuBuilder::update`].
840    fn for_gpu_building(
841        previous_transform: Option<&PreviousGlobalTransform>,
842        mesh: &Mesh3d,
843        tag: Option<&MeshTag>,
844        not_shadow_caster: bool,
845        no_automatic_batching: bool,
846        render_layers: Option<&RenderLayers>,
847        aabb: Option<&Aabb>,
848    ) -> Self {
849        Self::for_cpu_building(
850            previous_transform,
851            mesh,
852            tag,
853            default(),
854            not_shadow_caster,
855            no_automatic_batching,
856            render_layers,
857            aabb,
858        )
859    }
860
861    /// The cpu builder does not have an equivalent [`RenderMeshInstanceGpuBuilder::update`].
862    fn for_cpu_building(
863        previous_transform: Option<&PreviousGlobalTransform>,
864        mesh: &Mesh3d,
865        tag: Option<&MeshTag>,
866        material_bindings_index: MaterialBindingId,
867        not_shadow_caster: bool,
868        no_automatic_batching: bool,
869        render_layers: Option<&RenderLayers>,
870        aabb: Option<&Aabb>,
871    ) -> Self {
872        let mut mesh_instance_flags = RenderMeshInstanceFlags::empty();
873        mesh_instance_flags.set(RenderMeshInstanceFlags::SHADOW_CASTER, !not_shadow_caster);
874        mesh_instance_flags.set(
875            RenderMeshInstanceFlags::AUTOMATIC_BATCHING,
876            !no_automatic_batching,
877        );
878        mesh_instance_flags.set(
879            RenderMeshInstanceFlags::HAS_PREVIOUS_TRANSFORM,
880            previous_transform.is_some(),
881        );
882
883        RenderMeshInstanceShared {
884            mesh_asset_id: mesh.id(),
885            flags: mesh_instance_flags,
886            material_bindings_index,
887            lightmap_slab_index: None,
888            tag: tag.map_or(0, |i| **i),
889            render_layers: render_layers.cloned(),
890            center: aabb.map_or(Vec3::ZERO, |aabb| aabb.center.into()),
891        }
892    }
893
894    /// Returns true if this entity is eligible to participate in automatic
895    /// batching.
896    #[inline]
897    pub fn should_batch(&self) -> bool {
898        self.flags
899            .contains(RenderMeshInstanceFlags::AUTOMATIC_BATCHING)
900    }
901}
902
903/// Information that the render world keeps about each entity that contains a
904/// mesh.
905///
906/// The set of information needed is different depending on whether CPU or GPU
907/// [`MeshUniform`] building is in use.
908#[derive(Resource)]
909pub enum RenderMeshInstances {
910    /// Information needed when using CPU mesh instance data building.
911    CpuBuilding(RenderMeshInstancesCpu),
912    /// Information needed when using GPU mesh instance data building.
913    GpuBuilding(RenderMeshInstancesGpu),
914}
915
916/// Information that the render world keeps about each entity that contains a
917/// mesh, when using CPU mesh instance data building.
918#[derive(Default, Deref, DerefMut)]
919pub struct RenderMeshInstancesCpu(MainEntityHashMap<RenderMeshInstanceCpu>);
920
921/// Information that the render world keeps about each entity that contains a
922/// mesh, when using GPU mesh instance data building.
923#[derive(Default, Deref, DerefMut)]
924pub struct RenderMeshInstancesGpu(MainEntityHashMap<RenderMeshInstanceGpu>);
925
926impl RenderMeshInstances {
927    /// Creates a new [`RenderMeshInstances`] instance.
928    fn new(use_gpu_instance_buffer_builder: bool) -> RenderMeshInstances {
929        if use_gpu_instance_buffer_builder {
930            RenderMeshInstances::GpuBuilding(RenderMeshInstancesGpu::default())
931        } else {
932            RenderMeshInstances::CpuBuilding(RenderMeshInstancesCpu::default())
933        }
934    }
935
936    /// Returns the ID of the mesh asset attached to the given entity, if any.
937    pub fn mesh_asset_id(&self, entity: MainEntity) -> Option<AssetId<Mesh>> {
938        match *self {
939            RenderMeshInstances::CpuBuilding(ref instances) => instances.mesh_asset_id(entity),
940            RenderMeshInstances::GpuBuilding(ref instances) => instances.mesh_asset_id(entity),
941        }
942    }
943
944    /// Constructs [`RenderMeshQueueData`] for the given entity, if it has a
945    /// mesh attached.
946    pub fn render_mesh_queue_data(&self, entity: MainEntity) -> Option<RenderMeshQueueData<'_>> {
947        match *self {
948            RenderMeshInstances::CpuBuilding(ref instances) => {
949                instances.render_mesh_queue_data(entity)
950            }
951            RenderMeshInstances::GpuBuilding(ref instances) => {
952                instances.render_mesh_queue_data(entity)
953            }
954        }
955    }
956
957    /// Inserts the given flags into the CPU or GPU render mesh instance data
958    /// for the given mesh as appropriate.
959    fn insert_mesh_instance_flags(&mut self, entity: MainEntity, flags: RenderMeshInstanceFlags) {
960        match *self {
961            RenderMeshInstances::CpuBuilding(ref mut instances) => {
962                instances.insert_mesh_instance_flags(entity, flags);
963            }
964            RenderMeshInstances::GpuBuilding(ref mut instances) => {
965                instances.insert_mesh_instance_flags(entity, flags);
966            }
967        }
968    }
969}
970
971impl RenderMeshInstancesCpu {
972    fn mesh_asset_id(&self, entity: MainEntity) -> Option<AssetId<Mesh>> {
973        self.get(&entity)
974            .map(|render_mesh_instance| render_mesh_instance.mesh_asset_id)
975    }
976
977    fn render_mesh_queue_data(&self, entity: MainEntity) -> Option<RenderMeshQueueData<'_>> {
978        self.get(&entity).map(|render_mesh_instance| {
979            let world_from_local = &render_mesh_instance.transforms.world_from_local;
980            let center = world_from_local
981                .matrix3
982                .mul_vec3(render_mesh_instance.shared.center)
983                + world_from_local.translation;
984
985            RenderMeshQueueData {
986                shared: &render_mesh_instance.shared,
987                center,
988                current_uniform_index: InputUniformIndex::default(),
989            }
990        })
991    }
992
993    /// Inserts the given flags into the render mesh instance data for the given
994    /// mesh.
995    fn insert_mesh_instance_flags(&mut self, entity: MainEntity, flags: RenderMeshInstanceFlags) {
996        if let Some(instance) = self.get_mut(&entity) {
997            instance.flags.insert(flags);
998        }
999    }
1000}
1001
1002impl RenderMeshInstancesGpu {
1003    fn mesh_asset_id(&self, entity: MainEntity) -> Option<AssetId<Mesh>> {
1004        self.get(&entity)
1005            .map(|render_mesh_instance| render_mesh_instance.mesh_asset_id)
1006    }
1007
1008    fn render_mesh_queue_data(&self, entity: MainEntity) -> Option<RenderMeshQueueData<'_>> {
1009        self.get(&entity)
1010            .map(|render_mesh_instance| RenderMeshQueueData {
1011                shared: &render_mesh_instance.shared,
1012                center: render_mesh_instance.center,
1013                current_uniform_index: InputUniformIndex(
1014                    render_mesh_instance.current_uniform_index.into(),
1015                ),
1016            })
1017    }
1018
1019    /// Inserts the given flags into the render mesh instance data for the given
1020    /// mesh.
1021    fn insert_mesh_instance_flags(&mut self, entity: MainEntity, flags: RenderMeshInstanceFlags) {
1022        if let Some(instance) = self.get_mut(&entity) {
1023            instance.flags.insert(flags);
1024        }
1025    }
1026}
1027
1028impl RenderMeshInstanceGpuQueue {
1029    /// Clears out a [`RenderMeshInstanceGpuQueue`], creating or recreating it
1030    /// as necessary.
1031    ///
1032    /// `any_gpu_culling` should be set to true if any view has GPU culling
1033    /// enabled.
1034    fn init(&mut self, any_gpu_culling: bool) {
1035        match (any_gpu_culling, &mut *self) {
1036            (true, RenderMeshInstanceGpuQueue::GpuCulling { changed, removed }) => {
1037                changed.clear();
1038                removed.clear();
1039            }
1040            (true, _) => {
1041                *self = RenderMeshInstanceGpuQueue::GpuCulling {
1042                    changed: vec![],
1043                    removed: vec![],
1044                }
1045            }
1046            (false, RenderMeshInstanceGpuQueue::CpuCulling { changed, removed }) => {
1047                changed.clear();
1048                removed.clear();
1049            }
1050            (false, _) => {
1051                *self = RenderMeshInstanceGpuQueue::CpuCulling {
1052                    changed: vec![],
1053                    removed: vec![],
1054                }
1055            }
1056        }
1057    }
1058
1059    /// Adds a new mesh to this queue.
1060    fn push(
1061        &mut self,
1062        entity: MainEntity,
1063        instance_builder: RenderMeshInstanceGpuBuilder,
1064        culling_data_builder: Option<MeshCullingData>,
1065    ) {
1066        match (&mut *self, culling_data_builder) {
1067            (
1068                &mut RenderMeshInstanceGpuQueue::CpuCulling {
1069                    changed: ref mut queue,
1070                    ..
1071                },
1072                None,
1073            ) => {
1074                queue.push((entity, instance_builder));
1075            }
1076            (
1077                &mut RenderMeshInstanceGpuQueue::GpuCulling {
1078                    changed: ref mut queue,
1079                    ..
1080                },
1081                Some(culling_data_builder),
1082            ) => {
1083                queue.push((entity, instance_builder, culling_data_builder));
1084            }
1085            (_, None) => {
1086                *self = RenderMeshInstanceGpuQueue::CpuCulling {
1087                    changed: vec![(entity, instance_builder)],
1088                    removed: vec![],
1089                };
1090            }
1091            (_, Some(culling_data_builder)) => {
1092                *self = RenderMeshInstanceGpuQueue::GpuCulling {
1093                    changed: vec![(entity, instance_builder, culling_data_builder)],
1094                    removed: vec![],
1095                };
1096            }
1097        }
1098    }
1099
1100    /// Adds the given entity to the `removed` list, queuing it for removal.
1101    ///
1102    /// The `gpu_culling` parameter specifies whether GPU culling is enabled.
1103    fn remove(&mut self, entity: MainEntity, gpu_culling: bool) {
1104        match (&mut *self, gpu_culling) {
1105            (RenderMeshInstanceGpuQueue::None, false) => {
1106                *self = RenderMeshInstanceGpuQueue::CpuCulling {
1107                    changed: vec![],
1108                    removed: vec![entity],
1109                }
1110            }
1111            (RenderMeshInstanceGpuQueue::None, true) => {
1112                *self = RenderMeshInstanceGpuQueue::GpuCulling {
1113                    changed: vec![],
1114                    removed: vec![entity],
1115                }
1116            }
1117            (RenderMeshInstanceGpuQueue::CpuCulling { removed, .. }, _)
1118            | (RenderMeshInstanceGpuQueue::GpuCulling { removed, .. }, _) => {
1119                removed.push(entity);
1120            }
1121        }
1122    }
1123}
1124
1125impl RenderMeshInstanceGpuBuilder {
1126    /// Flushes this mesh instance to the [`RenderMeshInstanceGpu`] and
1127    /// [`MeshInputUniform`] tables, replacing the existing entry if applicable.
1128    fn update(
1129        mut self,
1130        entity: MainEntity,
1131        render_mesh_instances: &mut MainEntityHashMap<RenderMeshInstanceGpu>,
1132        current_input_buffer: &mut InstanceInputUniformBuffer<MeshInputUniform>,
1133        previous_input_buffer: &mut InstanceInputUniformBuffer<MeshInputUniform>,
1134        mesh_allocator: &MeshAllocator,
1135        mesh_material_ids: &RenderMaterialInstances,
1136        render_material_bindings: &RenderMaterialBindings,
1137        render_lightmaps: &RenderLightmaps,
1138        skin_uniforms: &SkinUniforms,
1139        timestamp: FrameCount,
1140        meshes_to_reextract_next_frame: &mut MeshesToReextractNextFrame,
1141    ) -> Option<u32> {
1142        let (first_vertex_index, vertex_count) =
1143            match mesh_allocator.mesh_vertex_slice(&self.shared.mesh_asset_id) {
1144                Some(mesh_vertex_slice) => (
1145                    mesh_vertex_slice.range.start,
1146                    mesh_vertex_slice.range.end - mesh_vertex_slice.range.start,
1147                ),
1148                None => (0, 0),
1149            };
1150        let (mesh_is_indexed, first_index_index, index_count) =
1151            match mesh_allocator.mesh_index_slice(&self.shared.mesh_asset_id) {
1152                Some(mesh_index_slice) => (
1153                    true,
1154                    mesh_index_slice.range.start,
1155                    mesh_index_slice.range.end - mesh_index_slice.range.start,
1156                ),
1157                None => (false, 0, 0),
1158            };
1159        let current_skin_index = match skin_uniforms.skin_byte_offset(entity) {
1160            Some(skin_index) => skin_index.index(),
1161            None => u32::MAX,
1162        };
1163
1164        // Look up the material index. If we couldn't fetch the material index,
1165        // then the material hasn't been prepared yet, perhaps because it hasn't
1166        // yet loaded. In that case, add the mesh to
1167        // `meshes_to_reextract_next_frame` and bail.
1168        let mesh_material = mesh_material_ids.mesh_material(entity);
1169        let mesh_material_binding_id = if mesh_material != DUMMY_MESH_MATERIAL.untyped() {
1170            match render_material_bindings.get(&mesh_material) {
1171                Some(binding_id) => *binding_id,
1172                None => {
1173                    meshes_to_reextract_next_frame.insert(entity);
1174                    return None;
1175                }
1176            }
1177        } else {
1178            // Use a dummy material binding ID.
1179            MaterialBindingId::default()
1180        };
1181        self.shared.material_bindings_index = mesh_material_binding_id;
1182
1183        let lightmap_slot = match render_lightmaps.render_lightmaps.get(&entity) {
1184            Some(render_lightmap) => u16::from(*render_lightmap.slot_index),
1185            None => u16::MAX,
1186        };
1187        let lightmap_slab_index = render_lightmaps
1188            .render_lightmaps
1189            .get(&entity)
1190            .map(|lightmap| lightmap.slab_index);
1191        self.shared.lightmap_slab_index = lightmap_slab_index;
1192
1193        // Create the mesh input uniform.
1194        let mut mesh_input_uniform = MeshInputUniform {
1195            world_from_local: self.world_from_local.to_transpose(),
1196            lightmap_uv_rect: self.lightmap_uv_rect,
1197            flags: self.mesh_flags.bits(),
1198            previous_input_index: u32::MAX,
1199            timestamp: timestamp.0,
1200            first_vertex_index,
1201            first_index_index,
1202            index_count: if mesh_is_indexed {
1203                index_count
1204            } else {
1205                vertex_count
1206            },
1207            current_skin_index,
1208            material_and_lightmap_bind_group_slot: u32::from(
1209                self.shared.material_bindings_index.slot,
1210            ) | ((lightmap_slot as u32) << 16),
1211            tag: self.shared.tag,
1212            pad: 0,
1213        };
1214
1215        // Did the last frame contain this entity as well?
1216        let current_uniform_index;
1217        let world_from_local = &self.world_from_local;
1218        let center =
1219            world_from_local.matrix3.mul_vec3(self.shared.center) + world_from_local.translation;
1220
1221        match render_mesh_instances.entry(entity) {
1222            Entry::Occupied(mut occupied_entry) => {
1223                // Yes, it did. Replace its entry with the new one.
1224
1225                // Reserve a slot.
1226                current_uniform_index = u32::from(occupied_entry.get_mut().current_uniform_index);
1227
1228                // Save the old mesh input uniform. The mesh preprocessing
1229                // shader will need it to compute motion vectors.
1230                let previous_mesh_input_uniform =
1231                    current_input_buffer.get_unchecked(current_uniform_index);
1232                let previous_input_index = previous_input_buffer.add(previous_mesh_input_uniform);
1233                mesh_input_uniform.previous_input_index = previous_input_index;
1234
1235                // Write in the new mesh input uniform.
1236                current_input_buffer.set(current_uniform_index, mesh_input_uniform);
1237
1238                occupied_entry.replace_entry_with(|_, _| {
1239                    Some(RenderMeshInstanceGpu {
1240                        shared: self.shared,
1241                        center,
1242                        current_uniform_index: NonMaxU32::new(current_uniform_index)
1243                            .unwrap_or_default(),
1244                    })
1245                });
1246            }
1247
1248            Entry::Vacant(vacant_entry) => {
1249                // No, this is a new entity. Push its data on to the buffer.
1250                current_uniform_index = current_input_buffer.add(mesh_input_uniform);
1251
1252                vacant_entry.insert(RenderMeshInstanceGpu {
1253                    shared: self.shared,
1254                    center,
1255                    current_uniform_index: NonMaxU32::new(current_uniform_index)
1256                        .unwrap_or_default(),
1257                });
1258            }
1259        }
1260
1261        Some(current_uniform_index)
1262    }
1263}
1264
1265/// Removes a [`MeshInputUniform`] corresponding to an entity that became
1266/// invisible from the buffer.
1267fn remove_mesh_input_uniform(
1268    entity: MainEntity,
1269    render_mesh_instances: &mut MainEntityHashMap<RenderMeshInstanceGpu>,
1270    current_input_buffer: &mut InstanceInputUniformBuffer<MeshInputUniform>,
1271) -> Option<u32> {
1272    // Remove the uniform data.
1273    let removed_render_mesh_instance = render_mesh_instances.remove(&entity)?;
1274
1275    let removed_uniform_index = removed_render_mesh_instance.current_uniform_index.get();
1276    current_input_buffer.remove(removed_uniform_index);
1277    Some(removed_uniform_index)
1278}
1279
1280impl MeshCullingData {
1281    /// Returns a new [`MeshCullingData`] initialized with the given AABB.
1282    ///
1283    /// If no AABB is provided, an infinitely-large one is conservatively
1284    /// chosen.
1285    fn new(aabb: Option<&Aabb>) -> Self {
1286        match aabb {
1287            Some(aabb) => MeshCullingData {
1288                aabb_center: aabb.center.extend(0.0),
1289                aabb_half_extents: aabb.half_extents.extend(0.0),
1290            },
1291            None => MeshCullingData {
1292                aabb_center: Vec3::ZERO.extend(0.0),
1293                aabb_half_extents: Vec3::INFINITY.extend(0.0),
1294            },
1295        }
1296    }
1297
1298    /// Flushes this mesh instance culling data to the
1299    /// [`MeshCullingDataBuffer`], replacing the existing entry if applicable.
1300    fn update(
1301        &self,
1302        mesh_culling_data_buffer: &mut MeshCullingDataBuffer,
1303        instance_data_index: usize,
1304    ) {
1305        while mesh_culling_data_buffer.len() < instance_data_index + 1 {
1306            mesh_culling_data_buffer.push(MeshCullingData::default());
1307        }
1308        mesh_culling_data_buffer.values_mut()[instance_data_index] = *self;
1309    }
1310}
1311
1312impl Default for MeshCullingDataBuffer {
1313    #[inline]
1314    fn default() -> Self {
1315        Self(RawBufferVec::new(BufferUsages::STORAGE))
1316    }
1317}
1318
1319/// Data that [`crate::material::queue_material_meshes`] and similar systems
1320/// need in order to place entities that contain meshes in the right batch.
1321#[derive(Deref)]
1322pub struct RenderMeshQueueData<'a> {
1323    /// General information about the mesh instance.
1324    #[deref]
1325    pub shared: &'a RenderMeshInstanceShared,
1326    /// The representative position of the mesh instance in world-space.
1327    ///
1328    /// This world-space center is used as a spatial proxy for view-dependent
1329    /// operations such as distance computation and render-order sorting.
1330    pub center: Vec3,
1331    /// The index of the [`MeshInputUniform`] in the GPU buffer for this mesh
1332    /// instance.
1333    pub current_uniform_index: InputUniformIndex,
1334}
1335
1336/// A [`SystemSet`] that encompasses both [`extract_meshes_for_cpu_building`]
1337/// and [`extract_meshes_for_gpu_building`].
1338#[derive(SystemSet, Clone, PartialEq, Eq, Debug, Hash)]
1339pub struct MeshExtractionSystems;
1340
1341/// Extracts meshes from the main world into the render world, populating the
1342/// [`RenderMeshInstances`].
1343///
1344/// This is the variant of the system that runs when we're *not* using GPU
1345/// [`MeshUniform`] building.
1346pub fn extract_meshes_for_cpu_building(
1347    mut render_mesh_instances: ResMut<RenderMeshInstances>,
1348    mesh_material_ids: Res<RenderMaterialInstances>,
1349    render_material_bindings: Res<RenderMaterialBindings>,
1350    render_visibility_ranges: Res<RenderVisibilityRanges>,
1351    mut render_mesh_instance_queues: Local<Parallel<Vec<(Entity, RenderMeshInstanceCpu)>>>,
1352    meshes_query: Extract<
1353        Query<(
1354            Entity,
1355            &ViewVisibility,
1356            &GlobalTransform,
1357            Option<&PreviousGlobalTransform>,
1358            &Mesh3d,
1359            Option<&MeshTag>,
1360            Has<NoFrustumCulling>,
1361            Has<NotShadowReceiver>,
1362            Has<TransmittedShadowReceiver>,
1363            Has<NotShadowCaster>,
1364            Has<NoAutomaticBatching>,
1365            Has<VisibilityRange>,
1366            Option<&RenderLayers>,
1367            Option<&Aabb>,
1368        )>,
1369    >,
1370) {
1371    meshes_query.par_iter().for_each_init(
1372        || render_mesh_instance_queues.borrow_local_mut(),
1373        |queue,
1374         (
1375            entity,
1376            view_visibility,
1377            transform,
1378            previous_transform,
1379            mesh,
1380            tag,
1381            no_frustum_culling,
1382            not_shadow_receiver,
1383            transmitted_receiver,
1384            not_shadow_caster,
1385            no_automatic_batching,
1386            visibility_range,
1387            render_layers,
1388            aabb,
1389        )| {
1390            if !view_visibility.get() {
1391                return;
1392            }
1393
1394            let mut lod_index = None;
1395            if visibility_range {
1396                lod_index = render_visibility_ranges.lod_index_for_entity(entity.into());
1397            }
1398
1399            let mesh_flags = MeshFlags::from_components(
1400                transform,
1401                lod_index,
1402                no_frustum_culling,
1403                not_shadow_receiver,
1404                transmitted_receiver,
1405            );
1406
1407            let mesh_material = mesh_material_ids.mesh_material(MainEntity::from(entity));
1408
1409            let material_bindings_index = render_material_bindings
1410                .get(&mesh_material)
1411                .copied()
1412                .unwrap_or_default();
1413
1414            let shared = RenderMeshInstanceShared::for_cpu_building(
1415                previous_transform,
1416                mesh,
1417                tag,
1418                material_bindings_index,
1419                not_shadow_caster,
1420                no_automatic_batching,
1421                render_layers,
1422                aabb,
1423            );
1424
1425            let world_from_local = transform.affine();
1426            queue.push((
1427                entity,
1428                RenderMeshInstanceCpu {
1429                    transforms: MeshTransforms {
1430                        world_from_local: (&world_from_local).into(),
1431                        previous_world_from_local: (&previous_transform
1432                            .map(|t| t.0)
1433                            .unwrap_or(world_from_local))
1434                            .into(),
1435                        flags: mesh_flags.bits(),
1436                    },
1437                    shared,
1438                },
1439            ));
1440        },
1441    );
1442
1443    // Collect the render mesh instances.
1444    let RenderMeshInstances::CpuBuilding(ref mut render_mesh_instances) = *render_mesh_instances
1445    else {
1446        panic!(
1447            "`extract_meshes_for_cpu_building` should only be called if we're using CPU \
1448            `MeshUniform` building"
1449        );
1450    };
1451
1452    render_mesh_instances.clear();
1453    for queue in render_mesh_instance_queues.iter_mut() {
1454        for (entity, render_mesh_instance) in queue.drain(..) {
1455            render_mesh_instances.insert(entity.into(), render_mesh_instance);
1456        }
1457    }
1458}
1459
1460/// All the data that we need from a mesh in the main world.
1461type GpuMeshExtractionQuery = (
1462    Entity,
1463    Read<ViewVisibility>,
1464    Read<GlobalTransform>,
1465    Option<Read<PreviousGlobalTransform>>,
1466    Option<Read<Lightmap>>,
1467    Option<Read<Aabb>>,
1468    Read<Mesh3d>,
1469    Option<Read<MeshTag>>,
1470    Has<NoFrustumCulling>,
1471    Has<NotShadowReceiver>,
1472    Has<TransmittedShadowReceiver>,
1473    Has<NotShadowCaster>,
1474    Has<NoAutomaticBatching>,
1475    Has<VisibilityRange>,
1476    Option<Read<RenderLayers>>,
1477);
1478
1479/// Extracts meshes from the main world into the render world and queues
1480/// [`MeshInputUniform`]s to be uploaded to the GPU.
1481///
1482/// This is optimized to only look at entities that have changed since the last
1483/// frame.
1484///
1485/// This is the variant of the system that runs when we're using GPU
1486/// [`MeshUniform`] building.
1487pub fn extract_meshes_for_gpu_building(
1488    mut render_mesh_instances: ResMut<RenderMeshInstances>,
1489    render_visibility_ranges: Res<RenderVisibilityRanges>,
1490    mut render_mesh_instance_queues: ResMut<RenderMeshInstanceGpuQueues>,
1491    changed_meshes_query: Extract<
1492        Query<
1493            GpuMeshExtractionQuery,
1494            Or<(
1495                Changed<ViewVisibility>,
1496                Changed<GlobalTransform>,
1497                Changed<PreviousGlobalTransform>,
1498                Changed<Lightmap>,
1499                Changed<Aabb>,
1500                Changed<Mesh3d>,
1501                Changed<MeshTag>,
1502                Changed<NoFrustumCulling>,
1503                Changed<NotShadowReceiver>,
1504                Changed<TransmittedShadowReceiver>,
1505                Changed<NotShadowCaster>,
1506                Changed<NoAutomaticBatching>,
1507                Changed<VisibilityRange>,
1508                Changed<SkinnedMesh>,
1509            )>,
1510        >,
1511    >,
1512    (
1513        mut removed_previous_global_transform_query,
1514        mut removed_lightmap_query,
1515        mut removed_aabb_query,
1516        mut removed_mesh_tag_query,
1517        mut removed_no_frustum_culling_query,
1518        mut removed_not_shadow_receiver_query,
1519        mut removed_transmitted_receiver_query,
1520        mut removed_not_shadow_caster_query,
1521        mut removed_no_automatic_batching_query,
1522        mut removed_visibility_range_query,
1523        mut removed_skinned_mesh_query,
1524    ): (
1525        Extract<RemovedComponents<PreviousGlobalTransform>>,
1526        Extract<RemovedComponents<Lightmap>>,
1527        Extract<RemovedComponents<Aabb>>,
1528        Extract<RemovedComponents<MeshTag>>,
1529        Extract<RemovedComponents<NoFrustumCulling>>,
1530        Extract<RemovedComponents<NotShadowReceiver>>,
1531        Extract<RemovedComponents<TransmittedShadowReceiver>>,
1532        Extract<RemovedComponents<NotShadowCaster>>,
1533        Extract<RemovedComponents<NoAutomaticBatching>>,
1534        Extract<RemovedComponents<VisibilityRange>>,
1535        Extract<RemovedComponents<SkinnedMesh>>,
1536    ),
1537    all_meshes_query: Extract<Query<GpuMeshExtractionQuery>>,
1538    mut removed_meshes_query: Extract<RemovedComponents<Mesh3d>>,
1539    gpu_culling_query: Extract<Query<(), (With<Camera>, Without<NoIndirectDrawing>)>>,
1540    meshes_to_reextract_next_frame: ResMut<MeshesToReextractNextFrame>,
1541    mut reextract_entities: Local<EntityHashSet>,
1542) {
1543    reextract_entities.clear();
1544
1545    let any_gpu_culling = !gpu_culling_query.is_empty();
1546
1547    for render_mesh_instance_queue in render_mesh_instance_queues.iter_mut() {
1548        render_mesh_instance_queue.init(any_gpu_culling);
1549    }
1550
1551    // Collect render mesh instances. Build up the uniform buffer.
1552
1553    let RenderMeshInstances::GpuBuilding(ref mut render_mesh_instances) = *render_mesh_instances
1554    else {
1555        panic!(
1556            "`extract_meshes_for_gpu_building` should only be called if we're \
1557            using GPU `MeshUniform` building"
1558        );
1559    };
1560
1561    // Find all meshes that have changed, and record information needed to
1562    // construct the `MeshInputUniform` for them.
1563    changed_meshes_query.par_iter().for_each_init(
1564        || render_mesh_instance_queues.borrow_local_mut(),
1565        |queue, query_row| {
1566            extract_mesh_for_gpu_building(
1567                query_row,
1568                &render_visibility_ranges,
1569                render_mesh_instances,
1570                queue,
1571                any_gpu_culling,
1572            );
1573        },
1574    );
1575
1576    // Process materials that `collect_meshes_for_gpu_building` marked as
1577    // needing to be reextracted. This will happen when we extracted a mesh on
1578    // some previous frame, but its material hadn't been prepared yet, perhaps
1579    // because the material hadn't yet been loaded. We reextract such materials
1580    // on subsequent frames so that `collect_meshes_for_gpu_building` will check
1581    // to see if their materials have been prepared.
1582    let iters = meshes_to_reextract_next_frame
1583        .iter()
1584        .map(|&e| *e)
1585        .chain(removed_previous_global_transform_query.read())
1586        .chain(removed_lightmap_query.read())
1587        .chain(removed_aabb_query.read())
1588        .chain(removed_mesh_tag_query.read())
1589        .chain(removed_no_frustum_culling_query.read())
1590        .chain(removed_not_shadow_receiver_query.read())
1591        .chain(removed_transmitted_receiver_query.read())
1592        .chain(removed_not_shadow_caster_query.read())
1593        .chain(removed_no_automatic_batching_query.read())
1594        .chain(removed_visibility_range_query.read())
1595        .chain(removed_skinned_mesh_query.read());
1596
1597    reextract_entities.extend_from_iter(iters);
1598
1599    let mut queue = render_mesh_instance_queues.borrow_local_mut();
1600    for entity in &reextract_entities {
1601        if let Ok(query_row) = all_meshes_query.get(*entity) {
1602            extract_mesh_for_gpu_building(
1603                query_row,
1604                &render_visibility_ranges,
1605                render_mesh_instances,
1606                &mut queue,
1607                any_gpu_culling,
1608            );
1609        }
1610    }
1611
1612    // Also record info about each mesh that became invisible.
1613    for entity in removed_meshes_query.read() {
1614        // Only queue a mesh for removal if we didn't pick it up above.
1615        // It's possible that a necessary component was removed and re-added in
1616        // the same frame.
1617        let entity = MainEntity::from(entity);
1618        if !changed_meshes_query.contains(*entity)
1619            && !meshes_to_reextract_next_frame.contains(&entity)
1620        {
1621            queue.remove(entity, any_gpu_culling);
1622        }
1623    }
1624}
1625
1626fn extract_mesh_for_gpu_building(
1627    (
1628        entity,
1629        view_visibility,
1630        transform,
1631        previous_transform,
1632        lightmap,
1633        aabb,
1634        mesh,
1635        tag,
1636        no_frustum_culling,
1637        not_shadow_receiver,
1638        transmitted_receiver,
1639        not_shadow_caster,
1640        no_automatic_batching,
1641        visibility_range,
1642        render_layers,
1643    ): <GpuMeshExtractionQuery as QueryData>::Item<'_, '_>,
1644    render_visibility_ranges: &RenderVisibilityRanges,
1645    render_mesh_instances: &RenderMeshInstancesGpu,
1646    queue: &mut RenderMeshInstanceGpuQueue,
1647    any_gpu_culling: bool,
1648) {
1649    if !view_visibility.get() {
1650        queue.remove(entity.into(), any_gpu_culling);
1651        return;
1652    }
1653
1654    let mut lod_index = None;
1655    if visibility_range {
1656        lod_index = render_visibility_ranges.lod_index_for_entity(entity.into());
1657    }
1658
1659    let mesh_flags = MeshFlags::from_components(
1660        transform,
1661        lod_index,
1662        no_frustum_culling,
1663        not_shadow_receiver,
1664        transmitted_receiver,
1665    );
1666
1667    let shared = RenderMeshInstanceShared::for_gpu_building(
1668        previous_transform,
1669        mesh,
1670        tag,
1671        not_shadow_caster,
1672        no_automatic_batching,
1673        render_layers,
1674        aabb,
1675    );
1676
1677    let lightmap_uv_rect = pack_lightmap_uv_rect(lightmap.map(|lightmap| lightmap.uv_rect));
1678
1679    let gpu_mesh_culling_data = any_gpu_culling.then(|| MeshCullingData::new(aabb));
1680
1681    let previous_input_index = if shared
1682        .flags
1683        .contains(RenderMeshInstanceFlags::HAS_PREVIOUS_TRANSFORM)
1684    {
1685        render_mesh_instances
1686            .get(&MainEntity::from(entity))
1687            .map(|render_mesh_instance| render_mesh_instance.current_uniform_index)
1688    } else {
1689        None
1690    };
1691
1692    let gpu_mesh_instance_builder = RenderMeshInstanceGpuBuilder {
1693        shared,
1694        world_from_local: (&transform.affine()).into(),
1695        lightmap_uv_rect,
1696        mesh_flags,
1697        previous_input_index,
1698    };
1699
1700    queue.push(
1701        entity.into(),
1702        gpu_mesh_instance_builder,
1703        gpu_mesh_culling_data,
1704    );
1705}
1706
1707/// A system that sets the [`RenderMeshInstanceFlags`] for each mesh based on
1708/// whether the previous frame had skins and/or morph targets.
1709///
1710/// Ordinarily, [`RenderMeshInstanceFlags`] are set during the extraction phase.
1711/// However, we can't do that for the flags related to skins and morph targets
1712/// because the previous frame's skin and morph targets are the responsibility
1713/// of [`extract_skins`] and [`extract_morphs`] respectively. We want to run
1714/// those systems in parallel with mesh extraction for performance, so we need
1715/// to defer setting of these mesh instance flags to after extraction, which
1716/// this system does. An alternative to having skin- and morph-target-related
1717/// data in [`RenderMeshInstanceFlags`] would be to have
1718/// [`crate::material::queue_material_meshes`] check the skin and morph target
1719/// tables for each mesh, but that would be too slow in the hot mesh queuing
1720/// loop.
1721pub fn set_mesh_motion_vector_flags(
1722    mut render_mesh_instances: ResMut<RenderMeshInstances>,
1723    skin_uniforms: Res<SkinUniforms>,
1724    morph_indices: Res<MorphIndices>,
1725) {
1726    for &entity in skin_uniforms.all_skins() {
1727        render_mesh_instances
1728            .insert_mesh_instance_flags(entity, RenderMeshInstanceFlags::HAS_PREVIOUS_SKIN);
1729    }
1730    for &entity in morph_indices.prev.keys() {
1731        render_mesh_instances
1732            .insert_mesh_instance_flags(entity, RenderMeshInstanceFlags::HAS_PREVIOUS_MORPH);
1733    }
1734}
1735
1736/// Creates the [`RenderMeshInstanceGpu`]s and [`MeshInputUniform`]s when GPU
1737/// mesh uniforms are built.
1738pub fn collect_meshes_for_gpu_building(
1739    render_mesh_instances: ResMut<RenderMeshInstances>,
1740    batched_instance_buffers: ResMut<
1741        gpu_preprocessing::BatchedInstanceBuffers<MeshUniform, MeshInputUniform>,
1742    >,
1743    mut mesh_culling_data_buffer: ResMut<MeshCullingDataBuffer>,
1744    mut render_mesh_instance_queues: ResMut<RenderMeshInstanceGpuQueues>,
1745    mesh_allocator: Res<MeshAllocator>,
1746    mesh_material_ids: Res<RenderMaterialInstances>,
1747    render_material_bindings: Res<RenderMaterialBindings>,
1748    render_lightmaps: Res<RenderLightmaps>,
1749    skin_uniforms: Res<SkinUniforms>,
1750    frame_count: Res<FrameCount>,
1751    mut meshes_to_reextract_next_frame: ResMut<MeshesToReextractNextFrame>,
1752) {
1753    let RenderMeshInstances::GpuBuilding(render_mesh_instances) =
1754        render_mesh_instances.into_inner()
1755    else {
1756        return;
1757    };
1758
1759    // We're going to rebuild `meshes_to_reextract_next_frame`.
1760    meshes_to_reextract_next_frame.clear();
1761
1762    // Collect render mesh instances. Build up the uniform buffer.
1763    let gpu_preprocessing::BatchedInstanceBuffers {
1764        current_input_buffer,
1765        previous_input_buffer,
1766        ..
1767    } = batched_instance_buffers.into_inner();
1768
1769    previous_input_buffer.clear();
1770
1771    // Build the [`RenderMeshInstance`]s and [`MeshInputUniform`]s.
1772
1773    for queue in render_mesh_instance_queues.iter_mut() {
1774        match *queue {
1775            RenderMeshInstanceGpuQueue::None => {
1776                // This can only happen if the queue is empty.
1777            }
1778
1779            RenderMeshInstanceGpuQueue::CpuCulling {
1780                ref mut changed,
1781                ref mut removed,
1782            } => {
1783                for (entity, mesh_instance_builder) in changed.drain(..) {
1784                    mesh_instance_builder.update(
1785                        entity,
1786                        &mut *render_mesh_instances,
1787                        current_input_buffer,
1788                        previous_input_buffer,
1789                        &mesh_allocator,
1790                        &mesh_material_ids,
1791                        &render_material_bindings,
1792                        &render_lightmaps,
1793                        &skin_uniforms,
1794                        *frame_count,
1795                        &mut meshes_to_reextract_next_frame,
1796                    );
1797                }
1798
1799                for entity in removed.drain(..) {
1800                    remove_mesh_input_uniform(
1801                        entity,
1802                        &mut *render_mesh_instances,
1803                        current_input_buffer,
1804                    );
1805                }
1806            }
1807
1808            RenderMeshInstanceGpuQueue::GpuCulling {
1809                ref mut changed,
1810                ref mut removed,
1811            } => {
1812                for (entity, mesh_instance_builder, mesh_culling_builder) in changed.drain(..) {
1813                    let Some(instance_data_index) = mesh_instance_builder.update(
1814                        entity,
1815                        &mut *render_mesh_instances,
1816                        current_input_buffer,
1817                        previous_input_buffer,
1818                        &mesh_allocator,
1819                        &mesh_material_ids,
1820                        &render_material_bindings,
1821                        &render_lightmaps,
1822                        &skin_uniforms,
1823                        *frame_count,
1824                        &mut meshes_to_reextract_next_frame,
1825                    ) else {
1826                        continue;
1827                    };
1828                    mesh_culling_builder
1829                        .update(&mut mesh_culling_data_buffer, instance_data_index as usize);
1830                }
1831
1832                for entity in removed.drain(..) {
1833                    remove_mesh_input_uniform(
1834                        entity,
1835                        &mut *render_mesh_instances,
1836                        current_input_buffer,
1837                    );
1838                }
1839            }
1840        }
1841    }
1842
1843    // Buffers can't be empty. Make sure there's something in the previous input buffer.
1844    previous_input_buffer.ensure_nonempty();
1845}
1846
1847/// All data needed to construct a pipeline for rendering 3D meshes.
1848#[derive(Resource, Clone)]
1849pub struct MeshPipeline {
1850    /// A reference to all the mesh pipeline view layouts.
1851    pub view_layouts: MeshPipelineViewLayouts,
1852    pub clustered_forward_buffer_binding_type: BufferBindingType,
1853    pub mesh_layouts: MeshLayouts,
1854    /// The shader asset handle.
1855    pub shader: Handle<Shader>,
1856    /// `MeshUniform`s are stored in arrays in buffers. If storage buffers are available, they
1857    /// are used and this will be `None`, otherwise uniform buffers will be used with batches
1858    /// of this many `MeshUniform`s, stored at dynamic offsets within the uniform buffer.
1859    /// Use code like this in custom shaders:
1860    /// ```wgsl
1861    /// ##ifdef PER_OBJECT_BUFFER_BATCH_SIZE
1862    /// @group(1) @binding(0) var<uniform> mesh: array<Mesh, #{PER_OBJECT_BUFFER_BATCH_SIZE}u>;
1863    /// ##else
1864    /// @group(1) @binding(0) var<storage> mesh: array<Mesh>;
1865    /// ##endif // PER_OBJECT_BUFFER_BATCH_SIZE
1866    /// ```
1867    pub per_object_buffer_batch_size: Option<u32>,
1868
1869    /// Whether binding arrays (a.k.a. bindless textures) are usable on the
1870    /// current render device.
1871    ///
1872    /// This affects whether reflection probes can be used.
1873    pub binding_arrays_are_usable: bool,
1874
1875    /// Whether clustered decals are usable on the current render device.
1876    pub clustered_decals_are_usable: bool,
1877
1878    /// Whether skins will use uniform buffers on account of storage buffers
1879    /// being unavailable on this platform.
1880    pub skins_use_uniform_buffers: bool,
1881}
1882
1883impl FromWorld for MeshPipeline {
1884    fn from_world(world: &mut World) -> Self {
1885        let shader = load_embedded_asset!(world, "mesh.wgsl");
1886        let mut system_state: SystemState<(
1887            Res<RenderDevice>,
1888            Res<RenderAdapter>,
1889            Res<MeshPipelineViewLayouts>,
1890        )> = SystemState::new(world);
1891        let (render_device, render_adapter, view_layouts) = system_state.get_mut(world);
1892
1893        let clustered_forward_buffer_binding_type = render_device
1894            .get_supported_read_only_binding_type(CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT);
1895
1896        MeshPipeline {
1897            view_layouts: view_layouts.clone(),
1898            clustered_forward_buffer_binding_type,
1899            mesh_layouts: MeshLayouts::new(&render_device, &render_adapter),
1900            shader,
1901            per_object_buffer_batch_size: GpuArrayBuffer::<MeshUniform>::batch_size(
1902                &render_device.limits(),
1903            ),
1904            binding_arrays_are_usable: binding_arrays_are_usable(&render_device, &render_adapter),
1905            clustered_decals_are_usable: decal::clustered::clustered_decals_are_usable(
1906                &render_device,
1907                &render_adapter,
1908            ),
1909            skins_use_uniform_buffers: skins_use_uniform_buffers(&render_device.limits()),
1910        }
1911    }
1912}
1913
1914impl MeshPipeline {
1915    pub fn get_view_layout(
1916        &self,
1917        layout_key: MeshPipelineViewLayoutKey,
1918    ) -> &MeshPipelineViewLayout {
1919        self.view_layouts.get_view_layout(layout_key)
1920    }
1921}
1922
1923/// A 1x1x1 'all 1.0' texture to use as a dummy texture in place of optional [`crate::pbr_material::StandardMaterial`] textures
1924pub fn build_dummy_white_gpu_image(
1925    render_device: Res<RenderDevice>,
1926    default_sampler: Res<DefaultImageSampler>,
1927    render_queue: Res<RenderQueue>,
1928) -> GpuImage {
1929    let image = Image::default();
1930    let texture = render_device.create_texture(&image.texture_descriptor);
1931    let sampler = match image.sampler {
1932        ImageSampler::Default => (**default_sampler).clone(),
1933        ImageSampler::Descriptor(ref descriptor) => {
1934            render_device.create_sampler(&descriptor.as_wgpu())
1935        }
1936    };
1937
1938    if let Ok(format_size) = image.texture_descriptor.format.pixel_size() {
1939        render_queue.write_texture(
1940            texture.as_image_copy(),
1941            image.data.as_ref().expect("Image was created without data"),
1942            TexelCopyBufferLayout {
1943                offset: 0,
1944                bytes_per_row: Some(image.width() * format_size as u32),
1945                rows_per_image: None,
1946            },
1947            image.texture_descriptor.size,
1948        );
1949    }
1950
1951    let texture_view = texture.create_view(&TextureViewDescriptor::default());
1952    GpuImage {
1953        texture,
1954        texture_view,
1955        texture_format: image.texture_descriptor.format,
1956        texture_view_format: image.texture_view_descriptor.and_then(|v| v.format),
1957        sampler,
1958        size: image.texture_descriptor.size,
1959        mip_level_count: image.texture_descriptor.mip_level_count,
1960        had_data: true,
1961    }
1962}
1963
1964pub fn get_image_texture<'a>(
1965    dummy_white_gpu_image: &'a GpuImage,
1966    gpu_images: &'a RenderAssets<GpuImage>,
1967    handle_option: &Option<Handle<Image>>,
1968) -> Option<(&'a TextureView, &'a Sampler)> {
1969    if let Some(handle) = handle_option {
1970        let gpu_image = gpu_images.get(handle)?;
1971        Some((&gpu_image.texture_view, &gpu_image.sampler))
1972    } else {
1973        Some((
1974            &dummy_white_gpu_image.texture_view,
1975            &dummy_white_gpu_image.sampler,
1976        ))
1977    }
1978}
1979
1980impl GetBatchData for MeshPipeline {
1981    type Param = (
1982        SRes<RenderMeshInstances>,
1983        SRes<RenderLightmaps>,
1984        SRes<RenderAssets<RenderMesh>>,
1985        SRes<MeshAllocator>,
1986        SRes<SkinUniforms>,
1987    );
1988    // The material bind group ID, the mesh ID, and the lightmap ID,
1989    // respectively.
1990    type CompareData = (
1991        MaterialBindGroupIndex,
1992        AssetId<Mesh>,
1993        Option<LightmapSlabIndex>,
1994    );
1995
1996    type BufferData = MeshUniform;
1997
1998    fn get_batch_data(
1999        (mesh_instances, lightmaps, _, mesh_allocator, skin_uniforms): &SystemParamItem<
2000            Self::Param,
2001        >,
2002        (_entity, main_entity): (Entity, MainEntity),
2003    ) -> Option<(Self::BufferData, Option<Self::CompareData>)> {
2004        let RenderMeshInstances::CpuBuilding(ref mesh_instances) = **mesh_instances else {
2005            error!(
2006                "`get_batch_data` should never be called in GPU mesh uniform \
2007                building mode"
2008            );
2009            return None;
2010        };
2011        let mesh_instance = mesh_instances.get(&main_entity)?;
2012        let first_vertex_index =
2013            match mesh_allocator.mesh_vertex_slice(&mesh_instance.mesh_asset_id) {
2014                Some(mesh_vertex_slice) => mesh_vertex_slice.range.start,
2015                None => 0,
2016            };
2017        let maybe_lightmap = lightmaps.render_lightmaps.get(&main_entity);
2018
2019        let current_skin_index = skin_uniforms.skin_index(main_entity);
2020        let material_bind_group_index = mesh_instance.material_bindings_index;
2021
2022        Some((
2023            MeshUniform::new(
2024                &mesh_instance.transforms,
2025                first_vertex_index,
2026                material_bind_group_index.slot,
2027                maybe_lightmap.map(|lightmap| (lightmap.slot_index, lightmap.uv_rect)),
2028                current_skin_index,
2029                Some(mesh_instance.tag),
2030            ),
2031            mesh_instance.should_batch().then_some((
2032                material_bind_group_index.group,
2033                mesh_instance.mesh_asset_id,
2034                maybe_lightmap.map(|lightmap| lightmap.slab_index),
2035            )),
2036        ))
2037    }
2038}
2039
2040impl GetFullBatchData for MeshPipeline {
2041    type BufferInputData = MeshInputUniform;
2042
2043    fn get_index_and_compare_data(
2044        (mesh_instances, lightmaps, _, _, _): &SystemParamItem<Self::Param>,
2045        main_entity: MainEntity,
2046    ) -> Option<(NonMaxU32, Option<Self::CompareData>)> {
2047        // This should only be called during GPU building.
2048        let RenderMeshInstances::GpuBuilding(ref mesh_instances) = **mesh_instances else {
2049            error!(
2050                "`get_index_and_compare_data` should never be called in CPU mesh uniform building \
2051                mode"
2052            );
2053            return None;
2054        };
2055
2056        let mesh_instance = mesh_instances.get(&main_entity)?;
2057        let maybe_lightmap = lightmaps.render_lightmaps.get(&main_entity);
2058
2059        Some((
2060            mesh_instance.current_uniform_index,
2061            mesh_instance.should_batch().then_some((
2062                mesh_instance.material_bindings_index.group,
2063                mesh_instance.mesh_asset_id,
2064                maybe_lightmap.map(|lightmap| lightmap.slab_index),
2065            )),
2066        ))
2067    }
2068
2069    fn get_binned_batch_data(
2070        (mesh_instances, lightmaps, _, mesh_allocator, skin_uniforms): &SystemParamItem<
2071            Self::Param,
2072        >,
2073        main_entity: MainEntity,
2074    ) -> Option<Self::BufferData> {
2075        let RenderMeshInstances::CpuBuilding(ref mesh_instances) = **mesh_instances else {
2076            error!(
2077                "`get_binned_batch_data` should never be called in GPU mesh uniform building mode"
2078            );
2079            return None;
2080        };
2081        let mesh_instance = mesh_instances.get(&main_entity)?;
2082        let first_vertex_index =
2083            match mesh_allocator.mesh_vertex_slice(&mesh_instance.mesh_asset_id) {
2084                Some(mesh_vertex_slice) => mesh_vertex_slice.range.start,
2085                None => 0,
2086            };
2087        let maybe_lightmap = lightmaps.render_lightmaps.get(&main_entity);
2088
2089        let current_skin_index = skin_uniforms.skin_index(main_entity);
2090
2091        Some(MeshUniform::new(
2092            &mesh_instance.transforms,
2093            first_vertex_index,
2094            mesh_instance.material_bindings_index.slot,
2095            maybe_lightmap.map(|lightmap| (lightmap.slot_index, lightmap.uv_rect)),
2096            current_skin_index,
2097            Some(mesh_instance.tag),
2098        ))
2099    }
2100
2101    fn get_binned_index(
2102        (mesh_instances, _, _, _, _): &SystemParamItem<Self::Param>,
2103        main_entity: MainEntity,
2104    ) -> Option<NonMaxU32> {
2105        // This should only be called during GPU building.
2106        let RenderMeshInstances::GpuBuilding(ref mesh_instances) = **mesh_instances else {
2107            error!(
2108                "`get_binned_index` should never be called in CPU mesh uniform \
2109                building mode"
2110            );
2111            return None;
2112        };
2113
2114        mesh_instances
2115            .get(&main_entity)
2116            .map(|entity| entity.current_uniform_index)
2117    }
2118
2119    fn write_batch_indirect_parameters_metadata(
2120        indexed: bool,
2121        base_output_index: u32,
2122        batch_set_index: Option<NonMaxU32>,
2123        phase_indirect_parameters_buffers: &mut UntypedPhaseIndirectParametersBuffers,
2124        indirect_parameters_offset: u32,
2125    ) {
2126        let indirect_parameters = IndirectParametersCpuMetadata {
2127            base_output_index,
2128            batch_set_index: match batch_set_index {
2129                Some(batch_set_index) => u32::from(batch_set_index),
2130                None => !0,
2131            },
2132        };
2133
2134        if indexed {
2135            phase_indirect_parameters_buffers
2136                .indexed
2137                .set(indirect_parameters_offset, indirect_parameters);
2138        } else {
2139            phase_indirect_parameters_buffers
2140                .non_indexed
2141                .set(indirect_parameters_offset, indirect_parameters);
2142        }
2143    }
2144}
2145
2146bitflags::bitflags! {
2147    #[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Hash)]
2148    #[repr(transparent)]
2149    // NOTE: Apparently quadro drivers support up to 64x MSAA.
2150    /// MSAA uses the highest 3 bits for the MSAA log2(sample count) to support up to 128x MSAA.
2151    pub struct MeshPipelineKey: u64 {
2152        // Nothing
2153        const NONE                              = 0;
2154
2155        // Inherited bits
2156        const MORPH_TARGETS                     = BaseMeshPipelineKey::MORPH_TARGETS.bits();
2157
2158        // Flag bits
2159        const HDR                               = 1 << 0;
2160        const TONEMAP_IN_SHADER                 = 1 << 1;
2161        const DEBAND_DITHER                     = 1 << 2;
2162        const DEPTH_PREPASS                     = 1 << 3;
2163        const NORMAL_PREPASS                    = 1 << 4;
2164        const DEFERRED_PREPASS                  = 1 << 5;
2165        const MOTION_VECTOR_PREPASS             = 1 << 6;
2166        const MAY_DISCARD                       = 1 << 7; // Guards shader codepaths that may discard, allowing early depth tests in most cases
2167                                                            // See: https://www.khronos.org/opengl/wiki/Early_Fragment_Test
2168        const ENVIRONMENT_MAP                   = 1 << 8;
2169        const SCREEN_SPACE_AMBIENT_OCCLUSION    = 1 << 9;
2170        const UNCLIPPED_DEPTH_ORTHO             = 1 << 10; // Disables depth clipping for use with directional light shadow views
2171                                                            // Emulated via fragment shader depth on hardware that doesn't support it natively
2172                                                            // See: https://www.w3.org/TR/webgpu/#depth-clipping and https://therealmjp.github.io/posts/shadow-maps/#disabling-z-clipping
2173        const TEMPORAL_JITTER                   = 1 << 11;
2174        const READS_VIEW_TRANSMISSION_TEXTURE   = 1 << 12;
2175        const LIGHTMAPPED                       = 1 << 13;
2176        const LIGHTMAP_BICUBIC_SAMPLING         = 1 << 14;
2177        const IRRADIANCE_VOLUME                 = 1 << 15;
2178        const VISIBILITY_RANGE_DITHER           = 1 << 16;
2179        const SCREEN_SPACE_REFLECTIONS          = 1 << 17;
2180        const HAS_PREVIOUS_SKIN                 = 1 << 18;
2181        const HAS_PREVIOUS_MORPH                = 1 << 19;
2182        const OIT_ENABLED                       = 1 << 20;
2183        const DISTANCE_FOG                      = 1 << 21;
2184        const ATMOSPHERE                        = 1 << 22;
2185        const INVERT_CULLING                    = 1 << 23;
2186        const LAST_FLAG                         = Self::INVERT_CULLING.bits();
2187
2188        // Bitfields
2189        const MSAA_RESERVED_BITS                = Self::MSAA_MASK_BITS << Self::MSAA_SHIFT_BITS;
2190        const BLEND_RESERVED_BITS               = Self::BLEND_MASK_BITS << Self::BLEND_SHIFT_BITS; // ← Bitmask reserving bits for the blend state
2191        const BLEND_OPAQUE                      = 0 << Self::BLEND_SHIFT_BITS;                     // ← Values are just sequential within the mask
2192        const BLEND_PREMULTIPLIED_ALPHA         = 1 << Self::BLEND_SHIFT_BITS;                     // ← As blend states is on 3 bits, it can range from 0 to 7
2193        const BLEND_MULTIPLY                    = 2 << Self::BLEND_SHIFT_BITS;                     // ← See `BLEND_MASK_BITS` for the number of bits available
2194        const BLEND_ALPHA                       = 3 << Self::BLEND_SHIFT_BITS;                     //
2195        const BLEND_ALPHA_TO_COVERAGE           = 4 << Self::BLEND_SHIFT_BITS;                     // ← We still have room for three more values without adding more bits
2196        const TONEMAP_METHOD_RESERVED_BITS      = Self::TONEMAP_METHOD_MASK_BITS << Self::TONEMAP_METHOD_SHIFT_BITS;
2197        const TONEMAP_METHOD_NONE               = 0 << Self::TONEMAP_METHOD_SHIFT_BITS;
2198        const TONEMAP_METHOD_REINHARD           = 1 << Self::TONEMAP_METHOD_SHIFT_BITS;
2199        const TONEMAP_METHOD_REINHARD_LUMINANCE = 2 << Self::TONEMAP_METHOD_SHIFT_BITS;
2200        const TONEMAP_METHOD_ACES_FITTED        = 3 << Self::TONEMAP_METHOD_SHIFT_BITS;
2201        const TONEMAP_METHOD_AGX                = 4 << Self::TONEMAP_METHOD_SHIFT_BITS;
2202        const TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM = 5 << Self::TONEMAP_METHOD_SHIFT_BITS;
2203        const TONEMAP_METHOD_TONY_MC_MAPFACE    = 6 << Self::TONEMAP_METHOD_SHIFT_BITS;
2204        const TONEMAP_METHOD_BLENDER_FILMIC     = 7 << Self::TONEMAP_METHOD_SHIFT_BITS;
2205        const SHADOW_FILTER_METHOD_RESERVED_BITS = Self::SHADOW_FILTER_METHOD_MASK_BITS << Self::SHADOW_FILTER_METHOD_SHIFT_BITS;
2206        const SHADOW_FILTER_METHOD_HARDWARE_2X2  = 0 << Self::SHADOW_FILTER_METHOD_SHIFT_BITS;
2207        const SHADOW_FILTER_METHOD_GAUSSIAN      = 1 << Self::SHADOW_FILTER_METHOD_SHIFT_BITS;
2208        const SHADOW_FILTER_METHOD_TEMPORAL      = 2 << Self::SHADOW_FILTER_METHOD_SHIFT_BITS;
2209        const VIEW_PROJECTION_RESERVED_BITS     = Self::VIEW_PROJECTION_MASK_BITS << Self::VIEW_PROJECTION_SHIFT_BITS;
2210        const VIEW_PROJECTION_NONSTANDARD       = 0 << Self::VIEW_PROJECTION_SHIFT_BITS;
2211        const VIEW_PROJECTION_PERSPECTIVE       = 1 << Self::VIEW_PROJECTION_SHIFT_BITS;
2212        const VIEW_PROJECTION_ORTHOGRAPHIC      = 2 << Self::VIEW_PROJECTION_SHIFT_BITS;
2213        const VIEW_PROJECTION_RESERVED          = 3 << Self::VIEW_PROJECTION_SHIFT_BITS;
2214        const SCREEN_SPACE_SPECULAR_TRANSMISSION_RESERVED_BITS = Self::SCREEN_SPACE_SPECULAR_TRANSMISSION_MASK_BITS << Self::SCREEN_SPACE_SPECULAR_TRANSMISSION_SHIFT_BITS;
2215        const SCREEN_SPACE_SPECULAR_TRANSMISSION_LOW    = 0 << Self::SCREEN_SPACE_SPECULAR_TRANSMISSION_SHIFT_BITS;
2216        const SCREEN_SPACE_SPECULAR_TRANSMISSION_MEDIUM = 1 << Self::SCREEN_SPACE_SPECULAR_TRANSMISSION_SHIFT_BITS;
2217        const SCREEN_SPACE_SPECULAR_TRANSMISSION_HIGH   = 2 << Self::SCREEN_SPACE_SPECULAR_TRANSMISSION_SHIFT_BITS;
2218        const SCREEN_SPACE_SPECULAR_TRANSMISSION_ULTRA  = 3 << Self::SCREEN_SPACE_SPECULAR_TRANSMISSION_SHIFT_BITS;
2219        const ALL_RESERVED_BITS =
2220            Self::BLEND_RESERVED_BITS.bits() |
2221            Self::MSAA_RESERVED_BITS.bits() |
2222            Self::TONEMAP_METHOD_RESERVED_BITS.bits() |
2223            Self::SHADOW_FILTER_METHOD_RESERVED_BITS.bits() |
2224            Self::VIEW_PROJECTION_RESERVED_BITS.bits() |
2225            Self::SCREEN_SPACE_SPECULAR_TRANSMISSION_RESERVED_BITS.bits();
2226    }
2227}
2228
2229impl MeshPipelineKey {
2230    const MSAA_MASK_BITS: u64 = 0b111;
2231    const MSAA_SHIFT_BITS: u64 = Self::LAST_FLAG.bits().trailing_zeros() as u64 + 1;
2232
2233    const BLEND_MASK_BITS: u64 = 0b111;
2234    const BLEND_SHIFT_BITS: u64 = Self::MSAA_MASK_BITS.count_ones() as u64 + Self::MSAA_SHIFT_BITS;
2235
2236    const TONEMAP_METHOD_MASK_BITS: u64 = 0b111;
2237    const TONEMAP_METHOD_SHIFT_BITS: u64 =
2238        Self::BLEND_MASK_BITS.count_ones() as u64 + Self::BLEND_SHIFT_BITS;
2239
2240    const SHADOW_FILTER_METHOD_MASK_BITS: u64 = 0b11;
2241    const SHADOW_FILTER_METHOD_SHIFT_BITS: u64 =
2242        Self::TONEMAP_METHOD_MASK_BITS.count_ones() as u64 + Self::TONEMAP_METHOD_SHIFT_BITS;
2243
2244    const VIEW_PROJECTION_MASK_BITS: u64 = 0b11;
2245    const VIEW_PROJECTION_SHIFT_BITS: u64 = Self::SHADOW_FILTER_METHOD_MASK_BITS.count_ones()
2246        as u64
2247        + Self::SHADOW_FILTER_METHOD_SHIFT_BITS;
2248
2249    const SCREEN_SPACE_SPECULAR_TRANSMISSION_MASK_BITS: u64 = 0b11;
2250    const SCREEN_SPACE_SPECULAR_TRANSMISSION_SHIFT_BITS: u64 =
2251        Self::VIEW_PROJECTION_MASK_BITS.count_ones() as u64 + Self::VIEW_PROJECTION_SHIFT_BITS;
2252
2253    pub fn from_msaa_samples(msaa_samples: u32) -> Self {
2254        let msaa_bits =
2255            (msaa_samples.trailing_zeros() as u64 & Self::MSAA_MASK_BITS) << Self::MSAA_SHIFT_BITS;
2256        Self::from_bits_retain(msaa_bits)
2257    }
2258
2259    pub fn from_hdr(hdr: bool) -> Self {
2260        if hdr {
2261            MeshPipelineKey::HDR
2262        } else {
2263            MeshPipelineKey::NONE
2264        }
2265    }
2266
2267    pub fn msaa_samples(&self) -> u32 {
2268        1 << ((self.bits() >> Self::MSAA_SHIFT_BITS) & Self::MSAA_MASK_BITS)
2269    }
2270
2271    pub fn from_primitive_topology(primitive_topology: PrimitiveTopology) -> Self {
2272        let primitive_topology_bits = ((primitive_topology as u64)
2273            & BaseMeshPipelineKey::PRIMITIVE_TOPOLOGY_MASK_BITS)
2274            << BaseMeshPipelineKey::PRIMITIVE_TOPOLOGY_SHIFT_BITS;
2275        Self::from_bits_retain(primitive_topology_bits)
2276    }
2277
2278    pub fn primitive_topology(&self) -> PrimitiveTopology {
2279        let primitive_topology_bits = (self.bits()
2280            >> BaseMeshPipelineKey::PRIMITIVE_TOPOLOGY_SHIFT_BITS)
2281            & BaseMeshPipelineKey::PRIMITIVE_TOPOLOGY_MASK_BITS;
2282        match primitive_topology_bits {
2283            x if x == PrimitiveTopology::PointList as u64 => PrimitiveTopology::PointList,
2284            x if x == PrimitiveTopology::LineList as u64 => PrimitiveTopology::LineList,
2285            x if x == PrimitiveTopology::LineStrip as u64 => PrimitiveTopology::LineStrip,
2286            x if x == PrimitiveTopology::TriangleList as u64 => PrimitiveTopology::TriangleList,
2287            x if x == PrimitiveTopology::TriangleStrip as u64 => PrimitiveTopology::TriangleStrip,
2288            _ => PrimitiveTopology::default(),
2289        }
2290    }
2291}
2292
2293// Ensure that we didn't overflow the number of bits available in `MeshPipelineKey`.
2294const_assert_eq!(
2295    (((MeshPipelineKey::LAST_FLAG.bits() << 1) - 1) | MeshPipelineKey::ALL_RESERVED_BITS.bits())
2296        & BaseMeshPipelineKey::all().bits(),
2297    0
2298);
2299
2300// Ensure that the reserved bits don't overlap with the topology bits
2301const_assert_eq!(
2302    (BaseMeshPipelineKey::PRIMITIVE_TOPOLOGY_MASK_BITS
2303        << BaseMeshPipelineKey::PRIMITIVE_TOPOLOGY_SHIFT_BITS)
2304        & MeshPipelineKey::ALL_RESERVED_BITS.bits(),
2305    0
2306);
2307
2308fn is_skinned(layout: &MeshVertexBufferLayoutRef) -> bool {
2309    layout.0.contains(Mesh::ATTRIBUTE_JOINT_INDEX)
2310        && layout.0.contains(Mesh::ATTRIBUTE_JOINT_WEIGHT)
2311}
2312pub fn setup_morph_and_skinning_defs(
2313    mesh_layouts: &MeshLayouts,
2314    layout: &MeshVertexBufferLayoutRef,
2315    offset: u32,
2316    key: &MeshPipelineKey,
2317    shader_defs: &mut Vec<ShaderDefVal>,
2318    vertex_attributes: &mut Vec<VertexAttributeDescriptor>,
2319    skins_use_uniform_buffers: bool,
2320) -> BindGroupLayoutDescriptor {
2321    let is_morphed = key.intersects(MeshPipelineKey::MORPH_TARGETS);
2322    let is_lightmapped = key.intersects(MeshPipelineKey::LIGHTMAPPED);
2323    let motion_vector_prepass = key.intersects(MeshPipelineKey::MOTION_VECTOR_PREPASS);
2324
2325    if skins_use_uniform_buffers {
2326        shader_defs.push("SKINS_USE_UNIFORM_BUFFERS".into());
2327    }
2328
2329    let mut add_skin_data = || {
2330        shader_defs.push("SKINNED".into());
2331        vertex_attributes.push(Mesh::ATTRIBUTE_JOINT_INDEX.at_shader_location(offset));
2332        vertex_attributes.push(Mesh::ATTRIBUTE_JOINT_WEIGHT.at_shader_location(offset + 1));
2333    };
2334
2335    match (
2336        is_skinned(layout),
2337        is_morphed,
2338        is_lightmapped,
2339        motion_vector_prepass,
2340    ) {
2341        (true, false, _, true) => {
2342            add_skin_data();
2343            mesh_layouts.skinned_motion.clone()
2344        }
2345        (true, false, _, false) => {
2346            add_skin_data();
2347            mesh_layouts.skinned.clone()
2348        }
2349        (true, true, _, true) => {
2350            add_skin_data();
2351            shader_defs.push("MORPH_TARGETS".into());
2352            mesh_layouts.morphed_skinned_motion.clone()
2353        }
2354        (true, true, _, false) => {
2355            add_skin_data();
2356            shader_defs.push("MORPH_TARGETS".into());
2357            mesh_layouts.morphed_skinned.clone()
2358        }
2359        (false, true, _, true) => {
2360            shader_defs.push("MORPH_TARGETS".into());
2361            mesh_layouts.morphed_motion.clone()
2362        }
2363        (false, true, _, false) => {
2364            shader_defs.push("MORPH_TARGETS".into());
2365            mesh_layouts.morphed.clone()
2366        }
2367        (false, false, true, _) => mesh_layouts.lightmapped.clone(),
2368        (false, false, false, _) => mesh_layouts.model_only.clone(),
2369    }
2370}
2371
2372impl SpecializedMeshPipeline for MeshPipeline {
2373    type Key = MeshPipelineKey;
2374
2375    fn specialize(
2376        &self,
2377        key: Self::Key,
2378        layout: &MeshVertexBufferLayoutRef,
2379    ) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
2380        let mut shader_defs = Vec::new();
2381        let mut vertex_attributes = Vec::new();
2382
2383        // Let the shader code know that it's running in a mesh pipeline.
2384        shader_defs.push("MESH_PIPELINE".into());
2385
2386        shader_defs.push("VERTEX_OUTPUT_INSTANCE_INDEX".into());
2387
2388        if layout.0.contains(Mesh::ATTRIBUTE_POSITION) {
2389            shader_defs.push("VERTEX_POSITIONS".into());
2390            vertex_attributes.push(Mesh::ATTRIBUTE_POSITION.at_shader_location(0));
2391        }
2392
2393        if layout.0.contains(Mesh::ATTRIBUTE_NORMAL) {
2394            shader_defs.push("VERTEX_NORMALS".into());
2395            vertex_attributes.push(Mesh::ATTRIBUTE_NORMAL.at_shader_location(1));
2396        }
2397
2398        if layout.0.contains(Mesh::ATTRIBUTE_UV_0) {
2399            shader_defs.push("VERTEX_UVS".into());
2400            shader_defs.push("VERTEX_UVS_A".into());
2401            vertex_attributes.push(Mesh::ATTRIBUTE_UV_0.at_shader_location(2));
2402        }
2403
2404        if layout.0.contains(Mesh::ATTRIBUTE_UV_1) {
2405            shader_defs.push("VERTEX_UVS".into());
2406            shader_defs.push("VERTEX_UVS_B".into());
2407            vertex_attributes.push(Mesh::ATTRIBUTE_UV_1.at_shader_location(3));
2408        }
2409
2410        if layout.0.contains(Mesh::ATTRIBUTE_TANGENT) {
2411            shader_defs.push("VERTEX_TANGENTS".into());
2412            vertex_attributes.push(Mesh::ATTRIBUTE_TANGENT.at_shader_location(4));
2413        }
2414
2415        if layout.0.contains(Mesh::ATTRIBUTE_COLOR) {
2416            shader_defs.push("VERTEX_COLORS".into());
2417            vertex_attributes.push(Mesh::ATTRIBUTE_COLOR.at_shader_location(5));
2418        }
2419
2420        if cfg!(feature = "pbr_transmission_textures") {
2421            shader_defs.push("PBR_TRANSMISSION_TEXTURES_SUPPORTED".into());
2422        }
2423        if cfg!(feature = "pbr_multi_layer_material_textures") {
2424            shader_defs.push("PBR_MULTI_LAYER_MATERIAL_TEXTURES_SUPPORTED".into());
2425        }
2426        if cfg!(feature = "pbr_anisotropy_texture") {
2427            shader_defs.push("PBR_ANISOTROPY_TEXTURE_SUPPORTED".into());
2428        }
2429        if cfg!(feature = "pbr_specular_textures") {
2430            shader_defs.push("PBR_SPECULAR_TEXTURES_SUPPORTED".into());
2431        }
2432
2433        let bind_group_layout = self.get_view_layout(key.into());
2434        let mut bind_group_layout = vec![
2435            bind_group_layout.main_layout.clone(),
2436            bind_group_layout.binding_array_layout.clone(),
2437        ];
2438
2439        if key.msaa_samples() > 1 {
2440            shader_defs.push("MULTISAMPLED".into());
2441        };
2442
2443        bind_group_layout.push(setup_morph_and_skinning_defs(
2444            &self.mesh_layouts,
2445            layout,
2446            6,
2447            &key,
2448            &mut shader_defs,
2449            &mut vertex_attributes,
2450            self.skins_use_uniform_buffers,
2451        ));
2452
2453        if key.contains(MeshPipelineKey::SCREEN_SPACE_AMBIENT_OCCLUSION) {
2454            shader_defs.push("SCREEN_SPACE_AMBIENT_OCCLUSION".into());
2455        }
2456
2457        let vertex_buffer_layout = layout.0.get_layout(&vertex_attributes)?;
2458
2459        let (label, blend, depth_write_enabled);
2460        let pass = key.intersection(MeshPipelineKey::BLEND_RESERVED_BITS);
2461        let (mut is_opaque, mut alpha_to_coverage_enabled) = (false, false);
2462        if key.contains(MeshPipelineKey::OIT_ENABLED) && pass == MeshPipelineKey::BLEND_ALPHA {
2463            label = "oit_mesh_pipeline".into();
2464            // TODO tail blending would need alpha blending
2465            blend = None;
2466            shader_defs.push("OIT_ENABLED".into());
2467            // TODO it should be possible to use this to combine MSAA and OIT
2468            // alpha_to_coverage_enabled = true;
2469            depth_write_enabled = false;
2470        } else if pass == MeshPipelineKey::BLEND_ALPHA {
2471            label = "alpha_blend_mesh_pipeline".into();
2472            blend = Some(BlendState::ALPHA_BLENDING);
2473            // For the transparent pass, fragments that are closer will be alpha blended
2474            // but their depth is not written to the depth buffer
2475            depth_write_enabled = false;
2476        } else if pass == MeshPipelineKey::BLEND_PREMULTIPLIED_ALPHA {
2477            label = "premultiplied_alpha_mesh_pipeline".into();
2478            blend = Some(BlendState::PREMULTIPLIED_ALPHA_BLENDING);
2479            shader_defs.push("PREMULTIPLY_ALPHA".into());
2480            shader_defs.push("BLEND_PREMULTIPLIED_ALPHA".into());
2481            // For the transparent pass, fragments that are closer will be alpha blended
2482            // but their depth is not written to the depth buffer
2483            depth_write_enabled = false;
2484        } else if pass == MeshPipelineKey::BLEND_MULTIPLY {
2485            label = "multiply_mesh_pipeline".into();
2486            blend = Some(BlendState {
2487                color: BlendComponent {
2488                    src_factor: BlendFactor::Dst,
2489                    dst_factor: BlendFactor::OneMinusSrcAlpha,
2490                    operation: BlendOperation::Add,
2491                },
2492                alpha: BlendComponent::OVER,
2493            });
2494            shader_defs.push("PREMULTIPLY_ALPHA".into());
2495            shader_defs.push("BLEND_MULTIPLY".into());
2496            // For the multiply pass, fragments that are closer will be alpha blended
2497            // but their depth is not written to the depth buffer
2498            depth_write_enabled = false;
2499        } else if pass == MeshPipelineKey::BLEND_ALPHA_TO_COVERAGE {
2500            label = "alpha_to_coverage_mesh_pipeline".into();
2501            // BlendState::REPLACE is not needed here, and None will be potentially much faster in some cases
2502            blend = None;
2503            // For the opaque and alpha mask passes, fragments that are closer will replace
2504            // the current fragment value in the output and the depth is written to the
2505            // depth buffer
2506            depth_write_enabled = true;
2507            is_opaque = !key.contains(MeshPipelineKey::READS_VIEW_TRANSMISSION_TEXTURE);
2508            alpha_to_coverage_enabled = true;
2509            shader_defs.push("ALPHA_TO_COVERAGE".into());
2510        } else {
2511            label = "opaque_mesh_pipeline".into();
2512            // BlendState::REPLACE is not needed here, and None will be potentially much faster in some cases
2513            blend = None;
2514            // For the opaque and alpha mask passes, fragments that are closer will replace
2515            // the current fragment value in the output and the depth is written to the
2516            // depth buffer
2517            depth_write_enabled = true;
2518            is_opaque = !key.contains(MeshPipelineKey::READS_VIEW_TRANSMISSION_TEXTURE);
2519        }
2520
2521        if key.contains(MeshPipelineKey::NORMAL_PREPASS) {
2522            shader_defs.push("NORMAL_PREPASS".into());
2523        }
2524
2525        if key.contains(MeshPipelineKey::DEPTH_PREPASS) {
2526            shader_defs.push("DEPTH_PREPASS".into());
2527        }
2528
2529        if key.contains(MeshPipelineKey::MOTION_VECTOR_PREPASS) {
2530            shader_defs.push("MOTION_VECTOR_PREPASS".into());
2531        }
2532
2533        if key.contains(MeshPipelineKey::HAS_PREVIOUS_SKIN) {
2534            shader_defs.push("HAS_PREVIOUS_SKIN".into());
2535        }
2536
2537        if key.contains(MeshPipelineKey::HAS_PREVIOUS_MORPH) {
2538            shader_defs.push("HAS_PREVIOUS_MORPH".into());
2539        }
2540
2541        if key.contains(MeshPipelineKey::DEFERRED_PREPASS) {
2542            shader_defs.push("DEFERRED_PREPASS".into());
2543        }
2544
2545        if key.contains(MeshPipelineKey::NORMAL_PREPASS) && key.msaa_samples() == 1 && is_opaque {
2546            shader_defs.push("LOAD_PREPASS_NORMALS".into());
2547        }
2548
2549        let view_projection = key.intersection(MeshPipelineKey::VIEW_PROJECTION_RESERVED_BITS);
2550        if view_projection == MeshPipelineKey::VIEW_PROJECTION_NONSTANDARD {
2551            shader_defs.push("VIEW_PROJECTION_NONSTANDARD".into());
2552        } else if view_projection == MeshPipelineKey::VIEW_PROJECTION_PERSPECTIVE {
2553            shader_defs.push("VIEW_PROJECTION_PERSPECTIVE".into());
2554        } else if view_projection == MeshPipelineKey::VIEW_PROJECTION_ORTHOGRAPHIC {
2555            shader_defs.push("VIEW_PROJECTION_ORTHOGRAPHIC".into());
2556        }
2557
2558        #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
2559        shader_defs.push("WEBGL2".into());
2560
2561        #[cfg(feature = "experimental_pbr_pcss")]
2562        shader_defs.push("PCSS_SAMPLERS_AVAILABLE".into());
2563
2564        if key.contains(MeshPipelineKey::TONEMAP_IN_SHADER) {
2565            shader_defs.push("TONEMAP_IN_SHADER".into());
2566            shader_defs.push(ShaderDefVal::UInt(
2567                "TONEMAPPING_LUT_TEXTURE_BINDING_INDEX".into(),
2568                TONEMAPPING_LUT_TEXTURE_BINDING_INDEX,
2569            ));
2570            shader_defs.push(ShaderDefVal::UInt(
2571                "TONEMAPPING_LUT_SAMPLER_BINDING_INDEX".into(),
2572                TONEMAPPING_LUT_SAMPLER_BINDING_INDEX,
2573            ));
2574
2575            let method = key.intersection(MeshPipelineKey::TONEMAP_METHOD_RESERVED_BITS);
2576
2577            if method == MeshPipelineKey::TONEMAP_METHOD_NONE {
2578                shader_defs.push("TONEMAP_METHOD_NONE".into());
2579            } else if method == MeshPipelineKey::TONEMAP_METHOD_REINHARD {
2580                shader_defs.push("TONEMAP_METHOD_REINHARD".into());
2581            } else if method == MeshPipelineKey::TONEMAP_METHOD_REINHARD_LUMINANCE {
2582                shader_defs.push("TONEMAP_METHOD_REINHARD_LUMINANCE".into());
2583            } else if method == MeshPipelineKey::TONEMAP_METHOD_ACES_FITTED {
2584                shader_defs.push("TONEMAP_METHOD_ACES_FITTED".into());
2585            } else if method == MeshPipelineKey::TONEMAP_METHOD_AGX {
2586                shader_defs.push("TONEMAP_METHOD_AGX".into());
2587            } else if method == MeshPipelineKey::TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM {
2588                shader_defs.push("TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM".into());
2589            } else if method == MeshPipelineKey::TONEMAP_METHOD_BLENDER_FILMIC {
2590                shader_defs.push("TONEMAP_METHOD_BLENDER_FILMIC".into());
2591            } else if method == MeshPipelineKey::TONEMAP_METHOD_TONY_MC_MAPFACE {
2592                shader_defs.push("TONEMAP_METHOD_TONY_MC_MAPFACE".into());
2593            }
2594
2595            // Debanding is tied to tonemapping in the shader, cannot run without it.
2596            if key.contains(MeshPipelineKey::DEBAND_DITHER) {
2597                shader_defs.push("DEBAND_DITHER".into());
2598            }
2599        }
2600
2601        if key.contains(MeshPipelineKey::MAY_DISCARD) {
2602            shader_defs.push("MAY_DISCARD".into());
2603        }
2604
2605        if key.contains(MeshPipelineKey::ENVIRONMENT_MAP) {
2606            shader_defs.push("ENVIRONMENT_MAP".into());
2607        }
2608
2609        if key.contains(MeshPipelineKey::IRRADIANCE_VOLUME) && IRRADIANCE_VOLUMES_ARE_USABLE {
2610            shader_defs.push("IRRADIANCE_VOLUME".into());
2611        }
2612
2613        if key.contains(MeshPipelineKey::LIGHTMAPPED) {
2614            shader_defs.push("LIGHTMAP".into());
2615        }
2616        if key.contains(MeshPipelineKey::LIGHTMAP_BICUBIC_SAMPLING) {
2617            shader_defs.push("LIGHTMAP_BICUBIC_SAMPLING".into());
2618        }
2619
2620        if key.contains(MeshPipelineKey::TEMPORAL_JITTER) {
2621            shader_defs.push("TEMPORAL_JITTER".into());
2622        }
2623
2624        let shadow_filter_method =
2625            key.intersection(MeshPipelineKey::SHADOW_FILTER_METHOD_RESERVED_BITS);
2626        if shadow_filter_method == MeshPipelineKey::SHADOW_FILTER_METHOD_HARDWARE_2X2 {
2627            shader_defs.push("SHADOW_FILTER_METHOD_HARDWARE_2X2".into());
2628        } else if shadow_filter_method == MeshPipelineKey::SHADOW_FILTER_METHOD_GAUSSIAN {
2629            shader_defs.push("SHADOW_FILTER_METHOD_GAUSSIAN".into());
2630        } else if shadow_filter_method == MeshPipelineKey::SHADOW_FILTER_METHOD_TEMPORAL {
2631            shader_defs.push("SHADOW_FILTER_METHOD_TEMPORAL".into());
2632        }
2633
2634        let blur_quality =
2635            key.intersection(MeshPipelineKey::SCREEN_SPACE_SPECULAR_TRANSMISSION_RESERVED_BITS);
2636
2637        shader_defs.push(ShaderDefVal::Int(
2638            "SCREEN_SPACE_SPECULAR_TRANSMISSION_BLUR_TAPS".into(),
2639            match blur_quality {
2640                MeshPipelineKey::SCREEN_SPACE_SPECULAR_TRANSMISSION_LOW => 4,
2641                MeshPipelineKey::SCREEN_SPACE_SPECULAR_TRANSMISSION_MEDIUM => 8,
2642                MeshPipelineKey::SCREEN_SPACE_SPECULAR_TRANSMISSION_HIGH => 16,
2643                MeshPipelineKey::SCREEN_SPACE_SPECULAR_TRANSMISSION_ULTRA => 32,
2644                _ => unreachable!(), // Not possible, since the mask is 2 bits, and we've covered all 4 cases
2645            },
2646        ));
2647
2648        if key.contains(MeshPipelineKey::VISIBILITY_RANGE_DITHER) {
2649            shader_defs.push("VISIBILITY_RANGE_DITHER".into());
2650        }
2651
2652        if key.contains(MeshPipelineKey::DISTANCE_FOG) {
2653            shader_defs.push("DISTANCE_FOG".into());
2654        }
2655
2656        if key.contains(MeshPipelineKey::ATMOSPHERE) {
2657            shader_defs.push("ATMOSPHERE".into());
2658        }
2659
2660        if self.binding_arrays_are_usable {
2661            shader_defs.push("MULTIPLE_LIGHT_PROBES_IN_ARRAY".into());
2662            shader_defs.push("MULTIPLE_LIGHTMAPS_IN_ARRAY".into());
2663        }
2664
2665        if IRRADIANCE_VOLUMES_ARE_USABLE {
2666            shader_defs.push("IRRADIANCE_VOLUMES_ARE_USABLE".into());
2667        }
2668
2669        if self.clustered_decals_are_usable {
2670            shader_defs.push("CLUSTERED_DECALS_ARE_USABLE".into());
2671            if cfg!(feature = "pbr_light_textures") {
2672                shader_defs.push("LIGHT_TEXTURES".into());
2673            }
2674        }
2675
2676        let format = if key.contains(MeshPipelineKey::HDR) {
2677            ViewTarget::TEXTURE_FORMAT_HDR
2678        } else {
2679            TextureFormat::bevy_default()
2680        };
2681
2682        // This is defined here so that custom shaders that use something other than
2683        // the mesh binding from bevy_pbr::mesh_bindings can easily make use of this
2684        // in their own shaders.
2685        if let Some(per_object_buffer_batch_size) = self.per_object_buffer_batch_size {
2686            shader_defs.push(ShaderDefVal::UInt(
2687                "PER_OBJECT_BUFFER_BATCH_SIZE".into(),
2688                per_object_buffer_batch_size,
2689            ));
2690        }
2691
2692        Ok(RenderPipelineDescriptor {
2693            vertex: VertexState {
2694                shader: self.shader.clone(),
2695                shader_defs: shader_defs.clone(),
2696                buffers: vec![vertex_buffer_layout],
2697                ..default()
2698            },
2699            fragment: Some(FragmentState {
2700                shader: self.shader.clone(),
2701                shader_defs,
2702                targets: vec![Some(ColorTargetState {
2703                    format,
2704                    blend,
2705                    write_mask: ColorWrites::ALL,
2706                })],
2707                ..default()
2708            }),
2709            layout: bind_group_layout,
2710            primitive: PrimitiveState {
2711                cull_mode: Some(Face::Back),
2712                unclipped_depth: false,
2713                topology: key.primitive_topology(),
2714                ..default()
2715            },
2716            depth_stencil: Some(DepthStencilState {
2717                format: CORE_3D_DEPTH_FORMAT,
2718                depth_write_enabled,
2719                depth_compare: CompareFunction::GreaterEqual,
2720                stencil: StencilState {
2721                    front: StencilFaceState::IGNORE,
2722                    back: StencilFaceState::IGNORE,
2723                    read_mask: 0,
2724                    write_mask: 0,
2725                },
2726                bias: DepthBiasState {
2727                    constant: 0,
2728                    slope_scale: 0.0,
2729                    clamp: 0.0,
2730                },
2731            }),
2732            multisample: MultisampleState {
2733                count: key.msaa_samples(),
2734                mask: !0,
2735                alpha_to_coverage_enabled,
2736            },
2737            label: Some(label),
2738            ..default()
2739        })
2740    }
2741}
2742
2743/// The bind groups for meshes currently loaded.
2744///
2745/// If GPU mesh preprocessing isn't in use, these are global to the scene. If
2746/// GPU mesh preprocessing is in use, these are specific to a single phase.
2747#[derive(Default)]
2748pub struct MeshPhaseBindGroups {
2749    model_only: Option<BindGroup>,
2750    skinned: Option<MeshBindGroupPair>,
2751    morph_targets: HashMap<AssetId<Mesh>, MeshBindGroupPair>,
2752    lightmaps: HashMap<LightmapSlabIndex, BindGroup>,
2753}
2754
2755pub struct MeshBindGroupPair {
2756    motion_vectors: BindGroup,
2757    no_motion_vectors: BindGroup,
2758}
2759
2760/// All bind groups for meshes currently loaded.
2761#[derive(Resource)]
2762pub enum MeshBindGroups {
2763    /// The bind groups for the meshes for the entire scene, if GPU mesh
2764    /// preprocessing isn't in use.
2765    CpuPreprocessing(MeshPhaseBindGroups),
2766    /// A mapping from the type ID of a phase (e.g. [`Opaque3d`]) to the mesh
2767    /// bind groups for that phase.
2768    GpuPreprocessing(TypeIdMap<MeshPhaseBindGroups>),
2769}
2770
2771impl MeshPhaseBindGroups {
2772    pub fn reset(&mut self) {
2773        self.model_only = None;
2774        self.skinned = None;
2775        self.morph_targets.clear();
2776        self.lightmaps.clear();
2777    }
2778    /// Get the `BindGroup` for `RenderMesh` with given `handle_id` and lightmap
2779    /// key `lightmap`.
2780    pub fn get(
2781        &self,
2782        asset_id: AssetId<Mesh>,
2783        lightmap: Option<LightmapSlabIndex>,
2784        is_skinned: bool,
2785        morph: bool,
2786        motion_vectors: bool,
2787    ) -> Option<&BindGroup> {
2788        match (is_skinned, morph, lightmap) {
2789            (_, true, _) => self
2790                .morph_targets
2791                .get(&asset_id)
2792                .map(|bind_group_pair| bind_group_pair.get(motion_vectors)),
2793            (true, false, _) => self
2794                .skinned
2795                .as_ref()
2796                .map(|bind_group_pair| bind_group_pair.get(motion_vectors)),
2797            (false, false, Some(lightmap_slab)) => self.lightmaps.get(&lightmap_slab),
2798            (false, false, None) => self.model_only.as_ref(),
2799        }
2800    }
2801}
2802
2803impl MeshBindGroupPair {
2804    fn get(&self, motion_vectors: bool) -> &BindGroup {
2805        if motion_vectors {
2806            &self.motion_vectors
2807        } else {
2808            &self.no_motion_vectors
2809        }
2810    }
2811}
2812
2813/// Creates the per-mesh bind groups for each type of mesh and each phase.
2814pub fn prepare_mesh_bind_groups(
2815    mut commands: Commands,
2816    meshes: Res<RenderAssets<RenderMesh>>,
2817    mesh_pipeline: Res<MeshPipeline>,
2818    render_device: Res<RenderDevice>,
2819    pipeline_cache: Res<PipelineCache>,
2820    cpu_batched_instance_buffer: Option<
2821        Res<no_gpu_preprocessing::BatchedInstanceBuffer<MeshUniform>>,
2822    >,
2823    gpu_batched_instance_buffers: Option<
2824        Res<gpu_preprocessing::BatchedInstanceBuffers<MeshUniform, MeshInputUniform>>,
2825    >,
2826    skins_uniform: Res<SkinUniforms>,
2827    weights_uniform: Res<MorphUniforms>,
2828    mut render_lightmaps: ResMut<RenderLightmaps>,
2829) {
2830    // CPU mesh preprocessing path.
2831    if let Some(cpu_batched_instance_buffer) = cpu_batched_instance_buffer
2832        && let Some(instance_data_binding) = cpu_batched_instance_buffer
2833            .into_inner()
2834            .instance_data_binding()
2835    {
2836        // In this path, we only have a single set of bind groups for all phases.
2837        let cpu_preprocessing_mesh_bind_groups = prepare_mesh_bind_groups_for_phase(
2838            instance_data_binding,
2839            &meshes,
2840            &mesh_pipeline,
2841            &render_device,
2842            &pipeline_cache,
2843            &skins_uniform,
2844            &weights_uniform,
2845            &mut render_lightmaps,
2846        );
2847
2848        commands.insert_resource(MeshBindGroups::CpuPreprocessing(
2849            cpu_preprocessing_mesh_bind_groups,
2850        ));
2851        return;
2852    }
2853
2854    // GPU mesh preprocessing path.
2855    if let Some(gpu_batched_instance_buffers) = gpu_batched_instance_buffers {
2856        let mut gpu_preprocessing_mesh_bind_groups = TypeIdMap::default();
2857
2858        // Loop over each phase.
2859        for (phase_type_id, batched_phase_instance_buffers) in
2860            &gpu_batched_instance_buffers.phase_instance_buffers
2861        {
2862            let Some(instance_data_binding) =
2863                batched_phase_instance_buffers.instance_data_binding()
2864            else {
2865                continue;
2866            };
2867
2868            let mesh_phase_bind_groups = prepare_mesh_bind_groups_for_phase(
2869                instance_data_binding,
2870                &meshes,
2871                &mesh_pipeline,
2872                &render_device,
2873                &pipeline_cache,
2874                &skins_uniform,
2875                &weights_uniform,
2876                &mut render_lightmaps,
2877            );
2878
2879            gpu_preprocessing_mesh_bind_groups.insert(*phase_type_id, mesh_phase_bind_groups);
2880        }
2881
2882        commands.insert_resource(MeshBindGroups::GpuPreprocessing(
2883            gpu_preprocessing_mesh_bind_groups,
2884        ));
2885    }
2886}
2887
2888/// Creates the per-mesh bind groups for each type of mesh, for a single phase.
2889fn prepare_mesh_bind_groups_for_phase(
2890    model: BindingResource,
2891    meshes: &RenderAssets<RenderMesh>,
2892    mesh_pipeline: &MeshPipeline,
2893    render_device: &RenderDevice,
2894    pipeline_cache: &PipelineCache,
2895    skins_uniform: &SkinUniforms,
2896    weights_uniform: &MorphUniforms,
2897    render_lightmaps: &mut RenderLightmaps,
2898) -> MeshPhaseBindGroups {
2899    let layouts = &mesh_pipeline.mesh_layouts;
2900
2901    // TODO: Reuse allocations.
2902    let mut groups = MeshPhaseBindGroups {
2903        model_only: Some(layouts.model_only(render_device, pipeline_cache, &model)),
2904        ..default()
2905    };
2906
2907    // Create the skinned mesh bind group with the current and previous buffers
2908    // (the latter being for motion vector computation).
2909    let (skin, prev_skin) = (&skins_uniform.current_buffer, &skins_uniform.prev_buffer);
2910    groups.skinned = Some(MeshBindGroupPair {
2911        motion_vectors: layouts.skinned_motion(
2912            render_device,
2913            pipeline_cache,
2914            &model,
2915            skin,
2916            prev_skin,
2917        ),
2918        no_motion_vectors: layouts.skinned(render_device, pipeline_cache, &model, skin),
2919    });
2920
2921    // Create the morphed bind groups just like we did for the skinned bind
2922    // group.
2923    if let Some(weights) = weights_uniform.current_buffer.buffer() {
2924        let prev_weights = weights_uniform.prev_buffer.buffer().unwrap_or(weights);
2925        for (id, gpu_mesh) in meshes.iter() {
2926            if let Some(targets) = gpu_mesh.morph_targets.as_ref() {
2927                let bind_group_pair = if is_skinned(&gpu_mesh.layout) {
2928                    let prev_skin = &skins_uniform.prev_buffer;
2929                    MeshBindGroupPair {
2930                        motion_vectors: layouts.morphed_skinned_motion(
2931                            render_device,
2932                            pipeline_cache,
2933                            &model,
2934                            skin,
2935                            weights,
2936                            targets,
2937                            prev_skin,
2938                            prev_weights,
2939                        ),
2940                        no_motion_vectors: layouts.morphed_skinned(
2941                            render_device,
2942                            pipeline_cache,
2943                            &model,
2944                            skin,
2945                            weights,
2946                            targets,
2947                        ),
2948                    }
2949                } else {
2950                    MeshBindGroupPair {
2951                        motion_vectors: layouts.morphed_motion(
2952                            render_device,
2953                            pipeline_cache,
2954                            &model,
2955                            weights,
2956                            targets,
2957                            prev_weights,
2958                        ),
2959                        no_motion_vectors: layouts.morphed(
2960                            render_device,
2961                            pipeline_cache,
2962                            &model,
2963                            weights,
2964                            targets,
2965                        ),
2966                    }
2967                };
2968                groups.morph_targets.insert(id, bind_group_pair);
2969            }
2970        }
2971    }
2972
2973    // Create lightmap bindgroups. There will be one bindgroup for each slab.
2974    let bindless_supported = render_lightmaps.bindless_supported;
2975    for (lightmap_slab_id, lightmap_slab) in render_lightmaps.slabs.iter_mut().enumerate() {
2976        groups.lightmaps.insert(
2977            LightmapSlabIndex(NonMaxU32::new(lightmap_slab_id as u32).unwrap()),
2978            layouts.lightmapped(
2979                render_device,
2980                pipeline_cache,
2981                &model,
2982                lightmap_slab,
2983                bindless_supported,
2984            ),
2985        );
2986    }
2987
2988    groups
2989}
2990
2991pub struct SetMeshViewBindGroup<const I: usize>;
2992impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetMeshViewBindGroup<I> {
2993    type Param = ();
2994    type ViewQuery = (
2995        Read<ViewUniformOffset>,
2996        Read<ViewLightsUniformOffset>,
2997        Read<ViewFogUniformOffset>,
2998        Read<ViewLightProbesUniformOffset>,
2999        Read<ViewScreenSpaceReflectionsUniformOffset>,
3000        Read<ViewEnvironmentMapUniformOffset>,
3001        Read<MeshViewBindGroup>,
3002        Option<Read<OrderIndependentTransparencySettingsOffset>>,
3003    );
3004    type ItemQuery = ();
3005
3006    #[inline]
3007    fn render<'w>(
3008        _item: &P,
3009        (
3010            view_uniform,
3011            view_lights,
3012            view_fog,
3013            view_light_probes,
3014            view_ssr,
3015            view_environment_map,
3016            mesh_view_bind_group,
3017            maybe_oit_layers_count_offset,
3018        ): ROQueryItem<'w, '_, Self::ViewQuery>,
3019        _entity: Option<()>,
3020        _: SystemParamItem<'w, '_, Self::Param>,
3021        pass: &mut TrackedRenderPass<'w>,
3022    ) -> RenderCommandResult {
3023        let mut offsets: SmallVec<[u32; 8]> = smallvec![
3024            view_uniform.offset,
3025            view_lights.offset,
3026            view_fog.offset,
3027            **view_light_probes,
3028            **view_ssr,
3029            **view_environment_map,
3030        ];
3031        if let Some(layers_count_offset) = maybe_oit_layers_count_offset {
3032            offsets.push(layers_count_offset.offset);
3033        }
3034        pass.set_bind_group(I, &mesh_view_bind_group.main, &offsets);
3035
3036        RenderCommandResult::Success
3037    }
3038}
3039
3040pub struct SetMeshViewBindingArrayBindGroup<const I: usize>;
3041impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetMeshViewBindingArrayBindGroup<I> {
3042    type Param = ();
3043    type ViewQuery = (Read<MeshViewBindGroup>,);
3044    type ItemQuery = ();
3045
3046    #[inline]
3047    fn render<'w>(
3048        _item: &P,
3049        (mesh_view_bind_group,): ROQueryItem<'w, '_, Self::ViewQuery>,
3050        _entity: Option<()>,
3051        _: SystemParamItem<'w, '_, Self::Param>,
3052        pass: &mut TrackedRenderPass<'w>,
3053    ) -> RenderCommandResult {
3054        pass.set_bind_group(I, &mesh_view_bind_group.binding_array, &[]);
3055
3056        RenderCommandResult::Success
3057    }
3058}
3059
3060pub struct SetMeshViewEmptyBindGroup<const I: usize>;
3061impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetMeshViewEmptyBindGroup<I> {
3062    type Param = ();
3063    type ViewQuery = (Read<MeshViewBindGroup>,);
3064    type ItemQuery = ();
3065
3066    #[inline]
3067    fn render<'w>(
3068        _item: &P,
3069        (mesh_view_bind_group,): ROQueryItem<'w, '_, Self::ViewQuery>,
3070        _entity: Option<()>,
3071        _: SystemParamItem<'w, '_, Self::Param>,
3072        pass: &mut TrackedRenderPass<'w>,
3073    ) -> RenderCommandResult {
3074        pass.set_bind_group(I, &mesh_view_bind_group.empty, &[]);
3075
3076        RenderCommandResult::Success
3077    }
3078}
3079
3080pub struct SetMeshBindGroup<const I: usize>;
3081impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetMeshBindGroup<I> {
3082    type Param = (
3083        SRes<RenderDevice>,
3084        SRes<MeshBindGroups>,
3085        SRes<RenderMeshInstances>,
3086        SRes<SkinUniforms>,
3087        SRes<MorphIndices>,
3088        SRes<RenderLightmaps>,
3089    );
3090    type ViewQuery = Has<MotionVectorPrepass>;
3091    type ItemQuery = ();
3092
3093    #[inline]
3094    fn render<'w>(
3095        item: &P,
3096        has_motion_vector_prepass: bool,
3097        _item_query: Option<()>,
3098        (
3099            render_device,
3100            bind_groups,
3101            mesh_instances,
3102            skin_uniforms,
3103            morph_indices,
3104            lightmaps,
3105        ): SystemParamItem<'w, '_, Self::Param>,
3106        pass: &mut TrackedRenderPass<'w>,
3107    ) -> RenderCommandResult {
3108        let bind_groups = bind_groups.into_inner();
3109        let mesh_instances = mesh_instances.into_inner();
3110        let skin_uniforms = skin_uniforms.into_inner();
3111        let morph_indices = morph_indices.into_inner();
3112
3113        let entity = &item.main_entity();
3114
3115        let Some(mesh_asset_id) = mesh_instances.mesh_asset_id(*entity) else {
3116            return RenderCommandResult::Success;
3117        };
3118
3119        let current_skin_byte_offset = skin_uniforms.skin_byte_offset(*entity);
3120        let current_morph_index = morph_indices.current.get(entity);
3121        let prev_morph_index = morph_indices.prev.get(entity);
3122
3123        let is_skinned = current_skin_byte_offset.is_some();
3124        let is_morphed = current_morph_index.is_some();
3125
3126        let lightmap_slab_index = lightmaps
3127            .render_lightmaps
3128            .get(entity)
3129            .map(|render_lightmap| render_lightmap.slab_index);
3130
3131        let Some(mesh_phase_bind_groups) = (match *bind_groups {
3132            MeshBindGroups::CpuPreprocessing(ref mesh_phase_bind_groups) => {
3133                Some(mesh_phase_bind_groups)
3134            }
3135            MeshBindGroups::GpuPreprocessing(ref mesh_phase_bind_groups) => {
3136                mesh_phase_bind_groups.get(&TypeId::of::<P>())
3137            }
3138        }) else {
3139            // This is harmless if e.g. we're rendering the `Shadow` phase and
3140            // there weren't any shadows.
3141            return RenderCommandResult::Success;
3142        };
3143
3144        let Some(bind_group) = mesh_phase_bind_groups.get(
3145            mesh_asset_id,
3146            lightmap_slab_index,
3147            is_skinned,
3148            is_morphed,
3149            has_motion_vector_prepass,
3150        ) else {
3151            return RenderCommandResult::Failure(
3152                "The MeshBindGroups resource wasn't set in the render phase. \
3153                It should be set by the prepare_mesh_bind_group system.\n\
3154                This is a bevy bug! Please open an issue.",
3155            );
3156        };
3157
3158        let mut dynamic_offsets: [u32; 5] = Default::default();
3159        let mut offset_count = 0;
3160        if let PhaseItemExtraIndex::DynamicOffset(dynamic_offset) = item.extra_index() {
3161            dynamic_offsets[offset_count] = dynamic_offset;
3162            offset_count += 1;
3163        }
3164        if let Some(current_skin_index) = current_skin_byte_offset
3165            && skins_use_uniform_buffers(&render_device.limits())
3166        {
3167            dynamic_offsets[offset_count] = current_skin_index.byte_offset;
3168            offset_count += 1;
3169        }
3170        if let Some(current_morph_index) = current_morph_index {
3171            dynamic_offsets[offset_count] = current_morph_index.index;
3172            offset_count += 1;
3173        }
3174
3175        // Attach motion vectors if needed.
3176        if has_motion_vector_prepass {
3177            // Attach the previous skin index for motion vector computation.
3178            if skins_use_uniform_buffers(&render_device.limits())
3179                && let Some(current_skin_byte_offset) = current_skin_byte_offset
3180            {
3181                dynamic_offsets[offset_count] = current_skin_byte_offset.byte_offset;
3182                offset_count += 1;
3183            }
3184
3185            // Attach the previous morph index for motion vector computation. If
3186            // there isn't one, just use zero as the shader will ignore it.
3187            if current_morph_index.is_some() {
3188                match prev_morph_index {
3189                    Some(prev_morph_index) => {
3190                        dynamic_offsets[offset_count] = prev_morph_index.index;
3191                    }
3192                    None => dynamic_offsets[offset_count] = 0,
3193                }
3194                offset_count += 1;
3195            }
3196        }
3197
3198        pass.set_bind_group(I, bind_group, &dynamic_offsets[0..offset_count]);
3199
3200        RenderCommandResult::Success
3201    }
3202}
3203
3204pub struct DrawMesh;
3205impl<P: PhaseItem> RenderCommand<P> for DrawMesh {
3206    type Param = (
3207        SRes<RenderAssets<RenderMesh>>,
3208        SRes<RenderMeshInstances>,
3209        SRes<IndirectParametersBuffers>,
3210        SRes<PipelineCache>,
3211        SRes<MeshAllocator>,
3212        Option<SRes<PreprocessPipelines>>,
3213        SRes<GpuPreprocessingSupport>,
3214    );
3215    type ViewQuery = Has<PreprocessBindGroups>;
3216    type ItemQuery = ();
3217    #[inline]
3218    fn render<'w>(
3219        item: &P,
3220        has_preprocess_bind_group: ROQueryItem<Self::ViewQuery>,
3221        _item_query: Option<()>,
3222        (
3223            meshes,
3224            mesh_instances,
3225            indirect_parameters_buffer,
3226            pipeline_cache,
3227            mesh_allocator,
3228            preprocess_pipelines,
3229            preprocessing_support,
3230        ): SystemParamItem<'w, '_, Self::Param>,
3231        pass: &mut TrackedRenderPass<'w>,
3232    ) -> RenderCommandResult {
3233        // If we're using GPU preprocessing, then we're dependent on that
3234        // compute shader having been run, which of course can only happen if
3235        // it's compiled. Otherwise, our mesh instance data won't be present.
3236        if let Some(preprocess_pipelines) = preprocess_pipelines
3237            && (!has_preprocess_bind_group
3238                || !preprocess_pipelines
3239                    .pipelines_are_loaded(&pipeline_cache, &preprocessing_support))
3240        {
3241            return RenderCommandResult::Skip;
3242        }
3243
3244        let meshes = meshes.into_inner();
3245        let mesh_instances = mesh_instances.into_inner();
3246        let indirect_parameters_buffer = indirect_parameters_buffer.into_inner();
3247        let mesh_allocator = mesh_allocator.into_inner();
3248
3249        let Some(mesh_asset_id) = mesh_instances.mesh_asset_id(item.main_entity()) else {
3250            return RenderCommandResult::Skip;
3251        };
3252        let Some(gpu_mesh) = meshes.get(mesh_asset_id) else {
3253            return RenderCommandResult::Skip;
3254        };
3255        let Some(vertex_buffer_slice) = mesh_allocator.mesh_vertex_slice(&mesh_asset_id) else {
3256            return RenderCommandResult::Skip;
3257        };
3258
3259        pass.set_vertex_buffer(0, vertex_buffer_slice.buffer.slice(..));
3260
3261        let batch_range = item.batch_range();
3262
3263        // Draw either directly or indirectly, as appropriate. If we're in
3264        // indirect mode, we can additionally multi-draw. (We can't multi-draw
3265        // in direct mode because `wgpu` doesn't expose that functionality.)
3266        match &gpu_mesh.buffer_info {
3267            RenderMeshBufferInfo::Indexed {
3268                index_format,
3269                count,
3270            } => {
3271                let Some(index_buffer_slice) = mesh_allocator.mesh_index_slice(&mesh_asset_id)
3272                else {
3273                    return RenderCommandResult::Skip;
3274                };
3275
3276                pass.set_index_buffer(index_buffer_slice.buffer.slice(..), *index_format);
3277
3278                match item.extra_index() {
3279                    PhaseItemExtraIndex::None | PhaseItemExtraIndex::DynamicOffset(_) => {
3280                        pass.draw_indexed(
3281                            index_buffer_slice.range.start
3282                                ..(index_buffer_slice.range.start + *count),
3283                            vertex_buffer_slice.range.start as i32,
3284                            batch_range.clone(),
3285                        );
3286                    }
3287                    PhaseItemExtraIndex::IndirectParametersIndex {
3288                        range: indirect_parameters_range,
3289                        batch_set_index,
3290                    } => {
3291                        // Look up the indirect parameters buffer, as well as
3292                        // the buffer we're going to use for
3293                        // `multi_draw_indexed_indirect_count` (if available).
3294                        let Some(phase_indirect_parameters_buffers) =
3295                            indirect_parameters_buffer.get(&TypeId::of::<P>())
3296                        else {
3297                            warn!(
3298                                "Not rendering mesh because indexed indirect parameters buffer \
3299                                 wasn't present for this phase",
3300                            );
3301                            return RenderCommandResult::Skip;
3302                        };
3303                        let (Some(indirect_parameters_buffer), Some(batch_sets_buffer)) = (
3304                            phase_indirect_parameters_buffers.indexed.data_buffer(),
3305                            phase_indirect_parameters_buffers
3306                                .indexed
3307                                .batch_sets_buffer(),
3308                        ) else {
3309                            warn!(
3310                                "Not rendering mesh because indexed indirect parameters buffer \
3311                                 wasn't present",
3312                            );
3313                            return RenderCommandResult::Skip;
3314                        };
3315
3316                        // Calculate the location of the indirect parameters
3317                        // within the buffer.
3318                        let indirect_parameters_offset = indirect_parameters_range.start as u64
3319                            * size_of::<IndirectParametersIndexed>() as u64;
3320                        let indirect_parameters_count =
3321                            indirect_parameters_range.end - indirect_parameters_range.start;
3322
3323                        // If we're using `multi_draw_indirect_count`, take the
3324                        // number of batches from the appropriate position in
3325                        // the batch sets buffer. Otherwise, supply the size of
3326                        // the batch set.
3327                        match batch_set_index {
3328                            Some(batch_set_index) => {
3329                                let count_offset = u32::from(batch_set_index)
3330                                    * (size_of::<IndirectBatchSet>() as u32);
3331                                pass.multi_draw_indexed_indirect_count(
3332                                    indirect_parameters_buffer,
3333                                    indirect_parameters_offset,
3334                                    batch_sets_buffer,
3335                                    count_offset as u64,
3336                                    indirect_parameters_count,
3337                                );
3338                            }
3339                            None => {
3340                                pass.multi_draw_indexed_indirect(
3341                                    indirect_parameters_buffer,
3342                                    indirect_parameters_offset,
3343                                    indirect_parameters_count,
3344                                );
3345                            }
3346                        }
3347                    }
3348                }
3349            }
3350
3351            RenderMeshBufferInfo::NonIndexed => match item.extra_index() {
3352                PhaseItemExtraIndex::None | PhaseItemExtraIndex::DynamicOffset(_) => {
3353                    pass.draw(vertex_buffer_slice.range, batch_range.clone());
3354                }
3355                PhaseItemExtraIndex::IndirectParametersIndex {
3356                    range: indirect_parameters_range,
3357                    batch_set_index,
3358                } => {
3359                    // Look up the indirect parameters buffer, as well as the
3360                    // buffer we're going to use for
3361                    // `multi_draw_indirect_count` (if available).
3362                    let Some(phase_indirect_parameters_buffers) =
3363                        indirect_parameters_buffer.get(&TypeId::of::<P>())
3364                    else {
3365                        warn!(
3366                            "Not rendering mesh because non-indexed indirect parameters buffer \
3367                                 wasn't present for this phase",
3368                        );
3369                        return RenderCommandResult::Skip;
3370                    };
3371                    let (Some(indirect_parameters_buffer), Some(batch_sets_buffer)) = (
3372                        phase_indirect_parameters_buffers.non_indexed.data_buffer(),
3373                        phase_indirect_parameters_buffers
3374                            .non_indexed
3375                            .batch_sets_buffer(),
3376                    ) else {
3377                        warn!(
3378                            "Not rendering mesh because non-indexed indirect parameters buffer \
3379                             wasn't present"
3380                        );
3381                        return RenderCommandResult::Skip;
3382                    };
3383
3384                    // Calculate the location of the indirect parameters within
3385                    // the buffer.
3386                    let indirect_parameters_offset = indirect_parameters_range.start as u64
3387                        * size_of::<IndirectParametersNonIndexed>() as u64;
3388                    let indirect_parameters_count =
3389                        indirect_parameters_range.end - indirect_parameters_range.start;
3390
3391                    // If we're using `multi_draw_indirect_count`, take the
3392                    // number of batches from the appropriate position in the
3393                    // batch sets buffer. Otherwise, supply the size of the
3394                    // batch set.
3395                    match batch_set_index {
3396                        Some(batch_set_index) => {
3397                            let count_offset =
3398                                u32::from(batch_set_index) * (size_of::<IndirectBatchSet>() as u32);
3399                            pass.multi_draw_indirect_count(
3400                                indirect_parameters_buffer,
3401                                indirect_parameters_offset,
3402                                batch_sets_buffer,
3403                                count_offset as u64,
3404                                indirect_parameters_count,
3405                            );
3406                        }
3407                        None => {
3408                            pass.multi_draw_indirect(
3409                                indirect_parameters_buffer,
3410                                indirect_parameters_offset,
3411                                indirect_parameters_count,
3412                            );
3413                        }
3414                    }
3415                }
3416            },
3417        }
3418        RenderCommandResult::Success
3419    }
3420}
3421
3422#[cfg(test)]
3423mod tests {
3424    use super::MeshPipelineKey;
3425    #[test]
3426    fn mesh_key_msaa_samples() {
3427        for i in [1, 2, 4, 8, 16, 32, 64, 128] {
3428            assert_eq!(MeshPipelineKey::from_msaa_samples(i).msaa_samples(), i);
3429        }
3430    }
3431}