Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(546)

Side by Side Diff: src/core/SkRecord.h

Issue 1068383003: Revert of Rearrange SkRecord with small N in mind (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | src/core/SkRecord.cpp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright 2014 Google Inc. 2 * Copyright 2014 Google Inc.
3 * 3 *
4 * Use of this source code is governed by a BSD-style license that can be 4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file. 5 * found in the LICENSE file.
6 */ 6 */
7 7
8 #ifndef SkRecord_DEFINED 8 #ifndef SkRecord_DEFINED
9 #define SkRecord_DEFINED 9 #define SkRecord_DEFINED
10 10
11 #include "SkRecords.h" 11 #include "SkRecords.h"
12 #include "SkTLogic.h" 12 #include "SkTLogic.h"
13 #include "SkTemplates.h" 13 #include "SkTemplates.h"
14 #include "SkVarAlloc.h" 14 #include "SkVarAlloc.h"
15 15
16 // SkRecord represents a sequence of SkCanvas calls, saved for future use. 16 // SkRecord (REC-ord) represents a sequence of SkCanvas calls, saved for future use.
17 // These future uses may include: replay, optimization, serialization, or combin ations of those. 17 // These future uses may include: replay, optimization, serialization, or combin ations of those.
18 // 18 //
19 // Though an enterprising user may find calling alloc(), append(), visit(), and mutate() enough to 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 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. 21 // for creating an SkRecord, and SkRecordDraw which plays an SkRecord back into another SkCanvas.
22 // 22 //
23 // SkRecord often looks like it's compatible with any type T, but really it's co mpatible with any 23 // SkRecord often looks like it's compatible with any type T, but really it's co mpatible with any
24 // type T which has a static const SkRecords::Type kType. That is to say, SkRec ord is compatible 24 // type T which has a static const SkRecords::Type kType. That is to say, SkRec ord is compatible
25 // only with SkRecords::* structs defined in SkRecords.h. Your compiler will he lpfully yell if you 25 // only with SkRecords::* structs defined in SkRecords.h. Your compiler will he lpfully yell if you
26 // get this wrong. 26 // get this wrong.
27 27
28 class SkRecord : public SkNVRefCnt<SkRecord> { 28 class SkRecord : public SkNVRefCnt<SkRecord> {
29 enum { 29 enum {
30 // TODO: tune these two constants. 30 kFirstReserveCount = 64 / sizeof(void*),
31 kInlineRecords = 4, // Ideally our lower limit on recorded ops per picture.
32 kInlineAllocLgBytes = 8, // 1<<8 == 256 bytes inline, then SkVarAlloc st arting at 512 bytes.
33 }; 31 };
34 public: 32 public:
35 SkRecord() 33 SkRecord() : fCount(0), fReserved(0), fAlloc(8/*start block sizes at 256 byt es*/) {}
36 : fCount(0)
37 , fReserved(kInlineRecords)
38 , fAlloc(kInlineAllocLgBytes+1, // First malloc'd block is 2x as large as fInlineAlloc.
39 fInlineAlloc, sizeof(fInlineAlloc)) {}
40 ~SkRecord(); 34 ~SkRecord();
41 35
42 // Returns the number of canvas commands in this SkRecord. 36 // Returns the number of canvas commands in this SkRecord.
43 unsigned count() const { return fCount; } 37 unsigned count() const { return fCount; }
44 38
45 // Visit the i-th canvas command with a functor matching this interface: 39 // Visit the i-th canvas command with a functor matching this interface:
46 // template <typename T> 40 // template <typename T>
47 // R operator()(const T& record) { ... } 41 // R operator()(const T& record) { ... }
48 // This operator() must be defined for at least all SkRecords::*. 42 // This operator() must be defined for at least all SkRecords::*.
49 template <typename R, typename F> 43 template <typename R, typename F>
50 R visit(unsigned i, F& f) const { 44 R visit(unsigned i, F& f) const {
51 SkASSERT(i < this->count()); 45 SkASSERT(i < this->count());
52 return fRecords[i].visit<R>(f); 46 return fRecords[i].visit<R>(fTypes[i], f);
53 } 47 }
54 48
55 // Mutate the i-th canvas command with a functor matching this interface: 49 // Mutate the i-th canvas command with a functor matching this interface:
56 // template <typename T> 50 // template <typename T>
57 // R operator()(T* record) { ... } 51 // R operator()(T* record) { ... }
58 // This operator() must be defined for at least all SkRecords::*. 52 // This operator() must be defined for at least all SkRecords::*.
59 template <typename R, typename F> 53 template <typename R, typename F>
60 R mutate(unsigned i, F& f) { 54 R mutate(unsigned i, F& f) {
61 SkASSERT(i < this->count()); 55 SkASSERT(i < this->count());
62 return fRecords[i].mutate<R>(f); 56 return fRecords[i].mutate<R>(fTypes[i], f);
63 } 57 }
64 58 // TODO: It'd be nice to infer R from F for visit and mutate if we ever get std::result_of.
65 // TODO: It'd be nice to infer R from F for visit and mutate.
66 59
67 // Allocate contiguous space for count Ts, to be freed when the SkRecord is destroyed. 60 // 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 failur e. 61 // Here T can be any class, not just those from SkRecords. Throws on failur e.
69 template <typename T> 62 template <typename T>
70 T* alloc(size_t count = 1) { 63 T* alloc(size_t count = 1) {
64 // 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); 65 return (T*)fAlloc.alloc(sizeof(T) * count, SK_MALLOC_THROW);
72 } 66 }
73 67
74 // Add a new command of type T to the end of this SkRecord. 68 // 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. 69 // You are expected to placement new an object of type T onto this pointer.
76 template <typename T> 70 template <typename T>
77 T* append() { 71 T* append() {
78 if (fCount == fReserved) { 72 if (fCount == fReserved) {
79 this->grow(); 73 this->grow();
80 } 74 }
75 fTypes[fCount] = T::kType;
81 return fRecords[fCount++].set(this->allocCommand<T>()); 76 return fRecords[fCount++].set(this->allocCommand<T>());
82 } 77 }
83 78
84 // Replace the i-th command with a new command of type T. 79 // 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. 80 // You are expected to placement new an object of type T onto this pointer.
86 // References to the original command are invalidated. 81 // References to the original command are invalidated.
87 template <typename T> 82 template <typename T>
88 T* replace(unsigned i) { 83 T* replace(unsigned i) {
89 SkASSERT(i < this->count()); 84 SkASSERT(i < this->count());
90 85
91 Destroyer destroyer; 86 Destroyer destroyer;
92 this->mutate<void>(i, destroyer); 87 this->mutate<void>(i, destroyer);
93 88
89 fTypes[i] = T::kType;
94 return fRecords[i].set(this->allocCommand<T>()); 90 return fRecords[i].set(this->allocCommand<T>());
95 } 91 }
96 92
97 // Replace the i-th command with a new command of type T. 93 // Replace the i-th command with a new command of type T.
98 // You are expected to placement new an object of type T onto this pointer. 94 // You are expected to placement new an object of type T onto this pointer.
99 // You must show proof that you've already adopted the existing command. 95 // You must show proof that you've already adopted the existing command.
100 template <typename T, typename Existing> 96 template <typename T, typename Existing>
101 T* replace(unsigned i, const SkRecords::Adopted<Existing>& proofOfAdoption) { 97 T* replace(unsigned i, const SkRecords::Adopted<Existing>& proofOfAdoption) {
102 SkASSERT(i < this->count()); 98 SkASSERT(i < this->count());
103 99
104 SkASSERT(Existing::kType == fRecords[i].type()); 100 SkASSERT(Existing::kType == fTypes[i]);
105 SkASSERT(proofOfAdoption == fRecords[i].ptr()); 101 SkASSERT(proofOfAdoption == fRecords[i].ptr<Existing>());
106 102
103 fTypes[i] = T::kType;
107 return fRecords[i].set(this->allocCommand<T>()); 104 return fRecords[i].set(this->allocCommand<T>());
108 } 105 }
109 106
110 // Does not return the bytes in any pointers embedded in the Records; caller s 107 // Does not return the bytes in any pointers embedded in the Records; caller s
111 // need to iterate with a visitor to measure those they care for. 108 // need to iterate with a visitor to measure those they care for.
112 size_t bytesUsed() const; 109 size_t bytesUsed() const;
113 110
114 private: 111 private:
115 // An SkRecord is structured as an array of pointers into a big chunk of mem ory where 112 // Implementation notes!
113 //
114 // Logically an SkRecord is structured as an array of pointers into a big ch unk of memory where
116 // records representing each canvas draw call are stored: 115 // records representing each canvas draw call are stored:
117 // 116 //
118 // fRecords: [*][*][*]... 117 // fRecords: [*][*][*]...
119 // | | | 118 // | | |
120 // | | | 119 // | | |
121 // | | +---------------------------------------+ 120 // | | +---------------------------------------+
122 // | +-----------------+ | 121 // | +-----------------+ |
123 // | | | 122 // | | |
124 // v v v 123 // v v v
125 // fAlloc: [SkRecords::DrawRect][SkRecords::DrawPosTextH][SkRecords::Draw Rect]... 124 // fAlloc: [SkRecords::DrawRect][SkRecords::DrawPosTextH][SkRecords::Draw Rect]...
126 // 125 //
127 // We store the types of each of the pointers alongside the pointer. 126 // In the scheme above, the pointers in fRecords are void*: they have no typ e. The type is not
128 // The cost to append a T to this structure is 8 + sizeof(T) bytes. 127 // stored in fAlloc either; we just write raw data there. But we need that type information.
128 // Here are some options:
129 // 1) use inheritance, virtuals, and vtables to make the fRecords pointers smarter
130 // 2) store the type data manually in fAlloc at the start of each record
131 // 3) store the type data manually somewhere with fRecords
132 //
133 // This code uses approach 3). The implementation feels very similar to 1), but it's
134 // devirtualized instead of using the language's polymorphism mechanisms. T his lets us work
135 // with the types themselves (as SkRecords::Type), a sort of limited free RT TI; it lets us pay
136 // only 1 byte to store the type instead of a full pointer (4-8 bytes); and it leads to better
137 // decoupling between the SkRecords::* record types and the operations perfo rmed on them in
138 // visit() or mutate(). The recorded canvas calls don't have to have any id ea about the
139 // operations performed on them.
140 //
141 // We store the types in a parallel fTypes array, mainly so that they can be tightly packed as
142 // single bytes. This has the side effect of allowing very fast analysis pa sses over an
143 // SkRecord looking for just patterns of draw commands (or using this as a q uick reject
144 // mechanism) though there's admittedly not a very good API exposed publical ly for this.
145 //
146 // The cost to append a T into this structure is 1 + sizeof(void*) + sizeof( T).
129 147
130 // A mutator that can be used with replace to destroy canvas commands. 148 // A mutator that can be used with replace to destroy canvas commands.
131 struct Destroyer { 149 struct Destroyer {
132 template <typename T> 150 template <typename T>
133 void operator()(T* record) { record->~T(); } 151 void operator()(T* record) { record->~T(); }
134 }; 152 };
135 153
154 // Logically the same as SkRecords::Type, but packed into 8 bits.
155 struct Type8 {
156 public:
157 // This intentionally converts implicitly back and forth.
158 Type8(SkRecords::Type type) : fType(type) { SkASSERT(*this == type); }
159 operator SkRecords::Type () { return (SkRecords::Type)fType; }
160
161 private:
162 uint8_t fType;
163 };
164
165 // No point in allocating any more than one of an empty struct.
166 // We could just return NULL but it's sort of confusing to return NULL on su ccess.
136 template <typename T> 167 template <typename T>
137 SK_WHEN(SkTIsEmpty<T>, T*) allocCommand() { 168 SK_WHEN(SkTIsEmpty<T>, T*) allocCommand() {
138 static T singleton = {}; 169 static T singleton = {};
139 return &singleton; 170 return &singleton;
140 } 171 }
141 172
142 template <typename T> 173 template <typename T>
143 SK_WHEN(!SkTIsEmpty<T>, T*) allocCommand() { return this->alloc<T>(); } 174 SK_WHEN(!SkTIsEmpty<T>, T*) allocCommand() { return this->alloc<T>(); }
144 175
176 // Called when we've run out of room to record new commands.
145 void grow(); 177 void grow();
146 178
147 // A typed pointer to some bytes in fAlloc. visit() and mutate() allow poly morphic dispatch. 179 // An untyped pointer to some bytes in fAlloc. This is the interface for po lymorphic dispatch:
180 // visit() and mutate() work with the parallel fTypes array to do the work o f a vtable.
148 struct Record { 181 struct Record {
149 // On 32-bit machines we store type in 4 bytes, followed by a pointer. Simple. 182 public:
150 // On 64-bit machines we store a pointer with the type slotted into two top (unused) bytes.
151 // FWIW, SkRecords::Type is tiny. It can easily fit in one byte.
152 uint64_t fTypeAndPtr;
153 static const int kTypeShift = sizeof(void*) == 4 ? 32 : 48;
154
155 // Point this record to its data in fAlloc. Returns ptr for convenience . 183 // Point this record to its data in fAlloc. Returns ptr for convenience .
156 template <typename T> 184 template <typename T>
157 T* set(T* ptr) { 185 T* set(T* ptr) {
158 fTypeAndPtr = ((uint64_t)T::kType) << kTypeShift | (uint64_t)ptr; 186 fPtr = ptr;
159 return ptr; 187 return ptr;
160 } 188 }
161 189
162 SkRecords::Type type() const { return (SkRecords::Type)(fTypeAndPtr >> k TypeShift); } 190 // Get the data in fAlloc, assuming it's of type T.
163 void* ptr() const { return (void*)(fTypeAndPtr & ((1ull<<kTypeShift)-1)) ; } 191 template <typename T>
192 T* ptr() const { return (T*)fPtr; }
164 193
165 // Visit this record with functor F (see public API above). 194 // Visit this record with functor F (see public API above) assuming the record we're
195 // pointing to has this type.
166 template <typename R, typename F> 196 template <typename R, typename F>
167 R visit(F& f) const { 197 R visit(Type8 type, F& f) const {
168 #define CASE(T) case SkRecords::T##_Type: return f(*(const SkRecords::T* )this->ptr()); 198 #define CASE(T) case SkRecords::T##_Type: return f(*this->ptr<SkRecords: :T>());
169 switch(this->type()) { SK_RECORD_TYPES(CASE) } 199 switch(type) { SK_RECORD_TYPES(CASE) }
170 #undef CASE 200 #undef CASE
171 SkDEBUGFAIL("Unreachable"); 201 SkDEBUGFAIL("Unreachable");
172 return R(); 202 return R();
173 } 203 }
174 204
175 // Mutate this record with functor F (see public API above). 205 // Mutate this record with functor F (see public API above) assuming the record we're
206 // pointing to has this type.
176 template <typename R, typename F> 207 template <typename R, typename F>
177 R mutate(F& f) { 208 R mutate(Type8 type, F& f) {
178 #define CASE(T) case SkRecords::T##_Type: return f((SkRecords::T*)this-> ptr()); 209 #define CASE(T) case SkRecords::T##_Type: return f(this->ptr<SkRecords:: T>());
179 switch(this->type()) { SK_RECORD_TYPES(CASE) } 210 switch(type) { SK_RECORD_TYPES(CASE) }
180 #undef CASE 211 #undef CASE
181 SkDEBUGFAIL("Unreachable"); 212 SkDEBUGFAIL("Unreachable");
182 return R(); 213 return R();
183 } 214 }
215
216 private:
217 void* fPtr;
184 }; 218 };
185 219
186 // fRecords needs to be a data structure that can append fixed length data, and need to
187 // support efficient random access and forward iteration. (It doesn't need to be contiguous.)
188 unsigned fCount, fReserved;
189 SkAutoSTMalloc<kInlineRecords, Record> fRecords;
190
191 // fAlloc needs to be a data structure which can append variable length data in contiguous 220 // fAlloc needs to be a data structure which can append variable length data in contiguous
192 // chunks, returning a stable handle to that data for later retrieval. 221 // chunks, returning a stable handle to that data for later retrieval.
222 //
223 // fRecords and fTypes need to be data structures that can append fixed leng th data, and need to
224 // support efficient random access and forward iteration. (They don't need to be contiguous.)
225
226 // fCount and fReserved measure both fRecords and fTypes, which always grow in lock step.
227 unsigned fCount;
228 unsigned fReserved;
229 SkAutoTMalloc<Record> fRecords;
230 SkAutoTMalloc<Type8> fTypes;
193 SkVarAlloc fAlloc; 231 SkVarAlloc fAlloc;
194 char fInlineAlloc[1 << kInlineAllocLgBytes]; 232 // Strangely the order of these fields matters. If the unsigneds don't go f irst we're 56 bytes.
233 // tomhudson and mtklein have no idea why.
195 }; 234 };
235 SK_COMPILE_ASSERT(sizeof(SkRecord) <= 56, SkRecordSize);
196 236
197 #endif//SkRecord_DEFINED 237 #endif//SkRecord_DEFINED
OLDNEW
« no previous file with comments | « no previous file | src/core/SkRecord.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698