Skip to content

Clean up specialized_mesh_pipeline #20107

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 28 additions & 127 deletions examples/shader/specialized_mesh_pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,20 @@

use bevy::{
core_pipeline::core_3d::{Opaque3d, Opaque3dBatchSetKey, Opaque3dBinKey, CORE_3D_DEPTH_FORMAT},
ecs::{component::Tick, system::StaticSystemParam},
ecs::component::Tick,
math::{vec3, vec4},
pbr::{
DrawMesh, MeshPipeline, MeshPipelineKey, MeshPipelineViewLayoutKey, RenderMeshInstances,
SetMeshBindGroup, SetMeshViewBindGroup, SetMeshViewEmptyBindGroup,
},
prelude::*,
render::{
batching::{
gpu_preprocessing::{
self, PhaseBatchedInstanceBuffers, PhaseIndirectParametersBuffers,
PreprocessWorkItem, UntypedPhaseBatchedInstanceBuffers,
},
GetBatchData, GetFullBatchData,
},
experimental::occlusion_culling::OcclusionCulling,
batching::gpu_preprocessing::GpuPreprocessingSupport,
extract_component::{ExtractComponent, ExtractComponentPlugin},
mesh::{Indices, MeshVertexBufferLayoutRef, PrimitiveTopology, RenderMesh},
mesh::{
allocator::MeshAllocator, Indices, MeshVertexBufferLayoutRef, PrimitiveTopology,
RenderMesh,
},
render_asset::{RenderAssetUsages, RenderAssets},
render_phase::{
AddRenderCommand, BinnedRenderPhaseType, DrawFunctions, SetItemPipeline,
Expand All @@ -37,7 +33,6 @@ use bevy::{
RenderPipelineDescriptor, SpecializedMeshPipeline, SpecializedMeshPipelineError,
SpecializedMeshPipelines, TextureFormat, VertexState,
},
view::NoIndirectDrawing,
view::{self, ExtractedView, RenderVisibleEntities, ViewTarget, VisibilityClass},
Render, RenderApp, RenderStartup, RenderSystems,
},
Expand Down Expand Up @@ -262,7 +257,6 @@ impl SpecializedMeshPipeline for CustomMeshPipeline {
count: mesh_key.msaa_samples(),
..default()
},

..default()
})
}
Expand All @@ -278,75 +272,32 @@ fn queue_custom_mesh_pipeline(
Res<DrawFunctions<Opaque3d>>,
),
mut specialized_mesh_pipelines: ResMut<SpecializedMeshPipelines<CustomMeshPipeline>>,
views: Query<(
&RenderVisibleEntities,
&ExtractedView,
&Msaa,
Has<NoIndirectDrawing>,
Has<OcclusionCulling>,
)>,
views: Query<(&RenderVisibleEntities, &ExtractedView, &Msaa)>,
(render_meshes, render_mesh_instances): (
Res<RenderAssets<RenderMesh>>,
Res<RenderMeshInstances>,
),
param: StaticSystemParam<<MeshPipeline as GetBatchData>::Param>,
mut phase_batched_instance_buffers: ResMut<
PhaseBatchedInstanceBuffers<Opaque3d, <MeshPipeline as GetBatchData>::BufferData>,
>,
mut phase_indirect_parameters_buffers: ResMut<PhaseIndirectParametersBuffers<Opaque3d>>,
mut change_tick: Local<Tick>,
mesh_allocator: Res<MeshAllocator>,
gpu_preprocessing_support: Res<GpuPreprocessingSupport>,
) {
let system_param_item = param.into_inner();

let UntypedPhaseBatchedInstanceBuffers {
ref mut data_buffer,
ref mut work_item_buffers,
ref mut late_indexed_indirect_parameters_buffer,
ref mut late_non_indexed_indirect_parameters_buffer,
..
} = phase_batched_instance_buffers.buffers;

// Get the id for our custom draw function
let draw_function_id = opaque_draw_functions
let draw_function = opaque_draw_functions
.read()
.id::<DrawSpecializedPipelineCommands>();

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

// Create *work item buffers* if necessary. Work item buffers store the
// indices of meshes that are to be rendered when indirect drawing is
// enabled.
let work_item_buffer = gpu_preprocessing::get_or_create_work_item_buffer::<Opaque3d>(
work_item_buffers,
view.retained_view_entity,
no_indirect_drawing,
gpu_occlusion_culling,
);

// Initialize those work item buffers in preparation for this new frame.
gpu_preprocessing::init_work_item_buffers(
work_item_buffer,
late_indexed_indirect_parameters_buffer,
late_non_indexed_indirect_parameters_buffer,
);

// Create the key based on the view. In this case we only care about MSAA and HDR
let view_key = MeshPipelineKey::from_msaa_samples(msaa.samples())
| MeshPipelineKey::from_hdr(view.hdr);

// Set up a slot to hold information about the batch set we're going to
// create. If there are any of our custom meshes in the scene, we'll
// need this information in order for Bevy to kick off the rendering.
let mut mesh_batch_set_info = None;

// Find all the custom rendered entities that are visible from this
// view.
for &(render_entity, visible_entity) in
Expand All @@ -363,34 +314,14 @@ fn queue_custom_mesh_pipeline(
continue;
};

let (vertex_slab, index_slab) = mesh_allocator.mesh_slabs(&mesh_instance.mesh_asset_id);

// Specialize the key for the current mesh entity
// For this example we only specialize based on the mesh topology
// but you could have more complex keys and that's where you'd need to create those keys
let mut mesh_key = view_key;
mesh_key |= MeshPipelineKey::from_primitive_topology(mesh.primitive_topology());

// Initialize the batch set information if this was the first custom
// mesh we saw. We'll need that information later to create the
// batch set.
if mesh_batch_set_info.is_none() {
mesh_batch_set_info = Some(MeshBatchSetInfo {
indirect_parameters_index: phase_indirect_parameters_buffers
.buffers
.allocate(mesh.indexed(), 1),
is_indexed: mesh.indexed(),
});
}
let mesh_info = mesh_batch_set_info.unwrap();

// Allocate some input and output indices. We'll need these to
// create the *work item* below.
let Some(input_index) =
MeshPipeline::get_binned_index(&system_param_item, visible_entity)
else {
continue;
};
let output_index = data_buffer.add() as u32;

// Finally, we can specialize the pipeline based on the key
let pipeline_id = specialized_mesh_pipelines
.specialize(
Expand All @@ -399,8 +330,8 @@ fn queue_custom_mesh_pipeline(
mesh_key,
&mesh.layout,
)
// This should never with this example, but if your pipeline specialization
// can fail you need to handle the error here
// This should never happen with this example, but if your pipeline
// specialization can fail you need to handle the error here
.expect("Failed to specialize mesh pipeline");

// Bump the change tick so that Bevy is forced to rebuild the bin.
Expand All @@ -410,59 +341,29 @@ fn queue_custom_mesh_pipeline(
// Add the mesh with our specialized pipeline
opaque_phase.add(
Opaque3dBatchSetKey {
draw_function: draw_function_id,
draw_function,
pipeline: pipeline_id,
material_bind_group_index: None,
vertex_slab: default(),
index_slab: None,
vertex_slab: vertex_slab.unwrap_or_default(),
index_slab,
lightmap_slab: None,
},
// The asset ID is arbitrary; we simply use [`AssetId::invalid`],
// but you can use anything you like. Note that the asset ID need
// not be the ID of a [`Mesh`].
// For this example we can use the mesh asset id as the bin key,
// but you can use any asset_id as a key
Opaque3dBinKey {
asset_id: AssetId::<Mesh>::invalid().untyped(),
asset_id: mesh_instance.mesh_asset_id.into(),
},
(render_entity, visible_entity),
mesh_instance.current_uniform_index,
// This example supports batching, but if your pipeline doesn't
// support it you can use `BinnedRenderPhaseType::UnbatchableMesh`
BinnedRenderPhaseType::BatchableMesh,
// This example supports batching and multi draw indirect,
// but if your pipeline doesn't support it you can use
// `BinnedRenderPhaseType::UnbatchableMesh`
BinnedRenderPhaseType::mesh(
mesh_instance.should_batch(),
&gpu_preprocessing_support,
),
*change_tick,
);

// Create a *work item*. A work item tells the Bevy renderer to
// transform the mesh on GPU.
work_item_buffer.push(
mesh.indexed(),
PreprocessWorkItem {
input_index: input_index.into(),
output_or_indirect_parameters_index: if no_indirect_drawing {
output_index
} else {
mesh_info.indirect_parameters_index
},
},
);
}

// Now if there were any meshes, we need to add a command to the
// indirect parameters buffer, so that the renderer will end up
// enqueuing a command to draw the mesh.
if let Some(mesh_info) = mesh_batch_set_info {
phase_indirect_parameters_buffers
.buffers
.add_batch_set(mesh_info.is_indexed, mesh_info.indirect_parameters_index);
}
}
}

// If we end up having any custom meshes to draw, this contains information
// needed to create the batch set.
#[derive(Clone, Copy)]
struct MeshBatchSetInfo {
/// The first index of the mesh batch in the indirect parameters buffer.
indirect_parameters_index: u32,
/// Whether the mesh is indexed (has an index buffer).
is_indexed: bool,
}
Loading