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