Line data Source code
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 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 : public SkRefCnt {
29 : public:
30 : SkRecord();
31 : ~SkRecord();
32 :
33 : // Returns the number of canvas commands in this SkRecord.
34 0 : int count() const { return fCount; }
35 :
36 : // Visit the i-th canvas command with a functor matching this interface:
37 : // template <typename T>
38 : // R operator()(const T& record) { ... }
39 : // This operator() must be defined for at least all SkRecords::*.
40 : template <typename F>
41 0 : auto visit(int i, F&& f) const -> decltype(f(SkRecords::NoOp())) {
42 0 : return fRecords[i].visit(f);
43 : }
44 :
45 : // Mutate the i-th canvas command with a functor matching this interface:
46 : // template <typename T>
47 : // R operator()(T* record) { ... }
48 : // This operator() must be defined for at least all SkRecords::*.
49 : template <typename F>
50 0 : auto mutate(int i, F&& f) -> decltype(f((SkRecords::NoOp*)nullptr)) {
51 0 : return fRecords[i].mutate(f);
52 : }
53 :
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.
56 : template <typename T>
57 0 : T* alloc(size_t count = 1) {
58 0 : return (T*)fAlloc.alloc(sizeof(T) * count);
59 : }
60 :
61 : // Add a new command of type T to the end of this SkRecord.
62 : // You are expected to placement new an object of type T onto this pointer.
63 : template <typename T>
64 0 : T* append() {
65 0 : if (fCount == fReserved) {
66 0 : this->grow();
67 : }
68 0 : return fRecords[fCount++].set(this->allocCommand<T>());
69 : }
70 :
71 : // Replace the i-th command with a new command of type T.
72 : // You are expected to placement new an object of type T onto this pointer.
73 : // References to the original command are invalidated.
74 : template <typename T>
75 0 : T* replace(int i) {
76 0 : SkASSERT(i < this->count());
77 :
78 : Destroyer destroyer;
79 0 : this->mutate(i, destroyer);
80 :
81 0 : return fRecords[i].set(this->allocCommand<T>());
82 : }
83 :
84 : // Replace the i-th command with a new command of type T.
85 : // You are expected to placement new an object of type T onto this pointer.
86 : // You must show proof that you've already adopted the existing command.
87 : template <typename T, typename Existing>
88 : T* replace(int i, const SkRecords::Adopted<Existing>& proofOfAdoption) {
89 : SkASSERT(i < this->count());
90 :
91 : SkASSERT(Existing::kType == fRecords[i].type());
92 : SkASSERT(proofOfAdoption == fRecords[i].ptr());
93 :
94 : return fRecords[i].set(this->allocCommand<T>());
95 : }
96 :
97 : // Does not return the bytes in any pointers embedded in the Records; callers
98 : // need to iterate with a visitor to measure those they care for.
99 : size_t bytesUsed() const;
100 :
101 : // Rearrange and resize this record to eliminate any NoOps.
102 : // May change count() and the indices of ops, but preserves their order.
103 : void defrag();
104 :
105 : private:
106 : // An SkRecord is structured as an array of pointers into a big chunk of memory where
107 : // records representing each canvas draw call are stored:
108 : //
109 : // fRecords: [*][*][*]...
110 : // | | |
111 : // | | |
112 : // | | +---------------------------------------+
113 : // | +-----------------+ |
114 : // | | |
115 : // v v v
116 : // fAlloc: [SkRecords::DrawRect][SkRecords::DrawPosTextH][SkRecords::DrawRect]...
117 : //
118 : // We store the types of each of the pointers alongside the pointer.
119 : // The cost to append a T to this structure is 8 + sizeof(T) bytes.
120 :
121 : // A mutator that can be used with replace to destroy canvas commands.
122 : struct Destroyer {
123 : template <typename T>
124 0 : void operator()(T* record) { record->~T(); }
125 : };
126 :
127 : template <typename T>
128 0 : SK_WHEN(std::is_empty<T>::value, T*) allocCommand() {
129 : static T singleton = {};
130 0 : return &singleton;
131 : }
132 :
133 : template <typename T>
134 0 : SK_WHEN(!std::is_empty<T>::value, T*) allocCommand() { return this->alloc<T>(); }
135 :
136 : void grow();
137 :
138 : // A typed pointer to some bytes in fAlloc. visit() and mutate() allow polymorphic dispatch.
139 : struct Record {
140 : // On 32-bit machines we store type in 4 bytes, followed by a pointer. Simple.
141 : // On 64-bit machines we store a pointer with the type slotted into two top (unused) bytes.
142 : // FWIW, SkRecords::Type is tiny. It can easily fit in one byte.
143 : uint64_t fTypeAndPtr;
144 : static const int kTypeShift = sizeof(void*) == 4 ? 32 : 48;
145 :
146 : // Point this record to its data in fAlloc. Returns ptr for convenience.
147 : template <typename T>
148 0 : T* set(T* ptr) {
149 0 : fTypeAndPtr = ((uint64_t)T::kType) << kTypeShift | (uintptr_t)ptr;
150 0 : SkASSERT(this->ptr() == ptr && this->type() == T::kType);
151 0 : return ptr;
152 : }
153 :
154 0 : SkRecords::Type type() const { return (SkRecords::Type)(fTypeAndPtr >> kTypeShift); }
155 0 : void* ptr() const { return (void*)(fTypeAndPtr & ((1ull<<kTypeShift)-1)); }
156 :
157 : // Visit this record with functor F (see public API above).
158 : template <typename F>
159 0 : auto visit(F&& f) const -> decltype(f(SkRecords::NoOp())) {
160 : #define CASE(T) case SkRecords::T##_Type: return f(*(const SkRecords::T*)this->ptr());
161 0 : switch(this->type()) { SK_RECORD_TYPES(CASE) }
162 : #undef CASE
163 0 : SkDEBUGFAIL("Unreachable");
164 0 : return f(SkRecords::NoOp());
165 : }
166 :
167 : // Mutate this record with functor F (see public API above).
168 : template <typename F>
169 0 : auto mutate(F&& f) -> decltype(f((SkRecords::NoOp*)nullptr)) {
170 : #define CASE(T) case SkRecords::T##_Type: return f((SkRecords::T*)this->ptr());
171 0 : switch(this->type()) { SK_RECORD_TYPES(CASE) }
172 : #undef CASE
173 0 : SkDEBUGFAIL("Unreachable");
174 0 : return f((SkRecords::NoOp*)nullptr);
175 : }
176 : };
177 :
178 : // fRecords needs to be a data structure that can append fixed length data, and need to
179 : // support efficient random access and forward iteration. (It doesn't need to be contiguous.)
180 : int fCount, fReserved;
181 : SkAutoTMalloc<Record> fRecords;
182 :
183 : // fAlloc needs to be a data structure which can append variable length data in contiguous
184 : // chunks, returning a stable handle to that data for later retrieval.
185 : SkVarAlloc fAlloc;
186 : };
187 :
188 : #endif//SkRecord_DEFINED
|