xref: /aosp_15_r20/external/skia/src/gpu/ganesh/GrRenderTaskCluster.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2021 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrRenderTaskCluster.h"
9*c8dee2aaSAndroid Build Coastguard Worker 
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRefCnt.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkString.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypes.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkTInternalLList.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkTHash.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrRenderTask.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrSurfaceProxy.h"
17*c8dee2aaSAndroid Build Coastguard Worker 
18*c8dee2aaSAndroid Build Coastguard Worker using namespace skia_private;
19*c8dee2aaSAndroid Build Coastguard Worker 
20*c8dee2aaSAndroid Build Coastguard Worker // Uncomment to get lots of logging.
21*c8dee2aaSAndroid Build Coastguard Worker #define CLUSTER_DEBUGF(...) //SkDebugf(__VA_ARGS__)
22*c8dee2aaSAndroid Build Coastguard Worker 
first_target(GrRenderTask * task)23*c8dee2aaSAndroid Build Coastguard Worker static GrSurfaceProxy* first_target(GrRenderTask* task) { return task->target(0); }
24*c8dee2aaSAndroid Build Coastguard Worker 
25*c8dee2aaSAndroid Build Coastguard Worker #ifdef SK_DEBUG
describe_task(GrRenderTask * t)26*c8dee2aaSAndroid Build Coastguard Worker [[maybe_unused]] static SkString describe_task(GrRenderTask* t) {
27*c8dee2aaSAndroid Build Coastguard Worker     if (GrSurfaceProxy* target = first_target(t)) {
28*c8dee2aaSAndroid Build Coastguard Worker         return SkStringPrintf("%s(%u)", target->getDebugName().c_str(), t->uniqueID());
29*c8dee2aaSAndroid Build Coastguard Worker     } else {
30*c8dee2aaSAndroid Build Coastguard Worker         return SkStringPrintf("%u", t->uniqueID());
31*c8dee2aaSAndroid Build Coastguard Worker     }
32*c8dee2aaSAndroid Build Coastguard Worker }
33*c8dee2aaSAndroid Build Coastguard Worker 
describe_tasks(SkSpan<const sk_sp<GrRenderTask>> collection)34*c8dee2aaSAndroid Build Coastguard Worker [[maybe_unused]] static SkString describe_tasks(SkSpan<const sk_sp<GrRenderTask>> collection) {
35*c8dee2aaSAndroid Build Coastguard Worker     SkString s;
36*c8dee2aaSAndroid Build Coastguard Worker     for (const sk_sp<GrRenderTask>& t : collection) {
37*c8dee2aaSAndroid Build Coastguard Worker         s.appendf("%s ", describe_task(t.get()).c_str());
38*c8dee2aaSAndroid Build Coastguard Worker     }
39*c8dee2aaSAndroid Build Coastguard Worker     return s;
40*c8dee2aaSAndroid Build Coastguard Worker }
41*c8dee2aaSAndroid Build Coastguard Worker 
describe_tasks(const SkTInternalLList<GrRenderTask> & collection)42*c8dee2aaSAndroid Build Coastguard Worker [[maybe_unused]] static SkString describe_tasks(const SkTInternalLList<GrRenderTask>& collection) {
43*c8dee2aaSAndroid Build Coastguard Worker     SkString s;
44*c8dee2aaSAndroid Build Coastguard Worker     for (GrRenderTask* t : collection) {
45*c8dee2aaSAndroid Build Coastguard Worker         s.appendf("%s ", describe_task(t).c_str());
46*c8dee2aaSAndroid Build Coastguard Worker     }
47*c8dee2aaSAndroid Build Coastguard Worker     return s;
48*c8dee2aaSAndroid Build Coastguard Worker }
49*c8dee2aaSAndroid Build Coastguard Worker 
validate(SkSpan<const sk_sp<GrRenderTask>> input,const SkTInternalLList<GrRenderTask> & llist)50*c8dee2aaSAndroid Build Coastguard Worker static void validate(SkSpan<const sk_sp<GrRenderTask>> input,
51*c8dee2aaSAndroid Build Coastguard Worker                      const SkTInternalLList<GrRenderTask>& llist) {
52*c8dee2aaSAndroid Build Coastguard Worker     // Check that we didn't break dependencies.
53*c8dee2aaSAndroid Build Coastguard Worker     THashSet<GrRenderTask*> seen;
54*c8dee2aaSAndroid Build Coastguard Worker     for (GrRenderTask* t : llist) {
55*c8dee2aaSAndroid Build Coastguard Worker         seen.add(t);
56*c8dee2aaSAndroid Build Coastguard Worker         for (GrRenderTask* dep : t->dependencies()) {
57*c8dee2aaSAndroid Build Coastguard Worker             SkASSERTF(seen.contains(dep),
58*c8dee2aaSAndroid Build Coastguard Worker                       "%s came before dependency %s",
59*c8dee2aaSAndroid Build Coastguard Worker                       describe_task(t).c_str(),
60*c8dee2aaSAndroid Build Coastguard Worker                       describe_task(dep).c_str());
61*c8dee2aaSAndroid Build Coastguard Worker         }
62*c8dee2aaSAndroid Build Coastguard Worker     }
63*c8dee2aaSAndroid Build Coastguard Worker     // Check that llist has the same entries as the input.
64*c8dee2aaSAndroid Build Coastguard Worker     for (const auto& orig : input) {
65*c8dee2aaSAndroid Build Coastguard Worker         seen.remove(orig.get());
66*c8dee2aaSAndroid Build Coastguard Worker     }
67*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(seen.empty());
68*c8dee2aaSAndroid Build Coastguard Worker }
69*c8dee2aaSAndroid Build Coastguard Worker 
70*c8dee2aaSAndroid Build Coastguard Worker #endif  // SK_DEBUG
71*c8dee2aaSAndroid Build Coastguard Worker 
72*c8dee2aaSAndroid Build Coastguard Worker // Returns whether `dependee` is a formal dependent or if it uses a surface `depender` targets.
depends_on(GrRenderTask * depender,GrRenderTask * dependee)73*c8dee2aaSAndroid Build Coastguard Worker static bool depends_on(GrRenderTask* depender, GrRenderTask* dependee) {
74*c8dee2aaSAndroid Build Coastguard Worker     // Check if depender writes to something dependee reads.
75*c8dee2aaSAndroid Build Coastguard Worker     // TODO: Establish real DAG dependencies for this during recording? E.g. when a task adds a
76*c8dee2aaSAndroid Build Coastguard Worker     // target, search backward for the last task that uses the target and add a dep.
77*c8dee2aaSAndroid Build Coastguard Worker     for (int i = 0; i < depender->numTargets(); i++) {
78*c8dee2aaSAndroid Build Coastguard Worker         if (dependee->isUsed(depender->target(i))) {
79*c8dee2aaSAndroid Build Coastguard Worker             CLUSTER_DEBUGF("Cluster: Bail, %s can't write before %s reads from %s.\n",
80*c8dee2aaSAndroid Build Coastguard Worker                            describe_task(depender).c_str(),
81*c8dee2aaSAndroid Build Coastguard Worker                            describe_task(dependee).c_str(),
82*c8dee2aaSAndroid Build Coastguard Worker                            depender->target(i)->getDebugName().c_str());
83*c8dee2aaSAndroid Build Coastguard Worker             return true;
84*c8dee2aaSAndroid Build Coastguard Worker         }
85*c8dee2aaSAndroid Build Coastguard Worker     }
86*c8dee2aaSAndroid Build Coastguard Worker     // Check for a formal dependency.
87*c8dee2aaSAndroid Build Coastguard Worker     if (depender->dependsOn(dependee)) {
88*c8dee2aaSAndroid Build Coastguard Worker         CLUSTER_DEBUGF("Cluster: Bail, %s depends on %s.\n",
89*c8dee2aaSAndroid Build Coastguard Worker                        describe_task(depender).c_str(),
90*c8dee2aaSAndroid Build Coastguard Worker                        describe_task(dependee).c_str());
91*c8dee2aaSAndroid Build Coastguard Worker         return true;
92*c8dee2aaSAndroid Build Coastguard Worker     }
93*c8dee2aaSAndroid Build Coastguard Worker     return false;
94*c8dee2aaSAndroid Build Coastguard Worker }
95*c8dee2aaSAndroid Build Coastguard Worker 
96*c8dee2aaSAndroid Build Coastguard Worker // Returns whether reordering occurred.
task_cluster_visit(GrRenderTask * task,SkTInternalLList<GrRenderTask> * llist,THashMap<GrSurfaceProxy *,GrRenderTask * > * lastTaskMap)97*c8dee2aaSAndroid Build Coastguard Worker static bool task_cluster_visit(GrRenderTask* task, SkTInternalLList<GrRenderTask>* llist,
98*c8dee2aaSAndroid Build Coastguard Worker                                THashMap<GrSurfaceProxy*, GrRenderTask*>* lastTaskMap) {
99*c8dee2aaSAndroid Build Coastguard Worker     CLUSTER_DEBUGF("Cluster: ***Step***\nLooking at %s\n",
100*c8dee2aaSAndroid Build Coastguard Worker                    describe_task(task).c_str());
101*c8dee2aaSAndroid Build Coastguard Worker     if (task->numTargets() != 1) {
102*c8dee2aaSAndroid Build Coastguard Worker         CLUSTER_DEBUGF("Cluster: %d targets. Emitting barriers.\n", task->numTargets());
103*c8dee2aaSAndroid Build Coastguard Worker         // Tasks with 0 or multiple targets are treated as full barriers
104*c8dee2aaSAndroid Build Coastguard Worker         // for all their targets.
105*c8dee2aaSAndroid Build Coastguard Worker         for (int j = 0; j < task->numTargets(); j++) {
106*c8dee2aaSAndroid Build Coastguard Worker             if (lastTaskMap->find(task->target(0))) {
107*c8dee2aaSAndroid Build Coastguard Worker                 lastTaskMap->remove(task->target(0));
108*c8dee2aaSAndroid Build Coastguard Worker             }
109*c8dee2aaSAndroid Build Coastguard Worker         }
110*c8dee2aaSAndroid Build Coastguard Worker         return false;
111*c8dee2aaSAndroid Build Coastguard Worker     }
112*c8dee2aaSAndroid Build Coastguard Worker 
113*c8dee2aaSAndroid Build Coastguard Worker     GrSurfaceProxy* target = first_target(task);
114*c8dee2aaSAndroid Build Coastguard Worker     GrRenderTask* clusterTail = (lastTaskMap->find(target) ? *lastTaskMap->find(target) : nullptr);
115*c8dee2aaSAndroid Build Coastguard Worker     lastTaskMap->set(target, task);
116*c8dee2aaSAndroid Build Coastguard Worker 
117*c8dee2aaSAndroid Build Coastguard Worker     if (!clusterTail) {
118*c8dee2aaSAndroid Build Coastguard Worker         CLUSTER_DEBUGF("Cluster: Bail, no cluster to extend.\n");
119*c8dee2aaSAndroid Build Coastguard Worker         return false;
120*c8dee2aaSAndroid Build Coastguard Worker     }
121*c8dee2aaSAndroid Build Coastguard Worker 
122*c8dee2aaSAndroid Build Coastguard Worker     CLUSTER_DEBUGF("Cluster: clusterTail is %s.\n", describe_task(clusterTail).c_str());
123*c8dee2aaSAndroid Build Coastguard Worker 
124*c8dee2aaSAndroid Build Coastguard Worker     if (clusterTail == llist->tail()) {
125*c8dee2aaSAndroid Build Coastguard Worker         CLUSTER_DEBUGF("Cluster: Bail, cluster is already tail.\n");
126*c8dee2aaSAndroid Build Coastguard Worker         return false;
127*c8dee2aaSAndroid Build Coastguard Worker     }
128*c8dee2aaSAndroid Build Coastguard Worker     GrRenderTask* movedHead = clusterTail->fNext;
129*c8dee2aaSAndroid Build Coastguard Worker 
130*c8dee2aaSAndroid Build Coastguard Worker     // Now, let's refer to the "cluster" as the chain of tasks with the same target, that we're
131*c8dee2aaSAndroid Build Coastguard Worker     // hoping to extend by reordering. Let "moved tasks" be the ones we want to move to extend the
132*c8dee2aaSAndroid Build Coastguard Worker     // cluster.
133*c8dee2aaSAndroid Build Coastguard Worker     GrRenderTask* clusterHead = clusterTail;
134*c8dee2aaSAndroid Build Coastguard Worker     while (clusterHead->fPrev
135*c8dee2aaSAndroid Build Coastguard Worker            && 1 == clusterHead->fPrev->numTargets()
136*c8dee2aaSAndroid Build Coastguard Worker            && target == first_target(clusterHead->fPrev)) {
137*c8dee2aaSAndroid Build Coastguard Worker         clusterHead = clusterHead->fPrev;
138*c8dee2aaSAndroid Build Coastguard Worker     }
139*c8dee2aaSAndroid Build Coastguard Worker 
140*c8dee2aaSAndroid Build Coastguard Worker     // We can't reorder if any moved task depends on anything in the cluster.
141*c8dee2aaSAndroid Build Coastguard Worker     // Time complexity here is high, but making a hash set is worse in profiling.
142*c8dee2aaSAndroid Build Coastguard Worker     for (GrRenderTask* moved = movedHead; moved; moved = moved->fNext) {
143*c8dee2aaSAndroid Build Coastguard Worker         for (GrRenderTask* passed = clusterHead; passed != movedHead; passed = passed->fNext) {
144*c8dee2aaSAndroid Build Coastguard Worker             if (depends_on(moved, passed)) {
145*c8dee2aaSAndroid Build Coastguard Worker                 return false;
146*c8dee2aaSAndroid Build Coastguard Worker             }
147*c8dee2aaSAndroid Build Coastguard Worker         }
148*c8dee2aaSAndroid Build Coastguard Worker     }
149*c8dee2aaSAndroid Build Coastguard Worker 
150*c8dee2aaSAndroid Build Coastguard Worker     // Grab the moved tasks and pull them before clusterHead.
151*c8dee2aaSAndroid Build Coastguard Worker     for (GrRenderTask* moved = movedHead; moved;) {
152*c8dee2aaSAndroid Build Coastguard Worker         CLUSTER_DEBUGF("Cluster: Reorder %s behind %s.\n",
153*c8dee2aaSAndroid Build Coastguard Worker                        describe_task(moved).c_str(),
154*c8dee2aaSAndroid Build Coastguard Worker                        describe_task(clusterHead).c_str());
155*c8dee2aaSAndroid Build Coastguard Worker         // Be careful to save fNext before each move.
156*c8dee2aaSAndroid Build Coastguard Worker         GrRenderTask* nextMoved = moved->fNext;
157*c8dee2aaSAndroid Build Coastguard Worker         llist->remove(moved);
158*c8dee2aaSAndroid Build Coastguard Worker         llist->addBefore(moved, clusterHead);
159*c8dee2aaSAndroid Build Coastguard Worker         moved = nextMoved;
160*c8dee2aaSAndroid Build Coastguard Worker     }
161*c8dee2aaSAndroid Build Coastguard Worker     return true;
162*c8dee2aaSAndroid Build Coastguard Worker }
163*c8dee2aaSAndroid Build Coastguard Worker 
GrClusterRenderTasks(SkSpan<const sk_sp<GrRenderTask>> input,SkTInternalLList<GrRenderTask> * llist)164*c8dee2aaSAndroid Build Coastguard Worker bool GrClusterRenderTasks(SkSpan<const sk_sp<GrRenderTask>> input,
165*c8dee2aaSAndroid Build Coastguard Worker                           SkTInternalLList<GrRenderTask>* llist) {
166*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(llist->isEmpty());
167*c8dee2aaSAndroid Build Coastguard Worker 
168*c8dee2aaSAndroid Build Coastguard Worker     if (input.size() < 3) {
169*c8dee2aaSAndroid Build Coastguard Worker         for (const auto& t : input) {
170*c8dee2aaSAndroid Build Coastguard Worker             llist->addToTail(t.get());
171*c8dee2aaSAndroid Build Coastguard Worker         }
172*c8dee2aaSAndroid Build Coastguard Worker         return false;
173*c8dee2aaSAndroid Build Coastguard Worker     }
174*c8dee2aaSAndroid Build Coastguard Worker 
175*c8dee2aaSAndroid Build Coastguard Worker     CLUSTER_DEBUGF("Cluster: Original order is %s\n", describe_tasks(input).c_str());
176*c8dee2aaSAndroid Build Coastguard Worker 
177*c8dee2aaSAndroid Build Coastguard Worker     THashMap<GrSurfaceProxy*, GrRenderTask*> lastTaskMap;
178*c8dee2aaSAndroid Build Coastguard Worker     bool didReorder = false;
179*c8dee2aaSAndroid Build Coastguard Worker     for (const auto& t : input) {
180*c8dee2aaSAndroid Build Coastguard Worker         didReorder |= task_cluster_visit(t.get(), llist, &lastTaskMap);
181*c8dee2aaSAndroid Build Coastguard Worker         llist->addToTail(t.get());
182*c8dee2aaSAndroid Build Coastguard Worker         CLUSTER_DEBUGF("Cluster: Output order is now: %s\n", describe_tasks(*llist).c_str());
183*c8dee2aaSAndroid Build Coastguard Worker     }
184*c8dee2aaSAndroid Build Coastguard Worker 
185*c8dee2aaSAndroid Build Coastguard Worker #ifdef SK_DEBUG
186*c8dee2aaSAndroid Build Coastguard Worker     if (didReorder) {
187*c8dee2aaSAndroid Build Coastguard Worker         validate(input, *llist);
188*c8dee2aaSAndroid Build Coastguard Worker     }
189*c8dee2aaSAndroid Build Coastguard Worker #endif
190*c8dee2aaSAndroid Build Coastguard Worker 
191*c8dee2aaSAndroid Build Coastguard Worker     return didReorder;
192*c8dee2aaSAndroid Build Coastguard Worker }
193*c8dee2aaSAndroid Build Coastguard Worker 
194*c8dee2aaSAndroid Build Coastguard Worker #undef CLUSTER_DEBUGF
195