Use TypeIdMap whenever possible (#11684)
Use `TypeIdMap<T>` instead of `HashMap<TypeId, T>` - ~~`TypeIdMap` was in `bevy_ecs`. I've kept it there because of #11478~~ - ~~I haven't swapped `bevy_reflect` over because it doesn't depend on `bevy_ecs`, but I'd also be happy with moving `TypeIdMap` to `bevy_utils` and then adding a dependency to that~~ - ~~this is a slight change in the public API of `DrawFunctionsInternal`, does this need to go in the changelog?~~ ## Changelog - moved `TypeIdMap` to `bevy_utils` - changed `DrawFunctionsInternal::indices` to `TypeIdMap` ## Migration Guide - `TypeIdMap` now lives in `bevy_utils` - `DrawFunctionsInternal::indices` now uses a `TypeIdMap`. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
This commit is contained in:
parent
4e9590a5ce
commit
21aa5fe2b6
@ -1,5 +1,5 @@
|
||||
use crate::{App, AppError, Plugin};
|
||||
use bevy_utils::{tracing::debug, tracing::warn, HashMap};
|
||||
use bevy_utils::{tracing::debug, tracing::warn, TypeIdMap};
|
||||
use std::any::TypeId;
|
||||
|
||||
/// Combines multiple [`Plugin`]s into a single unit.
|
||||
@ -33,7 +33,7 @@ impl PluginGroup for PluginGroupBuilder {
|
||||
/// can be disabled, enabled or reordered.
|
||||
pub struct PluginGroupBuilder {
|
||||
group_name: String,
|
||||
plugins: HashMap<TypeId, PluginEntry>,
|
||||
plugins: TypeIdMap<PluginEntry>,
|
||||
order: Vec<TypeId>,
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@ use crate::{
|
||||
};
|
||||
use bevy_ecs::world::World;
|
||||
use bevy_log::warn;
|
||||
use bevy_utils::{Entry, HashMap, HashSet};
|
||||
use bevy_utils::{Entry, HashMap, HashSet, TypeIdMap};
|
||||
use crossbeam_channel::Sender;
|
||||
use std::{
|
||||
any::TypeId,
|
||||
@ -61,7 +61,7 @@ impl AssetInfo {
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct AssetInfos {
|
||||
path_to_id: HashMap<AssetPath<'static>, HashMap<TypeId, UntypedAssetId>>,
|
||||
path_to_id: HashMap<AssetPath<'static>, TypeIdMap<UntypedAssetId>>,
|
||||
infos: HashMap<UntypedAssetId, AssetInfo>,
|
||||
/// If set to `true`, this informs [`AssetInfos`] to track data relevant to watching for changes (such as `load_dependants`)
|
||||
/// This should only be set at startup.
|
||||
@ -72,10 +72,10 @@ pub(crate) struct AssetInfos {
|
||||
/// Tracks living labeled assets for a given source asset.
|
||||
/// This should only be set when watching for changes to avoid unnecessary work.
|
||||
pub(crate) living_labeled_assets: HashMap<AssetPath<'static>, HashSet<String>>,
|
||||
pub(crate) handle_providers: HashMap<TypeId, AssetHandleProvider>,
|
||||
pub(crate) dependency_loaded_event_sender: HashMap<TypeId, fn(&mut World, UntypedAssetId)>,
|
||||
pub(crate) handle_providers: TypeIdMap<AssetHandleProvider>,
|
||||
pub(crate) dependency_loaded_event_sender: TypeIdMap<fn(&mut World, UntypedAssetId)>,
|
||||
pub(crate) dependency_failed_event_sender:
|
||||
HashMap<TypeId, fn(&mut World, UntypedAssetId, AssetPath<'static>, AssetLoadError)>,
|
||||
TypeIdMap<fn(&mut World, UntypedAssetId, AssetPath<'static>, AssetLoadError)>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for AssetInfos {
|
||||
@ -112,7 +112,7 @@ impl AssetInfos {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn create_handle_internal(
|
||||
infos: &mut HashMap<UntypedAssetId, AssetInfo>,
|
||||
handle_providers: &HashMap<TypeId, AssetHandleProvider>,
|
||||
handle_providers: &TypeIdMap<AssetHandleProvider>,
|
||||
living_labeled_assets: &mut HashMap<AssetPath<'static>, HashSet<String>>,
|
||||
watching_for_changes: bool,
|
||||
type_id: TypeId,
|
||||
@ -205,7 +205,7 @@ impl AssetInfos {
|
||||
.ok_or(GetOrCreateHandleInternalError::HandleMissingButTypeIdNotSpecified)?;
|
||||
|
||||
match handles.entry(type_id) {
|
||||
Entry::Occupied(entry) => {
|
||||
bevy_utils::hashbrown::hash_map::Entry::Occupied(entry) => {
|
||||
let id = *entry.get();
|
||||
// if there is a path_to_id entry, info always exists
|
||||
let info = self.infos.get_mut(&id).unwrap();
|
||||
@ -246,7 +246,7 @@ impl AssetInfos {
|
||||
}
|
||||
}
|
||||
// The entry does not exist, so this is a "fresh" asset load. We must create a new handle
|
||||
Entry::Vacant(entry) => {
|
||||
bevy_utils::hashbrown::hash_map::Entry::Vacant(entry) => {
|
||||
let should_load = match loading_mode {
|
||||
HandleLoadingMode::NotLoading => false,
|
||||
HandleLoadingMode::Request | HandleLoadingMode::Force => true,
|
||||
@ -640,7 +640,7 @@ impl AssetInfos {
|
||||
|
||||
fn process_handle_drop_internal(
|
||||
infos: &mut HashMap<UntypedAssetId, AssetInfo>,
|
||||
path_to_id: &mut HashMap<AssetPath<'static>, HashMap<TypeId, UntypedAssetId>>,
|
||||
path_to_id: &mut HashMap<AssetPath<'static>, TypeIdMap<UntypedAssetId>>,
|
||||
loader_dependants: &mut HashMap<AssetPath<'static>, HashSet<AssetPath<'static>>>,
|
||||
living_labeled_assets: &mut HashMap<AssetPath<'static>, HashSet<String>>,
|
||||
watching_for_changes: bool,
|
||||
|
@ -19,7 +19,7 @@ use crate::{
|
||||
use bevy_ecs::prelude::*;
|
||||
use bevy_log::{error, info, warn};
|
||||
use bevy_tasks::IoTaskPool;
|
||||
use bevy_utils::{CowArc, HashMap, HashSet};
|
||||
use bevy_utils::{CowArc, HashMap, HashSet, TypeIdMap};
|
||||
use crossbeam_channel::{Receiver, Sender};
|
||||
use futures_lite::StreamExt;
|
||||
use info::*;
|
||||
@ -1238,7 +1238,7 @@ pub fn handle_internal_asset_events(world: &mut World) {
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct AssetLoaders {
|
||||
type_id_to_loader: HashMap<TypeId, MaybeAssetLoader>,
|
||||
type_id_to_loader: TypeIdMap<MaybeAssetLoader>,
|
||||
extension_to_type_id: HashMap<String, TypeId>,
|
||||
type_name_to_type_id: HashMap<&'static str, TypeId>,
|
||||
preregistered_loaders: HashMap<&'static str, TypeId>,
|
||||
|
@ -3,7 +3,7 @@
|
||||
//! This module contains the [`Bundle`] trait and some other helper types.
|
||||
|
||||
pub use bevy_ecs_macros::Bundle;
|
||||
use bevy_utils::{HashMap, HashSet};
|
||||
use bevy_utils::{HashMap, HashSet, TypeIdMap};
|
||||
|
||||
use crate::{
|
||||
archetype::{
|
||||
@ -14,7 +14,6 @@ use crate::{
|
||||
entity::{Entities, Entity, EntityLocation},
|
||||
query::DebugCheckedUnwrap,
|
||||
storage::{SparseSetIndex, SparseSets, Storages, Table, TableRow},
|
||||
TypeIdMap,
|
||||
};
|
||||
use bevy_ptr::OwningPtr;
|
||||
use bevy_utils::all_tuples;
|
||||
|
@ -6,12 +6,12 @@ use crate::{
|
||||
storage::{SparseSetIndex, Storages},
|
||||
system::{Local, Resource, SystemParam},
|
||||
world::{FromWorld, World},
|
||||
TypeIdMap,
|
||||
};
|
||||
pub use bevy_ecs_macros::Component;
|
||||
use bevy_ptr::{OwningPtr, UnsafeCellDeref};
|
||||
#[cfg(feature = "bevy_reflect")]
|
||||
use bevy_reflect::Reflect;
|
||||
use bevy_utils::TypeIdMap;
|
||||
use std::cell::UnsafeCell;
|
||||
use std::{
|
||||
alloc::Layout,
|
||||
|
@ -21,8 +21,6 @@ pub mod storage;
|
||||
pub mod system;
|
||||
pub mod world;
|
||||
|
||||
use std::any::TypeId;
|
||||
|
||||
pub use bevy_ptr as ptr;
|
||||
|
||||
/// Most commonly used re-exported types.
|
||||
@ -56,34 +54,6 @@ pub mod prelude {
|
||||
|
||||
pub use bevy_utils::all_tuples;
|
||||
|
||||
/// A specialized hashmap type with Key of [`TypeId`]
|
||||
type TypeIdMap<V> =
|
||||
std::collections::HashMap<TypeId, V, std::hash::BuildHasherDefault<NoOpTypeIdHasher>>;
|
||||
|
||||
#[doc(hidden)]
|
||||
#[derive(Default)]
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate as bevy_ecs;
|
||||
@ -1755,23 +1725,6 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fast_typeid_hash() {
|
||||
struct Hasher;
|
||||
|
||||
impl std::hash::Hasher for Hasher {
|
||||
fn finish(&self) -> u64 {
|
||||
0
|
||||
}
|
||||
fn write(&mut self, _: &[u8]) {
|
||||
panic!("Hashing of std::any::TypeId changed");
|
||||
}
|
||||
fn write_u64(&mut self, _: u64) {}
|
||||
}
|
||||
|
||||
std::hash::Hash::hash(&TypeId::of::<()>(), &mut Hasher);
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
struct ComponentA(u32);
|
||||
|
||||
|
@ -9,6 +9,7 @@ use crate::{
|
||||
use bevy_utils::{
|
||||
thiserror::Error,
|
||||
tracing::{error, info, warn},
|
||||
TypeIdMap,
|
||||
};
|
||||
|
||||
#[cfg(test)]
|
||||
@ -617,7 +618,7 @@ struct ScheduleState {
|
||||
|
||||
/// changes to system behavior that should be applied the next time
|
||||
/// [`ScheduleState::skipped_systems()`] is called
|
||||
behavior_updates: HashMap<TypeId, Option<SystemBehavior>>,
|
||||
behavior_updates: TypeIdMap<Option<SystemBehavior>>,
|
||||
|
||||
/// This field contains the first steppable system in the schedule.
|
||||
first: Option<usize>,
|
||||
|
@ -6,7 +6,7 @@ pub use bevy_gizmos_macros::GizmoConfigGroup;
|
||||
use bevy_ecs::{component::Component, system::Resource};
|
||||
use bevy_reflect::{Reflect, TypePath};
|
||||
use bevy_render::view::RenderLayers;
|
||||
use bevy_utils::HashMap;
|
||||
use bevy_utils::TypeIdMap;
|
||||
use core::panic;
|
||||
use std::{
|
||||
any::TypeId,
|
||||
@ -30,7 +30,7 @@ pub struct DefaultGizmoConfigGroup;
|
||||
#[derive(Resource, Default)]
|
||||
pub struct GizmoConfigStore {
|
||||
// INVARIANT: must map TypeId::of::<T>() to correct type T
|
||||
store: HashMap<TypeId, (GizmoConfig, Box<dyn Reflect>)>,
|
||||
store: TypeIdMap<(GizmoConfig, Box<dyn Reflect>)>,
|
||||
}
|
||||
|
||||
impl GizmoConfigStore {
|
||||
|
@ -77,7 +77,7 @@ use bevy_render::{
|
||||
renderer::RenderDevice,
|
||||
Extract, ExtractSchedule, Render, RenderApp, RenderSet,
|
||||
};
|
||||
use bevy_utils::HashMap;
|
||||
use bevy_utils::TypeIdMap;
|
||||
use config::{
|
||||
DefaultGizmoConfigGroup, GizmoConfig, GizmoConfigGroup, GizmoConfigStore, GizmoMeshConfig,
|
||||
};
|
||||
@ -206,8 +206,8 @@ impl AppGizmoBuilder for App {
|
||||
|
||||
#[derive(Resource, Default)]
|
||||
struct LineGizmoHandles {
|
||||
list: HashMap<TypeId, Handle<LineGizmo>>,
|
||||
strip: HashMap<TypeId, Handle<LineGizmo>>,
|
||||
list: TypeIdMap<Handle<LineGizmo>>,
|
||||
strip: TypeIdMap<Handle<LineGizmo>>,
|
||||
}
|
||||
|
||||
fn update_gizmo_meshes<T: GizmoConfigGroup>(
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{serde::Serializable, Reflect, TypeInfo, TypePath, Typed};
|
||||
use bevy_ptr::{Ptr, PtrMut};
|
||||
use bevy_utils::{HashMap, HashSet};
|
||||
use bevy_utils::{HashMap, HashSet, TypeIdMap};
|
||||
use downcast_rs::{impl_downcast, Downcast};
|
||||
use serde::Deserialize;
|
||||
use std::{
|
||||
@ -22,7 +22,7 @@ use std::{
|
||||
/// [Registering]: TypeRegistry::register
|
||||
/// [crate-level documentation]: crate
|
||||
pub struct TypeRegistry {
|
||||
registrations: HashMap<TypeId, TypeRegistration>,
|
||||
registrations: TypeIdMap<TypeRegistration>,
|
||||
short_path_to_id: HashMap<&'static str, TypeId>,
|
||||
type_path_to_id: HashMap<&'static str, TypeId>,
|
||||
ambiguous_names: HashSet<&'static str>,
|
||||
@ -318,7 +318,7 @@ impl TypeRegistryArc {
|
||||
///
|
||||
/// [crate-level documentation]: crate
|
||||
pub struct TypeRegistration {
|
||||
data: HashMap<TypeId, Box<dyn TypeData>>,
|
||||
data: TypeIdMap<Box<dyn TypeData>>,
|
||||
type_info: &'static TypeInfo,
|
||||
}
|
||||
|
||||
@ -373,7 +373,7 @@ impl TypeRegistration {
|
||||
/// Creates type registration information for `T`.
|
||||
pub fn of<T: Reflect + Typed + TypePath>() -> Self {
|
||||
Self {
|
||||
data: HashMap::default(),
|
||||
data: Default::default(),
|
||||
type_info: T::type_info(),
|
||||
}
|
||||
}
|
||||
@ -381,7 +381,7 @@ impl TypeRegistration {
|
||||
|
||||
impl Clone for TypeRegistration {
|
||||
fn clone(&self) -> Self {
|
||||
let mut data = HashMap::default();
|
||||
let mut data = TypeIdMap::default();
|
||||
for (id, type_data) in &self.data {
|
||||
data.insert(*id, (*type_data).clone_type_data());
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ use bevy_ecs::{
|
||||
system::{ReadOnlySystemParam, Resource, SystemParam, SystemParamItem, SystemState},
|
||||
world::World,
|
||||
};
|
||||
use bevy_utils::{all_tuples, HashMap};
|
||||
use bevy_utils::{all_tuples, TypeIdMap};
|
||||
use std::{
|
||||
any::TypeId,
|
||||
fmt::Debug,
|
||||
@ -47,7 +47,7 @@ pub struct DrawFunctionId(u32);
|
||||
/// For retrieval, the [`Draw`] functions are mapped to their respective [`TypeId`]s.
|
||||
pub struct DrawFunctionsInternal<P: PhaseItem> {
|
||||
pub draw_functions: Vec<Box<dyn Draw<P>>>,
|
||||
pub indices: HashMap<TypeId, DrawFunctionId>,
|
||||
pub indices: TypeIdMap<DrawFunctionId>,
|
||||
}
|
||||
|
||||
impl<P: PhaseItem> DrawFunctionsInternal<P> {
|
||||
@ -111,7 +111,7 @@ impl<P: PhaseItem> Default for DrawFunctions<P> {
|
||||
Self {
|
||||
internal: RwLock::new(DrawFunctionsInternal {
|
||||
draw_functions: Vec::new(),
|
||||
indices: HashMap::default(),
|
||||
indices: Default::default(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
@ -5,8 +5,7 @@ use bevy_ecs::{
|
||||
world::World,
|
||||
};
|
||||
use bevy_reflect::{Reflect, TypePath, TypeRegistryArc};
|
||||
use bevy_utils::{EntityHashMap, HashMap};
|
||||
use std::any::TypeId;
|
||||
use bevy_utils::{EntityHashMap, TypeIdMap};
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
use crate::serde::SceneSerializer;
|
||||
@ -97,7 +96,7 @@ impl DynamicScene {
|
||||
// of which entities in the scene use that component.
|
||||
// This is so we can update the scene-internal references to references
|
||||
// of the actual entities in the world.
|
||||
let mut scene_mappings: HashMap<TypeId, Vec<Entity>> = HashMap::default();
|
||||
let mut scene_mappings: TypeIdMap<Vec<Entity>> = Default::default();
|
||||
|
||||
for scene_entity in &self.entities {
|
||||
// Fetch the entity with the given entity id from the `entity_map`
|
||||
@ -132,7 +131,7 @@ impl DynamicScene {
|
||||
if registration.data::<ReflectMapEntities>().is_some() {
|
||||
scene_mappings
|
||||
.entry(registration.type_id())
|
||||
.or_insert(Vec::new())
|
||||
.or_default()
|
||||
.push(entity);
|
||||
}
|
||||
|
||||
|
@ -43,6 +43,7 @@ pub mod nonmax {
|
||||
|
||||
use hashbrown::hash_map::RawEntryMut;
|
||||
use std::{
|
||||
any::TypeId,
|
||||
fmt::Debug,
|
||||
future::Future,
|
||||
hash::{BuildHasher, BuildHasherDefault, Hash, Hasher},
|
||||
@ -326,6 +327,34 @@ pub type EntityHashMap<K, V> = hashbrown::HashMap<K, V, EntityHash>;
|
||||
/// A [`HashSet`] pre-configured to use [`EntityHash`] hashing.
|
||||
pub type EntityHashSet<T> = hashbrown::HashSet<T, EntityHash>;
|
||||
|
||||
/// A specialized hashmap type with Key of [`TypeId`]
|
||||
pub type TypeIdMap<V> =
|
||||
hashbrown::HashMap<TypeId, V, std::hash::BuildHasherDefault<NoOpTypeIdHasher>>;
|
||||
|
||||
#[doc(hidden)]
|
||||
#[derive(Default)]
|
||||
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.
|
||||
/// This can be used to ensure that cleanup code is run even in case of a panic.
|
||||
///
|
||||
@ -423,4 +452,21 @@ mod tests {
|
||||
// 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);
|
||||
|
||||
#[test]
|
||||
fn fast_typeid_hash() {
|
||||
struct Hasher;
|
||||
|
||||
impl std::hash::Hasher for Hasher {
|
||||
fn finish(&self) -> u64 {
|
||||
0
|
||||
}
|
||||
fn write(&mut self, _: &[u8]) {
|
||||
panic!("Hashing of std::any::TypeId changed");
|
||||
}
|
||||
fn write_u64(&mut self, _: u64) {}
|
||||
}
|
||||
|
||||
std::hash::Hash::hash(&TypeId::of::<()>(), &mut Hasher);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user