OLD | NEW |
---|---|
1 | 1 |
2 /* | 2 /* |
3 * Copyright 2010 Google Inc. | 3 * Copyright 2010 Google Inc. |
4 * | 4 * |
5 * Use of this source code is governed by a BSD-style license that can be | 5 * Use of this source code is governed by a BSD-style license that can be |
6 * found in the LICENSE file. | 6 * found in the LICENSE file. |
7 */ | 7 */ |
8 | 8 |
9 | 9 |
10 | 10 |
11 #include "GrResourceCache.h" | 11 #include "GrResourceCache.h" |
12 #include "GrResource.h" | 12 #include "GrResource.h" |
13 | 13 |
14 | 14 |
15 GrResourceKey::ResourceType GrResourceKey::GenerateResourceType() { | 15 GrResourceKey::ResourceType GrResourceKey::GenerateResourceType() { |
16 static int32_t gNextType = 0; | 16 static int32_t gNextType = 0; |
17 | 17 |
18 int32_t type = sk_atomic_inc(&gNextType); | 18 int32_t type = sk_atomic_inc(&gNextType); |
19 if (type >= (1 << 8 * sizeof(ResourceType))) { | 19 if (type >= (1 << 8 * sizeof(ResourceType))) { |
20 GrCrash("Too many Resource Types"); | 20 GrCrash("Too many Resource Types"); |
21 } | 21 } |
22 | 22 |
23 return static_cast<ResourceType>(type); | 23 return static_cast<ResourceType>(type); |
24 } | 24 } |
25 | 25 |
26 /////////////////////////////////////////////////////////////////////////////// | 26 /////////////////////////////////////////////////////////////////////////////// |
27 | 27 |
28 GrResourceEntry::GrResourceEntry(const GrResourceKey& key, GrResource* resource) | 28 GrResourceEntry::GrResourceEntry(const GrResourceKey& key, GrResource* firstReso urce) |
29 : fKey(key), fResource(resource) { | 29 : fKey(key) |
30 // we assume ownership of the resource, and will unref it when we die | 30 , fExclusiveCount(0) { |
31 SkASSERT(resource); | 31 addResource(firstResource); |
mtklein
2013/12/03 19:02:02
this->
| |
32 resource->ref(); | |
33 } | 32 } |
34 | 33 |
35 GrResourceEntry::~GrResourceEntry() { | 34 GrResourceEntry::~GrResourceEntry() { |
mtklein
2013/12/03 19:02:02
The responsibility for resource ownership here see
| |
36 fResource->setCacheEntry(NULL); | 35 SkASSERT(isEmpty()); |
37 fResource->unref(); | 36 } |
37 | |
38 void GrResourceEntry::addResource(GrResource* resource) { | |
39 // We assume ownership of the resource, and will unref it when resource is | |
40 // removed from the entry. | |
41 resource->ref(); | |
42 resource->setCacheEntry(this); | |
43 fResources.addToHead(resource); | |
38 } | 44 } |
39 | 45 |
40 #ifdef SK_DEBUG | 46 bool GrResourceEntry::removeResource(GrResource* resource) { |
41 void GrResourceEntry::validate() const { | 47 fResources.remove(resource); |
42 SkASSERT(fResource); | 48 |
43 SkASSERT(fResource->getCacheEntry() == this); | 49 // The unref() might run destructor which would remove other resources from this entry and delete the |
44 fResource->validate(); | 50 // entry. Temporarily increase exclusive count, so that entry stays alive. |
51 ++fExclusiveCount; | |
52 | |
53 resource->setCacheEntry(NULL); | |
54 resource->unref(); | |
55 | |
56 --fExclusiveCount; | |
57 | |
58 return isEmpty(); | |
45 } | 59 } |
46 #endif | 60 |
61 bool GrResourceEntry::removeExclusiveResource(GrResource* resource) { | |
62 resource->setCacheEntry(NULL); | |
63 resource->unref(); | |
64 | |
65 // Decrease the count after unref(). See removeResource. | |
66 --fExclusiveCount; | |
67 | |
68 return isEmpty(); | |
69 } | |
70 | |
71 void GrResourceEntry::makeExclusive(GrResource* resource) { | |
72 fResources.remove(resource); | |
73 fExclusiveCount++; | |
74 } | |
75 | |
76 void GrResourceEntry::makeNonExclusive(GrResource* resource) { | |
77 fResources.addToHead(resource); | |
78 fExclusiveCount--; | |
79 } | |
47 | 80 |
48 /////////////////////////////////////////////////////////////////////////////// | 81 /////////////////////////////////////////////////////////////////////////////// |
49 | 82 |
50 GrResourceCache::GrResourceCache(int maxCount, size_t maxBytes) : | 83 GrResourceCache::GrResourceCache(int maxCount, size_t maxBytes) : |
51 fMaxCount(maxCount), | 84 fMaxCount(maxCount), |
52 fMaxBytes(maxBytes) { | 85 fMaxBytes(maxBytes) { |
53 #if GR_CACHE_STATS | 86 #if GR_CACHE_STATS |
54 fHighWaterEntryCount = 0; | 87 fHighWaterEntryCount = 0; |
55 fHighWaterEntryBytes = 0; | 88 fHighWaterEntryBytes = 0; |
56 fHighWaterClientDetachedCount = 0; | 89 fHighWaterClientDetachedCount = 0; |
57 fHighWaterClientDetachedBytes = 0; | 90 fHighWaterClientDetachedBytes = 0; |
58 #endif | 91 #endif |
59 | 92 |
60 fEntryCount = 0; | 93 fEntryCount = 0; |
61 fEntryBytes = 0; | 94 fEntryBytes = 0; |
62 fClientDetachedCount = 0; | 95 fClientDetachedCount = 0; |
63 fClientDetachedBytes = 0; | 96 fClientDetachedBytes = 0; |
64 | 97 |
65 fPurging = false; | 98 fPurging = false; |
66 | 99 |
67 fOverbudgetCB = NULL; | 100 fOverbudgetCB = NULL; |
68 fOverbudgetData = NULL; | 101 fOverbudgetData = NULL; |
69 } | 102 } |
70 | 103 |
71 GrResourceCache::~GrResourceCache() { | 104 GrResourceCache::~GrResourceCache() { |
72 GrAutoResourceCacheValidate atcv(this); | 105 GrAutoResourceCacheValidate atcv(this); |
73 | 106 |
74 EntryList::Iter iter; | 107 while (GrResource* resource = fList.head()) { |
108 GrAutoResourceCacheValidate atcv(this); | |
109 this->removeResource(resource); | |
110 } | |
75 | 111 |
76 // Unlike the removeAll, here we really remove everything, including locked resources. | 112 SkASSERT(fCache.count() == 0); |
77 while (GrResourceEntry* entry = fList.head()) { | |
78 GrAutoResourceCacheValidate atcv(this); | |
79 | |
80 // remove from our cache | |
81 fCache.remove(entry->fKey, entry); | |
82 | |
83 // remove from our llist | |
84 this->internalDetach(entry); | |
85 | |
86 delete entry; | |
87 } | |
88 } | 113 } |
89 | 114 |
90 void GrResourceCache::getLimits(int* maxResources, size_t* maxResourceBytes) con st{ | 115 void GrResourceCache::getLimits(int* maxResources, size_t* maxResourceBytes) con st{ |
91 if (NULL != maxResources) { | 116 if (NULL != maxResources) { |
92 *maxResources = fMaxCount; | 117 *maxResources = fMaxCount; |
93 } | 118 } |
94 if (NULL != maxResourceBytes) { | 119 if (NULL != maxResourceBytes) { |
95 *maxResourceBytes = fMaxBytes; | 120 *maxResourceBytes = fMaxBytes; |
96 } | 121 } |
97 } | 122 } |
98 | 123 |
99 void GrResourceCache::setLimits(int maxResources, size_t maxResourceBytes) { | 124 void GrResourceCache::setLimits(int maxResources, size_t maxResourceBytes) { |
100 bool smaller = (maxResources < fMaxCount) || (maxResourceBytes < fMaxBytes); | 125 bool smaller = (maxResources < fMaxCount) || (maxResourceBytes < fMaxBytes); |
101 | 126 |
102 fMaxCount = maxResources; | 127 fMaxCount = maxResources; |
103 fMaxBytes = maxResourceBytes; | 128 fMaxBytes = maxResourceBytes; |
104 | 129 |
105 if (smaller) { | 130 if (smaller) { |
106 this->purgeAsNeeded(); | 131 this->purgeAsNeeded(); |
107 } | 132 } |
108 } | 133 } |
109 | 134 |
110 void GrResourceCache::internalDetach(GrResourceEntry* entry, | 135 void GrResourceCache::internalDetach(GrResource* resource, |
111 BudgetBehaviors behavior) { | 136 BudgetBehaviors behavior) { |
112 fList.remove(entry); | 137 fList.remove(resource); |
113 | 138 |
114 // update our stats | 139 // update our stats |
115 if (kIgnore_BudgetBehavior == behavior) { | 140 if (kIgnore_BudgetBehavior == behavior) { |
116 fClientDetachedCount += 1; | 141 fClientDetachedCount += 1; |
117 fClientDetachedBytes += entry->resource()->sizeInBytes(); | 142 fClientDetachedBytes += resource->sizeInBytes(); |
118 | 143 |
119 #if GR_CACHE_STATS | 144 #if GR_CACHE_STATS |
120 if (fHighWaterClientDetachedCount < fClientDetachedCount) { | 145 if (fHighWaterClientDetachedCount < fClientDetachedCount) { |
121 fHighWaterClientDetachedCount = fClientDetachedCount; | 146 fHighWaterClientDetachedCount = fClientDetachedCount; |
122 } | 147 } |
123 if (fHighWaterClientDetachedBytes < fClientDetachedBytes) { | 148 if (fHighWaterClientDetachedBytes < fClientDetachedBytes) { |
124 fHighWaterClientDetachedBytes = fClientDetachedBytes; | 149 fHighWaterClientDetachedBytes = fClientDetachedBytes; |
125 } | 150 } |
126 #endif | 151 #endif |
127 | 152 |
128 } else { | 153 } else { |
129 SkASSERT(kAccountFor_BudgetBehavior == behavior); | 154 SkASSERT(kAccountFor_BudgetBehavior == behavior); |
130 | 155 |
131 fEntryCount -= 1; | 156 fEntryCount -= 1; |
132 fEntryBytes -= entry->resource()->sizeInBytes(); | 157 fEntryBytes -= resource->sizeInBytes(); |
133 } | 158 } |
134 } | 159 } |
135 | 160 |
136 void GrResourceCache::attachToHead(GrResourceEntry* entry, | 161 void GrResourceCache::attachToHead(GrResource* resource, |
137 BudgetBehaviors behavior) { | 162 BudgetBehaviors behavior) { |
138 fList.addToHead(entry); | 163 fList.addToHead(resource); |
139 | 164 |
140 // update our stats | 165 // update our stats |
141 if (kIgnore_BudgetBehavior == behavior) { | 166 if (kIgnore_BudgetBehavior == behavior) { |
142 fClientDetachedCount -= 1; | 167 fClientDetachedCount -= 1; |
143 fClientDetachedBytes -= entry->resource()->sizeInBytes(); | 168 fClientDetachedBytes -= resource->sizeInBytes(); |
144 } else { | 169 } else { |
145 SkASSERT(kAccountFor_BudgetBehavior == behavior); | 170 SkASSERT(kAccountFor_BudgetBehavior == behavior); |
146 | 171 |
147 fEntryCount += 1; | 172 fEntryCount += 1; |
148 fEntryBytes += entry->resource()->sizeInBytes(); | 173 fEntryBytes += resource->sizeInBytes(); |
149 | 174 |
150 #if GR_CACHE_STATS | 175 #if GR_CACHE_STATS |
151 if (fHighWaterEntryCount < fEntryCount) { | 176 if (fHighWaterEntryCount < fEntryCount) { |
152 fHighWaterEntryCount = fEntryCount; | 177 fHighWaterEntryCount = fEntryCount; |
153 } | 178 } |
154 if (fHighWaterEntryBytes < fEntryBytes) { | 179 if (fHighWaterEntryBytes < fEntryBytes) { |
155 fHighWaterEntryBytes = fEntryBytes; | 180 fHighWaterEntryBytes = fEntryBytes; |
156 } | 181 } |
157 #endif | 182 #endif |
158 } | 183 } |
159 } | 184 } |
160 | 185 |
161 // This functor just searches for an entry with only a single ref (from | 186 // This functor just searches for an entry with only a single ref (from |
162 // the texture cache itself). Presumably in this situation no one else | 187 // the texture cache itself). Presumably in this situation no one else |
163 // is relying on the texture. | 188 // is relying on the texture. |
164 class GrTFindUnreffedFunctor { | 189 class GrTFindUnreffedFunctor { |
165 public: | 190 public: |
166 bool operator()(const GrResourceEntry* entry) const { | 191 bool operator()(const GrResource* resource) const { |
167 return entry->resource()->unique(); | 192 return resource->unique(); |
168 } | 193 } |
169 }; | 194 }; |
170 | 195 |
171 GrResource* GrResourceCache::find(const GrResourceKey& key, uint32_t ownershipFl ags) { | 196 GrResource* GrResourceCache::find(const GrResourceKey& key, uint32_t ownershipFl ags) { |
172 GrAutoResourceCacheValidate atcv(this); | 197 GrAutoResourceCacheValidate atcv(this); |
173 | 198 |
174 GrResourceEntry* entry = NULL; | 199 GrResourceEntry* entry = fCache.find(key); |
175 | |
176 if (ownershipFlags & kNoOtherOwners_OwnershipFlag) { | |
177 GrTFindUnreffedFunctor functor; | |
178 | |
179 entry = fCache.find<GrTFindUnreffedFunctor>(key, functor); | |
180 } else { | |
181 entry = fCache.find(key); | |
182 } | |
183 | |
184 if (NULL == entry) { | 200 if (NULL == entry) { |
185 return NULL; | 201 return NULL; |
186 } | 202 } |
187 | 203 |
204 GrResource* resource; | |
205 if (ownershipFlags & kNoOtherOwners_OwnershipFlag) { | |
206 GrTFindUnreffedFunctor functor; | |
207 resource = entry->resources().find(functor); | |
208 } else { | |
209 // Find a resource not referenced outside cache, or the least referenced one. | |
210 typedef GrResourceEntry::CacheEntryResourcesInternalLListType::Iter Entr yResourcesIter; | |
211 | |
212 EntryResourcesIter iter; | |
213 resource = iter.init(entry->resources(), EntryResourcesIter::kTail_IterS tart); | |
mtklein
2013/12/03 19:02:02
If we don't need to iterate both ways down the lis
| |
214 if (NULL != resource && resource->getRefCnt() > 1) { | |
mtklein
2013/12/03 19:02:02
Oooh, calls to getRefCnt() make me super scared.
| |
215 int refCount = resource->getRefCnt(); | |
216 for (GrResource* nextResource = iter.next(); | |
217 NULL != nextResource && refCount > 1; | |
218 nextResource = iter.next()) { | |
219 if (nextResource->getRefCnt() > refCount) { | |
220 resource = nextResource; | |
221 refCount = nextResource->getRefCnt(); | |
222 } | |
223 } | |
224 } | |
225 } | |
226 | |
227 if (NULL == resource) { | |
228 return NULL; | |
229 } | |
230 | |
188 if (ownershipFlags & kHide_OwnershipFlag) { | 231 if (ownershipFlags & kHide_OwnershipFlag) { |
189 this->makeExclusive(entry); | 232 this->makeExclusive(resource); |
190 } else { | 233 } else { |
191 // Make this resource MRU | 234 // Make this resource MRU |
192 this->internalDetach(entry); | 235 this->internalDetach(resource); |
193 this->attachToHead(entry); | 236 this->attachToHead(resource); |
194 } | 237 } |
195 | 238 |
196 return entry->fResource; | 239 return resource; |
197 } | 240 } |
198 | 241 |
199 void GrResourceCache::addResource(const GrResourceKey& key, | 242 void GrResourceCache::addResource(const GrResourceKey& key, |
200 GrResource* resource, | 243 GrResource* resource, |
201 uint32_t ownershipFlags) { | 244 uint32_t ownershipFlags) { |
202 SkASSERT(NULL == resource->getCacheEntry()); | 245 SkASSERT(NULL == resource->getCacheEntry()); |
203 // we don't expect to create new resources during a purge. In theory | 246 // we don't expect to create new resources during a purge. In theory |
204 // this could cause purgeAsNeeded() into an infinite loop (e.g. | 247 // this could cause purgeAsNeeded() into an infinite loop (e.g. |
205 // each resource destroyed creates and locks 2 resources and | 248 // each resource destroyed creates and locks 2 resources and |
206 // unlocks 1 thereby causing a new purge). | 249 // unlocks 1 thereby causing a new purge). |
207 SkASSERT(!fPurging); | 250 SkASSERT(!fPurging); |
208 GrAutoResourceCacheValidate atcv(this); | 251 GrAutoResourceCacheValidate atcv(this); |
209 | 252 |
210 GrResourceEntry* entry = SkNEW_ARGS(GrResourceEntry, (key, resource)); | 253 GrResourceEntry* entry = fCache.find(key); |
211 resource->setCacheEntry(entry); | 254 if (NULL == entry) { |
255 entry = SkNEW_ARGS(GrResourceEntry, (key, resource)); | |
256 fCache.add(entry); | |
257 } else { | |
258 entry->addResource(resource); | |
259 } | |
212 | 260 |
213 this->attachToHead(entry); | 261 this->attachToHead(resource); |
214 fCache.insert(key, entry); | |
215 | 262 |
216 if (ownershipFlags & kHide_OwnershipFlag) { | 263 if (ownershipFlags & kHide_OwnershipFlag) { |
217 this->makeExclusive(entry); | 264 this->makeExclusive(resource); |
218 } | 265 } |
219 | |
220 } | 266 } |
221 | 267 |
222 void GrResourceCache::makeExclusive(GrResourceEntry* entry) { | 268 void GrResourceCache::makeExclusive(GrResource* resource) { |
223 GrAutoResourceCacheValidate atcv(this); | 269 GrAutoResourceCacheValidate atcv(this); |
224 | |
225 // When scratch textures are detached (to hide them from future finds) they | 270 // When scratch textures are detached (to hide them from future finds) they |
226 // still count against the resource budget | 271 // still count against the resource budget |
227 this->internalDetach(entry, kIgnore_BudgetBehavior); | 272 this->internalDetach(resource, kIgnore_BudgetBehavior); |
228 fCache.remove(entry->key(), entry); | 273 resource->getCacheEntry()->makeExclusive(resource); |
229 | 274 |
230 #ifdef SK_DEBUG | 275 #ifdef SK_DEBUG |
231 fExclusiveList.addToHead(entry); | 276 fExclusiveList.addToHead(resource); |
232 #endif | 277 #endif |
233 } | 278 } |
234 | 279 |
235 void GrResourceCache::removeInvalidResource(GrResourceEntry* entry) { | 280 void GrResourceCache::removeInvalidResource(GrResource* resource) { |
236 // If the resource went invalid while it was detached then purge it | 281 // If the resource went invalid while it was detached then purge it |
237 // This can happen when a 3D context was lost, | 282 // This can happen when a 3D context was lost, |
238 // the client called GrContext::contextDestroyed() to notify Gr, | 283 // the client called GrContext::contextDestroyed() to notify Gr, |
239 // and then later an SkGpuDevice's destructor releases its backing | 284 // and then later an SkGpuDevice's destructor releases its backing |
240 // texture (which was invalidated at contextDestroyed time). | 285 // texture (which was invalidated at contextDestroyed time). |
241 fClientDetachedCount -= 1; | 286 fClientDetachedCount -= 1; |
242 fEntryCount -= 1; | 287 fEntryCount -= 1; |
243 size_t size = entry->resource()->sizeInBytes(); | 288 size_t size = resource->sizeInBytes(); |
244 fClientDetachedBytes -= size; | 289 fClientDetachedBytes -= size; |
245 fEntryBytes -= size; | 290 fEntryBytes -= size; |
246 } | 291 } |
247 | 292 |
248 void GrResourceCache::makeNonExclusive(GrResourceEntry* entry) { | 293 void GrResourceCache::makeNonExclusive(GrResource* resource) { |
249 GrAutoResourceCacheValidate atcv(this); | 294 GrAutoResourceCacheValidate atcv(this); |
250 | 295 |
251 #ifdef SK_DEBUG | 296 #ifdef SK_DEBUG |
252 fExclusiveList.remove(entry); | 297 fExclusiveList.remove(resource); |
253 #endif | 298 #endif |
254 | 299 |
255 if (entry->resource()->isValid()) { | 300 if (resource->isValid()) { |
256 // Since scratch textures still count against the cache budget even | 301 // Since scratch textures still count against the cache budget even |
257 // when they have been removed from the cache, re-adding them doesn't | 302 // when they have been removed from the cache, re-adding them doesn't |
258 // alter the budget information. | 303 // alter the budget information. |
259 attachToHead(entry, kIgnore_BudgetBehavior); | 304 attachToHead(resource, kIgnore_BudgetBehavior); |
260 fCache.insert(entry->key(), entry); | 305 resource->getCacheEntry()->makeNonExclusive(resource); |
261 } else { | 306 } else { |
262 this->removeInvalidResource(entry); | 307 this->removeInvalidResource(resource); |
308 GrResourceEntry* entry = resource->getCacheEntry(); | |
309 if (entry->removeExclusiveResource(resource)) { | |
310 fCache.remove(entry->key()); | |
311 delete entry; | |
312 } | |
263 } | 313 } |
264 } | 314 } |
265 | 315 |
266 /** | 316 /** |
267 * Destroying a resource may potentially trigger the unlock of additional | 317 * Destroying a resource may potentially trigger the unlock of additional |
268 * resources which in turn will trigger a nested purge. We block the nested | 318 * resources which in turn will trigger a nested purge. We block the nested |
269 * purge using the fPurging variable. However, the initial purge will keep | 319 * purge using the fPurging variable. However, the initial purge will keep |
270 * looping until either all resources in the cache are unlocked or we've met | 320 * looping until either all resources in the cache are unlocked or we've met |
271 * the budget. There is an assertion in createAndLock to check against a | 321 * the budget. There is an assertion in createAndLock to check against a |
272 * resource's destructor inserting new resources into the cache. If these | 322 * resource's destructor inserting new resources into the cache. If these |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
305 fInvalidationInbox.poll(&invalidated); | 355 fInvalidationInbox.poll(&invalidated); |
306 | 356 |
307 for (int i = 0; i < invalidated.count(); i++) { | 357 for (int i = 0; i < invalidated.count(); i++) { |
308 // We're somewhat missing an opportunity here. We could use the | 358 // We're somewhat missing an opportunity here. We could use the |
309 // default find functor that gives us back resources whether we own | 359 // default find functor that gives us back resources whether we own |
310 // them exclusively or not, and when they're not exclusively owned mark | 360 // them exclusively or not, and when they're not exclusively owned mark |
311 // them for purging later when they do become exclusively owned. | 361 // them for purging later when they do become exclusively owned. |
312 // | 362 // |
313 // This is complicated and confusing. May try this in the future. For | 363 // This is complicated and confusing. May try this in the future. For |
314 // now, these resources are just LRU'd as if we never got the message. | 364 // now, these resources are just LRU'd as if we never got the message. |
315 GrResourceEntry* entry = fCache.find(invalidated[i].key, GrTFindUnreffed Functor()); | 365 GrResourceEntry* entry = fCache.find(invalidated[i].key); |
316 if (entry) { | 366 if (entry) { |
317 this->deleteResource(entry); | 367 GrTFindUnreffedFunctor functor; |
368 GrResource* resource; | |
369 do { | |
370 resource = entry->resources().find(functor); | |
371 } while (resource != NULL && this->removeResource(resource)); | |
mtklein
2013/12/03 19:02:02
Hmm, won't this be O(N^2)? This is a case where u
| |
318 } | 372 } |
319 } | 373 } |
320 } | 374 } |
321 | 375 |
322 void GrResourceCache::deleteResource(GrResourceEntry* entry) { | 376 bool GrResourceCache::removeResource(GrResource* resource) { |
323 SkASSERT(1 == entry->fResource->getRefCnt()); | 377 GrResourceEntry* entry = resource->getCacheEntry(); |
378 this->internalDetach(resource); | |
379 if (entry->removeResource(resource)) { | |
380 fCache.remove(entry->key()); | |
381 delete entry; | |
382 return false; | |
383 } | |
324 | 384 |
325 // remove from our cache | 385 return true; |
326 fCache.remove(entry->key(), entry); | |
327 | |
328 // remove from our llist | |
329 this->internalDetach(entry); | |
330 delete entry; | |
331 } | 386 } |
332 | 387 |
333 void GrResourceCache::internalPurge(int extraCount, size_t extraBytes) { | 388 void GrResourceCache::internalPurge(int extraCount, size_t extraBytes) { |
334 SkASSERT(fPurging); | 389 SkASSERT(fPurging); |
335 | 390 |
336 bool withinBudget = false; | 391 bool withinBudget = false; |
337 bool changed = false; | 392 bool changed = false; |
338 | 393 |
339 // The purging process is repeated several times since one pass | 394 // The purging process is repeated several times since one pass |
340 // may free up other resources | 395 // may free up other resources |
341 do { | 396 do { |
342 EntryList::Iter iter; | 397 CacheLRUInternalLListType::Iter iter; |
343 | 398 |
344 changed = false; | 399 changed = false; |
345 | 400 |
346 // Note: the following code relies on the fact that the | 401 // Note: the following code relies on the fact that the |
347 // doubly linked list doesn't invalidate its data/pointers | 402 // doubly linked list doesn't invalidate its data/pointers |
348 // outside of the specific area where a deletion occurs (e.g., | 403 // outside of the specific area where a deletion occurs (e.g., |
349 // in internalDetach) | 404 // in internalDetach) |
350 GrResourceEntry* entry = iter.init(fList, EntryList::Iter::kTail_IterSta rt); | 405 GrResource* resource = iter.init(fList, CacheLRUInternalLListType::Iter: :kTail_IterStart); |
351 | 406 |
352 while (NULL != entry) { | 407 while (NULL != resource) { |
353 GrAutoResourceCacheValidate atcv(this); | 408 GrAutoResourceCacheValidate atcv(this); |
354 | 409 |
355 if ((fEntryCount+extraCount) <= fMaxCount && | 410 if ((fEntryCount+extraCount) <= fMaxCount && |
356 (fEntryBytes+extraBytes) <= fMaxBytes) { | 411 (fEntryBytes+extraBytes) <= fMaxBytes) { |
357 withinBudget = true; | 412 withinBudget = true; |
358 break; | 413 break; |
359 } | 414 } |
360 | 415 |
361 GrResourceEntry* prev = iter.prev(); | 416 GrResource* prev = iter.prev(); |
362 if (entry->fResource->unique()) { | 417 if (resource->unique()) { |
363 changed = true; | 418 changed = true; |
364 this->deleteResource(entry); | 419 this->deleteResource(resource); |
365 } | 420 } |
366 entry = prev; | 421 resource = prev; |
367 } | 422 } |
368 } while (!withinBudget && changed); | 423 } while (!withinBudget && changed); |
369 } | 424 } |
370 | 425 |
371 void GrResourceCache::purgeAllUnlocked() { | 426 void GrResourceCache::purgeAllUnlocked() { |
372 GrAutoResourceCacheValidate atcv(this); | 427 GrAutoResourceCacheValidate atcv(this); |
373 | 428 |
374 // we can have one GrResource holding a lock on another | 429 // we can have one GrResource holding a lock on another |
375 // so we don't want to just do a simple loop kicking each | 430 // so we don't want to just do a simple loop kicking each |
376 // entry out. Instead change the budget and purge. | 431 // entry out. Instead change the budget and purge. |
377 | 432 |
378 size_t savedMaxBytes = fMaxBytes; | 433 size_t savedMaxBytes = fMaxBytes; |
379 int savedMaxCount = fMaxCount; | 434 int savedMaxCount = fMaxCount; |
380 fMaxBytes = (size_t) -1; | 435 fMaxBytes = (size_t) -1; |
381 fMaxCount = 0; | 436 fMaxCount = 0; |
382 this->purgeAsNeeded(); | 437 this->purgeAsNeeded(); |
383 | 438 |
384 #ifdef SK_DEBUG | 439 #ifdef SK_DEBUG |
385 SkASSERT(fExclusiveList.countEntries() == fClientDetachedCount); | 440 SkASSERT(fExclusiveList.countEntries() == fClientDetachedCount); |
386 SkASSERT(countBytes(fExclusiveList) == fClientDetachedBytes); | 441 SkASSERT(countBytes(fExclusiveList) == fClientDetachedBytes); |
387 if (!fCache.count()) { | 442 // Items may have been detached from the cache (such as the backing |
388 // Items may have been detached from the cache (such as the backing | 443 // texture for an SkGpuDevice). The above purge would not have removed |
389 // texture for an SkGpuDevice). The above purge would not have removed | 444 // them. |
390 // them. | 445 SkASSERT(fEntryCount == fClientDetachedCount); |
391 SkASSERT(fEntryCount == fClientDetachedCount); | 446 SkASSERT(fEntryBytes == fClientDetachedBytes); |
392 SkASSERT(fEntryBytes == fClientDetachedBytes); | 447 SkASSERT(fList.isEmpty()); |
393 SkASSERT(fList.isEmpty()); | |
394 } | |
395 #endif | 448 #endif |
396 | 449 |
397 fMaxBytes = savedMaxBytes; | 450 fMaxBytes = savedMaxBytes; |
398 fMaxCount = savedMaxCount; | 451 fMaxCount = savedMaxCount; |
399 } | 452 } |
400 | 453 |
401 /////////////////////////////////////////////////////////////////////////////// | 454 /////////////////////////////////////////////////////////////////////////////// |
402 | 455 |
403 #ifdef SK_DEBUG | 456 #ifdef SK_DEBUG |
404 size_t GrResourceCache::countBytes(const EntryList& list) { | 457 size_t GrResourceCache::countBytes(const CacheLRUInternalLListType& list) { |
405 size_t bytes = 0; | 458 size_t bytes = 0; |
406 | 459 |
407 EntryList::Iter iter; | 460 CacheLRUInternalLListType::Iter iter; |
461 const GrResource* resource = iter.init(const_cast<CacheLRUInternalLListType& >(list), | |
462 CacheLRUInternalLListType::Iter::kTai l_IterStart); | |
408 | 463 |
409 const GrResourceEntry* entry = iter.init(const_cast<EntryList&>(list), | 464 for ( ; NULL != resource; resource = iter.prev()) { |
410 EntryList::Iter::kTail_IterStart); | 465 bytes += resource->sizeInBytes(); |
411 | |
412 for ( ; NULL != entry; entry = iter.prev()) { | |
413 bytes += entry->resource()->sizeInBytes(); | |
414 } | 466 } |
415 return bytes; | 467 return bytes; |
416 } | 468 } |
417 | 469 |
418 static bool both_zero_or_nonzero(int count, size_t bytes) { | 470 static bool both_zero_or_nonzero(int count, size_t bytes) { |
419 return (count == 0 && bytes == 0) || (count > 0 && bytes > 0); | 471 return (count == 0 && bytes == 0) || (count > 0 && bytes > 0); |
420 } | 472 } |
421 | 473 |
422 void GrResourceCache::validate() const { | 474 void GrResourceCache::validate() const { |
423 fList.validate(); | 475 fList.validate(); |
424 fExclusiveList.validate(); | 476 fExclusiveList.validate(); |
425 SkASSERT(both_zero_or_nonzero(fEntryCount, fEntryBytes)); | 477 SkASSERT(both_zero_or_nonzero(fEntryCount, fEntryBytes)); |
426 SkASSERT(both_zero_or_nonzero(fClientDetachedCount, fClientDetachedBytes)); | 478 SkASSERT(both_zero_or_nonzero(fClientDetachedCount, fClientDetachedBytes)); |
427 SkASSERT(fClientDetachedBytes <= fEntryBytes); | 479 SkASSERT(fClientDetachedBytes <= fEntryBytes); |
428 SkASSERT(fClientDetachedCount <= fEntryCount); | 480 SkASSERT(fClientDetachedCount <= fEntryCount); |
429 SkASSERT((fEntryCount - fClientDetachedCount) == fCache.count()); | |
430 | 481 |
431 fCache.validate(); | 482 CacheLRUInternalLListType::Iter iter; |
432 | |
433 | |
434 EntryList::Iter iter; | |
435 | 483 |
436 // check that the exclusively held entries are okay | 484 // check that the exclusively held entries are okay |
437 const GrResourceEntry* entry = iter.init(const_cast<EntryList&>(fExclusiveLi st), | 485 GrResource* resource = iter.init(const_cast<CacheLRUInternalLListType&>(fExc lusiveList), |
438 EntryList::Iter::kHead_IterStart); | 486 CacheLRUInternalLListType::Iter::kHead_Iter Start); |
439 | 487 |
440 for ( ; NULL != entry; entry = iter.next()) { | 488 for ( ; NULL != resource; resource = iter.next()) { |
441 entry->validate(); | 489 resource->validate(); |
442 } | 490 } |
443 | 491 |
444 // check that the shareable entries are okay | 492 // check that the shareable entries are okay |
445 entry = iter.init(const_cast<EntryList&>(fList), EntryList::Iter::kHead_Iter Start); | 493 resource = iter.init(const_cast<CacheLRUInternalLListType&>(fList), |
494 CacheLRUInternalLListType::Iter::kHead_IterStart); | |
446 | 495 |
447 int count = 0; | 496 int count = 0; |
448 for ( ; NULL != entry; entry = iter.next()) { | 497 for ( ; NULL != resource; resource = iter.next()) { |
449 entry->validate(); | 498 resource->validate(); |
450 SkASSERT(fCache.find(entry->key())); | 499 SkASSERT(fCache.find(resource->getCacheEntry()->key())); |
451 count += 1; | 500 count += 1; |
452 } | 501 } |
453 SkASSERT(count == fEntryCount - fClientDetachedCount); | 502 SkASSERT(count == fEntryCount - fClientDetachedCount); |
454 | 503 |
455 size_t bytes = countBytes(fList); | 504 size_t bytes = countBytes(fList); |
456 SkASSERT(bytes == fEntryBytes - fClientDetachedBytes); | 505 SkASSERT(bytes == fEntryBytes - fClientDetachedBytes); |
457 | 506 |
458 bytes = countBytes(fExclusiveList); | 507 bytes = countBytes(fExclusiveList); |
459 SkASSERT(bytes == fClientDetachedBytes); | 508 SkASSERT(bytes == fClientDetachedBytes); |
460 | 509 |
461 SkASSERT(fList.countEntries() == fEntryCount - fClientDetachedCount); | 510 SkASSERT(fList.countEntries() == fEntryCount - fClientDetachedCount); |
462 | 511 |
463 SkASSERT(fExclusiveList.countEntries() == fClientDetachedCount); | 512 SkASSERT(fExclusiveList.countEntries() == fClientDetachedCount); |
464 } | 513 } |
465 #endif // SK_DEBUG | 514 #endif // SK_DEBUG |
466 | 515 |
467 #if GR_CACHE_STATS | 516 #if GR_CACHE_STATS |
468 | 517 |
469 void GrResourceCache::printStats() { | 518 void GrResourceCache::printStats() { |
470 int locked = 0; | 519 int locked = 0; |
471 | 520 |
472 EntryList::Iter iter; | 521 CacheLRUInternalLListType::Iter iter; |
473 | 522 |
474 GrResourceEntry* entry = iter.init(fList, EntryList::Iter::kTail_IterStart); | 523 GrResource* resource = iter.init(fList, CacheLRUInternalLListType::Iter::kTa il_IterStart); |
475 | 524 |
476 for ( ; NULL != entry; entry = iter.prev()) { | 525 for ( ; NULL != resource; resource = iter.prev()) { |
477 if (entry->fResource->getRefCnt() > 1) { | 526 if (resource->getRefCnt() > 1) { |
478 ++locked; | 527 ++locked; |
479 } | 528 } |
480 } | 529 } |
481 | 530 |
482 SkDebugf("Budget: %d items %d bytes\n", fMaxCount, fMaxBytes); | 531 SkDebugf("Budget: %d items %d bytes\n", fMaxCount, fMaxBytes); |
483 SkDebugf("\t\tEntry Count: current %d (%d locked) high %d\n", | 532 SkDebugf("\t\tEntry Count: current %d (%d locked) high %d\n", |
484 fEntryCount, locked, fHighWaterEntryCount); | 533 fEntryCount, locked, fHighWaterEntryCount); |
485 SkDebugf("\t\tEntry Bytes: current %d high %d\n", | 534 SkDebugf("\t\tEntry Bytes: current %d high %d\n", |
486 fEntryBytes, fHighWaterEntryBytes); | 535 fEntryBytes, fHighWaterEntryBytes); |
487 SkDebugf("\t\tDetached Entry Count: current %d high %d\n", | 536 SkDebugf("\t\tDetached Entry Count: current %d high %d\n", |
488 fClientDetachedCount, fHighWaterClientDetachedCount); | 537 fClientDetachedCount, fHighWaterClientDetachedCount); |
489 SkDebugf("\t\tDetached Bytes: current %d high %d\n", | 538 SkDebugf("\t\tDetached Bytes: current %d high %d\n", |
490 fClientDetachedBytes, fHighWaterClientDetachedBytes); | 539 fClientDetachedBytes, fHighWaterClientDetachedBytes); |
491 } | 540 } |
492 | 541 |
493 #endif | 542 #endif |
494 | 543 |
495 /////////////////////////////////////////////////////////////////////////////// | 544 /////////////////////////////////////////////////////////////////////////////// |
OLD | NEW |