1+ // Copyright (C) 2018-2023 - DevSH Graphics Programming Sp. z O.O.
2+ // This file is part of the "Nabla Engine".
3+ // For conditions of distribution and use, see copyright notice in nabla.h
4+
5+ #ifndef __NBL_ASSET_MATERIAL_COMPILER_V2_IR_H_INCLUDED__
6+ #define __NBL_ASSET_MATERIAL_COMPILER_V2_IR_H_INCLUDED__
7+
8+ #include < nbl/core/IReferenceCounted.h>
9+ #include < nbl/core/containers/refctd_dynamic_array.h>
10+
11+ namespace nbl ::asset::material_compiler::v2::ir {
12+
13+ // Traverse the tree of nodes in reverse bfs order which
14+ // gurantess we visit all nodes gets depended by upper level
15+ // nodes are visited beforehand. (kahn's algorithm)
16+ template <typename Visitor>
17+ NBL_FORCE_INLINE void traverse_node_tree_topological (NodeHandle root,
18+ Visitor visitor) {
19+ core::queue<NodeHandle> q;
20+ core::vector<NodeHandle> worklist;
21+ q.push (root);
22+ while (!q.empty ()) {
23+ NodeHandle cur = q.front ();
24+ q.pop ();
25+ for (OffsetToValue value_offset : cur->get_value_offsets ())
26+ if (cur->value (value_offset).is_node ())
27+ worklist.push_back (cur->value (value_offset).get_node ());
28+ }
29+ while (!worklist.empty ()) {
30+ NodeHandle cur = worklist.back ();
31+ worklist.pop_back ();
32+ visitor (cur);
33+ }
34+ }
35+
36+ using INodeCacheSet =
37+ core::unordered_set<NodeHandle, NodeHandleHasher, NodeHandleComparor>;
38+
39+ class NBL_API IRDAG : public core::IReferenceCounted
40+ {
41+ public:
42+ // Insert subdag into this DAG
43+ NodeHandle insert_subdag (IRDAG &dag) {
44+ assert (dag.root_nodes .size () == 1 ); // we must have only one root
45+ INodeCacheSet cache;
46+ for (NodeHandle node : nodes.get_handles ()) {
47+ cache.insert (node);
48+ }
49+ NodeHandle root = *dag.root_nodes .begin ();
50+ traverse_node_tree_topological (root, [&](NodeHandle cur) {
51+ if (cache.count (cur))
52+ return ;
53+ NodeHandle cloned = cur->clone (*this );
54+ for (OffsetToValue value_offset : cloned->get_value_offsets ()) {
55+ Value &value = cloned->value (value_offset);
56+ if (value.is_node ()) {
57+ auto it = cache.find (value.get_node ());
58+ assert (it != cache.end ()); // node must have been visited before
59+ value = Value (*it);
60+ }
61+ if (value.is_texture ())
62+ value = Value (create_texture (*value.get_texture ()));
63+ }
64+ cache.insert (cloned);
65+ });
66+ return *cache.find (root);
67+ }
68+
69+ NodeHandle clone_node (NodeHandle node) { return node->clone (*this ); }
70+
71+ template <typename ST, typename ... Args>
72+ NodeHandle create_node (Args &&...args) {
73+ return nodes.create <ST>(std::forward<Args>(args)...);
74+ }
75+
76+ };
77+
78+
79+ NBL_FORCE_INLINE void subexpression_elimination_pass (IRDAG &dag) {
80+ INodeCacheSet cache;
81+ for (NodeHandle root : dag.get_roots ()) {
82+ // traverse tree in topological order to properly add first known node of
83+ // the same hash into cache and replace children with cached node
84+ traverse_node_tree_topological (root, [&](NodeHandle cur) {
85+ if (cache.count (cur))
86+ return ;
87+ for (OffsetToValue value_offset : cur->get_value_offsets ()) {
88+ Value &value = cur->value (value_offset);
89+ if (!value.is_node ())
90+ continue ;
91+ auto it = cache.find (value.get_node ());
92+ assert (it != cache.end ()); // node must have been visited before
93+ value = Value (*it);
94+ }
95+ cache.insert (cur);
96+ });
97+ }
98+ dead_strip_pass (dag); // we can now remove unused nodes
99+ }
100+
101+ } // namespace nbl::asset::material_compiler::v2::ir
102+
103+ #endif
0 commit comments