specialized_mesh_pipeline/
specialized_mesh_pipeline.rs1use bevy::{
10 asset::RenderAssetUsages,
11 camera::visibility::{self, VisibilityClass},
12 core_pipeline::core_3d::{Opaque3d, Opaque3dBatchSetKey, Opaque3dBinKey, CORE_3D_DEPTH_FORMAT},
13 ecs::component::Tick,
14 math::{vec3, vec4},
15 mesh::{Indices, MeshVertexBufferLayoutRef, PrimitiveTopology},
16 pbr::{
17 DrawMesh, MeshPipeline, MeshPipelineKey, MeshPipelineViewLayoutKey, RenderMeshInstances,
18 SetMeshBindGroup, SetMeshViewBindGroup, SetMeshViewEmptyBindGroup,
19 },
20 prelude::*,
21 render::{
22 batching::gpu_preprocessing::GpuPreprocessingSupport,
23 extract_component::{ExtractComponent, ExtractComponentPlugin},
24 mesh::{allocator::MeshAllocator, RenderMesh},
25 render_asset::RenderAssets,
26 render_phase::{
27 AddRenderCommand, BinnedRenderPhaseType, DrawFunctions, SetItemPipeline,
28 ViewBinnedRenderPhases,
29 },
30 render_resource::{
31 ColorTargetState, ColorWrites, CompareFunction, DepthStencilState, Face, FragmentState,
32 FrontFace, MultisampleState, PipelineCache, PolygonMode, PrimitiveState,
33 RenderPipelineDescriptor, SpecializedMeshPipeline, SpecializedMeshPipelineError,
34 SpecializedMeshPipelines, TextureFormat, VertexState,
35 },
36 view::{ExtractedView, RenderVisibleEntities, ViewTarget},
37 Render, RenderApp, RenderStartup, RenderSystems,
38 },
39};
40
41const SHADER_ASSET_PATH: &str = "shaders/specialized_mesh_pipeline.wgsl";
42
43fn main() {
44 App::new()
45 .add_plugins(DefaultPlugins)
46 .add_plugins(CustomRenderedMeshPipelinePlugin)
47 .add_systems(Startup, setup)
48 .run();
49}
50
51fn setup(mut commands: Commands, mut meshes: ResMut<Assets<Mesh>>) {
53 let mesh = Mesh::new(
57 PrimitiveTopology::TriangleList,
58 RenderAssetUsages::default(),
59 )
60 .with_inserted_indices(Indices::U32(vec![0, 1, 2]))
61 .with_inserted_attribute(
62 Mesh::ATTRIBUTE_POSITION,
63 vec![
64 vec3(-0.5, -0.5, 0.0),
65 vec3(0.5, -0.5, 0.0),
66 vec3(0.0, 0.25, 0.0),
67 ],
68 )
69 .with_inserted_attribute(
70 Mesh::ATTRIBUTE_COLOR,
71 vec![
72 vec4(1.0, 0.0, 0.0, 1.0),
73 vec4(0.0, 1.0, 0.0, 1.0),
74 vec4(0.0, 0.0, 1.0, 1.0),
75 ],
76 );
77
78 for (x, y) in [-0.5, 0.0, 0.5].into_iter().zip([-0.25, 0.5, -0.25]) {
80 commands.spawn((
82 CustomRenderedEntity,
85 Mesh3d(meshes.add(mesh.clone())),
87 Transform::from_xyz(x, y, 0.0),
88 ));
89 }
90
91 commands.spawn((
93 Camera3d::default(),
94 Transform::from_xyz(0.0, 0.0, 3.0).looking_at(Vec3::ZERO, Vec3::Y),
96 ));
97}
98
99struct CustomRenderedMeshPipelinePlugin;
103impl Plugin for CustomRenderedMeshPipelinePlugin {
104 fn build(&self, app: &mut App) {
105 app.add_plugins(ExtractComponentPlugin::<CustomRenderedEntity>::default());
106
107 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
109 return;
110 };
111 render_app
112 .init_resource::<SpecializedMeshPipelines<CustomMeshPipeline>>()
114 .add_render_command::<Opaque3d, DrawSpecializedPipelineCommands>()
116 .add_systems(RenderStartup, init_custom_mesh_pipeline)
117 .add_systems(
118 Render,
119 queue_custom_mesh_pipeline.in_set(RenderSystems::Queue),
120 );
121 }
122}
123
124#[derive(Clone, Component, ExtractComponent)]
132#[require(VisibilityClass)]
133#[component(on_add = visibility::add_visibility_class::<CustomRenderedEntity>)]
134struct CustomRenderedEntity;
135
136type DrawSpecializedPipelineCommands = (
139 SetItemPipeline,
141 SetMeshViewBindGroup<0>,
143 SetMeshViewEmptyBindGroup<1>,
145 SetMeshBindGroup<2>,
147 DrawMesh,
149);
150
151#[derive(Resource)]
153struct CustomMeshPipeline {
154 mesh_pipeline: MeshPipeline,
159 shader_handle: Handle<Shader>,
162}
163
164fn init_custom_mesh_pipeline(
165 mut commands: Commands,
166 asset_server: Res<AssetServer>,
167 mesh_pipeline: Res<MeshPipeline>,
168) {
169 let shader_handle: Handle<Shader> = asset_server.load(SHADER_ASSET_PATH);
171 commands.insert_resource(CustomMeshPipeline {
172 mesh_pipeline: mesh_pipeline.clone(),
173 shader_handle,
174 });
175}
176
177impl SpecializedMeshPipeline for CustomMeshPipeline {
178 type Key = MeshPipelineKey;
185
186 fn specialize(
187 &self,
188 mesh_key: Self::Key,
189 layout: &MeshVertexBufferLayoutRef,
190 ) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
191 let mut vertex_attributes = Vec::new();
193 if layout.0.contains(Mesh::ATTRIBUTE_POSITION) {
194 vertex_attributes.push(Mesh::ATTRIBUTE_POSITION.at_shader_location(0));
196 }
197 if layout.0.contains(Mesh::ATTRIBUTE_COLOR) {
198 vertex_attributes.push(Mesh::ATTRIBUTE_COLOR.at_shader_location(1));
200 }
201 let vertex_buffer_layout = layout.0.get_layout(&vertex_attributes)?;
203
204 let view_layout = self
205 .mesh_pipeline
206 .get_view_layout(MeshPipelineViewLayoutKey::from(mesh_key));
207
208 Ok(RenderPipelineDescriptor {
209 label: Some("Specialized Mesh Pipeline".into()),
210 layout: vec![
211 view_layout.main_layout.clone(),
212 view_layout.empty_layout.clone(),
213 self.mesh_pipeline.mesh_layouts.model_only.clone(),
214 ],
215 vertex: VertexState {
216 shader: self.shader_handle.clone(),
217 buffers: vec![vertex_buffer_layout],
219 ..default()
220 },
221 fragment: Some(FragmentState {
222 shader: self.shader_handle.clone(),
223 targets: vec![Some(ColorTargetState {
224 format: if mesh_key.contains(MeshPipelineKey::HDR) {
227 ViewTarget::TEXTURE_FORMAT_HDR
228 } else {
229 TextureFormat::bevy_default()
230 },
231 blend: None,
234 write_mask: ColorWrites::ALL,
235 })],
236 ..default()
237 }),
238 primitive: PrimitiveState {
239 topology: mesh_key.primitive_topology(),
240 front_face: FrontFace::Ccw,
241 cull_mode: Some(Face::Back),
242 polygon_mode: PolygonMode::Fill,
243 ..default()
244 },
245 depth_stencil: Some(DepthStencilState {
248 format: CORE_3D_DEPTH_FORMAT,
249 depth_write_enabled: true,
250 depth_compare: CompareFunction::GreaterEqual,
251 stencil: default(),
252 bias: default(),
253 }),
254 multisample: MultisampleState {
257 count: mesh_key.msaa_samples(),
258 ..default()
259 },
260 ..default()
261 })
262 }
263}
264
265fn queue_custom_mesh_pipeline(
268 pipeline_cache: Res<PipelineCache>,
269 custom_mesh_pipeline: Res<CustomMeshPipeline>,
270 (mut opaque_render_phases, opaque_draw_functions): (
271 ResMut<ViewBinnedRenderPhases<Opaque3d>>,
272 Res<DrawFunctions<Opaque3d>>,
273 ),
274 mut specialized_mesh_pipelines: ResMut<SpecializedMeshPipelines<CustomMeshPipeline>>,
275 views: Query<(&RenderVisibleEntities, &ExtractedView, &Msaa)>,
276 (render_meshes, render_mesh_instances): (
277 Res<RenderAssets<RenderMesh>>,
278 Res<RenderMeshInstances>,
279 ),
280 mut change_tick: Local<Tick>,
281 mesh_allocator: Res<MeshAllocator>,
282 gpu_preprocessing_support: Res<GpuPreprocessingSupport>,
283) {
284 let draw_function = opaque_draw_functions
286 .read()
287 .id::<DrawSpecializedPipelineCommands>();
288
289 for (view_visible_entities, view, msaa) in views.iter() {
293 let Some(opaque_phase) = opaque_render_phases.get_mut(&view.retained_view_entity) else {
294 continue;
295 };
296
297 let view_key = MeshPipelineKey::from_msaa_samples(msaa.samples())
299 | MeshPipelineKey::from_hdr(view.hdr);
300
301 for &(render_entity, visible_entity) in
304 view_visible_entities.get::<CustomRenderedEntity>().iter()
305 {
306 let Some(mesh_instance) = render_mesh_instances.render_mesh_queue_data(visible_entity)
308 else {
309 continue;
310 };
311
312 let Some(mesh) = render_meshes.get(mesh_instance.mesh_asset_id) else {
314 continue;
315 };
316
317 let (vertex_slab, index_slab) = mesh_allocator.mesh_slabs(&mesh_instance.mesh_asset_id);
318
319 let mut mesh_key = view_key;
323 mesh_key |= MeshPipelineKey::from_primitive_topology(mesh.primitive_topology());
324
325 let pipeline_id = specialized_mesh_pipelines
327 .specialize(
328 &pipeline_cache,
329 &custom_mesh_pipeline,
330 mesh_key,
331 &mesh.layout,
332 )
333 .expect("Failed to specialize mesh pipeline");
336
337 let next_change_tick = change_tick.get() + 1;
339 change_tick.set(next_change_tick);
340
341 opaque_phase.add(
343 Opaque3dBatchSetKey {
344 draw_function,
345 pipeline: pipeline_id,
346 material_bind_group_index: None,
347 vertex_slab: vertex_slab.unwrap_or_default(),
348 index_slab,
349 lightmap_slab: None,
350 },
351 Opaque3dBinKey {
354 asset_id: mesh_instance.mesh_asset_id.into(),
355 },
356 (render_entity, visible_entity),
357 mesh_instance.current_uniform_index,
358 BinnedRenderPhaseType::mesh(
362 mesh_instance.should_batch(),
363 &gpu_preprocessing_support,
364 ),
365 *change_tick,
366 );
367 }
368 }
369}