зеркало из https://github.com/mozilla/moz-skia.git
Make SkGPipe use SkBitmapHeap.
In the single process (or hypothetical cross process/shared address space) mode, SkGPipe now uses SkBitmapHeap instead of SharedHeap. Still need to use the shared heap for shaders as well as for cross process. TEST=PipeTest Review URL: https://codereview.appspot.com/6461059 git-svn-id: http://skia.googlecode.com/svn/trunk@5008 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
Родитель
baa5d94ee3
Коммит
10dccde54a
|
@ -11,6 +11,7 @@
|
|||
'../src/core',
|
||||
'../src/effects',
|
||||
'../src/pdf',
|
||||
'../src/pipe/utils',
|
||||
'../src/utils',
|
||||
'../tools/',
|
||||
],
|
||||
|
@ -61,6 +62,7 @@
|
|||
'../tests/PathMeasureTest.cpp',
|
||||
'../tests/PathTest.cpp',
|
||||
'../tests/PDFPrimitivesTest.cpp',
|
||||
'../tests/PipeTest.cpp',
|
||||
'../tests/PictureUtilsTest.cpp',
|
||||
'../tests/PointTest.cpp',
|
||||
'../tests/PremulAlphaRoundTripTest.cpp',
|
||||
|
@ -90,6 +92,9 @@
|
|||
'../tests/WritePixelsTest.cpp',
|
||||
'../tests/Writer32Test.cpp',
|
||||
'../tests/XfermodeTest.cpp',
|
||||
|
||||
# Needed for PipeTest.
|
||||
'../src/pipe/utils/SamplePipeControllers.cpp',
|
||||
],
|
||||
'dependencies': [
|
||||
'core.gyp:core',
|
||||
|
|
|
@ -55,9 +55,26 @@ SkBitmapHeap::SkBitmapHeap(ExternalStorage* storage, int32_t preferredSize)
|
|||
, fPreferredCount(preferredSize)
|
||||
, fOwnerCount(IGNORE_OWNERS)
|
||||
, fBytesAllocated(0) {
|
||||
SkSafeRef(storage);
|
||||
}
|
||||
|
||||
SkBitmapHeap::~SkBitmapHeap() {
|
||||
SkDEBUGCODE(
|
||||
for (int i = 0; i < fStorage.count(); i++) {
|
||||
bool unused = false;
|
||||
for (int j = 0; j < fUnusedSlots.count(); j++) {
|
||||
if (fUnusedSlots[j] == fStorage[i]->fSlot) {
|
||||
unused = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!unused) {
|
||||
fBytesAllocated -= fStorage[i]->fBytesAllocated;
|
||||
}
|
||||
}
|
||||
fBytesAllocated -= (fStorage.count() * sizeof(SkBitmapHeapEntry));
|
||||
)
|
||||
SkASSERT(0 == fBytesAllocated);
|
||||
fStorage.deleteAll();
|
||||
SkSafeUnref(fExternalStorage);
|
||||
}
|
||||
|
@ -132,15 +149,53 @@ SkBitmapHeapEntry* SkBitmapHeap::findEntryToReplace(const SkBitmap& replacement)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
int SkBitmapHeap::findInLookupTable(const SkBitmap& bitmap, SkBitmapHeapEntry** entry) {
|
||||
LookupEntry indexEntry;
|
||||
indexEntry.fGenerationId = bitmap.getGenerationID();
|
||||
indexEntry.fPixelOffset = bitmap.pixelRefOffset();
|
||||
indexEntry.fWidth = bitmap.width();
|
||||
indexEntry.fHeight = bitmap.height();
|
||||
size_t SkBitmapHeap::freeMemoryIfPossible(size_t bytesToFree) {
|
||||
if (UNLIMITED_SIZE == fPreferredCount) {
|
||||
return 0;
|
||||
}
|
||||
SkBitmapHeapEntry* iter = fLeastRecentlyUsed;
|
||||
size_t origBytesAllocated = fBytesAllocated;
|
||||
// Purge starting from LRU until a non-evictable bitmap is found or until
|
||||
// everything is evicted.
|
||||
while (iter && 0 == iter->fRefCount) {
|
||||
SkBitmapHeapEntry* next = iter->fMoreRecentlyUsed;
|
||||
this->removeEntryFromLookupTable(*iter);
|
||||
// Free the pixel memory. removeEntryFromLookupTable already reduced
|
||||
// fBytesAllocated properly.
|
||||
iter->fBitmap.reset();
|
||||
// Add to list of unused slots which can be reused in the future.
|
||||
fUnusedSlots.push(iter->fSlot);
|
||||
// Remove its LRU pointers, so that it does not pretend it is already in
|
||||
// the list the next time it is used.
|
||||
iter->fMoreRecentlyUsed = iter->fLessRecentlyUsed = NULL;
|
||||
iter = next;
|
||||
if (origBytesAllocated - fBytesAllocated >= bytesToFree) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fLeastRecentlyUsed != iter) {
|
||||
// There was at least one eviction.
|
||||
fLeastRecentlyUsed = iter;
|
||||
if (NULL == fLeastRecentlyUsed) {
|
||||
// Everything was evicted
|
||||
fMostRecentlyUsed = NULL;
|
||||
fBytesAllocated -= (fStorage.count() * sizeof(SkBitmapHeapEntry));
|
||||
fStorage.deleteAll();
|
||||
fUnusedSlots.reset();
|
||||
SkASSERT(0 == fBytesAllocated);
|
||||
} else {
|
||||
fLeastRecentlyUsed->fLessRecentlyUsed = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return origBytesAllocated - fBytesAllocated;
|
||||
}
|
||||
|
||||
int SkBitmapHeap::findInLookupTable(const LookupEntry& indexEntry, SkBitmapHeapEntry** entry) {
|
||||
int index = SkTSearch<const LookupEntry>(fLookupTable.begin(),
|
||||
fLookupTable.count(),
|
||||
indexEntry, sizeof(indexEntry));
|
||||
fLookupTable.count(),
|
||||
indexEntry, sizeof(indexEntry));
|
||||
|
||||
if (index < 0) {
|
||||
// insert ourselves into the bitmapIndex
|
||||
|
@ -174,9 +229,25 @@ bool SkBitmapHeap::copyBitmap(const SkBitmap& originalBitmap, SkBitmap& copiedBi
|
|||
return true;
|
||||
}
|
||||
|
||||
int SkBitmapHeap::removeEntryFromLookupTable(const SkBitmapHeapEntry& entry) {
|
||||
// remove the bitmap index for the deleted entry
|
||||
SkDEBUGCODE(int count = fLookupTable.count();)
|
||||
// FIXME: If copying bitmaps retained the generation ID, we could
|
||||
// just grab the generation ID from entry.fBitmap
|
||||
LookupEntry key(entry.fBitmap, entry.fGenerationID);
|
||||
int index = this->findInLookupTable(key, NULL);
|
||||
// Verify that findInLookupTable found an existing entry rather than adding
|
||||
// a new entry to the lookup table.
|
||||
SkASSERT(count == fLookupTable.count());
|
||||
|
||||
fLookupTable.remove(index);
|
||||
fBytesAllocated -= entry.fBytesAllocated;
|
||||
return index;
|
||||
}
|
||||
|
||||
int32_t SkBitmapHeap::insert(const SkBitmap& originalBitmap) {
|
||||
SkBitmapHeapEntry* entry = NULL;
|
||||
int searchIndex = this->findInLookupTable(originalBitmap, &entry);
|
||||
int searchIndex = this->findInLookupTable(LookupEntry(originalBitmap), &entry);
|
||||
|
||||
// check to see if we already had a copy of the bitmap in the heap
|
||||
if (entry) {
|
||||
|
@ -195,13 +266,7 @@ int32_t SkBitmapHeap::insert(const SkBitmap& originalBitmap) {
|
|||
entry = this->findEntryToReplace(originalBitmap);
|
||||
// we found an entry to evict
|
||||
if (entry) {
|
||||
// remove the bitmap index for the deleted entry
|
||||
SkDEBUGCODE(int count = fLookupTable.count();)
|
||||
int index = findInLookupTable(entry->fBitmap, NULL);
|
||||
SkASSERT(count == fLookupTable.count());
|
||||
|
||||
fLookupTable.remove(index);
|
||||
fBytesAllocated -= entry->fBytesAllocated;
|
||||
int index = this->removeEntryFromLookupTable(*entry);
|
||||
|
||||
// update the current search index now that we have removed one
|
||||
if (index < searchIndex) {
|
||||
|
@ -212,10 +277,16 @@ int32_t SkBitmapHeap::insert(const SkBitmap& originalBitmap) {
|
|||
|
||||
// if we didn't have an entry yet we need to create one
|
||||
if (!entry) {
|
||||
entry = SkNEW(SkBitmapHeapEntry);
|
||||
fStorage.append(1, &entry);
|
||||
entry->fSlot = fStorage.count() - 1;
|
||||
fBytesAllocated += sizeof(SkBitmapHeapEntry);
|
||||
if (fPreferredCount != UNLIMITED_SIZE && fUnusedSlots.count() > 0) {
|
||||
int slot;
|
||||
fUnusedSlots.pop(&slot);
|
||||
entry = fStorage[slot];
|
||||
} else {
|
||||
entry = SkNEW(SkBitmapHeapEntry);
|
||||
fStorage.append(1, &entry);
|
||||
entry->fSlot = fStorage.count() - 1;
|
||||
fBytesAllocated += sizeof(SkBitmapHeapEntry);
|
||||
}
|
||||
}
|
||||
|
||||
// create a copy of the bitmap
|
||||
|
@ -230,9 +301,13 @@ int32_t SkBitmapHeap::insert(const SkBitmap& originalBitmap) {
|
|||
if (!copySucceeded) {
|
||||
// delete the index
|
||||
fLookupTable.remove(searchIndex);
|
||||
// free the slot
|
||||
fStorage.remove(entry->fSlot);
|
||||
SkDELETE(entry);
|
||||
// If entry is the last slot in storage, it is safe to delete it.
|
||||
if (fStorage.count() - 1 == entry->fSlot) {
|
||||
// free the slot
|
||||
fStorage.remove(entry->fSlot);
|
||||
fBytesAllocated -= sizeof(SkBitmapHeapEntry);
|
||||
SkDELETE(entry);
|
||||
}
|
||||
return INVALID_SLOT;
|
||||
}
|
||||
|
||||
|
@ -248,6 +323,8 @@ int32_t SkBitmapHeap::insert(const SkBitmap& originalBitmap) {
|
|||
// add the bytes from this entry to the total count
|
||||
fBytesAllocated += entry->fBytesAllocated;
|
||||
|
||||
entry->fGenerationID = originalBitmap.getGenerationID();
|
||||
|
||||
if (fOwnerCount != IGNORE_OWNERS) {
|
||||
entry->addReferences(fOwnerCount);
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ private:
|
|||
|
||||
int32_t fSlot;
|
||||
int32_t fRefCount;
|
||||
uint32_t fGenerationID;
|
||||
|
||||
SkBitmap fBitmap;
|
||||
// Keep track of the bytes allocated for this bitmap. When replacing the
|
||||
|
@ -186,8 +187,22 @@ public:
|
|||
return fBytesAllocated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to reduce the storage allocated.
|
||||
* @param bytesToFree minimum number of bytes that should be attempted to
|
||||
* be freed.
|
||||
* @return number of bytes actually freed.
|
||||
*/
|
||||
size_t freeMemoryIfPossible(size_t bytesToFree);
|
||||
|
||||
private:
|
||||
struct LookupEntry {
|
||||
LookupEntry(const SkBitmap& bm, uint32_t genId = 0) {
|
||||
fGenerationId = 0 == genId ? bm.getGenerationID() : genId;
|
||||
fPixelOffset = bm.pixelRefOffset();
|
||||
fWidth = bm.width();
|
||||
fHeight = bm.height();
|
||||
}
|
||||
uint32_t fGenerationId; // SkPixelRef GenerationID.
|
||||
size_t fPixelOffset;
|
||||
uint32_t fWidth;
|
||||
|
@ -214,15 +229,21 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove the entry from the lookup table.
|
||||
* @return The index in the lookup table of the entry before removal.
|
||||
*/
|
||||
int removeEntryFromLookupTable(const SkBitmapHeapEntry&);
|
||||
|
||||
/**
|
||||
* Searches for the bitmap in the lookup table and returns the bitmaps index within the table.
|
||||
* If the bitmap was not already in the table it is added.
|
||||
*
|
||||
* @param bitmap The bitmap we using as a key to search the lookup table
|
||||
* @param key The key to search the lookup table, created from a bitmap.
|
||||
* @param entry A pointer to a SkBitmapHeapEntry* that if non-null AND the bitmap is found
|
||||
* in the lookup table is populated with the entry from the heap storage.
|
||||
*/
|
||||
int findInLookupTable(const SkBitmap& bitmap, SkBitmapHeapEntry** entry);
|
||||
int findInLookupTable(const LookupEntry& key, SkBitmapHeapEntry** entry);
|
||||
|
||||
SkBitmapHeapEntry* findEntryToReplace(const SkBitmap& replacement);
|
||||
bool copyBitmap(const SkBitmap& originalBitmap, SkBitmap& copiedBitmap);
|
||||
|
@ -233,6 +254,9 @@ private:
|
|||
|
||||
// heap storage
|
||||
SkTDArray<SkBitmapHeapEntry*> fStorage;
|
||||
// Used to mark slots in fStorage as deleted without actually deleting
|
||||
// the slot so as not to mess up the numbering.
|
||||
SkTDArray<int> fUnusedSlots;
|
||||
ExternalStorage* fExternalStorage;
|
||||
|
||||
SkBitmapHeapEntry* fMostRecentlyUsed;
|
||||
|
|
|
@ -74,6 +74,7 @@ enum DrawOps {
|
|||
|
||||
// these are signals to playback, not drawing verbs
|
||||
kReportFlags_DrawOp,
|
||||
kShareHeap_DrawOp,
|
||||
kDone_DrawOp,
|
||||
};
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*/
|
||||
|
||||
|
||||
|
||||
#include "SkBitmapHeap.h"
|
||||
#include "SkCanvas.h"
|
||||
#include "SkPaint.h"
|
||||
#include "SkGPipe.h"
|
||||
|
@ -128,12 +128,22 @@ public:
|
|||
return fBitmaps[index - 1];
|
||||
}
|
||||
|
||||
void setSharedHeap(SkBitmapHeap* heap) {
|
||||
SkASSERT(!shouldFlattenBitmaps(fFlags) || NULL == heap);
|
||||
SkRefCnt_SafeAssign(fSharedHeap, heap);
|
||||
}
|
||||
|
||||
SkBitmapHeap* getSharedHeap() const {
|
||||
return fSharedHeap;
|
||||
}
|
||||
|
||||
void addTypeface() {
|
||||
size_t size = fReader->read32();
|
||||
const void* data = fReader->skip(SkAlign4(size));
|
||||
SkMemoryStream stream(data, size, false);
|
||||
*fTypefaces.append() = SkTypeface::Deserialize(&stream);
|
||||
}
|
||||
|
||||
void setTypeface(SkPaint* paint, unsigned id) {
|
||||
paint->setTypeface(id ? fTypefaces[id - 1] : NULL);
|
||||
}
|
||||
|
@ -158,6 +168,8 @@ private:
|
|||
SkTDArray<SkTypeface*> fTypefaces;
|
||||
SkTDArray<SkFlattenable::Factory> fFactoryArray;
|
||||
SkTDArray<SkBitmap*> fBitmaps;
|
||||
// Only used when sharing bitmaps with the writer.
|
||||
SkBitmapHeap* fSharedHeap;
|
||||
unsigned fFlags;
|
||||
};
|
||||
|
||||
|
@ -389,26 +401,27 @@ class BitmapHolder : SkNoncopyable {
|
|||
public:
|
||||
BitmapHolder(SkReader32* reader, uint32_t op32, SkGPipeState* state);
|
||||
~BitmapHolder() {
|
||||
if (fInfo != NULL) {
|
||||
fInfo->decDraws();
|
||||
if (fHeapEntry != NULL) {
|
||||
fHeapEntry->releaseRef();
|
||||
}
|
||||
}
|
||||
const SkBitmap* getBitmap() {
|
||||
return fBitmap;
|
||||
}
|
||||
private:
|
||||
BitmapInfo* fInfo;
|
||||
const SkBitmap* fBitmap;
|
||||
SkBitmapHeapEntry* fHeapEntry;
|
||||
const SkBitmap* fBitmap;
|
||||
};
|
||||
|
||||
BitmapHolder::BitmapHolder(SkReader32* reader, uint32_t op32,
|
||||
SkGPipeState* state) {
|
||||
unsigned index = DrawOp_unpackData(op32);
|
||||
if (shouldFlattenBitmaps(state->getFlags())) {
|
||||
fInfo = NULL;
|
||||
fBitmap = state->getBitmap(DrawOp_unpackData(op32));
|
||||
fHeapEntry = NULL;
|
||||
fBitmap = state->getBitmap(index);
|
||||
} else {
|
||||
fInfo = static_cast<BitmapInfo*>(reader->readPtr());
|
||||
fBitmap = fInfo->fBitmap;
|
||||
fHeapEntry = state->getSharedHeap()->getEntry(index);
|
||||
fBitmap = fHeapEntry->getBitmap();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -571,12 +584,17 @@ static void skip_rp(SkCanvas*, SkReader32* reader, uint32_t op32, SkGPipeState*)
|
|||
(void)reader->skip(bytes);
|
||||
}
|
||||
|
||||
static void reportflags_rp(SkCanvas*, SkReader32*, uint32_t op32,
|
||||
static void reportFlags_rp(SkCanvas*, SkReader32*, uint32_t op32,
|
||||
SkGPipeState* state) {
|
||||
unsigned flags = DrawOp_unpackFlags(op32);
|
||||
state->setFlags(flags);
|
||||
}
|
||||
|
||||
static void shareHeap_rp(SkCanvas*, SkReader32* reader, uint32_t,
|
||||
SkGPipeState* state) {
|
||||
state->setSharedHeap(static_cast<SkBitmapHeap*>(reader->readPtr()));
|
||||
}
|
||||
|
||||
static void done_rp(SkCanvas*, SkReader32*, uint32_t, SkGPipeState*) {}
|
||||
|
||||
typedef void (*ReadProc)(SkCanvas*, SkReader32*, uint32_t op32, SkGPipeState*);
|
||||
|
@ -620,18 +638,20 @@ static const ReadProc gReadTable[] = {
|
|||
def_Bitmap_rp,
|
||||
def_Factory_rp,
|
||||
|
||||
reportflags_rp,
|
||||
reportFlags_rp,
|
||||
shareHeap_rp,
|
||||
done_rp
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
SkGPipeState::SkGPipeState(): fReader(0), fFlags(0) {}
|
||||
SkGPipeState::SkGPipeState(): fReader(0), fFlags(0), fSharedHeap(NULL) {}
|
||||
|
||||
SkGPipeState::~SkGPipeState() {
|
||||
fTypefaces.safeUnrefAll();
|
||||
fFlatArray.safeUnrefAll();
|
||||
fBitmaps.deleteAll();
|
||||
SkSafeUnref(fSharedHeap);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -6,27 +6,26 @@
|
|||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include "SkBitmapHeap.h"
|
||||
#include "SkCanvas.h"
|
||||
#include "SkColorFilter.h"
|
||||
#include "SkData.h"
|
||||
#include "SkDrawLooper.h"
|
||||
#include "SkDevice.h"
|
||||
#include "SkPaint.h"
|
||||
#include "SkPathEffect.h"
|
||||
#include "SkGPipe.h"
|
||||
#include "SkGPipePriv.h"
|
||||
#include "SkImageFilter.h"
|
||||
#include "SkMaskFilter.h"
|
||||
#include "SkOrderedWriteBuffer.h"
|
||||
#include "SkPaint.h"
|
||||
#include "SkPathEffect.h"
|
||||
#include "SkPictureFlat.h"
|
||||
#include "SkRasterizer.h"
|
||||
#include "SkShader.h"
|
||||
#include "SkStream.h"
|
||||
#include "SkTSearch.h"
|
||||
#include "SkTypeface.h"
|
||||
#include "SkWriter32.h"
|
||||
#include "SkColorFilter.h"
|
||||
#include "SkDrawLooper.h"
|
||||
#include "SkMaskFilter.h"
|
||||
#include "SkRasterizer.h"
|
||||
#include "SkShader.h"
|
||||
#include "SkOrderedWriteBuffer.h"
|
||||
#include "SkPictureFlat.h"
|
||||
|
||||
static bool isCrossProcess(uint32_t flags) {
|
||||
return SkToBool(flags & SkGPipeWriter::kCrossProcess_Flag);
|
||||
|
@ -154,254 +153,6 @@ public:
|
|||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
* Shared heap for storing large things that can be shared, for a stream
|
||||
* used by multiple readers.
|
||||
* TODO: Make the allocations all come from cross process safe address space
|
||||
* TODO: Store paths (others?)
|
||||
* TODO: Generalize the LRU caching mechanism
|
||||
*/
|
||||
class SharedHeap {
|
||||
public:
|
||||
SharedHeap(bool shallow, int numOfReaders)
|
||||
: fBitmapCount(0)
|
||||
, fMostRecentlyUsed(NULL)
|
||||
, fLeastRecentlyUsed(NULL)
|
||||
, fCanDoShallowCopies(shallow)
|
||||
, fNumberOfReaders(numOfReaders)
|
||||
, fBytesAllocated(0) {}
|
||||
~SharedHeap() {
|
||||
BitmapInfo* iter = fMostRecentlyUsed;
|
||||
while (iter != NULL) {
|
||||
SkDEBUGCODE(fBytesAllocated -= (iter->fBytesAllocated + sizeof(BitmapInfo)));
|
||||
BitmapInfo* next = iter->fLessRecentlyUsed;
|
||||
SkDELETE(iter);
|
||||
fBitmapCount--;
|
||||
iter = next;
|
||||
}
|
||||
SkASSERT(0 == fBitmapCount);
|
||||
SkASSERT(0 == fBytesAllocated);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the approximate number of bytes allocated.
|
||||
*
|
||||
* Not exact. Some SkBitmaps may share SkPixelRefs, in which case only one
|
||||
* SkBitmap will take the size of the SkPixelRef into account (the first
|
||||
* one). It is possible that the one which accounts for the SkPixelRef has
|
||||
* been removed, in which case we will no longer be counting those bytes.
|
||||
*/
|
||||
size_t bytesAllocated() { return fBytesAllocated; }
|
||||
|
||||
/*
|
||||
* Add a copy of a bitmap to the heap.
|
||||
* @param bm The SkBitmap to be copied and placed in the heap.
|
||||
* @return void* Pointer to the BitmapInfo stored in the heap, which
|
||||
* contains a copy of the SkBitmap. If NULL,
|
||||
* the bitmap could not be copied.
|
||||
*/
|
||||
const void* addBitmap(const SkBitmap& orig) {
|
||||
const uint32_t genID = orig.getGenerationID();
|
||||
SkPixelRef* sharedPixelRef = NULL;
|
||||
// When looking to see if we've previously used this bitmap, start at
|
||||
// the end, assuming that the caller is more likely to reuse a recent
|
||||
// one.
|
||||
BitmapInfo* iter = fMostRecentlyUsed;
|
||||
while (iter != NULL) {
|
||||
if (genID == iter->fGenID) {
|
||||
SkBitmap* storedBitmap = iter->fBitmap;
|
||||
// TODO: Perhaps we can share code with
|
||||
// SkPictureRecord::PixelRefDictionaryEntry/
|
||||
// BitmapIndexCacheEntry so we can do a binary search for a
|
||||
// matching bitmap
|
||||
if (orig.pixelRefOffset() != storedBitmap->pixelRefOffset()
|
||||
|| orig.width() != storedBitmap->width()
|
||||
|| orig.height() != storedBitmap->height()) {
|
||||
// In this case, the bitmaps share a pixelRef, but have
|
||||
// different offsets or sizes. Keep track of the other
|
||||
// bitmap so that instead of making another copy of the
|
||||
// pixelRef we can use the copy we already made.
|
||||
sharedPixelRef = storedBitmap->pixelRef();
|
||||
break;
|
||||
}
|
||||
iter->addDraws(fNumberOfReaders);
|
||||
this->setMostRecentlyUsed(iter);
|
||||
return iter;
|
||||
}
|
||||
iter = iter->fLessRecentlyUsed;
|
||||
}
|
||||
SkAutoRef ar((SkRefCnt*)sharedPixelRef);
|
||||
BitmapInfo* replace = this->bitmapToReplace(orig);
|
||||
SkBitmap* copy;
|
||||
// If the bitmap is mutable, we still need to do a deep copy, since the
|
||||
// caller may modify it afterwards. That said, if the bitmap is mutable,
|
||||
// but has no pixelRef, the copy constructor actually does a deep copy.
|
||||
if (fCanDoShallowCopies && (orig.isImmutable() || !orig.pixelRef())) {
|
||||
if (NULL == replace) {
|
||||
copy = SkNEW_ARGS(SkBitmap, (orig));
|
||||
} else {
|
||||
*replace->fBitmap = orig;
|
||||
}
|
||||
} else {
|
||||
if (sharedPixelRef != NULL) {
|
||||
if (NULL == replace) {
|
||||
// Do a shallow copy of the bitmap to get the width, height, etc
|
||||
copy = SkNEW_ARGS(SkBitmap, (orig));
|
||||
// Replace the pixelRef with the copy that was already made, and
|
||||
// use the appropriate offset.
|
||||
copy->setPixelRef(sharedPixelRef, orig.pixelRefOffset());
|
||||
} else {
|
||||
*replace->fBitmap = orig;
|
||||
replace->fBitmap->setPixelRef(sharedPixelRef, orig.pixelRefOffset());
|
||||
}
|
||||
} else {
|
||||
if (NULL == replace) {
|
||||
copy = SkNEW(SkBitmap);
|
||||
if (!orig.copyTo(copy, orig.getConfig())) {
|
||||
delete copy;
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
if (!orig.copyTo(replace->fBitmap, orig.getConfig())) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BitmapInfo* info;
|
||||
if (NULL == replace) {
|
||||
fBytesAllocated += sizeof(BitmapInfo);
|
||||
info = SkNEW_ARGS(BitmapInfo, (copy, genID, fNumberOfReaders));
|
||||
fBitmapCount++;
|
||||
} else {
|
||||
fBytesAllocated -= replace->fBytesAllocated;
|
||||
replace->fGenID = genID;
|
||||
replace->addDraws(fNumberOfReaders);
|
||||
info = replace;
|
||||
}
|
||||
// Always include the size of the SkBitmap struct.
|
||||
info->fBytesAllocated = sizeof(SkBitmap);
|
||||
// If the SkBitmap does not share an SkPixelRef with an SkBitmap already
|
||||
// in the SharedHeap, also include the size of its pixels.
|
||||
if (NULL == sharedPixelRef) {
|
||||
info->fBytesAllocated += orig.getSize();
|
||||
}
|
||||
fBytesAllocated += info->fBytesAllocated;
|
||||
this->setMostRecentlyUsed(info);
|
||||
return info;
|
||||
}
|
||||
|
||||
size_t freeMemoryIfPossible(size_t bytesToFree) {
|
||||
BitmapInfo* info = fLeastRecentlyUsed;
|
||||
size_t origBytesAllocated = fBytesAllocated;
|
||||
// Purge starting from LRU until a non-evictable bitmap is found
|
||||
// or until everything is evicted.
|
||||
while (info && info->drawCount() == 0) {
|
||||
fBytesAllocated -= (info->fBytesAllocated + sizeof(BitmapInfo));
|
||||
fBitmapCount--;
|
||||
BitmapInfo* nextInfo = info->fMoreRecentlyUsed;
|
||||
SkDELETE(info);
|
||||
info = nextInfo;
|
||||
if ((origBytesAllocated - fBytesAllocated) >= bytesToFree) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fLeastRecentlyUsed != info) { // at least one eviction
|
||||
fLeastRecentlyUsed = info;
|
||||
if (NULL != fLeastRecentlyUsed) {
|
||||
fLeastRecentlyUsed->fLessRecentlyUsed = NULL;
|
||||
} else {
|
||||
// everything was evicted
|
||||
fMostRecentlyUsed = NULL;
|
||||
SkASSERT(0 == fBytesAllocated);
|
||||
SkASSERT(0 == fBitmapCount);
|
||||
}
|
||||
}
|
||||
|
||||
return origBytesAllocated - fBytesAllocated;
|
||||
}
|
||||
|
||||
private:
|
||||
void setMostRecentlyUsed(BitmapInfo* info);
|
||||
BitmapInfo* bitmapToReplace(const SkBitmap& bm) const;
|
||||
|
||||
int fBitmapCount;
|
||||
BitmapInfo* fLeastRecentlyUsed;
|
||||
BitmapInfo* fMostRecentlyUsed;
|
||||
const bool fCanDoShallowCopies;
|
||||
const int fNumberOfReaders;
|
||||
size_t fBytesAllocated;
|
||||
};
|
||||
|
||||
// We just "used" info. Update our LRU accordingly
|
||||
void SharedHeap::setMostRecentlyUsed(BitmapInfo* info) {
|
||||
SkASSERT(info != NULL);
|
||||
if (info == fMostRecentlyUsed) {
|
||||
return;
|
||||
}
|
||||
// Remove info from its prior place, and make sure to cover the hole.
|
||||
if (fLeastRecentlyUsed == info) {
|
||||
SkASSERT(info->fMoreRecentlyUsed != NULL);
|
||||
fLeastRecentlyUsed = info->fMoreRecentlyUsed;
|
||||
}
|
||||
if (info->fMoreRecentlyUsed != NULL) {
|
||||
SkASSERT(fMostRecentlyUsed != info);
|
||||
info->fMoreRecentlyUsed->fLessRecentlyUsed = info->fLessRecentlyUsed;
|
||||
}
|
||||
if (info->fLessRecentlyUsed != NULL) {
|
||||
SkASSERT(fLeastRecentlyUsed != info);
|
||||
info->fLessRecentlyUsed->fMoreRecentlyUsed = info->fMoreRecentlyUsed;
|
||||
}
|
||||
info->fMoreRecentlyUsed = NULL;
|
||||
// Set up the head and tail pointers properly.
|
||||
if (fMostRecentlyUsed != NULL) {
|
||||
SkASSERT(NULL == fMostRecentlyUsed->fMoreRecentlyUsed);
|
||||
fMostRecentlyUsed->fMoreRecentlyUsed = info;
|
||||
info->fLessRecentlyUsed = fMostRecentlyUsed;
|
||||
}
|
||||
fMostRecentlyUsed = info;
|
||||
if (NULL == fLeastRecentlyUsed) {
|
||||
fLeastRecentlyUsed = info;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a new bitmap to be added to the cache, return an existing one that
|
||||
* should be removed to make room, or NULL if there is already room.
|
||||
*/
|
||||
BitmapInfo* SharedHeap::bitmapToReplace(const SkBitmap& bm) const {
|
||||
// Arbitrarily set a limit of 5. We should test to find the best tradeoff
|
||||
// between time and space. A lower limit means that we use less space, but
|
||||
// it also means that we may have to insert the same bitmap into the heap
|
||||
// multiple times (depending on the input), potentially taking more time.
|
||||
// On the other hand, a lower limit also means searching through our stored
|
||||
// bitmaps takes less time.
|
||||
if (fBitmapCount > 5) {
|
||||
BitmapInfo* iter = fLeastRecentlyUsed;
|
||||
while (iter != NULL) {
|
||||
if (iter->drawCount() > 0) {
|
||||
// If the least recently used bitmap has not been drawn by some
|
||||
// reader, then a more recently used one will not have been
|
||||
// drawn yet either.
|
||||
return NULL;
|
||||
}
|
||||
if (bm.pixelRef() != NULL
|
||||
&& bm.pixelRef() == iter->fBitmap->pixelRef()) {
|
||||
// Do not replace a bitmap with a new one using the same
|
||||
// pixel ref. Instead look for a different one that will
|
||||
// potentially free up more space.
|
||||
iter = iter->fMoreRecentlyUsed;
|
||||
} else {
|
||||
return iter;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class SkGPipeCanvas : public SkCanvas {
|
||||
public:
|
||||
SkGPipeCanvas(SkGPipeController*, SkWriter32*, uint32_t flags);
|
||||
|
@ -421,7 +172,12 @@ public:
|
|||
size_t freeMemoryIfPossible(size_t bytesToFree);
|
||||
|
||||
size_t storageAllocatedForRecording() {
|
||||
return fSharedHeap.bytesAllocated();
|
||||
// FIXME: This can be removed once fSharedHeap is used by cross process
|
||||
// case.
|
||||
if (NULL == fSharedHeap) {
|
||||
return 0;
|
||||
}
|
||||
return fSharedHeap->bytesAllocated();
|
||||
}
|
||||
|
||||
// overrides from SkCanvas
|
||||
|
@ -481,7 +237,7 @@ private:
|
|||
};
|
||||
SkNamedFactorySet* fFactorySet;
|
||||
int fFirstSaveLayerStackLevel;
|
||||
SharedHeap fSharedHeap;
|
||||
SkBitmapHeap* fSharedHeap;
|
||||
SkGPipeController* fController;
|
||||
SkWriter32& fWriter;
|
||||
size_t fBlockSize; // amount allocated for writer
|
||||
|
@ -628,7 +384,6 @@ int SkGPipeCanvas::flattenToIndex(SkFlattenable* obj, PaintFlats paintflat) {
|
|||
SkGPipeCanvas::SkGPipeCanvas(SkGPipeController* controller,
|
||||
SkWriter32* writer, uint32_t flags)
|
||||
: fFactorySet(isCrossProcess(flags) ? SkNEW(SkNamedFactorySet) : NULL)
|
||||
, fSharedHeap(!isCrossProcess(flags), controller->numberOfReaders())
|
||||
, fWriter(*writer)
|
||||
, fFlags(flags)
|
||||
, fBitmapHeap(BITMAPS_TO_KEEP, fFactorySet)
|
||||
|
@ -649,15 +404,31 @@ SkGPipeCanvas::SkGPipeCanvas(SkGPipeController* controller,
|
|||
bitmap.setConfig(SkBitmap::kARGB_8888_Config, 32767, 32767);
|
||||
SkDevice* device = SkNEW_ARGS(SkDevice, (bitmap));
|
||||
this->setDevice(device)->unref();
|
||||
|
||||
// Tell the reader the appropriate flags to use.
|
||||
if (this->needOpBytes()) {
|
||||
this->writeOp(kReportFlags_DrawOp, fFlags, 0);
|
||||
}
|
||||
|
||||
if (shouldFlattenBitmaps(flags)) {
|
||||
// TODO: Use the shared heap for cross process case as well.
|
||||
fSharedHeap = NULL;
|
||||
} else {
|
||||
fSharedHeap = SkNEW_ARGS(SkBitmapHeap, (5, controller->numberOfReaders()));
|
||||
if (this->needOpBytes(sizeof(void*))) {
|
||||
this->writeOp(kShareHeap_DrawOp);
|
||||
fWriter.writePtr(static_cast<void*>(fSharedHeap));
|
||||
}
|
||||
}
|
||||
this->doNotify();
|
||||
}
|
||||
|
||||
SkGPipeCanvas::~SkGPipeCanvas() {
|
||||
this->finish();
|
||||
SkSafeUnref(fFactorySet);
|
||||
// FIXME: This can be changed to unref() once fSharedHeap is used by cross
|
||||
// process case.
|
||||
SkSafeUnref(fSharedHeap);
|
||||
}
|
||||
|
||||
bool SkGPipeCanvas::needOpBytes(size_t needed) {
|
||||
|
@ -931,17 +702,16 @@ bool SkGPipeCanvas::commonDrawBitmapHeap(const SkBitmap& bm, DrawOps op,
|
|||
unsigned flags,
|
||||
size_t opBytesNeeded,
|
||||
const SkPaint* paint) {
|
||||
const void* ptr = fSharedHeap.addBitmap(bm);
|
||||
if (NULL == ptr) {
|
||||
int32_t bitmapIndex = fSharedHeap->insert(bm);
|
||||
if (SkBitmapHeap::INVALID_SLOT == bitmapIndex) {
|
||||
return false;
|
||||
}
|
||||
if (paint != NULL) {
|
||||
flags |= kDrawBitmap_HasPaint_DrawOpsFlag;
|
||||
this->writePaint(*paint);
|
||||
}
|
||||
if (this->needOpBytes(opBytesNeeded + sizeof(void*))) {
|
||||
this->writeOp(op, flags, 0);
|
||||
fWriter.writePtr(const_cast<void*>(ptr));
|
||||
if (this->needOpBytes(opBytesNeeded)) {
|
||||
this->writeOp(op, flags, bitmapIndex);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -1190,7 +960,12 @@ void SkGPipeCanvas::flushRecording(bool detachCurrentBlock) {
|
|||
}
|
||||
|
||||
size_t SkGPipeCanvas::freeMemoryIfPossible(size_t bytesToFree) {
|
||||
return fSharedHeap.freeMemoryIfPossible(bytesToFree);
|
||||
// FIXME: This can be removed once fSharedHeap is used by cross process
|
||||
// case.
|
||||
if (NULL == fSharedHeap) {
|
||||
return 0;
|
||||
}
|
||||
return fSharedHeap->freeMemoryIfPossible(bytesToFree);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
|
||||
/*
|
||||
* Copyright 2012 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "SamplePipeControllers.h"
|
||||
#include "SkBitmap.h"
|
||||
#include "SkCanvas.h"
|
||||
#include "SkGPipe.h"
|
||||
#include "Test.h"
|
||||
|
||||
// Ensures that the pipe gracefully handles drawing an invalid bitmap.
|
||||
static void testDrawingBadBitmap(skiatest::Reporter* reporter, SkCanvas* pipeCanvas) {
|
||||
SkBitmap badBitmap;
|
||||
badBitmap.setConfig(SkBitmap::kNo_Config, 5, 5);
|
||||
pipeCanvas->drawBitmap(badBitmap, 0, 0);
|
||||
}
|
||||
|
||||
static void test_pipeTests(skiatest::Reporter* reporter) {
|
||||
SkBitmap bitmap;
|
||||
bitmap.setConfig(SkBitmap::kARGB_8888_Config, 64, 64);
|
||||
SkCanvas canvas(bitmap);
|
||||
|
||||
PipeController pipeController(&canvas);
|
||||
SkGPipeWriter writer;
|
||||
SkCanvas* pipeCanvas = writer.startRecording(&pipeController);
|
||||
testDrawingBadBitmap(reporter, pipeCanvas);
|
||||
writer.endRecording();
|
||||
}
|
||||
|
||||
#include "TestClassDef.h"
|
||||
DEFINE_TESTCLASS("PipeTest", PipeTestClass, test_pipeTests)
|
Загрузка…
Ссылка в новой задаче