Skip to content

Commit 8417aea

Browse files
authored
Clean up specialized_mesh_pipeline (#20107)
# Objective - The example doesn't work on webgl2 - It tries to reimplement logic for batching instead of using existing abstractions which breaks on webgl2 - It only uses batching ## Solution - Use existing abstractions and remove code related to batching - This fixes the webgl2 issue - It also makes it use multi draw indirect instead of just batching ## Testing - Tested the example with the bevy cli for webgl and wegpu and also ran the example locally
1 parent caafa03 commit 8417aea

File tree

1 file changed

+28
-127
lines changed

1 file changed

+28
-127
lines changed

examples/shader_advanced/specialized_mesh_pipeline.rs

Lines changed: 28 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -8,24 +8,20 @@
88
99
use bevy::{
1010
core_pipeline::core_3d::{Opaque3d, Opaque3dBatchSetKey, Opaque3dBinKey, CORE_3D_DEPTH_FORMAT},
11-
ecs::{component::Tick, system::StaticSystemParam},
11+
ecs::component::Tick,
1212
math::{vec3, vec4},
1313
pbr::{
1414
DrawMesh, MeshPipeline, MeshPipelineKey, MeshPipelineViewLayoutKey, RenderMeshInstances,
1515
SetMeshBindGroup, SetMeshViewBindGroup, SetMeshViewEmptyBindGroup,
1616
},
1717
prelude::*,
1818
render::{
19-
batching::{
20-
gpu_preprocessing::{
21-
self, PhaseBatchedInstanceBuffers, PhaseIndirectParametersBuffers,
22-
PreprocessWorkItem, UntypedPhaseBatchedInstanceBuffers,
23-
},
24-
GetBatchData, GetFullBatchData,
25-
},
26-
experimental::occlusion_culling::OcclusionCulling,
19+
batching::gpu_preprocessing::GpuPreprocessingSupport,
2720
extract_component::{ExtractComponent, ExtractComponentPlugin},
28-
mesh::{Indices, MeshVertexBufferLayoutRef, PrimitiveTopology, RenderMesh},
21+
mesh::{
22+
allocator::MeshAllocator, Indices, MeshVertexBufferLayoutRef, PrimitiveTopology,
23+
RenderMesh,
24+
},
2925
render_asset::{RenderAssetUsages, RenderAssets},
3026
render_phase::{
3127
AddRenderCommand, BinnedRenderPhaseType, DrawFunctions, SetItemPipeline,
@@ -37,7 +33,6 @@ use bevy::{
3733
RenderPipelineDescriptor, SpecializedMeshPipeline, SpecializedMeshPipelineError,
3834
SpecializedMeshPipelines, TextureFormat, VertexState,
3935
},
40-
view::NoIndirectDrawing,
4136
view::{self, ExtractedView, RenderVisibleEntities, ViewTarget, VisibilityClass},
4237
Render, RenderApp, RenderStartup, RenderSystems,
4338
},
@@ -262,7 +257,6 @@ impl SpecializedMeshPipeline for CustomMeshPipeline {
262257
count: mesh_key.msaa_samples(),
263258
..default()
264259
},
265-
266260
..default()
267261
})
268262
}
@@ -278,75 +272,32 @@ fn queue_custom_mesh_pipeline(
278272
Res<DrawFunctions<Opaque3d>>,
279273
),
280274
mut specialized_mesh_pipelines: ResMut<SpecializedMeshPipelines<CustomMeshPipeline>>,
281-
views: Query<(
282-
&RenderVisibleEntities,
283-
&ExtractedView,
284-
&Msaa,
285-
Has<NoIndirectDrawing>,
286-
Has<OcclusionCulling>,
287-
)>,
275+
views: Query<(&RenderVisibleEntities, &ExtractedView, &Msaa)>,
288276
(render_meshes, render_mesh_instances): (
289277
Res<RenderAssets<RenderMesh>>,
290278
Res<RenderMeshInstances>,
291279
),
292-
param: StaticSystemParam<<MeshPipeline as GetBatchData>::Param>,
293-
mut phase_batched_instance_buffers: ResMut<
294-
PhaseBatchedInstanceBuffers<Opaque3d, <MeshPipeline as GetBatchData>::BufferData>,
295-
>,
296-
mut phase_indirect_parameters_buffers: ResMut<PhaseIndirectParametersBuffers<Opaque3d>>,
297280
mut change_tick: Local<Tick>,
281+
mesh_allocator: Res<MeshAllocator>,
282+
gpu_preprocessing_support: Res<GpuPreprocessingSupport>,
298283
) {
299-
let system_param_item = param.into_inner();
300-
301-
let UntypedPhaseBatchedInstanceBuffers {
302-
ref mut data_buffer,
303-
ref mut work_item_buffers,
304-
ref mut late_indexed_indirect_parameters_buffer,
305-
ref mut late_non_indexed_indirect_parameters_buffer,
306-
..
307-
} = phase_batched_instance_buffers.buffers;
308-
309284
// Get the id for our custom draw function
310-
let draw_function_id = opaque_draw_functions
285+
let draw_function = opaque_draw_functions
311286
.read()
312287
.id::<DrawSpecializedPipelineCommands>();
313288

314289
// Render phases are per-view, so we need to iterate over all views so that
315290
// the entity appears in them. (In this example, we have only one view, but
316291
// it's good practice to loop over all views anyway.)
317-
for (view_visible_entities, view, msaa, no_indirect_drawing, gpu_occlusion_culling) in
318-
views.iter()
319-
{
292+
for (view_visible_entities, view, msaa) in views.iter() {
320293
let Some(opaque_phase) = opaque_render_phases.get_mut(&view.retained_view_entity) else {
321294
continue;
322295
};
323296

324-
// Create *work item buffers* if necessary. Work item buffers store the
325-
// indices of meshes that are to be rendered when indirect drawing is
326-
// enabled.
327-
let work_item_buffer = gpu_preprocessing::get_or_create_work_item_buffer::<Opaque3d>(
328-
work_item_buffers,
329-
view.retained_view_entity,
330-
no_indirect_drawing,
331-
gpu_occlusion_culling,
332-
);
333-
334-
// Initialize those work item buffers in preparation for this new frame.
335-
gpu_preprocessing::init_work_item_buffers(
336-
work_item_buffer,
337-
late_indexed_indirect_parameters_buffer,
338-
late_non_indexed_indirect_parameters_buffer,
339-
);
340-
341297
// Create the key based on the view. In this case we only care about MSAA and HDR
342298
let view_key = MeshPipelineKey::from_msaa_samples(msaa.samples())
343299
| MeshPipelineKey::from_hdr(view.hdr);
344300

345-
// Set up a slot to hold information about the batch set we're going to
346-
// create. If there are any of our custom meshes in the scene, we'll
347-
// need this information in order for Bevy to kick off the rendering.
348-
let mut mesh_batch_set_info = None;
349-
350301
// Find all the custom rendered entities that are visible from this
351302
// view.
352303
for &(render_entity, visible_entity) in
@@ -363,34 +314,14 @@ fn queue_custom_mesh_pipeline(
363314
continue;
364315
};
365316

317+
let (vertex_slab, index_slab) = mesh_allocator.mesh_slabs(&mesh_instance.mesh_asset_id);
318+
366319
// Specialize the key for the current mesh entity
367320
// For this example we only specialize based on the mesh topology
368321
// but you could have more complex keys and that's where you'd need to create those keys
369322
let mut mesh_key = view_key;
370323
mesh_key |= MeshPipelineKey::from_primitive_topology(mesh.primitive_topology());
371324

372-
// Initialize the batch set information if this was the first custom
373-
// mesh we saw. We'll need that information later to create the
374-
// batch set.
375-
if mesh_batch_set_info.is_none() {
376-
mesh_batch_set_info = Some(MeshBatchSetInfo {
377-
indirect_parameters_index: phase_indirect_parameters_buffers
378-
.buffers
379-
.allocate(mesh.indexed(), 1),
380-
is_indexed: mesh.indexed(),
381-
});
382-
}
383-
let mesh_info = mesh_batch_set_info.unwrap();
384-
385-
// Allocate some input and output indices. We'll need these to
386-
// create the *work item* below.
387-
let Some(input_index) =
388-
MeshPipeline::get_binned_index(&system_param_item, visible_entity)
389-
else {
390-
continue;
391-
};
392-
let output_index = data_buffer.add() as u32;
393-
394325
// Finally, we can specialize the pipeline based on the key
395326
let pipeline_id = specialized_mesh_pipelines
396327
.specialize(
@@ -399,8 +330,8 @@ fn queue_custom_mesh_pipeline(
399330
mesh_key,
400331
&mesh.layout,
401332
)
402-
// This should never with this example, but if your pipeline specialization
403-
// can fail you need to handle the error here
333+
// This should never happen with this example, but if your pipeline
334+
// specialization can fail you need to handle the error here
404335
.expect("Failed to specialize mesh pipeline");
405336

406337
// Bump the change tick so that Bevy is forced to rebuild the bin.
@@ -410,59 +341,29 @@ fn queue_custom_mesh_pipeline(
410341
// Add the mesh with our specialized pipeline
411342
opaque_phase.add(
412343
Opaque3dBatchSetKey {
413-
draw_function: draw_function_id,
344+
draw_function,
414345
pipeline: pipeline_id,
415346
material_bind_group_index: None,
416-
vertex_slab: default(),
417-
index_slab: None,
347+
vertex_slab: vertex_slab.unwrap_or_default(),
348+
index_slab,
418349
lightmap_slab: None,
419350
},
420-
// The asset ID is arbitrary; we simply use [`AssetId::invalid`],
421-
// but you can use anything you like. Note that the asset ID need
422-
// not be the ID of a [`Mesh`].
351+
// For this example we can use the mesh asset id as the bin key,
352+
// but you can use any asset_id as a key
423353
Opaque3dBinKey {
424-
asset_id: AssetId::<Mesh>::invalid().untyped(),
354+
asset_id: mesh_instance.mesh_asset_id.into(),
425355
},
426356
(render_entity, visible_entity),
427357
mesh_instance.current_uniform_index,
428-
// This example supports batching, but if your pipeline doesn't
429-
// support it you can use `BinnedRenderPhaseType::UnbatchableMesh`
430-
BinnedRenderPhaseType::BatchableMesh,
358+
// This example supports batching and multi draw indirect,
359+
// but if your pipeline doesn't support it you can use
360+
// `BinnedRenderPhaseType::UnbatchableMesh`
361+
BinnedRenderPhaseType::mesh(
362+
mesh_instance.should_batch(),
363+
&gpu_preprocessing_support,
364+
),
431365
*change_tick,
432366
);
433-
434-
// Create a *work item*. A work item tells the Bevy renderer to
435-
// transform the mesh on GPU.
436-
work_item_buffer.push(
437-
mesh.indexed(),
438-
PreprocessWorkItem {
439-
input_index: input_index.into(),
440-
output_or_indirect_parameters_index: if no_indirect_drawing {
441-
output_index
442-
} else {
443-
mesh_info.indirect_parameters_index
444-
},
445-
},
446-
);
447-
}
448-
449-
// Now if there were any meshes, we need to add a command to the
450-
// indirect parameters buffer, so that the renderer will end up
451-
// enqueuing a command to draw the mesh.
452-
if let Some(mesh_info) = mesh_batch_set_info {
453-
phase_indirect_parameters_buffers
454-
.buffers
455-
.add_batch_set(mesh_info.is_indexed, mesh_info.indirect_parameters_index);
456367
}
457368
}
458369
}
459-
460-
// If we end up having any custom meshes to draw, this contains information
461-
// needed to create the batch set.
462-
#[derive(Clone, Copy)]
463-
struct MeshBatchSetInfo {
464-
/// The first index of the mesh batch in the indirect parameters buffer.
465-
indirect_parameters_index: u32,
466-
/// Whether the mesh is indexed (has an index buffer).
467-
is_indexed: bool,
468-
}

0 commit comments

Comments
 (0)