1mod asset;
4#[cfg(feature = "meshlet_processor")]
5mod from_mesh;
6mod instance_manager;
7mod material_pipeline_prepare;
8mod material_shade_nodes;
9mod meshlet_mesh_manager;
10mod persistent_buffer;
11mod persistent_buffer_impls;
12mod pipelines;
13mod resource_manager;
14mod visibility_buffer_raster_node;
15
16pub 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
28pub(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
35pub use self::asset::{
36 MeshletMesh, MeshletMeshLoader, MeshletMeshSaver, MESHLET_MESH_ASSET_VERSION,
37};
38#[cfg(feature = "meshlet_processor")]
39pub use self::from_mesh::{
40 MeshToMeshletMeshConversionError, MESHLET_DEFAULT_VERTEX_POSITION_QUANTIZATION_FACTOR,
41};
42use 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};
59use crate::{
60 graph::NodePbr, meshlet::meshlet_mesh_manager::init_meshlet_mesh_manager,
61 PreviousGlobalTransform,
62};
63use bevy_app::{App, Plugin};
64use bevy_asset::{embedded_asset, AssetApp, AssetId, Handle};
65use bevy_camera::visibility::{self, Visibility, VisibilityClass};
66use bevy_core_pipeline::{
67 core_3d::graph::{Core3d, Node3d},
68 prepass::{DeferredPrepass, MotionVectorPrepass, NormalPrepass},
69};
70use bevy_derive::{Deref, DerefMut};
71use bevy_ecs::{
72 component::Component,
73 entity::Entity,
74 query::Has,
75 reflect::ReflectComponent,
76 schedule::IntoScheduleConfigs,
77 system::{Commands, Query, Res},
78};
79use bevy_reflect::{std_traits::ReflectDefault, Reflect};
80use 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};
87use bevy_shader::load_shader_library;
88use bevy_transform::components::Transform;
89use derive_more::From;
90use tracing::error;
91
92pub struct MeshletPlugin {
121 pub cluster_buffer_slots: u32,
130}
131
132impl MeshletPlugin {
133 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
144impl 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 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 NodeMeshlet::Prepass,
207 NodeMeshlet::DeferredPrepass,
209 Node3d::EndPrepasses,
210 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
248fn 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#[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>)]
264pub struct MeshletMesh3d(pub Handle<MeshletMesh>);
265
266impl From<MeshletMesh3d> for AssetId<MeshletMesh> {
267 fn from(mesh: MeshletMesh3d) -> Self {
268 mesh.id()
269 }
270}
271
272impl From<&MeshletMesh3d> for AssetId<MeshletMesh> {
273 fn from(mesh: &MeshletMesh3d) -> Self {
274 mesh.id()
275 }
276}
277
278fn 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 commands.entity(entity).insert((
301 MeshletViewMaterialsMainOpaquePass::default(),
302 MeshletViewMaterialsPrepass::default(),
303 MeshletViewMaterialsDeferredGBufferPrepass::default(),
304 ));
305 }
306 }
307}