Change check_visibility to use thread-local queues instead of a channel (#4663)
# Objective Further speed up visibility checking by removing the main sources of contention for the system. ## Solution - ~~Make `ComputedVisibility` a resource wrapping a `FixedBitset`.~~ - ~~Remove `ComputedVisibility` as a component.~~ ~~This adds a one-bit overhead to every entity in the app world. For a game with 100,000 entities, this is 12.5KB of memory. This is still small enough to fit entirely in most L1 caches. Also removes the need for a per-Entity change detection tick. This reduces the memory footprint of ComputedVisibility 72x.~~ ~~The decreased memory usage and less fragmented memory locality should provide significant performance benefits.~~ ~~Clearing visible entities should be significantly faster than before:~~ - ~~Setting one `u32` to 0 clears 32 entities per cycle.~~ - ~~No archetype fragmentation to contend with.~~ - ~~Change detection is applied to the resource, so there is no per-Entity update tick requirement.~~ ~~The side benefit of this design is that it removes one more "computed component" from userspace. Though accessing the values within it are now less ergonomic.~~ This PR changes `crossbeam_channel` in `check_visibility` to use a `Local<ThreadLocal<Cell<Vec<Entity>>>` to mark down visible entities instead. Co-Authored-By: TheRawMeatball <therawmeatball@gmail.com> Co-Authored-By: Aevyrie <aevyrie@gmail.com>
This commit is contained in:
		
							parent
							
								
									511bcc9633
								
							
						
					
					
						commit
						389df18343
					
				@ -54,9 +54,9 @@ bitflags = "1.2.1"
 | 
				
			|||||||
smallvec = { version = "1.6", features = ["union", "const_generics"] }
 | 
					smallvec = { version = "1.6", features = ["union", "const_generics"] }
 | 
				
			||||||
once_cell = "1.4.1" # TODO: replace once_cell with std equivalent if/when this lands: https://github.com/rust-lang/rfcs/pull/2788
 | 
					once_cell = "1.4.1" # TODO: replace once_cell with std equivalent if/when this lands: https://github.com/rust-lang/rfcs/pull/2788
 | 
				
			||||||
downcast-rs = "1.2.0"
 | 
					downcast-rs = "1.2.0"
 | 
				
			||||||
 | 
					thread_local = "1.1"
 | 
				
			||||||
thiserror = "1.0"
 | 
					thiserror = "1.0"
 | 
				
			||||||
futures-lite = "1.4.0"
 | 
					futures-lite = "1.4.0"
 | 
				
			||||||
crossbeam-channel = "0.5.0"
 | 
					 | 
				
			||||||
anyhow = "1.0"
 | 
					anyhow = "1.0"
 | 
				
			||||||
hex = "0.4.2"
 | 
					hex = "0.4.2"
 | 
				
			||||||
hexasphere = "7.0.0"
 | 
					hexasphere = "7.0.0"
 | 
				
			||||||
 | 
				
			|||||||
@ -10,6 +10,8 @@ use bevy_reflect::std_traits::ReflectDefault;
 | 
				
			|||||||
use bevy_reflect::Reflect;
 | 
					use bevy_reflect::Reflect;
 | 
				
			||||||
use bevy_transform::components::GlobalTransform;
 | 
					use bevy_transform::components::GlobalTransform;
 | 
				
			||||||
use bevy_transform::TransformSystem;
 | 
					use bevy_transform::TransformSystem;
 | 
				
			||||||
 | 
					use std::cell::Cell;
 | 
				
			||||||
 | 
					use thread_local::ThreadLocal;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{
 | 
					use crate::{
 | 
				
			||||||
    camera::{Camera, CameraProjection, OrthographicProjection, PerspectiveProjection, Projection},
 | 
					    camera::{Camera, CameraProjection, OrthographicProjection, PerspectiveProjection, Projection},
 | 
				
			||||||
@ -148,22 +150,30 @@ pub fn update_frusta<T: Component + CameraProjection + Send + Sync + 'static>(
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn check_visibility(
 | 
					pub fn check_visibility(
 | 
				
			||||||
 | 
					    mut thread_queues: Local<ThreadLocal<Cell<Vec<Entity>>>>,
 | 
				
			||||||
    mut view_query: Query<(&mut VisibleEntities, &Frustum, Option<&RenderLayers>), With<Camera>>,
 | 
					    mut view_query: Query<(&mut VisibleEntities, &Frustum, Option<&RenderLayers>), With<Camera>>,
 | 
				
			||||||
    mut visible_entity_query: Query<(
 | 
					    mut visible_entity_query: ParamSet<(
 | 
				
			||||||
        Entity,
 | 
					        Query<&mut ComputedVisibility>,
 | 
				
			||||||
        &Visibility,
 | 
					        Query<(
 | 
				
			||||||
        &mut ComputedVisibility,
 | 
					            Entity,
 | 
				
			||||||
        Option<&RenderLayers>,
 | 
					            &Visibility,
 | 
				
			||||||
        Option<&Aabb>,
 | 
					            &mut ComputedVisibility,
 | 
				
			||||||
        Option<&NoFrustumCulling>,
 | 
					            Option<&RenderLayers>,
 | 
				
			||||||
        Option<&GlobalTransform>,
 | 
					            Option<&Aabb>,
 | 
				
			||||||
 | 
					            Option<&NoFrustumCulling>,
 | 
				
			||||||
 | 
					            Option<&GlobalTransform>,
 | 
				
			||||||
 | 
					        )>,
 | 
				
			||||||
    )>,
 | 
					    )>,
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
 | 
					    // Reset the computed visibility to false
 | 
				
			||||||
 | 
					    for mut computed_visibility in visible_entity_query.p0().iter_mut() {
 | 
				
			||||||
 | 
					        computed_visibility.is_visible = false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (mut visible_entities, frustum, maybe_view_mask) in view_query.iter_mut() {
 | 
					    for (mut visible_entities, frustum, maybe_view_mask) in view_query.iter_mut() {
 | 
				
			||||||
        let view_mask = maybe_view_mask.copied().unwrap_or_default();
 | 
					        let view_mask = maybe_view_mask.copied().unwrap_or_default();
 | 
				
			||||||
        let (visible_entity_sender, visible_entity_receiver) = crossbeam_channel::unbounded();
 | 
					        visible_entities.entities.clear();
 | 
				
			||||||
 | 
					        visible_entity_query.p1().par_for_each_mut(
 | 
				
			||||||
        visible_entity_query.par_for_each_mut(
 | 
					 | 
				
			||||||
            1024,
 | 
					            1024,
 | 
				
			||||||
            |(
 | 
					            |(
 | 
				
			||||||
                entity,
 | 
					                entity,
 | 
				
			||||||
@ -174,12 +184,10 @@ pub fn check_visibility(
 | 
				
			|||||||
                maybe_no_frustum_culling,
 | 
					                maybe_no_frustum_culling,
 | 
				
			||||||
                maybe_transform,
 | 
					                maybe_transform,
 | 
				
			||||||
            )| {
 | 
					            )| {
 | 
				
			||||||
                // Reset visibility
 | 
					 | 
				
			||||||
                computed_visibility.is_visible = false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if !visibility.is_visible {
 | 
					                if !visibility.is_visible {
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                let entity_mask = maybe_entity_mask.copied().unwrap_or_default();
 | 
					                let entity_mask = maybe_entity_mask.copied().unwrap_or_default();
 | 
				
			||||||
                if !view_mask.intersects(&entity_mask) {
 | 
					                if !view_mask.intersects(&entity_mask) {
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
@ -205,9 +213,15 @@ pub fn check_visibility(
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                computed_visibility.is_visible = true;
 | 
					                computed_visibility.is_visible = true;
 | 
				
			||||||
                visible_entity_sender.send(entity).ok();
 | 
					                let cell = thread_queues.get_or_default();
 | 
				
			||||||
 | 
					                let mut queue = cell.take();
 | 
				
			||||||
 | 
					                queue.push(entity);
 | 
				
			||||||
 | 
					                cell.set(queue);
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
        visible_entities.entities = visible_entity_receiver.try_iter().collect();
 | 
					
 | 
				
			||||||
 | 
					        for cell in thread_queues.iter_mut() {
 | 
				
			||||||
 | 
					            visible_entities.entities.append(cell.get_mut());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user