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

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

Issue 245853002: Refactor SkRecord opts, converting playback optimizations where possible. (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: apply Created 6 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/record/SkRecordDraw.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
(...skipping 13 matching lines...) Expand all
24 // only with SkRecords::* structs defined in SkRecords.h. Your compiler will he lpfully yell if you 24 // only with SkRecords::* structs defined in SkRecords.h. Your compiler will he lpfully yell if you
25 // get this wrong. 25 // get this wrong.
26 26
27 class SkRecord : SkNoncopyable { 27 class SkRecord : SkNoncopyable {
28 public: 28 public:
29 SkRecord(size_t chunkBytes = 4096, unsigned firstReserveCount = 64 / sizeof( void*)) 29 SkRecord(size_t chunkBytes = 4096, unsigned firstReserveCount = 64 / sizeof( void*))
30 : fAlloc(chunkBytes), fCount(0), fReserved(0), kFirstReserveCount(firstR eserveCount) {} 30 : fAlloc(chunkBytes), fCount(0), fReserved(0), kFirstReserveCount(firstR eserveCount) {}
31 31
32 ~SkRecord() { 32 ~SkRecord() {
33 Destroyer destroyer; 33 Destroyer destroyer;
34 this->mutate(destroyer); 34 for (unsigned i = 0; i < this->count(); i++) {
35 this->mutate(i, destroyer);
36 }
35 } 37 }
36 38
39 // Returns the number of canvas commands in this SkRecord.
37 unsigned count() const { return fCount; } 40 unsigned count() const { return fCount; }
38 41
39 // Accepts a visitor functor with this interface: 42 // Visit the i-th canvas command with a functor matching this interface:
40 // template <typename T> 43 // template <typename T>
41 // void operator()(const T& record) { ... } 44 // void operator()(const T& record) { ... }
42 // This operator() must be defined for at least all SkRecords::*; your compi ler will help you 45 // This operator() must be defined for at least all SkRecords::*.
43 // get this right.
44 template <typename F> 46 template <typename F>
45 void visit(unsigned i, F& f) const { 47 void visit(unsigned i, F& f) const {
46 SkASSERT(i < this->count()); 48 SkASSERT(i < this->count());
47 fRecords[i].visit(fTypes[i], f); 49 fRecords[i].visit(fTypes[i], f);
48 } 50 }
49 51
50 // As above. f will be called on each recorded canvas call in the order the y were append()ed. 52 // Mutate the i-th canvas command with a functor matching this interface:
51 template <typename F>
52 void visit(F& f) const {
53 for (unsigned i = 0; i < fCount; i++) {
54 this->visit(i, f);
55 }
56 }
57
58 // Accepts a visitor functor with this interface:
59 // template <typename T> 53 // template <typename T>
60 // void operator()(T* record) { ... } 54 // void operator()(T* record) { ... }
61 // This operator() must be defined for at least all SkRecords::*; again, you r compiler will help 55 // This operator() must be defined for at least all SkRecords::*.
62 // you get this right.
63 template <typename F> 56 template <typename F>
64 void mutate(unsigned i, F& f) { 57 void mutate(unsigned i, F& f) {
65 SkASSERT(i < this->count()); 58 SkASSERT(i < this->count());
66 fRecords[i].mutate(fTypes[i], f); 59 fRecords[i].mutate(fTypes[i], f);
67 } 60 }
68 61
69 // As above. f will be called on each recorded canvas call in the order the y were append()ed. 62 // Allocate contiguous space for count Ts, to be freed when the SkRecord is destroyed.
70 template <typename F> 63 // Here T can be any class, not just those from SkRecords. Throws on failur e.
71 void mutate(F& f) {
72 for (unsigned i = 0; i < fCount; i++) {
73 this->mutate(i, f);
74 }
75 }
76
77 // Allocate contiguous space for count Ts, to be destroyed (not just freed) when the SkRecord is
78 // destroyed. For classes with constructors, placement new into this array. Throws on failure.
79 // Here T can really be any class, not just those from SkRecords.
80 template <typename T> 64 template <typename T>
81 T* alloc(unsigned count = 1) { 65 T* alloc(unsigned count = 1) {
82 return (T*)fAlloc.allocThrow(sizeof(T) * count); 66 return (T*)fAlloc.allocThrow(sizeof(T) * count);
83 } 67 }
84 68
85 // Allocate space to record a canvas call of type T at the end of this SkRec ord. You are 69 // Add a new command of type T to the end of this SkRecord.
86 // expected to placement new an object of type T onto this pointer. 70 // You are expected to placement new an object of type T onto this pointer.
87 template <typename T> 71 template <typename T>
88 T* append() { 72 T* append() {
89 if (fCount == fReserved) { 73 if (fCount == fReserved) {
90 fReserved = SkTMax(kFirstReserveCount, fReserved*2); 74 fReserved = SkTMax(kFirstReserveCount, fReserved*2);
91 fRecords.realloc(fReserved); 75 fRecords.realloc(fReserved);
92 fTypes.realloc(fReserved); 76 fTypes.realloc(fReserved);
93 } 77 }
94 78
95 fTypes[fCount] = T::kType; 79 fTypes[fCount] = T::kType;
96 return fRecords[fCount++].alloc<T>(this); 80 return fRecords[fCount++].set(this->alloc<T>());
97 } 81 }
98 82
83 // Replace the i-th command with a new command of type T.
84 // You are expected to placement new an object of type T onto this pointer.
85 // References to the old command remain valid for the life of the SkRecord, but
86 // you must destroy the old command. (It's okay to destroy it first before calling replace.)
87 template <typename T>
88 T* replace(unsigned i) {
89 SkASSERT(i < this->count());
90 fTypes[i] = T::kType;
91 return fRecords[i].set(this->alloc<T>());
92 }
93
94 // A mutator that can be used with replace to destroy canvas commands.
95 struct Destroyer {
96 template <typename T>
97 void operator()(T* record) { record->~T(); }
98 };
99
99 private: 100 private:
100 // Implementation notes! 101 // Implementation notes!
101 // 102 //
102 // Logically an SkRecord is structured as an array of pointers into a big ch unk of memory where 103 // Logically an SkRecord is structured as an array of pointers into a big ch unk of memory where
103 // records representing each canvas draw call are stored: 104 // records representing each canvas draw call are stored:
104 // 105 //
105 // fRecords: [*][*][*]... 106 // fRecords: [*][*][*]...
106 // | | | 107 // | | |
107 // | | | 108 // | | |
108 // | | +---------------------------------------+ 109 // | | +---------------------------------------+
(...skipping 15 matching lines...) Expand all
124 // only 1 byte to store the type instead of a full pointer (4-8 bytes); and it leads to better 125 // only 1 byte to store the type instead of a full pointer (4-8 bytes); and it leads to better
125 // decoupling between the SkRecords::* record types and the operations perfo rmed on them in 126 // decoupling between the SkRecords::* record types and the operations perfo rmed on them in
126 // visit() or mutate(). The recorded canvas calls don't have to have any id ea about the 127 // visit() or mutate(). The recorded canvas calls don't have to have any id ea about the
127 // operations performed on them. 128 // operations performed on them.
128 // 129 //
129 // We store the types in a parallel fTypes array, mainly so that they can be tightly packed as 130 // We store the types in a parallel fTypes array, mainly so that they can be tightly packed as
130 // single bytes. This has the side effect of allowing very fast analysis pa sses over an 131 // single bytes. This has the side effect of allowing very fast analysis pa sses over an
131 // SkRecord looking for just patterns of draw commands (or using this as a q uick reject 132 // SkRecord looking for just patterns of draw commands (or using this as a q uick reject
132 // mechanism) though there's admittedly not a very good API exposed publical ly for this. 133 // mechanism) though there's admittedly not a very good API exposed publical ly for this.
133 // 134 //
134 // We pull one final sneaky trick in the implementation. When recording can vas calls that need 135 // The cost to append a T into this structure is 1 + sizeof(void*) + sizeof( T).
135 // to store less than a pointer of data, we don't go through the usual path of allocating the
136 // draw command in fAlloc and a pointer to it in fRecords; instead, we ignor e fAlloc and
137 // directly allocate the object in the space we would have put the pointer i n fRecords. This is
138 // why you'll see uintptr_t instead of void* in Record below.
139 //
140 // The cost of appending a single record into this structure is then:
141 // - 1 + sizeof(void*) + sizeof(T) if sizeof(T) > sizeof(void*)
142 // - 1 + sizeof(void*) if sizeof(T) <= sizeof(void*)
143 136
144 137
145 // A mutator that calls destructors of all the canvas calls we've recorded.
146 struct Destroyer {
147 template <typename T>
148 void operator()(T* record) { record->~T(); }
149 };
150
151 // Logically the same as SkRecords::Type, but packed into 8 bits. 138 // Logically the same as SkRecords::Type, but packed into 8 bits.
152 struct Type8 { 139 struct Type8 {
153 public: 140 public:
154 // This intentionally converts implicitly back and forth. 141 // This intentionally converts implicitly back and forth.
155 Type8(SkRecords::Type type) : fType(type) { SkASSERT(*this == type); } 142 Type8(SkRecords::Type type) : fType(type) { SkASSERT(*this == type); }
156 operator SkRecords::Type () { return (SkRecords::Type)fType; } 143 operator SkRecords::Type () { return (SkRecords::Type)fType; }
157 144
158 private: 145 private:
159 uint8_t fType; 146 uint8_t fType;
160 }; 147 };
161 148
162 // Logically a void* to some bytes in fAlloc, but maybe has the bytes stored immediately 149 // An untyped pointer to some bytes in fAlloc. This is the interface for po lymorphic dispatch:
163 // instead. This is also the main interface for devirtualized polymorphic d ispatch: see visit() 150 // visit() and mutate() work with the parallel fTypes array to do the work o f a vtable.
164 // and mutate(), which essentially do the work of the missing vtable.
165 struct Record { 151 struct Record {
166 public: 152 public:
167 153 // Point this record to its data in fAlloc. Returns ptr for convenience .
168 // Allocate space for a T, perhaps using the SkRecord to allocate that s pace.
169 template <typename T> 154 template <typename T>
170 T* alloc(SkRecord* record) { 155 T* set(T* ptr) {
171 if (IsLarge<T>()) { 156 fPtr = ptr;
172 fRecord = (uintptr_t)record->alloc<T>(); 157 return ptr;
173 }
174 return this->ptr<T>();
175 } 158 }
176 159
177 // Visit this record with functor F (see public API above) assuming the record we're 160 // Visit this record with functor F (see public API above) assuming the record we're
178 // pointing to has this type. 161 // pointing to has this type.
179 template <typename F> 162 template <typename F>
180 void visit(Type8 type, F& f) const { 163 void visit(Type8 type, F& f) const {
181 #define CASE(T) case SkRecords::T##_Type: return f(*this->ptr<SkRecords: :T>()); 164 #define CASE(T) case SkRecords::T##_Type: return f(*this->ptr<SkRecords: :T>());
182 switch(type) { SK_RECORD_TYPES(CASE) } 165 switch(type) { SK_RECORD_TYPES(CASE) }
183 #undef CASE 166 #undef CASE
184 } 167 }
185 168
186 // Mutate this record with functor F (see public API above) assuming the record we're 169 // Mutate this record with functor F (see public API above) assuming the record we're
187 // pointing to has this type. 170 // pointing to has this type.
188 template <typename F> 171 template <typename F>
189 void mutate(Type8 type, F& f) { 172 void mutate(Type8 type, F& f) {
190 #define CASE(T) case SkRecords::T##_Type: return f(this->ptr<SkRecords:: T>()); 173 #define CASE(T) case SkRecords::T##_Type: return f(this->ptr<SkRecords:: T>());
191 switch(type) { SK_RECORD_TYPES(CASE) } 174 switch(type) { SK_RECORD_TYPES(CASE) }
192 #undef CASE 175 #undef CASE
193 } 176 }
194 177
195 private: 178 private:
196 template <typename T> 179 template <typename T>
197 T* ptr() const { return (T*)(IsLarge<T>() ? (void*)fRecord : &fRecord); } 180 T* ptr() const { return (T*)fPtr; }
198 181
199 // Is T too big to fit directly into a uintptr_t, neededing external all ocation? 182 void* fPtr;
200 template <typename T>
201 static bool IsLarge() { return sizeof(T) > sizeof(uintptr_t); }
202
203 uintptr_t fRecord;
204 }; 183 };
205 184
206 // fAlloc needs to be a data structure which can append variable length data in contiguous 185 // fAlloc needs to be a data structure which can append variable length data in contiguous
207 // chunks, returning a stable handle to that data for later retrieval. 186 // chunks, returning a stable handle to that data for later retrieval.
208 // 187 //
209 // fRecords and fTypes need to be data structures that can append fixed leng th data, and need to 188 // fRecords and fTypes need to be data structures that can append fixed leng th data, and need to
210 // support efficient forward iteration. (They don't need to be contiguous o r indexable.) 189 // support efficient forward iteration. (They don't need to be contiguous o r indexable.)
211 190
212 SkChunkAlloc fAlloc; 191 SkChunkAlloc fAlloc;
213 SkAutoTMalloc<Record> fRecords; 192 SkAutoTMalloc<Record> fRecords;
214 SkAutoTMalloc<Type8> fTypes; 193 SkAutoTMalloc<Type8> fTypes;
215 // fCount and fReserved measure both fRecords and fTypes, which always grow in lock step. 194 // fCount and fReserved measure both fRecords and fTypes, which always grow in lock step.
216 unsigned fCount; 195 unsigned fCount;
217 unsigned fReserved; 196 unsigned fReserved;
218 const unsigned kFirstReserveCount; 197 const unsigned kFirstReserveCount;
219 }; 198 };
220 199
221 #endif//SkRecord_DEFINED 200 #endif//SkRecord_DEFINED
OLDNEW
« no previous file with comments | « no previous file | src/record/SkRecordDraw.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698