Home | History | Annotate | Download | only in core
      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 "SkRefCnt.h"
     12 #include "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 GrOpList 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