1#![expect(deprecated)]
2mod 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
99pub struct MeshletPlugin {
128 pub cluster_buffer_slots: u32,
137}
138
139impl MeshletPlugin {
140 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 NodeMeshlet::Prepass,
267 Node3d::Prepass,
268 NodeMeshlet::DeferredPrepass,
270 Node3d::DeferredPrepass,
271 Node3d::CopyDeferredLightingId,
272 Node3d::EndPrepasses,
273 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#[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#[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 pub visibility: Visibility,
333 pub inherited_visibility: InheritedVisibility,
335 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 commands.entity(entity).insert((
376 MeshletViewMaterialsMainOpaquePass::default(),
377 MeshletViewMaterialsPrepass::default(),
378 MeshletViewMaterialsDeferredGBufferPrepass::default(),
379 ));
380 }
381 }
382}