commit-bot@chromium.org | c4b21e6 | 2014-04-11 18:33:31 +0000 | [diff] [blame] | 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 | |
commit-bot@chromium.org | e3ff558 | 2014-04-01 16:24:06 +0000 | [diff] [blame] | 8 | #ifndef SkRecord_DEFINED |
| 9 | #define SkRecord_DEFINED |
| 10 | |
Herb Derby | 4304d11 | 2017-04-18 11:42:49 -0400 | [diff] [blame] | 11 | #include "SkArenaAlloc.h" |
commit-bot@chromium.org | e3ff558 | 2014-04-01 16:24:06 +0000 | [diff] [blame] | 12 | #include "SkRecords.h" |
commit-bot@chromium.org | 08bf86c | 2014-05-07 18:01:57 +0000 | [diff] [blame] | 13 | #include "SkTLogic.h" |
commit-bot@chromium.org | e3ff558 | 2014-04-01 16:24:06 +0000 | [diff] [blame] | 14 | #include "SkTemplates.h" |
| 15 | |
mtklein | 29b1afc | 2015-04-09 07:46:41 -0700 | [diff] [blame] | 16 | // SkRecord represents a sequence of SkCanvas calls, saved for future use. |
commit-bot@chromium.org | e3ff558 | 2014-04-01 16:24:06 +0000 | [diff] [blame] | 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 | |
Mike Klein | cd25df9c | 2017-04-11 10:50:27 -0400 | [diff] [blame] | 28 | class SkRecord : public SkRefCnt { |
commit-bot@chromium.org | e3ff558 | 2014-04-01 16:24:06 +0000 | [diff] [blame] | 29 | public: |
Herb Derby | 4304d11 | 2017-04-18 11:42:49 -0400 | [diff] [blame] | 30 | SkRecord() = default; |
mtklein | f98862c | 2014-11-24 14:45:47 -0800 | [diff] [blame] | 31 | ~SkRecord(); |
commit-bot@chromium.org | 506db0b | 2014-04-08 23:31:35 +0000 | [diff] [blame] | 32 | |
commit-bot@chromium.org | 88c3e27 | 2014-04-22 16:57:20 +0000 | [diff] [blame] | 33 | // Returns the number of canvas commands in this SkRecord. |
mtklein | c6ad06a | 2015-08-19 09:51:00 -0700 | [diff] [blame] | 34 | int count() const { return fCount; } |
commit-bot@chromium.org | e3ff558 | 2014-04-01 16:24:06 +0000 | [diff] [blame] | 35 | |
commit-bot@chromium.org | 88c3e27 | 2014-04-22 16:57:20 +0000 | [diff] [blame] | 36 | // Visit the i-th canvas command with a functor matching this interface: |
commit-bot@chromium.org | e3ff558 | 2014-04-01 16:24:06 +0000 | [diff] [blame] | 37 | // template <typename T> |
commit-bot@chromium.org | c71da1f | 2014-05-07 21:16:09 +0000 | [diff] [blame] | 38 | // R operator()(const T& record) { ... } |
commit-bot@chromium.org | 88c3e27 | 2014-04-22 16:57:20 +0000 | [diff] [blame] | 39 | // This operator() must be defined for at least all SkRecords::*. |
mtklein | 343a63d | 2016-03-22 11:46:53 -0700 | [diff] [blame] | 40 | template <typename F> |
| 41 | auto visit(int i, F&& f) const -> decltype(f(SkRecords::NoOp())) { |
| 42 | return fRecords[i].visit(f); |
commit-bot@chromium.org | 506db0b | 2014-04-08 23:31:35 +0000 | [diff] [blame] | 43 | } |
| 44 | |
commit-bot@chromium.org | 88c3e27 | 2014-04-22 16:57:20 +0000 | [diff] [blame] | 45 | // Mutate the i-th canvas command with a functor matching this interface: |
commit-bot@chromium.org | e3ff558 | 2014-04-01 16:24:06 +0000 | [diff] [blame] | 46 | // template <typename T> |
commit-bot@chromium.org | c71da1f | 2014-05-07 21:16:09 +0000 | [diff] [blame] | 47 | // R operator()(T* record) { ... } |
commit-bot@chromium.org | 88c3e27 | 2014-04-22 16:57:20 +0000 | [diff] [blame] | 48 | // This operator() must be defined for at least all SkRecords::*. |
mtklein | 343a63d | 2016-03-22 11:46:53 -0700 | [diff] [blame] | 49 | template <typename F> |
| 50 | auto mutate(int i, F&& f) -> decltype(f((SkRecords::NoOp*)nullptr)) { |
| 51 | return fRecords[i].mutate(f); |
commit-bot@chromium.org | 506db0b | 2014-04-08 23:31:35 +0000 | [diff] [blame] | 52 | } |
mtklein | 29b1afc | 2015-04-09 07:46:41 -0700 | [diff] [blame] | 53 | |
commit-bot@chromium.org | 88c3e27 | 2014-04-22 16:57:20 +0000 | [diff] [blame] | 54 | // Allocate contiguous space for count Ts, to be freed when the SkRecord is destroyed. |
| 55 | // Here T can be any class, not just those from SkRecords. Throws on failure. |
commit-bot@chromium.org | e3ff558 | 2014-04-01 16:24:06 +0000 | [diff] [blame] | 56 | template <typename T> |
reed | 2347b62 | 2014-08-07 12:19:50 -0700 | [diff] [blame] | 57 | T* alloc(size_t count = 1) { |
Herb Derby | 4304d11 | 2017-04-18 11:42:49 -0400 | [diff] [blame] | 58 | struct RawBytes { |
| 59 | alignas(T) char data[sizeof(T)]; |
| 60 | }; |
| 61 | fApproxBytesAllocated += count * sizeof(T) + alignof(T); |
| 62 | return (T*)fAlloc.makeArrayDefault<RawBytes>(count); |
commit-bot@chromium.org | e3ff558 | 2014-04-01 16:24:06 +0000 | [diff] [blame] | 63 | } |
| 64 | |
commit-bot@chromium.org | 88c3e27 | 2014-04-22 16:57:20 +0000 | [diff] [blame] | 65 | // Add a new command of type T to the end of this SkRecord. |
| 66 | // You are expected to placement new an object of type T onto this pointer. |
commit-bot@chromium.org | e3ff558 | 2014-04-01 16:24:06 +0000 | [diff] [blame] | 67 | template <typename T> |
| 68 | T* append() { |
| 69 | if (fCount == fReserved) { |
mtklein | f98862c | 2014-11-24 14:45:47 -0800 | [diff] [blame] | 70 | this->grow(); |
commit-bot@chromium.org | e3ff558 | 2014-04-01 16:24:06 +0000 | [diff] [blame] | 71 | } |
commit-bot@chromium.org | 08bf86c | 2014-05-07 18:01:57 +0000 | [diff] [blame] | 72 | return fRecords[fCount++].set(this->allocCommand<T>()); |
commit-bot@chromium.org | e3ff558 | 2014-04-01 16:24:06 +0000 | [diff] [blame] | 73 | } |
| 74 | |
commit-bot@chromium.org | 88c3e27 | 2014-04-22 16:57:20 +0000 | [diff] [blame] | 75 | // Replace the i-th command with a new command of type T. |
| 76 | // You are expected to placement new an object of type T onto this pointer. |
commit-bot@chromium.org | f0ae5e4 | 2014-04-24 15:33:48 +0000 | [diff] [blame] | 77 | // References to the original command are invalidated. |
commit-bot@chromium.org | 88c3e27 | 2014-04-22 16:57:20 +0000 | [diff] [blame] | 78 | template <typename T> |
mtklein | c6ad06a | 2015-08-19 09:51:00 -0700 | [diff] [blame] | 79 | T* replace(int i) { |
commit-bot@chromium.org | 88c3e27 | 2014-04-22 16:57:20 +0000 | [diff] [blame] | 80 | SkASSERT(i < this->count()); |
commit-bot@chromium.org | f0ae5e4 | 2014-04-24 15:33:48 +0000 | [diff] [blame] | 81 | |
| 82 | Destroyer destroyer; |
mtklein | 343a63d | 2016-03-22 11:46:53 -0700 | [diff] [blame] | 83 | this->mutate(i, destroyer); |
commit-bot@chromium.org | f0ae5e4 | 2014-04-24 15:33:48 +0000 | [diff] [blame] | 84 | |
commit-bot@chromium.org | 08bf86c | 2014-05-07 18:01:57 +0000 | [diff] [blame] | 85 | return fRecords[i].set(this->allocCommand<T>()); |
commit-bot@chromium.org | 88c3e27 | 2014-04-22 16:57:20 +0000 | [diff] [blame] | 86 | } |
| 87 | |
commit-bot@chromium.org | f0ae5e4 | 2014-04-24 15:33:48 +0000 | [diff] [blame] | 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 | // You must show proof that you've already adopted the existing command. |
| 91 | template <typename T, typename Existing> |
mtklein | c6ad06a | 2015-08-19 09:51:00 -0700 | [diff] [blame] | 92 | T* replace(int i, const SkRecords::Adopted<Existing>& proofOfAdoption) { |
commit-bot@chromium.org | f0ae5e4 | 2014-04-24 15:33:48 +0000 | [diff] [blame] | 93 | SkASSERT(i < this->count()); |
| 94 | |
mtklein | 29b1afc | 2015-04-09 07:46:41 -0700 | [diff] [blame] | 95 | SkASSERT(Existing::kType == fRecords[i].type()); |
| 96 | SkASSERT(proofOfAdoption == fRecords[i].ptr()); |
commit-bot@chromium.org | f0ae5e4 | 2014-04-24 15:33:48 +0000 | [diff] [blame] | 97 | |
commit-bot@chromium.org | 08bf86c | 2014-05-07 18:01:57 +0000 | [diff] [blame] | 98 | return fRecords[i].set(this->allocCommand<T>()); |
commit-bot@chromium.org | f0ae5e4 | 2014-04-24 15:33:48 +0000 | [diff] [blame] | 99 | } |
commit-bot@chromium.org | 88c3e27 | 2014-04-22 16:57:20 +0000 | [diff] [blame] | 100 | |
tomhudson | 158fcaa | 2014-11-19 10:41:14 -0800 | [diff] [blame] | 101 | // Does not return the bytes in any pointers embedded in the Records; callers |
| 102 | // need to iterate with a visitor to measure those they care for. |
mtklein | f98862c | 2014-11-24 14:45:47 -0800 | [diff] [blame] | 103 | size_t bytesUsed() const; |
tomhudson | 158fcaa | 2014-11-19 10:41:14 -0800 | [diff] [blame] | 104 | |
mtklein | c3c6194 | 2015-11-19 07:23:49 -0800 | [diff] [blame] | 105 | // Rearrange and resize this record to eliminate any NoOps. |
| 106 | // May change count() and the indices of ops, but preserves their order. |
| 107 | void defrag(); |
| 108 | |
commit-bot@chromium.org | e3ff558 | 2014-04-01 16:24:06 +0000 | [diff] [blame] | 109 | private: |
mtklein | 29b1afc | 2015-04-09 07:46:41 -0700 | [diff] [blame] | 110 | // An SkRecord is structured as an array of pointers into a big chunk of memory where |
commit-bot@chromium.org | e3ff558 | 2014-04-01 16:24:06 +0000 | [diff] [blame] | 111 | // records representing each canvas draw call are stored: |
| 112 | // |
| 113 | // fRecords: [*][*][*]... |
| 114 | // | | | |
| 115 | // | | | |
| 116 | // | | +---------------------------------------+ |
| 117 | // | +-----------------+ | |
| 118 | // | | | |
| 119 | // v v v |
| 120 | // fAlloc: [SkRecords::DrawRect][SkRecords::DrawPosTextH][SkRecords::DrawRect]... |
| 121 | // |
mtklein | 29b1afc | 2015-04-09 07:46:41 -0700 | [diff] [blame] | 122 | // We store the types of each of the pointers alongside the pointer. |
| 123 | // The cost to append a T to this structure is 8 + sizeof(T) bytes. |
commit-bot@chromium.org | e3ff558 | 2014-04-01 16:24:06 +0000 | [diff] [blame] | 124 | |
commit-bot@chromium.org | f0ae5e4 | 2014-04-24 15:33:48 +0000 | [diff] [blame] | 125 | // A mutator that can be used with replace to destroy canvas commands. |
| 126 | struct Destroyer { |
| 127 | template <typename T> |
| 128 | void operator()(T* record) { record->~T(); } |
| 129 | }; |
commit-bot@chromium.org | e3ff558 | 2014-04-01 16:24:06 +0000 | [diff] [blame] | 130 | |
commit-bot@chromium.org | 08bf86c | 2014-05-07 18:01:57 +0000 | [diff] [blame] | 131 | template <typename T> |
bungeman | 0e10166 | 2016-01-08 11:05:09 -0800 | [diff] [blame] | 132 | SK_WHEN(std::is_empty<T>::value, T*) allocCommand() { |
commit-bot@chromium.org | 50ca12b | 2014-05-07 19:56:27 +0000 | [diff] [blame] | 133 | static T singleton = {}; |
commit-bot@chromium.org | 08bf86c | 2014-05-07 18:01:57 +0000 | [diff] [blame] | 134 | return &singleton; |
| 135 | } |
| 136 | |
| 137 | template <typename T> |
bungeman | 0e10166 | 2016-01-08 11:05:09 -0800 | [diff] [blame] | 138 | SK_WHEN(!std::is_empty<T>::value, T*) allocCommand() { return this->alloc<T>(); } |
commit-bot@chromium.org | 08bf86c | 2014-05-07 18:01:57 +0000 | [diff] [blame] | 139 | |
mtklein | f98862c | 2014-11-24 14:45:47 -0800 | [diff] [blame] | 140 | void grow(); |
| 141 | |
mtklein | 29b1afc | 2015-04-09 07:46:41 -0700 | [diff] [blame] | 142 | // A typed pointer to some bytes in fAlloc. visit() and mutate() allow polymorphic dispatch. |
commit-bot@chromium.org | e3ff558 | 2014-04-01 16:24:06 +0000 | [diff] [blame] | 143 | struct Record { |
mtklein | 29b1afc | 2015-04-09 07:46:41 -0700 | [diff] [blame] | 144 | // On 32-bit machines we store type in 4 bytes, followed by a pointer. Simple. |
| 145 | // On 64-bit machines we store a pointer with the type slotted into two top (unused) bytes. |
| 146 | // FWIW, SkRecords::Type is tiny. It can easily fit in one byte. |
| 147 | uint64_t fTypeAndPtr; |
| 148 | static const int kTypeShift = sizeof(void*) == 4 ? 32 : 48; |
| 149 | |
commit-bot@chromium.org | 88c3e27 | 2014-04-22 16:57:20 +0000 | [diff] [blame] | 150 | // Point this record to its data in fAlloc. Returns ptr for convenience. |
commit-bot@chromium.org | e3ff558 | 2014-04-01 16:24:06 +0000 | [diff] [blame] | 151 | template <typename T> |
commit-bot@chromium.org | 88c3e27 | 2014-04-22 16:57:20 +0000 | [diff] [blame] | 152 | T* set(T* ptr) { |
mtklein | 29b1afc | 2015-04-09 07:46:41 -0700 | [diff] [blame] | 153 | fTypeAndPtr = ((uint64_t)T::kType) << kTypeShift | (uintptr_t)ptr; |
| 154 | SkASSERT(this->ptr() == ptr && this->type() == T::kType); |
commit-bot@chromium.org | 88c3e27 | 2014-04-22 16:57:20 +0000 | [diff] [blame] | 155 | return ptr; |
commit-bot@chromium.org | e3ff558 | 2014-04-01 16:24:06 +0000 | [diff] [blame] | 156 | } |
| 157 | |
mtklein | 29b1afc | 2015-04-09 07:46:41 -0700 | [diff] [blame] | 158 | SkRecords::Type type() const { return (SkRecords::Type)(fTypeAndPtr >> kTypeShift); } |
| 159 | void* ptr() const { return (void*)(fTypeAndPtr & ((1ull<<kTypeShift)-1)); } |
commit-bot@chromium.org | f0ae5e4 | 2014-04-24 15:33:48 +0000 | [diff] [blame] | 160 | |
mtklein | 29b1afc | 2015-04-09 07:46:41 -0700 | [diff] [blame] | 161 | // Visit this record with functor F (see public API above). |
mtklein | 343a63d | 2016-03-22 11:46:53 -0700 | [diff] [blame] | 162 | template <typename F> |
| 163 | auto visit(F&& f) const -> decltype(f(SkRecords::NoOp())) { |
mtklein | 29b1afc | 2015-04-09 07:46:41 -0700 | [diff] [blame] | 164 | #define CASE(T) case SkRecords::T##_Type: return f(*(const SkRecords::T*)this->ptr()); |
| 165 | switch(this->type()) { SK_RECORD_TYPES(CASE) } |
commit-bot@chromium.org | e3ff558 | 2014-04-01 16:24:06 +0000 | [diff] [blame] | 166 | #undef CASE |
commit-bot@chromium.org | c71da1f | 2014-05-07 21:16:09 +0000 | [diff] [blame] | 167 | SkDEBUGFAIL("Unreachable"); |
Mike Klein | 9f18350 | 2017-06-30 11:04:37 -0400 | [diff] [blame] | 168 | static const SkRecords::NoOp noop{}; |
| 169 | return f(noop); |
commit-bot@chromium.org | e3ff558 | 2014-04-01 16:24:06 +0000 | [diff] [blame] | 170 | } |
| 171 | |
mtklein | 29b1afc | 2015-04-09 07:46:41 -0700 | [diff] [blame] | 172 | // Mutate this record with functor F (see public API above). |
mtklein | 343a63d | 2016-03-22 11:46:53 -0700 | [diff] [blame] | 173 | template <typename F> |
| 174 | auto mutate(F&& f) -> decltype(f((SkRecords::NoOp*)nullptr)) { |
mtklein | 29b1afc | 2015-04-09 07:46:41 -0700 | [diff] [blame] | 175 | #define CASE(T) case SkRecords::T##_Type: return f((SkRecords::T*)this->ptr()); |
| 176 | switch(this->type()) { SK_RECORD_TYPES(CASE) } |
commit-bot@chromium.org | e3ff558 | 2014-04-01 16:24:06 +0000 | [diff] [blame] | 177 | #undef CASE |
commit-bot@chromium.org | c71da1f | 2014-05-07 21:16:09 +0000 | [diff] [blame] | 178 | SkDEBUGFAIL("Unreachable"); |
Mike Klein | 9f18350 | 2017-06-30 11:04:37 -0400 | [diff] [blame] | 179 | static const SkRecords::NoOp noop{}; |
| 180 | return f(const_cast<SkRecords::NoOp*>(&noop)); |
commit-bot@chromium.org | e3ff558 | 2014-04-01 16:24:06 +0000 | [diff] [blame] | 181 | } |
commit-bot@chromium.org | e3ff558 | 2014-04-01 16:24:06 +0000 | [diff] [blame] | 182 | }; |
| 183 | |
mtklein | 29b1afc | 2015-04-09 07:46:41 -0700 | [diff] [blame] | 184 | // fRecords needs to be a data structure that can append fixed length data, and need to |
| 185 | // support efficient random access and forward iteration. (It doesn't need to be contiguous.) |
Herb Derby | 4304d11 | 2017-04-18 11:42:49 -0400 | [diff] [blame] | 186 | int fCount{0}, |
| 187 | fReserved{0}; |
Mike Klein | 0fa156f | 2017-04-11 10:36:48 -0400 | [diff] [blame] | 188 | SkAutoTMalloc<Record> fRecords; |
mtklein | 29b1afc | 2015-04-09 07:46:41 -0700 | [diff] [blame] | 189 | |
commit-bot@chromium.org | e3ff558 | 2014-04-01 16:24:06 +0000 | [diff] [blame] | 190 | // fAlloc needs to be a data structure which can append variable length data in contiguous |
| 191 | // chunks, returning a stable handle to that data for later retrieval. |
Herb Derby | 4304d11 | 2017-04-18 11:42:49 -0400 | [diff] [blame] | 192 | SkArenaAlloc fAlloc{256}; |
| 193 | size_t fApproxBytesAllocated{0}; |
commit-bot@chromium.org | e3ff558 | 2014-04-01 16:24:06 +0000 | [diff] [blame] | 194 | }; |
commit-bot@chromium.org | e3ff558 | 2014-04-01 16:24:06 +0000 | [diff] [blame] | 195 | |
| 196 | #endif//SkRecord_DEFINED |