1use crate::contact_shadows::ContactShadows;
2use crate::{resources::prepare_atmosphere_buffers, skin::skin_uniforms_from_world};
3use alloc::sync::Arc;
4use bevy_asset::uuid::Uuid;
5use bevy_asset::{embedded_asset, load_embedded_asset, AssetId, AssetIndex, AssetServer};
6use bevy_camera::visibility::NoCpuCulling;
7use bevy_camera::{
8 primitives::Aabb,
9 visibility::{NoFrustumCulling, RenderLayers, ViewVisibility, VisibilityRange},
10 Camera, Projection,
11};
12use bevy_core_pipeline::{
13 core_3d::{AlphaMask3d, Opaque3d, Transparent3d, CORE_3D_DEPTH_FORMAT},
14 deferred::{AlphaMask3dDeferred, Opaque3dDeferred},
15 oit::prepare_oit_buffers,
16 prepass::MotionVectorPrepass,
17};
18use bevy_derive::{Deref, DerefMut};
19use bevy_diagnostic::FrameCount;
20use bevy_ecs::entity::EntityHash;
21use bevy_ecs::{
22 entity::EntityHashSet,
23 prelude::*,
24 query::{QueryData, ROQueryItem},
25 relationship::RelationshipSourceCollection,
26 system::{lifetimeless::*, SystemParamItem},
27};
28use bevy_image::TextureFormatPixelInfo;
29use bevy_light::{
30 EnvironmentMapLight, IrradianceVolume, NotShadowCaster, NotShadowReceiver,
31 ShadowFilteringMethod, TransmittedShadowReceiver,
32};
33use bevy_math::{Affine3, Affine3Ext, Rect, UVec2, Vec3, Vec4};
34use bevy_mesh::{
35 skinning::SkinnedMesh, BaseMeshPipelineKey, Mesh, Mesh3d, MeshAttributeCompressionFlags,
36 MeshTag, MeshVertexBufferLayoutRef, VertexAttributeDescriptor,
37};
38use bevy_platform::collections::HashSet;
39use bevy_platform::collections::{hash_map::Entry, HashMap};
40use bevy_render::batching::gpu_preprocessing::{
41 BufferDataInput, PreviousInstanceInputUniformBuffer,
42};
43use bevy_render::impl_atomic_pod;
44use bevy_render::material_bind_groups::{
45 MaterialBindGroupIndex, MaterialBindGroupSlot, MaterialBindingId, RenderMaterialBindings,
46};
47use bevy_render::mesh::allocator::{MeshSlabId, MeshSlabs};
48use bevy_render::mesh::morph::{
49 MorphTargetImage, MorphTargetsResource, RenderMorphTargetAllocator,
50};
51use bevy_render::mesh::MeshMetadataFallbackBuffer;
52use bevy_render::{
53 batching::{
54 gpu_preprocessing::{
55 self, GpuPreprocessingSupport, IndirectBatchSet, IndirectParametersBuffers,
56 IndirectParametersIndexed, IndirectParametersMetadata, IndirectParametersNonIndexed,
57 InstanceInputUniformBuffer, UntypedPhaseIndirectParametersBuffers,
58 },
59 no_gpu_preprocessing, GetBatchData, GetFullBatchData, NoAutomaticBatching,
60 },
61 mesh::{allocator::MeshAllocator, RenderMesh, RenderMeshBufferInfo},
62 render_asset::RenderAssets,
63 render_phase::{
64 BinnedRenderPhasePlugin, InputUniformIndex, PhaseItem, PhaseItemExtraIndex, RenderCommand,
65 RenderCommandResult, SortedRenderPhasePlugin, TrackedRenderPass,
66 },
67 render_resource::*,
68 renderer::{RenderAdapter, RenderDevice, RenderQueue},
69 sync_world::MainEntityHashSet,
70 texture::{DefaultImageSampler, GpuImage},
71 view::{self, NoIndirectDrawing, RenderVisibilityRanges, RetainedViewEntity},
72 Extract,
73};
74use bevy_shader::{load_shader_library, Shader, ShaderDefVal, ShaderSettings};
75use bevy_transform::components::GlobalTransform;
76use bevy_utils::{default, Parallel, TypeIdHashMap};
77use core::any::TypeId;
78use core::iter;
79use core::mem::size_of;
80use core::sync::atomic::{AtomicU64, Ordering};
81use indexmap::IndexSet;
82use static_assertions::const_assert_eq;
83use std::sync::mpsc;
84#[cfg(feature = "trace")]
85use tracing::info_span;
86use tracing::{error, warn};
87
88use self::irradiance_volume::IRRADIANCE_VOLUMES_ARE_USABLE;
89use crate::{
90 render::{
91 morph::{
92 extract_morphs, no_automatic_morph_batching, write_morph_buffers, MorphIndices,
93 MorphUniforms,
94 },
95 skin::no_automatic_skin_batching,
96 },
97 *,
98};
99use bevy_core_pipeline::oit::OrderIndependentTransparencySettings;
100use bevy_core_pipeline::prepass::{DeferredPrepass, DepthPrepass, NormalPrepass};
101use bevy_core_pipeline::tonemapping::{DebandDither, Tonemapping};
102use bevy_render::camera::{DirtySpecializations, ExtractedCamera, TemporalJitter};
103use bevy_render::prelude::Msaa;
104use bevy_render::sync_world::{MainEntity, MainEntityHashMap};
105use bevy_render::view::{
106 texture_format_from_code, texture_format_to_code, ExtractedView,
107 RenderShadowMapVisibleEntities, RenderVisibleEntities,
108};
109use bevy_render::RenderSystems::PrepareAssets;
110use bevy_tasks::ComputeTaskPool;
111
112use bytemuck::{Pod, Zeroable};
113use nonmax::{NonMaxU16, NonMaxU32};
114
115pub struct MeshRenderPlugin {
117 pub use_gpu_instance_buffer_builder: bool,
122 pub debug_flags: RenderDebugFlags,
124}
125
126impl MeshRenderPlugin {
127 pub fn new(debug_flags: RenderDebugFlags) -> MeshRenderPlugin {
129 MeshRenderPlugin {
130 use_gpu_instance_buffer_builder: false,
131 debug_flags,
132 }
133 }
134}
135
136#[cfg(debug_assertions)]
145pub const MESH_PIPELINE_VIEW_LAYOUT_SAFE_MAX_TEXTURES: usize = 10;
146
147#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)]
148pub struct MeshPipelineSystems;
149
150impl Plugin for MeshRenderPlugin {
151 fn build(&self, app: &mut App) {
152 load_shader_library!(app, "forward_io.wesl");
153 load_shader_library!(app, "mesh_view_types.wesl", |settings| *settings =
154 ShaderSettings {
155 shader_defs: vec![
156 ShaderDefVal::UInt(
157 "MAX_DIRECTIONAL_LIGHTS".into(),
158 MAX_DIRECTIONAL_LIGHTS as u32
159 ),
160 ShaderDefVal::UInt(
161 "MAX_CASCADES_PER_LIGHT".into(),
162 MAX_CASCADES_PER_LIGHT as u32,
163 ),
164 ShaderDefVal::UInt("MAX_RECT_LIGHTS".into(), MAX_RECT_LIGHTS as u32,),
165 ],
166 });
167 load_shader_library!(app, "mesh_view_bindings.wesl");
168 load_shader_library!(app, "mesh_types.wesl");
169 load_shader_library!(app, "mesh_functions.wesl");
170 load_shader_library!(app, "skinning.wesl");
171 load_shader_library!(app, "morph.wesl");
172 load_shader_library!(app, "occlusion_culling.wesl");
173
174 embedded_asset!(app, "mesh.wesl");
175
176 if app.get_sub_app(RenderApp).is_none() {
177 return;
178 }
179
180 app.add_systems(
181 PostUpdate,
182 (no_automatic_skin_batching, no_automatic_morph_batching),
183 )
184 .add_plugins((
185 BinnedRenderPhasePlugin::<Opaque3d, MeshPipeline>::new(self.debug_flags),
186 BinnedRenderPhasePlugin::<AlphaMask3d, MeshPipeline>::new(self.debug_flags),
187 BinnedRenderPhasePlugin::<Shadow, MeshPipeline>::new(self.debug_flags),
188 BinnedRenderPhasePlugin::<Opaque3dDeferred, MeshPipeline>::new(self.debug_flags),
189 BinnedRenderPhasePlugin::<AlphaMask3dDeferred, MeshPipeline>::new(self.debug_flags),
190 SortedRenderPhasePlugin::<Transmissive3d, MeshPipeline>::new(self.debug_flags),
191 SortedRenderPhasePlugin::<Transparent3d, MeshPipeline>::new(self.debug_flags),
192 ));
193
194 if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
195 render_app
196 .init_gpu_resource::<MeshCullingDataBuffer>()
197 .init_resource::<RenderMaterialInstances>()
198 .configure_sets(
199 ExtractSchedule,
200 MeshExtractionSystems.after_weak(view::extract_visibility_ranges),
201 )
202 .add_systems(
203 ExtractSchedule,
204 (
205 extract_skins,
206 extract_morphs,
207 gpu_preprocessing::clear_batched_gpu_instance_buffers::<MeshPipeline>
208 .before(MeshExtractionSystems),
209 ),
210 )
211 .add_systems(
212 Render,
213 (
214 set_mesh_motion_vector_flags.in_set(RenderSystems::PrepareMeshes),
215 prepare_skins.in_set(RenderSystems::PrepareResources),
216 write_morph_buffers.in_set(RenderSystems::PrepareResourcesFlush),
217 prepare_mesh_bind_groups.in_set(RenderSystems::PrepareBindGroups),
218 prepare_mesh_view_bind_groups
219 .in_set(RenderSystems::PrepareBindGroups)
220 .after(prepare_oit_buffers)
221 .after(prepare_atmosphere_buffers),
222 no_gpu_preprocessing::clear_batched_cpu_instance_buffers::<MeshPipeline>
223 .in_set(RenderSystems::Cleanup)
224 .after(RenderSystems::Render),
225 prepare_morph_descriptors.in_set(RenderSystems::PrepareMeshes),
226 ),
227 );
228 }
229 }
230
231 fn finish(&self, app: &mut App) {
232 let mut mesh_bindings_shader_defs = Vec::with_capacity(1);
233
234 if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
235 render_app
236 .init_gpu_resource::<MorphIndices>()
237 .init_gpu_resource::<MorphUniforms>()
238 .init_gpu_resource::<ViewKeyCache>()
239 .init_resource::<GpuPreprocessingSupport>()
240 .init_resource::<RenderGpuCulledEntities>()
241 .add_systems(RenderStartup, skin_uniforms_from_world)
242 .add_systems(
243 Render,
244 check_views_need_specialization.in_set(PrepareAssets),
245 );
246
247 let gpu_preprocessing_support =
248 render_app.world().resource::<GpuPreprocessingSupport>();
249 let use_gpu_instance_buffer_builder =
250 self.use_gpu_instance_buffer_builder && gpu_preprocessing_support.is_available();
251
252 let render_mesh_instances = RenderMeshInstances::new(use_gpu_instance_buffer_builder);
253 render_app
254 .allow_ambiguous_resource::<no_gpu_preprocessing::BatchedInstanceBuffer::<MeshUniform>>()
255 .allow_ambiguous_resource::<gpu_preprocessing::BatchedInstanceBuffers<MeshUniform, MeshInputUniform>>()
256 .insert_resource(render_mesh_instances);
257
258 if use_gpu_instance_buffer_builder {
259 render_app
260 .init_gpu_resource::<gpu_preprocessing::BatchedInstanceBuffers<
261 MeshUniform,
262 MeshInputUniform
263 >>()
264 .init_gpu_resource::<RenderMeshInstanceGpuQueues>()
265 .init_resource::<MeshesToReextractNextFrame>()
266 .add_systems(
267 RenderStartup,
268 mark_all_meshes_for_reextraction,
269 )
270 .add_systems(
271 ExtractSchedule,
272 extract_meshes_for_gpu_building.in_set(MeshExtractionSystems),
273 )
274 .add_systems(
275 Render,
276 (
277 gpu_preprocessing::write_batched_instance_buffers::<MeshPipeline>
278 .in_set(RenderSystems::PrepareResourcesFlush)
279 .after(write_mesh_culling_data_buffer),
280 gpu_preprocessing::delete_old_work_item_buffers::<MeshPipeline>
281 .in_set(RenderSystems::PrepareResources),
282 collect_meshes_for_gpu_building
283 .in_set(RenderSystems::PrepareMeshes)
284 .before(set_mesh_motion_vector_flags)
288 .after(prepare_morph_descriptors),
293 collect_gpu_culled_meshes.in_set(RenderSystems::PrepareMeshes)
294 .after(collect_meshes_for_gpu_building)
295 .before(set_mesh_motion_vector_flags),
296 ),
297 );
298 } else {
299 render_app
300 .init_gpu_resource::<no_gpu_preprocessing::BatchedInstanceBuffer<MeshUniform>>()
301 .add_systems(
302 ExtractSchedule,
303 extract_meshes_for_cpu_building.in_set(MeshExtractionSystems),
304 )
305 .add_systems(
306 Render,
307 no_gpu_preprocessing::write_batched_instance_buffer::<MeshPipeline>
308 .in_set(RenderSystems::PrepareResourcesFlush),
309 );
310 };
311
312 let render_device = render_app.world().resource::<RenderDevice>();
313 if let Some(per_object_buffer_batch_size) =
314 GpuArrayBuffer::<MeshUniform>::batch_size(&render_device.limits())
315 {
316 mesh_bindings_shader_defs.push(ShaderDefVal::UInt(
317 "PER_OBJECT_BUFFER_BATCH_SIZE".into(),
318 per_object_buffer_batch_size,
319 ));
320 }
321
322 render_app.add_systems(
323 RenderStartup,
324 (init_mesh_pipeline_view_layouts, init_mesh_pipeline)
325 .chain()
326 .in_set(MeshPipelineSystems),
327 );
328 }
329
330 load_shader_library!(app, "mesh_bindings.wesl", move |settings| *settings =
333 ShaderSettings {
334 shader_defs: mesh_bindings_shader_defs.clone(),
335 });
336 }
337}
338
339fn mark_all_meshes_for_reextraction(
341 mut render_mesh_instances: ResMut<RenderMeshInstances>,
342 mut meshes_to_reextract: ResMut<MeshesToReextractNextFrame>,
343) {
344 match *render_mesh_instances {
345 RenderMeshInstances::CpuBuilding(ref mut cpu) => {
346 meshes_to_reextract.extend(cpu.keys());
347 cpu.clear();
348 }
349 RenderMeshInstances::GpuBuilding(ref mut gpu) => {
350 meshes_to_reextract.extend(gpu.keys());
351 gpu.clear();
352 }
353 }
354}
355
356#[derive(Resource, Deref, DerefMut, Default, Debug, Clone)]
359pub struct ViewKeyCache(HashMap<RetainedViewEntity, MeshPipelineKey>);
360
361pub fn check_views_need_specialization(
362 mut view_key_cache: ResMut<ViewKeyCache>,
363 mut dirty_specializations: ResMut<DirtySpecializations>,
364 mut views: Query<(
365 &ExtractedView,
366 Option<&ExtractedCamera>,
367 &Msaa,
368 (Option<&Tonemapping>, Option<&DebandDither>),
369 Option<&ShadowFilteringMethod>,
370 Has<ScreenSpaceAmbientOcclusion>,
371 (
372 Has<NormalPrepass>,
373 Has<DepthPrepass>,
374 Has<MotionVectorPrepass>,
375 Has<DeferredPrepass>,
376 ),
377 Option<&ScreenSpaceTransmission>,
378 Has<TemporalJitter>,
379 Option<&Projection>,
380 Has<DistanceFog>,
381 (
382 Has<RenderViewLightProbes<EnvironmentMapLight>>,
383 Has<RenderViewLightProbes<IrradianceVolume>>,
384 ),
385 (
386 Has<OrderIndependentTransparencySettings>,
387 Has<ExtractedAtmosphere>,
388 Has<ScreenSpaceReflectionsUniform>,
389 Has<ContactShadows>,
390 ),
391 )>,
392 mut seen_views: Local<HashSet<RetainedViewEntity>>,
393) {
394 seen_views.clear();
395
396 for (
397 view,
398 camera,
399 msaa,
400 (tonemapping, dither),
401 shadow_filter_method,
402 ssao,
403 (normal_prepass, depth_prepass, motion_vector_prepass, deferred_prepass),
404 transmission,
405 temporal_jitter,
406 projection,
407 distance_fog,
408 (has_environment_maps, has_irradiance_volumes),
409 (has_oit, has_atmosphere, has_ssr, has_contact_shadows),
410 ) in views.iter_mut()
411 {
412 seen_views.insert(view.retained_view_entity);
413
414 let mut view_key = MeshPipelineKey::from_msaa_samples(msaa.samples())
415 | MeshPipelineKey::from_target_format(view.target_format);
416
417 if normal_prepass {
418 view_key |= MeshPipelineKey::NORMAL_PREPASS;
419 }
420
421 if depth_prepass {
422 view_key |= MeshPipelineKey::DEPTH_PREPASS;
423 }
424
425 if motion_vector_prepass {
426 view_key |= MeshPipelineKey::MOTION_VECTOR_PREPASS;
427 }
428
429 if deferred_prepass {
430 view_key |= MeshPipelineKey::DEFERRED_PREPASS;
431 }
432
433 if temporal_jitter {
434 view_key |= MeshPipelineKey::TEMPORAL_JITTER;
435 }
436
437 if has_environment_maps {
438 view_key |= MeshPipelineKey::ENVIRONMENT_MAP;
439 }
440
441 if has_irradiance_volumes {
442 view_key |= MeshPipelineKey::IRRADIANCE_VOLUME;
443 }
444
445 if has_ssr {
446 view_key |= MeshPipelineKey::SCREEN_SPACE_REFLECTIONS;
447 }
448
449 if has_oit {
450 view_key |= MeshPipelineKey::OIT_ENABLED;
451 }
452
453 if has_atmosphere {
454 view_key |= MeshPipelineKey::ATMOSPHERE;
455 }
456
457 if has_contact_shadows {
458 view_key |= MeshPipelineKey::CONTACT_SHADOWS;
459 }
460
461 if view.invert_culling {
462 view_key |= MeshPipelineKey::INVERT_CULLING;
463 }
464
465 if let Some(projection) = projection {
466 view_key |= match projection {
467 Projection::Perspective(_) => MeshPipelineKey::VIEW_PROJECTION_PERSPECTIVE,
468 Projection::Orthographic(_) => MeshPipelineKey::VIEW_PROJECTION_ORTHOGRAPHIC,
469 Projection::Custom(_) => MeshPipelineKey::VIEW_PROJECTION_NONSTANDARD,
470 };
471 }
472
473 match shadow_filter_method.unwrap_or(&ShadowFilteringMethod::default()) {
474 ShadowFilteringMethod::Hardware2x2 => {
475 view_key |= MeshPipelineKey::SHADOW_FILTER_METHOD_HARDWARE_2X2;
476 }
477 ShadowFilteringMethod::Gaussian => {
478 view_key |= MeshPipelineKey::SHADOW_FILTER_METHOD_GAUSSIAN;
479 }
480 ShadowFilteringMethod::Temporal => {
481 view_key |= MeshPipelineKey::SHADOW_FILTER_METHOD_TEMPORAL;
482 }
483 }
484
485 if camera.is_none_or(|camera| !camera.hdr)
486 && let Some(tonemapping) = tonemapping
487 && tonemapping.is_enabled()
488 {
489 view_key |= MeshPipelineKey::TONEMAP_IN_SHADER;
490 view_key |= tonemapping_pipeline_key(*tonemapping);
491 if let Some(DebandDither::Enabled) = dither {
492 view_key |= MeshPipelineKey::DEBAND_DITHER;
493 }
494 }
495 if ssao {
496 view_key |= MeshPipelineKey::SCREEN_SPACE_AMBIENT_OCCLUSION;
497 }
498 if distance_fog {
499 view_key |= MeshPipelineKey::DISTANCE_FOG;
500 }
501 if let Some(transmission) = transmission {
502 view_key |= MeshPipelineKey::VIEW_TRANSMISSION_TEXTURE;
503 view_key |= transmission.quality.pipeline_key();
504 }
505 if !view_key_cache
506 .get_mut(&view.retained_view_entity)
507 .is_some_and(|current_key| *current_key == view_key)
508 {
509 view_key_cache.insert(view.retained_view_entity, view_key);
510 dirty_specializations
511 .views
512 .insert(view.retained_view_entity);
513 }
514 }
515
516 view_key_cache.retain(|view, _| seen_views.contains(view));
517}
518
519#[derive(Component)]
520pub struct MeshTransforms {
521 pub world_from_local: Affine3,
522 pub previous_world_from_local: Affine3,
523 pub flags: u32,
524}
525
526#[derive(ShaderType, Clone)]
527pub struct MeshUniform {
528 pub world_from_local: [Vec4; 3],
530 pub previous_world_from_local: [Vec4; 3],
531 pub local_from_world_transpose_a: [Vec4; 2],
536 pub local_from_world_transpose_b: f32,
537 pub flags: u32,
538 pub lightmap_uv_rect: UVec2,
548 pub first_vertex_index: u32,
554 pub current_skin_index: u32,
556 pub material_and_lightmap_bind_group_slot: u32,
561 pub tag: u32,
563 pub morph_descriptor_index: u32,
568 pub metadata_index: u32,
570}
571
572#[derive(ShaderType, Pod, Zeroable, Clone, Copy, Default, Debug)]
577#[repr(C)]
578pub struct MeshInputUniform {
579 pub world_from_local: [Vec4; 3],
581 pub lightmap_uv_rect: UVec2,
593 pub flags: u32,
595 pub previous_input_index: u32,
600 pub first_vertex_index: u32,
606 pub first_index_index: u32,
614 pub index_count: u32,
617 pub current_skin_index: u32,
619 pub material_and_lightmap_bind_group_slot: u32,
624 pub timestamp: u32,
632 pub tag: u32,
634 pub morph_descriptor_index: u32,
639 pub metadata_index: u32,
640 pub pad1: u32,
641 pub pad2: u32,
642 pub pad3: u32,
643}
644
645#[derive(ShaderType, Pod, Zeroable, Clone, Copy, Default, Debug)]
647#[repr(C)]
648pub struct PreviousMeshInputUniform {
649 pub world_from_local: [Vec4; 3],
651}
652
653impl BufferDataInput for MeshInputUniform {
654 type Previous = PreviousMeshInputUniform;
655}
656
657impl_atomic_pod!(MeshInputUniform, MeshInputUniformBlob);
658
659impl_atomic_pod!(PreviousMeshInputUniform, PreviousMeshInputUniformBlob);
660
661#[derive(ShaderType, Pod, Zeroable, Clone, Copy, Default)]
665#[repr(C)]
666pub struct MeshCullingData {
667 pub aabb_center: Vec4,
670 pub aabb_half_extents: Vec4,
673}
674
675#[derive(Resource, Deref, DerefMut)]
682pub struct MeshCullingDataBuffer(AtomicSparseBufferVec<MeshCullingData>);
683
684impl_atomic_pod!(MeshCullingData, MeshCullingDataBlob);
685
686impl MeshUniform {
687 pub fn new(
688 mesh_transforms: &MeshTransforms,
689 first_vertex_index: u32,
690 material_bind_group_slot: MaterialBindGroupSlot,
691 maybe_lightmap: Option<(LightmapSlotIndex, Rect)>,
692 current_skin_index: Option<u32>,
693 morph_descriptor_index: Option<MorphDescriptorIndex>,
694 tag: Option<u32>,
695 metadata_index: Option<u32>,
696 ) -> Self {
697 let (local_from_world_transpose_a, local_from_world_transpose_b) =
698 mesh_transforms.world_from_local.inverse_transpose_3x3();
699 let lightmap_bind_group_slot = match maybe_lightmap {
700 None => u16::MAX,
701 Some((slot_index, _)) => slot_index.into(),
702 };
703
704 let material_slot = u32::from(material_bind_group_slot);
705 debug_assert!(
706 material_slot <= 0xFFFF,
707 "Material bind group slot {material_slot} overflowed"
708 );
709 let material_and_lightmap_bind_group_slot =
710 material_slot | ((lightmap_bind_group_slot as u32) << 16);
711
712 Self {
713 world_from_local: mesh_transforms.world_from_local.to_transpose(),
714 previous_world_from_local: mesh_transforms.previous_world_from_local.to_transpose(),
715 lightmap_uv_rect: pack_lightmap_uv_rect(maybe_lightmap.map(|(_, uv_rect)| uv_rect)),
716 local_from_world_transpose_a,
717 local_from_world_transpose_b,
718 flags: mesh_transforms.flags,
719 first_vertex_index,
720 current_skin_index: current_skin_index.unwrap_or(u32::MAX),
721 material_and_lightmap_bind_group_slot,
722 tag: tag.unwrap_or(0),
723 morph_descriptor_index: match morph_descriptor_index {
724 Some(morph_descriptor_index) => morph_descriptor_index.0,
725 None => u32::MAX,
726 },
727 metadata_index: metadata_index.unwrap_or(0),
728 }
729 }
730}
731
732bitflags::bitflags! {
734 #[repr(transparent)]
739 pub struct MeshFlags: u32 {
740 const LOD_INDEX_MASK = (1 << 16) - 1;
744 const AABB_BASED_VISIBILITY_RANGE = 1 << 27;
750 const NO_FRUSTUM_CULLING = 1 << 28;
755 const SHADOW_RECEIVER = 1 << 29;
756 const TRANSMITTED_SHADOW_RECEIVER = 1 << 30;
757 const SIGN_DETERMINANT_MODEL_3X3 = 1 << 31;
760 const NONE = 0;
761 const UNINITIALIZED = 0xFFFFFFFF;
762 }
763}
764
765impl MeshFlags {
766 fn from_components(
767 transform: &GlobalTransform,
768 lod_index: Option<NonMaxU16>,
769 visibility_range: Option<&VisibilityRange>,
770 no_frustum_culling: bool,
771 not_shadow_receiver: bool,
772 transmitted_receiver: bool,
773 ) -> MeshFlags {
774 let mut mesh_flags = if not_shadow_receiver {
775 MeshFlags::empty()
776 } else {
777 MeshFlags::SHADOW_RECEIVER
778 };
779 if visibility_range.is_some_and(|visibility_range| visibility_range.use_aabb) {
780 mesh_flags |= MeshFlags::AABB_BASED_VISIBILITY_RANGE;
781 }
782 if no_frustum_culling {
783 mesh_flags |= MeshFlags::NO_FRUSTUM_CULLING;
784 }
785 if transmitted_receiver {
786 mesh_flags |= MeshFlags::TRANSMITTED_SHADOW_RECEIVER;
787 }
788 if transform.affine().matrix3.determinant().is_sign_positive() {
789 mesh_flags |= MeshFlags::SIGN_DETERMINANT_MODEL_3X3;
790 }
791
792 let lod_index_bits = match lod_index {
793 None => u16::MAX,
794 Some(lod_index) => u16::from(lod_index),
795 };
796 mesh_flags |=
797 MeshFlags::from_bits_retain((lod_index_bits as u32) << MeshFlags::LOD_INDEX_SHIFT);
798
799 mesh_flags
800 }
801
802 pub const LOD_INDEX_SHIFT: u32 = 0;
804}
805
806bitflags::bitflags! {
807 #[derive(Clone, Copy, Default, Pod, Zeroable)]
809 #[repr(C)]
810 pub struct RenderMeshInstanceFlags: u32 {
811 const SHADOW_CASTER = 1 << 0;
813 const AUTOMATIC_BATCHING = 1 << 1;
815 const HAS_PREVIOUS_TRANSFORM = 1 << 2;
818 const HAS_PREVIOUS_SKIN = 1 << 3;
821 const HAS_PREVIOUS_MORPH = 1 << 4;
824 const NO_CPU_CULLING = 1 << 5;
827 }
828}
829
830#[derive(Deref, DerefMut)]
833pub struct RenderMeshInstanceCpu {
834 #[deref]
837 pub shared: RenderMeshInstanceSharedFlatBlob,
838 pub transforms: MeshTransforms,
842 pub render_layers: Option<RenderLayers>,
844}
845
846#[derive(Deref)]
849pub struct RenderMeshInstanceGpu {
850 #[deref]
853 pub shared: RenderMeshInstanceSharedFlatBlob,
854 pub gpu_specific: RenderMeshInstanceGpuFlatBlob,
857 pub render_layers: Option<RenderLayers>,
861}
862
863#[derive(Clone, Copy, Default, Pod, Zeroable)]
871#[repr(C)]
872pub struct RenderMeshInstanceSharedFlat {
873 asset_id: MeshAssetIdFlat,
874 material_bindings_index: MaterialBindingId,
875 lightmap_slab_index: LightmapSlabIndexFlat,
876 tag: u32,
877 current_uniform_index: u32,
878 flags: RenderMeshInstanceFlags,
879}
880
881impl_atomic_pod!(
882 RenderMeshInstanceSharedFlat,
883 RenderMeshInstanceSharedFlatBlob,
884 field(asset_id: MeshAssetIdFlat, mesh_asset_id_flat, set_mesh_asset_id_flat),
885 field(
886 material_bindings_index: MaterialBindingId,
887 material_bindings_index,
888 set_material_bindings_index
889 ),
890 field(
891 lightmap_slab_index: LightmapSlabIndexFlat,
892 lightmap_slab_index_flat,
893 set_lightmap_slab_index_flat
894 ),
895 field(tag: u32, tag, set_tag),
896 field(flags: RenderMeshInstanceFlags, flags, set_flags),
897);
898
899impl RenderMeshInstanceSharedFlatBlob {
900 pub fn mesh_asset_id(&self) -> AssetId<Mesh> {
901 self.mesh_asset_id_flat().into()
902 }
903
904 pub fn set_mesh_asset_id(&self, asset_id: AssetId<Mesh>) {
905 self.set_mesh_asset_id_flat(asset_id.into());
906 }
907
908 pub fn lightmap_slab_index(&self) -> Option<LightmapSlabIndex> {
909 self.lightmap_slab_index_flat().into()
910 }
911
912 pub fn set_lightmap_slab_index(&self, lightmap_slab_index: Option<LightmapSlabIndex>) {
913 self.set_lightmap_slab_index_flat(lightmap_slab_index.into());
914 }
915
916 #[inline]
919 pub fn should_batch(&self) -> bool {
920 self.flags()
921 .contains(RenderMeshInstanceFlags::AUTOMATIC_BATCHING)
922 }
923}
924
925#[derive(Clone, Copy, Default, Pod, Zeroable)]
930#[repr(C)]
931pub struct RenderMeshInstanceGpuFlat {
932 current_uniform_index: u32,
933}
934
935impl_atomic_pod!(
936 RenderMeshInstanceGpuFlat,
937 RenderMeshInstanceGpuFlatBlob,
938 field(current_uniform_index: u32, current_uniform_index, set_current_uniform_index),
939);
940
941#[derive(Clone, Copy, Default, Pod, Zeroable)]
942#[repr(C)]
943struct MeshAssetIdFlat {
944 mode: u32,
945 words: [u32; 4],
946}
947
948#[derive(Clone, Copy, Default, Pod, Zeroable)]
949#[repr(C)]
950struct LightmapSlabIndexFlat(u32);
951
952const MESH_ASSET_ID_FLAT_MODE_INDEX: u32 = 0;
953const MESH_ASSET_ID_FLAT_MODE_UUID: u32 = 1;
954
955impl From<AssetId<Mesh>> for MeshAssetIdFlat {
956 #[inline]
957 fn from(value: AssetId<Mesh>) -> Self {
958 match value {
959 AssetId::Index { index, .. } => {
960 let bits = index.to_bits();
961 MeshAssetIdFlat {
962 mode: MESH_ASSET_ID_FLAT_MODE_INDEX,
963 words: [(bits & 0xffff_ffff) as u32, (bits >> 32) as u32, 0, 0],
964 }
965 }
966 AssetId::Uuid { uuid } => {
967 let (hi, lo) = uuid.as_u64_pair();
968 MeshAssetIdFlat {
969 mode: MESH_ASSET_ID_FLAT_MODE_UUID,
970 words: [
971 (lo & 0xffff_ffff) as u32,
972 (lo >> 32) as u32,
973 (hi & 0xffff_ffff) as u32,
974 (hi >> 32) as u32,
975 ],
976 }
977 }
978 }
979 }
980}
981
982impl From<MeshAssetIdFlat> for AssetId<Mesh> {
983 #[inline]
984 fn from(value: MeshAssetIdFlat) -> AssetId<Mesh> {
985 if value.mode == MESH_ASSET_ID_FLAT_MODE_INDEX {
986 AssetId::from(AssetIndex::from_bits(
987 (value.words[0] as u64) | ((value.words[1] as u64) << 32),
988 ))
989 } else {
990 let lo = (value.words[0] as u64) | ((value.words[1] as u64) << 32);
991 let hi = (value.words[2] as u64) | ((value.words[3] as u64) << 32);
992 AssetId::Uuid {
993 uuid: Uuid::from_u64_pair(hi, lo),
994 }
995 }
996 }
997}
998
999impl From<Option<LightmapSlabIndex>> for LightmapSlabIndexFlat {
1000 #[inline]
1001 fn from(value: Option<LightmapSlabIndex>) -> Self {
1002 match value {
1003 Some(slab_index) => LightmapSlabIndexFlat((*slab_index).into()),
1004 None => LightmapSlabIndexFlat(!0),
1005 }
1006 }
1007}
1008
1009impl From<LightmapSlabIndexFlat> for Option<LightmapSlabIndex> {
1010 #[inline]
1011 fn from(value: LightmapSlabIndexFlat) -> Self {
1012 NonMaxU32::new(value.0).map(LightmapSlabIndex)
1013 }
1014}
1015
1016pub struct RenderMeshInstanceShared {
1019 pub mesh_asset_id: AssetId<Mesh>,
1021 pub material_bindings_index: MaterialBindingId,
1023 pub lightmap_slab_index: Option<LightmapSlabIndex>,
1026 pub center: Vec3,
1032 pub tag: u32,
1034 pub flags: RenderMeshInstanceFlags,
1036}
1037
1038pub struct RenderMeshInstanceGpuBuilder {
1044 pub shared: RenderMeshInstanceSharedFlat,
1046 pub world_from_local: Affine3,
1048 pub lightmap_uv_rect: UVec2,
1060 pub previous_input_index: Option<NonMaxU32>,
1062 pub render_layers: Option<RenderLayers>,
1064 pub mesh_flags: MeshFlags,
1066}
1067
1068#[derive(Default)]
1074pub enum RenderMeshInstanceGpuQueue {
1075 #[default]
1080 None,
1081 CpuCulling {
1085 changed: Vec<(MainEntity, RenderMeshInstanceGpuBuilder)>,
1089 removed: Vec<MainEntity>,
1091 },
1092 GpuCulling {
1096 changed_cpu_culling: Vec<(MainEntity, RenderMeshInstanceGpuBuilder, MeshCullingData)>,
1102 changed_gpu_culling: Vec<(MainEntity, RenderMeshInstanceGpuBuilder, MeshCullingData)>,
1108 removed: Vec<MainEntity>,
1110 },
1111}
1112
1113#[derive(Resource, Default, Deref, DerefMut)]
1119pub struct RenderMeshInstanceGpuQueues(Parallel<RenderMeshInstanceGpuQueue>);
1120
1121#[derive(Resource, Default, Deref, DerefMut)]
1126pub struct MeshesToReextractNextFrame(MainEntityHashSet);
1127
1128impl RenderMeshInstanceSharedFlat {
1129 fn for_gpu_building(
1132 previous_transform: Option<&PreviousGlobalTransform>,
1133 mesh: &Mesh3d,
1134 tag: Option<&MeshTag>,
1135 not_shadow_caster: bool,
1136 no_automatic_batching: bool,
1137 no_cpu_culling: bool,
1138 ) -> Self {
1139 Self::new(
1140 previous_transform,
1141 mesh,
1142 tag,
1143 default(),
1144 not_shadow_caster,
1145 no_automatic_batching,
1146 no_cpu_culling,
1147 )
1148 }
1149
1150 fn for_cpu_building(
1152 previous_transform: Option<&PreviousGlobalTransform>,
1153 mesh: &Mesh3d,
1154 tag: Option<&MeshTag>,
1155 material_bindings_index: MaterialBindingId,
1156 not_shadow_caster: bool,
1157 no_automatic_batching: bool,
1158 ) -> Self {
1159 Self::new(
1160 previous_transform,
1161 mesh,
1162 tag,
1163 material_bindings_index,
1164 not_shadow_caster,
1165 no_automatic_batching,
1166 false,
1167 )
1168 }
1169
1170 fn new(
1171 previous_transform: Option<&PreviousGlobalTransform>,
1172 mesh: &Mesh3d,
1173 tag: Option<&MeshTag>,
1174 material_bindings_index: MaterialBindingId,
1175 not_shadow_caster: bool,
1176 no_automatic_batching: bool,
1177 no_cpu_culling: bool,
1178 ) -> Self {
1179 let mut mesh_instance_flags = RenderMeshInstanceFlags::empty();
1180 mesh_instance_flags.set(RenderMeshInstanceFlags::SHADOW_CASTER, !not_shadow_caster);
1181 mesh_instance_flags.set(
1182 RenderMeshInstanceFlags::AUTOMATIC_BATCHING,
1183 !no_automatic_batching,
1184 );
1185 mesh_instance_flags.set(
1186 RenderMeshInstanceFlags::HAS_PREVIOUS_TRANSFORM,
1187 previous_transform.is_some(),
1188 );
1189 mesh_instance_flags.set(RenderMeshInstanceFlags::NO_CPU_CULLING, no_cpu_culling);
1190
1191 RenderMeshInstanceSharedFlat {
1192 asset_id: mesh.id().into(),
1193 material_bindings_index,
1194 tag: tag.map_or(0, |i| **i),
1195 flags: mesh_instance_flags,
1196 lightmap_slab_index: LightmapSlabIndexFlat::default(),
1198 current_uniform_index: 0,
1200 }
1201 }
1202}
1203
1204#[derive(Resource)]
1210pub enum RenderMeshInstances {
1211 CpuBuilding(RenderMeshInstancesCpu),
1213 GpuBuilding(RenderMeshInstancesGpu),
1215}
1216
1217#[derive(Default, Deref, DerefMut)]
1220pub struct RenderMeshInstancesCpu(MainEntityHashMap<RenderMeshInstanceCpu>);
1221
1222#[derive(Default, Deref, DerefMut)]
1225pub struct RenderMeshInstancesGpu(MainEntityHashMap<RenderMeshInstanceGpu>);
1226
1227impl RenderMeshInstances {
1228 fn new(use_gpu_instance_buffer_builder: bool) -> RenderMeshInstances {
1230 if use_gpu_instance_buffer_builder {
1231 RenderMeshInstances::GpuBuilding(RenderMeshInstancesGpu::default())
1232 } else {
1233 RenderMeshInstances::CpuBuilding(RenderMeshInstancesCpu::default())
1234 }
1235 }
1236
1237 pub fn mesh_asset_id(&self, entity: MainEntity) -> Option<AssetId<Mesh>> {
1239 match *self {
1240 RenderMeshInstances::CpuBuilding(ref instances) => instances.mesh_asset_id(entity),
1241 RenderMeshInstances::GpuBuilding(ref instances) => instances.mesh_asset_id(entity),
1242 }
1243 }
1244
1245 pub fn render_mesh_queue_data(&self, entity: MainEntity) -> Option<RenderMeshQueueData<'_>> {
1248 match *self {
1249 RenderMeshInstances::CpuBuilding(ref instances) => {
1250 instances.render_mesh_queue_data(entity)
1251 }
1252 RenderMeshInstances::GpuBuilding(ref instances) => {
1253 instances.render_mesh_queue_data(entity)
1254 }
1255 }
1256 }
1257
1258 fn insert_mesh_instance_flags(&mut self, entity: MainEntity, flags: RenderMeshInstanceFlags) {
1261 match *self {
1262 RenderMeshInstances::CpuBuilding(ref mut instances) => {
1263 instances.insert_mesh_instance_flags(entity, flags);
1264 }
1265 RenderMeshInstances::GpuBuilding(ref mut instances) => {
1266 instances.insert_mesh_instance_flags(entity, flags);
1267 }
1268 }
1269 }
1270}
1271
1272impl RenderMeshInstancesCpu {
1273 fn mesh_asset_id(&self, entity: MainEntity) -> Option<AssetId<Mesh>> {
1274 self.get(&entity)
1275 .map(|render_mesh_instance| render_mesh_instance.mesh_asset_id())
1276 }
1277
1278 fn render_mesh_queue_data(&self, entity: MainEntity) -> Option<RenderMeshQueueData<'_>> {
1279 self.get(&entity)
1280 .map(|render_mesh_instance| RenderMeshQueueData {
1281 shared: &render_mesh_instance.shared,
1282 render_layers: render_mesh_instance.render_layers.clone(),
1283 current_uniform_index: InputUniformIndex::default(),
1284 })
1285 }
1286
1287 fn insert_mesh_instance_flags(&mut self, entity: MainEntity, flags: RenderMeshInstanceFlags) {
1290 if let Some(instance) = self.get_mut(&entity) {
1291 instance.set_flags(instance.flags() | flags);
1292 }
1293 }
1294}
1295
1296impl RenderMeshInstancesGpu {
1297 fn mesh_asset_id(&self, entity: MainEntity) -> Option<AssetId<Mesh>> {
1298 self.get(&entity)
1299 .map(|render_mesh_instance| render_mesh_instance.mesh_asset_id())
1300 }
1301
1302 fn render_mesh_queue_data(&self, entity: MainEntity) -> Option<RenderMeshQueueData<'_>> {
1303 self.get(&entity)
1304 .map(|render_mesh_instance| RenderMeshQueueData {
1305 shared: &render_mesh_instance.shared,
1306 render_layers: render_mesh_instance.render_layers.clone(),
1307 current_uniform_index: InputUniformIndex(
1308 render_mesh_instance.gpu_specific.current_uniform_index(),
1309 ),
1310 })
1311 }
1312
1313 fn insert_mesh_instance_flags(&mut self, entity: MainEntity, flags: RenderMeshInstanceFlags) {
1316 if let Some(instance) = self.get_mut(&entity) {
1317 instance.set_flags(instance.flags() | flags);
1318 }
1319 }
1320}
1321
1322impl RenderMeshInstanceGpuQueue {
1323 fn init(&mut self, any_gpu_culling: bool) {
1329 match (any_gpu_culling, &mut *self) {
1330 (
1331 true,
1332 RenderMeshInstanceGpuQueue::GpuCulling {
1333 changed_cpu_culling,
1334 changed_gpu_culling,
1335 removed,
1336 },
1337 ) => {
1338 changed_cpu_culling.clear();
1339 changed_gpu_culling.clear();
1340 removed.clear();
1341 }
1342 (true, _) => {
1343 *self = RenderMeshInstanceGpuQueue::GpuCulling {
1344 changed_cpu_culling: vec![],
1345 changed_gpu_culling: vec![],
1346 removed: vec![],
1347 }
1348 }
1349 (false, RenderMeshInstanceGpuQueue::CpuCulling { changed, removed }) => {
1350 changed.clear();
1351 removed.clear();
1352 }
1353 (false, _) => {
1354 *self = RenderMeshInstanceGpuQueue::CpuCulling {
1355 changed: vec![],
1356 removed: vec![],
1357 }
1358 }
1359 }
1360 }
1361
1362 fn push(
1364 &mut self,
1365 entity: MainEntity,
1366 instance_builder: RenderMeshInstanceGpuBuilder,
1367 culling_data_builder: Option<MeshCullingData>,
1368 no_cpu_culling: bool,
1369 ) {
1370 match (&mut *self, culling_data_builder, no_cpu_culling) {
1371 (
1372 &mut RenderMeshInstanceGpuQueue::CpuCulling {
1373 changed: ref mut queue,
1374 ..
1375 },
1376 None,
1377 _,
1378 ) => {
1379 queue.push((entity, instance_builder));
1380 }
1381
1382 (
1383 &mut RenderMeshInstanceGpuQueue::GpuCulling {
1384 changed_cpu_culling: ref mut queue,
1385 ..
1386 },
1387 Some(culling_data_builder),
1388 false,
1389 )
1390 | (
1391 &mut RenderMeshInstanceGpuQueue::GpuCulling {
1392 changed_gpu_culling: ref mut queue,
1393 ..
1394 },
1395 Some(culling_data_builder),
1396 true,
1397 ) => {
1398 queue.push((entity, instance_builder, culling_data_builder));
1399 }
1400
1401 (_, None, _) => {
1402 *self = RenderMeshInstanceGpuQueue::CpuCulling {
1403 changed: vec![(entity, instance_builder)],
1404 removed: vec![],
1405 };
1406 }
1407
1408 (_, Some(culling_data_builder), false) => {
1409 *self = RenderMeshInstanceGpuQueue::GpuCulling {
1410 changed_cpu_culling: vec![(entity, instance_builder, culling_data_builder)],
1411 changed_gpu_culling: vec![],
1412 removed: vec![],
1413 };
1414 }
1415 (_, Some(culling_data_builder), true) => {
1416 *self = RenderMeshInstanceGpuQueue::GpuCulling {
1417 changed_cpu_culling: vec![],
1418 changed_gpu_culling: vec![(entity, instance_builder, culling_data_builder)],
1419 removed: vec![],
1420 };
1421 }
1422 }
1423 }
1424
1425 fn remove(&mut self, entity: MainEntity, gpu_culling: bool) {
1429 match (&mut *self, gpu_culling) {
1430 (RenderMeshInstanceGpuQueue::None, false) => {
1431 *self = RenderMeshInstanceGpuQueue::CpuCulling {
1432 changed: vec![],
1433 removed: vec![entity],
1434 }
1435 }
1436 (RenderMeshInstanceGpuQueue::None, true) => {
1437 *self = RenderMeshInstanceGpuQueue::GpuCulling {
1438 changed_cpu_culling: vec![],
1439 changed_gpu_culling: vec![],
1440 removed: vec![entity],
1441 }
1442 }
1443 (RenderMeshInstanceGpuQueue::CpuCulling { removed, .. }, _)
1444 | (RenderMeshInstanceGpuQueue::GpuCulling { removed, .. }, _) => {
1445 removed.push(entity);
1446 }
1447 }
1448 }
1449}
1450
1451impl RenderMeshInstanceGpuBuilder {
1452 fn prepare(
1456 mut self,
1457 entity: MainEntity,
1458 mesh_allocator: &MeshAllocator,
1459 mesh_material_ids: &RenderMaterialInstances,
1460 render_material_bindings: &RenderMaterialBindings,
1461 render_lightmaps: &RenderLightmaps,
1462 skin_uniforms: &SkinUniforms,
1463 morph_indices: &MorphIndices,
1464 timestamp: FrameCount,
1465 ) -> Option<RenderMeshInstanceGpuPrepared> {
1466 let mesh_material = mesh_material_ids.mesh_material(entity);
1472 let mesh_material_binding_id = if let Some(mesh_material) = mesh_material {
1473 render_material_bindings.get(&mesh_material).copied()?
1474 } else {
1475 MaterialBindingId::default()
1477 };
1478 self.shared.material_bindings_index = mesh_material_binding_id;
1479
1480 let (first_vertex_index, vertex_count) =
1481 match mesh_allocator.mesh_vertex_slice(&self.shared.asset_id.into()) {
1482 Some(mesh_vertex_slice) => (
1483 mesh_vertex_slice.range.start,
1484 mesh_vertex_slice.range.end - mesh_vertex_slice.range.start,
1485 ),
1486 None => (0, 0),
1487 };
1488 let (mesh_is_indexed, first_index_index, index_count) =
1489 match mesh_allocator.mesh_index_slice(&self.shared.asset_id.into()) {
1490 Some(mesh_index_slice) => (
1491 true,
1492 mesh_index_slice.range.start,
1493 mesh_index_slice.range.end - mesh_index_slice.range.start,
1494 ),
1495 None => (false, 0, 0),
1496 };
1497 let current_skin_index = match skin_uniforms.skin_byte_offset(entity) {
1498 Some(skin_index) => skin_index.index(),
1499 None => u32::MAX,
1500 };
1501
1502 let lightmap_slot = match render_lightmaps.render_lightmaps.get(&entity) {
1503 Some(render_lightmap) => u16::from(*render_lightmap.slot_index),
1504 None => u16::MAX,
1505 };
1506 let lightmap_slab_index = render_lightmaps
1507 .render_lightmaps
1508 .get(&entity)
1509 .map(|lightmap| lightmap.slab_index);
1510 self.shared.lightmap_slab_index = lightmap_slab_index.into();
1511
1512 let morph_descriptor_index = match morph_indices.morph_descriptor_index(entity) {
1513 Some(morph_descriptor_index) => *morph_descriptor_index,
1514 None => u32::MAX,
1515 };
1516
1517 let metadata_index = mesh_allocator
1518 .mesh_metadata_slice(&self.shared.asset_id.into())
1519 .map(|mesh_metadata_slice| mesh_metadata_slice.range.start)
1520 .unwrap_or(0);
1521
1522 let material_slot = u32::from(self.shared.material_bindings_index.slot);
1524 debug_assert!(
1525 material_slot <= 0xFFFF,
1526 "Material bind group slot {material_slot} overflowed"
1527 );
1528 let material_and_lightmap_bind_group_slot = material_slot | ((lightmap_slot as u32) << 16);
1529
1530 let mesh_input_uniform = MeshInputUniform {
1531 world_from_local: self.world_from_local.to_transpose(),
1532 lightmap_uv_rect: self.lightmap_uv_rect,
1533 flags: self.mesh_flags.bits(),
1534 previous_input_index: u32::MAX,
1535 timestamp: timestamp.0,
1536 first_vertex_index,
1537 first_index_index,
1538 index_count: if mesh_is_indexed {
1539 index_count
1540 } else {
1541 vertex_count
1542 },
1543 current_skin_index,
1544 material_and_lightmap_bind_group_slot,
1545 tag: self.shared.tag,
1546 morph_descriptor_index,
1547 metadata_index,
1548 ..Default::default()
1549 };
1550
1551 Some(RenderMeshInstanceGpuPrepared {
1552 shared: self.shared,
1553 mesh_input_uniform,
1554 render_layers: self.render_layers,
1555 })
1556 }
1557}
1558
1559pub struct RenderMeshInstanceGpuPrepared {
1561 shared: RenderMeshInstanceSharedFlat,
1563 mesh_input_uniform: MeshInputUniform,
1565 render_layers: Option<RenderLayers>,
1567}
1568
1569impl RenderMeshInstanceGpuPrepared {
1570 fn update(
1573 mut self,
1574 entity: MainEntity,
1575 render_mesh_instances: &mut MainEntityHashMap<RenderMeshInstanceGpu>,
1576 current_input_buffer: &mut InstanceInputUniformBuffer<MeshInputUniform>,
1577 previous_input_buffer: &PreviousInstanceInputUniformBuffer<PreviousMeshInputUniform>,
1578 ) -> u32 {
1579 let current_uniform_index;
1581 match render_mesh_instances.entry(entity) {
1582 Entry::Occupied(mut occupied_entry) => {
1583 current_uniform_index = occupied_entry
1587 .get_mut()
1588 .gpu_specific
1589 .current_uniform_index();
1590
1591 let previous_world_from_local = current_input_buffer
1594 .get_unchecked(current_uniform_index)
1595 .world_from_local;
1596 let previous_input_index = previous_input_buffer.push(PreviousMeshInputUniform {
1597 world_from_local: previous_world_from_local,
1598 });
1599 self.mesh_input_uniform.previous_input_index = previous_input_index;
1600
1601 current_input_buffer.set(current_uniform_index, self.mesh_input_uniform);
1603
1604 let existing_instance = occupied_entry.get_mut();
1606 self.shared.write_to_blob(&existing_instance.shared);
1607 existing_instance
1608 .gpu_specific
1609 .set_current_uniform_index(current_uniform_index);
1610 existing_instance.render_layers = self.render_layers.clone();
1611 }
1612
1613 Entry::Vacant(vacant_entry) => {
1614 current_uniform_index = current_input_buffer.add(self.mesh_input_uniform);
1616
1617 let new_instance = vacant_entry.insert(RenderMeshInstanceGpu {
1618 shared: RenderMeshInstanceSharedFlatBlob::default(),
1619 gpu_specific: RenderMeshInstanceGpuFlatBlob::default(),
1620 render_layers: self.render_layers.clone(),
1621 });
1622 self.shared.write_to_blob(&new_instance.shared);
1623 RenderMeshInstanceGpuFlat {
1624 current_uniform_index,
1625 }
1626 .write_to_blob(&new_instance.gpu_specific);
1627 }
1628 }
1629
1630 current_uniform_index
1631 }
1632}
1633
1634fn remove_mesh_input_uniform(
1637 entity: MainEntity,
1638 render_mesh_instances: &mut MainEntityHashMap<RenderMeshInstanceGpu>,
1639 current_input_buffer: &mut InstanceInputUniformBuffer<MeshInputUniform>,
1640) -> Option<u32> {
1641 let removed_render_mesh_instance = render_mesh_instances.remove(&entity)?;
1643
1644 let removed_uniform_index = removed_render_mesh_instance
1645 .gpu_specific
1646 .current_uniform_index();
1647 current_input_buffer.remove(removed_uniform_index);
1648 Some(removed_uniform_index)
1649}
1650
1651impl MeshCullingData {
1652 fn new(aabb: Option<&Aabb>) -> Self {
1657 match aabb {
1658 Some(aabb) => MeshCullingData {
1659 aabb_center: aabb.center.extend(0.0),
1660 aabb_half_extents: aabb.half_extents.extend(0.0),
1661 },
1662 None => MeshCullingData {
1663 aabb_center: Vec3::ZERO.extend(0.0),
1664 aabb_half_extents: Vec3::INFINITY.extend(0.0),
1665 },
1666 }
1667 }
1668
1669 fn update(
1672 &self,
1673 mesh_culling_data_buffer: &mut MeshCullingDataBuffer,
1674 instance_data_index: u32,
1675 ) {
1676 while mesh_culling_data_buffer.len() < instance_data_index + 1 {
1677 mesh_culling_data_buffer.push(MeshCullingData::default());
1678 }
1679 mesh_culling_data_buffer.set(instance_data_index, *self);
1680 }
1681}
1682
1683impl Default for MeshCullingDataBuffer {
1684 #[inline]
1685 fn default() -> Self {
1686 Self(AtomicSparseBufferVec::new(
1687 BufferUsages::STORAGE,
1688 Arc::from("mesh culling data buffer"),
1689 ))
1690 }
1691}
1692
1693#[derive(Deref)]
1696pub struct RenderMeshQueueData<'a> {
1697 #[deref]
1699 pub shared: &'a RenderMeshInstanceSharedFlatBlob,
1700 pub render_layers: Option<RenderLayers>,
1702 pub current_uniform_index: InputUniformIndex,
1705}
1706
1707#[derive(SystemSet, Clone, PartialEq, Eq, Debug, Hash)]
1710pub struct MeshExtractionSystems;
1711
1712#[derive(Resource, Default)]
1719pub struct RenderGpuCulledEntities {
1720 pub entities: MainEntityHashMap<RenderLayers>,
1723
1724 pub added: Vec<MainEntity>,
1734
1735 pub removed: Vec<MainEntity>,
1744
1745 pub changed_layers: Vec<MainEntity>,
1748}
1749
1750impl RenderGpuCulledEntities {
1751 pub fn prepare_for_new_frame(&mut self) {
1755 self.added.clear();
1756 self.removed.clear();
1757 self.changed_layers.clear();
1758 }
1759
1760 pub fn update(
1772 &mut self,
1773 new_entity: MainEntity,
1774 render_layers: RenderLayers,
1775 no_cpu_culling: bool,
1776 ) {
1777 match self.entities.entry(new_entity) {
1778 Entry::Occupied(mut occupied_entry) => {
1779 if no_cpu_culling {
1780 if *occupied_entry.get() != render_layers {
1781 self.changed_layers.push(new_entity);
1782 }
1783
1784 occupied_entry.insert(render_layers);
1785 } else {
1786 occupied_entry.remove();
1787 self.removed.push(new_entity);
1788 }
1789 }
1790 Entry::Vacant(vacant_entry) => {
1791 if no_cpu_culling {
1792 vacant_entry.insert(render_layers);
1793 self.added.push(new_entity);
1794 }
1795 }
1796 }
1797 }
1798
1799 pub fn remove(&mut self, old_entity: MainEntity) {
1801 self.removed.push(old_entity);
1802 self.entities.remove(&old_entity);
1803 }
1804}
1805
1806pub fn extract_meshes_for_cpu_building(
1812 mut render_mesh_instances: ResMut<RenderMeshInstances>,
1813 mesh_material_ids: Res<RenderMaterialInstances>,
1814 render_material_bindings: Res<RenderMaterialBindings>,
1815 render_visibility_ranges: Res<RenderVisibilityRanges>,
1816 mut render_mesh_instance_queues: Local<Parallel<Vec<(Entity, RenderMeshInstanceCpu)>>>,
1817 meshes_query: Extract<
1818 Query<(
1819 Entity,
1820 &ViewVisibility,
1821 &GlobalTransform,
1822 Option<&PreviousGlobalTransform>,
1823 &Mesh3d,
1824 Option<&MeshTag>,
1825 Has<NoFrustumCulling>,
1826 Has<NotShadowReceiver>,
1827 Has<TransmittedShadowReceiver>,
1828 Has<NotShadowCaster>,
1829 Has<NoAutomaticBatching>,
1830 Option<&VisibilityRange>,
1831 Option<&RenderLayers>,
1832 )>,
1833 >,
1834) {
1835 meshes_query.par_iter().for_each_init(
1836 || render_mesh_instance_queues.borrow_local_mut(),
1837 |queue,
1838 (
1839 entity,
1840 view_visibility,
1841 transform,
1842 previous_transform,
1843 mesh,
1844 tag,
1845 no_frustum_culling,
1846 not_shadow_receiver,
1847 transmitted_receiver,
1848 not_shadow_caster,
1849 no_automatic_batching,
1850 visibility_range,
1851 render_layers,
1852 )| {
1853 if !view_visibility.get() {
1854 return;
1855 }
1856
1857 let mut lod_index = None;
1858 if visibility_range.is_some() {
1859 lod_index = render_visibility_ranges.lod_index_for_entity(entity.into());
1860 }
1861
1862 let mesh_flags = MeshFlags::from_components(
1863 transform,
1864 lod_index,
1865 visibility_range,
1866 no_frustum_culling,
1867 not_shadow_receiver,
1868 transmitted_receiver,
1869 );
1870
1871 let mesh_material = mesh_material_ids.mesh_material(MainEntity::from(entity));
1872
1873 let material_bindings_index = mesh_material
1874 .and_then(|mesh_material| render_material_bindings.get(&mesh_material).copied())
1875 .unwrap_or_default();
1876
1877 let shared = RenderMeshInstanceSharedFlat::for_cpu_building(
1878 previous_transform,
1879 mesh,
1880 tag,
1881 material_bindings_index,
1882 not_shadow_caster,
1883 no_automatic_batching,
1884 );
1885
1886 let world_from_local = transform.affine();
1887 let previous_world_from_local = previous_transform
1888 .map(|previous_transform| previous_transform.0)
1889 .unwrap_or(world_from_local);
1890
1891 queue.push((
1892 entity,
1893 RenderMeshInstanceCpu {
1894 transforms: MeshTransforms {
1895 world_from_local: world_from_local.into(),
1896 previous_world_from_local: previous_world_from_local.into(),
1897 flags: mesh_flags.bits(),
1898 },
1899 shared: (&shared).into(),
1900 render_layers: render_layers.cloned(),
1901 },
1902 ));
1903 },
1904 );
1905
1906 let RenderMeshInstances::CpuBuilding(ref mut render_mesh_instances) = *render_mesh_instances
1908 else {
1909 panic!(
1910 "`extract_meshes_for_cpu_building` should only be called if we're using CPU \
1911 `MeshUniform` building"
1912 );
1913 };
1914
1915 render_mesh_instances.clear();
1916 for queue in render_mesh_instance_queues.iter_mut() {
1917 for (entity, render_mesh_instance) in queue.drain(..) {
1918 render_mesh_instances.insert(entity.into(), render_mesh_instance);
1919 }
1920 }
1921}
1922
1923type GpuMeshExtractionQuery = (
1925 Entity,
1926 Read<ViewVisibility>,
1927 Read<GlobalTransform>,
1928 Option<Read<PreviousGlobalTransform>>,
1929 Option<Read<Lightmap>>,
1930 Option<Read<Aabb>>,
1931 Read<Mesh3d>,
1932 Option<Read<MeshTag>>,
1933 (
1934 Has<NoFrustumCulling>,
1935 Has<NotShadowReceiver>,
1936 Has<TransmittedShadowReceiver>,
1937 Has<NotShadowCaster>,
1938 Has<NoAutomaticBatching>,
1939 Has<NoCpuCulling>,
1940 ),
1941 Option<Read<VisibilityRange>>,
1942 Option<Read<RenderLayers>>,
1943);
1944
1945pub fn extract_meshes_for_gpu_building(
1954 mut render_mesh_instances: ResMut<RenderMeshInstances>,
1955 render_visibility_ranges: Res<RenderVisibilityRanges>,
1956 mut render_mesh_instance_queues: ResMut<RenderMeshInstanceGpuQueues>,
1957 changed_meshes_query: Extract<
1958 Query<
1959 GpuMeshExtractionQuery,
1960 Or<(
1961 Changed<ViewVisibility>,
1962 Changed<GlobalTransform>,
1963 Changed<PreviousGlobalTransform>,
1964 Changed<Lightmap>,
1965 Changed<Aabb>,
1966 Changed<Mesh3d>,
1967 Changed<MeshTag>,
1968 Or<(
1969 Changed<NoFrustumCulling>,
1970 Changed<NotShadowReceiver>,
1971 Changed<TransmittedShadowReceiver>,
1972 Changed<NotShadowCaster>,
1973 Changed<NoAutomaticBatching>,
1974 Changed<NoCpuCulling>,
1975 )>,
1976 Changed<VisibilityRange>,
1977 Changed<SkinnedMesh>,
1978 Changed<RenderLayers>,
1979 )>,
1980 >,
1981 >,
1982 (
1983 mut removed_previous_global_transform_query,
1984 mut removed_lightmap_query,
1985 mut removed_aabb_query,
1986 mut removed_mesh_tag_query,
1987 mut removed_no_frustum_culling_query,
1988 mut removed_not_shadow_receiver_query,
1989 mut removed_transmitted_receiver_query,
1990 mut removed_not_shadow_caster_query,
1991 mut removed_no_automatic_batching_query,
1992 mut removed_no_cpu_culling_query,
1993 mut removed_visibility_range_query,
1994 mut removed_skinned_mesh_query,
1995 mut removed_render_layers_query,
1996 ): (
1997 Extract<RemovedComponents<PreviousGlobalTransform>>,
1998 Extract<RemovedComponents<Lightmap>>,
1999 Extract<RemovedComponents<Aabb>>,
2000 Extract<RemovedComponents<MeshTag>>,
2001 Extract<RemovedComponents<NoFrustumCulling>>,
2002 Extract<RemovedComponents<NotShadowReceiver>>,
2003 Extract<RemovedComponents<TransmittedShadowReceiver>>,
2004 Extract<RemovedComponents<NotShadowCaster>>,
2005 Extract<RemovedComponents<NoAutomaticBatching>>,
2006 Extract<RemovedComponents<NoCpuCulling>>,
2007 Extract<RemovedComponents<VisibilityRange>>,
2008 Extract<RemovedComponents<SkinnedMesh>>,
2009 Extract<RemovedComponents<RenderLayers>>,
2010 ),
2011 all_meshes_query: Extract<Query<GpuMeshExtractionQuery>>,
2012 mut removed_meshes_query: Extract<RemovedComponents<Mesh3d>>,
2013 gpu_culling_query: Extract<Query<(), (With<Camera>, Without<NoIndirectDrawing>)>>,
2014 meshes_to_reextract_next_frame: ResMut<MeshesToReextractNextFrame>,
2015 mut reextract_entities: Local<EntityHashSet>,
2016 mut potential_reextraction_set: Local<IndexSet<Entity, EntityHash>>,
2017 mut potential_reextraction_bitfield: Local<Vec<AtomicU64>>,
2018) {
2019 reextract_entities.clear();
2020
2021 let any_gpu_culling = !gpu_culling_query.is_empty();
2023 for render_mesh_instance_queue in render_mesh_instance_queues.iter_mut() {
2024 render_mesh_instance_queue.init(any_gpu_culling);
2025 }
2026
2027 potential_reextraction_set.clear();
2034 potential_reextraction_set.extend(
2035 meshes_to_reextract_next_frame
2036 .iter()
2037 .map(|&e| *e)
2038 .chain(removed_previous_global_transform_query.read())
2039 .chain(removed_lightmap_query.read())
2040 .chain(removed_aabb_query.read())
2041 .chain(removed_mesh_tag_query.read())
2042 .chain(removed_no_frustum_culling_query.read())
2043 .chain(removed_not_shadow_receiver_query.read())
2044 .chain(removed_transmitted_receiver_query.read())
2045 .chain(removed_not_shadow_caster_query.read())
2046 .chain(removed_no_automatic_batching_query.read())
2047 .chain(removed_no_cpu_culling_query.read())
2048 .chain(removed_visibility_range_query.read())
2049 .chain(removed_skinned_mesh_query.read())
2050 .chain(removed_render_layers_query.read()),
2051 );
2052
2053 potential_reextraction_bitfield.clear();
2060 potential_reextraction_bitfield.extend(
2061 iter::repeat_with(|| AtomicU64::new(0)).take(potential_reextraction_set.len().div_ceil(64)),
2062 );
2063
2064 let RenderMeshInstances::GpuBuilding(ref mut render_mesh_instances) = *render_mesh_instances
2067 else {
2068 panic!(
2069 "`extract_meshes_for_gpu_building` should only be called if we're \
2070 using GPU `MeshUniform` building"
2071 );
2072 };
2073
2074 changed_meshes_query.par_iter().for_each_init(
2077 || render_mesh_instance_queues.borrow_local_mut(),
2078 |queue, query_row| {
2079 extract_mesh_for_gpu_building(
2080 query_row,
2081 &render_visibility_ranges,
2082 render_mesh_instances,
2083 queue,
2084 any_gpu_culling,
2085 );
2086
2087 if let Some(bit_index) = potential_reextraction_set.get_index_of(&query_row.0) {
2090 potential_reextraction_bitfield[bit_index / 64]
2091 .fetch_or(1 << (bit_index % 64), Ordering::Relaxed);
2092 }
2093 },
2094 );
2095
2096 for bit in AtomicU64ZeroBitIter::new(&potential_reextraction_bitfield) {
2105 if let Some(entity) = potential_reextraction_set.get_index(bit as usize) {
2106 reextract_entities.insert(*entity);
2107 }
2108 }
2109
2110 let mut queue = render_mesh_instance_queues.borrow_local_mut();
2112 for entity in &reextract_entities {
2113 if let Ok(query_row) = all_meshes_query.get(*entity) {
2114 extract_mesh_for_gpu_building(
2115 query_row,
2116 &render_visibility_ranges,
2117 render_mesh_instances,
2118 &mut queue,
2119 any_gpu_culling,
2120 );
2121 }
2122 }
2123
2124 for entity in removed_meshes_query.read() {
2126 let entity = MainEntity::from(entity);
2130 if !changed_meshes_query.contains(*entity)
2131 && !meshes_to_reextract_next_frame.contains(&entity)
2132 {
2133 queue.remove(entity, any_gpu_culling);
2134 }
2135 }
2136}
2137
2138fn extract_mesh_for_gpu_building(
2141 (
2142 entity,
2143 view_visibility,
2144 transform,
2145 previous_transform,
2146 lightmap,
2147 aabb,
2148 mesh,
2149 tag,
2150 (
2151 no_frustum_culling,
2152 not_shadow_receiver,
2153 transmitted_receiver,
2154 not_shadow_caster,
2155 no_automatic_batching,
2156 no_cpu_culling,
2157 ),
2158 visibility_range,
2159 render_layers,
2160 ): <GpuMeshExtractionQuery as QueryData>::Item<'_, '_>,
2161 render_visibility_ranges: &RenderVisibilityRanges,
2162 render_mesh_instances: &RenderMeshInstancesGpu,
2163 queue: &mut RenderMeshInstanceGpuQueue,
2164 any_gpu_culling: bool,
2165) {
2166 if !view_visibility.get() {
2168 queue.remove(entity.into(), any_gpu_culling);
2169 return;
2170 }
2171
2172 let mut lod_index = None;
2174 if visibility_range.is_some() {
2175 lod_index = render_visibility_ranges.lod_index_for_entity(entity.into());
2176 }
2177
2178 let mesh_flags = MeshFlags::from_components(
2180 transform,
2181 lod_index,
2182 visibility_range,
2183 no_frustum_culling,
2184 not_shadow_receiver,
2185 transmitted_receiver,
2186 );
2187
2188 let shared = RenderMeshInstanceSharedFlat::for_gpu_building(
2190 previous_transform,
2191 mesh,
2192 tag,
2193 not_shadow_caster,
2194 no_automatic_batching,
2195 no_cpu_culling,
2196 );
2197
2198 let lightmap_uv_rect = pack_lightmap_uv_rect(lightmap.map(|lightmap| lightmap.uv_rect));
2200
2201 let gpu_mesh_culling_data = any_gpu_culling.then(|| MeshCullingData::new(aabb));
2203
2204 let previous_input_index = if shared
2207 .flags
2208 .contains(RenderMeshInstanceFlags::HAS_PREVIOUS_TRANSFORM)
2209 {
2210 render_mesh_instances
2211 .get(&MainEntity::from(entity))
2212 .and_then(|render_mesh_instance| {
2213 NonMaxU32::new(render_mesh_instance.gpu_specific.current_uniform_index())
2214 })
2215 } else {
2216 None
2217 };
2218
2219 let gpu_mesh_instance_builder = RenderMeshInstanceGpuBuilder {
2222 shared,
2223 world_from_local: (transform.affine()).into(),
2224 lightmap_uv_rect,
2225 mesh_flags,
2226 previous_input_index,
2227 render_layers: render_layers.cloned(),
2228 };
2229
2230 queue.push(
2232 entity.into(),
2233 gpu_mesh_instance_builder,
2234 gpu_mesh_culling_data,
2235 no_cpu_culling,
2236 );
2237}
2238
2239struct AtomicU64ZeroBitIter<'a> {
2241 bits: &'a [AtomicU64],
2243 current_word: u64,
2247 next_index: usize,
2249}
2250
2251impl<'a> AtomicU64ZeroBitIter<'a> {
2252 fn new(bits: &'a [AtomicU64]) -> AtomicU64ZeroBitIter<'a> {
2255 AtomicU64ZeroBitIter {
2256 bits,
2257 current_word: !0,
2258 next_index: 0,
2259 }
2260 }
2261}
2262
2263impl<'a> Iterator for AtomicU64ZeroBitIter<'a> {
2264 type Item = u32;
2265
2266 fn next(&mut self) -> Option<Self::Item> {
2267 while self.current_word == !0 {
2269 self.current_word = self.bits.get(self.next_index)?.load(Ordering::Relaxed);
2270 self.next_index += 1;
2271 }
2272
2273 let bit_index = self.current_word.trailing_ones();
2275 self.current_word |= 1 << bit_index;
2276 let word_index = ((self.next_index - 1) * 64) as u32;
2277 Some(word_index + bit_index)
2278 }
2279}
2280
2281pub fn collect_gpu_culled_meshes(
2288 mut cameras: Query<(Option<&RenderLayers>, &mut RenderVisibleEntities), With<ExtractedView>>,
2289 mut lights: Query<(Option<&RenderLayers>, &mut RenderShadowMapVisibleEntities)>,
2290 render_gpu_culled_entities: Res<RenderGpuCulledEntities>,
2291) {
2292 let default_render_layers = RenderLayers::default();
2293
2294 for (maybe_render_layers, mut render_visible_entities) in &mut cameras {
2296 let just_added_render_visible_entities = render_visible_entities.is_added();
2297 collect_gpu_culled_meshes_for_subview(
2298 maybe_render_layers.unwrap_or(&default_render_layers),
2299 &mut render_visible_entities,
2300 just_added_render_visible_entities,
2301 &render_gpu_culled_entities,
2302 );
2303 }
2304
2305 for (maybe_render_layers, mut render_shadow_map_visible_entities) in &mut lights {
2307 let last_run = render_shadow_map_visible_entities.last_run();
2308 let this_run = render_shadow_map_visible_entities.this_run();
2309
2310 for (render_visible_entities_added_tick, render_visible_entities) in
2311 render_shadow_map_visible_entities.subviews.values_mut()
2312 {
2313 collect_gpu_culled_meshes_for_subview(
2314 maybe_render_layers.unwrap_or(&default_render_layers),
2315 render_visible_entities,
2316 render_visible_entities_added_tick.is_newer_than(last_run, this_run),
2317 &render_gpu_culled_entities,
2318 );
2319 }
2320 }
2321}
2322
2323fn collect_gpu_culled_meshes_for_subview(
2330 view_render_layers: &RenderLayers,
2331 render_visible_entities: &mut RenderVisibleEntities,
2332 just_added_render_visible_entities: bool,
2333 render_mesh_instance_gpu_queues: &RenderGpuCulledEntities,
2334) {
2335 let is_entity_relevant =
2336 |render_layers: &RenderLayers| -> bool { view_render_layers.intersects(render_layers) };
2337
2338 let render_view_visible_mesh_entities = render_visible_entities
2340 .classes
2341 .entry(TypeId::of::<Mesh3d>())
2342 .or_default();
2343
2344 if just_added_render_visible_entities {
2347 for (main_entity, render_layers) in render_mesh_instance_gpu_queues.entities.iter() {
2349 if is_entity_relevant(render_layers) {
2350 render_view_visible_mesh_entities
2353 .entities_gpu_culling
2354 .insert(*main_entity, Entity::PLACEHOLDER);
2355 render_view_visible_mesh_entities
2356 .added_entities
2357 .push((Entity::PLACEHOLDER, *main_entity));
2358 }
2359 }
2360
2361 render_view_visible_mesh_entities.sort_added_entities();
2362 } else {
2363 for main_entity in &render_mesh_instance_gpu_queues.removed {
2365 if render_view_visible_mesh_entities
2366 .entities_gpu_culling
2367 .remove(main_entity)
2368 .is_some()
2369 {
2370 render_view_visible_mesh_entities
2371 .removed_entities
2372 .push((Entity::PLACEHOLDER, *main_entity));
2373 }
2374 }
2375
2376 let mut any_added = false;
2378 for main_entity in &render_mesh_instance_gpu_queues.added {
2379 let maybe_render_layers = render_mesh_instance_gpu_queues.entities.get(main_entity);
2381 if maybe_render_layers.is_none_or(is_entity_relevant) {
2382 render_view_visible_mesh_entities
2385 .entities_gpu_culling
2386 .insert(*main_entity, Entity::PLACEHOLDER);
2387 render_view_visible_mesh_entities
2388 .added_entities
2389 .push((Entity::PLACEHOLDER, *main_entity));
2390 any_added = true;
2391 }
2392 }
2393
2394 for main_entity in &render_mesh_instance_gpu_queues.changed_layers {
2396 let Some(render_layers) = render_mesh_instance_gpu_queues.entities.get(main_entity)
2397 else {
2398 continue;
2399 };
2400
2401 let entity_was_relevant = render_view_visible_mesh_entities
2403 .entities_gpu_culling
2404 .contains_key(main_entity);
2405 let entity_is_relevant = is_entity_relevant(render_layers);
2406 match (entity_was_relevant, entity_is_relevant) {
2407 (false, false) | (true, true) => {
2408 }
2410 (false, true) => {
2411 render_view_visible_mesh_entities
2413 .entities_gpu_culling
2414 .insert(*main_entity, Entity::PLACEHOLDER);
2415 render_view_visible_mesh_entities
2416 .added_entities
2417 .push((Entity::PLACEHOLDER, *main_entity));
2418 any_added = true;
2419 }
2420 (true, false) => {
2421 render_view_visible_mesh_entities
2423 .entities_gpu_culling
2424 .remove(main_entity);
2425 render_view_visible_mesh_entities
2426 .removed_entities
2427 .push((Entity::PLACEHOLDER, *main_entity));
2428 }
2429 }
2430 }
2431
2432 if any_added {
2435 render_view_visible_mesh_entities.sort_added_entities();
2436 }
2437 }
2438}
2439
2440pub fn set_mesh_motion_vector_flags(
2455 mut render_mesh_instances: ResMut<RenderMeshInstances>,
2456 skin_uniforms: Res<SkinUniforms>,
2457 morph_indices: Res<MorphIndices>,
2458) {
2459 for &entity in skin_uniforms.all_skins() {
2462 render_mesh_instances
2463 .insert_mesh_instance_flags(entity, RenderMeshInstanceFlags::HAS_PREVIOUS_SKIN);
2464 }
2465
2466 match *morph_indices {
2469 MorphIndices::Uniform { ref prev, .. } => {
2470 for &entity in prev.keys() {
2471 render_mesh_instances.insert_mesh_instance_flags(
2472 entity,
2473 RenderMeshInstanceFlags::HAS_PREVIOUS_MORPH,
2474 );
2475 }
2476 }
2477 MorphIndices::Storage {
2478 morph_weights_info: ref morph_target_info,
2479 ..
2480 } => {
2481 for (entity, morph_target_info) in morph_target_info {
2482 if morph_target_info.prev_weight_offset.is_some() {
2483 render_mesh_instances.insert_mesh_instance_flags(
2484 *entity,
2485 RenderMeshInstanceFlags::HAS_PREVIOUS_MORPH,
2486 );
2487 }
2488 }
2489 }
2490 }
2491}
2492
2493pub fn collect_meshes_for_gpu_building(
2496 render_mesh_instances: ResMut<RenderMeshInstances>,
2497 batched_instance_buffers: ResMut<
2498 gpu_preprocessing::BatchedInstanceBuffers<MeshUniform, MeshInputUniform>,
2499 >,
2500 mut mesh_culling_data_buffer: ResMut<MeshCullingDataBuffer>,
2501 mut render_mesh_instance_queues: ResMut<RenderMeshInstanceGpuQueues>,
2502 mut render_gpu_culled_entities: ResMut<RenderGpuCulledEntities>,
2503 mesh_allocator: Res<MeshAllocator>,
2504 mesh_material_ids: Res<RenderMaterialInstances>,
2505 render_material_bindings: Res<RenderMaterialBindings>,
2506 render_lightmaps: Res<RenderLightmaps>,
2507 skin_uniforms: Res<SkinUniforms>,
2508 morph_indices: Res<MorphIndices>,
2509 frame_count: Res<FrameCount>,
2510 mut meshes_to_reextract_next_frame: ResMut<MeshesToReextractNextFrame>,
2511) {
2512 let RenderMeshInstances::GpuBuilding(render_mesh_instances) =
2513 render_mesh_instances.into_inner()
2514 else {
2515 return;
2516 };
2517
2518 meshes_to_reextract_next_frame.clear();
2520
2521 let gpu_preprocessing::BatchedInstanceBuffers {
2523 current_input_buffer,
2524 previous_input_buffer,
2525 ..
2526 } = batched_instance_buffers.into_inner();
2527
2528 if !current_input_buffer.is_empty() {
2530 mesh_culling_data_buffer.grow(current_input_buffer.len() as u32);
2531 }
2532
2533 previous_input_buffer.reserve(current_input_buffer.len() as u32);
2535
2536 render_gpu_culled_entities.prepare_for_new_frame();
2539
2540 let (prepared_tx, prepared_rx) = mpsc::channel();
2542 let (reextract_tx, reextract_rx) = mpsc::channel();
2543 let (removed_tx, removed_rx) = mpsc::channel();
2544
2545 {
2546 let mesh_allocator = &mesh_allocator;
2548 let mesh_material_ids = &mesh_material_ids;
2549 let render_material_bindings = &render_material_bindings;
2550 let render_lightmaps = &render_lightmaps;
2551 let skin_uniforms = &skin_uniforms;
2552 let frame_count = *frame_count;
2553 let render_mesh_instances = &*render_mesh_instances;
2554 let current_input_buffer = &*current_input_buffer;
2555 let previous_input_buffer = &*previous_input_buffer;
2556 let mesh_culling_data_buffer = &*mesh_culling_data_buffer;
2557 let morph_indices = &*morph_indices;
2558
2559 ComputeTaskPool::get().scope(|scope| {
2561 for queue in render_mesh_instance_queues.iter_mut() {
2564 match *queue {
2565 RenderMeshInstanceGpuQueue::None => {
2566 }
2568
2569 RenderMeshInstanceGpuQueue::CpuCulling {
2570 ref mut changed,
2571 ref mut removed,
2572 } => {
2573 let prepared_tx = prepared_tx.clone();
2574 let reextract_tx = reextract_tx.clone();
2575 let removed_tx = removed_tx.clone();
2576 scope.spawn(async move {
2577 #[cfg(feature = "trace")]
2578 let _span = info_span!("prepared_mesh_producer").entered();
2579 changed
2580 .drain(..)
2581 .for_each(|(entity, mesh_instance_builder)| {
2582 match mesh_instance_builder.prepare(
2583 entity,
2584 mesh_allocator,
2585 mesh_material_ids,
2586 render_material_bindings,
2587 render_lightmaps,
2588 skin_uniforms,
2589 morph_indices,
2590 frame_count,
2591 ) {
2592 Some(prepared) => {
2593 prepared_tx.send((entity, prepared, None)).ok();
2594 }
2595 None => {
2596 reextract_tx.send(entity).ok();
2597 }
2598 }
2599 });
2600
2601 for entity in removed.drain(..) {
2602 removed_tx.send(entity).unwrap();
2603 }
2604 });
2605 }
2606
2607 RenderMeshInstanceGpuQueue::GpuCulling {
2608 ref mut changed_cpu_culling,
2609 ref mut changed_gpu_culling,
2610 ref mut removed,
2611 } => {
2612 let prepared_tx = prepared_tx.clone();
2613 let reextract_tx = reextract_tx.clone();
2614 let removed_tx = removed_tx.clone();
2615 scope.spawn(async move {
2616 #[cfg(feature = "trace")]
2617 let _span = info_span!("prepared_mesh_producer").entered();
2618 for (entity, mesh_instance_builder, mesh_culling_builder) in
2619 changed_cpu_culling
2620 .drain(..)
2621 .chain(changed_gpu_culling.drain(..))
2622 {
2623 let Some(mut prepared) = mesh_instance_builder.prepare(
2624 entity,
2625 mesh_allocator,
2626 mesh_material_ids,
2627 render_material_bindings,
2628 render_lightmaps,
2629 skin_uniforms,
2630 morph_indices,
2631 frame_count,
2632 ) else {
2633 reextract_tx.send(entity).ok();
2634 continue;
2635 };
2636
2637 let Some(render_mesh_instance) = render_mesh_instances.get(&entity)
2638 else {
2639 let _ = prepared_tx.send((
2643 entity,
2644 prepared,
2645 Some(mesh_culling_builder),
2646 ));
2647 continue;
2648 };
2649
2650 if prepared.render_layers != render_mesh_instance.render_layers
2651 || prepared
2652 .shared
2653 .flags
2654 .contains(RenderMeshInstanceFlags::NO_CPU_CULLING)
2655 != render_mesh_instance
2656 .shared
2657 .flags()
2658 .contains(RenderMeshInstanceFlags::NO_CPU_CULLING)
2659 {
2660 let _ = prepared_tx.send((
2665 entity,
2666 prepared,
2667 Some(mesh_culling_builder),
2668 ));
2669 continue;
2670 }
2671
2672 prepared.shared.write_to_blob(&render_mesh_instance.shared);
2677
2678 let current_uniform_index =
2679 render_mesh_instance.gpu_specific.current_uniform_index();
2680
2681 let previous_world_from_local = current_input_buffer
2682 .get_unchecked(current_uniform_index)
2683 .world_from_local;
2684 let previous_input_index =
2685 previous_input_buffer.push(PreviousMeshInputUniform {
2686 world_from_local: previous_world_from_local,
2687 });
2688 prepared.mesh_input_uniform.previous_input_index =
2689 previous_input_index;
2690
2691 current_input_buffer
2692 .set(current_uniform_index, prepared.mesh_input_uniform);
2693 mesh_culling_data_buffer
2694 .set(current_uniform_index, mesh_culling_builder);
2695 }
2696
2697 for entity in removed.drain(..) {
2698 removed_tx.send(entity).unwrap();
2699 }
2700 });
2701 }
2702 }
2703 }
2704
2705 drop(prepared_tx);
2709 drop(reextract_tx);
2710 drop(removed_tx);
2711 });
2712 }
2713
2714 while let Ok(batch) = prepared_rx.recv() {
2715 let (entity, prepared, mesh_culling_builder) = batch;
2716 let instance_data_index = prepared.update(
2717 entity,
2718 &mut *render_mesh_instances,
2719 current_input_buffer,
2720 previous_input_buffer,
2721 );
2722 if let Some(mesh_culling_data) = mesh_culling_builder {
2723 mesh_culling_data.update(&mut mesh_culling_data_buffer, instance_data_index);
2724 }
2725 let (render_layers, no_cpu_culling) = match render_mesh_instances.get(&entity) {
2728 None => (RenderLayers::default(), false),
2729 Some(render_mesh_instance) => (
2730 render_mesh_instance
2731 .render_layers
2732 .clone()
2733 .unwrap_or_default(),
2734 render_mesh_instance
2735 .shared
2736 .flags()
2737 .contains(RenderMeshInstanceFlags::NO_CPU_CULLING),
2738 ),
2739 };
2740 render_gpu_culled_entities.update(entity, render_layers, no_cpu_culling);
2741 }
2742 while let Ok(batch) = removed_rx.recv() {
2743 let entity = batch;
2744 remove_mesh_input_uniform(entity, &mut *render_mesh_instances, current_input_buffer);
2745 render_gpu_culled_entities.remove(entity);
2746 }
2747 while let Ok(batch) = reextract_rx.recv() {
2748 let entity = batch;
2749 meshes_to_reextract_next_frame.insert(entity);
2750 }
2751 previous_input_buffer.ensure_nonempty();
2753}
2754
2755#[derive(Resource, Clone)]
2757pub struct MeshPipeline {
2758 pub view_layouts: MeshPipelineViewLayouts,
2760 pub clustered_forward_buffer_binding_type: BufferBindingType,
2761 pub mesh_layouts: MeshLayouts,
2762 pub shader: Handle<Shader>,
2764 pub per_object_buffer_batch_size: Option<u32>,
2775
2776 pub binding_arrays_are_usable: bool,
2781
2782 pub clustered_decals_are_usable: bool,
2784
2785 pub skins_use_uniform_buffers: bool,
2788
2789 pub metadata_use_uniform_buffers: bool,
2792}
2793
2794fn init_mesh_pipeline(
2795 mut commands: Commands,
2796 render_device: Res<RenderDevice>,
2797 render_adapter: Res<RenderAdapter>,
2798 view_layouts: Res<MeshPipelineViewLayouts>,
2799 asset_server: Res<AssetServer>,
2800) {
2801 let shader = load_embedded_asset!(asset_server.as_ref(), "mesh.wesl");
2802
2803 let clustered_forward_buffer_binding_type =
2804 render_device.get_supported_read_only_binding_type(CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT);
2805
2806 let res = MeshPipeline {
2807 view_layouts: view_layouts.clone(),
2808 clustered_forward_buffer_binding_type,
2809 mesh_layouts: MeshLayouts::new(&render_device, &render_adapter),
2810 shader,
2811 per_object_buffer_batch_size: GpuArrayBuffer::<MeshUniform>::batch_size(
2812 &render_device.limits(),
2813 ),
2814 binding_arrays_are_usable: binding_arrays_are_usable(&render_device, &render_adapter),
2815 clustered_decals_are_usable: decal::clustered::clustered_decals_are_usable(
2816 &render_device,
2817 &render_adapter,
2818 ),
2819 skins_use_uniform_buffers: skins_use_uniform_buffers(&render_device.limits()),
2820 metadata_use_uniform_buffers: bevy_render::storage_buffers_are_unsupported(
2821 &render_device.limits(),
2822 ),
2823 };
2824
2825 commands.insert_resource(res);
2826}
2827
2828impl MeshPipeline {
2829 pub fn get_view_layout(&self, layout_key: MeshPipelineViewLayoutKey) -> MeshPipelineViewLayout {
2830 self.view_layouts.get_view_layout(layout_key)
2831 }
2832}
2833
2834pub fn build_dummy_white_gpu_image(
2836 render_device: Res<RenderDevice>,
2837 default_sampler: Res<DefaultImageSampler>,
2838 render_queue: Res<RenderQueue>,
2839) -> GpuImage {
2840 let image = Image::default();
2841 let texture = render_device.create_texture(&image.texture_descriptor);
2842 let sampler = (**default_sampler).clone();
2844
2845 if let Ok(format_size) = image.texture_descriptor.format.pixel_size() {
2846 render_queue.write_texture(
2847 texture.as_image_copy(),
2848 image.data.as_ref().expect("Image was created without data"),
2849 TexelCopyBufferLayout {
2850 offset: 0,
2851 bytes_per_row: Some(image.width() * format_size as u32),
2852 rows_per_image: None,
2853 },
2854 image.texture_descriptor.size,
2855 );
2856 }
2857
2858 let texture_view = texture.create_view(&TextureViewDescriptor::default());
2859 GpuImage {
2860 texture,
2861 texture_view,
2862 sampler,
2863 texture_descriptor: image.texture_descriptor,
2864 texture_view_descriptor: image.texture_view_descriptor,
2865 had_data: true,
2866 }
2867}
2868
2869pub fn get_image_texture<'a>(
2870 dummy_white_gpu_image: &'a GpuImage,
2871 gpu_images: &'a RenderAssets<GpuImage>,
2872 handle_option: &Option<Handle<Image>>,
2873) -> Option<(&'a TextureView, &'a Sampler)> {
2874 if let Some(handle) = handle_option {
2875 let gpu_image = gpu_images.get(handle)?;
2876 Some((&gpu_image.texture_view, &gpu_image.sampler))
2877 } else {
2878 Some((
2879 &dummy_white_gpu_image.texture_view,
2880 &dummy_white_gpu_image.sampler,
2881 ))
2882 }
2883}
2884
2885#[derive(Clone, Copy, PartialEq)]
2887pub struct MeshBatchSetCompareData {
2888 material_bind_group_index: MaterialBindGroupIndex,
2890 mesh_slabs: MeshSlabs,
2892 lightmap_slab: Option<NonMaxU32>,
2894}
2895
2896impl GetBatchData for MeshPipeline {
2897 type Param = (
2898 SRes<RenderMeshInstances>,
2899 SRes<RenderLightmaps>,
2900 SRes<MeshAllocator>,
2901 SRes<SkinUniforms>,
2902 SRes<MorphIndices>,
2903 );
2904 type BatchSetCompareData = MeshBatchSetCompareData;
2905 type BatchCompareData = AssetId<Mesh>;
2906
2907 type BufferData = MeshUniform;
2908
2909 fn get_batch_data(
2910 (mesh_instances, lightmaps, mesh_allocator, skin_uniforms, morph_indices): &SystemParamItem<
2911 Self::Param,
2912 >,
2913 (_entity, main_entity): (Entity, MainEntity),
2914 ) -> Option<(
2915 Self::BufferData,
2916 Option<(Self::BatchSetCompareData, Self::BatchCompareData)>,
2917 )> {
2918 let RenderMeshInstances::CpuBuilding(ref mesh_instances) = **mesh_instances else {
2919 error!(
2920 "`get_batch_data` should never be called in GPU mesh uniform \
2921 building mode"
2922 );
2923 return None;
2924 };
2925 let mesh_instance = mesh_instances.get(&main_entity)?;
2926 let mesh_asset_id = mesh_instance.mesh_asset_id();
2927 let first_vertex_index = match mesh_allocator.mesh_vertex_slice(&mesh_asset_id) {
2928 Some(mesh_vertex_slice) => mesh_vertex_slice.range.start,
2929 None => 0,
2930 };
2931 let mesh_slabs = mesh_allocator.mesh_slabs(&mesh_asset_id)?;
2932 let maybe_lightmap = lightmaps.render_lightmaps.get(&main_entity);
2933
2934 let current_skin_index = skin_uniforms.skin_index(main_entity);
2935 let morph_descriptor_index = morph_indices.morph_descriptor_index(main_entity);
2936 let material_bind_group_index = mesh_instance.material_bindings_index();
2937
2938 let metadata_index = mesh_allocator
2939 .mesh_metadata_slice(&mesh_asset_id)
2940 .map(|mesh_metadata_slice| mesh_metadata_slice.range.start);
2941
2942 Some((
2943 MeshUniform::new(
2944 &mesh_instance.transforms,
2945 first_vertex_index,
2946 material_bind_group_index.slot,
2947 maybe_lightmap.map(|lightmap| (lightmap.slot_index, lightmap.uv_rect)),
2948 current_skin_index,
2949 morph_descriptor_index,
2950 Some(mesh_instance.tag()),
2951 metadata_index,
2952 ),
2953 mesh_instance.should_batch().then_some((
2954 MeshBatchSetCompareData {
2955 material_bind_group_index: material_bind_group_index.group,
2956 mesh_slabs,
2957 lightmap_slab: maybe_lightmap.map(|lightmap| lightmap.slab_index.0),
2958 },
2959 mesh_instance.mesh_asset_id(),
2960 )),
2961 ))
2962 }
2963}
2964
2965impl GetFullBatchData for MeshPipeline {
2966 type BufferInputData = MeshInputUniform;
2967
2968 fn get_index_and_compare_data(
2969 (mesh_instances, lightmaps, mesh_allocator, _, _): &SystemParamItem<Self::Param>,
2970 main_entity: MainEntity,
2971 ) -> Option<(
2972 NonMaxU32,
2973 Option<(Self::BatchSetCompareData, Self::BatchCompareData)>,
2974 )> {
2975 let RenderMeshInstances::GpuBuilding(ref mesh_instances) = **mesh_instances else {
2977 error!(
2978 "`get_index_and_compare_data` should never be called in CPU mesh uniform building \
2979 mode"
2980 );
2981 return None;
2982 };
2983
2984 let mesh_instance = mesh_instances.get(&main_entity)?;
2985 let mesh_slabs = mesh_allocator.mesh_slabs(&mesh_instance.mesh_asset_id())?;
2986 let maybe_lightmap = lightmaps.render_lightmaps.get(&main_entity);
2987 let material_bind_group_index = mesh_instance.material_bindings_index();
2988
2989 Some((
2990 NonMaxU32::new(mesh_instance.gpu_specific.current_uniform_index())?,
2991 mesh_instance.should_batch().then_some((
2992 MeshBatchSetCompareData {
2993 material_bind_group_index: material_bind_group_index.group,
2994 mesh_slabs,
2995 lightmap_slab: maybe_lightmap.map(|lightmap| lightmap.slab_index.0),
2996 },
2997 mesh_instance.mesh_asset_id(),
2998 )),
2999 ))
3000 }
3001
3002 fn get_binned_batch_data(
3003 (mesh_instances, lightmaps, mesh_allocator, skin_uniforms, morph_indices): &SystemParamItem<
3004 Self::Param,
3005 >,
3006 main_entity: MainEntity,
3007 ) -> Option<Self::BufferData> {
3008 let RenderMeshInstances::CpuBuilding(ref mesh_instances) = **mesh_instances else {
3009 error!(
3010 "`get_binned_batch_data` should never be called in GPU mesh uniform building mode"
3011 );
3012 return None;
3013 };
3014 let mesh_instance = mesh_instances.get(&main_entity)?;
3015 let mesh_asset_id = mesh_instance.mesh_asset_id();
3016 let first_vertex_index = match mesh_allocator.mesh_vertex_slice(&mesh_asset_id) {
3017 Some(mesh_vertex_slice) => mesh_vertex_slice.range.start,
3018 None => 0,
3019 };
3020 let maybe_lightmap = lightmaps.render_lightmaps.get(&main_entity);
3021
3022 let current_skin_index = skin_uniforms.skin_index(main_entity);
3023 let morph_descriptor_index = morph_indices.morph_descriptor_index(main_entity);
3024
3025 let metadata_index = mesh_allocator
3026 .mesh_metadata_slice(&mesh_asset_id)
3027 .map(|mesh_metadata_slice| mesh_metadata_slice.range.start);
3028
3029 Some(MeshUniform::new(
3030 &mesh_instance.transforms,
3031 first_vertex_index,
3032 mesh_instance.material_bindings_index().slot,
3033 maybe_lightmap.map(|lightmap| (lightmap.slot_index, lightmap.uv_rect)),
3034 current_skin_index,
3035 morph_descriptor_index,
3036 Some(mesh_instance.tag()),
3037 metadata_index,
3038 ))
3039 }
3040
3041 fn get_binned_index(
3042 (mesh_instances, _, _, _, _): &SystemParamItem<Self::Param>,
3043 main_entity: MainEntity,
3044 ) -> Option<NonMaxU32> {
3045 let RenderMeshInstances::GpuBuilding(ref mesh_instances) = **mesh_instances else {
3047 error!(
3048 "`get_binned_index` should never be called in CPU mesh uniform \
3049 building mode"
3050 );
3051 return None;
3052 };
3053
3054 mesh_instances
3055 .get(&main_entity)
3056 .and_then(|entity| NonMaxU32::new(entity.gpu_specific.current_uniform_index()))
3057 }
3058
3059 fn write_batch_indirect_parameters_metadata(
3060 indexed: bool,
3061 base_output_index: u32,
3062 batch_set_index: Option<NonMaxU32>,
3063 phase_indirect_parameters_buffers: &mut UntypedPhaseIndirectParametersBuffers,
3064 indirect_parameters_offset: u32,
3065 ) {
3066 let indirect_parameters = IndirectParametersMetadata {
3067 base_output_index,
3068 batch_set_index: match batch_set_index {
3069 Some(batch_set_index) => u32::from(batch_set_index),
3070 None => !0,
3071 },
3072 mesh_index: 0,
3074 early_instance_count: 0,
3075 late_instance_count: 0,
3076 };
3077
3078 if indexed {
3079 phase_indirect_parameters_buffers
3080 .indexed
3081 .set(indirect_parameters_offset, indirect_parameters);
3082 } else {
3083 phase_indirect_parameters_buffers
3084 .non_indexed
3085 .set(indirect_parameters_offset, indirect_parameters);
3086 }
3087 }
3088}
3089
3090bitflags::bitflags! {
3091 #[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Hash)]
3092 #[repr(transparent)]
3093 pub struct MeshPipelineKey: u64 {
3096 const NONE = 0;
3098
3099 const MORPH_TARGETS = BaseMeshPipelineKey::MORPH_TARGETS.bits();
3101
3102 const TONEMAP_IN_SHADER = 1 << 0;
3104 const DEBAND_DITHER = 1 << 1;
3105 const DEPTH_PREPASS = 1 << 2;
3106 const NORMAL_PREPASS = 1 << 3;
3107 const DEFERRED_PREPASS = 1 << 4;
3108 const MOTION_VECTOR_PREPASS = 1 << 5;
3109 const MAY_DISCARD = 1 << 6; const ENVIRONMENT_MAP = 1 << 7;
3112 const SCREEN_SPACE_AMBIENT_OCCLUSION = 1 << 8;
3113 const UNCLIPPED_DEPTH_ORTHO = 1 << 9; const TEMPORAL_JITTER = 1 << 10;
3117 const VIEW_TRANSMISSION_TEXTURE = 1 << 11;
3118 const LIGHTMAPPED = 1 << 12;
3119 const LIGHTMAP_BICUBIC_SAMPLING = 1 << 13;
3120 const IRRADIANCE_VOLUME = 1 << 14;
3121 const VISIBILITY_RANGE_DITHER = 1 << 15;
3122 const SCREEN_SPACE_REFLECTIONS = 1 << 16;
3123 const HAS_PREVIOUS_SKIN = 1 << 17;
3124 const HAS_PREVIOUS_MORPH = 1 << 18;
3125 const OIT_ENABLED = 1 << 19;
3126 const DISTANCE_FOG = 1 << 20;
3127 const ATMOSPHERE = 1 << 21;
3128 const INVERT_CULLING = 1 << 22;
3129 const PREPASS_READS_MATERIAL = 1 << 23;
3130 const CONTACT_SHADOWS = 1 << 24;
3131 const LAST_FLAG = Self::CONTACT_SHADOWS.bits();
3132
3133 const ALL_PREPASS_BITS = Self::DEPTH_PREPASS.bits()
3134 | Self::NORMAL_PREPASS.bits()
3135 | Self::DEFERRED_PREPASS.bits()
3136 | Self::MOTION_VECTOR_PREPASS.bits()
3137 | Self::MAY_DISCARD.bits()
3138 | Self::PREPASS_READS_MATERIAL.bits();
3139
3140 const MSAA_RESERVED_BITS = Self::MSAA_MASK_BITS << Self::MSAA_SHIFT_BITS;
3142 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;
3149 const TONEMAP_METHOD_LINEAR = 0 << Self::TONEMAP_METHOD_SHIFT_BITS;
3150 const TONEMAP_METHOD_REINHARD = 1 << Self::TONEMAP_METHOD_SHIFT_BITS;
3151 const TONEMAP_METHOD_REINHARD_LUMINANCE = 2 << Self::TONEMAP_METHOD_SHIFT_BITS;
3152 const TONEMAP_METHOD_ACES_FITTED = 3 << Self::TONEMAP_METHOD_SHIFT_BITS;
3153 const TONEMAP_METHOD_AGX = 4 << Self::TONEMAP_METHOD_SHIFT_BITS;
3154 const TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM = 5 << Self::TONEMAP_METHOD_SHIFT_BITS;
3155 const TONEMAP_METHOD_TONY_MC_MAPFACE = 6 << Self::TONEMAP_METHOD_SHIFT_BITS;
3156 const TONEMAP_METHOD_BLENDER_FILMIC = 7 << Self::TONEMAP_METHOD_SHIFT_BITS;
3157 const TONEMAP_METHOD_PBR_NEUTRAL = 8 << Self::TONEMAP_METHOD_SHIFT_BITS;
3158 const SHADOW_FILTER_METHOD_RESERVED_BITS = Self::SHADOW_FILTER_METHOD_MASK_BITS << Self::SHADOW_FILTER_METHOD_SHIFT_BITS;
3159 const SHADOW_FILTER_METHOD_HARDWARE_2X2 = 0 << Self::SHADOW_FILTER_METHOD_SHIFT_BITS;
3160 const SHADOW_FILTER_METHOD_GAUSSIAN = 1 << Self::SHADOW_FILTER_METHOD_SHIFT_BITS;
3161 const SHADOW_FILTER_METHOD_TEMPORAL = 2 << Self::SHADOW_FILTER_METHOD_SHIFT_BITS;
3162 const VIEW_PROJECTION_RESERVED_BITS = Self::VIEW_PROJECTION_MASK_BITS << Self::VIEW_PROJECTION_SHIFT_BITS;
3163 const VIEW_PROJECTION_NONSTANDARD = 0 << Self::VIEW_PROJECTION_SHIFT_BITS;
3164 const VIEW_PROJECTION_PERSPECTIVE = 1 << Self::VIEW_PROJECTION_SHIFT_BITS;
3165 const VIEW_PROJECTION_ORTHOGRAPHIC = 2 << Self::VIEW_PROJECTION_SHIFT_BITS;
3166 const VIEW_PROJECTION_RESERVED = 3 << Self::VIEW_PROJECTION_SHIFT_BITS;
3167 const SCREEN_SPACE_SPECULAR_TRANSMISSION_RESERVED_BITS = Self::SCREEN_SPACE_SPECULAR_TRANSMISSION_MASK_BITS << Self::SCREEN_SPACE_SPECULAR_TRANSMISSION_SHIFT_BITS;
3168 const SCREEN_SPACE_SPECULAR_TRANSMISSION_LOW = 0 << Self::SCREEN_SPACE_SPECULAR_TRANSMISSION_SHIFT_BITS;
3169 const SCREEN_SPACE_SPECULAR_TRANSMISSION_MEDIUM = 1 << Self::SCREEN_SPACE_SPECULAR_TRANSMISSION_SHIFT_BITS;
3170 const SCREEN_SPACE_SPECULAR_TRANSMISSION_HIGH = 2 << Self::SCREEN_SPACE_SPECULAR_TRANSMISSION_SHIFT_BITS;
3171 const SCREEN_SPACE_SPECULAR_TRANSMISSION_ULTRA = 3 << Self::SCREEN_SPACE_SPECULAR_TRANSMISSION_SHIFT_BITS;
3172 const COLOR_TARGET_FORMAT_RESERVED_BITS = Self::COLOR_TARGET_FORMAT_MASK_BITS
3173 << Self::COLOR_TARGET_FORMAT_SHIFT_BITS;
3174 const ALL_RESERVED_BITS =
3175 Self::BLEND_RESERVED_BITS.bits() |
3176 Self::MSAA_RESERVED_BITS.bits() |
3177 Self::TONEMAP_METHOD_RESERVED_BITS.bits() |
3178 Self::SHADOW_FILTER_METHOD_RESERVED_BITS.bits() |
3179 Self::VIEW_PROJECTION_RESERVED_BITS.bits() |
3180 Self::SCREEN_SPACE_SPECULAR_TRANSMISSION_RESERVED_BITS.bits() |
3181 Self::COLOR_TARGET_FORMAT_RESERVED_BITS.bits();
3182 }
3183}
3184
3185impl MeshPipelineKey {
3186 const MSAA_MASK_BITS: u64 = 0b111;
3187 const MSAA_SHIFT_BITS: u64 = Self::LAST_FLAG.bits().trailing_zeros() as u64 + 1;
3188
3189 const BLEND_MASK_BITS: u64 = 0b111;
3190 const BLEND_SHIFT_BITS: u64 = Self::MSAA_MASK_BITS.count_ones() as u64 + Self::MSAA_SHIFT_BITS;
3191
3192 const TONEMAP_METHOD_MASK_BITS: u64 = 0b1111;
3193 const TONEMAP_METHOD_SHIFT_BITS: u64 =
3194 Self::BLEND_MASK_BITS.count_ones() as u64 + Self::BLEND_SHIFT_BITS;
3195
3196 const SHADOW_FILTER_METHOD_MASK_BITS: u64 = 0b11;
3197 const SHADOW_FILTER_METHOD_SHIFT_BITS: u64 =
3198 Self::TONEMAP_METHOD_MASK_BITS.count_ones() as u64 + Self::TONEMAP_METHOD_SHIFT_BITS;
3199
3200 const VIEW_PROJECTION_MASK_BITS: u64 = 0b11;
3201 const VIEW_PROJECTION_SHIFT_BITS: u64 = Self::SHADOW_FILTER_METHOD_MASK_BITS.count_ones()
3202 as u64
3203 + Self::SHADOW_FILTER_METHOD_SHIFT_BITS;
3204
3205 const SCREEN_SPACE_SPECULAR_TRANSMISSION_MASK_BITS: u64 = 0b11;
3206 const SCREEN_SPACE_SPECULAR_TRANSMISSION_SHIFT_BITS: u64 =
3207 Self::VIEW_PROJECTION_MASK_BITS.count_ones() as u64 + Self::VIEW_PROJECTION_SHIFT_BITS;
3208
3209 const COLOR_TARGET_FORMAT_MASK_BITS: u64 = view::COLOR_TARGET_FORMAT_MASK_BITS as u64;
3210 const COLOR_TARGET_FORMAT_SHIFT_BITS: u64 = Self::SCREEN_SPACE_SPECULAR_TRANSMISSION_MASK_BITS
3211 .count_ones() as u64
3212 + Self::SCREEN_SPACE_SPECULAR_TRANSMISSION_SHIFT_BITS;
3213
3214 pub fn from_msaa_samples(msaa_samples: u32) -> Self {
3215 let msaa_bits =
3216 (msaa_samples.trailing_zeros() as u64 & Self::MSAA_MASK_BITS) << Self::MSAA_SHIFT_BITS;
3217 Self::from_bits_retain(msaa_bits)
3218 }
3219
3220 #[inline]
3222 pub fn from_target_format(format: TextureFormat) -> Self {
3223 let code = texture_format_to_code(format)
3224 .expect("Texture format is not supported by the pipeline") as u64;
3225 Self::from_bits_retain(
3226 (code & Self::COLOR_TARGET_FORMAT_MASK_BITS) << Self::COLOR_TARGET_FORMAT_SHIFT_BITS,
3227 )
3228 }
3229
3230 #[inline]
3232 pub fn target_format(&self) -> TextureFormat {
3233 let code = ((self.bits() >> Self::COLOR_TARGET_FORMAT_SHIFT_BITS)
3234 & Self::COLOR_TARGET_FORMAT_MASK_BITS) as u8;
3235 texture_format_from_code(code)
3236 .expect("Unknown bits in `COLOR_TARGET_FORMAT_MASK_BITS` of the pipeline key")
3237 }
3238
3239 pub fn msaa_samples(&self) -> u32 {
3240 1 << ((self.bits() >> Self::MSAA_SHIFT_BITS) & Self::MSAA_MASK_BITS)
3241 }
3242
3243 pub fn from_primitive_topology_and_strip_index(
3247 primitive_topology: PrimitiveTopology,
3248 strip_index_format: Option<IndexFormat>,
3249 ) -> Self {
3250 let index_bits = if primitive_topology.is_strip() {
3251 match strip_index_format {
3252 None => BaseMeshPipelineKey::STRIP_INDEX_FORMAT_NONE,
3253 Some(indices) => match indices {
3254 IndexFormat::Uint16 => BaseMeshPipelineKey::STRIP_INDEX_FORMAT_U16,
3255 IndexFormat::Uint32 => BaseMeshPipelineKey::STRIP_INDEX_FORMAT_U32,
3256 },
3257 }
3258 } else {
3259 BaseMeshPipelineKey::STRIP_INDEX_FORMAT_NONE
3260 }
3261 .bits();
3262 let primitive_topology_bits = ((primitive_topology as u64)
3263 & BaseMeshPipelineKey::PRIMITIVE_TOPOLOGY_MASK_BITS)
3264 << BaseMeshPipelineKey::PRIMITIVE_TOPOLOGY_SHIFT_BITS;
3265 Self::from_bits_retain(primitive_topology_bits | index_bits)
3266 }
3267
3268 pub fn primitive_topology(&self) -> PrimitiveTopology {
3269 let primitive_topology_bits = (self.bits()
3270 >> BaseMeshPipelineKey::PRIMITIVE_TOPOLOGY_SHIFT_BITS)
3271 & BaseMeshPipelineKey::PRIMITIVE_TOPOLOGY_MASK_BITS;
3272 match primitive_topology_bits {
3273 x if x == PrimitiveTopology::PointList as u64 => PrimitiveTopology::PointList,
3274 x if x == PrimitiveTopology::LineList as u64 => PrimitiveTopology::LineList,
3275 x if x == PrimitiveTopology::LineStrip as u64 => PrimitiveTopology::LineStrip,
3276 x if x == PrimitiveTopology::TriangleList as u64 => PrimitiveTopology::TriangleList,
3277 x if x == PrimitiveTopology::TriangleStrip as u64 => PrimitiveTopology::TriangleStrip,
3278 _ => PrimitiveTopology::default(),
3279 }
3280 }
3281
3282 pub fn strip_index_format(&self) -> Option<IndexFormat> {
3283 let index_bits = self.bits() & BaseMeshPipelineKey::STRIP_INDEX_FORMAT_RESERVED_BITS.bits();
3284 match index_bits {
3285 x if x == BaseMeshPipelineKey::STRIP_INDEX_FORMAT_U16.bits() => {
3286 Some(IndexFormat::Uint16)
3287 }
3288 x if x == BaseMeshPipelineKey::STRIP_INDEX_FORMAT_U32.bits() => {
3289 Some(IndexFormat::Uint32)
3290 }
3291 x if x == BaseMeshPipelineKey::STRIP_INDEX_FORMAT_NONE.bits() => None,
3292 _ => unreachable!(),
3293 }
3294 }
3295}
3296
3297impl From<u64> for MeshPipelineKey {
3298 fn from(value: u64) -> Self {
3299 MeshPipelineKey::from_bits_retain(value)
3300 }
3301}
3302
3303impl From<MeshPipelineKey> for u64 {
3304 fn from(value: MeshPipelineKey) -> Self {
3305 value.bits()
3306 }
3307}
3308
3309const_assert_eq!(
3311 (((MeshPipelineKey::LAST_FLAG.bits() << 1) - 1) | MeshPipelineKey::ALL_RESERVED_BITS.bits())
3312 & BaseMeshPipelineKey::all().bits(),
3313 0
3314);
3315
3316const_assert_eq!(
3319 BaseMeshPipelineKey::all().bits() & MeshPipelineKey::all().bits(),
3320 MeshPipelineKey::MORPH_TARGETS.bits()
3321);
3322
3323fn is_skinned(layout: &MeshVertexBufferLayoutRef) -> bool {
3324 layout.0.contains(Mesh::ATTRIBUTE_JOINT_INDEX)
3325 && layout.0.contains(Mesh::ATTRIBUTE_JOINT_WEIGHT)
3326}
3327pub fn setup_morph_and_skinning_defs(
3328 mesh_layouts: &MeshLayouts,
3329 layout: &MeshVertexBufferLayoutRef,
3330 offset: u32,
3331 key: &MeshPipelineKey,
3332 shader_defs: &mut Vec<ShaderDefVal>,
3333 vertex_attributes: &mut Vec<VertexAttributeDescriptor>,
3334 skins_use_uniform_buffers: bool,
3335) -> BindGroupLayoutDescriptor {
3336 let is_morphed = key.intersects(MeshPipelineKey::MORPH_TARGETS);
3337 let is_lightmapped = key.intersects(MeshPipelineKey::LIGHTMAPPED);
3338 let motion_vector_prepass = key.intersects(MeshPipelineKey::MOTION_VECTOR_PREPASS);
3339
3340 if skins_use_uniform_buffers {
3341 shader_defs.push("SKINS_USE_UNIFORM_BUFFERS".into());
3342 }
3343
3344 let mut add_skin_data = || {
3345 shader_defs.push("SKINNED".into());
3346 vertex_attributes.push(Mesh::ATTRIBUTE_JOINT_INDEX.at_shader_location(offset));
3347 vertex_attributes.push(Mesh::ATTRIBUTE_JOINT_WEIGHT.at_shader_location(offset + 1));
3348 };
3349
3350 match (
3351 is_skinned(layout),
3352 is_morphed,
3353 is_lightmapped,
3354 motion_vector_prepass,
3355 ) {
3356 (true, false, _, true) => {
3357 add_skin_data();
3358 mesh_layouts.skinned_motion.clone()
3359 }
3360 (true, false, _, false) => {
3361 add_skin_data();
3362 mesh_layouts.skinned.clone()
3363 }
3364 (true, true, _, true) => {
3365 add_skin_data();
3366 shader_defs.push("MORPH_TARGETS".into());
3367 mesh_layouts.morphed_skinned_motion.clone()
3368 }
3369 (true, true, _, false) => {
3370 add_skin_data();
3371 shader_defs.push("MORPH_TARGETS".into());
3372 mesh_layouts.morphed_skinned.clone()
3373 }
3374 (false, true, _, true) => {
3375 shader_defs.push("MORPH_TARGETS".into());
3376 mesh_layouts.morphed_motion.clone()
3377 }
3378 (false, true, _, false) => {
3379 shader_defs.push("MORPH_TARGETS".into());
3380 mesh_layouts.morphed.clone()
3381 }
3382 (false, false, true, _) => mesh_layouts.lightmapped.clone(),
3383 (false, false, false, _) => mesh_layouts.model_only.clone(),
3384 }
3385}
3386
3387impl SpecializedMeshPipeline for MeshPipeline {
3388 type Key = MeshPipelineKey;
3389
3390 fn specialize(
3391 &self,
3392 key: Self::Key,
3393 layout: &MeshVertexBufferLayoutRef,
3394 ) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
3395 let mut shader_defs = Vec::new();
3396 let mut vertex_attributes = Vec::new();
3397
3398 shader_defs.push("MESH_PIPELINE".into());
3400
3401 shader_defs.push("VERTEX_OUTPUT_INSTANCE_INDEX".into());
3402
3403 if layout.0.contains(Mesh::ATTRIBUTE_POSITION) {
3404 shader_defs.push("VERTEX_POSITIONS".into());
3405 if layout
3406 .0
3407 .get_attribute_compression()
3408 .contains(MeshAttributeCompressionFlags::COMPRESS_POSITION)
3409 {
3410 shader_defs.push("VERTEX_POSITIONS_COMPRESSED".into());
3411 }
3412 vertex_attributes.push(Mesh::ATTRIBUTE_POSITION.at_shader_location(0));
3413 }
3414
3415 if layout.0.contains(Mesh::ATTRIBUTE_NORMAL) {
3416 shader_defs.push("VERTEX_NORMALS".into());
3417 if layout
3418 .0
3419 .get_attribute_compression()
3420 .contains(MeshAttributeCompressionFlags::COMPRESS_NORMAL)
3421 {
3422 shader_defs.push("VERTEX_NORMALS_COMPRESSED".into());
3423 }
3424 vertex_attributes.push(Mesh::ATTRIBUTE_NORMAL.at_shader_location(1));
3425 }
3426
3427 if layout.0.contains(Mesh::ATTRIBUTE_UV_0) {
3428 shader_defs.push("VERTEX_UVS".into());
3429 shader_defs.push("VERTEX_UVS_A".into());
3430 if layout
3431 .0
3432 .get_attribute_compression()
3433 .contains(MeshAttributeCompressionFlags::COMPRESS_UV0)
3434 {
3435 shader_defs.push("VERTEX_UVS_A_COMPRESSED".into());
3436 }
3437 vertex_attributes.push(Mesh::ATTRIBUTE_UV_0.at_shader_location(2));
3438 }
3439
3440 if layout.0.contains(Mesh::ATTRIBUTE_UV_1) {
3441 shader_defs.push("VERTEX_UVS".into());
3442 shader_defs.push("VERTEX_UVS_B".into());
3443 if layout
3444 .0
3445 .get_attribute_compression()
3446 .contains(MeshAttributeCompressionFlags::COMPRESS_UV1)
3447 {
3448 shader_defs.push("VERTEX_UVS_B_COMPRESSED".into());
3449 }
3450 vertex_attributes.push(Mesh::ATTRIBUTE_UV_1.at_shader_location(3));
3451 }
3452
3453 if layout.0.contains(Mesh::ATTRIBUTE_TANGENT) {
3454 shader_defs.push("VERTEX_TANGENTS".into());
3455 if layout
3456 .0
3457 .get_attribute_compression()
3458 .contains(MeshAttributeCompressionFlags::COMPRESS_TANGENT)
3459 {
3460 shader_defs.push("VERTEX_TANGENTS_COMPRESSED".into());
3461 }
3462 vertex_attributes.push(Mesh::ATTRIBUTE_TANGENT.at_shader_location(4));
3463 }
3464
3465 if layout.0.contains(Mesh::ATTRIBUTE_COLOR) {
3466 shader_defs.push("VERTEX_COLORS".into());
3467 vertex_attributes.push(Mesh::ATTRIBUTE_COLOR.at_shader_location(5));
3468 }
3469
3470 if cfg!(feature = "pbr_transmission_textures") {
3471 shader_defs.push("PBR_TRANSMISSION_TEXTURES_SUPPORTED".into());
3472 }
3473 if cfg!(feature = "pbr_multi_layer_material_textures") {
3474 shader_defs.push("PBR_MULTI_LAYER_MATERIAL_TEXTURES_SUPPORTED".into());
3475 }
3476 if cfg!(feature = "pbr_anisotropy_texture") {
3477 shader_defs.push("PBR_ANISOTROPY_TEXTURE_SUPPORTED".into());
3478 }
3479 if cfg!(feature = "pbr_specular_textures") {
3480 shader_defs.push("PBR_SPECULAR_TEXTURES_SUPPORTED".into());
3481 }
3482 if cfg!(feature = "bluenoise_texture") {
3483 shader_defs.push("BLUE_NOISE_TEXTURE".into());
3484 }
3485 if cfg!(feature = "dfg_lut") {
3486 shader_defs.push("DFG_LUT".into());
3487 }
3488 if cfg!(feature = "area_light_luts") {
3489 shader_defs.push("AREA_LIGHT_LUTS".into());
3490 }
3491
3492 let bind_group_layout = self.get_view_layout(key.into());
3493 let mut bind_group_layout = vec![
3494 bind_group_layout.main_layout.clone(),
3495 bind_group_layout.binding_array_layout.clone(),
3496 ];
3497
3498 if key.msaa_samples() > 1 {
3499 shader_defs.push("MULTISAMPLED".into());
3500 };
3501
3502 if self.metadata_use_uniform_buffers {
3503 shader_defs.push("METADATA_USE_UNIFORM_BUFFERS".into());
3504 }
3505
3506 bind_group_layout.push(setup_morph_and_skinning_defs(
3507 &self.mesh_layouts,
3508 layout,
3509 6,
3510 &key,
3511 &mut shader_defs,
3512 &mut vertex_attributes,
3513 self.skins_use_uniform_buffers,
3514 ));
3515
3516 if key.contains(MeshPipelineKey::CONTACT_SHADOWS) {
3517 shader_defs.push("CONTACT_SHADOWS".into());
3518 }
3519
3520 if key.contains(MeshPipelineKey::OIT_ENABLED) {
3521 shader_defs.push("OIT_ENABLED".into());
3522 }
3523
3524 let vertex_buffer_layout = layout.0.get_layout(&vertex_attributes)?;
3525
3526 let (label, blend, depth_write_enabled);
3527 let pass = key.intersection(MeshPipelineKey::BLEND_RESERVED_BITS);
3528 let mut alpha_to_coverage_enabled = false;
3529 if pass == MeshPipelineKey::BLEND_ALPHA {
3530 label = "alpha_blend_mesh_pipeline".into();
3531 blend = Some(BlendState::ALPHA_BLENDING);
3532 depth_write_enabled = false;
3535 } else if pass == MeshPipelineKey::BLEND_PREMULTIPLIED_ALPHA {
3536 label = "premultiplied_alpha_mesh_pipeline".into();
3537 blend = Some(BlendState::PREMULTIPLIED_ALPHA_BLENDING);
3538 shader_defs.push("PREMULTIPLY_ALPHA".into());
3539 shader_defs.push("BLEND_PREMULTIPLIED_ALPHA".into());
3540 depth_write_enabled = false;
3543 } else if pass == MeshPipelineKey::BLEND_MULTIPLY {
3544 label = "multiply_mesh_pipeline".into();
3545 blend = Some(BlendState {
3546 color: BlendComponent {
3547 src_factor: BlendFactor::Dst,
3548 dst_factor: BlendFactor::OneMinusSrcAlpha,
3549 operation: BlendOperation::Add,
3550 },
3551 alpha: BlendComponent::OVER,
3552 });
3553 shader_defs.push("PREMULTIPLY_ALPHA".into());
3554 shader_defs.push("BLEND_MULTIPLY".into());
3555 depth_write_enabled = false;
3558 } else if pass == MeshPipelineKey::BLEND_ALPHA_TO_COVERAGE {
3559 label = "alpha_to_coverage_mesh_pipeline".into();
3560 blend = None;
3562 depth_write_enabled = true;
3566 alpha_to_coverage_enabled = true;
3567 shader_defs.push("ALPHA_TO_COVERAGE".into());
3568 } else {
3569 label = "opaque_mesh_pipeline".into();
3570 blend = None;
3572 depth_write_enabled = true;
3576 }
3577
3578 if key.contains(MeshPipelineKey::NORMAL_PREPASS) {
3579 shader_defs.push("NORMAL_PREPASS".into());
3580 }
3581
3582 if key.contains(MeshPipelineKey::DEPTH_PREPASS) {
3583 shader_defs.push("DEPTH_PREPASS".into());
3584 }
3585
3586 if key.contains(MeshPipelineKey::SCREEN_SPACE_AMBIENT_OCCLUSION) && depth_write_enabled {
3588 shader_defs.push("SCREEN_SPACE_AMBIENT_OCCLUSION".into());
3589 }
3590
3591 if key.contains(MeshPipelineKey::MOTION_VECTOR_PREPASS) {
3592 shader_defs.push("MOTION_VECTOR_PREPASS".into());
3593 }
3594
3595 if key.contains(MeshPipelineKey::HAS_PREVIOUS_SKIN) {
3596 shader_defs.push("HAS_PREVIOUS_SKIN".into());
3597 }
3598
3599 if key.contains(MeshPipelineKey::HAS_PREVIOUS_MORPH) {
3600 shader_defs.push("HAS_PREVIOUS_MORPH".into());
3601 }
3602
3603 if key.contains(MeshPipelineKey::DEFERRED_PREPASS) {
3604 shader_defs.push("DEFERRED_PREPASS".into());
3605 }
3606
3607 if key.contains(MeshPipelineKey::NORMAL_PREPASS) && key.msaa_samples() == 1 {
3608 shader_defs.push("LOAD_PREPASS_NORMALS".into());
3609 }
3610
3611 let view_projection = key.intersection(MeshPipelineKey::VIEW_PROJECTION_RESERVED_BITS);
3612 if view_projection == MeshPipelineKey::VIEW_PROJECTION_NONSTANDARD {
3613 shader_defs.push("VIEW_PROJECTION_NONSTANDARD".into());
3614 } else if view_projection == MeshPipelineKey::VIEW_PROJECTION_PERSPECTIVE {
3615 shader_defs.push("VIEW_PROJECTION_PERSPECTIVE".into());
3616 } else if view_projection == MeshPipelineKey::VIEW_PROJECTION_ORTHOGRAPHIC {
3617 shader_defs.push("VIEW_PROJECTION_ORTHOGRAPHIC".into());
3618 }
3619
3620 #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
3621 shader_defs.push("WEBGL2".into());
3622
3623 #[cfg(feature = "experimental_pbr_pcss")]
3624 shader_defs.push("PCSS_SAMPLERS_AVAILABLE".into());
3625
3626 if key.contains(MeshPipelineKey::TONEMAP_IN_SHADER) {
3627 shader_defs.push("TONEMAP_IN_SHADER".into());
3628 shader_defs.push(ShaderDefVal::UInt(
3629 "TONEMAPPING_LUT_TEXTURE_BINDING_INDEX".into(),
3630 TONEMAPPING_LUT_TEXTURE_BINDING_INDEX,
3631 ));
3632 shader_defs.push(ShaderDefVal::UInt(
3633 "TONEMAPPING_LUT_SAMPLER_BINDING_INDEX".into(),
3634 TONEMAPPING_LUT_SAMPLER_BINDING_INDEX,
3635 ));
3636
3637 let method = key.intersection(MeshPipelineKey::TONEMAP_METHOD_RESERVED_BITS);
3638
3639 if method == MeshPipelineKey::TONEMAP_METHOD_LINEAR {
3640 shader_defs.push("TONEMAP_METHOD_LINEAR".into());
3641 } else if method == MeshPipelineKey::TONEMAP_METHOD_REINHARD {
3642 shader_defs.push("TONEMAP_METHOD_REINHARD".into());
3643 } else if method == MeshPipelineKey::TONEMAP_METHOD_REINHARD_LUMINANCE {
3644 shader_defs.push("TONEMAP_METHOD_REINHARD_LUMINANCE".into());
3645 } else if method == MeshPipelineKey::TONEMAP_METHOD_ACES_FITTED {
3646 shader_defs.push("TONEMAP_METHOD_ACES_FITTED".into());
3647 } else if method == MeshPipelineKey::TONEMAP_METHOD_AGX {
3648 shader_defs.push("TONEMAP_METHOD_AGX".into());
3649 } else if method == MeshPipelineKey::TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM {
3650 shader_defs.push("TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM".into());
3651 } else if method == MeshPipelineKey::TONEMAP_METHOD_BLENDER_FILMIC {
3652 shader_defs.push("TONEMAP_METHOD_BLENDER_FILMIC".into());
3653 } else if method == MeshPipelineKey::TONEMAP_METHOD_TONY_MC_MAPFACE {
3654 shader_defs.push("TONEMAP_METHOD_TONY_MC_MAPFACE".into());
3655 } else if method == MeshPipelineKey::TONEMAP_METHOD_PBR_NEUTRAL {
3656 shader_defs.push("TONEMAP_METHOD_PBR_NEUTRAL".into());
3657 }
3658
3659 if key.contains(MeshPipelineKey::DEBAND_DITHER) {
3661 shader_defs.push("DEBAND_DITHER".into());
3662 }
3663 }
3664
3665 if key.contains(MeshPipelineKey::MAY_DISCARD) {
3666 shader_defs.push("MAY_DISCARD".into());
3667 }
3668
3669 if key.contains(MeshPipelineKey::ENVIRONMENT_MAP) {
3670 shader_defs.push("ENVIRONMENT_MAP".into());
3671 }
3672
3673 if key.contains(MeshPipelineKey::IRRADIANCE_VOLUME) && IRRADIANCE_VOLUMES_ARE_USABLE {
3674 shader_defs.push("IRRADIANCE_VOLUME".into());
3675 }
3676
3677 if key.contains(MeshPipelineKey::LIGHTMAPPED) {
3678 shader_defs.push("LIGHTMAP".into());
3679 }
3680 if key.contains(MeshPipelineKey::LIGHTMAP_BICUBIC_SAMPLING) {
3681 shader_defs.push("LIGHTMAP_BICUBIC_SAMPLING".into());
3682 }
3683
3684 if key.contains(MeshPipelineKey::TEMPORAL_JITTER) {
3685 shader_defs.push("TEMPORAL_JITTER".into());
3686 }
3687
3688 let shadow_filter_method =
3689 key.intersection(MeshPipelineKey::SHADOW_FILTER_METHOD_RESERVED_BITS);
3690 if shadow_filter_method == MeshPipelineKey::SHADOW_FILTER_METHOD_HARDWARE_2X2 {
3691 shader_defs.push("SHADOW_FILTER_METHOD_HARDWARE_2X2".into());
3692 } else if shadow_filter_method == MeshPipelineKey::SHADOW_FILTER_METHOD_GAUSSIAN {
3693 shader_defs.push("SHADOW_FILTER_METHOD_GAUSSIAN".into());
3694 } else if shadow_filter_method == MeshPipelineKey::SHADOW_FILTER_METHOD_TEMPORAL {
3695 shader_defs.push("SHADOW_FILTER_METHOD_TEMPORAL".into());
3696 }
3697
3698 if key.contains(MeshPipelineKey::VIEW_TRANSMISSION_TEXTURE) {
3699 shader_defs.push("VIEW_TRANSMISSION_TEXTURE".into());
3700 }
3701
3702 let blur_quality =
3703 key.intersection(MeshPipelineKey::SCREEN_SPACE_SPECULAR_TRANSMISSION_RESERVED_BITS);
3704
3705 shader_defs.push(ShaderDefVal::Int(
3706 "SCREEN_SPACE_SPECULAR_TRANSMISSION_BLUR_TAPS".into(),
3707 match blur_quality {
3708 MeshPipelineKey::SCREEN_SPACE_SPECULAR_TRANSMISSION_LOW => 4,
3709 MeshPipelineKey::SCREEN_SPACE_SPECULAR_TRANSMISSION_MEDIUM => 8,
3710 MeshPipelineKey::SCREEN_SPACE_SPECULAR_TRANSMISSION_HIGH => 16,
3711 MeshPipelineKey::SCREEN_SPACE_SPECULAR_TRANSMISSION_ULTRA => 32,
3712 _ => unreachable!(), },
3714 ));
3715
3716 if key.contains(MeshPipelineKey::VISIBILITY_RANGE_DITHER) {
3717 shader_defs.push("VISIBILITY_RANGE_DITHER".into());
3718 }
3719
3720 if key.contains(MeshPipelineKey::DISTANCE_FOG) {
3721 shader_defs.push("DISTANCE_FOG".into());
3722 }
3723
3724 if key.contains(MeshPipelineKey::ATMOSPHERE) {
3725 shader_defs.push("ATMOSPHERE".into());
3726 }
3727
3728 if self.binding_arrays_are_usable {
3729 shader_defs.push("MULTIPLE_LIGHT_PROBES_IN_ARRAY".into());
3730 shader_defs.push("MULTIPLE_LIGHTMAPS_IN_ARRAY".into());
3731 }
3732
3733 if IRRADIANCE_VOLUMES_ARE_USABLE {
3734 shader_defs.push("IRRADIANCE_VOLUMES_ARE_USABLE".into());
3735 }
3736
3737 if self.clustered_decals_are_usable {
3738 shader_defs.push("CLUSTERED_DECALS_ARE_USABLE".into());
3739 if cfg!(feature = "pbr_light_textures") {
3740 shader_defs.push("LIGHT_TEXTURES".into());
3741 }
3742 }
3743
3744 let format = key.target_format();
3745
3746 if let Some(per_object_buffer_batch_size) = self.per_object_buffer_batch_size {
3750 shader_defs.push(ShaderDefVal::UInt(
3751 "PER_OBJECT_BUFFER_BATCH_SIZE".into(),
3752 per_object_buffer_batch_size,
3753 ));
3754 }
3755
3756 Ok(RenderPipelineDescriptor {
3757 vertex: VertexState {
3758 shader: self.shader.clone(),
3759 shader_defs: shader_defs.clone(),
3760 buffers: vec![vertex_buffer_layout],
3761 ..default()
3762 },
3763 fragment: Some(FragmentState {
3764 shader: self.shader.clone(),
3765 shader_defs,
3766 targets: vec![Some(ColorTargetState {
3767 format,
3768 blend,
3769 write_mask: ColorWrites::ALL,
3770 })],
3771 ..default()
3772 }),
3773 layout: bind_group_layout,
3774 primitive: PrimitiveState {
3775 cull_mode: Some(Face::Back),
3776 unclipped_depth: false,
3777 topology: key.primitive_topology(),
3778 strip_index_format: key.strip_index_format(),
3779 ..default()
3780 },
3781 depth_stencil: Some(DepthStencilState {
3782 format: CORE_3D_DEPTH_FORMAT,
3783 depth_write_enabled: Some(depth_write_enabled),
3784 depth_compare: Some(CompareFunction::GreaterEqual),
3785 stencil: StencilState {
3786 front: StencilFaceState::IGNORE,
3787 back: StencilFaceState::IGNORE,
3788 read_mask: 0,
3789 write_mask: 0,
3790 },
3791 bias: DepthBiasState {
3792 constant: 0,
3793 slope_scale: 0.0,
3794 clamp: 0.0,
3795 },
3796 }),
3797 multisample: MultisampleState {
3798 count: key.msaa_samples(),
3799 mask: !0,
3800 alpha_to_coverage_enabled,
3801 },
3802 label: Some(label),
3803 ..default()
3804 })
3805 }
3806}
3807
3808pub struct MeshPhaseBindGroups {
3813 model_only: HashMap<MeshSlabId, BindGroup>,
3815 skinned: HashMap<MeshSlabId, MeshBindGroupPair>,
3817 morph_targets: MeshMorphTargetBindGroups,
3819 lightmaps: HashMap<(MeshSlabId, LightmapSlabIndex), BindGroup>,
3820}
3821
3822pub enum MeshMorphTargetBindGroups {
3828 Uniform(HashMap<AssetId<Mesh>, MeshBindGroupPair>),
3834
3835 Storage(HashMap<(MeshSlabId, MeshSlabId), MeshMorphTargetStorageBindGroups>),
3838}
3839
3840pub struct MeshMorphTargetStorageBindGroups {
3846 skinned: Option<MeshBindGroupPair>,
3848 unskinned: Option<MeshBindGroupPair>,
3850}
3851
3852impl MeshMorphTargetBindGroups {
3853 fn clear(&mut self) {
3855 match *self {
3856 MeshMorphTargetBindGroups::Uniform(ref mut mesh_to_bind_group_pair) => {
3857 mesh_to_bind_group_pair.clear();
3858 }
3859 MeshMorphTargetBindGroups::Storage(ref mut slab_id_to_bind_groups) => {
3860 slab_id_to_bind_groups.clear();
3861 }
3862 }
3863 }
3864}
3865
3866pub struct MeshBindGroupPair {
3867 motion_vectors: BindGroup,
3868 no_motion_vectors: BindGroup,
3869}
3870
3871#[derive(Resource)]
3873pub enum MeshBindGroups {
3874 CpuPreprocessing(MeshPhaseBindGroups),
3877 GpuPreprocessing(TypeIdHashMap<MeshPhaseBindGroups>),
3880}
3881
3882impl MeshPhaseBindGroups {
3883 pub fn new(render_device: &RenderDevice) -> MeshPhaseBindGroups {
3888 MeshPhaseBindGroups {
3889 model_only: HashMap::default(),
3890 skinned: HashMap::default(),
3891 morph_targets: if skins_use_uniform_buffers(&render_device.limits()) {
3892 MeshMorphTargetBindGroups::Uniform(HashMap::default())
3893 } else {
3894 MeshMorphTargetBindGroups::Storage(HashMap::default())
3895 },
3896 lightmaps: HashMap::default(),
3897 }
3898 }
3899
3900 pub fn reset(&mut self) {
3901 self.model_only.clear();
3902 self.skinned.clear();
3903 self.morph_targets.clear();
3904 self.lightmaps.clear();
3905 }
3906
3907 pub fn get(
3909 &self,
3910 metadata_slab_id: MeshSlabId,
3911 lightmap: Option<LightmapSlabIndex>,
3912 is_skinned: bool,
3913 morph: MeshMorphBindGroupKey,
3914 motion_vectors: bool,
3915 ) -> Option<&BindGroup> {
3916 match (is_skinned, morph, lightmap) {
3917 (_, MeshMorphBindGroupKey::Uniform(asset_id), _) => match self.morph_targets {
3918 MeshMorphTargetBindGroups::Uniform(ref morph_targets) => morph_targets
3919 .get(&asset_id)
3920 .map(|bind_group_pair| bind_group_pair.get(motion_vectors)),
3921 MeshMorphTargetBindGroups::Storage(..) => {
3922 error!(
3923 "Tried to look up a mesh morph target bind group using a mesh ID, but \
3924 we're using storage buffers. Look it up using the slab ID instead."
3925 );
3926 None
3927 }
3928 },
3929 (_, MeshMorphBindGroupKey::Storage(metadata_and_morph_slab_ids), _) => match &self
3930 .morph_targets
3931 {
3932 MeshMorphTargetBindGroups::Uniform(..) => {
3933 error!(
3934 "Tried to look up a mesh morph target bind group using a slab ID, but \
3935 we're using uniform buffers. Look it up using the mesh ID instead."
3936 );
3937 None
3938 }
3939 MeshMorphTargetBindGroups::Storage(slab_to_bind_group) => {
3940 let slab_bind_group = slab_to_bind_group.get(&metadata_and_morph_slab_ids)?;
3941 if is_skinned {
3942 slab_bind_group
3943 .skinned
3944 .as_ref()
3945 .map(|bind_group_pair| bind_group_pair.get(motion_vectors))
3946 } else {
3947 slab_bind_group
3948 .unskinned
3949 .as_ref()
3950 .map(|bind_group_pair| bind_group_pair.get(motion_vectors))
3951 }
3952 }
3953 },
3954 (true, MeshMorphBindGroupKey::NoMorphTargets, _) => self
3955 .skinned
3956 .get(&metadata_slab_id)
3957 .map(|bind_group_pair| bind_group_pair.get(motion_vectors)),
3958 (false, MeshMorphBindGroupKey::NoMorphTargets, Some(lightmap_slab)) => {
3959 self.lightmaps.get(&(metadata_slab_id, lightmap_slab))
3960 }
3961 (false, MeshMorphBindGroupKey::NoMorphTargets, None) => {
3962 self.model_only.get(&metadata_slab_id)
3963 }
3964 }
3965 }
3966}
3967
3968impl MeshBindGroupPair {
3969 fn get(&self, motion_vectors: bool) -> &BindGroup {
3970 if motion_vectors {
3971 &self.motion_vectors
3972 } else {
3973 &self.no_motion_vectors
3974 }
3975 }
3976}
3977
3978#[derive(Clone, Copy)]
3981pub enum MeshMorphBindGroupKey {
3982 NoMorphTargets,
3984 Uniform(AssetId<Mesh>),
3989 Storage((MeshSlabId, MeshSlabId)),
3995}
3996
3997pub fn prepare_mesh_bind_groups(
3999 mut commands: Commands,
4000 meshes: Res<RenderAssets<RenderMesh>>,
4001 mesh_pipeline: Res<MeshPipeline>,
4002 render_device: Res<RenderDevice>,
4003 pipeline_cache: Res<PipelineCache>,
4004 cpu_batched_instance_buffer: Option<
4005 Res<no_gpu_preprocessing::BatchedInstanceBuffer<MeshUniform>>,
4006 >,
4007 gpu_batched_instance_buffers: Option<
4008 Res<gpu_preprocessing::BatchedInstanceBuffers<MeshUniform, MeshInputUniform>>,
4009 >,
4010 skins_uniform: Res<SkinUniforms>,
4011 weights_uniform: Res<MorphUniforms>,
4012 mesh_allocator: Res<MeshAllocator>,
4013 render_morph_target_allocator: Res<RenderMorphTargetAllocator>,
4014 render_lightmaps: Res<RenderLightmaps>,
4015 metadata_fallback_buffer: Res<MeshMetadataFallbackBuffer>,
4016 mut mesh_bind_groups: Option<ResMut<MeshBindGroups>>,
4017) {
4018 if let Some(cpu_batched_instance_buffer) = cpu_batched_instance_buffer
4020 && let Some(instance_data_binding) = cpu_batched_instance_buffer
4021 .into_inner()
4022 .instance_data_binding()
4023 {
4024 let mut cpu_preprocessing_mesh_bind_groups = match mesh_bind_groups.as_deref_mut() {
4026 None | Some(MeshBindGroups::GpuPreprocessing(_)) => {
4027 MeshPhaseBindGroups::new(&render_device)
4028 }
4029 Some(MeshBindGroups::CpuPreprocessing(cpu_preprocessing_mesh_bind_groups)) => {
4030 core::mem::replace(
4031 cpu_preprocessing_mesh_bind_groups,
4032 MeshPhaseBindGroups::new(&render_device),
4033 )
4034 }
4035 };
4036 cpu_preprocessing_mesh_bind_groups.reset();
4037
4038 prepare_mesh_bind_groups_for_phase(
4040 &mut cpu_preprocessing_mesh_bind_groups,
4041 instance_data_binding,
4042 &meshes,
4043 &mesh_pipeline,
4044 &render_device,
4045 &pipeline_cache,
4046 &skins_uniform,
4047 &weights_uniform,
4048 &mesh_allocator,
4049 &render_morph_target_allocator,
4050 &render_lightmaps,
4051 &metadata_fallback_buffer,
4052 );
4053
4054 commands.insert_resource(MeshBindGroups::CpuPreprocessing(
4055 cpu_preprocessing_mesh_bind_groups,
4056 ));
4057 return;
4058 }
4059
4060 if let Some(gpu_batched_instance_buffers) = gpu_batched_instance_buffers {
4062 let mut gpu_preprocessing_mesh_bind_groups = match mesh_bind_groups.as_deref_mut() {
4064 None | Some(MeshBindGroups::CpuPreprocessing(_)) => TypeIdHashMap::default(),
4065 Some(MeshBindGroups::GpuPreprocessing(gpu_preprocessing_mesh_bind_groups)) => {
4066 core::mem::take(gpu_preprocessing_mesh_bind_groups)
4067 }
4068 };
4069
4070 for (phase_type_id, batched_phase_instance_buffers) in
4072 &gpu_batched_instance_buffers.phase_instance_buffers
4073 {
4074 let Some(instance_data_binding) =
4075 batched_phase_instance_buffers.instance_data_binding()
4076 else {
4077 continue;
4078 };
4079 let groups = gpu_preprocessing_mesh_bind_groups
4080 .entry(*phase_type_id)
4081 .or_insert(MeshPhaseBindGroups::new(&render_device));
4082 groups.reset();
4083
4084 prepare_mesh_bind_groups_for_phase(
4085 groups,
4086 instance_data_binding,
4087 &meshes,
4088 &mesh_pipeline,
4089 &render_device,
4090 &pipeline_cache,
4091 &skins_uniform,
4092 &weights_uniform,
4093 &mesh_allocator,
4094 &render_morph_target_allocator,
4095 &render_lightmaps,
4096 &metadata_fallback_buffer,
4097 );
4098 }
4099
4100 commands.insert_resource(MeshBindGroups::GpuPreprocessing(
4101 gpu_preprocessing_mesh_bind_groups,
4102 ));
4103 }
4104}
4105
4106fn prepare_mesh_bind_groups_for_phase(
4108 groups: &mut MeshPhaseBindGroups,
4109 model: BindingResource,
4110 meshes: &RenderAssets<RenderMesh>,
4111 mesh_pipeline: &MeshPipeline,
4112 render_device: &RenderDevice,
4113 pipeline_cache: &PipelineCache,
4114 skins_uniform: &SkinUniforms,
4115 weights_uniform: &MorphUniforms,
4116 mesh_allocator: &MeshAllocator,
4117 render_morph_target_allocator: &RenderMorphTargetAllocator,
4118 render_lightmaps: &RenderLightmaps,
4119 metadata_fallback_buffer: &MeshMetadataFallbackBuffer,
4120) {
4121 let layouts = &mesh_pipeline.mesh_layouts;
4122
4123 for metadata_slab_id in mesh_allocator.metadata_slabs() {
4124 let metadata_buffer = mesh_allocator
4125 .buffer_for_slab(metadata_slab_id)
4126 .unwrap_or(&metadata_fallback_buffer.buffer);
4127 groups.model_only.insert(
4128 metadata_slab_id,
4129 layouts.model_only(render_device, pipeline_cache, &model, metadata_buffer),
4130 );
4131
4132 let (skin, prev_skin) = (&skins_uniform.current_buffer, &skins_uniform.prev_buffer);
4135 groups.skinned.insert(
4136 metadata_slab_id,
4137 MeshBindGroupPair {
4138 motion_vectors: layouts.skinned_motion(
4139 render_device,
4140 pipeline_cache,
4141 &model,
4142 metadata_buffer,
4143 skin,
4144 prev_skin,
4145 ),
4146 no_motion_vectors: layouts.skinned(
4147 render_device,
4148 pipeline_cache,
4149 &model,
4150 metadata_buffer,
4151 skin,
4152 ),
4153 },
4154 );
4155
4156 if weights_uniform.current_buffer.buffer().is_some() {
4158 match (render_morph_target_allocator, &mut groups.morph_targets) {
4159 (
4160 RenderMorphTargetAllocator::Image {
4161 mesh_id_to_image: _,
4162 },
4163 &mut MeshMorphTargetBindGroups::Uniform(ref mut _morph_targets),
4164 ) => {
4165 }
4168
4169 (
4170 &RenderMorphTargetAllocator::Storage,
4171 &mut MeshMorphTargetBindGroups::Storage(
4172 ref mut morph_target_storage_bind_groups,
4173 ),
4174 ) => {
4175 prepare_mesh_morph_target_bind_groups_for_phase_using_storage(
4176 &model,
4177 layouts,
4178 render_device,
4179 pipeline_cache,
4180 skins_uniform,
4181 weights_uniform,
4182 mesh_allocator,
4183 morph_target_storage_bind_groups,
4184 (metadata_slab_id, metadata_buffer),
4185 );
4186 }
4187
4188 _ => {
4189 error!(
4190 "Mismatched render morph target allocator and mesh morph target bind groups"
4191 );
4192 }
4193 }
4194 }
4195
4196 let bindless_supported = render_lightmaps.bindless_supported;
4198 for (lightmap_slab_id, lightmap_slab) in render_lightmaps.slabs.iter().enumerate() {
4199 groups.lightmaps.insert(
4200 (
4201 metadata_slab_id,
4202 LightmapSlabIndex(NonMaxU32::new(lightmap_slab_id as u32).unwrap()),
4203 ),
4204 layouts.lightmapped(
4205 render_device,
4206 pipeline_cache,
4207 &model,
4208 metadata_buffer,
4209 lightmap_slab,
4210 bindless_supported,
4211 ),
4212 );
4213 }
4214 }
4215
4216 if weights_uniform.current_buffer.buffer().is_some() {
4218 match (render_morph_target_allocator, &mut groups.morph_targets) {
4219 (
4220 RenderMorphTargetAllocator::Image { mesh_id_to_image },
4221 &mut MeshMorphTargetBindGroups::Uniform(ref mut morph_targets),
4222 ) => {
4223 prepare_mesh_morph_target_bind_groups_for_phase_using_uniforms(
4224 &model,
4225 meshes,
4226 layouts,
4227 render_device,
4228 pipeline_cache,
4229 skins_uniform,
4230 weights_uniform,
4231 mesh_id_to_image,
4232 morph_targets,
4233 mesh_allocator,
4234 metadata_fallback_buffer,
4235 );
4236 }
4237
4238 (
4239 &RenderMorphTargetAllocator::Storage,
4240 &mut MeshMorphTargetBindGroups::Storage(ref mut _morph_target_storage_bind_groups),
4241 ) => {
4242 }
4244
4245 _ => {
4246 error!(
4247 "Mismatched render morph target allocator and mesh morph target bind groups"
4248 );
4249 }
4250 }
4251 }
4252}
4253
4254fn prepare_mesh_morph_target_bind_groups_for_phase_using_uniforms(
4259 model: &BindingResource,
4260 meshes: &RenderAssets<RenderMesh>,
4261 layouts: &MeshLayouts,
4262 render_device: &RenderDevice,
4263 pipeline_cache: &PipelineCache,
4264 skins_uniform: &SkinUniforms,
4265 weights_uniform: &MorphUniforms,
4266 mesh_id_to_image: &HashMap<AssetId<Mesh>, MorphTargetImage>,
4267 morph_targets: &mut HashMap<AssetId<Mesh>, MeshBindGroupPair>,
4268 mesh_allocator: &MeshAllocator,
4269 metadata_fallback_buffer: &MeshMetadataFallbackBuffer,
4270) {
4271 let (skin, prev_skin) = (&skins_uniform.current_buffer, &skins_uniform.prev_buffer);
4272 let weights = weights_uniform
4273 .current_buffer
4274 .buffer()
4275 .expect("Should have a weights buffer by now");
4276 let prev_weights = weights_uniform.prev_buffer.buffer().unwrap_or(weights);
4277 let maybe_morph_descriptors = weights_uniform
4278 .descriptors_buffer
4279 .as_ref()
4280 .and_then(|descriptors_buffer| descriptors_buffer.buffer());
4281
4282 for (id, gpu_mesh) in meshes.iter() {
4283 if !gpu_mesh.has_morph_targets() {
4284 continue;
4285 }
4286 let Some(morph_targets_image) = mesh_id_to_image.get(&id) else {
4287 continue;
4288 };
4289 let metadata_buffer = mesh_allocator
4290 .mesh_metadata_slice(&id)
4291 .map(|slice| slice.buffer)
4292 .unwrap_or(&metadata_fallback_buffer.buffer);
4293 let targets = MorphTargetsResource::Texture(&morph_targets_image.texture_view);
4294 let bind_group_pair = if is_skinned(&gpu_mesh.layout) {
4295 MeshBindGroupPair {
4296 motion_vectors: layouts.morphed_skinned_motion(
4297 render_device,
4298 pipeline_cache,
4299 model,
4300 metadata_buffer,
4301 skin,
4302 weights,
4303 targets,
4304 prev_skin,
4305 prev_weights,
4306 maybe_morph_descriptors,
4307 ),
4308 no_motion_vectors: layouts.morphed_skinned(
4309 render_device,
4310 pipeline_cache,
4311 model,
4312 metadata_buffer,
4313 skin,
4314 weights,
4315 targets,
4316 maybe_morph_descriptors,
4317 ),
4318 }
4319 } else {
4320 MeshBindGroupPair {
4321 motion_vectors: layouts.morphed_motion(
4322 render_device,
4323 pipeline_cache,
4324 model,
4325 metadata_buffer,
4326 weights,
4327 prev_weights,
4328 targets,
4329 maybe_morph_descriptors,
4330 ),
4331 no_motion_vectors: layouts.morphed(
4332 render_device,
4333 pipeline_cache,
4334 model,
4335 metadata_buffer,
4336 weights,
4337 targets,
4338 maybe_morph_descriptors,
4339 ),
4340 }
4341 };
4342 morph_targets.insert(id, bind_group_pair);
4343 }
4344}
4345
4346fn prepare_mesh_morph_target_bind_groups_for_phase_using_storage(
4350 model: &BindingResource,
4351 layouts: &MeshLayouts,
4352 render_device: &RenderDevice,
4353 pipeline_cache: &PipelineCache,
4354 skins_uniform: &SkinUniforms,
4355 weights_uniform: &MorphUniforms,
4356 mesh_allocator: &MeshAllocator,
4357 morph_target_storage_bind_groups: &mut HashMap<
4358 (MeshSlabId, MeshSlabId),
4359 MeshMorphTargetStorageBindGroups,
4360 >,
4361 (metadata_slab_id, metadata_buffer): (MeshSlabId, &Buffer),
4362) {
4363 let (skin, prev_skin) = (&skins_uniform.current_buffer, &skins_uniform.prev_buffer);
4364 let weights = weights_uniform
4365 .current_buffer
4366 .buffer()
4367 .expect("Should have a weights buffer by now");
4368 let prev_weights = weights_uniform.prev_buffer.buffer().unwrap_or(weights);
4369 let maybe_morph_descriptors = weights_uniform
4370 .descriptors_buffer
4371 .as_ref()
4372 .and_then(|descriptors_buffer| descriptors_buffer.buffer());
4373
4374 for morph_target_slab_id in mesh_allocator.morph_target_slabs() {
4375 let Some(buffer) = mesh_allocator.buffer_for_slab(morph_target_slab_id) else {
4376 continue;
4377 };
4378 let targets = MorphTargetsResource::Storage(buffer);
4379 morph_target_storage_bind_groups.insert(
4380 (metadata_slab_id, morph_target_slab_id),
4381 MeshMorphTargetStorageBindGroups {
4382 skinned: Some(MeshBindGroupPair {
4383 motion_vectors: layouts.morphed_skinned_motion(
4384 render_device,
4385 pipeline_cache,
4386 model,
4387 metadata_buffer,
4388 skin,
4389 weights,
4390 targets,
4391 prev_skin,
4392 prev_weights,
4393 maybe_morph_descriptors,
4394 ),
4395 no_motion_vectors: layouts.morphed_skinned(
4396 render_device,
4397 pipeline_cache,
4398 model,
4399 metadata_buffer,
4400 skin,
4401 weights,
4402 targets,
4403 maybe_morph_descriptors,
4404 ),
4405 }),
4406 unskinned: Some(MeshBindGroupPair {
4407 motion_vectors: layouts.morphed_motion(
4408 render_device,
4409 pipeline_cache,
4410 model,
4411 metadata_buffer,
4412 weights,
4413 prev_weights,
4414 targets,
4415 maybe_morph_descriptors,
4416 ),
4417 no_motion_vectors: layouts.morphed(
4418 render_device,
4419 pipeline_cache,
4420 model,
4421 metadata_buffer,
4422 weights,
4423 targets,
4424 maybe_morph_descriptors,
4425 ),
4426 }),
4427 },
4428 );
4429 }
4430}
4431
4432pub struct SetMeshViewBindGroup<const I: usize>;
4433impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetMeshViewBindGroup<I> {
4434 type Param = ();
4435 type ViewQuery = (Read<MeshViewBindGroup>,);
4436 type ItemQuery = ();
4437
4438 #[inline]
4439 fn render<'w>(
4440 _item: &P,
4441 (mesh_view_bind_group,): ROQueryItem<'w, '_, Self::ViewQuery>,
4442 _entity: Option<()>,
4443 _: SystemParamItem<'w, '_, Self::Param>,
4444 pass: &mut TrackedRenderPass<'w>,
4445 ) -> RenderCommandResult {
4446 pass.set_bind_group(
4447 I,
4448 &mesh_view_bind_group.main,
4449 &mesh_view_bind_group.main_offsets,
4450 );
4451
4452 RenderCommandResult::Success
4453 }
4454}
4455
4456pub struct SetMeshViewBindingArrayBindGroup<const I: usize>;
4457impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetMeshViewBindingArrayBindGroup<I> {
4458 type Param = ();
4459 type ViewQuery = (Read<MeshViewBindGroup>,);
4460 type ItemQuery = ();
4461
4462 #[inline]
4463 fn render<'w>(
4464 _item: &P,
4465 (mesh_view_bind_group,): ROQueryItem<'w, '_, Self::ViewQuery>,
4466 _entity: Option<()>,
4467 _: SystemParamItem<'w, '_, Self::Param>,
4468 pass: &mut TrackedRenderPass<'w>,
4469 ) -> RenderCommandResult {
4470 pass.set_bind_group(I, &mesh_view_bind_group.binding_array, &[]);
4471
4472 RenderCommandResult::Success
4473 }
4474}
4475
4476pub struct SetMeshViewEmptyBindGroup<const I: usize>;
4477impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetMeshViewEmptyBindGroup<I> {
4478 type Param = ();
4479 type ViewQuery = (Read<MeshViewBindGroup>,);
4480 type ItemQuery = ();
4481
4482 #[inline]
4483 fn render<'w>(
4484 _item: &P,
4485 (mesh_view_bind_group,): ROQueryItem<'w, '_, Self::ViewQuery>,
4486 _entity: Option<()>,
4487 _: SystemParamItem<'w, '_, Self::Param>,
4488 pass: &mut TrackedRenderPass<'w>,
4489 ) -> RenderCommandResult {
4490 pass.set_bind_group(I, &mesh_view_bind_group.empty, &[]);
4491
4492 RenderCommandResult::Success
4493 }
4494}
4495
4496pub struct SetMeshBindGroup<const I: usize>;
4497impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetMeshBindGroup<I> {
4498 type Param = (
4499 SRes<RenderDevice>,
4500 SRes<MeshBindGroups>,
4501 SRes<RenderMeshInstances>,
4502 SRes<SkinUniforms>,
4503 SRes<MorphIndices>,
4504 SRes<MeshAllocator>,
4505 SRes<RenderLightmaps>,
4506 SRes<MeshMetadataFallbackBuffer>,
4507 );
4508 type ViewQuery = Has<MotionVectorPrepass>;
4509 type ItemQuery = ();
4510
4511 #[inline]
4512 fn render<'w>(
4513 item: &P,
4514 has_motion_vector_prepass: bool,
4515 _item_query: Option<()>,
4516 (
4517 render_device,
4518 bind_groups,
4519 mesh_instances,
4520 skin_uniforms,
4521 morph_indices,
4522 mesh_allocator,
4523 lightmaps,
4524 metadata_fallback_buffer,
4525 ): SystemParamItem<'w, '_, Self::Param>,
4526 pass: &mut TrackedRenderPass<'w>,
4527 ) -> RenderCommandResult {
4528 let bind_groups = bind_groups.into_inner();
4529 let mesh_instances = mesh_instances.into_inner();
4530 let skin_uniforms = skin_uniforms.into_inner();
4531 let morph_indices = morph_indices.into_inner();
4532
4533 let entity = &item.main_entity();
4534
4535 let Some(mesh_asset_id) = mesh_instances.mesh_asset_id(*entity) else {
4536 return RenderCommandResult::Success;
4537 };
4538
4539 let mesh_slabs = mesh_allocator.mesh_slabs(&mesh_asset_id);
4540 let metadata_slab_id = mesh_slabs
4541 .and_then(|slabs| slabs.metadata_slab_id)
4542 .unwrap_or(metadata_fallback_buffer.slab_id);
4543 let skins_use_uniform_buffers = skins_use_uniform_buffers(&render_device.limits());
4544
4545 let current_skin_byte_offset = skin_uniforms.skin_byte_offset(*entity);
4546
4547 let (current_morph_index, prev_morph_index, morph_bind_group_key);
4553 match *morph_indices {
4554 MorphIndices::Uniform {
4555 ref current,
4556 ref prev,
4557 } => {
4558 current_morph_index = current.get(entity);
4559 prev_morph_index = prev.get(entity);
4560 morph_bind_group_key = if current_morph_index.is_some() {
4561 MeshMorphBindGroupKey::Uniform(mesh_asset_id)
4562 } else {
4563 MeshMorphBindGroupKey::NoMorphTargets
4564 };
4565 }
4566 MorphIndices::Storage { .. } => {
4567 current_morph_index = None;
4568 prev_morph_index = None;
4569 morph_bind_group_key =
4570 match mesh_slabs.and_then(|mesh_slabs| mesh_slabs.morph_target_slab_id) {
4571 Some(morph_target_slab_id) => {
4572 MeshMorphBindGroupKey::Storage((metadata_slab_id, morph_target_slab_id))
4573 }
4574 None => MeshMorphBindGroupKey::NoMorphTargets,
4575 };
4576 }
4577 };
4578
4579 let is_skinned = current_skin_byte_offset.is_some();
4580
4581 let lightmap_slab_index = lightmaps
4582 .render_lightmaps
4583 .get(entity)
4584 .map(|render_lightmap| render_lightmap.slab_index);
4585
4586 let Some(mesh_phase_bind_groups) = (match *bind_groups {
4587 MeshBindGroups::CpuPreprocessing(ref mesh_phase_bind_groups) => {
4588 Some(mesh_phase_bind_groups)
4589 }
4590 MeshBindGroups::GpuPreprocessing(ref mesh_phase_bind_groups) => {
4591 mesh_phase_bind_groups.get(&TypeId::of::<P>())
4592 }
4593 }) else {
4594 return RenderCommandResult::Success;
4597 };
4598
4599 let Some(bind_group) = mesh_phase_bind_groups.get(
4600 metadata_slab_id,
4601 lightmap_slab_index,
4602 is_skinned,
4603 morph_bind_group_key,
4604 has_motion_vector_prepass,
4605 ) else {
4606 return RenderCommandResult::Failure(
4607 "The MeshBindGroups resource wasn't set in the render phase. \
4608 It should be set by the prepare_mesh_bind_group system.\n\
4609 This is a bevy bug! Please open an issue.",
4610 );
4611 };
4612
4613 let mut dynamic_offsets: [u32; 5] = Default::default();
4614 let mut offset_count = 0;
4615 if let PhaseItemExtraIndex::DynamicOffset(dynamic_offset) = item.extra_index() {
4616 dynamic_offsets[offset_count] = dynamic_offset;
4617 offset_count += 1;
4618 }
4619 if skins_use_uniform_buffers {
4620 if let Some(current_skin_index) = current_skin_byte_offset {
4621 dynamic_offsets[offset_count] = current_skin_index.byte_offset;
4622 offset_count += 1;
4623 }
4624 if let Some(current_morph_index) = current_morph_index {
4625 dynamic_offsets[offset_count] = current_morph_index.index;
4626 offset_count += 1;
4627 }
4628 }
4629
4630 if skins_use_uniform_buffers && has_motion_vector_prepass {
4632 if let Some(current_skin_byte_offset) = current_skin_byte_offset {
4634 dynamic_offsets[offset_count] = current_skin_byte_offset.byte_offset;
4635 offset_count += 1;
4636 }
4637
4638 if current_morph_index.is_some() {
4641 match prev_morph_index {
4642 Some(prev_morph_index) => {
4643 dynamic_offsets[offset_count] = prev_morph_index.index;
4644 }
4645 None => dynamic_offsets[offset_count] = 0,
4646 }
4647 offset_count += 1;
4648 }
4649 }
4650
4651 pass.set_bind_group(I, bind_group, &dynamic_offsets[0..offset_count]);
4652
4653 RenderCommandResult::Success
4654 }
4655}
4656
4657pub struct DrawMesh;
4658impl<P: PhaseItem> RenderCommand<P> for DrawMesh {
4659 type Param = (
4660 SRes<RenderAssets<RenderMesh>>,
4661 SRes<RenderMeshInstances>,
4662 SRes<IndirectParametersBuffers>,
4663 SRes<PipelineCache>,
4664 SRes<MeshAllocator>,
4665 Option<SRes<PreprocessPipelines>>,
4666 SRes<GpuPreprocessingSupport>,
4667 );
4668 type ViewQuery = Has<PreprocessBindGroups>;
4669 type ItemQuery = ();
4670 #[inline]
4671 fn render<'w>(
4672 item: &P,
4673 has_preprocess_bind_group: ROQueryItem<Self::ViewQuery>,
4674 _item_query: Option<()>,
4675 (
4676 meshes,
4677 mesh_instances,
4678 indirect_parameters_buffer,
4679 pipeline_cache,
4680 mesh_allocator,
4681 preprocess_pipelines,
4682 preprocessing_support,
4683 ): SystemParamItem<'w, '_, Self::Param>,
4684 pass: &mut TrackedRenderPass<'w>,
4685 ) -> RenderCommandResult {
4686 if let Some(preprocess_pipelines) = preprocess_pipelines
4690 && (!has_preprocess_bind_group
4691 || !preprocess_pipelines
4692 .pipelines_are_loaded(&pipeline_cache, &preprocessing_support))
4693 {
4694 return RenderCommandResult::Skip;
4695 }
4696
4697 let meshes = meshes.into_inner();
4698 let mesh_instances = mesh_instances.into_inner();
4699 let indirect_parameters_buffer = indirect_parameters_buffer.into_inner();
4700 let mesh_allocator = mesh_allocator.into_inner();
4701
4702 let Some(mesh_asset_id) = mesh_instances.mesh_asset_id(item.main_entity()) else {
4703 return RenderCommandResult::Skip;
4704 };
4705 let Some(gpu_mesh) = meshes.get(mesh_asset_id) else {
4706 return RenderCommandResult::Skip;
4707 };
4708 let Some(vertex_buffer_slice) = mesh_allocator.mesh_vertex_slice(&mesh_asset_id) else {
4709 return RenderCommandResult::Skip;
4710 };
4711
4712 pass.set_vertex_buffer(0, vertex_buffer_slice.buffer.slice(..));
4713
4714 let batch_range = item.batch_range();
4715
4716 match &gpu_mesh.buffer_info {
4720 RenderMeshBufferInfo::Indexed {
4721 index_format,
4722 count,
4723 } => {
4724 let Some(index_buffer_slice) = mesh_allocator.mesh_index_slice(&mesh_asset_id)
4725 else {
4726 return RenderCommandResult::Skip;
4727 };
4728
4729 pass.set_index_buffer(index_buffer_slice.buffer.slice(..), *index_format);
4730
4731 match item.extra_index() {
4732 PhaseItemExtraIndex::None | PhaseItemExtraIndex::DynamicOffset(_) => {
4733 pass.draw_indexed(
4734 index_buffer_slice.range.start
4735 ..(index_buffer_slice.range.start + *count),
4736 vertex_buffer_slice.range.start as i32,
4737 batch_range.clone(),
4738 );
4739 }
4740 PhaseItemExtraIndex::IndirectParametersIndex {
4741 range: indirect_parameters_range,
4742 batch_set_index,
4743 } => {
4744 let Some(phase_indirect_parameters_buffers) =
4748 indirect_parameters_buffer.get(&TypeId::of::<P>())
4749 else {
4750 warn!(
4751 "Not rendering mesh because indexed indirect parameters buffer \
4752 wasn't present for this phase",
4753 );
4754 return RenderCommandResult::Skip;
4755 };
4756 let (Some(indirect_parameters_buffer), Some(batch_sets_buffer)) = (
4757 phase_indirect_parameters_buffers.indexed.data_buffer(),
4758 phase_indirect_parameters_buffers
4759 .indexed
4760 .batch_sets_buffer(),
4761 ) else {
4762 warn!(
4763 "Not rendering mesh because indexed indirect parameters buffer \
4764 wasn't present",
4765 );
4766 return RenderCommandResult::Skip;
4767 };
4768
4769 let indirect_parameters_offset = indirect_parameters_range.start as u64
4772 * size_of::<IndirectParametersIndexed>() as u64;
4773 let indirect_parameters_count =
4774 indirect_parameters_range.end - indirect_parameters_range.start;
4775
4776 match batch_set_index {
4781 Some(batch_set_index) => {
4782 let count_offset = u32::from(batch_set_index)
4783 * (size_of::<IndirectBatchSet>() as u32);
4784 pass.multi_draw_indexed_indirect_count(
4785 indirect_parameters_buffer,
4786 indirect_parameters_offset,
4787 batch_sets_buffer,
4788 count_offset as u64,
4789 indirect_parameters_count,
4790 );
4791 }
4792 None => {
4793 pass.multi_draw_indexed_indirect(
4794 indirect_parameters_buffer,
4795 indirect_parameters_offset,
4796 indirect_parameters_count,
4797 );
4798 }
4799 }
4800 }
4801 }
4802 }
4803
4804 RenderMeshBufferInfo::NonIndexed => match item.extra_index() {
4805 PhaseItemExtraIndex::None | PhaseItemExtraIndex::DynamicOffset(_) => {
4806 pass.draw(vertex_buffer_slice.range, batch_range.clone());
4807 }
4808 PhaseItemExtraIndex::IndirectParametersIndex {
4809 range: indirect_parameters_range,
4810 batch_set_index,
4811 } => {
4812 let Some(phase_indirect_parameters_buffers) =
4816 indirect_parameters_buffer.get(&TypeId::of::<P>())
4817 else {
4818 warn!(
4819 "Not rendering mesh because non-indexed indirect parameters buffer \
4820 wasn't present for this phase",
4821 );
4822 return RenderCommandResult::Skip;
4823 };
4824 let (Some(indirect_parameters_buffer), Some(batch_sets_buffer)) = (
4825 phase_indirect_parameters_buffers.non_indexed.data_buffer(),
4826 phase_indirect_parameters_buffers
4827 .non_indexed
4828 .batch_sets_buffer(),
4829 ) else {
4830 warn!(
4831 "Not rendering mesh because non-indexed indirect parameters buffer \
4832 wasn't present"
4833 );
4834 return RenderCommandResult::Skip;
4835 };
4836
4837 let indirect_parameters_offset = indirect_parameters_range.start as u64
4840 * size_of::<IndirectParametersNonIndexed>() as u64;
4841 let indirect_parameters_count =
4842 indirect_parameters_range.end - indirect_parameters_range.start;
4843
4844 match batch_set_index {
4849 Some(batch_set_index) => {
4850 let count_offset =
4851 u32::from(batch_set_index) * (size_of::<IndirectBatchSet>() as u32);
4852 pass.multi_draw_indirect_count(
4853 indirect_parameters_buffer,
4854 indirect_parameters_offset,
4855 batch_sets_buffer,
4856 count_offset as u64,
4857 indirect_parameters_count,
4858 );
4859 }
4860 None => {
4861 pass.multi_draw_indirect(
4862 indirect_parameters_buffer,
4863 indirect_parameters_offset,
4864 indirect_parameters_count,
4865 );
4866 }
4867 }
4868 }
4869 },
4870 }
4871 RenderCommandResult::Success
4872 }
4873}
4874
4875#[cfg(test)]
4876mod tests {
4877 use core::sync::atomic::{AtomicU64, Ordering};
4878
4879 use super::{AtomicU64ZeroBitIter, MeshPipelineKey};
4880
4881 #[test]
4882 fn mesh_key_msaa_samples() {
4883 for i in [1, 2, 4, 8, 16, 32, 64, 128] {
4884 assert_eq!(MeshPipelineKey::from_msaa_samples(i).msaa_samples(), i);
4885 }
4886 }
4887
4888 #[test]
4890 fn atomic_u64_zero_bit_iter() {
4891 static TEST_DATA_0: [AtomicU64; 1] = [AtomicU64::new(0x7b52f5ca63498b6a)];
4895 static TEST_DATA_1: [AtomicU64; 2] = [
4896 AtomicU64::new(0x5705b33451b95827),
4897 AtomicU64::new(0x1939ee614074abad),
4898 ];
4899 static TEST_DATA_2: [AtomicU64; 3] = [
4900 AtomicU64::new(0xf33c508d14d145c),
4901 AtomicU64::new(0x2a4749823594ea),
4902 AtomicU64::new(0x5e68df04196a3818),
4903 ];
4904 static TEST_DATA_3: [AtomicU64; 6] = [
4905 AtomicU64::new(0x2336dbda2bd74d09),
4906 AtomicU64::new(0x10b7da1dacf22d33),
4907 AtomicU64::new(0x6eaaf908d957923a),
4908 AtomicU64::new(0x7ec7ffb64cb9c4a6),
4909 AtomicU64::new(0x6dd027da8ad22fa0),
4910 AtomicU64::new(0x13278c5caa74f73d),
4911 ];
4912 static TEST_DATA_4: [AtomicU64; 4] = [
4913 AtomicU64::new(0x32c66f0334bb09e9),
4914 AtomicU64::new(0x60815770d307bdcd),
4915 AtomicU64::new(0x6270a1e972fb8469),
4916 AtomicU64::new(0x610e995c042d6df4),
4917 ];
4918 static TEST_DATA_5: [AtomicU64; 3] = [
4919 AtomicU64::new(0x7fe756fc690097eb),
4920 AtomicU64::new(0x15d87ce6679b1bd8),
4921 AtomicU64::new(0x1985ea515135b255),
4922 ];
4923 static TEST_DATA_6: [AtomicU64; 4] = [
4924 AtomicU64::new(0x1afb9d361c135827),
4925 AtomicU64::new(0x4a79ad582628a854),
4926 AtomicU64::new(0x57a802160315c974),
4927 AtomicU64::new(0x1c0aef068db1f6fb),
4928 ];
4929 static TEST_DATA_7: [AtomicU64; 3] = [
4930 AtomicU64::new(0x53d10eb77230c696),
4931 AtomicU64::new(0x2ca2d709994855fb),
4932 AtomicU64::new(0x26536a13f647f2f7),
4933 ];
4934 static TEST_DATA_8: [AtomicU64; 2] = [
4935 AtomicU64::new(0x6ed95eb903155e00),
4936 AtomicU64::new(0x5e5d58eec92cba0),
4937 ];
4938 static TEST_DATA_9: [AtomicU64; 1] = [AtomicU64::new(0x2caa4b77512b1664)];
4939
4940 let test_data = [
4941 &TEST_DATA_0[..],
4942 &TEST_DATA_1[..],
4943 &TEST_DATA_2[..],
4944 &TEST_DATA_3[..],
4945 &TEST_DATA_4[..],
4946 &TEST_DATA_5[..],
4947 &TEST_DATA_6[..],
4948 &TEST_DATA_7[..],
4949 &TEST_DATA_8[..],
4950 &TEST_DATA_9[..],
4951 ];
4952
4953 for bits in &test_data {
4954 let mut reference_zero_positions = vec![];
4956 for (word_index, word) in (*bits).iter().enumerate() {
4957 let word = word.load(Ordering::Relaxed);
4958 for bit_index in 0..64 {
4959 if ((word >> bit_index) & 1) == 0 {
4960 reference_zero_positions.push((word_index * 64 + bit_index) as u32);
4961 }
4962 }
4963 }
4964
4965 let test_zero_positions: Vec<_> = AtomicU64ZeroBitIter::new(bits).collect();
4967
4968 assert_eq!(test_zero_positions, reference_zero_positions);
4970 }
4971 }
4972}