• 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/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