Created
September 3, 2016 22:26
-
-
Save anonymous/176941be70ac1747c557ba68c47b56eb to your computer and use it in GitHub Desktop.
Shared via Rust Playground
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![feature(raw)] | |
use std::collections::HashMap; | |
use std::ops::Deref; | |
// Optional traits, could also be removed | |
trait Asset: std::any::Any {} | |
trait AssetSource<T: Asset>: std::any::Any {} | |
// Set of traits needed to be implemented for an asset loader | |
// Each asset loader is a combination of `Source` and `Asset` type | |
trait AssetSubLoader<T: Asset, U: AssetSource<T>>: std::any::Any + AssetLoader<T> { | |
fn from_raw(&self, data: &[u8]) -> U; | |
fn from_data(&self, data: &U) -> T; | |
} | |
// These can be usually derived directly if from the AssetSubLoader trait | |
// A macro to automatically implement these might be useful | |
trait AssetLoader<T: Asset>: std::any::Any + AssetUntypedLoader { | |
fn load_asset(&self, data: &[u8]) -> T; | |
} | |
trait AssetUntypedLoader { | |
fn load_asset_untyped(&self, data: &[u8]) -> Box<Asset>; | |
} | |
// HACK: need to store multiple vtables | |
struct AssetLoaderStorage { | |
loader_vtable: *mut (), | |
sub_loader_vtable: *mut (), | |
untyped_loader_vtable: *mut (), | |
data: *mut (), | |
} | |
impl AssetLoaderStorage { | |
fn as_loader<T: Asset>(&self) -> &AssetLoader<T> { | |
unsafe { ::std::mem::transmute( | |
::std::raw::TraitObject { | |
data: self.data, | |
vtable: self.loader_vtable, | |
} | |
)} | |
} | |
fn as_sub_loader<T: Asset, U: AssetSource<T>>(&self) -> &AssetSubLoader<T, U> { | |
unsafe { ::std::mem::transmute( | |
::std::raw::TraitObject { | |
data: self.data, | |
vtable: self.sub_loader_vtable, | |
} | |
)} | |
} | |
fn as_untyped_loader(&self) -> &AssetUntypedLoader { | |
unsafe { ::std::mem::transmute( | |
::std::raw::TraitObject { | |
data: self.data, | |
vtable: self.untyped_loader_vtable, | |
} | |
)} | |
} | |
} | |
// Central asset manager, storing all assets and loaders | |
// Possible pipeline: | |
// Asset request: AssetStores -> (raw data) -> [AssetSubLoader(raw) -> (data) -> AssetSubLoader(asset)] -> Asset | |
// | |
// AssetStore: A storage (like a simple package manager), which returns a [u8] bit stream for a specific asset request | |
// Possible storages would be a file archive(zip), directory or http-server | |
// | |
// It should be possible to run the different stages parallel and asynchronous. | |
// If the corresponding loader and some asset stores are registered to the asset manager, an assert can be loaded directly | |
// by an identifier(e.g. file extension) | |
struct AssetManager { | |
loader_ids: HashMap<String, std::any::TypeId>, | |
sub_loaders: HashMap<std::any::TypeId, Box<std::any::Any>>, | |
loaders_vtable: HashMap<std::any::TypeId, AssetLoaderStorage>, | |
} | |
impl AssetManager { | |
fn new() -> AssetManager { | |
AssetManager { | |
loader_ids: HashMap::new(), | |
sub_loaders: HashMap::new(), | |
loaders_vtable: HashMap::new(), | |
} | |
} | |
// Register an asset loader for a specific <Asset, Data> combination | |
fn register_loader<T: Asset, U: AssetSource<T>, K: AssetSubLoader<T, U>>(&mut self, file_id: &str, loader: K) { | |
let sub_loader_id = std::any::TypeId::of::<AssetSubLoader<T, U>>(); | |
self.loader_ids.insert(file_id.to_string(), sub_loader_id); | |
self.sub_loaders.insert(sub_loader_id, Box::new(loader)); | |
let loader_vtable = { | |
let loader_vtable = { | |
let s: &K = unsafe { ::std::mem::uninitialized() }; | |
let t: &AssetLoader<T> = s; | |
let r: ::std::raw::TraitObject = unsafe { ::std::mem::transmute(t) }; | |
r.vtable | |
}; | |
let sub_loader_vtable = { | |
let s: &K = unsafe { ::std::mem::uninitialized() }; | |
let t: &AssetSubLoader<T, U> = s; | |
let r: ::std::raw::TraitObject = unsafe { ::std::mem::transmute(t) }; | |
r.vtable | |
}; | |
let untyped_loader_vtable = { | |
let s: &K = unsafe { ::std::mem::uninitialized() }; | |
let t: &AssetUntypedLoader = s; | |
let r: ::std::raw::TraitObject = unsafe { ::std::mem::transmute(t) }; | |
r.vtable | |
}; | |
AssetLoaderStorage { | |
loader_vtable: loader_vtable, | |
sub_loader_vtable: sub_loader_vtable, | |
untyped_loader_vtable: untyped_loader_vtable, | |
data: self.sub_loaders.get(&sub_loader_id).unwrap().deref() as *const _ as *mut _, | |
} | |
}; | |
self.loaders_vtable.insert(sub_loader_id, loader_vtable); | |
} | |
fn load_asset_from_data<T: Asset, U: AssetSource<T>>(&mut self, data: &U) -> T { | |
self.loaders_vtable.get(&std::any::TypeId::of::<AssetSubLoader<T, U>>()) | |
.expect("Unregistered subloader") | |
.as_sub_loader::<T, U>() | |
.from_data(data) | |
} | |
fn load_asset<T: Asset>(&self, file_id: &str, raw: &[u8]) -> T { | |
self.loaders_vtable | |
.get(self.loader_ids.get(file_id) | |
.expect("Unregistered subloader")) | |
.expect("Unregistered subloader") | |
.as_loader::<T>() | |
.load_asset(raw) | |
} | |
// Preload assets, which can be accessed later | |
// E.g. loaded from a larger archive in real apps | |
fn preload_asset(&mut self, file_id: &str, raw: &[u8]) { | |
self.loaders_vtable | |
.get(self.loader_ids.get(file_id) | |
.expect("Unregistered subloader")) | |
.expect("Unregistered subloader") | |
.as_untyped_loader() | |
.load_asset_untyped(raw); | |
} | |
} | |
// Example: | |
struct MeshLoader; | |
impl Asset for u64 {} | |
impl AssetSource<u64> for String {} | |
impl AssetSubLoader<u64, String> for MeshLoader { | |
fn from_raw(&self, data: &[u8]) -> String { | |
match data[0] { | |
0 => "0".into(), | |
_ => "Invalid".into(), | |
} | |
} | |
fn from_data(&self, _data: &String) -> u64 { | |
1254 | |
} | |
} | |
impl AssetLoader<u64> for MeshLoader { | |
fn load_asset(&self, data: &[u8]) -> u64 { | |
let data = self.from_raw(data); | |
self.from_data(&data); | |
10000 // Note: Non-standard way, mainly for debug | |
} | |
} | |
impl AssetUntypedLoader for MeshLoader { | |
fn load_asset_untyped(&self, data: &[u8]) -> Box<Asset> { | |
let data = self.from_raw(data); | |
Box::new(self.from_data(&data)) | |
} | |
} | |
fn main() { | |
let mut asset_manager = AssetManager::new(); | |
asset_manager.register_loader::<u64, String, MeshLoader>("obj", MeshLoader); | |
println!("{:?}", asset_manager.load_asset_from_data::<u64, String>(&"hello".into())); | |
println!("{:?}", asset_manager.load_asset::<u64>("obj", &[0; 5])); | |
println!("{:?}", asset_manager.preload_asset("obj", &[0; 5])); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment