bevy_pbr/meshlet/
mod.rs

1#![expect(deprecated)]
2//! Render high-poly 3d meshes using an efficient GPU-driven method. See [`MeshletPlugin`] and [`MeshletMesh`] for details.
3
4mod asset;
5#[cfg(feature = "meshlet_processor")]
6mod from_mesh;
7mod instance_manager;
8mod material_pipeline_prepare;
9mod material_shade_nodes;
10mod meshlet_mesh_manager;
11mod persistent_buffer;
12mod persistent_buffer_impls;
13mod pipelines;
14mod resource_manager;
15mod visibility_buffer_raster_node;
16
17pub mod graph {
18    use bevy_render::render_graph::RenderLabel;
19
20    #[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)]
21    pub enum NodeMeshlet {
22        VisibilityBufferRasterPass,
23        Prepass,
24        DeferredPrepass,
25        MainOpaquePass,
26    }
27}
28
29pub(crate) use self::{
30    instance_manager::{queue_material_meshlet_meshes, InstanceManager},
31    material_pipeline_prepare::{
32        prepare_material_meshlet_meshes_main_opaque_pass, prepare_material_meshlet_meshes_prepass,
33    },
34};
35
36pub use self::asset::{
37    MeshletMesh, MeshletMeshLoader, MeshletMeshSaver, MESHLET_MESH_ASSET_VERSION,
38};
39#[cfg(feature = "meshlet_processor")]
40pub use self::from_mesh::{
41    MeshToMeshletMeshConversionError, MESHLET_DEFAULT_VERTEX_POSITION_QUANTIZATION_FACTOR,
42};
43
44use self::{
45    graph::NodeMeshlet,
46    instance_manager::extract_meshlet_mesh_entities,
47    material_pipeline_prepare::{
48        MeshletViewMaterialsDeferredGBufferPrepass, MeshletViewMaterialsMainOpaquePass,
49        MeshletViewMaterialsPrepass,
50    },
51    material_shade_nodes::{
52        MeshletDeferredGBufferPrepassNode, MeshletMainOpaquePass3dNode, MeshletPrepassNode,
53    },
54    meshlet_mesh_manager::{perform_pending_meshlet_mesh_writes, MeshletMeshManager},
55    pipelines::*,
56    resource_manager::{
57        prepare_meshlet_per_frame_resources, prepare_meshlet_view_bind_groups, ResourceManager,
58    },
59    visibility_buffer_raster_node::MeshletVisibilityBufferRasterPassNode,
60};
61use crate::{graph::NodePbr, Material, MeshMaterial3d, PreviousGlobalTransform};
62use bevy_app::{App, Plugin, PostUpdate};
63use bevy_asset::{load_internal_asset, AssetApp, AssetId, Handle};
64use bevy_core_pipeline::{
65    core_3d::graph::{Core3d, Node3d},
66    prepass::{DeferredPrepass, MotionVectorPrepass, NormalPrepass},
67};
68use bevy_derive::{Deref, DerefMut};
69use bevy_ecs::{
70    bundle::Bundle,
71    component::Component,
72    entity::Entity,
73    prelude::With,
74    query::Has,
75    reflect::ReflectComponent,
76    schedule::IntoSystemConfigs,
77    system::{Commands, Query},
78};
79use bevy_reflect::{std_traits::ReflectDefault, Reflect};
80use bevy_render::{
81    render_graph::{RenderGraphApp, ViewNodeRunner},
82    render_resource::Shader,
83    renderer::RenderDevice,
84    settings::WgpuFeatures,
85    view::{
86        check_visibility, prepare_view_targets, InheritedVisibility, Msaa, ViewVisibility,
87        Visibility, VisibilitySystems,
88    },
89    ExtractSchedule, Render, RenderApp, RenderSet,
90};
91use bevy_transform::components::{GlobalTransform, Transform};
92use bevy_utils::tracing::error;
93use derive_more::From;
94
95const MESHLET_BINDINGS_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(1325134235233421);
96const MESHLET_MESH_MATERIAL_SHADER_HANDLE: Handle<Shader> =
97    Handle::weak_from_u128(3325134235233421);
98
99/// Provides a plugin for rendering large amounts of high-poly 3d meshes using an efficient GPU-driven method. See also [`MeshletMesh`].
100///
101/// Rendering dense scenes made of high-poly meshes with thousands or millions of triangles is extremely expensive in Bevy's standard renderer.
102/// Once meshes are pre-processed into a [`MeshletMesh`], this plugin can render these kinds of scenes very efficiently.
103///
104/// In comparison to Bevy's standard renderer:
105/// * Much more efficient culling. Meshlets can be culled individually, instead of all or nothing culling for entire meshes at a time.
106///   Additionally, occlusion culling can eliminate meshlets that would cause overdraw.
107/// * Much more efficient batching. All geometry can be rasterized in a single draw.
108/// * Scales better with large amounts of dense geometry and overdraw. Bevy's standard renderer will bottleneck sooner.
109/// * Near-seamless level of detail (LOD).
110/// * Much greater base overhead. Rendering will be slower and use more memory than Bevy's standard renderer
111///   with small amounts of geometry and overdraw.
112/// * Requires preprocessing meshes. See [`MeshletMesh`] for details.
113/// * Limitations on the kinds of materials you can use. See [`MeshletMesh`] for details.
114///
115/// This plugin requires a fairly recent GPU that supports [`WgpuFeatures::SHADER_INT64_ATOMIC_MIN_MAX`].
116///
117/// This plugin currently works only on the Vulkan backend.
118///
119/// This plugin is not compatible with [`Msaa`]. Any camera rendering a [`MeshletMesh`] must have
120/// [`Msaa`] set to [`Msaa::Off`].
121///
122/// Mixing forward+prepass and deferred rendering for opaque materials is not currently supported when using this plugin.
123/// You must use one or the other by setting [`crate::DefaultOpaqueRendererMethod`].
124/// Do not override [`crate::Material::opaque_render_method`] for any material when using this plugin.
125///
126/// ![A render of the Stanford dragon as a `MeshletMesh`](https://raw.githubusercontent.com/bevyengine/bevy/main/crates/bevy_pbr/src/meshlet/meshlet_preview.png)
127pub struct MeshletPlugin {
128    /// The maximum amount of clusters that can be processed at once,
129    /// used to control the size of a pre-allocated GPU buffer.
130    ///
131    /// If this number is too low, you'll see rendering artifacts like missing or blinking meshes.
132    ///
133    /// Each cluster slot costs 4 bytes of VRAM.
134    ///
135    /// Must not be greater than 2^25.
136    pub cluster_buffer_slots: u32,
137}
138
139impl MeshletPlugin {
140    /// [`WgpuFeatures`] required for this plugin to function.
141    pub fn required_wgpu_features() -> WgpuFeatures {
142        WgpuFeatures::SHADER_INT64_ATOMIC_MIN_MAX
143            | WgpuFeatures::SHADER_INT64
144            | WgpuFeatures::SUBGROUP
145            | WgpuFeatures::PUSH_CONSTANTS
146    }
147}
148
149impl Plugin for MeshletPlugin {
150    fn build(&self, app: &mut App) {
151        #[cfg(target_endian = "big")]
152        compile_error!("MeshletPlugin is only supported on little-endian processors.");
153
154        if self.cluster_buffer_slots > 2_u32.pow(25) {
155            error!("MeshletPlugin::cluster_buffer_slots must not be greater than 2^25.");
156            std::process::exit(1);
157        }
158
159        load_internal_asset!(
160            app,
161            MESHLET_BINDINGS_SHADER_HANDLE,
162            "meshlet_bindings.wgsl",
163            Shader::from_wgsl
164        );
165        load_internal_asset!(
166            app,
167            super::MESHLET_VISIBILITY_BUFFER_RESOLVE_SHADER_HANDLE,
168            "visibility_buffer_resolve.wgsl",
169            Shader::from_wgsl
170        );
171        load_internal_asset!(
172            app,
173            MESHLET_FILL_CLUSTER_BUFFERS_SHADER_HANDLE,
174            "fill_cluster_buffers.wgsl",
175            Shader::from_wgsl
176        );
177        load_internal_asset!(
178            app,
179            MESHLET_CULLING_SHADER_HANDLE,
180            "cull_clusters.wgsl",
181            Shader::from_wgsl
182        );
183        load_internal_asset!(
184            app,
185            MESHLET_DOWNSAMPLE_DEPTH_SHADER_HANDLE,
186            "downsample_depth.wgsl",
187            Shader::from_wgsl
188        );
189        load_internal_asset!(
190            app,
191            MESHLET_VISIBILITY_BUFFER_SOFTWARE_RASTER_SHADER_HANDLE,
192            "visibility_buffer_software_raster.wgsl",
193            Shader::from_wgsl
194        );
195        load_internal_asset!(
196            app,
197            MESHLET_VISIBILITY_BUFFER_HARDWARE_RASTER_SHADER_HANDLE,
198            "visibility_buffer_hardware_raster.wgsl",
199            Shader::from_wgsl
200        );
201        load_internal_asset!(
202            app,
203            MESHLET_MESH_MATERIAL_SHADER_HANDLE,
204            "meshlet_mesh_material.wgsl",
205            Shader::from_wgsl
206        );
207        load_internal_asset!(
208            app,
209            MESHLET_RESOLVE_RENDER_TARGETS_SHADER_HANDLE,
210            "resolve_render_targets.wgsl",
211            Shader::from_wgsl
212        );
213        load_internal_asset!(
214            app,
215            MESHLET_REMAP_1D_TO_2D_DISPATCH_SHADER_HANDLE,
216            "remap_1d_to_2d_dispatch.wgsl",
217            Shader::from_wgsl
218        );
219
220        app.init_asset::<MeshletMesh>()
221            .register_asset_loader(MeshletMeshLoader)
222            .add_systems(
223                PostUpdate,
224                check_visibility::<With<MeshletMesh3d>>.in_set(VisibilitySystems::CheckVisibility),
225            );
226    }
227
228    fn finish(&self, app: &mut App) {
229        let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
230            return;
231        };
232
233        let render_device = render_app.world().resource::<RenderDevice>().clone();
234        let features = render_device.features();
235        if !features.contains(Self::required_wgpu_features()) {
236            error!(
237                "MeshletPlugin can't be used. GPU lacks support for required features: {:?}.",
238                Self::required_wgpu_features().difference(features)
239            );
240            std::process::exit(1);
241        }
242
243        render_app
244            .add_render_graph_node::<MeshletVisibilityBufferRasterPassNode>(
245                Core3d,
246                NodeMeshlet::VisibilityBufferRasterPass,
247            )
248            .add_render_graph_node::<ViewNodeRunner<MeshletPrepassNode>>(
249                Core3d,
250                NodeMeshlet::Prepass,
251            )
252            .add_render_graph_node::<ViewNodeRunner<MeshletDeferredGBufferPrepassNode>>(
253                Core3d,
254                NodeMeshlet::DeferredPrepass,
255            )
256            .add_render_graph_node::<ViewNodeRunner<MeshletMainOpaquePass3dNode>>(
257                Core3d,
258                NodeMeshlet::MainOpaquePass,
259            )
260            .add_render_graph_edges(
261                Core3d,
262                (
263                    NodeMeshlet::VisibilityBufferRasterPass,
264                    NodePbr::ShadowPass,
265                    //
266                    NodeMeshlet::Prepass,
267                    Node3d::Prepass,
268                    //
269                    NodeMeshlet::DeferredPrepass,
270                    Node3d::DeferredPrepass,
271                    Node3d::CopyDeferredLightingId,
272                    Node3d::EndPrepasses,
273                    //
274                    Node3d::StartMainPass,
275                    NodeMeshlet::MainOpaquePass,
276                    Node3d::MainOpaquePass,
277                    Node3d::EndMainPass,
278                ),
279            )
280            .init_resource::<MeshletMeshManager>()
281            .insert_resource(InstanceManager::new())
282            .insert_resource(ResourceManager::new(
283                self.cluster_buffer_slots,
284                &render_device,
285            ))
286            .init_resource::<MeshletPipelines>()
287            .add_systems(ExtractSchedule, extract_meshlet_mesh_entities)
288            .add_systems(
289                Render,
290                (
291                    perform_pending_meshlet_mesh_writes.in_set(RenderSet::PrepareAssets),
292                    configure_meshlet_views
293                        .after(prepare_view_targets)
294                        .in_set(RenderSet::ManageViews),
295                    prepare_meshlet_per_frame_resources.in_set(RenderSet::PrepareResources),
296                    prepare_meshlet_view_bind_groups.in_set(RenderSet::PrepareBindGroups),
297                ),
298            );
299    }
300}
301
302/// The meshlet mesh equivalent of [`bevy_render::mesh::Mesh3d`].
303#[derive(Component, Clone, Debug, Default, Deref, DerefMut, Reflect, PartialEq, Eq, From)]
304#[reflect(Component, Default)]
305#[require(Transform, PreviousGlobalTransform, Visibility)]
306pub struct MeshletMesh3d(pub Handle<MeshletMesh>);
307
308impl From<MeshletMesh3d> for AssetId<MeshletMesh> {
309    fn from(mesh: MeshletMesh3d) -> Self {
310        mesh.id()
311    }
312}
313
314impl From<&MeshletMesh3d> for AssetId<MeshletMesh> {
315    fn from(mesh: &MeshletMesh3d) -> Self {
316        mesh.id()
317    }
318}
319
320/// A component bundle for entities with a [`MeshletMesh`] and a [`Material`].
321#[derive(Bundle, Clone)]
322#[deprecated(
323    since = "0.15.0",
324    note = "Use the `MeshletMesh3d` and `MeshMaterial3d` components instead. Inserting them will now also insert the other components required by them automatically."
325)]
326pub struct MaterialMeshletMeshBundle<M: Material> {
327    pub meshlet_mesh: MeshletMesh3d,
328    pub material: MeshMaterial3d<M>,
329    pub transform: Transform,
330    pub global_transform: GlobalTransform,
331    /// User indication of whether an entity is visible
332    pub visibility: Visibility,
333    /// Inherited visibility of an entity.
334    pub inherited_visibility: InheritedVisibility,
335    /// Algorithmically-computed indication of whether an entity is visible and should be extracted for rendering
336    pub view_visibility: ViewVisibility,
337}
338
339impl<M: Material> Default for MaterialMeshletMeshBundle<M> {
340    fn default() -> Self {
341        Self {
342            meshlet_mesh: Default::default(),
343            material: Default::default(),
344            transform: Default::default(),
345            global_transform: Default::default(),
346            visibility: Default::default(),
347            inherited_visibility: Default::default(),
348            view_visibility: Default::default(),
349        }
350    }
351}
352
353fn configure_meshlet_views(
354    mut views_3d: Query<(
355        Entity,
356        &Msaa,
357        Has<NormalPrepass>,
358        Has<MotionVectorPrepass>,
359        Has<DeferredPrepass>,
360    )>,
361    mut commands: Commands,
362) {
363    for (entity, msaa, normal_prepass, motion_vector_prepass, deferred_prepass) in &mut views_3d {
364        if *msaa != Msaa::Off {
365            error!("MeshletPlugin can't be used with MSAA. Add Msaa::Off to your camera to use this plugin.");
366            std::process::exit(1);
367        }
368
369        if !(normal_prepass || motion_vector_prepass || deferred_prepass) {
370            commands
371                .entity(entity)
372                .insert(MeshletViewMaterialsMainOpaquePass::default());
373        } else {
374            // TODO: Should we add both Prepass and DeferredGBufferPrepass materials here, and in other systems/nodes?
375            commands.entity(entity).insert((
376                MeshletViewMaterialsMainOpaquePass::default(),
377                MeshletViewMaterialsPrepass::default(),
378                MeshletViewMaterialsDeferredGBufferPrepass::default(),
379            ));
380        }
381    }
382}