aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar commit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2014-01-14 20:51:26 +0000
committerGravatar commit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2014-01-14 20:51:26 +0000
commit19382421b916aab00be7265815ba4e2690adf2c9 (patch)
tree8e69446ccf2e3548e9f2986a5eba67a641e76b59
parentb638a3a941fc9514b3f26165bfaaa7b48a580139 (diff)
Convert SkWriter32 to use an SkTDArray for its internal storage.
This reduces the allocation overhead of a null picture (create, beginRecording(), endRecording) from about 18K to about 1.9K. (There's still lots more to prune.) SkPictureFlat can exploit the fact that Writer32 is contiguous simplify its memory management. The Writer32 itself becomes the scratch buffer. Remove lots and lots of arbitrary magic numbers that were size guesses and minimum allocation sizes. Keep your eyes open for the big obvious DUH why we save 16K per picture! (Spoiler alert. It's because that first save we issue in beginRecording() forces the old SkWriter32 to allocate 16K.) Tests passing, DM passing. bench --match writer: ~20% faster null bench_record: ~30% faster bench_record on buildbot .skps: ~3-6% slower, ranging 25% faster to 20% slower bench_pictures on buildbot .skps: ~1-2% faster, ranging 13% faster to 28% slower BUG=skia:1850 R=reed@google.com Author: mtklein@google.com Review URL: https://codereview.chromium.org/137433003 git-svn-id: http://skia.googlecode.com/svn/trunk@13073 2bbb7eff-a529-9590-31e7-b0007b416f81
-rw-r--r--bench/WriterBench.cpp2
-rw-r--r--include/core/SkWriter32.h245
-rw-r--r--src/core/SkFlattenableSerialization.cpp2
-rw-r--r--src/core/SkOrderedWriteBuffer.cpp7
-rw-r--r--src/core/SkOrderedWriteBuffer.h12
-rw-r--r--src/core/SkPaint.cpp6
-rw-r--r--src/core/SkPictureFlat.h90
-rw-r--r--src/core/SkPicturePlayback.cpp3
-rw-r--r--src/core/SkPictureRecord.cpp2
-rw-r--r--src/core/SkScalerContext.cpp2
-rw-r--r--src/core/SkWriter32.cpp234
-rw-r--r--src/pipe/SkGPipeWrite.cpp4
-rw-r--r--src/utils/SkCanvasStateUtils.cpp8
-rw-r--r--tests/AndroidPaintTest.cpp2
-rw-r--r--tests/ColorFilterTest.cpp2
-rw-r--r--tests/PathTest.cpp2
-rw-r--r--tests/SerializationTest.cpp6
-rw-r--r--tests/Writer32Test.cpp106
18 files changed, 211 insertions, 524 deletions
diff --git a/bench/WriterBench.cpp b/bench/WriterBench.cpp
index 46b26b1efa..7696eab832 100644
--- a/bench/WriterBench.cpp
+++ b/bench/WriterBench.cpp
@@ -24,7 +24,7 @@ protected:
virtual void onDraw(const int loops, SkCanvas*) SK_OVERRIDE {
static const char gStr[] = "abcdefghimjklmnopqrstuvwxyz";
static const size_t gLen = strlen(gStr);
- SkWriter32 writer(256 * 4);
+ SkWriter32 writer;
for (int i = 0; i < loops; i++) {
for (size_t j = 0; j <= gLen; j++) {
writer.writeString(gStr, j);
diff --git a/include/core/SkWriter32.h b/include/core/SkWriter32.h
index 9fb1f7b85b..61d4a95f7e 100644
--- a/include/core/SkWriter32.h
+++ b/include/core/SkWriter32.h
@@ -10,74 +10,96 @@
#ifndef SkWriter32_DEFINED
#define SkWriter32_DEFINED
-#include "SkTypes.h"
-
-#include "SkScalar.h"
+#include "SkMatrix.h"
#include "SkPath.h"
#include "SkPoint.h"
-#include "SkRect.h"
#include "SkRRect.h"
-#include "SkMatrix.h"
+#include "SkRect.h"
#include "SkRegion.h"
-
-class SkStream;
-class SkWStream;
+#include "SkScalar.h"
+#include "SkStream.h"
+#include "SkTDArray.h"
+#include "SkTypes.h"
class SkWriter32 : SkNoncopyable {
- struct BlockHeader;
public:
/**
* The caller can specify an initial block of storage, which the caller manages.
- * SkWriter32 will not attempt to free this in its destructor. It is up to the
- * implementation to decide if, and how much, of the storage to utilize, and it
- * is possible that it may be ignored entirely.
+ *
+ * SkWriter32 will try to back reserve and write calls with this external storage until the
+ * first time an allocation doesn't fit. From then it will use dynamically allocated storage.
+ * This used to be optional behavior, but pipe now relies on it.
*/
- SkWriter32(size_t minSize, void* initialStorage, size_t storageSize);
-
- SkWriter32(size_t minSize)
- : fHead(NULL)
- , fTail(NULL)
- , fMinSize(minSize)
- , fSize(0)
- , fWrittenBeforeLastBlock(0)
- {}
-
- ~SkWriter32();
+ SkWriter32(void* external = NULL, size_t externalBytes = 0) {
+ this->reset(external, externalBytes);
+ }
// return the current offset (will always be a multiple of 4)
- size_t bytesWritten() const { return fSize; }
+ size_t bytesWritten() const { return fCount * 4; }
SK_ATTR_DEPRECATED("use bytesWritten")
size_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* external = NULL, size_t externalBytes = 0) {
+ SkASSERT(SkIsAlign4((uintptr_t)external));
+ SkASSERT(SkIsAlign4(externalBytes));
+ fExternal = (uint32_t*)external;
+ fExternalLimit = externalBytes/4;
+ fCount = 0;
+ fInternal.rewind();
}
- void reset();
- void reset(void* storage, size_t size);
+ // If all data written is contiguous, then this returns a pointer to it, otherwise NULL.
+ // This will work if we've only written to the externally supplied block of storage, or if we've
+ // only written to our internal dynamic storage, but will fail if we have written into both.
+ const uint32_t* contiguousArray() const {
+ if (this->externalCount() == 0) {
+ return fInternal.begin();
+ } else if (fInternal.isEmpty()) {
+ return fExternal;
+ }
+ return NULL;
+ }
// size MUST be multiple of 4
uint32_t* reserve(size_t size) {
SkASSERT(SkAlign4(size) == size);
+ const int count = size/4;
+
+ uint32_t* p;
+ // Once we start writing to fInternal, we never write to fExternal again.
+ // This simplifies tracking what data is where.
+ if (fInternal.isEmpty() && this->externalCount() + count <= fExternalLimit) {
+ p = fExternal + fCount;
+ } else {
+ p = fInternal.append(count);
+ }
+
+ fCount += count;
+ return p;
+ }
- Block* block = fTail;
- if (NULL == block || block->available() < size) {
- block = this->doReserve(size);
+ // return the address of the 4byte int at the specified offset (which must
+ // be a multiple of 4. This does not allocate any new space, so the returned
+ // address is only valid for 1 int.
+ uint32_t* peek32(size_t offset) {
+ SkASSERT(SkAlign4(offset) == offset);
+ const int count = offset/4;
+ SkASSERT(count < fCount);
+
+ if (count < this->externalCount()) {
+ return fExternal + count;
}
- fSize += size;
- return block->alloc(size);
+ return &fInternal[count - this->externalCount()];
}
bool writeBool(bool value) {
- this->writeInt(value);
+ this->write32(value);
return value;
}
void writeInt(int32_t value) {
- *(int32_t*)this->reserve(sizeof(value)) = value;
+ this->write32(value);
}
void write8(int32_t value) {
@@ -92,15 +114,8 @@ public:
*(int32_t*)this->reserve(sizeof(value)) = value;
}
- void writePtr(void* ptr) {
- // Since we "know" that we're always 4-byte aligned, we can tell the
- // compiler that here, by assigning to an int32 ptr.
- int32_t* addr = (int32_t*)this->reserve(sizeof(void*));
- if (4 == sizeof(void*)) {
- *(void**)addr = ptr;
- } else {
- memcpy(addr, &ptr, sizeof(void*));
- }
+ void writePtr(void* value) {
+ *(void**)this->reserve(sizeof(value)) = value;
}
void writeScalar(SkScalar value) {
@@ -152,9 +167,8 @@ public:
*/
void write(const void* values, size_t size) {
SkASSERT(SkAlign4(size) == size);
- // if we could query how much is avail in the current block, we might
- // copy that much, and then alloc the rest. That would reduce the waste
- // in the current block
+ // TODO: If we're going to spill from fExternal to fInternal, we might want to fill
+ // fExternal as much as possible before writing to fInternal.
memcpy(this->reserve(size), values, size);
}
@@ -162,12 +176,25 @@ public:
* Reserve size bytes. Does not need to be 4 byte aligned. The remaining space (if any) will be
* filled in with zeroes.
*/
- uint32_t* reservePad(size_t size);
+ uint32_t* reservePad(size_t size) {
+ uint32_t* p = this->reserve(SkAlign4(size));
+ uint8_t* tail = (uint8_t*)p + size;
+ switch (SkAlign4(size) - size) {
+ default: SkDEBUGFAIL("SkAlign4(x) - x should always be 0, 1, 2, or 3.");
+ case 3: *tail++ = 0x00; // fallthrough is intentional
+ case 2: *tail++ = 0x00; // fallthrough is intentional
+ case 1: *tail++ = 0x00;
+ case 0: ;/*nothing to do*/
+ }
+ return p;
+ }
/**
* Write size bytes from src, and pad to 4 byte alignment with zeroes.
*/
- void writePad(const void* src, size_t size);
+ void writePad(const void* src, size_t size) {
+ memcpy(this->reservePad(size), src, size);
+ }
/**
* Writes a string to the writer, which can be retrieved with
@@ -186,103 +213,49 @@ public:
*/
static size_t WriteStringSize(const char* str, size_t len = (size_t)-1);
- // return the address of the 4byte int at the specified offset (which must
- // be a multiple of 4. This does not allocate any new space, so the returned
- // address is only valid for 1 int.
- uint32_t* peek32(size_t offset);
-
/**
* Move the cursor back to offset bytes from the beginning.
* This has the same restrictions as peek32: offset must be <= size() and
* offset must be a multiple of 4.
*/
- void rewindToOffset(size_t offset);
+ void rewindToOffset(size_t offset) {
+ SkASSERT(SkAlign4(offset) == offset);
+ const int count = offset/4;
+ if (count < this->externalCount()) {
+ fInternal.setCount(0);
+ } else {
+ fInternal.setCount(count - this->externalCount());
+ }
+ fCount = count;
+ }
// copy into a single buffer (allocated by caller). Must be at least size()
- void flatten(void* dst) const;
+ void flatten(void* dst) const {
+ const size_t externalBytes = this->externalCount()*4;
+ memcpy(dst, fExternal, externalBytes);
+ dst = (uint8_t*)dst + externalBytes;
+ memcpy(dst, fInternal.begin(), fInternal.bytes());
+ }
+
+ bool writeToStream(SkWStream* stream) const {
+ return stream->write(fExternal, this->externalCount()*4)
+ && stream->write(fInternal.begin(), fInternal.bytes());
+ }
// read from the stream, and write up to length bytes. Return the actual
// number of bytes written.
- size_t readFromStream(SkStream*, size_t length);
-
- bool writeToStream(SkWStream*);
-
-private:
- struct Block {
- Block* fNext;
- char* fBasePtr;
- size_t fSizeOfBlock; // total space allocated (after this)
- size_t fAllocatedSoFar; // space used so far
-
- size_t available() const { return fSizeOfBlock - fAllocatedSoFar; }
- char* base() { return fBasePtr; }
- const char* base() const { return fBasePtr; }
-
- uint32_t* alloc(size_t size) {
- SkASSERT(SkAlign4(size) == size);
- SkASSERT(this->available() >= size);
- void* ptr = this->base() + fAllocatedSoFar;
- fAllocatedSoFar += size;
- SkASSERT(fAllocatedSoFar <= fSizeOfBlock);
- return (uint32_t*)ptr;
- }
-
- uint32_t* peek32(size_t offset) {
- SkASSERT(offset <= fAllocatedSoFar + 4);
- void* ptr = this->base() + offset;
- return (uint32_t*)ptr;
- }
-
- void rewind() {
- fNext = NULL;
- fAllocatedSoFar = 0;
- // keep fSizeOfBlock as is
- }
-
- static Block* Create(size_t size) {
- SkASSERT(SkIsAlign4(size));
- Block* block = (Block*)sk_malloc_throw(sizeof(Block) + size);
- block->fNext = NULL;
- block->fBasePtr = (char*)(block + 1);
- block->fSizeOfBlock = size;
- block->fAllocatedSoFar = 0;
- return block;
- }
-
- Block* initFromStorage(void* storage, size_t size) {
- SkASSERT(SkIsAlign4((intptr_t)storage));
- SkASSERT(SkIsAlign4(size));
- Block* block = this;
- block->fNext = NULL;
- block->fBasePtr = (char*)storage;
- block->fSizeOfBlock = size;
- block->fAllocatedSoFar = 0;
- return block;
- }
- };
-
- enum {
- MIN_BLOCKSIZE = sizeof(SkWriter32::Block) + sizeof(intptr_t)
- };
-
- Block fExternalBlock;
- Block* fHead;
- Block* fTail;
- size_t fMinSize;
- size_t fSize;
- // sum of bytes written in all blocks *before* fTail
- size_t fWrittenBeforeLastBlock;
-
- bool isHeadExternallyAllocated() const {
- return fHead == &fExternalBlock;
+ size_t readFromStream(SkStream* stream, size_t length) {
+ return stream->read(this->reservePad(length), length);
}
- Block* newBlock(size_t bytes);
-
- // only call from reserve()
- Block* doReserve(size_t bytes);
+private:
+ // Number of uint32_t written into fExternal. <= fExternalLimit.
+ int externalCount() const { return fCount - fInternal.count(); }
- SkDEBUGCODE(void validate() const;)
+ int fCount; // Total number of uint32_t written.
+ int fExternalLimit; // Number of uint32_t we can write to fExternal.
+ uint32_t* fExternal; // Unmanaged memory block.
+ SkTDArray<uint32_t> fInternal; // Managed memory block.
};
/**
@@ -293,7 +266,7 @@ private:
*/
template <size_t SIZE> class SkSWriter32 : public SkWriter32 {
public:
- SkSWriter32(size_t minSize) : SkWriter32(minSize, fData.fStorage, SIZE) {}
+ SkSWriter32() : SkWriter32(fData.fStorage, SIZE) {}
private:
union {
diff --git a/src/core/SkFlattenableSerialization.cpp b/src/core/SkFlattenableSerialization.cpp
index b98d935ca7..5c83101eaf 100644
--- a/src/core/SkFlattenableSerialization.cpp
+++ b/src/core/SkFlattenableSerialization.cpp
@@ -12,7 +12,7 @@
#include "SkOrderedWriteBuffer.h"
SkData* SkValidatingSerializeFlattenable(SkFlattenable* flattenable) {
- SkOrderedWriteBuffer writer(1024);
+ SkOrderedWriteBuffer writer;
writer.setFlags(SkOrderedWriteBuffer::kValidation_Flag);
writer.writeFlattenable(flattenable);
uint32_t size = writer.bytesWritten();
diff --git a/src/core/SkOrderedWriteBuffer.cpp b/src/core/SkOrderedWriteBuffer.cpp
index 50fdc72869..9107ecd828 100644
--- a/src/core/SkOrderedWriteBuffer.cpp
+++ b/src/core/SkOrderedWriteBuffer.cpp
@@ -14,21 +14,20 @@
#include "SkStream.h"
#include "SkTypeface.h"
-SkOrderedWriteBuffer::SkOrderedWriteBuffer(size_t minSize)
+SkOrderedWriteBuffer::SkOrderedWriteBuffer()
: INHERITED()
, fFactorySet(NULL)
, fNamedFactorySet(NULL)
- , fWriter(minSize)
, fBitmapHeap(NULL)
, fTFSet(NULL)
, fBitmapEncoder(NULL) {
}
-SkOrderedWriteBuffer::SkOrderedWriteBuffer(size_t minSize, void* storage, size_t storageSize)
+SkOrderedWriteBuffer::SkOrderedWriteBuffer(void* storage, size_t storageSize)
: INHERITED()
, fFactorySet(NULL)
, fNamedFactorySet(NULL)
- , fWriter(minSize, storage, storageSize)
+ , fWriter(storage, storageSize)
, fBitmapHeap(NULL)
, fTFSet(NULL)
, fBitmapEncoder(NULL) {
diff --git a/src/core/SkOrderedWriteBuffer.h b/src/core/SkOrderedWriteBuffer.h
index f3b414e104..1f10152293 100644
--- a/src/core/SkOrderedWriteBuffer.h
+++ b/src/core/SkOrderedWriteBuffer.h
@@ -25,19 +25,17 @@ class SkRefCntSet;
class SkOrderedWriteBuffer : public SkFlattenableWriteBuffer {
public:
- SkOrderedWriteBuffer(size_t minSize);
- SkOrderedWriteBuffer(size_t minSize, void* initialStorage, size_t storageSize);
+ SkOrderedWriteBuffer();
+ SkOrderedWriteBuffer(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 reset(void* storage = NULL, size_t storageSize = 0) {
+ fWriter.reset(storage, storageSize);
+ }
void writeToMemory(void* dst) { fWriter.flatten(dst); }
uint32_t* reserve(size_t size) { return fWriter.reserve(size); }
diff --git a/src/core/SkPaint.cpp b/src/core/SkPaint.cpp
index 94ffa8d31b..af032d001e 100644
--- a/src/core/SkPaint.cpp
+++ b/src/core/SkPaint.cpp
@@ -1815,9 +1815,7 @@ void SkPaint::descriptorProc(const SkDeviceProperties* deviceProperties,
SkMaskFilter* mf = this->getMaskFilter();
SkRasterizer* ra = this->getRasterizer();
- SkOrderedWriteBuffer peBuffer(MIN_SIZE_FOR_EFFECT_BUFFER);
- SkOrderedWriteBuffer mfBuffer(MIN_SIZE_FOR_EFFECT_BUFFER);
- SkOrderedWriteBuffer raBuffer(MIN_SIZE_FOR_EFFECT_BUFFER);
+ SkOrderedWriteBuffer peBuffer, mfBuffer, raBuffer;
if (pe) {
peBuffer.writeFlattenable(pe);
@@ -1845,7 +1843,7 @@ void SkPaint::descriptorProc(const SkDeviceProperties* deviceProperties,
}
#ifdef SK_BUILD_FOR_ANDROID
- SkOrderedWriteBuffer androidBuffer(128);
+ SkOrderedWriteBuffer androidBuffer;
fPaintOptionsAndroid.flatten(androidBuffer);
descSize += androidBuffer.size();
entryCount += 1;
diff --git a/src/core/SkPictureFlat.h b/src/core/SkPictureFlat.h
index c3623eec69..8db9609b97 100644
--- a/src/core/SkPictureFlat.h
+++ b/src/core/SkPictureFlat.h
@@ -271,7 +271,7 @@ public:
static SkFlatData* Create(SkFlatController* controller, const T& obj, int index) {
// A buffer of 256 bytes should fit most paints, regions, and matrices.
uint32_t storage[64];
- SkOrderedWriteBuffer buffer(256, storage, sizeof(storage));
+ SkOrderedWriteBuffer buffer(storage, sizeof(storage));
buffer.setBitmapHeap(controller->getBitmapHeap());
buffer.setTypefaceRecorder(controller->getTypefaceSet());
@@ -366,19 +366,14 @@ private:
mutable SkScalar fTopBot[2]; // Cache of FontMetrics fTop, fBottom. Starts as [NaN,?].
// uint32_t flattenedData[] implicitly hangs off the end.
- template <typename T, typename Traits, int kScratchSizeGuess> friend class SkFlatDictionary;
+ template <typename T, typename Traits> friend class SkFlatDictionary;
};
-template <typename T, typename Traits, int kScratchSizeGuess=0>
+template <typename T, typename Traits>
class SkFlatDictionary {
- static const size_t kWriteBufferGrowthBytes = 1024;
-
public:
explicit SkFlatDictionary(SkFlatController* controller)
: fController(SkRef(controller))
- , fScratchSize(0)
- , fScratch(NULL)
- , fWriteBuffer(kWriteBufferGrowthBytes)
, fReady(false) {
this->reset();
}
@@ -391,10 +386,6 @@ public:
fIndexedData.rewind();
}
- ~SkFlatDictionary() {
- sk_free(fScratch);
- }
-
int count() const {
SkASSERT(fHash.count() == fIndexedData.count());
return fHash.count();
@@ -500,35 +491,19 @@ public:
}
private:
- // Layout: [ SkFlatData header, 20 bytes ] [ data ..., 4-byte aligned ]
- static size_t SizeWithPadding(size_t flatDataSize) {
- SkASSERT(SkIsAlign4(flatDataSize));
- return sizeof(SkFlatData) + flatDataSize;
- }
-
- // 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. We also delay allocating fScratch
- // to avoid unnecessary heap allocations, since we're paying the price of the conditional
- // anyway.
+ // We have to delay fScratch's initialization until its first use; fController might not
+ // be fully set up by the time we get it in the constructor.
void lazyInit() {
if (fReady) {
return;
}
- fScratchSize = kScratchSizeGuess;
- fScratch = AllocScratch(fScratchSize);
-
// 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());
+ fScratch.setBitmapHeap(fController->getBitmapHeap());
+ fScratch.setTypefaceRecorder(fController->getTypefaceSet());
+ fScratch.setNamedFactoryRecorder(fController->getNamedFactorySet());
+ fScratch.setFlags(fController->getWriteBufferFlags());
fReady = true;
}
@@ -551,28 +526,17 @@ private:
const SkFlatData& resetScratch(const T& element, int index) {
this->lazyInit();
- // Flatten element into fWriteBuffer (using fScratch as storage).
- fWriteBuffer.reset(fScratch->data(), fScratchSize);
- Traits::flatten(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;
- }
+ // Layout of fScratch: [ SkFlatData header, 20 bytes ] [ data ..., 4-byte aligned ]
+ fScratch.reset();
+ fScratch.reserve(sizeof(SkFlatData));
+ Traits::flatten(fScratch, element);
+ const size_t dataSize = fScratch.bytesWritten() - sizeof(SkFlatData);
- // The data is in fScratch now but we need to stamp its header.
- fScratch->stampHeader(index, bytesWritten);
- return *fScratch;
+ // Reinterpret data in fScratch as an SkFlatData.
+ SkFlatData* scratch = (SkFlatData*)fScratch.getWriter32()->contiguousArray();
+ SkASSERT(scratch != NULL);
+ scratch->stampHeader(index, dataSize);
+ return *scratch;
}
// This result is owned by fController and lives as long as it does (unless unalloc'd).
@@ -580,12 +544,12 @@ private:
// 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.
- SkASSERT(fScratch != NULL);
- const size_t paddedSize = SizeWithPadding(fScratch->flatSize());
- SkFlatData* detached = (SkFlatData*)fController->allocThrow(paddedSize);
+ SkFlatData* detached = (SkFlatData*)fController->allocThrow(fScratch.bytesWritten());
// Copy scratch into the new SkFlatData.
- memcpy(detached, fScratch, paddedSize);
+ SkFlatData* scratch = (SkFlatData*)fScratch.getWriter32()->contiguousArray();
+ SkASSERT(scratch != NULL);
+ memcpy(detached, scratch, fScratch.bytesWritten());
// We can now reuse fScratch, and detached will live until fController dies.
return detached;
@@ -599,9 +563,7 @@ private:
// All SkFlatData* stored in fIndexedData and fHash are owned by the controller.
SkAutoTUnref<SkFlatController> fController;
- size_t fScratchSize; // How many bytes fScratch has allocated for data itself.
- SkFlatData* fScratch; // Owned, lazily allocated, must be freed with sk_free.
- SkOrderedWriteBuffer fWriteBuffer;
+ SkOrderedWriteBuffer fScratch;
bool fReady;
// For index -> SkFlatData. 0-based, while all indices in the API are 1-based. Careful!
@@ -624,7 +586,7 @@ struct SkMatrixTraits {
buffer.getReader32()->readMatrix(matrix);
}
};
-typedef SkFlatDictionary<SkMatrix, SkMatrixTraits, 36> SkMatrixDictionary;
+typedef SkFlatDictionary<SkMatrix, SkMatrixTraits> SkMatrixDictionary;
struct SkRegionTraits {
@@ -646,7 +608,7 @@ struct SkPaintTraits {
paint->unflatten(buffer);
}
};
-typedef SkFlatDictionary<SkPaint, SkPaintTraits, 512> SkPaintDictionary;
+typedef SkFlatDictionary<SkPaint, SkPaintTraits> SkPaintDictionary;
class SkChunkFlatController : public SkFlatController {
public:
diff --git a/src/core/SkPicturePlayback.cpp b/src/core/SkPicturePlayback.cpp
index 97e566764b..27f0c58c6d 100644
--- a/src/core/SkPicturePlayback.cpp
+++ b/src/core/SkPicturePlayback.cpp
@@ -438,8 +438,7 @@ void SkPicturePlayback::serialize(SkWStream* stream,
SkRefCntSet typefaceSet;
SkFactorySet factSet;
- SkOrderedWriteBuffer buffer(1024);
-
+ SkOrderedWriteBuffer buffer;
buffer.setFlags(SkFlattenableWriteBuffer::kCrossProcess_Flag);
buffer.setTypefaceRecorder(&typefaceSet);
buffer.setFactoryRecorder(&factSet);
diff --git a/src/core/SkPictureRecord.cpp b/src/core/SkPictureRecord.cpp
index b7508b3623..4ae1c6b7df 100644
--- a/src/core/SkPictureRecord.cpp
+++ b/src/core/SkPictureRecord.cpp
@@ -13,7 +13,6 @@
#include "SkDevice.h"
#include "SkPictureStateTree.h"
-#define MIN_WRITER_SIZE 16384
#define HEAP_BLOCK_SIZE 4096
enum {
@@ -36,7 +35,6 @@ SkPictureRecord::SkPictureRecord(uint32_t flags, SkBaseDevice* device) :
fMatrices(&fFlattenableHeap),
fPaints(&fFlattenableHeap),
fRegions(&fFlattenableHeap),
- fWriter(MIN_WRITER_SIZE),
fRecordFlags(flags) {
#ifdef SK_DEBUG_SIZE
fPointBytes = fRectBytes = fTextBytes = 0;
diff --git a/src/core/SkScalerContext.cpp b/src/core/SkScalerContext.cpp
index 04ef2a9222..2a4e9d3d58 100644
--- a/src/core/SkScalerContext.cpp
+++ b/src/core/SkScalerContext.cpp
@@ -147,7 +147,7 @@ SkScalerContext* SkScalerContext::allocNextContext() const {
SkAutoTUnref<SkTypeface> aur(newFace);
uint32_t newFontID = newFace->uniqueID();
- SkOrderedWriteBuffer androidBuffer(128);
+ SkOrderedWriteBuffer androidBuffer;
fPaintOptionsAndroid.flatten(androidBuffer);
SkAutoDescriptor ad(sizeof(fRec) + androidBuffer.size() + SkDescriptor::ComputeOverhead(2));
diff --git a/src/core/SkWriter32.cpp b/src/core/SkWriter32.cpp
index e5befbafb4..5e89ed655b 100644
--- a/src/core/SkWriter32.cpp
+++ b/src/core/SkWriter32.cpp
@@ -5,239 +5,9 @@
* found in the LICENSE file.
*/
-#include "SkWriter32.h"
-
-SkWriter32::SkWriter32(size_t minSize, void* storage, size_t storageSize) {
- fMinSize = minSize;
- fSize = 0;
- fWrittenBeforeLastBlock = 0;
- fHead = fTail = NULL;
-
- if (storageSize) {
- this->reset(storage, storageSize);
- }
-}
-
-SkWriter32::~SkWriter32() {
- this->reset();
-}
-
-void SkWriter32::reset() {
- Block* block = fHead;
-
- if (this->isHeadExternallyAllocated()) {
- SkASSERT(block);
- // don't 'free' the first block, since it is owned by the caller
- block = block->fNext;
- }
- while (block) {
- Block* next = block->fNext;
- sk_free(block);
- block = next;
- }
-
- fSize = 0;
- fWrittenBeforeLastBlock = 0;
- fHead = fTail = NULL;
-}
-
-void SkWriter32::reset(void* storage, size_t storageSize) {
- this->reset();
-
- storageSize &= ~3; // trunc down to multiple of 4
- if (storageSize > 0 && SkIsAlign4((intptr_t)storage)) {
- fHead = fTail = fExternalBlock.initFromStorage(storage, storageSize);
- }
-}
-
-SkWriter32::Block* SkWriter32::doReserve(size_t size) {
- SkASSERT(SkAlign4(size) == size);
-
- Block* block = fTail;
- SkASSERT(NULL == block || block->available() < size);
-
- if (NULL == block) {
- SkASSERT(NULL == fHead);
- fHead = fTail = block = Block::Create(SkMax32(size, fMinSize));
- SkASSERT(0 == fWrittenBeforeLastBlock);
- } else {
- fWrittenBeforeLastBlock = fSize;
-
- fTail = Block::Create(SkMax32(size, fMinSize));
- block->fNext = fTail;
- block = fTail;
- }
- return block;
-}
-
-uint32_t* SkWriter32::peek32(size_t offset) {
- SkDEBUGCODE(this->validate();)
-
- SkASSERT(SkAlign4(offset) == offset);
- SkASSERT(offset <= fSize);
-
- // try the fast case, where offset is within fTail
- if (offset >= fWrittenBeforeLastBlock) {
- return fTail->peek32(offset - fWrittenBeforeLastBlock);
- }
-
- Block* block = fHead;
- SkASSERT(NULL != block);
-
- while (offset >= block->fAllocatedSoFar) {
- offset -= block->fAllocatedSoFar;
- block = block->fNext;
- SkASSERT(NULL != block);
- }
- return block->peek32(offset);
-}
-
-void SkWriter32::rewindToOffset(size_t offset) {
- if (offset >= fSize) {
- return;
- }
- if (0 == offset) {
- this->reset();
- return;
- }
-
- SkDEBUGCODE(this->validate();)
-
- SkASSERT(SkAlign4(offset) == offset);
- SkASSERT(offset <= fSize);
- fSize = offset;
-
- // Try the fast case, where offset is within fTail
- if (offset >= fWrittenBeforeLastBlock) {
- fTail->fAllocatedSoFar = offset - fWrittenBeforeLastBlock;
- } else {
- // Similar to peek32, except that we free up any following blocks.
- // We have to re-compute fWrittenBeforeLastBlock as well.
-
- size_t globalOffset = offset;
- Block* block = fHead;
- SkASSERT(NULL != block);
- while (offset >= block->fAllocatedSoFar) {
- offset -= block->fAllocatedSoFar;
- block = block->fNext;
- SkASSERT(NULL != block);
- }
-
- // this has to be recomputed, since we may free up fTail
- fWrittenBeforeLastBlock = globalOffset - offset;
-
- // update the size on the "last" block
- block->fAllocatedSoFar = offset;
- // end our list
- fTail = block;
- Block* next = block->fNext;
- block->fNext = NULL;
- // free up any trailing blocks
- block = next;
- while (block) {
- Block* next = block->fNext;
- sk_free(block);
- block = next;
- }
- }
- SkDEBUGCODE(this->validate();)
-}
-
-void SkWriter32::flatten(void* dst) const {
- const Block* block = fHead;
- SkDEBUGCODE(size_t total = 0;)
-
- while (block) {
- size_t allocated = block->fAllocatedSoFar;
- memcpy(dst, block->base(), allocated);
- dst = (char*)dst + allocated;
- block = block->fNext;
-
- SkDEBUGCODE(total += allocated;)
- SkASSERT(total <= fSize);
- }
- SkASSERT(total == fSize);
-}
-
-uint32_t* SkWriter32::reservePad(size_t size) {
- if (size > 0) {
- size_t alignedSize = SkAlign4(size);
- char* dst = (char*)this->reserve(alignedSize);
- // Pad the last four bytes with zeroes in one step.
- uint32_t* padding = (uint32_t*)(dst + (alignedSize - 4));
- *padding = 0;
- return (uint32_t*) dst;
- }
- return this->reserve(0);
-}
-
-void SkWriter32::writePad(const void* src, size_t size) {
- if (size > 0) {
- char* dst = (char*)this->reservePad(size);
- // Copy the actual data.
- memcpy(dst, src, size);
- }
-}
-
-#include "SkStream.h"
-
-size_t SkWriter32::readFromStream(SkStream* stream, size_t length) {
- char scratch[1024];
- const size_t MAX = sizeof(scratch);
- size_t remaining = length;
-
- while (remaining != 0) {
- size_t n = remaining;
- if (n > MAX) {
- n = MAX;
- }
- size_t bytes = stream->read(scratch, n);
- this->writePad(scratch, bytes);
- remaining -= bytes;
- if (bytes != n) {
- break;
- }
- }
- return length - remaining;
-}
-
-bool SkWriter32::writeToStream(SkWStream* stream) {
- const Block* block = fHead;
- while (block) {
- if (!stream->write(block->base(), block->fAllocatedSoFar)) {
- return false;
- }
- block = block->fNext;
- }
- return true;
-}
-
-#ifdef SK_DEBUG
-void SkWriter32::validate() const {
- SkASSERT(SkIsAlign4(fSize));
-
- size_t accum = 0;
- const Block* block = fHead;
- while (block) {
- SkASSERT(SkIsAlign4(block->fSizeOfBlock));
- SkASSERT(SkIsAlign4(block->fAllocatedSoFar));
- SkASSERT(block->fAllocatedSoFar <= block->fSizeOfBlock);
- if (NULL == block->fNext) {
- SkASSERT(fTail == block);
- SkASSERT(fWrittenBeforeLastBlock == accum);
- }
- accum += block->fAllocatedSoFar;
- SkASSERT(accum <= fSize);
- block = block->fNext;
- }
- SkASSERT(accum == fSize);
-}
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-
#include "SkReader32.h"
#include "SkString.h"
+#include "SkWriter32.h"
/*
* Strings are stored as: length[4-bytes] + string_data + '\0' + pad_to_mul_4
@@ -247,7 +17,7 @@ const char* SkReader32::readString(size_t* outLen) {
size_t len = this->readInt();
const void* ptr = this->peek();
- // skip over teh string + '\0' and then pad to a multiple of 4
+ // skip over the string + '\0' and then pad to a multiple of 4
size_t alignedSize = SkAlign4(len + 1);
this->skip(alignedSize);
diff --git a/src/pipe/SkGPipeWrite.cpp b/src/pipe/SkGPipeWrite.cpp
index 68acc179e7..2948f25d27 100644
--- a/src/pipe/SkGPipeWrite.cpp
+++ b/src/pipe/SkGPipeWrite.cpp
@@ -370,7 +370,7 @@ void SkGPipeCanvas::flattenFactoryNames() {
bool SkGPipeCanvas::shuttleBitmap(const SkBitmap& bm, int32_t slot) {
SkASSERT(shouldFlattenBitmaps(fFlags));
- SkOrderedWriteBuffer buffer(1024);
+ SkOrderedWriteBuffer buffer;
buffer.setNamedFactoryRecorder(fFactorySet);
buffer.writeBitmap(bm);
this->flattenFactoryNames();
@@ -1146,7 +1146,7 @@ void SkGPipeCanvas::writePaint(const SkPaint& paint) {
this->writeOp(kSetAnnotation_DrawOp, 0, 0);
}
} else {
- SkOrderedWriteBuffer buffer(1024);
+ SkOrderedWriteBuffer buffer;
paint.getAnnotation()->writeToBuffer(buffer);
const size_t size = buffer.bytesWritten();
if (this->needOpBytes(size)) {
diff --git a/src/utils/SkCanvasStateUtils.cpp b/src/utils/SkCanvasStateUtils.cpp
index 9c7d8fab3a..eb92c37b19 100644
--- a/src/utils/SkCanvasStateUtils.cpp
+++ b/src/utils/SkCanvasStateUtils.cpp
@@ -155,9 +155,7 @@ static void setup_MC_state(SkMCState* state, const SkMatrix& matrix, const SkReg
* and some more common complex clips (e.g. a clipRect with a sub-rect
* clipped out of its interior) without needing to malloc any additional memory.
*/
- const int clipBufferSize = 4 * sizeof(ClipRect);
- char clipBuffer[clipBufferSize];
- SkWriter32 clipWriter(sizeof(ClipRect), clipBuffer, clipBufferSize);
+ SkSWriter32<4*sizeof(ClipRect)> clipWriter;
if (!clip.isEmpty()) {
// only returns the b/w clip so aa clips fail
@@ -201,9 +199,7 @@ SkCanvasState* SkCanvasStateUtils::CaptureCanvasState(SkCanvas* canvas) {
* some view systems (e.g. Android) that a few non-clipped layers are present
* and we will not need to malloc any additional memory in those cases.
*/
- const int layerBufferSize = 3 * sizeof(SkCanvasLayerState);
- char layerBuffer[layerBufferSize];
- SkWriter32 layerWriter(sizeof(SkCanvasLayerState), layerBuffer, layerBufferSize);
+ SkSWriter32<3*sizeof(SkCanvasLayerState)> layerWriter;
int layerCount = 0;
for (SkCanvas::LayerIter layer(canvas, true/*skipEmptyClips*/); !layer.done(); layer.next()) {
diff --git a/tests/AndroidPaintTest.cpp b/tests/AndroidPaintTest.cpp
index a638da9351..0f6c1e9a96 100644
--- a/tests/AndroidPaintTest.cpp
+++ b/tests/AndroidPaintTest.cpp
@@ -9,7 +9,7 @@
#include "TestClassDef.h"
static size_t Reconstruct(const SkPaint& src, SkPaint* dst) {
- SkOrderedWriteBuffer writer(64 /*arbitrary*/);
+ SkOrderedWriteBuffer writer;
src.flatten(writer);
const size_t size = writer.bytesWritten();
diff --git a/tests/ColorFilterTest.cpp b/tests/ColorFilterTest.cpp
index 8444b73a1d..7ad19b7aff 100644
--- a/tests/ColorFilterTest.cpp
+++ b/tests/ColorFilterTest.cpp
@@ -17,7 +17,7 @@
#include "SkOrderedWriteBuffer.h"
static SkColorFilter* reincarnate_colorfilter(SkFlattenable* obj) {
- SkOrderedWriteBuffer wb(1024);
+ SkOrderedWriteBuffer wb;
wb.writeFlattenable(obj);
size_t size = wb.size();
diff --git a/tests/PathTest.cpp b/tests/PathTest.cpp
index 3f07ae04bc..063f05d5ed 100644
--- a/tests/PathTest.cpp
+++ b/tests/PathTest.cpp
@@ -1838,7 +1838,7 @@ static void test_isNestedRects(skiatest::Reporter* reporter) {
static void write_and_read_back(skiatest::Reporter* reporter,
const SkPath& p) {
- SkWriter32 writer(100);
+ SkWriter32 writer;
writer.writePath(p);
size_t size = writer.bytesWritten();
SkAutoMalloc storage(size);
diff --git a/tests/SerializationTest.cpp b/tests/SerializationTest.cpp
index a2601d22a7..38e7051d59 100644
--- a/tests/SerializationTest.cpp
+++ b/tests/SerializationTest.cpp
@@ -111,7 +111,7 @@ template<> struct SerializationUtils<SkScalar> {
template<typename T>
static void TestObjectSerialization(T* testObj, skiatest::Reporter* reporter) {
- SkOrderedWriteBuffer writer(1024);
+ SkOrderedWriteBuffer writer;
writer.setFlags(SkOrderedWriteBuffer::kValidation_Flag);
SerializationUtils<T>::Write(writer, testObj);
size_t bytesWritten = writer.bytesWritten();
@@ -142,7 +142,7 @@ static void TestObjectSerialization(T* testObj, skiatest::Reporter* reporter) {
template<typename T>
static T* TestFlattenableSerialization(T* testObj, bool shouldSucceed,
skiatest::Reporter* reporter) {
- SkOrderedWriteBuffer writer(1024);
+ SkOrderedWriteBuffer writer;
writer.setFlags(SkOrderedWriteBuffer::kValidation_Flag);
SerializationUtils<T>::Write(writer, testObj);
size_t bytesWritten = writer.bytesWritten();
@@ -181,7 +181,7 @@ static T* TestFlattenableSerialization(T* testObj, bool shouldSucceed,
template<typename T>
static void TestArraySerialization(T* data, skiatest::Reporter* reporter) {
- SkOrderedWriteBuffer writer(1024);
+ SkOrderedWriteBuffer writer;
writer.setFlags(SkOrderedWriteBuffer::kValidation_Flag);
SerializationUtils<T>::Write(writer, data, kArraySize);
size_t bytesWritten = writer.bytesWritten();
diff --git a/tests/Writer32Test.cpp b/tests/Writer32Test.cpp
index 2032fefae7..a76c35d34c 100644
--- a/tests/Writer32Test.cpp
+++ b/tests/Writer32Test.cpp
@@ -24,13 +24,13 @@ static void test_reserve(skiatest::Reporter* reporter) {
// There used to be a bug where we'd assert your first reservation had to
// fit in external storage if you used it. This would crash in debug mode.
uint8_t storage[4];
- SkWriter32 writer(0, storage, sizeof(storage));
+ SkWriter32 writer(storage, sizeof(storage));
writer.reserve(40);
}
static void test_string_null(skiatest::Reporter* reporter) {
uint8_t storage[8];
- SkWriter32 writer(0, storage, sizeof(storage));
+ SkWriter32 writer(storage, sizeof(storage));
// Can we write NULL?
writer.writeString(NULL);
@@ -39,7 +39,7 @@ static void test_string_null(skiatest::Reporter* reporter) {
}
static void test_rewind(skiatest::Reporter* reporter) {
- SkSWriter32<32> writer(32);
+ SkSWriter32<32> writer;
int32_t array[3] = { 1, 2, 4 };
REPORTER_ASSERT(reporter, 0 == writer.bytesWritten());
@@ -58,7 +58,7 @@ static void test_rewind(skiatest::Reporter* reporter) {
// test rewinding past allocated chunks. This used to crash because we
// didn't truncate our link-list after freeing trailing blocks
{
- SkWriter32 writer(64);
+ SkWriter32 writer;
for (int i = 0; i < 100; ++i) {
writer.writeInt(i);
}
@@ -71,7 +71,7 @@ static void test_rewind(skiatest::Reporter* reporter) {
}
static void test_ptr(skiatest::Reporter* reporter) {
- SkSWriter32<32> writer(32);
+ SkSWriter32<32> writer;
void* p0 = reporter;
void* p1 = &writer;
@@ -186,65 +186,59 @@ static void testWritePad(skiatest::Reporter* reporter, SkWriter32* writer) {
}
}
-DEF_TEST(Writer32, reporter) {
- // dynamic allocator
- {
- SkWriter32 writer(256 * 4);
- test1(reporter, &writer);
+DEF_TEST(Writer32_dynamic, reporter) {
+ SkWriter32 writer;
+ test1(reporter, &writer);
- writer.reset();
- test2(reporter, &writer);
+ writer.reset();
+ test2(reporter, &writer);
- writer.reset();
- testWritePad(reporter, &writer);
- }
+ writer.reset();
+ testWritePad(reporter, &writer);
+}
- // storage-block
- {
- 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.
- uint32_t smallStorage[8];
- writer.reset(smallStorage, sizeof(smallStorage));
- test2(reporter, &writer);
- REPORTER_ASSERT(reporter, !writer.wroteOnlyToStorage());
- }
+DEF_TEST(Writer32_contiguous, reporter) {
+ uint32_t storage[256];
+ SkWriter32 writer;
+ writer.reset(storage, sizeof(storage));
+ // This write is small enough to fit in storage, so it's contiguous.
+ test1(reporter, &writer);
+ REPORTER_ASSERT(reporter, writer.contiguousArray() != NULL);
+
+ // This write is too big for the 32 byte storage block we provide.
+ writer.reset(storage, 32);
+ test2(reporter, &writer);
+ // Some data is in storage, some in writer's internal storage.
+ REPORTER_ASSERT(reporter, writer.contiguousArray() == NULL);
+
+ writer.reset();
+ test2(reporter, &writer);
+ // There is no external storage. All the data is in internal storage,
+ // so we can always read it contiguously.
+ REPORTER_ASSERT(reporter, writer.contiguousArray() != NULL);
+}
- // small storage
- {
- SkSWriter32<8 * sizeof(intptr_t)> writer(100);
- test1(reporter, &writer);
- writer.reset(); // should just rewind our storage
- test2(reporter, &writer);
+DEF_TEST(Writer32_small, reporter) {
+ SkSWriter32<8 * sizeof(intptr_t)> writer;
+ test1(reporter, &writer);
+ writer.reset(); // should just rewind our storage
+ test2(reporter, &writer);
- writer.reset();
- testWritePad(reporter, &writer);
- }
+ writer.reset();
+ testWritePad(reporter, &writer);
+}
- // large storage
- {
- SkSWriter32<1024 * sizeof(intptr_t)> writer(100);
- test1(reporter, &writer);
- writer.reset(); // should just rewind our storage
- test2(reporter, &writer);
+DEF_TEST(Writer32_large, reporter) {
+ SkSWriter32<1024 * sizeof(intptr_t)> writer;
+ test1(reporter, &writer);
+ writer.reset(); // should just rewind our storage
+ test2(reporter, &writer);
- writer.reset();
- testWritePad(reporter, &writer);
- }
+ writer.reset();
+ testWritePad(reporter, &writer);
+}
+DEF_TEST(Writer32_misc, reporter) {
test_reserve(reporter);
test_string_null(reporter);
test_ptr(reporter);