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