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