Parallelize bevy 0.16-rc bottlenecks (#18632)
# Objective - Found stuttering and performance degradation while updating big_space stress tests. ## Solution - Identify and fix slow spots using tracy. Patch to verify fixes. ## Testing - Tracy - Before:  - prev_gt parallelization and mutating instead of component insertion:  - parallelize visibility ranges and mesh specialization  --------- Co-authored-by: Zachary Harrold <zac@harrold.com.au>
This commit is contained in:
parent
d5c5de20b1
commit
cba6698033
@ -52,6 +52,7 @@ use bevy_render::{
|
||||
};
|
||||
use bevy_render::{mesh::allocator::MeshAllocator, sync_world::MainEntityHashMap};
|
||||
use bevy_render::{texture::FallbackImage, view::RenderVisibleEntities};
|
||||
use bevy_utils::Parallel;
|
||||
use core::{hash::Hash, marker::PhantomData};
|
||||
use tracing::error;
|
||||
|
||||
@ -832,14 +833,18 @@ pub fn check_entities_needing_specialization<M>(
|
||||
With<MeshMaterial3d<M>>,
|
||||
),
|
||||
>,
|
||||
mut par_local: Local<Parallel<Vec<Entity>>>,
|
||||
mut entities_needing_specialization: ResMut<EntitiesNeedingSpecialization<M>>,
|
||||
) where
|
||||
M: Material,
|
||||
{
|
||||
entities_needing_specialization.clear();
|
||||
for entity in &needs_specialization {
|
||||
entities_needing_specialization.push(entity);
|
||||
}
|
||||
|
||||
needs_specialization
|
||||
.par_iter()
|
||||
.for_each(|entity| par_local.borrow_local_mut().push(entity));
|
||||
|
||||
par_local.drain_into(&mut entities_needing_specialization);
|
||||
}
|
||||
|
||||
pub fn specialize_material_meshes<M: Material>(
|
||||
|
@ -268,20 +268,22 @@ type PreviousMeshFilter = Or<(With<Mesh3d>, With<MeshletMesh3d>)>;
|
||||
pub fn update_mesh_previous_global_transforms(
|
||||
mut commands: Commands,
|
||||
views: Query<&Camera, Or<(With<Camera3d>, With<ShadowView>)>>,
|
||||
meshes: Query<(Entity, &GlobalTransform, Option<&PreviousGlobalTransform>), PreviousMeshFilter>,
|
||||
new_meshes: Query<
|
||||
(Entity, &GlobalTransform),
|
||||
(PreviousMeshFilter, Without<PreviousGlobalTransform>),
|
||||
>,
|
||||
mut meshes: Query<(&GlobalTransform, &mut PreviousGlobalTransform), PreviousMeshFilter>,
|
||||
) {
|
||||
let should_run = views.iter().any(|camera| camera.is_active);
|
||||
|
||||
if should_run {
|
||||
for (entity, transform, old_previous_transform) in &meshes {
|
||||
for (entity, transform) in &new_meshes {
|
||||
let new_previous_transform = PreviousGlobalTransform(transform.affine());
|
||||
// Make sure not to trigger change detection on
|
||||
// `PreviousGlobalTransform` if the previous transform hasn't
|
||||
// changed.
|
||||
if old_previous_transform != Some(&new_previous_transform) {
|
||||
commands.entity(entity).try_insert(new_previous_transform);
|
||||
}
|
||||
commands.entity(entity).try_insert(new_previous_transform);
|
||||
}
|
||||
meshes.par_iter_mut().for_each(|(transform, mut previous)| {
|
||||
previous.set_if_neq(PreviousGlobalTransform(transform.affine()));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,13 +15,13 @@ use bevy_ecs::{
|
||||
removal_detection::RemovedComponents,
|
||||
resource::Resource,
|
||||
schedule::IntoScheduleConfigs as _,
|
||||
system::{Query, Res, ResMut},
|
||||
system::{Local, Query, Res, ResMut},
|
||||
};
|
||||
use bevy_math::{vec4, FloatOrd, Vec4};
|
||||
use bevy_platform_support::collections::HashMap;
|
||||
use bevy_reflect::Reflect;
|
||||
use bevy_transform::components::GlobalTransform;
|
||||
use bevy_utils::prelude::default;
|
||||
use bevy_utils::{prelude::default, Parallel};
|
||||
use nonmax::NonMaxU16;
|
||||
use wgpu::{BufferBindingType, BufferUsages};
|
||||
|
||||
@ -385,7 +385,8 @@ impl VisibleEntityRanges {
|
||||
pub fn check_visibility_ranges(
|
||||
mut visible_entity_ranges: ResMut<VisibleEntityRanges>,
|
||||
view_query: Query<(Entity, &GlobalTransform), With<Camera>>,
|
||||
mut entity_query: Query<(Entity, &GlobalTransform, Option<&Aabb>, &VisibilityRange)>,
|
||||
mut par_local: Local<Parallel<Vec<(Entity, u32)>>>,
|
||||
entity_query: Query<(Entity, &GlobalTransform, Option<&Aabb>, &VisibilityRange)>,
|
||||
) {
|
||||
visible_entity_ranges.clear();
|
||||
|
||||
@ -404,30 +405,34 @@ pub fn check_visibility_ranges(
|
||||
|
||||
// Check each entity/view pair. Only consider entities with
|
||||
// [`VisibilityRange`] components.
|
||||
for (entity, entity_transform, maybe_model_aabb, visibility_range) in entity_query.iter_mut() {
|
||||
let mut visibility = 0;
|
||||
for (view_index, &(_, view_position)) in views.iter().enumerate() {
|
||||
// If instructed to use the AABB and the model has one, use its
|
||||
// center as the model position. Otherwise, use the model's
|
||||
// translation.
|
||||
let model_position = match (visibility_range.use_aabb, maybe_model_aabb) {
|
||||
(true, Some(model_aabb)) => entity_transform
|
||||
.affine()
|
||||
.transform_point3a(model_aabb.center),
|
||||
_ => entity_transform.translation_vec3a(),
|
||||
};
|
||||
entity_query.par_iter().for_each(
|
||||
|(entity, entity_transform, maybe_model_aabb, visibility_range)| {
|
||||
let mut visibility = 0;
|
||||
for (view_index, &(_, view_position)) in views.iter().enumerate() {
|
||||
// If instructed to use the AABB and the model has one, use its
|
||||
// center as the model position. Otherwise, use the model's
|
||||
// translation.
|
||||
let model_position = match (visibility_range.use_aabb, maybe_model_aabb) {
|
||||
(true, Some(model_aabb)) => entity_transform
|
||||
.affine()
|
||||
.transform_point3a(model_aabb.center),
|
||||
_ => entity_transform.translation_vec3a(),
|
||||
};
|
||||
|
||||
if visibility_range.is_visible_at_all((view_position - model_position).length()) {
|
||||
visibility |= 1 << view_index;
|
||||
if visibility_range.is_visible_at_all((view_position - model_position).length()) {
|
||||
visibility |= 1 << view_index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Invisible entities have no entry at all in the hash map. This speeds
|
||||
// up checks slightly in this common case.
|
||||
if visibility != 0 {
|
||||
visible_entity_ranges.entities.insert(entity, visibility);
|
||||
}
|
||||
}
|
||||
// Invisible entities have no entry at all in the hash map. This speeds
|
||||
// up checks slightly in this common case.
|
||||
if visibility != 0 {
|
||||
par_local.borrow_local_mut().push((entity, visibility));
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
visible_entity_ranges.entities.extend(par_local.drain());
|
||||
}
|
||||
|
||||
/// Extracts all [`VisibilityRange`] components from the main world to the
|
||||
|
@ -45,6 +45,7 @@ use bevy_render::{
|
||||
view::{ExtractedView, ViewVisibility},
|
||||
Extract, ExtractSchedule, Render, RenderApp, RenderSet,
|
||||
};
|
||||
use bevy_utils::Parallel;
|
||||
use core::{hash::Hash, marker::PhantomData};
|
||||
use derive_more::derive::From;
|
||||
use tracing::error;
|
||||
@ -660,14 +661,18 @@ pub fn check_entities_needing_specialization<M>(
|
||||
With<MeshMaterial2d<M>>,
|
||||
),
|
||||
>,
|
||||
mut par_local: Local<Parallel<Vec<Entity>>>,
|
||||
mut entities_needing_specialization: ResMut<EntitiesNeedingSpecialization<M>>,
|
||||
) where
|
||||
M: Material2d,
|
||||
{
|
||||
entities_needing_specialization.clear();
|
||||
for entity in &needs_specialization {
|
||||
entities_needing_specialization.push(entity);
|
||||
}
|
||||
|
||||
needs_specialization
|
||||
.par_iter()
|
||||
.for_each(|entity| par_local.borrow_local_mut().push(entity));
|
||||
|
||||
par_local.drain_into(&mut entities_needing_specialization);
|
||||
}
|
||||
|
||||
pub fn specialize_material2d_meshes<M: Material2d>(
|
||||
|
Loading…
Reference in New Issue
Block a user