xref: /aosp_15_r20/external/skia/tests/IncrTopoSortTest.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2018 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRefCnt.h"
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkString.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypes.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkDebug.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTArray.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTDArray.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkTSort.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "tests/Test.h"
16*c8dee2aaSAndroid Build Coastguard Worker 
17*c8dee2aaSAndroid Build Coastguard Worker #include <algorithm>
18*c8dee2aaSAndroid Build Coastguard Worker #include <cstdint>
19*c8dee2aaSAndroid Build Coastguard Worker 
20*c8dee2aaSAndroid Build Coastguard Worker using namespace skia_private;
21*c8dee2aaSAndroid Build Coastguard Worker 
22*c8dee2aaSAndroid Build Coastguard Worker // A node in the graph. This corresponds to an opsTask in the MDB world.
23*c8dee2aaSAndroid Build Coastguard Worker class Node : public SkRefCnt {
24*c8dee2aaSAndroid Build Coastguard Worker public:
id() const25*c8dee2aaSAndroid Build Coastguard Worker     char id() const { return fID; }
indexInSort() const26*c8dee2aaSAndroid Build Coastguard Worker     int indexInSort() const { SkASSERT(fIndexInSort >= 0); return fIndexInSort; }
visited() const27*c8dee2aaSAndroid Build Coastguard Worker     bool visited() const { return fVisited; }
28*c8dee2aaSAndroid Build Coastguard Worker 
29*c8dee2aaSAndroid Build Coastguard Worker #ifdef SK_DEBUG
print() const30*c8dee2aaSAndroid Build Coastguard Worker     void print() const {
31*c8dee2aaSAndroid Build Coastguard Worker         SkDebugf("%d: id %c", fIndexInSort, fID);
32*c8dee2aaSAndroid Build Coastguard Worker         if (fNodesIDependOn.size()) {
33*c8dee2aaSAndroid Build Coastguard Worker             SkDebugf(" I depend on (%d): ", fNodesIDependOn.size());
34*c8dee2aaSAndroid Build Coastguard Worker             for (Node* tmp : fNodesIDependOn) {
35*c8dee2aaSAndroid Build Coastguard Worker                 SkDebugf("%c, ", tmp->id());
36*c8dee2aaSAndroid Build Coastguard Worker             }
37*c8dee2aaSAndroid Build Coastguard Worker         }
38*c8dee2aaSAndroid Build Coastguard Worker         if (fNodesThatDependOnMe.size()) {
39*c8dee2aaSAndroid Build Coastguard Worker             SkDebugf(" (%d) depend on me: ", fNodesThatDependOnMe.size());
40*c8dee2aaSAndroid Build Coastguard Worker             for (Node* tmp : fNodesThatDependOnMe) {
41*c8dee2aaSAndroid Build Coastguard Worker                 SkDebugf("%c, ", tmp->id());
42*c8dee2aaSAndroid Build Coastguard Worker             }
43*c8dee2aaSAndroid Build Coastguard Worker         }
44*c8dee2aaSAndroid Build Coastguard Worker         SkDebugf("\n");
45*c8dee2aaSAndroid Build Coastguard Worker     }
46*c8dee2aaSAndroid Build Coastguard Worker #endif
47*c8dee2aaSAndroid Build Coastguard Worker 
validate(skiatest::Reporter * reporter) const48*c8dee2aaSAndroid Build Coastguard Worker     void validate(skiatest::Reporter* reporter) const {
49*c8dee2aaSAndroid Build Coastguard Worker         for (Node* dependedOn : fNodesIDependOn) {
50*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(reporter, dependedOn->indexInSort() < this->indexInSort());
51*c8dee2aaSAndroid Build Coastguard Worker         }
52*c8dee2aaSAndroid Build Coastguard Worker         for (Node* dependent : fNodesThatDependOnMe) {
53*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(reporter, this->indexInSort() < dependent->indexInSort());
54*c8dee2aaSAndroid Build Coastguard Worker         }
55*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, !fVisited); // this flag should only be true w/in depEdges()
56*c8dee2aaSAndroid Build Coastguard Worker     }
57*c8dee2aaSAndroid Build Coastguard Worker 
CompareIndicesGT(Node * const & a,Node * const & b)58*c8dee2aaSAndroid Build Coastguard Worker     static bool CompareIndicesGT(Node* const& a, Node* const& b) {
59*c8dee2aaSAndroid Build Coastguard Worker         return a->indexInSort() > b->indexInSort();
60*c8dee2aaSAndroid Build Coastguard Worker     }
61*c8dee2aaSAndroid Build Coastguard Worker 
numDependents() const62*c8dee2aaSAndroid Build Coastguard Worker     int numDependents() const { return fNodesThatDependOnMe.size(); }
dependent(int index) const63*c8dee2aaSAndroid Build Coastguard Worker     Node* dependent(int index) const {
64*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(0 <= index && index < fNodesThatDependOnMe.size());
65*c8dee2aaSAndroid Build Coastguard Worker         return fNodesThatDependOnMe[index];
66*c8dee2aaSAndroid Build Coastguard Worker     }
67*c8dee2aaSAndroid Build Coastguard Worker 
68*c8dee2aaSAndroid Build Coastguard Worker private:
69*c8dee2aaSAndroid Build Coastguard Worker     friend class Graph;
70*c8dee2aaSAndroid Build Coastguard Worker 
Node(char id)71*c8dee2aaSAndroid Build Coastguard Worker     explicit Node(char id) : fID(id), fIndexInSort(-1), fVisited(false) {}
72*c8dee2aaSAndroid Build Coastguard Worker 
setIndexInSort(int indexInSort)73*c8dee2aaSAndroid Build Coastguard Worker     void setIndexInSort(int indexInSort) { fIndexInSort = indexInSort; }
setVisited(bool visited)74*c8dee2aaSAndroid Build Coastguard Worker     void setVisited(bool visited) { fVisited = visited; }
addDependency(Node * dependedOn)75*c8dee2aaSAndroid Build Coastguard Worker     void addDependency(Node* dependedOn) {
76*c8dee2aaSAndroid Build Coastguard Worker         fNodesIDependOn.push_back(dependedOn);
77*c8dee2aaSAndroid Build Coastguard Worker 
78*c8dee2aaSAndroid Build Coastguard Worker         dependedOn->addDependent(this);
79*c8dee2aaSAndroid Build Coastguard Worker     }
addDependent(Node * dependent)80*c8dee2aaSAndroid Build Coastguard Worker     void addDependent(Node* dependent) {
81*c8dee2aaSAndroid Build Coastguard Worker         fNodesThatDependOnMe.push_back(dependent);
82*c8dee2aaSAndroid Build Coastguard Worker     }
83*c8dee2aaSAndroid Build Coastguard Worker 
84*c8dee2aaSAndroid Build Coastguard Worker     char             fID;
85*c8dee2aaSAndroid Build Coastguard Worker     SkTDArray<Node*> fNodesIDependOn;      // These nodes must appear before this one in the sort
86*c8dee2aaSAndroid Build Coastguard Worker     SkTDArray<Node*> fNodesThatDependOnMe; // These ones must appear after this one
87*c8dee2aaSAndroid Build Coastguard Worker     int              fIndexInSort;
88*c8dee2aaSAndroid Build Coastguard Worker     bool             fVisited;             // only used in addEdges()
89*c8dee2aaSAndroid Build Coastguard Worker };
90*c8dee2aaSAndroid Build Coastguard Worker 
91*c8dee2aaSAndroid Build Coastguard Worker // The DAG driving the incremental topological sort. This corresponds to the opsTask DAG in
92*c8dee2aaSAndroid Build Coastguard Worker // the MDB world.
93*c8dee2aaSAndroid Build Coastguard Worker class Graph {
94*c8dee2aaSAndroid Build Coastguard Worker public:
Graph(int numNodesToReserve,skiatest::Reporter * reporter)95*c8dee2aaSAndroid Build Coastguard Worker     Graph(int numNodesToReserve, skiatest::Reporter* reporter)
96*c8dee2aaSAndroid Build Coastguard Worker             : fNodes(numNodesToReserve)
97*c8dee2aaSAndroid Build Coastguard Worker             , fReporter(reporter) {
98*c8dee2aaSAndroid Build Coastguard Worker     }
99*c8dee2aaSAndroid Build Coastguard Worker 
addNode(uint32_t id)100*c8dee2aaSAndroid Build Coastguard Worker     Node* addNode(uint32_t id) {
101*c8dee2aaSAndroid Build Coastguard Worker         this->validate();
102*c8dee2aaSAndroid Build Coastguard Worker         sk_sp<Node> tmp(new Node(id));
103*c8dee2aaSAndroid Build Coastguard Worker 
104*c8dee2aaSAndroid Build Coastguard Worker         fNodes.push_back(tmp);       // The graph gets the creation ref
105*c8dee2aaSAndroid Build Coastguard Worker         tmp->setIndexInSort(fNodes.size()-1);
106*c8dee2aaSAndroid Build Coastguard Worker         this->validate();
107*c8dee2aaSAndroid Build Coastguard Worker         return tmp.get();
108*c8dee2aaSAndroid Build Coastguard Worker     }
109*c8dee2aaSAndroid Build Coastguard Worker 
110*c8dee2aaSAndroid Build Coastguard Worker     // 'dependedOn' must appear before 'dependent' in the sort
addEdge(Node * dependedOn,Node * dependent)111*c8dee2aaSAndroid Build Coastguard Worker     void addEdge(Node* dependedOn, Node* dependent) {
112*c8dee2aaSAndroid Build Coastguard Worker         // TODO: this would be faster if all the SkTDArray code was stripped out of
113*c8dee2aaSAndroid Build Coastguard Worker         // addEdges but, when used in MDB sorting, this entry point will never be used.
114*c8dee2aaSAndroid Build Coastguard Worker         SkTDArray<Node*> tmp(&dependedOn, 1);
115*c8dee2aaSAndroid Build Coastguard Worker         this->addEdges(&tmp, dependent);
116*c8dee2aaSAndroid Build Coastguard Worker     }
117*c8dee2aaSAndroid Build Coastguard Worker 
118*c8dee2aaSAndroid Build Coastguard Worker     // All the nodes in 'dependedOn' must appear before 'dependent' in the sort.
119*c8dee2aaSAndroid Build Coastguard Worker     // This is O(v + e + cost_of_sorting(b)) where:
120*c8dee2aaSAndroid Build Coastguard Worker     //    v: number of nodes
121*c8dee2aaSAndroid Build Coastguard Worker     //    e: number of edges
122*c8dee2aaSAndroid Build Coastguard Worker     //    b: number of new edges in 'dependedOn'
123*c8dee2aaSAndroid Build Coastguard Worker     //
124*c8dee2aaSAndroid Build Coastguard Worker     // The algorithm works by first finding the "affected region" that contains all the
125*c8dee2aaSAndroid Build Coastguard Worker     // nodes whose position in the topological sort is invalidated by the addition of the new
126*c8dee2aaSAndroid Build Coastguard Worker     // edges. It then traverses the affected region from left to right, temporarily removing
127*c8dee2aaSAndroid Build Coastguard Worker     // invalid nodes from 'fNodes' and shifting valid nodes left to fill in the gaps. In this
128*c8dee2aaSAndroid Build Coastguard Worker     // left to right traversal, when a node is shifted to the left the current set of invalid
129*c8dee2aaSAndroid Build Coastguard Worker     // nodes is examined to see if any needed to be moved to the right of that node. If so,
130*c8dee2aaSAndroid Build Coastguard Worker     // they are reintroduced to the 'fNodes' array but now in the appropriate position. The
131*c8dee2aaSAndroid Build Coastguard Worker     // separation of the algorithm into search (the dfs method) and readjustment (the shift
132*c8dee2aaSAndroid Build Coastguard Worker     // method) means that each node affected by the new edges is only ever moved once.
addEdges(SkTDArray<Node * > * dependedOn,Node * dependent)133*c8dee2aaSAndroid Build Coastguard Worker     void addEdges(SkTDArray<Node*>* dependedOn, Node* dependent) {
134*c8dee2aaSAndroid Build Coastguard Worker         this->validate();
135*c8dee2aaSAndroid Build Coastguard Worker 
136*c8dee2aaSAndroid Build Coastguard Worker         // remove any of the new dependencies that are already satisfied
137*c8dee2aaSAndroid Build Coastguard Worker         for (int i = 0; i < dependedOn->size(); ++i) {
138*c8dee2aaSAndroid Build Coastguard Worker             if ((*dependedOn)[i]->indexInSort() < dependent->indexInSort()) {
139*c8dee2aaSAndroid Build Coastguard Worker                 dependent->addDependency((*dependedOn)[i]);
140*c8dee2aaSAndroid Build Coastguard Worker                 dependedOn->removeShuffle(i);
141*c8dee2aaSAndroid Build Coastguard Worker                 i--;
142*c8dee2aaSAndroid Build Coastguard Worker             } else {
143*c8dee2aaSAndroid Build Coastguard Worker                 dependent->addDependency((*dependedOn)[i]);
144*c8dee2aaSAndroid Build Coastguard Worker             }
145*c8dee2aaSAndroid Build Coastguard Worker         }
146*c8dee2aaSAndroid Build Coastguard Worker 
147*c8dee2aaSAndroid Build Coastguard Worker         if (dependedOn->empty()) {
148*c8dee2aaSAndroid Build Coastguard Worker             return;
149*c8dee2aaSAndroid Build Coastguard Worker         }
150*c8dee2aaSAndroid Build Coastguard Worker 
151*c8dee2aaSAndroid Build Coastguard Worker         // Sort the remaining dependencies into descending order based on their indices in the
152*c8dee2aaSAndroid Build Coastguard Worker         // sort. This means that we will be proceeding from right to left in the sort when
153*c8dee2aaSAndroid Build Coastguard Worker         // correcting the order.
154*c8dee2aaSAndroid Build Coastguard Worker         // TODO: QSort is waaay overkill here!
155*c8dee2aaSAndroid Build Coastguard Worker         SkTQSort<Node*>(dependedOn->begin(), dependedOn->end(), Node::CompareIndicesGT);
156*c8dee2aaSAndroid Build Coastguard Worker 
157*c8dee2aaSAndroid Build Coastguard Worker         // TODO: although this is the general algorithm, I think this can be simplified for our
158*c8dee2aaSAndroid Build Coastguard Worker         // use case (i.e., the same dependent for all the new edges).
159*c8dee2aaSAndroid Build Coastguard Worker 
160*c8dee2aaSAndroid Build Coastguard Worker         int lowerBound = fNodes.size();  // 'lowerBound' tracks the left of the affected region
161*c8dee2aaSAndroid Build Coastguard Worker         for (int i = 0; i < dependedOn->size(); ++i) {
162*c8dee2aaSAndroid Build Coastguard Worker             if ((*dependedOn)[i]->indexInSort() < lowerBound) {
163*c8dee2aaSAndroid Build Coastguard Worker                 this->shift(lowerBound);
164*c8dee2aaSAndroid Build Coastguard Worker             }
165*c8dee2aaSAndroid Build Coastguard Worker 
166*c8dee2aaSAndroid Build Coastguard Worker             if (!dependent->visited()) {
167*c8dee2aaSAndroid Build Coastguard Worker                 this->dfs(dependent, (*dependedOn)[i]->indexInSort());
168*c8dee2aaSAndroid Build Coastguard Worker             }
169*c8dee2aaSAndroid Build Coastguard Worker 
170*c8dee2aaSAndroid Build Coastguard Worker             lowerBound = std::min(dependent->indexInSort(), lowerBound);
171*c8dee2aaSAndroid Build Coastguard Worker         }
172*c8dee2aaSAndroid Build Coastguard Worker 
173*c8dee2aaSAndroid Build Coastguard Worker         this->shift(lowerBound);
174*c8dee2aaSAndroid Build Coastguard Worker 
175*c8dee2aaSAndroid Build Coastguard Worker         this->validate();
176*c8dee2aaSAndroid Build Coastguard Worker     }
177*c8dee2aaSAndroid Build Coastguard Worker 
178*c8dee2aaSAndroid Build Coastguard Worker     // Get the list of node ids in the current sorted order
getActual(SkString * actual) const179*c8dee2aaSAndroid Build Coastguard Worker     void getActual(SkString* actual) const {
180*c8dee2aaSAndroid Build Coastguard Worker         this->validate();
181*c8dee2aaSAndroid Build Coastguard Worker 
182*c8dee2aaSAndroid Build Coastguard Worker         for (int i = 0; i < fNodes.size(); ++i) {
183*c8dee2aaSAndroid Build Coastguard Worker             (*actual) += fNodes[i]->id();
184*c8dee2aaSAndroid Build Coastguard Worker             if (i < fNodes.size()-1) {
185*c8dee2aaSAndroid Build Coastguard Worker                 (*actual) += ',';
186*c8dee2aaSAndroid Build Coastguard Worker             }
187*c8dee2aaSAndroid Build Coastguard Worker         }
188*c8dee2aaSAndroid Build Coastguard Worker     }
189*c8dee2aaSAndroid Build Coastguard Worker 
190*c8dee2aaSAndroid Build Coastguard Worker #ifdef SK_DEBUG
print() const191*c8dee2aaSAndroid Build Coastguard Worker     void print() const {
192*c8dee2aaSAndroid Build Coastguard Worker         SkDebugf("-------------------\n");
193*c8dee2aaSAndroid Build Coastguard Worker         for (int i = 0; i < fNodes.size(); ++i) {
194*c8dee2aaSAndroid Build Coastguard Worker             if (fNodes[i]) {
195*c8dee2aaSAndroid Build Coastguard Worker                 SkDebugf("%c ", fNodes[i]->id());
196*c8dee2aaSAndroid Build Coastguard Worker             } else {
197*c8dee2aaSAndroid Build Coastguard Worker                 SkDebugf("0 ");
198*c8dee2aaSAndroid Build Coastguard Worker             }
199*c8dee2aaSAndroid Build Coastguard Worker         }
200*c8dee2aaSAndroid Build Coastguard Worker         SkDebugf("\n");
201*c8dee2aaSAndroid Build Coastguard Worker 
202*c8dee2aaSAndroid Build Coastguard Worker         for (int i = 0; i < fNodes.size(); ++i) {
203*c8dee2aaSAndroid Build Coastguard Worker             if (fNodes[i]) {
204*c8dee2aaSAndroid Build Coastguard Worker                 fNodes[i]->print();
205*c8dee2aaSAndroid Build Coastguard Worker             }
206*c8dee2aaSAndroid Build Coastguard Worker         }
207*c8dee2aaSAndroid Build Coastguard Worker 
208*c8dee2aaSAndroid Build Coastguard Worker         SkDebugf("Stack: ");
209*c8dee2aaSAndroid Build Coastguard Worker         for (int i = 0; i < fStack.size(); ++i) {
210*c8dee2aaSAndroid Build Coastguard Worker            SkDebugf("%c/%c ", fStack[i].fNode->id(), fStack[i].fDest->id());
211*c8dee2aaSAndroid Build Coastguard Worker         }
212*c8dee2aaSAndroid Build Coastguard Worker         SkDebugf("\n");
213*c8dee2aaSAndroid Build Coastguard Worker     }
214*c8dee2aaSAndroid Build Coastguard Worker #endif
215*c8dee2aaSAndroid Build Coastguard Worker 
216*c8dee2aaSAndroid Build Coastguard Worker private:
validate() const217*c8dee2aaSAndroid Build Coastguard Worker     void validate() const {
218*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(fReporter, fStack.empty());
219*c8dee2aaSAndroid Build Coastguard Worker 
220*c8dee2aaSAndroid Build Coastguard Worker         for (int i = 0; i < fNodes.size(); ++i) {
221*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(fReporter, fNodes[i]->indexInSort() == i);
222*c8dee2aaSAndroid Build Coastguard Worker 
223*c8dee2aaSAndroid Build Coastguard Worker             fNodes[i]->validate(fReporter);
224*c8dee2aaSAndroid Build Coastguard Worker         }
225*c8dee2aaSAndroid Build Coastguard Worker 
226*c8dee2aaSAndroid Build Coastguard Worker         // All the nodes in the Queue had better have been marked as visited
227*c8dee2aaSAndroid Build Coastguard Worker         for (int i = 0; i < fStack.size(); ++i) {
228*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(fStack[i].fNode->visited());
229*c8dee2aaSAndroid Build Coastguard Worker         }
230*c8dee2aaSAndroid Build Coastguard Worker     }
231*c8dee2aaSAndroid Build Coastguard Worker 
232*c8dee2aaSAndroid Build Coastguard Worker     // Collect the nodes that need to be moved within the affected region. All the nodes found
233*c8dee2aaSAndroid Build Coastguard Worker     // to be in violation of the topological constraints are placed in 'fStack'.
dfs(Node * node,int upperBound)234*c8dee2aaSAndroid Build Coastguard Worker     void dfs(Node* node, int upperBound) {
235*c8dee2aaSAndroid Build Coastguard Worker         node->setVisited(true);
236*c8dee2aaSAndroid Build Coastguard Worker 
237*c8dee2aaSAndroid Build Coastguard Worker         for (int i = 0; i < node->numDependents(); ++i) {
238*c8dee2aaSAndroid Build Coastguard Worker             Node* dependent = node->dependent(i);
239*c8dee2aaSAndroid Build Coastguard Worker 
240*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(dependent->indexInSort() != upperBound); // this would be a cycle
241*c8dee2aaSAndroid Build Coastguard Worker 
242*c8dee2aaSAndroid Build Coastguard Worker             if (!dependent->visited() && dependent->indexInSort() < upperBound) {
243*c8dee2aaSAndroid Build Coastguard Worker                 this->dfs(dependent, upperBound);
244*c8dee2aaSAndroid Build Coastguard Worker             }
245*c8dee2aaSAndroid Build Coastguard Worker         }
246*c8dee2aaSAndroid Build Coastguard Worker 
247*c8dee2aaSAndroid Build Coastguard Worker         fStack.push_back({ sk_ref_sp(node), fNodes[upperBound].get() });
248*c8dee2aaSAndroid Build Coastguard Worker     }
249*c8dee2aaSAndroid Build Coastguard Worker 
250*c8dee2aaSAndroid Build Coastguard Worker     // Move 'node' to the index-th slot of the sort. The index-th slot should not have a current
251*c8dee2aaSAndroid Build Coastguard Worker     // occupant.
moveNodeInSort(const sk_sp<Node> & node,int index)252*c8dee2aaSAndroid Build Coastguard Worker     void moveNodeInSort(const sk_sp<Node>& node, int index) {
253*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(!fNodes[index]);
254*c8dee2aaSAndroid Build Coastguard Worker         fNodes[index] = node;
255*c8dee2aaSAndroid Build Coastguard Worker         node->setIndexInSort(index);
256*c8dee2aaSAndroid Build Coastguard Worker     }
257*c8dee2aaSAndroid Build Coastguard Worker 
258*c8dee2aaSAndroid Build Coastguard Worker #ifdef SK_DEBUG
259*c8dee2aaSAndroid Build Coastguard Worker     // Does 'fStack' have 'node'? That is, was 'node' discovered to be in violation of the
260*c8dee2aaSAndroid Build Coastguard Worker     // topological constraints?
stackContains(Node * node)261*c8dee2aaSAndroid Build Coastguard Worker     bool stackContains(Node* node) {
262*c8dee2aaSAndroid Build Coastguard Worker         for (int i = 0; i < fStack.size(); ++i) {
263*c8dee2aaSAndroid Build Coastguard Worker             if (node == fStack[i].fNode.get()) {
264*c8dee2aaSAndroid Build Coastguard Worker                 return true;
265*c8dee2aaSAndroid Build Coastguard Worker             }
266*c8dee2aaSAndroid Build Coastguard Worker         }
267*c8dee2aaSAndroid Build Coastguard Worker         return false;
268*c8dee2aaSAndroid Build Coastguard Worker     }
269*c8dee2aaSAndroid Build Coastguard Worker #endif
270*c8dee2aaSAndroid Build Coastguard Worker 
271*c8dee2aaSAndroid Build Coastguard Worker     // The 'shift' method iterates through the affected area from left to right moving Nodes that
272*c8dee2aaSAndroid Build Coastguard Worker     // were found to be in violation of the topological order (i.e., in 'fStack') to their correct
273*c8dee2aaSAndroid Build Coastguard Worker     // locations and shifting the non-violating nodes left, into the holes the violating nodes left
274*c8dee2aaSAndroid Build Coastguard Worker     // behind.
shift(int index)275*c8dee2aaSAndroid Build Coastguard Worker     void shift(int index) {
276*c8dee2aaSAndroid Build Coastguard Worker         int numRemoved = 0;
277*c8dee2aaSAndroid Build Coastguard Worker         while (!fStack.empty()) {
278*c8dee2aaSAndroid Build Coastguard Worker             sk_sp<Node> node = fNodes[index];
279*c8dee2aaSAndroid Build Coastguard Worker 
280*c8dee2aaSAndroid Build Coastguard Worker             if (node->visited()) {
281*c8dee2aaSAndroid Build Coastguard Worker                 // This node is in 'fStack' and was found to be in violation of the topological
282*c8dee2aaSAndroid Build Coastguard Worker                 // constraints. Remove it from 'fNodes' so non-violating nodes can be shifted
283*c8dee2aaSAndroid Build Coastguard Worker                 // left.
284*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(this->stackContains(node.get()));
285*c8dee2aaSAndroid Build Coastguard Worker                 node->setVisited(false);  // reset for future use
286*c8dee2aaSAndroid Build Coastguard Worker                 fNodes[index] = nullptr;
287*c8dee2aaSAndroid Build Coastguard Worker                 numRemoved++;
288*c8dee2aaSAndroid Build Coastguard Worker             } else {
289*c8dee2aaSAndroid Build Coastguard Worker                 // This node was found to not be in violation of any topological constraints but
290*c8dee2aaSAndroid Build Coastguard Worker                 // must be moved left to fill in for those that were.
291*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(!this->stackContains(node.get()));
292*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(numRemoved); // should be moving left
293*c8dee2aaSAndroid Build Coastguard Worker 
294*c8dee2aaSAndroid Build Coastguard Worker                 this->moveNodeInSort(node, index - numRemoved);
295*c8dee2aaSAndroid Build Coastguard Worker                 fNodes[index] = nullptr;
296*c8dee2aaSAndroid Build Coastguard Worker             }
297*c8dee2aaSAndroid Build Coastguard Worker 
298*c8dee2aaSAndroid Build Coastguard Worker             while (!fStack.empty() && node.get() == fStack.back().fDest) {
299*c8dee2aaSAndroid Build Coastguard Worker                 // The left to right loop has finally encountered the destination for one or more
300*c8dee2aaSAndroid Build Coastguard Worker                 // of the nodes in 'fStack'. Place them to the right of 'node' in the sort. Note
301*c8dee2aaSAndroid Build Coastguard Worker                 // that because the violating nodes were already removed from 'fNodes' there
302*c8dee2aaSAndroid Build Coastguard Worker                 // should be enough empty space for them to be placed now.
303*c8dee2aaSAndroid Build Coastguard Worker                 numRemoved--;
304*c8dee2aaSAndroid Build Coastguard Worker 
305*c8dee2aaSAndroid Build Coastguard Worker                 this->moveNodeInSort(fStack.back().fNode, index - numRemoved);
306*c8dee2aaSAndroid Build Coastguard Worker 
307*c8dee2aaSAndroid Build Coastguard Worker                 fStack.pop_back();
308*c8dee2aaSAndroid Build Coastguard Worker             }
309*c8dee2aaSAndroid Build Coastguard Worker 
310*c8dee2aaSAndroid Build Coastguard Worker             index++;
311*c8dee2aaSAndroid Build Coastguard Worker         }
312*c8dee2aaSAndroid Build Coastguard Worker     }
313*c8dee2aaSAndroid Build Coastguard Worker 
314*c8dee2aaSAndroid Build Coastguard Worker     TArray<sk_sp<Node>> fNodes;
315*c8dee2aaSAndroid Build Coastguard Worker 
316*c8dee2aaSAndroid Build Coastguard Worker     struct StackInfo {
317*c8dee2aaSAndroid Build Coastguard Worker         sk_sp<Node> fNode;  // This gets a ref bc, in 'shift' it will be pulled out of 'fNodes'
318*c8dee2aaSAndroid Build Coastguard Worker         Node*       fDest;
319*c8dee2aaSAndroid Build Coastguard Worker     };
320*c8dee2aaSAndroid Build Coastguard Worker 
321*c8dee2aaSAndroid Build Coastguard Worker     TArray<StackInfo>   fStack;     // only used in addEdges()
322*c8dee2aaSAndroid Build Coastguard Worker 
323*c8dee2aaSAndroid Build Coastguard Worker     skiatest::Reporter*   fReporter;
324*c8dee2aaSAndroid Build Coastguard Worker };
325*c8dee2aaSAndroid Build Coastguard Worker 
326*c8dee2aaSAndroid Build Coastguard Worker // This test adds a single invalidating edge.
test_1(skiatest::Reporter * reporter)327*c8dee2aaSAndroid Build Coastguard Worker static void test_1(skiatest::Reporter* reporter) {
328*c8dee2aaSAndroid Build Coastguard Worker     Graph g(10, reporter);
329*c8dee2aaSAndroid Build Coastguard Worker 
330*c8dee2aaSAndroid Build Coastguard Worker     Node* nodeQ = g.addNode('q');
331*c8dee2aaSAndroid Build Coastguard Worker     Node* nodeY = g.addNode('y');
332*c8dee2aaSAndroid Build Coastguard Worker     Node* nodeA = g.addNode('a');
333*c8dee2aaSAndroid Build Coastguard Worker     Node* nodeZ = g.addNode('z');
334*c8dee2aaSAndroid Build Coastguard Worker     Node* nodeB = g.addNode('b');
335*c8dee2aaSAndroid Build Coastguard Worker     /*Node* nodeC =*/ g.addNode('c');
336*c8dee2aaSAndroid Build Coastguard Worker     Node* nodeW = g.addNode('w');
337*c8dee2aaSAndroid Build Coastguard Worker     Node* nodeD = g.addNode('d');
338*c8dee2aaSAndroid Build Coastguard Worker     Node* nodeX = g.addNode('x');
339*c8dee2aaSAndroid Build Coastguard Worker     Node* nodeR = g.addNode('r');
340*c8dee2aaSAndroid Build Coastguard Worker 
341*c8dee2aaSAndroid Build Coastguard Worker     // All these edge are non-invalidating
342*c8dee2aaSAndroid Build Coastguard Worker     g.addEdge(nodeD, nodeR);
343*c8dee2aaSAndroid Build Coastguard Worker     g.addEdge(nodeZ, nodeW);
344*c8dee2aaSAndroid Build Coastguard Worker     g.addEdge(nodeA, nodeB);
345*c8dee2aaSAndroid Build Coastguard Worker     g.addEdge(nodeY, nodeZ);
346*c8dee2aaSAndroid Build Coastguard Worker     g.addEdge(nodeQ, nodeA);
347*c8dee2aaSAndroid Build Coastguard Worker 
348*c8dee2aaSAndroid Build Coastguard Worker     {
349*c8dee2aaSAndroid Build Coastguard Worker         const SkString kExpectedInitialState("q,y,a,z,b,c,w,d,x,r");
350*c8dee2aaSAndroid Build Coastguard Worker         SkString actualInitialState;
351*c8dee2aaSAndroid Build Coastguard Worker         g.getActual(&actualInitialState);
352*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, kExpectedInitialState == actualInitialState);
353*c8dee2aaSAndroid Build Coastguard Worker     }
354*c8dee2aaSAndroid Build Coastguard Worker 
355*c8dee2aaSAndroid Build Coastguard Worker     // Add the invalidating edge
356*c8dee2aaSAndroid Build Coastguard Worker     g.addEdge(nodeX, nodeY);
357*c8dee2aaSAndroid Build Coastguard Worker 
358*c8dee2aaSAndroid Build Coastguard Worker     {
359*c8dee2aaSAndroid Build Coastguard Worker         const SkString kExpectedFinalState("q,a,b,c,d,x,y,z,w,r");
360*c8dee2aaSAndroid Build Coastguard Worker         SkString actualFinalState;
361*c8dee2aaSAndroid Build Coastguard Worker         g.getActual(&actualFinalState);
362*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, kExpectedFinalState == actualFinalState);
363*c8dee2aaSAndroid Build Coastguard Worker     }
364*c8dee2aaSAndroid Build Coastguard Worker }
365*c8dee2aaSAndroid Build Coastguard Worker 
366*c8dee2aaSAndroid Build Coastguard Worker // This test adds two invalidating edge sequentially
test_2(skiatest::Reporter * reporter)367*c8dee2aaSAndroid Build Coastguard Worker static void test_2(skiatest::Reporter* reporter) {
368*c8dee2aaSAndroid Build Coastguard Worker     Graph g(10, reporter);
369*c8dee2aaSAndroid Build Coastguard Worker 
370*c8dee2aaSAndroid Build Coastguard Worker     Node* nodeY = g.addNode('y');
371*c8dee2aaSAndroid Build Coastguard Worker     /*Node* nodeA =*/ g.addNode('a');
372*c8dee2aaSAndroid Build Coastguard Worker     Node* nodeW = g.addNode('w');
373*c8dee2aaSAndroid Build Coastguard Worker     /*Node* nodeB =*/ g.addNode('b');
374*c8dee2aaSAndroid Build Coastguard Worker     Node* nodeZ = g.addNode('z');
375*c8dee2aaSAndroid Build Coastguard Worker     Node* nodeU = g.addNode('u');
376*c8dee2aaSAndroid Build Coastguard Worker     /*Node* nodeC =*/ g.addNode('c');
377*c8dee2aaSAndroid Build Coastguard Worker     Node* nodeX = g.addNode('x');
378*c8dee2aaSAndroid Build Coastguard Worker     /*Node* nodeD =*/ g.addNode('d');
379*c8dee2aaSAndroid Build Coastguard Worker     Node* nodeV = g.addNode('v');
380*c8dee2aaSAndroid Build Coastguard Worker 
381*c8dee2aaSAndroid Build Coastguard Worker     // All these edge are non-invalidating
382*c8dee2aaSAndroid Build Coastguard Worker     g.addEdge(nodeU, nodeX);
383*c8dee2aaSAndroid Build Coastguard Worker     g.addEdge(nodeW, nodeU);
384*c8dee2aaSAndroid Build Coastguard Worker     g.addEdge(nodeW, nodeZ);
385*c8dee2aaSAndroid Build Coastguard Worker     g.addEdge(nodeY, nodeZ);
386*c8dee2aaSAndroid Build Coastguard Worker 
387*c8dee2aaSAndroid Build Coastguard Worker     {
388*c8dee2aaSAndroid Build Coastguard Worker         const SkString kExpectedInitialState("y,a,w,b,z,u,c,x,d,v");
389*c8dee2aaSAndroid Build Coastguard Worker         SkString actualInitialState;
390*c8dee2aaSAndroid Build Coastguard Worker         g.getActual(&actualInitialState);
391*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, kExpectedInitialState == actualInitialState);
392*c8dee2aaSAndroid Build Coastguard Worker     }
393*c8dee2aaSAndroid Build Coastguard Worker 
394*c8dee2aaSAndroid Build Coastguard Worker     // Add the first invalidating edge
395*c8dee2aaSAndroid Build Coastguard Worker     g.addEdge(nodeX, nodeY);
396*c8dee2aaSAndroid Build Coastguard Worker 
397*c8dee2aaSAndroid Build Coastguard Worker     {
398*c8dee2aaSAndroid Build Coastguard Worker         const SkString kExpectedFirstState("a,w,b,u,c,x,y,z,d,v");
399*c8dee2aaSAndroid Build Coastguard Worker         SkString actualFirstState;
400*c8dee2aaSAndroid Build Coastguard Worker         g.getActual(&actualFirstState);
401*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, kExpectedFirstState == actualFirstState);
402*c8dee2aaSAndroid Build Coastguard Worker     }
403*c8dee2aaSAndroid Build Coastguard Worker 
404*c8dee2aaSAndroid Build Coastguard Worker     // Add the second invalidating edge
405*c8dee2aaSAndroid Build Coastguard Worker     g.addEdge(nodeV, nodeW);
406*c8dee2aaSAndroid Build Coastguard Worker 
407*c8dee2aaSAndroid Build Coastguard Worker     {
408*c8dee2aaSAndroid Build Coastguard Worker         const SkString kExpectedSecondState("a,b,c,d,v,w,u,x,y,z");
409*c8dee2aaSAndroid Build Coastguard Worker         SkString actualSecondState;
410*c8dee2aaSAndroid Build Coastguard Worker         g.getActual(&actualSecondState);
411*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, kExpectedSecondState == actualSecondState);
412*c8dee2aaSAndroid Build Coastguard Worker     }
413*c8dee2aaSAndroid Build Coastguard Worker }
414*c8dee2aaSAndroid Build Coastguard Worker 
test_diamond(skiatest::Reporter * reporter)415*c8dee2aaSAndroid Build Coastguard Worker static void test_diamond(skiatest::Reporter* reporter) {
416*c8dee2aaSAndroid Build Coastguard Worker     Graph g(4, reporter);
417*c8dee2aaSAndroid Build Coastguard Worker 
418*c8dee2aaSAndroid Build Coastguard Worker     /* Create the graph (the '.' is the pointy side of the arrow):
419*c8dee2aaSAndroid Build Coastguard Worker      *        b
420*c8dee2aaSAndroid Build Coastguard Worker      *      .    \
421*c8dee2aaSAndroid Build Coastguard Worker      *     /      .
422*c8dee2aaSAndroid Build Coastguard Worker      *   a          d
423*c8dee2aaSAndroid Build Coastguard Worker      *     \      .
424*c8dee2aaSAndroid Build Coastguard Worker      *       .   /
425*c8dee2aaSAndroid Build Coastguard Worker      *         c
426*c8dee2aaSAndroid Build Coastguard Worker      * Possible topological orders are [a,c,b,d] and [a,b,c,d].
427*c8dee2aaSAndroid Build Coastguard Worker      */
428*c8dee2aaSAndroid Build Coastguard Worker 
429*c8dee2aaSAndroid Build Coastguard Worker     Node* nodeD = g.addNode('d');
430*c8dee2aaSAndroid Build Coastguard Worker     Node* nodeC = g.addNode('c');
431*c8dee2aaSAndroid Build Coastguard Worker     Node* nodeB = g.addNode('b');
432*c8dee2aaSAndroid Build Coastguard Worker 
433*c8dee2aaSAndroid Build Coastguard Worker     {
434*c8dee2aaSAndroid Build Coastguard Worker         SkTDArray<Node*> dependedOn;
435*c8dee2aaSAndroid Build Coastguard Worker         dependedOn.push_back(nodeB);
436*c8dee2aaSAndroid Build Coastguard Worker         dependedOn.push_back(nodeC);
437*c8dee2aaSAndroid Build Coastguard Worker 
438*c8dee2aaSAndroid Build Coastguard Worker         g.addEdges(&dependedOn, nodeD); // nodes B and C must come before node D
439*c8dee2aaSAndroid Build Coastguard Worker     }
440*c8dee2aaSAndroid Build Coastguard Worker 
441*c8dee2aaSAndroid Build Coastguard Worker     Node* nodeA = g.addNode('a');
442*c8dee2aaSAndroid Build Coastguard Worker     g.addEdge(nodeA, nodeB);            // node A must come before node B
443*c8dee2aaSAndroid Build Coastguard Worker     g.addEdge(nodeA, nodeC);            // node A must come before node C
444*c8dee2aaSAndroid Build Coastguard Worker 
445*c8dee2aaSAndroid Build Coastguard Worker     const SkString kExpected0("a,c,b,d");
446*c8dee2aaSAndroid Build Coastguard Worker     const SkString kExpected1("a,b,c,d");
447*c8dee2aaSAndroid Build Coastguard Worker     SkString actual;
448*c8dee2aaSAndroid Build Coastguard Worker     g.getActual(&actual);
449*c8dee2aaSAndroid Build Coastguard Worker 
450*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, kExpected0 == actual || kExpected1 == actual);
451*c8dee2aaSAndroid Build Coastguard Worker }
452*c8dee2aaSAndroid Build Coastguard Worker 
test_lopsided_binary_tree(skiatest::Reporter * reporter)453*c8dee2aaSAndroid Build Coastguard Worker static void test_lopsided_binary_tree(skiatest::Reporter* reporter) {
454*c8dee2aaSAndroid Build Coastguard Worker     Graph g(7, reporter);
455*c8dee2aaSAndroid Build Coastguard Worker 
456*c8dee2aaSAndroid Build Coastguard Worker     /* Create the graph (the '.' is the pointy side of the arrow):
457*c8dee2aaSAndroid Build Coastguard Worker      *        a
458*c8dee2aaSAndroid Build Coastguard Worker      *       /  \
459*c8dee2aaSAndroid Build Coastguard Worker      *      .    .
460*c8dee2aaSAndroid Build Coastguard Worker      *     b      c
461*c8dee2aaSAndroid Build Coastguard Worker      *           /  \
462*c8dee2aaSAndroid Build Coastguard Worker      *          .    .
463*c8dee2aaSAndroid Build Coastguard Worker      *         d      e
464*c8dee2aaSAndroid Build Coastguard Worker      *               /  \
465*c8dee2aaSAndroid Build Coastguard Worker      *              .    .
466*c8dee2aaSAndroid Build Coastguard Worker      *             f      g
467*c8dee2aaSAndroid Build Coastguard Worker      *
468*c8dee2aaSAndroid Build Coastguard Worker      * Possible topological order is: [a,b,c,d,e,f,g].
469*c8dee2aaSAndroid Build Coastguard Worker      */
470*c8dee2aaSAndroid Build Coastguard Worker 
471*c8dee2aaSAndroid Build Coastguard Worker     Node* nodeG = g.addNode('g');
472*c8dee2aaSAndroid Build Coastguard Worker     Node* nodeF = g.addNode('f');
473*c8dee2aaSAndroid Build Coastguard Worker     Node* nodeE = g.addNode('e');
474*c8dee2aaSAndroid Build Coastguard Worker     Node* nodeD = g.addNode('d');
475*c8dee2aaSAndroid Build Coastguard Worker     Node* nodeC = g.addNode('c');
476*c8dee2aaSAndroid Build Coastguard Worker     Node* nodeB = g.addNode('b');
477*c8dee2aaSAndroid Build Coastguard Worker     Node* nodeA = g.addNode('a');
478*c8dee2aaSAndroid Build Coastguard Worker 
479*c8dee2aaSAndroid Build Coastguard Worker     g.addEdge(nodeE, nodeG);
480*c8dee2aaSAndroid Build Coastguard Worker     g.addEdge(nodeE, nodeF);
481*c8dee2aaSAndroid Build Coastguard Worker     g.addEdge(nodeC, nodeE);
482*c8dee2aaSAndroid Build Coastguard Worker     g.addEdge(nodeC, nodeD);
483*c8dee2aaSAndroid Build Coastguard Worker     g.addEdge(nodeA, nodeC);
484*c8dee2aaSAndroid Build Coastguard Worker     g.addEdge(nodeA, nodeB);
485*c8dee2aaSAndroid Build Coastguard Worker 
486*c8dee2aaSAndroid Build Coastguard Worker     const SkString kExpected("a,b,c,d,e,f,g");
487*c8dee2aaSAndroid Build Coastguard Worker     SkString actual;
488*c8dee2aaSAndroid Build Coastguard Worker     g.getActual(&actual);
489*c8dee2aaSAndroid Build Coastguard Worker 
490*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, kExpected == actual);
491*c8dee2aaSAndroid Build Coastguard Worker }
492*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(IncrTopoSort,reporter)493*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(IncrTopoSort, reporter) {
494*c8dee2aaSAndroid Build Coastguard Worker     test_1(reporter);
495*c8dee2aaSAndroid Build Coastguard Worker     test_2(reporter);
496*c8dee2aaSAndroid Build Coastguard Worker     test_diamond(reporter);
497*c8dee2aaSAndroid Build Coastguard Worker     test_lopsided_binary_tree(reporter);
498*c8dee2aaSAndroid Build Coastguard Worker }
499