From c523466f7377ee257df1ea18bf298581dd31ae07 Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Tue, 3 May 2022 18:28:04 +0000 Subject: [PATCH] Filter material handles on extraction (#4178) # Objective - While optimising many_cubes, I noticed that all material handles are extracted regardless of whether the entity to which the handle belongs is visible or not. As such >100k handles are extracted when only <20k are visible. ## Solution - Only extract material handles of visible entities. - This improves `many_cubes -- sphere` from ~42fps to ~48fps. It reduces not only the extraction time but also system commands time. `Handle` extraction and its system commands went from 0.522ms + 3.710ms respectively, to 0.267ms + 0.227ms an 88% reduction for this system for this case. It's very view dependent but... --- crates/bevy_pbr/src/material.rs | 2 +- crates/bevy_render/src/render_component.rs | 43 ++++++++++++++++++++-- crates/bevy_sprite/src/mesh2d/material.rs | 2 +- 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/crates/bevy_pbr/src/material.rs b/crates/bevy_pbr/src/material.rs index 2b1fd83cabe7d..b0d20dde6e25a 100644 --- a/crates/bevy_pbr/src/material.rs +++ b/crates/bevy_pbr/src/material.rs @@ -204,7 +204,7 @@ impl Default for MaterialPlugin { impl Plugin for MaterialPlugin { fn build(&self, app: &mut App) { app.add_asset::() - .add_plugin(ExtractComponentPlugin::>::default()) + .add_plugin(ExtractComponentPlugin::>::extract_visible()) .add_plugin(RenderAssetPlugin::::default()); if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { render_app diff --git a/crates/bevy_render/src/render_component.rs b/crates/bevy_render/src/render_component.rs index 8d15f2476d6ae..7cd3158cb94d7 100644 --- a/crates/bevy_render/src/render_component.rs +++ b/crates/bevy_render/src/render_component.rs @@ -1,6 +1,7 @@ use crate::{ render_resource::{std140::AsStd140, DynamicUniformVec}, renderer::{RenderDevice, RenderQueue}, + view::ComputedVisibility, RenderApp, RenderStage, }; use bevy_app::{App, Plugin}; @@ -131,18 +132,38 @@ fn prepare_uniform_components( /// /// Therefore it sets up the [`RenderStage::Extract`](crate::RenderStage::Extract) step /// for the specified [`ExtractComponent`]. -pub struct ExtractComponentPlugin(PhantomData (C, F)>); +pub struct ExtractComponentPlugin { + only_extract_visible: bool, + marker: PhantomData (C, F)>, +} impl Default for ExtractComponentPlugin { fn default() -> Self { - Self(PhantomData) + Self { + only_extract_visible: false, + marker: PhantomData, + } + } +} + +impl ExtractComponentPlugin { + pub fn extract_visible() -> Self { + Self { + only_extract_visible: true, + marker: PhantomData, + } } } impl Plugin for ExtractComponentPlugin { fn build(&self, app: &mut App) { if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { - render_app.add_system_to_stage(RenderStage::Extract, extract_components::); + if self.only_extract_visible { + render_app + .add_system_to_stage(RenderStage::Extract, extract_visible_components::); + } else { + render_app.add_system_to_stage(RenderStage::Extract, extract_components::); + } } } } @@ -170,3 +191,19 @@ fn extract_components( *previous_len = values.len(); commands.insert_or_spawn_batch(values); } + +/// This system extracts all visible components of the corresponding [`ExtractComponent`] type. +fn extract_visible_components( + mut commands: Commands, + mut previous_len: Local, + mut query: StaticSystemParam, C::Query), C::Filter>>, +) { + let mut values = Vec::with_capacity(*previous_len); + for (entity, computed_visibility, query_item) in query.iter_mut() { + if computed_visibility.is_visible { + values.push((entity, (C::extract_component(query_item),))); + } + } + *previous_len = values.len(); + commands.insert_or_spawn_batch(values); +} diff --git a/crates/bevy_sprite/src/mesh2d/material.rs b/crates/bevy_sprite/src/mesh2d/material.rs index f088e761a054c..77cb3c4ebe92f 100644 --- a/crates/bevy_sprite/src/mesh2d/material.rs +++ b/crates/bevy_sprite/src/mesh2d/material.rs @@ -194,7 +194,7 @@ impl Default for Material2dPlugin { impl Plugin for Material2dPlugin { fn build(&self, app: &mut App) { app.add_asset::() - .add_plugin(ExtractComponentPlugin::>::default()) + .add_plugin(ExtractComponentPlugin::>::extract_visible()) .add_plugin(RenderAssetPlugin::::default()); if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { render_app