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 "include/core/SkRefCnt.h" 12 #include "include/private/base/SkAssert.h" 13 #include "include/private/base/SkTemplates.h" 14 #include "src/base/SkArenaAlloc.h" 15 #include "src/core/SkRecords.h" 16 17 #include <cstddef> 18 #include <type_traits> 19 20 // SkRecord represents a sequence of SkCanvas calls, saved for future use. 21 // These future uses may include: replay, optimization, serialization, or combinations of those. 22 // 23 // Though an enterprising user may find calling alloc(), append(), visit(), and mutate() enough to 24 // work with SkRecord, you probably want to look at SkRecorder which presents an SkCanvas interface 25 // for creating an SkRecord, and SkRecordDraw which plays an SkRecord back into another SkCanvas. 26 // 27 // SkRecord often looks like it's compatible with any type T, but really it's compatible with any 28 // type T which has a static const SkRecords::Type kType. That is to say, SkRecord is compatible 29 // only with SkRecords::* structs defined in SkRecords.h. Your compiler will helpfully yell if you 30 // get this wrong. 31 32 class SkRecord : public SkRefCnt { 33 public: 34 SkRecord() = default; 35 ~SkRecord() override; 36 37 // Returns the number of canvas commands in this SkRecord. count()38 int count() const { return fCount; } 39 40 // Visit the i-th canvas command with a functor matching this interface: 41 // template <typename T> 42 // R operator()(const T& record) { ... } 43 // This operator() must be defined for at least all SkRecords::*. 44 template <typename F> 45 auto visit(int i, F&& f) const -> decltype(f(SkRecords::NoOp())) { 46 return fRecords[i].visit(f); 47 } 48 49 // Mutate the i-th canvas command with a functor matching this interface: 50 // template <typename T> 51 // R operator()(T* record) { ... } 52 // This operator() must be defined for at least all SkRecords::*. 53 template <typename F> 54 auto mutate(int i, F&& f) -> decltype(f((SkRecords::NoOp*)nullptr)) { 55 return fRecords[i].mutate(f); 56 } 57 58 // Allocate contiguous space for count Ts, to be freed when the SkRecord is destroyed. 59 // Here T can be any class, not just those from SkRecords. Throws on failure. 60 template <typename T> 61 T* alloc(size_t count = 1) { 62 struct RawBytes { 63 alignas(T) char data[sizeof(T)]; 64 }; 65 fApproxBytesAllocated += count * sizeof(T) + alignof(T); 66 return (T*)fAlloc.makeArrayDefault<RawBytes>(count); 67 } 68 69 // Add a new command of type T to the end of this SkRecord. 70 // You are expected to placement new an object of type T onto this pointer. 71 template <typename T> append()72 T* append() { 73 if (fCount == fReserved) { 74 this->grow(); 75 } 76 return fRecords[fCount++].set(this->allocCommand<T>()); 77 } 78 79 // Replace the i-th command with a new command of type T. 80 // You are expected to placement new an object of type T onto this pointer. 81 // References to the original command are invalidated. 82 template <typename T> replace(int i)83 T* replace(int i) { 84 SkASSERT(i < this->count()); 85 86 Destroyer destroyer; 87 this->mutate(i, destroyer); 88 89 return fRecords[i].set(this->allocCommand<T>()); 90 } 91 92 // Does not return the bytes in any pointers embedded in the Records; callers 93 // need to iterate with a visitor to measure those they care for. 94 size_t bytesUsed() const; 95 96 // Rearrange and resize this record to eliminate any NoOps. 97 // May change count() and the indices of ops, but preserves their order. 98 void defrag(); 99 100 private: 101 // An SkRecord is structured as an array of pointers into a big chunk of memory where 102 // records representing each canvas draw call are stored: 103 // 104 // fRecords: [*][*][*]... 105 // | | | 106 // | | | 107 // | | +---------------------------------------+ 108 // | +-----------------+ | 109 // | | | 110 // v v v 111 // fAlloc: [SkRecords::DrawRect][SkRecords::DrawPosTextH][SkRecords::DrawRect]... 112 // 113 // We store the types of each of the pointers alongside the pointer. 114 // The cost to append a T to this structure is 8 + sizeof(T) bytes. 115 116 // A mutator that can be used with replace to destroy canvas commands. 117 struct Destroyer { 118 template <typename T> operatorDestroyer119 void operator()(T* record) { record->~T(); } 120 }; 121 122 template <typename T> allocCommand()123 std::enable_if_t<std::is_empty<T>::value, T*> allocCommand() { 124 static T singleton = {}; 125 return &singleton; 126 } 127 128 template <typename T> allocCommand()129 std::enable_if_t<!std::is_empty<T>::value, T*> allocCommand() { return this->alloc<T>(); } 130 131 void grow(); 132 133 // A typed pointer to some bytes in fAlloc. visit() and mutate() allow polymorphic dispatch. 134 struct Record { 135 SkRecords::Type fType; 136 void* fPtr; 137 138 // Point this record to its data in fAlloc. Returns ptr for convenience. 139 template <typename T> setRecord140 T* set(T* ptr) { 141 fType = T::kType; 142 fPtr = ptr; 143 SkASSERT(this->ptr() == ptr && this->type() == T::kType); 144 return ptr; 145 } 146 typeRecord147 SkRecords::Type type() const { return fType; } ptrRecord148 void* ptr() const { return fPtr; } 149 150 // Visit this record with functor F (see public API above). 151 template <typename F> 152 auto visit(F&& f) const -> decltype(f(SkRecords::NoOp())) { 153 #define CASE(T) case SkRecords::T##_Type: return f(*(const SkRecords::T*)this->ptr()); 154 switch(this->type()) { SK_RECORD_TYPES(CASE) } 155 #undef CASE 156 SkDEBUGFAIL("Unreachable"); 157 static const SkRecords::NoOp noop{}; 158 return f(noop); 159 } 160 161 // Mutate this record with functor F (see public API above). 162 template <typename F> 163 auto mutate(F&& f) -> decltype(f((SkRecords::NoOp*)nullptr)) { 164 #define CASE(T) case SkRecords::T##_Type: return f((SkRecords::T*)this->ptr()); 165 switch(this->type()) { SK_RECORD_TYPES(CASE) } 166 #undef CASE 167 SkDEBUGFAIL("Unreachable"); 168 static const SkRecords::NoOp noop{}; 169 return f(const_cast<SkRecords::NoOp*>(&noop)); 170 } 171 }; 172 173 // fRecords needs to be a data structure that can append fixed length data, and need to 174 // support efficient random access and forward iteration. (It doesn't need to be contiguous.) 175 int fCount{0}, 176 fReserved{0}; 177 skia_private::AutoTMalloc<Record> fRecords; 178 179 // fAlloc needs to be a data structure which can append variable length data in contiguous 180 // chunks, returning a stable handle to that data for later retrieval. 181 SkArenaAlloc fAlloc{256}; 182 size_t fApproxBytesAllocated{0}; 183 }; 184 185 #endif//SkRecord_DEFINED 186