Skip to main content

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}