/* * Copyright 2015 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "include/core/SkRefCnt.h" #include "include/core/SkSpan.h" #include "include/core/SkTypes.h" #include "include/private/base/SkTArray.h" #include "src/base/SkRandom.h" #include "src/gpu/ganesh/GrTTopoSort.h" #include "tests/Test.h" #include "tools/ToolUtils.h" #include #include using namespace skia_private; typedef void (*CreateGraphPF)(TArray>* graph); /* Simple diamond * 3 * . . * / \ * 1 2 * . . * \ / * 0 */ static void create_graph0(TArray>* graph) { ToolUtils::TopoTestNode::AllocNodes(graph, 4); (*graph)[0]->dependsOn((*graph)[1].get()); (*graph)[0]->dependsOn((*graph)[2].get()); (*graph)[1]->dependsOn((*graph)[3].get()); (*graph)[2]->dependsOn((*graph)[3].get()); } static void create_simple_chain(TArray>* graph, int n) { ToolUtils::TopoTestNode::AllocNodes(graph, n); for (int i = 0; i < n - 1; ++i) { (*graph)[i+1]->dependsOn((*graph)[i].get()); } } /* Simple chain * 0 * ^ * | * 1 * ^ * | * 2 * ^ * | * 3 */ static void create_graph1(TArray>* graph) { create_simple_chain(graph, 4); } /* Simple Loop * 2 * / . * / \ * . \ * 0 ---> 1 */ static void create_graph2(TArray>* graph) { ToolUtils::TopoTestNode::AllocNodes(graph, 3); (*graph)[0]->dependsOn((*graph)[1].get()); (*graph)[1]->dependsOn((*graph)[2].get()); (*graph)[2]->dependsOn((*graph)[0].get()); } /* Double diamond * 6 * . . * / \ * 4 5 * . . * \ / * 3 * . . * / \ * 1 2 * . . * \ / * 0 */ static void create_graph3(TArray>* graph) { ToolUtils::TopoTestNode::AllocNodes(graph, 7); (*graph)[0]->dependsOn((*graph)[1].get()); (*graph)[0]->dependsOn((*graph)[2].get()); (*graph)[1]->dependsOn((*graph)[3].get()); (*graph)[2]->dependsOn((*graph)[3].get()); (*graph)[3]->dependsOn((*graph)[4].get()); (*graph)[3]->dependsOn((*graph)[5].get()); (*graph)[4]->dependsOn((*graph)[6].get()); (*graph)[5]->dependsOn((*graph)[6].get()); } /* Two independent diamonds * 3 7 * . . . . * / \ / \ * 1 2 5 6 * . . . . * \ / \ / * 0 4 */ static void create_graph4(TArray>* graph) { ToolUtils::TopoTestNode::AllocNodes(graph, 8); (*graph)[0]->dependsOn((*graph)[1].get()); (*graph)[0]->dependsOn((*graph)[2].get()); (*graph)[1]->dependsOn((*graph)[3].get()); (*graph)[2]->dependsOn((*graph)[3].get()); (*graph)[4]->dependsOn((*graph)[5].get()); (*graph)[4]->dependsOn((*graph)[6].get()); (*graph)[5]->dependsOn((*graph)[7].get()); (*graph)[6]->dependsOn((*graph)[7].get()); } /* Two linked diamonds w/ two loops * 5 6 * / . . \ * . \ / . * 2 3 4 * \ . / * . / \ . * 0 1 */ static void create_graph5(TArray>* graph) { ToolUtils::TopoTestNode::AllocNodes(graph, 7); (*graph)[0]->dependsOn((*graph)[3].get()); (*graph)[1]->dependsOn((*graph)[3].get()); (*graph)[2]->dependsOn((*graph)[0].get()); (*graph)[3]->dependsOn((*graph)[5].get()); (*graph)[3]->dependsOn((*graph)[6].get()); (*graph)[4]->dependsOn((*graph)[1].get()); (*graph)[5]->dependsOn((*graph)[2].get()); (*graph)[6]->dependsOn((*graph)[4].get()); } /* Two disjoint loops * 2 5 * / . / . * / \ / \ * . \ . \ * 0 ---> 1 3 ---> 4 */ static void create_graph6(TArray>* graph) { ToolUtils::TopoTestNode::AllocNodes(graph, 6); (*graph)[0]->dependsOn((*graph)[1].get()); (*graph)[1]->dependsOn((*graph)[2].get()); (*graph)[2]->dependsOn((*graph)[0].get()); (*graph)[3]->dependsOn((*graph)[4].get()); (*graph)[4]->dependsOn((*graph)[5].get()); (*graph)[5]->dependsOn((*graph)[3].get()); } DEF_TEST(TopoSort, reporter) { SkRandom rand; struct { CreateGraphPF fCreate; bool fExpectedResult; } tests[] = { { create_graph0, true }, { create_graph1, true }, { create_graph2, false }, { create_graph3, true }, { create_graph4, true }, { create_graph5, false }, { create_graph6, false }, }; for (size_t i = 0; i < std::size(tests); ++i) { TArray> graph; (tests[i].fCreate)(&graph); const int numNodes = graph.size(); ToolUtils::TopoTestNode::Shuffle(graph, &rand); bool actualResult = GrTTopoSort(graph); REPORTER_ASSERT(reporter, actualResult == tests[i].fExpectedResult); REPORTER_ASSERT(reporter, numNodes == graph.size()); if (tests[i].fExpectedResult) { for (const auto& node : graph) { REPORTER_ASSERT(reporter, node->check()); } } else { // When the topological sort fails all the nodes should still appear in the result // but their order can be somewhat arbitrary. std::vector seen(numNodes, false); for (const auto& node : graph) { SkASSERT(node); SkASSERT(!seen[node->id()]); seen[node->id()] = true; } } //SkDEBUGCODE(print(graph);) } // Some additional tests that do multiple partial sorts of graphs where we know nothing in an // earlier partion depends on anything in a later partition. for (int n = 2; n < 6; ++n) { for (int split = 1; split < n; ++split) { TArray> graph; create_simple_chain(&graph, n); SkSpan spanA = SkSpan(graph.begin(), split); SkSpan spanB = SkSpan(graph.begin() + split, n - split); ToolUtils::TopoTestNode::Shuffle(spanA, &rand); ToolUtils::TopoTestNode::Shuffle(spanB, &rand); bool result = GrTTopoSort(spanA); if (!result) { ERRORF(reporter, "Topo sort on partial chain failed."); return; } // Nothing outside of the processed span should have been output. for (const auto& node : spanB) { REPORTER_ASSERT(reporter, !ToolUtils::TopoTestNode::WasOutput(node.get())); } result = GrTTopoSort(spanB, split); if (!result) { ERRORF(reporter, "Topo sort on partial chain failed."); return; } for (const auto& node : graph) { REPORTER_ASSERT(reporter, node->check()); } } } }