Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_pbr/src/meshlet/material_pipeline_prepare.rs
9492 views
1
use super::{
2
instance_manager::InstanceManager, pipelines::MeshletPipelines,
3
resource_manager::ResourceManager,
4
};
5
use crate::*;
6
use bevy_camera::{Camera3d, Projection};
7
use bevy_core_pipeline::{
8
prepass::{DeferredPrepass, DepthPrepass, MotionVectorPrepass, NormalPrepass},
9
tonemapping::{DebandDither, Tonemapping},
10
};
11
use bevy_derive::{Deref, DerefMut};
12
use bevy_light::{EnvironmentMapLight, IrradianceVolume, ShadowFilteringMethod};
13
use bevy_material::{
14
key::{ErasedMaterialPipelineKey, ErasedMeshPipelineKey},
15
OpaqueRendererMethod,
16
};
17
use bevy_mesh::VertexBufferLayout;
18
use bevy_mesh::{Mesh, MeshVertexBufferLayout, MeshVertexBufferLayoutRef, MeshVertexBufferLayouts};
19
use bevy_platform::collections::{HashMap, HashSet};
20
use bevy_render::erased_render_asset::ErasedRenderAssets;
21
use bevy_render::{camera::TemporalJitter, render_resource::*, view::ExtractedView};
22
use bevy_utils::default;
23
use core::any::{Any, TypeId};
24
25
/// A list of `(Material ID, Pipeline, BindGroup)` for a view for use in [`meshlet_main_opaque_pass`](`super::meshlet_main_opaque_pass`).
26
#[derive(Component, Deref, DerefMut, Default)]
27
pub struct MeshletViewMaterialsMainOpaquePass(pub Vec<(u32, CachedRenderPipelineId, BindGroup)>);
28
29
/// Prepare [`Material`] pipelines for [`MeshletMesh`](`super::MeshletMesh`) entities for use in [`meshlet_main_opaque_pass`](`super::meshlet_main_opaque_pass`),
30
/// and register the material with [`InstanceManager`].
31
pub fn prepare_material_meshlet_meshes_main_opaque_pass(
32
resource_manager: ResMut<ResourceManager>,
33
mut instance_manager: ResMut<InstanceManager>,
34
mut cache: Local<HashMap<(MeshPipelineKey, TypeId), CachedRenderPipelineId>>,
35
pipeline_cache: Res<PipelineCache>,
36
material_pipeline: Res<MaterialPipeline>,
37
mesh_pipeline: Res<MeshPipeline>,
38
render_materials: Res<ErasedRenderAssets<PreparedMaterial>>,
39
meshlet_pipelines: Res<MeshletPipelines>,
40
render_material_instances: Res<RenderMaterialInstances>,
41
material_bind_group_allocators: Res<MaterialBindGroupAllocators>,
42
mut mesh_vertex_buffer_layouts: ResMut<MeshVertexBufferLayouts>,
43
mut views: Query<
44
(
45
&mut MeshletViewMaterialsMainOpaquePass,
46
&ExtractedView,
47
Option<&Tonemapping>,
48
Option<&DebandDither>,
49
Option<&ShadowFilteringMethod>,
50
(Has<ScreenSpaceAmbientOcclusion>, Has<DistanceFog>),
51
(
52
Has<NormalPrepass>,
53
Has<DepthPrepass>,
54
Has<MotionVectorPrepass>,
55
Has<DeferredPrepass>,
56
),
57
Has<TemporalJitter>,
58
Option<&Projection>,
59
Has<RenderViewLightProbes<EnvironmentMapLight>>,
60
Has<RenderViewLightProbes<IrradianceVolume>>,
61
),
62
With<Camera3d>,
63
>,
64
) {
65
let fake_vertex_buffer_layout = &fake_vertex_buffer_layout(&mut mesh_vertex_buffer_layouts);
66
67
for (
68
mut materials,
69
view,
70
tonemapping,
71
dither,
72
shadow_filter_method,
73
(ssao, distance_fog),
74
(normal_prepass, depth_prepass, motion_vector_prepass, deferred_prepass),
75
temporal_jitter,
76
projection,
77
has_environment_maps,
78
has_irradiance_volumes,
79
) in &mut views
80
{
81
let mut view_key =
82
MeshPipelineKey::from_msaa_samples(1) | MeshPipelineKey::from_hdr(view.hdr);
83
84
if normal_prepass {
85
view_key |= MeshPipelineKey::NORMAL_PREPASS;
86
}
87
if depth_prepass {
88
view_key |= MeshPipelineKey::DEPTH_PREPASS;
89
}
90
if motion_vector_prepass {
91
view_key |= MeshPipelineKey::MOTION_VECTOR_PREPASS;
92
}
93
if deferred_prepass {
94
view_key |= MeshPipelineKey::DEFERRED_PREPASS;
95
}
96
97
if temporal_jitter {
98
view_key |= MeshPipelineKey::TEMPORAL_JITTER;
99
}
100
101
if has_environment_maps {
102
view_key |= MeshPipelineKey::ENVIRONMENT_MAP;
103
}
104
105
if has_irradiance_volumes {
106
view_key |= MeshPipelineKey::IRRADIANCE_VOLUME;
107
}
108
109
if let Some(projection) = projection {
110
view_key |= match projection {
111
Projection::Perspective(_) => MeshPipelineKey::VIEW_PROJECTION_PERSPECTIVE,
112
Projection::Orthographic(_) => MeshPipelineKey::VIEW_PROJECTION_ORTHOGRAPHIC,
113
Projection::Custom(_) => MeshPipelineKey::VIEW_PROJECTION_NONSTANDARD,
114
};
115
}
116
117
match shadow_filter_method.unwrap_or(&ShadowFilteringMethod::default()) {
118
ShadowFilteringMethod::Hardware2x2 => {
119
view_key |= MeshPipelineKey::SHADOW_FILTER_METHOD_HARDWARE_2X2;
120
}
121
ShadowFilteringMethod::Gaussian => {
122
view_key |= MeshPipelineKey::SHADOW_FILTER_METHOD_GAUSSIAN;
123
}
124
ShadowFilteringMethod::Temporal => {
125
view_key |= MeshPipelineKey::SHADOW_FILTER_METHOD_TEMPORAL;
126
}
127
}
128
129
if !view.hdr {
130
if let Some(tonemapping) = tonemapping {
131
view_key |= MeshPipelineKey::TONEMAP_IN_SHADER;
132
view_key |= tonemapping_pipeline_key(*tonemapping);
133
}
134
if let Some(DebandDither::Enabled) = dither {
135
view_key |= MeshPipelineKey::DEBAND_DITHER;
136
}
137
}
138
139
if ssao {
140
view_key |= MeshPipelineKey::SCREEN_SPACE_AMBIENT_OCCLUSION;
141
}
142
if distance_fog {
143
view_key |= MeshPipelineKey::DISTANCE_FOG;
144
}
145
146
view_key |= MeshPipelineKey::from_primitive_topology(PrimitiveTopology::TriangleList);
147
148
for material_id in render_material_instances
149
.instances
150
.values()
151
.map(|instance| instance.asset_id)
152
.collect::<HashSet<_>>()
153
{
154
let Some(material) = render_materials.get(material_id) else {
155
continue;
156
};
157
158
if material.properties.render_method != OpaqueRendererMethod::Forward
159
|| material.properties.alpha_mode != AlphaMode::Opaque
160
|| material.properties.reads_view_transmission_texture
161
{
162
continue;
163
}
164
165
let erased_key = ErasedMaterialPipelineKey {
166
mesh_key: ErasedMeshPipelineKey::new(view_key),
167
material_key: material.properties.material_key.clone(),
168
type_id: material_id.type_id(),
169
};
170
let material_pipeline_specializer = MaterialPipelineSpecializer {
171
pipeline: material_pipeline.clone(),
172
properties: material.properties.clone(),
173
};
174
let Ok(material_pipeline_descriptor) =
175
material_pipeline_specializer.specialize(erased_key, fake_vertex_buffer_layout)
176
else {
177
continue;
178
};
179
let material_fragment = material_pipeline_descriptor.fragment.unwrap();
180
181
let mut shader_defs = material_fragment.shader_defs;
182
shader_defs.push("MESHLET_MESH_MATERIAL_PASS".into());
183
184
let layout = mesh_pipeline.get_view_layout(view_key.into());
185
let layout = vec![
186
layout.main_layout.clone(),
187
layout.binding_array_layout.clone(),
188
resource_manager.material_shade_bind_group_layout.clone(),
189
material
190
.properties
191
.material_layout
192
.as_ref()
193
.unwrap()
194
.clone(),
195
];
196
197
let pipeline_descriptor = RenderPipelineDescriptor {
198
label: material_pipeline_descriptor.label,
199
layout,
200
immediate_size: 0,
201
vertex: VertexState {
202
shader: meshlet_pipelines.meshlet_mesh_material.clone(),
203
shader_defs: shader_defs.clone(),
204
entry_point: material_pipeline_descriptor.vertex.entry_point,
205
buffers: Vec::new(),
206
},
207
primitive: PrimitiveState::default(),
208
depth_stencil: Some(DepthStencilState {
209
format: TextureFormat::Depth16Unorm,
210
depth_write_enabled: false,
211
depth_compare: CompareFunction::Equal,
212
stencil: StencilState::default(),
213
bias: DepthBiasState::default(),
214
}),
215
multisample: MultisampleState::default(),
216
fragment: Some(FragmentState {
217
shader: match material.properties.get_shader(MeshletFragmentShader) {
218
Some(shader) => shader.clone(),
219
None => meshlet_pipelines.meshlet_mesh_material.clone(),
220
},
221
shader_defs,
222
entry_point: material_fragment.entry_point,
223
targets: material_fragment.targets,
224
}),
225
zero_initialize_workgroup_memory: false,
226
};
227
let type_id = material_id.type_id();
228
let Some(material_bind_group_allocator) = material_bind_group_allocators.get(&type_id)
229
else {
230
continue;
231
};
232
let material_id = instance_manager.get_material_id(material_id);
233
234
let pipeline_id = *cache.entry((view_key, type_id)).or_insert_with(|| {
235
pipeline_cache.queue_render_pipeline(pipeline_descriptor.clone())
236
});
237
238
let Some(material_bind_group) =
239
material_bind_group_allocator.get(material.binding.group)
240
else {
241
continue;
242
};
243
let Some(bind_group) = material_bind_group.bind_group() else {
244
continue;
245
};
246
247
materials.push((material_id, pipeline_id, (*bind_group).clone()));
248
}
249
}
250
}
251
252
/// A list of `(Material ID, Pipeline, BindGroup)` for a view for use in [`meshlet_prepass`](`super::meshlet_prepass`).
253
#[derive(Component, Deref, DerefMut, Default)]
254
pub struct MeshletViewMaterialsPrepass(pub Vec<(u32, CachedRenderPipelineId, BindGroup)>);
255
256
/// A list of `(Material ID, Pipeline, BindGroup)` for a view for use in [`meshlet_deferred_gbuffer_prepass`](`super::meshlet_deferred_gbuffer_prepass`).
257
#[derive(Component, Deref, DerefMut, Default)]
258
pub struct MeshletViewMaterialsDeferredGBufferPrepass(
259
pub Vec<(u32, CachedRenderPipelineId, BindGroup)>,
260
);
261
262
/// Prepare [`Material`] pipelines for [`MeshletMesh`](`super::MeshletMesh`) entities for use in [`meshlet_prepass`](`super::meshlet_prepass`),
263
/// and [`meshlet_deferred_gbuffer_prepass`](`super::meshlet_deferred_gbuffer_prepass`) and register the material with [`InstanceManager`].
264
pub fn prepare_material_meshlet_meshes_prepass(
265
resource_manager: ResMut<ResourceManager>,
266
mut instance_manager: ResMut<InstanceManager>,
267
mut cache: Local<HashMap<(MeshPipelineKey, TypeId), CachedRenderPipelineId>>,
268
pipeline_cache: Res<PipelineCache>,
269
prepass_pipeline: Res<PrepassPipeline>,
270
material_bind_group_allocators: Res<MaterialBindGroupAllocators>,
271
render_materials: Res<ErasedRenderAssets<PreparedMaterial>>,
272
meshlet_pipelines: Res<MeshletPipelines>,
273
render_material_instances: Res<RenderMaterialInstances>,
274
mut mesh_vertex_buffer_layouts: ResMut<MeshVertexBufferLayouts>,
275
mut views: Query<
276
(
277
&mut MeshletViewMaterialsPrepass,
278
&mut MeshletViewMaterialsDeferredGBufferPrepass,
279
&ExtractedView,
280
AnyOf<(&NormalPrepass, &MotionVectorPrepass, &DeferredPrepass)>,
281
),
282
With<Camera3d>,
283
>,
284
) {
285
let fake_vertex_buffer_layout = &fake_vertex_buffer_layout(&mut mesh_vertex_buffer_layouts);
286
287
for (
288
mut materials,
289
mut deferred_materials,
290
view,
291
(normal_prepass, motion_vector_prepass, deferred_prepass),
292
) in &mut views
293
{
294
let mut view_key =
295
MeshPipelineKey::from_msaa_samples(1) | MeshPipelineKey::from_hdr(view.hdr);
296
297
if normal_prepass.is_some() {
298
view_key |= MeshPipelineKey::NORMAL_PREPASS;
299
}
300
if motion_vector_prepass.is_some() {
301
view_key |= MeshPipelineKey::MOTION_VECTOR_PREPASS;
302
}
303
304
view_key |= MeshPipelineKey::from_primitive_topology(PrimitiveTopology::TriangleList);
305
306
for material_id in render_material_instances
307
.instances
308
.values()
309
.map(|instance| instance.asset_id)
310
.collect::<HashSet<_>>()
311
{
312
let Some(material) = render_materials.get(material_id) else {
313
continue;
314
};
315
let Some(material_bind_group_allocator) =
316
material_bind_group_allocators.get(&material_id.type_id())
317
else {
318
continue;
319
};
320
321
if material.properties.alpha_mode != AlphaMode::Opaque
322
|| material.properties.reads_view_transmission_texture
323
{
324
continue;
325
}
326
327
let material_wants_deferred = matches!(
328
material.properties.render_method,
329
OpaqueRendererMethod::Deferred
330
);
331
if deferred_prepass.is_some() && material_wants_deferred {
332
view_key |= MeshPipelineKey::DEFERRED_PREPASS;
333
} else if normal_prepass.is_none() && motion_vector_prepass.is_none() {
334
continue;
335
}
336
337
let erased_key = ErasedMaterialPipelineKey {
338
mesh_key: ErasedMeshPipelineKey::new(view_key),
339
material_key: material.properties.material_key.clone(),
340
type_id: material_id.type_id(),
341
};
342
let material_pipeline_specializer = PrepassPipelineSpecializer {
343
pipeline: prepass_pipeline.clone(),
344
properties: material.properties.clone(),
345
};
346
let Ok(material_pipeline_descriptor) =
347
material_pipeline_specializer.specialize(erased_key, fake_vertex_buffer_layout)
348
else {
349
continue;
350
};
351
let material_fragment = material_pipeline_descriptor.fragment.unwrap();
352
353
let mut shader_defs = material_fragment.shader_defs;
354
shader_defs.push("MESHLET_MESH_MATERIAL_PASS".into());
355
356
let view_layout = if view_key.contains(MeshPipelineKey::MOTION_VECTOR_PREPASS) {
357
prepass_pipeline.view_layout_motion_vectors.clone()
358
} else {
359
prepass_pipeline.view_layout_no_motion_vectors.clone()
360
};
361
362
let fragment_shader = if view_key.contains(MeshPipelineKey::DEFERRED_PREPASS) {
363
material
364
.properties
365
.get_shader(MeshletDeferredFragmentShader)
366
.unwrap_or(meshlet_pipelines.meshlet_mesh_material.clone())
367
} else {
368
material
369
.properties
370
.get_shader(MeshletPrepassFragmentShader)
371
.unwrap_or(meshlet_pipelines.meshlet_mesh_material.clone())
372
};
373
374
let entry_point = if fragment_shader == meshlet_pipelines.meshlet_mesh_material {
375
material_fragment.entry_point.clone()
376
} else {
377
None
378
};
379
380
let pipeline_descriptor = RenderPipelineDescriptor {
381
label: material_pipeline_descriptor.label,
382
layout: vec![
383
view_layout,
384
prepass_pipeline.empty_layout.clone(),
385
resource_manager.material_shade_bind_group_layout.clone(),
386
material
387
.properties
388
.material_layout
389
.as_ref()
390
.unwrap()
391
.clone(),
392
],
393
vertex: VertexState {
394
shader: meshlet_pipelines.meshlet_mesh_material.clone(),
395
shader_defs: shader_defs.clone(),
396
entry_point: material_pipeline_descriptor.vertex.entry_point,
397
..default()
398
},
399
primitive: PrimitiveState::default(),
400
depth_stencil: Some(DepthStencilState {
401
format: TextureFormat::Depth16Unorm,
402
depth_write_enabled: false,
403
depth_compare: CompareFunction::Equal,
404
stencil: StencilState::default(),
405
bias: DepthBiasState::default(),
406
}),
407
fragment: Some(FragmentState {
408
shader: fragment_shader,
409
shader_defs,
410
entry_point,
411
targets: material_fragment.targets,
412
}),
413
..default()
414
};
415
416
let material_id = instance_manager.get_material_id(material_id);
417
418
let pipeline_id = *cache
419
.entry((view_key, material_id.type_id()))
420
.or_insert_with(|| {
421
pipeline_cache.queue_render_pipeline(pipeline_descriptor.clone())
422
});
423
424
let Some(material_bind_group) =
425
material_bind_group_allocator.get(material.binding.group)
426
else {
427
continue;
428
};
429
let Some(bind_group) = material_bind_group.bind_group() else {
430
continue;
431
};
432
433
let item = (material_id, pipeline_id, (*bind_group).clone());
434
if view_key.contains(MeshPipelineKey::DEFERRED_PREPASS) {
435
deferred_materials.push(item);
436
} else {
437
materials.push(item);
438
}
439
}
440
}
441
}
442
443
// Meshlet materials don't use a traditional vertex buffer, but the material specialization requires one.
444
fn fake_vertex_buffer_layout(layouts: &mut MeshVertexBufferLayouts) -> MeshVertexBufferLayoutRef {
445
layouts.insert(MeshVertexBufferLayout::new(
446
vec![
447
Mesh::ATTRIBUTE_POSITION.id,
448
Mesh::ATTRIBUTE_NORMAL.id,
449
Mesh::ATTRIBUTE_UV_0.id,
450
Mesh::ATTRIBUTE_TANGENT.id,
451
],
452
VertexBufferLayout {
453
array_stride: 48,
454
step_mode: VertexStepMode::Vertex,
455
attributes: vec![
456
VertexAttribute {
457
format: Mesh::ATTRIBUTE_POSITION.format,
458
offset: 0,
459
shader_location: 0,
460
},
461
VertexAttribute {
462
format: Mesh::ATTRIBUTE_NORMAL.format,
463
offset: 12,
464
shader_location: 1,
465
},
466
VertexAttribute {
467
format: Mesh::ATTRIBUTE_UV_0.format,
468
offset: 24,
469
shader_location: 2,
470
},
471
VertexAttribute {
472
format: Mesh::ATTRIBUTE_TANGENT.format,
473
offset: 32,
474
shader_location: 3,
475
},
476
],
477
},
478
))
479
}
480
481