Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_pbr/src/render/mesh.rs
6849 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<MeshTag>,
1462
Changed<NoFrustumCulling>,
1463
Changed<NotShadowReceiver>,
1464
Changed<TransmittedShadowReceiver>,
1465
Changed<NotShadowCaster>,
1466
Changed<NoAutomaticBatching>,
1467
Changed<VisibilityRange>,
1468
Changed<SkinnedMesh>,
1469
)>,
1470
>,
1471
>,
1472
all_meshes_query: Extract<Query<GpuMeshExtractionQuery>>,
1473
mut removed_meshes_query: Extract<RemovedComponents<Mesh3d>>,
1474
gpu_culling_query: Extract<Query<(), (With<Camera>, Without<NoIndirectDrawing>)>>,
1475
meshes_to_reextract_next_frame: ResMut<MeshesToReextractNextFrame>,
1476
) {
1477
let any_gpu_culling = !gpu_culling_query.is_empty();
1478
1479
for render_mesh_instance_queue in render_mesh_instance_queues.iter_mut() {
1480
render_mesh_instance_queue.init(any_gpu_culling);
1481
}
1482
1483
// Collect render mesh instances. Build up the uniform buffer.
1484
1485
let RenderMeshInstances::GpuBuilding(ref mut render_mesh_instances) = *render_mesh_instances
1486
else {
1487
panic!(
1488
"`extract_meshes_for_gpu_building` should only be called if we're \
1489
using GPU `MeshUniform` building"
1490
);
1491
};
1492
1493
// Find all meshes that have changed, and record information needed to
1494
// construct the `MeshInputUniform` for them.
1495
changed_meshes_query.par_iter().for_each_init(
1496
|| render_mesh_instance_queues.borrow_local_mut(),
1497
|queue, query_row| {
1498
extract_mesh_for_gpu_building(
1499
query_row,
1500
&render_visibility_ranges,
1501
render_mesh_instances,
1502
queue,
1503
any_gpu_culling,
1504
);
1505
},
1506
);
1507
1508
// Process materials that `collect_meshes_for_gpu_building` marked as
1509
// needing to be reextracted. This will happen when we extracted a mesh on
1510
// some previous frame, but its material hadn't been prepared yet, perhaps
1511
// because the material hadn't yet been loaded. We reextract such materials
1512
// on subsequent frames so that `collect_meshes_for_gpu_building` will check
1513
// to see if their materials have been prepared.
1514
let mut queue = render_mesh_instance_queues.borrow_local_mut();
1515
for &mesh_entity in &**meshes_to_reextract_next_frame {
1516
if let Ok(query_row) = all_meshes_query.get(*mesh_entity) {
1517
extract_mesh_for_gpu_building(
1518
query_row,
1519
&render_visibility_ranges,
1520
render_mesh_instances,
1521
&mut queue,
1522
any_gpu_culling,
1523
);
1524
}
1525
}
1526
1527
// Also record info about each mesh that became invisible.
1528
for entity in removed_meshes_query.read() {
1529
// Only queue a mesh for removal if we didn't pick it up above.
1530
// It's possible that a necessary component was removed and re-added in
1531
// the same frame.
1532
let entity = MainEntity::from(entity);
1533
if !changed_meshes_query.contains(*entity)
1534
&& !meshes_to_reextract_next_frame.contains(&entity)
1535
{
1536
queue.remove(entity, any_gpu_culling);
1537
}
1538
}
1539
}
1540
1541
fn extract_mesh_for_gpu_building(
1542
(
1543
entity,
1544
view_visibility,
1545
transform,
1546
previous_transform,
1547
lightmap,
1548
aabb,
1549
mesh,
1550
tag,
1551
no_frustum_culling,
1552
not_shadow_receiver,
1553
transmitted_receiver,
1554
not_shadow_caster,
1555
no_automatic_batching,
1556
visibility_range,
1557
render_layers,
1558
): <GpuMeshExtractionQuery as QueryData>::Item<'_, '_>,
1559
render_visibility_ranges: &RenderVisibilityRanges,
1560
render_mesh_instances: &RenderMeshInstancesGpu,
1561
queue: &mut RenderMeshInstanceGpuQueue,
1562
any_gpu_culling: bool,
1563
) {
1564
if !view_visibility.get() {
1565
queue.remove(entity.into(), any_gpu_culling);
1566
return;
1567
}
1568
1569
let mut lod_index = None;
1570
if visibility_range {
1571
lod_index = render_visibility_ranges.lod_index_for_entity(entity.into());
1572
}
1573
1574
let mesh_flags = MeshFlags::from_components(
1575
transform,
1576
lod_index,
1577
no_frustum_culling,
1578
not_shadow_receiver,
1579
transmitted_receiver,
1580
);
1581
1582
let shared = RenderMeshInstanceShared::for_gpu_building(
1583
previous_transform,
1584
mesh,
1585
tag,
1586
not_shadow_caster,
1587
no_automatic_batching,
1588
render_layers,
1589
);
1590
1591
let lightmap_uv_rect = pack_lightmap_uv_rect(lightmap.map(|lightmap| lightmap.uv_rect));
1592
1593
let gpu_mesh_culling_data = any_gpu_culling.then(|| MeshCullingData::new(aabb));
1594
1595
let previous_input_index = if shared
1596
.flags
1597
.contains(RenderMeshInstanceFlags::HAS_PREVIOUS_TRANSFORM)
1598
{
1599
render_mesh_instances
1600
.get(&MainEntity::from(entity))
1601
.map(|render_mesh_instance| render_mesh_instance.current_uniform_index)
1602
} else {
1603
None
1604
};
1605
1606
let gpu_mesh_instance_builder = RenderMeshInstanceGpuBuilder {
1607
shared,
1608
world_from_local: (&transform.affine()).into(),
1609
lightmap_uv_rect,
1610
mesh_flags,
1611
previous_input_index,
1612
};
1613
1614
queue.push(
1615
entity.into(),
1616
gpu_mesh_instance_builder,
1617
gpu_mesh_culling_data,
1618
);
1619
}
1620
1621
/// A system that sets the [`RenderMeshInstanceFlags`] for each mesh based on
1622
/// whether the previous frame had skins and/or morph targets.
1623
///
1624
/// Ordinarily, [`RenderMeshInstanceFlags`] are set during the extraction phase.
1625
/// However, we can't do that for the flags related to skins and morph targets
1626
/// because the previous frame's skin and morph targets are the responsibility
1627
/// of [`extract_skins`] and [`extract_morphs`] respectively. We want to run
1628
/// those systems in parallel with mesh extraction for performance, so we need
1629
/// to defer setting of these mesh instance flags to after extraction, which
1630
/// this system does. An alternative to having skin- and morph-target-related
1631
/// data in [`RenderMeshInstanceFlags`] would be to have
1632
/// [`crate::material::queue_material_meshes`] check the skin and morph target
1633
/// tables for each mesh, but that would be too slow in the hot mesh queuing
1634
/// loop.
1635
pub(crate) fn set_mesh_motion_vector_flags(
1636
mut render_mesh_instances: ResMut<RenderMeshInstances>,
1637
skin_uniforms: Res<SkinUniforms>,
1638
morph_indices: Res<MorphIndices>,
1639
) {
1640
for &entity in skin_uniforms.all_skins() {
1641
render_mesh_instances
1642
.insert_mesh_instance_flags(entity, RenderMeshInstanceFlags::HAS_PREVIOUS_SKIN);
1643
}
1644
for &entity in morph_indices.prev.keys() {
1645
render_mesh_instances
1646
.insert_mesh_instance_flags(entity, RenderMeshInstanceFlags::HAS_PREVIOUS_MORPH);
1647
}
1648
}
1649
1650
/// Creates the [`RenderMeshInstanceGpu`]s and [`MeshInputUniform`]s when GPU
1651
/// mesh uniforms are built.
1652
pub fn collect_meshes_for_gpu_building(
1653
render_mesh_instances: ResMut<RenderMeshInstances>,
1654
batched_instance_buffers: ResMut<
1655
gpu_preprocessing::BatchedInstanceBuffers<MeshUniform, MeshInputUniform>,
1656
>,
1657
mut mesh_culling_data_buffer: ResMut<MeshCullingDataBuffer>,
1658
mut render_mesh_instance_queues: ResMut<RenderMeshInstanceGpuQueues>,
1659
mesh_allocator: Res<MeshAllocator>,
1660
mesh_material_ids: Res<RenderMaterialInstances>,
1661
render_material_bindings: Res<RenderMaterialBindings>,
1662
render_lightmaps: Res<RenderLightmaps>,
1663
skin_uniforms: Res<SkinUniforms>,
1664
frame_count: Res<FrameCount>,
1665
mut meshes_to_reextract_next_frame: ResMut<MeshesToReextractNextFrame>,
1666
) {
1667
let RenderMeshInstances::GpuBuilding(render_mesh_instances) =
1668
render_mesh_instances.into_inner()
1669
else {
1670
return;
1671
};
1672
1673
// We're going to rebuild `meshes_to_reextract_next_frame`.
1674
meshes_to_reextract_next_frame.clear();
1675
1676
// Collect render mesh instances. Build up the uniform buffer.
1677
let gpu_preprocessing::BatchedInstanceBuffers {
1678
current_input_buffer,
1679
previous_input_buffer,
1680
..
1681
} = batched_instance_buffers.into_inner();
1682
1683
previous_input_buffer.clear();
1684
1685
// Build the [`RenderMeshInstance`]s and [`MeshInputUniform`]s.
1686
1687
for queue in render_mesh_instance_queues.iter_mut() {
1688
match *queue {
1689
RenderMeshInstanceGpuQueue::None => {
1690
// This can only happen if the queue is empty.
1691
}
1692
1693
RenderMeshInstanceGpuQueue::CpuCulling {
1694
ref mut changed,
1695
ref mut removed,
1696
} => {
1697
for (entity, mesh_instance_builder) in changed.drain(..) {
1698
mesh_instance_builder.update(
1699
entity,
1700
&mut *render_mesh_instances,
1701
current_input_buffer,
1702
previous_input_buffer,
1703
&mesh_allocator,
1704
&mesh_material_ids,
1705
&render_material_bindings,
1706
&render_lightmaps,
1707
&skin_uniforms,
1708
*frame_count,
1709
&mut meshes_to_reextract_next_frame,
1710
);
1711
}
1712
1713
for entity in removed.drain(..) {
1714
remove_mesh_input_uniform(
1715
entity,
1716
&mut *render_mesh_instances,
1717
current_input_buffer,
1718
);
1719
}
1720
}
1721
1722
RenderMeshInstanceGpuQueue::GpuCulling {
1723
ref mut changed,
1724
ref mut removed,
1725
} => {
1726
for (entity, mesh_instance_builder, mesh_culling_builder) in changed.drain(..) {
1727
let Some(instance_data_index) = mesh_instance_builder.update(
1728
entity,
1729
&mut *render_mesh_instances,
1730
current_input_buffer,
1731
previous_input_buffer,
1732
&mesh_allocator,
1733
&mesh_material_ids,
1734
&render_material_bindings,
1735
&render_lightmaps,
1736
&skin_uniforms,
1737
*frame_count,
1738
&mut meshes_to_reextract_next_frame,
1739
) else {
1740
continue;
1741
};
1742
mesh_culling_builder
1743
.update(&mut mesh_culling_data_buffer, instance_data_index as usize);
1744
}
1745
1746
for entity in removed.drain(..) {
1747
remove_mesh_input_uniform(
1748
entity,
1749
&mut *render_mesh_instances,
1750
current_input_buffer,
1751
);
1752
}
1753
}
1754
}
1755
}
1756
1757
// Buffers can't be empty. Make sure there's something in the previous input buffer.
1758
previous_input_buffer.ensure_nonempty();
1759
}
1760
1761
/// All data needed to construct a pipeline for rendering 3D meshes.
1762
#[derive(Resource, Clone)]
1763
pub struct MeshPipeline {
1764
/// A reference to all the mesh pipeline view layouts.
1765
pub view_layouts: MeshPipelineViewLayouts,
1766
// This dummy white texture is to be used in place of optional StandardMaterial textures
1767
pub dummy_white_gpu_image: GpuImage,
1768
pub clustered_forward_buffer_binding_type: BufferBindingType,
1769
pub mesh_layouts: MeshLayouts,
1770
/// The shader asset handle.
1771
pub shader: Handle<Shader>,
1772
/// `MeshUniform`s are stored in arrays in buffers. If storage buffers are available, they
1773
/// are used and this will be `None`, otherwise uniform buffers will be used with batches
1774
/// of this many `MeshUniform`s, stored at dynamic offsets within the uniform buffer.
1775
/// Use code like this in custom shaders:
1776
/// ```wgsl
1777
/// ##ifdef PER_OBJECT_BUFFER_BATCH_SIZE
1778
/// @group(1) @binding(0) var<uniform> mesh: array<Mesh, #{PER_OBJECT_BUFFER_BATCH_SIZE}u>;
1779
/// ##else
1780
/// @group(1) @binding(0) var<storage> mesh: array<Mesh>;
1781
/// ##endif // PER_OBJECT_BUFFER_BATCH_SIZE
1782
/// ```
1783
pub per_object_buffer_batch_size: Option<u32>,
1784
1785
/// Whether binding arrays (a.k.a. bindless textures) are usable on the
1786
/// current render device.
1787
///
1788
/// This affects whether reflection probes can be used.
1789
pub binding_arrays_are_usable: bool,
1790
1791
/// Whether clustered decals are usable on the current render device.
1792
pub clustered_decals_are_usable: bool,
1793
1794
/// Whether skins will use uniform buffers on account of storage buffers
1795
/// being unavailable on this platform.
1796
pub skins_use_uniform_buffers: bool,
1797
}
1798
1799
impl FromWorld for MeshPipeline {
1800
fn from_world(world: &mut World) -> Self {
1801
let shader = load_embedded_asset!(world, "mesh.wgsl");
1802
let mut system_state: SystemState<(
1803
Res<RenderDevice>,
1804
Res<RenderAdapter>,
1805
Res<DefaultImageSampler>,
1806
Res<RenderQueue>,
1807
Res<MeshPipelineViewLayouts>,
1808
)> = SystemState::new(world);
1809
let (render_device, render_adapter, default_sampler, render_queue, view_layouts) =
1810
system_state.get_mut(world);
1811
1812
let clustered_forward_buffer_binding_type = render_device
1813
.get_supported_read_only_binding_type(CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT);
1814
1815
// A 1x1x1 'all 1.0' texture to use as a dummy texture to use in place of optional StandardMaterial textures
1816
let dummy_white_gpu_image = {
1817
let image = Image::default();
1818
let texture = render_device.create_texture(&image.texture_descriptor);
1819
let sampler = match image.sampler {
1820
ImageSampler::Default => (**default_sampler).clone(),
1821
ImageSampler::Descriptor(ref descriptor) => {
1822
render_device.create_sampler(&descriptor.as_wgpu())
1823
}
1824
};
1825
1826
if let Ok(format_size) = image.texture_descriptor.format.pixel_size() {
1827
render_queue.write_texture(
1828
texture.as_image_copy(),
1829
image.data.as_ref().expect("Image was created without data"),
1830
TexelCopyBufferLayout {
1831
offset: 0,
1832
bytes_per_row: Some(image.width() * format_size as u32),
1833
rows_per_image: None,
1834
},
1835
image.texture_descriptor.size,
1836
);
1837
}
1838
1839
let texture_view = texture.create_view(&TextureViewDescriptor::default());
1840
GpuImage {
1841
texture,
1842
texture_view,
1843
texture_format: image.texture_descriptor.format,
1844
sampler,
1845
size: image.texture_descriptor.size,
1846
mip_level_count: image.texture_descriptor.mip_level_count,
1847
}
1848
};
1849
1850
MeshPipeline {
1851
view_layouts: view_layouts.clone(),
1852
clustered_forward_buffer_binding_type,
1853
dummy_white_gpu_image,
1854
mesh_layouts: MeshLayouts::new(&render_device, &render_adapter),
1855
shader,
1856
per_object_buffer_batch_size: GpuArrayBuffer::<MeshUniform>::batch_size(&render_device),
1857
binding_arrays_are_usable: binding_arrays_are_usable(&render_device, &render_adapter),
1858
clustered_decals_are_usable: decal::clustered::clustered_decals_are_usable(
1859
&render_device,
1860
&render_adapter,
1861
),
1862
skins_use_uniform_buffers: skins_use_uniform_buffers(&render_device),
1863
}
1864
}
1865
}
1866
1867
impl MeshPipeline {
1868
pub fn get_image_texture<'a>(
1869
&'a self,
1870
gpu_images: &'a RenderAssets<GpuImage>,
1871
handle_option: &Option<Handle<Image>>,
1872
) -> Option<(&'a TextureView, &'a Sampler)> {
1873
if let Some(handle) = handle_option {
1874
let gpu_image = gpu_images.get(handle)?;
1875
Some((&gpu_image.texture_view, &gpu_image.sampler))
1876
} else {
1877
Some((
1878
&self.dummy_white_gpu_image.texture_view,
1879
&self.dummy_white_gpu_image.sampler,
1880
))
1881
}
1882
}
1883
1884
pub fn get_view_layout(
1885
&self,
1886
layout_key: MeshPipelineViewLayoutKey,
1887
) -> &MeshPipelineViewLayout {
1888
self.view_layouts.get_view_layout(layout_key)
1889
}
1890
}
1891
1892
impl GetBatchData for MeshPipeline {
1893
type Param = (
1894
SRes<RenderMeshInstances>,
1895
SRes<RenderLightmaps>,
1896
SRes<RenderAssets<RenderMesh>>,
1897
SRes<MeshAllocator>,
1898
SRes<SkinUniforms>,
1899
);
1900
// The material bind group ID, the mesh ID, and the lightmap ID,
1901
// respectively.
1902
type CompareData = (
1903
MaterialBindGroupIndex,
1904
AssetId<Mesh>,
1905
Option<LightmapSlabIndex>,
1906
);
1907
1908
type BufferData = MeshUniform;
1909
1910
fn get_batch_data(
1911
(mesh_instances, lightmaps, _, mesh_allocator, skin_uniforms): &SystemParamItem<
1912
Self::Param,
1913
>,
1914
(_entity, main_entity): (Entity, MainEntity),
1915
) -> Option<(Self::BufferData, Option<Self::CompareData>)> {
1916
let RenderMeshInstances::CpuBuilding(ref mesh_instances) = **mesh_instances else {
1917
error!(
1918
"`get_batch_data` should never be called in GPU mesh uniform \
1919
building mode"
1920
);
1921
return None;
1922
};
1923
let mesh_instance = mesh_instances.get(&main_entity)?;
1924
let first_vertex_index =
1925
match mesh_allocator.mesh_vertex_slice(&mesh_instance.mesh_asset_id) {
1926
Some(mesh_vertex_slice) => mesh_vertex_slice.range.start,
1927
None => 0,
1928
};
1929
let maybe_lightmap = lightmaps.render_lightmaps.get(&main_entity);
1930
1931
let current_skin_index = skin_uniforms.skin_index(main_entity);
1932
let material_bind_group_index = mesh_instance.material_bindings_index;
1933
1934
Some((
1935
MeshUniform::new(
1936
&mesh_instance.transforms,
1937
first_vertex_index,
1938
material_bind_group_index.slot,
1939
maybe_lightmap.map(|lightmap| (lightmap.slot_index, lightmap.uv_rect)),
1940
current_skin_index,
1941
Some(mesh_instance.tag),
1942
),
1943
mesh_instance.should_batch().then_some((
1944
material_bind_group_index.group,
1945
mesh_instance.mesh_asset_id,
1946
maybe_lightmap.map(|lightmap| lightmap.slab_index),
1947
)),
1948
))
1949
}
1950
}
1951
1952
impl GetFullBatchData for MeshPipeline {
1953
type BufferInputData = MeshInputUniform;
1954
1955
fn get_index_and_compare_data(
1956
(mesh_instances, lightmaps, _, _, _): &SystemParamItem<Self::Param>,
1957
main_entity: MainEntity,
1958
) -> Option<(NonMaxU32, Option<Self::CompareData>)> {
1959
// This should only be called during GPU building.
1960
let RenderMeshInstances::GpuBuilding(ref mesh_instances) = **mesh_instances else {
1961
error!(
1962
"`get_index_and_compare_data` should never be called in CPU mesh uniform building \
1963
mode"
1964
);
1965
return None;
1966
};
1967
1968
let mesh_instance = mesh_instances.get(&main_entity)?;
1969
let maybe_lightmap = lightmaps.render_lightmaps.get(&main_entity);
1970
1971
Some((
1972
mesh_instance.current_uniform_index,
1973
mesh_instance.should_batch().then_some((
1974
mesh_instance.material_bindings_index.group,
1975
mesh_instance.mesh_asset_id,
1976
maybe_lightmap.map(|lightmap| lightmap.slab_index),
1977
)),
1978
))
1979
}
1980
1981
fn get_binned_batch_data(
1982
(mesh_instances, lightmaps, _, mesh_allocator, skin_uniforms): &SystemParamItem<
1983
Self::Param,
1984
>,
1985
main_entity: MainEntity,
1986
) -> Option<Self::BufferData> {
1987
let RenderMeshInstances::CpuBuilding(ref mesh_instances) = **mesh_instances else {
1988
error!(
1989
"`get_binned_batch_data` should never be called in GPU mesh uniform building mode"
1990
);
1991
return None;
1992
};
1993
let mesh_instance = mesh_instances.get(&main_entity)?;
1994
let first_vertex_index =
1995
match mesh_allocator.mesh_vertex_slice(&mesh_instance.mesh_asset_id) {
1996
Some(mesh_vertex_slice) => mesh_vertex_slice.range.start,
1997
None => 0,
1998
};
1999
let maybe_lightmap = lightmaps.render_lightmaps.get(&main_entity);
2000
2001
let current_skin_index = skin_uniforms.skin_index(main_entity);
2002
2003
Some(MeshUniform::new(
2004
&mesh_instance.transforms,
2005
first_vertex_index,
2006
mesh_instance.material_bindings_index.slot,
2007
maybe_lightmap.map(|lightmap| (lightmap.slot_index, lightmap.uv_rect)),
2008
current_skin_index,
2009
Some(mesh_instance.tag),
2010
))
2011
}
2012
2013
fn get_binned_index(
2014
(mesh_instances, _, _, _, _): &SystemParamItem<Self::Param>,
2015
main_entity: MainEntity,
2016
) -> Option<NonMaxU32> {
2017
// This should only be called during GPU building.
2018
let RenderMeshInstances::GpuBuilding(ref mesh_instances) = **mesh_instances else {
2019
error!(
2020
"`get_binned_index` should never be called in CPU mesh uniform \
2021
building mode"
2022
);
2023
return None;
2024
};
2025
2026
mesh_instances
2027
.get(&main_entity)
2028
.map(|entity| entity.current_uniform_index)
2029
}
2030
2031
fn write_batch_indirect_parameters_metadata(
2032
indexed: bool,
2033
base_output_index: u32,
2034
batch_set_index: Option<NonMaxU32>,
2035
phase_indirect_parameters_buffers: &mut UntypedPhaseIndirectParametersBuffers,
2036
indirect_parameters_offset: u32,
2037
) {
2038
let indirect_parameters = IndirectParametersCpuMetadata {
2039
base_output_index,
2040
batch_set_index: match batch_set_index {
2041
Some(batch_set_index) => u32::from(batch_set_index),
2042
None => !0,
2043
},
2044
};
2045
2046
if indexed {
2047
phase_indirect_parameters_buffers
2048
.indexed
2049
.set(indirect_parameters_offset, indirect_parameters);
2050
} else {
2051
phase_indirect_parameters_buffers
2052
.non_indexed
2053
.set(indirect_parameters_offset, indirect_parameters);
2054
}
2055
}
2056
}
2057
2058
bitflags::bitflags! {
2059
#[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Hash)]
2060
#[repr(transparent)]
2061
// NOTE: Apparently quadro drivers support up to 64x MSAA.
2062
/// MSAA uses the highest 3 bits for the MSAA log2(sample count) to support up to 128x MSAA.
2063
pub struct MeshPipelineKey: u64 {
2064
// Nothing
2065
const NONE = 0;
2066
2067
// Inherited bits
2068
const MORPH_TARGETS = BaseMeshPipelineKey::MORPH_TARGETS.bits();
2069
2070
// Flag bits
2071
const HDR = 1 << 0;
2072
const TONEMAP_IN_SHADER = 1 << 1;
2073
const DEBAND_DITHER = 1 << 2;
2074
const DEPTH_PREPASS = 1 << 3;
2075
const NORMAL_PREPASS = 1 << 4;
2076
const DEFERRED_PREPASS = 1 << 5;
2077
const MOTION_VECTOR_PREPASS = 1 << 6;
2078
const MAY_DISCARD = 1 << 7; // Guards shader codepaths that may discard, allowing early depth tests in most cases
2079
// See: https://www.khronos.org/opengl/wiki/Early_Fragment_Test
2080
const ENVIRONMENT_MAP = 1 << 8;
2081
const SCREEN_SPACE_AMBIENT_OCCLUSION = 1 << 9;
2082
const UNCLIPPED_DEPTH_ORTHO = 1 << 10; // Disables depth clipping for use with directional light shadow views
2083
// Emulated via fragment shader depth on hardware that doesn't support it natively
2084
// See: https://www.w3.org/TR/webgpu/#depth-clipping and https://therealmjp.github.io/posts/shadow-maps/#disabling-z-clipping
2085
const TEMPORAL_JITTER = 1 << 11;
2086
const READS_VIEW_TRANSMISSION_TEXTURE = 1 << 12;
2087
const LIGHTMAPPED = 1 << 13;
2088
const LIGHTMAP_BICUBIC_SAMPLING = 1 << 14;
2089
const IRRADIANCE_VOLUME = 1 << 15;
2090
const VISIBILITY_RANGE_DITHER = 1 << 16;
2091
const SCREEN_SPACE_REFLECTIONS = 1 << 17;
2092
const HAS_PREVIOUS_SKIN = 1 << 18;
2093
const HAS_PREVIOUS_MORPH = 1 << 19;
2094
const OIT_ENABLED = 1 << 20;
2095
const DISTANCE_FOG = 1 << 21;
2096
const LAST_FLAG = Self::DISTANCE_FOG.bits();
2097
2098
// Bitfields
2099
const MSAA_RESERVED_BITS = Self::MSAA_MASK_BITS << Self::MSAA_SHIFT_BITS;
2100
const BLEND_RESERVED_BITS = Self::BLEND_MASK_BITS << Self::BLEND_SHIFT_BITS; // ← Bitmask reserving bits for the blend state
2101
const BLEND_OPAQUE = 0 << Self::BLEND_SHIFT_BITS; // ← Values are just sequential within the mask
2102
const BLEND_PREMULTIPLIED_ALPHA = 1 << Self::BLEND_SHIFT_BITS; // ← As blend states is on 3 bits, it can range from 0 to 7
2103
const BLEND_MULTIPLY = 2 << Self::BLEND_SHIFT_BITS; // ← See `BLEND_MASK_BITS` for the number of bits available
2104
const BLEND_ALPHA = 3 << Self::BLEND_SHIFT_BITS; //
2105
const BLEND_ALPHA_TO_COVERAGE = 4 << Self::BLEND_SHIFT_BITS; // ← We still have room for three more values without adding more bits
2106
const TONEMAP_METHOD_RESERVED_BITS = Self::TONEMAP_METHOD_MASK_BITS << Self::TONEMAP_METHOD_SHIFT_BITS;
2107
const TONEMAP_METHOD_NONE = 0 << Self::TONEMAP_METHOD_SHIFT_BITS;
2108
const TONEMAP_METHOD_REINHARD = 1 << Self::TONEMAP_METHOD_SHIFT_BITS;
2109
const TONEMAP_METHOD_REINHARD_LUMINANCE = 2 << Self::TONEMAP_METHOD_SHIFT_BITS;
2110
const TONEMAP_METHOD_ACES_FITTED = 3 << Self::TONEMAP_METHOD_SHIFT_BITS;
2111
const TONEMAP_METHOD_AGX = 4 << Self::TONEMAP_METHOD_SHIFT_BITS;
2112
const TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM = 5 << Self::TONEMAP_METHOD_SHIFT_BITS;
2113
const TONEMAP_METHOD_TONY_MC_MAPFACE = 6 << Self::TONEMAP_METHOD_SHIFT_BITS;
2114
const TONEMAP_METHOD_BLENDER_FILMIC = 7 << Self::TONEMAP_METHOD_SHIFT_BITS;
2115
const SHADOW_FILTER_METHOD_RESERVED_BITS = Self::SHADOW_FILTER_METHOD_MASK_BITS << Self::SHADOW_FILTER_METHOD_SHIFT_BITS;
2116
const SHADOW_FILTER_METHOD_HARDWARE_2X2 = 0 << Self::SHADOW_FILTER_METHOD_SHIFT_BITS;
2117
const SHADOW_FILTER_METHOD_GAUSSIAN = 1 << Self::SHADOW_FILTER_METHOD_SHIFT_BITS;
2118
const SHADOW_FILTER_METHOD_TEMPORAL = 2 << Self::SHADOW_FILTER_METHOD_SHIFT_BITS;
2119
const VIEW_PROJECTION_RESERVED_BITS = Self::VIEW_PROJECTION_MASK_BITS << Self::VIEW_PROJECTION_SHIFT_BITS;
2120
const VIEW_PROJECTION_NONSTANDARD = 0 << Self::VIEW_PROJECTION_SHIFT_BITS;
2121
const VIEW_PROJECTION_PERSPECTIVE = 1 << Self::VIEW_PROJECTION_SHIFT_BITS;
2122
const VIEW_PROJECTION_ORTHOGRAPHIC = 2 << Self::VIEW_PROJECTION_SHIFT_BITS;
2123
const VIEW_PROJECTION_RESERVED = 3 << Self::VIEW_PROJECTION_SHIFT_BITS;
2124
const SCREEN_SPACE_SPECULAR_TRANSMISSION_RESERVED_BITS = Self::SCREEN_SPACE_SPECULAR_TRANSMISSION_MASK_BITS << Self::SCREEN_SPACE_SPECULAR_TRANSMISSION_SHIFT_BITS;
2125
const SCREEN_SPACE_SPECULAR_TRANSMISSION_LOW = 0 << Self::SCREEN_SPACE_SPECULAR_TRANSMISSION_SHIFT_BITS;
2126
const SCREEN_SPACE_SPECULAR_TRANSMISSION_MEDIUM = 1 << Self::SCREEN_SPACE_SPECULAR_TRANSMISSION_SHIFT_BITS;
2127
const SCREEN_SPACE_SPECULAR_TRANSMISSION_HIGH = 2 << Self::SCREEN_SPACE_SPECULAR_TRANSMISSION_SHIFT_BITS;
2128
const SCREEN_SPACE_SPECULAR_TRANSMISSION_ULTRA = 3 << Self::SCREEN_SPACE_SPECULAR_TRANSMISSION_SHIFT_BITS;
2129
const ALL_RESERVED_BITS =
2130
Self::BLEND_RESERVED_BITS.bits() |
2131
Self::MSAA_RESERVED_BITS.bits() |
2132
Self::TONEMAP_METHOD_RESERVED_BITS.bits() |
2133
Self::SHADOW_FILTER_METHOD_RESERVED_BITS.bits() |
2134
Self::VIEW_PROJECTION_RESERVED_BITS.bits() |
2135
Self::SCREEN_SPACE_SPECULAR_TRANSMISSION_RESERVED_BITS.bits();
2136
}
2137
}
2138
2139
impl MeshPipelineKey {
2140
const MSAA_MASK_BITS: u64 = 0b111;
2141
const MSAA_SHIFT_BITS: u64 = Self::LAST_FLAG.bits().trailing_zeros() as u64 + 1;
2142
2143
const BLEND_MASK_BITS: u64 = 0b111;
2144
const BLEND_SHIFT_BITS: u64 = Self::MSAA_MASK_BITS.count_ones() as u64 + Self::MSAA_SHIFT_BITS;
2145
2146
const TONEMAP_METHOD_MASK_BITS: u64 = 0b111;
2147
const TONEMAP_METHOD_SHIFT_BITS: u64 =
2148
Self::BLEND_MASK_BITS.count_ones() as u64 + Self::BLEND_SHIFT_BITS;
2149
2150
const SHADOW_FILTER_METHOD_MASK_BITS: u64 = 0b11;
2151
const SHADOW_FILTER_METHOD_SHIFT_BITS: u64 =
2152
Self::TONEMAP_METHOD_MASK_BITS.count_ones() as u64 + Self::TONEMAP_METHOD_SHIFT_BITS;
2153
2154
const VIEW_PROJECTION_MASK_BITS: u64 = 0b11;
2155
const VIEW_PROJECTION_SHIFT_BITS: u64 = Self::SHADOW_FILTER_METHOD_MASK_BITS.count_ones()
2156
as u64
2157
+ Self::SHADOW_FILTER_METHOD_SHIFT_BITS;
2158
2159
const SCREEN_SPACE_SPECULAR_TRANSMISSION_MASK_BITS: u64 = 0b11;
2160
const SCREEN_SPACE_SPECULAR_TRANSMISSION_SHIFT_BITS: u64 =
2161
Self::VIEW_PROJECTION_MASK_BITS.count_ones() as u64 + Self::VIEW_PROJECTION_SHIFT_BITS;
2162
2163
pub fn from_msaa_samples(msaa_samples: u32) -> Self {
2164
let msaa_bits =
2165
(msaa_samples.trailing_zeros() as u64 & Self::MSAA_MASK_BITS) << Self::MSAA_SHIFT_BITS;
2166
Self::from_bits_retain(msaa_bits)
2167
}
2168
2169
pub fn from_hdr(hdr: bool) -> Self {
2170
if hdr {
2171
MeshPipelineKey::HDR
2172
} else {
2173
MeshPipelineKey::NONE
2174
}
2175
}
2176
2177
pub fn msaa_samples(&self) -> u32 {
2178
1 << ((self.bits() >> Self::MSAA_SHIFT_BITS) & Self::MSAA_MASK_BITS)
2179
}
2180
2181
pub fn from_primitive_topology(primitive_topology: PrimitiveTopology) -> Self {
2182
let primitive_topology_bits = ((primitive_topology as u64)
2183
& BaseMeshPipelineKey::PRIMITIVE_TOPOLOGY_MASK_BITS)
2184
<< BaseMeshPipelineKey::PRIMITIVE_TOPOLOGY_SHIFT_BITS;
2185
Self::from_bits_retain(primitive_topology_bits)
2186
}
2187
2188
pub fn primitive_topology(&self) -> PrimitiveTopology {
2189
let primitive_topology_bits = (self.bits()
2190
>> BaseMeshPipelineKey::PRIMITIVE_TOPOLOGY_SHIFT_BITS)
2191
& BaseMeshPipelineKey::PRIMITIVE_TOPOLOGY_MASK_BITS;
2192
match primitive_topology_bits {
2193
x if x == PrimitiveTopology::PointList as u64 => PrimitiveTopology::PointList,
2194
x if x == PrimitiveTopology::LineList as u64 => PrimitiveTopology::LineList,
2195
x if x == PrimitiveTopology::LineStrip as u64 => PrimitiveTopology::LineStrip,
2196
x if x == PrimitiveTopology::TriangleList as u64 => PrimitiveTopology::TriangleList,
2197
x if x == PrimitiveTopology::TriangleStrip as u64 => PrimitiveTopology::TriangleStrip,
2198
_ => PrimitiveTopology::default(),
2199
}
2200
}
2201
}
2202
2203
// Ensure that we didn't overflow the number of bits available in `MeshPipelineKey`.
2204
const_assert_eq!(
2205
(((MeshPipelineKey::LAST_FLAG.bits() << 1) - 1) | MeshPipelineKey::ALL_RESERVED_BITS.bits())
2206
& BaseMeshPipelineKey::all().bits(),
2207
0
2208
);
2209
2210
// Ensure that the reserved bits don't overlap with the topology bits
2211
const_assert_eq!(
2212
(BaseMeshPipelineKey::PRIMITIVE_TOPOLOGY_MASK_BITS
2213
<< BaseMeshPipelineKey::PRIMITIVE_TOPOLOGY_SHIFT_BITS)
2214
& MeshPipelineKey::ALL_RESERVED_BITS.bits(),
2215
0
2216
);
2217
2218
fn is_skinned(layout: &MeshVertexBufferLayoutRef) -> bool {
2219
layout.0.contains(Mesh::ATTRIBUTE_JOINT_INDEX)
2220
&& layout.0.contains(Mesh::ATTRIBUTE_JOINT_WEIGHT)
2221
}
2222
pub fn setup_morph_and_skinning_defs(
2223
mesh_layouts: &MeshLayouts,
2224
layout: &MeshVertexBufferLayoutRef,
2225
offset: u32,
2226
key: &MeshPipelineKey,
2227
shader_defs: &mut Vec<ShaderDefVal>,
2228
vertex_attributes: &mut Vec<VertexAttributeDescriptor>,
2229
skins_use_uniform_buffers: bool,
2230
) -> BindGroupLayout {
2231
let is_morphed = key.intersects(MeshPipelineKey::MORPH_TARGETS);
2232
let is_lightmapped = key.intersects(MeshPipelineKey::LIGHTMAPPED);
2233
let motion_vector_prepass = key.intersects(MeshPipelineKey::MOTION_VECTOR_PREPASS);
2234
2235
if skins_use_uniform_buffers {
2236
shader_defs.push("SKINS_USE_UNIFORM_BUFFERS".into());
2237
}
2238
2239
let mut add_skin_data = || {
2240
shader_defs.push("SKINNED".into());
2241
vertex_attributes.push(Mesh::ATTRIBUTE_JOINT_INDEX.at_shader_location(offset));
2242
vertex_attributes.push(Mesh::ATTRIBUTE_JOINT_WEIGHT.at_shader_location(offset + 1));
2243
};
2244
2245
match (
2246
is_skinned(layout),
2247
is_morphed,
2248
is_lightmapped,
2249
motion_vector_prepass,
2250
) {
2251
(true, false, _, true) => {
2252
add_skin_data();
2253
mesh_layouts.skinned_motion.clone()
2254
}
2255
(true, false, _, false) => {
2256
add_skin_data();
2257
mesh_layouts.skinned.clone()
2258
}
2259
(true, true, _, true) => {
2260
add_skin_data();
2261
shader_defs.push("MORPH_TARGETS".into());
2262
mesh_layouts.morphed_skinned_motion.clone()
2263
}
2264
(true, true, _, false) => {
2265
add_skin_data();
2266
shader_defs.push("MORPH_TARGETS".into());
2267
mesh_layouts.morphed_skinned.clone()
2268
}
2269
(false, true, _, true) => {
2270
shader_defs.push("MORPH_TARGETS".into());
2271
mesh_layouts.morphed_motion.clone()
2272
}
2273
(false, true, _, false) => {
2274
shader_defs.push("MORPH_TARGETS".into());
2275
mesh_layouts.morphed.clone()
2276
}
2277
(false, false, true, _) => mesh_layouts.lightmapped.clone(),
2278
(false, false, false, _) => mesh_layouts.model_only.clone(),
2279
}
2280
}
2281
2282
impl SpecializedMeshPipeline for MeshPipeline {
2283
type Key = MeshPipelineKey;
2284
2285
fn specialize(
2286
&self,
2287
key: Self::Key,
2288
layout: &MeshVertexBufferLayoutRef,
2289
) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
2290
let mut shader_defs = Vec::new();
2291
let mut vertex_attributes = Vec::new();
2292
2293
// Let the shader code know that it's running in a mesh pipeline.
2294
shader_defs.push("MESH_PIPELINE".into());
2295
2296
shader_defs.push("VERTEX_OUTPUT_INSTANCE_INDEX".into());
2297
2298
if layout.0.contains(Mesh::ATTRIBUTE_POSITION) {
2299
shader_defs.push("VERTEX_POSITIONS".into());
2300
vertex_attributes.push(Mesh::ATTRIBUTE_POSITION.at_shader_location(0));
2301
}
2302
2303
if layout.0.contains(Mesh::ATTRIBUTE_NORMAL) {
2304
shader_defs.push("VERTEX_NORMALS".into());
2305
vertex_attributes.push(Mesh::ATTRIBUTE_NORMAL.at_shader_location(1));
2306
}
2307
2308
if layout.0.contains(Mesh::ATTRIBUTE_UV_0) {
2309
shader_defs.push("VERTEX_UVS".into());
2310
shader_defs.push("VERTEX_UVS_A".into());
2311
vertex_attributes.push(Mesh::ATTRIBUTE_UV_0.at_shader_location(2));
2312
}
2313
2314
if layout.0.contains(Mesh::ATTRIBUTE_UV_1) {
2315
shader_defs.push("VERTEX_UVS".into());
2316
shader_defs.push("VERTEX_UVS_B".into());
2317
vertex_attributes.push(Mesh::ATTRIBUTE_UV_1.at_shader_location(3));
2318
}
2319
2320
if layout.0.contains(Mesh::ATTRIBUTE_TANGENT) {
2321
shader_defs.push("VERTEX_TANGENTS".into());
2322
vertex_attributes.push(Mesh::ATTRIBUTE_TANGENT.at_shader_location(4));
2323
}
2324
2325
if layout.0.contains(Mesh::ATTRIBUTE_COLOR) {
2326
shader_defs.push("VERTEX_COLORS".into());
2327
vertex_attributes.push(Mesh::ATTRIBUTE_COLOR.at_shader_location(5));
2328
}
2329
2330
if cfg!(feature = "pbr_transmission_textures") {
2331
shader_defs.push("PBR_TRANSMISSION_TEXTURES_SUPPORTED".into());
2332
}
2333
if cfg!(feature = "pbr_multi_layer_material_textures") {
2334
shader_defs.push("PBR_MULTI_LAYER_MATERIAL_TEXTURES_SUPPORTED".into());
2335
}
2336
if cfg!(feature = "pbr_anisotropy_texture") {
2337
shader_defs.push("PBR_ANISOTROPY_TEXTURE_SUPPORTED".into());
2338
}
2339
if cfg!(feature = "pbr_specular_textures") {
2340
shader_defs.push("PBR_SPECULAR_TEXTURES_SUPPORTED".into());
2341
}
2342
2343
let bind_group_layout = self.get_view_layout(key.into());
2344
let mut bind_group_layout = vec![
2345
bind_group_layout.main_layout.clone(),
2346
bind_group_layout.binding_array_layout.clone(),
2347
];
2348
2349
if key.msaa_samples() > 1 {
2350
shader_defs.push("MULTISAMPLED".into());
2351
};
2352
2353
bind_group_layout.push(setup_morph_and_skinning_defs(
2354
&self.mesh_layouts,
2355
layout,
2356
6,
2357
&key,
2358
&mut shader_defs,
2359
&mut vertex_attributes,
2360
self.skins_use_uniform_buffers,
2361
));
2362
2363
if key.contains(MeshPipelineKey::SCREEN_SPACE_AMBIENT_OCCLUSION) {
2364
shader_defs.push("SCREEN_SPACE_AMBIENT_OCCLUSION".into());
2365
}
2366
2367
let vertex_buffer_layout = layout.0.get_layout(&vertex_attributes)?;
2368
2369
let (label, blend, depth_write_enabled);
2370
let pass = key.intersection(MeshPipelineKey::BLEND_RESERVED_BITS);
2371
let (mut is_opaque, mut alpha_to_coverage_enabled) = (false, false);
2372
if key.contains(MeshPipelineKey::OIT_ENABLED) && pass == MeshPipelineKey::BLEND_ALPHA {
2373
label = "oit_mesh_pipeline".into();
2374
// TODO tail blending would need alpha blending
2375
blend = None;
2376
shader_defs.push("OIT_ENABLED".into());
2377
// TODO it should be possible to use this to combine MSAA and OIT
2378
// alpha_to_coverage_enabled = true;
2379
depth_write_enabled = false;
2380
} else if pass == MeshPipelineKey::BLEND_ALPHA {
2381
label = "alpha_blend_mesh_pipeline".into();
2382
blend = Some(BlendState::ALPHA_BLENDING);
2383
// For the transparent pass, fragments that are closer will be alpha blended
2384
// but their depth is not written to the depth buffer
2385
depth_write_enabled = false;
2386
} else if pass == MeshPipelineKey::BLEND_PREMULTIPLIED_ALPHA {
2387
label = "premultiplied_alpha_mesh_pipeline".into();
2388
blend = Some(BlendState::PREMULTIPLIED_ALPHA_BLENDING);
2389
shader_defs.push("PREMULTIPLY_ALPHA".into());
2390
shader_defs.push("BLEND_PREMULTIPLIED_ALPHA".into());
2391
// For the transparent pass, fragments that are closer will be alpha blended
2392
// but their depth is not written to the depth buffer
2393
depth_write_enabled = false;
2394
} else if pass == MeshPipelineKey::BLEND_MULTIPLY {
2395
label = "multiply_mesh_pipeline".into();
2396
blend = Some(BlendState {
2397
color: BlendComponent {
2398
src_factor: BlendFactor::Dst,
2399
dst_factor: BlendFactor::OneMinusSrcAlpha,
2400
operation: BlendOperation::Add,
2401
},
2402
alpha: BlendComponent::OVER,
2403
});
2404
shader_defs.push("PREMULTIPLY_ALPHA".into());
2405
shader_defs.push("BLEND_MULTIPLY".into());
2406
// For the multiply pass, fragments that are closer will be alpha blended
2407
// but their depth is not written to the depth buffer
2408
depth_write_enabled = false;
2409
} else if pass == MeshPipelineKey::BLEND_ALPHA_TO_COVERAGE {
2410
label = "alpha_to_coverage_mesh_pipeline".into();
2411
// BlendState::REPLACE is not needed here, and None will be potentially much faster in some cases
2412
blend = None;
2413
// For the opaque and alpha mask passes, fragments that are closer will replace
2414
// the current fragment value in the output and the depth is written to the
2415
// depth buffer
2416
depth_write_enabled = true;
2417
is_opaque = !key.contains(MeshPipelineKey::READS_VIEW_TRANSMISSION_TEXTURE);
2418
alpha_to_coverage_enabled = true;
2419
shader_defs.push("ALPHA_TO_COVERAGE".into());
2420
} else {
2421
label = "opaque_mesh_pipeline".into();
2422
// BlendState::REPLACE is not needed here, and None will be potentially much faster in some cases
2423
blend = None;
2424
// For the opaque and alpha mask passes, fragments that are closer will replace
2425
// the current fragment value in the output and the depth is written to the
2426
// depth buffer
2427
depth_write_enabled = true;
2428
is_opaque = !key.contains(MeshPipelineKey::READS_VIEW_TRANSMISSION_TEXTURE);
2429
}
2430
2431
if key.contains(MeshPipelineKey::NORMAL_PREPASS) {
2432
shader_defs.push("NORMAL_PREPASS".into());
2433
}
2434
2435
if key.contains(MeshPipelineKey::DEPTH_PREPASS) {
2436
shader_defs.push("DEPTH_PREPASS".into());
2437
}
2438
2439
if key.contains(MeshPipelineKey::MOTION_VECTOR_PREPASS) {
2440
shader_defs.push("MOTION_VECTOR_PREPASS".into());
2441
}
2442
2443
if key.contains(MeshPipelineKey::HAS_PREVIOUS_SKIN) {
2444
shader_defs.push("HAS_PREVIOUS_SKIN".into());
2445
}
2446
2447
if key.contains(MeshPipelineKey::HAS_PREVIOUS_MORPH) {
2448
shader_defs.push("HAS_PREVIOUS_MORPH".into());
2449
}
2450
2451
if key.contains(MeshPipelineKey::DEFERRED_PREPASS) {
2452
shader_defs.push("DEFERRED_PREPASS".into());
2453
}
2454
2455
if key.contains(MeshPipelineKey::NORMAL_PREPASS) && key.msaa_samples() == 1 && is_opaque {
2456
shader_defs.push("LOAD_PREPASS_NORMALS".into());
2457
}
2458
2459
let view_projection = key.intersection(MeshPipelineKey::VIEW_PROJECTION_RESERVED_BITS);
2460
if view_projection == MeshPipelineKey::VIEW_PROJECTION_NONSTANDARD {
2461
shader_defs.push("VIEW_PROJECTION_NONSTANDARD".into());
2462
} else if view_projection == MeshPipelineKey::VIEW_PROJECTION_PERSPECTIVE {
2463
shader_defs.push("VIEW_PROJECTION_PERSPECTIVE".into());
2464
} else if view_projection == MeshPipelineKey::VIEW_PROJECTION_ORTHOGRAPHIC {
2465
shader_defs.push("VIEW_PROJECTION_ORTHOGRAPHIC".into());
2466
}
2467
2468
#[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
2469
shader_defs.push("WEBGL2".into());
2470
2471
#[cfg(feature = "experimental_pbr_pcss")]
2472
shader_defs.push("PCSS_SAMPLERS_AVAILABLE".into());
2473
2474
if key.contains(MeshPipelineKey::TONEMAP_IN_SHADER) {
2475
shader_defs.push("TONEMAP_IN_SHADER".into());
2476
shader_defs.push(ShaderDefVal::UInt(
2477
"TONEMAPPING_LUT_TEXTURE_BINDING_INDEX".into(),
2478
TONEMAPPING_LUT_TEXTURE_BINDING_INDEX,
2479
));
2480
shader_defs.push(ShaderDefVal::UInt(
2481
"TONEMAPPING_LUT_SAMPLER_BINDING_INDEX".into(),
2482
TONEMAPPING_LUT_SAMPLER_BINDING_INDEX,
2483
));
2484
2485
let method = key.intersection(MeshPipelineKey::TONEMAP_METHOD_RESERVED_BITS);
2486
2487
if method == MeshPipelineKey::TONEMAP_METHOD_NONE {
2488
shader_defs.push("TONEMAP_METHOD_NONE".into());
2489
} else if method == MeshPipelineKey::TONEMAP_METHOD_REINHARD {
2490
shader_defs.push("TONEMAP_METHOD_REINHARD".into());
2491
} else if method == MeshPipelineKey::TONEMAP_METHOD_REINHARD_LUMINANCE {
2492
shader_defs.push("TONEMAP_METHOD_REINHARD_LUMINANCE".into());
2493
} else if method == MeshPipelineKey::TONEMAP_METHOD_ACES_FITTED {
2494
shader_defs.push("TONEMAP_METHOD_ACES_FITTED".into());
2495
} else if method == MeshPipelineKey::TONEMAP_METHOD_AGX {
2496
shader_defs.push("TONEMAP_METHOD_AGX".into());
2497
} else if method == MeshPipelineKey::TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM {
2498
shader_defs.push("TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM".into());
2499
} else if method == MeshPipelineKey::TONEMAP_METHOD_BLENDER_FILMIC {
2500
shader_defs.push("TONEMAP_METHOD_BLENDER_FILMIC".into());
2501
} else if method == MeshPipelineKey::TONEMAP_METHOD_TONY_MC_MAPFACE {
2502
shader_defs.push("TONEMAP_METHOD_TONY_MC_MAPFACE".into());
2503
}
2504
2505
// Debanding is tied to tonemapping in the shader, cannot run without it.
2506
if key.contains(MeshPipelineKey::DEBAND_DITHER) {
2507
shader_defs.push("DEBAND_DITHER".into());
2508
}
2509
}
2510
2511
if key.contains(MeshPipelineKey::MAY_DISCARD) {
2512
shader_defs.push("MAY_DISCARD".into());
2513
}
2514
2515
if key.contains(MeshPipelineKey::ENVIRONMENT_MAP) {
2516
shader_defs.push("ENVIRONMENT_MAP".into());
2517
}
2518
2519
if key.contains(MeshPipelineKey::IRRADIANCE_VOLUME) && IRRADIANCE_VOLUMES_ARE_USABLE {
2520
shader_defs.push("IRRADIANCE_VOLUME".into());
2521
}
2522
2523
if key.contains(MeshPipelineKey::LIGHTMAPPED) {
2524
shader_defs.push("LIGHTMAP".into());
2525
}
2526
if key.contains(MeshPipelineKey::LIGHTMAP_BICUBIC_SAMPLING) {
2527
shader_defs.push("LIGHTMAP_BICUBIC_SAMPLING".into());
2528
}
2529
2530
if key.contains(MeshPipelineKey::TEMPORAL_JITTER) {
2531
shader_defs.push("TEMPORAL_JITTER".into());
2532
}
2533
2534
let shadow_filter_method =
2535
key.intersection(MeshPipelineKey::SHADOW_FILTER_METHOD_RESERVED_BITS);
2536
if shadow_filter_method == MeshPipelineKey::SHADOW_FILTER_METHOD_HARDWARE_2X2 {
2537
shader_defs.push("SHADOW_FILTER_METHOD_HARDWARE_2X2".into());
2538
} else if shadow_filter_method == MeshPipelineKey::SHADOW_FILTER_METHOD_GAUSSIAN {
2539
shader_defs.push("SHADOW_FILTER_METHOD_GAUSSIAN".into());
2540
} else if shadow_filter_method == MeshPipelineKey::SHADOW_FILTER_METHOD_TEMPORAL {
2541
shader_defs.push("SHADOW_FILTER_METHOD_TEMPORAL".into());
2542
}
2543
2544
let blur_quality =
2545
key.intersection(MeshPipelineKey::SCREEN_SPACE_SPECULAR_TRANSMISSION_RESERVED_BITS);
2546
2547
shader_defs.push(ShaderDefVal::Int(
2548
"SCREEN_SPACE_SPECULAR_TRANSMISSION_BLUR_TAPS".into(),
2549
match blur_quality {
2550
MeshPipelineKey::SCREEN_SPACE_SPECULAR_TRANSMISSION_LOW => 4,
2551
MeshPipelineKey::SCREEN_SPACE_SPECULAR_TRANSMISSION_MEDIUM => 8,
2552
MeshPipelineKey::SCREEN_SPACE_SPECULAR_TRANSMISSION_HIGH => 16,
2553
MeshPipelineKey::SCREEN_SPACE_SPECULAR_TRANSMISSION_ULTRA => 32,
2554
_ => unreachable!(), // Not possible, since the mask is 2 bits, and we've covered all 4 cases
2555
},
2556
));
2557
2558
if key.contains(MeshPipelineKey::VISIBILITY_RANGE_DITHER) {
2559
shader_defs.push("VISIBILITY_RANGE_DITHER".into());
2560
}
2561
2562
if key.contains(MeshPipelineKey::DISTANCE_FOG) {
2563
shader_defs.push("DISTANCE_FOG".into());
2564
}
2565
2566
if self.binding_arrays_are_usable {
2567
shader_defs.push("MULTIPLE_LIGHT_PROBES_IN_ARRAY".into());
2568
shader_defs.push("MULTIPLE_LIGHTMAPS_IN_ARRAY".into());
2569
}
2570
2571
if IRRADIANCE_VOLUMES_ARE_USABLE {
2572
shader_defs.push("IRRADIANCE_VOLUMES_ARE_USABLE".into());
2573
}
2574
2575
if self.clustered_decals_are_usable {
2576
shader_defs.push("CLUSTERED_DECALS_ARE_USABLE".into());
2577
if cfg!(feature = "pbr_light_textures") {
2578
shader_defs.push("LIGHT_TEXTURES".into());
2579
}
2580
}
2581
2582
let format = if key.contains(MeshPipelineKey::HDR) {
2583
ViewTarget::TEXTURE_FORMAT_HDR
2584
} else {
2585
TextureFormat::bevy_default()
2586
};
2587
2588
// This is defined here so that custom shaders that use something other than
2589
// the mesh binding from bevy_pbr::mesh_bindings can easily make use of this
2590
// in their own shaders.
2591
if let Some(per_object_buffer_batch_size) = self.per_object_buffer_batch_size {
2592
shader_defs.push(ShaderDefVal::UInt(
2593
"PER_OBJECT_BUFFER_BATCH_SIZE".into(),
2594
per_object_buffer_batch_size,
2595
));
2596
}
2597
2598
Ok(RenderPipelineDescriptor {
2599
vertex: VertexState {
2600
shader: self.shader.clone(),
2601
shader_defs: shader_defs.clone(),
2602
buffers: vec![vertex_buffer_layout],
2603
..default()
2604
},
2605
fragment: Some(FragmentState {
2606
shader: self.shader.clone(),
2607
shader_defs,
2608
targets: vec![Some(ColorTargetState {
2609
format,
2610
blend,
2611
write_mask: ColorWrites::ALL,
2612
})],
2613
..default()
2614
}),
2615
layout: bind_group_layout,
2616
primitive: PrimitiveState {
2617
cull_mode: Some(Face::Back),
2618
unclipped_depth: false,
2619
topology: key.primitive_topology(),
2620
..default()
2621
},
2622
depth_stencil: Some(DepthStencilState {
2623
format: CORE_3D_DEPTH_FORMAT,
2624
depth_write_enabled,
2625
depth_compare: CompareFunction::GreaterEqual,
2626
stencil: StencilState {
2627
front: StencilFaceState::IGNORE,
2628
back: StencilFaceState::IGNORE,
2629
read_mask: 0,
2630
write_mask: 0,
2631
},
2632
bias: DepthBiasState {
2633
constant: 0,
2634
slope_scale: 0.0,
2635
clamp: 0.0,
2636
},
2637
}),
2638
multisample: MultisampleState {
2639
count: key.msaa_samples(),
2640
mask: !0,
2641
alpha_to_coverage_enabled,
2642
},
2643
label: Some(label),
2644
..default()
2645
})
2646
}
2647
}
2648
2649
/// The bind groups for meshes currently loaded.
2650
///
2651
/// If GPU mesh preprocessing isn't in use, these are global to the scene. If
2652
/// GPU mesh preprocessing is in use, these are specific to a single phase.
2653
#[derive(Default)]
2654
pub struct MeshPhaseBindGroups {
2655
model_only: Option<BindGroup>,
2656
skinned: Option<MeshBindGroupPair>,
2657
morph_targets: HashMap<AssetId<Mesh>, MeshBindGroupPair>,
2658
lightmaps: HashMap<LightmapSlabIndex, BindGroup>,
2659
}
2660
2661
pub struct MeshBindGroupPair {
2662
motion_vectors: BindGroup,
2663
no_motion_vectors: BindGroup,
2664
}
2665
2666
/// All bind groups for meshes currently loaded.
2667
#[derive(Resource)]
2668
pub enum MeshBindGroups {
2669
/// The bind groups for the meshes for the entire scene, if GPU mesh
2670
/// preprocessing isn't in use.
2671
CpuPreprocessing(MeshPhaseBindGroups),
2672
/// A mapping from the type ID of a phase (e.g. [`Opaque3d`]) to the mesh
2673
/// bind groups for that phase.
2674
GpuPreprocessing(TypeIdMap<MeshPhaseBindGroups>),
2675
}
2676
2677
impl MeshPhaseBindGroups {
2678
pub fn reset(&mut self) {
2679
self.model_only = None;
2680
self.skinned = None;
2681
self.morph_targets.clear();
2682
self.lightmaps.clear();
2683
}
2684
/// Get the `BindGroup` for `RenderMesh` with given `handle_id` and lightmap
2685
/// key `lightmap`.
2686
pub fn get(
2687
&self,
2688
asset_id: AssetId<Mesh>,
2689
lightmap: Option<LightmapSlabIndex>,
2690
is_skinned: bool,
2691
morph: bool,
2692
motion_vectors: bool,
2693
) -> Option<&BindGroup> {
2694
match (is_skinned, morph, lightmap) {
2695
(_, true, _) => self
2696
.morph_targets
2697
.get(&asset_id)
2698
.map(|bind_group_pair| bind_group_pair.get(motion_vectors)),
2699
(true, false, _) => self
2700
.skinned
2701
.as_ref()
2702
.map(|bind_group_pair| bind_group_pair.get(motion_vectors)),
2703
(false, false, Some(lightmap_slab)) => self.lightmaps.get(&lightmap_slab),
2704
(false, false, None) => self.model_only.as_ref(),
2705
}
2706
}
2707
}
2708
2709
impl MeshBindGroupPair {
2710
fn get(&self, motion_vectors: bool) -> &BindGroup {
2711
if motion_vectors {
2712
&self.motion_vectors
2713
} else {
2714
&self.no_motion_vectors
2715
}
2716
}
2717
}
2718
2719
/// Creates the per-mesh bind groups for each type of mesh and each phase.
2720
pub fn prepare_mesh_bind_groups(
2721
mut commands: Commands,
2722
meshes: Res<RenderAssets<RenderMesh>>,
2723
mesh_pipeline: Res<MeshPipeline>,
2724
render_device: Res<RenderDevice>,
2725
cpu_batched_instance_buffer: Option<
2726
Res<no_gpu_preprocessing::BatchedInstanceBuffer<MeshUniform>>,
2727
>,
2728
gpu_batched_instance_buffers: Option<
2729
Res<gpu_preprocessing::BatchedInstanceBuffers<MeshUniform, MeshInputUniform>>,
2730
>,
2731
skins_uniform: Res<SkinUniforms>,
2732
weights_uniform: Res<MorphUniforms>,
2733
mut render_lightmaps: ResMut<RenderLightmaps>,
2734
) {
2735
// CPU mesh preprocessing path.
2736
if let Some(cpu_batched_instance_buffer) = cpu_batched_instance_buffer
2737
&& let Some(instance_data_binding) = cpu_batched_instance_buffer
2738
.into_inner()
2739
.instance_data_binding()
2740
{
2741
// In this path, we only have a single set of bind groups for all phases.
2742
let cpu_preprocessing_mesh_bind_groups = prepare_mesh_bind_groups_for_phase(
2743
instance_data_binding,
2744
&meshes,
2745
&mesh_pipeline,
2746
&render_device,
2747
&skins_uniform,
2748
&weights_uniform,
2749
&mut render_lightmaps,
2750
);
2751
2752
commands.insert_resource(MeshBindGroups::CpuPreprocessing(
2753
cpu_preprocessing_mesh_bind_groups,
2754
));
2755
return;
2756
}
2757
2758
// GPU mesh preprocessing path.
2759
if let Some(gpu_batched_instance_buffers) = gpu_batched_instance_buffers {
2760
let mut gpu_preprocessing_mesh_bind_groups = TypeIdMap::default();
2761
2762
// Loop over each phase.
2763
for (phase_type_id, batched_phase_instance_buffers) in
2764
&gpu_batched_instance_buffers.phase_instance_buffers
2765
{
2766
let Some(instance_data_binding) =
2767
batched_phase_instance_buffers.instance_data_binding()
2768
else {
2769
continue;
2770
};
2771
2772
let mesh_phase_bind_groups = prepare_mesh_bind_groups_for_phase(
2773
instance_data_binding,
2774
&meshes,
2775
&mesh_pipeline,
2776
&render_device,
2777
&skins_uniform,
2778
&weights_uniform,
2779
&mut render_lightmaps,
2780
);
2781
2782
gpu_preprocessing_mesh_bind_groups.insert(*phase_type_id, mesh_phase_bind_groups);
2783
}
2784
2785
commands.insert_resource(MeshBindGroups::GpuPreprocessing(
2786
gpu_preprocessing_mesh_bind_groups,
2787
));
2788
}
2789
}
2790
2791
/// Creates the per-mesh bind groups for each type of mesh, for a single phase.
2792
fn prepare_mesh_bind_groups_for_phase(
2793
model: BindingResource,
2794
meshes: &RenderAssets<RenderMesh>,
2795
mesh_pipeline: &MeshPipeline,
2796
render_device: &RenderDevice,
2797
skins_uniform: &SkinUniforms,
2798
weights_uniform: &MorphUniforms,
2799
render_lightmaps: &mut RenderLightmaps,
2800
) -> MeshPhaseBindGroups {
2801
let layouts = &mesh_pipeline.mesh_layouts;
2802
2803
// TODO: Reuse allocations.
2804
let mut groups = MeshPhaseBindGroups {
2805
model_only: Some(layouts.model_only(render_device, &model)),
2806
..default()
2807
};
2808
2809
// Create the skinned mesh bind group with the current and previous buffers
2810
// (the latter being for motion vector computation).
2811
let (skin, prev_skin) = (&skins_uniform.current_buffer, &skins_uniform.prev_buffer);
2812
groups.skinned = Some(MeshBindGroupPair {
2813
motion_vectors: layouts.skinned_motion(render_device, &model, skin, prev_skin),
2814
no_motion_vectors: layouts.skinned(render_device, &model, skin),
2815
});
2816
2817
// Create the morphed bind groups just like we did for the skinned bind
2818
// group.
2819
if let Some(weights) = weights_uniform.current_buffer.buffer() {
2820
let prev_weights = weights_uniform.prev_buffer.buffer().unwrap_or(weights);
2821
for (id, gpu_mesh) in meshes.iter() {
2822
if let Some(targets) = gpu_mesh.morph_targets.as_ref() {
2823
let bind_group_pair = if is_skinned(&gpu_mesh.layout) {
2824
let prev_skin = &skins_uniform.prev_buffer;
2825
MeshBindGroupPair {
2826
motion_vectors: layouts.morphed_skinned_motion(
2827
render_device,
2828
&model,
2829
skin,
2830
weights,
2831
targets,
2832
prev_skin,
2833
prev_weights,
2834
),
2835
no_motion_vectors: layouts.morphed_skinned(
2836
render_device,
2837
&model,
2838
skin,
2839
weights,
2840
targets,
2841
),
2842
}
2843
} else {
2844
MeshBindGroupPair {
2845
motion_vectors: layouts.morphed_motion(
2846
render_device,
2847
&model,
2848
weights,
2849
targets,
2850
prev_weights,
2851
),
2852
no_motion_vectors: layouts.morphed(render_device, &model, weights, targets),
2853
}
2854
};
2855
groups.morph_targets.insert(id, bind_group_pair);
2856
}
2857
}
2858
}
2859
2860
// Create lightmap bindgroups. There will be one bindgroup for each slab.
2861
let bindless_supported = render_lightmaps.bindless_supported;
2862
for (lightmap_slab_id, lightmap_slab) in render_lightmaps.slabs.iter_mut().enumerate() {
2863
groups.lightmaps.insert(
2864
LightmapSlabIndex(NonMaxU32::new(lightmap_slab_id as u32).unwrap()),
2865
layouts.lightmapped(render_device, &model, lightmap_slab, bindless_supported),
2866
);
2867
}
2868
2869
groups
2870
}
2871
2872
pub struct SetMeshViewBindGroup<const I: usize>;
2873
impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetMeshViewBindGroup<I> {
2874
type Param = ();
2875
type ViewQuery = (
2876
Read<ViewUniformOffset>,
2877
Read<ViewLightsUniformOffset>,
2878
Read<ViewFogUniformOffset>,
2879
Read<ViewLightProbesUniformOffset>,
2880
Read<ViewScreenSpaceReflectionsUniformOffset>,
2881
Read<ViewEnvironmentMapUniformOffset>,
2882
Read<MeshViewBindGroup>,
2883
Option<Read<OrderIndependentTransparencySettingsOffset>>,
2884
);
2885
type ItemQuery = ();
2886
2887
#[inline]
2888
fn render<'w>(
2889
_item: &P,
2890
(
2891
view_uniform,
2892
view_lights,
2893
view_fog,
2894
view_light_probes,
2895
view_ssr,
2896
view_environment_map,
2897
mesh_view_bind_group,
2898
maybe_oit_layers_count_offset,
2899
): ROQueryItem<'w, '_, Self::ViewQuery>,
2900
_entity: Option<()>,
2901
_: SystemParamItem<'w, '_, Self::Param>,
2902
pass: &mut TrackedRenderPass<'w>,
2903
) -> RenderCommandResult {
2904
let mut offsets: SmallVec<[u32; 8]> = smallvec![
2905
view_uniform.offset,
2906
view_lights.offset,
2907
view_fog.offset,
2908
**view_light_probes,
2909
**view_ssr,
2910
**view_environment_map,
2911
];
2912
if let Some(layers_count_offset) = maybe_oit_layers_count_offset {
2913
offsets.push(layers_count_offset.offset);
2914
}
2915
pass.set_bind_group(I, &mesh_view_bind_group.main, &offsets);
2916
2917
RenderCommandResult::Success
2918
}
2919
}
2920
2921
pub struct SetMeshViewBindingArrayBindGroup<const I: usize>;
2922
impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetMeshViewBindingArrayBindGroup<I> {
2923
type Param = ();
2924
type ViewQuery = (Read<MeshViewBindGroup>,);
2925
type ItemQuery = ();
2926
2927
#[inline]
2928
fn render<'w>(
2929
_item: &P,
2930
(mesh_view_bind_group,): ROQueryItem<'w, '_, Self::ViewQuery>,
2931
_entity: Option<()>,
2932
_: SystemParamItem<'w, '_, Self::Param>,
2933
pass: &mut TrackedRenderPass<'w>,
2934
) -> RenderCommandResult {
2935
pass.set_bind_group(I, &mesh_view_bind_group.binding_array, &[]);
2936
2937
RenderCommandResult::Success
2938
}
2939
}
2940
2941
pub struct SetMeshViewEmptyBindGroup<const I: usize>;
2942
impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetMeshViewEmptyBindGroup<I> {
2943
type Param = ();
2944
type ViewQuery = (Read<MeshViewBindGroup>,);
2945
type ItemQuery = ();
2946
2947
#[inline]
2948
fn render<'w>(
2949
_item: &P,
2950
(mesh_view_bind_group,): ROQueryItem<'w, '_, Self::ViewQuery>,
2951
_entity: Option<()>,
2952
_: SystemParamItem<'w, '_, Self::Param>,
2953
pass: &mut TrackedRenderPass<'w>,
2954
) -> RenderCommandResult {
2955
pass.set_bind_group(I, &mesh_view_bind_group.empty, &[]);
2956
2957
RenderCommandResult::Success
2958
}
2959
}
2960
2961
pub struct SetMeshBindGroup<const I: usize>;
2962
impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetMeshBindGroup<I> {
2963
type Param = (
2964
SRes<RenderDevice>,
2965
SRes<MeshBindGroups>,
2966
SRes<RenderMeshInstances>,
2967
SRes<SkinUniforms>,
2968
SRes<MorphIndices>,
2969
SRes<RenderLightmaps>,
2970
);
2971
type ViewQuery = Has<MotionVectorPrepass>;
2972
type ItemQuery = ();
2973
2974
#[inline]
2975
fn render<'w>(
2976
item: &P,
2977
has_motion_vector_prepass: bool,
2978
_item_query: Option<()>,
2979
(
2980
render_device,
2981
bind_groups,
2982
mesh_instances,
2983
skin_uniforms,
2984
morph_indices,
2985
lightmaps,
2986
): SystemParamItem<'w, '_, Self::Param>,
2987
pass: &mut TrackedRenderPass<'w>,
2988
) -> RenderCommandResult {
2989
let bind_groups = bind_groups.into_inner();
2990
let mesh_instances = mesh_instances.into_inner();
2991
let skin_uniforms = skin_uniforms.into_inner();
2992
let morph_indices = morph_indices.into_inner();
2993
2994
let entity = &item.main_entity();
2995
2996
let Some(mesh_asset_id) = mesh_instances.mesh_asset_id(*entity) else {
2997
return RenderCommandResult::Success;
2998
};
2999
3000
let current_skin_byte_offset = skin_uniforms.skin_byte_offset(*entity);
3001
let current_morph_index = morph_indices.current.get(entity);
3002
let prev_morph_index = morph_indices.prev.get(entity);
3003
3004
let is_skinned = current_skin_byte_offset.is_some();
3005
let is_morphed = current_morph_index.is_some();
3006
3007
let lightmap_slab_index = lightmaps
3008
.render_lightmaps
3009
.get(entity)
3010
.map(|render_lightmap| render_lightmap.slab_index);
3011
3012
let Some(mesh_phase_bind_groups) = (match *bind_groups {
3013
MeshBindGroups::CpuPreprocessing(ref mesh_phase_bind_groups) => {
3014
Some(mesh_phase_bind_groups)
3015
}
3016
MeshBindGroups::GpuPreprocessing(ref mesh_phase_bind_groups) => {
3017
mesh_phase_bind_groups.get(&TypeId::of::<P>())
3018
}
3019
}) else {
3020
// This is harmless if e.g. we're rendering the `Shadow` phase and
3021
// there weren't any shadows.
3022
return RenderCommandResult::Success;
3023
};
3024
3025
let Some(bind_group) = mesh_phase_bind_groups.get(
3026
mesh_asset_id,
3027
lightmap_slab_index,
3028
is_skinned,
3029
is_morphed,
3030
has_motion_vector_prepass,
3031
) else {
3032
return RenderCommandResult::Failure(
3033
"The MeshBindGroups resource wasn't set in the render phase. \
3034
It should be set by the prepare_mesh_bind_group system.\n\
3035
This is a bevy bug! Please open an issue.",
3036
);
3037
};
3038
3039
let mut dynamic_offsets: [u32; 5] = Default::default();
3040
let mut offset_count = 0;
3041
if let PhaseItemExtraIndex::DynamicOffset(dynamic_offset) = item.extra_index() {
3042
dynamic_offsets[offset_count] = dynamic_offset;
3043
offset_count += 1;
3044
}
3045
if let Some(current_skin_index) = current_skin_byte_offset
3046
&& skins_use_uniform_buffers(&render_device)
3047
{
3048
dynamic_offsets[offset_count] = current_skin_index.byte_offset;
3049
offset_count += 1;
3050
}
3051
if let Some(current_morph_index) = current_morph_index {
3052
dynamic_offsets[offset_count] = current_morph_index.index;
3053
offset_count += 1;
3054
}
3055
3056
// Attach motion vectors if needed.
3057
if has_motion_vector_prepass {
3058
// Attach the previous skin index for motion vector computation.
3059
if skins_use_uniform_buffers(&render_device)
3060
&& let Some(current_skin_byte_offset) = current_skin_byte_offset
3061
{
3062
dynamic_offsets[offset_count] = current_skin_byte_offset.byte_offset;
3063
offset_count += 1;
3064
}
3065
3066
// Attach the previous morph index for motion vector computation. If
3067
// there isn't one, just use zero as the shader will ignore it.
3068
if current_morph_index.is_some() {
3069
match prev_morph_index {
3070
Some(prev_morph_index) => {
3071
dynamic_offsets[offset_count] = prev_morph_index.index;
3072
}
3073
None => dynamic_offsets[offset_count] = 0,
3074
}
3075
offset_count += 1;
3076
}
3077
}
3078
3079
pass.set_bind_group(I, bind_group, &dynamic_offsets[0..offset_count]);
3080
3081
RenderCommandResult::Success
3082
}
3083
}
3084
3085
pub struct DrawMesh;
3086
impl<P: PhaseItem> RenderCommand<P> for DrawMesh {
3087
type Param = (
3088
SRes<RenderAssets<RenderMesh>>,
3089
SRes<RenderMeshInstances>,
3090
SRes<IndirectParametersBuffers>,
3091
SRes<PipelineCache>,
3092
SRes<MeshAllocator>,
3093
Option<SRes<PreprocessPipelines>>,
3094
SRes<GpuPreprocessingSupport>,
3095
);
3096
type ViewQuery = Has<PreprocessBindGroups>;
3097
type ItemQuery = ();
3098
#[inline]
3099
fn render<'w>(
3100
item: &P,
3101
has_preprocess_bind_group: ROQueryItem<Self::ViewQuery>,
3102
_item_query: Option<()>,
3103
(
3104
meshes,
3105
mesh_instances,
3106
indirect_parameters_buffer,
3107
pipeline_cache,
3108
mesh_allocator,
3109
preprocess_pipelines,
3110
preprocessing_support,
3111
): SystemParamItem<'w, '_, Self::Param>,
3112
pass: &mut TrackedRenderPass<'w>,
3113
) -> RenderCommandResult {
3114
// If we're using GPU preprocessing, then we're dependent on that
3115
// compute shader having been run, which of course can only happen if
3116
// it's compiled. Otherwise, our mesh instance data won't be present.
3117
if let Some(preprocess_pipelines) = preprocess_pipelines
3118
&& (!has_preprocess_bind_group
3119
|| !preprocess_pipelines
3120
.pipelines_are_loaded(&pipeline_cache, &preprocessing_support))
3121
{
3122
return RenderCommandResult::Skip;
3123
}
3124
3125
let meshes = meshes.into_inner();
3126
let mesh_instances = mesh_instances.into_inner();
3127
let indirect_parameters_buffer = indirect_parameters_buffer.into_inner();
3128
let mesh_allocator = mesh_allocator.into_inner();
3129
3130
let Some(mesh_asset_id) = mesh_instances.mesh_asset_id(item.main_entity()) else {
3131
return RenderCommandResult::Skip;
3132
};
3133
let Some(gpu_mesh) = meshes.get(mesh_asset_id) else {
3134
return RenderCommandResult::Skip;
3135
};
3136
let Some(vertex_buffer_slice) = mesh_allocator.mesh_vertex_slice(&mesh_asset_id) else {
3137
return RenderCommandResult::Skip;
3138
};
3139
3140
pass.set_vertex_buffer(0, vertex_buffer_slice.buffer.slice(..));
3141
3142
let batch_range = item.batch_range();
3143
3144
// Draw either directly or indirectly, as appropriate. If we're in
3145
// indirect mode, we can additionally multi-draw. (We can't multi-draw
3146
// in direct mode because `wgpu` doesn't expose that functionality.)
3147
match &gpu_mesh.buffer_info {
3148
RenderMeshBufferInfo::Indexed {
3149
index_format,
3150
count,
3151
} => {
3152
let Some(index_buffer_slice) = mesh_allocator.mesh_index_slice(&mesh_asset_id)
3153
else {
3154
return RenderCommandResult::Skip;
3155
};
3156
3157
pass.set_index_buffer(index_buffer_slice.buffer.slice(..), 0, *index_format);
3158
3159
match item.extra_index() {
3160
PhaseItemExtraIndex::None | PhaseItemExtraIndex::DynamicOffset(_) => {
3161
pass.draw_indexed(
3162
index_buffer_slice.range.start
3163
..(index_buffer_slice.range.start + *count),
3164
vertex_buffer_slice.range.start as i32,
3165
batch_range.clone(),
3166
);
3167
}
3168
PhaseItemExtraIndex::IndirectParametersIndex {
3169
range: indirect_parameters_range,
3170
batch_set_index,
3171
} => {
3172
// Look up the indirect parameters buffer, as well as
3173
// the buffer we're going to use for
3174
// `multi_draw_indexed_indirect_count` (if available).
3175
let Some(phase_indirect_parameters_buffers) =
3176
indirect_parameters_buffer.get(&TypeId::of::<P>())
3177
else {
3178
warn!(
3179
"Not rendering mesh because indexed indirect parameters buffer \
3180
wasn't present for this phase",
3181
);
3182
return RenderCommandResult::Skip;
3183
};
3184
let (Some(indirect_parameters_buffer), Some(batch_sets_buffer)) = (
3185
phase_indirect_parameters_buffers.indexed.data_buffer(),
3186
phase_indirect_parameters_buffers
3187
.indexed
3188
.batch_sets_buffer(),
3189
) else {
3190
warn!(
3191
"Not rendering mesh because indexed indirect parameters buffer \
3192
wasn't present",
3193
);
3194
return RenderCommandResult::Skip;
3195
};
3196
3197
// Calculate the location of the indirect parameters
3198
// within the buffer.
3199
let indirect_parameters_offset = indirect_parameters_range.start as u64
3200
* size_of::<IndirectParametersIndexed>() as u64;
3201
let indirect_parameters_count =
3202
indirect_parameters_range.end - indirect_parameters_range.start;
3203
3204
// If we're using `multi_draw_indirect_count`, take the
3205
// number of batches from the appropriate position in
3206
// the batch sets buffer. Otherwise, supply the size of
3207
// the batch set.
3208
match batch_set_index {
3209
Some(batch_set_index) => {
3210
let count_offset = u32::from(batch_set_index)
3211
* (size_of::<IndirectBatchSet>() as u32);
3212
pass.multi_draw_indexed_indirect_count(
3213
indirect_parameters_buffer,
3214
indirect_parameters_offset,
3215
batch_sets_buffer,
3216
count_offset as u64,
3217
indirect_parameters_count,
3218
);
3219
}
3220
None => {
3221
pass.multi_draw_indexed_indirect(
3222
indirect_parameters_buffer,
3223
indirect_parameters_offset,
3224
indirect_parameters_count,
3225
);
3226
}
3227
}
3228
}
3229
}
3230
}
3231
3232
RenderMeshBufferInfo::NonIndexed => match item.extra_index() {
3233
PhaseItemExtraIndex::None | PhaseItemExtraIndex::DynamicOffset(_) => {
3234
pass.draw(vertex_buffer_slice.range, batch_range.clone());
3235
}
3236
PhaseItemExtraIndex::IndirectParametersIndex {
3237
range: indirect_parameters_range,
3238
batch_set_index,
3239
} => {
3240
// Look up the indirect parameters buffer, as well as the
3241
// buffer we're going to use for
3242
// `multi_draw_indirect_count` (if available).
3243
let Some(phase_indirect_parameters_buffers) =
3244
indirect_parameters_buffer.get(&TypeId::of::<P>())
3245
else {
3246
warn!(
3247
"Not rendering mesh because non-indexed indirect parameters buffer \
3248
wasn't present for this phase",
3249
);
3250
return RenderCommandResult::Skip;
3251
};
3252
let (Some(indirect_parameters_buffer), Some(batch_sets_buffer)) = (
3253
phase_indirect_parameters_buffers.non_indexed.data_buffer(),
3254
phase_indirect_parameters_buffers
3255
.non_indexed
3256
.batch_sets_buffer(),
3257
) else {
3258
warn!(
3259
"Not rendering mesh because non-indexed indirect parameters buffer \
3260
wasn't present"
3261
);
3262
return RenderCommandResult::Skip;
3263
};
3264
3265
// Calculate the location of the indirect parameters within
3266
// the buffer.
3267
let indirect_parameters_offset = indirect_parameters_range.start as u64
3268
* size_of::<IndirectParametersNonIndexed>() as u64;
3269
let indirect_parameters_count =
3270
indirect_parameters_range.end - indirect_parameters_range.start;
3271
3272
// If we're using `multi_draw_indirect_count`, take the
3273
// number of batches from the appropriate position in the
3274
// batch sets buffer. Otherwise, supply the size of the
3275
// batch set.
3276
match batch_set_index {
3277
Some(batch_set_index) => {
3278
let count_offset =
3279
u32::from(batch_set_index) * (size_of::<IndirectBatchSet>() as u32);
3280
pass.multi_draw_indirect_count(
3281
indirect_parameters_buffer,
3282
indirect_parameters_offset,
3283
batch_sets_buffer,
3284
count_offset as u64,
3285
indirect_parameters_count,
3286
);
3287
}
3288
None => {
3289
pass.multi_draw_indirect(
3290
indirect_parameters_buffer,
3291
indirect_parameters_offset,
3292
indirect_parameters_count,
3293
);
3294
}
3295
}
3296
}
3297
},
3298
}
3299
RenderCommandResult::Success
3300
}
3301
}
3302
3303
#[cfg(test)]
3304
mod tests {
3305
use super::MeshPipelineKey;
3306
#[test]
3307
fn mesh_key_msaa_samples() {
3308
for i in [1, 2, 4, 8, 16, 32, 64, 128] {
3309
assert_eq!(MeshPipelineKey::from_msaa_samples(i).msaa_samples(), i);
3310
}
3311
}
3312
}
3313
3314