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