From a6425892a373798aed2c130ab8af6b14b33418cc Mon Sep 17 00:00:00 2001 From: Doonv <58695417+doonv@users.noreply.github.com> Date: Tue, 23 Jan 2024 19:04:00 +0200 Subject: [PATCH 01/17] Move `EntityHash` related types into `bevy_ecs` --- crates/bevy_ecs/Cargo.toml | 1 + crates/bevy_ecs/src/entity/hash.rs | 93 +++++++++++++++++++ crates/bevy_ecs/src/entity/map_entities.rs | 10 +- crates/bevy_ecs/src/entity/mod.rs | 14 ++- crates/bevy_ecs/src/reflect/map_entities.rs | 3 +- crates/bevy_ecs/src/reflect/mod.rs | 6 +- crates/bevy_gltf/src/loader.rs | 3 +- crates/bevy_pbr/src/bundle.rs | 2 +- crates/bevy_pbr/src/light.rs | 3 +- crates/bevy_pbr/src/light_probe/mod.rs | 3 +- crates/bevy_pbr/src/lightmap/mod.rs | 3 +- crates/bevy_pbr/src/render/light.rs | 2 +- crates/bevy_pbr/src/render/mesh.rs | 3 +- crates/bevy_pbr/src/render/morph.rs | 2 +- crates/bevy_pbr/src/render/skin.rs | 2 +- crates/bevy_reflect/src/impls/std.rs | 6 +- crates/bevy_render/src/extract_instances.rs | 5 +- crates/bevy_render/src/primitives/mod.rs | 3 +- crates/bevy_render/src/view/window/mod.rs | 4 +- .../bevy_render/src/view/window/screenshot.rs | 3 +- crates/bevy_scene/src/dynamic_scene.rs | 5 +- crates/bevy_scene/src/scene.rs | 2 +- crates/bevy_scene/src/scene_spawner.rs | 3 +- crates/bevy_scene/src/serde.rs | 2 +- crates/bevy_sprite/src/mesh2d/material.rs | 3 +- crates/bevy_sprite/src/mesh2d/mesh.rs | 2 +- crates/bevy_sprite/src/render/mod.rs | 3 +- crates/bevy_ui/src/layout/mod.rs | 3 +- crates/bevy_ui/src/render/mod.rs | 3 +- crates/bevy_utils/src/lib.rs | 78 ---------------- crates/bevy_winit/src/accessibility.rs | 2 +- crates/bevy_winit/src/system.rs | 6 +- crates/bevy_winit/src/winit_windows.rs | 3 +- 33 files changed, 152 insertions(+), 134 deletions(-) create mode 100644 crates/bevy_ecs/src/entity/hash.rs diff --git a/crates/bevy_ecs/Cargo.toml b/crates/bevy_ecs/Cargo.toml index da5bc549b9cbc..6e8346aef1c18 100644 --- a/crates/bevy_ecs/Cargo.toml +++ b/crates/bevy_ecs/Cargo.toml @@ -31,6 +31,7 @@ thiserror = "1.0" [dev-dependencies] rand = "0.8" +static_assertions = "1.1.0" [[example]] name = "events" diff --git a/crates/bevy_ecs/src/entity/hash.rs b/crates/bevy_ecs/src/entity/hash.rs new file mode 100644 index 0000000000000..2b04cce7b90b3 --- /dev/null +++ b/crates/bevy_ecs/src/entity/hash.rs @@ -0,0 +1,93 @@ +use std::hash::{BuildHasher, Hasher}; + +use bevy_reflect::Reflect; +use bevy_utils::hashbrown; + +/// A [`BuildHasher`] that results in a [`EntityHasher`]. +#[derive(Default, Clone, Reflect)] +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 + + // 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 + // + // 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. +pub type EntityHashMap = hashbrown::HashMap; + +/// A [`HashSet`] pre-configured to use [`EntityHash`] hashing. +pub type EntityHashSet = hashbrown::HashSet; + +#[cfg(test)] +mod tests { + use super::*; + 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::: Clone); + // EntityHashMap should implement Reflect + assert_impl_all!(EntityHashMap::: Reflect); +} diff --git a/crates/bevy_ecs/src/entity/map_entities.rs b/crates/bevy_ecs/src/entity/map_entities.rs index 4c99ff69d5f51..33cc55e3475b9 100644 --- a/crates/bevy_ecs/src/entity/map_entities.rs +++ b/crates/bevy_ecs/src/entity/map_entities.rs @@ -3,7 +3,8 @@ use crate::{ identifier::masks::{IdentifierMask, HIGH_MASK}, world::World, }; -use bevy_utils::EntityHashMap; + +use super::EntityHashMap; /// Operation to map all contained [`Entity`] fields in a type to new values. /// @@ -136,12 +137,9 @@ impl<'m> EntityMapper<'m> { #[cfg(test)] mod tests { - use bevy_utils::EntityHashMap; + use super::{Entity, EntityHashMap, EntityMapper}; - use crate::{ - entity::{Entity, EntityMapper}, - world::World, - }; + use crate::world::World; #[test] fn entity_mapper() { diff --git a/crates/bevy_ecs/src/entity/mod.rs b/crates/bevy_ecs/src/entity/mod.rs index ab7d167cff67c..6afe3ccbb374d 100644 --- a/crates/bevy_ecs/src/entity/mod.rs +++ b/crates/bevy_ecs/src/entity/mod.rs @@ -36,9 +36,13 @@ //! [`EntityWorldMut::insert`]: crate::world::EntityWorldMut::insert //! [`EntityWorldMut::remove`]: crate::world::EntityWorldMut::remove mod map_entities; +use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize}; +pub use map_entities::*; + +mod hash; +pub use hash::*; use bevy_utils::tracing::warn; -pub use map_entities::*; use crate::{ archetype::{ArchetypeId, ArchetypeRow}, @@ -122,7 +126,8 @@ type IdCursor = isize; /// [`EntityCommands`]: crate::system::EntityCommands /// [`Query::get`]: crate::system::Query::get /// [`World`]: crate::world::World -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Reflect)] +#[reflect_value(Hash, PartialEq, Serialize, Deserialize)] // Alignment repr necessary to allow LLVM to better output // optimised codegen for `to_bits`, `PartialEq` and `Ord`. #[repr(C, align(8))] @@ -1092,7 +1097,7 @@ mod tests { #[test] fn entity_hash_keeps_similar_ids_together() { use std::hash::BuildHasher; - let hash = bevy_utils::EntityHash; + let hash = EntityHash; let first_id = 0xC0FFEE << 8; let first_hash = hash.hash_one(Entity::from_raw(first_id)); @@ -1107,7 +1112,8 @@ mod tests { #[test] fn entity_hash_id_bitflip_affects_high_7_bits() { use std::hash::BuildHasher; - let hash = bevy_utils::EntityHash; + + let hash = EntityHash; let first_id = 0xC0FFEE; let first_hash = hash.hash_one(Entity::from_raw(first_id)) >> 57; diff --git a/crates/bevy_ecs/src/reflect/map_entities.rs b/crates/bevy_ecs/src/reflect/map_entities.rs index 27bcb03653546..852668ea4798a 100644 --- a/crates/bevy_ecs/src/reflect/map_entities.rs +++ b/crates/bevy_ecs/src/reflect/map_entities.rs @@ -1,10 +1,9 @@ use crate::{ component::Component, - entity::{Entity, EntityMapper, MapEntities}, + entity::{Entity, EntityHashMap, EntityMapper, MapEntities}, world::World, }; 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. /// Since a given `Entity` ID is only valid for the world it came from, when performing deserialization diff --git a/crates/bevy_ecs/src/reflect/mod.rs b/crates/bevy_ecs/src/reflect/mod.rs index 544ad02932472..720fe8dbf5ae6 100644 --- a/crates/bevy_ecs/src/reflect/mod.rs +++ b/crates/bevy_ecs/src/reflect/mod.rs @@ -3,8 +3,8 @@ use std::ops::{Deref, DerefMut}; use crate as bevy_ecs; -use crate::{entity::Entity, system::Resource}; -use bevy_reflect::{impl_reflect_value, ReflectDeserialize, ReflectSerialize, TypeRegistryArc}; +use crate::system::Resource; +use bevy_reflect::TypeRegistryArc; mod bundle; mod component; @@ -40,5 +40,3 @@ impl DerefMut for AppTypeRegistry { &mut self.0 } } - -impl_reflect_value!((in bevy_ecs) Entity(Hash, PartialEq, Serialize, Deserialize)); diff --git a/crates/bevy_gltf/src/loader.rs b/crates/bevy_gltf/src/loader.rs index 9340ef7ebc721..d31bc97b77757 100644 --- a/crates/bevy_gltf/src/loader.rs +++ b/crates/bevy_gltf/src/loader.rs @@ -4,6 +4,7 @@ use bevy_asset::{ }; use bevy_core::Name; use bevy_core_pipeline::prelude::Camera3dBundle; +use bevy_ecs::entity::EntityHashMap; use bevy_ecs::{entity::Entity, world::World}; use bevy_hierarchy::{BuildWorldChildren, WorldChildBuilder}; use bevy_log::{error, warn}; @@ -33,7 +34,7 @@ use bevy_scene::Scene; #[cfg(not(target_arch = "wasm32"))] use bevy_tasks::IoTaskPool; use bevy_transform::components::Transform; -use bevy_utils::{EntityHashMap, HashMap, HashSet}; +use bevy_utils::{HashMap, HashSet}; use gltf::{ accessor::Iter, mesh::{util::ReadIndices, Mode}, diff --git a/crates/bevy_pbr/src/bundle.rs b/crates/bevy_pbr/src/bundle.rs index 6c7829bd1242d..c0433ac030b0c 100644 --- a/crates/bevy_pbr/src/bundle.rs +++ b/crates/bevy_pbr/src/bundle.rs @@ -3,6 +3,7 @@ use crate::{ StandardMaterial, }; use bevy_asset::Handle; +use bevy_ecs::entity::EntityHashMap; use bevy_ecs::{bundle::Bundle, component::Component, prelude::Entity, reflect::ReflectComponent}; use bevy_reflect::Reflect; use bevy_render::{ @@ -11,7 +12,6 @@ use bevy_render::{ view::{InheritedVisibility, ViewVisibility, Visibility, VisibleEntities}, }; use bevy_transform::components::{GlobalTransform, Transform}; -use bevy_utils::EntityHashMap; /// A component bundle for PBR entities with a [`Mesh`] and a [`StandardMaterial`]. pub type PbrBundle = MaterialMeshBundle; diff --git a/crates/bevy_pbr/src/light.rs b/crates/bevy_pbr/src/light.rs index c4d35164533fa..1b5c5e2fbc2f0 100644 --- a/crates/bevy_pbr/src/light.rs +++ b/crates/bevy_pbr/src/light.rs @@ -1,5 +1,6 @@ use std::collections::HashSet; +use bevy_ecs::entity::EntityHashMap; use bevy_ecs::prelude::*; use bevy_math::{ AspectRatio, Mat4, UVec2, UVec3, Vec2, Vec3, Vec3A, Vec3Swizzles, Vec4, Vec4Swizzles, @@ -16,7 +17,7 @@ use bevy_render::{ view::{InheritedVisibility, RenderLayers, ViewVisibility, VisibleEntities}, }; use bevy_transform::components::{GlobalTransform, Transform}; -use bevy_utils::{tracing::warn, EntityHashMap}; +use bevy_utils::tracing::warn; use crate::*; diff --git a/crates/bevy_pbr/src/light_probe/mod.rs b/crates/bevy_pbr/src/light_probe/mod.rs index c76d661eead56..6fe80e3448008 100644 --- a/crates/bevy_pbr/src/light_probe/mod.rs +++ b/crates/bevy_pbr/src/light_probe/mod.rs @@ -4,6 +4,7 @@ use bevy_app::{App, Plugin}; use bevy_asset::load_internal_asset; use bevy_core_pipeline::core_3d::Camera3d; use bevy_derive::{Deref, DerefMut}; +use bevy_ecs::entity::EntityHashMap; use bevy_ecs::{ component::Component, entity::Entity, @@ -24,7 +25,7 @@ use bevy_render::{ Extract, ExtractSchedule, Render, RenderApp, RenderSet, }; use bevy_transform::prelude::GlobalTransform; -use bevy_utils::{EntityHashMap, FloatOrd}; +use bevy_utils::FloatOrd; use crate::light_probe::environment_map::{ binding_arrays_are_usable, EnvironmentMapIds, EnvironmentMapLight, RenderViewEnvironmentMaps, diff --git a/crates/bevy_pbr/src/lightmap/mod.rs b/crates/bevy_pbr/src/lightmap/mod.rs index 3185507fbb55c..bcdbefa5ba50a 100644 --- a/crates/bevy_pbr/src/lightmap/mod.rs +++ b/crates/bevy_pbr/src/lightmap/mod.rs @@ -30,6 +30,7 @@ use bevy_app::{App, Plugin}; use bevy_asset::{load_internal_asset, AssetId, Handle}; +use bevy_ecs::entity::EntityHashMap; use bevy_ecs::{ component::Component, entity::Entity, @@ -43,7 +44,7 @@ use bevy_render::{ mesh::Mesh, render_asset::RenderAssets, render_resource::Shader, texture::Image, view::ViewVisibility, Extract, ExtractSchedule, RenderApp, }; -use bevy_utils::{EntityHashMap, HashSet}; +use bevy_utils::HashSet; use crate::RenderMeshInstances; diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index 0fdbb8066531a..9b64a3d3b51a4 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -19,8 +19,8 @@ use bevy_transform::{components::GlobalTransform, prelude::Transform}; use bevy_utils::{ nonmax::NonMaxU32, tracing::{error, warn}, - EntityHashMap, }; +use bevy_ecs::entity::EntityHashMap; use std::{hash::Hash, num::NonZeroU64, ops::Range}; use crate::*; diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index c905332b1fbb6..3728afed5919c 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -10,6 +10,7 @@ use bevy_core_pipeline::{ deferred::{AlphaMask3dDeferred, Opaque3dDeferred}, }; use bevy_derive::{Deref, DerefMut}; +use bevy_ecs::entity::EntityHashMap; use bevy_ecs::{ prelude::*, query::ROQueryItem, @@ -33,7 +34,7 @@ use bevy_render::{ Extract, ExtractSchedule, Render, RenderApp, RenderSet, }; 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 thread_local::ThreadLocal; diff --git a/crates/bevy_pbr/src/render/morph.rs b/crates/bevy_pbr/src/render/morph.rs index 61dfef75d5280..4d132deb83535 100644 --- a/crates/bevy_pbr/src/render/morph.rs +++ b/crates/bevy_pbr/src/render/morph.rs @@ -1,6 +1,7 @@ use std::{iter, mem}; use bevy_derive::{Deref, DerefMut}; +use bevy_ecs::entity::EntityHashMap; use bevy_ecs::prelude::*; use bevy_render::{ batching::NoAutomaticBatching, @@ -10,7 +11,6 @@ use bevy_render::{ view::ViewVisibility, Extract, }; -use bevy_utils::EntityHashMap; use bytemuck::Pod; #[derive(Component)] diff --git a/crates/bevy_pbr/src/render/skin.rs b/crates/bevy_pbr/src/render/skin.rs index bfb12fd794427..ec8891719e784 100644 --- a/crates/bevy_pbr/src/render/skin.rs +++ b/crates/bevy_pbr/src/render/skin.rs @@ -1,5 +1,6 @@ use bevy_asset::Assets; use bevy_derive::{Deref, DerefMut}; +use bevy_ecs::entity::EntityHashMap; use bevy_ecs::prelude::*; use bevy_math::Mat4; use bevy_render::{ @@ -11,7 +12,6 @@ use bevy_render::{ Extract, }; use bevy_transform::prelude::GlobalTransform; -use bevy_utils::EntityHashMap; /// Maximum number of joints supported for skinned meshes. pub const MAX_JOINTS: usize = 256; diff --git a/crates/bevy_reflect/src/impls/std.rs b/crates/bevy_reflect/src/impls/std.rs index 3d522e9462155..67d7453f58eaa 100644 --- a/crates/bevy_reflect/src/impls/std.rs +++ b/crates/bevy_reflect/src/impls/std.rs @@ -80,7 +80,6 @@ impl_reflect_value!(isize( impl_reflect_value!(f32(Debug, PartialEq, Serialize, Deserialize, Default)); impl_reflect_value!(f64(Debug, PartialEq, Serialize, Deserialize, Default)); impl_type_path!(str); -impl_type_path!(::bevy_utils::EntityHash); impl_reflect_value!(::alloc::string::String( Debug, Hash, @@ -1516,15 +1515,12 @@ mod tests { Enum, FromReflect, Reflect, ReflectSerialize, TypeInfo, TypeRegistry, Typed, VariantInfo, VariantType, }; + use bevy_utils::HashMap; use bevy_utils::{Duration, Instant}; - use bevy_utils::{EntityHashMap, HashMap}; use static_assertions::assert_impl_all; use std::f32::consts::{PI, TAU}; use std::path::Path; - // EntityHashMap should implement Reflect - assert_impl_all!(EntityHashMap: Reflect); - #[test] fn can_serialize_duration() { let mut type_registry = TypeRegistry::default(); diff --git a/crates/bevy_render/src/extract_instances.rs b/crates/bevy_render/src/extract_instances.rs index 96fcfea7cb84e..982dc42703c46 100644 --- a/crates/bevy_render/src/extract_instances.rs +++ b/crates/bevy_render/src/extract_instances.rs @@ -10,11 +10,8 @@ use bevy_app::{App, Plugin}; use bevy_asset::{Asset, AssetId, Handle}; use bevy_derive::{Deref, DerefMut}; use bevy_ecs::{ - prelude::Entity, - query::{QueryFilter, QueryItem, ReadOnlyQueryData}, - system::{lifetimeless::Read, Query, ResMut, Resource}, + entity::EntityHashMap, prelude::Entity, query::{QueryFilter, QueryItem, ReadOnlyQueryData}, system::{lifetimeless::Read, Query, ResMut, Resource} }; -use bevy_utils::EntityHashMap; use crate::{prelude::ViewVisibility, Extract, ExtractSchedule, RenderApp}; diff --git a/crates/bevy_render/src/primitives/mod.rs b/crates/bevy_render/src/primitives/mod.rs index b06511a9ba138..e30bfb956fcab 100644 --- a/crates/bevy_render/src/primitives/mod.rs +++ b/crates/bevy_render/src/primitives/mod.rs @@ -1,9 +1,8 @@ use std::borrow::Borrow; -use bevy_ecs::{component::Component, prelude::Entity, reflect::ReflectComponent}; +use bevy_ecs::{component::Component, entity::EntityHashMap, prelude::Entity, reflect::ReflectComponent}; use bevy_math::{Affine3A, Mat3A, Mat4, Vec3, Vec3A, Vec4, Vec4Swizzles}; use bevy_reflect::Reflect; -use bevy_utils::EntityHashMap; /// An axis-aligned bounding box, defined by: /// - a center, diff --git a/crates/bevy_render/src/view/window/mod.rs b/crates/bevy_render/src/view/window/mod.rs index aec0e53d1aee3..44c9c2250697a 100644 --- a/crates/bevy_render/src/view/window/mod.rs +++ b/crates/bevy_render/src/view/window/mod.rs @@ -7,8 +7,8 @@ use crate::{ Extract, ExtractSchedule, Render, RenderApp, RenderSet, }; use bevy_app::{App, Plugin}; -use bevy_ecs::prelude::*; -use bevy_utils::{default, tracing::debug, EntityHashMap, HashSet}; +use bevy_ecs::{entity::EntityHashMap, prelude::*}; +use bevy_utils::{default, tracing::debug, HashSet}; use bevy_window::{ CompositeAlphaMode, PresentMode, PrimaryWindow, RawHandleWrapper, Window, WindowClosed, }; diff --git a/crates/bevy_render/src/view/window/screenshot.rs b/crates/bevy_render/src/view/window/screenshot.rs index 7238df17a7635..ecaf9b219889c 100644 --- a/crates/bevy_render/src/view/window/screenshot.rs +++ b/crates/bevy_render/src/view/window/screenshot.rs @@ -2,10 +2,9 @@ use std::{borrow::Cow, path::Path, sync::PoisonError}; use bevy_app::Plugin; 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_tasks::AsyncComputeTaskPool; -use bevy_utils::EntityHashMap; use std::sync::Mutex; use thiserror::Error; use wgpu::{ diff --git a/crates/bevy_scene/src/dynamic_scene.rs b/crates/bevy_scene/src/dynamic_scene.rs index 676fb9fdcd834..24651ae918724 100644 --- a/crates/bevy_scene/src/dynamic_scene.rs +++ b/crates/bevy_scene/src/dynamic_scene.rs @@ -1,11 +1,12 @@ use crate::{ron, DynamicSceneBuilder, Scene, SceneSpawnError}; +use bevy_ecs::entity::EntityHashMap; use bevy_ecs::{ entity::Entity, reflect::{AppTypeRegistry, ReflectComponent, ReflectMapEntities}, world::World, }; use bevy_reflect::{Reflect, TypePath, TypeRegistryArc}; -use bevy_utils::{EntityHashMap, HashMap}; +use bevy_utils::HashMap; use std::any::TypeId; #[cfg(feature = "serialize")] @@ -192,9 +193,9 @@ where #[cfg(test)] mod tests { + use bevy_ecs::entity::EntityHashMap; use bevy_ecs::{reflect::AppTypeRegistry, system::Command, world::World}; use bevy_hierarchy::{Parent, PushChild}; - use bevy_utils::EntityHashMap; use crate::dynamic_scene_builder::DynamicSceneBuilder; diff --git a/crates/bevy_scene/src/scene.rs b/crates/bevy_scene/src/scene.rs index 28cf824aa061c..30d4d4743fcd3 100644 --- a/crates/bevy_scene/src/scene.rs +++ b/crates/bevy_scene/src/scene.rs @@ -1,11 +1,11 @@ use crate::{DynamicScene, InstanceInfo, SceneSpawnError}; use bevy_asset::Asset; +use bevy_ecs::entity::EntityHashMap; use bevy_ecs::{ reflect::{AppTypeRegistry, ReflectComponent, ReflectMapEntities, ReflectResource}, world::World, }; use bevy_reflect::TypePath; -use bevy_utils::EntityHashMap; /// To spawn a scene, you can use either: /// * [`SceneSpawner::spawn`](crate::SceneSpawner::spawn) diff --git a/crates/bevy_scene/src/scene_spawner.rs b/crates/bevy_scene/src/scene_spawner.rs index fc42c522f16ee..653eff209b797 100644 --- a/crates/bevy_scene/src/scene_spawner.rs +++ b/crates/bevy_scene/src/scene_spawner.rs @@ -8,7 +8,8 @@ use bevy_ecs::{ world::{Mut, World}, }; use bevy_hierarchy::{Parent, PushChild}; -use bevy_utils::{tracing::error, EntityHashMap, HashMap, HashSet}; +use bevy_utils::{tracing::error, HashMap, HashSet}; +use bevy_ecs::entity::EntityHashMap; use thiserror::Error; use uuid::Uuid; diff --git a/crates/bevy_scene/src/serde.rs b/crates/bevy_scene/src/serde.rs index 0567a9451b617..ca582f6ddecac 100644 --- a/crates/bevy_scene/src/serde.rs +++ b/crates/bevy_scene/src/serde.rs @@ -499,13 +499,13 @@ mod tests { use crate::ron; use crate::serde::{SceneDeserializer, SceneSerializer}; use crate::{DynamicScene, DynamicSceneBuilder}; + use bevy_ecs::entity::EntityHashMap; use bevy_ecs::entity::{Entity, EntityMapper, MapEntities}; use bevy_ecs::prelude::{Component, ReflectComponent, ReflectResource, Resource, World}; use bevy_ecs::query::{With, Without}; use bevy_ecs::reflect::{AppTypeRegistry, ReflectMapEntities}; use bevy_ecs::world::FromWorld; use bevy_reflect::{Reflect, ReflectSerialize}; - use bevy_utils::EntityHashMap; use bincode::Options; use serde::de::DeserializeSeed; use serde::Serialize; diff --git a/crates/bevy_sprite/src/mesh2d/material.rs b/crates/bevy_sprite/src/mesh2d/material.rs index 07626d47a5c9f..61a18e4929be1 100644 --- a/crates/bevy_sprite/src/mesh2d/material.rs +++ b/crates/bevy_sprite/src/mesh2d/material.rs @@ -29,7 +29,8 @@ use bevy_render::{ Extract, ExtractSchedule, Render, RenderApp, RenderSet, }; use bevy_transform::components::{GlobalTransform, Transform}; -use bevy_utils::{EntityHashMap, FloatOrd, HashMap, HashSet}; +use bevy_utils::{FloatOrd, HashMap, HashSet}; +use bevy_ecs::entity::EntityHashMap; use std::hash::Hash; use std::marker::PhantomData; diff --git a/crates/bevy_sprite/src/mesh2d/mesh.rs b/crates/bevy_sprite/src/mesh2d/mesh.rs index 4b9c9943dc793..50497bd044206 100644 --- a/crates/bevy_sprite/src/mesh2d/mesh.rs +++ b/crates/bevy_sprite/src/mesh2d/mesh.rs @@ -30,7 +30,7 @@ use bevy_render::{ Extract, ExtractSchedule, Render, RenderApp, RenderSet, }; use bevy_transform::components::GlobalTransform; -use bevy_utils::EntityHashMap; +use bevy_ecs::entity::EntityHashMap; use crate::Material2dBindGroupId; diff --git a/crates/bevy_sprite/src/render/mod.rs b/crates/bevy_sprite/src/render/mod.rs index df319a7857e34..7ed2ca40c117f 100644 --- a/crates/bevy_sprite/src/render/mod.rs +++ b/crates/bevy_sprite/src/render/mod.rs @@ -9,6 +9,7 @@ use bevy_core_pipeline::{ core_2d::Transparent2d, tonemapping::{DebandDither, Tonemapping}, }; +use bevy_ecs::entity::EntityHashMap; use bevy_ecs::{ prelude::*, system::{lifetimeless::*, SystemParamItem, SystemState}, @@ -36,7 +37,7 @@ use bevy_render::{ Extract, }; use bevy_transform::components::GlobalTransform; -use bevy_utils::{EntityHashMap, FloatOrd, HashMap}; +use bevy_utils::{FloatOrd, HashMap}; use bytemuck::{Pod, Zeroable}; use fixedbitset::FixedBitSet; diff --git a/crates/bevy_ui/src/layout/mod.rs b/crates/bevy_ui/src/layout/mod.rs index 25df73d6af353..223157e137f4d 100644 --- a/crates/bevy_ui/src/layout/mod.rs +++ b/crates/bevy_ui/src/layout/mod.rs @@ -2,6 +2,7 @@ mod convert; pub mod debug; use crate::{ContentSize, DefaultUiCamera, Node, Outline, Style, TargetCamera, UiScale}; +use bevy_ecs::entity::EntityHashMap; use bevy_ecs::{ change_detection::{DetectChanges, DetectChangesMut}, entity::Entity, @@ -16,7 +17,7 @@ use bevy_log::warn; use bevy_math::{UVec2, Vec2}; use bevy_render::camera::{Camera, NormalizedRenderTarget}; 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 std::fmt; use taffy::{tree::LayoutTree, Taffy}; diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index b4d333735b6ef..8aaba9c31e242 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -20,6 +20,7 @@ use crate::{DefaultUiCamera, Outline, TargetCamera}; use bevy_app::prelude::*; use bevy_asset::{load_internal_asset, AssetEvent, AssetId, Assets, Handle}; +use bevy_ecs::entity::EntityHashMap; use bevy_ecs::prelude::*; use bevy_math::{Mat4, Rect, URect, UVec4, Vec2, Vec3, Vec4Swizzles}; use bevy_render::{ @@ -39,7 +40,7 @@ use bevy_sprite::TextureAtlasLayout; #[cfg(feature = "bevy_text")] use bevy_text::{PositionedGlyph, Text, TextLayoutInfo}; use bevy_transform::components::GlobalTransform; -use bevy_utils::{EntityHashMap, FloatOrd, HashMap}; +use bevy_utils::{FloatOrd, HashMap}; use bytemuck::{Pod, Zeroable}; use std::ops::Range; diff --git a/crates/bevy_utils/src/lib.rs b/crates/bevy_utils/src/lib.rs index 37a8380e4d06a..08c77ed0866be 100644 --- a/crates/bevy_utils/src/lib.rs +++ b/crates/bevy_utils/src/lib.rs @@ -251,83 +251,6 @@ impl PreHashMapExt for PreHashMap 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 - - // 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 - // - // 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. -pub type EntityHashMap = hashbrown::HashMap; - -/// A [`HashSet`] pre-configured to use [`EntityHash`] hashing. -pub type EntityHashSet = hashbrown::HashSet; - /// 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. /// @@ -423,6 +346,5 @@ mod tests { use static_assertions::assert_impl_all; // Check that the HashMaps are Clone if the key/values are Clone - assert_impl_all!(EntityHashMap::: Clone); assert_impl_all!(PreHashMap::: Clone); } diff --git a/crates/bevy_winit/src/accessibility.rs b/crates/bevy_winit/src/accessibility.rs index 633cb151259a9..1d1d31c9f74ba 100644 --- a/crates/bevy_winit/src/accessibility.rs +++ b/crates/bevy_winit/src/accessibility.rs @@ -15,6 +15,7 @@ use bevy_a11y::{ use bevy_a11y::{ActionRequest as ActionRequestWrapper, ManageAccessibilityUpdates}; use bevy_app::{App, Plugin, PostUpdate}; use bevy_derive::{Deref, DerefMut}; +use bevy_ecs::entity::EntityHashMap; use bevy_ecs::{ prelude::{DetectChanges, Entity, EventReader, EventWriter}, query::With, @@ -22,7 +23,6 @@ use bevy_ecs::{ system::{NonSend, NonSendMut, Query, Res, ResMut, Resource}, }; use bevy_hierarchy::{Children, Parent}; -use bevy_utils::EntityHashMap; use bevy_window::{PrimaryWindow, Window, WindowClosed}; /// Maps window entities to their `AccessKit` [`Adapter`]s. diff --git a/crates/bevy_winit/src/system.rs b/crates/bevy_winit/src/system.rs index 45557b3639a4b..ec41fd849095f 100644 --- a/crates/bevy_winit/src/system.rs +++ b/crates/bevy_winit/src/system.rs @@ -1,4 +1,5 @@ use bevy_a11y::AccessibilityRequested; +use bevy_ecs::entity::EntityHashMap; use bevy_ecs::{ entity::Entity, event::EventWriter, @@ -7,10 +8,7 @@ use bevy_ecs::{ system::{Commands, NonSendMut, Query, ResMut}, world::Mut, }; -use bevy_utils::{ - tracing::{error, info, warn}, - EntityHashMap, -}; +use bevy_utils::tracing::{error, info, warn}; use bevy_window::{RawHandleWrapper, Window, WindowClosed, WindowCreated}; use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle}; diff --git a/crates/bevy_winit/src/winit_windows.rs b/crates/bevy_winit/src/winit_windows.rs index be7b3ffac2f02..15e86fa83d038 100644 --- a/crates/bevy_winit/src/winit_windows.rs +++ b/crates/bevy_winit/src/winit_windows.rs @@ -7,7 +7,8 @@ use bevy_a11y::{ }; 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 winit::{ From 98cec1164a37de408dbee5e51090c45518872a07 Mon Sep 17 00:00:00 2001 From: Doonv <58695417+doonv@users.noreply.github.com> Date: Tue, 23 Jan 2024 19:47:00 +0200 Subject: [PATCH 02/17] Hardcode `Entity` key into `EntityHashMap` --- CHANGELOG.md | 2 +- crates/bevy_ecs/src/entity/hash.rs | 18 ++++++++------- crates/bevy_ecs/src/entity/map_entities.rs | 22 +++++++++---------- crates/bevy_ecs/src/reflect/map_entities.rs | 18 ++++++--------- crates/bevy_gltf/src/loader.rs | 2 +- crates/bevy_pbr/src/bundle.rs | 4 ++-- crates/bevy_pbr/src/light.rs | 2 +- crates/bevy_pbr/src/light_probe/mod.rs | 2 +- crates/bevy_pbr/src/lightmap/mod.rs | 2 +- crates/bevy_pbr/src/render/light.rs | 8 +++---- crates/bevy_pbr/src/render/mesh.rs | 2 +- crates/bevy_pbr/src/render/morph.rs | 2 +- crates/bevy_pbr/src/render/skin.rs | 2 +- crates/bevy_render/src/extract_instances.rs | 7 ++++-- crates/bevy_render/src/primitives/mod.rs | 6 +++-- crates/bevy_render/src/view/window/mod.rs | 6 ++--- .../bevy_render/src/view/window/screenshot.rs | 2 +- crates/bevy_scene/src/dynamic_scene.rs | 4 ++-- crates/bevy_scene/src/scene_spawner.rs | 6 ++--- crates/bevy_sprite/src/mesh2d/material.rs | 4 ++-- crates/bevy_sprite/src/mesh2d/mesh.rs | 4 ++-- crates/bevy_sprite/src/render/mod.rs | 2 +- crates/bevy_ui/src/layout/mod.rs | 6 ++--- crates/bevy_ui/src/render/mod.rs | 2 +- crates/bevy_winit/src/accessibility.rs | 4 ++-- crates/bevy_winit/src/system.rs | 2 +- crates/bevy_winit/src/winit_windows.rs | 2 +- 27 files changed, 73 insertions(+), 70 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a5d237f65d522..537377edfbb81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -574,7 +574,7 @@ current changes on git with [previous release tags][git_tag_comparison]. - [Automatic batching/instancing of draw commands][9685] - [Directly copy data into uniform buffers][9865] - [Allow other plugins to create renderer resources][9925] -- [Use EntityHashMap for render world entity storage for better performance][9903] +- [Use EntityHashMap for render world entity storage for better performance][9903] - [Parallelize extract_meshes][9966] - [Fix comment grammar][9990] - [Allow overriding global wireframe setting.][7328] diff --git a/crates/bevy_ecs/src/entity/hash.rs b/crates/bevy_ecs/src/entity/hash.rs index 2b04cce7b90b3..0c9dae82504df 100644 --- a/crates/bevy_ecs/src/entity/hash.rs +++ b/crates/bevy_ecs/src/entity/hash.rs @@ -3,6 +3,8 @@ use std::hash::{BuildHasher, Hasher}; use bevy_reflect::Reflect; use bevy_utils::hashbrown; +use super::Entity; + /// A [`BuildHasher`] that results in a [`EntityHasher`]. #[derive(Default, Clone, Reflect)] pub struct EntityHash; @@ -16,7 +18,7 @@ impl BuildHasher for EntityHash { } /// 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 +/// like [`Entity`](super::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 @@ -24,7 +26,7 @@ impl BuildHasher for EntityHash { /// /// 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. +/// try [`AHasher`](bevy_utils::AHasher) for a slower hash computation but fewer lookup conflicts. #[derive(Debug, Default)] pub struct EntityHasher { hash: u64, @@ -74,11 +76,11 @@ impl Hasher for EntityHasher { } } -/// A [`HashMap`] pre-configured to use [`EntityHash`] hashing. -pub type EntityHashMap = hashbrown::HashMap; +/// A [`HashMap`](hashbrown::HashMap) pre-configured to use [`EntityHash`] hashing. +pub type EntityHashMap = hashbrown::HashMap; -/// A [`HashSet`] pre-configured to use [`EntityHash`] hashing. -pub type EntityHashSet = hashbrown::HashSet; +/// A [`HashSet`](hashbrown::HashSet) pre-configured to use [`EntityHash`] hashing. +pub type EntityHashSet = hashbrown::HashSet; #[cfg(test)] mod tests { @@ -87,7 +89,7 @@ mod tests { use static_assertions::assert_impl_all; // Check that the HashMaps are Clone if the key/values are Clone - assert_impl_all!(EntityHashMap::: Clone); + assert_impl_all!(EntityHashMap::: Clone); // EntityHashMap should implement Reflect - assert_impl_all!(EntityHashMap::: Reflect); + assert_impl_all!(EntityHashMap::: Reflect); } diff --git a/crates/bevy_ecs/src/entity/map_entities.rs b/crates/bevy_ecs/src/entity/map_entities.rs index 33cc55e3475b9..ca5194b2b8261 100644 --- a/crates/bevy_ecs/src/entity/map_entities.rs +++ b/crates/bevy_ecs/src/entity/map_entities.rs @@ -10,7 +10,7 @@ use super::EntityHashMap; /// /// As entity IDs are valid only for the [`World`] they're sourced from, using [`Entity`] /// as references in components copied from another world will be invalid. This trait -/// allows defining custom mappings for these references via [`EntityHashMap`] +/// allows defining custom mappings for these references via [`EntityHashMap`] /// /// Implementing this trait correctly is required for properly loading components /// with entity references from scenes. @@ -43,7 +43,7 @@ pub trait MapEntities { fn map_entities(&mut self, entity_mapper: &mut EntityMapper); } -/// A wrapper for [`EntityHashMap`], augmenting it with the ability to allocate new [`Entity`] references in a destination +/// A wrapper for [`EntityHashMap`], 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. /// /// References are allocated by returning increasing generations starting from an internally initialized base @@ -56,9 +56,9 @@ pub struct EntityMapper<'m> { /// or over the network. This is required as [`Entity`] identifiers are opaque; you cannot and do not want to reuse /// identifiers directly. /// - /// On its own, a [`EntityHashMap`] is not capable of allocating new entity identifiers, which is needed to map references + /// On its own, a [`EntityHashMap`] 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 [`EntityMapper::world_scope()`]. - map: &'m mut EntityHashMap, + map: &'m mut EntityHashMap, /// A base [`Entity`] used to allocate new references. dead_start: Entity, /// The number of generations this mapper has allocated thus far. @@ -86,18 +86,18 @@ impl<'m> EntityMapper<'m> { new } - /// Gets a reference to the underlying [`EntityHashMap`]. - pub fn get_map(&'m self) -> &'m EntityHashMap { + /// Gets a reference to the underlying [`EntityHashMap`]. + pub fn get_map(&'m self) -> &'m EntityHashMap { self.map } - /// Gets a mutable reference to the underlying [`EntityHashMap`]. - pub fn get_map_mut(&'m mut self) -> &'m mut EntityHashMap { + /// Gets a mutable reference to the underlying [`EntityHashMap`]. + pub fn get_map_mut(&'m mut self) -> &'m mut EntityHashMap { self.map } /// Creates a new [`EntityMapper`], spawning a temporary base [`Entity`] in the provided [`World`] - fn new(map: &'m mut EntityHashMap, world: &mut World) -> Self { + fn new(map: &'m mut EntityHashMap, world: &mut World) -> Self { Self { map, // SAFETY: Entities data is kept in a valid state via `EntityMapper::world_scope` @@ -117,14 +117,14 @@ impl<'m> EntityMapper<'m> { assert!(entities.reserve_generations(self.dead_start.index(), self.generations)); } - /// Creates an [`EntityMapper`] from a provided [`World`] and [`EntityHashMap`], then calls the + /// Creates an [`EntityMapper`] from a provided [`World`] and [`EntityHashMap`], then calls the /// 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 /// 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 /// parameter `R`. pub fn world_scope( - entity_map: &'m mut EntityHashMap, + entity_map: &'m mut EntityHashMap, world: &mut World, f: impl FnOnce(&mut World, &mut Self) -> R, ) -> R { diff --git a/crates/bevy_ecs/src/reflect/map_entities.rs b/crates/bevy_ecs/src/reflect/map_entities.rs index 852668ea4798a..b48e8c2f0f202 100644 --- a/crates/bevy_ecs/src/reflect/map_entities.rs +++ b/crates/bevy_ecs/src/reflect/map_entities.rs @@ -17,33 +17,29 @@ pub struct ReflectMapEntities { } impl ReflectMapEntities { - /// A general method for applying [`MapEntities`] behavior to all elements in an [`EntityHashMap`]. + /// A general method for applying [`MapEntities`] behavior to all elements in an [`EntityHashMap`]. /// - /// Be mindful in its usage: Works best in situations where the entities in the [`EntityHashMap`] are newly + /// Be mindful in its usage: Works best in situations where the entities in the [`EntityHashMap`] are newly /// created, before systems have a chance to add new components. If some of the entities referred to - /// by the [`EntityHashMap`] might already contain valid entity references, you should use [`map_entities`](Self::map_entities). + /// by the [`EntityHashMap`] 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 /// 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. - pub fn map_all_entities( - &self, - world: &mut World, - entity_map: &mut EntityHashMap, - ) { + pub fn map_all_entities(&self, world: &mut World, entity_map: &mut EntityHashMap) { EntityMapper::world_scope(entity_map, world, self.map_all_entities); } - /// A general method for applying [`MapEntities`] behavior to elements in an [`EntityHashMap`]. Unlike + /// A general method for applying [`MapEntities`] behavior to elements in an [`EntityHashMap`]. Unlike /// [`map_all_entities`](Self::map_all_entities), this is applied to specific entities, not all values - /// in the [`EntityHashMap`]. + /// in the [`EntityHashMap`]. /// /// 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. pub fn map_entities( &self, world: &mut World, - entity_map: &mut EntityHashMap, + entity_map: &mut EntityHashMap, entities: &[Entity], ) { EntityMapper::world_scope(entity_map, world, |world, mapper| { diff --git a/crates/bevy_gltf/src/loader.rs b/crates/bevy_gltf/src/loader.rs index d31bc97b77757..7dc836341c07c 100644 --- a/crates/bevy_gltf/src/loader.rs +++ b/crates/bevy_gltf/src/loader.rs @@ -923,7 +923,7 @@ fn load_node( load_context: &mut LoadContext, settings: &GltfLoaderSettings, node_index_to_entity_map: &mut HashMap, - entity_to_skin_index_map: &mut EntityHashMap, + entity_to_skin_index_map: &mut EntityHashMap, active_camera_found: &mut bool, parent_transform: &Transform, ) -> Result<(), GltfError> { diff --git a/crates/bevy_pbr/src/bundle.rs b/crates/bevy_pbr/src/bundle.rs index c0433ac030b0c..beb8789c5625f 100644 --- a/crates/bevy_pbr/src/bundle.rs +++ b/crates/bevy_pbr/src/bundle.rs @@ -4,7 +4,7 @@ use crate::{ }; use bevy_asset::Handle; use bevy_ecs::entity::EntityHashMap; -use bevy_ecs::{bundle::Bundle, component::Component, prelude::Entity, reflect::ReflectComponent}; +use bevy_ecs::{bundle::Bundle, component::Component, reflect::ReflectComponent}; use bevy_reflect::Reflect; use bevy_render::{ mesh::Mesh, @@ -75,7 +75,7 @@ impl CubemapVisibleEntities { pub struct CascadesVisibleEntities { /// Map of view entity to the visible entities for each cascade frustum. #[reflect(ignore)] - pub entities: EntityHashMap>, + pub entities: EntityHashMap>, } /// A component bundle for [`PointLight`] entities. diff --git a/crates/bevy_pbr/src/light.rs b/crates/bevy_pbr/src/light.rs index 1b5c5e2fbc2f0..21ed064229bcf 100644 --- a/crates/bevy_pbr/src/light.rs +++ b/crates/bevy_pbr/src/light.rs @@ -382,7 +382,7 @@ impl From for CascadeShadowConfig { #[reflect(Component)] pub struct Cascades { /// Map from a view to the configuration of each of its [`Cascade`]s. - pub(crate) cascades: EntityHashMap>, + pub(crate) cascades: EntityHashMap>, } #[derive(Clone, Debug, Default, Reflect)] diff --git a/crates/bevy_pbr/src/light_probe/mod.rs b/crates/bevy_pbr/src/light_probe/mod.rs index 6fe80e3448008..51cf047a19f81 100644 --- a/crates/bevy_pbr/src/light_probe/mod.rs +++ b/crates/bevy_pbr/src/light_probe/mod.rs @@ -108,7 +108,7 @@ pub struct LightProbesUniform { /// A map from each camera to the light probe uniform associated with it. #[derive(Resource, Default, Deref, DerefMut)] -struct RenderLightProbes(EntityHashMap); +struct RenderLightProbes(EntityHashMap); /// A GPU buffer that stores information about all light probes. #[derive(Resource, Default, Deref, DerefMut)] diff --git a/crates/bevy_pbr/src/lightmap/mod.rs b/crates/bevy_pbr/src/lightmap/mod.rs index bcdbefa5ba50a..c34ac4885707e 100644 --- a/crates/bevy_pbr/src/lightmap/mod.rs +++ b/crates/bevy_pbr/src/lightmap/mod.rs @@ -105,7 +105,7 @@ pub struct RenderLightmaps { /// /// Entities without lightmaps, or for which the mesh or lightmap isn't /// loaded, won't have entries in this table. - pub(crate) render_lightmaps: EntityHashMap, + pub(crate) render_lightmaps: EntityHashMap, /// All active lightmap images in the scene. /// diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index 9b64a3d3b51a4..12c828c8e5cfa 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -1,4 +1,5 @@ use bevy_core_pipeline::core_3d::{Transparent3d, CORE_3D_DEPTH_FORMAT}; +use bevy_ecs::entity::EntityHashMap; use bevy_ecs::prelude::*; use bevy_math::{Mat4, UVec3, UVec4, Vec2, Vec3, Vec3Swizzles, Vec4, Vec4Swizzles}; use bevy_render::{ @@ -20,7 +21,6 @@ use bevy_utils::{ nonmax::NonMaxU32, tracing::{error, warn}, }; -use bevy_ecs::entity::EntityHashMap; use std::{hash::Hash, num::NonZeroU64, ops::Range}; use crate::*; @@ -48,8 +48,8 @@ pub struct ExtractedDirectionalLight { shadow_depth_bias: f32, shadow_normal_bias: f32, cascade_shadow_config: CascadeShadowConfig, - cascades: EntityHashMap>, - frusta: EntityHashMap>, + cascades: EntityHashMap>, + frusta: EntityHashMap>, render_layers: RenderLayers, } @@ -561,7 +561,7 @@ pub const CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT: u32 = 3; #[derive(Resource)] pub struct GlobalLightMeta { pub gpu_point_lights: GpuPointLights, - pub entity_to_index: EntityHashMap, + pub entity_to_index: EntityHashMap, } impl FromWorld for GlobalLightMeta { diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index 3728afed5919c..62b6f80bcb268 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -263,7 +263,7 @@ pub struct RenderMeshInstance { } #[derive(Default, Resource, Deref, DerefMut)] -pub struct RenderMeshInstances(EntityHashMap); +pub struct RenderMeshInstances(EntityHashMap); #[derive(Component)] pub struct Mesh3d; diff --git a/crates/bevy_pbr/src/render/morph.rs b/crates/bevy_pbr/src/render/morph.rs index 4d132deb83535..7516bb495b7f1 100644 --- a/crates/bevy_pbr/src/render/morph.rs +++ b/crates/bevy_pbr/src/render/morph.rs @@ -19,7 +19,7 @@ pub struct MorphIndex { } #[derive(Default, Resource, Deref, DerefMut)] -pub struct MorphIndices(EntityHashMap); +pub struct MorphIndices(EntityHashMap); #[derive(Resource)] pub struct MorphUniform { diff --git a/crates/bevy_pbr/src/render/skin.rs b/crates/bevy_pbr/src/render/skin.rs index ec8891719e784..8ad3c35d6ee47 100644 --- a/crates/bevy_pbr/src/render/skin.rs +++ b/crates/bevy_pbr/src/render/skin.rs @@ -31,7 +31,7 @@ impl SkinIndex { } #[derive(Default, Resource, Deref, DerefMut)] -pub struct SkinIndices(EntityHashMap); +pub struct SkinIndices(EntityHashMap); // Notes on implementation: see comment on top of the `extract_skins` system. #[derive(Resource)] diff --git a/crates/bevy_render/src/extract_instances.rs b/crates/bevy_render/src/extract_instances.rs index 982dc42703c46..5f09184d22ec3 100644 --- a/crates/bevy_render/src/extract_instances.rs +++ b/crates/bevy_render/src/extract_instances.rs @@ -10,7 +10,10 @@ use bevy_app::{App, Plugin}; use bevy_asset::{Asset, AssetId, Handle}; use bevy_derive::{Deref, DerefMut}; use bevy_ecs::{ - entity::EntityHashMap, prelude::Entity, query::{QueryFilter, QueryItem, ReadOnlyQueryData}, system::{lifetimeless::Read, Query, ResMut, Resource} + entity::EntityHashMap, + prelude::Entity, + query::{QueryFilter, QueryItem, ReadOnlyQueryData}, + system::{lifetimeless::Read, Query, ResMut, Resource}, }; use crate::{prelude::ViewVisibility, Extract, ExtractSchedule, RenderApp}; @@ -50,7 +53,7 @@ where /// Stores all extract instances of a type in the render world. #[derive(Resource, Deref, DerefMut)] -pub struct ExtractedInstances(EntityHashMap) +pub struct ExtractedInstances(EntityHashMap) where EI: ExtractInstance; diff --git a/crates/bevy_render/src/primitives/mod.rs b/crates/bevy_render/src/primitives/mod.rs index e30bfb956fcab..dfd136b4d9481 100644 --- a/crates/bevy_render/src/primitives/mod.rs +++ b/crates/bevy_render/src/primitives/mod.rs @@ -1,6 +1,8 @@ use std::borrow::Borrow; -use bevy_ecs::{component::Component, entity::EntityHashMap, 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_reflect::Reflect; @@ -322,7 +324,7 @@ impl CubemapFrusta { #[reflect(Component)] pub struct CascadesFrusta { #[reflect(ignore)] - pub frusta: EntityHashMap>, + pub frusta: EntityHashMap>, } #[cfg(test)] diff --git a/crates/bevy_render/src/view/window/mod.rs b/crates/bevy_render/src/view/window/mod.rs index 44c9c2250697a..027433fd05c41 100644 --- a/crates/bevy_render/src/view/window/mod.rs +++ b/crates/bevy_render/src/view/window/mod.rs @@ -89,11 +89,11 @@ impl ExtractedWindow { #[derive(Default, Resource)] pub struct ExtractedWindows { pub primary: Option, - pub windows: EntityHashMap, + pub windows: EntityHashMap, } impl Deref for ExtractedWindows { - type Target = EntityHashMap; + type Target = EntityHashMap; fn deref(&self) -> &Self::Target { &self.windows @@ -199,7 +199,7 @@ struct SurfaceData { #[derive(Resource, Default)] pub struct WindowSurfaces { - surfaces: EntityHashMap, + surfaces: EntityHashMap, /// List of windows that we have already called the initial `configure_surface` for configured_windows: HashSet, } diff --git a/crates/bevy_render/src/view/window/screenshot.rs b/crates/bevy_render/src/view/window/screenshot.rs index ecaf9b219889c..6733fbefc0430 100644 --- a/crates/bevy_render/src/view/window/screenshot.rs +++ b/crates/bevy_render/src/view/window/screenshot.rs @@ -32,7 +32,7 @@ pub type ScreenshotFn = Box; #[derive(Resource, Default)] pub struct ScreenshotManager { // this is in a mutex to enable extraction with only an immutable reference - pub(crate) callbacks: Mutex>, + pub(crate) callbacks: Mutex>, } #[derive(Error, Debug)] diff --git a/crates/bevy_scene/src/dynamic_scene.rs b/crates/bevy_scene/src/dynamic_scene.rs index 24651ae918724..2b310550f1d0c 100644 --- a/crates/bevy_scene/src/dynamic_scene.rs +++ b/crates/bevy_scene/src/dynamic_scene.rs @@ -67,7 +67,7 @@ impl DynamicScene { pub fn write_to_world_with( &self, world: &mut World, - entity_map: &mut EntityHashMap, + entity_map: &mut EntityHashMap, type_registry: &AppTypeRegistry, ) -> Result<(), SceneSpawnError> { let type_registry = type_registry.read(); @@ -165,7 +165,7 @@ impl DynamicScene { pub fn write_to_world( &self, world: &mut World, - entity_map: &mut EntityHashMap, + entity_map: &mut EntityHashMap, ) -> Result<(), SceneSpawnError> { let registry = world.resource::().clone(); self.write_to_world_with(world, entity_map, ®istry) diff --git a/crates/bevy_scene/src/scene_spawner.rs b/crates/bevy_scene/src/scene_spawner.rs index 653eff209b797..a4fa6f8c07ae9 100644 --- a/crates/bevy_scene/src/scene_spawner.rs +++ b/crates/bevy_scene/src/scene_spawner.rs @@ -1,5 +1,6 @@ use crate::{DynamicScene, Scene}; use bevy_asset::{AssetEvent, AssetId, Assets, Handle}; +use bevy_ecs::entity::EntityHashMap; use bevy_ecs::{ entity::Entity, event::{Event, Events, ManualEventReader}, @@ -9,7 +10,6 @@ use bevy_ecs::{ }; use bevy_hierarchy::{Parent, PushChild}; use bevy_utils::{tracing::error, HashMap, HashSet}; -use bevy_ecs::entity::EntityHashMap; use thiserror::Error; use uuid::Uuid; @@ -26,7 +26,7 @@ pub struct SceneInstanceReady { #[derive(Debug)] pub struct InstanceInfo { /// Mapping of entities from the scene world to the instance world. - pub entity_map: EntityHashMap, + pub entity_map: EntityHashMap, } /// Unique id identifying a scene instance. @@ -214,7 +214,7 @@ impl SceneSpawner { fn spawn_dynamic_internal( world: &mut World, id: AssetId, - entity_map: &mut EntityHashMap, + entity_map: &mut EntityHashMap, ) -> Result<(), SceneSpawnError> { world.resource_scope(|world, scenes: Mut>| { let scene = scenes diff --git a/crates/bevy_sprite/src/mesh2d/material.rs b/crates/bevy_sprite/src/mesh2d/material.rs index 61a18e4929be1..ea57181fdc02a 100644 --- a/crates/bevy_sprite/src/mesh2d/material.rs +++ b/crates/bevy_sprite/src/mesh2d/material.rs @@ -5,6 +5,7 @@ use bevy_core_pipeline::{ tonemapping::{DebandDither, Tonemapping}, }; use bevy_derive::{Deref, DerefMut}; +use bevy_ecs::entity::EntityHashMap; use bevy_ecs::{ prelude::*, system::{lifetimeless::SRes, SystemParamItem}, @@ -30,7 +31,6 @@ use bevy_render::{ }; use bevy_transform::components::{GlobalTransform, Transform}; use bevy_utils::{FloatOrd, HashMap, HashSet}; -use bevy_ecs::entity::EntityHashMap; use std::hash::Hash; use std::marker::PhantomData; @@ -181,7 +181,7 @@ where } #[derive(Resource, Deref, DerefMut)] -pub struct RenderMaterial2dInstances(EntityHashMap>); +pub struct RenderMaterial2dInstances(EntityHashMap>); impl Default for RenderMaterial2dInstances { fn default() -> Self { diff --git a/crates/bevy_sprite/src/mesh2d/mesh.rs b/crates/bevy_sprite/src/mesh2d/mesh.rs index 50497bd044206..102962d3da2f4 100644 --- a/crates/bevy_sprite/src/mesh2d/mesh.rs +++ b/crates/bevy_sprite/src/mesh2d/mesh.rs @@ -3,6 +3,7 @@ use bevy_asset::{load_internal_asset, AssetId, Handle}; use bevy_core_pipeline::core_2d::Transparent2d; use bevy_derive::{Deref, DerefMut}; +use bevy_ecs::entity::EntityHashMap; use bevy_ecs::{ prelude::*, query::ROQueryItem, @@ -30,7 +31,6 @@ use bevy_render::{ Extract, ExtractSchedule, Render, RenderApp, RenderSet, }; use bevy_transform::components::GlobalTransform; -use bevy_ecs::entity::EntityHashMap; use crate::Material2dBindGroupId; @@ -192,7 +192,7 @@ pub struct RenderMesh2dInstance { } #[derive(Default, Resource, Deref, DerefMut)] -pub struct RenderMesh2dInstances(EntityHashMap); +pub struct RenderMesh2dInstances(EntityHashMap); #[derive(Component)] pub struct Mesh2d; diff --git a/crates/bevy_sprite/src/render/mod.rs b/crates/bevy_sprite/src/render/mod.rs index 7ed2ca40c117f..5ef5931d27dc7 100644 --- a/crates/bevy_sprite/src/render/mod.rs +++ b/crates/bevy_sprite/src/render/mod.rs @@ -313,7 +313,7 @@ pub struct ExtractedSprite { #[derive(Resource, Default)] pub struct ExtractedSprites { - pub sprites: EntityHashMap, + pub sprites: EntityHashMap, } #[derive(Resource, Default)] diff --git a/crates/bevy_ui/src/layout/mod.rs b/crates/bevy_ui/src/layout/mod.rs index 223157e137f4d..31f123ff6d3f5 100644 --- a/crates/bevy_ui/src/layout/mod.rs +++ b/crates/bevy_ui/src/layout/mod.rs @@ -52,14 +52,14 @@ struct RootNodePair { #[derive(Resource)] pub struct UiSurface { - entity_to_taffy: EntityHashMap, - camera_roots: EntityHashMap>, + entity_to_taffy: EntityHashMap, + camera_roots: EntityHashMap>, taffy: Taffy, } fn _assert_send_sync_ui_surface_impl_safe() { fn _assert_send_sync() {} - _assert_send_sync::>(); + _assert_send_sync::>(); _assert_send_sync::(); _assert_send_sync::(); } diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 8aaba9c31e242..597e6d59b59c6 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -170,7 +170,7 @@ pub struct ExtractedUiNode { #[derive(Resource, Default)] pub struct ExtractedUiNodes { - pub uinodes: EntityHashMap, + pub uinodes: EntityHashMap, } pub(crate) fn resolve_border_thickness(value: Val, parent_width: f32, viewport_size: Vec2) -> f32 { diff --git a/crates/bevy_winit/src/accessibility.rs b/crates/bevy_winit/src/accessibility.rs index 1d1d31c9f74ba..7836408677f61 100644 --- a/crates/bevy_winit/src/accessibility.rs +++ b/crates/bevy_winit/src/accessibility.rs @@ -27,11 +27,11 @@ use bevy_window::{PrimaryWindow, Window, WindowClosed}; /// Maps window entities to their `AccessKit` [`Adapter`]s. #[derive(Default, Deref, DerefMut)] -pub struct AccessKitAdapters(pub EntityHashMap); +pub struct AccessKitAdapters(pub EntityHashMap); /// Maps window entities to their respective [`WinitActionHandler`]s. #[derive(Resource, Default, Deref, DerefMut)] -pub struct WinitActionHandlers(pub EntityHashMap); +pub struct WinitActionHandlers(pub EntityHashMap); /// Forwards `AccessKit` [`ActionRequest`]s from winit to an event channel. #[derive(Clone, Default, Deref, DerefMut)] diff --git a/crates/bevy_winit/src/system.rs b/crates/bevy_winit/src/system.rs index ec41fd849095f..8bf60bcd352b6 100644 --- a/crates/bevy_winit/src/system.rs +++ b/crates/bevy_winit/src/system.rs @@ -85,7 +85,7 @@ pub(crate) fn create_windows<'a>( /// Cache for closing windows so we can get better debug information. #[derive(Debug, Clone, Resource)] -pub struct WindowTitleCache(EntityHashMap); +pub struct WindowTitleCache(EntityHashMap); pub(crate) fn despawn_windows( mut closed: RemovedComponents, diff --git a/crates/bevy_winit/src/winit_windows.rs b/crates/bevy_winit/src/winit_windows.rs index 15e86fa83d038..ee1ab9cd7a3da 100644 --- a/crates/bevy_winit/src/winit_windows.rs +++ b/crates/bevy_winit/src/winit_windows.rs @@ -28,7 +28,7 @@ pub struct WinitWindows { /// Stores [`winit`] windows by window identifier. pub windows: HashMap, /// Maps entities to `winit` window identifiers. - pub entity_to_winit: EntityHashMap, + pub entity_to_winit: EntityHashMap, /// Maps `winit` window identifiers to entities. pub winit_to_entity: HashMap, // Many `winit` window functions (e.g. `set_window_icon`) can only be called on the main thread. From 3e072f4bc4374a58ab151eb7e9b0b675eee4f57a Mon Sep 17 00:00:00 2001 From: Doonv <58695417+doonv@users.noreply.github.com> Date: Tue, 23 Jan 2024 19:47:57 +0200 Subject: [PATCH 03/17] `cargo fmt` --- crates/bevy_render/src/primitives/mod.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/bevy_render/src/primitives/mod.rs b/crates/bevy_render/src/primitives/mod.rs index dfd136b4d9481..25fef4abd2c09 100644 --- a/crates/bevy_render/src/primitives/mod.rs +++ b/crates/bevy_render/src/primitives/mod.rs @@ -1,8 +1,6 @@ use std::borrow::Borrow; -use bevy_ecs::{ - component::Component, entity::EntityHashMap, reflect::ReflectComponent, -}; +use bevy_ecs::{component::Component, entity::EntityHashMap, reflect::ReflectComponent}; use bevy_math::{Affine3A, Mat3A, Mat4, Vec3, Vec3A, Vec4, Vec4Swizzles}; use bevy_reflect::Reflect; From f65f623460a3fd13055599dac5d3d1011a14283a Mon Sep 17 00:00:00 2001 From: Doonv <58695417+doonv@users.noreply.github.com> Date: Tue, 23 Jan 2024 19:57:15 +0200 Subject: [PATCH 04/17] Fix CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 537377edfbb81..7bc6ae5f9d8f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -574,7 +574,7 @@ current changes on git with [previous release tags][git_tag_comparison]. - [Automatic batching/instancing of draw commands][9685] - [Directly copy data into uniform buffers][9865] - [Allow other plugins to create renderer resources][9925] -- [Use EntityHashMap for render world entity storage for better performance][9903] +- [Use `EntityHashMap` for render world entity storage for better performance][9903] - [Parallelize extract_meshes][9966] - [Fix comment grammar][9990] - [Allow overriding global wireframe setting.][7328] From 0a2c7f7173342e94e157cdfb11c6e39e99ca226d Mon Sep 17 00:00:00 2001 From: Doonv <58695417+doonv@users.noreply.github.com> Date: Tue, 23 Jan 2024 20:38:33 +0200 Subject: [PATCH 05/17] Fix benches --- .../{bevy_utils => bevy_ecs/world}/entity_hash.rs | 5 ++--- benches/benches/bevy_ecs/world/mod.rs | 11 ++++++++--- 2 files changed, 10 insertions(+), 6 deletions(-) rename benches/benches/{bevy_utils => bevy_ecs/world}/entity_hash.rs (96%) diff --git a/benches/benches/bevy_utils/entity_hash.rs b/benches/benches/bevy_ecs/world/entity_hash.rs similarity index 96% rename from benches/benches/bevy_utils/entity_hash.rs rename to benches/benches/bevy_ecs/world/entity_hash.rs index 44cf80ba9a4ec..03faf268330ca 100644 --- a/benches/benches/bevy_utils/entity_hash.rs +++ b/benches/benches/bevy_ecs/world/entity_hash.rs @@ -1,5 +1,4 @@ -use bevy_ecs::entity::Entity; -use bevy_utils::EntityHashSet; +use bevy_ecs::entity::{Entity, EntityHashMap, EntityHashSet}; use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; use rand::{Rng, SeedableRng}; use rand_chacha::ChaCha8Rng; @@ -28,7 +27,7 @@ fn make_entity(rng: &mut impl Rng, size: usize) -> Entity { 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"); for size in SIZES { // Get some random-but-consistent entities to use for all the benches below. diff --git a/benches/benches/bevy_ecs/world/mod.rs b/benches/benches/bevy_ecs/world/mod.rs index f594d082d7fb0..421f7a06ad239 100644 --- a/benches/benches/bevy_ecs/world/mod.rs +++ b/benches/benches/bevy_ecs/world/mod.rs @@ -1,13 +1,17 @@ use criterion::criterion_group; mod commands; -mod spawn; -mod world_get; - use commands::*; + +mod spawn; use spawn::*; + +mod world_get; use world_get::*; +mod entity_hash; +use entity_hash::*; + criterion_group!( world_benches, empty_commands, @@ -30,4 +34,5 @@ criterion_group!( query_get_many::<2>, query_get_many::<5>, query_get_many::<10>, + entity_set_build_and_lookup ); From 5d34c0ce9be24da46ae934d721f3d34045767b79 Mon Sep 17 00:00:00 2001 From: Doonv <58695417+doonv@users.noreply.github.com> Date: Tue, 23 Jan 2024 20:49:12 +0200 Subject: [PATCH 06/17] Actually fix benches --- benches/Cargo.toml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/benches/Cargo.toml b/benches/Cargo.toml index 7afdde7e2001e..2edecb8c9d4af 100644 --- a/benches/Cargo.toml +++ b/benches/Cargo.toml @@ -61,8 +61,3 @@ harness = false name = "bezier" path = "benches/bevy_math/bezier.rs" harness = false - -[[bench]] -name = "utils" -path = "benches/bevy_utils/entity_hash.rs" -harness = false From 186eb91128680c67914a3282b6f18e7cca6df45a Mon Sep 17 00:00:00 2001 From: Doonv <58695417+doonv@users.noreply.github.com> Date: Tue, 23 Jan 2024 21:08:22 +0200 Subject: [PATCH 07/17] Fix doc --- crates/bevy_ecs/src/entity/hash.rs | 2 +- crates/bevy_ecs/src/entity/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_ecs/src/entity/hash.rs b/crates/bevy_ecs/src/entity/hash.rs index 0c9dae82504df..270330dc4c4d4 100644 --- a/crates/bevy_ecs/src/entity/hash.rs +++ b/crates/bevy_ecs/src/entity/hash.rs @@ -18,7 +18,7 @@ impl BuildHasher for EntityHash { } /// A very fast hash that is only designed to work on generational indices -/// like [`Entity`](super::Entity). It will panic if attempting to hash a type containing +/// like [`Entity`].g 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 diff --git a/crates/bevy_ecs/src/entity/mod.rs b/crates/bevy_ecs/src/entity/mod.rs index 6afe3ccbb374d..54e62fa30e8dc 100644 --- a/crates/bevy_ecs/src/entity/mod.rs +++ b/crates/bevy_ecs/src/entity/mod.rs @@ -223,7 +223,7 @@ impl Entity { /// // ... 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 /// # use bevy_ecs::{prelude::*, component::*}; From 630d56150fb23214c435de4cf4ae17f27c8a9f58 Mon Sep 17 00:00:00 2001 From: Doonv <58695417+doonv@users.noreply.github.com> Date: Tue, 23 Jan 2024 21:46:47 +0200 Subject: [PATCH 08/17] Additional improvements --- crates/bevy_ecs/src/entity/hash.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/bevy_ecs/src/entity/hash.rs b/crates/bevy_ecs/src/entity/hash.rs index 270330dc4c4d4..0156951e7e302 100644 --- a/crates/bevy_ecs/src/entity/hash.rs +++ b/crates/bevy_ecs/src/entity/hash.rs @@ -13,12 +13,12 @@ impl BuildHasher for EntityHash { type Hasher = EntityHasher; fn build_hasher(&self) -> Self::Hasher { - EntityHasher::default() + Self::Hasher::default() } } /// A very fast hash that is only designed to work on generational indices -/// like [`Entity`].g It will panic if attempting to hash a type containing +/// 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 @@ -39,7 +39,7 @@ impl Hasher for EntityHasher { } fn write(&mut self, _bytes: &[u8]) { - panic!("can only hash u64 using EntityHasher"); + panic!("EntityHasher can only hash u64 fields."); } #[inline] From 894d7e6a42db6bba2ab87b5442e593fdba382516 Mon Sep 17 00:00:00 2001 From: Doonv <58695417+doonv@users.noreply.github.com> Date: Fri, 9 Feb 2024 15:22:00 +0200 Subject: [PATCH 09/17] Fix merge --- crates/bevy_utils/src/lib.rs | 39 ++++++++++++++++++++++++++++++++++++ examples/a.rs | 13 ++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 examples/a.rs diff --git a/crates/bevy_utils/src/lib.rs b/crates/bevy_utils/src/lib.rs index 6836b0379a602..b0b76848a1937 100644 --- a/crates/bevy_utils/src/lib.rs +++ b/crates/bevy_utils/src/lib.rs @@ -320,6 +320,45 @@ impl Drop for OnDrop { } } +/// A specialized hashmap type with Key of [`TypeId`] +/// Iteration order only depends on the order of insertions and deletions. +pub type TypeIdMap = hashbrown::HashMap; + +/// [`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. pub fn info(data: T) { tracing::info!("{:?}", data); diff --git a/examples/a.rs b/examples/a.rs new file mode 100644 index 0000000000000..30c3438317c62 --- /dev/null +++ b/examples/a.rs @@ -0,0 +1,13 @@ +//! d + +use bevy::prelude::*; +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_systems(Startup, setup) + .run(); +} + +fn setup(mut commands: Commands) { + commands.spawn(Camera3dBundle::default()); +} From b5ca4a6d539014829492185c10177a447a686705 Mon Sep 17 00:00:00 2001 From: Doonv <58695417+doonv@users.noreply.github.com> Date: Fri, 9 Feb 2024 15:23:10 +0200 Subject: [PATCH 10/17] Remove temporary file --- examples/a.rs | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 examples/a.rs diff --git a/examples/a.rs b/examples/a.rs deleted file mode 100644 index 30c3438317c62..0000000000000 --- a/examples/a.rs +++ /dev/null @@ -1,13 +0,0 @@ -//! d - -use bevy::prelude::*; -fn main() { - App::new() - .add_plugins(DefaultPlugins) - .add_systems(Startup, setup) - .run(); -} - -fn setup(mut commands: Commands) { - commands.spawn(Camera3dBundle::default()); -} From 9c7fe4393ebd6b45f9ea07546c5704d43195112d Mon Sep 17 00:00:00 2001 From: Doonv <58695417+doonv@users.noreply.github.com> Date: Fri, 9 Feb 2024 15:30:20 +0200 Subject: [PATCH 11/17] Fix tests and format --- crates/bevy_ecs/src/entity/map_entities.rs | 2 +- crates/bevy_ecs/src/reflect/map_entities.rs | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/crates/bevy_ecs/src/entity/map_entities.rs b/crates/bevy_ecs/src/entity/map_entities.rs index 7a904487715a2..32d49ec6bcde9 100644 --- a/crates/bevy_ecs/src/entity/map_entities.rs +++ b/crates/bevy_ecs/src/entity/map_entities.rs @@ -182,7 +182,7 @@ impl<'m> SceneEntityMapper<'m> { #[cfg(test)] mod tests { use crate::{ - entity::{Entity, EntityMapper, SceneEntityMapper}, + entity::{Entity, EntityHashMap, EntityMapper, SceneEntityMapper}, world::World, }; diff --git a/crates/bevy_ecs/src/reflect/map_entities.rs b/crates/bevy_ecs/src/reflect/map_entities.rs index 54e892fa89df0..0d783885dab63 100644 --- a/crates/bevy_ecs/src/reflect/map_entities.rs +++ b/crates/bevy_ecs/src/reflect/map_entities.rs @@ -1,6 +1,6 @@ use crate::{ component::Component, - entity::{Entity, EntityHashMap, SceneEntityMapper, MapEntities}, + entity::{Entity, EntityHashMap, MapEntities, SceneEntityMapper}, world::World, }; use bevy_reflect::FromType; @@ -26,11 +26,7 @@ impl ReflectMapEntities { /// 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` /// components with already valid entity references could be updated to point at something else entirely. - pub fn map_all_entities( - &self, - world: &mut World, - entity_map: &mut EntityHashMap, - ) { + pub fn map_all_entities(&self, world: &mut World, entity_map: &mut EntityHashMap) { SceneEntityMapper::world_scope(entity_map, world, self.map_all_entities); } From ff5fd4638162e87ee0e4b79b204d88ccd4879c8b Mon Sep 17 00:00:00 2001 From: Doonv <58695417+doonv@users.noreply.github.com> Date: Fri, 9 Feb 2024 15:39:16 +0200 Subject: [PATCH 12/17] Remove unusd import --- crates/bevy_pbr/src/light_probe/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/bevy_pbr/src/light_probe/mod.rs b/crates/bevy_pbr/src/light_probe/mod.rs index 6a7088b6e31ae..4cb9325a410dd 100644 --- a/crates/bevy_pbr/src/light_probe/mod.rs +++ b/crates/bevy_pbr/src/light_probe/mod.rs @@ -4,7 +4,6 @@ use bevy_app::{App, Plugin}; use bevy_asset::{load_internal_asset, AssetId, Handle}; use bevy_core_pipeline::core_3d::Camera3d; use bevy_derive::{Deref, DerefMut}; -use bevy_ecs::entity::EntityHashMap; use bevy_ecs::{ component::Component, entity::Entity, From 5c1ef357254a874d2ad6db1c15b0db68c7ac6698 Mon Sep 17 00:00:00 2001 From: Doonv <58695417+doonv@users.noreply.github.com> Date: Fri, 9 Feb 2024 15:41:26 +0200 Subject: [PATCH 13/17] Don't modify CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7bc6ae5f9d8f2..c2b8ed96b0b82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -574,7 +574,7 @@ current changes on git with [previous release tags][git_tag_comparison]. - [Automatic batching/instancing of draw commands][9685] - [Directly copy data into uniform buffers][9865] - [Allow other plugins to create renderer resources][9925] -- [Use `EntityHashMap` for render world entity storage for better performance][9903] +- [Use EntityHashMap` for render world entity storage for better performance][9903] - [Parallelize extract_meshes][9966] - [Fix comment grammar][9990] - [Allow overriding global wireframe setting.][7328] From cb570979f2046acdd00b90dbf8e3dbfce07ebcbe Mon Sep 17 00:00:00 2001 From: Doonv <58695417+doonv@users.noreply.github.com> Date: Fri, 9 Feb 2024 15:52:55 +0200 Subject: [PATCH 14/17] Add `entity_hash` as a benchmark --- benches/Cargo.toml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/benches/Cargo.toml b/benches/Cargo.toml index 2edecb8c9d4af..e64a79d2b389a 100644 --- a/benches/Cargo.toml +++ b/benches/Cargo.toml @@ -61,3 +61,8 @@ harness = false name = "bezier" path = "benches/bevy_math/bezier.rs" harness = false + +[[bench]] +name = "entity_hash" +path = "benches/bevy_ecs/world/entity_hash.rs" +harness = false From 1c81848fdc45ef040060a95641117c49e9b67e98 Mon Sep 17 00:00:00 2001 From: Doonv <58695417+doonv@users.noreply.github.com> Date: Fri, 9 Feb 2024 16:02:05 +0200 Subject: [PATCH 15/17] Fix doc --- crates/bevy_ecs/src/entity/map_entities.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/entity/map_entities.rs b/crates/bevy_ecs/src/entity/map_entities.rs index 32d49ec6bcde9..1b8a2943707dc 100644 --- a/crates/bevy_ecs/src/entity/map_entities.rs +++ b/crates/bevy_ecs/src/entity/map_entities.rs @@ -57,7 +57,7 @@ pub trait MapEntities { /// /// ``` /// # use bevy_ecs::entity::{Entity, EntityMapper}; -/// # use bevy_utils::EntityHashMap; +/// # use bevy_ecs::entity::EntityHashMap; /// # /// pub struct SimpleEntityMapper { /// map: EntityHashMap, From 04a83e88e051338ed102c174af27f8e528587cf0 Mon Sep 17 00:00:00 2001 From: Doonv <58695417+doonv@users.noreply.github.com> Date: Fri, 9 Feb 2024 16:19:34 +0200 Subject: [PATCH 16/17] Actually undo CHANGELOG.md changes --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c2b8ed96b0b82..a5d237f65d522 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -574,7 +574,7 @@ current changes on git with [previous release tags][git_tag_comparison]. - [Automatic batching/instancing of draw commands][9685] - [Directly copy data into uniform buffers][9865] - [Allow other plugins to create renderer resources][9925] -- [Use EntityHashMap` for render world entity storage for better performance][9903] +- [Use EntityHashMap for render world entity storage for better performance][9903] - [Parallelize extract_meshes][9966] - [Fix comment grammar][9990] - [Allow overriding global wireframe setting.][7328] From 290efce5cb3b507c91b5375c4bb37cfb0d5d5433 Mon Sep 17 00:00:00 2001 From: Doonv <58695417+doonv@users.noreply.github.com> Date: Mon, 12 Feb 2024 14:08:28 +0200 Subject: [PATCH 17/17] Fix CI --- crates/bevy_ecs/src/entity/hash.rs | 6 +++++- crates/bevy_ecs/src/entity/mod.rs | 9 +++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/crates/bevy_ecs/src/entity/hash.rs b/crates/bevy_ecs/src/entity/hash.rs index 0156951e7e302..b3de84e52d264 100644 --- a/crates/bevy_ecs/src/entity/hash.rs +++ b/crates/bevy_ecs/src/entity/hash.rs @@ -1,12 +1,14 @@ 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, Reflect)] +#[derive(Default, Clone)] +#[cfg_attr(feature = "bevy_reflect", derive(Reflect))] pub struct EntityHash; impl BuildHasher for EntityHash { @@ -85,11 +87,13 @@ pub type EntityHashSet = hashbrown::HashSet; #[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::: Clone); // EntityHashMap should implement Reflect + #[cfg(feature = "bevy_reflect")] assert_impl_all!(EntityHashMap::: Reflect); } diff --git a/crates/bevy_ecs/src/entity/mod.rs b/crates/bevy_ecs/src/entity/mod.rs index 54e62fa30e8dc..efc0a22458195 100644 --- a/crates/bevy_ecs/src/entity/mod.rs +++ b/crates/bevy_ecs/src/entity/mod.rs @@ -36,6 +36,7 @@ //! [`EntityWorldMut::insert`]: crate::world::EntityWorldMut::insert //! [`EntityWorldMut::remove`]: crate::world::EntityWorldMut::remove mod map_entities; +#[cfg(feature = "bevy_reflect")] use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize}; pub use map_entities::*; @@ -126,8 +127,12 @@ type IdCursor = isize; /// [`EntityCommands`]: crate::system::EntityCommands /// [`Query::get`]: crate::system::Query::get /// [`World`]: crate::world::World -#[derive(Clone, Copy, Reflect)] -#[reflect_value(Hash, PartialEq, Serialize, Deserialize)] +#[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 // optimised codegen for `to_bits`, `PartialEq` and `Ord`. #[repr(C, align(8))]