Improvements/additions to SkImageCache/SkLazyPixelRef.
SkPurgeableImageCache: New image cache that uses virtual memory to store the pixels. Combines features of SkAshmemImageCache (which has been removed) with SkPurgeableMemoryBlock, which has android and Mac versions. SkImageCache: Modified the API. pinCache now returns a status out parameter which states whether the pinned memory retained the old data. This allows allocAndPinCache to only be used for allocations. Add a new debug only interface to purge unpinned data. Updates to documentation, clarifying behavior. Changed CachedStatus to MemoryStatus SkLruImageCache: Implement the new function purgeAllUnpinnedCaches and change implementation of pinCache for the new behavior. SkLazyPixelRef: Rewrite onLockPixels to account for the new behavior of pinCache. BitmapFactoryTest: Test the new SkPurgeableImageCache. Write tests which directly test the SkImageCaches. Create a larger bitmap, since some of the SkImageCaches are designed to handle large bitmaps. bench_ and render_pictures: Consolidate lazy_decode_bitmap into one function. Allow using a flag to specify using the purgeable image cache. Clean up some #includes. Review URL: https://codereview.chromium.org/12433020 git-svn-id: http://skia.googlecode.com/svn/trunk@8207 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
parent
e1575aa216
commit
bb281f7f96
@ -288,6 +288,7 @@
|
||||
'<(skia_include_path)/lazy/SkBitmapFactory.h',
|
||||
'<(skia_include_path)/lazy/SkImageCache.h',
|
||||
'<(skia_include_path)/lazy/SkLruImageCache.h',
|
||||
'<(skia_include_path)/lazy/SkPurgeableImageCache.h',
|
||||
|
||||
'<(skia_src_path)/lazy/SkBitmapFactory.cpp',
|
||||
'<(skia_src_path)/lazy/SkLazyPixelRef.h',
|
||||
@ -295,6 +296,7 @@
|
||||
'<(skia_src_path)/lazy/SkLruImageCache.cpp',
|
||||
'<(skia_src_path)/lazy/SkPurgeableMemoryBlock.h',
|
||||
'<(skia_src_path)/lazy/SkPurgeableMemoryBlock_common.cpp',
|
||||
'<(skia_src_path)/lazy/SkPurgeableImageCache.cpp',
|
||||
],
|
||||
}
|
||||
|
||||
|
@ -167,16 +167,13 @@
|
||||
'../src/ports/SkPurgeableMemoryBlock_none.cpp',
|
||||
],
|
||||
'sources': [
|
||||
'../include/ports/SkAshmemImageCache.h',
|
||||
|
||||
'../src/ports/FontHostConfiguration_android.cpp',
|
||||
'../src/ports/SkDebug_android.cpp',
|
||||
'../src/ports/SkThread_pthread.cpp',
|
||||
'../src/ports/SkFontHost_android.cpp',
|
||||
'../src/ports/SkFontHost_FreeType.cpp',
|
||||
'../src/ports/SkFontHost_FreeType_common.cpp',
|
||||
'../src/ports/SkPurgeableMemoryBlock_android.cpp',
|
||||
'../src/ports/FontHostConfiguration_android.cpp',
|
||||
'../src/ports/SkAshmemImageCache.cpp',
|
||||
],
|
||||
'dependencies': [
|
||||
'freetype.gyp:freetype',
|
||||
|
@ -100,6 +100,7 @@
|
||||
'skia_base_libs.gyp:skia_base_libs',
|
||||
'tools.gyp:picture_renderer',
|
||||
'tools.gyp:picture_utils',
|
||||
'ports.gyp:ports',
|
||||
],
|
||||
},
|
||||
{
|
||||
@ -123,6 +124,7 @@
|
||||
'tools.gyp:picture_utils',
|
||||
'tools.gyp:picture_renderer',
|
||||
'bench.gyp:bench_timer',
|
||||
'ports.gyp:ports',
|
||||
],
|
||||
},
|
||||
{
|
||||
|
@ -67,20 +67,29 @@ public:
|
||||
bool installPixelRef(SkData*, SkBitmap*);
|
||||
|
||||
/**
|
||||
* A function for selecting an SkImageCache to use based on an SkImage::Info.
|
||||
* An object for selecting an SkImageCache to use based on an SkImage::Info.
|
||||
*/
|
||||
typedef SkImageCache* (*CacheSelector)(const SkImage::Info&);
|
||||
class CacheSelector : public SkRefCnt {
|
||||
|
||||
public:
|
||||
/**
|
||||
* Return an SkImageCache to use based on the provided SkImage::Info. If the caller decides
|
||||
* to hang on to the result, it will call ref, so the implementation should not add a ref
|
||||
* as a result of this call.
|
||||
*/
|
||||
virtual SkImageCache* selectCache(const SkImage::Info&) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the function to be used to select which SkImageCache to use. Mutually exclusive with
|
||||
* fImageCache.
|
||||
*/
|
||||
void setCacheSelector(CacheSelector);
|
||||
void setCacheSelector(CacheSelector*);
|
||||
|
||||
private:
|
||||
DecodeProc fDecodeProc;
|
||||
SkImageCache* fImageCache;
|
||||
CacheSelector fCacheSelector;
|
||||
DecodeProc fDecodeProc;
|
||||
SkImageCache* fImageCache;
|
||||
CacheSelector* fCacheSelector;
|
||||
};
|
||||
|
||||
#endif // SkBitmapFactory_DEFINED
|
||||
|
@ -22,22 +22,45 @@ public:
|
||||
* call to releaseCache and a call to throwAwayCache.
|
||||
* @param bytes Number of bytes needed.
|
||||
* @param ID Output parameter which must not be NULL. On success, ID will be set to a value
|
||||
* associated with that memory which can be used as a parameter to the other functions
|
||||
* in SkImageCache. On failure, ID is unchanged.
|
||||
* associated with that memory which can be used as a parameter to the other functions
|
||||
* in SkImageCache. On failure, ID is unchanged.
|
||||
* @return Pointer to the newly allocated memory, or NULL. This memory is safe to use until
|
||||
* releaseCache is called with ID.
|
||||
* releaseCache is called with ID.
|
||||
*/
|
||||
virtual void* allocAndPinCache(size_t bytes, intptr_t* ID) = 0;
|
||||
|
||||
/**
|
||||
* Re-request the memory associated with ID.
|
||||
* @param ID Unique ID for the memory block.
|
||||
* @return Pointer: If non-NULL, points to the previously allocated memory, in which case
|
||||
* this call must be balanced with a call to releaseCache. If NULL, the memory
|
||||
* has been reclaimed, so allocAndPinCache must be called again with a pointer to
|
||||
* the same ID.
|
||||
* Output parameter for pinCache, stating whether the memory still contains the data it held
|
||||
* when releaseCache was last called for the same ID.
|
||||
*/
|
||||
virtual void* pinCache(intptr_t ID) = 0;
|
||||
enum DataStatus {
|
||||
/**
|
||||
* The data has been purged, and therefore needs to be rewritten to the returned memory.
|
||||
*/
|
||||
kUninitialized_DataStatus,
|
||||
|
||||
/**
|
||||
* The memory still contains the data it held when releaseCache was last called with the
|
||||
* same ID.
|
||||
*/
|
||||
kRetained_DataStatus,
|
||||
};
|
||||
|
||||
/**
|
||||
* Re-request the memory associated with ID and pin it so that it will not be reclaimed until
|
||||
* the next call to releaseCache with the same ID.
|
||||
* @param ID Unique ID for the memory block.
|
||||
* @param status Output parameter which must not be NULL. On success (i.e. the return value is
|
||||
* not NULL), status will be set to one of two states representing the cached memory. If
|
||||
* status is set to kRetained_DataStatus, the memory contains the same data it did
|
||||
* before releaseCache was called with this ID. If status is set to
|
||||
* kUninitialized_DataStatus, the memory is still pinned, but the previous data is no
|
||||
* longer available. If the return value is NULL, status is unchanged.
|
||||
* @return Pointer: If non-NULL, points to the previously allocated memory, in which case
|
||||
* this call must be balanced with a call to releaseCache. If NULL, the memory
|
||||
* has been reclaimed, and throwAwayCache MUST NOT be called.
|
||||
*/
|
||||
virtual void* pinCache(intptr_t ID, DataStatus* status) = 0;
|
||||
|
||||
/**
|
||||
* Inform the cache that it is safe to free the block of memory corresponding to ID. After
|
||||
@ -61,16 +84,42 @@ public:
|
||||
static const intptr_t UNINITIALIZED_ID = 0;
|
||||
|
||||
#ifdef SK_DEBUG
|
||||
enum CacheStatus {
|
||||
kPinned_CacheStatus,
|
||||
kUnpinned_CacheStatus,
|
||||
kThrownAway_CacheStatus,
|
||||
/**
|
||||
* Debug only status of a memory block.
|
||||
*/
|
||||
enum MemoryStatus {
|
||||
/**
|
||||
* It is safe to use the pointer returned by the most recent of allocAndPinCache(ID) or
|
||||
* pinCache(ID) with the same ID.
|
||||
*/
|
||||
kPinned_MemoryStatus,
|
||||
|
||||
/**
|
||||
* The pointer returned by the most recent call to allocAndPinCache(ID) or pinCache(ID) has
|
||||
* since been released by releaseCache(ID). In order to reuse it, pinCache(ID) must be
|
||||
* called again. Note that after calling releaseCache(ID), the status of that particular
|
||||
* ID may not be kUnpinned_MemoryStatus, depending on the implementation, but it will not
|
||||
* be kPinned_MemoryStatus.
|
||||
*/
|
||||
kUnpinned_MemoryStatus,
|
||||
|
||||
/**
|
||||
* The memory associated with ID has been thrown away. No calls should be made using the
|
||||
* same ID.
|
||||
*/
|
||||
kFreed_MemoryStatus,
|
||||
};
|
||||
|
||||
/**
|
||||
* Debug only function to get the status of a particular block of memory.
|
||||
* Debug only function to get the status of a particular block of memory. Safe to call after
|
||||
* throwAwayCache has been called with this ID.
|
||||
*/
|
||||
virtual CacheStatus getCacheStatus(intptr_t ID) const = 0;
|
||||
virtual MemoryStatus getMemoryStatus(intptr_t ID) const = 0;
|
||||
|
||||
/**
|
||||
* Debug only function to clear all unpinned caches.
|
||||
*/
|
||||
virtual void purgeAllUnpinnedCaches() = 0;
|
||||
#endif
|
||||
};
|
||||
#endif // SkImageCache_DEFINED
|
||||
|
@ -25,7 +25,8 @@ public:
|
||||
virtual ~SkLruImageCache();
|
||||
|
||||
#ifdef SK_DEBUG
|
||||
CacheStatus getCacheStatus(intptr_t ID) const SK_OVERRIDE;
|
||||
virtual MemoryStatus getMemoryStatus(intptr_t ID) const SK_OVERRIDE;
|
||||
virtual void purgeAllUnpinnedCaches() SK_OVERRIDE;
|
||||
#endif
|
||||
|
||||
/**
|
||||
@ -45,7 +46,7 @@ public:
|
||||
size_t getImageCacheUsed() const { return fRamUsed; }
|
||||
|
||||
virtual void* allocAndPinCache(size_t bytes, intptr_t* ID) SK_OVERRIDE;
|
||||
virtual void* pinCache(intptr_t ID) SK_OVERRIDE;
|
||||
virtual void* pinCache(intptr_t ID, SkImageCache::DataStatus*) SK_OVERRIDE;
|
||||
virtual void releaseCache(intptr_t ID) SK_OVERRIDE;
|
||||
virtual void throwAwayCache(intptr_t ID) SK_OVERRIDE;
|
||||
|
||||
@ -55,7 +56,7 @@ private:
|
||||
typedef SkTInternalLList<CachedPixels>::Iter Iter;
|
||||
|
||||
#ifdef SK_DEBUG
|
||||
// fMutex is mutable so that getCacheStatus can be const
|
||||
// fMutex is mutable so that getMemoryStatus can be const
|
||||
mutable
|
||||
#endif
|
||||
SkMutex fMutex;
|
||||
|
45
include/lazy/SkPurgeableImageCache.h
Normal file
45
include/lazy/SkPurgeableImageCache.h
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef SkPurgeableImageCache_DEFINED
|
||||
#define SkPurgeableImageCache_DEFINED
|
||||
|
||||
#include "SkImageCache.h"
|
||||
|
||||
#ifdef SK_DEBUG
|
||||
#include "SkTDArray.h"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Implementation for SkImageCache that uses system defined purgeable memory.
|
||||
*/
|
||||
class SkPurgeableImageCache : public SkImageCache {
|
||||
|
||||
public:
|
||||
static SkImageCache* Create();
|
||||
|
||||
virtual void* allocAndPinCache(size_t bytes, intptr_t* ID) SK_OVERRIDE;
|
||||
virtual void* pinCache(intptr_t ID, SkImageCache::DataStatus*) SK_OVERRIDE;
|
||||
virtual void releaseCache(intptr_t ID) SK_OVERRIDE;
|
||||
virtual void throwAwayCache(intptr_t ID) SK_OVERRIDE;
|
||||
|
||||
#ifdef SK_DEBUG
|
||||
virtual MemoryStatus getMemoryStatus(intptr_t ID) const SK_OVERRIDE;
|
||||
virtual void purgeAllUnpinnedCaches() SK_OVERRIDE;
|
||||
virtual ~SkPurgeableImageCache();
|
||||
#endif
|
||||
|
||||
private:
|
||||
SkPurgeableImageCache();
|
||||
|
||||
#ifdef SK_DEBUG
|
||||
SkTDArray<intptr_t> fRecs;
|
||||
int findRec(intptr_t) const;
|
||||
#endif
|
||||
void removeRec(intptr_t);
|
||||
};
|
||||
#endif // SkPurgeableImageCache_DEFINED
|
@ -1,72 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef SkAshmemImageCache_DEFINED
|
||||
#define SkAshmemImageCache_DEFINED
|
||||
|
||||
#include "SkImageCache.h"
|
||||
#include "SkTDArray.h"
|
||||
#include "SkTypes.h"
|
||||
|
||||
class SkAshmemImageCache : public SkImageCache {
|
||||
|
||||
public:
|
||||
/**
|
||||
* Get a pointer to the single global instance of SkAshmemImageCache.
|
||||
*/
|
||||
static SkAshmemImageCache* GetAshmemImageCache();
|
||||
|
||||
virtual void* allocAndPinCache(size_t bytes, intptr_t* ID) SK_OVERRIDE;
|
||||
virtual void* pinCache(intptr_t ID) SK_OVERRIDE;
|
||||
virtual void releaseCache(intptr_t ID) SK_OVERRIDE;
|
||||
virtual void throwAwayCache(intptr_t ID) SK_OVERRIDE;
|
||||
|
||||
#ifdef SK_DEBUG
|
||||
SkImageCache::CacheStatus getCacheStatus(intptr_t ID) const SK_OVERRIDE;
|
||||
|
||||
virtual ~SkAshmemImageCache();
|
||||
#endif
|
||||
|
||||
private:
|
||||
struct AshmemRec {
|
||||
int fFD;
|
||||
void* fAddr;
|
||||
size_t fSize;
|
||||
#ifdef SK_DEBUG
|
||||
bool fPinned;
|
||||
|
||||
static int Compare(const AshmemRec*, const AshmemRec*);
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor is private. The correct way to get this cache is through
|
||||
* GetAshmemImageCache, so that all callers can get the single global.
|
||||
*/
|
||||
SkAshmemImageCache();
|
||||
|
||||
#ifdef SK_DEBUG
|
||||
// Stores a list of AshmemRecs to track deletion.
|
||||
SkTDArray<AshmemRec*> fRecs;
|
||||
|
||||
/**
|
||||
* Debug only function to add an AshmemRec to the list.
|
||||
*/
|
||||
void appendRec(AshmemRec*);
|
||||
|
||||
/**
|
||||
* Return the index of AshmemRec.
|
||||
*/
|
||||
int findRec(const AshmemRec*) const;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Deletes AshmemRec. In debug, also removes from the list.
|
||||
*/
|
||||
void removeRec(AshmemRec*);
|
||||
};
|
||||
#endif // SkAshmemImageCache_DEFINED
|
@ -22,17 +22,19 @@ SkBitmapFactory::SkBitmapFactory(SkBitmapFactory::DecodeProc proc)
|
||||
|
||||
SkBitmapFactory::~SkBitmapFactory() {
|
||||
SkSafeUnref(fImageCache);
|
||||
SkSafeUnref(fCacheSelector);
|
||||
}
|
||||
|
||||
void SkBitmapFactory::setImageCache(SkImageCache *cache) {
|
||||
SkRefCnt_SafeAssign(fImageCache, cache);
|
||||
if (cache != NULL) {
|
||||
SkSafeUnref(fCacheSelector);
|
||||
fCacheSelector = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void SkBitmapFactory::setCacheSelector(CacheSelector selector) {
|
||||
fCacheSelector = selector;
|
||||
void SkBitmapFactory::setCacheSelector(CacheSelector* selector) {
|
||||
SkRefCnt_SafeAssign(fCacheSelector, selector);
|
||||
if (selector != NULL) {
|
||||
SkSafeUnref(fImageCache);
|
||||
fImageCache = NULL;
|
||||
@ -63,7 +65,7 @@ bool SkBitmapFactory::installPixelRef(SkData* data, SkBitmap* dst) {
|
||||
// fImageCache and fCacheSelector are mutually exclusive.
|
||||
SkASSERT(NULL == fImageCache || NULL == fCacheSelector);
|
||||
|
||||
SkImageCache* cache = NULL == fCacheSelector ? fImageCache : fCacheSelector(info);
|
||||
SkImageCache* cache = NULL == fCacheSelector ? fImageCache : fCacheSelector->selectCache(info);
|
||||
|
||||
if (cache != NULL) {
|
||||
// Now set a new LazyPixelRef on dst.
|
||||
|
@ -24,7 +24,8 @@ SkLazyPixelRef::SkLazyPixelRef(SkData* data, SkBitmapFactory::DecodeProc proc, S
|
||||
: INHERITED(NULL)
|
||||
, fDecodeProc(proc)
|
||||
, fImageCache(cache)
|
||||
, fCacheId(SkImageCache::UNINITIALIZED_ID) {
|
||||
, fCacheId(SkImageCache::UNINITIALIZED_ID)
|
||||
, fRowBytes(0) {
|
||||
SkASSERT(fDecodeProc != NULL);
|
||||
if (NULL == data) {
|
||||
fData = SkData::NewEmpty();
|
||||
@ -71,37 +72,54 @@ void* SkLazyPixelRef::onLockPixels(SkColorTable**) {
|
||||
if (SkImageCache::UNINITIALIZED_ID == fCacheId) {
|
||||
target.fAddr = NULL;
|
||||
} else {
|
||||
target.fAddr = fImageCache->pinCache(fCacheId);
|
||||
if (NULL != target.fAddr) {
|
||||
SkImageCache::DataStatus status;
|
||||
target.fAddr = fImageCache->pinCache(fCacheId, &status);
|
||||
if (target.fAddr == NULL) {
|
||||
fCacheId = SkImageCache::UNINITIALIZED_ID;
|
||||
} else {
|
||||
if (SkImageCache::kRetained_DataStatus == status) {
|
||||
#if LAZY_CACHE_STATS
|
||||
sk_atomic_inc(&gCacheHits);
|
||||
sk_atomic_inc(&gCacheHits);
|
||||
#endif
|
||||
return target.fAddr;
|
||||
return target.fAddr;
|
||||
}
|
||||
SkASSERT(SkImageCache::kUninitialized_DataStatus == status);
|
||||
}
|
||||
// Cache miss. Either pinCache returned NULL or it returned a memory address without the old
|
||||
// data
|
||||
#if LAZY_CACHE_STATS
|
||||
sk_atomic_inc(&gCacheMisses);
|
||||
#endif
|
||||
}
|
||||
SkASSERT(NULL == target.fAddr);
|
||||
SkImage::Info info;
|
||||
SkASSERT(fData != NULL && fData->size() > 0);
|
||||
// FIXME: As an optimization, only do this part once.
|
||||
fErrorInDecoding = !fDecodeProc(fData->data(), fData->size(), &info, NULL);
|
||||
if (fErrorInDecoding) {
|
||||
// In case a previous call to allocAndPinCache succeeded.
|
||||
fImageCache->throwAwayCache(fCacheId);
|
||||
fCacheId = SkImageCache::UNINITIALIZED_ID;
|
||||
return NULL;
|
||||
}
|
||||
// Allocate the memory.
|
||||
size_t bytes = ComputeMinRowBytesAndSize(info, &target.fRowBytes);
|
||||
|
||||
target.fAddr = fImageCache->allocAndPinCache(bytes, &fCacheId);
|
||||
if (NULL == target.fAddr) {
|
||||
// Space could not be allocated.
|
||||
fCacheId = SkImageCache::UNINITIALIZED_ID;
|
||||
return NULL;
|
||||
// Determine the size of the image in order to determine how much memory to allocate.
|
||||
// FIXME: As an optimization, only do this part once.
|
||||
fErrorInDecoding = !fDecodeProc(fData->data(), fData->size(), &info, NULL);
|
||||
if (fErrorInDecoding) {
|
||||
// We can only reach here if fCacheId was already set to UNINITIALIZED_ID, or if
|
||||
// pinCache returned NULL, in which case it was reset to UNINITIALIZED_ID.
|
||||
SkASSERT(SkImageCache::UNINITIALIZED_ID == fCacheId);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t bytes = ComputeMinRowBytesAndSize(info, &target.fRowBytes);
|
||||
target.fAddr = fImageCache->allocAndPinCache(bytes, &fCacheId);
|
||||
if (NULL == target.fAddr) {
|
||||
// Space could not be allocated.
|
||||
// Just like the last assert, fCacheId must be UNINITIALIZED_ID.
|
||||
SkASSERT(SkImageCache::UNINITIALIZED_ID == fCacheId);
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
// pinCache returned purged memory to which target.fAddr already points. Set
|
||||
// target.fRowBytes properly.
|
||||
target.fRowBytes = fRowBytes;
|
||||
// Assume that the size is correct, since it was determined by this same function
|
||||
// previously.
|
||||
}
|
||||
SkASSERT(target.fAddr != NULL);
|
||||
SkASSERT(SkImageCache::UNINITIALIZED_ID != fCacheId);
|
||||
fErrorInDecoding = !fDecodeProc(fData->data(), fData->size(), &info, &target);
|
||||
if (fErrorInDecoding) {
|
||||
@ -109,6 +127,8 @@ void* SkLazyPixelRef::onLockPixels(SkColorTable**) {
|
||||
fCacheId = SkImageCache::UNINITIALIZED_ID;
|
||||
return NULL;
|
||||
}
|
||||
// Upon success, store fRowBytes so it can be used in case pinCache later returns purged memory.
|
||||
fRowBytes = target.fRowBytes;
|
||||
return target.fAddr;
|
||||
}
|
||||
|
||||
|
@ -68,6 +68,7 @@ private:
|
||||
SkBitmapFactory::DecodeProc fDecodeProc;
|
||||
SkImageCache* fImageCache;
|
||||
intptr_t fCacheId;
|
||||
size_t fRowBytes;
|
||||
|
||||
#if LAZY_CACHE_STATS
|
||||
static int32_t gCacheHits;
|
||||
|
@ -74,16 +74,24 @@ SkLruImageCache::~SkLruImageCache() {
|
||||
}
|
||||
|
||||
#ifdef SK_DEBUG
|
||||
SkImageCache::CacheStatus SkLruImageCache::getCacheStatus(intptr_t ID) const {
|
||||
SkImageCache::MemoryStatus SkLruImageCache::getMemoryStatus(intptr_t ID) const {
|
||||
if (SkImageCache::UNINITIALIZED_ID == ID) {
|
||||
return SkImageCache::kFreed_MemoryStatus;
|
||||
}
|
||||
SkAutoMutexAcquire ac(&fMutex);
|
||||
CachedPixels* pixels = this->findByID(ID);
|
||||
if (NULL == pixels) {
|
||||
return SkImageCache::kThrownAway_CacheStatus;
|
||||
return SkImageCache::kFreed_MemoryStatus;
|
||||
}
|
||||
if (pixels->isLocked()) {
|
||||
return SkImageCache::kPinned_CacheStatus;
|
||||
return SkImageCache::kPinned_MemoryStatus;
|
||||
}
|
||||
return SkImageCache::kUnpinned_CacheStatus;
|
||||
return SkImageCache::kUnpinned_MemoryStatus;
|
||||
}
|
||||
|
||||
void SkLruImageCache::purgeAllUnpinnedCaches() {
|
||||
SkAutoMutexAcquire ac(&fMutex);
|
||||
this->purgeTilAtOrBelow(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -108,7 +116,7 @@ void* SkLruImageCache::allocAndPinCache(size_t bytes, intptr_t* ID) {
|
||||
return pixels->getData();
|
||||
}
|
||||
|
||||
void* SkLruImageCache::pinCache(intptr_t ID) {
|
||||
void* SkLruImageCache::pinCache(intptr_t ID, SkImageCache::DataStatus* status) {
|
||||
SkASSERT(ID != SkImageCache::UNINITIALIZED_ID);
|
||||
SkAutoMutexAcquire ac(&fMutex);
|
||||
CachedPixels* pixels = this->findByID(ID);
|
||||
@ -119,6 +127,9 @@ void* SkLruImageCache::pinCache(intptr_t ID) {
|
||||
fLRU.remove(pixels);
|
||||
fLRU.addToHead(pixels);
|
||||
}
|
||||
SkASSERT(status != NULL);
|
||||
// This cache will never return pinned memory whose data has been overwritten.
|
||||
*status = SkImageCache::kRetained_DataStatus;
|
||||
pixels->lock();
|
||||
return pixels->getData();
|
||||
}
|
||||
@ -133,6 +144,7 @@ void SkLruImageCache::releaseCache(intptr_t ID) {
|
||||
}
|
||||
|
||||
void SkLruImageCache::throwAwayCache(intptr_t ID) {
|
||||
SkASSERT(ID != SkImageCache::UNINITIALIZED_ID);
|
||||
SkAutoMutexAcquire ac(&fMutex);
|
||||
CachedPixels* pixels = this->findByID(ID);
|
||||
if (pixels != NULL) {
|
||||
@ -155,9 +167,6 @@ void SkLruImageCache::removePixels(CachedPixels* pixels) {
|
||||
|
||||
CachedPixels* SkLruImageCache::findByID(intptr_t ID) const {
|
||||
// Mutex is already locked.
|
||||
if (SkImageCache::UNINITIALIZED_ID == ID) {
|
||||
return NULL;
|
||||
}
|
||||
Iter iter;
|
||||
// Start from the head, most recently used.
|
||||
CachedPixels* pixels = iter.init(fLRU, Iter::kHead_IterStart);
|
||||
|
159
src/lazy/SkPurgeableImageCache.cpp
Normal file
159
src/lazy/SkPurgeableImageCache.cpp
Normal file
@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "SkThread.h"
|
||||
#include "SkPurgeableImageCache.h"
|
||||
#include "SkPurgeableMemoryBlock.h"
|
||||
|
||||
#ifdef SK_DEBUG
|
||||
#include "SkTSearch.h"
|
||||
#endif
|
||||
|
||||
SK_DECLARE_STATIC_MUTEX(gPurgeableImageMutex);
|
||||
|
||||
SkImageCache* SkPurgeableImageCache::Create() {
|
||||
if (!SkPurgeableMemoryBlock::IsSupported()) {
|
||||
return NULL;
|
||||
}
|
||||
SkAutoMutexAcquire ac(&gPurgeableImageMutex);
|
||||
static SkPurgeableImageCache gCache;
|
||||
gCache.ref();
|
||||
return &gCache;
|
||||
}
|
||||
|
||||
SkPurgeableImageCache::SkPurgeableImageCache() {}
|
||||
|
||||
#ifdef SK_DEBUG
|
||||
SkPurgeableImageCache::~SkPurgeableImageCache() {
|
||||
SkASSERT(fRecs.count() == 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void* SkPurgeableImageCache::allocAndPinCache(size_t bytes, intptr_t* ID) {
|
||||
SkAutoMutexAcquire ac(&gPurgeableImageMutex);
|
||||
|
||||
SkPurgeableMemoryBlock* block = SkPurgeableMemoryBlock::Create(bytes);
|
||||
if (NULL == block) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SkPurgeableMemoryBlock::PinResult pinResult;
|
||||
void* data = block->pin(&pinResult);
|
||||
if (NULL == data) {
|
||||
SkDELETE(block);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SkASSERT(ID != NULL);
|
||||
*ID = reinterpret_cast<intptr_t>(block);
|
||||
#ifdef SK_DEBUG
|
||||
// Insert into the array of all recs:
|
||||
int index = this->findRec(*ID);
|
||||
SkASSERT(index < 0);
|
||||
fRecs.insert(~index, 1, ID);
|
||||
#endif
|
||||
return data;
|
||||
}
|
||||
|
||||
void* SkPurgeableImageCache::pinCache(intptr_t ID, SkImageCache::DataStatus* status) {
|
||||
SkASSERT(ID != SkImageCache::UNINITIALIZED_ID);
|
||||
SkAutoMutexAcquire ac(&gPurgeableImageMutex);
|
||||
|
||||
SkASSERT(this->findRec(ID) >= 0);
|
||||
SkPurgeableMemoryBlock* block = reinterpret_cast<SkPurgeableMemoryBlock*>(ID);
|
||||
SkPurgeableMemoryBlock::PinResult pinResult;
|
||||
void* data = block->pin(&pinResult);
|
||||
if (NULL == data) {
|
||||
this->removeRec(ID);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (pinResult) {
|
||||
case SkPurgeableMemoryBlock::kRetained_PinResult:
|
||||
*status = SkImageCache::kRetained_DataStatus;
|
||||
break;
|
||||
|
||||
case SkPurgeableMemoryBlock::kUninitialized_PinResult:
|
||||
*status = SkImageCache::kUninitialized_DataStatus;
|
||||
break;
|
||||
|
||||
default:
|
||||
// Invalid value. Treat as a failure to pin.
|
||||
SkASSERT(false);
|
||||
this->removeRec(ID);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void SkPurgeableImageCache::releaseCache(intptr_t ID) {
|
||||
SkASSERT(ID != SkImageCache::UNINITIALIZED_ID);
|
||||
SkAutoMutexAcquire ac(&gPurgeableImageMutex);
|
||||
|
||||
SkASSERT(this->findRec(ID) >= 0);
|
||||
SkPurgeableMemoryBlock* block = reinterpret_cast<SkPurgeableMemoryBlock*>(ID);
|
||||
block->unpin();
|
||||
}
|
||||
|
||||
void SkPurgeableImageCache::throwAwayCache(intptr_t ID) {
|
||||
SkASSERT(ID != SkImageCache::UNINITIALIZED_ID);
|
||||
SkAutoMutexAcquire ac(&gPurgeableImageMutex);
|
||||
|
||||
this->removeRec(ID);
|
||||
}
|
||||
|
||||
#ifdef SK_DEBUG
|
||||
SkImageCache::MemoryStatus SkPurgeableImageCache::getMemoryStatus(intptr_t ID) const {
|
||||
SkAutoMutexAcquire ac(&gPurgeableImageMutex);
|
||||
if (SkImageCache::UNINITIALIZED_ID == ID || this->findRec(ID) < 0) {
|
||||
return SkImageCache::kFreed_MemoryStatus;
|
||||
}
|
||||
|
||||
SkPurgeableMemoryBlock* block = reinterpret_cast<SkPurgeableMemoryBlock*>(ID);
|
||||
if (block->isPinned()) {
|
||||
return SkImageCache::kPinned_MemoryStatus;
|
||||
}
|
||||
return SkImageCache::kUnpinned_MemoryStatus;
|
||||
}
|
||||
|
||||
void SkPurgeableImageCache::purgeAllUnpinnedCaches() {
|
||||
SkAutoMutexAcquire ac(&gPurgeableImageMutex);
|
||||
if (SkPurgeableMemoryBlock::PlatformSupportsPurgingAllUnpinnedBlocks()) {
|
||||
SkPurgeableMemoryBlock::PurgeAllUnpinnedBlocks();
|
||||
} else {
|
||||
// Go through the blocks, and purge them individually.
|
||||
// Rather than deleting the blocks, which would interfere with further calls, purge them
|
||||
// and keep them around.
|
||||
for (int i = 0; i < fRecs.count(); i++) {
|
||||
SkPurgeableMemoryBlock* block = reinterpret_cast<SkPurgeableMemoryBlock*>(fRecs[i]);
|
||||
if (!block->isPinned()) {
|
||||
if (!block->purge()) {
|
||||
// FIXME: This should be more meaningful (which one, etc...)
|
||||
SkDebugf("Failed to purge\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int SkPurgeableImageCache::findRec(intptr_t rec) const {
|
||||
return SkTSearch(fRecs.begin(), fRecs.count(), rec, sizeof(intptr_t));
|
||||
}
|
||||
#endif
|
||||
|
||||
void SkPurgeableImageCache::removeRec(intptr_t ID) {
|
||||
#ifdef SK_DEBUG
|
||||
int index = this->findRec(ID);
|
||||
SkASSERT(index >= 0);
|
||||
fRecs.remove(index);
|
||||
#endif
|
||||
SkPurgeableMemoryBlock* block = reinterpret_cast<SkPurgeableMemoryBlock*>(ID);
|
||||
SkASSERT(!block->isPinned());
|
||||
SkDELETE(block);
|
||||
}
|
@ -1,161 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "SkAshmemImageCache.h"
|
||||
#include "SkThread.h"
|
||||
|
||||
#ifdef SK_DEBUG
|
||||
#include "SkTSearch.h"
|
||||
#endif
|
||||
|
||||
#include "android/ashmem.h"
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
SkAshmemImageCache::SkAshmemImageCache() {}
|
||||
|
||||
SK_DECLARE_STATIC_MUTEX(gAshmemMutex);
|
||||
|
||||
SkAshmemImageCache* SkAshmemImageCache::GetAshmemImageCache() {
|
||||
SkAutoMutexAcquire ac(&gAshmemMutex);
|
||||
static SkAshmemImageCache gCache;
|
||||
return &gCache;
|
||||
}
|
||||
|
||||
#ifdef SK_DEBUG
|
||||
SkAshmemImageCache::~SkAshmemImageCache() {
|
||||
SkASSERT(fRecs.count() == 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
// ashmem likes lengths on page boundaries.
|
||||
static size_t roundToPageSize(size_t size) {
|
||||
const size_t mask = getpagesize() - 1;
|
||||
size_t newSize = (size + mask) & ~mask;
|
||||
return newSize;
|
||||
}
|
||||
|
||||
void* SkAshmemImageCache::allocAndPinCache(size_t bytes, intptr_t* ID) {
|
||||
SkASSERT(ID != NULL);
|
||||
|
||||
SkAutoMutexAcquire ac(&gAshmemMutex);
|
||||
|
||||
if (*ID != SkImageCache::UNINITIALIZED_ID) {
|
||||
// This rec was previously allocated, but pinCache subsequently
|
||||
// failed.
|
||||
AshmemRec* pRec = reinterpret_cast<AshmemRec*>(*ID);
|
||||
SkASSERT(roundToPageSize(bytes) == pRec->fSize);
|
||||
SkASSERT(pRec->fFD != -1);
|
||||
(void) ashmem_pin_region(pRec->fFD, 0, 0);
|
||||
#ifdef SK_DEBUG
|
||||
pRec->fPinned = true;
|
||||
#endif
|
||||
return pRec->fAddr;
|
||||
}
|
||||
|
||||
AshmemRec rec;
|
||||
rec.fSize = roundToPageSize(bytes);
|
||||
|
||||
rec.fFD = ashmem_create_region(NULL, rec.fSize);
|
||||
if (-1 == rec.fFD) {
|
||||
SkDebugf("ashmem_create_region failed\n");
|
||||
return NULL;
|
||||
}
|
||||
int err = ashmem_set_prot_region(rec.fFD, PROT_READ | PROT_WRITE);
|
||||
if (err != 0) {
|
||||
SkDebugf("ashmem_set_prot_region failed\n");
|
||||
close(rec.fFD);
|
||||
return NULL;
|
||||
}
|
||||
rec.fAddr = mmap(NULL, rec.fSize, PROT_READ | PROT_WRITE, MAP_PRIVATE, rec.fFD, 0);
|
||||
if (-1 == (long) rec.fAddr) {
|
||||
SkDebugf("mmap failed\n");
|
||||
close(rec.fFD);
|
||||
return NULL;
|
||||
}
|
||||
(void) ashmem_pin_region(rec.fFD, 0, 0);
|
||||
#ifdef SK_DEBUG
|
||||
rec.fPinned = true;
|
||||
#endif
|
||||
// In release mode, we do not keep a pointer to this object. It will be destroyed
|
||||
// either when pinCache returns NULL or when throwAwayCache is called.
|
||||
AshmemRec* pRec = SkNEW_ARGS(AshmemRec, (rec));
|
||||
*ID = reinterpret_cast<intptr_t>(pRec);
|
||||
#ifdef SK_DEBUG
|
||||
this->appendRec(pRec);
|
||||
#endif
|
||||
return rec.fAddr;
|
||||
}
|
||||
|
||||
void* SkAshmemImageCache::pinCache(intptr_t ID) {
|
||||
SkAutoMutexAcquire ac(&gAshmemMutex);
|
||||
AshmemRec* rec = reinterpret_cast<AshmemRec*>(ID);
|
||||
const int fd = rec->fFD;
|
||||
int pin = ashmem_pin_region(fd, 0, 0);
|
||||
if (ASHMEM_NOT_PURGED == pin) {
|
||||
#ifdef SK_DEBUG
|
||||
rec->fPinned = true;
|
||||
#endif
|
||||
return rec->fAddr;
|
||||
}
|
||||
ashmem_unpin_region(fd, 0, 0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void SkAshmemImageCache::releaseCache(intptr_t ID) {
|
||||
SkAutoMutexAcquire ac(&gAshmemMutex);
|
||||
AshmemRec* rec = reinterpret_cast<AshmemRec*>(ID);
|
||||
ashmem_unpin_region(rec->fFD, 0, 0);
|
||||
#ifdef SK_DEBUG
|
||||
rec->fPinned = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void SkAshmemImageCache::throwAwayCache(intptr_t ID) {
|
||||
SkAutoMutexAcquire ac(&gAshmemMutex);
|
||||
AshmemRec* rec = reinterpret_cast<AshmemRec*>(ID);
|
||||
munmap(rec->fAddr, rec->fSize);
|
||||
close(rec->fFD);
|
||||
#ifdef SK_DEBUG
|
||||
SkASSERT(!rec->fPinned);
|
||||
int index = this->findRec(rec);
|
||||
SkASSERT(index >= 0);
|
||||
fRecs.remove(index);
|
||||
#endif
|
||||
SkDELETE(rec);
|
||||
}
|
||||
|
||||
#ifdef SK_DEBUG
|
||||
void SkAshmemImageCache::appendRec(SkAshmemImageCache::AshmemRec* rec) {
|
||||
int index = this->findRec(rec);
|
||||
// Should not already exist.
|
||||
SkASSERT(index < 0);
|
||||
fRecs.insert(~index, 1, &rec);
|
||||
}
|
||||
|
||||
int SkAshmemImageCache::AshmemRec::Compare(const SkAshmemImageCache::AshmemRec* a,
|
||||
const SkAshmemImageCache::AshmemRec* b) {
|
||||
return reinterpret_cast<intptr_t>(a) - reinterpret_cast<intptr_t>(b);
|
||||
}
|
||||
|
||||
int SkAshmemImageCache::findRec(const SkAshmemImageCache::AshmemRec* rec) const {
|
||||
return SkTSearch<AshmemRec>((const AshmemRec**)fRecs.begin(), fRecs.count(), rec,
|
||||
sizeof(intptr_t), AshmemRec::Compare);
|
||||
}
|
||||
|
||||
SkImageCache::CacheStatus SkAshmemImageCache::getCacheStatus(intptr_t ID) const {
|
||||
SkAutoMutexAcquire ac(&gAshmemMutex);
|
||||
AshmemRec* rec = reinterpret_cast<AshmemRec*>(ID);
|
||||
int index = this->findRec(rec);
|
||||
if (index < 0) {
|
||||
return SkImageCache::kThrownAway_CacheStatus;
|
||||
}
|
||||
return rec->fPinned ? SkImageCache::kPinned_CacheStatus
|
||||
: SkImageCache::kUnpinned_CacheStatus;
|
||||
}
|
||||
#endif
|
@ -17,17 +17,15 @@
|
||||
#include "SkLazyPixelRef.h"
|
||||
#include "SkLruImageCache.h"
|
||||
#include "SkPaint.h"
|
||||
#include "SkPurgeableImageCache.h"
|
||||
#include "SkStream.h"
|
||||
#include "SkTemplates.h"
|
||||
#include "Test.h"
|
||||
|
||||
#ifdef SK_BUILD_FOR_ANDROID
|
||||
#include "SkAshmemImageCache.h"
|
||||
#endif
|
||||
|
||||
static SkBitmap* create_bitmap() {
|
||||
SkBitmap* bm = SkNEW(SkBitmap);
|
||||
const int W = 100, H = 100;
|
||||
// Use a large bitmap.
|
||||
const int W = 1000, H = 1000;
|
||||
bm->setConfig(SkBitmap::kARGB_8888_Config, W, H);
|
||||
bm->allocPixels();
|
||||
bm->eraseColor(SK_ColorBLACK);
|
||||
@ -52,7 +50,53 @@ static void assert_bounds_equal(skiatest::Reporter* reporter, const SkBitmap& bm
|
||||
REPORTER_ASSERT(reporter, bm1.height() == bm2.height());
|
||||
}
|
||||
|
||||
static void test_cache(skiatest::Reporter* reporter, SkImageCache* cache, SkData* encodedData,
|
||||
static void test_cache(skiatest::Reporter* reporter, SkImageCache* cache) {
|
||||
// Test the cache directly:
|
||||
cache->purgeAllUnpinnedCaches();
|
||||
intptr_t ID = SkImageCache::UNINITIALIZED_ID;
|
||||
const size_t size = 1000;
|
||||
char buffer[size];
|
||||
sk_bzero((void*) buffer, size);
|
||||
void* memory = cache->allocAndPinCache(size, &ID);
|
||||
if (memory != NULL) {
|
||||
memcpy(memory, (void*)buffer, size);
|
||||
REPORTER_ASSERT(reporter, cache->getMemoryStatus(ID) == SkImageCache::kPinned_MemoryStatus);
|
||||
cache->releaseCache(ID);
|
||||
REPORTER_ASSERT(reporter, cache->getMemoryStatus(ID) != SkImageCache::kPinned_MemoryStatus);
|
||||
SkImageCache::DataStatus dataStatus;
|
||||
memory = cache->pinCache(ID, &dataStatus);
|
||||
if (memory != NULL) {
|
||||
REPORTER_ASSERT(reporter, cache->getMemoryStatus(ID)
|
||||
== SkImageCache::kPinned_MemoryStatus);
|
||||
if (SkImageCache::kRetained_DataStatus == dataStatus) {
|
||||
REPORTER_ASSERT(reporter, !memcmp(memory, (void*) buffer, size));
|
||||
}
|
||||
cache->releaseCache(ID);
|
||||
REPORTER_ASSERT(reporter, cache->getMemoryStatus(ID)
|
||||
!= SkImageCache::kPinned_MemoryStatus);
|
||||
cache->purgeAllUnpinnedCaches();
|
||||
REPORTER_ASSERT(reporter, cache->getMemoryStatus(ID)
|
||||
!= SkImageCache::kPinned_MemoryStatus);
|
||||
memory = cache->pinCache(ID, &dataStatus);
|
||||
if (memory != NULL) {
|
||||
// Since the cache was thrown away, and ID was not pinned, it should have
|
||||
// been purged.
|
||||
REPORTER_ASSERT(reporter, SkImageCache::kUninitialized_DataStatus == dataStatus);
|
||||
cache->releaseCache(ID);
|
||||
REPORTER_ASSERT(reporter, cache->getMemoryStatus(ID)
|
||||
!= SkImageCache::kPinned_MemoryStatus);
|
||||
cache->throwAwayCache(ID);
|
||||
REPORTER_ASSERT(reporter, cache->getMemoryStatus(ID)
|
||||
== SkImageCache::kFreed_MemoryStatus);
|
||||
} else {
|
||||
REPORTER_ASSERT(reporter, cache->getMemoryStatus(ID)
|
||||
== SkImageCache::kFreed_MemoryStatus);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void test_factory(skiatest::Reporter* reporter, SkImageCache* cache, SkData* encodedData,
|
||||
const SkBitmap& origBitmap) {
|
||||
SkBitmapFactory factory(&SkImageDecoder::DecodeMemoryToTarget);
|
||||
factory.setImageCache(cache);
|
||||
@ -72,41 +116,80 @@ static void test_cache(skiatest::Reporter* reporter, SkImageCache* cache, SkData
|
||||
// Lazy decoding
|
||||
REPORTER_ASSERT(reporter, !bitmapFromFactory->readyToDraw());
|
||||
SkLazyPixelRef* lazyRef = static_cast<SkLazyPixelRef*>(pixelRef);
|
||||
int32_t cacheID = lazyRef->getCacheId();
|
||||
REPORTER_ASSERT(reporter, cache->getCacheStatus(cacheID)
|
||||
!= SkImageCache::kPinned_CacheStatus);
|
||||
intptr_t cacheID = lazyRef->getCacheId();
|
||||
REPORTER_ASSERT(reporter, cache->getMemoryStatus(cacheID)
|
||||
!= SkImageCache::kPinned_MemoryStatus);
|
||||
{
|
||||
SkAutoLockPixels alp(*bitmapFromFactory.get());
|
||||
REPORTER_ASSERT(reporter, bitmapFromFactory->readyToDraw());
|
||||
cacheID = lazyRef->getCacheId();
|
||||
REPORTER_ASSERT(reporter, cache->getCacheStatus(cacheID)
|
||||
== SkImageCache::kPinned_CacheStatus);
|
||||
REPORTER_ASSERT(reporter, cache->getMemoryStatus(cacheID)
|
||||
== SkImageCache::kPinned_MemoryStatus);
|
||||
}
|
||||
REPORTER_ASSERT(reporter, !bitmapFromFactory->readyToDraw());
|
||||
REPORTER_ASSERT(reporter, cache->getCacheStatus(cacheID)
|
||||
!= SkImageCache::kPinned_CacheStatus);
|
||||
REPORTER_ASSERT(reporter, cache->getMemoryStatus(cacheID)
|
||||
!= SkImageCache::kPinned_MemoryStatus);
|
||||
bitmapFromFactory.free();
|
||||
REPORTER_ASSERT(reporter, cache->getCacheStatus(cacheID)
|
||||
== SkImageCache::kThrownAway_CacheStatus);
|
||||
REPORTER_ASSERT(reporter, cache->getMemoryStatus(cacheID)
|
||||
== SkImageCache::kFreed_MemoryStatus);
|
||||
}
|
||||
}
|
||||
|
||||
class ImageCacheHolder : public SkNoncopyable {
|
||||
|
||||
public:
|
||||
~ImageCacheHolder() {
|
||||
fCaches.safeUnrefAll();
|
||||
}
|
||||
|
||||
void addImageCache(SkImageCache* cache) {
|
||||
SkSafeRef(cache);
|
||||
*fCaches.append() = cache;
|
||||
}
|
||||
|
||||
int count() const { return fCaches.count(); }
|
||||
|
||||
SkImageCache* getAt(int i) {
|
||||
if (i < 0 || i > fCaches.count()) {
|
||||
return NULL;
|
||||
}
|
||||
return fCaches.getAt(i);
|
||||
}
|
||||
|
||||
private:
|
||||
SkTDArray<SkImageCache*> fCaches;
|
||||
};
|
||||
|
||||
static void TestBitmapFactory(skiatest::Reporter* reporter) {
|
||||
SkAutoTDelete<SkBitmap> bitmap(create_bitmap());
|
||||
SkASSERT(bitmap.get() != NULL);
|
||||
|
||||
SkAutoDataUnref encodedBitmap(create_data_from_bitmap(*bitmap.get()));
|
||||
if (encodedBitmap.get() == NULL) {
|
||||
// Encoding failed.
|
||||
return;
|
||||
}
|
||||
bool encodeSucceeded = encodedBitmap.get() != NULL;
|
||||
SkASSERT(encodeSucceeded);
|
||||
|
||||
ImageCacheHolder cacheHolder;
|
||||
|
||||
SkAutoTUnref<SkLruImageCache> lruCache(SkNEW_ARGS(SkLruImageCache, (1024 * 1024)));
|
||||
test_cache(reporter, lruCache, encodedBitmap, *bitmap.get());
|
||||
test_cache(reporter, NULL, encodedBitmap, *bitmap.get());
|
||||
#ifdef SK_BUILD_FOR_ANDROID
|
||||
test_cache(reporter, SkAshmemImageCache::GetAshmemImageCache(), encodedBitmap, *bitmap.get());
|
||||
#endif
|
||||
cacheHolder.addImageCache(lruCache);
|
||||
|
||||
cacheHolder.addImageCache(NULL);
|
||||
|
||||
SkImageCache* purgeableCache = SkPurgeableImageCache::Create();
|
||||
if (purgeableCache != NULL) {
|
||||
cacheHolder.addImageCache(purgeableCache);
|
||||
purgeableCache->unref();
|
||||
}
|
||||
|
||||
for (int i = 0; i < cacheHolder.count(); i++) {
|
||||
SkImageCache* cache = cacheHolder.getAt(i);
|
||||
if (cache != NULL) {
|
||||
test_cache(reporter, cache);
|
||||
}
|
||||
if (encodeSucceeded) {
|
||||
test_factory(reporter, cache, encodedBitmap, *bitmap.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#include "TestClassDef.h"
|
||||
|
@ -11,7 +11,14 @@
|
||||
#include "PictureRenderer.h"
|
||||
#include "picture_utils.h"
|
||||
|
||||
#include "SkBitmapFactory.h"
|
||||
#include "SkData.h"
|
||||
#include "SkFlags.h"
|
||||
#include "SkImage.h"
|
||||
#include "SkImageDecoder.h"
|
||||
#include "SkLruImageCache.h"
|
||||
#include "SkPurgeableImageCache.h"
|
||||
#include "SkString.h"
|
||||
|
||||
// Alphabetized list of flags used by this file or bench_ and render_pictures.
|
||||
DEFINE_string(bbh, "none", "bbhType [width height]: Set the bounding box hierarchy type to "
|
||||
@ -57,6 +64,9 @@ DEFINE_string(r, "", "skp files or directories of skp files to process.");
|
||||
DEFINE_double(scale, 1, "Set the scale factor.");
|
||||
DEFINE_string(tiles, "", "Used with --mode copyTile to specify number of tiles per larger tile "
|
||||
"in the x and y directions.");
|
||||
DEFINE_bool(useVolatileCache, false, "Use a volatile cache for deferred image decoding pixels. "
|
||||
"Only meaningful if --deferImageDecoding is set to true and the platform has an "
|
||||
"implementation.");
|
||||
DEFINE_string(viewport, "", "width height: Set the viewport.");
|
||||
|
||||
sk_tools::PictureRenderer* parseRenderer(SkString& error, PictureTool tool) {
|
||||
@ -308,3 +318,52 @@ sk_tools::PictureRenderer* parseRenderer(SkString& error, PictureTool tool) {
|
||||
|
||||
return renderer.detach();
|
||||
}
|
||||
|
||||
SkLruImageCache gLruImageCache(1024*1024);
|
||||
|
||||
// Simple cache selector to choose between a purgeable cache for large images and the standard one
|
||||
// for smaller images.
|
||||
class MyCacheSelector : public SkBitmapFactory::CacheSelector {
|
||||
|
||||
public:
|
||||
MyCacheSelector() {
|
||||
fPurgeableImageCache = SkPurgeableImageCache::Create();
|
||||
}
|
||||
|
||||
~MyCacheSelector() {
|
||||
SkSafeUnref(fPurgeableImageCache);
|
||||
}
|
||||
|
||||
virtual SkImageCache* selectCache(const SkImage::Info& info) SK_OVERRIDE {
|
||||
if (info.fWidth * info.fHeight > 32 * 1024 && fPurgeableImageCache != NULL) {
|
||||
return fPurgeableImageCache;
|
||||
}
|
||||
return &gLruImageCache;
|
||||
}
|
||||
private:
|
||||
SkImageCache* fPurgeableImageCache;
|
||||
};
|
||||
|
||||
static MyCacheSelector gCacheSelector;
|
||||
static SkBitmapFactory gFactory(&SkImageDecoder::DecodeMemoryToTarget);
|
||||
|
||||
bool lazy_decode_bitmap(const void* buffer, size_t size, SkBitmap* bitmap);
|
||||
bool lazy_decode_bitmap(const void* buffer, size_t size, SkBitmap* bitmap) {
|
||||
void* copiedBuffer = sk_malloc_throw(size);
|
||||
memcpy(copiedBuffer, buffer, size);
|
||||
SkAutoDataUnref data(SkData::NewFromMalloc(copiedBuffer, size));
|
||||
|
||||
static bool gOnce;
|
||||
if (!gOnce) {
|
||||
// Only use the cache selector if there is a purgeable image cache to use for large
|
||||
// images.
|
||||
if (FLAGS_useVolatileCache && SkAutoTUnref<SkImageCache>(
|
||||
SkPurgeableImageCache::Create()).get() != NULL) {
|
||||
gFactory.setCacheSelector(&gCacheSelector);
|
||||
} else {
|
||||
gFactory.setImageCache(&gLruImageCache);
|
||||
}
|
||||
gOnce = true;
|
||||
}
|
||||
return gFactory.installPixelRef(data, bitmap);
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
#ifndef PICTURE_RENDERING_FLAGS
|
||||
#define PICTURE_RENDERING_FLAGS
|
||||
|
||||
#include "SkString.h"
|
||||
class SkString;
|
||||
|
||||
namespace sk_tools {
|
||||
class PictureRenderer;
|
||||
|
@ -10,16 +10,17 @@
|
||||
#include "PictureBenchmark.h"
|
||||
#include "PictureRenderingFlags.h"
|
||||
#include "SkBenchLogger.h"
|
||||
#include "SkBitmapFactory.h"
|
||||
#include "SkCanvas.h"
|
||||
#include "SkFlags.h"
|
||||
#include "SkGraphics.h"
|
||||
#include "SkImageDecoder.h"
|
||||
#if LAZY_CACHE_STATS
|
||||
#include "SkLazyPixelRef.h"
|
||||
#endif
|
||||
#include "SkLruImageCache.h"
|
||||
#include "SkMath.h"
|
||||
#include "SkOSFile.h"
|
||||
#include "SkPicture.h"
|
||||
#include "SkStream.h"
|
||||
#include "SkTArray.h"
|
||||
#include "picture_utils.h"
|
||||
|
||||
|
||||
@ -142,20 +143,9 @@ static SkString filterFlagsUsage() {
|
||||
return result;
|
||||
}
|
||||
|
||||
#include "SkData.h"
|
||||
#include "SkLruImageCache.h"
|
||||
#include "SkLazyPixelRef.h"
|
||||
|
||||
static SkLruImageCache gLruImageCache(1024*1024);
|
||||
|
||||
static bool lazy_decode_bitmap(const void* buffer, size_t size, SkBitmap* bitmap) {
|
||||
void* copiedBuffer = sk_malloc_throw(size);
|
||||
memcpy(copiedBuffer, buffer, size);
|
||||
SkAutoDataUnref data(SkData::NewFromMalloc(copiedBuffer, size));
|
||||
SkBitmapFactory factory(&SkImageDecoder::DecodeMemoryToTarget);
|
||||
factory.setImageCache(&gLruImageCache);
|
||||
return factory.installPixelRef(data, bitmap);
|
||||
}
|
||||
// These are defined in PictureRenderingFlags.cpp
|
||||
extern SkLruImageCache gLruImageCache;
|
||||
extern bool lazy_decode_bitmap(const void* buffer, size_t size, SkBitmap* bitmap);
|
||||
|
||||
#if LAZY_CACHE_STATS
|
||||
static int32_t gTotalCacheHits;
|
||||
|
@ -7,8 +7,6 @@
|
||||
|
||||
#include "CopyTilesRenderer.h"
|
||||
#include "SkBitmap.h"
|
||||
#include "SkBitmapFactory.h"
|
||||
#include "SkCanvas.h"
|
||||
#include "SkDevice.h"
|
||||
#include "SkFlags.h"
|
||||
#include "SkGraphics.h"
|
||||
@ -19,7 +17,6 @@
|
||||
#include "SkPicture.h"
|
||||
#include "SkStream.h"
|
||||
#include "SkString.h"
|
||||
#include "SkTArray.h"
|
||||
#include "PictureRenderer.h"
|
||||
#include "PictureRenderingFlags.h"
|
||||
#include "picture_utils.h"
|
||||
@ -45,36 +42,8 @@ static void make_output_filepath(SkString* path, const SkString& dir,
|
||||
path->remove(path->size() - 4, 4);
|
||||
}
|
||||
|
||||
#include "SkData.h"
|
||||
#include "SkLruImageCache.h"
|
||||
|
||||
static SkLruImageCache gLruImageCache(1024*1024);
|
||||
|
||||
#ifdef SK_BUILD_FOR_ANDROID
|
||||
#include "SkAshmemImageCache.h"
|
||||
#include "SkImage.h"
|
||||
|
||||
static SkImageCache* cache_selector(const SkImage::Info& info) {
|
||||
if (info.fWidth * info.fHeight > 32 * 1024) {
|
||||
return SkAshmemImageCache::GetAshmemImageCache();
|
||||
}
|
||||
return &gLruImageCache;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static bool lazy_decode_bitmap(const void* buffer, size_t size, SkBitmap* bitmap) {
|
||||
void* copiedBuffer = sk_malloc_throw(size);
|
||||
memcpy(copiedBuffer, buffer, size);
|
||||
SkAutoDataUnref data(SkData::NewFromMalloc(copiedBuffer, size));
|
||||
SkBitmapFactory factory(&SkImageDecoder::DecodeMemoryToTarget);
|
||||
#ifdef SK_BUILD_FOR_ANDROID
|
||||
factory.setCacheSelector(&cache_selector);
|
||||
#else
|
||||
factory.setImageCache(&gLruImageCache);
|
||||
#endif
|
||||
return factory.installPixelRef(data, bitmap);
|
||||
}
|
||||
// Defined in PictureRenderingFlags.cpp
|
||||
extern bool lazy_decode_bitmap(const void* buffer, size_t size, SkBitmap* bitmap);
|
||||
|
||||
static bool render_picture(const SkString& inputPath, const SkString* outputDir,
|
||||
sk_tools::PictureRenderer& renderer,
|
||||
|
Loading…
Reference in New Issue
Block a user