|  | /* | 
|  | * 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 <cstddef> | 
|  | #include <vector> | 
|  |  | 
|  | using namespace skia_private; | 
|  |  | 
|  | typedef void (*CreateGraphPF)(TArray<sk_sp<ToolUtils::TopoTestNode>>* graph); | 
|  |  | 
|  | /* Simple diamond | 
|  | *       3 | 
|  | *      . . | 
|  | *     /   \ | 
|  | *    1     2 | 
|  | *    .     . | 
|  | *     \   / | 
|  | *       0 | 
|  | */ | 
|  | static void create_graph0(TArray<sk_sp<ToolUtils::TopoTestNode>>* 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<sk_sp<ToolUtils::TopoTestNode>>* 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<sk_sp<ToolUtils::TopoTestNode>>* graph) { | 
|  | create_simple_chain(graph, 4); | 
|  | } | 
|  |  | 
|  | /* Simple Loop | 
|  | *       2 | 
|  | *      / . | 
|  | *     /   \ | 
|  | *    .     \ | 
|  | *    0 ---> 1 | 
|  | */ | 
|  | static void create_graph2(TArray<sk_sp<ToolUtils::TopoTestNode>>* 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<sk_sp<ToolUtils::TopoTestNode>>* 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<sk_sp<ToolUtils::TopoTestNode>>* 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<sk_sp<ToolUtils::TopoTestNode>>* 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<sk_sp<ToolUtils::TopoTestNode>>* 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<sk_sp<ToolUtils::TopoTestNode>> graph; | 
|  |  | 
|  | (tests[i].fCreate)(&graph); | 
|  |  | 
|  | const int numNodes = graph.size(); | 
|  |  | 
|  | ToolUtils::TopoTestNode::Shuffle(graph, &rand); | 
|  |  | 
|  | bool actualResult = GrTTopoSort<ToolUtils::TopoTestNode>(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<bool> 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<sk_sp<ToolUtils::TopoTestNode>> 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()); | 
|  | } | 
|  | } | 
|  | } | 
|  | } |