1 /*
2 * Copyright 2020 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 "src/gpu/GrThreadSafeCache.h"
9
10 #include "include/gpu/GrDirectContext.h"
11 #include "src/gpu/GrDirectContextPriv.h"
12 #include "src/gpu/GrGpuBuffer.h"
13 #include "src/gpu/GrProxyProvider.h"
14 #include "src/gpu/GrResourceCache.h"
15
~VertexData()16 GrThreadSafeCache::VertexData::~VertexData () {
17 this->reset();
18 }
19
GrThreadSafeCache()20 GrThreadSafeCache::GrThreadSafeCache()
21 : fFreeEntryList(nullptr) {
22 }
23
~GrThreadSafeCache()24 GrThreadSafeCache::~GrThreadSafeCache() {
25 this->dropAllRefs();
26 }
27
28 #if GR_TEST_UTILS
numEntries() const29 int GrThreadSafeCache::numEntries() const {
30 SkAutoSpinlock lock{fSpinLock};
31
32 return fUniquelyKeyedEntryMap.count();
33 }
34
approxBytesUsedForHash() const35 size_t GrThreadSafeCache::approxBytesUsedForHash() const {
36 SkAutoSpinlock lock{fSpinLock};
37
38 return fUniquelyKeyedEntryMap.approxBytesUsed();
39 }
40 #endif
41
dropAllRefs()42 void GrThreadSafeCache::dropAllRefs() {
43 SkAutoSpinlock lock{fSpinLock};
44
45 fUniquelyKeyedEntryMap.reset();
46 while (auto tmp = fUniquelyKeyedEntryList.head()) {
47 fUniquelyKeyedEntryList.remove(tmp);
48 this->recycleEntry(tmp);
49 }
50 // TODO: should we empty out the fFreeEntryList and reset fEntryAllocator?
51 }
52
53 // TODO: If iterating becomes too expensive switch to using something like GrIORef for the
54 // GrSurfaceProxy
dropUniqueRefs(GrResourceCache * resourceCache)55 void GrThreadSafeCache::dropUniqueRefs(GrResourceCache* resourceCache) {
56 SkAutoSpinlock lock{fSpinLock};
57
58 // Iterate from LRU to MRU
59 Entry* cur = fUniquelyKeyedEntryList.tail();
60 Entry* prev = cur ? cur->fPrev : nullptr;
61
62 while (cur) {
63 if (resourceCache && !resourceCache->overBudget()) {
64 return;
65 }
66
67 if (cur->uniquelyHeld()) {
68 fUniquelyKeyedEntryMap.remove(cur->key());
69 fUniquelyKeyedEntryList.remove(cur);
70 this->recycleEntry(cur);
71 }
72
73 cur = prev;
74 prev = cur ? cur->fPrev : nullptr;
75 }
76 }
77
dropUniqueRefsOlderThan(GrStdSteadyClock::time_point purgeTime)78 void GrThreadSafeCache::dropUniqueRefsOlderThan(GrStdSteadyClock::time_point purgeTime) {
79 SkAutoSpinlock lock{fSpinLock};
80
81 // Iterate from LRU to MRU
82 Entry* cur = fUniquelyKeyedEntryList.tail();
83 Entry* prev = cur ? cur->fPrev : nullptr;
84
85 while (cur) {
86 if (cur->fLastAccess >= purgeTime) {
87 // This entry and all the remaining ones in the list will be newer than 'purgeTime'
88 return;
89 }
90
91 if (cur->uniquelyHeld()) {
92 fUniquelyKeyedEntryMap.remove(cur->key());
93 fUniquelyKeyedEntryList.remove(cur);
94 this->recycleEntry(cur);
95 }
96
97 cur = prev;
98 prev = cur ? cur->fPrev : nullptr;
99 }
100 }
101
makeExistingEntryMRU(Entry * entry)102 void GrThreadSafeCache::makeExistingEntryMRU(Entry* entry) {
103 SkASSERT(fUniquelyKeyedEntryList.isInList(entry));
104
105 entry->fLastAccess = GrStdSteadyClock::now();
106 fUniquelyKeyedEntryList.remove(entry);
107 fUniquelyKeyedEntryList.addToHead(entry);
108 }
109
internalFind(const GrUniqueKey & key)110 std::tuple<GrSurfaceProxyView, sk_sp<SkData>> GrThreadSafeCache::internalFind(
111 const GrUniqueKey& key) {
112 Entry* tmp = fUniquelyKeyedEntryMap.find(key);
113 if (tmp) {
114 this->makeExistingEntryMRU(tmp);
115 return { tmp->view(), tmp->refCustomData() };
116 }
117
118 return {};
119 }
120
121 #ifdef SK_DEBUG
has(const GrUniqueKey & key)122 bool GrThreadSafeCache::has(const GrUniqueKey& key) {
123 SkAutoSpinlock lock{fSpinLock};
124
125 Entry* tmp = fUniquelyKeyedEntryMap.find(key);
126 return SkToBool(tmp);
127 }
128 #endif
129
find(const GrUniqueKey & key)130 GrSurfaceProxyView GrThreadSafeCache::find(const GrUniqueKey& key) {
131 SkAutoSpinlock lock{fSpinLock};
132
133 GrSurfaceProxyView view;
134 std::tie(view, std::ignore) = this->internalFind(key);
135 return view;
136 }
137
findWithData(const GrUniqueKey & key)138 std::tuple<GrSurfaceProxyView, sk_sp<SkData>> GrThreadSafeCache::findWithData(
139 const GrUniqueKey& key) {
140 SkAutoSpinlock lock{fSpinLock};
141
142 return this->internalFind(key);
143 }
144
getEntry(const GrUniqueKey & key,const GrSurfaceProxyView & view)145 GrThreadSafeCache::Entry* GrThreadSafeCache::getEntry(const GrUniqueKey& key,
146 const GrSurfaceProxyView& view) {
147 Entry* entry;
148
149 if (fFreeEntryList) {
150 entry = fFreeEntryList;
151 fFreeEntryList = entry->fNext;
152 entry->fNext = nullptr;
153
154 entry->set(key, view);
155 } else {
156 entry = fEntryAllocator.make<Entry>(key, view);
157 }
158
159 return this->makeNewEntryMRU(entry);
160 }
161
makeNewEntryMRU(Entry * entry)162 GrThreadSafeCache::Entry* GrThreadSafeCache::makeNewEntryMRU(Entry* entry) {
163 entry->fLastAccess = GrStdSteadyClock::now();
164 fUniquelyKeyedEntryList.addToHead(entry);
165 fUniquelyKeyedEntryMap.add(entry);
166 return entry;
167 }
168
getEntry(const GrUniqueKey & key,sk_sp<VertexData> vertData)169 GrThreadSafeCache::Entry* GrThreadSafeCache::getEntry(const GrUniqueKey& key,
170 sk_sp<VertexData> vertData) {
171 Entry* entry;
172
173 if (fFreeEntryList) {
174 entry = fFreeEntryList;
175 fFreeEntryList = entry->fNext;
176 entry->fNext = nullptr;
177
178 entry->set(key, std::move(vertData));
179 } else {
180 entry = fEntryAllocator.make<Entry>(key, std::move(vertData));
181 }
182
183 return this->makeNewEntryMRU(entry);
184 }
185
recycleEntry(Entry * dead)186 void GrThreadSafeCache::recycleEntry(Entry* dead) {
187 SkASSERT(!dead->fPrev && !dead->fNext && !dead->fList);
188
189 dead->makeEmpty();
190
191 dead->fNext = fFreeEntryList;
192 fFreeEntryList = dead;
193 }
194
internalAdd(const GrUniqueKey & key,const GrSurfaceProxyView & view)195 std::tuple<GrSurfaceProxyView, sk_sp<SkData>> GrThreadSafeCache::internalAdd(
196 const GrUniqueKey& key,
197 const GrSurfaceProxyView& view) {
198 Entry* tmp = fUniquelyKeyedEntryMap.find(key);
199 if (!tmp) {
200 tmp = this->getEntry(key, view);
201
202 SkASSERT(fUniquelyKeyedEntryMap.find(key));
203 }
204
205 return { tmp->view(), tmp->refCustomData() };
206 }
207
add(const GrUniqueKey & key,const GrSurfaceProxyView & view)208 GrSurfaceProxyView GrThreadSafeCache::add(const GrUniqueKey& key, const GrSurfaceProxyView& view) {
209 SkAutoSpinlock lock{fSpinLock};
210
211 GrSurfaceProxyView newView;
212 std::tie(newView, std::ignore) = this->internalAdd(key, view);
213 return newView;
214 }
215
addWithData(const GrUniqueKey & key,const GrSurfaceProxyView & view)216 std::tuple<GrSurfaceProxyView, sk_sp<SkData>> GrThreadSafeCache::addWithData(
217 const GrUniqueKey& key,
218 const GrSurfaceProxyView& view) {
219 SkAutoSpinlock lock{fSpinLock};
220
221 return this->internalAdd(key, view);
222 }
223
findOrAdd(const GrUniqueKey & key,const GrSurfaceProxyView & v)224 GrSurfaceProxyView GrThreadSafeCache::findOrAdd(const GrUniqueKey& key,
225 const GrSurfaceProxyView& v) {
226 SkAutoSpinlock lock{fSpinLock};
227
228 GrSurfaceProxyView view;
229 std::tie(view, std::ignore) = this->internalFind(key);
230 if (view) {
231 return view;
232 }
233
234 std::tie(view, std::ignore) = this->internalAdd(key, v);
235 return view;
236 }
237
findOrAddWithData(const GrUniqueKey & key,const GrSurfaceProxyView & v)238 std::tuple<GrSurfaceProxyView, sk_sp<SkData>> GrThreadSafeCache::findOrAddWithData(
239 const GrUniqueKey& key,
240 const GrSurfaceProxyView& v) {
241 SkAutoSpinlock lock{fSpinLock};
242
243 auto [view, data] = this->internalFind(key);
244 if (view) {
245 return { std::move(view), std::move(data) };
246 }
247
248 return this->internalAdd(key, v);
249 }
250
MakeVertexData(const void * vertices,int vertexCount,size_t vertexSize)251 sk_sp<GrThreadSafeCache::VertexData> GrThreadSafeCache::MakeVertexData(const void* vertices,
252 int vertexCount,
253 size_t vertexSize) {
254 return sk_sp<VertexData>(new VertexData(vertices, vertexCount, vertexSize));
255 }
256
MakeVertexData(sk_sp<GrGpuBuffer> buffer,int vertexCount,size_t vertexSize)257 sk_sp<GrThreadSafeCache::VertexData> GrThreadSafeCache::MakeVertexData(sk_sp<GrGpuBuffer> buffer,
258 int vertexCount,
259 size_t vertexSize) {
260 return sk_sp<VertexData>(new VertexData(std::move(buffer), vertexCount, vertexSize));
261 }
262
internalFindVerts(const GrUniqueKey & key)263 std::tuple<sk_sp<GrThreadSafeCache::VertexData>, sk_sp<SkData>> GrThreadSafeCache::internalFindVerts(
264 const GrUniqueKey& key) {
265 Entry* tmp = fUniquelyKeyedEntryMap.find(key);
266 if (tmp) {
267 this->makeExistingEntryMRU(tmp);
268 return { tmp->vertexData(), tmp->refCustomData() };
269 }
270
271 return {};
272 }
273
findVertsWithData(const GrUniqueKey & key)274 std::tuple<sk_sp<GrThreadSafeCache::VertexData>, sk_sp<SkData>> GrThreadSafeCache::findVertsWithData(
275 const GrUniqueKey& key) {
276 SkAutoSpinlock lock{fSpinLock};
277
278 return this->internalFindVerts(key);
279 }
280
internalAddVerts(const GrUniqueKey & key,sk_sp<VertexData> vertData,IsNewerBetter isNewerBetter)281 std::tuple<sk_sp<GrThreadSafeCache::VertexData>, sk_sp<SkData>> GrThreadSafeCache::internalAddVerts(
282 const GrUniqueKey& key,
283 sk_sp<VertexData> vertData,
284 IsNewerBetter isNewerBetter) {
285 Entry* tmp = fUniquelyKeyedEntryMap.find(key);
286 if (!tmp) {
287 tmp = this->getEntry(key, std::move(vertData));
288
289 SkASSERT(fUniquelyKeyedEntryMap.find(key));
290 } else if (isNewerBetter(tmp->getCustomData(), key.getCustomData())) {
291 // This orphans any existing uses of the prior vertex data but ensures the best
292 // version is in the cache.
293 tmp->set(key, std::move(vertData));
294 }
295
296 return { tmp->vertexData(), tmp->refCustomData() };
297 }
298
addVertsWithData(const GrUniqueKey & key,sk_sp<VertexData> vertData,IsNewerBetter isNewerBetter)299 std::tuple<sk_sp<GrThreadSafeCache::VertexData>, sk_sp<SkData>> GrThreadSafeCache::addVertsWithData(
300 const GrUniqueKey& key,
301 sk_sp<VertexData> vertData,
302 IsNewerBetter isNewerBetter) {
303 SkAutoSpinlock lock{fSpinLock};
304
305 return this->internalAddVerts(key, std::move(vertData), isNewerBetter);
306 }
307
remove(const GrUniqueKey & key)308 void GrThreadSafeCache::remove(const GrUniqueKey& key) {
309 SkAutoSpinlock lock{fSpinLock};
310
311 Entry* tmp = fUniquelyKeyedEntryMap.find(key);
312 if (tmp) {
313 fUniquelyKeyedEntryMap.remove(key);
314 fUniquelyKeyedEntryList.remove(tmp);
315 this->recycleEntry(tmp);
316 }
317 }
318
319 std::tuple<GrSurfaceProxyView, sk_sp<GrThreadSafeCache::Trampoline>>
CreateLazyView(GrDirectContext * dContext,GrColorType origCT,SkISize dimensions,GrSurfaceOrigin origin,SkBackingFit fit)320 GrThreadSafeCache::CreateLazyView(GrDirectContext* dContext,
321 GrColorType origCT,
322 SkISize dimensions,
323 GrSurfaceOrigin origin,
324 SkBackingFit fit) {
325 GrProxyProvider* proxyProvider = dContext->priv().proxyProvider();
326 const GrCaps* caps = dContext->priv().caps();
327
328 constexpr int kSampleCnt = 1;
329 auto [newCT, format] = caps->getFallbackColorTypeAndFormat(origCT, kSampleCnt);
330
331 if (newCT == GrColorType::kUnknown) {
332 return {GrSurfaceProxyView(nullptr), nullptr};
333 }
334
335 sk_sp<Trampoline> trampoline(new Trampoline);
336
337 GrProxyProvider::TextureInfo texInfo{ GrMipMapped::kNo, GrTextureType::k2D };
338
339 sk_sp<GrRenderTargetProxy> proxy = proxyProvider->createLazyRenderTargetProxy(
340 [trampoline](
341 GrResourceProvider* resourceProvider,
342 const GrSurfaceProxy::LazySurfaceDesc&) -> GrSurfaceProxy::LazyCallbackResult {
343 if (!resourceProvider || !trampoline->fProxy ||
344 !trampoline->fProxy->isInstantiated()) {
345 return GrSurfaceProxy::LazyCallbackResult(nullptr, true);
346 }
347
348 SkASSERT(!trampoline->fProxy->peekTexture()->getUniqueKey().isValid());
349 return GrSurfaceProxy::LazyCallbackResult(
350 sk_ref_sp(trampoline->fProxy->peekTexture()));
351 },
352 format,
353 dimensions,
354 kSampleCnt,
355 GrInternalSurfaceFlags::kNone,
356 &texInfo,
357 GrMipmapStatus::kNotAllocated,
358 fit,
359 SkBudgeted::kYes,
360 GrProtected::kNo,
361 /* wrapsVkSecondaryCB */ false,
362 GrSurfaceProxy::UseAllocator::kYes);
363
364 // TODO: It seems like this 'newCT' usage should be 'origCT' but this is
365 // what skgpu::v1::SurfaceDrawContext::MakeWithFallback does
366 GrSwizzle swizzle = dContext->priv().caps()->getReadSwizzle(format, newCT);
367
368 return {{std::move(proxy), origin, swizzle}, std::move(trampoline)};
369 }
370