GrContext.cpp revision aecc018f86d911198b7c7775cee04f61bd10b430
1/*
2 * Copyright 2011 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 "GrContext.h"
9#include "GrContextOptions.h"
10#include "GrDrawingManager.h"
11#include "GrDrawContext.h"
12#include "GrLayerCache.h"
13#include "GrResourceCache.h"
14#include "GrResourceProvider.h"
15#include "GrSoftwarePathRenderer.h"
16#include "GrSurfacePriv.h"
17
18#include "SkConfig8888.h"
19#include "SkGrPriv.h"
20
21#include "batches/GrCopySurfaceBatch.h"
22#include "effects/GrConfigConversionEffect.h"
23#include "text/GrTextBlobCache.h"
24
25#define ASSERT_OWNED_RESOURCE(R) SkASSERT(!(R) || (R)->getContext() == this)
26#define ASSERT_SINGLE_OWNER \
27    SkDEBUGCODE(GrSingleOwner::AutoEnforce debug_SingleOwner(&fSingleOwner);)
28#define RETURN_IF_ABANDONED if (fDrawingManager->abandoned()) { return; }
29#define RETURN_FALSE_IF_ABANDONED if (fDrawingManager->abandoned()) { return false; }
30#define RETURN_NULL_IF_ABANDONED if (fDrawingManager->abandoned()) { return nullptr; }
31
32////////////////////////////////////////////////////////////////////////////////
33
34GrContext* GrContext::Create(GrBackend backend, GrBackendContext backendContext) {
35    GrContextOptions defaultOptions;
36    return Create(backend, backendContext, defaultOptions);
37}
38
39GrContext* GrContext::Create(GrBackend backend, GrBackendContext backendContext,
40                             const GrContextOptions& options) {
41    GrContext* context = new GrContext;
42
43    if (context->init(backend, backendContext, options)) {
44        return context;
45    } else {
46        context->unref();
47        return nullptr;
48    }
49}
50
51static int32_t gNextID = 1;
52static int32_t next_id() {
53    int32_t id;
54    do {
55        id = sk_atomic_inc(&gNextID);
56    } while (id == SK_InvalidGenID);
57    return id;
58}
59
60GrContext::GrContext() : fUniqueID(next_id()) {
61    fGpu = nullptr;
62    fCaps = nullptr;
63    fResourceCache = nullptr;
64    fResourceProvider = nullptr;
65    fBatchFontCache = nullptr;
66    fFlushToReduceCacheSize = false;
67}
68
69bool GrContext::init(GrBackend backend, GrBackendContext backendContext,
70                     const GrContextOptions& options) {
71    ASSERT_SINGLE_OWNER
72    SkASSERT(!fGpu);
73
74    fGpu = GrGpu::Create(backend, backendContext, options, this);
75    if (!fGpu) {
76        return false;
77    }
78    this->initCommon(options);
79    return true;
80}
81
82void GrContext::initCommon(const GrContextOptions& options) {
83    ASSERT_SINGLE_OWNER
84
85    fCaps = SkRef(fGpu->caps());
86    fResourceCache = new GrResourceCache(fCaps);
87    fResourceCache->setOverBudgetCallback(OverBudgetCB, this);
88    fResourceProvider = new GrResourceProvider(fGpu, fResourceCache, &fSingleOwner);
89
90    fLayerCache.reset(new GrLayerCache(this));
91
92    fDidTestPMConversions = false;
93
94    GrDrawTarget::Options dtOptions;
95    dtOptions.fClipBatchToBounds = options.fClipBatchToBounds;
96    dtOptions.fDrawBatchBounds = options.fDrawBatchBounds;
97    dtOptions.fMaxBatchLookback = options.fMaxBatchLookback;
98    dtOptions.fMaxBatchLookahead = options.fMaxBatchLookahead;
99    fDrawingManager.reset(new GrDrawingManager(this, dtOptions, &fSingleOwner));
100
101    // GrBatchFontCache will eventually replace GrFontCache
102    fBatchFontCache = new GrBatchFontCache(this);
103
104    fTextBlobCache.reset(new GrTextBlobCache(TextBlobCacheOverBudgetCB, this));
105}
106
107GrContext::~GrContext() {
108    ASSERT_SINGLE_OWNER
109
110    if (!fGpu) {
111        SkASSERT(!fCaps);
112        return;
113    }
114
115    this->flush();
116
117    fDrawingManager->cleanup();
118
119    for (int i = 0; i < fCleanUpData.count(); ++i) {
120        (*fCleanUpData[i].fFunc)(this, fCleanUpData[i].fInfo);
121    }
122
123    delete fResourceProvider;
124    delete fResourceCache;
125    delete fBatchFontCache;
126
127    fGpu->unref();
128    fCaps->unref();
129}
130
131void GrContext::abandonContext() {
132    ASSERT_SINGLE_OWNER
133
134    fResourceProvider->abandon();
135
136    // Need to abandon the drawing manager first so all the render targets
137    // will be released/forgotten before they too are abandoned.
138    fDrawingManager->abandon();
139
140    // abandon first to so destructors
141    // don't try to free the resources in the API.
142    fResourceCache->abandonAll();
143
144    fGpu->contextAbandoned();
145
146    fBatchFontCache->freeAll();
147    fLayerCache->freeAll();
148    fTextBlobCache->freeAll();
149}
150
151void GrContext::resetContext(uint32_t state) {
152    ASSERT_SINGLE_OWNER
153    fGpu->markContextDirty(state);
154}
155
156void GrContext::freeGpuResources() {
157    ASSERT_SINGLE_OWNER
158
159    this->flush();
160
161    fBatchFontCache->freeAll();
162    fLayerCache->freeAll();
163
164    fDrawingManager->freeGpuResources();
165
166    fResourceCache->purgeAllUnlocked();
167}
168
169void GrContext::getResourceCacheUsage(int* resourceCount, size_t* resourceBytes) const {
170    ASSERT_SINGLE_OWNER
171
172    if (resourceCount) {
173        *resourceCount = fResourceCache->getBudgetedResourceCount();
174    }
175    if (resourceBytes) {
176        *resourceBytes = fResourceCache->getBudgetedResourceBytes();
177    }
178}
179
180////////////////////////////////////////////////////////////////////////////////
181
182void GrContext::OverBudgetCB(void* data) {
183    SkASSERT(data);
184
185    GrContext* context = reinterpret_cast<GrContext*>(data);
186
187    // Flush the GrBufferedDrawTarget to possibly free up some textures
188    context->fFlushToReduceCacheSize = true;
189}
190
191void GrContext::TextBlobCacheOverBudgetCB(void* data) {
192    SkASSERT(data);
193
194    // Unlike the GrResourceCache, TextBlobs are drawn at the SkGpuDevice level, therefore they
195    // cannot use fFlushTorReduceCacheSize because it uses AutoCheckFlush.  The solution is to move
196    // drawText calls to below the GrContext level, but this is not trivial because they call
197    // drawPath on SkGpuDevice
198    GrContext* context = reinterpret_cast<GrContext*>(data);
199    context->flush();
200}
201
202////////////////////////////////////////////////////////////////////////////////
203
204void GrContext::flush(int flagsBitfield) {
205    ASSERT_SINGLE_OWNER
206    RETURN_IF_ABANDONED
207
208    if (kDiscard_FlushBit & flagsBitfield) {
209        fDrawingManager->reset();
210    } else {
211        fDrawingManager->flush();
212    }
213    fResourceCache->notifyFlushOccurred();
214    fFlushToReduceCacheSize = false;
215}
216
217bool sw_convert_to_premul(GrPixelConfig srcConfig, int width, int height, size_t inRowBytes,
218                          const void* inPixels, size_t outRowBytes, void* outPixels) {
219    SkSrcPixelInfo srcPI;
220    if (!GrPixelConfig2ColorAndProfileType(srcConfig, &srcPI.fColorType, nullptr)) {
221        return false;
222    }
223    srcPI.fAlphaType = kUnpremul_SkAlphaType;
224    srcPI.fPixels = inPixels;
225    srcPI.fRowBytes = inRowBytes;
226
227    SkDstPixelInfo dstPI;
228    dstPI.fColorType = srcPI.fColorType;
229    dstPI.fAlphaType = kPremul_SkAlphaType;
230    dstPI.fPixels = outPixels;
231    dstPI.fRowBytes = outRowBytes;
232
233    return srcPI.convertPixelsTo(&dstPI, width, height);
234}
235
236bool GrContext::writeSurfacePixels(GrSurface* surface,
237                                   int left, int top, int width, int height,
238                                   GrPixelConfig srcConfig, const void* buffer, size_t rowBytes,
239                                   uint32_t pixelOpsFlags) {
240    ASSERT_SINGLE_OWNER
241    RETURN_FALSE_IF_ABANDONED
242    ASSERT_OWNED_RESOURCE(surface);
243    SkASSERT(surface);
244    GR_AUDIT_TRAIL_AUTO_FRAME(&fAuditTrail, "GrContext::writeSurfacePixels");
245
246    this->testPMConversionsIfNecessary(pixelOpsFlags);
247
248    // Trim the params here so that if we wind up making a temporary surface it can be as small as
249    // necessary and because GrGpu::getWritePixelsInfo requires it.
250    if (!GrSurfacePriv::AdjustWritePixelParams(surface->width(), surface->height(),
251                                               GrBytesPerPixel(srcConfig), &left, &top, &width,
252                                               &height, &buffer, &rowBytes)) {
253        return false;
254    }
255
256    bool applyPremulToSrc = false;
257    if (kUnpremul_PixelOpsFlag & pixelOpsFlags) {
258        if (!GrPixelConfigIs8888(srcConfig)) {
259            return false;
260        }
261        applyPremulToSrc = true;
262    }
263
264    GrGpu::DrawPreference drawPreference = GrGpu::kNoDraw_DrawPreference;
265    // Don't prefer to draw for the conversion (and thereby access a texture from the cache) when
266    // we've already determined that there isn't a roundtrip preserving conversion processor pair.
267    if (applyPremulToSrc && !this->didFailPMUPMConversionTest()) {
268        drawPreference = GrGpu::kCallerPrefersDraw_DrawPreference;
269    }
270
271    GrGpu::WritePixelTempDrawInfo tempDrawInfo;
272    if (!fGpu->getWritePixelsInfo(surface, width, height, srcConfig, &drawPreference,
273                                  &tempDrawInfo)) {
274        return false;
275    }
276
277    if (!(kDontFlush_PixelOpsFlag & pixelOpsFlags) && surface->surfacePriv().hasPendingIO()) {
278        this->flush();
279    }
280
281    SkAutoTUnref<GrTexture> tempTexture;
282    if (GrGpu::kNoDraw_DrawPreference != drawPreference) {
283        tempTexture.reset(
284            this->textureProvider()->createApproxTexture(tempDrawInfo.fTempSurfaceDesc));
285        if (!tempTexture && GrGpu::kRequireDraw_DrawPreference == drawPreference) {
286            return false;
287        }
288    }
289
290    // temp buffer for doing sw premul conversion, if needed.
291    SkAutoSTMalloc<128 * 128, uint32_t> tmpPixels(0);
292    if (tempTexture) {
293        SkAutoTUnref<const GrFragmentProcessor> fp;
294        SkMatrix textureMatrix;
295        textureMatrix.setIDiv(tempTexture->width(), tempTexture->height());
296        if (applyPremulToSrc) {
297            fp.reset(this->createUPMToPMEffect(tempTexture, tempDrawInfo.fSwizzle,
298                                               textureMatrix));
299            // If premultiplying was the only reason for the draw, fall back to a straight write.
300            if (!fp) {
301                if (GrGpu::kCallerPrefersDraw_DrawPreference == drawPreference) {
302                    tempTexture.reset(nullptr);
303                }
304            } else {
305                applyPremulToSrc = false;
306            }
307        }
308        if (tempTexture) {
309            if (!fp) {
310                fp.reset(GrConfigConversionEffect::Create(tempTexture, tempDrawInfo.fSwizzle,
311                    GrConfigConversionEffect::kNone_PMConversion, textureMatrix));
312                if (!fp) {
313                    return false;
314                }
315            }
316            GrRenderTarget* renderTarget = surface->asRenderTarget();
317            SkASSERT(renderTarget);
318            if (tempTexture->surfacePriv().hasPendingIO()) {
319                this->flush();
320            }
321            if (applyPremulToSrc) {
322                size_t tmpRowBytes = 4 * width;
323                tmpPixels.reset(width * height);
324                if (!sw_convert_to_premul(srcConfig, width, height, rowBytes, buffer, tmpRowBytes,
325                                          tmpPixels.get())) {
326                    return false;
327                }
328                rowBytes = tmpRowBytes;
329                buffer = tmpPixels.get();
330                applyPremulToSrc = false;
331            }
332            if (!fGpu->writePixels(tempTexture, 0, 0, width, height,
333                                   tempDrawInfo.fWriteConfig, buffer,
334                                   rowBytes)) {
335                return false;
336            }
337            SkMatrix matrix;
338            matrix.setTranslate(SkIntToScalar(left), SkIntToScalar(top));
339            SkAutoTUnref<GrDrawContext> drawContext(this->drawContext(renderTarget));
340            if (!drawContext) {
341                return false;
342            }
343            GrPaint paint;
344            paint.addColorFragmentProcessor(fp);
345            paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode);
346            SkRect rect = SkRect::MakeWH(SkIntToScalar(width), SkIntToScalar(height));
347            drawContext->drawRect(GrClip::WideOpen(), paint, matrix, rect, nullptr);
348
349            if (kFlushWrites_PixelOp & pixelOpsFlags) {
350                this->flushSurfaceWrites(surface);
351            }
352        }
353    }
354    if (!tempTexture) {
355        if (applyPremulToSrc) {
356            size_t tmpRowBytes = 4 * width;
357            tmpPixels.reset(width * height);
358            if (!sw_convert_to_premul(srcConfig, width, height, rowBytes, buffer, tmpRowBytes,
359                                      tmpPixels.get())) {
360                return false;
361            }
362            rowBytes = tmpRowBytes;
363            buffer = tmpPixels.get();
364            applyPremulToSrc = false;
365        }
366        return fGpu->writePixels(surface, left, top, width, height, srcConfig, buffer, rowBytes);
367    }
368    return true;
369}
370
371bool GrContext::readSurfacePixels(GrSurface* src,
372                                  int left, int top, int width, int height,
373                                  GrPixelConfig dstConfig, void* buffer, size_t rowBytes,
374                                  uint32_t flags) {
375    ASSERT_SINGLE_OWNER
376    RETURN_FALSE_IF_ABANDONED
377    ASSERT_OWNED_RESOURCE(src);
378    SkASSERT(src);
379    GR_AUDIT_TRAIL_AUTO_FRAME(&fAuditTrail, "GrContext::readSurfacePixels");
380
381    this->testPMConversionsIfNecessary(flags);
382    SkAutoMutexAcquire ama(fReadPixelsMutex);
383
384    // Adjust the params so that if we wind up using an intermediate surface we've already done
385    // all the trimming and the temporary can be the min size required.
386    if (!GrSurfacePriv::AdjustReadPixelParams(src->width(), src->height(),
387                                              GrBytesPerPixel(dstConfig), &left,
388                                              &top, &width, &height, &buffer, &rowBytes)) {
389        return false;
390    }
391
392    if (!(kDontFlush_PixelOpsFlag & flags) && src->surfacePriv().hasPendingWrite()) {
393        this->flush();
394    }
395
396    bool unpremul = SkToBool(kUnpremul_PixelOpsFlag & flags);
397    if (unpremul && !GrPixelConfigIs8888(dstConfig)) {
398        // The unpremul flag is only allowed for 8888 configs.
399        return false;
400    }
401
402    GrGpu::DrawPreference drawPreference = GrGpu::kNoDraw_DrawPreference;
403    // Don't prefer to draw for the conversion (and thereby access a texture from the cache) when
404    // we've already determined that there isn't a roundtrip preserving conversion processor pair.
405    if (unpremul && !this->didFailPMUPMConversionTest()) {
406        drawPreference = GrGpu::kCallerPrefersDraw_DrawPreference;
407    }
408
409    GrGpu::ReadPixelTempDrawInfo tempDrawInfo;
410    if (!fGpu->getReadPixelsInfo(src, width, height, rowBytes, dstConfig, &drawPreference,
411                                 &tempDrawInfo)) {
412        return false;
413    }
414
415    SkAutoTUnref<GrSurface> surfaceToRead(SkRef(src));
416    bool didTempDraw = false;
417    if (GrGpu::kNoDraw_DrawPreference != drawPreference) {
418        if (tempDrawInfo.fUseExactScratch) {
419            // We only respect this when the entire src is being read. Otherwise we can trigger too
420            // many odd ball texture sizes and trash the cache.
421            if (width != src->width() || height != src->height()) {
422                tempDrawInfo.fUseExactScratch = false;
423            }
424        }
425        SkAutoTUnref<GrTexture> temp;
426        if (tempDrawInfo.fUseExactScratch) {
427            temp.reset(this->textureProvider()->createTexture(tempDrawInfo.fTempSurfaceDesc,
428                                                              SkBudgeted::kYes));
429        } else {
430            temp.reset(this->textureProvider()->createApproxTexture(tempDrawInfo.fTempSurfaceDesc));
431        }
432        if (temp) {
433            SkMatrix textureMatrix;
434            textureMatrix.setTranslate(SkIntToScalar(left), SkIntToScalar(top));
435            textureMatrix.postIDiv(src->width(), src->height());
436            SkAutoTUnref<const GrFragmentProcessor> fp;
437            if (unpremul) {
438                fp.reset(this->createPMToUPMEffect(src->asTexture(), tempDrawInfo.fSwizzle,
439                    textureMatrix));
440                if (fp) {
441                    unpremul = false; // we no longer need to do this on CPU after the read back.
442                } else if (GrGpu::kCallerPrefersDraw_DrawPreference == drawPreference) {
443                    // We only wanted to do the draw in order to perform the unpremul so don't
444                    // bother.
445                    temp.reset(nullptr);
446                }
447            }
448            if (!fp && temp) {
449                fp.reset(GrConfigConversionEffect::Create(src->asTexture(), tempDrawInfo.fSwizzle,
450                    GrConfigConversionEffect::kNone_PMConversion, textureMatrix));
451            }
452            if (fp) {
453                GrPaint paint;
454                paint.addColorFragmentProcessor(fp);
455                paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode);
456                SkRect rect = SkRect::MakeWH(SkIntToScalar(width), SkIntToScalar(height));
457                SkAutoTUnref<GrDrawContext> drawContext(this->drawContext(temp->asRenderTarget()));
458                drawContext->drawRect(GrClip::WideOpen(), paint, SkMatrix::I(), rect, nullptr);
459                surfaceToRead.reset(SkRef(temp.get()));
460                left = 0;
461                top = 0;
462                didTempDraw = true;
463            }
464        }
465    }
466
467    if (GrGpu::kRequireDraw_DrawPreference == drawPreference && !didTempDraw) {
468        return false;
469    }
470    GrPixelConfig configToRead = dstConfig;
471    if (didTempDraw) {
472        this->flushSurfaceWrites(surfaceToRead);
473        configToRead = tempDrawInfo.fReadConfig;
474    }
475    if (!fGpu->readPixels(surfaceToRead, left, top, width, height, configToRead, buffer,
476                           rowBytes)) {
477        return false;
478    }
479
480    // Perform umpremul conversion if we weren't able to perform it as a draw.
481    if (unpremul) {
482        SkDstPixelInfo dstPI;
483        if (!GrPixelConfig2ColorAndProfileType(dstConfig, &dstPI.fColorType, nullptr)) {
484            return false;
485        }
486        dstPI.fAlphaType = kUnpremul_SkAlphaType;
487        dstPI.fPixels = buffer;
488        dstPI.fRowBytes = rowBytes;
489
490        SkSrcPixelInfo srcPI;
491        srcPI.fColorType = dstPI.fColorType;
492        srcPI.fAlphaType = kPremul_SkAlphaType;
493        srcPI.fPixels = buffer;
494        srcPI.fRowBytes = rowBytes;
495
496        return srcPI.convertPixelsTo(&dstPI, width, height);
497    }
498    return true;
499}
500
501void GrContext::prepareSurfaceForExternalIO(GrSurface* surface) {
502    ASSERT_SINGLE_OWNER
503    RETURN_IF_ABANDONED
504    SkASSERT(surface);
505    ASSERT_OWNED_RESOURCE(surface);
506    if (surface->surfacePriv().hasPendingIO()) {
507        this->flush();
508    }
509    GrRenderTarget* rt = surface->asRenderTarget();
510    if (fGpu && rt) {
511        fGpu->resolveRenderTarget(rt);
512    }
513}
514
515bool GrContext::copySurface(GrSurface* dst, GrSurface* src, const SkIRect& srcRect,
516                            const SkIPoint& dstPoint) {
517    ASSERT_SINGLE_OWNER
518    RETURN_FALSE_IF_ABANDONED
519    GR_AUDIT_TRAIL_AUTO_FRAME(&fAuditTrail, "GrContext::copySurface");
520
521    if (!src || !dst) {
522        return false;
523    }
524    ASSERT_OWNED_RESOURCE(src);
525    ASSERT_OWNED_RESOURCE(dst);
526
527    if (!dst->asRenderTarget()) {
528        SkIRect clippedSrcRect;
529        SkIPoint clippedDstPoint;
530        if (!GrCopySurfaceBatch::ClipSrcRectAndDstPoint(dst, src, srcRect, dstPoint,
531                                                        &clippedSrcRect, &clippedDstPoint)) {
532            return false;
533        }
534        // If we don't have an RT for the dst then we won't have a GrDrawContext to insert the
535        // the copy surface into. In the future we plan to have a more limited Context type
536        // (GrCopyContext?) that has the subset of GrDrawContext operations that should be
537        // allowed on textures that aren't render targets.
538        // For now we just flush any writes to the src and issue an immediate copy to the dst.
539        src->flushWrites();
540        return fGpu->copySurface(dst, src, clippedSrcRect, clippedDstPoint);
541    }
542    SkAutoTUnref<GrDrawContext> drawContext(this->drawContext(dst->asRenderTarget()));
543    if (!drawContext) {
544        return false;
545    }
546
547    if (!drawContext->copySurface(src, srcRect, dstPoint)) {
548        return false;
549    }
550    return true;
551}
552
553void GrContext::flushSurfaceWrites(GrSurface* surface) {
554    ASSERT_SINGLE_OWNER
555    RETURN_IF_ABANDONED
556    if (surface->surfacePriv().hasPendingWrite()) {
557        this->flush();
558    }
559}
560
561////////////////////////////////////////////////////////////////////////////////
562int GrContext::getRecommendedSampleCount(GrPixelConfig config,
563                                         SkScalar dpi) const {
564    ASSERT_SINGLE_OWNER
565
566    if (!this->caps()->isConfigRenderable(config, true)) {
567        return 0;
568    }
569    int chosenSampleCount = 0;
570    if (fGpu->caps()->shaderCaps()->pathRenderingSupport()) {
571        if (dpi >= 250.0f) {
572            chosenSampleCount = 4;
573        } else {
574            chosenSampleCount = 16;
575        }
576    }
577    return chosenSampleCount <= fGpu->caps()->maxSampleCount() ? chosenSampleCount : 0;
578}
579
580
581GrDrawContext* GrContext::drawContext(GrRenderTarget* rt, const SkSurfaceProps* surfaceProps) {
582    ASSERT_SINGLE_OWNER
583    return fDrawingManager->drawContext(rt, surfaceProps);
584}
585
586bool GrContext::abandoned() const {
587    ASSERT_SINGLE_OWNER
588    return fDrawingManager->abandoned();
589}
590
591namespace {
592void test_pm_conversions(GrContext* ctx, int* pmToUPMValue, int* upmToPMValue) {
593    GrConfigConversionEffect::PMConversion pmToUPM;
594    GrConfigConversionEffect::PMConversion upmToPM;
595    GrConfigConversionEffect::TestForPreservingPMConversions(ctx, &pmToUPM, &upmToPM);
596    *pmToUPMValue = pmToUPM;
597    *upmToPMValue = upmToPM;
598}
599}
600
601void GrContext::testPMConversionsIfNecessary(uint32_t flags) {
602    ASSERT_SINGLE_OWNER
603    if (SkToBool(kUnpremul_PixelOpsFlag & flags)) {
604        SkAutoMutexAcquire ama(fTestPMConversionsMutex);
605        if (!fDidTestPMConversions) {
606            test_pm_conversions(this, &fPMToUPMConversion, &fUPMToPMConversion);
607            fDidTestPMConversions = true;
608        }
609    }
610}
611
612const GrFragmentProcessor* GrContext::createPMToUPMEffect(GrTexture* texture,
613                                                          const GrSwizzle& swizzle,
614                                                          const SkMatrix& matrix) const {
615    ASSERT_SINGLE_OWNER
616    // We should have already called this->testPMConversionsIfNecessary().
617    SkASSERT(fDidTestPMConversions);
618    GrConfigConversionEffect::PMConversion pmToUPM =
619        static_cast<GrConfigConversionEffect::PMConversion>(fPMToUPMConversion);
620    if (GrConfigConversionEffect::kNone_PMConversion != pmToUPM) {
621        return GrConfigConversionEffect::Create(texture, swizzle, pmToUPM, matrix);
622    } else {
623        return nullptr;
624    }
625}
626
627const GrFragmentProcessor* GrContext::createUPMToPMEffect(GrTexture* texture,
628                                                          const GrSwizzle& swizzle,
629                                                          const SkMatrix& matrix) const {
630    ASSERT_SINGLE_OWNER
631    // We should have already called this->testPMConversionsIfNecessary().
632    SkASSERT(fDidTestPMConversions);
633    GrConfigConversionEffect::PMConversion upmToPM =
634        static_cast<GrConfigConversionEffect::PMConversion>(fUPMToPMConversion);
635    if (GrConfigConversionEffect::kNone_PMConversion != upmToPM) {
636        return GrConfigConversionEffect::Create(texture, swizzle, upmToPM, matrix);
637    } else {
638        return nullptr;
639    }
640}
641
642bool GrContext::didFailPMUPMConversionTest() const {
643    ASSERT_SINGLE_OWNER
644    // We should have already called this->testPMConversionsIfNecessary().
645    SkASSERT(fDidTestPMConversions);
646    // The PM<->UPM tests fail or succeed together so we only need to check one.
647    return GrConfigConversionEffect::kNone_PMConversion == fPMToUPMConversion;
648}
649
650//////////////////////////////////////////////////////////////////////////////
651
652void GrContext::getResourceCacheLimits(int* maxTextures, size_t* maxTextureBytes) const {
653    ASSERT_SINGLE_OWNER
654    if (maxTextures) {
655        *maxTextures = fResourceCache->getMaxResourceCount();
656    }
657    if (maxTextureBytes) {
658        *maxTextureBytes = fResourceCache->getMaxResourceBytes();
659    }
660}
661
662void GrContext::setResourceCacheLimits(int maxTextures, size_t maxTextureBytes) {
663    ASSERT_SINGLE_OWNER
664    fResourceCache->setLimits(maxTextures, maxTextureBytes);
665}
666
667//////////////////////////////////////////////////////////////////////////////
668
669void GrContext::dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const {
670    ASSERT_SINGLE_OWNER
671    fResourceCache->dumpMemoryStatistics(traceMemoryDump);
672}
673