Use immutable key for HashMap
and HashSet
(#12086)
# Objective Memory usage optimisation ## Solution `HashMap` and `HashSet`'s keys are immutable. So using mutable types like `String`, `Vec<T>`, or `PathBuf` as a key is a waste of memory: they have an extra `usize` for their capacity and may have spare capacity. This PR replaces these types by their immutable equivalents `Box<str>`, `Box<[T]>`, and `Box<Path>`. For more context, I recommend watching the [Use Arc Instead of Vec](https://www.youtube.com/watch?v=A4cKi7PTJSs) video. --------- Co-authored-by: James Liu <contact@jamessliu.com>
This commit is contained in:
parent
c97d0103cc
commit
1cded6ac60
@ -78,7 +78,7 @@ pub struct App {
|
|||||||
pub main_schedule_label: InternedScheduleLabel,
|
pub main_schedule_label: InternedScheduleLabel,
|
||||||
sub_apps: HashMap<InternedAppLabel, SubApp>,
|
sub_apps: HashMap<InternedAppLabel, SubApp>,
|
||||||
plugin_registry: Vec<Box<dyn Plugin>>,
|
plugin_registry: Vec<Box<dyn Plugin>>,
|
||||||
plugin_name_added: HashSet<String>,
|
plugin_name_added: HashSet<Box<str>>,
|
||||||
/// A private counter to prevent incorrect calls to `App::run()` from `Plugin::build()`
|
/// A private counter to prevent incorrect calls to `App::run()` from `Plugin::build()`
|
||||||
building_plugin_depth: usize,
|
building_plugin_depth: usize,
|
||||||
plugins_state: PluginsState,
|
plugins_state: PluginsState,
|
||||||
@ -642,7 +642,7 @@ impl App {
|
|||||||
plugin: Box<dyn Plugin>,
|
plugin: Box<dyn Plugin>,
|
||||||
) -> Result<&mut Self, AppError> {
|
) -> Result<&mut Self, AppError> {
|
||||||
debug!("added plugin: {}", plugin.name());
|
debug!("added plugin: {}", plugin.name());
|
||||||
if plugin.is_unique() && !self.plugin_name_added.insert(plugin.name().to_string()) {
|
if plugin.is_unique() && !self.plugin_name_added.insert(plugin.name().into()) {
|
||||||
Err(AppError::DuplicatePlugin {
|
Err(AppError::DuplicatePlugin {
|
||||||
plugin_name: plugin.name().to_string(),
|
plugin_name: plugin.name().to_string(),
|
||||||
})?;
|
})?;
|
||||||
|
@ -25,7 +25,7 @@ pub struct EmbeddedWatcher {
|
|||||||
impl EmbeddedWatcher {
|
impl EmbeddedWatcher {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
dir: Dir,
|
dir: Dir,
|
||||||
root_paths: Arc<RwLock<HashMap<PathBuf, PathBuf>>>,
|
root_paths: Arc<RwLock<HashMap<Box<Path>, PathBuf>>>,
|
||||||
sender: crossbeam_channel::Sender<AssetSourceEvent>,
|
sender: crossbeam_channel::Sender<AssetSourceEvent>,
|
||||||
debounce_wait_time: Duration,
|
debounce_wait_time: Duration,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
@ -49,7 +49,7 @@ impl AssetWatcher for EmbeddedWatcher {}
|
|||||||
/// the initial static bytes from the file embedded in the binary.
|
/// the initial static bytes from the file embedded in the binary.
|
||||||
pub(crate) struct EmbeddedEventHandler {
|
pub(crate) struct EmbeddedEventHandler {
|
||||||
sender: crossbeam_channel::Sender<AssetSourceEvent>,
|
sender: crossbeam_channel::Sender<AssetSourceEvent>,
|
||||||
root_paths: Arc<RwLock<HashMap<PathBuf, PathBuf>>>,
|
root_paths: Arc<RwLock<HashMap<Box<Path>, PathBuf>>>,
|
||||||
root: PathBuf,
|
root: PathBuf,
|
||||||
dir: Dir,
|
dir: Dir,
|
||||||
last_event: Option<AssetSourceEvent>,
|
last_event: Option<AssetSourceEvent>,
|
||||||
@ -61,7 +61,7 @@ impl FilesystemEventHandler for EmbeddedEventHandler {
|
|||||||
|
|
||||||
fn get_path(&self, absolute_path: &Path) -> Option<(PathBuf, bool)> {
|
fn get_path(&self, absolute_path: &Path) -> Option<(PathBuf, bool)> {
|
||||||
let (local_path, is_meta) = get_asset_path(&self.root, absolute_path);
|
let (local_path, is_meta) = get_asset_path(&self.root, absolute_path);
|
||||||
let final_path = self.root_paths.read().get(&local_path)?.clone();
|
let final_path = self.root_paths.read().get(local_path.as_path())?.clone();
|
||||||
if is_meta {
|
if is_meta {
|
||||||
warn!("Meta file asset hot-reloading is not supported yet: {final_path:?}");
|
warn!("Meta file asset hot-reloading is not supported yet: {final_path:?}");
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ pub const EMBEDDED: &str = "embedded";
|
|||||||
pub struct EmbeddedAssetRegistry {
|
pub struct EmbeddedAssetRegistry {
|
||||||
dir: Dir,
|
dir: Dir,
|
||||||
#[cfg(feature = "embedded_watcher")]
|
#[cfg(feature = "embedded_watcher")]
|
||||||
root_paths: std::sync::Arc<parking_lot::RwLock<bevy_utils::HashMap<PathBuf, PathBuf>>>,
|
root_paths: std::sync::Arc<parking_lot::RwLock<bevy_utils::HashMap<Box<Path>, PathBuf>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EmbeddedAssetRegistry {
|
impl EmbeddedAssetRegistry {
|
||||||
@ -35,7 +35,7 @@ impl EmbeddedAssetRegistry {
|
|||||||
#[cfg(feature = "embedded_watcher")]
|
#[cfg(feature = "embedded_watcher")]
|
||||||
self.root_paths
|
self.root_paths
|
||||||
.write()
|
.write()
|
||||||
.insert(full_path.to_owned(), asset_path.to_owned());
|
.insert(full_path.into(), asset_path.to_owned());
|
||||||
self.dir.insert_asset(asset_path, value);
|
self.dir.insert_asset(asset_path, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,7 +48,7 @@ impl EmbeddedAssetRegistry {
|
|||||||
#[cfg(feature = "embedded_watcher")]
|
#[cfg(feature = "embedded_watcher")]
|
||||||
self.root_paths
|
self.root_paths
|
||||||
.write()
|
.write()
|
||||||
.insert(full_path.to_owned(), asset_path.to_owned());
|
.insert(full_path.into(), asset_path.to_owned());
|
||||||
self.dir.insert_meta(asset_path, value);
|
self.dir.insert_meta(asset_path, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,10 +2,7 @@ use crate::io::{AssetReader, AssetReaderError, PathStream, Reader};
|
|||||||
use bevy_utils::{BoxedFuture, HashMap};
|
use bevy_utils::{BoxedFuture, HashMap};
|
||||||
use crossbeam_channel::{Receiver, Sender};
|
use crossbeam_channel::{Receiver, Sender};
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use std::{
|
use std::{path::Path, sync::Arc};
|
||||||
path::{Path, PathBuf},
|
|
||||||
sync::Arc,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// A "gated" reader that will prevent asset reads from returning until
|
/// A "gated" reader that will prevent asset reads from returning until
|
||||||
/// a given path has been "opened" using [`GateOpener`].
|
/// a given path has been "opened" using [`GateOpener`].
|
||||||
@ -13,7 +10,7 @@ use std::{
|
|||||||
/// This is built primarily for unit tests.
|
/// This is built primarily for unit tests.
|
||||||
pub struct GatedReader<R: AssetReader> {
|
pub struct GatedReader<R: AssetReader> {
|
||||||
reader: R,
|
reader: R,
|
||||||
gates: Arc<RwLock<HashMap<PathBuf, (Sender<()>, Receiver<()>)>>>,
|
gates: Arc<RwLock<HashMap<Box<Path>, (Sender<()>, Receiver<()>)>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: AssetReader + Clone> Clone for GatedReader<R> {
|
impl<R: AssetReader + Clone> Clone for GatedReader<R> {
|
||||||
@ -27,7 +24,7 @@ impl<R: AssetReader + Clone> Clone for GatedReader<R> {
|
|||||||
|
|
||||||
/// Opens path "gates" for a [`GatedReader`].
|
/// Opens path "gates" for a [`GatedReader`].
|
||||||
pub struct GateOpener {
|
pub struct GateOpener {
|
||||||
gates: Arc<RwLock<HashMap<PathBuf, (Sender<()>, Receiver<()>)>>>,
|
gates: Arc<RwLock<HashMap<Box<Path>, (Sender<()>, Receiver<()>)>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GateOpener {
|
impl GateOpener {
|
||||||
@ -36,7 +33,7 @@ impl GateOpener {
|
|||||||
pub fn open<P: AsRef<Path>>(&self, path: P) {
|
pub fn open<P: AsRef<Path>>(&self, path: P) {
|
||||||
let mut gates = self.gates.write();
|
let mut gates = self.gates.write();
|
||||||
let gates = gates
|
let gates = gates
|
||||||
.entry(path.as_ref().to_path_buf())
|
.entry_ref(path.as_ref())
|
||||||
.or_insert_with(crossbeam_channel::unbounded);
|
.or_insert_with(crossbeam_channel::unbounded);
|
||||||
gates.0.send(()).unwrap();
|
gates.0.send(()).unwrap();
|
||||||
}
|
}
|
||||||
@ -65,7 +62,7 @@ impl<R: AssetReader> AssetReader for GatedReader<R> {
|
|||||||
let receiver = {
|
let receiver = {
|
||||||
let mut gates = self.gates.write();
|
let mut gates = self.gates.write();
|
||||||
let gates = gates
|
let gates = gates
|
||||||
.entry(path.to_path_buf())
|
.entry_ref(path.as_ref())
|
||||||
.or_insert_with(crossbeam_channel::unbounded);
|
.or_insert_with(crossbeam_channel::unbounded);
|
||||||
gates.1.clone()
|
gates.1.clone()
|
||||||
};
|
};
|
||||||
|
@ -12,9 +12,9 @@ use std::{
|
|||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
struct DirInternal {
|
struct DirInternal {
|
||||||
assets: HashMap<String, Data>,
|
assets: HashMap<Box<str>, Data>,
|
||||||
metadata: HashMap<String, Data>,
|
metadata: HashMap<Box<str>, Data>,
|
||||||
dirs: HashMap<String, Dir>,
|
dirs: HashMap<Box<str>, Dir>,
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ impl Dir {
|
|||||||
dir = self.get_or_insert_dir(parent);
|
dir = self.get_or_insert_dir(parent);
|
||||||
}
|
}
|
||||||
dir.0.write().assets.insert(
|
dir.0.write().assets.insert(
|
||||||
path.file_name().unwrap().to_string_lossy().to_string(),
|
path.file_name().unwrap().to_string_lossy().into(),
|
||||||
Data {
|
Data {
|
||||||
value: value.into(),
|
value: value.into(),
|
||||||
path: path.to_owned(),
|
path: path.to_owned(),
|
||||||
@ -60,7 +60,7 @@ impl Dir {
|
|||||||
dir = self.get_or_insert_dir(parent);
|
dir = self.get_or_insert_dir(parent);
|
||||||
}
|
}
|
||||||
dir.0.write().metadata.insert(
|
dir.0.write().metadata.insert(
|
||||||
path.file_name().unwrap().to_string_lossy().to_string(),
|
path.file_name().unwrap().to_string_lossy().into(),
|
||||||
Data {
|
Data {
|
||||||
value: value.into(),
|
value: value.into(),
|
||||||
path: path.to_owned(),
|
path: path.to_owned(),
|
||||||
@ -73,7 +73,7 @@ impl Dir {
|
|||||||
let mut full_path = PathBuf::new();
|
let mut full_path = PathBuf::new();
|
||||||
for c in path.components() {
|
for c in path.components() {
|
||||||
full_path.push(c);
|
full_path.push(c);
|
||||||
let name = c.as_os_str().to_string_lossy().to_string();
|
let name = c.as_os_str().to_string_lossy().into();
|
||||||
dir = {
|
dir = {
|
||||||
let dirs = &mut dir.0.write().dirs;
|
let dirs = &mut dir.0.write().dirs;
|
||||||
dirs.entry(name)
|
dirs.entry(name)
|
||||||
@ -147,7 +147,12 @@ impl Stream for DirStream {
|
|||||||
let dir = this.dir.0.read();
|
let dir = this.dir.0.read();
|
||||||
|
|
||||||
let dir_index = this.dir_index;
|
let dir_index = this.dir_index;
|
||||||
if let Some(dir_path) = dir.dirs.keys().nth(dir_index).map(|d| dir.path.join(d)) {
|
if let Some(dir_path) = dir
|
||||||
|
.dirs
|
||||||
|
.keys()
|
||||||
|
.nth(dir_index)
|
||||||
|
.map(|d| dir.path.join(d.as_ref()))
|
||||||
|
{
|
||||||
this.dir_index += 1;
|
this.dir_index += 1;
|
||||||
Poll::Ready(Some(dir_path))
|
Poll::Ready(Some(dir_path))
|
||||||
} else {
|
} else {
|
||||||
|
@ -451,10 +451,7 @@ mod tests {
|
|||||||
use bevy_utils::{BoxedFuture, Duration, HashMap};
|
use bevy_utils::{BoxedFuture, Duration, HashMap};
|
||||||
use futures_lite::AsyncReadExt;
|
use futures_lite::AsyncReadExt;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{
|
use std::{path::Path, sync::Arc};
|
||||||
path::{Path, PathBuf},
|
|
||||||
sync::Arc,
|
|
||||||
};
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Asset, TypePath, Debug, Default)]
|
#[derive(Asset, TypePath, Debug, Default)]
|
||||||
@ -545,7 +542,7 @@ mod tests {
|
|||||||
/// A dummy [`CoolText`] asset reader that only succeeds after `failure_count` times it's read from for each asset.
|
/// A dummy [`CoolText`] asset reader that only succeeds after `failure_count` times it's read from for each asset.
|
||||||
#[derive(Default, Clone)]
|
#[derive(Default, Clone)]
|
||||||
pub struct UnstableMemoryAssetReader {
|
pub struct UnstableMemoryAssetReader {
|
||||||
pub attempt_counters: Arc<std::sync::Mutex<HashMap<PathBuf, usize>>>,
|
pub attempt_counters: Arc<std::sync::Mutex<HashMap<Box<Path>, usize>>>,
|
||||||
pub load_delay: Duration,
|
pub load_delay: Duration,
|
||||||
memory_reader: MemoryAssetReader,
|
memory_reader: MemoryAssetReader,
|
||||||
failure_count: usize,
|
failure_count: usize,
|
||||||
@ -589,13 +586,12 @@ mod tests {
|
|||||||
Result<Box<bevy_asset::io::Reader<'a>>, bevy_asset::io::AssetReaderError>,
|
Result<Box<bevy_asset::io::Reader<'a>>, bevy_asset::io::AssetReaderError>,
|
||||||
> {
|
> {
|
||||||
let attempt_number = {
|
let attempt_number = {
|
||||||
let key = PathBuf::from(path);
|
|
||||||
let mut attempt_counters = self.attempt_counters.lock().unwrap();
|
let mut attempt_counters = self.attempt_counters.lock().unwrap();
|
||||||
if let Some(existing) = attempt_counters.get_mut(&key) {
|
if let Some(existing) = attempt_counters.get_mut(path) {
|
||||||
*existing += 1;
|
*existing += 1;
|
||||||
*existing
|
*existing
|
||||||
} else {
|
} else {
|
||||||
attempt_counters.insert(key, 1);
|
attempt_counters.insert(path.into(), 1);
|
||||||
1
|
1
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -56,7 +56,7 @@ pub struct AssetProcessorData {
|
|||||||
log: async_lock::RwLock<Option<ProcessorTransactionLog>>,
|
log: async_lock::RwLock<Option<ProcessorTransactionLog>>,
|
||||||
processors: RwLock<HashMap<&'static str, Arc<dyn ErasedProcessor>>>,
|
processors: RwLock<HashMap<&'static str, Arc<dyn ErasedProcessor>>>,
|
||||||
/// Default processors for file extensions
|
/// Default processors for file extensions
|
||||||
default_processors: RwLock<HashMap<String, &'static str>>,
|
default_processors: RwLock<HashMap<Box<str>, &'static str>>,
|
||||||
state: async_lock::RwLock<ProcessorState>,
|
state: async_lock::RwLock<ProcessorState>,
|
||||||
sources: AssetSources,
|
sources: AssetSources,
|
||||||
initialized_sender: async_broadcast::Sender<()>,
|
initialized_sender: async_broadcast::Sender<()>,
|
||||||
@ -482,7 +482,7 @@ impl AssetProcessor {
|
|||||||
/// Set the default processor for the given `extension`. Make sure `P` is registered with [`AssetProcessor::register_processor`].
|
/// Set the default processor for the given `extension`. Make sure `P` is registered with [`AssetProcessor::register_processor`].
|
||||||
pub fn set_default_processor<P: Process>(&self, extension: &str) {
|
pub fn set_default_processor<P: Process>(&self, extension: &str) {
|
||||||
let mut default_processors = self.data.default_processors.write();
|
let mut default_processors = self.data.default_processors.write();
|
||||||
default_processors.insert(extension.to_string(), std::any::type_name::<P>());
|
default_processors.insert(extension.into(), std::any::type_name::<P>());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the default processor for the given `extension`, if it exists.
|
/// Returns the default processor for the given `extension`, if it exists.
|
||||||
|
@ -71,7 +71,7 @@ pub(crate) struct AssetInfos {
|
|||||||
pub(crate) loader_dependants: HashMap<AssetPath<'static>, HashSet<AssetPath<'static>>>,
|
pub(crate) loader_dependants: HashMap<AssetPath<'static>, HashSet<AssetPath<'static>>>,
|
||||||
/// Tracks living labeled assets for a given source asset.
|
/// Tracks living labeled assets for a given source asset.
|
||||||
/// This should only be set when watching for changes to avoid unnecessary work.
|
/// 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) living_labeled_assets: HashMap<AssetPath<'static>, HashSet<Box<str>>>,
|
||||||
pub(crate) handle_providers: TypeIdMap<AssetHandleProvider>,
|
pub(crate) handle_providers: TypeIdMap<AssetHandleProvider>,
|
||||||
pub(crate) dependency_loaded_event_sender: TypeIdMap<fn(&mut World, UntypedAssetId)>,
|
pub(crate) dependency_loaded_event_sender: TypeIdMap<fn(&mut World, UntypedAssetId)>,
|
||||||
pub(crate) dependency_failed_event_sender:
|
pub(crate) dependency_failed_event_sender:
|
||||||
@ -113,7 +113,7 @@ impl AssetInfos {
|
|||||||
fn create_handle_internal(
|
fn create_handle_internal(
|
||||||
infos: &mut HashMap<UntypedAssetId, AssetInfo>,
|
infos: &mut HashMap<UntypedAssetId, AssetInfo>,
|
||||||
handle_providers: &TypeIdMap<AssetHandleProvider>,
|
handle_providers: &TypeIdMap<AssetHandleProvider>,
|
||||||
living_labeled_assets: &mut HashMap<AssetPath<'static>, HashSet<String>>,
|
living_labeled_assets: &mut HashMap<AssetPath<'static>, HashSet<Box<str>>>,
|
||||||
watching_for_changes: bool,
|
watching_for_changes: bool,
|
||||||
type_id: TypeId,
|
type_id: TypeId,
|
||||||
path: Option<AssetPath<'static>>,
|
path: Option<AssetPath<'static>>,
|
||||||
@ -129,7 +129,7 @@ impl AssetInfos {
|
|||||||
let mut without_label = path.to_owned();
|
let mut without_label = path.to_owned();
|
||||||
if let Some(label) = without_label.take_label() {
|
if let Some(label) = without_label.take_label() {
|
||||||
let labels = living_labeled_assets.entry(without_label).or_default();
|
let labels = living_labeled_assets.entry(without_label).or_default();
|
||||||
labels.insert(label.to_string());
|
labels.insert(label.as_ref().into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -613,7 +613,7 @@ impl AssetInfos {
|
|||||||
info: &AssetInfo,
|
info: &AssetInfo,
|
||||||
loader_dependants: &mut HashMap<AssetPath<'static>, HashSet<AssetPath<'static>>>,
|
loader_dependants: &mut HashMap<AssetPath<'static>, HashSet<AssetPath<'static>>>,
|
||||||
path: &AssetPath<'static>,
|
path: &AssetPath<'static>,
|
||||||
living_labeled_assets: &mut HashMap<AssetPath<'static>, HashSet<String>>,
|
living_labeled_assets: &mut HashMap<AssetPath<'static>, HashSet<Box<str>>>,
|
||||||
) {
|
) {
|
||||||
for loader_dependency in info.loader_dependencies.keys() {
|
for loader_dependency in info.loader_dependencies.keys() {
|
||||||
if let Some(dependants) = loader_dependants.get_mut(loader_dependency) {
|
if let Some(dependants) = loader_dependants.get_mut(loader_dependency) {
|
||||||
@ -642,7 +642,7 @@ impl AssetInfos {
|
|||||||
infos: &mut HashMap<UntypedAssetId, AssetInfo>,
|
infos: &mut HashMap<UntypedAssetId, AssetInfo>,
|
||||||
path_to_id: &mut HashMap<AssetPath<'static>, TypeIdMap<UntypedAssetId>>,
|
path_to_id: &mut HashMap<AssetPath<'static>, TypeIdMap<UntypedAssetId>>,
|
||||||
loader_dependants: &mut HashMap<AssetPath<'static>, HashSet<AssetPath<'static>>>,
|
loader_dependants: &mut HashMap<AssetPath<'static>, HashSet<AssetPath<'static>>>,
|
||||||
living_labeled_assets: &mut HashMap<AssetPath<'static>, HashSet<String>>,
|
living_labeled_assets: &mut HashMap<AssetPath<'static>, HashSet<Box<str>>>,
|
||||||
watching_for_changes: bool,
|
watching_for_changes: bool,
|
||||||
id: UntypedAssetId,
|
id: UntypedAssetId,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
|
@ -13,7 +13,7 @@ use thiserror::Error;
|
|||||||
pub(crate) struct AssetLoaders {
|
pub(crate) struct AssetLoaders {
|
||||||
loaders: Vec<MaybeAssetLoader>,
|
loaders: Vec<MaybeAssetLoader>,
|
||||||
type_id_to_loaders: TypeIdMap<Vec<usize>>,
|
type_id_to_loaders: TypeIdMap<Vec<usize>>,
|
||||||
extension_to_loaders: HashMap<String, Vec<usize>>,
|
extension_to_loaders: HashMap<Box<str>, Vec<usize>>,
|
||||||
type_name_to_loader: HashMap<&'static str, usize>,
|
type_name_to_loader: HashMap<&'static str, usize>,
|
||||||
preregistered_loaders: HashMap<&'static str, usize>,
|
preregistered_loaders: HashMap<&'static str, usize>,
|
||||||
}
|
}
|
||||||
@ -44,7 +44,7 @@ impl AssetLoaders {
|
|||||||
for extension in loader.extensions() {
|
for extension in loader.extensions() {
|
||||||
let list = self
|
let list = self
|
||||||
.extension_to_loaders
|
.extension_to_loaders
|
||||||
.entry(extension.to_string())
|
.entry((*extension).into())
|
||||||
.or_default();
|
.or_default();
|
||||||
|
|
||||||
if !list.is_empty() {
|
if !list.is_empty() {
|
||||||
@ -105,7 +105,7 @@ impl AssetLoaders {
|
|||||||
for extension in extensions {
|
for extension in extensions {
|
||||||
let list = self
|
let list = self
|
||||||
.extension_to_loaders
|
.extension_to_loaders
|
||||||
.entry(extension.to_string())
|
.entry((*extension).into())
|
||||||
.or_default();
|
.or_default();
|
||||||
|
|
||||||
if !list.is_empty() {
|
if !list.is_empty() {
|
||||||
|
@ -806,7 +806,7 @@ pub struct Bundles {
|
|||||||
/// Cache static [`BundleId`]
|
/// Cache static [`BundleId`]
|
||||||
bundle_ids: TypeIdMap<BundleId>,
|
bundle_ids: TypeIdMap<BundleId>,
|
||||||
/// Cache dynamic [`BundleId`] with multiple components
|
/// Cache dynamic [`BundleId`] with multiple components
|
||||||
dynamic_bundle_ids: HashMap<Vec<ComponentId>, (BundleId, Vec<StorageType>)>,
|
dynamic_bundle_ids: HashMap<Box<[ComponentId]>, (BundleId, Vec<StorageType>)>,
|
||||||
/// Cache optimized dynamic [`BundleId`] with single component
|
/// Cache optimized dynamic [`BundleId`] with single component
|
||||||
dynamic_component_bundle_ids: HashMap<ComponentId, (BundleId, StorageType)>,
|
dynamic_component_bundle_ids: HashMap<ComponentId, (BundleId, StorageType)>,
|
||||||
}
|
}
|
||||||
@ -871,7 +871,7 @@ impl Bundles {
|
|||||||
.from_key(component_ids)
|
.from_key(component_ids)
|
||||||
.or_insert_with(|| {
|
.or_insert_with(|| {
|
||||||
(
|
(
|
||||||
Vec::from(component_ids),
|
component_ids.into(),
|
||||||
initialize_dynamic_bundle(bundle_infos, components, Vec::from(component_ids)),
|
initialize_dynamic_bundle(bundle_infos, components, Vec::from(component_ids)),
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
@ -796,7 +796,7 @@ impl Table {
|
|||||||
/// Can be accessed via [`Storages`](crate::storage::Storages)
|
/// Can be accessed via [`Storages`](crate::storage::Storages)
|
||||||
pub struct Tables {
|
pub struct Tables {
|
||||||
tables: Vec<Table>,
|
tables: Vec<Table>,
|
||||||
table_ids: HashMap<Vec<ComponentId>, TableId>,
|
table_ids: HashMap<Box<[ComponentId]>, TableId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Tables {
|
impl Default for Tables {
|
||||||
@ -872,10 +872,7 @@ impl Tables {
|
|||||||
table = table.add_column(components.get_info_unchecked(*component_id));
|
table = table.add_column(components.get_info_unchecked(*component_id));
|
||||||
}
|
}
|
||||||
tables.push(table.build());
|
tables.push(table.build());
|
||||||
(
|
(component_ids.into(), TableId::from_usize(tables.len() - 1))
|
||||||
component_ids.to_vec(),
|
|
||||||
TableId::from_usize(tables.len() - 1),
|
|
||||||
)
|
|
||||||
});
|
});
|
||||||
|
|
||||||
*value
|
*value
|
||||||
|
@ -26,7 +26,7 @@ use bevy_scene::Scene;
|
|||||||
/// Adds support for glTF file loading to the app.
|
/// Adds support for glTF file loading to the app.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct GltfPlugin {
|
pub struct GltfPlugin {
|
||||||
custom_vertex_attributes: HashMap<String, MeshVertexAttribute>,
|
custom_vertex_attributes: HashMap<Box<str>, MeshVertexAttribute>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GltfPlugin {
|
impl GltfPlugin {
|
||||||
@ -40,8 +40,7 @@ impl GltfPlugin {
|
|||||||
name: &str,
|
name: &str,
|
||||||
attribute: MeshVertexAttribute,
|
attribute: MeshVertexAttribute,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
self.custom_vertex_attributes
|
self.custom_vertex_attributes.insert(name.into(), attribute);
|
||||||
.insert(name.to_string(), attribute);
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -75,19 +74,19 @@ pub struct Gltf {
|
|||||||
/// All scenes loaded from the glTF file.
|
/// All scenes loaded from the glTF file.
|
||||||
pub scenes: Vec<Handle<Scene>>,
|
pub scenes: Vec<Handle<Scene>>,
|
||||||
/// Named scenes loaded from the glTF file.
|
/// Named scenes loaded from the glTF file.
|
||||||
pub named_scenes: HashMap<String, Handle<Scene>>,
|
pub named_scenes: HashMap<Box<str>, Handle<Scene>>,
|
||||||
/// All meshes loaded from the glTF file.
|
/// All meshes loaded from the glTF file.
|
||||||
pub meshes: Vec<Handle<GltfMesh>>,
|
pub meshes: Vec<Handle<GltfMesh>>,
|
||||||
/// Named meshes loaded from the glTF file.
|
/// Named meshes loaded from the glTF file.
|
||||||
pub named_meshes: HashMap<String, Handle<GltfMesh>>,
|
pub named_meshes: HashMap<Box<str>, Handle<GltfMesh>>,
|
||||||
/// All materials loaded from the glTF file.
|
/// All materials loaded from the glTF file.
|
||||||
pub materials: Vec<Handle<StandardMaterial>>,
|
pub materials: Vec<Handle<StandardMaterial>>,
|
||||||
/// Named materials loaded from the glTF file.
|
/// Named materials loaded from the glTF file.
|
||||||
pub named_materials: HashMap<String, Handle<StandardMaterial>>,
|
pub named_materials: HashMap<Box<str>, Handle<StandardMaterial>>,
|
||||||
/// All nodes loaded from the glTF file.
|
/// All nodes loaded from the glTF file.
|
||||||
pub nodes: Vec<Handle<GltfNode>>,
|
pub nodes: Vec<Handle<GltfNode>>,
|
||||||
/// Named nodes loaded from the glTF file.
|
/// Named nodes loaded from the glTF file.
|
||||||
pub named_nodes: HashMap<String, Handle<GltfNode>>,
|
pub named_nodes: HashMap<Box<str>, Handle<GltfNode>>,
|
||||||
/// Default scene to be displayed.
|
/// Default scene to be displayed.
|
||||||
pub default_scene: Option<Handle<Scene>>,
|
pub default_scene: Option<Handle<Scene>>,
|
||||||
/// All animations loaded from the glTF file.
|
/// All animations loaded from the glTF file.
|
||||||
@ -95,7 +94,7 @@ pub struct Gltf {
|
|||||||
pub animations: Vec<Handle<AnimationClip>>,
|
pub animations: Vec<Handle<AnimationClip>>,
|
||||||
/// Named animations loaded from the glTF file.
|
/// Named animations loaded from the glTF file.
|
||||||
#[cfg(feature = "bevy_animation")]
|
#[cfg(feature = "bevy_animation")]
|
||||||
pub named_animations: HashMap<String, Handle<AnimationClip>>,
|
pub named_animations: HashMap<Box<str>, Handle<AnimationClip>>,
|
||||||
/// The gltf root of the gltf asset, see <https://docs.rs/gltf/latest/gltf/struct.Gltf.html>. Only has a value when `GltfLoaderSettings::include_source` is true.
|
/// The gltf root of the gltf asset, see <https://docs.rs/gltf/latest/gltf/struct.Gltf.html>. Only has a value when `GltfLoaderSettings::include_source` is true.
|
||||||
pub source: Option<gltf::Gltf>,
|
pub source: Option<gltf::Gltf>,
|
||||||
}
|
}
|
||||||
|
@ -110,7 +110,7 @@ pub struct GltfLoader {
|
|||||||
/// Keys must be the attribute names as found in the glTF data, which must start with an underscore.
|
/// Keys must be the attribute names as found in the glTF data, which must start with an underscore.
|
||||||
/// See [this section of the glTF specification](https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#meshes-overview)
|
/// See [this section of the glTF specification](https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#meshes-overview)
|
||||||
/// for additional details on custom attributes.
|
/// for additional details on custom attributes.
|
||||||
pub custom_vertex_attributes: HashMap<String, MeshVertexAttribute>,
|
pub custom_vertex_attributes: HashMap<Box<str>, MeshVertexAttribute>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Specifies optional settings for processing gltfs at load time. By default, all recognized contents of
|
/// Specifies optional settings for processing gltfs at load time. By default, all recognized contents of
|
||||||
@ -293,7 +293,7 @@ async fn load_gltf<'a, 'b, 'c>(
|
|||||||
let handle = load_context
|
let handle = load_context
|
||||||
.add_labeled_asset(format!("Animation{}", animation.index()), animation_clip);
|
.add_labeled_asset(format!("Animation{}", animation.index()), animation_clip);
|
||||||
if let Some(name) = animation.name() {
|
if let Some(name) = animation.name() {
|
||||||
named_animations.insert(name.to_string(), handle.clone());
|
named_animations.insert(name.into(), handle.clone());
|
||||||
}
|
}
|
||||||
animations.push(handle);
|
animations.push(handle);
|
||||||
}
|
}
|
||||||
@ -383,7 +383,7 @@ async fn load_gltf<'a, 'b, 'c>(
|
|||||||
for material in gltf.materials() {
|
for material in gltf.materials() {
|
||||||
let handle = load_material(&material, load_context, false);
|
let handle = load_material(&material, load_context, false);
|
||||||
if let Some(name) = material.name() {
|
if let Some(name) = material.name() {
|
||||||
named_materials.insert(name.to_string(), handle.clone());
|
named_materials.insert(name.into(), handle.clone());
|
||||||
}
|
}
|
||||||
materials.push(handle);
|
materials.push(handle);
|
||||||
}
|
}
|
||||||
@ -526,7 +526,7 @@ async fn load_gltf<'a, 'b, 'c>(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
if let Some(name) = gltf_mesh.name() {
|
if let Some(name) = gltf_mesh.name() {
|
||||||
named_meshes.insert(name.to_string(), handle.clone());
|
named_meshes.insert(name.into(), handle.clone());
|
||||||
}
|
}
|
||||||
meshes.push(handle);
|
meshes.push(handle);
|
||||||
}
|
}
|
||||||
@ -560,11 +560,7 @@ async fn load_gltf<'a, 'b, 'c>(
|
|||||||
.collect::<Vec<Handle<GltfNode>>>();
|
.collect::<Vec<Handle<GltfNode>>>();
|
||||||
let named_nodes = named_nodes_intermediate
|
let named_nodes = named_nodes_intermediate
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|(name, index)| {
|
.filter_map(|(name, index)| nodes.get(index).map(|handle| (name.into(), handle.clone())))
|
||||||
nodes
|
|
||||||
.get(index)
|
|
||||||
.map(|handle| (name.to_string(), handle.clone()))
|
|
||||||
})
|
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let skinned_mesh_inverse_bindposes: Vec<_> = gltf
|
let skinned_mesh_inverse_bindposes: Vec<_> = gltf
|
||||||
@ -661,7 +657,7 @@ async fn load_gltf<'a, 'b, 'c>(
|
|||||||
let scene_handle = load_context.add_loaded_labeled_asset(scene_label(&scene), loaded_scene);
|
let scene_handle = load_context.add_loaded_labeled_asset(scene_label(&scene), loaded_scene);
|
||||||
|
|
||||||
if let Some(name) = scene.name() {
|
if let Some(name) = scene.name() {
|
||||||
named_scenes.insert(name.to_string(), scene_handle.clone());
|
named_scenes.insert(name.into(), scene_handle.clone());
|
||||||
}
|
}
|
||||||
scenes.push(scene_handle);
|
scenes.push(scene_handle);
|
||||||
}
|
}
|
||||||
|
@ -255,7 +255,7 @@ pub(crate) fn convert_attribute(
|
|||||||
semantic: gltf::Semantic,
|
semantic: gltf::Semantic,
|
||||||
accessor: gltf::Accessor,
|
accessor: gltf::Accessor,
|
||||||
buffer_data: &Vec<Vec<u8>>,
|
buffer_data: &Vec<Vec<u8>>,
|
||||||
custom_vertex_attributes: &HashMap<String, MeshVertexAttribute>,
|
custom_vertex_attributes: &HashMap<Box<str>, MeshVertexAttribute>,
|
||||||
) -> Result<(MeshVertexAttribute, Values), ConvertAttributeError> {
|
) -> Result<(MeshVertexAttribute, Values), ConvertAttributeError> {
|
||||||
if let Some((attribute, conversion)) = match &semantic {
|
if let Some((attribute, conversion)) = match &semantic {
|
||||||
gltf::Semantic::Positions => Some((Mesh::ATTRIBUTE_POSITION, ConversionMode::Any)),
|
gltf::Semantic::Positions => Some((Mesh::ATTRIBUTE_POSITION, ConversionMode::Any)),
|
||||||
@ -271,7 +271,7 @@ pub(crate) fn convert_attribute(
|
|||||||
Some((Mesh::ATTRIBUTE_JOINT_WEIGHT, ConversionMode::JointWeight))
|
Some((Mesh::ATTRIBUTE_JOINT_WEIGHT, ConversionMode::JointWeight))
|
||||||
}
|
}
|
||||||
gltf::Semantic::Extras(name) => custom_vertex_attributes
|
gltf::Semantic::Extras(name) => custom_vertex_attributes
|
||||||
.get(name)
|
.get(name.as_str())
|
||||||
.map(|attr| (attr.clone(), ConversionMode::Any)),
|
.map(|attr| (attr.clone(), ConversionMode::Any)),
|
||||||
_ => None,
|
_ => None,
|
||||||
} {
|
} {
|
||||||
|
@ -4,10 +4,11 @@ use bevy_asset::{AssetEvent, AssetId, Assets};
|
|||||||
use bevy_ecs::system::{Res, ResMut};
|
use bevy_ecs::system::{Res, ResMut};
|
||||||
use bevy_ecs::{event::EventReader, system::Resource};
|
use bevy_ecs::{event::EventReader, system::Resource};
|
||||||
use bevy_tasks::Task;
|
use bevy_tasks::Task;
|
||||||
|
use bevy_utils::hashbrown::hash_map::EntryRef;
|
||||||
use bevy_utils::{
|
use bevy_utils::{
|
||||||
default,
|
default,
|
||||||
tracing::{debug, error},
|
tracing::{debug, error},
|
||||||
Entry, HashMap, HashSet,
|
HashMap, HashSet,
|
||||||
};
|
};
|
||||||
use naga::valid::Capabilities;
|
use naga::valid::Capabilities;
|
||||||
use std::{
|
use std::{
|
||||||
@ -122,7 +123,7 @@ impl CachedPipelineState {
|
|||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct ShaderData {
|
struct ShaderData {
|
||||||
pipelines: HashSet<CachedPipelineId>,
|
pipelines: HashSet<CachedPipelineId>,
|
||||||
processed_shaders: HashMap<Vec<ShaderDefVal>, ErasedShaderModule>,
|
processed_shaders: HashMap<Box<[ShaderDefVal]>, ErasedShaderModule>,
|
||||||
resolved_imports: HashMap<ShaderImport, AssetId<Shader>>,
|
resolved_imports: HashMap<ShaderImport, AssetId<Shader>>,
|
||||||
dependents: HashSet<AssetId<Shader>>,
|
dependents: HashSet<AssetId<Shader>>,
|
||||||
}
|
}
|
||||||
@ -274,9 +275,9 @@ impl ShaderCache {
|
|||||||
data.pipelines.insert(pipeline);
|
data.pipelines.insert(pipeline);
|
||||||
|
|
||||||
// PERF: this shader_defs clone isn't great. use raw_entry_mut when it stabilizes
|
// PERF: this shader_defs clone isn't great. use raw_entry_mut when it stabilizes
|
||||||
let module = match data.processed_shaders.entry(shader_defs.to_vec()) {
|
let module = match data.processed_shaders.entry_ref(shader_defs) {
|
||||||
Entry::Occupied(entry) => entry.into_mut(),
|
EntryRef::Occupied(entry) => entry.into_mut(),
|
||||||
Entry::Vacant(entry) => {
|
EntryRef::Vacant(entry) => {
|
||||||
let mut shader_defs = shader_defs.to_vec();
|
let mut shader_defs = shader_defs.to_vec();
|
||||||
#[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
|
#[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
|
||||||
{
|
{
|
||||||
|
@ -14,3 +14,4 @@ toml_edit = { version = "0.22", default-features = false, features = ["parse"] }
|
|||||||
tera = "1.15"
|
tera = "1.15"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
bitflags = "2.3"
|
bitflags = "2.3"
|
||||||
|
hashbrown = { version = "0.14", features = ["serde"] }
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use std::{cmp::Ordering, collections::HashMap, fs::File};
|
use std::{cmp::Ordering, fs::File};
|
||||||
|
|
||||||
|
use hashbrown::HashMap;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use tera::{Context, Tera};
|
use tera::{Context, Tera};
|
||||||
use toml_edit::Document;
|
use toml_edit::Document;
|
||||||
@ -80,7 +81,7 @@ fn parse_examples(panic_on_missing: bool) -> Vec<Example> {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_categories() -> HashMap<String, String> {
|
fn parse_categories() -> HashMap<Box<str>, String> {
|
||||||
let manifest_file = std::fs::read_to_string("Cargo.toml").unwrap();
|
let manifest_file = std::fs::read_to_string("Cargo.toml").unwrap();
|
||||||
let manifest = manifest_file.parse::<Document>().unwrap();
|
let manifest = manifest_file.parse::<Document>().unwrap();
|
||||||
manifest
|
manifest
|
||||||
@ -95,7 +96,7 @@ fn parse_categories() -> HashMap<String, String> {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|v| {
|
.map(|v| {
|
||||||
(
|
(
|
||||||
v.get("name").unwrap().as_str().unwrap().to_string(),
|
v.get("name").unwrap().as_str().unwrap().into(),
|
||||||
v.get("description").unwrap().as_str().unwrap().to_string(),
|
v.get("description").unwrap().as_str().unwrap().to_string(),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@ -107,10 +108,10 @@ pub(crate) fn check(what_to_run: Command) {
|
|||||||
|
|
||||||
if what_to_run.contains(Command::UPDATE) {
|
if what_to_run.contains(Command::UPDATE) {
|
||||||
let categories = parse_categories();
|
let categories = parse_categories();
|
||||||
let examples_by_category: HashMap<String, Category> = examples
|
let examples_by_category: HashMap<Box<str>, Category> = examples
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.fold(HashMap::<String, Vec<Example>>::new(), |mut v, ex| {
|
.fold(HashMap::<Box<str>, Vec<Example>>::new(), |mut v, ex| {
|
||||||
v.entry(ex.category.clone()).or_default().push(ex);
|
v.entry_ref(ex.category.as_str()).or_default().push(ex);
|
||||||
v
|
v
|
||||||
})
|
})
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
Loading…
Reference in New Issue
Block a user