From 10dccde54a769b8d472bccf8c1993034b93ef58d Mon Sep 17 00:00:00 2001 From: "scroggo@google.com" Date: Wed, 8 Aug 2012 20:43:22 +0000 Subject: [PATCH] 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 --- gyp/tests.gyp | 5 + src/core/SkBitmapHeap.cpp | 123 ++++++++++++++---- src/core/SkBitmapHeap.h | 28 ++++- src/pipe/SkGPipePriv.h | 1 + src/pipe/SkGPipeRead.cpp | 44 +++++-- src/pipe/SkGPipeWrite.cpp | 311 +++++++--------------------------------------- tests/PipeTest.cpp | 35 ++++++ 7 files changed, 242 insertions(+), 305 deletions(-) create mode 100644 tests/PipeTest.cpp diff --git a/gyp/tests.gyp b/gyp/tests.gyp index 278dce7..b128a54 100644 --- a/gyp/tests.gyp +++ b/gyp/tests.gyp @@ -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', diff --git a/src/core/SkBitmapHeap.cpp b/src/core/SkBitmapHeap.cpp index 7cff8ea..924264b 100644 --- a/src/core/SkBitmapHeap.cpp +++ b/src/core/SkBitmapHeap.cpp @@ -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(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); } diff --git a/src/core/SkBitmapHeap.h b/src/core/SkBitmapHeap.h index bcfd039..3c00b52 100644 --- a/src/core/SkBitmapHeap.h +++ b/src/core/SkBitmapHeap.h @@ -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; @@ -215,14 +230,20 @@ 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 fStorage; + // Used to mark slots in fStorage as deleted without actually deleting + // the slot so as not to mess up the numbering. + SkTDArray fUnusedSlots; ExternalStorage* fExternalStorage; SkBitmapHeapEntry* fMostRecentlyUsed; diff --git a/src/pipe/SkGPipePriv.h b/src/pipe/SkGPipePriv.h index 1eb0604..5caecd8 100644 --- a/src/pipe/SkGPipePriv.h +++ b/src/pipe/SkGPipePriv.h @@ -74,6 +74,7 @@ enum DrawOps { // these are signals to playback, not drawing verbs kReportFlags_DrawOp, + kShareHeap_DrawOp, kDone_DrawOp, }; diff --git a/src/pipe/SkGPipeRead.cpp b/src/pipe/SkGPipeRead.cpp index 725a57b..e6013db 100644 --- a/src/pipe/SkGPipeRead.cpp +++ b/src/pipe/SkGPipeRead.cpp @@ -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 fTypefaces; SkTDArray fFactoryArray; SkTDArray 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(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(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); } /////////////////////////////////////////////////////////////////////////////// diff --git a/src/pipe/SkGPipeWrite.cpp b/src/pipe/SkGPipeWrite.cpp index e020bea..128a458 100644 --- a/src/pipe/SkGPipeWrite.cpp +++ b/src/pipe/SkGPipeWrite.cpp @@ -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(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(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); } /////////////////////////////////////////////////////////////////////////////// diff --git a/tests/PipeTest.cpp b/tests/PipeTest.cpp new file mode 100644 index 0000000..3724223 --- /dev/null +++ b/tests/PipeTest.cpp @@ -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) -- 2.7.4