resource: implement waiting for resource dependencies to be loaded
This commit is contained in:
parent
4a285e5866
commit
aa3a4a17d7
|
@ -1849,10 +1849,12 @@ dependencies = [
|
|||
"instant",
|
||||
"lyra-ecs",
|
||||
"lyra-math",
|
||||
"lyra-reflect",
|
||||
"mime",
|
||||
"notify",
|
||||
"notify-debouncer-full",
|
||||
"percent-encoding",
|
||||
"rand 0.8.5",
|
||||
"thiserror",
|
||||
"tracing",
|
||||
"uuid",
|
||||
|
|
|
@ -110,6 +110,7 @@ async fn main() {
|
|||
let sponza_model = resman.request::<Gltf>("assets/sponza/Sponza.gltf").unwrap();
|
||||
drop(resman);
|
||||
|
||||
sponza_model.wait_recurse_dependencies_load();
|
||||
let sponza_scene = &sponza_model.data_ref()
|
||||
.unwrap().scenes[0];
|
||||
|
||||
|
|
|
@ -441,9 +441,9 @@ fn gen_enum_variant_type(enum_id: &proc_macro2::Ident, data: &DataEnum) -> proc_
|
|||
let vty = VariantType::from(var);
|
||||
|
||||
match vty {
|
||||
VariantType::Struct => quote! { #variant_ident => EnumType::Struct },
|
||||
VariantType::Tuple => quote! { #variant_ident => EnumType::Tuple },
|
||||
VariantType::Unit => quote! { #variant_ident => EnumType::Unit },
|
||||
VariantType::Struct => quote! { #variant_ident => lyra_engine::reflect::EnumType::Struct },
|
||||
VariantType::Tuple => quote! { #variant_ident => lyra_engine::reflect::EnumType::Tuple },
|
||||
VariantType::Unit => quote! { #variant_ident => lyra_engine::reflect::EnumType::Unit },
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
lyra-ecs = { path = "../lyra-ecs", features = [ "math" ] }
|
||||
lyra-reflect = { path = "../lyra-reflect" }
|
||||
lyra-math = { path = "../lyra-math" }
|
||||
anyhow = "1.0.75"
|
||||
base64 = "0.21.4"
|
||||
|
@ -26,3 +27,6 @@ tracing = "0.1.37"
|
|||
uuid = { version = "1.4.1", features = ["v4"] }
|
||||
instant = "0.1"
|
||||
async-std = "1.12.0"
|
||||
|
||||
[dev-dependencies]
|
||||
rand = "0.8.5"
|
|
@ -0,0 +1,109 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use crate::{loader::LoaderError, ResourceState, UntypedResHandle};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum DependencyState {
|
||||
Loading,
|
||||
Error {
|
||||
/// The resource that had the error.
|
||||
handle: UntypedResHandle,
|
||||
/// The error that the resource ran into when loading.
|
||||
error: Arc<LoaderError>,
|
||||
},
|
||||
Ready,
|
||||
}
|
||||
|
||||
impl DependencyState {
|
||||
/// Creates a DependencyState from a resource by retrieving its state. Does not include
|
||||
/// the states of the dependencies.
|
||||
pub fn shallow_from_res(handle: &UntypedResHandle) -> DependencyState {
|
||||
let res = handle.read();
|
||||
match &res.state {
|
||||
ResourceState::Loading => DependencyState::Loading,
|
||||
ResourceState::Error(er) => DependencyState::Error {
|
||||
handle: handle.clone(),
|
||||
error: er.clone(),
|
||||
},
|
||||
ResourceState::Ready(_) => DependencyState::Ready,
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve the state of the handle and its dependencies, does not recursively retrieve.
|
||||
pub fn from_res(handle: &UntypedResHandle) -> DependencyState {
|
||||
let res = handle.read();
|
||||
match &res.state {
|
||||
ResourceState::Loading => DependencyState::Loading,
|
||||
ResourceState::Error(er) => DependencyState::Error {
|
||||
handle: handle.clone(),
|
||||
error: er.clone(),
|
||||
},
|
||||
ResourceState::Ready(res) => {
|
||||
let mut lowest_state = DependencyState::Ready;
|
||||
|
||||
for dep in res.dependencies() {
|
||||
let state = DependencyState::shallow_from_res(&dep);
|
||||
|
||||
// try to find the "lowest" dependency. Meaning the state of a dependency
|
||||
// that would stop the parent from being ready.
|
||||
if state.is_loading() {
|
||||
lowest_state = state;
|
||||
break;
|
||||
} else if state.is_error() {
|
||||
lowest_state = state;
|
||||
break;
|
||||
}
|
||||
|
||||
// anything else would be loaded, so no need to update `lowest_state`
|
||||
}
|
||||
|
||||
lowest_state
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve the state of the handle and its dependencies, does not recursively retrieve.
|
||||
pub fn from_res_recurse(handle: &UntypedResHandle) -> DependencyState {
|
||||
let res = handle.read();
|
||||
match &res.state {
|
||||
ResourceState::Loading => DependencyState::Loading,
|
||||
ResourceState::Error(er) => DependencyState::Error {
|
||||
handle: handle.clone(),
|
||||
error: er.clone(),
|
||||
},
|
||||
ResourceState::Ready(res) => {
|
||||
let mut lowest_state = DependencyState::Ready;
|
||||
|
||||
for dep in res.dependencies() {
|
||||
let state = DependencyState::from_res_recurse(&dep);
|
||||
|
||||
// try to find the "lowest" dependency. Meaning the state of a dependency
|
||||
// that would stop the parent from being ready.
|
||||
if state.is_loading() {
|
||||
lowest_state = state;
|
||||
break;
|
||||
} else if state.is_error() {
|
||||
lowest_state = state;
|
||||
break;
|
||||
}
|
||||
|
||||
// anything else would be loaded, so no need to update `lowest_state`
|
||||
}
|
||||
|
||||
lowest_state
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_ready(&self) -> bool {
|
||||
matches!(self, DependencyState::Ready)
|
||||
}
|
||||
|
||||
pub fn is_error(&self) -> bool {
|
||||
matches!(self, DependencyState::Error { handle: _, error: _ })
|
||||
}
|
||||
|
||||
pub fn is_loading(&self) -> bool {
|
||||
matches!(self, DependencyState::Loading)
|
||||
}
|
||||
}
|
|
@ -1,12 +1,14 @@
|
|||
use std::{collections::hash_map::DefaultHasher, hash::{Hash, Hasher}};
|
||||
|
||||
use gltf::texture::{MagFilter, MinFilter};
|
||||
use lyra_reflect::Reflect;
|
||||
use crate::{lyra_engine, optionally_add_to_dep, ResourceData};
|
||||
|
||||
use crate::{util, FilterMode, Image, ResHandle, Texture, TextureSampler, WrappingMode};
|
||||
use super::loader::GltfLoadContext;
|
||||
|
||||
/// PBR metallic roughness
|
||||
#[derive(Clone, Debug, Default)]
|
||||
#[derive(Clone, Debug, Default, Reflect)]
|
||||
pub struct PbrRoughness {
|
||||
/// The rgba base color of the PBR material
|
||||
pub base_color: [f32; 4],
|
||||
|
@ -54,7 +56,7 @@ impl From<gltf::material::PbrSpecularGlossiness<'_>> for PbrGlossiness {
|
|||
|
||||
/// The alpha rendering mode of a material.
|
||||
/// This is essentially a re-export of gltf::material::AlphaMode
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Debug, Default)]
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Debug, Default, Reflect)]
|
||||
pub enum AlphaMode {
|
||||
/// The alpha value is ignored and the rendered output is fully opaque.
|
||||
#[default]
|
||||
|
@ -181,6 +183,33 @@ pub struct Material {
|
|||
pub specular: Option<Specular>,
|
||||
}
|
||||
|
||||
impl ResourceData for Material {
|
||||
|
||||
|
||||
fn dependencies(&self) -> Vec<crate::UntypedResHandle> {
|
||||
let mut deps = vec![];
|
||||
|
||||
optionally_add_to_dep(&mut deps, &self.metallic_roughness_texture);
|
||||
optionally_add_to_dep(&mut deps, &self.base_color_texture);
|
||||
optionally_add_to_dep(&mut deps, &self.metallic_roughness_texture);
|
||||
|
||||
if let Some(spec) = &self.specular {
|
||||
optionally_add_to_dep(&mut deps, &spec.texture);
|
||||
optionally_add_to_dep(&mut deps, &spec.color_texture);
|
||||
}
|
||||
|
||||
deps
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl Material {
|
||||
/// Get a uri's identifier
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use crate::{lyra_engine, ResHandle};
|
||||
use lyra_reflect::Reflect;
|
||||
use crate::lyra_engine;
|
||||
|
||||
use crate::ResHandle;
|
||||
use crate::ResourceData;
|
||||
|
||||
use super::Material;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[derive(Clone, Debug, PartialEq, Reflect)]
|
||||
pub enum MeshIndices {
|
||||
//U8(Vec<u8>),
|
||||
U16(Vec<u16>),
|
||||
|
@ -68,7 +72,7 @@ impl VertexAttributeData {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Reflect)]
|
||||
pub enum MeshVertexAttribute {
|
||||
Position,
|
||||
Normals,
|
||||
|
@ -90,6 +94,23 @@ pub struct Mesh {
|
|||
pub material: Option<ResHandle<Material>>,
|
||||
}
|
||||
|
||||
impl ResourceData for Mesh {
|
||||
fn dependencies(&self) -> Vec<crate::UntypedResHandle> {
|
||||
match &self.material {
|
||||
Some(m) => vec![m.untyped_clone()],
|
||||
None => vec![]
|
||||
}
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Mesh {
|
||||
pub fn add_attribute(&mut self, attribute: MeshVertexAttribute, data: VertexAttributeData) {
|
||||
self.attributes.insert(attribute, data);
|
||||
|
|
|
@ -3,6 +3,7 @@ pub use loader::*;
|
|||
|
||||
pub mod material;
|
||||
use lyra_math::Transform;
|
||||
use crate::ResourceData;
|
||||
pub use material::*;
|
||||
|
||||
pub mod mesh;
|
||||
|
@ -21,6 +22,36 @@ pub struct Gltf {
|
|||
pub meshes: Vec<ResHandle<Mesh>>,
|
||||
}
|
||||
|
||||
impl ResourceData for Gltf {
|
||||
fn dependencies(&self) -> Vec<crate::UntypedResHandle> {
|
||||
let mut deps = vec![];
|
||||
|
||||
for scene in self.scenes.iter() {
|
||||
deps.push(scene.untyped_clone())
|
||||
}
|
||||
|
||||
for mat in self.materials.iter() {
|
||||
deps.push(mat.untyped_clone())
|
||||
}
|
||||
|
||||
for mesh in self.meshes.iter() {
|
||||
deps.push(mesh.untyped_clone())
|
||||
}
|
||||
|
||||
deps
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
impl Gltf {
|
||||
/// Collects all Gltf meshes and gets their world Transform.
|
||||
pub fn collect_world_meshes(&self) -> Vec<(ResHandle<Mesh>, Transform)> {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use lyra_math::Transform;
|
||||
use crate::{optionally_add_to_dep, ResourceData, UntypedResHandle};
|
||||
|
||||
use super::Mesh;
|
||||
use crate::ResHandle;
|
||||
|
@ -12,12 +13,51 @@ pub struct GltfNode {
|
|||
pub children: Vec<GltfNode>,
|
||||
}
|
||||
|
||||
impl ResourceData for GltfNode {
|
||||
fn dependencies(&self) -> Vec<crate::UntypedResHandle> {
|
||||
let mut deps: Vec<UntypedResHandle> = self.children.iter()
|
||||
.flat_map(|c| c.mesh.as_ref().map(|h| h.untyped_clone()))
|
||||
.collect();
|
||||
|
||||
optionally_add_to_dep(&mut deps, &self.mesh);
|
||||
|
||||
deps
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// A Scene in a Gltf file
|
||||
#[derive(Clone)]
|
||||
pub struct GltfScene {
|
||||
pub nodes: Vec<GltfNode>,
|
||||
}
|
||||
|
||||
impl ResourceData for GltfScene {
|
||||
fn dependencies(&self) -> Vec<crate::UntypedResHandle> {
|
||||
let deps: Vec<UntypedResHandle> = self.nodes.iter()
|
||||
.map(|n| n.dependencies())
|
||||
.flatten()
|
||||
.collect();
|
||||
|
||||
deps
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl GltfScene {
|
||||
fn collect_node(&self, parent_node: &GltfNode, node: &GltfNode) -> Vec<(ResHandle<Mesh>, Transform)> {
|
||||
let mut v = vec![];
|
||||
|
|
|
@ -7,6 +7,9 @@ pub use resource::*;
|
|||
mod texture;
|
||||
pub use texture::*;
|
||||
|
||||
mod dep_state;
|
||||
pub use dep_state::*;
|
||||
|
||||
pub mod loader;
|
||||
|
||||
pub mod gltf;
|
||||
|
@ -24,4 +27,8 @@ pub(crate) mod lyra_engine {
|
|||
pub(crate) mod ecs {
|
||||
pub use lyra_ecs::*;
|
||||
}
|
||||
|
||||
pub(crate) mod reflect {
|
||||
pub use lyra_reflect::*;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ use std::{ffi::OsStr, path::Path, sync::Arc};
|
|||
|
||||
use async_std::io::ReadExt;
|
||||
use image::ImageError;
|
||||
use tracing::trace;
|
||||
use tracing::debug;
|
||||
|
||||
use crate::{Image, ResHandle, ResourceData, ResourceManager};
|
||||
|
||||
|
@ -87,14 +87,14 @@ impl ResourceLoader for ImageLoader {
|
|||
|
||||
fn load_bytes(&self, _resource_manager: ResourceManager, bytes: Vec<u8>, offset: usize, length: usize) -> PinedBoxLoaderFuture {
|
||||
Box::pin(async move {
|
||||
trace!("Loading {} bytes as an image", length);
|
||||
|
||||
let image = image::load_from_memory(&bytes[offset..(length-offset)])
|
||||
.map_err(|e| match e {
|
||||
ImageError::IoError(e) => LoaderError::IoError(e),
|
||||
_ => LoaderError::DecodingError(e.into()),
|
||||
})?;
|
||||
let image = Image::from(image);
|
||||
debug!("Finished loading image ({} bytes)", length);
|
||||
|
||||
Ok(Box::new(image) as Box<dyn ResourceData>)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,40 +1,74 @@
|
|||
use std::{any::{Any, TypeId}, marker::PhantomData, sync::{Arc, RwLock}};
|
||||
use std::{any::{Any, TypeId}, marker::PhantomData, ops::{Deref, DerefMut}, sync::{Arc, Condvar, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard}};
|
||||
|
||||
use lyra_ecs::Component;
|
||||
use crate::{loader::LoaderError, lyra_engine};
|
||||
use crate::{loader::LoaderError, lyra_engine, DependencyState};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::ResourceStorage;
|
||||
|
||||
pub fn optionally_add_to_dep<R: ResourceData>(deps: &mut Vec<UntypedResHandle>, handle: &Option<ResHandle<R>>) {
|
||||
if let Some(h) = handle {
|
||||
deps.push(h.untyped_clone());
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait that that each resource type should implement.
|
||||
pub trait ResourceData: Send + Sync + Any + 'static {
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any;
|
||||
fn type_id(&self) -> TypeId;
|
||||
}
|
||||
|
||||
impl<T: Send + Sync + Any + 'static> ResourceData for T {
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
/// Collect the dependencies of the Resource.
|
||||
fn dependencies(&self) -> Vec<UntypedResHandle>;
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
/// Recursively collect the dependencies of the Resource.
|
||||
///
|
||||
/// If a dependency has a child dependency, it will not show up in this list until its
|
||||
/// parent is loaded.
|
||||
fn recur_dependencies(&self) -> Vec<UntypedResHandle> {
|
||||
let deps = self.dependencies();
|
||||
let mut all_deps = deps.clone();
|
||||
|
||||
fn type_id(&self) -> TypeId {
|
||||
TypeId::of::<T>()
|
||||
for dep in deps.into_iter() {
|
||||
let dep = dep.read();
|
||||
match &dep.state {
|
||||
ResourceState::Ready(data) => {
|
||||
let mut deps_dep = data.dependencies();
|
||||
all_deps.append(&mut deps_dep);
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
all_deps
|
||||
}
|
||||
}
|
||||
|
||||
//impl<T: Send + Sync + Reflect> ResourceData for T { }
|
||||
|
||||
pub enum ResourceState {
|
||||
Loading,
|
||||
Error(Arc<LoaderError>),
|
||||
Ready(Box<dyn ResourceData>),
|
||||
}
|
||||
|
||||
impl ResourceState {
|
||||
/// Returns a boolean indicating if the state of still loading
|
||||
pub fn is_loading(&self) -> bool {
|
||||
matches!(self, ResourceState::Loading)
|
||||
}
|
||||
|
||||
pub fn is_error(&self) -> bool {
|
||||
matches!(self, ResourceState::Error(_))
|
||||
}
|
||||
|
||||
pub fn is_ready(&self) -> bool {
|
||||
matches!(self, ResourceState::Ready(_))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ResourceDataRef<'a, T> {
|
||||
guard: std::sync::RwLockReadGuard<'a, Resource<T>>,
|
||||
guard: std::sync::RwLockReadGuard<'a, UntypedResource>,
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<'a, T: 'static> std::ops::Deref for ResourceDataRef<'a, T> {
|
||||
|
@ -52,13 +86,125 @@ impl<'a, T: 'static> std::ops::Deref for ResourceDataRef<'a, T> {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct Resource<T> {
|
||||
pub struct UntypedResource {
|
||||
pub(crate) version: usize,
|
||||
pub(crate) state: ResourceState,
|
||||
uuid: Uuid,
|
||||
path: Option<String>,
|
||||
pub(crate) is_watched: bool,
|
||||
_marker: PhantomData<T>,
|
||||
/// can be used to wait for the resource to load.
|
||||
pub(crate) condvar: Arc<(Mutex<bool>, Condvar)>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct UntypedResHandle{
|
||||
pub(crate) res: Arc<RwLock<UntypedResource>>,
|
||||
#[allow(dead_code)]
|
||||
tyid: TypeId,
|
||||
}
|
||||
|
||||
impl UntypedResHandle {
|
||||
pub fn new(res: UntypedResource, tyid: TypeId) -> Self {
|
||||
Self {
|
||||
res: Arc::new(RwLock::new(res)),
|
||||
tyid
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read(&self) -> RwLockReadGuard<UntypedResource> {
|
||||
self.res.read().unwrap()
|
||||
}
|
||||
|
||||
pub fn write(&self) -> RwLockWriteGuard<UntypedResource> {
|
||||
self.res.write().unwrap()
|
||||
}
|
||||
|
||||
/// Returns a boolean indicating if this resource's path is being watched.
|
||||
pub fn is_watched(&self) -> bool {
|
||||
let d = self.read();
|
||||
d.is_watched
|
||||
}
|
||||
|
||||
/// Returns a boolean indicating if this resource is loaded
|
||||
pub fn is_loaded(&self) -> bool {
|
||||
let d = self.read();
|
||||
matches!(d.state, ResourceState::Ready(_))
|
||||
}
|
||||
|
||||
/// Returns the uuid of the resource.
|
||||
pub fn uuid(&self) -> Uuid {
|
||||
let d = self.read();
|
||||
d.uuid
|
||||
}
|
||||
|
||||
pub fn path(&self) -> Option<String> {
|
||||
let d = self.read();
|
||||
d.path.clone()
|
||||
}
|
||||
|
||||
/// Retrieves the current version of the resource. This gets incremented when the resource
|
||||
/// is reloaded.
|
||||
pub fn version(&self) -> usize {
|
||||
let d = self.read();
|
||||
d.version
|
||||
}
|
||||
|
||||
/// Wait for the resource to be loaded, not including its dependencies
|
||||
/// (see[`UntypedResHandle::wait_recurse_dependencies_load`]).
|
||||
///
|
||||
/// This blocks the thread without consuming CPU time; its backed by a
|
||||
/// [`Condvar`](std::sync::Condvar).
|
||||
pub fn wait_for_load(&self) {
|
||||
let d = self.read();
|
||||
|
||||
if matches!(d.state, ResourceState::Ready(_)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let cv = d.condvar.clone();
|
||||
drop(d);
|
||||
|
||||
let l = cv.0.lock().unwrap();
|
||||
let _unused = cv.1.wait(l).unwrap();
|
||||
}
|
||||
|
||||
/// Wait for the entire resource, including its dependencies to be loaded.
|
||||
///
|
||||
/// This blocks the thread without consuming CPU time; its backed by a
|
||||
/// [`Condvar`](std::sync::Condvar).
|
||||
pub fn wait_recurse_dependencies_load(&self) {
|
||||
self.wait_for_load();
|
||||
|
||||
let res = self.read();
|
||||
match &res.state {
|
||||
ResourceState::Ready(data) => {
|
||||
// `recur_dependencies` wont return resources that are not loaded in yet
|
||||
// if we did not check if the resource was finished loading, we could miss
|
||||
// waiting for some resources and finish early.
|
||||
while self.recurse_dependency_state().is_loading() {
|
||||
for dep in data.recur_dependencies() {
|
||||
dep.wait_for_load();
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => unreachable!() // the self.wait_for_load ensures that the state is ready
|
||||
}
|
||||
}
|
||||
|
||||
/// Recursively get the state of the dependencies.
|
||||
///
|
||||
/// This doesn't return any resource data, it can be used to check if the resource and its
|
||||
/// dependencies are loaded.
|
||||
pub fn recurse_dependency_state(&self) -> DependencyState {
|
||||
DependencyState::from_res_recurse(self)
|
||||
}
|
||||
|
||||
pub fn as_typed<T: ResourceData>(&self) -> ResHandle<T> {
|
||||
ResHandle {
|
||||
handle: self.clone(),
|
||||
_marker: PhantomData::<T>,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A handle to a resource.
|
||||
|
@ -69,89 +215,69 @@ pub struct Resource<T> {
|
|||
/// and has a write lock on the data. This means that most of the time, it is not blocking.
|
||||
#[derive(Component)]
|
||||
pub struct ResHandle<T: 'static> {
|
||||
pub(crate) data: Arc<RwLock<Resource<T>>>,
|
||||
pub(crate) handle: UntypedResHandle,
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> Clone for ResHandle<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self { data: self.data.clone() }
|
||||
Self {
|
||||
handle: self.handle.clone(),
|
||||
_marker: PhantomData::<T>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for ResHandle<T> {
|
||||
type Target = UntypedResHandle;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.handle
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for ResHandle<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.handle
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ResourceData> ResHandle<T> {
|
||||
pub fn new_loading(path: Option<&str>) -> Self {
|
||||
let res_version = Resource {
|
||||
let res_version = UntypedResource {
|
||||
version: 0,
|
||||
path: path.map(str::to_string),
|
||||
state: ResourceState::Loading,
|
||||
uuid: Uuid::new_v4(),
|
||||
is_watched: false,
|
||||
_marker: PhantomData::<T>
|
||||
condvar: Arc::new((Mutex::new(false), Condvar::new())),
|
||||
};
|
||||
|
||||
Self {
|
||||
data: Arc::new(RwLock::new(res_version)),
|
||||
handle: UntypedResHandle::new(res_version, TypeId::of::<T>()),
|
||||
_marker: PhantomData::<T>,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create the resource with data, its assumed the state is `Ready`
|
||||
pub fn new_ready(path: Option<&str>, data: T) -> Self {
|
||||
let res_version = Resource {
|
||||
version: 0,
|
||||
path: path.map(str::to_string),
|
||||
state: ResourceState::Ready(Box::new(data)),
|
||||
uuid: Uuid::new_v4(),
|
||||
is_watched: false,
|
||||
_marker: PhantomData::<T>
|
||||
};
|
||||
|
||||
Self {
|
||||
data: Arc::new(RwLock::new(res_version)),
|
||||
}
|
||||
let han = Self::new_loading(path);
|
||||
han.set_state(ResourceState::Ready(Box::new(data)));
|
||||
han
|
||||
}
|
||||
|
||||
/// Returns a boolean indicating if this resource's path is being watched.
|
||||
pub fn is_watched(&self) -> bool {
|
||||
let d = self.data.read().expect("Resource mutex was poisoned!");
|
||||
d.is_watched
|
||||
}
|
||||
|
||||
/// Returns a boolean indicating if this resource is loaded
|
||||
pub fn is_loaded(&self) -> bool {
|
||||
let d = self.data.read().expect("Resource mutex was poisoned!");
|
||||
matches!(d.state, ResourceState::Ready(_))
|
||||
}
|
||||
|
||||
/// Returns the current state of the resource.
|
||||
/* pub fn state(&self) -> &ResourceState {
|
||||
let d = self.data.read().expect("Resource mutex was poisoned!");
|
||||
&d.state
|
||||
} */
|
||||
|
||||
/// Returns the uuid of the resource.
|
||||
pub fn uuid(&self) -> Uuid {
|
||||
let d = self.data.read().expect("Resource mutex was poisoned!");
|
||||
d.uuid
|
||||
}
|
||||
|
||||
pub fn path(&self) -> Option<String> {
|
||||
let d = self.data.read().expect("Resource mutex was poisoned!");
|
||||
d.path.clone()
|
||||
}
|
||||
|
||||
/// Retrieves the current version of the resource. This gets incremented when the resource
|
||||
/// is reloaded.
|
||||
pub fn version(&self) -> usize {
|
||||
let d = self.data.read().expect("Resource mutex was poisoned!");
|
||||
d.version
|
||||
/// Retrieve an untyped clone of the handle
|
||||
pub fn untyped_clone(&self) -> UntypedResHandle {
|
||||
self.handle.clone()
|
||||
}
|
||||
|
||||
/// Get a reference to the data in the resource
|
||||
pub fn data_ref<'a>(&'a self) -> Option<ResourceDataRef<'a, T>> {
|
||||
if self.is_loaded() {
|
||||
let d = self.data.read().expect("Resource mutex was poisoned!");
|
||||
let d = self.handle.read();
|
||||
Some(ResourceDataRef {
|
||||
guard: d
|
||||
guard: d,
|
||||
_marker: PhantomData::<T>
|
||||
})
|
||||
} else {
|
||||
None
|
||||
|
@ -177,34 +303,185 @@ impl<T: Send + Sync + 'static> ResourceStorage for ResHandle<T> {
|
|||
}
|
||||
|
||||
fn set_watched(&self, watched: bool) {
|
||||
let mut w = self.data.write().unwrap();
|
||||
w.is_watched = watched;
|
||||
let mut d = self.handle.write();
|
||||
d.is_watched = watched;
|
||||
}
|
||||
|
||||
fn version(&self) -> usize {
|
||||
self.version()
|
||||
self.handle.version()
|
||||
}
|
||||
|
||||
fn uuid(&self) -> Uuid {
|
||||
self.uuid()
|
||||
self.handle.uuid()
|
||||
}
|
||||
|
||||
fn path(&self) -> Option<String> {
|
||||
self.path()
|
||||
self.handle.path()
|
||||
}
|
||||
|
||||
fn is_watched(&self) -> bool {
|
||||
self.is_watched()
|
||||
self.handle.is_watched()
|
||||
}
|
||||
|
||||
fn is_loaded(&self) -> bool {
|
||||
self.is_loaded()
|
||||
self.handle.is_loaded()
|
||||
}
|
||||
|
||||
fn set_state(&self, new: ResourceState) {
|
||||
let mut d = self.data.write().expect("Resource mutex was poisoned!");
|
||||
let mut d = self.handle.write();
|
||||
d.state = new;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{path::PathBuf, str::FromStr, sync::Arc};
|
||||
|
||||
use async_std::task;
|
||||
use instant::Duration;
|
||||
use rand::Rng;
|
||||
|
||||
use crate::{loader::ResourceLoader, ResHandle, ResourceData, ResourceManager};
|
||||
|
||||
#[allow(dead_code)]
|
||||
struct SimpleDepend {
|
||||
file_name: String,
|
||||
ext: String,
|
||||
}
|
||||
|
||||
impl ResourceData for SimpleDepend {
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn dependencies(&self) -> Vec<crate::UntypedResHandle> {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct SlowSimpleDependLoader;
|
||||
|
||||
impl ResourceLoader for SlowSimpleDependLoader {
|
||||
fn extensions(&self) -> &[&str] {
|
||||
&["txt", "buf"]
|
||||
}
|
||||
|
||||
fn mime_types(&self) -> &[&str] {
|
||||
&[]
|
||||
}
|
||||
|
||||
fn load(&self, _: crate::ResourceManager, path: &str) -> crate::loader::PinedBoxLoaderFuture {
|
||||
let path = path.to_string();
|
||||
Box::pin(async move {
|
||||
let path = PathBuf::from_str(&path).unwrap();
|
||||
|
||||
let file_name = path.file_name()
|
||||
.and_then(|os| os.to_str())
|
||||
.unwrap();
|
||||
let path_ext = path.extension()
|
||||
.and_then(|os| os.to_str())
|
||||
.unwrap();
|
||||
|
||||
let res = rand::thread_rng().gen_range(500..1000);
|
||||
|
||||
task::sleep(Duration::from_millis(res)).await;
|
||||
|
||||
let simple = SimpleDepend {
|
||||
file_name: file_name.to_string(),
|
||||
ext: path_ext.to_string(),
|
||||
};
|
||||
|
||||
Ok(Box::new(simple) as Box<dyn ResourceData>)
|
||||
})
|
||||
}
|
||||
|
||||
fn load_bytes(&self, _: crate::ResourceManager, _: Vec<u8>, _: usize, _: usize) -> crate::loader::PinedBoxLoaderFuture {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn create_erased_handle(&self) -> std::sync::Arc<dyn crate::ResourceStorage> {
|
||||
Arc::from(ResHandle::<SimpleDepend>::new_loading(None))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct SimpleResource {
|
||||
depend_a: ResHandle<SimpleDepend>,
|
||||
}
|
||||
|
||||
impl ResourceData for SimpleResource {
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn dependencies(&self) -> Vec<crate::UntypedResHandle> {
|
||||
vec![self.depend_a.untyped_clone()]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct SlowSimpleResourceLoader;
|
||||
|
||||
impl ResourceLoader for SlowSimpleResourceLoader {
|
||||
fn extensions(&self) -> &[&str] {
|
||||
&["res", "large"]
|
||||
}
|
||||
|
||||
fn mime_types(&self) -> &[&str] {
|
||||
&[]
|
||||
}
|
||||
|
||||
fn load(&self, res_man: crate::ResourceManager, _: &str) -> crate::loader::PinedBoxLoaderFuture {
|
||||
Box::pin(async move {
|
||||
let res = rand::thread_rng().gen_range(500..1000);
|
||||
|
||||
task::sleep(Duration::from_millis(res)).await;
|
||||
|
||||
// load dummy dependency that will take a bit
|
||||
let depend_path = "depend.txt";
|
||||
let depend_han = res_man.request::<SimpleDepend>(depend_path).unwrap();
|
||||
|
||||
let simple = SimpleResource {
|
||||
depend_a: depend_han,
|
||||
};
|
||||
|
||||
Ok(Box::new(simple) as Box<dyn ResourceData>)
|
||||
})
|
||||
}
|
||||
|
||||
fn load_bytes(&self, _: crate::ResourceManager, _: Vec<u8>, _: usize, _: usize) -> crate::loader::PinedBoxLoaderFuture {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn create_erased_handle(&self) -> std::sync::Arc<dyn crate::ResourceStorage> {
|
||||
Arc::from(ResHandle::<SimpleResource>::new_loading(None))
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn recursive() {
|
||||
let man = ResourceManager::new();
|
||||
man.register_loader::<SlowSimpleDependLoader>();
|
||||
man.register_loader::<SlowSimpleResourceLoader>();
|
||||
|
||||
let res = man.request::<SimpleResource>("massive_asset.res").unwrap();
|
||||
|
||||
let state = res.recurse_dependency_state();
|
||||
assert!(state.is_loading());
|
||||
|
||||
// this will take a bit
|
||||
res.wait_recurse_dependencies_load();
|
||||
|
||||
let state = res.recurse_dependency_state();
|
||||
assert!(!state.is_loading());
|
||||
}
|
||||
}
|
|
@ -7,7 +7,7 @@ use notify_debouncer_full::{DebouncedEvent, FileIdMap};
|
|||
use thiserror::Error;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{gltf::ModelLoader, loader::{ImageLoader, LoaderError, ResourceLoader}, resource::ResHandle, ResourceState};
|
||||
use crate::{gltf::ModelLoader, loader::{ImageLoader, LoaderError, ResourceLoader}, resource::ResHandle, ResourceData, ResourceState};
|
||||
|
||||
/// A trait for type erased storage of a resource.
|
||||
/// Implemented for [`ResHandle<T>`]
|
||||
|
@ -61,6 +61,7 @@ pub struct ResourceWatcher {
|
|||
/// The state of the ResourceManager
|
||||
pub struct ResourceManagerState {
|
||||
resources: HashMap<String, Arc<dyn ResourceStorage>>,
|
||||
uuid_resources: HashMap<Uuid, Arc<dyn ResourceStorage>>,
|
||||
loaders: Vec<Arc<dyn ResourceLoader>>,
|
||||
watchers: HashMap<String, ResourceWatcher>,
|
||||
}
|
||||
|
@ -79,6 +80,7 @@ impl Default for ResourceManager {
|
|||
inner: Arc::new(RwLock::new(
|
||||
ResourceManagerState {
|
||||
resources: HashMap::new(),
|
||||
uuid_resources: HashMap::new(),
|
||||
loaders: vec![ Arc::new(ImageLoader), Arc::new(ModelLoader) ],
|
||||
watchers: HashMap::new(),
|
||||
}
|
||||
|
@ -121,7 +123,7 @@ impl ResourceManager {
|
|||
/// handle to check if the resource is loaded.
|
||||
pub fn request<T>(&self, path: &str) -> Result<ResHandle<T>, RequestError>
|
||||
where
|
||||
T: Send + Sync + Any + 'static
|
||||
T: ResourceData
|
||||
{
|
||||
let mut state = self.state_mut();
|
||||
match state.resources.get(&path.to_string()) {
|
||||
|
@ -146,18 +148,20 @@ impl ResourceManager {
|
|||
task::spawn(async move {
|
||||
match res.await {
|
||||
Ok(data) => {
|
||||
let mut d = thand.data.write().unwrap();
|
||||
let mut d = thand.write();
|
||||
d.state = ResourceState::Ready(data);
|
||||
d.condvar.1.notify_all();
|
||||
}
|
||||
Err(err) => {
|
||||
let mut d = thand.data.write().unwrap();
|
||||
let mut d = thand.write();
|
||||
d.state = ResourceState::Error(Arc::new(err));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let res: Arc<dyn ResourceStorage> = Arc::from(handle.clone());
|
||||
state.resources.insert(path.to_string(), res);
|
||||
state.resources.insert(path.to_string(), res.clone());
|
||||
state.uuid_resources.insert(res.uuid(), res);
|
||||
|
||||
Ok(handle)
|
||||
} else {
|
||||
|
@ -174,13 +178,13 @@ impl ResourceManager {
|
|||
/// let res: Arc<ResHandle<T>> = res.downcast::<ResHandle<T>>().expect("Failure to downcast resource");
|
||||
/// ```
|
||||
pub fn request_raw(&self, path: &str) -> Result<Arc<dyn ResourceStorage>, RequestError> {
|
||||
let inner = self.inner.write().unwrap();
|
||||
match inner.resources.get(&path.to_string()) {
|
||||
let mut state = self.state_mut();
|
||||
match state.resources.get(&path.to_string()) {
|
||||
Some(res) => {
|
||||
Ok(res.clone())
|
||||
},
|
||||
None => {
|
||||
if let Some(loader) = inner.loaders.iter()
|
||||
if let Some(loader) = state.loaders.iter()
|
||||
.find(|l| l.does_support_file(path)) {
|
||||
|
||||
// Load the resource and store it
|
||||
|
@ -202,6 +206,9 @@ impl ResourceManager {
|
|||
}
|
||||
});
|
||||
|
||||
let res: Arc<dyn ResourceStorage> = Arc::from(handle.clone());
|
||||
state.uuid_resources.insert(res.uuid(), res);
|
||||
|
||||
Ok(handle)
|
||||
} else {
|
||||
Err(RequestError::UnsupportedFileExtension(path.to_string()))
|
||||
|
@ -225,7 +232,9 @@ impl ResourceManager {
|
|||
/// stored with [`ResourceManager::request`] to return `Some`.
|
||||
pub fn request_uuid<T: Send + Sync + 'static>(&self, uuid: &Uuid) -> Option<ResHandle<T>> {
|
||||
let state = self.state();
|
||||
match state.resources.get(&uuid.to_string()) {
|
||||
match state.resources.get(&uuid.to_string())
|
||||
.or_else(|| state.uuid_resources.get(&uuid))
|
||||
{
|
||||
Some(res) => {
|
||||
let res = res.clone().as_arc_any();
|
||||
let res: Arc<ResHandle<T>> = res.downcast::<ResHandle<T>>().expect("Failure to downcast resource");
|
||||
|
@ -246,7 +255,7 @@ impl ResourceManager {
|
|||
/// Returns: The `Arc` to the now stored resource
|
||||
pub fn load_bytes<T>(&self, ident: &str, mime_type: &str, bytes: Vec<u8>, offset: usize, length: usize) -> Result<ResHandle<T>, RequestError>
|
||||
where
|
||||
T: Send + Sync + Any + 'static
|
||||
T: ResourceData
|
||||
{
|
||||
let mut state = self.state_mut();
|
||||
if let Some(loader) = state.loaders.iter()
|
||||
|
@ -259,18 +268,19 @@ impl ResourceManager {
|
|||
task::spawn(async move {
|
||||
match res.await {
|
||||
Ok(data) => {
|
||||
let mut d = thand.data.write().unwrap();
|
||||
let mut d = thand.write();
|
||||
d.state = ResourceState::Ready(data);
|
||||
}
|
||||
Err(err) => {
|
||||
let mut d = thand.data.write().unwrap();
|
||||
let mut d = thand.write();
|
||||
d.state = ResourceState::Error(Arc::new(err));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let res: Arc<dyn ResourceStorage> = Arc::from(handle.clone());
|
||||
state.resources.insert(ident.to_string(), res);
|
||||
state.resources.insert(ident.to_string(), res.clone());
|
||||
state.uuid_resources.insert(res.uuid(), res);
|
||||
|
||||
Ok(handle)
|
||||
} else {
|
||||
|
@ -376,12 +386,12 @@ impl ResourceManager {
|
|||
task::spawn(async move {
|
||||
match res.await {
|
||||
Ok(data) => {
|
||||
let mut d = thand.data.write().unwrap();
|
||||
let mut d = thand.write();
|
||||
d.state = ResourceState::Ready(data);
|
||||
d.version += 1;
|
||||
}
|
||||
Err(err) => {
|
||||
let mut d = thand.data.write().unwrap();
|
||||
let mut d = thand.write();
|
||||
d.state = ResourceState::Error(Arc::new(err));
|
||||
}
|
||||
}
|
||||
|
@ -444,7 +454,8 @@ pub(crate) mod tests {
|
|||
let res = man.request::<Image>(&get_image("squiggles.png")).unwrap();
|
||||
assert!(!res.is_loaded());
|
||||
|
||||
busy_wait_resource(&res, 10.0);
|
||||
res.wait_for_load();
|
||||
//busy_wait_resource(&res, 10.0);
|
||||
|
||||
// shouldn't panic because of the loop
|
||||
res.data_ref().unwrap();
|
||||
|
@ -455,10 +466,10 @@ pub(crate) mod tests {
|
|||
fn ensure_single() {
|
||||
let man = ResourceManager::new();
|
||||
let res = man.request::<Texture>(&get_image("squiggles.png")).unwrap();
|
||||
assert_eq!(Arc::strong_count(&res.data), 3);
|
||||
assert_eq!(Arc::strong_count(&res.handle.res), 3);
|
||||
|
||||
let resagain = man.request::<Texture>(&get_image("squiggles.png")).unwrap();
|
||||
assert_eq!(Arc::strong_count(&resagain.data), 4);
|
||||
assert_eq!(Arc::strong_count(&resagain.handle.res), 4);
|
||||
}
|
||||
|
||||
/// Ensures that an error is returned when a file that doesn't exist is requested
|
||||
|
@ -471,7 +482,7 @@ pub(crate) mod tests {
|
|||
// 1 second should be enough to run into an error
|
||||
std::thread::sleep(Duration::from_secs(1));
|
||||
//busy_wait_resource(&res, 10.0);
|
||||
let state = &res.data.read().unwrap().state;
|
||||
let state = &res.read().state;
|
||||
|
||||
assert!(
|
||||
match state {
|
||||
|
|
|
@ -2,20 +2,23 @@ use std::ops::{Deref, DerefMut};
|
|||
|
||||
//pub use gltf::texture::{MagFilter, MinFilter, WrappingMode};
|
||||
use image::DynamicImage;
|
||||
use lyra_reflect::Reflect;
|
||||
use crate::lyra_engine;
|
||||
|
||||
use crate::ResHandle;
|
||||
use crate::ResourceData;
|
||||
|
||||
/// The filter mode of the sampler.
|
||||
///
|
||||
/// This is used for minification, magnification, and mipmap filters
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Reflect)]
|
||||
pub enum FilterMode {
|
||||
Nearest,
|
||||
Linear,
|
||||
}
|
||||
|
||||
/// The wrapping mode of the Texture coordinates
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Reflect)]
|
||||
pub enum WrappingMode {
|
||||
ClampToEdge,
|
||||
MirroredRepeat,
|
||||
|
@ -23,7 +26,7 @@ pub enum WrappingMode {
|
|||
}
|
||||
|
||||
/// The descriptor of the sampler for a Texture.
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Reflect)]
|
||||
pub struct TextureSampler {
|
||||
pub mag_filter: Option<FilterMode>,
|
||||
pub min_filter: Option<FilterMode>,
|
||||
|
@ -33,8 +36,24 @@ pub struct TextureSampler {
|
|||
pub wrap_w: WrappingMode,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Image(DynamicImage);
|
||||
#[derive(Clone, Reflect)]
|
||||
pub struct Image(#[reflect(skip)] DynamicImage);
|
||||
|
||||
impl ResourceData for Image {
|
||||
fn dependencies(&self) -> Vec<crate::UntypedResHandle> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
impl Deref for Image {
|
||||
type Target = DynamicImage;
|
||||
|
@ -62,6 +81,22 @@ pub struct Texture {
|
|||
pub sampler: Option<TextureSampler>,
|
||||
}
|
||||
|
||||
impl ResourceData for Texture {
|
||||
fn dependencies(&self) -> Vec<crate::UntypedResHandle> {
|
||||
vec![self.image.untyped_clone()]
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
impl Texture {
|
||||
/// Create a texture from an image.
|
||||
pub fn from_image(image: ResHandle<Image>) -> Self {
|
||||
|
|
|
@ -4,7 +4,7 @@ use crossbeam::channel::Receiver;
|
|||
use lyra_ecs::World;
|
||||
use notify_debouncer_full::DebouncedEvent;
|
||||
|
||||
use crate::{loader::ResourceLoader, RequestError, ResHandle, ResourceManager};
|
||||
use crate::{loader::ResourceLoader, RequestError, ResHandle, ResourceData, ResourceManager};
|
||||
|
||||
pub trait WorldAssetExt {
|
||||
/// Register a resource loader with the resource manager.
|
||||
|
@ -15,7 +15,7 @@ pub trait WorldAssetExt {
|
|||
/// Request a resource from the resource manager.
|
||||
fn request_res<T>(&mut self, path: &str) -> Result<ResHandle<T>, RequestError>
|
||||
where
|
||||
T: Send + Sync + Any + 'static;
|
||||
T: ResourceData;
|
||||
|
||||
/// Start watching a resource for changes. Returns a crossbeam channel that can be used to listen for events.
|
||||
fn watch_res(&mut self, path: &str, recursive: bool) -> notify::Result<Receiver<Result<Vec<DebouncedEvent>, Vec<notify::Error>>>>;
|
||||
|
@ -44,7 +44,7 @@ impl WorldAssetExt for World {
|
|||
|
||||
fn request_res<T>(&mut self, path: &str) -> Result<ResHandle<T>, RequestError>
|
||||
where
|
||||
T: Send + Sync + Any + 'static
|
||||
T: ResourceData
|
||||
{
|
||||
let man = self.get_resource_or_default::<ResourceManager>();
|
||||
man.request(path)
|
||||
|
|
Loading…
Reference in New Issue