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