OLD | NEW |
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 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
75 fRecords.realloc(fReserved); | 75 fRecords.realloc(fReserved); |
76 fTypes.realloc(fReserved); | 76 fTypes.realloc(fReserved); |
77 } | 77 } |
78 | 78 |
79 fTypes[fCount] = T::kType; | 79 fTypes[fCount] = T::kType; |
80 return fRecords[fCount++].set(this->alloc<T>()); | 80 return fRecords[fCount++].set(this->alloc<T>()); |
81 } | 81 } |
82 | 82 |
83 // Replace the i-th command with a new command of type T. | 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. | 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 | 85 // References to the original command are invalidated. |
86 // you must destroy the old command. (It's okay to destroy it first before
calling replace.) | |
87 template <typename T> | 86 template <typename T> |
88 T* replace(unsigned i) { | 87 T* replace(unsigned i) { |
89 SkASSERT(i < this->count()); | 88 SkASSERT(i < this->count()); |
| 89 |
| 90 Destroyer destroyer; |
| 91 this->mutate(i, destroyer); |
| 92 |
90 fTypes[i] = T::kType; | 93 fTypes[i] = T::kType; |
91 return fRecords[i].set(this->alloc<T>()); | 94 return fRecords[i].set(this->alloc<T>()); |
92 } | 95 } |
93 | 96 |
94 // A mutator that can be used with replace to destroy canvas commands. | 97 // Replace the i-th command with a new command of type T. |
95 struct Destroyer { | 98 // You are expected to placement new an object of type T onto this pointer. |
96 template <typename T> | 99 // You must show proof that you've already adopted the existing command. |
97 void operator()(T* record) { record->~T(); } | 100 template <typename T, typename Existing> |
98 }; | 101 T* replace(unsigned i, const SkRecords::Adopted<Existing>& proofOfAdoption)
{ |
| 102 SkASSERT(i < this->count()); |
| 103 |
| 104 SkASSERT(Existing::kType == fTypes[i]); |
| 105 SkASSERT(proofOfAdoption == fRecords[i].ptr<Existing>()); |
| 106 |
| 107 fTypes[i] = T::kType; |
| 108 return fRecords[i].set(this->alloc<T>()); |
| 109 } |
99 | 110 |
100 private: | 111 private: |
101 // Implementation notes! | 112 // Implementation notes! |
102 // | 113 // |
103 // Logically an SkRecord is structured as an array of pointers into a big ch
unk of memory where | 114 // Logically an SkRecord is structured as an array of pointers into a big ch
unk of memory where |
104 // records representing each canvas draw call are stored: | 115 // records representing each canvas draw call are stored: |
105 // | 116 // |
106 // fRecords: [*][*][*]... | 117 // fRecords: [*][*][*]... |
107 // | | | | 118 // | | | |
108 // | | | | 119 // | | | |
(...skipping 18 matching lines...) Expand all Loading... |
127 // visit() or mutate(). The recorded canvas calls don't have to have any id
ea about the | 138 // visit() or mutate(). The recorded canvas calls don't have to have any id
ea about the |
128 // operations performed on them. | 139 // operations performed on them. |
129 // | 140 // |
130 // We store the types in a parallel fTypes array, mainly so that they can be
tightly packed as | 141 // We store the types in a parallel fTypes array, mainly so that they can be
tightly packed as |
131 // single bytes. This has the side effect of allowing very fast analysis pa
sses over an | 142 // single bytes. This has the side effect of allowing very fast analysis pa
sses over an |
132 // SkRecord looking for just patterns of draw commands (or using this as a q
uick reject | 143 // SkRecord looking for just patterns of draw commands (or using this as a q
uick reject |
133 // mechanism) though there's admittedly not a very good API exposed publical
ly for this. | 144 // mechanism) though there's admittedly not a very good API exposed publical
ly for this. |
134 // | 145 // |
135 // The cost to append a T into this structure is 1 + sizeof(void*) + sizeof(
T). | 146 // The cost to append a T into this structure is 1 + sizeof(void*) + sizeof(
T). |
136 | 147 |
| 148 // A mutator that can be used with replace to destroy canvas commands. |
| 149 struct Destroyer { |
| 150 template <typename T> |
| 151 void operator()(T* record) { record->~T(); } |
| 152 }; |
137 | 153 |
138 // Logically the same as SkRecords::Type, but packed into 8 bits. | 154 // Logically the same as SkRecords::Type, but packed into 8 bits. |
139 struct Type8 { | 155 struct Type8 { |
140 public: | 156 public: |
141 // This intentionally converts implicitly back and forth. | 157 // This intentionally converts implicitly back and forth. |
142 Type8(SkRecords::Type type) : fType(type) { SkASSERT(*this == type); } | 158 Type8(SkRecords::Type type) : fType(type) { SkASSERT(*this == type); } |
143 operator SkRecords::Type () { return (SkRecords::Type)fType; } | 159 operator SkRecords::Type () { return (SkRecords::Type)fType; } |
144 | 160 |
145 private: | 161 private: |
146 uint8_t fType; | 162 uint8_t fType; |
147 }; | 163 }; |
148 | 164 |
149 // An untyped pointer to some bytes in fAlloc. This is the interface for po
lymorphic dispatch: | 165 // An untyped pointer to some bytes in fAlloc. This is the interface for po
lymorphic dispatch: |
150 // visit() and mutate() work with the parallel fTypes array to do the work o
f a vtable. | 166 // visit() and mutate() work with the parallel fTypes array to do the work o
f a vtable. |
151 struct Record { | 167 struct Record { |
152 public: | 168 public: |
153 // Point this record to its data in fAlloc. Returns ptr for convenience
. | 169 // Point this record to its data in fAlloc. Returns ptr for convenience
. |
154 template <typename T> | 170 template <typename T> |
155 T* set(T* ptr) { | 171 T* set(T* ptr) { |
156 fPtr = ptr; | 172 fPtr = ptr; |
157 return ptr; | 173 return ptr; |
158 } | 174 } |
159 | 175 |
| 176 // Get the data in fAlloc, assuming it's of type T. |
| 177 template <typename T> |
| 178 T* ptr() const { return (T*)fPtr; } |
| 179 |
160 // Visit this record with functor F (see public API above) assuming the
record we're | 180 // Visit this record with functor F (see public API above) assuming the
record we're |
161 // pointing to has this type. | 181 // pointing to has this type. |
162 template <typename F> | 182 template <typename F> |
163 void visit(Type8 type, F& f) const { | 183 void visit(Type8 type, F& f) const { |
164 #define CASE(T) case SkRecords::T##_Type: return f(*this->ptr<SkRecords:
:T>()); | 184 #define CASE(T) case SkRecords::T##_Type: return f(*this->ptr<SkRecords:
:T>()); |
165 switch(type) { SK_RECORD_TYPES(CASE) } | 185 switch(type) { SK_RECORD_TYPES(CASE) } |
166 #undef CASE | 186 #undef CASE |
167 } | 187 } |
168 | 188 |
169 // Mutate this record with functor F (see public API above) assuming the
record we're | 189 // Mutate this record with functor F (see public API above) assuming the
record we're |
170 // pointing to has this type. | 190 // pointing to has this type. |
171 template <typename F> | 191 template <typename F> |
172 void mutate(Type8 type, F& f) { | 192 void mutate(Type8 type, F& f) { |
173 #define CASE(T) case SkRecords::T##_Type: return f(this->ptr<SkRecords::
T>()); | 193 #define CASE(T) case SkRecords::T##_Type: return f(this->ptr<SkRecords::
T>()); |
174 switch(type) { SK_RECORD_TYPES(CASE) } | 194 switch(type) { SK_RECORD_TYPES(CASE) } |
175 #undef CASE | 195 #undef CASE |
176 } | 196 } |
177 | 197 |
178 private: | 198 private: |
179 template <typename T> | |
180 T* ptr() const { return (T*)fPtr; } | |
181 | |
182 void* fPtr; | 199 void* fPtr; |
183 }; | 200 }; |
184 | 201 |
185 // fAlloc needs to be a data structure which can append variable length data
in contiguous | 202 // fAlloc needs to be a data structure which can append variable length data
in contiguous |
186 // chunks, returning a stable handle to that data for later retrieval. | 203 // chunks, returning a stable handle to that data for later retrieval. |
187 // | 204 // |
188 // fRecords and fTypes need to be data structures that can append fixed leng
th data, and need to | 205 // fRecords and fTypes need to be data structures that can append fixed leng
th data, and need to |
189 // support efficient forward iteration. (They don't need to be contiguous o
r indexable.) | 206 // support efficient forward iteration. (They don't need to be contiguous o
r indexable.) |
190 | 207 |
191 SkChunkAlloc fAlloc; | 208 SkChunkAlloc fAlloc; |
192 SkAutoTMalloc<Record> fRecords; | 209 SkAutoTMalloc<Record> fRecords; |
193 SkAutoTMalloc<Type8> fTypes; | 210 SkAutoTMalloc<Type8> fTypes; |
194 // fCount and fReserved measure both fRecords and fTypes, which always grow
in lock step. | 211 // fCount and fReserved measure both fRecords and fTypes, which always grow
in lock step. |
195 unsigned fCount; | 212 unsigned fCount; |
196 unsigned fReserved; | 213 unsigned fReserved; |
197 const unsigned kFirstReserveCount; | 214 const unsigned kFirstReserveCount; |
198 }; | 215 }; |
199 | 216 |
200 #endif//SkRecord_DEFINED | 217 #endif//SkRecord_DEFINED |
OLD | NEW |