Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / third_party / skia / src / gpu / GrResourceCache.cpp
1
2 /*
3  * Copyright 2010 Google Inc.
4  *
5  * Use of this source code is governed by a BSD-style license that can be
6  * found in the LICENSE file.
7  */
8
9 #include "GrResourceCache.h"
10 #include "GrGpuResource.h"
11 #include "GrTexturePriv.h"
12
13 DECLARE_SKMESSAGEBUS_MESSAGE(GrResourceInvalidatedMessage);
14
15 ///////////////////////////////////////////////////////////////////////////////
16
17 void GrGpuResource::didChangeGpuMemorySize() const {
18     if (this->isInCache()) {
19         fCacheEntry->didChangeResourceSize();
20     }
21 }
22
23 ///////////////////////////////////////////////////////////////////////////////
24
25 GrResourceKey::ResourceType GrResourceKey::GenerateResourceType() {
26     static int32_t gNextType = 0;
27
28     int32_t type = sk_atomic_inc(&gNextType);
29     if (type >= (1 << 8 * sizeof(ResourceType))) {
30         SkFAIL("Too many Resource Types");
31     }
32
33     return static_cast<ResourceType>(type);
34 }
35
36 ///////////////////////////////////////////////////////////////////////////////
37
38 GrResourceCacheEntry::GrResourceCacheEntry(GrResourceCache* resourceCache,
39                                            const GrResourceKey& key,
40                                            GrGpuResource* resource)
41         : fResourceCache(resourceCache),
42           fKey(key),
43           fResource(resource),
44           fCachedSize(resource->gpuMemorySize()),
45           fIsExclusive(false) {
46     // we assume ownership of the resource, and will unref it when we die
47     SkASSERT(resource);
48     resource->ref();
49 }
50
51 GrResourceCacheEntry::~GrResourceCacheEntry() {
52     fResource->setCacheEntry(NULL);
53     fResource->unref();
54 }
55
56 #ifdef SK_DEBUG
57 void GrResourceCacheEntry::validate() const {
58     SkASSERT(fResourceCache);
59     SkASSERT(fResource);
60     SkASSERT(fResource->getCacheEntry() == this);
61     SkASSERT(fResource->gpuMemorySize() == fCachedSize);
62     fResource->validate();
63 }
64 #endif
65
66 void GrResourceCacheEntry::didChangeResourceSize() {
67     size_t oldSize = fCachedSize;
68     fCachedSize = fResource->gpuMemorySize();
69     if (fCachedSize > oldSize) {
70         fResourceCache->didIncreaseResourceSize(this, fCachedSize - oldSize);
71     } else if (fCachedSize < oldSize) {
72         fResourceCache->didDecreaseResourceSize(this, oldSize - fCachedSize);
73     }
74 }
75
76 ///////////////////////////////////////////////////////////////////////////////
77
78 GrResourceCache::GrResourceCache(const GrDrawTargetCaps* caps, int maxCount, size_t maxBytes)
79     : fMaxCount(maxCount)
80     , fMaxBytes(maxBytes)
81     , fCaps(SkRef(caps)) {
82 #if GR_CACHE_STATS
83     fHighWaterEntryCount          = 0;
84     fHighWaterEntryBytes          = 0;
85 #endif
86
87     fEntryCount                   = 0;
88     fEntryBytes                   = 0;
89
90     fPurging                      = false;
91
92     fOverbudgetCB                 = NULL;
93     fOverbudgetData               = NULL;
94 }
95
96 GrResourceCache::~GrResourceCache() {
97     GrAutoResourceCacheValidate atcv(this);
98
99     EntryList::Iter iter;
100
101     // Unlike the removeAll, here we really remove everything, including locked resources.
102     while (GrResourceCacheEntry* entry = fList.head()) {
103         GrAutoResourceCacheValidate atcv(this);
104
105         // remove from our cache
106         fCache.remove(entry->fKey, entry);
107
108         // remove from our llist
109         this->internalDetach(entry);
110
111         delete entry;
112     }
113 }
114
115 void GrResourceCache::getLimits(int* maxResources, size_t* maxResourceBytes) const{
116     if (maxResources) {
117         *maxResources = fMaxCount;
118     }
119     if (maxResourceBytes) {
120         *maxResourceBytes = fMaxBytes;
121     }
122 }
123
124 void GrResourceCache::setLimits(int maxResources, size_t maxResourceBytes) {
125     bool smaller = (maxResources < fMaxCount) || (maxResourceBytes < fMaxBytes);
126
127     fMaxCount = maxResources;
128     fMaxBytes = maxResourceBytes;
129
130     if (smaller) {
131         this->purgeAsNeeded();
132     }
133 }
134
135 void GrResourceCache::internalDetach(GrResourceCacheEntry* entry) {
136     fList.remove(entry);
137     fEntryCount -= 1;
138     fEntryBytes -= entry->fCachedSize;
139 }
140
141 void GrResourceCache::attachToHead(GrResourceCacheEntry* entry) {
142     fList.addToHead(entry);
143
144     fEntryCount += 1;
145     fEntryBytes += entry->fCachedSize;
146
147 #if GR_CACHE_STATS
148     if (fHighWaterEntryCount < fEntryCount) {
149         fHighWaterEntryCount = fEntryCount;
150     }
151     if (fHighWaterEntryBytes < fEntryBytes) {
152         fHighWaterEntryBytes = fEntryBytes;
153     }
154 #endif
155 }
156
157 // This functor just searches for an entry with only a single ref (from
158 // the texture cache itself). Presumably in this situation no one else
159 // is relying on the texture.
160 class GrTFindUnreffedFunctor {
161 public:
162     bool operator()(const GrResourceCacheEntry* entry) const {
163         return entry->resource()->isPurgable();
164     }
165 };
166
167
168 void GrResourceCache::makeResourceMRU(GrGpuResource* resource) {
169     GrResourceCacheEntry* entry = resource->getCacheEntry();
170     if (entry) {
171         this->internalDetach(entry);
172         this->attachToHead(entry);
173     }
174 }
175
176 void GrResourceCache::notifyPurgable(const GrGpuResource* resource) {
177     // Remove scratch textures from the cache the moment they become purgeable if
178     // scratch texture reuse is turned off.
179     SkASSERT(resource->getCacheEntry());
180     if (resource->getCacheEntry()->key().getResourceType() == GrTexturePriv::ResourceType() &&
181         resource->getCacheEntry()->key().isScratch() &&
182         !fCaps->reuseScratchTextures() &&
183         !(static_cast<const GrSurface*>(resource)->desc().fFlags & kRenderTarget_GrSurfaceFlag)) {
184         this->deleteResource(resource->getCacheEntry());
185     }
186 }
187
188 GrGpuResource* GrResourceCache::find(const GrResourceKey& key) {
189     // GrResourceCache2 is responsible for scratch resources.
190     SkASSERT(!key.isScratch());
191
192     GrAutoResourceCacheValidate atcv(this);
193
194     GrResourceCacheEntry* entry = fCache.find(key);
195     if (NULL == entry) {
196         return NULL;
197     }
198
199     // Make this resource MRU
200     this->internalDetach(entry);
201     this->attachToHead(entry);
202
203     return entry->fResource;
204 }
205
206 void GrResourceCache::addResource(const GrResourceKey& key, GrGpuResource* resource) {
207     SkASSERT(NULL == resource->getCacheEntry());
208     // we don't expect to create new resources during a purge. In theory
209     // this could cause purgeAsNeeded() into an infinite loop (e.g.
210     // each resource destroyed creates and locks 2 resources and
211     // unlocks 1 thereby causing a new purge).
212     SkASSERT(!fPurging);
213     GrAutoResourceCacheValidate atcv(this);
214
215     GrResourceCacheEntry* entry = SkNEW_ARGS(GrResourceCacheEntry, (this, key, resource));
216     resource->setCacheEntry(entry);
217
218     this->attachToHead(entry);
219     fCache.insert(key, entry);
220
221     this->purgeAsNeeded();
222 }
223
224 void GrResourceCache::didIncreaseResourceSize(const GrResourceCacheEntry* entry, size_t amountInc) {
225     fEntryBytes += amountInc;
226     this->purgeAsNeeded();
227 }
228
229 void GrResourceCache::didDecreaseResourceSize(const GrResourceCacheEntry* entry, size_t amountDec) {
230     fEntryBytes -= amountDec;
231 #ifdef SK_DEBUG
232     this->validate();
233 #endif
234 }
235
236 /**
237  * Destroying a resource may potentially trigger the unlock of additional
238  * resources which in turn will trigger a nested purge. We block the nested
239  * purge using the fPurging variable. However, the initial purge will keep
240  * looping until either all resources in the cache are unlocked or we've met
241  * the budget. There is an assertion in createAndLock to check against a
242  * resource's destructor inserting new resources into the cache. If these
243  * new resources were unlocked before purgeAsNeeded completed it could
244  * potentially make purgeAsNeeded loop infinitely.
245  *
246  * extraCount and extraBytes are added to the current resource totals to account
247  * for incoming resources (e.g., GrContext is about to add 10MB split between
248  * 10 textures).
249  */
250 void GrResourceCache::purgeAsNeeded(int extraCount, size_t extraBytes) {
251     if (fPurging) {
252         return;
253     }
254
255     fPurging = true;
256
257     this->purgeInvalidated();
258
259     this->internalPurge(extraCount, extraBytes);
260     if (((fEntryCount+extraCount) > fMaxCount ||
261         (fEntryBytes+extraBytes) > fMaxBytes) &&
262         fOverbudgetCB) {
263         // Despite the purge we're still over budget. See if Ganesh can
264         // release some resources and purge again.
265         if ((*fOverbudgetCB)(fOverbudgetData)) {
266             this->internalPurge(extraCount, extraBytes);
267         }
268     }
269
270     fPurging = false;
271 }
272
273 void GrResourceCache::purgeInvalidated() {
274     SkTDArray<GrResourceInvalidatedMessage> invalidated;
275     fInvalidationInbox.poll(&invalidated);
276
277     for (int i = 0; i < invalidated.count(); i++) {
278         while (GrResourceCacheEntry* entry = fCache.find(invalidated[i].key, GrTFindUnreffedFunctor())) {
279             this->deleteResource(entry);
280         }
281     }
282 }
283
284 void GrResourceCache::deleteResource(GrResourceCacheEntry* entry) {
285     SkASSERT(entry->fResource->isPurgable());
286
287     // remove from our cache
288     fCache.remove(entry->key(), entry);
289
290     // remove from our llist
291     this->internalDetach(entry);
292     delete entry;
293 }
294
295 void GrResourceCache::internalPurge(int extraCount, size_t extraBytes) {
296     SkASSERT(fPurging);
297
298     bool withinBudget = false;
299     bool changed = false;
300
301     // The purging process is repeated several times since one pass
302     // may free up other resources
303     do {
304         EntryList::Iter iter;
305
306         changed = false;
307
308         // Note: the following code relies on the fact that the
309         // doubly linked list doesn't invalidate its data/pointers
310         // outside of the specific area where a deletion occurs (e.g.,
311         // in internalDetach)
312         GrResourceCacheEntry* entry = iter.init(fList, EntryList::Iter::kTail_IterStart);
313
314         while (entry) {
315             GrAutoResourceCacheValidate atcv(this);
316
317             if ((fEntryCount+extraCount) <= fMaxCount &&
318                 (fEntryBytes+extraBytes) <= fMaxBytes) {
319                 withinBudget = true;
320                 break;
321             }
322
323             GrResourceCacheEntry* prev = iter.prev();
324             if (entry->fResource->isPurgable()) {
325                 changed = true;
326                 this->deleteResource(entry);
327             }
328             entry = prev;
329         }
330     } while (!withinBudget && changed);
331 }
332
333 void GrResourceCache::purgeAllUnlocked() {
334     GrAutoResourceCacheValidate atcv(this);
335
336     // we can have one GrCacheable holding a lock on another
337     // so we don't want to just do a simple loop kicking each
338     // entry out. Instead change the budget and purge.
339
340     size_t savedMaxBytes = fMaxBytes;
341     int savedMaxCount = fMaxCount;
342     fMaxBytes = (size_t) -1;
343     fMaxCount = 0;
344     this->purgeAsNeeded();
345
346 #ifdef SK_DEBUG
347     if (!fCache.count()) {
348         SkASSERT(fList.isEmpty());
349     }
350 #endif
351
352     fMaxBytes = savedMaxBytes;
353     fMaxCount = savedMaxCount;
354 }
355
356 ///////////////////////////////////////////////////////////////////////////////
357
358 #ifdef SK_DEBUG
359 size_t GrResourceCache::countBytes(const EntryList& list) {
360     size_t bytes = 0;
361
362     EntryList::Iter iter;
363
364     const GrResourceCacheEntry* entry = iter.init(const_cast<EntryList&>(list),
365                                                   EntryList::Iter::kTail_IterStart);
366
367     for ( ; entry; entry = iter.prev()) {
368         bytes += entry->resource()->gpuMemorySize();
369     }
370     return bytes;
371 }
372
373 static bool both_zero_or_nonzero(int count, size_t bytes) {
374     return (count == 0 && bytes == 0) || (count > 0 && bytes > 0);
375 }
376
377 void GrResourceCache::validate() const {
378     fList.validate();
379     SkASSERT(both_zero_or_nonzero(fEntryCount, fEntryBytes));
380     SkASSERT(fEntryCount == fCache.count());
381
382     EntryList::Iter iter;
383
384     // check that the shareable entries are okay
385     const GrResourceCacheEntry* entry = iter.init(const_cast<EntryList&>(fList),
386                                                   EntryList::Iter::kHead_IterStart);
387
388     int count = 0;
389     for ( ; entry; entry = iter.next()) {
390         entry->validate();
391         SkASSERT(fCache.find(entry->key()));
392         count += 1;
393     }
394     SkASSERT(count == fEntryCount);
395
396     size_t bytes = this->countBytes(fList);
397     SkASSERT(bytes == fEntryBytes);
398     SkASSERT(fList.countEntries() == fEntryCount);
399 }
400 #endif // SK_DEBUG
401
402 #if GR_CACHE_STATS
403
404 void GrResourceCache::printStats() {
405     int locked = 0;
406     int scratch = 0;
407
408     EntryList::Iter iter;
409
410     GrResourceCacheEntry* entry = iter.init(fList, EntryList::Iter::kTail_IterStart);
411
412     for ( ; entry; entry = iter.prev()) {
413         if (!entry->fResource->isPurgable()) {
414             ++locked;
415         }
416         if (entry->fResource->isScratch()) {
417             ++scratch;
418         }
419     }
420
421     float countUtilization = (100.f * fEntryCount) / fMaxCount;
422     float byteUtilization = (100.f * fEntryBytes) / fMaxBytes;
423
424     SkDebugf("Budget: %d items %d bytes\n", fMaxCount, fMaxBytes);
425     SkDebugf("\t\tEntry Count: current %d (%d locked, %d scratch %.2g%% full), high %d\n",
426                 fEntryCount, locked, scratch, countUtilization, fHighWaterEntryCount);
427     SkDebugf("\t\tEntry Bytes: current %d (%.2g%% full) high %d\n",
428                 fEntryBytes, byteUtilization, fHighWaterEntryBytes);
429 }
430
431 #endif
432
433 ///////////////////////////////////////////////////////////////////////////////