aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar commit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2013-07-24 18:45:27 +0000
committerGravatar commit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2013-07-24 18:45:27 +0000
commitcae5d8d5705081cb4d22af7a30be0f01bffce745 (patch)
treea1a008a6f2a4cc347f4c50b1a2d6538d714c0949 /src
parent588f3d3896ce84a00ba0b229a4fe8fff2bf67516 (diff)
Start from scratch on a faster SkFlatDictionary.
This is like codereview.chromium.org/19276003, except it fits in better with the existing code, doesn't leak memory, and because it's back using SkChunkFlatController it's a little faster too, now a win across the board: Slowdown bench -1.59% desk_youtubetvbrowse.skp -2.56% desk_googlehome.skp -6.40% tabl_androidpolice.skp -6.45% desk_youtubetvvideo.skp -6.91% tabl_googlecalendar.skp ... -29.70% desk_yahoogames.skp -32.17% desk_googlespreadsheet.skp -32.23% mobi_wikipedia.skp -37.16% desk_chalkboard.skp -41.57% desk_pokemonwiki.skp Overall slowdown: -22.74% running bench [640 480] picture_record_recurring_paint_dictionary NONRENDERING: cmsecs = 9.92 running bench [640 480] picture_record_unique_paint_dictionary NONRENDERING: cmsecs = 22.16 running bench [640 480] picture_record_dictionaries NONRENDERING: cmsecs = 9.18 BUG= R=tomhudson@google.com, reed@google.com, scroggo@google.com Author: mtklein@google.com Review URL: https://chromiumcodereview.appspot.com/19564007 git-svn-id: http://skia.googlecode.com/svn/trunk@10328 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'src')
-rw-r--r--src/core/SkOrderedWriteBuffer.h13
-rw-r--r--src/core/SkPictureFlat.cpp15
-rw-r--r--src/core/SkPictureFlat.h188
3 files changed, 153 insertions, 63 deletions
diff --git a/src/core/SkOrderedWriteBuffer.h b/src/core/SkOrderedWriteBuffer.h
index ebec7d1c21..180f9a4d63 100644
--- a/src/core/SkOrderedWriteBuffer.h
+++ b/src/core/SkOrderedWriteBuffer.h
@@ -26,18 +26,25 @@ class SkRefCntSet;
class SkOrderedWriteBuffer : public SkFlattenableWriteBuffer {
public:
SkOrderedWriteBuffer(size_t minSize);
- SkOrderedWriteBuffer(size_t minSize, void* initialStorage,
- size_t storageSize);
+ SkOrderedWriteBuffer(size_t minSize, void* initialStorage, size_t storageSize);
virtual ~SkOrderedWriteBuffer();
virtual bool isOrderedBinaryBuffer() SK_OVERRIDE { return true; }
virtual SkOrderedWriteBuffer* getOrderedBinaryBuffer() SK_OVERRIDE { return this; }
SkWriter32* getWriter32() { return &fWriter; }
+ void reset(void* storage, size_t storageSize) { fWriter.reset(storage, storageSize); }
+
+ // Returns true if we've written only into the storage passed into constructor or reset.
+ // (You may be able to use this to avoid a call to writeToMemory.)
+ bool wroteOnlyToStorage() const { return fWriter.wroteOnlyToStorage(); }
void writeToMemory(void* dst) { fWriter.flatten(dst); }
uint32_t* reserve(size_t size) { return fWriter.reserve(size); }
- uint32_t size() { return fWriter.size(); }
+
+ uint32_t bytesWritten() const { return fWriter.bytesWritten(); }
+ // Deprecated. Please call bytesWritten instead. TODO(mtklein): clean up
+ uint32_t size() const { return this->bytesWritten(); }
virtual void writeByteArray(const void* data, size_t size) SK_OVERRIDE;
virtual void writeBool(bool value) SK_OVERRIDE;
diff --git a/src/core/SkPictureFlat.cpp b/src/core/SkPictureFlat.cpp
index e9eec0fb00..2c6efa2a8f 100644
--- a/src/core/SkPictureFlat.cpp
+++ b/src/core/SkPictureFlat.cpp
@@ -94,6 +94,14 @@ SkNamedFactorySet* SkFlatController::setNamedFactorySet(SkNamedFactorySet* set)
///////////////////////////////////////////////////////////////////////////////
+void SkFlatData::stampHeaderAndSentinel(int index, int32_t size) {
+ fIndex = index;
+ fFlatSize = size;
+ fChecksum = SkChecksum::Compute(this->data32(), size);
+ this->setTopBotUnwritten();
+ this->setSentinelAsCandidate();
+}
+
SkFlatData* SkFlatData::Create(SkFlatController* controller, const void* obj,
int index, void (*flattenProc)(SkOrderedWriteBuffer&, const void*)) {
// a buffer of 256 bytes should be sufficient for most paints, regions,
@@ -119,14 +127,9 @@ SkFlatData* SkFlatData::Create(SkFlatController* controller, const void* obj,
size_t allocSize = sizeof(SkFlatData) + size + sizeof(uint32_t);
SkFlatData* result = (SkFlatData*) controller->allocThrow(allocSize);
- result->setIndex(index);
- result->setTopBotUnwritten();
- result->fFlatSize = size;
-
// put the serialized contents into the data section of the new allocation
buffer.writeToMemory(result->data());
- result->fChecksum = SkChecksum::Compute(result->data32(), size);
- result->setSentinelAsCandidate();
+ result->stampHeaderAndSentinel(index, size);
return result;
}
diff --git a/src/core/SkPictureFlat.h b/src/core/SkPictureFlat.h
index 0a147defe8..3ac1d2d907 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(this->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 owned by this dictionary. Must be sk_freed.
+ SkFlatData* allocScratch(size_t scratchSize) const {
+ 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 = this->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;
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>;
}