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