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