blob: 9cf0343805ed73cd7286327d3ac4bb3d70bf0ee1 [file] [log] [blame]
Adlai Holler08f53112021-01-20 17:44:15 -05001/*
2 * Copyright 2021 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#include "src/gpu/GrRenderTaskCluster.h"
9
10#include "include/private/SkTHash.h"
11#include "src/gpu/GrRenderTask.h"
12
13// Uncomment to get lots of logging.
14#define CLUSTER_DEBUGF(...) //SkDebugf(__VA_ARGS__)
15
16static GrSurfaceProxy* first_target(GrRenderTask* task) {
17 return task->target(0).proxy();
18}
19
20#ifdef SK_DEBUG
21[[maybe_unused]] static SkString describe_task(GrRenderTask* t) {
22 if (GrSurfaceProxy* target = first_target(t)) {
23 return SkStringPrintf("%s(%d)", target->getDebugName().c_str(), t->uniqueID());
24 } else {
25 return SkStringPrintf("%d", t->uniqueID());
26 }
27}
28
29[[maybe_unused]] static SkString describe_tasks(SkSpan<const sk_sp<GrRenderTask>> collection) {
30 SkString s;
31 for (const sk_sp<GrRenderTask>& t : collection) {
32 s.appendf("%s ", describe_task(t.get()).c_str());
33 }
34 return s;
35}
36
37[[maybe_unused]] static SkString describe_tasks(const SkTInternalLList<GrRenderTask>& collection) {
38 SkString s;
39 for (GrRenderTask* t : collection) {
40 s.appendf("%s ", describe_task(t).c_str());
41 }
42 return s;
43}
44
45static void validate(SkSpan<const sk_sp<GrRenderTask>> input,
46 const SkTInternalLList<GrRenderTask>& llist) {
47 // Check that we didn't break dependencies.
48 SkTHashSet<GrRenderTask*> seen;
49 for (GrRenderTask* t : llist) {
50 seen.add(t);
51 for (GrRenderTask* dep : t->dependencies()) {
52 SkASSERTF(seen.contains(dep),
53 "%s came before dependency %s",
54 describe_task(t).c_str(),
55 describe_task(dep).c_str());
56 }
57 }
58 // Check that llist has the same entries as the input.
59 for (const auto& orig : input) {
60 seen.remove(orig.get());
61 }
62 SkASSERT(seen.empty());
63}
64
65#endif // SK_DEBUG
66
67// Returns whether reordering occurred.
68static bool task_cluster_visit(GrRenderTask* task, SkTInternalLList<GrRenderTask>* llist,
69 SkTHashMap<GrSurfaceProxy*, GrRenderTask*>* lastTaskMap) {
70 CLUSTER_DEBUGF("Cluster: ***Step***\nLooking at %s\n",
71 describe_task(task).c_str());
72 if (task->numTargets() != 1) {
73 CLUSTER_DEBUGF("Cluster: %d targets. Emitting barriers.\n", task->numTargets());
74 // Tasks with 0 or multiple targets are treated as full barriers
75 // for all their targets.
76 for (int j = 0; j < task->numTargets(); j++) {
77 lastTaskMap->remove(task->target(0).proxy());
78 }
79 return false;
80 }
81
82 GrSurfaceProxy* target = first_target(task);
83 GrRenderTask* clusterTail = (lastTaskMap->find(target) ? *lastTaskMap->find(target) : nullptr);
84 lastTaskMap->set(target, task);
85
86 if (!clusterTail) {
87 CLUSTER_DEBUGF("Cluster: Bail, no cluster to extend.\n");
88 return false;
89 }
90
91 CLUSTER_DEBUGF("Cluster: clusterTail is %s.\n", describe_task(clusterTail).c_str());
92
93 if (clusterTail == llist->tail()) {
94 CLUSTER_DEBUGF("Cluster: Bail, cluster is already tail.\n");
95 return false;
96 }
97 GrRenderTask* movedHead = clusterTail->fNext;
98
99 // Now, let's refer to the "cluster" as the chain of tasks with the same target, that we're
100 // hoping to extend by reordering. Let "moved tasks" be the ones we want to move to extend the
101 // cluster.
102 GrRenderTask* clusterHead = clusterTail;
103 while (clusterHead->fPrev
104 && 1 == clusterHead->fPrev->numTargets()
105 && target == first_target(clusterHead->fPrev)) {
106 clusterHead = clusterHead->fPrev;
107 }
108
109 // We can't reorder if any moved task depends on anything in the cluster.
110 // Time complexity here is high, but making a hash set is a lot worse.
111 for (GrRenderTask* moved = movedHead; moved; moved = moved->fNext) {
112 for (GrRenderTask* dep : moved->dependencies()) {
113 for (GrRenderTask* t = clusterHead; t != movedHead; t = t->fNext) {
114 if (t == dep) {
115 CLUSTER_DEBUGF("Cluster: Bail, %s depends on %s.\n",
116 describe_task(moved).c_str(),
117 describe_task(dep).c_str());
118 return false;
119 }
120 }
121 }
122 }
123
124 // Grab the moved tasks and pull them before clusterHead.
125 for (GrRenderTask* moved = movedHead; moved;) {
126 CLUSTER_DEBUGF("Cluster: Reorder %s behind %s.\n",
127 describe_task(moved).c_str(),
128 describe_task(clusterHead).c_str());
129 // Be careful to save fNext before each move.
130 GrRenderTask* nextMoved = moved->fNext;
131 llist->remove(moved);
132 llist->addBefore(moved, clusterHead);
133 moved = nextMoved;
134 }
135 return true;
136}
137
138bool GrClusterRenderTasks(SkSpan<const sk_sp<GrRenderTask>> input,
139 SkTInternalLList<GrRenderTask>* llist) {
140 SkASSERT(llist->isEmpty());
141
142 if (input.count() < 3) {
143 return false;
144 }
145
146 CLUSTER_DEBUGF("Cluster: Original order is %s\n", describe_tasks(input).c_str());
147
148 SkTHashMap<GrSurfaceProxy*, GrRenderTask*> lastTaskMap;
149 bool didReorder = false;
150 for (const auto& t : input) {
151 didReorder |= task_cluster_visit(t.get(), llist, &lastTaskMap);
152 llist->addToTail(t.get());
153 CLUSTER_DEBUGF("Cluster: Output order is now: %s\n", describe_tasks(*llist).c_str());
154 }
155
156#ifdef SK_DEBUG
157 if (didReorder) {
158 validate(input, *llist);
159 }
160#endif
161
162 return didReorder;
163}
164
165#undef CLUSTER_DEBUGF