1 /*
2  * Copyright 2015 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "GrTextBlobCache.h"
9 
10 DECLARE_SKMESSAGEBUS_MESSAGE(GrTextBlobCache::PurgeBlobMessage)
11 
12 GrTextBlobCache::~GrTextBlobCache() {
13     this->freeAll();
14     delete fPool;
15 }
16 
17 void GrTextBlobCache::freeAll() {
18     fBlobIDCache.foreach([this](uint32_t, BlobIDCacheEntry* entry) {
19         for (const auto& blob : entry->fBlobs) {
20             fBlobList.remove(blob.get());
21         }
22     });
23 
24     fBlobIDCache.reset();
25 
26     // There should be no allocations in the memory pool at this point
27     SkASSERT(!fPool || fPool->isEmpty());
28     SkASSERT(fBlobList.isEmpty());
29 }
30 
31 void GrTextBlobCache::PostPurgeBlobMessage(uint32_t blobID, uint32_t cacheID) {
32     SkASSERT(blobID != SK_InvalidGenID);
33     SkMessageBus<PurgeBlobMessage>::Post(PurgeBlobMessage({blobID}), cacheID);
34 }
35 
36 void GrTextBlobCache::purgeStaleBlobs() {
37     SkTArray<PurgeBlobMessage> msgs;
38     fPurgeBlobInbox.poll(&msgs);
39 
40     for (const auto& msg : msgs) {
41         auto* idEntry = fBlobIDCache.find(msg.fID);
42         if (!idEntry) {
43             // no cache entries for id
44             continue;
45         }
46 
47         // remove all blob entries from the LRU list
48         for (const auto& blob : idEntry->fBlobs) {
49             fBlobList.remove(blob.get());
50         }
51 
52         // drop the idEntry itself (unrefs all blobs)
53         fBlobIDCache.remove(msg.fID);
54     }
55 }
56 
57 bool GrTextBlobCache::overBudget() const {
58     if (fPool) {
59         return fPool->size() > fBudget;
60     }
61 
62     // When DDLs are being recorded no GrAtlasTextBlob will be deleted so the cache budget is
63     // somewhat meaningless.
64     return false;
65 }
66 
67 void GrTextBlobCache::checkPurge(GrAtlasTextBlob* blob) {
68     // First, purge all stale blob IDs.
69     this->purgeStaleBlobs();
70 
71     // If we are still over budget, then unref until we are below budget again
72     if (this->overBudget()) {
73         BitmapBlobList::Iter iter;
74         iter.init(fBlobList, BitmapBlobList::Iter::kTail_IterStart);
75         GrAtlasTextBlob* lruBlob = nullptr;
76         while (this->overBudget() && (lruBlob = iter.get()) && lruBlob != blob) {
77             // Backup the iterator before removing and unrefing the blob
78             iter.prev();
79 
80             this->remove(lruBlob);
81         }
82 
83         // If we break out of the loop with lruBlob == blob, then we haven't purged enough
84         // use the call back and try to free some more.  If we are still overbudget after this,
85         // then this single textblob is over our budget
86         if (blob && lruBlob == blob) {
87             (*fCallback)(fData);
88         }
89 
90 #ifdef SPEW_BUDGET_MESSAGE
91         if (this->overBudget()) {
92             SkDebugf("Single textblob is larger than our whole budget");
93         }
94 #endif
95     }
96 }
97 
98 
99 
100