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
97pub struct MeshRenderPlugin {
99 pub use_gpu_instance_buffer_builder: bool,
104 pub debug_flags: RenderDebugFlags,
106}
107
108impl MeshRenderPlugin {
109 pub fn new(debug_flags: RenderDebugFlags) -> MeshRenderPlugin {
111 MeshRenderPlugin {
112 use_gpu_instance_buffer_builder: false,
113 debug_flags,
114 }
115 }
116}
117
118#[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 .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_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 pub world_from_local: [Vec4; 3],
459 pub previous_world_from_local: [Vec4; 3],
460 pub local_from_world_transpose_a: [Vec4; 2],
465 pub local_from_world_transpose_b: f32,
466 pub flags: u32,
467 pub lightmap_uv_rect: UVec2,
477 pub first_vertex_index: u32,
483 pub current_skin_index: u32,
485 pub material_and_lightmap_bind_group_slot: u32,
490 pub tag: u32,
492 pub pad: u32,
494}
495
496#[derive(ShaderType, Pod, Zeroable, Clone, Copy, Default, Debug)]
501#[repr(C)]
502pub struct MeshInputUniform {
503 pub world_from_local: [Vec4; 3],
505 pub lightmap_uv_rect: UVec2,
517 pub flags: u32,
519 pub previous_input_index: u32,
524 pub first_vertex_index: u32,
530 pub first_index_index: u32,
538 pub index_count: u32,
541 pub current_skin_index: u32,
543 pub material_and_lightmap_bind_group_slot: u32,
548 pub timestamp: u32,
556 pub tag: u32,
558 pub pad: u32,
560}
561
562#[derive(ShaderType, Pod, Zeroable, Clone, Copy, Default)]
566#[repr(C)]
567pub struct MeshCullingData {
568 pub aabb_center: Vec4,
571 pub aabb_half_extents: Vec4,
574}
575
576#[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
618bitflags::bitflags! {
620 #[repr(transparent)]
625 pub struct MeshFlags: u32 {
626 const LOD_INDEX_MASK = (1 << 16) - 1;
630 const NO_FRUSTUM_CULLING = 1 << 28;
635 const SHADOW_RECEIVER = 1 << 29;
636 const TRANSMITTED_SHADOW_RECEIVER = 1 << 30;
637 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 pub const LOD_INDEX_SHIFT: u32 = 0;
680}
681
682bitflags::bitflags! {
683 #[derive(Clone, Copy)]
685 pub struct RenderMeshInstanceFlags: u8 {
686 const SHADOW_CASTER = 1 << 0;
688 const AUTOMATIC_BATCHING = 1 << 1;
690 const HAS_PREVIOUS_TRANSFORM = 1 << 2;
693 const HAS_PREVIOUS_SKIN = 1 << 3;
696 const HAS_PREVIOUS_MORPH = 1 << 4;
699 }
700}
701
702#[derive(Deref, DerefMut)]
705pub struct RenderMeshInstanceCpu {
706 #[deref]
709 pub shared: RenderMeshInstanceShared,
710 pub transforms: MeshTransforms,
714}
715
716#[derive(Deref, DerefMut)]
719pub struct RenderMeshInstanceGpu {
720 #[deref]
723 pub shared: RenderMeshInstanceShared,
724 pub center: Vec3,
729 pub current_uniform_index: NonMaxU32,
731}
732
733pub struct RenderMeshInstanceShared {
736 pub mesh_asset_id: AssetId<Mesh>,
738 pub material_bindings_index: MaterialBindingId,
740 pub flags: RenderMeshInstanceFlags,
742 pub lightmap_slab_index: Option<LightmapSlabIndex>,
745 pub tag: u32,
747 pub render_layers: Option<RenderLayers>,
749 pub center: Vec3,
755}
756
757pub struct RenderMeshInstanceGpuBuilder {
763 pub shared: RenderMeshInstanceShared,
765 pub world_from_local: Affine3,
767 pub lightmap_uv_rect: UVec2,
779 pub previous_input_index: Option<NonMaxU32>,
781 pub mesh_flags: MeshFlags,
783}
784
785#[derive(Default)]
791pub enum RenderMeshInstanceGpuQueue {
792 #[default]
797 None,
798 CpuCulling {
802 changed: Vec<(MainEntity, RenderMeshInstanceGpuBuilder)>,
806 removed: Vec<MainEntity>,
808 },
809 GpuCulling {
813 changed: Vec<(MainEntity, RenderMeshInstanceGpuBuilder, MeshCullingData)>,
817 removed: Vec<MainEntity>,
819 },
820}
821
822#[derive(Resource, Default, Deref, DerefMut)]
828pub struct RenderMeshInstanceGpuQueues(Parallel<RenderMeshInstanceGpuQueue>);
829
830#[derive(Resource, Default, Deref, DerefMut)]
835pub struct MeshesToReextractNextFrame(MainEntityHashSet);
836
837impl RenderMeshInstanceShared {
838 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 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 #[inline]
897 pub fn should_batch(&self) -> bool {
898 self.flags
899 .contains(RenderMeshInstanceFlags::AUTOMATIC_BATCHING)
900 }
901}
902
903#[derive(Resource)]
909pub enum RenderMeshInstances {
910 CpuBuilding(RenderMeshInstancesCpu),
912 GpuBuilding(RenderMeshInstancesGpu),
914}
915
916#[derive(Default, Deref, DerefMut)]
919pub struct RenderMeshInstancesCpu(MainEntityHashMap<RenderMeshInstanceCpu>);
920
921#[derive(Default, Deref, DerefMut)]
924pub struct RenderMeshInstancesGpu(MainEntityHashMap<RenderMeshInstanceGpu>);
925
926impl RenderMeshInstances {
927 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 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 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 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 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 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 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 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 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 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 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 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 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 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 current_uniform_index = u32::from(occupied_entry.get_mut().current_uniform_index);
1227
1228 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 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 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
1265fn 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 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 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 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#[derive(Deref)]
1322pub struct RenderMeshQueueData<'a> {
1323 #[deref]
1325 pub shared: &'a RenderMeshInstanceShared,
1326 pub center: Vec3,
1331 pub current_uniform_index: InputUniformIndex,
1334}
1335
1336#[derive(SystemSet, Clone, PartialEq, Eq, Debug, Hash)]
1339pub struct MeshExtractionSystems;
1340
1341pub 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 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
1460type 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
1479pub 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 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 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 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 for entity in removed_meshes_query.read() {
1614 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
1707pub 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
1736pub 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 meshes_to_reextract_next_frame.clear();
1761
1762 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 for queue in render_mesh_instance_queues.iter_mut() {
1774 match *queue {
1775 RenderMeshInstanceGpuQueue::None => {
1776 }
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 previous_input_buffer.ensure_nonempty();
1845}
1846
1847#[derive(Resource, Clone)]
1849pub struct MeshPipeline {
1850 pub view_layouts: MeshPipelineViewLayouts,
1852 pub clustered_forward_buffer_binding_type: BufferBindingType,
1853 pub mesh_layouts: MeshLayouts,
1854 pub shader: Handle<Shader>,
1856 pub per_object_buffer_batch_size: Option<u32>,
1868
1869 pub binding_arrays_are_usable: bool,
1874
1875 pub clustered_decals_are_usable: bool,
1877
1878 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
1923pub 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 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 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 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 pub struct MeshPipelineKey: u64 {
2152 const NONE = 0;
2154
2155 const MORPH_TARGETS = BaseMeshPipelineKey::MORPH_TARGETS.bits();
2157
2158 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; const ENVIRONMENT_MAP = 1 << 8;
2169 const SCREEN_SPACE_AMBIENT_OCCLUSION = 1 << 9;
2170 const UNCLIPPED_DEPTH_ORTHO = 1 << 10; 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 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; const BLEND_OPAQUE = 0 << Self::BLEND_SHIFT_BITS; const BLEND_PREMULTIPLIED_ALPHA = 1 << Self::BLEND_SHIFT_BITS; const BLEND_MULTIPLY = 2 << Self::BLEND_SHIFT_BITS; const BLEND_ALPHA = 3 << Self::BLEND_SHIFT_BITS; const BLEND_ALPHA_TO_COVERAGE = 4 << Self::BLEND_SHIFT_BITS; 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
2293const_assert_eq!(
2295 (((MeshPipelineKey::LAST_FLAG.bits() << 1) - 1) | MeshPipelineKey::ALL_RESERVED_BITS.bits())
2296 & BaseMeshPipelineKey::all().bits(),
2297 0
2298);
2299
2300const_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 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 blend = None;
2466 shader_defs.push("OIT_ENABLED".into());
2467 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 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 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 depth_write_enabled = false;
2499 } else if pass == MeshPipelineKey::BLEND_ALPHA_TO_COVERAGE {
2500 label = "alpha_to_coverage_mesh_pipeline".into();
2501 blend = None;
2503 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 blend = None;
2514 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 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!(), },
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 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#[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#[derive(Resource)]
2762pub enum MeshBindGroups {
2763 CpuPreprocessing(MeshPhaseBindGroups),
2766 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 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
2813pub 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 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 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 if let Some(gpu_batched_instance_buffers) = gpu_batched_instance_buffers {
2856 let mut gpu_preprocessing_mesh_bind_groups = TypeIdMap::default();
2857
2858 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
2888fn 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 let mut groups = MeshPhaseBindGroups {
2903 model_only: Some(layouts.model_only(render_device, pipeline_cache, &model)),
2904 ..default()
2905 };
2906
2907 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 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 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 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 if has_motion_vector_prepass {
3177 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 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 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 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 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 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 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 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 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 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}