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