Chromium Code Reviews| Index: src/core/SkPictureFlat.h |
| diff --git a/src/core/SkPictureFlat.h b/src/core/SkPictureFlat.h |
| index 0a147defe8a104da2fcddc205e79dbe86d1f0388..e4f1492b8080439a414fa3cdf33def2f9b658c3a 100644 |
| --- a/src/core/SkPictureFlat.h |
| +++ b/src/core/SkPictureFlat.h |
| @@ -151,9 +151,9 @@ private: |
| // also responsible for flattening/unflattening objects but |
| // details of that operation are hidden in the provided procs |
| // SkFlatDictionary: is an abstract templated dictionary that maintains a |
| -// searchable set of SkFlataData objects of type T. |
| +// searchable set of SkFlatData objects of type T. |
| // SkFlatController: is an interface provided to SkFlatDictionary which handles |
| -// allocation and unallocation in some cases. It also holds |
| +// allocation (and unallocation in some cases). It also holds |
| // ref count recorders and the like. |
| // |
| // NOTE: any class that wishes to be used in conjunction with SkFlatDictionary |
| @@ -175,16 +175,15 @@ public: |
| SkFlatController(); |
| virtual ~SkFlatController(); |
| /** |
| - * Provide a new block of memory for the SkFlatDictionary to use. |
| + * Return a new block of memory for the SkFlatDictionary to use. |
| + * This memory is owned by the controller and has the same lifetime unless you |
| + * call unalloc(), in which case it may be freed early. |
| */ |
| virtual void* allocThrow(size_t bytes) = 0; |
| /** |
| - * Unallocate a previously allocated block, returned by allocThrow. |
| - * Implementation should at least perform an unallocation if passed the last |
| - * pointer returned by allocThrow. If findAndReplace() is intended to be |
| - * used, unalloc should also be able to unallocate the SkFlatData that is |
| - * provided. |
| + * Hint that this block, which was allocated with allocThrow, is no longer needed. |
| + * The implementation may choose to free this memory any time beteween now and destruction. |
| */ |
| virtual void unalloc(void* ptr) = 0; |
| @@ -400,26 +399,33 @@ private: |
| SkASSERT(SkIsAlign4(fFlatSize)); |
| this->data32()[fFlatSize >> 2] = value; |
| } |
| + |
| + // This does not modify the payload flat data, in case it's already been written. |
| + void stampHeaderAndSentinel(int index, int32_t size); |
| + template <class T> friend class SkFlatDictionary; // For stampHeaderAndSentinel(). |
| }; |
| template <class T> |
| class SkFlatDictionary { |
| + static const size_t kWriteBufferGrowthBytes = 1024; |
| + |
| public: |
| - SkFlatDictionary(SkFlatController* controller) |
| - : fController(controller) { |
| - fFlattenProc = NULL; |
| - fUnflattenProc = NULL; |
| - SkASSERT(controller); |
| - fController->ref(); |
| - // set to 1 since returning a zero from find() indicates failure |
| - fNextIndex = 1; |
| + SkFlatDictionary(SkFlatController* controller, size_t scratchSizeGuess = 0) |
| + : fFlattenProc(NULL) |
| + , fUnflattenProc(NULL) |
| + , fController(SkRef(controller)) |
| + , fScratchSize(scratchSizeGuess) |
| + , fScratch(AllocScratch(fScratchSize)) |
| + , fWriteBuffer(kWriteBufferGrowthBytes) |
| + , fWriteBufferReady(false) |
| + , fNextIndex(1) { // set to 1 since returning a zero from find() indicates failure |
| sk_bzero(fHash, sizeof(fHash)); |
| // index 0 is always empty since it is used as a signal that find failed |
| fIndexedData.push(NULL); |
| } |
| - virtual ~SkFlatDictionary() { |
| - fController->unref(); |
| + ~SkFlatDictionary() { |
| + sk_free(fScratch); |
| } |
| int count() const { |
| @@ -532,33 +538,40 @@ public: |
| } |
| const SkFlatData* findAndReturnFlat(const T& element) { |
| - SkFlatData* flat = SkFlatData::Create(fController, &element, fNextIndex, fFlattenProc); |
| + // Only valid until the next call to resetScratch(). |
| + const SkFlatData& scratch = this->resetScratch(element, fNextIndex); |
| - int hashIndex = ChecksumToHashIndex(flat->checksum()); |
| + // See if we have it in the hash? |
| + const int hashIndex = ChecksumToHashIndex(scratch.checksum()); |
| const SkFlatData* candidate = fHash[hashIndex]; |
| - if (candidate && !SkFlatData::Compare(*flat, *candidate)) { |
| - fController->unalloc(flat); |
| + if (candidate != NULL && SkFlatData::Compare(scratch, *candidate) == 0) { |
| return candidate; |
| } |
| - int index = SkTSearch<const SkFlatData, |
| - SkFlatData::Less>((const SkFlatData**) fSortedData.begin(), |
| - fSortedData.count(), flat, sizeof(flat)); |
| + // See if we have it at all? |
| + const int index = SkTSearch<const SkFlatData, SkFlatData::Less>(fSortedData.begin(), |
| + fSortedData.count(), |
| + &scratch, |
| + sizeof(&scratch)); |
| if (index >= 0) { |
| - fController->unalloc(flat); |
| + // Found. Update hash before we return. |
| fHash[hashIndex] = fSortedData[index]; |
| return fSortedData[index]; |
| } |
| - index = ~index; |
| - *fSortedData.insert(index) = flat; |
| - *fIndexedData.insert(flat->index()) = flat; |
| + // We don't have it. Add it. |
| + SkFlatData* detached = this->detachScratch(); |
| + // detached will live beyond the next call to resetScratch(), but is owned by fController. |
| + *fSortedData.insert(~index) = detached; // SkTSearch returned bit-not of where to insert. |
| + *fIndexedData.insert(detached->index()) = detached; |
| + fHash[hashIndex] = detached; |
| + |
| + SkASSERT(detached->index() == fNextIndex); |
| SkASSERT(fSortedData.count() == fNextIndex); |
| + SkASSERT(fIndexedData.count() == fNextIndex+1); |
| fNextIndex++; |
| - flat->setSentinelInCache(); |
| - fHash[hashIndex] = flat; |
| - SkASSERT(fIndexedData.count() == fSortedData.count()+1); |
| - return flat; |
| + |
| + return detached; |
| } |
| protected: |
| @@ -566,6 +579,76 @@ protected: |
| void (*fUnflattenProc)(SkOrderedReadBuffer&, void*); |
| private: |
| + // Layout: [ SkFlatData header, 20 bytes ] [ data ..., 4-byte aligned ] [ sentinel, 4 bytes] |
| + static size_t SizeWithPadding(size_t flatDataSize) { |
| + SkASSERT(SkIsAlign4(flatDataSize)); |
| + return sizeof(SkFlatData) + flatDataSize + sizeof(uint32_t); |
| + } |
| + |
| + // Allocate a new scratch SkFlatData. Must be sk_freed. |
| + static SkFlatData* AllocScratch(size_t scratchSize) { |
| + return (SkFlatData*) sk_malloc_throw(SizeWithPadding(scratchSize)); |
| + } |
| + |
| + // We have to delay fWriteBuffer's initialization until its first use; fController might not |
| + // be fully set up by the time we get it in the constructor. |
| + void lazyWriteBufferInit() { |
| + if (fWriteBufferReady) { |
| + return; |
| + } |
| + // Without a bitmap heap, we'll flatten bitmaps into paints. That's never what you want. |
| + SkASSERT(fController->getBitmapHeap() != NULL); |
| + fWriteBuffer.setBitmapHeap(fController->getBitmapHeap()); |
| + fWriteBuffer.setTypefaceRecorder(fController->getTypefaceSet()); |
| + fWriteBuffer.setNamedFactoryRecorder(fController->getNamedFactorySet()); |
| + fWriteBuffer.setFlags(fController->getWriteBufferFlags()); |
| + fWriteBufferReady = true; |
| + } |
| + |
| + // This reference is valid only until the next call to resetScratch() or detachScratch(). |
| + const SkFlatData& resetScratch(const T& element, int index) { |
| + this->lazyWriteBufferInit(); |
| + |
| + // Flatten element into fWriteBuffer (using fScratch as storage). |
| + fWriteBuffer.reset(fScratch->data(), fScratchSize); |
| + fFlattenProc(fWriteBuffer, &element); |
| + const size_t bytesWritten = fWriteBuffer.bytesWritten(); |
| + |
| + // If all the flattened bytes fit into fScratch, we can skip a call to writeToMemory. |
| + if (!fWriteBuffer.wroteOnlyToStorage()) { |
| + SkASSERT(bytesWritten > fScratchSize); |
| + // It didn't all fit. Copy into a larger replacement SkFlatData. |
| + // We can't just realloc because it might move the pointer and confuse writeToMemory. |
| + SkFlatData* larger = AllocScratch(bytesWritten); |
| + fWriteBuffer.writeToMemory(larger->data()); |
| + |
| + // Carry on with this larger scratch to minimize the likelihood of future resizing. |
| + sk_free(fScratch); |
| + fScratchSize = bytesWritten; |
| + fScratch = larger; |
| + } |
| + |
| + // The data is in fScratch now, but we need to stamp its header and trailing sentinel. |
| + fScratch->stampHeaderAndSentinel(index, bytesWritten); |
| + return *fScratch; |
| + } |
| + |
| + // This result is owned by fController and lives as long as it does (unless unalloc'd). |
| + SkFlatData* detachScratch() { |
| + // Allocate a new SkFlatData exactly big enough to hold our current scratch. |
| + // We use the controller for this allocation to extend the allocation's lifetime and allow |
| + // the controller to do whatever memory management it wants. |
| + const size_t paddedSize = SizeWithPadding(fScratch->flatSize()); |
| + SkFlatData* detached = (SkFlatData*)fController->allocThrow(paddedSize); |
| + |
| + // Copy scratch into the new SkFlatData, setting the sentinel for cache storage. |
| + memcpy(detached, fScratch, paddedSize); |
| + detached->setSentinelInCache(); |
| + |
| + // We can now reuse fScratch, and detached will live until fController dies. |
| + return detached; |
| + } |
| + |
| void unflatten(T* dst, const SkFlatData* element) const { |
| element->unflatten(dst, fUnflattenProc, |
| fController->getBitmapHeap(), |
| @@ -584,14 +667,18 @@ private: |
| } |
| } |
| - SkFlatController * const fController; |
| - int fNextIndex; |
| + SkAutoTUnref<SkFlatController> fController; |
| + size_t fScratchSize; // How many bytes fScratch has allocated for data itself. |
| + SkFlatData* fScratch; // Owned, must be freed with sk_free. |
| + SkOrderedWriteBuffer fWriteBuffer; |
| + bool fWriteBufferReady; |
| // SkFlatDictionary has two copies of the data one indexed by the |
| // SkFlatData's index and the other sorted. The sorted data is used |
| // for finding and uniquification while the indexed copy is used |
| // for standard array-style lookups based on the SkFlatData's index |
| // (as in 'unflatten'). |
| + int fNextIndex; |
| SkTDArray<const SkFlatData*> fIndexedData; |
| // fSortedData is sorted by checksum/size/data. |
| SkTDArray<const SkFlatData*> fSortedData; |
| @@ -645,20 +732,19 @@ public: |
| this->setTypefacePlayback(&fTypefacePlayback); |
| } |
| - ~SkChunkFlatController() { |
| - fTypefaceSet->unref(); |
| - } |
| - |
| virtual void* allocThrow(size_t bytes) SK_OVERRIDE { |
| - return fHeap.allocThrow(bytes); |
| + fLastAllocated = fHeap.allocThrow(bytes); |
| + return fLastAllocated; |
| } |
| virtual void unalloc(void* ptr) SK_OVERRIDE { |
| - (void) fHeap.unalloc(ptr); |
| + // fHeap can only free a pointer if it was the last one allocated. Otherwise, we'll just |
| + // have to wait until fHeap is destroyed. |
| + if (ptr == fLastAllocated) (void)fHeap.unalloc(ptr); |
| } |
| void setupPlaybacks() const { |
| - fTypefacePlayback.reset(fTypefaceSet); |
| + fTypefacePlayback.reset(fTypefaceSet.get()); |
| } |
| void setBitmapStorage(SkBitmapHeap* heap) { |
| @@ -667,23 +753,16 @@ public: |
| private: |
| SkChunkAlloc fHeap; |
| - SkRefCntSet* fTypefaceSet; |
| + SkAutoTUnref<SkRefCntSet> fTypefaceSet; |
| + void* fLastAllocated; |
|
scroggo
2013/07/30 21:21:12
This should never be a problem in practice, but sh
|
| mutable SkTypefacePlayback fTypefacePlayback; |
| }; |
| -class SkBitmapDictionary : public SkFlatDictionary<SkBitmap> { |
| -public: |
| - SkBitmapDictionary(SkFlatController* controller) |
| - : SkFlatDictionary<SkBitmap>(controller) { |
| - fFlattenProc = &SkFlattenObjectProc<SkBitmap>; |
| - fUnflattenProc = &SkUnflattenObjectProc<SkBitmap>; |
| - } |
| -}; |
| - |
| class SkMatrixDictionary : public SkFlatDictionary<SkMatrix> { |
| public: |
| + // All matrices fit in 36 bytes. |
| SkMatrixDictionary(SkFlatController* controller) |
| - : SkFlatDictionary<SkMatrix>(controller) { |
| + : SkFlatDictionary<SkMatrix>(controller, 36) { |
| fFlattenProc = &flattenMatrix; |
| fUnflattenProc = &unflattenMatrix; |
| } |
| @@ -699,8 +778,9 @@ class SkMatrixDictionary : public SkFlatDictionary<SkMatrix> { |
| class SkPaintDictionary : public SkFlatDictionary<SkPaint> { |
| public: |
| + // The largest paint across ~60 .skps was 500 bytes. |
| SkPaintDictionary(SkFlatController* controller) |
| - : SkFlatDictionary<SkPaint>(controller) { |
| + : SkFlatDictionary<SkPaint>(controller, 512) { |
| fFlattenProc = &SkFlattenObjectProc<SkPaint>; |
| fUnflattenProc = &SkUnflattenObjectProc<SkPaint>; |
| } |