Stop extracting mesh entities to the render world. (#11803)

This fixes a `FIXME` in `extract_meshes` and results in a performance
improvement.

As a result of this change, meshes in the render world might not be
attached to entities anymore. Therefore, the `entity` parameter to
`RenderCommand::render()` is now wrapped in an `Option`. Most
applications that use the render app's ECS can simply unwrap the
`Option`.

Note that for now sprites, gizmos, and UI elements still use the render
world as usual.

## Migration guide

* For efficiency reasons, some meshes in the render world may not have
corresponding `Entity` IDs anymore. As a result, the `entity` parameter
to `RenderCommand::render()` is now wrapped in an `Option`. Custom
rendering code may need to be updated to handle the case in which no
`Entity` exists for an object that is to be rendered.
This commit is contained in:
Patrick Walton 2024-02-10 02:46:10 -08:00 committed by GitHub
parent 55ada617cb
commit 3af8526786
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 73 additions and 39 deletions

View file

@ -383,10 +383,13 @@ impl<const I: usize, P: PhaseItem> RenderCommand<P> for SetLineGizmoBindGroup<I>
fn render<'w>(
_item: &P,
_view: ROQueryItem<'w, Self::ViewQuery>,
uniform_index: ROQueryItem<'w, Self::ItemQuery>,
uniform_index: Option<ROQueryItem<'w, Self::ItemQuery>>,
bind_group: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {
let Some(uniform_index) = uniform_index else {
return RenderCommandResult::Failure;
};
pass.set_bind_group(
I,
&bind_group.into_inner().bindgroup,
@ -406,10 +409,13 @@ impl<P: PhaseItem> RenderCommand<P> for DrawLineGizmo {
fn render<'w>(
_item: &P,
_view: ROQueryItem<'w, Self::ViewQuery>,
handle: ROQueryItem<'w, Self::ItemQuery>,
handle: Option<ROQueryItem<'w, Self::ItemQuery>>,
line_gizmos: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {
let Some(handle) = handle else {
return RenderCommandResult::Failure;
};
let Some(line_gizmo) = line_gizmos.into_inner().get(handle) else {
return RenderCommandResult::Failure;
};

View file

@ -388,7 +388,7 @@ impl<P: PhaseItem, M: Material, const I: usize> RenderCommand<P> for SetMaterial
fn render<'w>(
item: &P,
_view: (),
_item_query: (),
_item_query: Option<()>,
(materials, material_instances): SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {

View file

@ -912,7 +912,7 @@ impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetPrepassViewBindGroup<
&'_ ViewUniformOffset,
Option<&'_ PreviousViewProjectionUniformOffset>,
),
_entity: (),
_entity: Option<()>,
prepass_view_bind_group: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {

View file

@ -256,12 +256,7 @@ pub struct RenderMeshInstance {
#[derive(Default, Resource, Deref, DerefMut)]
pub struct RenderMeshInstances(EntityHashMap<Entity, RenderMeshInstance>);
#[derive(Component)]
pub struct Mesh3d;
pub fn extract_meshes(
mut commands: Commands,
mut previous_len: Local<usize>,
mut render_mesh_instances: ResMut<RenderMeshInstances>,
mut thread_local_queues: Local<ThreadLocal<Cell<Vec<(Entity, RenderMeshInstance)>>>>,
meshes_query: Extract<
@ -328,15 +323,9 @@ pub fn extract_meshes(
);
render_mesh_instances.clear();
let mut entities = Vec::with_capacity(*previous_len);
for queue in thread_local_queues.iter_mut() {
// FIXME: Remove this - it is just a workaround to enable rendering to work as
// render commands require an entity to exist at the moment.
entities.extend(queue.get_mut().iter().map(|(e, _)| (*e, Mesh3d)));
render_mesh_instances.extend(queue.get_mut().drain(..));
}
*previous_len = entities.len();
commands.insert_or_spawn_batch(entities);
}
#[derive(Resource, Clone)]
@ -1050,7 +1039,7 @@ impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetMeshViewBindGroup<I>
'w,
Self::ViewQuery,
>,
_entity: (),
_entity: Option<()>,
_: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {
@ -1085,7 +1074,7 @@ impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetMeshBindGroup<I> {
fn render<'w>(
item: &P,
_view: (),
_item_query: (),
_item_query: Option<()>,
(bind_groups, mesh_instances, skin_indices, morph_indices, lightmaps): SystemParamItem<
'w,
'_,
@ -1154,7 +1143,7 @@ impl<P: PhaseItem> RenderCommand<P> for DrawMesh {
fn render<'w>(
item: &P,
_view: (),
_item_query: (),
_item_query: Option<()>,
(meshes, mesh_instances): SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {

View file

@ -190,6 +190,11 @@ pub trait RenderCommand<P: PhaseItem> {
///
/// The item is the entity that will be rendered for the corresponding view.
/// All components have to be accessed read only.
///
/// For efficiency reasons, Bevy doesn't always extract entities to the
/// render world; for instance, entities that simply consist of meshes are
/// often not extracted. If the entity doesn't exist in the render world,
/// the supplied query data will be `None`.
type ItemQuery: ReadOnlyQueryData;
/// Renders a [`PhaseItem`] by recording commands (e.g. setting pipelines, binding bind groups,
@ -197,7 +202,7 @@ pub trait RenderCommand<P: PhaseItem> {
fn render<'w>(
item: &P,
view: ROQueryItem<'w, Self::ViewQuery>,
entity: ROQueryItem<'w, Self::ItemQuery>,
entity: Option<ROQueryItem<'w, Self::ItemQuery>>,
param: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult;
@ -220,13 +225,22 @@ macro_rules! render_command_tuple_impl {
fn render<'w>(
_item: &P,
($($view,)*): ROQueryItem<'w, Self::ViewQuery>,
($($entity,)*): ROQueryItem<'w, Self::ItemQuery>,
maybe_entities: Option<ROQueryItem<'w, Self::ItemQuery>>,
($($name,)*): SystemParamItem<'w, '_, Self::Param>,
_pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {
$(if let RenderCommandResult::Failure = $name::render(_item, $view, $entity, $name, _pass) {
return RenderCommandResult::Failure;
})*
match maybe_entities {
None => {
$(if let RenderCommandResult::Failure = $name::render(_item, $view, None, $name, _pass) {
return RenderCommandResult::Failure;
})*
}
Some(($($entity,)*)) => {
$(if let RenderCommandResult::Failure = $name::render(_item, $view, Some($entity), $name, _pass) {
return RenderCommandResult::Failure;
})*
}
}
RenderCommandResult::Success
}
}
@ -278,7 +292,7 @@ where
) {
let param = self.state.get_manual(world);
let view = self.view.get_manual(world, view).unwrap();
let entity = self.entity.get_manual(world, item.entity()).unwrap();
let entity = self.entity.get_manual(world, item.entity()).ok();
// TODO: handle/log `RenderCommand` failure
C::render(item, view, entity, param, pass);
}

View file

@ -202,7 +202,7 @@ impl<P: CachedRenderPipelinePhaseItem> RenderCommand<P> for SetItemPipeline {
fn render<'w>(
item: &P,
_view: (),
_entity: (),
_entity: Option<()>,
pipeline_cache: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {

View file

@ -337,7 +337,7 @@ impl<P: PhaseItem, M: Material2d, const I: usize> RenderCommand<P>
fn render<'w>(
item: &P,
_view: (),
_item_query: (),
_item_query: Option<()>,
(materials, material_instances): SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {

View file

@ -624,7 +624,7 @@ impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetMesh2dViewBindGroup<I
fn render<'w>(
_item: &P,
(view_uniform, mesh2d_view_bind_group): ROQueryItem<'w, Self::ViewQuery>,
_view: (),
_view: Option<()>,
_param: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {
@ -644,7 +644,7 @@ impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetMesh2dBindGroup<I> {
fn render<'w>(
item: &P,
_view: (),
_item_query: (),
_item_query: Option<()>,
mesh2d_bind_group: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {
@ -673,7 +673,7 @@ impl<P: PhaseItem> RenderCommand<P> for DrawMesh2d {
fn render<'w>(
item: &P,
_view: (),
_item_query: (),
_item_query: Option<()>,
(meshes, render_mesh2d_instances): SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {

View file

@ -752,7 +752,7 @@ impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetSpriteViewBindGroup<I
fn render<'w>(
_item: &P,
view_uniform: &'_ ViewUniformOffset,
_entity: (),
_entity: Option<()>,
sprite_meta: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {
@ -773,11 +773,14 @@ impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetSpriteTextureBindGrou
fn render<'w>(
_item: &P,
_view: (),
batch: &'_ SpriteBatch,
batch: Option<&'_ SpriteBatch>,
image_bind_groups: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {
let image_bind_groups = image_bind_groups.into_inner();
let Some(batch) = batch else {
return RenderCommandResult::Failure;
};
pass.set_bind_group(
I,
@ -800,11 +803,15 @@ impl<P: PhaseItem> RenderCommand<P> for DrawSpriteBatch {
fn render<'w>(
_item: &P,
_view: (),
batch: &'_ SpriteBatch,
batch: Option<&'_ SpriteBatch>,
sprite_meta: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {
let sprite_meta = sprite_meta.into_inner();
let Some(batch) = batch else {
return RenderCommandResult::Failure;
};
pass.set_index_buffer(
sprite_meta.sprite_index_buffer.buffer().unwrap().slice(..),
0,

View file

@ -161,7 +161,7 @@ impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetUiViewBindGroup<I> {
fn render<'w>(
_item: &P,
view_uniform: &'w ViewUniformOffset,
_entity: (),
_entity: Option<()>,
ui_meta: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {
@ -183,11 +183,15 @@ impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetUiTextureBindGroup<I>
fn render<'w>(
_item: &P,
_view: (),
batch: &'w UiBatch,
batch: Option<&'w UiBatch>,
image_bind_groups: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {
let image_bind_groups = image_bind_groups.into_inner();
let Some(batch) = batch else {
return RenderCommandResult::Failure;
};
pass.set_bind_group(I, image_bind_groups.values.get(&batch.image).unwrap(), &[]);
RenderCommandResult::Success
}
@ -202,10 +206,14 @@ impl<P: PhaseItem> RenderCommand<P> for DrawUiNode {
fn render<'w>(
_item: &P,
_view: (),
batch: &'w UiBatch,
batch: Option<&'w UiBatch>,
ui_meta: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {
let Some(batch) = batch else {
return RenderCommandResult::Failure;
};
pass.set_vertex_buffer(0, ui_meta.into_inner().vertices.buffer().unwrap().slice(..));
pass.draw(batch.range.clone(), 0..1);
RenderCommandResult::Success

View file

@ -272,7 +272,7 @@ impl<P: PhaseItem, M: UiMaterial, const I: usize> RenderCommand<P> for SetMatUiV
fn render<'w>(
_item: &P,
view_uniform: &'w ViewUniformOffset,
_entity: (),
_entity: Option<()>,
ui_meta: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {
@ -296,10 +296,13 @@ impl<P: PhaseItem, M: UiMaterial, const I: usize> RenderCommand<P>
fn render<'w>(
_item: &P,
_view: (),
material_handle: ROQueryItem<'_, Self::ItemQuery>,
material_handle: Option<ROQueryItem<'_, Self::ItemQuery>>,
materials: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {
let Some(material_handle) = material_handle else {
return RenderCommandResult::Failure;
};
let Some(material) = materials.into_inner().get(&material_handle.material) else {
return RenderCommandResult::Failure;
};
@ -318,10 +321,14 @@ impl<P: PhaseItem, M: UiMaterial> RenderCommand<P> for DrawUiMaterialNode<M> {
fn render<'w>(
_item: &P,
_view: (),
batch: &'w UiMaterialBatch<M>,
batch: Option<&'w UiMaterialBatch<M>>,
ui_meta: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {
let Some(batch) = batch else {
return RenderCommandResult::Failure;
};
pass.set_vertex_buffer(0, ui_meta.into_inner().vertices.buffer().unwrap().slice(..));
pass.draw(batch.range.clone(), 0..1);
RenderCommandResult::Success

View file

@ -244,7 +244,7 @@ impl<P: PhaseItem> RenderCommand<P> for DrawMeshInstanced {
fn render<'w>(
item: &P,
_view: (),
instance_buffer: &'w InstanceBuffer,
instance_buffer: Option<&'w InstanceBuffer>,
(meshes, render_mesh_instances): SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {
@ -254,6 +254,9 @@ impl<P: PhaseItem> RenderCommand<P> for DrawMeshInstanced {
let Some(gpu_mesh) = meshes.into_inner().get(mesh_instance.mesh_asset_id) else {
return RenderCommandResult::Failure;
};
let Some(instance_buffer) = instance_buffer else {
return RenderCommandResult::Failure;
};
pass.set_vertex_buffer(0, gpu_mesh.vertex_buffer.slice(..));
pass.set_vertex_buffer(1, instance_buffer.buffer.slice(..));