fyrox_resource/graph.rs
1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21//! Resource dependency graph. See [`ResourceDependencyGraph`] docs for more info.
22
23use crate::{collect_used_resources, state::ResourceState, untyped::UntypedResource};
24use fxhash::FxHashSet;
25
26/// A node of [`ResourceDependencyGraph`].
27pub struct ResourceGraphNode {
28 /// A resource associated with the graph node.
29 pub resource: UntypedResource,
30 /// A list of children nodes of the graph.
31 pub children: Vec<ResourceGraphNode>,
32}
33
34impl ResourceGraphNode {
35 /// Creates a new resource graph node for a given untyped resource. This method is recursive -
36 /// it will initialize the entire sub-graph of dependencies automatically.
37 pub fn new(resource: &UntypedResource) -> Self {
38 let mut children = Vec::new();
39
40 // Look for dependent resources.
41 let mut dependent_resources = FxHashSet::default();
42
43 let header = resource.lock();
44 if let ResourceState::Ok { ref data, .. } = header.state {
45 (**data).as_reflect(&mut |entity| {
46 collect_used_resources(entity, &mut dependent_resources);
47 });
48 }
49
50 children.extend(
51 dependent_resources
52 .into_iter()
53 .map(|r| ResourceGraphNode::new(&r)),
54 );
55
56 Self {
57 resource: resource.clone(),
58 children,
59 }
60 }
61
62 /// Recursively prints the dependency graph node and its descendant nodes to the specified string, applying
63 /// specified level offset.
64 pub fn pretty_print(&self, level: usize, out: &mut String) {
65 *out += &format!(
66 "{}{}\n",
67 String::from('\t').repeat(level),
68 self.resource.kind()
69 );
70
71 for child in self.children.iter() {
72 child.pretty_print(level + 1, out);
73 }
74 }
75
76 /// Iterates over each dependency graph node and applying the specific function to them.
77 pub fn for_each<F: FnMut(&UntypedResource)>(&self, func: &mut F) {
78 func(&self.resource);
79
80 for child in self.children.iter() {
81 child.for_each(func)
82 }
83 }
84}
85
86/// Resource dependency graph allows you to collect all dependencies of a resource in structured form.
87/// Internally, it uses reflection to look into resources content and find dependent resources. An example
88/// of dependent resource is very simple: if you have a 3D model, then it most likely has a bunch of
89/// textures - these textures are dependent resources. A more complex example - a game level could depend
90/// on lots of prefabs, which in their turn may depend on other prefabs, textures, sounds, etc.
91pub struct ResourceDependencyGraph {
92 /// Root node of the graph.
93 pub root: ResourceGraphNode,
94}
95
96impl ResourceDependencyGraph {
97 /// Creates a new resource dependency graph starting from a given untyped resource.
98 pub fn new(resource: &UntypedResource) -> Self {
99 Self {
100 root: ResourceGraphNode::new(resource),
101 }
102 }
103
104 /// Iterates over each dependency graph node and applying the specific function to them.
105 pub fn for_each<F: FnMut(&UntypedResource)>(&self, mut func: F) {
106 self.root.for_each(&mut func)
107 }
108
109 /// Prints the entire dependency graph into a string.
110 pub fn pretty_print(&self) -> String {
111 let mut out = String::new();
112 self.root.pretty_print(0, &mut out);
113 out
114 }
115}
116#[cfg(test)]
117mod test {
118 use fyrox_core::Uuid;
119
120 use super::*;
121 use crate::untyped::ResourceKind;
122
123 #[test]
124 fn resource_graph_node_new() {
125 let resource = UntypedResource::default();
126 let node = ResourceGraphNode::new(&resource);
127
128 assert_eq!(node.resource, resource);
129 assert_eq!(node.children.len(), 0);
130 }
131
132 #[test]
133 fn resource_graph_node_pretty_print() {
134 let mut s = String::new();
135 let mut node = ResourceGraphNode::new(&UntypedResource::new_pending(
136 Uuid::new_v4(),
137 ResourceKind::External,
138 ));
139 let node2 = ResourceGraphNode::new(&UntypedResource::new_pending(
140 Uuid::new_v4(),
141 ResourceKind::External,
142 ));
143 node.children.push(node2);
144 node.pretty_print(1, &mut s);
145
146 assert_eq!(s, "\tExternal\n\t\tExternal\n".to_string());
147 }
148
149 #[test]
150 fn resource_dependency_graph_new() {
151 let resource = UntypedResource::default();
152 let graph = ResourceDependencyGraph::new(&resource);
153
154 assert_eq!(graph.root.resource, resource);
155 assert_eq!(graph.root.children.len(), 0);
156 }
157
158 #[test]
159 fn resource_dependency_pretty_print() {
160 let mut graph = ResourceDependencyGraph::new(&UntypedResource::new_pending(
161 Default::default(),
162 ResourceKind::External,
163 ));
164 graph
165 .root
166 .children
167 .push(ResourceGraphNode::new(&UntypedResource::new_pending(
168 Default::default(),
169 ResourceKind::External,
170 )));
171
172 let s = graph.pretty_print();
173 assert_eq!(s, "External\n\tExternal\n".to_string());
174 }
175}