Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_pbr/src/meshlet/mod.rs
6600 views
1
//! Render high-poly 3d meshes using an efficient GPU-driven method. See [`MeshletPlugin`] and [`MeshletMesh`] for details.
2
3
mod asset;
4
#[cfg(feature = "meshlet_processor")]
5
mod from_mesh;
6
mod instance_manager;
7
mod material_pipeline_prepare;
8
mod material_shade_nodes;
9
mod meshlet_mesh_manager;
10
mod persistent_buffer;
11
mod persistent_buffer_impls;
12
mod pipelines;
13
mod resource_manager;
14
mod visibility_buffer_raster_node;
15
16
pub mod graph {
17
use bevy_render::render_graph::RenderLabel;
18
19
#[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)]
20
pub enum NodeMeshlet {
21
VisibilityBufferRasterPass,
22
Prepass,
23
DeferredPrepass,
24
MainOpaquePass,
25
}
26
}
27
28
pub(crate) use self::{
29
instance_manager::{queue_material_meshlet_meshes, InstanceManager},
30
material_pipeline_prepare::{
31
prepare_material_meshlet_meshes_main_opaque_pass, prepare_material_meshlet_meshes_prepass,
32
},
33
};
34
35
pub use self::asset::{
36
MeshletMesh, MeshletMeshLoader, MeshletMeshSaver, MESHLET_MESH_ASSET_VERSION,
37
};
38
#[cfg(feature = "meshlet_processor")]
39
pub use self::from_mesh::{
40
MeshToMeshletMeshConversionError, MESHLET_DEFAULT_VERTEX_POSITION_QUANTIZATION_FACTOR,
41
};
42
use self::{
43
graph::NodeMeshlet,
44
instance_manager::extract_meshlet_mesh_entities,
45
material_pipeline_prepare::{
46
MeshletViewMaterialsDeferredGBufferPrepass, MeshletViewMaterialsMainOpaquePass,
47
MeshletViewMaterialsPrepass,
48
},
49
material_shade_nodes::{
50
MeshletDeferredGBufferPrepassNode, MeshletMainOpaquePass3dNode, MeshletPrepassNode,
51
},
52
meshlet_mesh_manager::perform_pending_meshlet_mesh_writes,
53
pipelines::*,
54
resource_manager::{
55
prepare_meshlet_per_frame_resources, prepare_meshlet_view_bind_groups, ResourceManager,
56
},
57
visibility_buffer_raster_node::MeshletVisibilityBufferRasterPassNode,
58
};
59
use crate::{
60
graph::NodePbr, meshlet::meshlet_mesh_manager::init_meshlet_mesh_manager,
61
PreviousGlobalTransform,
62
};
63
use bevy_app::{App, Plugin};
64
use bevy_asset::{embedded_asset, AssetApp, AssetId, Handle};
65
use bevy_camera::visibility::{self, Visibility, VisibilityClass};
66
use bevy_core_pipeline::{
67
core_3d::graph::{Core3d, Node3d},
68
prepass::{DeferredPrepass, MotionVectorPrepass, NormalPrepass},
69
};
70
use bevy_derive::{Deref, DerefMut};
71
use bevy_ecs::{
72
component::Component,
73
entity::Entity,
74
query::Has,
75
reflect::ReflectComponent,
76
schedule::IntoScheduleConfigs,
77
system::{Commands, Query, Res},
78
};
79
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
80
use bevy_render::{
81
render_graph::{RenderGraphExt, ViewNodeRunner},
82
renderer::RenderDevice,
83
settings::WgpuFeatures,
84
view::{prepare_view_targets, Msaa},
85
ExtractSchedule, Render, RenderApp, RenderStartup, RenderSystems,
86
};
87
use bevy_shader::load_shader_library;
88
use bevy_transform::components::Transform;
89
use derive_more::From;
90
use tracing::error;
91
92
/// Provides a plugin for rendering large amounts of high-poly 3d meshes using an efficient GPU-driven method. See also [`MeshletMesh`].
93
///
94
/// Rendering dense scenes made of high-poly meshes with thousands or millions of triangles is extremely expensive in Bevy's standard renderer.
95
/// Once meshes are pre-processed into a [`MeshletMesh`], this plugin can render these kinds of scenes very efficiently.
96
///
97
/// In comparison to Bevy's standard renderer:
98
/// * Much more efficient culling. Meshlets can be culled individually, instead of all or nothing culling for entire meshes at a time.
99
/// Additionally, occlusion culling can eliminate meshlets that would cause overdraw.
100
/// * Much more efficient batching. All geometry can be rasterized in a single draw.
101
/// * Scales better with large amounts of dense geometry and overdraw. Bevy's standard renderer will bottleneck sooner.
102
/// * Near-seamless level of detail (LOD).
103
/// * Much greater base overhead. Rendering will be slower and use more memory than Bevy's standard renderer
104
/// with small amounts of geometry and overdraw.
105
/// * Requires preprocessing meshes. See [`MeshletMesh`] for details.
106
/// * Limitations on the kinds of materials you can use. See [`MeshletMesh`] for details.
107
///
108
/// This plugin requires a fairly recent GPU that supports [`WgpuFeatures::TEXTURE_INT64_ATOMIC`].
109
///
110
/// This plugin currently works only on the Vulkan and Metal backends.
111
///
112
/// This plugin is not compatible with [`Msaa`]. Any camera rendering a [`MeshletMesh`] must have
113
/// [`Msaa`] set to [`Msaa::Off`].
114
///
115
/// Mixing forward+prepass and deferred rendering for opaque materials is not currently supported when using this plugin.
116
/// You must use one or the other by setting [`crate::DefaultOpaqueRendererMethod`].
117
/// Do not override [`crate::Material::opaque_render_method`] for any material when using this plugin.
118
///
119
/// ![A render of the Stanford dragon as a `MeshletMesh`](https://raw.githubusercontent.com/bevyengine/bevy/main/crates/bevy_pbr/src/meshlet/meshlet_preview.png)
120
pub struct MeshletPlugin {
121
/// The maximum amount of clusters that can be processed at once,
122
/// used to control the size of a pre-allocated GPU buffer.
123
///
124
/// If this number is too low, you'll see rendering artifacts like missing or blinking meshes.
125
///
126
/// Each cluster slot costs 4 bytes of VRAM.
127
///
128
/// Must not be greater than 2^25.
129
pub cluster_buffer_slots: u32,
130
}
131
132
impl MeshletPlugin {
133
/// [`WgpuFeatures`] required for this plugin to function.
134
pub fn required_wgpu_features() -> WgpuFeatures {
135
WgpuFeatures::TEXTURE_INT64_ATOMIC
136
| WgpuFeatures::TEXTURE_ATOMIC
137
| WgpuFeatures::SHADER_INT64
138
| WgpuFeatures::SUBGROUP
139
| WgpuFeatures::DEPTH_CLIP_CONTROL
140
| WgpuFeatures::PUSH_CONSTANTS
141
}
142
}
143
144
impl Plugin for MeshletPlugin {
145
fn build(&self, app: &mut App) {
146
#[cfg(target_endian = "big")]
147
compile_error!("MeshletPlugin is only supported on little-endian processors.");
148
149
if self.cluster_buffer_slots > 2_u32.pow(25) {
150
error!("MeshletPlugin::cluster_buffer_slots must not be greater than 2^25.");
151
std::process::exit(1);
152
}
153
154
load_shader_library!(app, "meshlet_bindings.wgsl");
155
load_shader_library!(app, "visibility_buffer_resolve.wgsl");
156
load_shader_library!(app, "meshlet_cull_shared.wgsl");
157
embedded_asset!(app, "clear_visibility_buffer.wgsl");
158
embedded_asset!(app, "cull_instances.wgsl");
159
embedded_asset!(app, "cull_bvh.wgsl");
160
embedded_asset!(app, "cull_clusters.wgsl");
161
embedded_asset!(app, "visibility_buffer_software_raster.wgsl");
162
embedded_asset!(app, "visibility_buffer_hardware_raster.wgsl");
163
embedded_asset!(app, "meshlet_mesh_material.wgsl");
164
embedded_asset!(app, "resolve_render_targets.wgsl");
165
embedded_asset!(app, "remap_1d_to_2d_dispatch.wgsl");
166
embedded_asset!(app, "fill_counts.wgsl");
167
168
app.init_asset::<MeshletMesh>()
169
.register_asset_loader(MeshletMeshLoader);
170
171
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
172
return;
173
};
174
175
// Create a variable here so we can move-capture it.
176
let cluster_buffer_slots = self.cluster_buffer_slots;
177
let init_resource_manager_system =
178
move |mut commands: Commands, render_device: Res<RenderDevice>| {
179
commands
180
.insert_resource(ResourceManager::new(cluster_buffer_slots, &render_device));
181
};
182
183
render_app
184
.add_render_graph_node::<MeshletVisibilityBufferRasterPassNode>(
185
Core3d,
186
NodeMeshlet::VisibilityBufferRasterPass,
187
)
188
.add_render_graph_node::<ViewNodeRunner<MeshletPrepassNode>>(
189
Core3d,
190
NodeMeshlet::Prepass,
191
)
192
.add_render_graph_node::<ViewNodeRunner<MeshletDeferredGBufferPrepassNode>>(
193
Core3d,
194
NodeMeshlet::DeferredPrepass,
195
)
196
.add_render_graph_node::<ViewNodeRunner<MeshletMainOpaquePass3dNode>>(
197
Core3d,
198
NodeMeshlet::MainOpaquePass,
199
)
200
.add_render_graph_edges(
201
Core3d,
202
(
203
NodeMeshlet::VisibilityBufferRasterPass,
204
NodePbr::EarlyShadowPass,
205
//
206
NodeMeshlet::Prepass,
207
//
208
NodeMeshlet::DeferredPrepass,
209
Node3d::EndPrepasses,
210
//
211
Node3d::StartMainPass,
212
NodeMeshlet::MainOpaquePass,
213
Node3d::MainOpaquePass,
214
Node3d::EndMainPass,
215
),
216
)
217
.insert_resource(InstanceManager::new())
218
.add_systems(
219
RenderStartup,
220
(
221
check_meshlet_features,
222
(
223
(init_resource_manager_system, init_meshlet_pipelines).chain(),
224
init_meshlet_mesh_manager,
225
),
226
)
227
.chain(),
228
)
229
.add_systems(ExtractSchedule, extract_meshlet_mesh_entities)
230
.add_systems(
231
Render,
232
(
233
perform_pending_meshlet_mesh_writes.in_set(RenderSystems::PrepareAssets),
234
configure_meshlet_views
235
.after(prepare_view_targets)
236
.in_set(RenderSystems::ManageViews),
237
prepare_meshlet_per_frame_resources.in_set(RenderSystems::PrepareResources),
238
prepare_meshlet_view_bind_groups.in_set(RenderSystems::PrepareBindGroups),
239
queue_material_meshlet_meshes.in_set(RenderSystems::QueueMeshes),
240
prepare_material_meshlet_meshes_main_opaque_pass
241
.in_set(RenderSystems::QueueMeshes)
242
.before(queue_material_meshlet_meshes),
243
),
244
);
245
}
246
}
247
248
fn check_meshlet_features(render_device: Res<RenderDevice>) {
249
let features = render_device.features();
250
if !features.contains(MeshletPlugin::required_wgpu_features()) {
251
error!(
252
"MeshletPlugin can't be used. GPU lacks support for required features: {:?}.",
253
MeshletPlugin::required_wgpu_features().difference(features)
254
);
255
std::process::exit(1);
256
}
257
}
258
259
/// The meshlet mesh equivalent of [`bevy_mesh::Mesh3d`].
260
#[derive(Component, Clone, Debug, Default, Deref, DerefMut, Reflect, PartialEq, Eq, From)]
261
#[reflect(Component, Default, Clone, PartialEq)]
262
#[require(Transform, PreviousGlobalTransform, Visibility, VisibilityClass)]
263
#[component(on_add = visibility::add_visibility_class::<MeshletMesh3d>)]
264
pub struct MeshletMesh3d(pub Handle<MeshletMesh>);
265
266
impl From<MeshletMesh3d> for AssetId<MeshletMesh> {
267
fn from(mesh: MeshletMesh3d) -> Self {
268
mesh.id()
269
}
270
}
271
272
impl From<&MeshletMesh3d> for AssetId<MeshletMesh> {
273
fn from(mesh: &MeshletMesh3d) -> Self {
274
mesh.id()
275
}
276
}
277
278
fn configure_meshlet_views(
279
mut views_3d: Query<(
280
Entity,
281
&Msaa,
282
Has<NormalPrepass>,
283
Has<MotionVectorPrepass>,
284
Has<DeferredPrepass>,
285
)>,
286
mut commands: Commands,
287
) {
288
for (entity, msaa, normal_prepass, motion_vector_prepass, deferred_prepass) in &mut views_3d {
289
if *msaa != Msaa::Off {
290
error!("MeshletPlugin can't be used with MSAA. Add Msaa::Off to your camera to use this plugin.");
291
std::process::exit(1);
292
}
293
294
if !(normal_prepass || motion_vector_prepass || deferred_prepass) {
295
commands
296
.entity(entity)
297
.insert(MeshletViewMaterialsMainOpaquePass::default());
298
} else {
299
// TODO: Should we add both Prepass and DeferredGBufferPrepass materials here, and in other systems/nodes?
300
commands.entity(entity).insert((
301
MeshletViewMaterialsMainOpaquePass::default(),
302
MeshletViewMaterialsPrepass::default(),
303
MeshletViewMaterialsDeferredGBufferPrepass::default(),
304
));
305
}
306
}
307
}
308
309