Picture shader resource caching.

Replace the current/naive shader caching mechanism with a more general
implementation based on SkResourceCache.

Caching the bitmap shader itself (as opposed to just the tile bitmap)
makes for a chunkier key, but OTOH avoids allocating new shaders on
cache hit.

R=reed@google.com,mtklein@google.com

Review URL: https://codereview.chromium.org/671683004
This commit is contained in:
fmalita 2014-10-22 07:39:08 -07:00 committed by Commit bot
parent 02b4725cb8
commit 23df2d6933
2 changed files with 133 additions and 18 deletions

View File

@ -10,14 +10,130 @@
#include "SkBitmap.h"
#include "SkBitmapProcShader.h"
#include "SkCanvas.h"
#include "SkDiscardableMemory.h"
#include "SkMatrixUtils.h"
#include "SkPicture.h"
#include "SkReadBuffer.h"
#include "SkResourceCache.h"
#include "SkThread.h"
#if SK_SUPPORT_GPU
#include "GrContext.h"
#endif
struct BitmapShaderKey : public SkResourceCache::Key {
public:
BitmapShaderKey(uint32_t pictureID,
const SkRect& tile,
SkShader::TileMode tmx,
SkShader::TileMode tmy,
const SkSize& scale,
const SkMatrix& localMatrix)
: fPictureID(pictureID)
, fTile(tile)
, fTmx(tmx)
, fTmy(tmy)
, fScale(scale)
, fLocalMatrix(localMatrix) {
static const size_t keySize = sizeof(fPictureID) +
sizeof(fTile) +
sizeof(fTmx) + sizeof(fTmy) +
sizeof(fScale) +
sizeof(fLocalMatrix);
// This better be packed.
SkASSERT(sizeof(uint32_t) * (&fEndOfStruct - &fPictureID) == keySize);
this->init(keySize);
}
private:
uint32_t fPictureID;
SkRect fTile;
SkShader::TileMode fTmx, fTmy;
SkSize fScale;
SkMatrix fLocalMatrix;
SkDEBUGCODE(uint32_t fEndOfStruct;)
};
struct BitmapShaderRec : public SkResourceCache::Rec {
BitmapShaderRec(const BitmapShaderKey& key, SkShader* tileShader, size_t bitmapBytes)
: fKey(key)
, fShader(SkRef(tileShader))
, fBitmapBytes(bitmapBytes) {}
BitmapShaderKey fKey;
SkAutoTUnref<SkShader> fShader;
size_t fBitmapBytes;
virtual const Key& getKey() const SK_OVERRIDE { return fKey; }
virtual size_t bytesUsed() const SK_OVERRIDE {
return sizeof(fKey) + sizeof(SkShader) + fBitmapBytes;
}
static bool Visitor(const SkResourceCache::Rec& baseRec, void* contextShader) {
const BitmapShaderRec& rec = static_cast<const BitmapShaderRec&>(baseRec);
SkAutoTUnref<SkShader>* result = reinterpret_cast<SkAutoTUnref<SkShader>*>(contextShader);
result->reset(SkRef(rec.fShader.get()));
return true;
}
};
// FIXME: there's considerable boilerplate/duplication here vs. the global resource cache.
SK_DECLARE_STATIC_MUTEX(gBitmapShaderCacheMutex);
static SkResourceCache* gBitmapShaderCache = NULL;
#ifndef SK_DEFAULT_TILE_CACHE_LIMIT
#define SK_DEFAULT_TILE_CACHE_LIMIT (2 * 1024 * 1024)
#endif
static void cleanup_cache() {
// We'll clean this up in our own tests, but disable for clients.
// Chrome seems to have funky multi-process things going on in unit tests that
// makes this unsafe to delete when the main process atexit()s.
// SkLazyPtr does the same sort of thing.
#if SK_DEVELOPER
SkDELETE(gBitmapShaderCache);
#endif
}
/** Must hold gBitmapShaderCacheMutex when calling. */
static SkResourceCache* cache() {
// gTileCacheMutex is always held when this is called, so we don't need to be fancy in here.
gBitmapShaderCacheMutex.assertHeld();
if (NULL == gBitmapShaderCache) {
#ifdef SK_USE_DISCARDABLE_SCALEDIMAGECACHE
gBitmapShaderCache = SkNEW_ARGS(SkResourceCache, (SkDiscardableMemory::Create));
#else
gBitmapShaderCache = SkNEW_ARGS(SkResourceCache, (SK_DEFAULT_TILE_CACHE_LIMIT));
#endif
atexit(cleanup_cache);
}
return gBitmapShaderCache;
}
static bool cache_find(const BitmapShaderKey& key, SkAutoTUnref<SkShader>* result) {
SkAutoMutexAcquire am(gBitmapShaderCacheMutex);
return cache()->find(key, BitmapShaderRec::Visitor, result);
}
static void cache_add(BitmapShaderRec* rec) {
SkAutoMutexAcquire am(gBitmapShaderCacheMutex);
cache()->add(rec);
}
static bool cache_try_alloc_pixels(SkBitmap* bitmap) {
SkAutoMutexAcquire am(gBitmapShaderCacheMutex);
SkBitmap::Allocator* allocator = cache()->allocator();
if (NULL != allocator) {
return allocator->allocPixelRef(bitmap, NULL);
} else {
return bitmap->tryAllocPixels();
}
}
SkPictureShader::SkPictureShader(const SkPicture* picture, TileMode tmx, TileMode tmy,
const SkMatrix* localMatrix, const SkRect* tile)
: INHERITED(localMatrix)
@ -103,11 +219,18 @@ SkShader* SkPictureShader::refBitmapShader(const SkMatrix& matrix, const SkMatri
SkSize tileScale = SkSize::Make(SkIntToScalar(tileSize.width()) / fTile.width(),
SkIntToScalar(tileSize.height()) / fTile.height());
SkAutoMutexAcquire ama(fCachedBitmapShaderMutex);
SkAutoTUnref<SkShader> tileShader;
BitmapShaderKey key(fPicture->uniqueID(),
fTile,
fTmx,
fTmy,
tileScale,
this->getLocalMatrix());
if (!fCachedBitmapShader || tileScale != fCachedTileScale) {
if (!cache_find(key, &tileShader)) {
SkBitmap bm;
if (!bm.tryAllocN32Pixels(tileSize.width(), tileSize.height())) {
bm.setInfo(SkImageInfo::MakeN32Premul(tileSize));
if (!cache_try_alloc_pixels(&bm)) {
return NULL;
}
bm.eraseColor(SK_ColorTRANSPARENT);
@ -117,18 +240,14 @@ SkShader* SkPictureShader::refBitmapShader(const SkMatrix& matrix, const SkMatri
canvas.translate(fTile.x(), fTile.y());
canvas.drawPicture(fPicture);
fCachedTileScale = tileScale;
SkMatrix shaderMatrix = this->getLocalMatrix();
shaderMatrix.preScale(1 / tileScale.width(), 1 / tileScale.height());
fCachedBitmapShader.reset(CreateBitmapShader(bm, fTmx, fTmy, &shaderMatrix));
tileShader.reset(CreateBitmapShader(bm, fTmx, fTmy, &shaderMatrix));
cache_add(SkNEW_ARGS(BitmapShaderRec, (key, tileShader.get(), bm.getSize())));
}
// Increment the ref counter inside the mutex to ensure the returned pointer is still valid.
// Otherwise, the pointer may have been overwritten on a different thread before the object's
// ref count was incremented.
fCachedBitmapShader.get()->ref();
return fCachedBitmapShader;
return tileShader.detach();
}
size_t SkPictureShader::contextSize() const {

View File

@ -43,13 +43,9 @@ private:
SkShader* refBitmapShader(const SkMatrix&, const SkMatrix* localMatrix) const;
const SkPicture* fPicture;
SkRect fTile;
TileMode fTmx, fTmy;
mutable SkMutex fCachedBitmapShaderMutex;
mutable SkAutoTUnref<SkShader> fCachedBitmapShader;
mutable SkSize fCachedTileScale;
const SkPicture* fPicture;
SkRect fTile;
TileMode fTmx, fTmy;
class PictureShaderContext : public SkShader::Context {
public: