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