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:
parent
02b4725cb8
commit
23df2d6933
@ -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 {
|
||||
|
@ -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:
|
||||
|
Loading…
Reference in New Issue
Block a user