Revert "Start from scratch on a faster SkFlatDictionary."

This reverts commit fec9bfa02d5d2b27bfa2dad3e37e5825a720784d.

git-svn-id: http://skia.googlecode.com/svn/trunk@10331 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
mtklein@google.com 2013-07-24 19:11:15 +00:00
parent cae5d8d570
commit 5174286bc5
6 changed files with 68 additions and 177 deletions

View File

@ -46,17 +46,10 @@ public:
// return the current offset (will always be a multiple of 4)
uint32_t bytesWritten() const { return fSize; }
// DEPRECATED: use bytesWritten instead TODO(mtklein): clean up
// DEPRECATED: use byetsWritten instead
uint32_t size() const { return this->bytesWritten(); }
// 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 flatten.)
bool wroteOnlyToStorage() const {
return fHead == &fExternalBlock && this->bytesWritten() <= fExternalBlock.fSizeOfBlock;
}
void reset();
void reset(void* storage, size_t size);
void reset();
// size MUST be multiple of 4
uint32_t* reserve(size_t size) {
@ -70,6 +63,8 @@ public:
return block->alloc(size);
}
void reset(void* storage, size_t size);
bool writeBool(bool value) {
this->writeInt(value);
return value;

View File

@ -26,25 +26,18 @@ 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 bytesWritten() const { return fWriter.bytesWritten(); }
// Deprecated. Please call bytesWritten instead. TODO(mtklein): clean up
uint32_t size() const { return this->bytesWritten(); }
uint32_t size() { return fWriter.size(); }
virtual void writeByteArray(const void* data, size_t size) SK_OVERRIDE;
virtual void writeBool(bool value) SK_OVERRIDE;

View File

@ -94,14 +94,6 @@ 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,
@ -127,9 +119,14 @@ 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->stampHeaderAndSentinel(index, size);
result->fChecksum = SkChecksum::Compute(result->data32(), size);
result->setSentinelAsCandidate();
return result;
}

View File

@ -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 SkFlatData objects of type T.
// searchable set of SkFlataData 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,15 +175,16 @@ public:
SkFlatController();
virtual ~SkFlatController();
/**
* 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.
* Provide a new block of memory for the SkFlatDictionary to use.
*/
virtual void* allocThrow(size_t bytes) = 0;
/**
* 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.
* 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.
*/
virtual void unalloc(void* ptr) = 0;
@ -399,33 +400,26 @@ 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, 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
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;
sk_bzero(fHash, sizeof(fHash));
// index 0 is always empty since it is used as a signal that find failed
fIndexedData.push(NULL);
}
~SkFlatDictionary() {
sk_free(fScratch);
virtual ~SkFlatDictionary() {
fController->unref();
}
int count() const {
@ -538,40 +532,33 @@ public:
}
const SkFlatData* findAndReturnFlat(const T& element) {
// Only valid until the next call to resetScratch().
const SkFlatData& scratch = this->resetScratch(element, fNextIndex);
SkFlatData* flat = SkFlatData::Create(fController, &element, fNextIndex, fFlattenProc);
// See if we have it in the hash?
const int hashIndex = ChecksumToHashIndex(scratch.checksum());
int hashIndex = ChecksumToHashIndex(flat->checksum());
const SkFlatData* candidate = fHash[hashIndex];
if (candidate != NULL && SkFlatData::Compare(scratch, *candidate) == 0) {
if (candidate && !SkFlatData::Compare(*flat, *candidate)) {
fController->unalloc(flat);
return candidate;
}
// See if we have it at all?
const int index = SkTSearch<const SkFlatData, SkFlatData::Less>(fSortedData.begin(),
fSortedData.count(),
&scratch,
sizeof(&scratch));
int index = SkTSearch<const SkFlatData,
SkFlatData::Less>((const SkFlatData**) fSortedData.begin(),
fSortedData.count(), flat, sizeof(flat));
if (index >= 0) {
// Found. Update hash before we return.
fController->unalloc(flat);
fHash[hashIndex] = fSortedData[index];
return fSortedData[index];
}
// 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);
index = ~index;
*fSortedData.insert(index) = flat;
*fIndexedData.insert(flat->index()) = flat;
SkASSERT(fSortedData.count() == fNextIndex);
SkASSERT(fIndexedData.count() == fNextIndex+1);
fNextIndex++;
return detached;
flat->setSentinelInCache();
fHash[hashIndex] = flat;
SkASSERT(fIndexedData.count() == fSortedData.count()+1);
return flat;
}
protected:
@ -579,76 +566,6 @@ 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(),
@ -667,18 +584,14 @@ private:
}
}
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;
SkFlatController * const fController;
int fNextIndex;
// 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;
@ -732,19 +645,20 @@ public:
this->setTypefacePlayback(&fTypefacePlayback);
}
~SkChunkFlatController() {
fTypefaceSet->unref();
}
virtual void* allocThrow(size_t bytes) SK_OVERRIDE {
fLastAllocated = fHeap.allocThrow(bytes);
return fLastAllocated;
return fHeap.allocThrow(bytes);
}
virtual void unalloc(void* ptr) SK_OVERRIDE {
// 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) fHeap.unalloc(ptr);
}
void setupPlaybacks() const {
fTypefacePlayback.reset(fTypefaceSet.get());
fTypefacePlayback.reset(fTypefaceSet);
}
void setBitmapStorage(SkBitmapHeap* heap) {
@ -753,16 +667,23 @@ public:
private:
SkChunkAlloc fHeap;
SkAutoTUnref<SkRefCntSet> fTypefaceSet;
void* fLastAllocated;
SkRefCntSet* fTypefaceSet;
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, 36) {
: SkFlatDictionary<SkMatrix>(controller) {
fFlattenProc = &flattenMatrix;
fUnflattenProc = &unflattenMatrix;
}
@ -778,9 +699,8 @@ 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, 512) {
: SkFlatDictionary<SkPaint>(controller) {
fFlattenProc = &SkFlattenObjectProc<SkPaint>;
fUnflattenProc = &SkUnflattenObjectProc<SkPaint>;
}

View File

@ -187,23 +187,18 @@ static void Tests(skiatest::Reporter* reporter) {
SkWriter32 writer(0);
uint32_t storage[256];
writer.reset(storage, sizeof(storage));
// These three writes are small enough to fit in storage.
test1(reporter, &writer);
REPORTER_ASSERT(reporter, writer.wroteOnlyToStorage());
writer.reset(storage, sizeof(storage));
test2(reporter, &writer);
REPORTER_ASSERT(reporter, writer.wroteOnlyToStorage());
writer.reset(storage, sizeof(storage));
testWritePad(reporter, &writer);
REPORTER_ASSERT(reporter, writer.wroteOnlyToStorage());
// Try overflowing the storage-block.
// try overflowing the storage-block
uint32_t smallStorage[8];
writer.reset(smallStorage, sizeof(smallStorage));
test2(reporter, &writer);
REPORTER_ASSERT(reporter, !writer.wroteOnlyToStorage());
}
// small storage

View File

@ -39,8 +39,6 @@ DEFINE_bool(validate, false, "Verify that the rendered image contains the same p
"the picture rendered in simple mode. When used in conjunction with --bbh, results "
"are validated against the picture rendered in the same mode, but without the bbh.");
DEFINE_bool(bench_record, false, "If true, drop into an infinite loop of recording the picture.");
static void make_output_filepath(SkString* path, const SkString& dir,
const SkString& name) {
sk_tools::make_filepath(path, dir, name);
@ -165,13 +163,6 @@ static bool render_picture(const SkString& inputPath, const SkString* outputDir,
return false;
}
while (FLAGS_bench_record) {
const int kRecordFlags = 0;
SkPicture other;
picture->draw(other.beginRecording(picture->width(), picture->height(), kRecordFlags));
other.endRecording();
}
for (int i = 0; i < FLAGS_clone; ++i) {
SkPicture* clone = picture->clone();
SkDELETE(picture);