Move EntityHash related types into bevy_ecs (#11498)

# Objective

Reduce the size of `bevy_utils`
(https://github.com/bevyengine/bevy/issues/11478)

## Solution

Move `EntityHash` related types into `bevy_ecs`. This also allows us
access to `Entity`, which means we no longer need `EntityHashMap`'s
first generic argument.

---

## Changelog

- Moved `bevy::utils::{EntityHash, EntityHasher, EntityHashMap,
EntityHashSet}` into `bevy::ecs::entity::hash` .
- Removed `EntityHashMap`'s first generic argument. It is now hardcoded
to always be `Entity`.

## Migration Guide

- Uses of `bevy::utils::{EntityHash, EntityHasher, EntityHashMap,
EntityHashSet}` now have to be imported from `bevy::ecs::entity::hash`.
- Uses of `EntityHashMap` no longer have to specify the first generic
parameter. It is now hardcoded to always be `Entity`.
This commit is contained in:
Doonv 2024-02-12 17:02:24 +02:00 committed by GitHub
parent c1a4e29a1e
commit 1c67e020f7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
35 changed files with 262 additions and 229 deletions

View File

@ -63,6 +63,6 @@ path = "benches/bevy_math/bezier.rs"
harness = false harness = false
[[bench]] [[bench]]
name = "utils" name = "entity_hash"
path = "benches/bevy_utils/entity_hash.rs" path = "benches/bevy_ecs/world/entity_hash.rs"
harness = false harness = false

View File

@ -1,5 +1,4 @@
use bevy_ecs::entity::Entity; use bevy_ecs::entity::{Entity, EntityHashMap, EntityHashSet};
use bevy_utils::EntityHashSet;
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use rand::{Rng, SeedableRng}; use rand::{Rng, SeedableRng};
use rand_chacha::ChaCha8Rng; use rand_chacha::ChaCha8Rng;
@ -28,7 +27,7 @@ fn make_entity(rng: &mut impl Rng, size: usize) -> Entity {
e e
} }
fn entity_set_build_and_lookup(c: &mut Criterion) { pub fn entity_set_build_and_lookup(c: &mut Criterion) {
let mut group = c.benchmark_group("entity_hash"); let mut group = c.benchmark_group("entity_hash");
for size in SIZES { for size in SIZES {
// Get some random-but-consistent entities to use for all the benches below. // Get some random-but-consistent entities to use for all the benches below.

View File

@ -1,13 +1,17 @@
use criterion::criterion_group; use criterion::criterion_group;
mod commands; mod commands;
mod spawn;
mod world_get;
use commands::*; use commands::*;
mod spawn;
use spawn::*; use spawn::*;
mod world_get;
use world_get::*; use world_get::*;
mod entity_hash;
use entity_hash::*;
criterion_group!( criterion_group!(
world_benches, world_benches,
empty_commands, empty_commands,
@ -30,4 +34,5 @@ criterion_group!(
query_get_many::<2>, query_get_many::<2>,
query_get_many::<5>, query_get_many::<5>,
query_get_many::<10>, query_get_many::<10>,
entity_set_build_and_lookup
); );

View File

@ -32,6 +32,7 @@ thiserror = "1.0"
[dev-dependencies] [dev-dependencies]
rand = "0.8" rand = "0.8"
static_assertions = "1.1.0"
[[example]] [[example]]
name = "events" name = "events"

View File

@ -0,0 +1,99 @@
use std::hash::{BuildHasher, Hasher};
#[cfg(feature = "bevy_reflect")]
use bevy_reflect::Reflect;
use bevy_utils::hashbrown;
use super::Entity;
/// A [`BuildHasher`] that results in a [`EntityHasher`].
#[derive(Default, Clone)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
pub struct EntityHash;
impl BuildHasher for EntityHash {
type Hasher = EntityHasher;
fn build_hasher(&self) -> Self::Hasher {
Self::Hasher::default()
}
}
/// A very fast hash that is only designed to work on generational indices
/// like [`Entity`]. It will panic if attempting to hash a type containing
/// non-u64 fields.
///
/// This is heavily optimized for typical cases, where you have mostly live
/// entities, and works particularly well for contiguous indices.
///
/// If you have an unusual case -- say all your indices are multiples of 256
/// or most of the entities are dead generations -- then you might want also to
/// try [`AHasher`](bevy_utils::AHasher) for a slower hash computation but fewer lookup conflicts.
#[derive(Debug, Default)]
pub struct EntityHasher {
hash: u64,
}
impl Hasher for EntityHasher {
#[inline]
fn finish(&self) -> u64 {
self.hash
}
fn write(&mut self, _bytes: &[u8]) {
panic!("EntityHasher can only hash u64 fields.");
}
#[inline]
fn write_u64(&mut self, bits: u64) {
// SwissTable (and thus `hashbrown`) cares about two things from the hash:
// - H1: low bits (masked by `2ⁿ-1`) to pick the slot in which to store the item
// - H2: high 7 bits are used to SIMD optimize hash collision probing
// For more see <https://abseil.io/about/design/swisstables#metadata-layout>
// This hash function assumes that the entity ids are still well-distributed,
// so for H1 leaves the entity id alone in the low bits so that id locality
// will also give memory locality for things spawned together.
// For H2, take advantage of the fact that while multiplication doesn't
// spread entropy to the low bits, it's incredibly good at spreading it
// upward, which is exactly where we need it the most.
// While this does include the generation in the output, it doesn't do so
// *usefully*. H1 won't care until you have over 3 billion entities in
// the table, and H2 won't care until something hits generation 33 million.
// Thus the comment suggesting that this is best for live entities,
// where there won't be generation conflicts where it would matter.
// The high 32 bits of this are ⅟φ for Fibonacci hashing. That works
// particularly well for hashing for the same reason as described in
// <https://extremelearning.com.au/unreasonable-effectiveness-of-quasirandom-sequences/>
// It loses no information because it has a modular inverse.
// (Specifically, `0x144c_bc89_u32 * 0x9e37_79b9_u32 == 1`.)
//
// The low 32 bits make that part of the just product a pass-through.
const UPPER_PHI: u64 = 0x9e37_79b9_0000_0001;
// This is `(MAGIC * index + generation) << 32 + index`, in a single instruction.
self.hash = bits.wrapping_mul(UPPER_PHI);
}
}
/// A [`HashMap`](hashbrown::HashMap) pre-configured to use [`EntityHash`] hashing.
pub type EntityHashMap<V> = hashbrown::HashMap<Entity, V, EntityHash>;
/// A [`HashSet`](hashbrown::HashSet) pre-configured to use [`EntityHash`] hashing.
pub type EntityHashSet = hashbrown::HashSet<Entity, EntityHash>;
#[cfg(test)]
mod tests {
use super::*;
#[cfg(feature = "bevy_reflect")]
use bevy_reflect::Reflect;
use static_assertions::assert_impl_all;
// Check that the HashMaps are Clone if the key/values are Clone
assert_impl_all!(EntityHashMap::<usize>: Clone);
// EntityHashMap should implement Reflect
#[cfg(feature = "bevy_reflect")]
assert_impl_all!(EntityHashMap::<i32>: Reflect);
}

View File

@ -3,7 +3,8 @@ use crate::{
identifier::masks::{IdentifierMask, HIGH_MASK}, identifier::masks::{IdentifierMask, HIGH_MASK},
world::World, world::World,
}; };
use bevy_utils::EntityHashMap;
use super::EntityHashMap;
/// Operation to map all contained [`Entity`] fields in a type to new values. /// Operation to map all contained [`Entity`] fields in a type to new values.
/// ///
@ -11,7 +12,7 @@ use bevy_utils::EntityHashMap;
/// as references in components copied from another world will be invalid. This trait /// as references in components copied from another world will be invalid. This trait
/// allows defining custom mappings for these references via [`EntityMappers`](EntityMapper), which /// allows defining custom mappings for these references via [`EntityMappers`](EntityMapper), which
/// inject the entity mapping strategy between your `MapEntities` type and the current world /// inject the entity mapping strategy between your `MapEntities` type and the current world
/// (usually by using an [`EntityHashMap<Entity, Entity>`] between source entities and entities in the /// (usually by using an [`EntityHashMap<Entity>`] between source entities and entities in the
/// current world). /// current world).
/// ///
/// Implementing this trait correctly is required for properly loading components /// Implementing this trait correctly is required for properly loading components
@ -47,7 +48,7 @@ pub trait MapEntities {
/// An implementor of this trait knows how to map an [`Entity`] into another [`Entity`]. /// An implementor of this trait knows how to map an [`Entity`] into another [`Entity`].
/// ///
/// Usually this is done by using an [`EntityHashMap<Entity, Entity>`] to map source entities /// Usually this is done by using an [`EntityHashMap<Entity>`] to map source entities
/// (mapper inputs) to the current world's entities (mapper outputs). /// (mapper inputs) to the current world's entities (mapper outputs).
/// ///
/// More generally, this can be used to map [`Entity`] references between any two [`Worlds`](World). /// More generally, this can be used to map [`Entity`] references between any two [`Worlds`](World).
@ -56,10 +57,10 @@ pub trait MapEntities {
/// ///
/// ``` /// ```
/// # use bevy_ecs::entity::{Entity, EntityMapper}; /// # use bevy_ecs::entity::{Entity, EntityMapper};
/// # use bevy_utils::EntityHashMap; /// # use bevy_ecs::entity::EntityHashMap;
/// # /// #
/// pub struct SimpleEntityMapper { /// pub struct SimpleEntityMapper {
/// map: EntityHashMap<Entity, Entity>, /// map: EntityHashMap<Entity>,
/// } /// }
/// ///
/// // Example implementation of EntityMapper where we map an entity to another entity if it exists /// // Example implementation of EntityMapper where we map an entity to another entity if it exists
@ -97,7 +98,7 @@ impl EntityMapper for SceneEntityMapper<'_> {
} }
} }
/// A wrapper for [`EntityHashMap<Entity, Entity>`], augmenting it with the ability to allocate new [`Entity`] references in a destination /// A wrapper for [`EntityHashMap<Entity>`], augmenting it with the ability to allocate new [`Entity`] references in a destination
/// world. These newly allocated references are guaranteed to never point to any living entity in that world. /// world. These newly allocated references are guaranteed to never point to any living entity in that world.
/// ///
/// References are allocated by returning increasing generations starting from an internally initialized base /// References are allocated by returning increasing generations starting from an internally initialized base
@ -110,9 +111,9 @@ pub struct SceneEntityMapper<'m> {
/// or over the network. This is required as [`Entity`] identifiers are opaque; you cannot and do not want to reuse /// or over the network. This is required as [`Entity`] identifiers are opaque; you cannot and do not want to reuse
/// identifiers directly. /// identifiers directly.
/// ///
/// On its own, a [`EntityHashMap<Entity, Entity>`] is not capable of allocating new entity identifiers, which is needed to map references /// On its own, a [`EntityHashMap<Entity>`] is not capable of allocating new entity identifiers, which is needed to map references
/// to entities that lie outside the source entity set. This functionality can be accessed through [`SceneEntityMapper::world_scope()`]. /// to entities that lie outside the source entity set. This functionality can be accessed through [`SceneEntityMapper::world_scope()`].
map: &'m mut EntityHashMap<Entity, Entity>, map: &'m mut EntityHashMap<Entity>,
/// A base [`Entity`] used to allocate new references. /// A base [`Entity`] used to allocate new references.
dead_start: Entity, dead_start: Entity,
/// The number of generations this mapper has allocated thus far. /// The number of generations this mapper has allocated thus far.
@ -129,18 +130,18 @@ impl<'m> SceneEntityMapper<'m> {
self.map_entity(entity) self.map_entity(entity)
} }
/// Gets a reference to the underlying [`EntityHashMap<Entity, Entity>`]. /// Gets a reference to the underlying [`EntityHashMap<Entity>`].
pub fn get_map(&'m self) -> &'m EntityHashMap<Entity, Entity> { pub fn get_map(&'m self) -> &'m EntityHashMap<Entity> {
self.map self.map
} }
/// Gets a mutable reference to the underlying [`EntityHashMap<Entity, Entity>`]. /// Gets a mutable reference to the underlying [`EntityHashMap<Entity>`].
pub fn get_map_mut(&'m mut self) -> &'m mut EntityHashMap<Entity, Entity> { pub fn get_map_mut(&'m mut self) -> &'m mut EntityHashMap<Entity> {
self.map self.map
} }
/// Creates a new [`SceneEntityMapper`], spawning a temporary base [`Entity`] in the provided [`World`] /// Creates a new [`SceneEntityMapper`], spawning a temporary base [`Entity`] in the provided [`World`]
fn new(map: &'m mut EntityHashMap<Entity, Entity>, world: &mut World) -> Self { fn new(map: &'m mut EntityHashMap<Entity>, world: &mut World) -> Self {
Self { Self {
map, map,
// SAFETY: Entities data is kept in a valid state via `EntityMapper::world_scope` // SAFETY: Entities data is kept in a valid state via `EntityMapper::world_scope`
@ -160,14 +161,14 @@ impl<'m> SceneEntityMapper<'m> {
assert!(entities.reserve_generations(self.dead_start.index(), self.generations)); assert!(entities.reserve_generations(self.dead_start.index(), self.generations));
} }
/// Creates an [`SceneEntityMapper`] from a provided [`World`] and [`EntityHashMap<Entity, Entity>`], then calls the /// Creates an [`SceneEntityMapper`] from a provided [`World`] and [`EntityHashMap<Entity>`], then calls the
/// provided function with it. This allows one to allocate new entity references in this [`World`] that are /// provided function with it. This allows one to allocate new entity references in this [`World`] that are
/// guaranteed to never point at a living entity now or in the future. This functionality is useful for safely /// guaranteed to never point at a living entity now or in the future. This functionality is useful for safely
/// mapping entity identifiers that point at entities outside the source world. The passed function, `f`, is called /// mapping entity identifiers that point at entities outside the source world. The passed function, `f`, is called
/// within the scope of this world. Its return value is then returned from `world_scope` as the generic type /// within the scope of this world. Its return value is then returned from `world_scope` as the generic type
/// parameter `R`. /// parameter `R`.
pub fn world_scope<R>( pub fn world_scope<R>(
entity_map: &'m mut EntityHashMap<Entity, Entity>, entity_map: &'m mut EntityHashMap<Entity>,
world: &mut World, world: &mut World,
f: impl FnOnce(&mut World, &mut Self) -> R, f: impl FnOnce(&mut World, &mut Self) -> R,
) -> R { ) -> R {
@ -180,10 +181,8 @@ impl<'m> SceneEntityMapper<'m> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use bevy_utils::EntityHashMap;
use crate::{ use crate::{
entity::{Entity, EntityMapper, SceneEntityMapper}, entity::{Entity, EntityHashMap, EntityMapper, SceneEntityMapper},
world::World, world::World,
}; };

View File

@ -36,9 +36,14 @@
//! [`EntityWorldMut::insert`]: crate::world::EntityWorldMut::insert //! [`EntityWorldMut::insert`]: crate::world::EntityWorldMut::insert
//! [`EntityWorldMut::remove`]: crate::world::EntityWorldMut::remove //! [`EntityWorldMut::remove`]: crate::world::EntityWorldMut::remove
mod map_entities; mod map_entities;
#[cfg(feature = "bevy_reflect")]
use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize};
pub use map_entities::*;
mod hash;
pub use hash::*;
use bevy_utils::tracing::warn; use bevy_utils::tracing::warn;
pub use map_entities::*;
use crate::{ use crate::{
archetype::{ArchetypeId, ArchetypeRow}, archetype::{ArchetypeId, ArchetypeRow},
@ -123,6 +128,11 @@ type IdCursor = isize;
/// [`Query::get`]: crate::system::Query::get /// [`Query::get`]: crate::system::Query::get
/// [`World`]: crate::world::World /// [`World`]: crate::world::World
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
#[cfg_attr(
feature = "bevy_reflect",
reflect_value(Hash, PartialEq, Serialize, Deserialize)
)]
// Alignment repr necessary to allow LLVM to better output // Alignment repr necessary to allow LLVM to better output
// optimised codegen for `to_bits`, `PartialEq` and `Ord`. // optimised codegen for `to_bits`, `PartialEq` and `Ord`.
#[repr(C, align(8))] #[repr(C, align(8))]
@ -218,7 +228,7 @@ impl Entity {
/// // ... replace the entities with valid ones. /// // ... replace the entities with valid ones.
/// ``` /// ```
/// ///
/// Deriving [`Reflect`](bevy_reflect::Reflect) for a component that has an `Entity` field: /// Deriving [`Reflect`] for a component that has an `Entity` field:
/// ///
/// ```no_run /// ```no_run
/// # use bevy_ecs::{prelude::*, component::*}; /// # use bevy_ecs::{prelude::*, component::*};
@ -1092,7 +1102,7 @@ mod tests {
#[test] #[test]
fn entity_hash_keeps_similar_ids_together() { fn entity_hash_keeps_similar_ids_together() {
use std::hash::BuildHasher; use std::hash::BuildHasher;
let hash = bevy_utils::EntityHash; let hash = EntityHash;
let first_id = 0xC0FFEE << 8; let first_id = 0xC0FFEE << 8;
let first_hash = hash.hash_one(Entity::from_raw(first_id)); let first_hash = hash.hash_one(Entity::from_raw(first_id));
@ -1107,7 +1117,8 @@ mod tests {
#[test] #[test]
fn entity_hash_id_bitflip_affects_high_7_bits() { fn entity_hash_id_bitflip_affects_high_7_bits() {
use std::hash::BuildHasher; use std::hash::BuildHasher;
let hash = bevy_utils::EntityHash;
let hash = EntityHash;
let first_id = 0xC0FFEE; let first_id = 0xC0FFEE;
let first_hash = hash.hash_one(Entity::from_raw(first_id)) >> 57; let first_hash = hash.hash_one(Entity::from_raw(first_id)) >> 57;

View File

@ -1,10 +1,9 @@
use crate::{ use crate::{
component::Component, component::Component,
entity::{Entity, MapEntities, SceneEntityMapper}, entity::{Entity, EntityHashMap, MapEntities, SceneEntityMapper},
world::World, world::World,
}; };
use bevy_reflect::FromType; use bevy_reflect::FromType;
use bevy_utils::EntityHashMap;
/// For a specific type of component, this maps any fields with values of type [`Entity`] to a new world. /// For a specific type of component, this maps any fields with values of type [`Entity`] to a new world.
/// Since a given `Entity` ID is only valid for the world it came from, when performing deserialization /// Since a given `Entity` ID is only valid for the world it came from, when performing deserialization
@ -18,33 +17,29 @@ pub struct ReflectMapEntities {
} }
impl ReflectMapEntities { impl ReflectMapEntities {
/// A general method for applying [`MapEntities`] behavior to all elements in an [`EntityHashMap<Entity, Entity>`]. /// A general method for applying [`MapEntities`] behavior to all elements in an [`EntityHashMap<Entity>`].
/// ///
/// Be mindful in its usage: Works best in situations where the entities in the [`EntityHashMap<Entity, Entity>`] are newly /// Be mindful in its usage: Works best in situations where the entities in the [`EntityHashMap<Entity>`] are newly
/// created, before systems have a chance to add new components. If some of the entities referred to /// created, before systems have a chance to add new components. If some of the entities referred to
/// by the [`EntityHashMap<Entity, Entity>`] might already contain valid entity references, you should use [`map_entities`](Self::map_entities). /// by the [`EntityHashMap<Entity>`] might already contain valid entity references, you should use [`map_entities`](Self::map_entities).
/// ///
/// An example of this: A scene can be loaded with `Parent` components, but then a `Parent` component can be added /// An example of this: A scene can be loaded with `Parent` components, but then a `Parent` component can be added
/// to these entities after they have been loaded. If you reload the scene using [`map_all_entities`](Self::map_all_entities), those `Parent` /// to these entities after they have been loaded. If you reload the scene using [`map_all_entities`](Self::map_all_entities), those `Parent`
/// components with already valid entity references could be updated to point at something else entirely. /// components with already valid entity references could be updated to point at something else entirely.
pub fn map_all_entities( pub fn map_all_entities(&self, world: &mut World, entity_map: &mut EntityHashMap<Entity>) {
&self,
world: &mut World,
entity_map: &mut EntityHashMap<Entity, Entity>,
) {
SceneEntityMapper::world_scope(entity_map, world, self.map_all_entities); SceneEntityMapper::world_scope(entity_map, world, self.map_all_entities);
} }
/// A general method for applying [`MapEntities`] behavior to elements in an [`EntityHashMap<Entity, Entity>`]. Unlike /// A general method for applying [`MapEntities`] behavior to elements in an [`EntityHashMap<Entity>`]. Unlike
/// [`map_all_entities`](Self::map_all_entities), this is applied to specific entities, not all values /// [`map_all_entities`](Self::map_all_entities), this is applied to specific entities, not all values
/// in the [`EntityHashMap<Entity, Entity>`]. /// in the [`EntityHashMap<Entity>`].
/// ///
/// This is useful mostly for when you need to be careful not to update components that already contain valid entity /// This is useful mostly for when you need to be careful not to update components that already contain valid entity
/// values. See [`map_all_entities`](Self::map_all_entities) for more details. /// values. See [`map_all_entities`](Self::map_all_entities) for more details.
pub fn map_entities( pub fn map_entities(
&self, &self,
world: &mut World, world: &mut World,
entity_map: &mut EntityHashMap<Entity, Entity>, entity_map: &mut EntityHashMap<Entity>,
entities: &[Entity], entities: &[Entity],
) { ) {
SceneEntityMapper::world_scope(entity_map, world, |world, mapper| { SceneEntityMapper::world_scope(entity_map, world, |world, mapper| {

View File

@ -3,8 +3,8 @@
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use crate as bevy_ecs; use crate as bevy_ecs;
use crate::{entity::Entity, system::Resource}; use crate::system::Resource;
use bevy_reflect::{impl_reflect_value, ReflectDeserialize, ReflectSerialize, TypeRegistryArc}; use bevy_reflect::TypeRegistryArc;
mod bundle; mod bundle;
mod component; mod component;
@ -40,5 +40,3 @@ impl DerefMut for AppTypeRegistry {
&mut self.0 &mut self.0
} }
} }
impl_reflect_value!((in bevy_ecs) Entity(Hash, PartialEq, Serialize, Deserialize));

View File

@ -4,6 +4,7 @@ use bevy_asset::{
}; };
use bevy_core::Name; use bevy_core::Name;
use bevy_core_pipeline::prelude::Camera3dBundle; use bevy_core_pipeline::prelude::Camera3dBundle;
use bevy_ecs::entity::EntityHashMap;
use bevy_ecs::{entity::Entity, world::World}; use bevy_ecs::{entity::Entity, world::World};
use bevy_hierarchy::{BuildWorldChildren, WorldChildBuilder}; use bevy_hierarchy::{BuildWorldChildren, WorldChildBuilder};
use bevy_log::{error, warn}; use bevy_log::{error, warn};
@ -33,7 +34,7 @@ use bevy_scene::Scene;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
use bevy_tasks::IoTaskPool; use bevy_tasks::IoTaskPool;
use bevy_transform::components::Transform; use bevy_transform::components::Transform;
use bevy_utils::{EntityHashMap, HashMap, HashSet}; use bevy_utils::{HashMap, HashSet};
use gltf::{ use gltf::{
accessor::Iter, accessor::Iter,
mesh::{util::ReadIndices, Mode}, mesh::{util::ReadIndices, Mode},
@ -930,7 +931,7 @@ fn load_node(
load_context: &mut LoadContext, load_context: &mut LoadContext,
settings: &GltfLoaderSettings, settings: &GltfLoaderSettings,
node_index_to_entity_map: &mut HashMap<usize, Entity>, node_index_to_entity_map: &mut HashMap<usize, Entity>,
entity_to_skin_index_map: &mut EntityHashMap<Entity, usize>, entity_to_skin_index_map: &mut EntityHashMap<usize>,
active_camera_found: &mut bool, active_camera_found: &mut bool,
parent_transform: &Transform, parent_transform: &Transform,
) -> Result<(), GltfError> { ) -> Result<(), GltfError> {

View File

@ -3,7 +3,8 @@ use crate::{
StandardMaterial, StandardMaterial,
}; };
use bevy_asset::Handle; use bevy_asset::Handle;
use bevy_ecs::{bundle::Bundle, component::Component, prelude::Entity, reflect::ReflectComponent}; use bevy_ecs::entity::EntityHashMap;
use bevy_ecs::{bundle::Bundle, component::Component, reflect::ReflectComponent};
use bevy_reflect::Reflect; use bevy_reflect::Reflect;
use bevy_render::{ use bevy_render::{
mesh::Mesh, mesh::Mesh,
@ -11,7 +12,6 @@ use bevy_render::{
view::{InheritedVisibility, ViewVisibility, Visibility, VisibleEntities}, view::{InheritedVisibility, ViewVisibility, Visibility, VisibleEntities},
}; };
use bevy_transform::components::{GlobalTransform, Transform}; use bevy_transform::components::{GlobalTransform, Transform};
use bevy_utils::EntityHashMap;
/// A component bundle for PBR entities with a [`Mesh`] and a [`StandardMaterial`]. /// A component bundle for PBR entities with a [`Mesh`] and a [`StandardMaterial`].
pub type PbrBundle = MaterialMeshBundle<StandardMaterial>; pub type PbrBundle = MaterialMeshBundle<StandardMaterial>;
@ -75,7 +75,7 @@ impl CubemapVisibleEntities {
pub struct CascadesVisibleEntities { pub struct CascadesVisibleEntities {
/// Map of view entity to the visible entities for each cascade frustum. /// Map of view entity to the visible entities for each cascade frustum.
#[reflect(ignore)] #[reflect(ignore)]
pub entities: EntityHashMap<Entity, Vec<VisibleEntities>>, pub entities: EntityHashMap<Vec<VisibleEntities>>,
} }
/// A component bundle for [`PointLight`] entities. /// A component bundle for [`PointLight`] entities.

View File

@ -1,5 +1,6 @@
use std::collections::HashSet; use std::collections::HashSet;
use bevy_ecs::entity::EntityHashMap;
use bevy_ecs::prelude::*; use bevy_ecs::prelude::*;
use bevy_math::{ use bevy_math::{
AspectRatio, Mat4, UVec2, UVec3, Vec2, Vec3, Vec3A, Vec3Swizzles, Vec4, Vec4Swizzles, AspectRatio, Mat4, UVec2, UVec3, Vec2, Vec3, Vec3A, Vec3Swizzles, Vec4, Vec4Swizzles,
@ -16,7 +17,7 @@ use bevy_render::{
view::{InheritedVisibility, RenderLayers, ViewVisibility, VisibleEntities}, view::{InheritedVisibility, RenderLayers, ViewVisibility, VisibleEntities},
}; };
use bevy_transform::components::{GlobalTransform, Transform}; use bevy_transform::components::{GlobalTransform, Transform};
use bevy_utils::{tracing::warn, EntityHashMap}; use bevy_utils::tracing::warn;
use crate::*; use crate::*;
@ -390,7 +391,7 @@ impl From<CascadeShadowConfigBuilder> for CascadeShadowConfig {
#[reflect(Component)] #[reflect(Component)]
pub struct Cascades { pub struct Cascades {
/// Map from a view to the configuration of each of its [`Cascade`]s. /// Map from a view to the configuration of each of its [`Cascade`]s.
pub(crate) cascades: EntityHashMap<Entity, Vec<Cascade>>, pub(crate) cascades: EntityHashMap<Vec<Cascade>>,
} }
#[derive(Clone, Debug, Default, Reflect)] #[derive(Clone, Debug, Default, Reflect)]

View File

@ -159,6 +159,7 @@ pub struct LightProbesUniform {
intensity_for_view: f32, intensity_for_view: f32,
} }
/// A GPU buffer that stores information about all light probes.
#[derive(Resource, Default, Deref, DerefMut)] #[derive(Resource, Default, Deref, DerefMut)]
pub struct LightProbesBuffer(DynamicUniformBuffer<LightProbesUniform>); pub struct LightProbesBuffer(DynamicUniformBuffer<LightProbesUniform>);

View File

@ -30,6 +30,7 @@
use bevy_app::{App, Plugin}; use bevy_app::{App, Plugin};
use bevy_asset::{load_internal_asset, AssetId, Handle}; use bevy_asset::{load_internal_asset, AssetId, Handle};
use bevy_ecs::entity::EntityHashMap;
use bevy_ecs::{ use bevy_ecs::{
component::Component, component::Component,
entity::Entity, entity::Entity,
@ -43,7 +44,7 @@ use bevy_render::{
mesh::Mesh, render_asset::RenderAssets, render_resource::Shader, texture::Image, mesh::Mesh, render_asset::RenderAssets, render_resource::Shader, texture::Image,
view::ViewVisibility, Extract, ExtractSchedule, RenderApp, view::ViewVisibility, Extract, ExtractSchedule, RenderApp,
}; };
use bevy_utils::{EntityHashMap, HashSet}; use bevy_utils::HashSet;
use crate::RenderMeshInstances; use crate::RenderMeshInstances;
@ -104,7 +105,7 @@ pub struct RenderLightmaps {
/// ///
/// Entities without lightmaps, or for which the mesh or lightmap isn't /// Entities without lightmaps, or for which the mesh or lightmap isn't
/// loaded, won't have entries in this table. /// loaded, won't have entries in this table.
pub(crate) render_lightmaps: EntityHashMap<Entity, RenderLightmap>, pub(crate) render_lightmaps: EntityHashMap<RenderLightmap>,
/// All active lightmap images in the scene. /// All active lightmap images in the scene.
/// ///

View File

@ -1,4 +1,5 @@
use bevy_core_pipeline::core_3d::{Transparent3d, CORE_3D_DEPTH_FORMAT}; use bevy_core_pipeline::core_3d::{Transparent3d, CORE_3D_DEPTH_FORMAT};
use bevy_ecs::entity::EntityHashMap;
use bevy_ecs::prelude::*; use bevy_ecs::prelude::*;
use bevy_math::{Mat4, UVec3, UVec4, Vec2, Vec3, Vec3Swizzles, Vec4, Vec4Swizzles}; use bevy_math::{Mat4, UVec3, UVec4, Vec2, Vec3, Vec3Swizzles, Vec4, Vec4Swizzles};
use bevy_render::{ use bevy_render::{
@ -21,7 +22,6 @@ use bevy_utils::tracing::info_span;
use bevy_utils::{ use bevy_utils::{
nonmax::NonMaxU32, nonmax::NonMaxU32,
tracing::{error, warn}, tracing::{error, warn},
EntityHashMap,
}; };
use std::{hash::Hash, num::NonZeroU64, ops::Range}; use std::{hash::Hash, num::NonZeroU64, ops::Range};
@ -50,8 +50,8 @@ pub struct ExtractedDirectionalLight {
shadow_depth_bias: f32, shadow_depth_bias: f32,
shadow_normal_bias: f32, shadow_normal_bias: f32,
cascade_shadow_config: CascadeShadowConfig, cascade_shadow_config: CascadeShadowConfig,
cascades: EntityHashMap<Entity, Vec<Cascade>>, cascades: EntityHashMap<Vec<Cascade>>,
frusta: EntityHashMap<Entity, Vec<Frustum>>, frusta: EntityHashMap<Vec<Frustum>>,
render_layers: RenderLayers, render_layers: RenderLayers,
} }
@ -586,7 +586,7 @@ pub const CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT: u32 = 3;
#[derive(Resource)] #[derive(Resource)]
pub struct GlobalLightMeta { pub struct GlobalLightMeta {
pub gpu_point_lights: GpuPointLights, pub gpu_point_lights: GpuPointLights,
pub entity_to_index: EntityHashMap<Entity, usize>, pub entity_to_index: EntityHashMap<usize>,
} }
impl FromWorld for GlobalLightMeta { impl FromWorld for GlobalLightMeta {

View File

@ -10,6 +10,7 @@ use bevy_core_pipeline::{
deferred::{AlphaMask3dDeferred, Opaque3dDeferred}, deferred::{AlphaMask3dDeferred, Opaque3dDeferred},
}; };
use bevy_derive::{Deref, DerefMut}; use bevy_derive::{Deref, DerefMut};
use bevy_ecs::entity::EntityHashMap;
use bevy_ecs::{ use bevy_ecs::{
prelude::*, prelude::*,
query::ROQueryItem, query::ROQueryItem,
@ -33,7 +34,7 @@ use bevy_render::{
Extract, ExtractSchedule, Render, RenderApp, RenderSet, Extract, ExtractSchedule, Render, RenderApp, RenderSet,
}; };
use bevy_transform::components::GlobalTransform; use bevy_transform::components::GlobalTransform;
use bevy_utils::{tracing::error, EntityHashMap, Entry, HashMap, Hashed}; use bevy_utils::{tracing::error, Entry, HashMap, Hashed};
use std::cell::Cell; use std::cell::Cell;
use thread_local::ThreadLocal; use thread_local::ThreadLocal;
@ -254,7 +255,7 @@ pub struct RenderMeshInstance {
} }
#[derive(Default, Resource, Deref, DerefMut)] #[derive(Default, Resource, Deref, DerefMut)]
pub struct RenderMeshInstances(EntityHashMap<Entity, RenderMeshInstance>); pub struct RenderMeshInstances(EntityHashMap<RenderMeshInstance>);
pub fn extract_meshes( pub fn extract_meshes(
mut render_mesh_instances: ResMut<RenderMeshInstances>, mut render_mesh_instances: ResMut<RenderMeshInstances>,

View File

@ -1,6 +1,7 @@
use std::{iter, mem}; use std::{iter, mem};
use bevy_derive::{Deref, DerefMut}; use bevy_derive::{Deref, DerefMut};
use bevy_ecs::entity::EntityHashMap;
use bevy_ecs::prelude::*; use bevy_ecs::prelude::*;
use bevy_render::{ use bevy_render::{
batching::NoAutomaticBatching, batching::NoAutomaticBatching,
@ -10,7 +11,6 @@ use bevy_render::{
view::ViewVisibility, view::ViewVisibility,
Extract, Extract,
}; };
use bevy_utils::EntityHashMap;
use bytemuck::Pod; use bytemuck::Pod;
#[derive(Component)] #[derive(Component)]
@ -19,7 +19,7 @@ pub struct MorphIndex {
} }
#[derive(Default, Resource, Deref, DerefMut)] #[derive(Default, Resource, Deref, DerefMut)]
pub struct MorphIndices(EntityHashMap<Entity, MorphIndex>); pub struct MorphIndices(EntityHashMap<MorphIndex>);
#[derive(Resource)] #[derive(Resource)]
pub struct MorphUniform { pub struct MorphUniform {

View File

@ -1,5 +1,6 @@
use bevy_asset::Assets; use bevy_asset::Assets;
use bevy_derive::{Deref, DerefMut}; use bevy_derive::{Deref, DerefMut};
use bevy_ecs::entity::EntityHashMap;
use bevy_ecs::prelude::*; use bevy_ecs::prelude::*;
use bevy_math::Mat4; use bevy_math::Mat4;
use bevy_render::{ use bevy_render::{
@ -11,7 +12,6 @@ use bevy_render::{
Extract, Extract,
}; };
use bevy_transform::prelude::GlobalTransform; use bevy_transform::prelude::GlobalTransform;
use bevy_utils::EntityHashMap;
/// Maximum number of joints supported for skinned meshes. /// Maximum number of joints supported for skinned meshes.
pub const MAX_JOINTS: usize = 256; pub const MAX_JOINTS: usize = 256;
@ -31,7 +31,7 @@ impl SkinIndex {
} }
#[derive(Default, Resource, Deref, DerefMut)] #[derive(Default, Resource, Deref, DerefMut)]
pub struct SkinIndices(EntityHashMap<Entity, SkinIndex>); pub struct SkinIndices(EntityHashMap<SkinIndex>);
// Notes on implementation: see comment on top of the `extract_skins` system. // Notes on implementation: see comment on top of the `extract_skins` system.
#[derive(Resource)] #[derive(Resource)]

View File

@ -80,7 +80,6 @@ impl_reflect_value!(isize(
impl_reflect_value!(f32(Debug, PartialEq, Serialize, Deserialize, Default)); impl_reflect_value!(f32(Debug, PartialEq, Serialize, Deserialize, Default));
impl_reflect_value!(f64(Debug, PartialEq, Serialize, Deserialize, Default)); impl_reflect_value!(f64(Debug, PartialEq, Serialize, Deserialize, Default));
impl_type_path!(str); impl_type_path!(str);
impl_type_path!(::bevy_utils::EntityHash);
impl_reflect_value!(::alloc::string::String( impl_reflect_value!(::alloc::string::String(
Debug, Debug,
Hash, Hash,
@ -1655,15 +1654,12 @@ mod tests {
Enum, FromReflect, Reflect, ReflectSerialize, TypeInfo, TypeRegistry, Typed, VariantInfo, Enum, FromReflect, Reflect, ReflectSerialize, TypeInfo, TypeRegistry, Typed, VariantInfo,
VariantType, VariantType,
}; };
use bevy_utils::HashMap;
use bevy_utils::{Duration, Instant}; use bevy_utils::{Duration, Instant};
use bevy_utils::{EntityHashMap, HashMap};
use static_assertions::assert_impl_all; use static_assertions::assert_impl_all;
use std::f32::consts::{PI, TAU}; use std::f32::consts::{PI, TAU};
use std::path::Path; use std::path::Path;
// EntityHashMap should implement Reflect
assert_impl_all!(EntityHashMap<i32, i32>: Reflect);
#[test] #[test]
fn can_serialize_duration() { fn can_serialize_duration() {
let mut type_registry = TypeRegistry::default(); let mut type_registry = TypeRegistry::default();

View File

@ -10,11 +10,11 @@ use bevy_app::{App, Plugin};
use bevy_asset::{Asset, AssetId, Handle}; use bevy_asset::{Asset, AssetId, Handle};
use bevy_derive::{Deref, DerefMut}; use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{ use bevy_ecs::{
entity::EntityHashMap,
prelude::Entity, prelude::Entity,
query::{QueryFilter, QueryItem, ReadOnlyQueryData}, query::{QueryFilter, QueryItem, ReadOnlyQueryData},
system::{lifetimeless::Read, Query, ResMut, Resource}, system::{lifetimeless::Read, Query, ResMut, Resource},
}; };
use bevy_utils::EntityHashMap;
use crate::{prelude::ViewVisibility, Extract, ExtractSchedule, RenderApp}; use crate::{prelude::ViewVisibility, Extract, ExtractSchedule, RenderApp};
@ -53,7 +53,7 @@ where
/// Stores all extract instances of a type in the render world. /// Stores all extract instances of a type in the render world.
#[derive(Resource, Deref, DerefMut)] #[derive(Resource, Deref, DerefMut)]
pub struct ExtractedInstances<EI>(EntityHashMap<Entity, EI>) pub struct ExtractedInstances<EI>(EntityHashMap<EI>)
where where
EI: ExtractInstance; EI: ExtractInstance;

View File

@ -1,9 +1,8 @@
use std::borrow::Borrow; use std::borrow::Borrow;
use bevy_ecs::{component::Component, prelude::Entity, reflect::ReflectComponent}; use bevy_ecs::{component::Component, entity::EntityHashMap, reflect::ReflectComponent};
use bevy_math::{Affine3A, Mat3A, Mat4, Vec3, Vec3A, Vec4, Vec4Swizzles}; use bevy_math::{Affine3A, Mat3A, Mat4, Vec3, Vec3A, Vec4, Vec4Swizzles};
use bevy_reflect::Reflect; use bevy_reflect::Reflect;
use bevy_utils::EntityHashMap;
/// An axis-aligned bounding box, defined by: /// An axis-aligned bounding box, defined by:
/// - a center, /// - a center,
@ -323,7 +322,7 @@ impl CubemapFrusta {
#[reflect(Component)] #[reflect(Component)]
pub struct CascadesFrusta { pub struct CascadesFrusta {
#[reflect(ignore)] #[reflect(ignore)]
pub frusta: EntityHashMap<Entity, Vec<Frustum>>, pub frusta: EntityHashMap<Vec<Frustum>>,
} }
#[cfg(test)] #[cfg(test)]

View File

@ -7,8 +7,8 @@ use crate::{
Extract, ExtractSchedule, Render, RenderApp, RenderSet, Extract, ExtractSchedule, Render, RenderApp, RenderSet,
}; };
use bevy_app::{App, Plugin}; use bevy_app::{App, Plugin};
use bevy_ecs::prelude::*; use bevy_ecs::{entity::EntityHashMap, prelude::*};
use bevy_utils::{default, tracing::debug, EntityHashMap, HashSet}; use bevy_utils::{default, tracing::debug, HashSet};
use bevy_window::{ use bevy_window::{
CompositeAlphaMode, PresentMode, PrimaryWindow, RawHandleWrapper, Window, WindowClosed, CompositeAlphaMode, PresentMode, PrimaryWindow, RawHandleWrapper, Window, WindowClosed,
}; };
@ -92,11 +92,11 @@ impl ExtractedWindow {
#[derive(Default, Resource)] #[derive(Default, Resource)]
pub struct ExtractedWindows { pub struct ExtractedWindows {
pub primary: Option<Entity>, pub primary: Option<Entity>,
pub windows: EntityHashMap<Entity, ExtractedWindow>, pub windows: EntityHashMap<ExtractedWindow>,
} }
impl Deref for ExtractedWindows { impl Deref for ExtractedWindows {
type Target = EntityHashMap<Entity, ExtractedWindow>; type Target = EntityHashMap<ExtractedWindow>;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.windows &self.windows
@ -203,7 +203,7 @@ struct SurfaceData {
#[derive(Resource, Default)] #[derive(Resource, Default)]
pub struct WindowSurfaces { pub struct WindowSurfaces {
surfaces: EntityHashMap<Entity, SurfaceData>, surfaces: EntityHashMap<SurfaceData>,
/// List of windows that we have already called the initial `configure_surface` for /// List of windows that we have already called the initial `configure_surface` for
configured_windows: HashSet<Entity>, configured_windows: HashSet<Entity>,
} }

View File

@ -2,10 +2,9 @@ use std::{borrow::Cow, path::Path, sync::PoisonError};
use bevy_app::Plugin; use bevy_app::Plugin;
use bevy_asset::{load_internal_asset, Handle}; use bevy_asset::{load_internal_asset, Handle};
use bevy_ecs::prelude::*; use bevy_ecs::{entity::EntityHashMap, prelude::*};
use bevy_log::{error, info, info_span}; use bevy_log::{error, info, info_span};
use bevy_tasks::AsyncComputeTaskPool; use bevy_tasks::AsyncComputeTaskPool;
use bevy_utils::EntityHashMap;
use std::sync::Mutex; use std::sync::Mutex;
use thiserror::Error; use thiserror::Error;
use wgpu::{ use wgpu::{
@ -33,7 +32,7 @@ pub type ScreenshotFn = Box<dyn FnOnce(Image) + Send + Sync>;
#[derive(Resource, Default)] #[derive(Resource, Default)]
pub struct ScreenshotManager { pub struct ScreenshotManager {
// this is in a mutex to enable extraction with only an immutable reference // this is in a mutex to enable extraction with only an immutable reference
pub(crate) callbacks: Mutex<EntityHashMap<Entity, ScreenshotFn>>, pub(crate) callbacks: Mutex<EntityHashMap<ScreenshotFn>>,
} }
#[derive(Error, Debug)] #[derive(Error, Debug)]

View File

@ -1,11 +1,12 @@
use crate::{ron, DynamicSceneBuilder, Scene, SceneSpawnError}; use crate::{ron, DynamicSceneBuilder, Scene, SceneSpawnError};
use bevy_ecs::entity::EntityHashMap;
use bevy_ecs::{ use bevy_ecs::{
entity::Entity, entity::Entity,
reflect::{AppTypeRegistry, ReflectComponent, ReflectMapEntities}, reflect::{AppTypeRegistry, ReflectComponent, ReflectMapEntities},
world::World, world::World,
}; };
use bevy_reflect::{Reflect, TypePath, TypeRegistryArc}; use bevy_reflect::{Reflect, TypePath, TypeRegistryArc};
use bevy_utils::{EntityHashMap, TypeIdMap}; use bevy_utils::TypeIdMap;
#[cfg(feature = "serialize")] #[cfg(feature = "serialize")]
use crate::serde::SceneSerializer; use crate::serde::SceneSerializer;
@ -65,7 +66,7 @@ impl DynamicScene {
pub fn write_to_world_with( pub fn write_to_world_with(
&self, &self,
world: &mut World, world: &mut World,
entity_map: &mut EntityHashMap<Entity, Entity>, entity_map: &mut EntityHashMap<Entity>,
type_registry: &AppTypeRegistry, type_registry: &AppTypeRegistry,
) -> Result<(), SceneSpawnError> { ) -> Result<(), SceneSpawnError> {
let type_registry = type_registry.read(); let type_registry = type_registry.read();
@ -163,7 +164,7 @@ impl DynamicScene {
pub fn write_to_world( pub fn write_to_world(
&self, &self,
world: &mut World, world: &mut World,
entity_map: &mut EntityHashMap<Entity, Entity>, entity_map: &mut EntityHashMap<Entity>,
) -> Result<(), SceneSpawnError> { ) -> Result<(), SceneSpawnError> {
let registry = world.resource::<AppTypeRegistry>().clone(); let registry = world.resource::<AppTypeRegistry>().clone();
self.write_to_world_with(world, entity_map, &registry) self.write_to_world_with(world, entity_map, &registry)
@ -191,9 +192,9 @@ where
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use bevy_ecs::entity::EntityHashMap;
use bevy_ecs::{reflect::AppTypeRegistry, system::Command, world::World}; use bevy_ecs::{reflect::AppTypeRegistry, system::Command, world::World};
use bevy_hierarchy::{Parent, PushChild}; use bevy_hierarchy::{Parent, PushChild};
use bevy_utils::EntityHashMap;
use crate::dynamic_scene_builder::DynamicSceneBuilder; use crate::dynamic_scene_builder::DynamicSceneBuilder;

View File

@ -1,11 +1,11 @@
use crate::{DynamicScene, InstanceInfo, SceneSpawnError}; use crate::{DynamicScene, InstanceInfo, SceneSpawnError};
use bevy_asset::Asset; use bevy_asset::Asset;
use bevy_ecs::entity::EntityHashMap;
use bevy_ecs::{ use bevy_ecs::{
reflect::{AppTypeRegistry, ReflectComponent, ReflectMapEntities, ReflectResource}, reflect::{AppTypeRegistry, ReflectComponent, ReflectMapEntities, ReflectResource},
world::World, world::World,
}; };
use bevy_reflect::TypePath; use bevy_reflect::TypePath;
use bevy_utils::EntityHashMap;
/// To spawn a scene, you can use either: /// To spawn a scene, you can use either:
/// * [`SceneSpawner::spawn`](crate::SceneSpawner::spawn) /// * [`SceneSpawner::spawn`](crate::SceneSpawner::spawn)

View File

@ -1,5 +1,6 @@
use crate::{DynamicScene, Scene}; use crate::{DynamicScene, Scene};
use bevy_asset::{AssetEvent, AssetId, Assets, Handle}; use bevy_asset::{AssetEvent, AssetId, Assets, Handle};
use bevy_ecs::entity::EntityHashMap;
use bevy_ecs::{ use bevy_ecs::{
entity::Entity, entity::Entity,
event::{Event, Events, ManualEventReader}, event::{Event, Events, ManualEventReader},
@ -8,7 +9,7 @@ use bevy_ecs::{
world::{Mut, World}, world::{Mut, World},
}; };
use bevy_hierarchy::{Parent, PushChild}; use bevy_hierarchy::{Parent, PushChild};
use bevy_utils::{tracing::error, EntityHashMap, HashMap, HashSet}; use bevy_utils::{tracing::error, HashMap, HashSet};
use thiserror::Error; use thiserror::Error;
use uuid::Uuid; use uuid::Uuid;
@ -25,7 +26,7 @@ pub struct SceneInstanceReady {
#[derive(Debug)] #[derive(Debug)]
pub struct InstanceInfo { pub struct InstanceInfo {
/// Mapping of entities from the scene world to the instance world. /// Mapping of entities from the scene world to the instance world.
pub entity_map: EntityHashMap<Entity, Entity>, pub entity_map: EntityHashMap<Entity>,
} }
/// Unique id identifying a scene instance. /// Unique id identifying a scene instance.
@ -213,7 +214,7 @@ impl SceneSpawner {
fn spawn_dynamic_internal( fn spawn_dynamic_internal(
world: &mut World, world: &mut World,
id: AssetId<DynamicScene>, id: AssetId<DynamicScene>,
entity_map: &mut EntityHashMap<Entity, Entity>, entity_map: &mut EntityHashMap<Entity>,
) -> Result<(), SceneSpawnError> { ) -> Result<(), SceneSpawnError> {
world.resource_scope(|world, scenes: Mut<Assets<DynamicScene>>| { world.resource_scope(|world, scenes: Mut<Assets<DynamicScene>>| {
let scene = scenes let scene = scenes

View File

@ -499,13 +499,13 @@ mod tests {
use crate::ron; use crate::ron;
use crate::serde::{SceneDeserializer, SceneSerializer}; use crate::serde::{SceneDeserializer, SceneSerializer};
use crate::{DynamicScene, DynamicSceneBuilder}; use crate::{DynamicScene, DynamicSceneBuilder};
use bevy_ecs::entity::EntityHashMap;
use bevy_ecs::entity::{Entity, EntityMapper, MapEntities}; use bevy_ecs::entity::{Entity, EntityMapper, MapEntities};
use bevy_ecs::prelude::{Component, ReflectComponent, ReflectResource, Resource, World}; use bevy_ecs::prelude::{Component, ReflectComponent, ReflectResource, Resource, World};
use bevy_ecs::query::{With, Without}; use bevy_ecs::query::{With, Without};
use bevy_ecs::reflect::{AppTypeRegistry, ReflectMapEntities}; use bevy_ecs::reflect::{AppTypeRegistry, ReflectMapEntities};
use bevy_ecs::world::FromWorld; use bevy_ecs::world::FromWorld;
use bevy_reflect::{Reflect, ReflectSerialize}; use bevy_reflect::{Reflect, ReflectSerialize};
use bevy_utils::EntityHashMap;
use bincode::Options; use bincode::Options;
use serde::de::DeserializeSeed; use serde::de::DeserializeSeed;
use serde::Serialize; use serde::Serialize;

View File

@ -5,6 +5,7 @@ use bevy_core_pipeline::{
tonemapping::{DebandDither, Tonemapping}, tonemapping::{DebandDither, Tonemapping},
}; };
use bevy_derive::{Deref, DerefMut}; use bevy_derive::{Deref, DerefMut};
use bevy_ecs::entity::EntityHashMap;
use bevy_ecs::{ use bevy_ecs::{
prelude::*, prelude::*,
system::{lifetimeless::SRes, SystemParamItem}, system::{lifetimeless::SRes, SystemParamItem},
@ -29,7 +30,7 @@ use bevy_render::{
Extract, ExtractSchedule, Render, RenderApp, RenderSet, Extract, ExtractSchedule, Render, RenderApp, RenderSet,
}; };
use bevy_transform::components::{GlobalTransform, Transform}; use bevy_transform::components::{GlobalTransform, Transform};
use bevy_utils::{EntityHashMap, FloatOrd, HashMap, HashSet}; use bevy_utils::{FloatOrd, HashMap, HashSet};
use std::hash::Hash; use std::hash::Hash;
use std::marker::PhantomData; use std::marker::PhantomData;
@ -180,7 +181,7 @@ where
} }
#[derive(Resource, Deref, DerefMut)] #[derive(Resource, Deref, DerefMut)]
pub struct RenderMaterial2dInstances<M: Material2d>(EntityHashMap<Entity, AssetId<M>>); pub struct RenderMaterial2dInstances<M: Material2d>(EntityHashMap<AssetId<M>>);
impl<M: Material2d> Default for RenderMaterial2dInstances<M> { impl<M: Material2d> Default for RenderMaterial2dInstances<M> {
fn default() -> Self { fn default() -> Self {

View File

@ -3,6 +3,7 @@ use bevy_asset::{load_internal_asset, AssetId, Handle};
use bevy_core_pipeline::core_2d::Transparent2d; use bevy_core_pipeline::core_2d::Transparent2d;
use bevy_derive::{Deref, DerefMut}; use bevy_derive::{Deref, DerefMut};
use bevy_ecs::entity::EntityHashMap;
use bevy_ecs::{ use bevy_ecs::{
prelude::*, prelude::*,
query::ROQueryItem, query::ROQueryItem,
@ -30,7 +31,6 @@ use bevy_render::{
Extract, ExtractSchedule, Render, RenderApp, RenderSet, Extract, ExtractSchedule, Render, RenderApp, RenderSet,
}; };
use bevy_transform::components::GlobalTransform; use bevy_transform::components::GlobalTransform;
use bevy_utils::EntityHashMap;
use crate::Material2dBindGroupId; use crate::Material2dBindGroupId;
@ -192,7 +192,7 @@ pub struct RenderMesh2dInstance {
} }
#[derive(Default, Resource, Deref, DerefMut)] #[derive(Default, Resource, Deref, DerefMut)]
pub struct RenderMesh2dInstances(EntityHashMap<Entity, RenderMesh2dInstance>); pub struct RenderMesh2dInstances(EntityHashMap<RenderMesh2dInstance>);
#[derive(Component)] #[derive(Component)]
pub struct Mesh2d; pub struct Mesh2d;

View File

@ -9,6 +9,7 @@ use bevy_core_pipeline::{
core_2d::Transparent2d, core_2d::Transparent2d,
tonemapping::{DebandDither, Tonemapping}, tonemapping::{DebandDither, Tonemapping},
}; };
use bevy_ecs::entity::EntityHashMap;
use bevy_ecs::{ use bevy_ecs::{
prelude::*, prelude::*,
system::{lifetimeless::*, SystemParamItem, SystemState}, system::{lifetimeless::*, SystemParamItem, SystemState},
@ -36,7 +37,7 @@ use bevy_render::{
Extract, Extract,
}; };
use bevy_transform::components::GlobalTransform; use bevy_transform::components::GlobalTransform;
use bevy_utils::{EntityHashMap, FloatOrd, HashMap}; use bevy_utils::{FloatOrd, HashMap};
use bytemuck::{Pod, Zeroable}; use bytemuck::{Pod, Zeroable};
use fixedbitset::FixedBitSet; use fixedbitset::FixedBitSet;
@ -312,7 +313,7 @@ pub struct ExtractedSprite {
#[derive(Resource, Default)] #[derive(Resource, Default)]
pub struct ExtractedSprites { pub struct ExtractedSprites {
pub sprites: EntityHashMap<Entity, ExtractedSprite>, pub sprites: EntityHashMap<ExtractedSprite>,
} }
#[derive(Resource, Default)] #[derive(Resource, Default)]

View File

@ -2,6 +2,7 @@ mod convert;
pub mod debug; pub mod debug;
use crate::{ContentSize, DefaultUiCamera, Node, Outline, Style, TargetCamera, UiScale}; use crate::{ContentSize, DefaultUiCamera, Node, Outline, Style, TargetCamera, UiScale};
use bevy_ecs::entity::EntityHashMap;
use bevy_ecs::{ use bevy_ecs::{
change_detection::{DetectChanges, DetectChangesMut}, change_detection::{DetectChanges, DetectChangesMut},
entity::Entity, entity::Entity,
@ -16,7 +17,7 @@ use bevy_log::warn;
use bevy_math::{UVec2, Vec2}; use bevy_math::{UVec2, Vec2};
use bevy_render::camera::{Camera, NormalizedRenderTarget}; use bevy_render::camera::{Camera, NormalizedRenderTarget};
use bevy_transform::components::Transform; use bevy_transform::components::Transform;
use bevy_utils::{default, EntityHashMap, HashMap, HashSet}; use bevy_utils::{default, HashMap, HashSet};
use bevy_window::{PrimaryWindow, Window, WindowScaleFactorChanged}; use bevy_window::{PrimaryWindow, Window, WindowScaleFactorChanged};
use std::fmt; use std::fmt;
use taffy::{tree::LayoutTree, Taffy}; use taffy::{tree::LayoutTree, Taffy};
@ -51,14 +52,14 @@ struct RootNodePair {
#[derive(Resource)] #[derive(Resource)]
pub struct UiSurface { pub struct UiSurface {
entity_to_taffy: EntityHashMap<Entity, taffy::node::Node>, entity_to_taffy: EntityHashMap<taffy::node::Node>,
camera_roots: EntityHashMap<Entity, Vec<RootNodePair>>, camera_roots: EntityHashMap<Vec<RootNodePair>>,
taffy: Taffy, taffy: Taffy,
} }
fn _assert_send_sync_ui_surface_impl_safe() { fn _assert_send_sync_ui_surface_impl_safe() {
fn _assert_send_sync<T: Send + Sync>() {} fn _assert_send_sync<T: Send + Sync>() {}
_assert_send_sync::<EntityHashMap<Entity, taffy::node::Node>>(); _assert_send_sync::<EntityHashMap<taffy::node::Node>>();
_assert_send_sync::<Taffy>(); _assert_send_sync::<Taffy>();
_assert_send_sync::<UiSurface>(); _assert_send_sync::<UiSurface>();
} }

View File

@ -23,6 +23,7 @@ use crate::{
use bevy_app::prelude::*; use bevy_app::prelude::*;
use bevy_asset::{load_internal_asset, AssetEvent, AssetId, Assets, Handle}; use bevy_asset::{load_internal_asset, AssetEvent, AssetId, Assets, Handle};
use bevy_ecs::entity::EntityHashMap;
use bevy_ecs::prelude::*; use bevy_ecs::prelude::*;
use bevy_math::{Mat4, Rect, URect, UVec4, Vec2, Vec3, Vec4Swizzles}; use bevy_math::{Mat4, Rect, URect, UVec4, Vec2, Vec3, Vec4Swizzles};
use bevy_render::{ use bevy_render::{
@ -41,7 +42,7 @@ use bevy_sprite::TextureAtlasLayout;
#[cfg(feature = "bevy_text")] #[cfg(feature = "bevy_text")]
use bevy_text::{PositionedGlyph, Text, TextLayoutInfo}; use bevy_text::{PositionedGlyph, Text, TextLayoutInfo};
use bevy_transform::components::GlobalTransform; use bevy_transform::components::GlobalTransform;
use bevy_utils::{EntityHashMap, FloatOrd, HashMap}; use bevy_utils::{FloatOrd, HashMap};
use bytemuck::{Pod, Zeroable}; use bytemuck::{Pod, Zeroable};
use std::ops::Range; use std::ops::Range;
@ -147,7 +148,7 @@ pub struct ExtractedUiNode {
#[derive(Resource, Default)] #[derive(Resource, Default)]
pub struct ExtractedUiNodes { pub struct ExtractedUiNodes {
pub uinodes: EntityHashMap<Entity, ExtractedUiNode>, pub uinodes: EntityHashMap<ExtractedUiNode>,
} }
pub(crate) fn resolve_border_thickness(value: Val, parent_width: f32, viewport_size: Vec2) -> f32 { pub(crate) fn resolve_border_thickness(value: Val, parent_width: f32, viewport_size: Vec2) -> f32 {

View File

@ -265,124 +265,6 @@ impl<K: Hash + Eq + PartialEq + Clone, V> PreHashMapExt<K, V> for PreHashMap<K,
} }
} }
/// A [`BuildHasher`] that results in a [`EntityHasher`].
#[derive(Default, Clone)]
pub struct EntityHash;
impl BuildHasher for EntityHash {
type Hasher = EntityHasher;
fn build_hasher(&self) -> Self::Hasher {
EntityHasher::default()
}
}
/// A very fast hash that is only designed to work on generational indices
/// like `Entity`. It will panic if attempting to hash a type containing
/// non-u64 fields.
///
/// This is heavily optimized for typical cases, where you have mostly live
/// entities, and works particularly well for contiguous indices.
///
/// If you have an unusual case -- say all your indices are multiples of 256
/// or most of the entities are dead generations -- then you might want also to
/// try [`AHasher`] for a slower hash computation but fewer lookup conflicts.
#[derive(Debug, Default)]
pub struct EntityHasher {
hash: u64,
}
impl Hasher for EntityHasher {
#[inline]
fn finish(&self) -> u64 {
self.hash
}
fn write(&mut self, _bytes: &[u8]) {
panic!("can only hash u64 using EntityHasher");
}
#[inline]
fn write_u64(&mut self, bits: u64) {
// SwissTable (and thus `hashbrown`) cares about two things from the hash:
// - H1: low bits (masked by `2ⁿ-1`) to pick the slot in which to store the item
// - H2: high 7 bits are used to SIMD optimize hash collision probing
// For more see <https://abseil.io/about/design/swisstables#metadata-layout>
// This hash function assumes that the entity ids are still well-distributed,
// so for H1 leaves the entity id alone in the low bits so that id locality
// will also give memory locality for things spawned together.
// For H2, take advantage of the fact that while multiplication doesn't
// spread entropy to the low bits, it's incredibly good at spreading it
// upward, which is exactly where we need it the most.
// While this does include the generation in the output, it doesn't do so
// *usefully*. H1 won't care until you have over 3 billion entities in
// the table, and H2 won't care until something hits generation 33 million.
// Thus the comment suggesting that this is best for live entities,
// where there won't be generation conflicts where it would matter.
// The high 32 bits of this are ⅟φ for Fibonacci hashing. That works
// particularly well for hashing for the same reason as described in
// <https://extremelearning.com.au/unreasonable-effectiveness-of-quasirandom-sequences/>
// It loses no information because it has a modular inverse.
// (Specifically, `0x144c_bc89_u32 * 0x9e37_79b9_u32 == 1`.)
//
// The low 32 bits make that part of the just product a pass-through.
const UPPER_PHI: u64 = 0x9e37_79b9_0000_0001;
// This is `(MAGIC * index + generation) << 32 + index`, in a single instruction.
self.hash = bits.wrapping_mul(UPPER_PHI);
}
}
/// A [`HashMap`] pre-configured to use [`EntityHash`] hashing.
/// Iteration order only depends on the order of insertions and deletions.
pub type EntityHashMap<K, V> = hashbrown::HashMap<K, V, EntityHash>;
/// A [`HashSet`] pre-configured to use [`EntityHash`] hashing.
/// Iteration order only depends on the order of insertions and deletions.
pub type EntityHashSet<T> = hashbrown::HashSet<T, EntityHash>;
/// A specialized hashmap type with Key of [`TypeId`]
/// Iteration order only depends on the order of insertions and deletions.
pub type TypeIdMap<V> = hashbrown::HashMap<TypeId, V, NoOpTypeIdHash>;
/// [`BuildHasher`] for [`TypeId`]s.
#[derive(Default)]
pub struct NoOpTypeIdHash;
impl BuildHasher for NoOpTypeIdHash {
type Hasher = NoOpTypeIdHasher;
fn build_hasher(&self) -> Self::Hasher {
NoOpTypeIdHasher(0)
}
}
#[doc(hidden)]
pub struct NoOpTypeIdHasher(u64);
// TypeId already contains a high-quality hash, so skip re-hashing that hash.
impl std::hash::Hasher for NoOpTypeIdHasher {
fn finish(&self) -> u64 {
self.0
}
fn write(&mut self, bytes: &[u8]) {
// This will never be called: TypeId always just calls write_u64 once!
// This is a known trick and unlikely to change, but isn't officially guaranteed.
// Don't break applications (slower fallback, just check in test):
self.0 = bytes.iter().fold(self.0, |hash, b| {
hash.rotate_left(8).wrapping_add(*b as u64)
});
}
fn write_u64(&mut self, i: u64) {
self.0 = i;
}
}
/// A type which calls a function when dropped. /// A type which calls a function when dropped.
/// This can be used to ensure that cleanup code is run even in case of a panic. /// This can be used to ensure that cleanup code is run even in case of a panic.
/// ///
@ -438,6 +320,45 @@ impl<F: FnOnce()> Drop for OnDrop<F> {
} }
} }
/// A specialized hashmap type with Key of [`TypeId`]
/// Iteration order only depends on the order of insertions and deletions.
pub type TypeIdMap<V> = hashbrown::HashMap<TypeId, V, NoOpTypeIdHash>;
/// [`BuildHasher`] for [`TypeId`]s.
#[derive(Default)]
pub struct NoOpTypeIdHash;
impl BuildHasher for NoOpTypeIdHash {
type Hasher = NoOpTypeIdHasher;
fn build_hasher(&self) -> Self::Hasher {
NoOpTypeIdHasher(0)
}
}
#[doc(hidden)]
#[derive(Default)]
pub struct NoOpTypeIdHasher(pub u64);
// TypeId already contains a high-quality hash, so skip re-hashing that hash.
impl std::hash::Hasher for NoOpTypeIdHasher {
fn finish(&self) -> u64 {
self.0
}
fn write(&mut self, bytes: &[u8]) {
// This will never be called: TypeId always just calls write_u64 once!
// This is a known trick and unlikely to change, but isn't officially guaranteed.
// Don't break applications (slower fallback, just check in test):
self.0 = bytes.iter().fold(self.0, |hash, b| {
hash.rotate_left(8).wrapping_add(*b as u64)
});
}
fn write_u64(&mut self, i: u64) {
self.0 = i;
}
}
/// Calls the [`tracing::info!`] macro on a value. /// Calls the [`tracing::info!`] macro on a value.
pub fn info<T: Debug>(data: T) { pub fn info<T: Debug>(data: T) {
tracing::info!("{:?}", data); tracing::info!("{:?}", data);
@ -478,7 +399,6 @@ mod tests {
use static_assertions::assert_impl_all; use static_assertions::assert_impl_all;
// Check that the HashMaps are Clone if the key/values are Clone // Check that the HashMaps are Clone if the key/values are Clone
assert_impl_all!(EntityHashMap::<u64, usize>: Clone);
assert_impl_all!(PreHashMap::<u64, usize>: Clone); assert_impl_all!(PreHashMap::<u64, usize>: Clone);
#[test] #[test]

View File

@ -15,6 +15,7 @@ use bevy_a11y::{
use bevy_a11y::{ActionRequest as ActionRequestWrapper, ManageAccessibilityUpdates}; use bevy_a11y::{ActionRequest as ActionRequestWrapper, ManageAccessibilityUpdates};
use bevy_app::{App, Plugin, PostUpdate}; use bevy_app::{App, Plugin, PostUpdate};
use bevy_derive::{Deref, DerefMut}; use bevy_derive::{Deref, DerefMut};
use bevy_ecs::entity::EntityHashMap;
use bevy_ecs::{ use bevy_ecs::{
prelude::{DetectChanges, Entity, EventReader, EventWriter}, prelude::{DetectChanges, Entity, EventReader, EventWriter},
query::With, query::With,
@ -22,16 +23,15 @@ use bevy_ecs::{
system::{NonSend, NonSendMut, Query, Res, ResMut, Resource}, system::{NonSend, NonSendMut, Query, Res, ResMut, Resource},
}; };
use bevy_hierarchy::{Children, Parent}; use bevy_hierarchy::{Children, Parent};
use bevy_utils::EntityHashMap;
use bevy_window::{PrimaryWindow, Window, WindowClosed}; use bevy_window::{PrimaryWindow, Window, WindowClosed};
/// Maps window entities to their `AccessKit` [`Adapter`]s. /// Maps window entities to their `AccessKit` [`Adapter`]s.
#[derive(Default, Deref, DerefMut)] #[derive(Default, Deref, DerefMut)]
pub struct AccessKitAdapters(pub EntityHashMap<Entity, Adapter>); pub struct AccessKitAdapters(pub EntityHashMap<Adapter>);
/// Maps window entities to their respective [`WinitActionHandler`]s. /// Maps window entities to their respective [`WinitActionHandler`]s.
#[derive(Resource, Default, Deref, DerefMut)] #[derive(Resource, Default, Deref, DerefMut)]
pub struct WinitActionHandlers(pub EntityHashMap<Entity, WinitActionHandler>); pub struct WinitActionHandlers(pub EntityHashMap<WinitActionHandler>);
/// Forwards `AccessKit` [`ActionRequest`]s from winit to an event channel. /// Forwards `AccessKit` [`ActionRequest`]s from winit to an event channel.
#[derive(Clone, Default, Deref, DerefMut)] #[derive(Clone, Default, Deref, DerefMut)]

View File

@ -5,7 +5,8 @@ use bevy_a11y::{
}; };
use bevy_ecs::entity::Entity; use bevy_ecs::entity::Entity;
use bevy_utils::{tracing::warn, EntityHashMap, HashMap}; use bevy_ecs::entity::EntityHashMap;
use bevy_utils::{tracing::warn, HashMap};
use bevy_window::{CursorGrabMode, Window, WindowMode, WindowPosition, WindowResolution}; use bevy_window::{CursorGrabMode, Window, WindowMode, WindowPosition, WindowResolution};
use winit::{ use winit::{
@ -25,7 +26,7 @@ pub struct WinitWindows {
/// Stores [`winit`] windows by window identifier. /// Stores [`winit`] windows by window identifier.
pub windows: HashMap<winit::window::WindowId, winit::window::Window>, pub windows: HashMap<winit::window::WindowId, winit::window::Window>,
/// Maps entities to `winit` window identifiers. /// Maps entities to `winit` window identifiers.
pub entity_to_winit: EntityHashMap<Entity, winit::window::WindowId>, pub entity_to_winit: EntityHashMap<winit::window::WindowId>,
/// Maps `winit` window identifiers to entities. /// Maps `winit` window identifiers to entities.
pub winit_to_entity: HashMap<winit::window::WindowId, Entity>, pub winit_to_entity: HashMap<winit::window::WindowId, Entity>,
// Many `winit` window functions (e.g. `set_window_icon`) can only be called on the main thread. // Many `winit` window functions (e.g. `set_window_icon`) can only be called on the main thread.