Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(152)

Unified Diff: src/core/SkPictureFlat.h

Issue 19564007: Start from scratch on a faster SkFlatDictionary. (Closed) Base URL: https://skia.googlecode.com/svn/trunk
Patch Set: fix windows build by making AllocScratch static Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/core/SkOrderedWriteBuffer.h ('k') | src/core/SkPictureFlat.cpp » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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>;
}
« no previous file with comments | « src/core/SkOrderedWriteBuffer.h ('k') | src/core/SkPictureFlat.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698