1/*
2 * Copyright 2015 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 "GrDrawingManager.h"
9
10#include "GrContext.h"
11#include "GrRenderTargetContext.h"
12#include "GrPathRenderingRenderTargetContext.h"
13#include "GrRenderTargetProxy.h"
14#include "GrResourceProvider.h"
15#include "GrSoftwarePathRenderer.h"
16#include "GrSurfacePriv.h"
17#include "GrTextureContext.h"
18#include "GrTextureOpList.h"
19#include "SkSurface_Gpu.h"
20#include "SkTTopoSort.h"
21
22#include "text/GrAtlasTextContext.h"
23#include "text/GrStencilAndCoverTextContext.h"
24
25void GrDrawingManager::cleanup() {
26    for (int i = 0; i < fOpLists.count(); ++i) {
27        fOpLists[i]->makeClosed();  // no opList should receive a new command after this
28        fOpLists[i]->clearTarget();
29
30        // We shouldn't need to do this, but it turns out some clients still hold onto opLists
31        // after a cleanup
32        fOpLists[i]->reset();
33        fOpLists[i]->unref();
34    }
35
36    fOpLists.reset();
37
38    delete fPathRendererChain;
39    fPathRendererChain = nullptr;
40    SkSafeSetNull(fSoftwarePathRenderer);
41}
42
43GrDrawingManager::~GrDrawingManager() {
44    this->cleanup();
45}
46
47void GrDrawingManager::abandon() {
48    fAbandoned = true;
49    for (int i = 0; i < fOpLists.count(); ++i) {
50        fOpLists[i]->abandonGpuResources();
51    }
52    this->cleanup();
53}
54
55void GrDrawingManager::freeGpuResources() {
56    // a path renderer may be holding onto resources
57    delete fPathRendererChain;
58    fPathRendererChain = nullptr;
59    SkSafeSetNull(fSoftwarePathRenderer);
60    for (int i = 0; i < fOpLists.count(); ++i) {
61        fOpLists[i]->freeGpuResources();
62    }
63}
64
65void GrDrawingManager::reset() {
66    for (int i = 0; i < fOpLists.count(); ++i) {
67        fOpLists[i]->reset();
68    }
69    fFlushState.reset();
70}
71
72void GrDrawingManager::internalFlush(GrResourceCache::FlushType type) {
73    if (fFlushing || this->wasAbandoned()) {
74        return;
75    }
76    fFlushing = true;
77    bool flushed = false;
78
79    for (int i = 0; i < fOpLists.count(); ++i) {
80        // Semi-usually the GrOpLists are already closed at this point, but sometimes Ganesh
81        // needs to flush mid-draw. In that case, the SkGpuDevice's GrOpLists won't be closed
82        // but need to be flushed anyway. Closing such GrOpLists here will mean new
83        // GrOpLists will be created to replace them if the SkGpuDevice(s) write to them again.
84        fOpLists[i]->makeClosed();
85    }
86
87    SkDEBUGCODE(bool result =)
88                        SkTTopoSort<GrOpList, GrOpList::TopoSortTraits>(&fOpLists);
89    SkASSERT(result);
90
91    GrPreFlushResourceProvider preFlushProvider(this);
92
93    if (fPreFlushCBObjects.count()) {
94        // MDB TODO: pre-MDB '1' is the correct pre-allocated size. Post-MDB it will need
95        // to be larger.
96        SkAutoSTArray<1, uint32_t> opListIds(fOpLists.count());
97        for (int i = 0; i < fOpLists.count(); ++i) {
98            opListIds[i] = fOpLists[i]->uniqueID();
99        }
100
101        SkSTArray<1, sk_sp<GrRenderTargetContext>> renderTargetContexts;
102        for (int i = 0; i < fPreFlushCBObjects.count(); ++i) {
103            fPreFlushCBObjects[i]->preFlush(&preFlushProvider,
104                                            opListIds.get(), opListIds.count(),
105                                            &renderTargetContexts);
106            if (!renderTargetContexts.count()) {
107                continue;       // This is fine. No atlases of this type are required for this flush
108            }
109
110            for (int j = 0; j < renderTargetContexts.count(); ++j) {
111                GrRenderTargetOpList* opList = renderTargetContexts[j]->getOpList();
112                if (!opList) {
113                    continue;   // Odd - but not a big deal
114                }
115                SkDEBUGCODE(opList->validateTargetsSingleRenderTarget());
116                opList->prepareOps(&fFlushState);
117                if (!opList->executeOps(&fFlushState)) {
118                    continue;         // This is bad
119                }
120            }
121            renderTargetContexts.reset();
122        }
123    }
124
125    for (int i = 0; i < fOpLists.count(); ++i) {
126        fOpLists[i]->prepareOps(&fFlushState);
127    }
128
129#if 0
130    // Enable this to print out verbose GrOp information
131    for (int i = 0; i < fOpLists.count(); ++i) {
132        SkDEBUGCODE(fOpLists[i]->dump();)
133    }
134#endif
135
136    // Upload all data to the GPU
137    fFlushState.preIssueDraws();
138
139    for (int i = 0; i < fOpLists.count(); ++i) {
140        if (fOpLists[i]->executeOps(&fFlushState)) {
141            flushed = true;
142        }
143    }
144
145    SkASSERT(fFlushState.nextDrawToken() == fFlushState.nextTokenToFlush());
146
147    for (int i = 0; i < fOpLists.count(); ++i) {
148        fOpLists[i]->reset();
149#ifdef ENABLE_MDB
150        fOpLists[i]->unref();
151#endif
152    }
153
154#ifndef ENABLE_MDB
155    // When MDB is disabled we keep reusing the same GrOpList
156    if (fOpLists.count()) {
157        SkASSERT(fOpLists.count() == 1);
158        // Clear out this flag so the topological sort's SkTTopoSort_CheckAllUnmarked check
159        // won't bark
160        fOpLists[0]->resetFlag(GrOpList::kWasOutput_Flag);
161    }
162#else
163    fOpLists.reset();
164#endif
165
166    fFlushState.reset();
167    // We always have to notify the cache when it requested a flush so it can reset its state.
168    if (flushed || type == GrResourceCache::FlushType::kCacheRequested) {
169        fContext->getResourceCache()->notifyFlushOccurred(type);
170    }
171    fFlushing = false;
172}
173
174void GrDrawingManager::prepareSurfaceForExternalIO(GrSurface* surface) {
175    if (this->wasAbandoned()) {
176        return;
177    }
178    SkASSERT(surface);
179    SkASSERT(surface->getContext() == fContext);
180
181    if (surface->surfacePriv().hasPendingIO()) {
182        this->flush();
183    }
184
185    GrRenderTarget* rt = surface->asRenderTarget();
186    if (fContext->getGpu() && rt) {
187        fContext->getGpu()->resolveRenderTarget(rt);
188    }
189}
190
191void GrDrawingManager::addPreFlushCallbackObject(sk_sp<GrPreFlushCallbackObject> preFlushCBObject) {
192    fPreFlushCBObjects.push_back(preFlushCBObject);
193}
194
195GrRenderTargetOpList* GrDrawingManager::newOpList(GrRenderTargetProxy* rtp) {
196    SkASSERT(fContext);
197
198#ifndef ENABLE_MDB
199    // When MDB is disabled we always just return the single GrOpList
200    if (fOpLists.count()) {
201        SkASSERT(fOpLists.count() == 1);
202        // In the non-MDB-world the same GrOpList gets reused for multiple render targets.
203        // Update this pointer so all the asserts are happy
204        rtp->setLastOpList(fOpLists[0]);
205        // DrawingManager gets the creation ref - this ref is for the caller
206
207        // TODO: although this is true right now it isn't cool
208        return SkRef((GrRenderTargetOpList*) fOpLists[0]);
209    }
210#endif
211
212    GrRenderTargetOpList* opList = new GrRenderTargetOpList(rtp,
213                                                            fContext->getGpu(),
214                                                            fContext->resourceProvider(),
215                                                            fContext->getAuditTrail(),
216                                                            fOptionsForOpLists);
217
218    *fOpLists.append() = opList;
219
220    // DrawingManager gets the creation ref - this ref is for the caller
221    return SkRef(opList);
222}
223
224GrTextureOpList* GrDrawingManager::newOpList(GrTextureProxy* textureProxy) {
225    SkASSERT(fContext);
226
227    GrTextureOpList* opList = new GrTextureOpList(textureProxy, fContext->getGpu(),
228                                                  fContext->getAuditTrail());
229
230#ifndef ENABLE_MDB
231    // When MDB is disabled we still create a new GrOpList, but don't store or ref it - we rely
232    // on the caller to immediately execute and free it.
233    return opList;
234#else
235    *fOpLists.append() = opList;
236
237    // Drawing manager gets the creation ref - this ref is for the caller
238    return SkRef(opList);
239#endif
240}
241
242GrAtlasTextContext* GrDrawingManager::getAtlasTextContext() {
243    if (!fAtlasTextContext) {
244        fAtlasTextContext.reset(GrAtlasTextContext::Create());
245    }
246
247    return fAtlasTextContext.get();
248}
249
250/*
251 * This method finds a path renderer that can draw the specified path on
252 * the provided target.
253 * Due to its expense, the software path renderer has split out so it can
254 * can be individually allowed/disallowed via the "allowSW" boolean.
255 */
256GrPathRenderer* GrDrawingManager::getPathRenderer(const GrPathRenderer::CanDrawPathArgs& args,
257                                                  bool allowSW,
258                                                  GrPathRendererChain::DrawType drawType,
259                                                  GrPathRenderer::StencilSupport* stencilSupport) {
260
261    if (!fPathRendererChain) {
262        fPathRendererChain = new GrPathRendererChain(fContext, fOptionsForPathRendererChain);
263    }
264
265    GrPathRenderer* pr = fPathRendererChain->getPathRenderer(args, drawType, stencilSupport);
266    if (!pr && allowSW) {
267        if (!fSoftwarePathRenderer) {
268            fSoftwarePathRenderer =
269                    new GrSoftwarePathRenderer(fContext->resourceProvider(),
270                                               fOptionsForPathRendererChain.fAllowPathMaskCaching);
271        }
272        if (fSoftwarePathRenderer->canDrawPath(args)) {
273            pr = fSoftwarePathRenderer;
274        }
275    }
276
277    return pr;
278}
279
280sk_sp<GrRenderTargetContext> GrDrawingManager::makeRenderTargetContext(
281                                                            sk_sp<GrSurfaceProxy> sProxy,
282                                                            sk_sp<SkColorSpace> colorSpace,
283                                                            const SkSurfaceProps* surfaceProps) {
284    if (this->wasAbandoned() || !sProxy->asRenderTargetProxy()) {
285        return nullptr;
286    }
287
288    // SkSurface catches bad color space usage at creation. This check handles anything that slips
289    // by, including internal usage. We allow a null color space here, for read/write pixels and
290    // other special code paths. If a color space is provided, though, enforce all other rules.
291    if (colorSpace && !SkSurface_Gpu::Valid(fContext, sProxy->config(), colorSpace.get())) {
292        SkDEBUGFAIL("Invalid config and colorspace combination");
293        return nullptr;
294    }
295
296    sk_sp<GrRenderTargetProxy> rtp(sk_ref_sp(sProxy->asRenderTargetProxy()));
297
298    bool useDIF = false;
299    if (surfaceProps) {
300        useDIF = surfaceProps->isUseDeviceIndependentFonts();
301    }
302
303    if (useDIF && fContext->caps()->shaderCaps()->pathRenderingSupport() &&
304        rtp->isStencilBufferMultisampled()) {
305        // TODO: defer stencil buffer attachment for PathRenderingDrawContext
306        sk_sp<GrRenderTarget> rt(sk_ref_sp(rtp->instantiate(fContext->resourceProvider())));
307        if (!rt) {
308            return nullptr;
309        }
310        GrStencilAttachment* sb = fContext->resourceProvider()->attachStencilAttachment(rt.get());
311        if (sb) {
312            return sk_sp<GrRenderTargetContext>(new GrPathRenderingRenderTargetContext(
313                                                        fContext, this, std::move(rtp),
314                                                        std::move(colorSpace), surfaceProps,
315                                                        fContext->getAuditTrail(), fSingleOwner));
316        }
317    }
318
319    return sk_sp<GrRenderTargetContext>(new GrRenderTargetContext(fContext, this, std::move(rtp),
320                                                                  std::move(colorSpace),
321                                                                  surfaceProps,
322                                                                  fContext->getAuditTrail(),
323                                                                  fSingleOwner));
324}
325
326sk_sp<GrTextureContext> GrDrawingManager::makeTextureContext(sk_sp<GrSurfaceProxy> sProxy,
327                                                             sk_sp<SkColorSpace> colorSpace) {
328    if (this->wasAbandoned() || !sProxy->asTextureProxy()) {
329        return nullptr;
330    }
331
332    // SkSurface catches bad color space usage at creation. This check handles anything that slips
333    // by, including internal usage. We allow a null color space here, for read/write pixels and
334    // other special code paths. If a color space is provided, though, enforce all other rules.
335    if (colorSpace && !SkSurface_Gpu::Valid(fContext, sProxy->config(), colorSpace.get())) {
336        SkDEBUGFAIL("Invalid config and colorspace combination");
337        return nullptr;
338    }
339
340    // GrTextureRenderTargets should always be using GrRenderTargetContext
341    SkASSERT(!sProxy->asRenderTargetProxy());
342
343    sk_sp<GrTextureProxy> textureProxy(sk_ref_sp(sProxy->asTextureProxy()));
344
345    return sk_sp<GrTextureContext>(new GrTextureContext(fContext, this, std::move(textureProxy),
346                                                        std::move(colorSpace),
347                                                        fContext->getAuditTrail(),
348                                                        fSingleOwner));
349}
350