1 /*
2 * Copyright 2023 Google LLC
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 "src/gpu/graphite/ProxyCache.h"
9
10 #include "include/core/SkBitmap.h"
11 #include "include/core/SkPixelRef.h"
12 #include "include/gpu/GpuTypes.h"
13 #include "src/core/SkMipmap.h"
14 #include "src/gpu/ResourceKey.h"
15 #include "src/gpu/graphite/RecorderPriv.h"
16 #include "src/gpu/graphite/Texture.h"
17 #include "src/gpu/graphite/TextureProxy.h"
18 #include "src/gpu/graphite/TextureUtils.h"
19
20 using namespace skia_private;
21
22 DECLARE_SKMESSAGEBUS_MESSAGE(skgpu::UniqueKeyInvalidatedMsg_Graphite, uint32_t,
23 /* AllowCopyableMessage= */ true)
24
25 namespace {
26
make_bitmap_key(skgpu::UniqueKey * key,const SkBitmap & bm)27 void make_bitmap_key(skgpu::UniqueKey* key, const SkBitmap& bm) {
28 SkASSERT(key);
29
30 SkIPoint origin = bm.pixelRefOrigin();
31 SkIRect subset = SkIRect::MakePtSize(origin, bm.dimensions());
32
33 static const skgpu::UniqueKey::Domain kProxyCacheDomain = skgpu::UniqueKey::GenerateDomain();
34 skgpu::UniqueKey::Builder builder(key, kProxyCacheDomain, 5, "ProxyCache");
35 builder[0] = bm.pixelRef()->getGenerationID();
36 builder[1] = subset.fLeft;
37 builder[2] = subset.fTop;
38 builder[3] = subset.fRight;
39 builder[4] = subset.fBottom;
40 }
41
make_unique_key_invalidation_listener(const skgpu::UniqueKey & key,uint32_t recorderID)42 sk_sp<SkIDChangeListener> make_unique_key_invalidation_listener(const skgpu::UniqueKey& key,
43 uint32_t recorderID) {
44 class Listener : public SkIDChangeListener {
45 public:
46 Listener(const skgpu::UniqueKey& key, uint32_t recorderUniqueID)
47 : fMsg(key, recorderUniqueID) {}
48
49 void changed() override {
50 SkMessageBus<skgpu::UniqueKeyInvalidatedMsg_Graphite, uint32_t>::Post(fMsg);
51 }
52
53 private:
54 skgpu::UniqueKeyInvalidatedMsg_Graphite fMsg;
55 };
56
57 return sk_make_sp<Listener>(key, recorderID);
58 }
59
60 } // anonymous namespace
61
62 namespace skgpu::graphite {
63
ProxyCache(uint32_t recorderID)64 ProxyCache::ProxyCache(uint32_t recorderID) : fInvalidUniqueKeyInbox(recorderID) {
65 SkASSERT(recorderID != SK_InvalidGenID);
66 }
67
~ProxyCache()68 ProxyCache::~ProxyCache() {}
69
operator ()(const UniqueKey & key) const70 uint32_t ProxyCache::UniqueKeyHash::operator()(const UniqueKey& key) const {
71 return key.hash();
72 }
73
findOrCreateCachedProxy(Recorder * recorder,const SkBitmap & bitmap,std::string_view label)74 sk_sp<TextureProxy> ProxyCache::findOrCreateCachedProxy(Recorder* recorder,
75 const SkBitmap& bitmap,
76 std::string_view label) {
77
78 skgpu::UniqueKey key;
79 make_bitmap_key(&key, bitmap);
80 return this->findOrCreateCachedProxy(
81 recorder, key, &bitmap,
82 [](const void* context) { return *static_cast<const SkBitmap*>(context); },
83 label);
84 }
85
findOrCreateCachedProxy(Recorder * recorder,const UniqueKey & key,BitmapGeneratorContext context,BitmapGeneratorFn generator,std::string_view label)86 sk_sp<TextureProxy> ProxyCache::findOrCreateCachedProxy(Recorder* recorder,
87 const UniqueKey& key,
88 BitmapGeneratorContext context,
89 BitmapGeneratorFn generator,
90 std::string_view label) {
91 this->processInvalidKeyMsgs();
92
93 if (sk_sp<TextureProxy>* cached = fCache.find(key)) {
94 if (Resource* resource = (*cached)->texture()) {
95 resource->updateAccessTime();
96 }
97 return *cached;
98 }
99
100 SkBitmap bitmap = generator(context);
101 if (bitmap.empty()) {
102 return nullptr;
103 }
104 auto [ view, ct ] = MakeBitmapProxyView(recorder, bitmap, nullptr, Mipmapped::kNo,
105 Budgeted::kYes, label.empty() ? key.tag() : label);
106 if (view) {
107 // Since if the bitmap is held by more than just this function call (e.g. it likely came
108 // from findOrCreateCachedProxy() that takes an existing SkBitmap), it's worth adding a
109 // listener to remove them from the cache automatically when no one holds on to it anymore.
110 // Skip adding a listener for immutable bitmaps since those should never be invalidated.
111 const bool addListener = !bitmap.isImmutable() && !bitmap.pixelRef()->unique();
112 if (addListener) {
113 auto listener = make_unique_key_invalidation_listener(key, recorder->priv().uniqueID());
114 bitmap.pixelRef()->addGenIDChangeListener(std::move(listener));
115 }
116 fCache.set(key, view.refProxy());
117 }
118 return view.refProxy();
119 }
120
purgeAll()121 void ProxyCache::purgeAll() {
122 fCache.reset();
123 }
124
processInvalidKeyMsgs()125 void ProxyCache::processInvalidKeyMsgs() {
126 TArray<skgpu::UniqueKeyInvalidatedMsg_Graphite> invalidKeyMsgs;
127 fInvalidUniqueKeyInbox.poll(&invalidKeyMsgs);
128
129 if (!invalidKeyMsgs.empty()) {
130 for (int i = 0; i < invalidKeyMsgs.size(); ++i) {
131 // TODO: this should stop crbug.com/1480570 for now but more investigation needs to be
132 // done into how we're getting into the situation where an invalid key has been
133 // purged from the cache prior to processing of the invalid key messages.
134 if (fCache.find(invalidKeyMsgs[i].key())) {
135 fCache.remove(invalidKeyMsgs[i].key());
136 }
137 }
138 }
139 }
140
freeUniquelyHeld()141 void ProxyCache::freeUniquelyHeld() {
142 this->processInvalidKeyMsgs();
143
144 std::vector<skgpu::UniqueKey> toRemove;
145
146 fCache.foreach([&](const skgpu::UniqueKey& key, const sk_sp<TextureProxy>* proxy) {
147 if ((*proxy)->unique()) {
148 toRemove.push_back(key);
149 }
150 });
151
152 for (const skgpu::UniqueKey& k : toRemove) {
153 fCache.remove(k);
154 }
155 }
156
purgeProxiesNotUsedSince(const skgpu::StdSteadyClock::time_point * purgeTime)157 void ProxyCache::purgeProxiesNotUsedSince(const skgpu::StdSteadyClock::time_point* purgeTime) {
158 this->processInvalidKeyMsgs();
159
160 std::vector<skgpu::UniqueKey> toRemove;
161
162 fCache.foreach([&](const skgpu::UniqueKey& key, const sk_sp<TextureProxy>* proxy) {
163 if (Resource* resource = (*proxy)->texture();
164 resource &&
165 (!purgeTime || resource->lastAccessTime() < *purgeTime)) {
166 toRemove.push_back(key);
167 }
168 });
169
170 for (const skgpu::UniqueKey& k : toRemove) {
171 fCache.remove(k);
172 }
173 }
174
175 #if defined(GPU_TEST_UTILS)
numCached() const176 int ProxyCache::numCached() const {
177 return fCache.count();
178 }
179
find(const SkBitmap & bitmap)180 sk_sp<TextureProxy> ProxyCache::find(const SkBitmap& bitmap) {
181
182 skgpu::UniqueKey key;
183
184 make_bitmap_key(&key, bitmap);
185
186 if (sk_sp<TextureProxy>* cached = fCache.find(key)) {
187 return *cached;
188 }
189
190 return nullptr;
191 }
192
forceProcessInvalidKeyMsgs()193 void ProxyCache::forceProcessInvalidKeyMsgs() {
194 this->processInvalidKeyMsgs();
195 }
196
forceFreeUniquelyHeld()197 void ProxyCache::forceFreeUniquelyHeld() {
198 this->freeUniquelyHeld();
199 }
200
forcePurgeProxiesNotUsedSince(skgpu::StdSteadyClock::time_point purgeTime)201 void ProxyCache::forcePurgeProxiesNotUsedSince(skgpu::StdSteadyClock::time_point purgeTime) {
202 this->purgeProxiesNotUsedSince(&purgeTime);
203 }
204
205 #endif // defined(GPU_TEST_UTILS)
206
207 } // namespace skgpu::graphite
208