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