blob: 91eba8afc600b99e96c96a8438f77555966551ac [file] [log] [blame]
Robert Phillips26f3aeb2020-09-16 10:57:32 -04001/*
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 "include/core/SkCanvas.h"
9#include "include/core/SkDeferredDisplayListRecorder.h"
10#include "include/core/SkSurfaceCharacterization.h"
11#include "src/gpu/GrContextPriv.h"
12#include "src/gpu/GrProxyProvider.h"
13#include "src/gpu/GrRecordingContextPriv.h"
14#include "src/gpu/GrRenderTargetContext.h"
15#include "src/gpu/GrThreadSafeUniquelyKeyedProxyViewCache.h"
16#include "tests/Test.h"
17#include "tests/TestUtils.h"
18
19static constexpr int kImageWH = 32;
20static constexpr auto kImageOrigin = kBottomLeft_GrSurfaceOrigin;
21
22static SkImageInfo default_ii(int wh) {
23 return SkImageInfo::Make(wh, wh, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
24}
25
26static void create_key(GrUniqueKey* key, int wh) {
27 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
28 GrUniqueKey::Builder builder(key, kDomain, 1);
29 builder[0] = wh;
30 builder.finish();
31};
32
33SkBitmap create_up_arrow_bitmap(int wh) {
34 SkBitmap bitmap;
35
36 bitmap.allocPixels(default_ii(wh));
37
38 SkCanvas tmp(bitmap);
39 tmp.clear(SK_ColorWHITE);
40
41 SkPaint blue;
42 blue.setColor(SK_ColorBLUE);
43 blue.setAntiAlias(true);
44
45 float halfW = wh / 2.0f;
46 float halfH = wh / 2.0f;
47 float thirdW = wh / 3.0f;
48
49 SkPath path;
50 path.moveTo(0, halfH);
51 path.lineTo(thirdW, halfH);
52 path.lineTo(thirdW, wh);
53 path.lineTo(2*thirdW, wh);
54 path.lineTo(2*thirdW, halfH);
55 path.lineTo(wh, halfH);
56 path.lineTo(halfW, 0);
57 path.close();
58
59 tmp.drawPath(path, blue);
60
61 return bitmap;
62}
63
64class TestHelper {
65public:
66 struct Stats {
67 int fCacheHits = 0;
68 int fCacheMisses = 0;
69
70 int fNumSWCreations = 0;
71 int fNumHWCreations = 0;
72 };
73
74 TestHelper(GrDirectContext* dContext) : fDContext(dContext) {
75
76 fDst = SkSurface::MakeRenderTarget(dContext, SkBudgeted::kNo, default_ii(kImageWH));
77 SkAssertResult(fDst);
78
79 SkSurfaceCharacterization characterization;
80 SkAssertResult(fDst->characterize(&characterization));
81
82 fRecorder1 = std::make_unique<SkDeferredDisplayListRecorder>(characterization);
83
84 fRecorder2 = std::make_unique<SkDeferredDisplayListRecorder>(characterization);
Robert Phillips26f3aeb2020-09-16 10:57:32 -040085 }
86
87 ~TestHelper() {
Robert Phillips187b04b2020-09-22 12:18:16 -040088 fDContext->flush();
89 fDContext->submit(true);
Robert Phillips26f3aeb2020-09-16 10:57:32 -040090 }
91
92 Stats* stats() { return &fStats; }
93
Robert Phillips752f7e12020-09-18 12:28:59 -040094 int numCacheEntries() const { return this->threadSafeViewCache()->numEntries(); }
Robert Phillips26f3aeb2020-09-16 10:57:32 -040095
96 GrDirectContext* dContext() { return fDContext; }
97
98 SkCanvas* liveCanvas() { return fDst ? fDst->getCanvas() : nullptr; }
99 SkCanvas* ddlCanvas1() { return fRecorder1 ? fRecorder1->getCanvas() : nullptr; }
Robert Phillipsf3e2b3c2020-09-18 14:07:43 -0400100 sk_sp<SkDeferredDisplayList> snap1() {
101 if (fRecorder1) {
102 sk_sp<SkDeferredDisplayList> tmp = fRecorder1->detach();
103 fRecorder1 = nullptr;
104 return tmp;
105 }
106
107 return nullptr;
108 }
Robert Phillips26f3aeb2020-09-16 10:57:32 -0400109 SkCanvas* ddlCanvas2() { return fRecorder2 ? fRecorder2->getCanvas() : nullptr; }
Robert Phillipsf3e2b3c2020-09-18 14:07:43 -0400110 sk_sp<SkDeferredDisplayList> snap2() {
111 if (fRecorder2) {
112 sk_sp<SkDeferredDisplayList> tmp = fRecorder2->detach();
113 fRecorder2 = nullptr;
114 return tmp;
115 }
116
117 return nullptr;
118 }
Robert Phillips26f3aeb2020-09-16 10:57:32 -0400119
120 GrThreadSafeUniquelyKeyedProxyViewCache* threadSafeViewCache() {
Robert Phillips752f7e12020-09-18 12:28:59 -0400121 return fDContext->priv().threadSafeViewCache();
122 }
123
124 const GrThreadSafeUniquelyKeyedProxyViewCache* threadSafeViewCache() const {
125 return fDContext->priv().threadSafeViewCache();
Robert Phillips26f3aeb2020-09-16 10:57:32 -0400126 }
127
128 // Add a draw on 'canvas' that will introduce a ref on the 'wh' view
129 void accessCachedView(SkCanvas* canvas,
130 int wh,
131 bool failLookup = false) {
132 GrRecordingContext* rContext = canvas->recordingContext();
133
134 auto view = AccessCachedView(rContext, this->threadSafeViewCache(),
Robert Phillips187b04b2020-09-22 12:18:16 -0400135 wh, failLookup, &fStats);
Robert Phillips26f3aeb2020-09-16 10:57:32 -0400136 SkASSERT(view);
137
138 auto rtc = canvas->internal_private_accessTopLayerRenderTargetContext();
139
140 rtc->drawTexture(nullptr,
141 view,
142 kPremul_SkAlphaType,
143 GrSamplerState::Filter::kNearest,
144 GrSamplerState::MipmapMode::kNone,
145 SkBlendMode::kSrcOver,
146 SkPMColor4f(),
147 SkRect::MakeWH(wh, wh),
148 SkRect::MakeWH(wh, wh),
149 GrAA::kNo,
150 GrQuadAAFlags::kNone,
151 SkCanvas::kFast_SrcRectConstraint,
152 SkMatrix::I(),
153 nullptr);
154 }
155
Robert Phillipsf3e2b3c2020-09-18 14:07:43 -0400156 // Besides checking that the number of refs and cache hits and misses are as expected, this
157 // method also validates that the unique key doesn't appear in any of the other caches.
Robert Phillips26f3aeb2020-09-16 10:57:32 -0400158 bool checkView(SkCanvas* canvas, int wh, int hits, int misses, int numRefs) {
159 if (fStats.fCacheHits != hits || fStats.fCacheMisses != misses) {
Robert Phillips187b04b2020-09-22 12:18:16 -0400160 SkDebugf("Hits E: %d A: %d --- Misses E: %d A: %d\n",
161 hits, fStats.fCacheHits, misses, fStats.fCacheMisses);
Robert Phillips26f3aeb2020-09-16 10:57:32 -0400162 return false;
163 }
164
165 GrUniqueKey key;
166 create_key(&key, wh);
167
Robert Phillips26f3aeb2020-09-16 10:57:32 -0400168 auto threadSafeViewCache = this->threadSafeViewCache();
169
170 GrSurfaceProxyView view = threadSafeViewCache->find(key);
Robert Phillips187b04b2020-09-22 12:18:16 -0400171 if (!view.proxy()) {
172 return false;
173 }
Robert Phillips26f3aeb2020-09-16 10:57:32 -0400174
175 if (!view.proxy()->refCntGreaterThan(numRefs+1) || // +1 for 'view's ref
176 view.proxy()->refCntGreaterThan(numRefs+2)) {
177 return false;
178 }
179
Robert Phillipsf3e2b3c2020-09-18 14:07:43 -0400180 if (canvas) {
181 GrRecordingContext* rContext = canvas->recordingContext();
Robert Phillips26f3aeb2020-09-16 10:57:32 -0400182 GrProxyProvider* recordingProxyProvider = rContext->priv().proxyProvider();
183 sk_sp<GrTextureProxy> result = recordingProxyProvider->findProxyByUniqueKey(key);
184 if (result) {
185 // views in this cache should never appear in the recorder's cache
186 return false;
187 }
188 }
189
190 {
191 GrProxyProvider* directProxyProvider = fDContext->priv().proxyProvider();
192 sk_sp<GrTextureProxy> result = directProxyProvider->findProxyByUniqueKey(key);
193 if (result) {
194 // views in this cache should never appear in the main proxy cache
195 return false;
196 }
197 }
198
199 {
200 auto resourceProvider = fDContext->priv().resourceProvider();
201 sk_sp<GrSurface> surf = resourceProvider->findByUniqueKey<GrSurface>(key);
202 if (surf) {
203 // the textures backing the views in this cache should never be discoverable in the
204 // resource cache
205 return false;
206 }
207 }
208
209 return true;
210 }
211
Robert Phillips187b04b2020-09-22 12:18:16 -0400212 size_t gpuSize(int wh) const {
213 GrBackendFormat format = fDContext->defaultBackendFormat(kRGBA_8888_SkColorType,
214 GrRenderable::kNo);
215
216 return GrSurface::ComputeSize(*fDContext->priv().caps(), format,
217 {wh, wh}, 1, GrMipMapped::kNo, false);
218 }
219
Robert Phillips26f3aeb2020-09-16 10:57:32 -0400220private:
221 static GrSurfaceProxyView AccessCachedView(GrRecordingContext*,
222 GrThreadSafeUniquelyKeyedProxyViewCache*,
Robert Phillips187b04b2020-09-22 12:18:16 -0400223 int wh,
Robert Phillips26f3aeb2020-09-16 10:57:32 -0400224 bool failLookup, Stats*);
225 static GrSurfaceProxyView CreateViewOnCpu(GrRecordingContext*, int wh, Stats*);
Robert Phillips187b04b2020-09-22 12:18:16 -0400226 static GrSurfaceProxyView CreateViewOnGpu(GrDirectContext*, int wh, Stats*);
Robert Phillips26f3aeb2020-09-16 10:57:32 -0400227
228 Stats fStats;
229 GrDirectContext* fDContext = nullptr;
230
Robert Phillips26f3aeb2020-09-16 10:57:32 -0400231 sk_sp<SkSurface> fDst;
232 std::unique_ptr<SkDeferredDisplayListRecorder> fRecorder1;
233 std::unique_ptr<SkDeferredDisplayListRecorder> fRecorder2;
Robert Phillips26f3aeb2020-09-16 10:57:32 -0400234};
235
236GrSurfaceProxyView TestHelper::CreateViewOnCpu(GrRecordingContext* rContext,
237 int wh,
238 Stats* stats) {
239 GrProxyProvider* proxyProvider = rContext->priv().proxyProvider();
240
241 sk_sp<GrTextureProxy> proxy = proxyProvider->createProxyFromBitmap(create_up_arrow_bitmap(wh),
242 GrMipmapped::kNo,
243 SkBackingFit::kExact,
244 SkBudgeted::kYes);
245 if (!proxy) {
246 return {};
247 }
248
249 GrSwizzle swizzle = rContext->priv().caps()->getReadSwizzle(proxy->backendFormat(),
250 GrColorType::kRGBA_8888);
251 ++stats->fNumSWCreations;
252 return {std::move(proxy), kImageOrigin, swizzle};
253}
254
255GrSurfaceProxyView TestHelper::CreateViewOnGpu(GrDirectContext* dContext,
Robert Phillips26f3aeb2020-09-16 10:57:32 -0400256 int wh,
257 Stats* stats) {
Robert Phillips26f3aeb2020-09-16 10:57:32 -0400258 GrProxyProvider* proxyProvider = dContext->priv().proxyProvider();
259
Robert Phillips187b04b2020-09-22 12:18:16 -0400260 sk_sp<GrTextureProxy> proxy = proxyProvider->createProxyFromBitmap(create_up_arrow_bitmap(wh),
261 GrMipmapped::kNo,
262 SkBackingFit::kExact,
263 SkBudgeted::kYes);
264
Robert Phillips26f3aeb2020-09-16 10:57:32 -0400265 GrSwizzle swizzle = dContext->priv().caps()->getReadSwizzle(proxy->backendFormat(),
266 GrColorType::kRGBA_8888);
267 ++stats->fNumHWCreations;
268 return {std::move(proxy), kImageOrigin, swizzle};
269}
270
271// TODO: this doesn't actually implement the correct behavior for the gpu-thread. It needs to
272// add a view to the cache and then queue up the calls to draw the content.
273GrSurfaceProxyView TestHelper::AccessCachedView(
274 GrRecordingContext* rContext,
275 GrThreadSafeUniquelyKeyedProxyViewCache* threadSafeViewCache,
Robert Phillips26f3aeb2020-09-16 10:57:32 -0400276 int wh,
277 bool failLookup,
278 Stats* stats) {
279 GrUniqueKey key;
280 create_key(&key, wh);
281
282 // We can "fail the lookup" to simulate a threaded race condition
283 if (auto view = threadSafeViewCache->find(key); !failLookup && view) {
284 ++stats->fCacheHits;
285 return view;
286 }
287
288 ++stats->fCacheMisses;
289
290 GrSurfaceProxyView view;
291 if (GrDirectContext* dContext = rContext->asDirectContext()) {
Robert Phillips187b04b2020-09-22 12:18:16 -0400292 view = CreateViewOnGpu(dContext, wh, stats);
Robert Phillips26f3aeb2020-09-16 10:57:32 -0400293 } else {
294 view = CreateViewOnCpu(rContext, wh, stats);
295 }
296 SkASSERT(view);
297
298 return threadSafeViewCache->add(key, view);
299}
300
301// Case 1: ensure two DDL recorders share the view
302DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeViewCache1, reporter, ctxInfo) {
303 TestHelper helper(ctxInfo.directContext());
304
305 helper.accessCachedView(helper.ddlCanvas1(), kImageWH);
Robert Phillips187b04b2020-09-22 12:18:16 -0400306 REPORTER_ASSERT(reporter, helper.checkView(helper.ddlCanvas1(), kImageWH,
307 /*hits*/ 0, /*misses*/ 1, /*refs*/ 1));
Robert Phillips26f3aeb2020-09-16 10:57:32 -0400308
309 helper.accessCachedView(helper.ddlCanvas2(), kImageWH);
Robert Phillips187b04b2020-09-22 12:18:16 -0400310 REPORTER_ASSERT(reporter, helper.checkView(helper.ddlCanvas2(), kImageWH,
311 /*hits*/ 1, /*misses*/ 1, /*refs*/ 2));
Robert Phillips26f3aeb2020-09-16 10:57:32 -0400312
313 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 1);
314 REPORTER_ASSERT(reporter, helper.stats()->fNumHWCreations == 0);
315 REPORTER_ASSERT(reporter, helper.stats()->fNumSWCreations == 1);
316}
317
318// Case 2: ensure that, if the direct context version wins, it is reused by the DDL recorders
319DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeViewCache2, reporter, ctxInfo) {
320 TestHelper helper(ctxInfo.directContext());
321
322 helper.accessCachedView(helper.liveCanvas(), kImageWH);
Robert Phillips187b04b2020-09-22 12:18:16 -0400323 REPORTER_ASSERT(reporter, helper.checkView(helper.liveCanvas(), kImageWH,
324 /*hits*/ 0, /*misses*/ 1, /*refs*/ 1));
Robert Phillips26f3aeb2020-09-16 10:57:32 -0400325
326 helper.accessCachedView(helper.ddlCanvas1(), kImageWH);
Robert Phillips187b04b2020-09-22 12:18:16 -0400327 REPORTER_ASSERT(reporter, helper.checkView(helper.ddlCanvas1(), kImageWH,
328 /*hits*/ 1, /*misses*/ 1, /*refs*/ 2));
Robert Phillips26f3aeb2020-09-16 10:57:32 -0400329
330 helper.accessCachedView(helper.ddlCanvas2(), kImageWH);
Robert Phillips187b04b2020-09-22 12:18:16 -0400331 REPORTER_ASSERT(reporter, helper.checkView(helper.ddlCanvas2(), kImageWH,
332 /*hits*/ 2, /*misses*/ 1, /*refs*/ 3));
Robert Phillips26f3aeb2020-09-16 10:57:32 -0400333
334 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 1);
335 REPORTER_ASSERT(reporter, helper.stats()->fNumHWCreations == 1);
336 REPORTER_ASSERT(reporter, helper.stats()->fNumSWCreations == 0);
337}
338
339// Case 3: ensure that, if the cpu-version wins, it is reused by the direct context
340DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeViewCache3, reporter, ctxInfo) {
341 TestHelper helper(ctxInfo.directContext());
342
343 helper.accessCachedView(helper.ddlCanvas1(), kImageWH);
Robert Phillips187b04b2020-09-22 12:18:16 -0400344 REPORTER_ASSERT(reporter, helper.checkView(helper.ddlCanvas1(), kImageWH,
345 /*hits*/ 0, /*misses*/ 1, /*refs*/ 1));
Robert Phillips26f3aeb2020-09-16 10:57:32 -0400346
347 helper.accessCachedView(helper.liveCanvas(), kImageWH);
Robert Phillips187b04b2020-09-22 12:18:16 -0400348 REPORTER_ASSERT(reporter, helper.checkView(helper.liveCanvas(), kImageWH,
349 /*hits*/ 1, /*misses*/ 1, /*refs*/ 2));
Robert Phillips26f3aeb2020-09-16 10:57:32 -0400350
351 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 1);
352 REPORTER_ASSERT(reporter, helper.stats()->fNumHWCreations == 0);
353 REPORTER_ASSERT(reporter, helper.stats()->fNumSWCreations == 1);
354}
355
356// Case 4: ensure that, if two DDL recorders get in a race, they still end up sharing a single view
357DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeViewCache4, reporter, ctxInfo) {
358 TestHelper helper(ctxInfo.directContext());
359
360 helper.accessCachedView(helper.ddlCanvas1(), kImageWH);
Robert Phillips187b04b2020-09-22 12:18:16 -0400361 REPORTER_ASSERT(reporter, helper.checkView(helper.ddlCanvas1(), kImageWH,
362 /*hits*/ 0, /*misses*/ 1, /*refs*/ 1));
Robert Phillips26f3aeb2020-09-16 10:57:32 -0400363
364 static const bool kFailLookup = true;
365 helper.accessCachedView(helper.ddlCanvas2(), kImageWH, kFailLookup);
Robert Phillips187b04b2020-09-22 12:18:16 -0400366 REPORTER_ASSERT(reporter, helper.checkView(helper.ddlCanvas2(), kImageWH,
367 /*hits*/ 0, /*misses*/ 2, /*refs*/ 2));
Robert Phillips26f3aeb2020-09-16 10:57:32 -0400368
369 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 1);
370 REPORTER_ASSERT(reporter, helper.stats()->fNumHWCreations == 0);
371 REPORTER_ASSERT(reporter, helper.stats()->fNumSWCreations == 2);
372}
Robert Phillips752f7e12020-09-18 12:28:59 -0400373
374// Case 5: ensure that expanding the map works
375DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeViewCache5, reporter, ctxInfo) {
376 TestHelper helper(ctxInfo.directContext());
377
378 auto threadSafeViewCache = helper.threadSafeViewCache();
379
380 int size = 16;
381 helper.accessCachedView(helper.ddlCanvas1(), size);
382
Robert Phillipsc61c8952020-09-22 14:24:43 -0400383 size_t initialSize = threadSafeViewCache->approxBytesUsedForHash();
Robert Phillips752f7e12020-09-18 12:28:59 -0400384
Robert Phillipsc61c8952020-09-22 14:24:43 -0400385 while (initialSize == threadSafeViewCache->approxBytesUsedForHash()) {
Robert Phillips752f7e12020-09-18 12:28:59 -0400386 size *= 2;
387 helper.accessCachedView(helper.ddlCanvas1(), size);
388 }
389}
390
Robert Phillipsf3e2b3c2020-09-18 14:07:43 -0400391// Case 6: check on dropping refs
392DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeViewCache6, reporter, ctxInfo) {
393 TestHelper helper(ctxInfo.directContext());
394
395 helper.accessCachedView(helper.ddlCanvas1(), kImageWH);
396 sk_sp<SkDeferredDisplayList> ddl1 = helper.snap1();
Robert Phillips187b04b2020-09-22 12:18:16 -0400397 REPORTER_ASSERT(reporter, helper.checkView(nullptr, kImageWH,
398 /*hits*/ 0, /*misses*/ 1, /*refs*/ 1));
Robert Phillipsf3e2b3c2020-09-18 14:07:43 -0400399
400 helper.accessCachedView(helper.ddlCanvas2(), kImageWH);
401 sk_sp<SkDeferredDisplayList> ddl2 = helper.snap2();
Robert Phillips187b04b2020-09-22 12:18:16 -0400402 REPORTER_ASSERT(reporter, helper.checkView(nullptr, kImageWH,
403 /*hits*/ 1, /*misses*/ 1, /*refs*/ 2));
Robert Phillipsf3e2b3c2020-09-18 14:07:43 -0400404
405 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 1);
406
407 ddl1 = nullptr;
Robert Phillips187b04b2020-09-22 12:18:16 -0400408 REPORTER_ASSERT(reporter, helper.checkView(nullptr, kImageWH,
409 /*hits*/ 1, /*misses*/ 1, /*refs*/ 1));
Robert Phillipsf3e2b3c2020-09-18 14:07:43 -0400410
411 ddl2 = nullptr;
Robert Phillips187b04b2020-09-22 12:18:16 -0400412 REPORTER_ASSERT(reporter, helper.checkView(nullptr, kImageWH,
413 /*hits*/ 1, /*misses*/ 1, /*refs*/ 0));
Robert Phillipsf3e2b3c2020-09-18 14:07:43 -0400414
415 // The cache still has its ref
416 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 1);
417
Robert Phillips187b04b2020-09-22 12:18:16 -0400418 REPORTER_ASSERT(reporter, helper.checkView(nullptr, kImageWH,
419 /*hits*/ 1, /*misses*/ 1, /*refs*/ 0));
Robert Phillipsf3e2b3c2020-09-18 14:07:43 -0400420}
Robert Phillips45593682020-09-18 16:16:33 -0400421
Robert Phillips187b04b2020-09-22 12:18:16 -0400422// Case 7: check that invoking dropAllRefs and dropAllUniqueRefs directly works as expected
Robert Phillips45593682020-09-18 16:16:33 -0400423DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeViewCache7, reporter, ctxInfo) {
424 TestHelper helper(ctxInfo.directContext());
425
426 helper.accessCachedView(helper.ddlCanvas1(), kImageWH);
427 sk_sp<SkDeferredDisplayList> ddl1 = helper.snap1();
Robert Phillips187b04b2020-09-22 12:18:16 -0400428 REPORTER_ASSERT(reporter, helper.checkView(nullptr, kImageWH,
429 /*hits*/ 0, /*misses*/ 1, /*refs*/ 1));
Robert Phillips45593682020-09-18 16:16:33 -0400430
431 helper.accessCachedView(helper.ddlCanvas2(), 2*kImageWH);
432 sk_sp<SkDeferredDisplayList> ddl2 = helper.snap2();
Robert Phillips187b04b2020-09-22 12:18:16 -0400433 REPORTER_ASSERT(reporter, helper.checkView(nullptr, 2*kImageWH,
434 /*hits*/ 0, /*misses*/ 2, /*refs*/ 1));
Robert Phillips45593682020-09-18 16:16:33 -0400435
436 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 2);
437
Robert Phillips187b04b2020-09-22 12:18:16 -0400438 helper.threadSafeViewCache()->dropAllUniqueRefs(nullptr);
Robert Phillips45593682020-09-18 16:16:33 -0400439 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 2);
440
441 ddl1 = nullptr;
442
Robert Phillips187b04b2020-09-22 12:18:16 -0400443 helper.threadSafeViewCache()->dropAllUniqueRefs(nullptr);
Robert Phillips45593682020-09-18 16:16:33 -0400444 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 1);
Robert Phillips187b04b2020-09-22 12:18:16 -0400445 REPORTER_ASSERT(reporter, helper.checkView(nullptr, 2*kImageWH,
446 /*hits*/ 0, /*misses*/ 2, /*refs*/ 1));
Robert Phillips45593682020-09-18 16:16:33 -0400447
448 helper.threadSafeViewCache()->dropAllRefs();
449 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 0);
450
451 ddl2 = nullptr;
452}
Robert Phillips187b04b2020-09-22 12:18:16 -0400453
454// Case 8: This checks that GrContext::abandonContext works as expected wrt the thread
455// safe cache. This simulates the case where we have one DDL that has finished
456// recording but one still recording when the abandonContext fires.
457DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeViewCache8, reporter, ctxInfo) {
458 TestHelper helper(ctxInfo.directContext());
459
460 helper.accessCachedView(helper.liveCanvas(), kImageWH);
461 REPORTER_ASSERT(reporter, helper.checkView(helper.liveCanvas(), kImageWH,
462 /*hits*/ 0, /*misses*/ 1, /*refs*/ 1));
463
464 helper.accessCachedView(helper.ddlCanvas1(), kImageWH);
465 sk_sp<SkDeferredDisplayList> ddl1 = helper.snap1();
466 REPORTER_ASSERT(reporter, helper.checkView(helper.ddlCanvas1(), kImageWH,
467 /*hits*/ 1, /*misses*/ 1, /*refs*/ 2));
468
469 helper.accessCachedView(helper.ddlCanvas2(), kImageWH);
470 REPORTER_ASSERT(reporter, helper.checkView(helper.ddlCanvas2(), kImageWH,
471 /*hits*/ 2, /*misses*/ 1, /*refs*/ 3));
472
473 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 1);
474 REPORTER_ASSERT(reporter, helper.stats()->fNumHWCreations == 1);
475 REPORTER_ASSERT(reporter, helper.stats()->fNumSWCreations == 0);
476
477 ctxInfo.directContext()->abandonContext(); // This should exercise dropAllRefs
478
479 sk_sp<SkDeferredDisplayList> ddl2 = helper.snap2();
480
481 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 0);
482
483 ddl1 = nullptr;
484 ddl2 = nullptr;
485}
486
487// Case 9: This checks that GrContext::releaseResourcesAndAbandonContext works as expected wrt
488// the thread safe cache. This simulates the case where we have one DDL that has finished
489// recording but one still recording when the releaseResourcesAndAbandonContext fires.
490DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeViewCache9, reporter, ctxInfo) {
491 TestHelper helper(ctxInfo.directContext());
492
493 helper.accessCachedView(helper.liveCanvas(), kImageWH);
494 REPORTER_ASSERT(reporter, helper.checkView(helper.liveCanvas(), kImageWH,
495 /*hits*/ 0, /*misses*/ 1, /*refs*/ 1));
496
497 helper.accessCachedView(helper.ddlCanvas1(), kImageWH);
498 sk_sp<SkDeferredDisplayList> ddl1 = helper.snap1();
499 REPORTER_ASSERT(reporter, helper.checkView(helper.ddlCanvas1(), kImageWH,
500 /*hits*/ 1, /*misses*/ 1, /*refs*/ 2));
501
502 helper.accessCachedView(helper.ddlCanvas2(), kImageWH);
503 REPORTER_ASSERT(reporter, helper.checkView(helper.ddlCanvas2(), kImageWH,
504 /*hits*/ 2, /*misses*/ 1, /*refs*/ 3));
505
506 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 1);
507 REPORTER_ASSERT(reporter, helper.stats()->fNumHWCreations == 1);
508 REPORTER_ASSERT(reporter, helper.stats()->fNumSWCreations == 0);
509
510 ctxInfo.directContext()->releaseResourcesAndAbandonContext(); // This should hit dropAllRefs
511
512 sk_sp<SkDeferredDisplayList> ddl2 = helper.snap2();
513
514 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 0);
515
516 ddl1 = nullptr;
517 ddl2 = nullptr;
518}
519
520// Case 10: This checks that the GrContext::purgeUnlockedResources(size_t) variant works as
521// expected wrt the thread safe cache. It, in particular, tests out the MRU behavior
522// of the shared cache.
523DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeViewCache10, reporter, ctxInfo) {
524 auto dContext = ctxInfo.directContext();
525
526 if (GrBackendApi::kOpenGL != dContext->backend()) {
527 // The lower-level backends have too much going on for the following simple purging
528 // test to work
529 return;
530 }
531
532 TestHelper helper(dContext);
533
534 helper.accessCachedView(helper.liveCanvas(), kImageWH);
535 REPORTER_ASSERT(reporter, helper.checkView(helper.liveCanvas(), kImageWH,
536 /*hits*/ 0, /*misses*/ 1, /*refs*/ 1));
537
538 helper.accessCachedView(helper.ddlCanvas1(), kImageWH);
539 sk_sp<SkDeferredDisplayList> ddl1 = helper.snap1();
540 REPORTER_ASSERT(reporter, helper.checkView(helper.ddlCanvas1(), kImageWH,
541 /*hits*/ 1, /*misses*/ 1, /*refs*/ 2));
542
543 helper.accessCachedView(helper.liveCanvas(), 2*kImageWH);
544 REPORTER_ASSERT(reporter, helper.checkView(helper.liveCanvas(), 2*kImageWH,
545 /*hits*/ 1, /*misses*/ 2, /*refs*/ 1));
546
547 helper.accessCachedView(helper.ddlCanvas2(), 2*kImageWH);
548 sk_sp<SkDeferredDisplayList> ddl2 = helper.snap2();
549 REPORTER_ASSERT(reporter, helper.checkView(helper.ddlCanvas2(), 2*kImageWH,
550 /*hits*/ 2, /*misses*/ 2, /*refs*/ 2));
551
552 dContext->flush();
553 dContext->submit(true);
554
555 // This should clear out everything but the textures locked in the thread-safe cache
556 dContext->purgeUnlockedResources(false);
557
558 ddl1 = nullptr;
559 ddl2 = nullptr;
560
561 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 2);
562 REPORTER_ASSERT(reporter, helper.checkView(helper.liveCanvas(), kImageWH,
563 /*hits*/ 2, /*misses*/ 2, /*refs*/ 0));
564 REPORTER_ASSERT(reporter, helper.checkView(helper.liveCanvas(), 2*kImageWH,
565 /*hits*/ 2, /*misses*/ 2, /*refs*/ 0));
566
567 // Regardless of which image is MRU, this should force the other out
568 size_t desiredBytes = helper.gpuSize(2*kImageWH) + helper.gpuSize(kImageWH)/2;
569
570 auto cache = dContext->priv().getResourceCache();
571 size_t currentBytes = cache->getResourceBytes();
572
573 SkASSERT(currentBytes >= desiredBytes);
574 size_t amountToPurge = currentBytes - desiredBytes;
575
576 // The 2*kImageWH texture should be MRU.
577 dContext->purgeUnlockedResources(amountToPurge, true);
578
579 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 1);
580
581 REPORTER_ASSERT(reporter, helper.checkView(helper.liveCanvas(), 2*kImageWH,
582 /*hits*/ 2, /*misses*/ 2, /*refs*/ 0));
583}