1 /* 2 * Copyright 2014 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #ifndef SkRecord_DEFINED 9 #define SkRecord_DEFINED 10 11 #include "SkChunkAlloc.h" 12 #include "SkRecords.h" 13 #include "SkTLogic.h" 14 #include "SkTemplates.h" 15 16 // SkRecord (REC-ord) represents a sequence of SkCanvas calls, saved for future use. 17 // These future uses may include: replay, optimization, serialization, or combinations of those. 18 // 19 // Though an enterprising user may find calling alloc(), append(), visit(), and mutate() enough to 20 // work with SkRecord, you probably want to look at SkRecorder which presents an SkCanvas interface 21 // for creating an SkRecord, and SkRecordDraw which plays an SkRecord back into another SkCanvas. 22 // 23 // SkRecord often looks like it's compatible with any type T, but really it's compatible with any 24 // type T which has a static const SkRecords::Type kType. That is to say, SkRecord is compatible 25 // only with SkRecords::* structs defined in SkRecords.h. Your compiler will helpfully yell if you 26 // get this wrong. 27 28 class SkRecord : SkNoncopyable { 29 enum { 30 kChunkBytes = 4096, 31 kFirstReserveCount = 64 / sizeof(void*), 32 }; 33 public: 34 SkRecord() : fAlloc(kChunkBytes), fCount(0), fReserved(0) {} 35 36 ~SkRecord() { 37 Destroyer destroyer; 38 for (unsigned i = 0; i < this->count(); i++) { 39 this->mutate<void>(i, destroyer); 40 } 41 } 42 43 // Returns the number of canvas commands in this SkRecord. 44 unsigned count() const { return fCount; } 45 46 // Visit the i-th canvas command with a functor matching this interface: 47 // template <typename T> 48 // R operator()(const T& record) { ... } 49 // This operator() must be defined for at least all SkRecords::*. 50 template <typename R, typename F> 51 R visit(unsigned i, F& f) const { 52 SkASSERT(i < this->count()); 53 return fRecords[i].visit<R>(fTypes[i], f); 54 } 55 56 // Mutate the i-th canvas command with a functor matching this interface: 57 // template <typename T> 58 // R operator()(T* record) { ... } 59 // This operator() must be defined for at least all SkRecords::*. 60 template <typename R, typename F> 61 R mutate(unsigned i, F& f) { 62 SkASSERT(i < this->count()); 63 return fRecords[i].mutate<R>(fTypes[i], f); 64 } 65 // TODO: It'd be nice to infer R from F for visit and mutate if we ever get std::result_of. 66 67 // Allocate contiguous space for count Ts, to be freed when the SkRecord is destroyed. 68 // Here T can be any class, not just those from SkRecords. Throws on failure. 69 template <typename T> 70 T* alloc(size_t count = 1) { 71 // Bump up to the next pointer width if needed, so all allocations start pointer-aligned. 72 return (T*)fAlloc.allocThrow(SkAlignPtr(sizeof(T) * count)); 73 } 74 75 // Add a new command of type T to the end of this SkRecord. 76 // You are expected to placement new an object of type T onto this pointer. 77 template <typename T> 78 T* append() { 79 if (fCount == fReserved) { 80 fReserved = SkTMax<unsigned>(kFirstReserveCount, fReserved*2); 81 fRecords.realloc(fReserved); 82 fTypes.realloc(fReserved); 83 } 84 85 fTypes[fCount] = T::kType; 86 return fRecords[fCount++].set(this->allocCommand<T>()); 87 } 88 89 // Replace the i-th command with a new command of type T. 90 // You are expected to placement new an object of type T onto this pointer. 91 // References to the original command are invalidated. 92 template <typename T> 93 T* replace(unsigned i) { 94 SkASSERT(i < this->count()); 95 96 Destroyer destroyer; 97 this->mutate<void>(i, destroyer); 98 99 fTypes[i] = T::kType; 100 return fRecords[i].set(this->allocCommand<T>()); 101 } 102 103 // Replace the i-th command with a new command of type T. 104 // You are expected to placement new an object of type T onto this pointer. 105 // You must show proof that you've already adopted the existing command. 106 template <typename T, typename Existing> 107 T* replace(unsigned i, const SkRecords::Adopted<Existing>& proofOfAdoption) { 108 SkASSERT(i < this->count()); 109 110 SkASSERT(Existing::kType == fTypes[i]); 111 SkASSERT(proofOfAdoption == fRecords[i].ptr<Existing>()); 112 113 fTypes[i] = T::kType; 114 return fRecords[i].set(this->allocCommand<T>()); 115 } 116 117 private: 118 // Implementation notes! 119 // 120 // Logically an SkRecord is structured as an array of pointers into a big chunk of memory where 121 // records representing each canvas draw call are stored: 122 // 123 // fRecords: [*][*][*]... 124 // | | | 125 // | | | 126 // | | +---------------------------------------+ 127 // | +-----------------+ | 128 // | | | 129 // v v v 130 // fAlloc: [SkRecords::DrawRect][SkRecords::DrawPosTextH][SkRecords::DrawRect]... 131 // 132 // In the scheme above, the pointers in fRecords are void*: they have no type. The type is not 133 // stored in fAlloc either; we just write raw data there. But we need that type information. 134 // Here are some options: 135 // 1) use inheritance, virtuals, and vtables to make the fRecords pointers smarter 136 // 2) store the type data manually in fAlloc at the start of each record 137 // 3) store the type data manually somewhere with fRecords 138 // 139 // This code uses approach 3). The implementation feels very similar to 1), but it's 140 // devirtualized instead of using the language's polymorphism mechanisms. This lets us work 141 // with the types themselves (as SkRecords::Type), a sort of limited free RTTI; it lets us pay 142 // only 1 byte to store the type instead of a full pointer (4-8 bytes); and it leads to better 143 // decoupling between the SkRecords::* record types and the operations performed on them in 144 // visit() or mutate(). The recorded canvas calls don't have to have any idea about the 145 // operations performed on them. 146 // 147 // We store the types in a parallel fTypes array, mainly so that they can be tightly packed as 148 // single bytes. This has the side effect of allowing very fast analysis passes over an 149 // SkRecord looking for just patterns of draw commands (or using this as a quick reject 150 // mechanism) though there's admittedly not a very good API exposed publically for this. 151 // 152 // The cost to append a T into this structure is 1 + sizeof(void*) + sizeof(T). 153 154 // A mutator that can be used with replace to destroy canvas commands. 155 struct Destroyer { 156 template <typename T> 157 void operator()(T* record) { record->~T(); } 158 }; 159 160 // Logically the same as SkRecords::Type, but packed into 8 bits. 161 struct Type8 { 162 public: 163 // This intentionally converts implicitly back and forth. 164 Type8(SkRecords::Type type) : fType(type) { SkASSERT(*this == type); } 165 operator SkRecords::Type () { return (SkRecords::Type)fType; } 166 167 private: 168 uint8_t fType; 169 }; 170 171 // No point in allocating any more than one of an empty struct. 172 // We could just return NULL but it's sort of confusing to return NULL on success. 173 template <typename T> 174 SK_WHEN(SkTIsEmpty<T>, T*) allocCommand() { 175 static T singleton = {}; 176 return &singleton; 177 } 178 179 template <typename T> 180 SK_WHEN(!SkTIsEmpty<T>, T*) allocCommand() { return this->alloc<T>(); } 181 182 // An untyped pointer to some bytes in fAlloc. This is the interface for polymorphic dispatch: 183 // visit() and mutate() work with the parallel fTypes array to do the work of a vtable. 184 struct Record { 185 public: 186 // Point this record to its data in fAlloc. Returns ptr for convenience. 187 template <typename T> 188 T* set(T* ptr) { 189 fPtr = ptr; 190 return ptr; 191 } 192 193 // Get the data in fAlloc, assuming it's of type T. 194 template <typename T> 195 T* ptr() const { return (T*)fPtr; } 196 197 // Visit this record with functor F (see public API above) assuming the record we're 198 // pointing to has this type. 199 template <typename R, typename F> 200 R visit(Type8 type, F& f) const { 201 #define CASE(T) case SkRecords::T##_Type: return f(*this->ptr<SkRecords::T>()); 202 switch(type) { SK_RECORD_TYPES(CASE) } 203 #undef CASE 204 SkDEBUGFAIL("Unreachable"); 205 return R(); 206 } 207 208 // Mutate this record with functor F (see public API above) assuming the record we're 209 // pointing to has this type. 210 template <typename R, typename F> 211 R mutate(Type8 type, F& f) { 212 #define CASE(T) case SkRecords::T##_Type: return f(this->ptr<SkRecords::T>()); 213 switch(type) { SK_RECORD_TYPES(CASE) } 214 #undef CASE 215 SkDEBUGFAIL("Unreachable"); 216 return R(); 217 } 218 219 private: 220 void* fPtr; 221 }; 222 223 // fAlloc needs to be a data structure which can append variable length data in contiguous 224 // chunks, returning a stable handle to that data for later retrieval. 225 // 226 // fRecords and fTypes need to be data structures that can append fixed length data, and need to 227 // support efficient random access and forward iteration. (They don't need to be contiguous.) 228 229 SkChunkAlloc fAlloc; 230 SkAutoTMalloc<Record> fRecords; 231 SkAutoTMalloc<Type8> fTypes; 232 // fCount and fReserved measure both fRecords and fTypes, which always grow in lock step. 233 unsigned fCount; 234 unsigned fReserved; 235 }; 236 237 #endif//SkRecord_DEFINED 238