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