1 /*
2  * Copyright 2016 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "GrTextureOpList.h"
9 
10 #include "GrAuditTrail.h"
11 #include "GrContext.h"
12 #include "GrContextPriv.h"
13 #include "GrGpu.h"
14 #include "GrMemoryPool.h"
15 #include "GrResourceAllocator.h"
16 #include "GrTextureProxy.h"
17 #include "SkStringUtils.h"
18 #include "ops/GrCopySurfaceOp.h"
19 
20 ////////////////////////////////////////////////////////////////////////////////
21 
22 GrTextureOpList::GrTextureOpList(GrResourceProvider* resourceProvider,
23                                  sk_sp<GrOpMemoryPool> opMemoryPool,
24                                  GrTextureProxy* proxy,
25                                  GrAuditTrail* auditTrail)
26         : INHERITED(resourceProvider, std::move(opMemoryPool), proxy, auditTrail) {
27     SkASSERT(fOpMemoryPool);
28     SkASSERT(!proxy->readOnly());
29 }
30 
31 void GrTextureOpList::deleteOp(int index) {
32     SkASSERT(index >= 0 && index < fRecordedOps.count());
33     fOpMemoryPool->release(std::move(fRecordedOps[index]));
34 }
35 
36 void GrTextureOpList::deleteOps() {
37     for (int i = 0; i < fRecordedOps.count(); ++i) {
38         if (fRecordedOps[i]) {
39             fOpMemoryPool->release(std::move(fRecordedOps[i]));
40         }
41     }
42     fRecordedOps.reset();
43     fOpMemoryPool = nullptr;
44 }
45 
46 GrTextureOpList::~GrTextureOpList() {
47     this->deleteOps();
48 }
49 
50 ////////////////////////////////////////////////////////////////////////////////
51 
52 #ifdef SK_DEBUG
53 void GrTextureOpList::dump(bool printDependencies) const {
54     INHERITED::dump(printDependencies);
55 
56     SkDebugf("ops (%d):\n", fRecordedOps.count());
57     for (int i = 0; i < fRecordedOps.count(); ++i) {
58         if (!fRecordedOps[i]) {
59             SkDebugf("%d: <failed instantiation>\n", i);
60         } else {
61             SkDebugf("*******************************\n");
62             SkDebugf("%d: %s\n", i, fRecordedOps[i]->name());
63             SkString str = fRecordedOps[i]->dumpInfo();
64             SkDebugf("%s\n", str.c_str());
65             const SkRect& clippedBounds = fRecordedOps[i]->bounds();
66             SkDebugf("ClippedBounds: [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n",
67                      clippedBounds.fLeft, clippedBounds.fTop, clippedBounds.fRight,
68                      clippedBounds.fBottom);
69         }
70     }
71 }
72 
73 #endif
74 
75 void GrTextureOpList::onPrepare(GrOpFlushState* flushState) {
76     SkASSERT(fTarget.get()->peekTexture());
77     SkASSERT(this->isClosed());
78 
79     // Loop over the ops that haven't yet generated their geometry
80     for (int i = 0; i < fRecordedOps.count(); ++i) {
81         if (fRecordedOps[i]) {
82             SkASSERT(fRecordedOps[i]->isChainHead());
83             GrOpFlushState::OpArgs opArgs = {
84                 fRecordedOps[i].get(),
85                 nullptr,
86                 nullptr,
87                 GrXferProcessor::DstProxy()
88             };
89             flushState->setOpArgs(&opArgs);
90             fRecordedOps[i]->prepare(flushState);
91             flushState->setOpArgs(nullptr);
92         }
93     }
94 }
95 
96 bool GrTextureOpList::onExecute(GrOpFlushState* flushState) {
97     if (0 == fRecordedOps.count()) {
98         return false;
99     }
100 
101     SkASSERT(fTarget.get()->peekTexture());
102 
103     GrGpuTextureCommandBuffer* commandBuffer(
104                          flushState->gpu()->getCommandBuffer(fTarget.get()->peekTexture(),
105                                                              fTarget.get()->origin()));
106     flushState->setCommandBuffer(commandBuffer);
107 
108     for (int i = 0; i < fRecordedOps.count(); ++i) {
109         if (!fRecordedOps[i]) {
110             continue;
111         }
112         SkASSERT(fRecordedOps[i]->isChainHead());
113         GrOpFlushState::OpArgs opArgs = {
114             fRecordedOps[i].get(),
115             nullptr,
116             nullptr,
117             GrXferProcessor::DstProxy()
118         };
119         flushState->setOpArgs(&opArgs);
120         fRecordedOps[i]->execute(flushState, fRecordedOps[i].get()->bounds());
121         flushState->setOpArgs(nullptr);
122     }
123 
124     flushState->gpu()->submit(commandBuffer);
125     flushState->setCommandBuffer(nullptr);
126 
127     return true;
128 }
129 
130 void GrTextureOpList::endFlush() {
131     this->deleteOps();
132     INHERITED::endFlush();
133 }
134 
135 ////////////////////////////////////////////////////////////////////////////////
136 
137 // This closely parallels GrRenderTargetOpList::copySurface but renderTargetOpList
138 // stores extra data with the op
139 bool GrTextureOpList::copySurface(GrContext* context,
140                                   GrSurfaceProxy* dst,
141                                   GrSurfaceProxy* src,
142                                   const SkIRect& srcRect,
143                                   const SkIPoint& dstPoint) {
144     SkASSERT(dst == fTarget.get());
145 
146     std::unique_ptr<GrOp> op = GrCopySurfaceOp::Make(context, dst, src, srcRect, dstPoint);
147     if (!op) {
148         return false;
149     }
150 
151     const GrCaps* caps = context->contextPriv().caps();
152     auto addDependency = [ caps, this ] (GrSurfaceProxy* p) {
153         this->addDependency(p, *caps);
154     };
155     op->visitProxies(addDependency);
156 
157     this->recordOp(std::move(op));
158     return true;
159 }
160 
161 void GrTextureOpList::purgeOpsWithUninstantiatedProxies() {
162     bool hasUninstantiatedProxy = false;
163     auto checkInstantiation = [&hasUninstantiatedProxy](GrSurfaceProxy* p) {
164         if (!p->isInstantiated()) {
165             hasUninstantiatedProxy = true;
166         }
167     };
168     for (int i = 0; i < fRecordedOps.count(); ++i) {
169         const GrOp* op = fRecordedOps[i].get(); // only diff from the GrRenderTargetOpList version
170         hasUninstantiatedProxy = false;
171         if (op) {
172             op->visitProxies(checkInstantiation);
173         }
174         if (hasUninstantiatedProxy) {
175             // When instantiation of the proxy fails we drop the Op
176             this->deleteOp(i);
177         }
178     }
179 }
180 
181 void GrTextureOpList::gatherProxyIntervals(GrResourceAllocator* alloc) const {
182     unsigned int cur = alloc->numOps();
183 
184     // Add the interval for all the writes to this opList's target
185     if (fRecordedOps.count()) {
186         alloc->addInterval(fTarget.get(), cur, cur+fRecordedOps.count()-1);
187     } else {
188         // This can happen if there is a loadOp (e.g., a clear) but no other draws. In this case we
189         // still need to add an interval for the destination so we create a fake op# for
190         // the missing clear op.
191         alloc->addInterval(fTarget.get());
192         alloc->incOps();
193     }
194 
195     auto gather = [ alloc SkDEBUGCODE(, this) ] (GrSurfaceProxy* p) {
196         alloc->addInterval(p SkDEBUGCODE(, p == fTarget.get()));
197     };
198     for (int i = 0; i < fRecordedOps.count(); ++i) {
199         const GrOp* op = fRecordedOps[i].get(); // only diff from the GrRenderTargetOpList version
200         if (op) {
201             op->visitProxies(gather, GrOp::VisitorType::kAllocatorGather);
202         }
203 
204         // Even though the op may have been moved we still need to increment the op count to
205         // keep all the math consistent.
206         alloc->incOps();
207     }
208 }
209 
210 void GrTextureOpList::recordOp(std::unique_ptr<GrOp> op) {
211     SkASSERT(fTarget.get());
212     // A closed GrOpList should never receive new/more ops
213     SkASSERT(!this->isClosed());
214 
215     GR_AUDIT_TRAIL_ADD_OP(fAuditTrail, op.get(), fTarget.get()->uniqueID());
216     GrOP_INFO("Re-Recording (%s, opID: %u)\n"
217         "\tBounds LRTB (%f, %f, %f, %f)\n",
218         op->name(),
219         op->uniqueID(),
220         op->bounds().fLeft, op->bounds().fRight,
221         op->bounds().fTop, op->bounds().fBottom);
222     GrOP_INFO(SkTabString(op->dumpInfo(), 1).c_str());
223 
224     fRecordedOps.emplace_back(std::move(op));
225 }
226