blob: bc1b07ef59d0b5674f758b46b6816cae6245924f [file] [log] [blame]
robertphillips423f6462015-10-19 12:15:55 -07001/*
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
Mike Kleinc0bd9f92019-04-23 12:05:21 -050011#include "include/core/SkRefCnt.h"
12#include "include/private/SkTArray.h"
robertphillips423f6462015-10-19 12:15:55 -070013
14#ifdef SK_DEBUG
15template <typename T, typename Traits = T>
Robert Phillips4150eea2018-02-07 17:08:21 -050016void SkTTopoSort_CheckAllUnmarked(const SkTArray<sk_sp<T>>& graph) {
robertphillips423f6462015-10-19 12:15:55 -070017 for (int i = 0; i < graph.count(); ++i) {
Robert Phillips4150eea2018-02-07 17:08:21 -050018 SkASSERT(!Traits::IsTempMarked(graph[i].get()));
19 SkASSERT(!Traits::WasOutput(graph[i].get()));
robertphillips423f6462015-10-19 12:15:55 -070020 }
21}
22
23template <typename T, typename Traits = T>
Robert Phillips4150eea2018-02-07 17:08:21 -050024void SkTTopoSort_CleanExit(const SkTArray<sk_sp<T>>& graph) {
robertphillips423f6462015-10-19 12:15:55 -070025 for (int i = 0; i < graph.count(); ++i) {
Robert Phillips4150eea2018-02-07 17:08:21 -050026 SkASSERT(!Traits::IsTempMarked(graph[i].get()));
27 SkASSERT(Traits::WasOutput(graph[i].get()));
robertphillips423f6462015-10-19 12:15:55 -070028 }
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.
34template <typename T, typename Traits = T>
Robert Phillips4150eea2018-02-07 17:08:21 -050035bool SkTTopoSort_Visit(T* node, SkTArray<sk_sp<T>>* result) {
robertphillips423f6462015-10-19 12:15:55 -070036 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
Robert Phillips4150eea2018-02-07 17:08:21 -050055 result->push_back(sk_ref_sp(node));
robertphillips423f6462015-10-19 12:15:55 -070056 }
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
Greg Danielf41b2bd2019-08-22 16:19:24 -040080// flush a GrRenderTask DAG.
robertphillips423f6462015-10-19 12:15:55 -070081template <typename T, typename Traits = T>
Robert Phillips4150eea2018-02-07 17:08:21 -050082bool SkTTopoSort(SkTArray<sk_sp<T>>* graph) {
83 SkTArray<sk_sp<T>> result;
robertphillips423f6462015-10-19 12:15:55 -070084
85#ifdef SK_DEBUG
86 SkTTopoSort_CheckAllUnmarked<T, Traits>(*graph);
87#endif
88
Robert Phillips4150eea2018-02-07 17:08:21 -050089 result.reserve(graph->count());
robertphillips423f6462015-10-19 12:15:55 -070090
91 for (int i = 0; i < graph->count(); ++i) {
Robert Phillips4150eea2018-02-07 17:08:21 -050092 if (Traits::WasOutput((*graph)[i].get())) {
robertphillips423f6462015-10-19 12:15:55 -070093 // 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.
Robert Phillips4150eea2018-02-07 17:08:21 -050099 if (!SkTTopoSort_Visit<T, Traits>((*graph)[i].get(), &result)) {
robertphillips423f6462015-10-19 12:15:55 -0700100 return false;
101 }
102 }
103
104 SkASSERT(graph->count() == result.count());
Ben Wagnerf08d1d02018-06-18 15:11:00 -0400105 graph->swap(result);
robertphillips423f6462015-10-19 12:15:55 -0700106
107#ifdef SK_DEBUG
108 SkTTopoSort_CleanExit<T, Traits>(*graph);
109#endif
110 return true;
111}
112
113#endif