1 | /* |
2 | * Copyright 2015 Google Inc. |
3 | * |
4 | * Use of this source code is governed by a BSD-style license that can be |
5 | * found in the LICENSE file. |
6 | */ |
7 | |
8 | #ifndef SkTTopoSort_DEFINED |
9 | #define SkTTopoSort_DEFINED |
10 | |
11 | #include "include/core/SkRefCnt.h" |
12 | #include "include/private/SkTArray.h" |
13 | |
14 | #ifdef SK_DEBUG |
15 | template <typename T, typename Traits = T> |
16 | void SkTTopoSort_CheckAllUnmarked(const SkTArray<sk_sp<T>>& graph) { |
17 | for (int i = 0; i < graph.count(); ++i) { |
18 | SkASSERT(!Traits::IsTempMarked(graph[i].get())); |
19 | SkASSERT(!Traits::WasOutput(graph[i].get())); |
20 | } |
21 | } |
22 | |
23 | template <typename T, typename Traits = T> |
24 | void SkTTopoSort_CleanExit(const SkTArray<sk_sp<T>>& graph) { |
25 | for (int i = 0; i < graph.count(); ++i) { |
26 | SkASSERT(!Traits::IsTempMarked(graph[i].get())); |
27 | SkASSERT(Traits::WasOutput(graph[i].get())); |
28 | } |
29 | } |
30 | #endif |
31 | |
32 | // Recursively visit a node and all the other nodes it depends on. |
33 | // Return false if there is a loop. |
34 | template <typename T, typename Traits = T> |
35 | bool SkTTopoSort_Visit(T* node, SkTArray<sk_sp<T>>* result) { |
36 | if (Traits::IsTempMarked(node)) { |
37 | // There is a loop. |
38 | return false; |
39 | } |
40 | |
41 | // If the node under consideration has been already been output it means it |
42 | // (and all the nodes it depends on) are already in 'result'. |
43 | if (!Traits::WasOutput(node)) { |
44 | // This node hasn't been output yet. Recursively assess all the |
45 | // nodes it depends on outputing them first. |
46 | Traits::SetTempMark(node); |
47 | for (int i = 0; i < Traits::NumDependencies(node); ++i) { |
48 | if (!SkTTopoSort_Visit<T, Traits>(Traits::Dependency(node, i), result)) { |
49 | return false; |
50 | } |
51 | } |
52 | Traits::Output(node, result->count()); // mark this node as output |
53 | Traits::ResetTempMark(node); |
54 | |
55 | result->push_back(sk_ref_sp(node)); |
56 | } |
57 | |
58 | return true; |
59 | } |
60 | |
61 | // Topologically sort the nodes in 'graph'. For this sort, when node 'i' depends |
62 | // on node 'j' it means node 'j' must appear in the result before node 'i'. |
63 | // A false return value means there was a loop and the contents of 'graph' will |
64 | // be in some arbitrary state. |
65 | // |
66 | // Traits requires: |
67 | // static void Output(T* t, int index) { ... } // 'index' is 't's position in the result |
68 | // static bool WasOutput(const T* t) { ... } |
69 | // |
70 | // static void SetTempMark(T* t) { ... } // transiently used during toposort |
71 | // static void ResetTempMark(T* t) { ... } |
72 | // static bool IsTempMarked(const T* t) { ... } |
73 | // |
74 | // static int NumDependencies(const T* t) { ... } // 't' will be output after all the other - |
75 | // static T* Dependency(T* t, int index) { ... } // nodes on which it depends |
76 | // We'll look on T for these by default, or you can pass a custom Traits type. |
77 | // |
78 | // TODO: potentially add a version that takes a seed node and just outputs that |
79 | // node and all the nodes on which it depends. This could be used to partially |
80 | // flush a GrRenderTask DAG. |
81 | template <typename T, typename Traits = T> |
82 | bool SkTTopoSort(SkTArray<sk_sp<T>>* graph) { |
83 | SkTArray<sk_sp<T>> result; |
84 | |
85 | #ifdef SK_DEBUG |
86 | SkTTopoSort_CheckAllUnmarked<T, Traits>(*graph); |
87 | #endif |
88 | |
89 | result.reserve(graph->count()); |
90 | |
91 | for (int i = 0; i < graph->count(); ++i) { |
92 | if (Traits::WasOutput((*graph)[i].get())) { |
93 | // This node was depended on by some earlier node and has already |
94 | // been output |
95 | continue; |
96 | } |
97 | |
98 | // Output this node after all the nodes it depends on have been output. |
99 | if (!SkTTopoSort_Visit<T, Traits>((*graph)[i].get(), &result)) { |
100 | return false; |
101 | } |
102 | } |
103 | |
104 | SkASSERT(graph->count() == result.count()); |
105 | graph->swap(result); |
106 | |
107 | #ifdef SK_DEBUG |
108 | SkTTopoSort_CleanExit<T, Traits>(*graph); |
109 | #endif |
110 | return true; |
111 | } |
112 | |
113 | #endif |
114 | |