use std::{borrow::Borrow, hash::Hash, path::PathBuf, sync::Arc};
use fnv::FnvHashMap;
use log::debug;
use rayon::ThreadPool;
use amethyst_error::ResultExt;
#[cfg(feature = "profiler")]
use thread_profiler::profile_scope;
use crate::{
error::Error,
storage::{AssetStorage, Handle, Processed},
Asset, Directory, Format, FormatValue, Progress, Source,
};
pub struct Loader {
hot_reload: bool,
pool: Arc<ThreadPool>,
sources: FnvHashMap<String, Arc<dyn Source>>,
}
impl Loader {
pub fn new<P>(directory: P, pool: Arc<ThreadPool>) -> Self
where
P: Into<PathBuf>,
{
Self::with_default_source(Directory::new(directory), pool)
}
pub fn with_default_source<S>(source: S, pool: Arc<ThreadPool>) -> Self
where
S: Source,
{
let mut loader = Loader {
hot_reload: true,
pool,
sources: Default::default(),
};
loader.set_default_source(source);
loader
}
pub fn add_source<I, S>(&mut self, id: I, source: S)
where
I: Into<String>,
S: Source,
{
self.sources
.insert(id.into(), Arc::new(source) as Arc<dyn Source>);
}
pub fn set_default_source<S>(&mut self, source: S)
where
S: Source,
{
self.add_source(String::new(), source);
}
pub fn set_hot_reload(&mut self, value: bool) {
self.hot_reload = value;
}
pub fn load<A, F, N, P>(
&self,
name: N,
format: F,
progress: P,
storage: &AssetStorage<A>,
) -> Handle<A>
where
A: Asset,
F: Format<A::Data>,
N: Into<String>,
P: Progress,
{
#[cfg(feature = "profiler")]
profile_scope!("initialise_loading_assets");
self.load_from::<A, F, _, _, _>(name, format, "", progress, storage)
}
pub fn load_from<A, F, N, P, S>(
&self,
name: N,
format: F,
source: &S,
mut progress: P,
storage: &AssetStorage<A>,
) -> Handle<A>
where
A: Asset,
F: Format<A::Data>,
N: Into<String>,
P: Progress,
S: AsRef<str> + Eq + Hash + ?Sized,
String: Borrow<S>,
{
#[cfg(feature = "profiler")]
profile_scope!("load_asset_from");
use crate::progress::Tracker;
let name = name.into();
let source = source.as_ref();
let format_name = format.name();
let source_name = match source {
"" => "[default source]",
other => other,
};
let handle = storage.allocate();
debug!(
"{:?}: Loading asset {:?} with format {:?} from source {:?} (handle id: {:?})",
A::NAME,
name,
format_name,
source_name,
handle,
);
progress.add_assets(1);
let tracker = progress.create_tracker();
let source = self.source(source);
let handle_clone = handle.clone();
let processed = storage.processed.clone();
let hot_reload = if self.hot_reload {
Some(objekt::clone_box(&format) as Box<dyn Format<A::Data>>)
} else {
None
};
let cl = move || {
#[cfg(feature = "profiler")]
profile_scope!("load_asset_from_worker");
let data = format
.import(name.clone(), source, hot_reload)
.with_context(|_| Error::Format(format_name));
let tracker = Box::new(tracker) as Box<dyn Tracker>;
processed.push(Processed::NewAsset {
data,
handle,
name,
tracker,
});
};
self.pool.spawn(cl);
handle_clone
}
pub fn load_from_data<A, P>(
&self,
data: A::Data,
mut progress: P,
storage: &AssetStorage<A>,
) -> Handle<A>
where
A: Asset,
P: Progress,
{
progress.add_assets(1);
let tracker = progress.create_tracker();
let tracker = Box::new(tracker);
let handle = storage.allocate();
storage.processed.push(Processed::NewAsset {
data: Ok(FormatValue::data(data)),
handle: handle.clone(),
name: "<Data>".into(),
tracker,
});
handle
}
pub fn load_from_data_async<A, P, F>(
&self,
data: F,
mut progress: P,
storage: &AssetStorage<A>,
) -> Handle<A>
where
A: Asset,
P: Progress,
F: FnOnce() -> A::Data + Send + Sync + 'static,
{
progress.add_assets(1);
let tracker = progress.create_tracker();
let tracker = Box::new(tracker);
let handle = storage.allocate();
let processed = storage.processed.clone();
self.pool.spawn({
let handle = handle.clone();
move || {
processed.push(Processed::NewAsset {
data: Ok(FormatValue::data(data())),
handle: handle.clone(),
name: "<Data>".into(),
tracker,
});
}
});
handle
}
fn source(&self, source: &str) -> Arc<dyn Source> {
self.sources
.get(source)
.expect("No such source. Maybe you forgot to add it with `Loader::add_source`?")
.clone()
}
}