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 "include/core/SkCanvas.h"
9 #include "include/core/SkDeferredDisplayListRecorder.h"
10 #include "include/core/SkSurfaceCharacterization.h"
11 #include "include/private/SkMalloc.h"
12 #include "include/utils/SkRandom.h"
13 #include "src/core/SkCanvasPriv.h"
14 #include "src/core/SkMessageBus.h"
15 #include "src/gpu/GrDefaultGeoProcFactory.h"
16 #include "src/gpu/GrDirectContextPriv.h"
17 #include "src/gpu/GrGpu.h"
18 #include "src/gpu/GrMemoryPool.h"
19 #include "src/gpu/GrOpFlushState.h"
20 #include "src/gpu/GrProxyProvider.h"
21 #include "src/gpu/GrRecordingContextPriv.h"
22 #include "src/gpu/GrResourceProvider.h"
23 #include "src/gpu/GrStyle.h"
24 #include "src/gpu/GrThreadSafeCache.h"
25 #include "src/gpu/ops/GrDrawOp.h"
26 #include "src/gpu/v1/SurfaceDrawContext_v1.h"
27 #include "tests/Test.h"
28 #include "tests/TestUtils.h"
29 #include "tools/gpu/ProxyUtils.h"
30
31 #include <thread>
32
33 static constexpr int kImageWH = 32;
34 static constexpr auto kImageOrigin = kBottomLeft_GrSurfaceOrigin;
35 static constexpr int kNoID = -1;
36
default_ii(int wh)37 static SkImageInfo default_ii(int wh) {
38 return SkImageInfo::Make(wh, wh, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
39 }
40
new_SDC(GrRecordingContext * rContext,int wh)41 static std::unique_ptr<skgpu::v1::SurfaceDrawContext> new_SDC(GrRecordingContext* rContext,
42 int wh) {
43 return skgpu::v1::SurfaceDrawContext::Make(rContext,
44 GrColorType::kRGBA_8888,
45 nullptr,
46 SkBackingFit::kExact,
47 {wh, wh},
48 SkSurfaceProps(),
49 1,
50 GrMipMapped::kNo,
51 GrProtected::kNo,
52 kImageOrigin,
53 SkBudgeted::kYes);
54 }
55
create_view_key(GrUniqueKey * key,int wh,int id)56 static void create_view_key(GrUniqueKey* key, int wh, int id) {
57 static const GrUniqueKey::Domain kViewDomain = GrUniqueKey::GenerateDomain();
58 GrUniqueKey::Builder builder(key, kViewDomain, 1);
59 builder[0] = wh;
60 builder.finish();
61
62 if (id != kNoID) {
63 key->setCustomData(SkData::MakeWithCopy(&id, sizeof(id)));
64 }
65 }
66
create_vert_key(GrUniqueKey * key,int wh,int id)67 static void create_vert_key(GrUniqueKey* key, int wh, int id) {
68 static const GrUniqueKey::Domain kVertDomain = GrUniqueKey::GenerateDomain();
69 GrUniqueKey::Builder builder(key, kVertDomain, 1);
70 builder[0] = wh;
71 builder.finish();
72
73 if (id != kNoID) {
74 key->setCustomData(SkData::MakeWithCopy(&id, sizeof(id)));
75 }
76 }
77
default_is_newer_better(SkData * incumbent,SkData * challenger)78 static bool default_is_newer_better(SkData* incumbent, SkData* challenger) {
79 return false;
80 }
81
82 // When testing views we create a bitmap that covers the entire screen and has an inset blue rect
83 // atop a field of white.
84 // When testing verts we clear the background to white and simply draw an inset blur rect.
create_bitmap(int wh)85 static SkBitmap create_bitmap(int wh) {
86 SkBitmap bitmap;
87
88 bitmap.allocPixels(default_ii(wh));
89
90 SkCanvas tmp(bitmap);
91 tmp.clear(SK_ColorWHITE);
92
93 SkPaint blue;
94 blue.setColor(SK_ColorBLUE);
95 blue.setAntiAlias(false);
96
97 tmp.drawRect({10, 10, wh-10.0f, wh-10.0f}, blue);
98
99 bitmap.setImmutable();
100 return bitmap;
101 }
102
103 class GrThreadSafeVertexTestOp;
104
105 class TestHelper {
106 public:
107 struct Stats {
108 int fCacheHits = 0;
109 int fCacheMisses = 0;
110
111 int fNumSWCreations = 0;
112 int fNumLazyCreations = 0;
113 int fNumHWCreations = 0;
114 };
115
TestHelper(GrDirectContext * dContext,GrThreadSafeCache::IsNewerBetter isNewerBetter=default_is_newer_better)116 TestHelper(GrDirectContext* dContext,
117 GrThreadSafeCache::IsNewerBetter isNewerBetter = default_is_newer_better)
118 : fDContext(dContext)
119 , fIsNewerBetter(isNewerBetter) {
120
121 fDst = SkSurface::MakeRenderTarget(dContext, SkBudgeted::kNo, default_ii(kImageWH));
122 SkAssertResult(fDst);
123
124 SkSurfaceCharacterization characterization;
125 SkAssertResult(fDst->characterize(&characterization));
126
127 fRecorder1 = std::make_unique<SkDeferredDisplayListRecorder>(characterization);
128 this->ddlCanvas1()->clear(SkColors::kWhite);
129
130 fRecorder2 = std::make_unique<SkDeferredDisplayListRecorder>(characterization);
131 this->ddlCanvas2()->clear(SkColors::kWhite);
132
133 fDst->getCanvas()->clear(SkColors::kWhite);
134 }
135
~TestHelper()136 ~TestHelper() {
137 fDContext->flush();
138 fDContext->submit(true);
139 }
140
stats()141 Stats* stats() { return &fStats; }
142
numCacheEntries() const143 int numCacheEntries() const { return this->threadSafeCache()->numEntries(); }
144
dContext()145 GrDirectContext* dContext() { return fDContext; }
146
liveCanvas()147 SkCanvas* liveCanvas() { return fDst ? fDst->getCanvas() : nullptr; }
ddlCanvas1()148 SkCanvas* ddlCanvas1() { return fRecorder1 ? fRecorder1->getCanvas() : nullptr; }
snap1()149 sk_sp<SkDeferredDisplayList> snap1() {
150 if (fRecorder1) {
151 sk_sp<SkDeferredDisplayList> tmp = fRecorder1->detach();
152 fRecorder1 = nullptr;
153 return tmp;
154 }
155
156 return nullptr;
157 }
ddlCanvas2()158 SkCanvas* ddlCanvas2() { return fRecorder2 ? fRecorder2->getCanvas() : nullptr; }
snap2()159 sk_sp<SkDeferredDisplayList> snap2() {
160 if (fRecorder2) {
161 sk_sp<SkDeferredDisplayList> tmp = fRecorder2->detach();
162 fRecorder2 = nullptr;
163 return tmp;
164 }
165
166 return nullptr;
167 }
168
threadSafeCache()169 GrThreadSafeCache* threadSafeCache() { return fDContext->priv().threadSafeCache(); }
threadSafeCache() const170 const GrThreadSafeCache* threadSafeCache() const { return fDContext->priv().threadSafeCache(); }
171
172 typedef void (TestHelper::*addAccessFP)(SkCanvas*, int wh, int id,
173 bool failLookUp, bool failFillingIn);
174 typedef bool (TestHelper::*checkFP)(SkCanvas*, int wh,
175 int expectedHits, int expectedMisses,
176 int expectedNumRefs, int expectedID);
177
178 // Add a draw on 'canvas' that will introduce a ref on the 'wh' view
addViewAccess(SkCanvas * canvas,int wh,int id=kNoID,bool failLookup=false,bool failFillingIn=false)179 void addViewAccess(SkCanvas* canvas,
180 int wh,
181 int id = kNoID,
182 bool failLookup = false,
183 bool failFillingIn = false) {
184 auto rContext = canvas->recordingContext();
185
186 auto view = AccessCachedView(rContext, this->threadSafeCache(),
187 wh, failLookup, failFillingIn, id, &fStats);
188 SkASSERT(view);
189
190 auto sdc = SkCanvasPriv::TopDeviceSurfaceDrawContext(canvas);
191
192 sdc->drawTexture(nullptr,
193 view,
194 kPremul_SkAlphaType,
195 GrSamplerState::Filter::kNearest,
196 GrSamplerState::MipmapMode::kNone,
197 SkBlendMode::kSrcOver,
198 {1.0f, 1.0f, 1.0f, 1.0f},
199 SkRect::MakeWH(wh, wh),
200 SkRect::MakeWH(wh, wh),
201 GrAA::kNo,
202 GrQuadAAFlags::kNone,
203 SkCanvas::kFast_SrcRectConstraint,
204 SkMatrix::I(),
205 nullptr);
206 }
207
208 // Besides checking that the number of refs and cache hits and misses are as expected, this
209 // method also validates that the unique key doesn't appear in any of the other caches.
checkView(SkCanvas * canvas,int wh,int expectedHits,int expectedMisses,int expectedNumRefs,int expectedID)210 bool checkView(SkCanvas* canvas, int wh,
211 int expectedHits, int expectedMisses, int expectedNumRefs, int expectedID) {
212 if (fStats.fCacheHits != expectedHits || fStats.fCacheMisses != expectedMisses) {
213 SkDebugf("Hits E: %d A: %d --- Misses E: %d A: %d\n",
214 expectedHits, fStats.fCacheHits, expectedMisses, fStats.fCacheMisses);
215 return false;
216 }
217
218 GrUniqueKey key;
219 create_view_key(&key, wh, kNoID);
220
221 auto threadSafeCache = this->threadSafeCache();
222
223 auto [view, xtraData] = threadSafeCache->findWithData(key);
224 if (!view.proxy()) {
225 return false;
226 }
227
228 if (expectedID < 0) {
229 if (xtraData) {
230 return false;
231 }
232 } else {
233 if (!xtraData) {
234 return false;
235 }
236
237 const int* cachedID = static_cast<const int*>(xtraData->data());
238 if (*cachedID != expectedID) {
239 return false;
240 }
241 }
242
243 if (!view.proxy()->refCntGreaterThan(expectedNumRefs+1) || // +1 for 'view's ref
244 view.proxy()->refCntGreaterThan(expectedNumRefs+2)) {
245 return false;
246 }
247
248 if (canvas) {
249 GrRecordingContext* rContext = canvas->recordingContext();
250 GrProxyProvider* recordingProxyProvider = rContext->priv().proxyProvider();
251 sk_sp<GrTextureProxy> result = recordingProxyProvider->findProxyByUniqueKey(key);
252 if (result) {
253 // views in this cache should never appear in the recorder's cache
254 return false;
255 }
256 }
257
258 {
259 GrProxyProvider* directProxyProvider = fDContext->priv().proxyProvider();
260 sk_sp<GrTextureProxy> result = directProxyProvider->findProxyByUniqueKey(key);
261 if (result) {
262 // views in this cache should never appear in the main proxy cache
263 return false;
264 }
265 }
266
267 {
268 auto resourceProvider = fDContext->priv().resourceProvider();
269 sk_sp<GrSurface> surf = resourceProvider->findByUniqueKey<GrSurface>(key);
270 if (surf) {
271 // the textures backing the views in this cache should never be discoverable in the
272 // resource cache
273 return false;
274 }
275 }
276
277 return true;
278 }
279
280 void addVertAccess(SkCanvas* canvas,
281 int wh,
282 int id,
283 bool failLookup,
284 bool failFillingIn,
285 GrThreadSafeVertexTestOp** createdOp);
286
287 // Add a draw on 'canvas' that will introduce a ref on a 'wh' vertex data
addVertAccess(SkCanvas * canvas,int wh,int id=kNoID,bool failLookup=false,bool failFillingIn=false)288 void addVertAccess(SkCanvas* canvas,
289 int wh,
290 int id = kNoID,
291 bool failLookup = false,
292 bool failFillingIn = false) {
293 this->addVertAccess(canvas, wh, id, failLookup, failFillingIn, nullptr);
294 }
295
checkVert(SkCanvas * canvas,int wh,int expectedHits,int expectedMisses,int expectedNumRefs,int expectedID)296 bool checkVert(SkCanvas* canvas, int wh,
297 int expectedHits, int expectedMisses, int expectedNumRefs, int expectedID) {
298 if (fStats.fCacheHits != expectedHits || fStats.fCacheMisses != expectedMisses) {
299 SkDebugf("Hits E: %d A: %d --- Misses E: %d A: %d\n",
300 expectedHits, fStats.fCacheHits, expectedMisses, fStats.fCacheMisses);
301 return false;
302 }
303
304 GrUniqueKey key;
305 create_vert_key(&key, wh, kNoID);
306
307 auto threadSafeCache = this->threadSafeCache();
308
309 auto [vertData, xtraData] = threadSafeCache->findVertsWithData(key);
310 if (!vertData) {
311 return false;
312 }
313
314 if (expectedID < 0) {
315 if (xtraData) {
316 return false;
317 }
318 } else {
319 if (!xtraData) {
320 return false;
321 }
322
323 const int* cachedID = static_cast<const int*>(xtraData->data());
324 if (*cachedID != expectedID) {
325 return false;
326 }
327 }
328
329 if (!vertData->refCntGreaterThan(expectedNumRefs+1) || // +1 for 'vertData's ref
330 vertData->refCntGreaterThan(expectedNumRefs+2)) {
331 return false;
332 }
333
334 {
335 auto resourceProvider = fDContext->priv().resourceProvider();
336 sk_sp<GrGpuBuffer> buffer = resourceProvider->findByUniqueKey<GrGpuBuffer>(key);
337 if (buffer) {
338 // the buffer holding the vertex data in this cache should never be discoverable
339 // in the resource cache
340 return false;
341 }
342 }
343
344 return true;
345 }
346
checkImage(skiatest::Reporter * reporter,sk_sp<SkSurface> s)347 bool checkImage(skiatest::Reporter* reporter, sk_sp<SkSurface> s) {
348 SkBitmap actual;
349
350 actual.allocPixels(default_ii(kImageWH));
351
352 if (!s->readPixels(actual, 0, 0)) {
353 return false;
354 }
355
356 SkBitmap expected = create_bitmap(kImageWH);
357
358 const float tols[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
359
360 auto error = std::function<ComparePixmapsErrorReporter>(
361 [reporter](int x, int y, const float diffs[4]) {
362 SkASSERT(x >= 0 && y >= 0);
363 ERRORF(reporter, "mismatch at %d, %d (%f, %f, %f %f)",
364 x, y, diffs[0], diffs[1], diffs[2], diffs[3]);
365 });
366
367 return ComparePixels(expected.pixmap(), actual.pixmap(), tols, error);
368 }
369
checkImage(skiatest::Reporter * reporter)370 bool checkImage(skiatest::Reporter* reporter) {
371 return this->checkImage(reporter, fDst);
372 }
373
checkImage(skiatest::Reporter * reporter,sk_sp<SkDeferredDisplayList> ddl)374 bool checkImage(skiatest::Reporter* reporter, sk_sp<SkDeferredDisplayList> ddl) {
375 sk_sp<SkSurface> tmp = SkSurface::MakeRenderTarget(fDContext,
376 SkBudgeted::kNo,
377 default_ii(kImageWH));
378 if (!tmp) {
379 return false;
380 }
381
382 if (!tmp->draw(std::move(ddl))) {
383 return false;
384 }
385
386 return this->checkImage(reporter, std::move(tmp));
387 }
388
gpuSize(int wh) const389 size_t gpuSize(int wh) const {
390 GrBackendFormat format = fDContext->defaultBackendFormat(kRGBA_8888_SkColorType,
391 GrRenderable::kNo);
392
393 return GrSurface::ComputeSize(format, {wh, wh}, /*colorSamplesPerPixel=*/1,
394 GrMipMapped::kNo, /*binSize=*/false);
395 }
396
397 private:
398 static GrSurfaceProxyView AccessCachedView(GrRecordingContext*,
399 GrThreadSafeCache*,
400 int wh,
401 bool failLookup, bool failFillingIn, int id,
402 Stats*);
403 static GrSurfaceProxyView CreateViewOnCpu(GrRecordingContext*, int wh, Stats*);
404 static bool FillInViewOnGpu(GrDirectContext*, int wh, Stats*,
405 const GrSurfaceProxyView& lazyView,
406 sk_sp<GrThreadSafeCache::Trampoline>);
407
408 Stats fStats;
409 GrDirectContext* fDContext = nullptr;
410 GrThreadSafeCache::IsNewerBetter fIsNewerBetter;
411
412 sk_sp<SkSurface> fDst;
413 std::unique_ptr<SkDeferredDisplayListRecorder> fRecorder1;
414 std::unique_ptr<SkDeferredDisplayListRecorder> fRecorder2;
415 };
416
417 class GrThreadSafeVertexTestOp : public GrDrawOp {
418 public:
419 DEFINE_OP_CLASS_ID
420
Make(GrRecordingContext * rContext,TestHelper::Stats * stats,int wh,int id,bool failLookup,bool failFillingIn,GrThreadSafeCache::IsNewerBetter isNewerBetter)421 static GrOp::Owner Make(GrRecordingContext* rContext, TestHelper::Stats* stats,
422 int wh, int id, bool failLookup, bool failFillingIn,
423 GrThreadSafeCache::IsNewerBetter isNewerBetter) {
424
425 return GrOp::Make<GrThreadSafeVertexTestOp>(
426 rContext, rContext, stats, wh, id, failLookup, failFillingIn, isNewerBetter);
427 }
428
vertexData() const429 const GrThreadSafeCache::VertexData* vertexData() const { return fVertexData.get(); }
430
431 private:
432 friend class GrOp; // for ctor
433
GrThreadSafeVertexTestOp(GrRecordingContext * rContext,TestHelper::Stats * stats,int wh,int id,bool failLookup,bool failFillingIn,GrThreadSafeCache::IsNewerBetter isNewerBetter)434 GrThreadSafeVertexTestOp(GrRecordingContext* rContext, TestHelper::Stats* stats, int wh, int id,
435 bool failLookup, bool failFillingIn,
436 GrThreadSafeCache::IsNewerBetter isNewerBetter)
437 : INHERITED(ClassID())
438 , fStats(stats)
439 , fWH(wh)
440 , fID(id)
441 , fFailFillingIn(failFillingIn)
442 , fIsNewerBetter(isNewerBetter) {
443 this->setBounds(SkRect::MakeIWH(fWH, fWH), HasAABloat::kNo, IsHairline::kNo);
444
445 // Normally we wouldn't add a ref to the vertex data at this point. However, it is
446 // needed in this unit test to get the ref counts on the uniquely keyed resources
447 // to be as expected.
448 this->findOrCreateVertices(rContext, failLookup, fFailFillingIn);
449 }
450
name() const451 const char* name() const override { return "GrThreadSafeVertexTestOp"; }
fixedFunctionFlags() const452 FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
finalize(const GrCaps &,const GrAppliedClip *,GrClampType)453 GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*, GrClampType) override {
454 return GrProcessorSet::EmptySetAnalysis();
455 }
456
createProgramInfo(const GrCaps * caps,SkArenaAlloc * arena,const GrSurfaceProxyView & writeView,bool usesMSAASurface,GrAppliedClip && appliedClip,const GrDstProxyView & dstProxyView,GrXferBarrierFlags renderPassXferBarriers,GrLoadOp colorLoadOp) const457 GrProgramInfo* createProgramInfo(const GrCaps* caps,
458 SkArenaAlloc* arena,
459 const GrSurfaceProxyView& writeView,
460 bool usesMSAASurface,
461 GrAppliedClip&& appliedClip,
462 const GrDstProxyView& dstProxyView,
463 GrXferBarrierFlags renderPassXferBarriers,
464 GrLoadOp colorLoadOp) const {
465 using namespace GrDefaultGeoProcFactory;
466
467 Color color({ 0.0f, 0.0f, 1.0f, 1.0f });
468
469 auto gp = MakeForDeviceSpace(arena, color,
470 Coverage::kSolid_Type,
471 LocalCoords::kUnused_Type,
472 SkMatrix::I());
473
474 return sk_gpu_test::CreateProgramInfo(caps, arena, writeView, usesMSAASurface,
475 std::move(appliedClip), dstProxyView,
476 gp, SkBlendMode::kSrcOver,
477 GrPrimitiveType::kTriangleStrip,
478 renderPassXferBarriers, colorLoadOp);
479 }
480
createProgramInfo(GrOpFlushState * flushState) const481 GrProgramInfo* createProgramInfo(GrOpFlushState* flushState) const {
482 return this->createProgramInfo(&flushState->caps(),
483 flushState->allocator(),
484 flushState->writeView(),
485 flushState->usesMSAASurface(),
486 flushState->detachAppliedClip(),
487 flushState->dstProxyView(),
488 flushState->renderPassBarriers(),
489 flushState->colorLoadOp());
490 }
491
findOrCreateVertices(GrRecordingContext * rContext,bool failLookup,bool failFillingIn)492 void findOrCreateVertices(GrRecordingContext* rContext, bool failLookup, bool failFillingIn) {
493
494 if (!fVertexData) {
495 auto threadSafeViewCache = rContext->priv().threadSafeCache();
496
497 if (rContext->asDirectContext()) {
498 // The vertex variant doesn't have a correlate to lazyProxies but increment this
499 // here to make the unit tests happy.
500 ++fStats->fNumLazyCreations;
501 }
502
503 GrUniqueKey key;
504 create_vert_key(&key, fWH, fID);
505
506 // We can "fail the lookup" to simulate a threaded race condition
507 auto [cachedVerts, data] = threadSafeViewCache->findVertsWithData(key);
508 if (cachedVerts && !failLookup) {
509 fVertexData = cachedVerts;
510 ++fStats->fCacheHits;
511 return;
512 }
513
514 ++fStats->fCacheMisses;
515 if (!rContext->asDirectContext()) {
516 ++fStats->fNumSWCreations;
517 }
518
519 constexpr size_t kVertSize = sizeof(SkPoint);
520 SkPoint* verts = static_cast<SkPoint*>(sk_malloc_throw(4 * kVertSize));
521
522 verts[0].set(10.0f, 10.0f);
523 verts[1].set(fWH-10.0f, 10.0f);
524 verts[2].set(10.0f, fWH-10.0f);
525 verts[3].set(fWH-10.0f, fWH-10.0f);
526
527 fVertexData = GrThreadSafeCache::MakeVertexData(verts, 4, kVertSize);
528
529 auto [tmpV, tmpD] = threadSafeViewCache->addVertsWithData(key, fVertexData,
530 fIsNewerBetter);
531 if (tmpV != fVertexData) {
532 // Someone beat us to creating the vertex data. Use that version.
533 fVertexData = tmpV;
534 }
535 }
536
537 if (auto dContext = rContext->asDirectContext(); dContext && !fVertexData->gpuBuffer()) {
538 auto rp = dContext->priv().resourceProvider();
539
540 if (!failFillingIn) {
541 ++fStats->fNumHWCreations;
542
543 sk_sp<GrGpuBuffer> tmp = rp->createBuffer(fVertexData->size(),
544 GrGpuBufferType::kVertex,
545 kStatic_GrAccessPattern,
546 fVertexData->vertices());
547 fVertexData->setGpuBuffer(std::move(tmp));
548 }
549 }
550 }
551
onPrePrepare(GrRecordingContext * rContext,const GrSurfaceProxyView & writeView,GrAppliedClip * clip,const GrDstProxyView & dstProxyView,GrXferBarrierFlags renderPassXferBarriers,GrLoadOp colorLoadOp)552 void onPrePrepare(GrRecordingContext* rContext,
553 const GrSurfaceProxyView& writeView,
554 GrAppliedClip* clip,
555 const GrDstProxyView& dstProxyView,
556 GrXferBarrierFlags renderPassXferBarriers,
557 GrLoadOp colorLoadOp) override {
558 SkArenaAlloc* arena = rContext->priv().recordTimeAllocator();
559
560 // DMSAA is not supported on DDL.
561 bool usesMSAASurface = writeView.asRenderTargetProxy()->numSamples() > 1;
562
563 // This is equivalent to a GrOpFlushState::detachAppliedClip
564 GrAppliedClip appliedClip = clip ? std::move(*clip) : GrAppliedClip::Disabled();
565
566 fProgramInfo = this->createProgramInfo(rContext->priv().caps(), arena, writeView,
567 usesMSAASurface, std::move(appliedClip),
568 dstProxyView, renderPassXferBarriers, colorLoadOp);
569
570 rContext->priv().recordProgramInfo(fProgramInfo);
571
572 // This is now a noop (bc it is always called in the ctor) but is where we would normally
573 // create the vertices.
574 this->findOrCreateVertices(rContext, false, fFailFillingIn);
575 }
576
onPrepare(GrOpFlushState * flushState)577 void onPrepare(GrOpFlushState* flushState) override {
578 auto dContext = flushState->gpu()->getContext();
579
580 // This call site is not a noop bc this op could've been created on with DDL context
581 // and, therefore, could be lacking a gpu-side buffer
582 this->findOrCreateVertices(dContext, false, fFailFillingIn);
583 }
584
onExecute(GrOpFlushState * flushState,const SkRect & chainBounds)585 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
586 if (!fVertexData || !fVertexData->gpuBuffer()) {
587 return;
588 }
589
590 if (!fProgramInfo) {
591 fProgramInfo = this->createProgramInfo(flushState);
592 }
593
594 flushState->bindPipeline(*fProgramInfo, SkRect::MakeIWH(fWH, fWH));
595 flushState->bindBuffers(nullptr, nullptr, fVertexData->refGpuBuffer());
596 flushState->draw(4, 0);
597 }
598
599 TestHelper::Stats* fStats;
600 int fWH;
601 int fID;
602 bool fFailFillingIn;
603 GrThreadSafeCache::IsNewerBetter fIsNewerBetter;
604
605 sk_sp<GrThreadSafeCache::VertexData> fVertexData;
606 GrProgramInfo* fProgramInfo = nullptr;
607
608 using INHERITED = GrDrawOp;
609 };
610
addVertAccess(SkCanvas * canvas,int wh,int id,bool failLookup,bool failFillingIn,GrThreadSafeVertexTestOp ** createdOp)611 void TestHelper::addVertAccess(SkCanvas* canvas,
612 int wh, int id,
613 bool failLookup, bool failFillingIn,
614 GrThreadSafeVertexTestOp** createdOp) {
615 auto rContext = canvas->recordingContext();
616 auto sdc = SkCanvasPriv::TopDeviceSurfaceDrawContext(canvas);
617
618 GrOp::Owner op = GrThreadSafeVertexTestOp::Make(rContext, &fStats,
619 wh, id,
620 failLookup, failFillingIn,
621 fIsNewerBetter);
622 if (createdOp) {
623 *createdOp = (GrThreadSafeVertexTestOp*) op.get();
624 }
625
626 sdc->addDrawOp(std::move(op));
627 }
628
CreateViewOnCpu(GrRecordingContext * rContext,int wh,Stats * stats)629 GrSurfaceProxyView TestHelper::CreateViewOnCpu(GrRecordingContext* rContext,
630 int wh,
631 Stats* stats) {
632 GrProxyProvider* proxyProvider = rContext->priv().proxyProvider();
633
634 sk_sp<GrTextureProxy> proxy = proxyProvider->createProxyFromBitmap(create_bitmap(wh),
635 GrMipmapped::kNo,
636 SkBackingFit::kExact,
637 SkBudgeted::kYes);
638 if (!proxy) {
639 return {};
640 }
641
642 GrSwizzle swizzle = rContext->priv().caps()->getReadSwizzle(proxy->backendFormat(),
643 GrColorType::kRGBA_8888);
644 ++stats->fNumSWCreations;
645 return {std::move(proxy), kImageOrigin, swizzle};
646 }
647
FillInViewOnGpu(GrDirectContext * dContext,int wh,Stats * stats,const GrSurfaceProxyView & lazyView,sk_sp<GrThreadSafeCache::Trampoline> trampoline)648 bool TestHelper::FillInViewOnGpu(GrDirectContext* dContext, int wh, Stats* stats,
649 const GrSurfaceProxyView& lazyView,
650 sk_sp<GrThreadSafeCache::Trampoline> trampoline) {
651
652 std::unique_ptr<skgpu::v1::SurfaceDrawContext> sdc = new_SDC(dContext, wh);
653
654 GrPaint paint;
655 paint.setColor4f({0.0f, 0.0f, 1.0f, 1.0f});
656
657 sdc->clear(SkPMColor4f{1.0f, 1.0f, 1.0f, 1.0f});
658 sdc->drawRect(nullptr, std::move(paint), GrAA::kNo, SkMatrix::I(),
659 { 10, 10, wh-10.0f, wh-10.0f }, &GrStyle::SimpleFill());
660
661 ++stats->fNumHWCreations;
662 auto view = sdc->readSurfaceView();
663
664 SkASSERT(view.swizzle() == lazyView.swizzle());
665 SkASSERT(view.origin() == lazyView.origin());
666 trampoline->fProxy = view.asTextureProxyRef();
667
668 return true;
669 }
670
AccessCachedView(GrRecordingContext * rContext,GrThreadSafeCache * threadSafeCache,int wh,bool failLookup,bool failFillingIn,int id,Stats * stats)671 GrSurfaceProxyView TestHelper::AccessCachedView(GrRecordingContext* rContext,
672 GrThreadSafeCache* threadSafeCache,
673 int wh,
674 bool failLookup, bool failFillingIn, int id,
675 Stats* stats) {
676 GrUniqueKey key;
677 create_view_key(&key, wh, id);
678
679 if (GrDirectContext* dContext = rContext->asDirectContext()) {
680 // The gpu thread gets priority over the recording threads. If the gpu thread is first,
681 // it crams a lazy proxy into the cache and then fills it in later.
682 auto [lazyView, trampoline] = GrThreadSafeCache::CreateLazyView(
683 dContext, GrColorType::kRGBA_8888, {wh, wh}, kImageOrigin, SkBackingFit::kExact);
684 ++stats->fNumLazyCreations;
685
686 auto [view, data] = threadSafeCache->findOrAddWithData(key, lazyView);
687 if (view != lazyView) {
688 ++stats->fCacheHits;
689 return view;
690 } else if (id != kNoID) {
691 // Make sure, in this case, that the customData stuck
692 SkASSERT(data);
693 SkDEBUGCODE(const int* cachedID = static_cast<const int*>(data->data());)
694 SkASSERT(*cachedID == id);
695 }
696
697 ++stats->fCacheMisses;
698
699 if (failFillingIn) {
700 // Simulate something going horribly wrong at flush-time so no GrTexture is
701 // available to fulfill the lazy proxy.
702 return view;
703 }
704
705 if (!FillInViewOnGpu(dContext, wh, stats, lazyView, std::move(trampoline))) {
706 // In this case something has gone disastrously wrong so set up to drop the draw
707 // that needed this resource and reduce future pollution of the cache.
708 threadSafeCache->remove(key);
709 return {};
710 }
711
712 return view;
713 } else {
714 GrSurfaceProxyView view;
715
716 // We can "fail the lookup" to simulate a threaded race condition
717 if (view = threadSafeCache->find(key); !failLookup && view) {
718 ++stats->fCacheHits;
719 return view;
720 }
721
722 ++stats->fCacheMisses;
723
724 view = CreateViewOnCpu(rContext, wh, stats);
725 SkASSERT(view);
726
727 auto [newView, data] = threadSafeCache->addWithData(key, view);
728 if (view == newView && id != kNoID) {
729 // Make sure, in this case, that the customData stuck
730 SkASSERT(data);
731 SkDEBUGCODE(const int* cachedID = static_cast<const int*>(data->data());)
732 SkASSERT(*cachedID == id);
733 }
734 return newView;
735 }
736 }
737
738 // Case 1: ensure two DDL recorders share the view/vertexData
test_1(GrDirectContext * dContext,skiatest::Reporter * reporter,TestHelper::addAccessFP addAccess,TestHelper::checkFP check)739 static void test_1(GrDirectContext* dContext, skiatest::Reporter* reporter,
740 TestHelper::addAccessFP addAccess,
741 TestHelper::checkFP check) {
742
743 TestHelper helper(dContext);
744
745 (helper.*addAccess)(helper.ddlCanvas1(), kImageWH, 1, false, false);
746 REPORTER_ASSERT(reporter, (helper.*check)(helper.ddlCanvas1(), kImageWH,
747 /*hits*/ 0, /*misses*/ 1, /*refs*/ 1, /*id*/ 1));
748
749 (helper.*addAccess)(helper.ddlCanvas2(), kImageWH, 2, false, false);
750 REPORTER_ASSERT(reporter, (helper.*check)(helper.ddlCanvas2(), kImageWH,
751 /*hits*/ 1, /*misses*/ 1, /*refs*/ 2, /*id*/ 1));
752
753 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 1);
754 REPORTER_ASSERT(reporter, helper.stats()->fNumLazyCreations == 0);
755 REPORTER_ASSERT(reporter, helper.stats()->fNumHWCreations == 0);
756 REPORTER_ASSERT(reporter, helper.stats()->fNumSWCreations == 1);
757
758 helper.checkImage(reporter, helper.snap1());
759 helper.checkImage(reporter, helper.snap2());
760 }
761
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache1View,reporter,ctxInfo)762 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache1View, reporter, ctxInfo) {
763 test_1(ctxInfo.directContext(), reporter, &TestHelper::addViewAccess, &TestHelper::checkView);
764 }
765
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache1Verts,reporter,ctxInfo)766 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache1Verts, reporter, ctxInfo) {
767 test_1(ctxInfo.directContext(), reporter, &TestHelper::addVertAccess, &TestHelper::checkVert);
768 }
769
770 // Case 2: ensure that, if the direct context version wins, its result is reused by the
771 // DDL recorders
test_2(GrDirectContext * dContext,skiatest::Reporter * reporter,TestHelper::addAccessFP addAccess,TestHelper::checkFP check)772 static void test_2(GrDirectContext* dContext, skiatest::Reporter* reporter,
773 TestHelper::addAccessFP addAccess,
774 TestHelper::checkFP check) {
775
776 TestHelper helper(dContext);
777
778 (helper.*addAccess)(helper.liveCanvas(), kImageWH, 1, false, false);
779 REPORTER_ASSERT(reporter, (helper.*check)(helper.liveCanvas(), kImageWH,
780 /*hits*/ 0, /*misses*/ 1, /*refs*/ 1, /*id*/ 1));
781
782 (helper.*addAccess)(helper.ddlCanvas1(), kImageWH, 2, false, false);
783 REPORTER_ASSERT(reporter, (helper.*check)(helper.ddlCanvas1(), kImageWH,
784 /*hits*/ 1, /*misses*/ 1, /*refs*/ 2, /*id*/ 1));
785
786 (helper.*addAccess)(helper.ddlCanvas2(), kImageWH, 3, false, false);
787 REPORTER_ASSERT(reporter, (helper.*check)(helper.ddlCanvas2(), kImageWH,
788 /*hits*/ 2, /*misses*/ 1, /*refs*/ 3, /*id*/ 1));
789
790 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 1);
791 REPORTER_ASSERT(reporter, helper.stats()->fNumLazyCreations == 1);
792 REPORTER_ASSERT(reporter, helper.stats()->fNumHWCreations == 1);
793 REPORTER_ASSERT(reporter, helper.stats()->fNumSWCreations == 0);
794
795 helper.checkImage(reporter);
796 helper.checkImage(reporter, helper.snap1());
797 helper.checkImage(reporter, helper.snap2());
798 }
799
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache2View,reporter,ctxInfo)800 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache2View, reporter, ctxInfo) {
801 test_2(ctxInfo.directContext(), reporter, &TestHelper::addViewAccess, &TestHelper::checkView);
802 }
803
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache2Verts,reporter,ctxInfo)804 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache2Verts, reporter, ctxInfo) {
805 test_2(ctxInfo.directContext(), reporter, &TestHelper::addVertAccess, &TestHelper::checkVert);
806 }
807
808 // Case 3: ensure that, if the cpu-version wins, its result is reused by the direct context
test_3(GrDirectContext * dContext,skiatest::Reporter * reporter,TestHelper::addAccessFP addAccess,TestHelper::checkFP check)809 static void test_3(GrDirectContext* dContext, skiatest::Reporter* reporter,
810 TestHelper::addAccessFP addAccess,
811 TestHelper::checkFP check) {
812
813 TestHelper helper(dContext);
814
815 (helper.*addAccess)(helper.ddlCanvas1(), kImageWH, 1, false, false);
816 REPORTER_ASSERT(reporter, (helper.*check)(helper.ddlCanvas1(), kImageWH,
817 /*hits*/ 0, /*misses*/ 1, /*refs*/ 1, /*id*/ 1));
818
819 (helper.*addAccess)(helper.liveCanvas(), kImageWH, 2, false, false);
820 REPORTER_ASSERT(reporter, (helper.*check)(helper.liveCanvas(), kImageWH,
821 /*hits*/ 1, /*misses*/ 1, /*refs*/ 2, /*id*/ 1));
822
823 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 1);
824 REPORTER_ASSERT(reporter, helper.stats()->fNumLazyCreations == 1);
825 REPORTER_ASSERT(reporter, helper.stats()->fNumHWCreations == 0);
826 REPORTER_ASSERT(reporter, helper.stats()->fNumSWCreations == 1);
827
828 helper.checkImage(reporter);
829 helper.checkImage(reporter, helper.snap1());
830 }
831
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache3View,reporter,ctxInfo)832 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache3View, reporter, ctxInfo) {
833 test_3(ctxInfo.directContext(), reporter, &TestHelper::addViewAccess, &TestHelper::checkView);
834 }
835
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache3Verts,reporter,ctxInfo)836 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache3Verts, reporter, ctxInfo) {
837 test_3(ctxInfo.directContext(), reporter, &TestHelper::addVertAccess, &TestHelper::checkVert);
838 }
839
840 // Case 4: ensure that, if two DDL recorders get in a race, they still end up sharing a single
841 // view/vertexData
test_4(GrDirectContext * dContext,skiatest::Reporter * reporter,TestHelper::addAccessFP addAccess,TestHelper::checkFP check)842 static void test_4(GrDirectContext* dContext, skiatest::Reporter* reporter,
843 TestHelper::addAccessFP addAccess,
844 TestHelper::checkFP check) {
845
846 TestHelper helper(dContext);
847
848 (helper.*addAccess)(helper.ddlCanvas1(), kImageWH, 1, false, false);
849 REPORTER_ASSERT(reporter, (helper.*check)(helper.ddlCanvas1(), kImageWH,
850 /*hits*/ 0, /*misses*/ 1, /*refs*/ 1, /*id*/ 1));
851
852 static const bool kFailLookup = true;
853 (helper.*addAccess)(helper.ddlCanvas2(), kImageWH, 2, kFailLookup, false);
854 REPORTER_ASSERT(reporter, (helper.*check)(helper.ddlCanvas2(), kImageWH,
855 /*hits*/ 0, /*misses*/ 2, /*refs*/ 2, /*id*/ 1));
856
857 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 1);
858 REPORTER_ASSERT(reporter, helper.stats()->fNumLazyCreations == 0);
859 REPORTER_ASSERT(reporter, helper.stats()->fNumHWCreations == 0);
860 REPORTER_ASSERT(reporter, helper.stats()->fNumSWCreations == 2);
861
862 helper.checkImage(reporter, helper.snap1());
863 helper.checkImage(reporter, helper.snap2());
864 }
865
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache4View,reporter,ctxInfo)866 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache4View, reporter, ctxInfo) {
867 test_4(ctxInfo.directContext(), reporter, &TestHelper::addViewAccess, &TestHelper::checkView);
868 }
869
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache4Verts,reporter,ctxInfo)870 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache4Verts, reporter, ctxInfo) {
871 test_4(ctxInfo.directContext(), reporter, &TestHelper::addVertAccess, &TestHelper::checkVert);
872 }
873
874 // Case 4.5: check that, if a live rendering and a DDL recording get into a race, the live
875 // rendering takes precedence.
test_4_5(GrDirectContext * dContext,skiatest::Reporter * reporter,TestHelper::addAccessFP addAccess,TestHelper::checkFP check)876 static void test_4_5(GrDirectContext* dContext, skiatest::Reporter* reporter,
877 TestHelper::addAccessFP addAccess,
878 TestHelper::checkFP check) {
879
880 TestHelper helper(dContext);
881
882 (helper.*addAccess)(helper.liveCanvas(), kImageWH, 1, false, false);
883 REPORTER_ASSERT(reporter, (helper.*check)(helper.liveCanvas(), kImageWH,
884 /*hits*/ 0, /*misses*/ 1, /*refs*/ 1, /*id*/ 1));
885
886 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 1);
887 REPORTER_ASSERT(reporter, helper.stats()->fNumLazyCreations == 1);
888 REPORTER_ASSERT(reporter, helper.stats()->fNumHWCreations == 1);
889 REPORTER_ASSERT(reporter, helper.stats()->fNumSWCreations == 0);
890
891 static const bool kFailLookup = true;
892 (helper.*addAccess)(helper.ddlCanvas1(), kImageWH, 2, kFailLookup, false);
893 REPORTER_ASSERT(reporter, (helper.*check)(helper.ddlCanvas1(), kImageWH,
894 /*hits*/ 0, /*misses*/ 2, /*refs*/ 2, /*id*/ 1));
895
896 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 1);
897 REPORTER_ASSERT(reporter, helper.stats()->fNumLazyCreations == 1);
898 REPORTER_ASSERT(reporter, helper.stats()->fNumHWCreations == 1);
899 REPORTER_ASSERT(reporter, helper.stats()->fNumSWCreations == 1);
900
901 helper.checkImage(reporter);
902 helper.checkImage(reporter, helper.snap1());
903 }
904
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache4_5View,reporter,ctxInfo)905 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache4_5View, reporter, ctxInfo) {
906 test_4_5(ctxInfo.directContext(), reporter,
907 &TestHelper::addViewAccess, &TestHelper::checkView);
908 }
909
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache4_5Verts,reporter,ctxInfo)910 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache4_5Verts, reporter, ctxInfo) {
911 test_4_5(ctxInfo.directContext(), reporter,
912 &TestHelper::addVertAccess, &TestHelper::checkVert);
913 }
914
915 // Case 4.75: check that, if a live rendering fails to generate the content needed to instantiate
916 // its lazy proxy, life goes on
test_4_75(GrDirectContext * dContext,skiatest::Reporter * reporter,TestHelper::addAccessFP addAccess,TestHelper::checkFP check)917 static void test_4_75(GrDirectContext* dContext, skiatest::Reporter* reporter,
918 TestHelper::addAccessFP addAccess,
919 TestHelper::checkFP check) {
920
921 TestHelper helper(dContext);
922
923 static const bool kFailFillingIn = true;
924 (helper.*addAccess)(helper.liveCanvas(), kImageWH, kNoID, false, kFailFillingIn);
925 REPORTER_ASSERT(reporter, (helper.*check)(helper.liveCanvas(), kImageWH,
926 /*hits*/ 0, /*misses*/ 1, /*refs*/ 1, kNoID));
927
928 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 1);
929 REPORTER_ASSERT(reporter, helper.stats()->fNumLazyCreations == 1);
930 REPORTER_ASSERT(reporter, helper.stats()->fNumHWCreations == 0);
931 REPORTER_ASSERT(reporter, helper.stats()->fNumSWCreations == 0);
932
933 dContext->flush();
934 dContext->submit(true);
935
936 REPORTER_ASSERT(reporter, (helper.*check)(helper.liveCanvas(), kImageWH,
937 /*hits*/ 0, /*misses*/ 1, /*refs*/ 0, kNoID));
938
939 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 1);
940 REPORTER_ASSERT(reporter, helper.stats()->fNumLazyCreations == 1);
941 REPORTER_ASSERT(reporter, helper.stats()->fNumHWCreations == 0);
942 REPORTER_ASSERT(reporter, helper.stats()->fNumSWCreations == 0);
943 }
944
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache4_75View,reporter,ctxInfo)945 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache4_75View, reporter, ctxInfo) {
946 test_4_75(ctxInfo.directContext(), reporter,
947 &TestHelper::addViewAccess, &TestHelper::checkView);
948 }
949
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache4_75Verts,reporter,ctxInfo)950 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache4_75Verts, reporter, ctxInfo) {
951 test_4_75(ctxInfo.directContext(), reporter,
952 &TestHelper::addVertAccess, &TestHelper::checkVert);
953 }
954
955 // Case 5: ensure that expanding the map works (esp. wrt custom data)
test_5(GrDirectContext * dContext,skiatest::Reporter * reporter,TestHelper::addAccessFP addAccess,TestHelper::checkFP check)956 static void test_5(GrDirectContext* dContext, skiatest::Reporter* reporter,
957 TestHelper::addAccessFP addAccess,
958 TestHelper::checkFP check) {
959
960 TestHelper helper(dContext);
961
962 auto threadSafeCache = helper.threadSafeCache();
963
964 int size = 16;
965 (helper.*addAccess)(helper.ddlCanvas1(), size, /*id*/ size, false, false);
966
967 size_t initialSize = threadSafeCache->approxBytesUsedForHash();
968
969 while (initialSize == threadSafeCache->approxBytesUsedForHash()) {
970 size *= 2;
971 (helper.*addAccess)(helper.ddlCanvas1(), size, /*id*/ size, false, false);
972 }
973
974 for (int i = 16; i <= size; i *= 2) {
975 REPORTER_ASSERT(reporter, (helper.*check)(helper.ddlCanvas1(),
976 /*wh*/ i,
977 /*hits*/ 0,
978 /*misses*/ threadSafeCache->numEntries(),
979 /*refs*/ 1,
980 /*id*/ i));
981 }
982 }
983
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache5View,reporter,ctxInfo)984 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache5View, reporter, ctxInfo) {
985 test_5(ctxInfo.directContext(), reporter, &TestHelper::addViewAccess, &TestHelper::checkView);
986 }
987
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache5Verts,reporter,ctxInfo)988 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache5Verts, reporter, ctxInfo) {
989 test_5(ctxInfo.directContext(), reporter, &TestHelper::addVertAccess, &TestHelper::checkVert);
990 }
991
992 // Case 6: Check on dropping refs. In particular, that the cache has its own ref to keep
993 // the backing resource alive and locked.
test_6(GrDirectContext * dContext,skiatest::Reporter * reporter,TestHelper::addAccessFP addAccess,TestHelper::checkFP check)994 static void test_6(GrDirectContext* dContext, skiatest::Reporter* reporter,
995 TestHelper::addAccessFP addAccess,
996 TestHelper::checkFP check) {
997
998 TestHelper helper(dContext);
999
1000 (helper.*addAccess)(helper.ddlCanvas1(), kImageWH, kNoID, false, false);
1001 sk_sp<SkDeferredDisplayList> ddl1 = helper.snap1();
1002 REPORTER_ASSERT(reporter, (helper.*check)(nullptr, kImageWH,
1003 /*hits*/ 0, /*misses*/ 1, /*refs*/ 1, kNoID));
1004
1005 (helper.*addAccess)(helper.ddlCanvas2(), kImageWH, kNoID, false, false);
1006 sk_sp<SkDeferredDisplayList> ddl2 = helper.snap2();
1007 REPORTER_ASSERT(reporter, (helper.*check)(nullptr, kImageWH,
1008 /*hits*/ 1, /*misses*/ 1, /*refs*/ 2, kNoID));
1009
1010 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 1);
1011
1012 ddl1 = nullptr;
1013 REPORTER_ASSERT(reporter, (helper.*check)(nullptr, kImageWH,
1014 /*hits*/ 1, /*misses*/ 1, /*refs*/ 1, kNoID));
1015
1016 ddl2 = nullptr;
1017 REPORTER_ASSERT(reporter, (helper.*check)(nullptr, kImageWH,
1018 /*hits*/ 1, /*misses*/ 1, /*refs*/ 0, kNoID));
1019
1020 // The cache still has its ref
1021 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 1);
1022
1023 REPORTER_ASSERT(reporter, (helper.*check)(nullptr, kImageWH,
1024 /*hits*/ 1, /*misses*/ 1, /*refs*/ 0, kNoID));
1025 }
1026
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache6View,reporter,ctxInfo)1027 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache6View, reporter, ctxInfo) {
1028 test_6(ctxInfo.directContext(), reporter, &TestHelper::addViewAccess, &TestHelper::checkView);
1029 }
1030
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache6Verts,reporter,ctxInfo)1031 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache6Verts, reporter, ctxInfo) {
1032 test_6(ctxInfo.directContext(), reporter, &TestHelper::addVertAccess, &TestHelper::checkVert);
1033 }
1034
1035 // Case 7: Check that invoking dropAllRefs and dropUniqueRefs directly works as expected; i.e.,
1036 // dropAllRefs removes everything while dropUniqueRefs is more measured.
test_7(GrDirectContext * dContext,skiatest::Reporter * reporter,TestHelper::addAccessFP addAccess,TestHelper::checkFP check)1037 static void test_7(GrDirectContext* dContext, skiatest::Reporter* reporter,
1038 TestHelper::addAccessFP addAccess,
1039 TestHelper::checkFP check) {
1040
1041 TestHelper helper(dContext);
1042
1043 (helper.*addAccess)(helper.ddlCanvas1(), kImageWH, kNoID, false, false);
1044 sk_sp<SkDeferredDisplayList> ddl1 = helper.snap1();
1045 REPORTER_ASSERT(reporter, (helper.*check)(nullptr, kImageWH,
1046 /*hits*/ 0, /*misses*/ 1, /*refs*/ 1, kNoID));
1047
1048 (helper.*addAccess)(helper.ddlCanvas2(), 2*kImageWH, kNoID, false, false);
1049 sk_sp<SkDeferredDisplayList> ddl2 = helper.snap2();
1050 REPORTER_ASSERT(reporter, (helper.*check)(nullptr, 2*kImageWH,
1051 /*hits*/ 0, /*misses*/ 2, /*refs*/ 1, kNoID));
1052
1053 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 2);
1054
1055 helper.threadSafeCache()->dropUniqueRefs(nullptr);
1056 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 2);
1057
1058 ddl1 = nullptr;
1059
1060 helper.threadSafeCache()->dropUniqueRefs(nullptr);
1061 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 1);
1062 REPORTER_ASSERT(reporter, (helper.*check)(nullptr, 2*kImageWH,
1063 /*hits*/ 0, /*misses*/ 2, /*refs*/ 1, kNoID));
1064
1065 helper.threadSafeCache()->dropAllRefs();
1066 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 0);
1067
1068 ddl2 = nullptr;
1069 }
1070
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache7View,reporter,ctxInfo)1071 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache7View, reporter, ctxInfo) {
1072 test_7(ctxInfo.directContext(), reporter, &TestHelper::addViewAccess, &TestHelper::checkView);
1073 }
1074
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache7Verts,reporter,ctxInfo)1075 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache7Verts, reporter, ctxInfo) {
1076 test_7(ctxInfo.directContext(), reporter, &TestHelper::addVertAccess, &TestHelper::checkVert);
1077 }
1078
1079 // Case 8: This checks that GrContext::abandonContext works as expected wrt the thread
1080 // safe cache. This simulates the case where we have one DDL that has finished
1081 // recording but one still recording when the abandonContext fires.
test_8(GrDirectContext * dContext,skiatest::Reporter * reporter,TestHelper::addAccessFP addAccess,TestHelper::checkFP check)1082 static void test_8(GrDirectContext* dContext, skiatest::Reporter* reporter,
1083 TestHelper::addAccessFP addAccess,
1084 TestHelper::checkFP check) {
1085
1086 TestHelper helper(dContext);
1087
1088 (helper.*addAccess)(helper.liveCanvas(), kImageWH, kNoID, false, false);
1089 REPORTER_ASSERT(reporter, (helper.*check)(helper.liveCanvas(), kImageWH,
1090 /*hits*/ 0, /*misses*/ 1, /*refs*/ 1, kNoID));
1091
1092 (helper.*addAccess)(helper.ddlCanvas1(), kImageWH, kNoID, false, false);
1093 sk_sp<SkDeferredDisplayList> ddl1 = helper.snap1();
1094 REPORTER_ASSERT(reporter, (helper.*check)(helper.ddlCanvas1(), kImageWH,
1095 /*hits*/ 1, /*misses*/ 1, /*refs*/ 2, kNoID));
1096
1097 (helper.*addAccess)(helper.ddlCanvas2(), kImageWH, kNoID, false, false);
1098 REPORTER_ASSERT(reporter, (helper.*check)(helper.ddlCanvas2(), kImageWH,
1099 /*hits*/ 2, /*misses*/ 1, /*refs*/ 3, kNoID));
1100
1101 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 1);
1102 REPORTER_ASSERT(reporter, helper.stats()->fNumLazyCreations == 1);
1103 REPORTER_ASSERT(reporter, helper.stats()->fNumHWCreations == 1);
1104 REPORTER_ASSERT(reporter, helper.stats()->fNumSWCreations == 0);
1105
1106 dContext->abandonContext(); // This should exercise dropAllRefs
1107
1108 sk_sp<SkDeferredDisplayList> ddl2 = helper.snap2();
1109
1110 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 0);
1111
1112 ddl1 = nullptr;
1113 ddl2 = nullptr;
1114 }
1115
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache8View,reporter,ctxInfo)1116 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache8View, reporter, ctxInfo) {
1117 test_8(ctxInfo.directContext(), reporter, &TestHelper::addViewAccess, &TestHelper::checkView);
1118 }
1119
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache8Verts,reporter,ctxInfo)1120 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache8Verts, reporter, ctxInfo) {
1121 test_8(ctxInfo.directContext(), reporter, &TestHelper::addVertAccess, &TestHelper::checkVert);
1122 }
1123
1124 // Case 9: This checks that GrContext::releaseResourcesAndAbandonContext works as expected wrt
1125 // the thread safe cache. This simulates the case where we have one DDL that has finished
1126 // recording but one still recording when the releaseResourcesAndAbandonContext fires.
test_9(GrDirectContext * dContext,skiatest::Reporter * reporter,TestHelper::addAccessFP addAccess,TestHelper::checkFP check)1127 static void test_9(GrDirectContext* dContext, skiatest::Reporter* reporter,
1128 TestHelper::addAccessFP addAccess,
1129 TestHelper::checkFP check) {
1130
1131 TestHelper helper(dContext);
1132
1133 (helper.*addAccess)(helper.liveCanvas(), kImageWH, kNoID, false, false);
1134 REPORTER_ASSERT(reporter, (helper.*check)(helper.liveCanvas(), kImageWH,
1135 /*hits*/ 0, /*misses*/ 1, /*refs*/ 1, kNoID));
1136
1137 (helper.*addAccess)(helper.ddlCanvas1(), kImageWH, kNoID, false, false);
1138 sk_sp<SkDeferredDisplayList> ddl1 = helper.snap1();
1139 REPORTER_ASSERT(reporter, (helper.*check)(helper.ddlCanvas1(), kImageWH,
1140 /*hits*/ 1, /*misses*/ 1, /*refs*/ 2, kNoID));
1141
1142 (helper.*addAccess)(helper.ddlCanvas2(), kImageWH, kNoID, false, false);
1143 REPORTER_ASSERT(reporter, (helper.*check)(helper.ddlCanvas2(), kImageWH,
1144 /*hits*/ 2, /*misses*/ 1, /*refs*/ 3, kNoID));
1145
1146 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 1);
1147 REPORTER_ASSERT(reporter, helper.stats()->fNumLazyCreations == 1);
1148 REPORTER_ASSERT(reporter, helper.stats()->fNumHWCreations == 1);
1149 REPORTER_ASSERT(reporter, helper.stats()->fNumSWCreations == 0);
1150
1151 dContext->releaseResourcesAndAbandonContext(); // This should hit dropAllRefs
1152
1153 sk_sp<SkDeferredDisplayList> ddl2 = helper.snap2();
1154
1155 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 0);
1156
1157 ddl1 = nullptr;
1158 ddl2 = nullptr;
1159 }
1160
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache9View,reporter,ctxInfo)1161 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache9View, reporter, ctxInfo) {
1162 test_9(ctxInfo.directContext(), reporter, &TestHelper::addViewAccess, &TestHelper::checkView);
1163 }
1164
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache9Verts,reporter,ctxInfo)1165 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache9Verts, reporter, ctxInfo) {
1166 test_9(ctxInfo.directContext(), reporter, &TestHelper::addVertAccess, &TestHelper::checkVert);
1167 }
1168
1169 // Case 10: This checks that the GrContext::purgeUnlockedResources(size_t) variant works as
1170 // expected wrt the thread safe cache. It, in particular, tests out the MRU behavior
1171 // of the shared cache.
test_10(GrDirectContext * dContext,skiatest::Reporter * reporter,TestHelper::addAccessFP addAccess,TestHelper::checkFP check)1172 static void test_10(GrDirectContext* dContext, skiatest::Reporter* reporter,
1173 TestHelper::addAccessFP addAccess,
1174 TestHelper::checkFP check) {
1175
1176 if (GrBackendApi::kOpenGL != dContext->backend()) {
1177 // The lower-level backends have too much going on for the following simple purging
1178 // test to work
1179 return;
1180 }
1181
1182 TestHelper helper(dContext);
1183
1184 (helper.*addAccess)(helper.liveCanvas(), kImageWH, kNoID, false, false);
1185 REPORTER_ASSERT(reporter, (helper.*check)(helper.liveCanvas(), kImageWH,
1186 /*hits*/ 0, /*misses*/ 1, /*refs*/ 1, kNoID));
1187
1188 (helper.*addAccess)(helper.ddlCanvas1(), kImageWH, kNoID, false, false);
1189 sk_sp<SkDeferredDisplayList> ddl1 = helper.snap1();
1190 REPORTER_ASSERT(reporter, (helper.*check)(helper.ddlCanvas1(), kImageWH,
1191 /*hits*/ 1, /*misses*/ 1, /*refs*/ 2, kNoID));
1192
1193 (helper.*addAccess)(helper.liveCanvas(), 2*kImageWH, kNoID, false, false);
1194 REPORTER_ASSERT(reporter, (helper.*check)(helper.liveCanvas(), 2*kImageWH,
1195 /*hits*/ 1, /*misses*/ 2, /*refs*/ 1, kNoID));
1196
1197 (helper.*addAccess)(helper.ddlCanvas2(), 2*kImageWH, kNoID, false, false);
1198 sk_sp<SkDeferredDisplayList> ddl2 = helper.snap2();
1199 REPORTER_ASSERT(reporter, (helper.*check)(helper.ddlCanvas2(), 2*kImageWH,
1200 /*hits*/ 2, /*misses*/ 2, /*refs*/ 2, kNoID));
1201
1202 dContext->flush();
1203 dContext->submit(true);
1204
1205 // This should clear out everything but the textures locked in the thread-safe cache
1206 dContext->purgeUnlockedResources(false);
1207
1208 ddl1 = nullptr;
1209 ddl2 = nullptr;
1210
1211 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 2);
1212 REPORTER_ASSERT(reporter, (helper.*check)(helper.liveCanvas(), kImageWH,
1213 /*hits*/ 2, /*misses*/ 2, /*refs*/ 0, kNoID));
1214 REPORTER_ASSERT(reporter, (helper.*check)(helper.liveCanvas(), 2*kImageWH,
1215 /*hits*/ 2, /*misses*/ 2, /*refs*/ 0, kNoID));
1216
1217 // Regardless of which image is MRU, this should force the other out
1218 size_t desiredBytes = helper.gpuSize(2*kImageWH) + helper.gpuSize(kImageWH)/2;
1219
1220 auto cache = dContext->priv().getResourceCache();
1221 size_t currentBytes = cache->getResourceBytes();
1222
1223 SkASSERT(currentBytes >= desiredBytes);
1224 size_t amountToPurge = currentBytes - desiredBytes;
1225
1226 // The 2*kImageWH texture should be MRU.
1227 dContext->purgeUnlockedResources(amountToPurge, true);
1228
1229 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 1);
1230
1231 REPORTER_ASSERT(reporter, (helper.*check)(helper.liveCanvas(), 2*kImageWH,
1232 /*hits*/ 2, /*misses*/ 2, /*refs*/ 0, kNoID));
1233 }
1234
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache10View,reporter,ctxInfo)1235 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache10View, reporter, ctxInfo) {
1236 test_10(ctxInfo.directContext(), reporter, &TestHelper::addViewAccess, &TestHelper::checkView);
1237 }
1238
1239 // To enable test_10 with verts would require a bit more work, namely:
1240 // have a different # of verts based on size
1241 // also pass in a gpuSize function to 'test_10'
1242 //DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache10Verts, reporter, ctxInfo) {
1243 // test_10(ctxInfo.directContext(), reporter, &TestHelper::addVertAccess, &TestHelper::checkVert);
1244 //}
1245
1246 // Case 11: This checks that scratch-only variant of GrContext::purgeUnlockedResources works as
1247 // expected wrt the thread safe cache. In particular, that when 'scratchResourcesOnly'
1248 // is true, the call has no effect on the cache.
test_11(GrDirectContext * dContext,skiatest::Reporter * reporter,TestHelper::addAccessFP addAccess,TestHelper::checkFP check)1249 static void test_11(GrDirectContext* dContext, skiatest::Reporter* reporter,
1250 TestHelper::addAccessFP addAccess,
1251 TestHelper::checkFP check) {
1252
1253 TestHelper helper(dContext);
1254
1255 (helper.*addAccess)(helper.liveCanvas(), kImageWH, kNoID, false, false);
1256 REPORTER_ASSERT(reporter, (helper.*check)(helper.liveCanvas(), kImageWH,
1257 /*hits*/ 0, /*misses*/ 1, /*refs*/ 1, kNoID));
1258
1259 (helper.*addAccess)(helper.liveCanvas(), 2*kImageWH, kNoID, false, false);
1260 REPORTER_ASSERT(reporter, (helper.*check)(helper.liveCanvas(), 2*kImageWH,
1261 /*hits*/ 0, /*misses*/ 2, /*refs*/ 1, kNoID));
1262
1263 dContext->flush();
1264 dContext->submit(true);
1265
1266 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 2);
1267 REPORTER_ASSERT(reporter, (helper.*check)(helper.liveCanvas(), kImageWH,
1268 /*hits*/ 0, /*misses*/ 2, /*refs*/ 0, kNoID));
1269 REPORTER_ASSERT(reporter, (helper.*check)(helper.liveCanvas(), 2*kImageWH,
1270 /*hits*/ 0, /*misses*/ 2, /*refs*/ 0, kNoID));
1271
1272 // This shouldn't remove anything from the cache
1273 dContext->purgeUnlockedResources(/* scratchResourcesOnly */ true);
1274
1275 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 2);
1276
1277 dContext->purgeUnlockedResources(/* scratchResourcesOnly */ false);
1278
1279 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 0);
1280 }
1281
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache11View,reporter,ctxInfo)1282 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache11View, reporter, ctxInfo) {
1283 test_11(ctxInfo.directContext(), reporter, &TestHelper::addViewAccess, &TestHelper::checkView);
1284 }
1285
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache11Verts,reporter,ctxInfo)1286 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache11Verts, reporter, ctxInfo) {
1287 test_11(ctxInfo.directContext(), reporter, &TestHelper::addVertAccess, &TestHelper::checkVert);
1288 }
1289
1290 // Case 12: Test out purges caused by resetting the cache budget to 0. Note that, due to
1291 // the how the cache operates (i.e., not directly driven by ref/unrefs) there
1292 // needs to be an explicit kick to purge the cache.
test_12(GrDirectContext * dContext,skiatest::Reporter * reporter,TestHelper::addAccessFP addAccess,TestHelper::checkFP check)1293 static void test_12(GrDirectContext* dContext, skiatest::Reporter* reporter,
1294 TestHelper::addAccessFP addAccess,
1295 TestHelper::checkFP check) {
1296
1297 TestHelper helper(dContext);
1298
1299 (helper.*addAccess)(helper.liveCanvas(), kImageWH, kNoID, false, false);
1300 REPORTER_ASSERT(reporter, (helper.*check)(helper.liveCanvas(), kImageWH,
1301 /*hits*/ 0, /*misses*/ 1, /*refs*/ 1, kNoID));
1302 (helper.*addAccess)(helper.ddlCanvas1(), kImageWH, kNoID, false, false);
1303 sk_sp<SkDeferredDisplayList> ddl1 = helper.snap1();
1304 REPORTER_ASSERT(reporter, (helper.*check)(helper.ddlCanvas1(), kImageWH,
1305 /*hits*/ 1, /*misses*/ 1, /*refs*/ 2, kNoID));
1306
1307 (helper.*addAccess)(helper.liveCanvas(), 2*kImageWH, kNoID, false, false);
1308 REPORTER_ASSERT(reporter, (helper.*check)(helper.liveCanvas(), 2*kImageWH,
1309 /*hits*/ 1, /*misses*/ 2, /*refs*/ 1, kNoID));
1310
1311 dContext->flush();
1312 dContext->submit(true);
1313
1314 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 2);
1315 REPORTER_ASSERT(reporter, (helper.*check)(helper.liveCanvas(), kImageWH,
1316 /*hits*/ 1, /*misses*/ 2, /*refs*/ 1, kNoID));
1317 REPORTER_ASSERT(reporter, (helper.*check)(helper.liveCanvas(), 2*kImageWH,
1318 /*hits*/ 1, /*misses*/ 2, /*refs*/ 0, kNoID));
1319
1320 dContext->setResourceCacheLimit(0);
1321
1322 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 1);
1323
1324 ddl1 = nullptr;
1325
1326 // Explicitly kick off the purge - it won't happen automatically on unref
1327 dContext->performDeferredCleanup(std::chrono::milliseconds(0));
1328
1329 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 0);
1330 }
1331
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache12View,reporter,ctxInfo)1332 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache12View, reporter, ctxInfo) {
1333 test_12(ctxInfo.directContext(), reporter, &TestHelper::addViewAccess, &TestHelper::checkView);
1334 }
1335
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache12Verts,reporter,ctxInfo)1336 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache12Verts, reporter, ctxInfo) {
1337 test_12(ctxInfo.directContext(), reporter, &TestHelper::addVertAccess, &TestHelper::checkVert);
1338 }
1339
1340 // Case 13: Test out the 'msNotUsed' parameter to GrContext::performDeferredCleanup.
test_13(GrDirectContext * dContext,skiatest::Reporter * reporter,TestHelper::addAccessFP addAccess,TestHelper::checkFP check)1341 static void test_13(GrDirectContext* dContext, skiatest::Reporter* reporter,
1342 TestHelper::addAccessFP addAccess,
1343 TestHelper::checkFP check) {
1344
1345 TestHelper helper(dContext);
1346
1347 (helper.*addAccess)(helper.ddlCanvas1(), kImageWH, kNoID, false, false);
1348 REPORTER_ASSERT(reporter, (helper.*check)(helper.ddlCanvas1(), kImageWH,
1349 /*hits*/ 0, /*misses*/ 1, /*refs*/ 1, kNoID));
1350 sk_sp<SkDeferredDisplayList> ddl1 = helper.snap1();
1351
1352 std::this_thread::sleep_for(std::chrono::milliseconds(5));
1353 auto firstTime = GrStdSteadyClock::now();
1354 std::this_thread::sleep_for(std::chrono::milliseconds(5));
1355
1356 (helper.*addAccess)(helper.ddlCanvas2(), 2*kImageWH, kNoID, false, false);
1357
1358 REPORTER_ASSERT(reporter, (helper.*check)(helper.ddlCanvas2(), 2*kImageWH,
1359 /*hits*/ 0, /*misses*/ 2, /*refs*/ 1, kNoID));
1360 sk_sp<SkDeferredDisplayList> ddl2 = helper.snap2();
1361
1362 ddl1 = nullptr;
1363 ddl2 = nullptr;
1364
1365 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 2);
1366
1367 auto secondTime = GrStdSteadyClock::now();
1368
1369 auto msecs = std::chrono::duration_cast<std::chrono::milliseconds>(secondTime - firstTime);
1370 dContext->performDeferredCleanup(msecs);
1371
1372 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 1);
1373 REPORTER_ASSERT(reporter, (helper.*check)(helper.liveCanvas(), 2*kImageWH,
1374 /*hits*/ 0, /*misses*/ 2, /*refs*/ 0, kNoID));
1375 }
1376
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache13View,reporter,ctxInfo)1377 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache13View, reporter, ctxInfo) {
1378 test_13(ctxInfo.directContext(), reporter, &TestHelper::addViewAccess, &TestHelper::checkView);
1379 }
1380
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache13Verts,reporter,ctxInfo)1381 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache13Verts, reporter, ctxInfo) {
1382 test_13(ctxInfo.directContext(), reporter, &TestHelper::addVertAccess, &TestHelper::checkVert);
1383 }
1384
1385 // Case 14: Test out mixing & matching view & vertex data w/ recycling of the cache entries to
1386 // wring out the anonymous union code. This is mainly for the MSAN bot's consumption.
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache14,reporter,ctxInfo)1387 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache14, reporter, ctxInfo) {
1388 constexpr int kBestPrimeNumber = 73; // palindromic in binary
1389 SkRandom rand(kBestPrimeNumber);
1390
1391 TestHelper helper(ctxInfo.directContext());
1392
1393 for (int i = 0; i < 2; ++i) {
1394 SkCanvas* ddlCanvas = (!i) ? helper.ddlCanvas1() : helper.ddlCanvas2();
1395
1396 for (int j = 0; j < 10; ++j) {
1397 int numResources = 10*i + j + 1;
1398 int wh = numResources;
1399
1400 if (rand.nextBool()) {
1401 helper.addViewAccess(ddlCanvas, wh, kNoID, false, false);
1402 REPORTER_ASSERT(reporter, helper.checkView(ddlCanvas, wh,
1403 /*hits*/ 0, /*misses*/ numResources,
1404 /*refs*/ 1, kNoID));
1405 } else {
1406 helper.addVertAccess(ddlCanvas, wh, kNoID, false, false);
1407 REPORTER_ASSERT(reporter, helper.checkVert(ddlCanvas, wh,
1408 /*hits*/ 0, /*misses*/ numResources,
1409 /*refs*/ 1, kNoID));
1410 }
1411 }
1412
1413 if (!i) {
1414 // Drop all the accumulated resources from the thread-safe cache
1415 helper.snap1();
1416 ctxInfo.directContext()->purgeUnlockedResources(/* scratchResourcesOnly */ false);
1417 }
1418 }
1419 }
1420
1421 // Case 15: Test out posting invalidation messages that involve the thread safe cache
test_15(GrDirectContext * dContext,skiatest::Reporter * reporter,TestHelper::addAccessFP addAccess,TestHelper::checkFP check,void (* create_key)(GrUniqueKey *,int wh,int id))1422 static void test_15(GrDirectContext* dContext, skiatest::Reporter* reporter,
1423 TestHelper::addAccessFP addAccess,
1424 TestHelper::checkFP check,
1425 void (*create_key)(GrUniqueKey*, int wh, int id)) {
1426
1427 TestHelper helper(dContext);
1428
1429 (helper.*addAccess)(helper.ddlCanvas1(), kImageWH, kNoID, false, false);
1430 REPORTER_ASSERT(reporter, (helper.*check)(helper.ddlCanvas1(), kImageWH,
1431 /*hits*/ 0, /*misses*/ 1, /*refs*/ 1, kNoID));
1432 sk_sp<SkDeferredDisplayList> ddl1 = helper.snap1();
1433
1434 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 1);
1435
1436 GrUniqueKey key;
1437 (*create_key)(&key, kImageWH, kNoID);
1438
1439 GrUniqueKeyInvalidatedMessage msg(key, dContext->priv().contextID(),
1440 /* inThreadSafeCache */ true);
1441
1442 SkMessageBus<GrUniqueKeyInvalidatedMessage, uint32_t>::Post(msg);
1443
1444 // This purge call is needed to process the invalidation messages
1445 dContext->purgeUnlockedResources(/* scratchResourcesOnly */ true);
1446
1447 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 0);
1448
1449 (helper.*addAccess)(helper.ddlCanvas2(), kImageWH, kNoID, false, false);
1450 REPORTER_ASSERT(reporter, (helper.*check)(helper.ddlCanvas2(), kImageWH,
1451 /*hits*/ 0, /*misses*/ 2, /*refs*/ 1, kNoID));
1452 sk_sp<SkDeferredDisplayList> ddl2 = helper.snap2();
1453
1454 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 1);
1455
1456 helper.checkImage(reporter, std::move(ddl1));
1457 helper.checkImage(reporter, std::move(ddl2));
1458 }
1459
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache15View,reporter,ctxInfo)1460 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache15View, reporter, ctxInfo) {
1461 test_15(ctxInfo.directContext(), reporter, &TestHelper::addViewAccess, &TestHelper::checkView,
1462 create_view_key);
1463 }
1464
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache15Verts,reporter,ctxInfo)1465 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache15Verts, reporter, ctxInfo) {
1466 test_15(ctxInfo.directContext(), reporter, &TestHelper::addVertAccess, &TestHelper::checkVert,
1467 create_vert_key);
1468 }
1469
1470 // Case 16: Test out pre-emption of an existing vertex-data cache entry. This test simulates
1471 // the case where there is a race to create vertex data. However, the second one
1472 // to finish is better and usurps the first's position in the cache.
1473 //
1474 // This capability isn't available for views.
1475
newer_is_always_better(SkData *,SkData *)1476 static bool newer_is_always_better(SkData* /* incumbent */, SkData* /* challenger */) {
1477 return true;
1478 };
1479
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache16Verts,reporter,ctxInfo)1480 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache16Verts, reporter, ctxInfo) {
1481 GrUniqueKey key;
1482 create_vert_key(&key, kImageWH, kNoID);
1483
1484 TestHelper helper(ctxInfo.directContext(), newer_is_always_better);
1485
1486 GrThreadSafeVertexTestOp* op1 = nullptr, *op2 = nullptr;
1487
1488 helper.addVertAccess(helper.ddlCanvas1(), kImageWH, kNoID, false, false, &op1);
1489 REPORTER_ASSERT(reporter, helper.checkVert(helper.ddlCanvas1(), kImageWH,
1490 /*hits*/ 0, /*misses*/ 1, /*refs*/ 1, kNoID));
1491 sk_sp<SkDeferredDisplayList> ddl1 = helper.snap1();
1492
1493 {
1494 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 1);
1495 auto [vertexData, xtraData] = helper.threadSafeCache()->findVertsWithData(key);
1496 REPORTER_ASSERT(reporter, vertexData.get() == op1->vertexData());
1497 }
1498
1499 helper.addVertAccess(helper.ddlCanvas2(), kImageWH, kNoID, /* failLookup */ true, false, &op2);
1500 REPORTER_ASSERT(reporter, helper.checkVert(helper.ddlCanvas2(), kImageWH,
1501 /*hits*/ 0, /*misses*/ 2, /*refs*/ 1, kNoID));
1502 sk_sp<SkDeferredDisplayList> ddl2 = helper.snap2();
1503
1504 REPORTER_ASSERT(reporter, op1->vertexData() != op2->vertexData());
1505
1506 {
1507 REPORTER_ASSERT(reporter, helper.numCacheEntries() == 1);
1508 auto [vertexData, xtraData] = helper.threadSafeCache()->findVertsWithData(key);
1509 REPORTER_ASSERT(reporter, vertexData.get() == op2->vertexData());
1510 }
1511
1512 helper.checkImage(reporter, std::move(ddl1));
1513 helper.checkImage(reporter, std::move(ddl2));
1514 }
1515