| Index: src/record/SkRecord.h
|
| diff --git a/src/record/SkRecord.h b/src/record/SkRecord.h
|
| index 4c2f5b5ed4d40510eabb14bb7bcb037d50b50f49..ddf1b3d95fabaf927c45bd7152263699f67a7097 100644
|
| --- a/src/record/SkRecord.h
|
| +++ b/src/record/SkRecord.h
|
| @@ -31,59 +31,43 @@ public:
|
|
|
| ~SkRecord() {
|
| Destroyer destroyer;
|
| - this->mutate(destroyer);
|
| + for (unsigned i = 0; i < this->count(); i++) {
|
| + this->mutate(i, destroyer);
|
| + }
|
| }
|
|
|
| + // Returns the number of canvas commands in this SkRecord.
|
| unsigned count() const { return fCount; }
|
|
|
| - // Accepts a visitor functor with this interface:
|
| + // Visit the i-th canvas command with a functor matching this interface:
|
| // template <typename T>
|
| // void operator()(const T& record) { ... }
|
| - // This operator() must be defined for at least all SkRecords::*; your compiler will help you
|
| - // get this right.
|
| + // This operator() must be defined for at least all SkRecords::*.
|
| template <typename F>
|
| void visit(unsigned i, F& f) const {
|
| SkASSERT(i < this->count());
|
| fRecords[i].visit(fTypes[i], f);
|
| }
|
|
|
| - // As above. f will be called on each recorded canvas call in the order they were append()ed.
|
| - template <typename F>
|
| - void visit(F& f) const {
|
| - for (unsigned i = 0; i < fCount; i++) {
|
| - this->visit(i, f);
|
| - }
|
| - }
|
| -
|
| - // Accepts a visitor functor with this interface:
|
| + // Mutate the i-th canvas command with a functor matching this interface:
|
| // template <typename T>
|
| // void operator()(T* record) { ... }
|
| - // This operator() must be defined for at least all SkRecords::*; again, your compiler will help
|
| - // you get this right.
|
| + // This operator() must be defined for at least all SkRecords::*.
|
| template <typename F>
|
| void mutate(unsigned i, F& f) {
|
| SkASSERT(i < this->count());
|
| fRecords[i].mutate(fTypes[i], f);
|
| }
|
|
|
| - // As above. f will be called on each recorded canvas call in the order they were append()ed.
|
| - template <typename F>
|
| - void mutate(F& f) {
|
| - for (unsigned i = 0; i < fCount; i++) {
|
| - this->mutate(i, f);
|
| - }
|
| - }
|
| -
|
| - // Allocate contiguous space for count Ts, to be destroyed (not just freed) when the SkRecord is
|
| - // destroyed. For classes with constructors, placement new into this array. Throws on failure.
|
| - // Here T can really be any class, not just those from SkRecords.
|
| + // Allocate contiguous space for count Ts, to be freed when the SkRecord is destroyed.
|
| + // Here T can be any class, not just those from SkRecords. Throws on failure.
|
| template <typename T>
|
| T* alloc(unsigned count = 1) {
|
| return (T*)fAlloc.allocThrow(sizeof(T) * count);
|
| }
|
|
|
| - // Allocate space to record a canvas call of type T at the end of this SkRecord. You are
|
| - // expected to placement new an object of type T onto this pointer.
|
| + // Add a new command of type T to the end of this SkRecord.
|
| + // You are expected to placement new an object of type T onto this pointer.
|
| template <typename T>
|
| T* append() {
|
| if (fCount == fReserved) {
|
| @@ -93,9 +77,26 @@ public:
|
| }
|
|
|
| fTypes[fCount] = T::kType;
|
| - return fRecords[fCount++].alloc<T>(this);
|
| + return fRecords[fCount++].set(this->alloc<T>());
|
| }
|
|
|
| + // Replace the i-th command with a new command of type T.
|
| + // You are expected to placement new an object of type T onto this pointer.
|
| + // References to the old command remain valid for the life of the SkRecord, but
|
| + // you must destroy the old command. (It's okay to destroy it first before calling replace.)
|
| + template <typename T>
|
| + T* replace(unsigned i) {
|
| + SkASSERT(i < this->count());
|
| + fTypes[i] = T::kType;
|
| + return fRecords[i].set(this->alloc<T>());
|
| + }
|
| +
|
| + // A mutator that can be used with replace to destroy canvas commands.
|
| + struct Destroyer {
|
| + template <typename T>
|
| + void operator()(T* record) { record->~T(); }
|
| + };
|
| +
|
| private:
|
| // Implementation notes!
|
| //
|
| @@ -131,23 +132,9 @@ private:
|
| // SkRecord looking for just patterns of draw commands (or using this as a quick reject
|
| // mechanism) though there's admittedly not a very good API exposed publically for this.
|
| //
|
| - // We pull one final sneaky trick in the implementation. When recording canvas calls that need
|
| - // to store less than a pointer of data, we don't go through the usual path of allocating the
|
| - // draw command in fAlloc and a pointer to it in fRecords; instead, we ignore fAlloc and
|
| - // directly allocate the object in the space we would have put the pointer in fRecords. This is
|
| - // why you'll see uintptr_t instead of void* in Record below.
|
| - //
|
| - // The cost of appending a single record into this structure is then:
|
| - // - 1 + sizeof(void*) + sizeof(T) if sizeof(T) > sizeof(void*)
|
| - // - 1 + sizeof(void*) if sizeof(T) <= sizeof(void*)
|
| + // The cost to append a T into this structure is 1 + sizeof(void*) + sizeof(T).
|
|
|
|
|
| - // A mutator that calls destructors of all the canvas calls we've recorded.
|
| - struct Destroyer {
|
| - template <typename T>
|
| - void operator()(T* record) { record->~T(); }
|
| - };
|
| -
|
| // Logically the same as SkRecords::Type, but packed into 8 bits.
|
| struct Type8 {
|
| public:
|
| @@ -159,19 +146,15 @@ private:
|
| uint8_t fType;
|
| };
|
|
|
| - // Logically a void* to some bytes in fAlloc, but maybe has the bytes stored immediately
|
| - // instead. This is also the main interface for devirtualized polymorphic dispatch: see visit()
|
| - // and mutate(), which essentially do the work of the missing vtable.
|
| + // An untyped pointer to some bytes in fAlloc. This is the interface for polymorphic dispatch:
|
| + // visit() and mutate() work with the parallel fTypes array to do the work of a vtable.
|
| struct Record {
|
| public:
|
| -
|
| - // Allocate space for a T, perhaps using the SkRecord to allocate that space.
|
| + // Point this record to its data in fAlloc. Returns ptr for convenience.
|
| template <typename T>
|
| - T* alloc(SkRecord* record) {
|
| - if (IsLarge<T>()) {
|
| - fRecord = (uintptr_t)record->alloc<T>();
|
| - }
|
| - return this->ptr<T>();
|
| + T* set(T* ptr) {
|
| + fPtr = ptr;
|
| + return ptr;
|
| }
|
|
|
| // Visit this record with functor F (see public API above) assuming the record we're
|
| @@ -194,13 +177,9 @@ private:
|
|
|
| private:
|
| template <typename T>
|
| - T* ptr() const { return (T*)(IsLarge<T>() ? (void*)fRecord : &fRecord); }
|
| -
|
| - // Is T too big to fit directly into a uintptr_t, neededing external allocation?
|
| - template <typename T>
|
| - static bool IsLarge() { return sizeof(T) > sizeof(uintptr_t); }
|
| + T* ptr() const { return (T*)fPtr; }
|
|
|
| - uintptr_t fRecord;
|
| + void* fPtr;
|
| };
|
|
|
| // fAlloc needs to be a data structure which can append variable length data in contiguous
|
|
|