tidy up resource header content

- moved resource uuid to ResourceState::Ok
This commit is contained in:
Dmitry Stepanov
2025-04-01 23:14:30 +03:00
parent 42dc2635b3
commit a747e3c00d
12 changed files with 240 additions and 276 deletions
+3 -3
View File
@@ -1144,7 +1144,7 @@ pub trait SceneGraph: BaseSceneGraph {
let mut nodes_to_delete = Vec::new();
for (_, node) in self.traverse_iter(instance_root) {
if let Some(resource) = node.resource() {
let kind = resource.kind().clone();
let kind = resource.kind();
if let Some(model) = resource.state().data() {
if !model
.graph()
@@ -1182,7 +1182,7 @@ pub trait SceneGraph: BaseSceneGraph {
// Step 2. Look for missing nodes and create appropriate instances for them.
let mut model = resource.state();
let model_kind = model.kind().clone();
let model_kind = model.kind();
if let Some(data) = model.data() {
let resource_graph = data.graph();
@@ -1279,7 +1279,7 @@ pub trait SceneGraph: BaseSceneGraph {
for node in self.linear_iter_mut() {
if let Some(model) = node.resource() {
let mut header = model.state();
let model_kind = header.kind().clone();
let model_kind = header.kind();
if let Some(data) = header.data() {
let resource_graph = data.graph();
+1 -1
View File
@@ -2563,7 +2563,7 @@ impl Engine {
let mut state = self.resource_manager.state();
for resource in state.resources().iter() {
let data = resource.0.lock();
if let ResourceState::Ok(ref data) = data.state {
if let ResourceState::Ok { ref data, .. } = data.state {
data.as_reflect(&mut |reflect| {
if reflect.assembly_name() == plugin_assembly_name {
resources_to_reload.insert(resource.clone());
+4 -4
View File
@@ -1232,14 +1232,14 @@ impl MaterialResourceExtension for MaterialResource {
let material_state = self.header();
let kind = material_state.kind.clone();
match material_state.state {
ResourceState::Pending { .. } => MaterialResource::new_pending(Uuid::new_v4(), kind),
ResourceState::Pending { .. } => MaterialResource::new_pending(kind),
ResourceState::LoadError { ref error, .. } => {
MaterialResource::new_load_error(Uuid::new_v4(), kind.clone(), error.clone())
MaterialResource::new_load_error(kind.clone(), error.clone())
}
ResourceState::Ok(ref material) => MaterialResource::new_ok(
ResourceState::Ok { ref data, .. } => MaterialResource::new_ok(
Uuid::new_v4(),
kind,
ResourceDataAsAny::as_any(&**material)
ResourceDataAsAny::as_any(&**data)
.downcast_ref::<Material>()
.unwrap()
.clone(),
+3 -1
View File
@@ -355,7 +355,9 @@ impl SceneLoader {
.filter(|res| {
let state = self.resource_manager.state();
let registry = state.resource_registry.lock();
registry.uuid_to_path(res.resource_uuid()) == Some(&path)
res.resource_uuid()
.and_then(|uuid| registry.uuid_to_path(uuid))
== Some(&path)
})
.cloned()
.collect::<Vec<_>>();
+1 -1
View File
@@ -457,7 +457,7 @@ impl NodeTrait for Sound {
Some(buffer) => {
let header = buffer.header();
match header.state {
ResourceState::Pending { .. } | ResourceState::Ok(_) => Ok(()),
ResourceState::Pending { .. } | ResourceState::Ok { .. } => Ok(()),
ResourceState::LoadError { ref error, .. } => {
match &error.0 {
None => Err("Sound buffer is failed to load, the reason is unknown!"
+16 -6
View File
@@ -562,8 +562,12 @@ impl TileBook {
pub fn path(&self, registry: &ResourceRegistry) -> Option<PathBuf> {
match self {
TileBook::Empty => None,
TileBook::TileSet(r) => registry.uuid_to_path_buf(r.header().resource_uuid),
TileBook::Brush(r) => registry.uuid_to_path_buf(r.header().resource_uuid),
TileBook::TileSet(r) => r
.resource_uuid()
.and_then(|uuid| registry.uuid_to_path_buf(uuid)),
TileBook::Brush(r) => r
.resource_uuid()
.and_then(|uuid| registry.uuid_to_path_buf(uuid)),
}
}
/// True if the resource is external and its `change_count` is not zero.
@@ -585,8 +589,11 @@ impl TileBook {
TileBook::Empty => Ok(()),
TileBook::TileSet(r) => {
if r.header().kind.is_external() && r.data_ref().change_flag.needs_save() {
let result =
r.save(&registry.uuid_to_path_buf(r.header().resource_uuid).unwrap());
let result = r.save(
&registry
.uuid_to_path_buf(r.resource_uuid().unwrap())
.unwrap(),
);
if result.is_ok() {
r.data_ref().change_flag.reset();
}
@@ -597,8 +604,11 @@ impl TileBook {
}
TileBook::Brush(r) => {
if r.header().kind.is_external() && r.data_ref().change_flag.needs_save() {
let result =
r.save(&registry.uuid_to_path_buf(r.header().resource_uuid).unwrap());
let result = r.save(
&registry
.uuid_to_path_buf(r.resource_uuid().unwrap())
.unwrap(),
);
if result.is_ok() {
r.data_ref().change_flag.reset();
}
+9 -26
View File
@@ -41,8 +41,8 @@ impl ResourceGraphNode {
let mut dependent_resources = FxHashSet::default();
let header = resource.0.lock();
if let ResourceState::Ok(ref resource_data) = header.state {
(**resource_data).as_reflect(&mut |entity| {
if let ResourceState::Ok { ref data, .. } = header.state {
(**data).as_reflect(&mut |entity| {
collect_used_resources(entity, &mut dependent_resources);
});
}
@@ -131,16 +131,9 @@ mod test {
#[test]
fn resource_graph_node_pretty_print() {
let mut s = String::new();
let mut node = ResourceGraphNode::new(&UntypedResource::new_pending(
Uuid::new_v4(),
ResourceKind::External,
Uuid::default(),
));
let node2 = ResourceGraphNode::new(&UntypedResource::new_pending(
Uuid::new_v4(),
ResourceKind::External,
Uuid::default(),
));
let mut node =
ResourceGraphNode::new(&UntypedResource::new_pending(ResourceKind::External));
let node2 = ResourceGraphNode::new(&UntypedResource::new_pending(ResourceKind::External));
node.children.push(node2);
node.pretty_print(1, &mut s);
@@ -169,18 +162,13 @@ mod test {
#[test]
fn resource_dependency_pretty_print() {
let mut graph = ResourceDependencyGraph::new(&UntypedResource::new_pending(
Uuid::new_v4(),
ResourceKind::External,
Uuid::default(),
));
let mut graph =
ResourceDependencyGraph::new(&UntypedResource::new_pending(ResourceKind::External));
graph
.root
.children
.push(ResourceGraphNode::new(&UntypedResource::new_pending(
Uuid::new_v4(),
ResourceKind::External,
Uuid::default(),
)));
let s = graph.pretty_print();
@@ -189,18 +177,13 @@ mod test {
#[test]
fn resource_dependency_for_each() {
let mut graph = ResourceDependencyGraph::new(&UntypedResource::new_pending(
Uuid::new_v4(),
ResourceKind::External,
Uuid::default(),
));
let mut graph =
ResourceDependencyGraph::new(&UntypedResource::new_pending(ResourceKind::External));
graph
.root
.children
.push(ResourceGraphNode::new(&UntypedResource::new_pending(
Uuid::new_v4(),
ResourceKind::External,
Uuid::default(),
)));
let mut uuids = Vec::new();
+26 -26
View File
@@ -125,12 +125,12 @@ impl<T> ResourceHeaderGuard<'_, T>
where
T: TypedResourceData,
{
pub fn kind(&self) -> &ResourceKind {
&self.guard.kind
pub fn kind(&self) -> ResourceKind {
self.guard.kind
}
pub fn data(&mut self) -> Option<&mut T> {
if let ResourceState::Ok(ref mut data) = self.guard.state {
if let ResourceState::Ok { ref mut data, .. } = self.guard.state {
ResourceDataAsAny::as_any_mut(&mut **data).downcast_mut::<T>()
} else {
None
@@ -138,7 +138,7 @@ where
}
pub fn data_ref(&self) -> Option<&T> {
if let ResourceState::Ok(ref data) = self.guard.state {
if let ResourceState::Ok { ref data, .. } = self.guard.state {
ResourceDataAsAny::as_any(&**data).downcast_ref::<T>()
} else {
None
@@ -184,14 +184,15 @@ where
let mut untyped = UntypedResource::default();
if untyped.visit(name, visitor).is_ok() {
let untyped_data_type = untyped.type_uuid();
if untyped_data_type == <T as TypeUuidProvider>::type_uuid() {
if untyped_data_type == Some(<T as TypeUuidProvider>::type_uuid()) {
self.untyped = untyped;
return Ok(());
} else {
Log::err(format!(
"Unable to deserialize untyped resource into its typed \
version, because types do not match! Untyped resource has \
{untyped_data_type} type, but the required type is {}",
{:?} type, but the required type is {}",
untyped_data_type,
<T as TypeUuidProvider>::type_uuid(),
))
}
@@ -242,13 +243,9 @@ where
{
/// Creates new resource in pending state.
#[inline]
pub fn new_pending(resource_uuid: Uuid, kind: ResourceKind) -> Self {
pub fn new_pending(kind: ResourceKind) -> Self {
Self {
untyped: UntypedResource::new_pending(
resource_uuid,
kind,
<T as TypeUuidProvider>::type_uuid(),
),
untyped: UntypedResource::new_pending(kind),
phantom: PhantomData,
}
}
@@ -264,14 +261,9 @@ where
/// Creates new resource in error state.
#[inline]
pub fn new_load_error(resource_uuid: Uuid, kind: ResourceKind, error: LoadError) -> Self {
pub fn new_load_error(kind: ResourceKind, error: LoadError) -> Self {
Self {
untyped: UntypedResource::new_load_error(
resource_uuid,
kind,
error,
<T as TypeUuidProvider>::type_uuid(),
),
untyped: UntypedResource::new_load_error(kind, error),
phantom: PhantomData,
}
}
@@ -315,7 +307,7 @@ where
/// Returns true if the resource is fully loaded and ready for use.
#[inline]
pub fn is_ok(&self) -> bool {
matches!(self.untyped.0.lock().state, ResourceState::Ok(_))
matches!(self.untyped.0.lock().state, ResourceState::Ok { .. })
}
/// Returns true if the resource is failed to load.
@@ -342,6 +334,11 @@ where
self.untyped.kind()
}
#[inline]
pub fn resource_uuid(&self) -> Option<Uuid> {
self.untyped.resource_uuid()
}
/// Sets a new kind of the resource.
#[inline]
pub fn set_path(&mut self, new_kind: ResourceKind) {
@@ -407,7 +404,10 @@ where
{
#[inline]
fn from(untyped: UntypedResource) -> Self {
assert_eq!(untyped.type_uuid(), <T as TypeUuidProvider>::type_uuid());
assert_eq!(
untyped.type_uuid(),
Some(<T as TypeUuidProvider>::type_uuid())
);
Self {
untyped,
phantom: Default::default(),
@@ -456,7 +456,7 @@ where
#[inline]
pub fn as_loaded_ref(&self) -> Option<&T> {
match self.guard.state {
ResourceState::Ok(ref data) => ResourceDataAsAny::as_any(&**data).downcast_ref(),
ResourceState::Ok { ref data, .. } => ResourceDataAsAny::as_any(&**data).downcast_ref(),
_ => None,
}
}
@@ -464,7 +464,7 @@ where
#[inline]
pub fn as_loaded_mut(&mut self) -> Option<&mut T> {
match self.guard.state {
ResourceState::Ok(ref mut data) => {
ResourceState::Ok { ref mut data, .. } => {
ResourceDataAsAny::as_any_mut(&mut **data).downcast_mut()
}
_ => None,
@@ -492,7 +492,7 @@ where
self.guard.kind
)
}
ResourceState::Ok(ref data) => data.fmt(f),
ResourceState::Ok { ref data, .. } => data.fmt(f),
}
}
}
@@ -517,7 +517,7 @@ where
self.guard.kind
)
}
ResourceState::Ok(ref data) => ResourceDataAsAny::as_any(&**data)
ResourceState::Ok { ref data, .. } => ResourceDataAsAny::as_any(&**data)
.downcast_ref()
.expect("Type mismatch!"),
}
@@ -543,7 +543,7 @@ where
header.kind
)
}
ResourceState::Ok(ref mut data) => ResourceDataAsAny::as_any_mut(&mut **data)
ResourceState::Ok { ref mut data, .. } => ResourceDataAsAny::as_any_mut(&mut **data)
.downcast_mut()
.expect("Type mismatch!"),
}
+61 -119
View File
@@ -32,7 +32,6 @@ use crate::{
parking_lot::{Mutex, MutexGuard},
task::TaskPool,
watcher::FileSystemWatcher,
TypeUuidProvider,
},
entry::{TimedEntry, DEFAULT_RESOURCE_LIFETIME},
event::{ResourceEvent, ResourceEventBroadcaster},
@@ -327,11 +326,6 @@ impl ResourceManager {
/// Actual resource state can be fetched by [`Resource::state`] method. If you know for sure that the resource
/// is already loaded, then you can use [`Resource::data_ref`] to obtain a reference to the actual resource data.
/// Keep in mind, that this method will panic if the resource non in `Ok` state.
///
/// ## Panic
///
/// This method will panic, if type UUID of `T` does not match the actual type UUID of the resource. If this
/// is undesirable, use [`Self::try_request`] instead.
pub fn request<T>(&self, path: impl AsRef<Path>) -> Resource<T>
where
T: TypedResourceData,
@@ -342,28 +336,6 @@ impl ResourceManager {
}
}
/// The same as [`Self::request`], but returns [`None`] if type UUID of `T` does not match the actual type UUID
/// of the resource.
///
/// ## Panic
///
/// This method does not panic.
pub fn try_request<T>(&self, path: impl AsRef<Path>) -> Option<Resource<T>>
where
T: TypedResourceData,
{
let untyped = self.state().request(path);
let actual_type_uuid = untyped.type_uuid();
if actual_type_uuid == <T as TypeUuidProvider>::type_uuid() {
Some(Resource {
untyped,
phantom: PhantomData::<T>,
})
} else {
None
}
}
/// Same as [`Self::request`], but returns untyped resource.
pub fn request_untyped<P>(&self, path: P) -> UntypedResource
where
@@ -389,12 +361,14 @@ impl ResourceManager {
F: FnMut(&mut dyn ResourceData, &Path) -> bool,
{
let path = path.as_ref().to_owned();
let resource_uuid = resource.resource_uuid();
let resource_uuid = resource
.resource_uuid()
.ok_or(ResourceRegistrationError::InvalidState)?;
let mut state = self.state();
if let Some(resource) = state.find(resource_uuid) {
let resource_state = resource.0.lock();
if let ResourceState::Ok(_) = resource_state.state {
if let ResourceState::Ok { .. } = resource_state.state {
return Err(ResourceRegistrationError::AlreadyRegistered);
}
}
@@ -403,14 +377,16 @@ impl ResourceManager {
let mut header = resource.0.lock();
header.kind.make_external();
if let ResourceState::Ok(ref mut data) = header.state {
if let ResourceState::Ok {
ref mut data,
resource_uuid,
..
} = header.state
{
if !on_register(&mut **data, path.as_ref()) {
Err(ResourceRegistrationError::UnableToRegister)
} else {
state
.resource_registry
.lock()
.register(header.resource_uuid, path);
state.resource_registry.lock().register(resource_uuid, path);
drop(header);
state.push(resource);
Ok(())
@@ -426,12 +402,16 @@ impl ResourceManager {
resource: UntypedResource,
new_path: impl AsRef<Path>,
) -> Result<(), FileError> {
let resource_uuid = resource
.resource_uuid()
.ok_or_else(|| FileError::Custom("Unable to move non-loaded resource!".to_string()))?;
let new_path = new_path.as_ref().to_owned();
let io = self.state().resource_io.clone();
let registry = self.state().resource_registry.clone();
let existing_path = registry
.lock()
.uuid_to_path(resource.resource_uuid())
.uuid_to_path(resource_uuid)
.map(|path| path.to_path_buf())
.ok_or_else(|| FileError::Custom("Cannot move embedded resource!".to_string()))?;
@@ -604,10 +584,10 @@ impl ResourceManagerState {
if resource.value.use_count() <= 1 {
resource.time_to_live -= dt;
if resource.time_to_live <= 0.0 {
if let Some(path) = self
.resource_registry
.lock()
.uuid_to_path(resource.0.lock().resource_uuid)
let registry = self.resource_registry.lock();
let resource_uuid = resource.resource_uuid();
if let Some(path) =
resource_uuid.and_then(|resource_uuid| registry.uuid_to_path(resource_uuid))
{
Log::info(format!(
"Resource {} destroyed because it is not used anymore!",
@@ -671,16 +651,21 @@ impl ResourceManagerState {
pub fn find(&self, uuid: Uuid) -> Option<&UntypedResource> {
self.resources
.iter()
.find(|entry| entry.value.resource_uuid() == uuid)
.find(|entry| entry.value.resource_uuid() == Some(uuid))
.map(|entry| &entry.value)
}
pub fn find_by_path(&self, path: &Path) -> Option<&UntypedResource> {
let registry = self.resource_registry.lock();
self.resources
.iter()
.find(|entry| registry.uuid_to_path(entry.value.resource_uuid()) == Some(path))
.map(|entry| &entry.value)
self.resources.iter().find_map(|entry| {
let header = entry.value.0.lock();
if let ResourceState::Ok { resource_uuid, .. } = header.state {
if registry.uuid_to_path(resource_uuid) == Some(path) {
return Some(&entry.value);
}
}
None
})
}
/// Returns total amount of resources in the container.
@@ -718,7 +703,7 @@ impl ResourceManagerState {
/// Returns total amount of completely loaded resources.
pub fn count_loaded_resources(&self) -> usize {
self.resources.iter().fold(0, |counter, resource| {
if let ResourceState::Ok(_) = resource.0.lock().state {
if let ResourceState::Ok { .. } = resource.0.lock().state {
counter + 1
} else {
counter
@@ -743,11 +728,13 @@ impl ResourceManagerState {
self.find_or_load(ResourcePath::Explicit(path.as_ref().to_path_buf()))
}
/// Tries to load a resource by a unique identifier. The identifier must not be a zero-uuid,
/// otherwise this method will panic!
pub fn request_by_uuid(&mut self, resource_uuid: Uuid) -> UntypedResource {
if let Some(built_in_resource) = self
.built_in_resources
.values()
.find(|r| r.resource.resource_uuid() == resource_uuid)
.find(|r| r.resource.resource_uuid() == Some(resource_uuid))
{
return built_in_resource.resource.clone();
}
@@ -763,14 +750,12 @@ impl ResourceManagerState {
match header.state {
ResourceState::Pending { ref path, .. }
| ResourceState::LoadError { ref path, .. } => path == path_to_search,
ResourceState::Ok(_) => match path_to_search {
ResourceState::Ok { resource_uuid, .. } => match path_to_search {
ResourcePath::Explicit(fs_path) => {
self.resource_registry
.lock()
.uuid_to_path(header.resource_uuid)
self.resource_registry.lock().uuid_to_path(resource_uuid)
== Some(fs_path)
}
ResourcePath::Implicit(uuid) => &header.resource_uuid == uuid,
ResourcePath::Implicit(uuid) => &resource_uuid == uuid,
},
}
})
@@ -781,8 +766,7 @@ impl ResourceManagerState {
match self.find_by_resource_path(&path) {
Some(existing) => existing.clone(),
None => {
let resource =
UntypedResource::new_pending(Uuid::nil(), ResourceKind::External, Uuid::nil());
let resource = UntypedResource::new_pending(ResourceKind::External);
self.spawn_loading_task(path, resource.clone(), false);
self.push(resource.clone());
resource
@@ -791,6 +775,10 @@ impl ResourceManagerState {
}
fn spawn_loading_task(&self, path: ResourcePath, resource: UntypedResource, reload: bool) {
if let ResourcePath::Implicit(ref uuid) = path {
assert_ne!(*uuid, Uuid::nil());
}
let event_broadcaster = self.event_broadcaster.clone();
let loaders = self.loaders.clone();
let registry = self.resource_registry.clone();
@@ -843,11 +831,12 @@ impl ResourceManagerState {
// Separate scope to keep mutex locking time at minimum.
{
let mut mutex_guard = resource.0.lock();
mutex_guard.resource_uuid =
registry.lock().path_to_uuid(&fs_path).unwrap();
mutex_guard.type_uuid = data.type_uuid();
let resource_uuid = registry.lock().path_to_uuid(&fs_path).unwrap();
assert!(mutex_guard.kind.is_external());
mutex_guard.state.commit(ResourceState::Ok(data));
mutex_guard.state.commit(ResourceState::Ok {
data,
resource_uuid,
});
}
event_broadcaster.broadcast_loaded_or_reloaded(resource, reload);
@@ -886,8 +875,8 @@ impl ResourceManagerState {
drop(header);
self.spawn_loading_task(path, resource, true)
}
ResourceState::Ok(_) => {
let path = ResourcePath::Implicit(header.resource_uuid);
ResourceState::Ok { resource_uuid, .. } => {
let path = ResourcePath::Implicit(resource_uuid);
drop(header);
self.spawn_loading_task(path, resource, true)
}
@@ -941,7 +930,7 @@ impl ResourceManagerState {
if let Some(position) = self
.resources
.iter()
.position(|entry| entry.value.resource_uuid() == uuid)
.position(|entry| entry.value.resource_uuid() == Some(uuid))
{
self.resources.remove(position);
}
@@ -1012,17 +1001,8 @@ mod test {
let cx = ResourceWaitContext {
resources: vec![
UntypedResource::new_pending(
Uuid::new_v4(),
ResourceKind::External,
Uuid::default(),
),
UntypedResource::new_load_error(
Uuid::new_v4(),
ResourceKind::External,
Default::default(),
Uuid::default(),
),
UntypedResource::new_pending(ResourceKind::External),
UntypedResource::new_load_error(ResourceKind::External, Default::default()),
],
};
assert!(!cx.is_all_loaded());
@@ -1062,16 +1042,10 @@ mod test {
assert_eq!(state.count_registered_resources(), 0);
assert_eq!(state.len(), 0);
state.push(UntypedResource::new_pending(
Uuid::new_v4(),
ResourceKind::External,
Uuid::default(),
));
state.push(UntypedResource::new_pending(ResourceKind::External));
state.push(UntypedResource::new_load_error(
Uuid::new_v4(),
ResourceKind::External,
Default::default(),
Uuid::default(),
));
state.push(UntypedResource::new_ok(
Uuid::new_v4(),
@@ -1091,16 +1065,10 @@ mod test {
assert_eq!(state.loading_progress(), 100);
state.push(UntypedResource::new_pending(
Uuid::new_v4(),
ResourceKind::External,
Uuid::default(),
));
state.push(UntypedResource::new_pending(ResourceKind::External));
state.push(UntypedResource::new_load_error(
Uuid::new_v4(),
ResourceKind::External,
Default::default(),
Uuid::default(),
));
state.push(UntypedResource::new_ok(
Uuid::new_v4(),
@@ -1118,9 +1086,7 @@ mod test {
assert!(state.find_by_path(Path::new("foo.txt")).is_none());
let path = PathBuf::from("test.txt");
let type_uuid = Uuid::default();
let resource =
UntypedResource::new_pending(Uuid::new_v4(), ResourceKind::External, type_uuid);
let resource = UntypedResource::new_pending(ResourceKind::External);
state.push(resource.clone());
assert_eq!(state.find_by_path(&path), Some(&resource));
@@ -1132,14 +1098,8 @@ mod test {
assert_eq!(state.resources(), Vec::new());
let r1 =
UntypedResource::new_pending(Uuid::new_v4(), ResourceKind::External, Uuid::default());
let r2 = UntypedResource::new_load_error(
Uuid::new_v4(),
ResourceKind::External,
Default::default(),
Uuid::default(),
);
let r1 = UntypedResource::new_pending(ResourceKind::External);
let r2 = UntypedResource::new_load_error(ResourceKind::External, Default::default());
let r3 = UntypedResource::new_ok(Uuid::new_v4(), ResourceKind::Embedded, Stub {});
state.push(r1.clone());
state.push(r2.clone());
@@ -1153,11 +1113,7 @@ mod test {
fn resource_manager_state_destroy_unused_resources() {
let mut state = new_resource_manager();
state.push(UntypedResource::new_pending(
Uuid::new_v4(),
ResourceKind::External,
Uuid::default(),
));
state.push(UntypedResource::new_pending(ResourceKind::External));
assert_eq!(state.len(), 1);
state.destroy_unused_resources();
@@ -1168,14 +1124,8 @@ mod test {
fn resource_manager_state_request() {
let mut state = new_resource_manager();
let path = PathBuf::from("test.txt");
let type_uuid = Uuid::default();
let resource = UntypedResource::new_load_error(
Uuid::new_v4(),
ResourceKind::External,
Default::default(),
type_uuid,
);
let resource = UntypedResource::new_load_error(ResourceKind::External, Default::default());
state.push(resource.clone());
let res = state.request(path);
@@ -1185,7 +1135,6 @@ mod test {
let res = state.request(path.clone());
assert_eq!(res.kind(), ResourceKind::External);
assert_eq!(res.type_uuid(), type_uuid);
assert!(!res.is_loading());
}
@@ -1194,12 +1143,7 @@ mod test {
let mut state = new_resource_manager();
state.loaders.lock().set(Stub {});
let resource = UntypedResource::new_load_error(
Uuid::new_v4(),
ResourceKind::External,
Default::default(),
Uuid::default(),
);
let resource = UntypedResource::new_load_error(ResourceKind::External, Default::default());
state.push(resource.clone());
assert!(!state.try_reload_resource_from_path(Path::new("foo.txt")));
@@ -1231,10 +1175,8 @@ mod test {
fn resource_manager_register() {
let manager = ResourceManager::new(Arc::new(Default::default()));
let path = PathBuf::from("test.txt");
let type_uuid = Uuid::default();
let resource =
UntypedResource::new_pending(Uuid::new_v4(), ResourceKind::External, type_uuid);
let resource = UntypedResource::new_pending(ResourceKind::External);
let res = manager.register(resource.clone(), path.clone(), |_, _| true);
assert!(res.is_err());
+62 -16
View File
@@ -20,11 +20,13 @@
//! A module that handles resource states.
use crate::untyped::ResourceKind;
use crate::{
core::{reflect::prelude::*, uuid::Uuid, visitor::prelude::*},
manager::ResourceManager,
ResourceData, ResourceLoadError,
};
use fyrox_core::warn;
use std::path::PathBuf;
use std::{
ops::{Deref, DerefMut},
@@ -108,6 +110,13 @@ impl Default for ResourcePath {
/// Resources do not store their paths to respective files in the file system, instead resource only
/// stores their unique identifiers (UUID). Use [`crate::registry::ResourceRegistry`] to get a path
/// associated with the resource uuid.
///
/// ## UUID
///
/// Resource UUID is available only if the resource is fully loaded. This is because there's no way
/// to get the UUID earlier: the UUID is stored in a metadata file which exists only if the resource
/// is present. It is somewhat possible to get a UUID when a resource is failed to load, but not in
/// 100% cases.
#[derive(Debug, Reflect)]
pub enum ResourceState {
/// Resource is loading from external resource or in the queue to load.
@@ -130,7 +139,12 @@ pub enum ResourceState {
error: LoadError,
},
/// Actual resource data when it is fully loaded.
Ok(Box<dyn ResourceData>),
Ok {
/// Unique id of the resource.
resource_uuid: Uuid,
/// Actual data of the resource.
data: Box<dyn ResourceData>,
},
}
impl Default for ResourceState {
@@ -150,12 +164,20 @@ impl Drop for ResourceState {
}
}
impl Visit for ResourceState {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
impl ResourceState {
pub fn visit(&mut self, kind: ResourceKind, name: &str, visitor: &mut Visitor) -> VisitResult {
if visitor.is_reading() {
let mut type_uuid = Uuid::default();
type_uuid.visit("TypeUuid", visitor)?;
let mut resource_uuid = Uuid::default();
if resource_uuid.visit("ResourceUuid", visitor).is_err() {
warn!(
"A resource of type {type_uuid} has no uuid! It looks like a resource in \
the old format; trying to read it..."
);
}
let resource_manager = visitor.blackboard.get::<ResourceManager>().expect(
"Resource data constructor container must be \
provided when serializing resources!",
@@ -167,25 +189,43 @@ impl Visit for ResourceState {
.try_create(&type_uuid)
{
drop(resource_manager_state);
instance.visit(name, visitor)?;
*self = Self::Ok(instance);
Ok(())
if kind == ResourceKind::Embedded {
instance.visit(name, visitor)?;
}
*self = Self::Ok {
resource_uuid,
data: instance,
};
} else {
Err(VisitError::User(format!(
return Err(VisitError::User(format!(
"There's no constructor registered for type {type_uuid}!"
)))
)));
}
} else if let Self::Ok(instance) = self {
instance.visit(name, visitor)?;
Ok(())
} else if let Self::Ok {
resource_uuid,
data,
} = self
{
resource_uuid.visit("ResourceUuid", visitor)?;
let mut type_uuid = data.type_uuid();
type_uuid.visit("TypeUuid", visitor)?;
if kind == ResourceKind::Embedded {
data.visit(name, visitor)?;
}
Ok(())
} else {
// Do not save other variants, because they're needed only for runtime purposes.
Ok(())
}
}
}
impl ResourceState {
/// Creates new resource in pending state.
#[inline]
pub fn new_pending() -> Self {
@@ -203,8 +243,11 @@ impl ResourceState {
/// Creates new resource in ok (resource with data) state.
#[inline]
pub fn new_ok<T: ResourceData>(data: T) -> Self {
Self::Ok(Box::new(data))
pub fn new_ok<T: ResourceData>(resource_uuid: Uuid, data: T) -> Self {
Self::Ok {
resource_uuid,
data: Box::new(data),
}
}
/// Checks whether the resource is still loading or not.
@@ -240,8 +283,11 @@ impl ResourceState {
}
/// Changes internal state to [`ResourceState::Ok`]
pub fn commit_ok<T: ResourceData>(&mut self, data: T) {
self.commit(ResourceState::Ok(Box::new(data)))
pub fn commit_ok<T: ResourceData>(&mut self, resource_uuid: Uuid, data: T) {
self.commit(ResourceState::Ok {
resource_uuid,
data: Box::new(data),
})
}
/// Changes internal state to [`ResourceState::LoadError`].
+53 -72
View File
@@ -87,7 +87,7 @@ fn guess_uuid(region: &mut RegionGuard) -> Uuid {
}
/// Kind of a resource. It defines how the resource manager will treat a resource content on serialization.
#[derive(Default, Reflect, Debug, Visit, Clone, PartialEq, Eq, Hash)]
#[derive(Default, Reflect, Debug, Visit, Copy, Clone, PartialEq, Eq, Hash)]
pub enum ResourceKind {
/// The content of embedded resources will be fully serialized.
#[default]
@@ -155,10 +155,6 @@ impl Display for ResourceKind {
/// its kind, etc.
#[derive(Reflect, Debug)]
pub struct ResourceHeader {
/// Unique id of a resource.
pub resource_uuid: Uuid,
/// UUID of the internal data type.
pub type_uuid: Uuid,
/// Kind of the resource. See [`ResourceKind`] for more info.
pub kind: ResourceKind,
/// Actual state of the resource. See [`ResourceState`] for more info.
@@ -216,7 +212,11 @@ impl Visit for ResourceHeader {
instance.visit("Details", &mut region)?;
self.state = ResourceState::Ok(instance);
self.state = ResourceState::Ok {
data: instance,
// The old format does not contain an uuid.
resource_uuid: Uuid::nil(),
};
return Ok(());
} else {
@@ -232,27 +232,18 @@ impl Visit for ResourceHeader {
}
return Ok(());
}
}
if region.is_reading() {
let mut old_kind = OldResourceKind::Embedded;
if old_kind.visit("Kind", &mut region).is_ok() {
if let OldResourceKind::External(path) = old_kind {
self.old_format_path = Some(path);
} else {
let mut old_kind = OldResourceKind::Embedded;
if old_kind.visit("Kind", &mut region).is_ok() {
if let OldResourceKind::External(path) = old_kind {
self.old_format_path = Some(path);
}
}
}
}
self.kind.visit("Kind", &mut region)?;
self.type_uuid.visit("TypeUuid", &mut region)?;
// Backward compatibility.
let _ = self.resource_uuid.visit("ResourceUuid", &mut region);
if self.kind == ResourceKind::Embedded {
self.state.visit("State", &mut region)?;
}
self.state.visit(self.kind, "State", &mut region)?;
Ok(())
}
@@ -306,20 +297,21 @@ impl Visit for UntypedResource {
self.0.visit(name, visitor)?;
// Try to restore the shallow handle on deserialization for external resources.
if visitor.is_reading() && !self.is_embedded() {
let resource_manager = visitor
.blackboard
.get::<ResourceManager>()
.expect("Resource manager must be available when deserializing resources!");
if visitor.is_reading() {
let inner_lock = self.0.lock();
let resource_uuid = inner_lock.resource_uuid;
if let Some(old_format_path) = inner_lock.old_format_path.clone() {
drop(inner_lock);
self.0 = resource_manager.request_untyped(old_format_path).0;
} else {
drop(inner_lock);
self.0 = resource_manager.request_by_uuid(resource_uuid).0;
if inner_lock.kind == ResourceKind::External {
let resource_manager = visitor
.blackboard
.get::<ResourceManager>()
.expect("Resource manager must be available when deserializing resources!");
if let Some(old_format_path) = inner_lock.old_format_path.clone() {
drop(inner_lock);
self.0 = resource_manager.request_untyped(old_format_path).0;
} else if let ResourceState::Ok { resource_uuid, .. } = inner_lock.state {
drop(inner_lock);
self.0 = resource_manager.request_by_uuid(resource_uuid).0;
}
}
}
@@ -330,9 +322,7 @@ impl Visit for UntypedResource {
impl Default for UntypedResource {
fn default() -> Self {
Self(Arc::new(Mutex::new(ResourceHeader {
resource_uuid: Default::default(),
kind: Default::default(),
type_uuid: Default::default(),
state: ResourceState::new_load_error(
Default::default(),
LoadError::new("Default resource state of unknown type."),
@@ -364,11 +354,9 @@ impl Hash for UntypedResource {
impl UntypedResource {
/// Creates new untyped resource in pending state using the given path and type uuid.
pub fn new_pending(resource_uuid: Uuid, kind: ResourceKind, type_uuid: Uuid) -> Self {
pub fn new_pending(kind: ResourceKind) -> Self {
Self(Arc::new(Mutex::new(ResourceHeader {
resource_uuid,
kind,
type_uuid,
state: ResourceState::new_pending(),
old_format_path: None,
})))
@@ -381,37 +369,36 @@ impl UntypedResource {
T: ResourceData,
{
Self(Arc::new(Mutex::new(ResourceHeader {
resource_uuid,
kind,
type_uuid: data.type_uuid(),
state: ResourceState::new_ok(data),
state: ResourceState::new_ok(resource_uuid, data),
old_format_path: None,
})))
}
/// Creates new untyped resource in error state.
pub fn new_load_error(
resource_uuid: Uuid,
kind: ResourceKind,
error: LoadError,
type_uuid: Uuid,
) -> Self {
pub fn new_load_error(kind: ResourceKind, error: LoadError) -> Self {
Self(Arc::new(Mutex::new(ResourceHeader {
resource_uuid,
kind,
type_uuid,
state: ResourceState::new_load_error(Default::default(), error),
old_format_path: None,
})))
}
pub fn resource_uuid(&self) -> Uuid {
self.0.lock().resource_uuid
pub fn resource_uuid(&self) -> Option<Uuid> {
let header = self.0.lock();
match header.state {
ResourceState::Ok { resource_uuid, .. } => Some(resource_uuid),
_ => None,
}
}
/// Returns actual unique type id of underlying resource data.
pub fn type_uuid(&self) -> Uuid {
self.0.lock().type_uuid
pub fn type_uuid(&self) -> Option<Uuid> {
let header = self.0.lock();
match header.state {
ResourceState::Ok { ref data, .. } => Some(data.type_uuid()),
_ => None,
}
}
/// Returns true if the resource is still loading.
@@ -439,7 +426,7 @@ impl UntypedResource {
/// Returns path of the untyped resource.
pub fn kind(&self) -> ResourceKind {
self.0.lock().kind.clone()
self.0.lock().kind
}
/// Set a new path for the untyped resource.
@@ -454,7 +441,7 @@ impl UntypedResource {
ResourceState::Pending { .. } | ResourceState::LoadError { .. } => {
Err("Unable to save unloaded resource!".into())
}
ResourceState::Ok(ref mut data) => data.save(path),
ResourceState::Ok { ref mut data, .. } => data.save(path),
}
}
@@ -463,7 +450,7 @@ impl UntypedResource {
where
T: TypedResourceData,
{
if self.type_uuid() == <T as TypeUuidProvider>::type_uuid() {
if self.type_uuid() == Some(<T as TypeUuidProvider>::type_uuid()) {
Some(Resource {
untyped: self.clone(),
phantom: PhantomData::<T>,
@@ -481,10 +468,9 @@ impl UntypedResource {
}
/// Changes internal state to [`ResourceState::Ok`]
pub fn commit_ok<T: ResourceData>(&self, data: T) {
pub fn commit_ok<T: ResourceData>(&self, resource_uuid: Uuid, data: T) {
let mut guard = self.0.lock();
guard.type_uuid = data.type_uuid();
guard.state.commit_ok(data);
guard.state.commit_ok(resource_uuid, data);
}
/// Changes internal state to [`ResourceState::LoadError`].
@@ -505,7 +491,7 @@ impl Future for UntypedResource {
Poll::Pending
}
ResourceState::LoadError { ref error, .. } => Poll::Ready(Err(error.clone())),
ResourceState::Ok(_) => Poll::Ready(Ok(self.clone())),
ResourceState::Ok { .. } => Poll::Ready(Ok(self.clone())),
}
}
}
@@ -569,11 +555,7 @@ mod test {
#[test]
fn untyped_resource_try_cast() {
let r = UntypedResource::default();
let r2 = UntypedResource::new_pending(
Uuid::new_v4(),
ResourceKind::External,
Uuid::from_u128(0xa1a2a3a4b1b2c1c2d1d2d3d4d5d6d7d8u128),
);
let r2 = UntypedResource::new_pending(ResourceKind::External);
assert!(r.try_cast::<Stub>().is_some());
assert!(r2.try_cast::<Stub>().is_none());
@@ -587,18 +569,17 @@ mod test {
let mut cx = task::Context::from_waker(&waker);
let mut r = UntypedResource(Arc::new(Mutex::new(ResourceHeader {
resource_uuid: Default::default(),
kind: ResourceKind::External,
type_uuid: Uuid::default(),
state: ResourceState::Ok(Box::new(stub)),
state: ResourceState::Ok {
data: Box::new(stub),
resource_uuid: Uuid::new_v4(),
},
old_format_path: None,
})));
assert!(Pin::new(&mut r).poll(&mut cx).is_ready());
let mut r = UntypedResource(Arc::new(Mutex::new(ResourceHeader {
resource_uuid: Default::default(),
kind: ResourceKind::External,
type_uuid: Uuid::default(),
state: ResourceState::LoadError {
path: Default::default(),
error: Default::default(),
+1 -1
View File
@@ -790,7 +790,7 @@ impl TextureResourceExtension for TextureResource {
}
fn deep_clone(&self) -> Self {
let kind = self.header().kind.clone();
let kind = self.header().kind;
let data = self.data_ref().clone();
Resource::new_ok(Uuid::new_v4(), kind, data)
}