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