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 "GrGpu.h"
12 #include "GrOnFlushResourceProvider.h"
13 #include "GrRenderTargetContext.h"
14 #include "GrPathRenderingRenderTargetContext.h"
15 #include "GrRenderTargetProxy.h"
16 #include "GrResourceProvider.h"
17 #include "GrSoftwarePathRenderer.h"
18 #include "GrSurfacePriv.h"
19 #include "GrSurfaceProxyPriv.h"
20 #include "GrTextureContext.h"
21 #include "GrTextureOpList.h"
22 #include "SkSurface_Gpu.h"
23 #include "SkTTopoSort.h"
24
25 #include "GrTracing.h"
26 #include "text/GrAtlasTextContext.h"
27 #include "text/GrStencilAndCoverTextContext.h"
28
cleanup()29 void GrDrawingManager::cleanup() {
30 for (int i = 0; i < fOpLists.count(); ++i) {
31 // no opList should receive a new command after this
32 fOpLists[i]->makeClosed(*fContext->caps());
33
34 // We shouldn't need to do this, but it turns out some clients still hold onto opLists
35 // after a cleanup.
36 // MDB TODO: is this still true?
37 fOpLists[i]->reset();
38 }
39
40 fOpLists.reset();
41
42 delete fPathRendererChain;
43 fPathRendererChain = nullptr;
44 SkSafeSetNull(fSoftwarePathRenderer);
45 }
46
~GrDrawingManager()47 GrDrawingManager::~GrDrawingManager() {
48 this->cleanup();
49 }
50
abandon()51 void GrDrawingManager::abandon() {
52 fAbandoned = true;
53 for (int i = 0; i < fOpLists.count(); ++i) {
54 fOpLists[i]->abandonGpuResources();
55 }
56 this->cleanup();
57 }
58
freeGpuResources()59 void GrDrawingManager::freeGpuResources() {
60 // a path renderer may be holding onto resources
61 delete fPathRendererChain;
62 fPathRendererChain = nullptr;
63 SkSafeSetNull(fSoftwarePathRenderer);
64 for (int i = 0; i < fOpLists.count(); ++i) {
65 fOpLists[i]->freeGpuResources();
66 }
67 }
68
reset()69 void GrDrawingManager::reset() {
70 for (int i = 0; i < fOpLists.count(); ++i) {
71 fOpLists[i]->reset();
72 }
73 fFlushState.reset();
74 }
75
instancingAllocator()76 gr_instanced::OpAllocator* GrDrawingManager::instancingAllocator() {
77 if (fInstancingAllocator) {
78 return fInstancingAllocator.get();
79 }
80
81 fInstancingAllocator = fContext->getGpu()->createInstancedRenderingAllocator();
82 return fInstancingAllocator.get();
83 }
84
85 // MDB TODO: make use of the 'proxy' parameter.
internalFlush(GrSurfaceProxy *,GrResourceCache::FlushType type)86 void GrDrawingManager::internalFlush(GrSurfaceProxy*, GrResourceCache::FlushType type) {
87 GR_CREATE_TRACE_MARKER_CONTEXT("GrDrawingManager", "internalFlush", fContext);
88
89 if (fFlushing || this->wasAbandoned()) {
90 return;
91 }
92 fFlushing = true;
93 bool flushed = false;
94
95 for (int i = 0; i < fOpLists.count(); ++i) {
96 // Semi-usually the GrOpLists are already closed at this point, but sometimes Ganesh
97 // needs to flush mid-draw. In that case, the SkGpuDevice's GrOpLists won't be closed
98 // but need to be flushed anyway. Closing such GrOpLists here will mean new
99 // GrOpLists will be created to replace them if the SkGpuDevice(s) write to them again.
100 fOpLists[i]->makeClosed(*fContext->caps());
101 }
102
103 #ifdef SK_DEBUG
104 // This block checks for any unnecessary splits in the opLists. If two sequential opLists
105 // share the same backing GrSurfaceProxy it means the opList was artificially split.
106 if (fOpLists.count()) {
107 GrRenderTargetOpList* prevOpList = fOpLists[0]->asRenderTargetOpList();
108 for (int i = 1; i < fOpLists.count(); ++i) {
109 GrRenderTargetOpList* curOpList = fOpLists[i]->asRenderTargetOpList();
110
111 if (prevOpList && curOpList) {
112 SkASSERT(prevOpList->fTarget.get() != curOpList->fTarget.get());
113 }
114
115 prevOpList = curOpList;
116 }
117 }
118 #endif
119
120 #ifdef ENABLE_MDB
121 SkDEBUGCODE(bool result =)
122 SkTTopoSort<GrOpList, GrOpList::TopoSortTraits>(&fOpLists);
123 SkASSERT(result);
124 #endif
125
126 GrOnFlushResourceProvider onFlushProvider(this);
127
128 if (!fOnFlushCBObjects.empty()) {
129 // MDB TODO: pre-MDB '1' is the correct pre-allocated size. Post-MDB it will need
130 // to be larger.
131 SkAutoSTArray<1, uint32_t> opListIds(fOpLists.count());
132 for (int i = 0; i < fOpLists.count(); ++i) {
133 opListIds[i] = fOpLists[i]->uniqueID();
134 }
135
136 SkSTArray<1, sk_sp<GrRenderTargetContext>> renderTargetContexts;
137 for (GrOnFlushCallbackObject* onFlushCBObject : fOnFlushCBObjects) {
138 onFlushCBObject->preFlush(&onFlushProvider,
139 opListIds.get(), opListIds.count(),
140 &renderTargetContexts);
141 if (!renderTargetContexts.count()) {
142 continue; // This is fine. No atlases of this type are required for this flush
143 }
144
145 for (int j = 0; j < renderTargetContexts.count(); ++j) {
146 GrOpList* opList = renderTargetContexts[j]->getOpList();
147 if (!opList) {
148 continue; // Odd - but not a big deal
149 }
150 opList->makeClosed(*fContext->caps());
151 opList->prepareOps(&fFlushState);
152 if (!opList->executeOps(&fFlushState)) {
153 continue; // This is bad
154 }
155 }
156 renderTargetContexts.reset();
157 }
158 }
159
160 #if 0
161 // Enable this to print out verbose GrOp information
162 for (int i = 0; i < fOpLists.count(); ++i) {
163 SkDEBUGCODE(fOpLists[i]->dump();)
164 }
165 #endif
166
167 for (int i = 0; i < fOpLists.count(); ++i) {
168 if (!fOpLists[i]->instantiate(fContext->resourceProvider())) {
169 fOpLists[i] = nullptr;
170 continue;
171 }
172
173 fOpLists[i]->prepareOps(&fFlushState);
174 }
175
176 // Upload all data to the GPU
177 fFlushState.preIssueDraws();
178
179 for (int i = 0; i < fOpLists.count(); ++i) {
180 if (!fOpLists[i]) {
181 continue;
182 }
183
184 if (fOpLists[i]->executeOps(&fFlushState)) {
185 flushed = true;
186 }
187 fOpLists[i]->reset();
188 }
189 fOpLists.reset();
190
191 SkASSERT(fFlushState.nextDrawToken() == fFlushState.nextTokenToFlush());
192
193 fContext->getGpu()->finishFlush();
194
195 fFlushState.reset();
196 // We always have to notify the cache when it requested a flush so it can reset its state.
197 if (flushed || type == GrResourceCache::FlushType::kCacheRequested) {
198 fContext->getResourceCache()->notifyFlushOccurred(type);
199 }
200 for (GrOnFlushCallbackObject* onFlushCBObject : fOnFlushCBObjects) {
201 onFlushCBObject->postFlush();
202 }
203 fFlushing = false;
204 }
205
prepareSurfaceForExternalIO(GrSurfaceProxy * proxy)206 void GrDrawingManager::prepareSurfaceForExternalIO(GrSurfaceProxy* proxy) {
207 if (this->wasAbandoned()) {
208 return;
209 }
210 SkASSERT(proxy);
211
212 if (proxy->priv().hasPendingIO()) {
213 this->flush(proxy);
214 }
215
216 if (!proxy->instantiate(fContext->resourceProvider())) {
217 return;
218 }
219
220 GrSurface* surface = proxy->priv().peekSurface();
221
222 if (fContext->getGpu() && surface->asRenderTarget()) {
223 fContext->getGpu()->resolveRenderTarget(surface->asRenderTarget());
224 }
225 }
226
addOnFlushCallbackObject(GrOnFlushCallbackObject * onFlushCBObject)227 void GrDrawingManager::addOnFlushCallbackObject(GrOnFlushCallbackObject* onFlushCBObject) {
228 fOnFlushCBObjects.push_back(onFlushCBObject);
229 }
230
newRTOpList(GrRenderTargetProxy * rtp,bool managedOpList)231 sk_sp<GrRenderTargetOpList> GrDrawingManager::newRTOpList(GrRenderTargetProxy* rtp,
232 bool managedOpList) {
233 SkASSERT(fContext);
234
235 // This is a temporary fix for the partial-MDB world. In that world we're not reordering
236 // so ops that (in the single opList world) would've just glommed onto the end of the single
237 // opList but referred to a far earlier RT need to appear in their own opList.
238 if (!fOpLists.empty()) {
239 fOpLists.back()->makeClosed(*fContext->caps());
240 }
241
242 sk_sp<GrRenderTargetOpList> opList(new GrRenderTargetOpList(rtp,
243 fContext->getGpu(),
244 fContext->getAuditTrail()));
245 SkASSERT(rtp->getLastOpList() == opList.get());
246
247 if (managedOpList) {
248 fOpLists.push_back() = opList;
249 }
250
251 return opList;
252 }
253
newTextureOpList(GrTextureProxy * textureProxy)254 sk_sp<GrTextureOpList> GrDrawingManager::newTextureOpList(GrTextureProxy* textureProxy) {
255 SkASSERT(fContext);
256
257 // This is a temporary fix for the partial-MDB world. In that world we're not reordering
258 // so ops that (in the single opList world) would've just glommed onto the end of the single
259 // opList but referred to a far earlier RT need to appear in their own opList.
260 if (!fOpLists.empty()) {
261 fOpLists.back()->makeClosed(*fContext->caps());
262 }
263
264 sk_sp<GrTextureOpList> opList(new GrTextureOpList(fContext->resourceProvider(),
265 textureProxy,
266 fContext->getAuditTrail()));
267
268 SkASSERT(textureProxy->getLastOpList() == opList.get());
269
270 fOpLists.push_back() = opList;
271
272 return opList;
273 }
274
getAtlasTextContext()275 GrAtlasTextContext* GrDrawingManager::getAtlasTextContext() {
276 if (!fAtlasTextContext) {
277 fAtlasTextContext.reset(GrAtlasTextContext::Create());
278 }
279
280 return fAtlasTextContext.get();
281 }
282
283 /*
284 * This method finds a path renderer that can draw the specified path on
285 * the provided target.
286 * Due to its expense, the software path renderer has split out so it can
287 * can be individually allowed/disallowed via the "allowSW" boolean.
288 */
getPathRenderer(const GrPathRenderer::CanDrawPathArgs & args,bool allowSW,GrPathRendererChain::DrawType drawType,GrPathRenderer::StencilSupport * stencilSupport)289 GrPathRenderer* GrDrawingManager::getPathRenderer(const GrPathRenderer::CanDrawPathArgs& args,
290 bool allowSW,
291 GrPathRendererChain::DrawType drawType,
292 GrPathRenderer::StencilSupport* stencilSupport) {
293
294 if (!fPathRendererChain) {
295 fPathRendererChain = new GrPathRendererChain(fContext, fOptionsForPathRendererChain);
296 }
297
298 GrPathRenderer* pr = fPathRendererChain->getPathRenderer(args, drawType, stencilSupport);
299 if (!pr && allowSW) {
300 if (!fSoftwarePathRenderer) {
301 fSoftwarePathRenderer =
302 new GrSoftwarePathRenderer(fContext->resourceProvider(),
303 fOptionsForPathRendererChain.fAllowPathMaskCaching);
304 }
305 if (fSoftwarePathRenderer->canDrawPath(args)) {
306 pr = fSoftwarePathRenderer;
307 }
308 }
309
310 return pr;
311 }
312
makeRenderTargetContext(sk_sp<GrSurfaceProxy> sProxy,sk_sp<SkColorSpace> colorSpace,const SkSurfaceProps * surfaceProps,bool managedOpList)313 sk_sp<GrRenderTargetContext> GrDrawingManager::makeRenderTargetContext(
314 sk_sp<GrSurfaceProxy> sProxy,
315 sk_sp<SkColorSpace> colorSpace,
316 const SkSurfaceProps* surfaceProps,
317 bool managedOpList) {
318 if (this->wasAbandoned() || !sProxy->asRenderTargetProxy()) {
319 return nullptr;
320 }
321
322 // SkSurface catches bad color space usage at creation. This check handles anything that slips
323 // by, including internal usage. We allow a null color space here, for read/write pixels and
324 // other special code paths. If a color space is provided, though, enforce all other rules.
325 if (colorSpace && !SkSurface_Gpu::Valid(fContext, sProxy->config(), colorSpace.get())) {
326 SkDEBUGFAIL("Invalid config and colorspace combination");
327 return nullptr;
328 }
329
330 sk_sp<GrRenderTargetProxy> rtp(sk_ref_sp(sProxy->asRenderTargetProxy()));
331
332 bool useDIF = false;
333 if (surfaceProps) {
334 useDIF = surfaceProps->isUseDeviceIndependentFonts();
335 }
336
337 if (useDIF && fContext->caps()->shaderCaps()->pathRenderingSupport() &&
338 GrFSAAType::kNone != rtp->fsaaType()) {
339 // TODO: defer stencil buffer attachment for PathRenderingDrawContext
340 if (!rtp->instantiate(fContext->resourceProvider())) {
341 return nullptr;
342 }
343 GrRenderTarget* rt = rtp->priv().peekRenderTarget();
344
345 GrStencilAttachment* sb = fContext->resourceProvider()->attachStencilAttachment(rt);
346 if (sb) {
347 return sk_sp<GrRenderTargetContext>(new GrPathRenderingRenderTargetContext(
348 fContext, this, std::move(rtp),
349 std::move(colorSpace), surfaceProps,
350 fContext->getAuditTrail(), fSingleOwner));
351 }
352 }
353
354 return sk_sp<GrRenderTargetContext>(new GrRenderTargetContext(fContext, this, std::move(rtp),
355 std::move(colorSpace),
356 surfaceProps,
357 fContext->getAuditTrail(),
358 fSingleOwner, managedOpList));
359 }
360
makeTextureContext(sk_sp<GrSurfaceProxy> sProxy,sk_sp<SkColorSpace> colorSpace)361 sk_sp<GrTextureContext> GrDrawingManager::makeTextureContext(sk_sp<GrSurfaceProxy> sProxy,
362 sk_sp<SkColorSpace> colorSpace) {
363 if (this->wasAbandoned() || !sProxy->asTextureProxy()) {
364 return nullptr;
365 }
366
367 // SkSurface catches bad color space usage at creation. This check handles anything that slips
368 // by, including internal usage. We allow a null color space here, for read/write pixels and
369 // other special code paths. If a color space is provided, though, enforce all other rules.
370 if (colorSpace && !SkSurface_Gpu::Valid(fContext, sProxy->config(), colorSpace.get())) {
371 SkDEBUGFAIL("Invalid config and colorspace combination");
372 return nullptr;
373 }
374
375 // GrTextureRenderTargets should always be using GrRenderTargetContext
376 SkASSERT(!sProxy->asRenderTargetProxy());
377
378 sk_sp<GrTextureProxy> textureProxy(sk_ref_sp(sProxy->asTextureProxy()));
379
380 return sk_sp<GrTextureContext>(new GrTextureContext(fContext, this, std::move(textureProxy),
381 std::move(colorSpace),
382 fContext->getAuditTrail(),
383 fSingleOwner));
384 }
385