| /* |
| * Copyright 2015 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #ifndef GrTTopoSort_DEFINED |
| #define GrTTopoSort_DEFINED |
| |
| #include "include/core/SkRefCnt.h" |
| #include "include/core/SkSpan.h" |
| |
| #ifdef SK_DEBUG |
| template <typename T, typename Traits = T> |
| void GrTTopoSort_CheckAllUnmarked(SkSpan<const sk_sp<T>> graph) { |
| for (const auto& node : graph) { |
| SkASSERT(!Traits::IsTempMarked(node.get())); |
| SkASSERT(!Traits::WasOutput(node.get())); |
| } |
| } |
| |
| template <typename T, typename Traits = T> |
| void GrTTopoSort_CleanExit(SkSpan<const sk_sp<T>> graph, uint32_t offset) { |
| for (size_t i = 0; i < graph.size(); ++i) { |
| SkASSERT(!Traits::IsTempMarked(graph[i].get())); |
| SkASSERT(Traits::WasOutput(graph[i].get())); |
| SkASSERT(Traits::GetIndex(graph[i].get()) - offset == (uint32_t) i); |
| } |
| } |
| #endif |
| |
| // Recursively visit a node and all the other nodes it depends on. |
| // Return false if there is a loop. |
| template <typename T, typename Traits = T> |
| bool GrTTopoSort_Visit(T* node, uint32_t* counter) { |
| if (Traits::IsTempMarked(node)) { |
| // There is a loop. |
| return false; |
| } |
| |
| // If the node under consideration has been already been output it means it |
| // (and all the nodes it depends on) are already in 'result'. |
| if (Traits::WasOutput(node)) { |
| return true; |
| } |
| |
| bool succeeded = true; |
| // This node hasn't been output yet. Recursively assess all the |
| // nodes it depends on outputing them first. |
| Traits::SetTempMark(node); |
| for (int i = 0; i < Traits::NumDependencies(node); ++i) { |
| if (!GrTTopoSort_Visit<T, Traits>(Traits::Dependency(node, i), counter)) { |
| succeeded = false; |
| } |
| } |
| |
| Traits::Output(node, *counter); // mark this node as output |
| ++(*counter); |
| Traits::ResetTempMark(node); |
| |
| return succeeded; |
| } |
| |
| // Topologically sort the nodes in 'graph'. For this sort, when node 'i' depends |
| // on node 'j' it means node 'j' must appear in the result before node 'i'. Note that all |
| // dependencies of a node in the Span must also be in the Span or already have WasOutput() = true. |
| // |
| // A false return value means there was a loop and the contents of 'graph' will |
| // be in some arbitrary state. |
| // |
| // Traits requires: |
| // static void Output(T* t, uint32_t index) { ... } // 'index' is 't's position in the result |
| // static bool WasOutput(const T* t) { ... } |
| // static uint32_t GetIndex() { ... } |
| // |
| // static void SetTempMark(T* t) { ... } // transiently used during toposort |
| // static void ResetTempMark(T* t) { ... } |
| // static bool IsTempMarked(const T* t) { ... } |
| // |
| // static int NumDependencies(const T* t) { ... } // 't' will be output after all the other - |
| // static T* Dependency(T* t, int index) { ... } // nodes on which it depends |
| // We'll look on T for these by default, or you can pass a custom Traits type. |
| // |
| // The offset parameter is useful if you are sorting ranges of a larger graph and when Output() |
| // is called on a T it must know it's position in the full graph array. |
| // |
| // TODO: potentially add a version that takes a seed node and just outputs that |
| // node and all the nodes on which it depends. This could be used to partially |
| // flush a GrRenderTask DAG. |
| template <typename T, typename Traits = T> |
| bool GrTTopoSort(SkSpan<sk_sp<T>> graph, uint32_t offset = 0) { |
| uint32_t counter = offset; |
| |
| #ifdef SK_DEBUG |
| GrTTopoSort_CheckAllUnmarked<T, Traits>(graph); |
| #endif |
| |
| bool succeeded = true; |
| |
| for (size_t i = 0; i < graph.size(); ++i) { |
| if (Traits::WasOutput(graph[i].get())) { |
| // This node was depended on by some earlier node and has already |
| // been output |
| continue; |
| } |
| |
| // Output this node after all the nodes it depends on have been output. |
| if (!GrTTopoSort_Visit<T, Traits>(graph[i].get(), &counter)) { |
| succeeded = false; |
| } |
| } |
| |
| SkASSERT(counter - offset == (uint32_t) graph.size()); |
| |
| // Reorder the array given the output order |
| for (uint32_t i = 0; i < (uint32_t) graph.size(); ++i) { |
| for (uint32_t correctIndex = Traits::GetIndex(graph[i].get()) - offset; |
| correctIndex != i; |
| correctIndex = Traits::GetIndex(graph[i].get()) - offset) { |
| graph[i].swap(graph[correctIndex]); |
| } |
| } |
| |
| #ifdef SK_DEBUG |
| GrTTopoSort_CleanExit<T, Traits>(graph, offset); |
| #endif |
| return succeeded; |
| } |
| |
| #endif |