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