blob: 0f2c5e3c8e382f2f8e067ef376edafe555bd02d8 [file] [log] [blame]
/*
* 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);
}