Coverage Report

Created: 2024-09-14 07:19

/src/skia/src/gpu/ganesh/ops/OpsTask.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2019 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
#include "src/gpu/ganesh/ops/OpsTask.h"
8
9
#include "include/core/SkSize.h"
10
#include "include/core/SkString.h"
11
#include "include/gpu/GpuTypes.h"
12
#include "include/gpu/ganesh/GrRecordingContext.h"
13
#include "include/private/base/SkPoint_impl.h"
14
#include "src/base/SkArenaAlloc.h"
15
#include "src/base/SkScopeExit.h"
16
#include "src/core/SkRectPriv.h"
17
#include "src/core/SkStringUtils.h"
18
#include "src/core/SkTraceEvent.h"
19
#include "src/gpu/ganesh/GrAppliedClip.h"
20
#include "src/gpu/ganesh/GrAttachment.h"
21
#include "src/gpu/ganesh/GrAuditTrail.h"
22
#include "src/gpu/ganesh/GrCaps.h"
23
#include "src/gpu/ganesh/GrGpu.h"
24
#include "src/gpu/ganesh/GrNativeRect.h"
25
#include "src/gpu/ganesh/GrOpFlushState.h"
26
#include "src/gpu/ganesh/GrOpsRenderPass.h"
27
#include "src/gpu/ganesh/GrRecordingContextPriv.h"
28
#include "src/gpu/ganesh/GrRenderTarget.h"
29
#include "src/gpu/ganesh/GrRenderTargetProxy.h"
30
#include "src/gpu/ganesh/GrResourceAllocator.h"
31
#include "src/gpu/ganesh/GrResourceProvider.h"
32
#include "src/gpu/ganesh/GrSurfaceProxyView.h"
33
#include "src/gpu/ganesh/GrTextureProxy.h"
34
#include "src/gpu/ganesh/GrTextureResolveManager.h"
35
#include "src/gpu/ganesh/geometry/GrRect.h"
36
37
#include <algorithm>
38
#include <cstddef>
39
#include <cstdint>
40
#include <functional>
41
#include <memory>
42
#include <utility>
43
44
class GrDrawingManager;
45
enum GrSurfaceOrigin : int;
46
47
using namespace skia_private;
48
49
////////////////////////////////////////////////////////////////////////////////
50
51
namespace {
52
53
// Experimentally we have found that most combining occurs within the first 10 comparisons.
54
static const int kMaxOpMergeDistance = 10;
55
static const int kMaxOpChainDistance = 10;
56
57
////////////////////////////////////////////////////////////////////////////////
58
59
179k
inline bool can_reorder(const SkRect& a, const SkRect& b) { return !GrRectsOverlap(a, b); }
60
61
GrOpsRenderPass* create_render_pass(GrGpu* gpu,
62
                                    GrRenderTarget* rt,
63
                                    bool useMSAASurface,
64
                                    GrAttachment* stencil,
65
                                    GrSurfaceOrigin origin,
66
                                    const SkIRect& bounds,
67
                                    GrLoadOp colorLoadOp,
68
                                    const std::array<float, 4>& loadClearColor,
69
                                    GrLoadOp stencilLoadOp,
70
                                    GrStoreOp stencilStoreOp,
71
                                    const TArray<GrSurfaceProxy*, true>& sampledProxies,
72
172k
                                    GrXferBarrierFlags renderPassXferBarriers) {
73
172k
    const GrOpsRenderPass::LoadAndStoreInfo kColorLoadStoreInfo {
74
172k
        colorLoadOp,
75
172k
        GrStoreOp::kStore,
76
172k
        loadClearColor
77
172k
    };
78
79
    // TODO:
80
    // We would like to (at this level) only ever clear & discard. We would need
81
    // to stop splitting up higher level OpsTasks for copyOps to achieve that.
82
    // Note: we would still need SB loads and stores but they would happen at a
83
    // lower level (inside the VK command buffer).
84
172k
    const GrOpsRenderPass::StencilLoadAndStoreInfo stencilLoadAndStoreInfo {
85
172k
        stencilLoadOp,
86
172k
        stencilStoreOp,
87
172k
    };
88
89
172k
    return gpu->getOpsRenderPass(rt, useMSAASurface, stencil, origin, bounds, kColorLoadStoreInfo,
90
172k
                                 stencilLoadAndStoreInfo, sampledProxies, renderPassXferBarriers);
91
172k
}
92
93
} // anonymous namespace
94
95
////////////////////////////////////////////////////////////////////////////////
96
97
namespace skgpu::ganesh {
98
99
inline OpsTask::OpChain::List::List(GrOp::Owner op)
100
432k
        : fHead(std::move(op)), fTail(fHead.get()) {
101
432k
    this->validate();
102
432k
}
103
104
12.7k
inline OpsTask::OpChain::List::List(List&& that) { *this = std::move(that); }
105
106
12.9k
inline OpsTask::OpChain::List& OpsTask::OpChain::List::operator=(List&& that) {
107
12.9k
    fHead = std::move(that.fHead);
108
12.9k
    fTail = that.fTail;
109
12.9k
    that.fTail = nullptr;
110
12.9k
    this->validate();
111
12.9k
    return *this;
112
12.9k
}
113
114
432k
inline GrOp::Owner OpsTask::OpChain::List::popHead() {
115
432k
    SkASSERT(fHead);
116
432k
    auto temp = fHead->cutChain();
117
432k
    std::swap(temp, fHead);
118
432k
    if (!fHead) {
119
432k
        SkASSERT(fTail == temp.get());
120
432k
        fTail = nullptr;
121
432k
    }
122
432k
    return temp;
123
432k
}
124
125
0
inline GrOp::Owner OpsTask::OpChain::List::removeOp(GrOp* op) {
126
#ifdef SK_DEBUG
127
    auto head = op;
128
0
    while (head->prevInChain()) { head = head->prevInChain(); }
129
0
    SkASSERT(head == fHead.get());
130
#endif
131
0
    auto prev = op->prevInChain();
132
0
    if (!prev) {
133
0
        SkASSERT(op == fHead.get());
134
0
        return this->popHead();
135
0
    }
136
0
    auto temp = prev->cutChain();
137
0
    if (auto next = temp->cutChain()) {
138
0
        prev->chainConcat(std::move(next));
139
0
    } else {
140
0
        SkASSERT(fTail == op);
141
0
        fTail = prev;
142
0
    }
143
0
    this->validate();
144
0
    return temp;
145
0
}
Unexecuted instantiation: skgpu::ganesh::OpsTask::OpChain::List::removeOp(GrOp*)
Unexecuted instantiation: skgpu::ganesh::OpsTask::OpChain::List::removeOp(GrOp*)
146
147
0
inline void OpsTask::OpChain::List::pushHead(GrOp::Owner op) {
148
0
    SkASSERT(op);
149
0
    SkASSERT(op->isChainHead());
150
0
    SkASSERT(op->isChainTail());
151
0
    if (fHead) {
152
0
        op->chainConcat(std::move(fHead));
153
0
        fHead = std::move(op);
154
0
    } else {
155
0
        fHead = std::move(op);
156
0
        fTail = fHead.get();
157
0
    }
158
0
}
Unexecuted instantiation: skgpu::ganesh::OpsTask::OpChain::List::pushHead(std::__1::unique_ptr<GrOp, std::__1::default_delete<GrOp> >)
Unexecuted instantiation: skgpu::ganesh::OpsTask::OpChain::List::pushHead(std::__1::unique_ptr<GrOp, std::__1::default_delete<GrOp> >)
159
160
0
inline void OpsTask::OpChain::List::pushTail(GrOp::Owner op) {
161
0
    SkASSERT(op->isChainTail());
162
0
    fTail->chainConcat(std::move(op));
163
0
    fTail = fTail->nextInChain();
164
0
}
Unexecuted instantiation: skgpu::ganesh::OpsTask::OpChain::List::pushTail(std::__1::unique_ptr<GrOp, std::__1::default_delete<GrOp> >)
Unexecuted instantiation: skgpu::ganesh::OpsTask::OpChain::List::pushTail(std::__1::unique_ptr<GrOp, std::__1::default_delete<GrOp> >)
165
166
445k
inline void OpsTask::OpChain::List::validate() const {
167
#ifdef SK_DEBUG
168
    if (fHead) {
169
        SkASSERT(fTail);
170
        fHead->validateChain(fTail);
171
    }
172
#endif
173
445k
}
174
175
////////////////////////////////////////////////////////////////////////////////
176
177
OpsTask::OpChain::OpChain(GrOp::Owner op, GrProcessorSet::Analysis processorAnalysis,
178
                          GrAppliedClip* appliedClip, const GrDstProxyView* dstProxyView)
179
        : fList{std::move(op)}
180
        , fProcessorAnalysis(processorAnalysis)
181
241k
        , fAppliedClip(appliedClip) {
182
241k
    if (fProcessorAnalysis.requiresDstTexture()) {
183
37.1k
        SkASSERT(dstProxyView && dstProxyView->proxy());
184
37.1k
        fDstProxyView = *dstProxyView;
185
37.1k
    }
186
241k
    fBounds = fList.head()->bounds();
187
241k
}
188
189
255k
void OpsTask::OpChain::visitProxies(const GrVisitProxyFunc& func) const {
190
255k
    if (fList.empty()) {
191
210
        return;
192
210
    }
193
255k
    for (const auto& op : GrOp::ChainRange<>(fList.head())) {
194
255k
        op.visitProxies(func);
195
255k
    }
196
255k
    if (fDstProxyView.proxy()) {
197
37.1k
        func(fDstProxyView.proxy(), skgpu::Mipmapped::kNo);
198
37.1k
    }
199
255k
    if (fAppliedClip) {
200
175k
        fAppliedClip->visitProxies(func);
201
175k
    }
202
255k
}
203
204
241k
void OpsTask::OpChain::deleteOps() {
205
483k
    while (!fList.empty()) {
206
        // Since the value goes out of scope immediately, the GrOp::Owner deletes the op.
207
241k
        fList.popHead();
208
241k
    }
209
241k
}
210
211
// Concatenates two op chains and attempts to merge ops across the chains. Assumes that we know that
212
// the two chains are chainable. Returns the new chain.
213
OpsTask::OpChain::List OpsTask::OpChain::DoConcat(List chainA, List chainB, const GrCaps& caps,
214
                                                  SkArenaAlloc* opsTaskArena,
215
0
                                                  GrAuditTrail* auditTrail) {
216
    // We process ops in chain b from head to tail. We attempt to merge with nodes in a, starting
217
    // at chain a's tail and working toward the head. We produce one of the following outcomes:
218
    // 1) b's head is merged into an op in a.
219
    // 2) An op from chain a is merged into b's head. (In this case b's head gets processed again.)
220
    // 3) b's head is popped from chain a and added at the tail of a.
221
    // After result 3 we don't want to attempt to merge the next head of b with the new tail of a,
222
    // as we assume merges were already attempted when chain b was created. So we keep track of the
223
    // original tail of a and start our iteration of a there. We also track the bounds of the nodes
224
    // appended to chain a that will be skipped for bounds testing. If the original tail of a is
225
    // merged into an op in b (case 2) then we advance the "original tail" towards the head of a.
226
0
    GrOp* origATail = chainA.tail();
227
0
    SkRect skipBounds = SkRectPriv::MakeLargestInverted();
228
0
    do {
229
0
        int numMergeChecks = 0;
230
0
        bool merged = false;
231
0
        bool noSkip = (origATail == chainA.tail());
232
0
        SkASSERT(noSkip == (skipBounds == SkRectPriv::MakeLargestInverted()));
233
0
        bool canBackwardMerge = noSkip || can_reorder(chainB.head()->bounds(), skipBounds);
234
0
        SkRect forwardMergeBounds = skipBounds;
235
0
        GrOp* a = origATail;
236
0
        while (a) {
237
0
            bool canForwardMerge =
238
0
                    (a == chainA.tail()) || can_reorder(a->bounds(), forwardMergeBounds);
239
0
            if (canForwardMerge || canBackwardMerge) {
240
0
                auto result = a->combineIfPossible(chainB.head(), opsTaskArena, caps);
241
0
                SkASSERT(result != GrOp::CombineResult::kCannotCombine);
242
0
                merged = (result == GrOp::CombineResult::kMerged);
243
0
                GrOP_INFO("\t\t: (%s opID: %u) -> Combining with (%s, opID: %u)\n",
244
0
                          chainB.head()->name(), chainB.head()->uniqueID(), a->name(),
245
0
                          a->uniqueID());
246
0
            }
247
0
            if (merged) {
248
0
                GR_AUDIT_TRAIL_OPS_RESULT_COMBINED(auditTrail, a, chainB.head());
249
0
                if (canBackwardMerge) {
250
                    // The GrOp::Owner releases the op.
251
0
                    chainB.popHead();
252
0
                } else {
253
                    // We merged the contents of b's head into a. We will replace b's head with a in
254
                    // chain b.
255
0
                    SkASSERT(canForwardMerge);
256
0
                    if (a == origATail) {
257
0
                        origATail = a->prevInChain();
258
0
                    }
259
0
                    GrOp::Owner detachedA = chainA.removeOp(a);
260
                    // The GrOp::Owner releases the op.
261
0
                    chainB.popHead();
262
0
                    chainB.pushHead(std::move(detachedA));
263
0
                    if (chainA.empty()) {
264
                        // We merged all the nodes in chain a to chain b.
265
0
                        return chainB;
266
0
                    }
267
0
                }
268
0
                break;
269
0
            } else {
270
0
                if (++numMergeChecks == kMaxOpMergeDistance) {
271
0
                    break;
272
0
                }
273
0
                forwardMergeBounds.joinNonEmptyArg(a->bounds());
274
0
                canBackwardMerge =
275
0
                        canBackwardMerge && can_reorder(chainB.head()->bounds(), a->bounds());
276
0
                a = a->prevInChain();
277
0
            }
278
0
        }
279
        // If we weren't able to merge b's head then pop b's head from chain b and make it the new
280
        // tail of a.
281
0
        if (!merged) {
282
0
            chainA.pushTail(chainB.popHead());
283
0
            skipBounds.joinNonEmptyArg(chainA.tail()->bounds());
284
0
        }
285
0
    } while (!chainB.empty());
286
0
    return chainA;
287
0
}
Unexecuted instantiation: skgpu::ganesh::OpsTask::OpChain::DoConcat(skgpu::ganesh::OpsTask::OpChain::List, skgpu::ganesh::OpsTask::OpChain::List, GrCaps const&, SkArenaAlloc*, GrAuditTrail*)
Unexecuted instantiation: skgpu::ganesh::OpsTask::OpChain::DoConcat(skgpu::ganesh::OpsTask::OpChain::List, skgpu::ganesh::OpsTask::OpChain::List, GrCaps const&, SkArenaAlloc*, GrAuditTrail*)
288
289
// Attempts to concatenate the given chain onto our own and merge ops across the chains. Returns
290
// whether the operation succeeded. On success, the provided list will be returned empty.
291
bool OpsTask::OpChain::tryConcat(
292
        List* list, GrProcessorSet::Analysis processorAnalysis, const GrDstProxyView& dstProxyView,
293
        const GrAppliedClip* appliedClip, const SkRect& bounds, const GrCaps& caps,
294
280k
        SkArenaAlloc* opsTaskArena, GrAuditTrail* auditTrail) {
295
280k
    SkASSERT(!fList.empty());
296
280k
    SkASSERT(!list->empty());
297
280k
    SkASSERT(fProcessorAnalysis.requiresDstTexture() == SkToBool(fDstProxyView.proxy()));
298
280k
    SkASSERT(processorAnalysis.requiresDstTexture() == SkToBool(dstProxyView.proxy()));
299
    // All returns use explicit tuple constructor rather than {a, b} to work around old GCC bug.
300
280k
    if (fList.head()->classID() != list->head()->classID() ||
301
280k
        SkToBool(fAppliedClip) != SkToBool(appliedClip) ||
302
280k
        (fAppliedClip && *fAppliedClip != *appliedClip) ||
303
280k
        (fProcessorAnalysis.requiresNonOverlappingDraws() !=
304
212k
                processorAnalysis.requiresNonOverlappingDraws()) ||
305
280k
        (fProcessorAnalysis.requiresNonOverlappingDraws() &&
306
                // Non-overlaping draws are only required when Ganesh will either insert a barrier,
307
                // or read back a new dst texture between draws. In either case, we can neither
308
                // chain nor combine overlapping Ops.
309
210k
                GrRectsTouchOrOverlap(fBounds, bounds)) ||
310
280k
        (fProcessorAnalysis.requiresDstTexture() != processorAnalysis.requiresDstTexture()) ||
311
280k
        (fProcessorAnalysis.requiresDstTexture() && fDstProxyView != dstProxyView)) {
312
69.2k
        return false;
313
69.2k
    }
314
315
0
    SkDEBUGCODE(bool first = true;)
316
210k
    do {
317
210k
        switch (fList.tail()->combineIfPossible(list->head(), opsTaskArena, caps))
318
210k
        {
319
110k
            case GrOp::CombineResult::kCannotCombine:
320
                // If an op supports chaining then it is required that chaining is transitive and
321
                // that if any two ops in two different chains can merge then the two chains
322
                // may also be chained together. Thus, we should only hit this on the first
323
                // iteration.
324
110k
                SkASSERT(first);
325
110k
                return false;
326
0
            case GrOp::CombineResult::kMayChain:
327
0
                fList = DoConcat(std::move(fList), std::exchange(*list, List()), caps, opsTaskArena,
328
0
                                 auditTrail);
329
                // The above exchange cleared out 'list'. The list needs to be empty now for the
330
                // loop to terminate.
331
0
                SkASSERT(list->empty());
332
0
                break;
333
100k
            case GrOp::CombineResult::kMerged: {
334
100k
                GrOP_INFO("\t\t: (%s opID: %u) -> Combining with (%s, opID: %u)\n",
335
100k
                          list->tail()->name(), list->tail()->uniqueID(), list->head()->name(),
336
100k
                          list->head()->uniqueID());
337
100k
                GR_AUDIT_TRAIL_OPS_RESULT_COMBINED(auditTrail, fList.tail(), list->head());
338
                // The GrOp::Owner releases the op.
339
100k
                list->popHead();
340
100k
                break;
341
0
            }
342
210k
        }
343
100k
        SkDEBUGCODE(first = false);
344
100k
    } while (!list->empty());
345
346
    // The new ops were successfully merged and/or chained onto our own.
347
100k
    fBounds.joinPossiblyEmptyRect(bounds);
348
100k
    return true;
349
210k
}
skgpu::ganesh::OpsTask::OpChain::tryConcat(skgpu::ganesh::OpsTask::OpChain::List*, GrProcessorSet::Analysis, GrDstProxyView const&, GrAppliedClip const*, SkRect const&, GrCaps const&, SkArenaAlloc*, GrAuditTrail*)
Line
Count
Source
294
280k
        SkArenaAlloc* opsTaskArena, GrAuditTrail* auditTrail) {
295
280k
    SkASSERT(!fList.empty());
296
280k
    SkASSERT(!list->empty());
297
280k
    SkASSERT(fProcessorAnalysis.requiresDstTexture() == SkToBool(fDstProxyView.proxy()));
298
280k
    SkASSERT(processorAnalysis.requiresDstTexture() == SkToBool(dstProxyView.proxy()));
299
    // All returns use explicit tuple constructor rather than {a, b} to work around old GCC bug.
300
280k
    if (fList.head()->classID() != list->head()->classID() ||
301
280k
        SkToBool(fAppliedClip) != SkToBool(appliedClip) ||
302
280k
        (fAppliedClip && *fAppliedClip != *appliedClip) ||
303
280k
        (fProcessorAnalysis.requiresNonOverlappingDraws() !=
304
212k
                processorAnalysis.requiresNonOverlappingDraws()) ||
305
280k
        (fProcessorAnalysis.requiresNonOverlappingDraws() &&
306
                // Non-overlaping draws are only required when Ganesh will either insert a barrier,
307
                // or read back a new dst texture between draws. In either case, we can neither
308
                // chain nor combine overlapping Ops.
309
210k
                GrRectsTouchOrOverlap(fBounds, bounds)) ||
310
280k
        (fProcessorAnalysis.requiresDstTexture() != processorAnalysis.requiresDstTexture()) ||
311
280k
        (fProcessorAnalysis.requiresDstTexture() && fDstProxyView != dstProxyView)) {
312
69.2k
        return false;
313
69.2k
    }
314
315
210k
    SkDEBUGCODE(bool first = true;)
316
210k
    do {
317
210k
        switch (fList.tail()->combineIfPossible(list->head(), opsTaskArena, caps))
318
210k
        {
319
110k
            case GrOp::CombineResult::kCannotCombine:
320
                // If an op supports chaining then it is required that chaining is transitive and
321
                // that if any two ops in two different chains can merge then the two chains
322
                // may also be chained together. Thus, we should only hit this on the first
323
                // iteration.
324
110k
                SkASSERT(first);
325
110k
                return false;
326
0
            case GrOp::CombineResult::kMayChain:
327
0
                fList = DoConcat(std::move(fList), std::exchange(*list, List()), caps, opsTaskArena,
328
0
                                 auditTrail);
329
                // The above exchange cleared out 'list'. The list needs to be empty now for the
330
                // loop to terminate.
331
0
                SkASSERT(list->empty());
332
0
                break;
333
100k
            case GrOp::CombineResult::kMerged: {
334
100k
                GrOP_INFO("\t\t: (%s opID: %u) -> Combining with (%s, opID: %u)\n",
335
100k
                          list->tail()->name(), list->tail()->uniqueID(), list->head()->name(),
336
100k
                          list->head()->uniqueID());
337
100k
                GR_AUDIT_TRAIL_OPS_RESULT_COMBINED(auditTrail, fList.tail(), list->head());
338
                // The GrOp::Owner releases the op.
339
100k
                list->popHead();
340
100k
                break;
341
0
            }
342
210k
        }
343
100k
        SkDEBUGCODE(first = false);
344
100k
    } while (!list->empty());
345
346
    // The new ops were successfully merged and/or chained onto our own.
347
100k
    fBounds.joinPossiblyEmptyRect(bounds);
348
100k
    return true;
349
210k
}
Unexecuted instantiation: skgpu::ganesh::OpsTask::OpChain::tryConcat(skgpu::ganesh::OpsTask::OpChain::List*, GrProcessorSet::Analysis, GrDstProxyView const&, GrAppliedClip const*, SkRect const&, GrCaps const&, SkArenaAlloc*, GrAuditTrail*)
350
351
bool OpsTask::OpChain::prependChain(OpChain* that, const GrCaps& caps, SkArenaAlloc* opsTaskArena,
352
89.0k
                                    GrAuditTrail* auditTrail) {
353
89.0k
    if (!that->tryConcat(&fList, fProcessorAnalysis, fDstProxyView, fAppliedClip, fBounds, caps,
354
89.0k
                         opsTaskArena, auditTrail)) {
355
88.8k
        this->validate();
356
        // append failed
357
88.8k
        return false;
358
88.8k
    }
359
360
    // 'that' owns the combined chain. Move it into 'this'.
361
210
    SkASSERT(fList.empty());
362
210
    fList = std::move(that->fList);
363
210
    fBounds = that->fBounds;
364
365
210
    that->fDstProxyView.setProxyView({});
366
210
    if (that->fAppliedClip && that->fAppliedClip->hasCoverageFragmentProcessor()) {
367
        // Obliterates the processor.
368
13
        that->fAppliedClip->detachCoverageFragmentProcessor();
369
13
    }
370
210
    this->validate();
371
210
    return true;
372
89.0k
}
373
374
GrOp::Owner OpsTask::OpChain::appendOp(
375
        GrOp::Owner op, GrProcessorSet::Analysis processorAnalysis,
376
        const GrDstProxyView* dstProxyView, const GrAppliedClip* appliedClip, const GrCaps& caps,
377
191k
        SkArenaAlloc* opsTaskArena, GrAuditTrail* auditTrail) {
378
191k
    const GrDstProxyView noDstProxyView;
379
191k
    if (!dstProxyView) {
380
5.09k
        dstProxyView = &noDstProxyView;
381
5.09k
    }
382
191k
    SkASSERT(op->isChainHead() && op->isChainTail());
383
191k
    SkRect opBounds = op->bounds();
384
191k
    List chain(std::move(op));
385
191k
    if (!this->tryConcat(&chain, processorAnalysis, *dstProxyView, appliedClip, opBounds, caps,
386
191k
                         opsTaskArena, auditTrail)) {
387
        // append failed, give the op back to the caller.
388
90.5k
        this->validate();
389
90.5k
        return chain.popHead();
390
90.5k
    }
391
392
100k
    SkASSERT(chain.empty());
393
100k
    this->validate();
394
100k
    return nullptr;
395
191k
}
396
397
280k
inline void OpsTask::OpChain::validate() const {
398
#ifdef SK_DEBUG
399
    fList.validate();
400
    for (const auto& op : GrOp::ChainRange<>(fList.head())) {
401
        // Not using SkRect::contains because we allow empty rects.
402
        SkASSERT(fBounds.fLeft <= op.bounds().fLeft && fBounds.fTop <= op.bounds().fTop &&
403
                 fBounds.fRight >= op.bounds().fRight && fBounds.fBottom >= op.bounds().fBottom);
404
    }
405
#endif
406
280k
}
407
408
////////////////////////////////////////////////////////////////////////////////
409
410
OpsTask::OpsTask(GrDrawingManager* drawingMgr,
411
                 GrSurfaceProxyView view,
412
                 GrAuditTrail* auditTrail,
413
                 sk_sp<GrArenas> arenas)
414
        : GrRenderTask()
415
        , fAuditTrail(auditTrail)
416
        , fUsesMSAASurface(view.asRenderTargetProxy()->numSamples() > 1)
417
        , fTargetSwizzle(view.swizzle())
418
        , fTargetOrigin(view.origin())
419
        , fArenas{std::move(arenas)}
420
195k
          SkDEBUGCODE(, fNumClips(0)) {
421
195k
    this->addTarget(drawingMgr, view.detachProxy());
422
195k
}
423
424
283k
void OpsTask::deleteOps() {
425
283k
    for (auto& chain : fOpChains) {
426
241k
        chain.deleteOps();
427
241k
    }
428
283k
    fOpChains.clear();
429
283k
}
430
431
195k
OpsTask::~OpsTask() {
432
195k
    this->deleteOps();
433
195k
}
434
435
void OpsTask::addOp(GrDrawingManager* drawingMgr, GrOp::Owner op,
436
4.98k
                    GrTextureResolveManager textureResolveManager, const GrCaps& caps) {
437
4.98k
    auto addDependency = [&](GrSurfaceProxy* p, skgpu::Mipmapped mipmapped) {
438
0
        this->addDependency(drawingMgr, p, mipmapped, textureResolveManager, caps);
439
0
    };
440
441
4.98k
    op->visitProxies(addDependency);
442
443
4.98k
    this->recordOp(std::move(op), false/*usesMSAA*/, GrProcessorSet::EmptySetAnalysis(), nullptr,
444
4.98k
                   nullptr, caps);
445
4.98k
}
446
447
void OpsTask::addDrawOp(GrDrawingManager* drawingMgr, GrOp::Owner op, bool usesMSAA,
448
                        const GrProcessorSet::Analysis& processorAnalysis, GrAppliedClip&& clip,
449
                        const GrDstProxyView& dstProxyView,
450
337k
                        GrTextureResolveManager textureResolveManager, const GrCaps& caps) {
451
370k
    auto addDependency = [&](GrSurfaceProxy* p, skgpu::Mipmapped mipmapped) {
452
370k
        this->addSampledTexture(p);
453
370k
        this->addDependency(drawingMgr, p, mipmapped, textureResolveManager, caps);
454
370k
    };
455
456
337k
    op->visitProxies(addDependency);
457
337k
    clip.visitProxies(addDependency);
458
337k
    if (dstProxyView.proxy()) {
459
37.1k
        if (!(dstProxyView.dstSampleFlags() & GrDstSampleFlags::kAsInputAttachment)) {
460
37.1k
            this->addSampledTexture(dstProxyView.proxy());
461
37.1k
        }
462
37.1k
        if (dstProxyView.dstSampleFlags() & GrDstSampleFlags::kRequiresTextureBarrier) {
463
0
            fRenderPassXferBarriers |= GrXferBarrierFlags::kTexture;
464
0
        }
465
37.1k
        addDependency(dstProxyView.proxy(), skgpu::Mipmapped::kNo);
466
37.1k
        SkASSERT(!(dstProxyView.dstSampleFlags() & GrDstSampleFlags::kAsInputAttachment) ||
467
37.1k
                 dstProxyView.offset().isZero());
468
37.1k
    }
469
470
337k
    if (processorAnalysis.usesNonCoherentHWBlending()) {
471
0
        fRenderPassXferBarriers |= GrXferBarrierFlags::kBlend;
472
0
    }
473
474
337k
    this->recordOp(std::move(op), usesMSAA, processorAnalysis, clip.doesClip() ? &clip : nullptr,
475
337k
                   &dstProxyView, caps);
476
337k
}
skgpu::ganesh::OpsTask::addDrawOp(GrDrawingManager*, std::__1::unique_ptr<GrOp, std::__1::default_delete<GrOp> >, bool, GrProcessorSet::Analysis const&, GrAppliedClip&&, GrDstProxyView const&, GrTextureResolveManager, GrCaps const&)
Line
Count
Source
450
337k
                        GrTextureResolveManager textureResolveManager, const GrCaps& caps) {
451
337k
    auto addDependency = [&](GrSurfaceProxy* p, skgpu::Mipmapped mipmapped) {
452
337k
        this->addSampledTexture(p);
453
337k
        this->addDependency(drawingMgr, p, mipmapped, textureResolveManager, caps);
454
337k
    };
455
456
337k
    op->visitProxies(addDependency);
457
337k
    clip.visitProxies(addDependency);
458
337k
    if (dstProxyView.proxy()) {
459
37.1k
        if (!(dstProxyView.dstSampleFlags() & GrDstSampleFlags::kAsInputAttachment)) {
460
37.1k
            this->addSampledTexture(dstProxyView.proxy());
461
37.1k
        }
462
37.1k
        if (dstProxyView.dstSampleFlags() & GrDstSampleFlags::kRequiresTextureBarrier) {
463
0
            fRenderPassXferBarriers |= GrXferBarrierFlags::kTexture;
464
0
        }
465
37.1k
        addDependency(dstProxyView.proxy(), skgpu::Mipmapped::kNo);
466
37.1k
        SkASSERT(!(dstProxyView.dstSampleFlags() & GrDstSampleFlags::kAsInputAttachment) ||
467
37.1k
                 dstProxyView.offset().isZero());
468
37.1k
    }
469
470
337k
    if (processorAnalysis.usesNonCoherentHWBlending()) {
471
0
        fRenderPassXferBarriers |= GrXferBarrierFlags::kBlend;
472
0
    }
473
474
337k
    this->recordOp(std::move(op), usesMSAA, processorAnalysis, clip.doesClip() ? &clip : nullptr,
475
337k
                   &dstProxyView, caps);
476
337k
}
Unexecuted instantiation: skgpu::ganesh::OpsTask::addDrawOp(GrDrawingManager*, std::__1::unique_ptr<GrOp, std::__1::default_delete<GrOp> >, bool, GrProcessorSet::Analysis const&, GrAppliedClip&&, GrDstProxyView const&, GrTextureResolveManager, GrCaps const&)
477
478
1.77k
void OpsTask::endFlush(GrDrawingManager* drawingMgr) {
479
1.77k
    fLastClipStackGenID = SK_InvalidUniqueID;
480
1.77k
    this->deleteOps();
481
482
1.77k
    fDeferredProxies.clear();
483
1.77k
    fSampledProxies.clear();
484
1.77k
    fAuditTrail = nullptr;
485
486
1.77k
    GrRenderTask::endFlush(drawingMgr);
487
1.77k
}
488
489
0
void OpsTask::onPrePrepare(GrRecordingContext* context) {
490
0
    SkASSERT(this->isClosed());
491
    // TODO: remove the check for discard here once reduced op splitting is turned on. Currently we
492
    // can end up with OpsTasks that only have a discard load op and no ops. For vulkan validation
493
    // we need to keep that discard and not drop it. Once we have reduce op list splitting enabled
494
    // we shouldn't end up with OpsTasks with only discard.
495
0
    if (this->isColorNoOp() ||
496
0
        (fClippedContentBounds.isEmpty() && fColorLoadOp != GrLoadOp::kDiscard)) {
497
0
        return;
498
0
    }
499
0
    TRACE_EVENT0("skia.gpu", TRACE_FUNC);
500
501
0
    GrSurfaceProxyView dstView(sk_ref_sp(this->target(0)), fTargetOrigin, fTargetSwizzle);
502
0
    for (const auto& chain : fOpChains) {
503
0
        if (chain.shouldExecute()) {
504
0
            chain.head()->prePrepare(context,
505
0
                                     dstView,
506
0
                                     chain.appliedClip(),
507
0
                                     chain.dstProxyView(),
508
0
                                     fRenderPassXferBarriers,
509
0
                                     fColorLoadOp);
510
0
        }
511
0
    }
512
0
}
Unexecuted instantiation: skgpu::ganesh::OpsTask::onPrePrepare(GrRecordingContext*)
Unexecuted instantiation: skgpu::ganesh::OpsTask::onPrePrepare(GrRecordingContext*)
513
514
172k
void OpsTask::onPrepare(GrOpFlushState* flushState) {
515
172k
    SkASSERT(this->target(0)->peekRenderTarget());
516
172k
    SkASSERT(this->isClosed());
517
    // TODO: remove the check for discard here once reduced op splitting is turned on. Currently we
518
    // can end up with OpsTasks that only have a discard load op and no ops. For vulkan validation
519
    // we need to keep that discard and not drop it. Once we have reduce op list splitting enabled
520
    // we shouldn't end up with OpsTasks with only discard.
521
172k
    if (this->isColorNoOp() ||
522
172k
        (fClippedContentBounds.isEmpty() && fColorLoadOp != GrLoadOp::kDiscard)) {
523
244
        return;
524
244
    }
525
172k
    TRACE_EVENT0_ALWAYS("skia.gpu", TRACE_FUNC);
526
527
172k
    flushState->setSampledProxyArray(&fSampledProxies);
528
172k
    GrSurfaceProxyView dstView(sk_ref_sp(this->target(0)), fTargetOrigin, fTargetSwizzle);
529
    // Loop over the ops that haven't yet been prepared.
530
241k
    for (const auto& chain : fOpChains) {
531
241k
        if (chain.shouldExecute()) {
532
241k
            GrOpFlushState::OpArgs opArgs(chain.head(),
533
241k
                                          dstView,
534
241k
                                          fUsesMSAASurface,
535
241k
                                          chain.appliedClip(),
536
241k
                                          chain.dstProxyView(),
537
241k
                                          fRenderPassXferBarriers,
538
241k
                                          fColorLoadOp);
539
540
241k
            flushState->setOpArgs(&opArgs);
541
542
            // Temporary debugging helper: for debugging prePrepare w/o going through DDLs
543
            // Delete once most of the GrOps have an onPrePrepare.
544
            // chain.head()->prePrepare(flushState->gpu()->getContext(), &this->target(0),
545
            //                          chain.appliedClip());
546
547
            // GrOp::prePrepare may or may not have been called at this point
548
241k
            chain.head()->prepare(flushState);
549
241k
            flushState->setOpArgs(nullptr);
550
241k
        }
551
241k
    }
552
172k
    flushState->setSampledProxyArray(nullptr);
553
172k
}
skgpu::ganesh::OpsTask::onPrepare(GrOpFlushState*)
Line
Count
Source
514
172k
void OpsTask::onPrepare(GrOpFlushState* flushState) {
515
172k
    SkASSERT(this->target(0)->peekRenderTarget());
516
172k
    SkASSERT(this->isClosed());
517
    // TODO: remove the check for discard here once reduced op splitting is turned on. Currently we
518
    // can end up with OpsTasks that only have a discard load op and no ops. For vulkan validation
519
    // we need to keep that discard and not drop it. Once we have reduce op list splitting enabled
520
    // we shouldn't end up with OpsTasks with only discard.
521
172k
    if (this->isColorNoOp() ||
522
172k
        (fClippedContentBounds.isEmpty() && fColorLoadOp != GrLoadOp::kDiscard)) {
523
244
        return;
524
244
    }
525
172k
    TRACE_EVENT0_ALWAYS("skia.gpu", TRACE_FUNC);
526
527
172k
    flushState->setSampledProxyArray(&fSampledProxies);
528
172k
    GrSurfaceProxyView dstView(sk_ref_sp(this->target(0)), fTargetOrigin, fTargetSwizzle);
529
    // Loop over the ops that haven't yet been prepared.
530
241k
    for (const auto& chain : fOpChains) {
531
241k
        if (chain.shouldExecute()) {
532
241k
            GrOpFlushState::OpArgs opArgs(chain.head(),
533
241k
                                          dstView,
534
241k
                                          fUsesMSAASurface,
535
241k
                                          chain.appliedClip(),
536
241k
                                          chain.dstProxyView(),
537
241k
                                          fRenderPassXferBarriers,
538
241k
                                          fColorLoadOp);
539
540
241k
            flushState->setOpArgs(&opArgs);
541
542
            // Temporary debugging helper: for debugging prePrepare w/o going through DDLs
543
            // Delete once most of the GrOps have an onPrePrepare.
544
            // chain.head()->prePrepare(flushState->gpu()->getContext(), &this->target(0),
545
            //                          chain.appliedClip());
546
547
            // GrOp::prePrepare may or may not have been called at this point
548
241k
            chain.head()->prepare(flushState);
549
241k
            flushState->setOpArgs(nullptr);
550
241k
        }
551
241k
    }
552
172k
    flushState->setSampledProxyArray(nullptr);
553
172k
}
Unexecuted instantiation: skgpu::ganesh::OpsTask::onPrepare(GrOpFlushState*)
554
555
// TODO: this is where GrOp::renderTarget is used (which is fine since it
556
// is at flush time). However, we need to store the RenderTargetProxy in the
557
// Ops and instantiate them here.
558
172k
bool OpsTask::onExecute(GrOpFlushState* flushState) {
559
172k
    SkASSERT(this->numTargets() == 1);
560
172k
    GrRenderTargetProxy* proxy = this->target(0)->asRenderTargetProxy();
561
172k
    SkASSERT(proxy);
562
172k
    SK_AT_SCOPE_EXIT(proxy->clearArenas());
563
564
172k
    if (this->isColorNoOp() || fClippedContentBounds.isEmpty()) {
565
244
        return false;
566
244
    }
567
172k
    TRACE_EVENT0_ALWAYS("skia.gpu", TRACE_FUNC);
568
569
    // Make sure load ops are not kClear if the GPU needs to use draws for clears
570
172k
    SkASSERT(fColorLoadOp != GrLoadOp::kClear ||
571
172k
             !flushState->gpu()->caps()->performColorClearsAsDraws());
572
573
172k
    const GrCaps& caps = *flushState->gpu()->caps();
574
172k
    GrRenderTarget* renderTarget = proxy->peekRenderTarget();
575
172k
    SkASSERT(renderTarget);
576
577
172k
    GrAttachment* stencil = nullptr;
578
172k
    if (proxy->needsStencil()) {
579
9.87k
        SkASSERT(proxy->canUseStencil(caps));
580
9.87k
        if (!flushState->resourceProvider()->attachStencilAttachment(renderTarget,
581
9.87k
                                                                     fUsesMSAASurface)) {
582
0
            SkDebugf("WARNING: failed to attach a stencil buffer. Rendering will be skipped.\n");
583
0
            return false;
584
0
        }
585
9.87k
        stencil = renderTarget->getStencilAttachment(fUsesMSAASurface);
586
9.87k
    }
587
588
172k
    GrLoadOp stencilLoadOp;
589
172k
    switch (fInitialStencilContent) {
590
164k
        case StencilContent::kDontCare:
591
164k
            stencilLoadOp = GrLoadOp::kDiscard;
592
164k
            break;
593
644
        case StencilContent::kUserBitsCleared:
594
644
            SkASSERT(!caps.performStencilClearsAsDraws());
595
644
            SkASSERT(stencil);
596
644
            if (caps.discardStencilValuesAfterRenderPass()) {
597
                // Always clear the stencil if it is being discarded after render passes. This is
598
                // also an optimization because we are on a tiler and it avoids loading the values
599
                // from memory.
600
0
                stencilLoadOp = GrLoadOp::kClear;
601
0
                break;
602
0
            }
603
644
            if (!stencil->hasPerformedInitialClear()) {
604
507
                stencilLoadOp = GrLoadOp::kClear;
605
507
                stencil->markHasPerformedInitialClear();
606
507
                break;
607
507
            }
608
            // SurfaceDrawContexts are required to leave the user stencil bits in a cleared state
609
            // once finished, meaning the stencil values will always remain cleared after the
610
            // initial clear. Just fall through to reloading the existing (cleared) stencil values
611
            // from memory.
612
644
            [[fallthrough]];
613
7.17k
        case StencilContent::kPreserved:
614
7.17k
            SkASSERT(stencil);
615
7.17k
            stencilLoadOp = GrLoadOp::kLoad;
616
7.17k
            break;
617
172k
    }
618
619
    // NOTE: If fMustPreserveStencil is set, then we are executing a surfaceDrawContext that split
620
    // its opsTask.
621
    //
622
    // FIXME: We don't currently flag render passes that don't use stencil at all. In that case
623
    // their store op might be "discard", and we currently make the assumption that a discard will
624
    // not invalidate what's already in main memory. This is probably ok for now, but certainly
625
    // something we want to address soon.
626
172k
    GrStoreOp stencilStoreOp = (caps.discardStencilValuesAfterRenderPass() && !fMustPreserveStencil)
627
172k
            ? GrStoreOp::kDiscard
628
172k
            : GrStoreOp::kStore;
629
630
172k
    GrOpsRenderPass* renderPass = create_render_pass(flushState->gpu(),
631
172k
                                                     proxy->peekRenderTarget(),
632
172k
                                                     fUsesMSAASurface,
633
172k
                                                     stencil,
634
172k
                                                     fTargetOrigin,
635
172k
                                                     fClippedContentBounds,
636
172k
                                                     fColorLoadOp,
637
172k
                                                     fLoadClearColor,
638
172k
                                                     stencilLoadOp,
639
172k
                                                     stencilStoreOp,
640
172k
                                                     fSampledProxies,
641
172k
                                                     fRenderPassXferBarriers);
642
643
172k
    if (!renderPass) {
644
0
        return false;
645
0
    }
646
172k
    flushState->setOpsRenderPass(renderPass);
647
172k
    renderPass->begin();
648
649
172k
    GrSurfaceProxyView dstView(sk_ref_sp(this->target(0)), fTargetOrigin, fTargetSwizzle);
650
651
    // Draw all the generated geometry.
652
241k
    for (const auto& chain : fOpChains) {
653
241k
        if (!chain.shouldExecute()) {
654
210
            continue;
655
210
        }
656
657
241k
        GrOpFlushState::OpArgs opArgs(chain.head(),
658
241k
                                      dstView,
659
241k
                                      fUsesMSAASurface,
660
241k
                                      chain.appliedClip(),
661
241k
                                      chain.dstProxyView(),
662
241k
                                      fRenderPassXferBarriers,
663
241k
                                      fColorLoadOp);
664
665
241k
        flushState->setOpArgs(&opArgs);
666
241k
        chain.head()->execute(flushState, chain.bounds());
667
241k
        flushState->setOpArgs(nullptr);
668
241k
    }
669
670
172k
    renderPass->end();
671
172k
    flushState->gpu()->submit(renderPass);
672
172k
    flushState->setOpsRenderPass(nullptr);
673
674
172k
    return true;
675
172k
}
skgpu::ganesh::OpsTask::onExecute(GrOpFlushState*)
Line
Count
Source
558
172k
bool OpsTask::onExecute(GrOpFlushState* flushState) {
559
172k
    SkASSERT(this->numTargets() == 1);
560
172k
    GrRenderTargetProxy* proxy = this->target(0)->asRenderTargetProxy();
561
172k
    SkASSERT(proxy);
562
172k
    SK_AT_SCOPE_EXIT(proxy->clearArenas());
563
564
172k
    if (this->isColorNoOp() || fClippedContentBounds.isEmpty()) {
565
244
        return false;
566
244
    }
567
172k
    TRACE_EVENT0_ALWAYS("skia.gpu", TRACE_FUNC);
568
569
    // Make sure load ops are not kClear if the GPU needs to use draws for clears
570
172k
    SkASSERT(fColorLoadOp != GrLoadOp::kClear ||
571
172k
             !flushState->gpu()->caps()->performColorClearsAsDraws());
572
573
172k
    const GrCaps& caps = *flushState->gpu()->caps();
574
172k
    GrRenderTarget* renderTarget = proxy->peekRenderTarget();
575
172k
    SkASSERT(renderTarget);
576
577
172k
    GrAttachment* stencil = nullptr;
578
172k
    if (proxy->needsStencil()) {
579
9.87k
        SkASSERT(proxy->canUseStencil(caps));
580
9.87k
        if (!flushState->resourceProvider()->attachStencilAttachment(renderTarget,
581
9.87k
                                                                     fUsesMSAASurface)) {
582
0
            SkDebugf("WARNING: failed to attach a stencil buffer. Rendering will be skipped.\n");
583
0
            return false;
584
0
        }
585
9.87k
        stencil = renderTarget->getStencilAttachment(fUsesMSAASurface);
586
9.87k
    }
587
588
172k
    GrLoadOp stencilLoadOp;
589
172k
    switch (fInitialStencilContent) {
590
164k
        case StencilContent::kDontCare:
591
164k
            stencilLoadOp = GrLoadOp::kDiscard;
592
164k
            break;
593
644
        case StencilContent::kUserBitsCleared:
594
644
            SkASSERT(!caps.performStencilClearsAsDraws());
595
644
            SkASSERT(stencil);
596
644
            if (caps.discardStencilValuesAfterRenderPass()) {
597
                // Always clear the stencil if it is being discarded after render passes. This is
598
                // also an optimization because we are on a tiler and it avoids loading the values
599
                // from memory.
600
0
                stencilLoadOp = GrLoadOp::kClear;
601
0
                break;
602
0
            }
603
644
            if (!stencil->hasPerformedInitialClear()) {
604
507
                stencilLoadOp = GrLoadOp::kClear;
605
507
                stencil->markHasPerformedInitialClear();
606
507
                break;
607
507
            }
608
            // SurfaceDrawContexts are required to leave the user stencil bits in a cleared state
609
            // once finished, meaning the stencil values will always remain cleared after the
610
            // initial clear. Just fall through to reloading the existing (cleared) stencil values
611
            // from memory.
612
644
            [[fallthrough]];
613
7.17k
        case StencilContent::kPreserved:
614
7.17k
            SkASSERT(stencil);
615
7.17k
            stencilLoadOp = GrLoadOp::kLoad;
616
7.17k
            break;
617
172k
    }
618
619
    // NOTE: If fMustPreserveStencil is set, then we are executing a surfaceDrawContext that split
620
    // its opsTask.
621
    //
622
    // FIXME: We don't currently flag render passes that don't use stencil at all. In that case
623
    // their store op might be "discard", and we currently make the assumption that a discard will
624
    // not invalidate what's already in main memory. This is probably ok for now, but certainly
625
    // something we want to address soon.
626
172k
    GrStoreOp stencilStoreOp = (caps.discardStencilValuesAfterRenderPass() && !fMustPreserveStencil)
627
172k
            ? GrStoreOp::kDiscard
628
172k
            : GrStoreOp::kStore;
629
630
172k
    GrOpsRenderPass* renderPass = create_render_pass(flushState->gpu(),
631
172k
                                                     proxy->peekRenderTarget(),
632
172k
                                                     fUsesMSAASurface,
633
172k
                                                     stencil,
634
172k
                                                     fTargetOrigin,
635
172k
                                                     fClippedContentBounds,
636
172k
                                                     fColorLoadOp,
637
172k
                                                     fLoadClearColor,
638
172k
                                                     stencilLoadOp,
639
172k
                                                     stencilStoreOp,
640
172k
                                                     fSampledProxies,
641
172k
                                                     fRenderPassXferBarriers);
642
643
172k
    if (!renderPass) {
644
0
        return false;
645
0
    }
646
172k
    flushState->setOpsRenderPass(renderPass);
647
172k
    renderPass->begin();
648
649
172k
    GrSurfaceProxyView dstView(sk_ref_sp(this->target(0)), fTargetOrigin, fTargetSwizzle);
650
651
    // Draw all the generated geometry.
652
241k
    for (const auto& chain : fOpChains) {
653
241k
        if (!chain.shouldExecute()) {
654
210
            continue;
655
210
        }
656
657
241k
        GrOpFlushState::OpArgs opArgs(chain.head(),
658
241k
                                      dstView,
659
241k
                                      fUsesMSAASurface,
660
241k
                                      chain.appliedClip(),
661
241k
                                      chain.dstProxyView(),
662
241k
                                      fRenderPassXferBarriers,
663
241k
                                      fColorLoadOp);
664
665
241k
        flushState->setOpArgs(&opArgs);
666
241k
        chain.head()->execute(flushState, chain.bounds());
667
241k
        flushState->setOpArgs(nullptr);
668
241k
    }
669
670
172k
    renderPass->end();
671
172k
    flushState->gpu()->submit(renderPass);
672
172k
    flushState->setOpsRenderPass(nullptr);
673
674
172k
    return true;
675
172k
}
Unexecuted instantiation: skgpu::ganesh::OpsTask::onExecute(GrOpFlushState*)
676
677
86.2k
void OpsTask::setColorLoadOp(GrLoadOp op, std::array<float, 4> color) {
678
86.2k
    fColorLoadOp = op;
679
86.2k
    fLoadClearColor = color;
680
86.2k
    if (GrLoadOp::kClear == fColorLoadOp) {
681
86.1k
        GrSurfaceProxy* proxy = this->target(0);
682
86.1k
        SkASSERT(proxy);
683
86.1k
        fTotalBounds = proxy->backingStoreBoundsRect();
684
86.1k
    }
685
86.2k
}
686
687
0
void OpsTask::reset() {
688
0
    fDeferredProxies.clear();
689
0
    fSampledProxies.clear();
690
0
    fClippedContentBounds = SkIRect::MakeEmpty();
691
0
    fTotalBounds = SkRect::MakeEmpty();
692
0
    this->deleteOps();
693
0
    fRenderPassXferBarriers = GrXferBarrierFlags::kNone;
694
0
}
695
696
126k
bool OpsTask::canMerge(const OpsTask* opsTask) const {
697
126k
    return this->target(0) == opsTask->target(0) &&
698
126k
           fArenas == opsTask->fArenas &&
699
126k
           !opsTask->fCannotMergeBackward;
700
126k
}
701
702
133k
int OpsTask::mergeFrom(SkSpan<const sk_sp<GrRenderTask>> tasks) {
703
133k
    int mergedCount = 0;
704
155k
    for (const sk_sp<GrRenderTask>& task : tasks) {
705
155k
        auto opsTask = task->asOpsTask();
706
155k
        if (!opsTask || !this->canMerge(opsTask)) {
707
130k
            break;
708
130k
        }
709
24.9k
        SkASSERT(fTargetSwizzle == opsTask->fTargetSwizzle);
710
24.9k
        SkASSERT(fTargetOrigin == opsTask->fTargetOrigin);
711
24.9k
        if (GrLoadOp::kClear == opsTask->fColorLoadOp) {
712
            // TODO(11903): Go back to actually dropping ops tasks when we are merged with
713
            // color clear.
714
888
            return 0;
715
888
        }
716
24.0k
        mergedCount += 1;
717
24.0k
    }
718
132k
    if (0 == mergedCount) {
719
115k
        return 0;
720
115k
    }
721
722
17.1k
    SkSpan<const sk_sp<OpsTask>> mergingNodes(
723
17.1k
            reinterpret_cast<const sk_sp<OpsTask>*>(tasks.data()), SkToSizeT(mergedCount));
724
17.1k
    int addlDeferredProxyCount = 0;
725
17.1k
    int addlProxyCount = 0;
726
17.1k
    int addlOpChainCount = 0;
727
22.8k
    for (const auto& toMerge : mergingNodes) {
728
22.8k
        addlDeferredProxyCount += toMerge->fDeferredProxies.size();
729
22.8k
        addlProxyCount += toMerge->fSampledProxies.size();
730
22.8k
        addlOpChainCount += toMerge->fOpChains.size();
731
22.8k
        fClippedContentBounds.join(toMerge->fClippedContentBounds);
732
22.8k
        fTotalBounds.join(toMerge->fTotalBounds);
733
22.8k
        fRenderPassXferBarriers |= toMerge->fRenderPassXferBarriers;
734
22.8k
        if (fInitialStencilContent == StencilContent::kDontCare) {
735
            // Propogate the first stencil content that isn't kDontCare.
736
            //
737
            // Once the stencil has any kind of initial content that isn't kDontCare, then the
738
            // inital contents of subsequent opsTasks that get merged in don't matter.
739
            //
740
            // (This works because the opsTask all target the same render target and are in
741
            // painter's order. kPreserved obviously happens automatically with a merge, and kClear
742
            // is also automatic because the contract is for ops to leave the stencil buffer in a
743
            // cleared state when finished.)
744
21.0k
            fInitialStencilContent = toMerge->fInitialStencilContent;
745
21.0k
        }
746
22.8k
        fUsesMSAASurface |= toMerge->fUsesMSAASurface;
747
22.8k
        SkDEBUGCODE(fNumClips += toMerge->fNumClips);
748
22.8k
    }
749
750
17.1k
    fLastClipStackGenID = SK_InvalidUniqueID;
751
17.1k
    fDeferredProxies.reserve_exact(fDeferredProxies.size() + addlDeferredProxyCount);
752
17.1k
    fSampledProxies.reserve_exact(fSampledProxies.size() + addlProxyCount);
753
17.1k
    fOpChains.reserve_exact(fOpChains.size() + addlOpChainCount);
754
22.8k
    for (const auto& toMerge : mergingNodes) {
755
22.8k
        for (GrRenderTask* renderTask : toMerge->dependents()) {
756
16.9k
            renderTask->replaceDependency(toMerge.get(), this);
757
16.9k
        }
758
22.8k
        for (GrRenderTask* renderTask : toMerge->dependencies()) {
759
8.76k
            renderTask->replaceDependent(toMerge.get(), this);
760
8.76k
        }
761
22.8k
        fDeferredProxies.move_back_n(toMerge->fDeferredProxies.size(),
762
22.8k
                                     toMerge->fDeferredProxies.data());
763
22.8k
        fSampledProxies.move_back_n(toMerge->fSampledProxies.size(),
764
22.8k
                                    toMerge->fSampledProxies.data());
765
22.8k
        fOpChains.move_back_n(toMerge->fOpChains.size(),
766
22.8k
                              toMerge->fOpChains.data());
767
22.8k
        toMerge->fDeferredProxies.clear();
768
22.8k
        toMerge->fSampledProxies.clear();
769
22.8k
        toMerge->fOpChains.clear();
770
22.8k
    }
771
17.1k
    fMustPreserveStencil = mergingNodes.back()->fMustPreserveStencil;
772
17.1k
    return mergedCount;
773
132k
}
skgpu::ganesh::OpsTask::mergeFrom(SkSpan<sk_sp<GrRenderTask> const>)
Line
Count
Source
702
133k
int OpsTask::mergeFrom(SkSpan<const sk_sp<GrRenderTask>> tasks) {
703
133k
    int mergedCount = 0;
704
155k
    for (const sk_sp<GrRenderTask>& task : tasks) {
705
155k
        auto opsTask = task->asOpsTask();
706
155k
        if (!opsTask || !this->canMerge(opsTask)) {
707
130k
            break;
708
130k
        }
709
24.9k
        SkASSERT(fTargetSwizzle == opsTask->fTargetSwizzle);
710
24.9k
        SkASSERT(fTargetOrigin == opsTask->fTargetOrigin);
711
24.9k
        if (GrLoadOp::kClear == opsTask->fColorLoadOp) {
712
            // TODO(11903): Go back to actually dropping ops tasks when we are merged with
713
            // color clear.
714
888
            return 0;
715
888
        }
716
24.0k
        mergedCount += 1;
717
24.0k
    }
718
132k
    if (0 == mergedCount) {
719
115k
        return 0;
720
115k
    }
721
722
17.1k
    SkSpan<const sk_sp<OpsTask>> mergingNodes(
723
17.1k
            reinterpret_cast<const sk_sp<OpsTask>*>(tasks.data()), SkToSizeT(mergedCount));
724
17.1k
    int addlDeferredProxyCount = 0;
725
17.1k
    int addlProxyCount = 0;
726
17.1k
    int addlOpChainCount = 0;
727
22.8k
    for (const auto& toMerge : mergingNodes) {
728
22.8k
        addlDeferredProxyCount += toMerge->fDeferredProxies.size();
729
22.8k
        addlProxyCount += toMerge->fSampledProxies.size();
730
22.8k
        addlOpChainCount += toMerge->fOpChains.size();
731
22.8k
        fClippedContentBounds.join(toMerge->fClippedContentBounds);
732
22.8k
        fTotalBounds.join(toMerge->fTotalBounds);
733
22.8k
        fRenderPassXferBarriers |= toMerge->fRenderPassXferBarriers;
734
22.8k
        if (fInitialStencilContent == StencilContent::kDontCare) {
735
            // Propogate the first stencil content that isn't kDontCare.
736
            //
737
            // Once the stencil has any kind of initial content that isn't kDontCare, then the
738
            // inital contents of subsequent opsTasks that get merged in don't matter.
739
            //
740
            // (This works because the opsTask all target the same render target and are in
741
            // painter's order. kPreserved obviously happens automatically with a merge, and kClear
742
            // is also automatic because the contract is for ops to leave the stencil buffer in a
743
            // cleared state when finished.)
744
21.0k
            fInitialStencilContent = toMerge->fInitialStencilContent;
745
21.0k
        }
746
22.8k
        fUsesMSAASurface |= toMerge->fUsesMSAASurface;
747
22.8k
        SkDEBUGCODE(fNumClips += toMerge->fNumClips);
748
22.8k
    }
749
750
17.1k
    fLastClipStackGenID = SK_InvalidUniqueID;
751
17.1k
    fDeferredProxies.reserve_exact(fDeferredProxies.size() + addlDeferredProxyCount);
752
17.1k
    fSampledProxies.reserve_exact(fSampledProxies.size() + addlProxyCount);
753
17.1k
    fOpChains.reserve_exact(fOpChains.size() + addlOpChainCount);
754
22.8k
    for (const auto& toMerge : mergingNodes) {
755
22.8k
        for (GrRenderTask* renderTask : toMerge->dependents()) {
756
16.9k
            renderTask->replaceDependency(toMerge.get(), this);
757
16.9k
        }
758
22.8k
        for (GrRenderTask* renderTask : toMerge->dependencies()) {
759
8.76k
            renderTask->replaceDependent(toMerge.get(), this);
760
8.76k
        }
761
22.8k
        fDeferredProxies.move_back_n(toMerge->fDeferredProxies.size(),
762
22.8k
                                     toMerge->fDeferredProxies.data());
763
22.8k
        fSampledProxies.move_back_n(toMerge->fSampledProxies.size(),
764
22.8k
                                    toMerge->fSampledProxies.data());
765
22.8k
        fOpChains.move_back_n(toMerge->fOpChains.size(),
766
22.8k
                              toMerge->fOpChains.data());
767
22.8k
        toMerge->fDeferredProxies.clear();
768
22.8k
        toMerge->fSampledProxies.clear();
769
22.8k
        toMerge->fOpChains.clear();
770
22.8k
    }
771
17.1k
    fMustPreserveStencil = mergingNodes.back()->fMustPreserveStencil;
772
17.1k
    return mergedCount;
773
132k
}
Unexecuted instantiation: skgpu::ganesh::OpsTask::mergeFrom(SkSpan<sk_sp<GrRenderTask> const>)
774
775
86.2k
bool OpsTask::resetForFullscreenClear(CanDiscardPreviousOps canDiscardPreviousOps) {
776
86.2k
    if (CanDiscardPreviousOps::kYes == canDiscardPreviousOps || this->isEmpty()) {
777
86.1k
        this->deleteOps();
778
86.1k
        fDeferredProxies.clear();
779
86.1k
        fSampledProxies.clear();
780
781
        // If the opsTask is using a render target which wraps a vulkan command buffer, we can't do
782
        // a clear load since we cannot change the render pass that we are using. Thus we fall back
783
        // to making a clear op in this case.
784
86.1k
        return !this->target(0)->asRenderTargetProxy()->wrapsVkSecondaryCB();
785
86.1k
    }
786
787
    // Could not empty the task, so an op must be added to handle the clear
788
106
    return false;
789
86.2k
}
790
791
0
void OpsTask::discard() {
792
    // Discard calls to in-progress opsTasks are ignored. Calls at the start update the
793
    // opsTasks' color & stencil load ops.
794
0
    if (this->isEmpty()) {
795
0
        fColorLoadOp = GrLoadOp::kDiscard;
796
0
        fInitialStencilContent = StencilContent::kDontCare;
797
0
        fTotalBounds.setEmpty();
798
0
    }
799
0
}
800
801
////////////////////////////////////////////////////////////////////////////////
802
803
#if defined(GPU_TEST_UTILS)
804
void OpsTask::dump(const SkString& label,
805
                   SkString indent,
806
                   bool printDependencies,
807
0
                   bool close) const {
808
0
    GrRenderTask::dump(label, indent, printDependencies, false);
809
810
0
    SkDebugf("%sfColorLoadOp: ", indent.c_str());
811
0
    switch (fColorLoadOp) {
812
0
        case GrLoadOp::kLoad:
813
0
            SkDebugf("kLoad\n");
814
0
            break;
815
0
        case GrLoadOp::kClear:
816
0
            SkDebugf("kClear {%g, %g, %g, %g}\n",
817
0
                     fLoadClearColor[0],
818
0
                     fLoadClearColor[1],
819
0
                     fLoadClearColor[2],
820
0
                     fLoadClearColor[3]);
821
0
            break;
822
0
        case GrLoadOp::kDiscard:
823
0
            SkDebugf("kDiscard\n");
824
0
            break;
825
0
    }
826
827
0
    SkDebugf("%sfInitialStencilContent: ", indent.c_str());
828
0
    switch (fInitialStencilContent) {
829
0
        case StencilContent::kDontCare:
830
0
            SkDebugf("kDontCare\n");
831
0
            break;
832
0
        case StencilContent::kUserBitsCleared:
833
0
            SkDebugf("kUserBitsCleared\n");
834
0
            break;
835
0
        case StencilContent::kPreserved:
836
0
            SkDebugf("kPreserved\n");
837
0
            break;
838
0
    }
839
840
0
    SkDebugf("%s%d ops:\n", indent.c_str(), fOpChains.size());
841
0
    for (int i = 0; i < fOpChains.size(); ++i) {
842
0
        SkDebugf("%s*******************************\n", indent.c_str());
843
0
        if (!fOpChains[i].head()) {
844
0
            SkDebugf("%s%d: <combined forward or failed instantiation>\n", indent.c_str(), i);
845
0
        } else {
846
0
            SkDebugf("%s%d: %s\n", indent.c_str(), i, fOpChains[i].head()->name());
847
0
            SkRect bounds = fOpChains[i].bounds();
848
0
            SkDebugf("%sClippedBounds: [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n",
849
0
                     indent.c_str(),
850
0
                     bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom);
851
0
            for (const auto& op : GrOp::ChainRange<>(fOpChains[i].head())) {
852
0
                SkString info = SkTabString(op.dumpInfo(), 1);
853
0
                SkDebugf("%s%s\n", indent.c_str(), info.c_str());
854
0
                bounds = op.bounds();
855
0
                SkDebugf("%s\tClippedBounds: [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n",
856
0
                         indent.c_str(),
857
0
                         bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom);
858
0
            }
859
0
        }
860
0
    }
861
862
0
    if (close) {
863
0
        SkDebugf("%s--------------------------------------------------------------\n\n",
864
0
                 indent.c_str());
865
0
    }
866
0
}
867
#endif
868
869
#ifdef SK_DEBUG
870
0
void OpsTask::visitProxies_debugOnly(const GrVisitProxyFunc& func) const {
871
0
    auto textureFunc = [func](GrSurfaceProxy* tex, skgpu::Mipmapped mipmapped) {
872
0
        func(tex, mipmapped);
873
0
    };
874
875
0
    for (const OpChain& chain : fOpChains) {
876
0
        chain.visitProxies(textureFunc);
877
0
    }
878
0
}
879
880
#endif
881
882
////////////////////////////////////////////////////////////////////////////////
883
884
0
void OpsTask::onMakeSkippable() {
885
0
    this->deleteOps();
886
0
    fDeferredProxies.clear();
887
0
    fColorLoadOp = GrLoadOp::kLoad;
888
0
    SkASSERT(this->isColorNoOp());
889
0
}
Unexecuted instantiation: skgpu::ganesh::OpsTask::onMakeSkippable()
Unexecuted instantiation: skgpu::ganesh::OpsTask::onMakeSkippable()
890
891
2.04M
bool OpsTask::onIsUsed(GrSurfaceProxy* proxyToCheck) const {
892
2.04M
    bool used = false;
893
3.08M
    for (GrSurfaceProxy* proxy : fSampledProxies) {
894
3.08M
        if (proxy == proxyToCheck) {
895
0
            used = true;
896
0
            break;
897
0
        }
898
3.08M
    }
899
#ifdef SK_DEBUG
900
    bool usedSlow = false;
901
0
    auto visit = [proxyToCheck, &usedSlow](GrSurfaceProxy* p, skgpu::Mipmapped) {
902
0
        if (p == proxyToCheck) {
903
0
            usedSlow = true;
904
0
        }
905
0
    };
906
    this->visitProxies_debugOnly(visit);
907
    SkASSERT(used == usedSlow);
908
#endif
909
910
2.04M
    return used;
911
2.04M
}
912
913
206k
void OpsTask::gatherProxyIntervals(GrResourceAllocator* alloc) const {
914
206k
    SkASSERT(this->isClosed());
915
206k
    if (this->isColorNoOp()) {
916
12.9k
        return;
917
12.9k
    }
918
919
193k
    for (int i = 0; i < fDeferredProxies.size(); ++i) {
920
0
        SkASSERT(!fDeferredProxies[i]->isInstantiated());
921
        // We give all the deferred proxies a write usage at the very start of flushing. This
922
        // locks them out of being reused for the entire flush until they are read - and then
923
        // they can be recycled. This is a bit unfortunate because a flush can proceed in waves
924
        // with sub-flushes. The deferred proxies only need to be pinned from the start of
925
        // the sub-flush in which they appear.
926
0
        alloc->addInterval(fDeferredProxies[i], 0, 0, GrResourceAllocator::ActualUse::kNo,
927
0
                           GrResourceAllocator::AllowRecycling::kYes);
928
0
    }
929
930
193k
    GrSurfaceProxy* targetSurface = this->target(0);
931
193k
    SkASSERT(targetSurface);
932
193k
    GrRenderTargetProxy* targetProxy = targetSurface->asRenderTargetProxy();
933
934
    // Add the interval for all the writes to this OpsTasks's target
935
193k
    if (!fOpChains.empty()) {
936
173k
        unsigned int cur = alloc->curOp();
937
938
173k
        alloc->addInterval(targetProxy, cur, cur + fOpChains.size() - 1,
939
173k
                           GrResourceAllocator::ActualUse::kYes,
940
173k
                           GrResourceAllocator::AllowRecycling::kYes);
941
173k
    } else {
942
        // This can happen if there is a loadOp (e.g., a clear) but no other draws. In this case we
943
        // still need to add an interval for the destination so we create a fake op# for
944
        // the missing clear op.
945
20.8k
        alloc->addInterval(targetProxy, alloc->curOp(), alloc->curOp(),
946
20.8k
                           GrResourceAllocator::ActualUse::kYes,
947
20.8k
                           GrResourceAllocator::AllowRecycling::kYes);
948
20.8k
        alloc->incOps();
949
20.8k
    }
950
951
193k
    GrResourceAllocator::AllowRecycling allowRecycling =
952
193k
            targetProxy->wrapsVkSecondaryCB() ? GrResourceAllocator::AllowRecycling::kNo
953
193k
                                              : GrResourceAllocator::AllowRecycling::kYes;
954
955
290k
    auto gather = [alloc, allowRecycling SkDEBUGCODE(, this)](GrSurfaceProxy* p, skgpu::Mipmapped) {
956
290k
        alloc->addInterval(p,
957
290k
                           alloc->curOp(),
958
290k
                           alloc->curOp(),
959
290k
                           GrResourceAllocator::ActualUse::kYes,
960
290k
                           allowRecycling
961
290k
                           SkDEBUGCODE(, this->target(0) == p));
962
290k
    };
OpsTask.cpp:skgpu::ganesh::OpsTask::gatherProxyIntervals(GrResourceAllocator*) const::$_0::operator()(GrSurfaceProxy*, skgpu::Mipmapped) const
Line
Count
Source
955
290k
    auto gather = [alloc, allowRecycling SkDEBUGCODE(, this)](GrSurfaceProxy* p, skgpu::Mipmapped) {
956
290k
        alloc->addInterval(p,
957
290k
                           alloc->curOp(),
958
290k
                           alloc->curOp(),
959
290k
                           GrResourceAllocator::ActualUse::kYes,
960
290k
                           allowRecycling
961
290k
                           SkDEBUGCODE(, this->target(0) == p));
962
290k
    };
Unexecuted instantiation: OpsTask.cpp:skgpu::ganesh::OpsTask::gatherProxyIntervals(GrResourceAllocator*) const::$_3::operator()(GrSurfaceProxy*, skgpu::Mipmapped) const
963
    // TODO: visitProxies is expensive. Can we do this with fSampledProxies instead?
964
255k
    for (const OpChain& recordedOp : fOpChains) {
965
255k
        recordedOp.visitProxies(gather);
966
967
        // Even though the op may have been (re)moved we still need to increment the op count to
968
        // keep all the math consistent.
969
255k
        alloc->incOps();
970
255k
    }
971
193k
}
972
973
void OpsTask::recordOp(
974
        GrOp::Owner op, bool usesMSAA, GrProcessorSet::Analysis processorAnalysis,
975
342k
        GrAppliedClip* clip, const GrDstProxyView* dstProxyView, const GrCaps& caps) {
976
342k
    GrSurfaceProxy* proxy = this->target(0);
977
#ifdef SK_DEBUG
978
    op->validate();
979
    SkASSERT(processorAnalysis.requiresDstTexture() == (dstProxyView && dstProxyView->proxy()));
980
    SkASSERT(proxy);
981
    // A closed OpsTask should never receive new/more ops
982
    SkASSERT(!this->isClosed());
983
    // Ensure we can support dynamic msaa if the caller is trying to trigger it.
984
    if (proxy->asRenderTargetProxy()->numSamples() == 1 && usesMSAA) {
985
        SkASSERT(caps.supportsDynamicMSAA(proxy->asRenderTargetProxy()));
986
    }
987
#endif
988
989
342k
    if (!op->bounds().isFinite()) {
990
0
        return;
991
0
    }
992
993
342k
    fUsesMSAASurface |= usesMSAA;
994
995
    // Account for this op's bounds before we attempt to combine.
996
    // NOTE: The caller should have already called "op->setClippedBounds()" by now, if applicable.
997
342k
    fTotalBounds.join(op->bounds());
998
999
    // Check if there is an op we can combine with by linearly searching back until we either
1000
    // 1) check every op
1001
    // 2) intersect with something
1002
    // 3) find a 'blocker'
1003
342k
    GR_AUDIT_TRAIL_ADD_OP(fAuditTrail, op.get(), proxy->uniqueID());
1004
342k
    GrOP_INFO("opsTask: %d Recording (%s, opID: %u)\n"
1005
342k
              "\tBounds [L: %.2f, T: %.2f R: %.2f B: %.2f]\n",
1006
342k
               this->uniqueID(),
1007
342k
               op->name(),
1008
342k
               op->uniqueID(),
1009
342k
               op->bounds().fLeft, op->bounds().fTop,
1010
342k
               op->bounds().fRight, op->bounds().fBottom);
1011
342k
    GrOP_INFO(SkTabString(op->dumpInfo(), 1).c_str());
1012
342k
    GrOP_INFO("\tOutcome:\n");
1013
342k
    int maxCandidates = std::min(kMaxOpChainDistance, fOpChains.size());
1014
342k
    if (maxCandidates) {
1015
179k
        int i = 0;
1016
191k
        while (true) {
1017
191k
            OpChain& candidate = fOpChains.fromBack(i);
1018
191k
            op = candidate.appendOp(std::move(op), processorAnalysis, dstProxyView, clip, caps,
1019
191k
                                    fArenas->arenaAlloc(), fAuditTrail);
1020
191k
            if (!op) {
1021
100k
                return;
1022
100k
            }
1023
            // Stop going backwards if we would cause a painter's order violation.
1024
90.5k
            if (!can_reorder(candidate.bounds(), op->bounds())) {
1025
76.8k
                GrOP_INFO("\t\tBackward: Intersects with chain (%s, head opID: %u)\n",
1026
76.8k
                          candidate.head()->name(), candidate.head()->uniqueID());
1027
76.8k
                break;
1028
76.8k
            }
1029
13.6k
            if (++i == maxCandidates) {
1030
2.25k
                GrOP_INFO("\t\tBackward: Reached max lookback or beginning of op array %d\n", i);
1031
2.25k
                break;
1032
2.25k
            }
1033
13.6k
        }
1034
179k
    } else {
1035
162k
        GrOP_INFO("\t\tBackward: FirstOp\n");
1036
162k
    }
1037
241k
    if (clip) {
1038
167k
        clip = fArenas->arenaAlloc()->make<GrAppliedClip>(std::move(*clip));
1039
167k
        SkDEBUGCODE(fNumClips++;)
1040
167k
    }
1041
241k
    fOpChains.emplace_back(std::move(op), processorAnalysis, clip, dstProxyView);
1042
241k
}
1043
1044
195k
void OpsTask::forwardCombine(const GrCaps& caps) {
1045
195k
    SkASSERT(!this->isClosed());
1046
195k
    GrOP_INFO("opsTask: %d ForwardCombine %d ops:\n", this->uniqueID(), fOpChains.size());
1047
1048
274k
    for (int i = 0; i < fOpChains.size() - 1; ++i) {
1049
78.9k
        OpChain& chain = fOpChains[i];
1050
78.9k
        int maxCandidateIdx = std::min(i + kMaxOpChainDistance, fOpChains.size() - 1);
1051
78.9k
        int j = i + 1;
1052
89.0k
        while (true) {
1053
89.0k
            OpChain& candidate = fOpChains[j];
1054
89.0k
            if (candidate.prependChain(&chain, caps, fArenas->arenaAlloc(), fAuditTrail)) {
1055
210
                break;
1056
210
            }
1057
            // Stop traversing if we would cause a painter's order violation.
1058
88.8k
            if (!can_reorder(chain.bounds(), candidate.bounds())) {
1059
77.5k
                GrOP_INFO(
1060
77.5k
                        "\t\t%d: chain (%s head opID: %u) -> "
1061
77.5k
                        "Intersects with chain (%s, head opID: %u)\n",
1062
77.5k
                        i, chain.head()->name(), chain.head()->uniqueID(), candidate.head()->name(),
1063
77.5k
                        candidate.head()->uniqueID());
1064
77.5k
                break;
1065
77.5k
            }
1066
11.3k
            if (++j > maxCandidateIdx) {
1067
1.18k
                GrOP_INFO("\t\t%d: chain (%s opID: %u) -> Reached max lookahead or end of array\n",
1068
1.18k
                          i, chain.head()->name(), chain.head()->uniqueID());
1069
1.18k
                break;
1070
1.18k
            }
1071
11.3k
        }
1072
78.9k
    }
1073
195k
}
1074
1075
GrRenderTask::ExpectedOutcome OpsTask::onMakeClosed(GrRecordingContext* rContext,
1076
195k
                                                    SkIRect* targetUpdateBounds) {
1077
195k
    this->forwardCombine(*rContext->priv().caps());
1078
195k
    if (!this->isColorNoOp()) {
1079
182k
        GrSurfaceProxy* proxy = this->target(0);
1080
        // Use the entire backing store bounds since the GPU doesn't clip automatically to the
1081
        // logical dimensions.
1082
182k
        SkRect clippedContentBounds = proxy->backingStoreBoundsRect();
1083
        // TODO: If we can fix up GLPrograms test to always intersect the target proxy bounds
1084
        // then we can simply assert here that the bounds intersect.
1085
182k
        if (clippedContentBounds.intersect(fTotalBounds)) {
1086
182k
            clippedContentBounds.roundOut(&fClippedContentBounds);
1087
182k
            *targetUpdateBounds = GrNativeRect::MakeIRectRelativeTo(
1088
182k
                    fTargetOrigin,
1089
182k
                    this->target(0)->backingStoreDimensions().height(),
1090
182k
                    fClippedContentBounds);
1091
182k
            return ExpectedOutcome::kTargetDirty;
1092
182k
        }
1093
182k
    }
1094
12.8k
    return ExpectedOutcome::kTargetUnchanged;
1095
195k
}
1096
1097
}  // namespace skgpu::ganesh