aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar robertphillips <robertphillips@google.com>2014-09-30 06:54:17 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2014-09-30 06:54:17 -0700
commitdbe6074a06efc5fb6883bb5e4f251ed67c8c0ab4 (patch)
tree9da0008e6fe68fbe2010ad14f5e6c54c7fa5008b
parent3d398c876440deaab39bbf2a9b881c337e6dc8d4 (diff)
Revert of GrResourceCache2 manages scratch texture. (patchset #14 id:260001 of https://codereview.chromium.org/608883003/)
Reason for revert: Turning bots red: Nanobench seems to be uniformly failing on Android (http://108.170.220.21:10117/builders/Perf-Android-Venue8-PowerVR-x86-Release/builds/99/steps/RunNanobench/logs/stdio) Ubuntu GTX660 32bit is failing in both Debug and Release on GM generation (it appears to be out of memory) (http://108.170.220.120:10117/builders/Test-Ubuntu12-ShuttleA-GTX660-x86-Debug/builds/2457/steps/GenerateGMs/logs/stdio) Original issue's description: > GrResourceCache2 manages scratch texture. > > > BUG=skia: > > Committed: https://skia.googlesource.com/skia/+/3d398c876440deaab39bbf2a9b881c337e6dc8d4 R=bsalomon@google.com TBR=bsalomon@google.com NOTREECHECKS=true NOTRY=true BUG=skia: Author: robertphillips@google.com Review URL: https://codereview.chromium.org/611383003
-rw-r--r--expectations/gm/ignored-tests.txt13
-rw-r--r--gm/texturedomaineffect.cpp5
-rw-r--r--gm/yuvtorgbeffect.cpp70
-rw-r--r--include/gpu/GrContext.h38
-rw-r--r--include/gpu/GrGpuResource.h32
-rw-r--r--include/gpu/GrTexture.h2
-rw-r--r--include/gpu/SkGr.h4
-rw-r--r--include/gpu/SkGrPixelRef.h8
-rw-r--r--src/core/SkBitmapProcShader.cpp5
-rw-r--r--src/core/SkImageFilter.cpp5
-rw-r--r--src/effects/SkPerlinNoiseShader.cpp19
-rw-r--r--src/effects/SkTableColorFilter.cpp14
-rw-r--r--src/effects/gradients/SkGradientShader.cpp7
-rwxr-xr-xsrc/gpu/GrContext.cpp207
-rw-r--r--src/gpu/GrLayerCache.cpp1
-rw-r--r--src/gpu/GrResourceCache.cpp209
-rw-r--r--src/gpu/GrResourceCache.h61
-rw-r--r--src/gpu/GrResourceCache2.cpp18
-rw-r--r--src/gpu/GrResourceCache2.h8
-rw-r--r--src/gpu/GrTexture.cpp36
-rw-r--r--src/gpu/SkGpuDevice.cpp63
-rw-r--r--src/gpu/SkGpuDevice.h6
-rw-r--r--src/gpu/SkGr.cpp13
-rw-r--r--src/gpu/SkGrPixelRef.cpp11
-rw-r--r--src/gpu/effects/GrTextureStripAtlas.cpp1
-rw-r--r--src/image/SkSurface_Gpu.cpp12
-rw-r--r--tests/ResourceCacheTest.cpp31
27 files changed, 656 insertions, 243 deletions
diff --git a/expectations/gm/ignored-tests.txt b/expectations/gm/ignored-tests.txt
index d0af9fb7d2..be7d4d336a 100644
--- a/expectations/gm/ignored-tests.txt
+++ b/expectations/gm/ignored-tests.txt
@@ -42,18 +42,5 @@ fontcache
# reed - conservative_rasterclip CL
multipicturedraw_pathclip_tiled
-#bsalomon - small changes after moving scratch texture management to new cache
-multipicturedraw_pathclip_tiled
-multipicturedraw_invpathclip_tiled
-multipicturedraw_noclip_tiled
-multipicturedraw_rectclip_tiled
-multipicturedraw_rrectclip_tiled
-multipicturedraw_pathclip_simple
-multipicturedraw_invpathclip_simple
-multipicturedraw_noclip_simple
-multipicturedraw_rectclip_simple
-multipicturedraw_rrectclip_simple
-multipicturedraw_sierpinski_tiled
-
# rileya - https://codereview.chromium.org/516463005/ will rebaseline after bots cycle
yuv_to_rgb_effect
diff --git a/gm/texturedomaineffect.cpp b/gm/texturedomaineffect.cpp
index acf039513d..6534b0c1dd 100644
--- a/gm/texturedomaineffect.cpp
+++ b/gm/texturedomaineffect.cpp
@@ -92,8 +92,8 @@ protected:
GrDrawState* drawState = tt.target()->drawState();
- SkAutoTUnref<GrTexture> texture(GrRefCachedBitmapTexture(context, fBmp, NULL));
- if (!texture) {
+ GrTexture* texture = GrLockAndRefCachedBitmapTexture(context, fBmp, NULL);
+ if (NULL == texture) {
return;
}
@@ -144,6 +144,7 @@ protected:
y += renderRect.height() + kTestPad;
}
}
+ GrUnlockAndUnrefCachedBitmapTexture(texture);
}
private:
diff --git a/gm/yuvtorgbeffect.cpp b/gm/yuvtorgbeffect.cpp
index 265491b757..026823e8ee 100644
--- a/gm/yuvtorgbeffect.cpp
+++ b/gm/yuvtorgbeffect.cpp
@@ -83,12 +83,11 @@ protected:
GrDrawState* drawState = tt.target()->drawState();
- SkAutoTUnref<GrTexture> texture[3];
- texture[0].reset(GrRefCachedBitmapTexture(context, fBmp[0], NULL));
- texture[1].reset(GrRefCachedBitmapTexture(context, fBmp[1], NULL));
- texture[2].reset(GrRefCachedBitmapTexture(context, fBmp[2], NULL));
-
- if (!texture[0] || !texture[1] || !texture[2]) {
+ GrTexture* texture[3];
+ texture[0] = GrLockAndRefCachedBitmapTexture(context, fBmp[0], NULL);
+ texture[1] = GrLockAndRefCachedBitmapTexture(context, fBmp[1], NULL);
+ texture[2] = GrLockAndRefCachedBitmapTexture(context, fBmp[2], NULL);
+ if ((NULL == texture[0]) || (NULL == texture[1]) || (NULL == texture[2])) {
return;
}
@@ -98,35 +97,38 @@ protected:
for (int space = kJPEG_SkYUVColorSpace; space <= kLastEnum_SkYUVColorSpace;
++space) {
- SkRect renderRect = SkRect::MakeWH(SkIntToScalar(fBmp[0].width()),
- SkIntToScalar(fBmp[0].height()));
- renderRect.outset(kDrawPad, kDrawPad);
-
- SkScalar y = kDrawPad + kTestPad + space * kColorSpaceOffset;
- SkScalar x = kDrawPad + kTestPad;
-
- const int indices[6][3] = {{0, 1, 2}, {0, 2, 1}, {1, 0, 2},
- {1, 2, 0}, {2, 0, 1}, {2, 1, 0}};
-
- for (int i = 0; i < 6; ++i) {
- SkAutoTUnref<GrFragmentProcessor> fp(
- GrYUVtoRGBEffect::Create(texture[indices[i][0]],
- texture[indices[i][1]],
- texture[indices[i][2]],
- static_cast<SkYUVColorSpace>(space)));
- if (fp) {
- SkMatrix viewMatrix;
- viewMatrix.setTranslate(x, y);
- drawState->reset(viewMatrix);
- drawState->setRenderTarget(rt);
- drawState->setColor(0xffffffff);
- drawState->addColorProcessor(fp);
- tt.target()->drawSimpleRect(renderRect);
- }
- x += renderRect.width() + kTestPad;
- }
+ SkRect renderRect = SkRect::MakeWH(SkIntToScalar(fBmp[0].width()),
+ SkIntToScalar(fBmp[0].height()));
+ renderRect.outset(kDrawPad, kDrawPad);
+
+ SkScalar y = kDrawPad + kTestPad + space * kColorSpaceOffset;
+ SkScalar x = kDrawPad + kTestPad;
+
+ const int indices[6][3] = {{0, 1, 2}, {0, 2, 1}, {1, 0, 2}, {1, 2, 0}, {2, 0, 1}, {2, 1, 0}};
+
+ for (int i = 0; i < 6; ++i) {
+ SkAutoTUnref<GrFragmentProcessor> fp(
+ GrYUVtoRGBEffect::Create(texture[indices[i][0]],
+ texture[indices[i][1]],
+ texture[indices[i][2]],
+ static_cast<SkYUVColorSpace>(space)));
+ if (fp) {
+ SkMatrix viewMatrix;
+ viewMatrix.setTranslate(x, y);
+ drawState->reset(viewMatrix);
+ drawState->setRenderTarget(rt);
+ drawState->setColor(0xffffffff);
+ drawState->addColorProcessor(fp);
+ tt.target()->drawSimpleRect(renderRect);
+ }
+ x += renderRect.width() + kTestPad;
+ }
}
- }
+
+ GrUnlockAndUnrefCachedBitmapTexture(texture[0]);
+ GrUnlockAndUnrefCachedBitmapTexture(texture[1]);
+ GrUnlockAndUnrefCachedBitmapTexture(texture[2]);
+ }
private:
SkBitmap fBmp[3];
diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h
index d2f3729f15..e1af73ccb2 100644
--- a/include/gpu/GrContext.h
+++ b/include/gpu/GrContext.h
@@ -295,6 +295,12 @@ public:
GrTexture* lockAndRefScratchTexture(const GrTextureDesc&, ScratchTexMatch match);
/**
+ * When done with an entry, call unlockScratchTexture(entry) on it, which returns
+ * it to the cache, where it may be purged. This does not unref the texture.
+ */
+ void unlockScratchTexture(GrTexture* texture);
+
+ /**
* Creates a texture that is outside the cache. Does not count against
* cache's budget.
*/
@@ -1045,7 +1051,15 @@ private:
size_t rowBytes,
bool filter);
- bool createNewScratchTexture(const GrTextureDesc& desc);
+ // Needed so GrTexture's returnToCache helper function can call
+ // addExistingTextureToCache
+ friend class GrTexture;
+ friend class GrStencilAndCoverPathRenderer;
+ friend class GrStencilAndCoverTextContext;
+
+ // Add an existing texture to the texture cache. This is intended solely
+ // for use with textures released from an GrAutoScratchTexture.
+ void addExistingTextureToCache(GrTexture* texture);
/**
* These functions create premul <-> unpremul effects if it is possible to generate a pair
@@ -1065,7 +1079,8 @@ private:
};
/**
- * This is deprecated. Don't use it.
+ * Gets and locks a scratch texture from a descriptor using either exact or approximate criteria.
+ * Unlocks texture in the destructor.
*/
class GrAutoScratchTexture : public ::SkNoncopyable {
public:
@@ -1088,16 +1103,25 @@ public:
void reset() {
if (fContext && fTexture) {
+ fContext->unlockScratchTexture(fTexture);
fTexture->unref();
fTexture = NULL;
}
}
- GrTexture* detach() {
- GrTexture* texture = fTexture;
- fTexture = NULL;
- return texture;
- }
+ /*
+ * When detaching a texture we do not unlock it in the texture cache but
+ * we do set the returnToCache flag. In this way the texture remains
+ * "locked" in the texture cache until it is freed and recycled in
+ * GrTexture::internal_dispose. In reality, the texture has been removed
+ * from the cache (because this is in AutoScratchTexture) and by not
+ * calling unlockScratchTexture we simply don't re-add it. It will be
+ * reattached in GrTexture::internal_dispose.
+ *
+ * Note that the caller is assumed to accept and manage the ref to the
+ * returned texture.
+ */
+ GrTexture* detach();
GrTexture* set(GrContext* context,
const GrTextureDesc& desc,
diff --git a/include/gpu/GrGpuResource.h b/include/gpu/GrGpuResource.h
index 17f34d62b2..61849e7232 100644
--- a/include/gpu/GrGpuResource.h
+++ b/include/gpu/GrGpuResource.h
@@ -49,21 +49,27 @@ public:
// templated helper classes (e.g. SkAutoTUnref). However, we have different categories of
// refs (e.g. pending reads). We also don't require thread safety as GrCacheable objects are
// not intended to cross thread boundaries.
+ // internal_dispose() exists because of GrTexture's reliance on it. It will be removed
+ // soon.
void ref() const {
- this->validate();
++fRefCnt;
+ // pre-validate once internal_dispose is removed (and therefore 0 ref cnt is not allowed).
+ this->validate();
}
void unref() const {
this->validate();
--fRefCnt;
if (0 == fRefCnt && 0 == fPendingReads && 0 == fPendingWrites) {
- SkDELETE(this);
+ this->internal_dispose();
}
}
- bool isPurgable() const { return this->reffedOnlyByCache() && !this->internalHasPendingIO(); }
- bool reffedOnlyByCache() const { return 1 == fRefCnt; }
+ virtual void internal_dispose() const { SkDELETE(this); }
+
+ /** This is exists to service the old mechanism for recycling scratch textures. It will
+ be removed soon. */
+ bool unique() const { return 1 == (fRefCnt + fPendingReads + fPendingWrites); }
void validate() const {
#ifdef SK_DEBUG
@@ -74,8 +80,9 @@ public:
#endif
}
+
protected:
- GrIORef() : fRefCnt(1), fPendingReads(0), fPendingWrites(0), fIsScratch(kNo_IsScratch) { }
+ GrIORef() : fRefCnt(1), fPendingReads(0), fPendingWrites(0) {}
bool internalHasPendingRead() const { return SkToBool(fPendingReads); }
bool internalHasPendingWrite() const { return SkToBool(fPendingWrites); }
@@ -91,7 +98,7 @@ private:
this->validate();
--fPendingReads;
if (0 == fRefCnt && 0 == fPendingReads && 0 == fPendingWrites) {
- SkDELETE(this);
+ this->internal_dispose();
}
}
@@ -104,7 +111,7 @@ private:
this->validate();
--fPendingWrites;
if (0 == fRefCnt && 0 == fPendingReads && 0 == fPendingWrites) {
- SkDELETE(this);
+ this->internal_dispose();
}
}
@@ -115,17 +122,6 @@ private:
// This class is used to manage conversion of refs to pending reads/writes.
friend class GrGpuResourceRef;
-
- // This is temporary until GrResourceCache is fully replaced by GrResourceCache2.
- enum IsScratch {
- kNo_IsScratch,
- kYes_IsScratch
- } fIsScratch;
-
- friend class GrContext; // to set the above field.
- friend class GrResourceCache; // to check the above field.
- friend class GrResourceCache2; // to check the above field.
-
template <typename, IOType> friend class GrPendingIOResource;
};
diff --git a/include/gpu/GrTexture.h b/include/gpu/GrTexture.h
index 43b69eade2..8bdff34df5 100644
--- a/include/gpu/GrTexture.h
+++ b/include/gpu/GrTexture.h
@@ -99,6 +99,8 @@ protected:
void validateDesc() const;
private:
+ void abandonReleaseCommon();
+ virtual void internal_dispose() const SK_OVERRIDE;
void dirtyMipMaps(bool mipMapsDirty);
enum MipMapsStatus {
diff --git a/include/gpu/SkGr.h b/include/gpu/SkGr.h
index 026525be5d..df2ae5a320 100644
--- a/include/gpu/SkGr.h
+++ b/include/gpu/SkGr.h
@@ -70,7 +70,9 @@ static inline GrColor SkColor2GrColorJustAlpha(SkColor c) {
bool GrIsBitmapInCache(const GrContext*, const SkBitmap&, const GrTextureParams*);
-GrTexture* GrRefCachedBitmapTexture(GrContext*, const SkBitmap&, const GrTextureParams*);
+GrTexture* GrLockAndRefCachedBitmapTexture(GrContext*, const SkBitmap&, const GrTextureParams*);
+
+void GrUnlockAndUnrefCachedBitmapTexture(GrTexture*);
////////////////////////////////////////////////////////////////////////////////
diff --git a/include/gpu/SkGrPixelRef.h b/include/gpu/SkGrPixelRef.h
index 9a81be67de..7e6a9d02ed 100644
--- a/include/gpu/SkGrPixelRef.h
+++ b/include/gpu/SkGrPixelRef.h
@@ -41,9 +41,11 @@ class SK_API SkGrPixelRef : public SkROLockPixelsPixelRef {
public:
SK_DECLARE_INST_COUNT(SkGrPixelRef)
/**
- * Constructs a pixel ref around a GrSurface.
+ * Constructs a pixel ref around a GrSurface. If the caller has locked the GrSurface in the
+ * cache and would like the pixel ref to unlock it in its destructor then transferCacheLock
+ * should be set to true.
*/
- SkGrPixelRef(const SkImageInfo&, GrSurface*);
+ SkGrPixelRef(const SkImageInfo&, GrSurface*, bool transferCacheLock = false);
virtual ~SkGrPixelRef();
// override from SkPixelRef
@@ -56,6 +58,8 @@ protected:
private:
GrSurface* fSurface;
+ bool fUnlock; // if true the pixel ref owns a texture cache lock on fSurface
+
typedef SkROLockPixelsPixelRef INHERITED;
};
diff --git a/src/core/SkBitmapProcShader.cpp b/src/core/SkBitmapProcShader.cpp
index a9798b8e24..9c900d35f8 100644
--- a/src/core/SkBitmapProcShader.cpp
+++ b/src/core/SkBitmapProcShader.cpp
@@ -459,9 +459,9 @@ bool SkBitmapProcShader::asFragmentProcessor(GrContext* context, const SkPaint&
}
GrTextureParams params(tm, textureFilterMode);
- SkAutoTUnref<GrTexture> texture(GrRefCachedBitmapTexture(context, fRawBitmap, &params));
+ GrTexture* texture = GrLockAndRefCachedBitmapTexture(context, fRawBitmap, &params);
- if (!texture) {
+ if (NULL == texture) {
SkErrorInternals::SetError( kInternalError_SkError,
"Couldn't convert bitmap to texture.");
return false;
@@ -476,6 +476,7 @@ bool SkBitmapProcShader::asFragmentProcessor(GrContext* context, const SkPaint&
} else {
*fp = GrSimpleTextureEffect::Create(texture, matrix, params);
}
+ GrUnlockAndUnrefCachedBitmapTexture(texture);
return true;
}
diff --git a/src/core/SkImageFilter.cpp b/src/core/SkImageFilter.cpp
index 6f7762ac46..56c310d37e 100644
--- a/src/core/SkImageFilter.cpp
+++ b/src/core/SkImageFilter.cpp
@@ -399,8 +399,9 @@ bool SkImageFilter::getInputResultGPU(SkImageFilter::Proxy* proxy,
if (kUnknown_SkColorType == info.colorType()) {
return false;
}
- SkAutoTUnref<GrTexture> resultTex(GrRefCachedBitmapTexture(context, *result, NULL));
- result->setPixelRef(SkNEW_ARGS(SkGrPixelRef, (info, resultTex)))->unref();
+ GrTexture* resultTex = GrLockAndRefCachedBitmapTexture(context, *result, NULL);
+ result->setPixelRef(new SkGrPixelRef(info, resultTex))->unref();
+ GrUnlockAndUnrefCachedBitmapTexture(resultTex);
}
return true;
} else {
diff --git a/src/effects/SkPerlinNoiseShader.cpp b/src/effects/SkPerlinNoiseShader.cpp
index 2668728366..88e6caddb9 100644
--- a/src/effects/SkPerlinNoiseShader.cpp
+++ b/src/effects/SkPerlinNoiseShader.cpp
@@ -996,10 +996,10 @@ bool SkPerlinNoiseShader::asFragmentProcessor(GrContext* context, const SkPaint&
SkPerlinNoiseShader::PaintingData* paintingData =
SkNEW_ARGS(PaintingData, (fTileSize, fSeed, fBaseFrequencyX, fBaseFrequencyY, matrix));
- SkAutoTUnref<GrTexture> permutationsTexture(
- GrRefCachedBitmapTexture(context, paintingData->getPermutationsBitmap(), NULL));
- SkAutoTUnref<GrTexture> noiseTexture(
- GrRefCachedBitmapTexture(context, paintingData->getNoiseBitmap(), NULL));
+ GrTexture* permutationsTexture = GrLockAndRefCachedBitmapTexture(
+ context, paintingData->getPermutationsBitmap(), NULL);
+ GrTexture* noiseTexture = GrLockAndRefCachedBitmapTexture(
+ context, paintingData->getNoiseBitmap(), NULL);
SkMatrix m = context->getMatrix();
m.setTranslateX(-localMatrix.getTranslateX() + SK_Scalar1);
@@ -1015,6 +1015,17 @@ bool SkPerlinNoiseShader::asFragmentProcessor(GrContext* context, const SkPaint&
SkDELETE(paintingData);
*fp = NULL;
}
+
+ // Unlock immediately, this is not great, but we don't have a way of
+ // knowing when else to unlock it currently. TODO: Remove this when
+ // unref becomes the unlock replacement for all types of textures.
+ if (permutationsTexture) {
+ GrUnlockAndUnrefCachedBitmapTexture(permutationsTexture);
+ }
+ if (noiseTexture) {
+ GrUnlockAndUnrefCachedBitmapTexture(noiseTexture);
+ }
+
return true;
}
diff --git a/src/effects/SkTableColorFilter.cpp b/src/effects/SkTableColorFilter.cpp
index bed99fc9de..4853f73e5c 100644
--- a/src/effects/SkTableColorFilter.cpp
+++ b/src/effects/SkTableColorFilter.cpp
@@ -418,6 +418,7 @@ void ColorTableEffect::getConstantColorComponents(GrColor* color, uint32_t* vali
}
}
+
///////////////////////////////////////////////////////////////////////////////
GR_DEFINE_FRAGMENT_PROCESSOR_TEST(ColorTableEffect);
@@ -433,10 +434,19 @@ GrFragmentProcessor* ColorTableEffect::TestCreate(SkRandom* random,
GrFragmentProcessor* SkTable_ColorFilter::asFragmentProcessor(GrContext* context) const {
SkBitmap bitmap;
+ GrFragmentProcessor* fp = NULL;
this->asComponentTable(&bitmap);
// passing NULL because this effect does no tiling or filtering.
- SkAutoTUnref<GrTexture> texture(GrRefCachedBitmapTexture(context, bitmap, NULL));
- return texture ? ColorTableEffect::Create(texture, fFlags) : NULL;
+ GrTexture* texture = GrLockAndRefCachedBitmapTexture(context, bitmap, NULL);
+ if (texture) {
+ fp = ColorTableEffect::Create(texture, fFlags);
+
+ // Unlock immediately, this is not great, but we don't have a way of
+ // knowing when else to unlock it currently. TODO: Remove this when
+ // unref becomes the unlock replacement for all types of textures.
+ GrUnlockAndUnrefCachedBitmapTexture(texture);
+ }
+ return fp;
}
#endif // SK_SUPPORT_GPU
diff --git a/src/effects/gradients/SkGradientShader.cpp b/src/effects/gradients/SkGradientShader.cpp
index 39f0e75df1..d25873b438 100644
--- a/src/effects/gradients/SkGradientShader.cpp
+++ b/src/effects/gradients/SkGradientShader.cpp
@@ -1164,10 +1164,15 @@ GrGradientEffect::GrGradientEffect(GrContext* ctx,
fCoordTransform.reset(kCoordSet, matrix, fAtlas->getTexture());
fTextureAccess.reset(fAtlas->getTexture(), params);
} else {
- SkAutoTUnref<GrTexture> texture(GrRefCachedBitmapTexture(ctx, bitmap, &params));
+ GrTexture* texture = GrLockAndRefCachedBitmapTexture(ctx, bitmap, &params);
fCoordTransform.reset(kCoordSet, matrix, texture);
fTextureAccess.reset(texture, params);
fYCoord = SK_ScalarHalf;
+
+ // Unlock immediately, this is not great, but we don't have a way of
+ // knowing when else to unlock it currently, so it may get purged from
+ // the cache, but it'll still be ref'd until it's no longer being used.
+ GrUnlockAndUnrefCachedBitmapTexture(texture);
}
this->addTextureAccess(&fTextureAccess);
}
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index de34f23552..46a5576b11 100755
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -70,6 +70,25 @@ static const int DRAW_BUFFER_IBPOOL_PREALLOC_BUFFERS = 4;
#define ASSERT_OWNED_RESOURCE(R) SkASSERT(!(R) || (R)->getContext() == this)
+GrTexture* GrAutoScratchTexture::detach() {
+ if (NULL == fTexture) {
+ return NULL;
+ }
+ GrTexture* texture = fTexture;
+ fTexture = NULL;
+
+ // This GrAutoScratchTexture has a ref from lockAndRefScratchTexture, which we give up now.
+ // The cache also has a ref which we are lending to the caller of detach(). When the caller
+ // lets go of the ref and the ref count goes to 0 internal_dispose will see this flag is
+ // set and re-ref the texture, thereby restoring the cache's ref.
+ SkASSERT(!texture->unique());
+ texture->texturePriv().setFlag((GrTextureFlags) GrTexture::kReturnToCache_FlagBit);
+ texture->unref();
+ SkASSERT(texture->getCacheEntry());
+
+ return texture;
+}
+
// Glorified typedef to avoid including GrDrawState.h in GrContext.h
class GrContext::AutoRestoreEffects : public GrDrawState::AutoRestoreEffects {};
@@ -440,77 +459,157 @@ GrTexture* GrContext::createTexture(const GrTextureParams* params,
return texture;
}
-bool GrContext::createNewScratchTexture(const GrTextureDesc& desc) {
- SkAutoTUnref<GrTexture> texture(fGpu->createTexture(desc, NULL, 0));
- if (!texture) {
- return false;
+static GrTexture* create_scratch_texture(GrGpu* gpu,
+ GrResourceCache* resourceCache,
+ const GrTextureDesc& desc) {
+ GrTexture* texture = gpu->createTexture(desc, NULL, 0);
+ if (texture) {
+ GrResourceKey key = GrTexturePriv::ComputeScratchKey(texture->desc());
+ // Adding a resource could put us overbudget. Try to free up the
+ // necessary space before adding it.
+ resourceCache->purgeAsNeeded(1, texture->gpuMemorySize());
+ // Make the resource exclusive so future 'find' calls don't return it
+ resourceCache->addResource(key, texture, GrResourceCache::kHide_OwnershipFlag);
}
- fResourceCache->addResource(texture->getScratchKey(), texture);
- texture->fIsScratch = GrIORef::kYes_IsScratch;
- return true;
+ return texture;
}
GrTexture* GrContext::lockAndRefScratchTexture(const GrTextureDesc& inDesc, ScratchTexMatch match) {
- // kNoStencil has no meaning if kRT isn't set.
SkASSERT((inDesc.fFlags & kRenderTarget_GrTextureFlagBit) ||
!(inDesc.fFlags & kNoStencil_GrTextureFlagBit));
- // Make sure caller has checked for renderability if kRT is set.
- SkASSERT(!(inDesc.fFlags & kRenderTarget_GrTextureFlagBit) ||
- this->isConfigRenderable(inDesc.fConfig, inDesc.fSampleCnt > 0));
-
- SkTCopyOnFirstWrite<GrTextureDesc> desc(inDesc);
-
- // There is a regression here in that when reuseScratchTextures is false, the texture won't be
- // freed when its ref and io counts reach zero. TODO: Make GrResourceCache2 free scratch
- // resources immediately after it is the sole owner and reuseScratchTextures is false.
- if (fGpu->caps()->reuseScratchTextures() || (desc->fFlags & kRenderTarget_GrTextureFlagBit)) {
- GrTextureFlags origFlags = desc->fFlags;
- if (kApprox_ScratchTexMatch == match) {
- // bin by pow2 with a reasonable min
- static const int MIN_SIZE = 16;
- GrTextureDesc* wdesc = desc.writable();
- wdesc->fWidth = SkTMax(MIN_SIZE, GrNextPow2(desc->fWidth));
- wdesc->fHeight = SkTMax(MIN_SIZE, GrNextPow2(desc->fHeight));
+ // Renderable A8 targets are not universally supported (e.g., not on ANGLE)
+ SkASSERT(this->isConfigRenderable(kAlpha_8_GrPixelConfig, inDesc.fSampleCnt > 0) ||
+ !(inDesc.fFlags & kRenderTarget_GrTextureFlagBit) ||
+ (inDesc.fConfig != kAlpha_8_GrPixelConfig));
+
+ if (!fGpu->caps()->reuseScratchTextures() &&
+ !(inDesc.fFlags & kRenderTarget_GrTextureFlagBit)) {
+ // If we're never recycling this texture we can always make it the right size
+ return create_scratch_texture(fGpu, fResourceCache, inDesc);
+ }
+
+ GrTextureDesc desc = inDesc;
+
+ if (kApprox_ScratchTexMatch == match) {
+ // bin by pow2 with a reasonable min
+ static const int MIN_SIZE = 16;
+ desc.fWidth = SkTMax(MIN_SIZE, GrNextPow2(desc.fWidth));
+ desc.fHeight = SkTMax(MIN_SIZE, GrNextPow2(desc.fHeight));
+ }
+
+ GrGpuResource* resource = NULL;
+ int origWidth = desc.fWidth;
+ int origHeight = desc.fHeight;
+
+ do {
+ GrResourceKey key = GrTexturePriv::ComputeScratchKey(desc);
+ // Ensure we have exclusive access to the texture so future 'find' calls don't return it
+ resource = fResourceCache->find(key, GrResourceCache::kHide_OwnershipFlag);
+ if (resource) {
+ resource->ref();
+ break;
+ }
+ if (kExact_ScratchTexMatch == match) {
+ break;
}
+ // We had a cache miss and we are in approx mode, relax the fit of the flags.
- do {
- GrResourceKey key = GrTexturePriv::ComputeScratchKey(*desc);
- GrGpuResource* resource = fResourceCache2->findAndRefScratchResource(key);
- if (resource) {
- fResourceCache->makeResourceMRU(resource);
- return static_cast<GrTexture*>(resource);
- }
+ // We no longer try to reuse textures that were previously used as render targets in
+ // situations where no RT is needed; doing otherwise can confuse the video driver and
+ // cause significant performance problems in some cases.
+ if (desc.fFlags & kNoStencil_GrTextureFlagBit) {
+ desc.fFlags = desc.fFlags & ~kNoStencil_GrTextureFlagBit;
+ } else {
+ break;
+ }
- if (kExact_ScratchTexMatch == match) {
- break;
- }
- // We had a cache miss and we are in approx mode, relax the fit of the flags.
-
- // We no longer try to reuse textures that were previously used as render targets in
- // situations where no RT is needed; doing otherwise can confuse the video driver and
- // cause significant performance problems in some cases.
- if (desc->fFlags & kNoStencil_GrTextureFlagBit) {
- desc.writable()->fFlags = desc->fFlags & ~kNoStencil_GrTextureFlagBit;
- } else {
- break;
- }
+ } while (true);
- } while (true);
+ if (NULL == resource) {
+ desc.fFlags = inDesc.fFlags;
+ desc.fWidth = origWidth;
+ desc.fHeight = origHeight;
+ resource = create_scratch_texture(fGpu, fResourceCache, desc);
+ }
- desc.writable()->fFlags = origFlags;
+ return static_cast<GrTexture*>(resource);
+}
+
+void GrContext::addExistingTextureToCache(GrTexture* texture) {
+
+ if (NULL == texture) {
+ return;
}
- if (!this->createNewScratchTexture(*desc)) {
- return NULL;
+ // This texture should already have a cache entry since it was once
+ // attached
+ SkASSERT(texture->getCacheEntry());
+
+ // Conceptually, the cache entry is going to assume responsibility
+ // for the creation ref. Assert refcnt == 1.
+ // Except that this also gets called when the texture is prematurely
+ // abandoned. In that case the ref count may be > 1.
+ // SkASSERT(texture->unique());
+
+ if (fGpu->caps()->reuseScratchTextures() || texture->asRenderTarget()) {
+ // Since this texture came from an AutoScratchTexture it should
+ // still be in the exclusive pile. Recycle it.
+ fResourceCache->makeNonExclusive(texture->getCacheEntry());
+ this->purgeCache();
+ } else {
+ // When we aren't reusing textures we know this scratch texture
+ // will never be reused and would be just wasting time in the cache
+ fResourceCache->makeNonExclusive(texture->getCacheEntry());
+ fResourceCache->deleteResource(texture->getCacheEntry());
}
+}
- // If we got here then we didn't find a cached texture, but we just added one.
- GrResourceKey key = GrTexturePriv::ComputeScratchKey(*desc);
- GrGpuResource* resource = fResourceCache2->findAndRefScratchResource(key);
- SkASSERT(resource);
- return static_cast<GrTexture*>(resource);
+void GrContext::unlockScratchTexture(GrTexture* texture) {
+ if (texture->wasDestroyed()) {
+ if (texture->getCacheEntry()->key().isScratch()) {
+ // This texture was detached from the cache but the cache still had a ref to it but
+ // not a pointer to it. This will unref the texture and delete its resource cache
+ // entry.
+ delete texture->getCacheEntry();
+ }
+ return;
+ }
+
+ ASSERT_OWNED_RESOURCE(texture);
+ SkASSERT(texture->getCacheEntry());
+
+ // If this is a scratch texture we detached it from the cache
+ // while it was locked (to avoid two callers simultaneously getting
+ // the same texture).
+ if (texture->getCacheEntry()->key().isScratch()) {
+ if (fGpu->caps()->reuseScratchTextures() || texture->asRenderTarget()) {
+ fResourceCache->makeNonExclusive(texture->getCacheEntry());
+ this->purgeCache();
+ } else if (texture->unique()) {
+ // Only the cache now knows about this texture. Since we're never
+ // reusing scratch textures (in this code path) it would just be
+ // wasting time sitting in the cache.
+ fResourceCache->makeNonExclusive(texture->getCacheEntry());
+ fResourceCache->deleteResource(texture->getCacheEntry());
+ } else {
+ // In this case (there is still a non-cache ref) but we don't really
+ // want to readd it to the cache (since it will never be reused).
+ // Instead, give up the cache's ref and leave the decision up to
+ // addExistingTextureToCache once its ref count reaches 0. For
+ // this to work we need to leave it in the exclusive list.
+ texture->texturePriv().setFlag((GrTextureFlags) GrTexture::kReturnToCache_FlagBit);
+ // Give up the cache's ref to the texture
+ texture->unref();
+ }
+ }
+}
+
+void GrContext::purgeCache() {
+ if (fResourceCache) {
+ fResourceCache->purgeAsNeeded();
+ }
}
bool GrContext::OverbudgetCB(void* data) {
diff --git a/src/gpu/GrLayerCache.cpp b/src/gpu/GrLayerCache.cpp
index 658121ec73..6ec07580c8 100644
--- a/src/gpu/GrLayerCache.cpp
+++ b/src/gpu/GrLayerCache.cpp
@@ -245,6 +245,7 @@ void GrLayerCache::unlock(GrCachedLayer* layer) {
#endif
} else {
+ fContext->unlockScratchTexture(layer->texture());
layer->setTexture(NULL, GrIRect16::MakeEmpty());
}
diff --git a/src/gpu/GrResourceCache.cpp b/src/gpu/GrResourceCache.cpp
index 12e959f198..e9be509569 100644
--- a/src/gpu/GrResourceCache.cpp
+++ b/src/gpu/GrResourceCache.cpp
@@ -82,10 +82,14 @@ GrResourceCache::GrResourceCache(int maxCount, size_t maxBytes) :
#if GR_CACHE_STATS
fHighWaterEntryCount = 0;
fHighWaterEntryBytes = 0;
+ fHighWaterClientDetachedCount = 0;
+ fHighWaterClientDetachedBytes = 0;
#endif
fEntryCount = 0;
fEntryBytes = 0;
+ fClientDetachedCount = 0;
+ fClientDetachedBytes = 0;
fPurging = false;
@@ -132,26 +136,55 @@ void GrResourceCache::setLimits(int maxResources, size_t maxResourceBytes) {
}
}
-void GrResourceCache::internalDetach(GrResourceCacheEntry* entry) {
+void GrResourceCache::internalDetach(GrResourceCacheEntry* entry,
+ BudgetBehaviors behavior) {
fList.remove(entry);
- fEntryCount -= 1;
- fEntryBytes -= entry->fCachedSize;
+
+ // update our stats
+ if (kIgnore_BudgetBehavior == behavior) {
+ fClientDetachedCount += 1;
+ fClientDetachedBytes += entry->fCachedSize;
+
+#if GR_CACHE_STATS
+ if (fHighWaterClientDetachedCount < fClientDetachedCount) {
+ fHighWaterClientDetachedCount = fClientDetachedCount;
+ }
+ if (fHighWaterClientDetachedBytes < fClientDetachedBytes) {
+ fHighWaterClientDetachedBytes = fClientDetachedBytes;
+ }
+#endif
+
+ } else {
+ SkASSERT(kAccountFor_BudgetBehavior == behavior);
+
+ fEntryCount -= 1;
+ fEntryBytes -= entry->fCachedSize;
+ }
}
-void GrResourceCache::attachToHead(GrResourceCacheEntry* entry) {
+void GrResourceCache::attachToHead(GrResourceCacheEntry* entry,
+ BudgetBehaviors behavior) {
fList.addToHead(entry);
- fEntryCount += 1;
- fEntryBytes += entry->fCachedSize;
+ // update our stats
+ if (kIgnore_BudgetBehavior == behavior) {
+ fClientDetachedCount -= 1;
+ fClientDetachedBytes -= entry->fCachedSize;
+ } else {
+ SkASSERT(kAccountFor_BudgetBehavior == behavior);
+
+ fEntryCount += 1;
+ fEntryBytes += entry->fCachedSize;
#if GR_CACHE_STATS
- if (fHighWaterEntryCount < fEntryCount) {
- fHighWaterEntryCount = fEntryCount;
- }
- if (fHighWaterEntryBytes < fEntryBytes) {
- fHighWaterEntryBytes = fEntryBytes;
- }
+ if (fHighWaterEntryCount < fEntryCount) {
+ fHighWaterEntryCount = fEntryCount;
+ }
+ if (fHighWaterEntryBytes < fEntryBytes) {
+ fHighWaterEntryBytes = fEntryBytes;
+ }
#endif
+ }
}
// This functor just searches for an entry with only a single ref (from
@@ -160,40 +193,41 @@ void GrResourceCache::attachToHead(GrResourceCacheEntry* entry) {
class GrTFindUnreffedFunctor {
public:
bool operator()(const GrResourceCacheEntry* entry) const {
- return entry->resource()->isPurgable();
+ return entry->resource()->unique();
}
};
-
-void GrResourceCache::makeResourceMRU(GrGpuResource* resource) {
- GrResourceCacheEntry* entry = resource->getCacheEntry();
- if (entry) {
- this->internalDetach(entry);
- this->attachToHead(entry);
- }
-}
-
-GrGpuResource* GrResourceCache::find(const GrResourceKey& key) {
+GrGpuResource* GrResourceCache::find(const GrResourceKey& key, uint32_t ownershipFlags) {
GrAutoResourceCacheValidate atcv(this);
GrResourceCacheEntry* entry = NULL;
- entry = fCache.find(key);
+ if (ownershipFlags & kNoOtherOwners_OwnershipFlag) {
+ GrTFindUnreffedFunctor functor;
+
+ entry = fCache.find<GrTFindUnreffedFunctor>(key, functor);
+ } else {
+ entry = fCache.find(key);
+ }
if (NULL == entry) {
return NULL;
}
- // Make this resource MRU
- this->internalDetach(entry);
- this->attachToHead(entry);
+ if (ownershipFlags & kHide_OwnershipFlag) {
+ this->makeExclusive(entry);
+ } else {
+ // Make this resource MRU
+ this->internalDetach(entry);
+ this->attachToHead(entry);
+ }
- // GrResourceCache2 is responsible for scratch resources.
- SkASSERT(GrIORef::kNo_IsScratch == entry->resource()->fIsScratch);
return entry->fResource;
}
-void GrResourceCache::addResource(const GrResourceKey& key, GrGpuResource* resource) {
+void GrResourceCache::addResource(const GrResourceKey& key,
+ GrGpuResource* resource,
+ uint32_t ownershipFlags) {
SkASSERT(NULL == resource->getCacheEntry());
// we don't expect to create new resources during a purge. In theory
// this could cause purgeAsNeeded() into an infinite loop (e.g.
@@ -207,15 +241,77 @@ void GrResourceCache::addResource(const GrResourceKey& key, GrGpuResource* resou
this->attachToHead(entry);
fCache.insert(key, entry);
+
+ if (ownershipFlags & kHide_OwnershipFlag) {
+ this->makeExclusive(entry);
+ }
+
+}
+
+void GrResourceCache::makeExclusive(GrResourceCacheEntry* entry) {
+ GrAutoResourceCacheValidate atcv(this);
+
+ SkASSERT(!entry->fIsExclusive);
+ entry->fIsExclusive = true;
+
+ // When scratch textures are detached (to hide them from future finds) they
+ // still count against the resource budget
+ this->internalDetach(entry, kIgnore_BudgetBehavior);
+ fCache.remove(entry->key(), entry);
+
+#ifdef SK_DEBUG
+ fExclusiveList.addToHead(entry);
+#endif
+}
+
+void GrResourceCache::removeInvalidResource(GrResourceCacheEntry* entry) {
+ // If the resource went invalid while it was detached then purge it
+ // This can happen when a 3D context was lost,
+ // the client called GrContext::abandonContext() to notify Gr,
+ // and then later an SkGpuDevice's destructor releases its backing
+ // texture (which was invalidated at contextDestroyed time).
+ // TODO: Safely delete the GrResourceCacheEntry as well.
+ fClientDetachedCount -= 1;
+ fEntryCount -= 1;
+ fClientDetachedBytes -= entry->fCachedSize;
+ fEntryBytes -= entry->fCachedSize;
+ entry->fCachedSize = 0;
+}
+
+void GrResourceCache::makeNonExclusive(GrResourceCacheEntry* entry) {
+ GrAutoResourceCacheValidate atcv(this);
+
+#ifdef SK_DEBUG
+ fExclusiveList.remove(entry);
+#endif
+
+ if (!entry->resource()->wasDestroyed()) {
+ // Since scratch textures still count against the cache budget even
+ // when they have been removed from the cache, re-adding them doesn't
+ // alter the budget information.
+ attachToHead(entry, kIgnore_BudgetBehavior);
+ fCache.insert(entry->key(), entry);
+
+ SkASSERT(entry->fIsExclusive);
+ entry->fIsExclusive = false;
+ } else {
+ this->removeInvalidResource(entry);
+ }
}
void GrResourceCache::didIncreaseResourceSize(const GrResourceCacheEntry* entry, size_t amountInc) {
fEntryBytes += amountInc;
+ if (entry->fIsExclusive) {
+ fClientDetachedBytes += amountInc;
+ }
this->purgeAsNeeded();
}
void GrResourceCache::didDecreaseResourceSize(const GrResourceCacheEntry* entry, size_t amountDec) {
fEntryBytes -= amountDec;
+ if (entry->fIsExclusive) {
+ fClientDetachedBytes -= amountDec;
+ }
#ifdef SK_DEBUG
this->validate();
#endif
@@ -263,6 +359,13 @@ void GrResourceCache::purgeInvalidated() {
fInvalidationInbox.poll(&invalidated);
for (int i = 0; i < invalidated.count(); i++) {
+ // We're somewhat missing an opportunity here. We could use the
+ // default find functor that gives us back resources whether we own
+ // them exclusively or not, and when they're not exclusively owned mark
+ // them for purging later when they do become exclusively owned.
+ //
+ // This is complicated and confusing. May try this in the future. For
+ // now, these resources are just LRU'd as if we never got the message.
while (GrResourceCacheEntry* entry = fCache.find(invalidated[i].key, GrTFindUnreffedFunctor())) {
this->deleteResource(entry);
}
@@ -270,7 +373,7 @@ void GrResourceCache::purgeInvalidated() {
}
void GrResourceCache::deleteResource(GrResourceCacheEntry* entry) {
- SkASSERT(entry->fResource->isPurgable());
+ SkASSERT(entry->fResource->unique());
// remove from our cache
fCache.remove(entry->key(), entry);
@@ -309,7 +412,7 @@ void GrResourceCache::internalPurge(int extraCount, size_t extraBytes) {
}
GrResourceCacheEntry* prev = iter.prev();
- if (entry->fResource->isPurgable()) {
+ if (entry->fResource->unique()) {
changed = true;
this->deleteResource(entry);
}
@@ -332,7 +435,14 @@ void GrResourceCache::purgeAllUnlocked() {
this->purgeAsNeeded();
#ifdef SK_DEBUG
+ SkASSERT(fExclusiveList.countEntries() == fClientDetachedCount);
+ SkASSERT(countBytes(fExclusiveList) == fClientDetachedBytes);
if (!fCache.count()) {
+ // Items may have been detached from the cache (such as the backing
+ // texture for an SkGpuDevice). The above purge would not have removed
+ // them.
+ SkASSERT(fEntryCount == fClientDetachedCount);
+ SkASSERT(fEntryBytes == fClientDetachedBytes);
SkASSERT(fList.isEmpty());
}
#endif
@@ -364,26 +474,43 @@ static bool both_zero_or_nonzero(int count, size_t bytes) {
void GrResourceCache::validate() const {
fList.validate();
+ fExclusiveList.validate();
SkASSERT(both_zero_or_nonzero(fEntryCount, fEntryBytes));
- SkASSERT(fEntryCount == fCache.count());
+ SkASSERT(both_zero_or_nonzero(fClientDetachedCount, fClientDetachedBytes));
+ SkASSERT(fClientDetachedBytes <= fEntryBytes);
+ SkASSERT(fClientDetachedCount <= fEntryCount);
+ SkASSERT((fEntryCount - fClientDetachedCount) == fCache.count());
EntryList::Iter iter;
- // check that the shareable entries are okay
- const GrResourceCacheEntry* entry = iter.init(const_cast<EntryList&>(fList),
+ // check that the exclusively held entries are okay
+ const GrResourceCacheEntry* entry = iter.init(const_cast<EntryList&>(fExclusiveList),
EntryList::Iter::kHead_IterStart);
+ for ( ; entry; entry = iter.next()) {
+ entry->validate();
+ }
+
+ // check that the shareable entries are okay
+ entry = iter.init(const_cast<EntryList&>(fList), EntryList::Iter::kHead_IterStart);
+
int count = 0;
for ( ; entry; entry = iter.next()) {
entry->validate();
SkASSERT(fCache.find(entry->key()));
count += 1;
}
- SkASSERT(count == fEntryCount);
+ SkASSERT(count == fEntryCount - fClientDetachedCount);
+
+ size_t bytes = countBytes(fList);
+ SkASSERT(bytes == fEntryBytes - fClientDetachedBytes);
+
+ bytes = countBytes(fExclusiveList);
+ SkASSERT(bytes == fClientDetachedBytes);
+
+ SkASSERT(fList.countEntries() == fEntryCount - fClientDetachedCount);
- size_t bytes = this->countBytes(fList);
- SkASSERT(bytes == fEntryBytes);
- SkASSERT(fList.countEntries() == fEntryCount);
+ SkASSERT(fExclusiveList.countEntries() == fClientDetachedCount);
}
#endif // SK_DEBUG
@@ -407,6 +534,10 @@ void GrResourceCache::printStats() {
fEntryCount, locked, fHighWaterEntryCount);
SkDebugf("\t\tEntry Bytes: current %d high %d\n",
fEntryBytes, fHighWaterEntryBytes);
+ SkDebugf("\t\tDetached Entry Count: current %d high %d\n",
+ fClientDetachedCount, fHighWaterClientDetachedCount);
+ SkDebugf("\t\tDetached Bytes: current %d high %d\n",
+ fClientDetachedBytes, fHighWaterClientDetachedBytes);
}
#endif
diff --git a/src/gpu/GrResourceCache.h b/src/gpu/GrResourceCache.h
index 880a0a98b5..83337805a4 100644
--- a/src/gpu/GrResourceCache.h
+++ b/src/gpu/GrResourceCache.h
@@ -141,13 +141,26 @@ public:
*/
int getCachedResourceCount() const { return fEntryCount; }
+ // For a found or added resource to be completely exclusive to the caller
+ // both the kNoOtherOwners and kHide flags need to be specified
+ enum OwnershipFlags {
+ kNoOtherOwners_OwnershipFlag = 0x1, // found/added resource has no other owners
+ kHide_OwnershipFlag = 0x2 // found/added resource is hidden from future 'find's
+ };
+
/**
* Search for an entry with the same Key. If found, return it.
* If not found, return null.
+ * If ownershipFlags includes kNoOtherOwners and a resource is returned
+ * then that resource has no other refs to it.
+ * If ownershipFlags includes kHide and a resource is returned then that
+ * resource will not be returned from future 'find' calls until it is
+ * 'freed' (and recycled) or makeNonExclusive is called.
+ * For a resource to be completely exclusive to a caller both kNoOtherOwners
+ * and kHide must be specified.
*/
- GrGpuResource* find(const GrResourceKey& key);
-
- void makeResourceMRU(GrGpuResource*);
+ GrGpuResource* find(const GrResourceKey& key,
+ uint32_t ownershipFlags = 0);
/**
* Add the new resource to the cache (by creating a new cache entry based
@@ -155,8 +168,14 @@ public:
*
* Ownership of the resource is transferred to the resource cache,
* which will unref() it when it is purged or deleted.
+ *
+ * If ownershipFlags includes kHide, subsequent calls to 'find' will not
+ * return 'resource' until it is 'freed' (and recycled) or makeNonExclusive
+ * is called.
*/
- void addResource(const GrResourceKey& key, GrGpuResource* resource);
+ void addResource(const GrResourceKey& key,
+ GrGpuResource* resource,
+ uint32_t ownershipFlags = 0);
/**
* Determines if the cache contains an entry matching a key. If a matching
@@ -165,6 +184,20 @@ public:
bool hasKey(const GrResourceKey& key) const { return SkToBool(fCache.find(key)); }
/**
+ * Hide 'entry' so that future searches will not find it. Such
+ * hidden entries will not be purged. The entry still counts against
+ * the cache's budget and should be made non-exclusive when exclusive access
+ * is no longer needed.
+ */
+ void makeExclusive(GrResourceCacheEntry* entry);
+
+ /**
+ * Restore 'entry' so that it can be found by future searches. 'entry'
+ * will also be purgeable (provided its lock count is now 0.)
+ */
+ void makeNonExclusive(GrResourceCacheEntry* entry);
+
+ /**
* Notify the cache that the size of a resource has changed.
*/
void didIncreaseResourceSize(const GrResourceCacheEntry*, size_t amountInc);
@@ -204,8 +237,15 @@ public:
#endif
private:
- void internalDetach(GrResourceCacheEntry*);
- void attachToHead(GrResourceCacheEntry*);
+ enum BudgetBehaviors {
+ kAccountFor_BudgetBehavior,
+ kIgnore_BudgetBehavior
+ };
+
+ void internalDetach(GrResourceCacheEntry*, BudgetBehaviors behavior = kAccountFor_BudgetBehavior);
+ void attachToHead(GrResourceCacheEntry*, BudgetBehaviors behavior = kAccountFor_BudgetBehavior);
+
+ void removeInvalidResource(GrResourceCacheEntry* entry);
SkTMultiMap<GrResourceCacheEntry, GrResourceKey> fCache;
@@ -213,6 +253,11 @@ private:
typedef SkTInternalLList<GrResourceCacheEntry> EntryList;
EntryList fList;
+#ifdef SK_DEBUG
+ // These objects cannot be returned by a search
+ EntryList fExclusiveList;
+#endif
+
// our budget, used in purgeAsNeeded()
int fMaxCount;
size_t fMaxBytes;
@@ -221,10 +266,14 @@ private:
#if GR_CACHE_STATS
int fHighWaterEntryCount;
size_t fHighWaterEntryBytes;
+ int fHighWaterClientDetachedCount;
+ size_t fHighWaterClientDetachedBytes;
#endif
int fEntryCount;
size_t fEntryBytes;
+ int fClientDetachedCount;
+ size_t fClientDetachedBytes;
// prevents recursive purging
bool fPurging;
diff --git a/src/gpu/GrResourceCache2.cpp b/src/gpu/GrResourceCache2.cpp
index e2322a684e..e0ba26ae83 100644
--- a/src/gpu/GrResourceCache2.cpp
+++ b/src/gpu/GrResourceCache2.cpp
@@ -8,8 +8,7 @@
#include "GrResourceCache2.h"
-#include "GrGpuResource.h"
-#include "SkRefCnt.h"
+#include "GrGpuResource.h"
GrResourceCache2::~GrResourceCache2() {
this->releaseAll();
@@ -56,18 +55,3 @@ void GrResourceCache2::releaseAll() {
SkASSERT(!fScratchMap.count());
SkASSERT(!fCount);
}
-
-class GrResourceCache2::AvailableForScratchUse {
-public:
- bool operator()(const GrGpuResource* resource) const {
- // Because duties are currently shared between GrResourceCache and GrResourceCache2, the
- // current interpretation of this rule is that only GrResourceCache has a ref but that
- // it has been marked as a scratch resource.
- return resource->reffedOnlyByCache() && GrIORef::kYes_IsScratch == resource->fIsScratch;
- }
-};
-
-GrGpuResource* GrResourceCache2::findAndRefScratchResource(const GrResourceKey& scratchKey) {
- SkASSERT(scratchKey.isScratch());
- return SkSafeRef(fScratchMap.find(scratchKey, AvailableForScratchUse()));
-}
diff --git a/src/gpu/GrResourceCache2.h b/src/gpu/GrResourceCache2.h
index da088a6ead..e05efd744e 100644
--- a/src/gpu/GrResourceCache2.h
+++ b/src/gpu/GrResourceCache2.h
@@ -24,16 +24,14 @@ public:
GrResourceCache2() : fCount(0) {};
~GrResourceCache2();
- void insertResource(GrGpuResource*);
+ void insertResource(GrGpuResource* resource);
- void removeResource(GrGpuResource*);
+ void removeResource(GrGpuResource* resource);
void abandonAll();
void releaseAll();
- GrGpuResource* findAndRefScratchResource(const GrResourceKey& scratchKey);
-
private:
#ifdef SK_DEBUG
bool isInCache(const GrGpuResource* r) const {
@@ -41,8 +39,8 @@ private:
}
#endif
- class AvailableForScratchUse;
+ void removeScratch(const GrGpuResource* resource);
struct ScratchMapTraits {
static const GrResourceKey& GetKey(const GrGpuResource& r) {
return r.getScratchKey();
diff --git a/src/gpu/GrTexture.cpp b/src/gpu/GrTexture.cpp
index 36ed8232d7..e1188f961b 100644
--- a/src/gpu/GrTexture.cpp
+++ b/src/gpu/GrTexture.cpp
@@ -21,6 +21,27 @@ GrTexture::~GrTexture() {
}
}
+/**
+ * This method allows us to interrupt the normal deletion process and place
+ * textures back in the texture cache when their ref count goes to zero.
+ */
+void GrTexture::internal_dispose() const {
+ if (this->texturePriv().isSetFlag((GrTextureFlags) GrTexture::kReturnToCache_FlagBit) &&
+ this->INHERITED::getContext()) {
+ GrTexture* nonConstThis = const_cast<GrTexture *>(this);
+ this->ref(); // restore ref count to initial setting
+
+ nonConstThis->texturePriv().resetFlag((GrTextureFlags) kReturnToCache_FlagBit);
+ nonConstThis->INHERITED::getContext()->addExistingTextureToCache(nonConstThis);
+
+ // Note: "this" texture might be freed inside addExistingTextureToCache
+ // if it is purged.
+ return;
+ }
+
+ this->INHERITED::internal_dispose();
+}
+
void GrTexture::dirtyMipMaps(bool mipMapsDirty) {
if (mipMapsDirty) {
if (kValid_MipMapsStatus == fMipMapsStatus) {
@@ -81,12 +102,27 @@ void GrTexture::writePixels(int left, int top, int width, int height,
pixelOpsFlags);
}
+void GrTexture::abandonReleaseCommon() {
+ // In debug builds the resource cache tracks removed/exclusive textures and has an unref'ed ptr.
+ // After abandon() or release() the resource cache will be unreachable (getContext() == NULL).
+ // So we readd the texture to the cache here so that it is removed from the exclusive list and
+ // there is no longer an unref'ed ptr to the texture in the cache.
+ if (this->texturePriv().isSetFlag((GrTextureFlags)kReturnToCache_FlagBit)) {
+ SkASSERT(!this->wasDestroyed());
+ this->ref(); // restores the ref the resource cache gave up when it marked this exclusive.
+ this->texturePriv().resetFlag((GrTextureFlags) kReturnToCache_FlagBit);
+ this->getContext()->addExistingTextureToCache(this);
+ }
+}
+
void GrTexture::onRelease() {
+ this->abandonReleaseCommon();
SkASSERT(!this->texturePriv().isSetFlag((GrTextureFlags) kReturnToCache_FlagBit));
INHERITED::onRelease();
}
void GrTexture::onAbandon() {
+ this->abandonReleaseCommon();
if (fRenderTarget.get()) {
fRenderTarget->abandon();
}
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 888e97ac5a..792ce6d30d 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -76,37 +76,50 @@ enum { kDefaultImageFilterCacheSize = 32 * 1024 * 1024 };
///////////////////////////////////////////////////////////////////////////////
-// Helper for turning a bitmap into a texture. If the bitmap is GrTexture backed this
-// just accesses the backing GrTexture. Otherwise, it creates a cached texture
-// representation and releases it in the destructor.
-class AutoBitmapTexture : public SkNoncopyable {
+
+class SkGpuDevice::SkAutoCachedTexture : public ::SkNoncopyable {
public:
- AutoBitmapTexture() {}
+ SkAutoCachedTexture()
+ : fDevice(NULL)
+ , fTexture(NULL) {
+ }
- AutoBitmapTexture(GrContext* context,
- const SkBitmap& bitmap,
- const GrTextureParams* params,
- GrTexture** texture) {
+ SkAutoCachedTexture(SkGpuDevice* device,
+ const SkBitmap& bitmap,
+ const GrTextureParams* params,
+ GrTexture** texture)
+ : fDevice(NULL)
+ , fTexture(NULL) {
SkASSERT(texture);
- *texture = this->set(context, bitmap, params);
+ *texture = this->set(device, bitmap, params);
}
- GrTexture* set(GrContext* context,
+ ~SkAutoCachedTexture() {
+ if (fTexture) {
+ GrUnlockAndUnrefCachedBitmapTexture(fTexture);
+ }
+ }
+
+ GrTexture* set(SkGpuDevice* device,
const SkBitmap& bitmap,
const GrTextureParams* params) {
- // Either get the texture directly from the bitmap, or else use the cache and
- // remember to unref it.
- if (GrTexture* bmpTexture = bitmap.getTexture()) {
- fTexture.reset(NULL);
- return bmpTexture;
- } else {
- fTexture.reset(GrRefCachedBitmapTexture(context, bitmap, params));
- return fTexture.get();
+ if (fTexture) {
+ GrUnlockAndUnrefCachedBitmapTexture(fTexture);
+ fTexture = NULL;
+ }
+ fDevice = device;
+ GrTexture* result = (GrTexture*)bitmap.getTexture();
+ if (NULL == result) {
+ // Cannot return the native texture so look it up in our cache
+ fTexture = GrLockAndRefCachedBitmapTexture(device->context(), bitmap, params);
+ result = fTexture;
}
+ return result;
}
private:
- SkAutoTUnref<GrTexture> fTexture;
+ SkGpuDevice* fDevice;
+ GrTexture* fTexture;
};
///////////////////////////////////////////////////////////////////////////////
@@ -139,7 +152,8 @@ SkGpuDevice::SkGpuDevice(GrSurface* surface, const SkSurfaceProps& props, unsign
fRenderTarget = SkRef(surface->asRenderTarget());
SkImageInfo info = surface->surfacePriv().info();
- SkPixelRef* pr = SkNEW_ARGS(SkGrPixelRef, (info, surface));
+ SkPixelRef* pr = SkNEW_ARGS(SkGrPixelRef,
+ (info, surface, SkToBool(flags & kCached_Flag)));
fLegacyBitmap.setInfo(info);
fLegacyBitmap.setPixelRef(pr)->unref();
@@ -1280,7 +1294,7 @@ void SkGpuDevice::internalDrawBitmap(const SkBitmap& bitmap,
bitmap.height() <= fContext->getMaxTextureSize());
GrTexture* texture;
- AutoBitmapTexture abt(fContext, bitmap, &params, &texture);
+ SkAutoCachedTexture act(this, bitmap, &params, &texture);
if (NULL == texture) {
return;
}
@@ -1375,7 +1389,7 @@ void SkGpuDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
GrTexture* texture;
// draw sprite uses the default texture params
- AutoBitmapTexture abt(fContext, bitmap, NULL, &texture);
+ SkAutoCachedTexture act(this, bitmap, NULL, &texture);
SkImageFilter* filter = paint.getImageFilter();
// This bitmap will own the filtered result as a texture.
@@ -1552,7 +1566,7 @@ bool SkGpuDevice::filterImage(const SkImageFilter* filter, const SkBitmap& src,
GrTexture* texture;
// We assume here that the filter will not attempt to tile the src. Otherwise, this cache lookup
// must be pushed upstack.
- AutoBitmapTexture abt(fContext, src, NULL, &texture);
+ SkAutoCachedTexture act(this, src, NULL, &texture);
return filter_texture(this, fContext, texture, filter, src.width(), src.height(), ctx,
result, offset);
@@ -1783,6 +1797,7 @@ SkBaseDevice* SkGpuDevice::onCreateDevice(const SkImageInfo& info, Usage usage)
#if CACHE_COMPATIBLE_DEVICE_TEXTURES
// layers are never draw in repeat modes, so we can request an approx
// match and ignore any padding.
+ flags |= kCached_Flag;
const GrContext::ScratchTexMatch match = (kSaveLayer_Usage == usage) ?
GrContext::kApprox_ScratchTexMatch :
GrContext::kExact_ScratchTexMatch;
diff --git a/src/gpu/SkGpuDevice.h b/src/gpu/SkGpuDevice.h
index fe03e32f73..285a73745c 100644
--- a/src/gpu/SkGpuDevice.h
+++ b/src/gpu/SkGpuDevice.h
@@ -34,7 +34,8 @@ class SK_API SkGpuDevice : public SkBaseDevice {
public:
enum Flags {
kNeedClear_Flag = 1 << 0, //!< Surface requires an initial clear
- kDFFonts_Flag = 1 << 1, //!< Surface should render fonts using signed distance fields
+ kCached_Flag = 1 << 1, //!< Surface is cached and needs to be unlocked when released
+ kDFFonts_Flag = 1 << 2, //!< Surface should render fonts using signed distance fields
};
/**
@@ -116,6 +117,9 @@ public:
const SkImageFilter::Context&,
SkBitmap*, SkIPoint*) SK_OVERRIDE;
+ class SkAutoCachedTexture; // used internally
+
+
protected:
virtual bool onReadPixels(const SkImageInfo&, void*, size_t, int, int) SK_OVERRIDE;
virtual bool onWritePixels(const SkImageInfo&, const void*, size_t, int, int) SK_OVERRIDE;
diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp
index d9435459f1..e81abdbae4 100644
--- a/src/gpu/SkGr.cpp
+++ b/src/gpu/SkGr.cpp
@@ -369,9 +369,9 @@ bool GrIsBitmapInCache(const GrContext* ctx,
return ctx->isTextureInCache(desc, cacheID, params);
}
-GrTexture* GrRefCachedBitmapTexture(GrContext* ctx,
- const SkBitmap& bitmap,
- const GrTextureParams* params) {
+GrTexture* GrLockAndRefCachedBitmapTexture(GrContext* ctx,
+ const SkBitmap& bitmap,
+ const GrTextureParams* params) {
GrTexture* result = NULL;
bool cache = !bitmap.isVolatile();
@@ -397,6 +397,13 @@ GrTexture* GrRefCachedBitmapTexture(GrContext* ctx,
return result;
}
+void GrUnlockAndUnrefCachedBitmapTexture(GrTexture* texture) {
+ SkASSERT(texture->getContext());
+
+ texture->getContext()->unlockScratchTexture(texture);
+ texture->unref();
+}
+
///////////////////////////////////////////////////////////////////////////////
// alphatype is ignore for now, but if GrPixelConfig is expanded to encompass
diff --git a/src/gpu/SkGrPixelRef.cpp b/src/gpu/SkGrPixelRef.cpp
index fed07c4335..fcf22e350b 100644
--- a/src/gpu/SkGrPixelRef.cpp
+++ b/src/gpu/SkGrPixelRef.cpp
@@ -101,7 +101,8 @@ static SkGrPixelRef* copy_to_new_texture_pixelref(GrTexture* texture, SkColorTyp
///////////////////////////////////////////////////////////////////////////////
-SkGrPixelRef::SkGrPixelRef(const SkImageInfo& info, GrSurface* surface) : INHERITED(info) {
+SkGrPixelRef::SkGrPixelRef(const SkImageInfo& info, GrSurface* surface,
+ bool transferCacheLock) : INHERITED(info) {
// For surfaces that are both textures and render targets, the texture owns the
// render target but not vice versa. So we ref the texture to keep both alive for
// the lifetime of this pixel ref.
@@ -109,6 +110,7 @@ SkGrPixelRef::SkGrPixelRef(const SkImageInfo& info, GrSurface* surface) : INHERI
if (NULL == fSurface) {
fSurface = SkSafeRef(surface);
}
+ fUnlock = transferCacheLock;
if (fSurface) {
SkASSERT(info.width() <= fSurface->width());
@@ -117,6 +119,13 @@ SkGrPixelRef::SkGrPixelRef(const SkImageInfo& info, GrSurface* surface) : INHERI
}
SkGrPixelRef::~SkGrPixelRef() {
+ if (fUnlock) {
+ GrContext* context = fSurface->getContext();
+ GrTexture* texture = fSurface->asTexture();
+ if (context && texture) {
+ context->unlockScratchTexture(texture);
+ }
+ }
SkSafeUnref(fSurface);
}
diff --git a/src/gpu/effects/GrTextureStripAtlas.cpp b/src/gpu/effects/GrTextureStripAtlas.cpp
index 9755ccd2af..91df897eb8 100644
--- a/src/gpu/effects/GrTextureStripAtlas.cpp
+++ b/src/gpu/effects/GrTextureStripAtlas.cpp
@@ -216,6 +216,7 @@ void GrTextureStripAtlas::unlockTexture() {
SkASSERT(fTexture && 0 == fLockedRows);
fTexture->unref();
fTexture = NULL;
+ fDesc.fContext->purgeCache();
}
void GrTextureStripAtlas::initLRU() {
diff --git a/src/image/SkSurface_Gpu.cpp b/src/image/SkSurface_Gpu.cpp
index 76456d0a5c..024c151cea 100644
--- a/src/image/SkSurface_Gpu.cpp
+++ b/src/image/SkSurface_Gpu.cpp
@@ -14,7 +14,7 @@ class SkSurface_Gpu : public SkSurface_Base {
public:
SK_DECLARE_INST_COUNT(SkSurface_Gpu)
- SkSurface_Gpu(GrRenderTarget*, const SkSurfaceProps*, bool doClear);
+ SkSurface_Gpu(GrRenderTarget*, bool cached, const SkSurfaceProps*, bool doClear);
virtual ~SkSurface_Gpu();
virtual SkCanvas* onNewCanvas() SK_OVERRIDE;
@@ -33,10 +33,12 @@ private:
///////////////////////////////////////////////////////////////////////////////
-SkSurface_Gpu::SkSurface_Gpu(GrRenderTarget* renderTarget, const SkSurfaceProps* props, bool doClear)
+SkSurface_Gpu::SkSurface_Gpu(GrRenderTarget* renderTarget, bool cached, const SkSurfaceProps* props,
+ bool doClear)
: INHERITED(renderTarget->width(), renderTarget->height(), props)
{
int deviceFlags = 0;
+ deviceFlags |= cached ? SkGpuDevice::kCached_Flag : 0;
deviceFlags |= this->props().isUseDistanceFieldFonts() ? SkGpuDevice::kDFFonts_Flag : 0;
fDevice = SkGpuDevice::Create(renderTarget, this->props(), deviceFlags);
@@ -109,7 +111,7 @@ SkSurface* SkSurface::NewRenderTargetDirect(GrRenderTarget* target, const SkSurf
if (NULL == target) {
return NULL;
}
- return SkNEW_ARGS(SkSurface_Gpu, (target, props, false));
+ return SkNEW_ARGS(SkSurface_Gpu, (target, false, props, false));
}
SkSurface* SkSurface::NewRenderTarget(GrContext* ctx, const SkImageInfo& info, int sampleCount,
@@ -130,7 +132,7 @@ SkSurface* SkSurface::NewRenderTarget(GrContext* ctx, const SkImageInfo& info, i
return NULL;
}
- return SkNEW_ARGS(SkSurface_Gpu, (tex->asRenderTarget(), props, true));
+ return SkNEW_ARGS(SkSurface_Gpu, (tex->asRenderTarget(), false, props, true));
}
SkSurface* SkSurface::NewScratchRenderTarget(GrContext* ctx, const SkImageInfo& info,
@@ -152,5 +154,5 @@ SkSurface* SkSurface::NewScratchRenderTarget(GrContext* ctx, const SkImageInfo&
return NULL;
}
- return SkNEW_ARGS(SkSurface_Gpu, (tex->asRenderTarget(), props, true));
+ return SkNEW_ARGS(SkSurface_Gpu, (tex->asRenderTarget(), true, props, true));
}
diff --git a/tests/ResourceCacheTest.cpp b/tests/ResourceCacheTest.cpp
index d62d09d933..f9f94d1b27 100644
--- a/tests/ResourceCacheTest.cpp
+++ b/tests/ResourceCacheTest.cpp
@@ -247,6 +247,37 @@ static void test_resource_size_changed(skiatest::Reporter* reporter,
REPORTER_ASSERT(reporter, 201 == cache.getCachedResourceBytes());
REPORTER_ASSERT(reporter, 1 == cache.getCachedResourceCount());
}
+
+ // Test changing the size of an exclusively-held resource.
+ {
+ GrResourceCache cache(2, 300);
+
+ TestResource* a = new TestResource(context->getGpu(), 100);
+ cache.addResource(key1, a);
+ cache.makeExclusive(a->getCacheEntry());
+
+ TestResource* b = new TestResource(context->getGpu(), 100);
+ cache.addResource(key2, b);
+ b->unref();
+
+ REPORTER_ASSERT(reporter, 200 == cache.getCachedResourceBytes());
+ REPORTER_ASSERT(reporter, 2 == cache.getCachedResourceCount());
+ REPORTER_ASSERT(reporter, NULL == cache.find(key1));
+
+ a->setSize(200);
+
+ REPORTER_ASSERT(reporter, 300 == cache.getCachedResourceBytes());
+ REPORTER_ASSERT(reporter, 2 == cache.getCachedResourceCount());
+ // Internal resource cache validation will test the detached size (debug mode only).
+
+ cache.makeNonExclusive(a->getCacheEntry());
+ a->unref();
+
+ REPORTER_ASSERT(reporter, 300 == cache.getCachedResourceBytes());
+ REPORTER_ASSERT(reporter, 2 == cache.getCachedResourceCount());
+ REPORTER_ASSERT(reporter, cache.find(key1));
+ // Internal resource cache validation will test the detached size (debug mode only).
+ }
}
////////////////////////////////////////////////////////////////////////////////