Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
GrThreadSafeCacheTest.cpp
Go to the documentation of this file.
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
15#include "include/core/SkData.h"
20#include "include/core/SkRect.h"
30#include "include/gpu/GrTypes.h"
39#include "src/base/SkRandom.h"
42#include "src/gpu/ResourceKey.h"
44#include "src/gpu/Swizzle.h"
73#include "tests/Test.h"
74#include "tests/TestUtils.h"
76
77#include <chrono>
78#include <cstddef>
79#include <cstdint>
80#include <functional>
81#include <memory>
82#include <thread>
83#include <utility>
84
85class GrDstProxyView;
86class GrProgramInfo;
88class SkArenaAlloc;
89enum class GrXferBarrierFlags;
90struct GrContextOptions;
91
92static constexpr int kImageWH = 32;
94static constexpr int kNoID = -1;
95
99
100static std::unique_ptr<skgpu::ganesh::SurfaceDrawContext> new_SDC(GrRecordingContext* rContext,
101 int wh) {
104 nullptr,
106 {wh, wh},
108 /* label= */ {},
109 /* sampleCnt= */ 1,
110 skgpu::Mipmapped::kNo,
111 GrProtected::kNo,
114}
115
116static void create_view_key(skgpu::UniqueKey* key, int wh, int id) {
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
127static void create_vert_key(skgpu::UniqueKey* key, int wh, int id) {
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
138static 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.
145static SkBitmap create_bitmap(int wh) {
147
148 bitmap.allocPixels(default_ii(wh));
149
150 SkCanvas tmp(bitmap);
151 tmp.clear(SK_ColorWHITE);
152
153 SkPaint blue;
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
164public:
165 struct Stats {
166 int fCacheHits = 0;
168
172 };
173
176 : fDContext(dContext)
177 , fIsNewerBetter(isNewerBetter) {
179 SkAssertResult(fDst);
180
181 GrSurfaceCharacterization characterization;
182 SkAssertResult(fDst->characterize(&characterization));
183
184 fRecorder1 = std::make_unique<GrDeferredDisplayListRecorder>(characterization);
186
187 fRecorder2 = std::make_unique<GrDeferredDisplayListRecorder>(characterization);
189
191 }
192
194 fDContext->flush();
195 fDContext->submit(GrSyncCpu::kYes);
196 }
197
198 Stats* stats() { return &fStats; }
199
200 int numCacheEntries() const { return this->threadSafeCache()->numEntries(); }
201
202 GrDirectContext* dContext() { return fDContext; }
203
204 SkCanvas* liveCanvas() { return fDst ? fDst->getCanvas() : nullptr; }
205 SkCanvas* ddlCanvas1() { return fRecorder1 ? fRecorder1->getCanvas() : nullptr; }
207 if (fRecorder1) {
208 sk_sp<GrDeferredDisplayList> tmp = fRecorder1->detach();
209 fRecorder1 = nullptr;
210 return tmp;
211 }
212
213 return nullptr;
214 }
215 SkCanvas* ddlCanvas2() { return fRecorder2 ? fRecorder2->getCanvas() : nullptr; }
217 if (fRecorder2) {
218 sk_sp<GrDeferredDisplayList> tmp = fRecorder2->detach();
219 fRecorder2 = nullptr;
220 return tmp;
221 }
222
223 return nullptr;
224 }
225
227 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
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
248
249 sdc->drawTexture(nullptr,
250 view,
252 GrSamplerState::Filter::kNearest,
253 GrSamplerState::MipmapMode::kNone,
255 {1.0f, 1.0f, 1.0f, 1.0f},
256 SkRect::MakeWH(wh, wh),
257 SkRect::MakeWH(wh, wh),
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.
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
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();
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
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
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
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
404 SkBitmap actual;
405
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
427 return this->checkImage(reporter, fDst);
428 }
429
431 sk_sp<SkSurface> tmp =
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
444 size_t gpuSize(int wh) const {
446 GrRenderable::kNo);
447
449 {wh, wh},
450 /*colorSamplesPerPixel=*/1,
451 skgpu::Mipmapped::kNo,
452 /*binSize=*/false);
453 }
454
455private:
456 static GrSurfaceProxyView AccessCachedView(GrRecordingContext*,
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,
465
466 Stats fStats;
467 GrDirectContext* fDContext = nullptr;
469
470 sk_sp<SkSurface> fDst;
471 std::unique_ptr<GrDeferredDisplayListRecorder> fRecorder1;
472 std::unique_ptr<GrDeferredDisplayListRecorder> fRecorder2;
473};
474
476public:
478
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
487 const GrThreadSafeCache::VertexData* vertexData() const { return fVertexData.get(); }
488
489private:
490 friend class GrOp; // for ctor
491
492 GrThreadSafeVertexTestOp(GrRecordingContext* rContext, TestHelper::Stats* stats, int wh, int id,
493 bool failLookup, bool failFillingIn,
495 : INHERITED(ClassID())
496 , fStats(stats)
497 , fWH(wh)
498 , fID(id)
499 , fFailFillingIn(failFillingIn)
500 , fIsNewerBetter(isNewerBetter) {
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
509 const char* name() const override { return "GrThreadSafeVertexTestOp"; }
514
515 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,
536 renderPassXferBarriers, colorLoadOp);
537 }
538
539 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
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
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(),
605 fVertexData->setGpuBuffer(std::move(tmp));
606 }
607 }
608 }
609
611 const GrSurfaceProxyView& writeView,
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
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
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;
662
664 GrProgramInfo* fProgramInfo = nullptr;
665
666 using INHERITED = GrDrawOp;
667};
668
670 int wh, int id,
671 bool failLookup, bool failFillingIn,
672 GrThreadSafeVertexTestOp** createdOp) {
673 auto rContext = canvas->recordingContext();
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
687GrSurfaceProxyView 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(),
701 return {std::move(proxy), kImageOrigin, swizzle};
702}
703
704bool TestHelper::FillInViewOnGpu(GrDirectContext* dContext, int wh, Stats* stats,
705 const GrSurfaceProxyView& lazyView,
707 std::unique_ptr<skgpu::ganesh::SurfaceDrawContext> sdc = new_SDC(dContext, wh);
708
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
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
726GrSurfaceProxyView TestHelper::AccessCachedView(GrRecordingContext* rContext,
727 GrThreadSafeCache* threadSafeCache,
728 int wh,
729 bool failLookup, bool failFillingIn, int id,
730 Stats* stats) {
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(
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
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.
764 return {};
765 }
766
767 return view;
768 } else {
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
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
795 TestHelper::addAccessFP addAccess,
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
812
813 helper.checkImage(reporter, helper.snap1());
814 helper.checkImage(reporter, helper.snap2());
815}
816
818 reporter,
819 ctxInfo,
822}
823
825 reporter,
826 ctxInfo,
829}
830
831// Case 2: ensure that, if the direct context version wins, its result is reused by the
832// DDL recorders
834 TestHelper::addAccessFP addAccess,
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
855
856 helper.checkImage(reporter);
857 helper.checkImage(reporter, helper.snap1());
858 helper.checkImage(reporter, helper.snap2());
859}
860
862 reporter,
863 ctxInfo,
866}
867
869 reporter,
870 ctxInfo,
873}
874
875// Case 3: ensure that, if the cpu-version wins, its result is reused by the direct context
877 TestHelper::addAccessFP addAccess,
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
894
895 helper.checkImage(reporter);
896 helper.checkImage(reporter, helper.snap1());
897}
898
900 reporter,
901 ctxInfo,
904}
905
907 reporter,
908 ctxInfo,
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
916 TestHelper::addAccessFP addAccess,
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
934
935 helper.checkImage(reporter, helper.snap1());
936 helper.checkImage(reporter, helper.snap2());
937}
938
940 reporter,
941 ctxInfo,
944}
945
947 reporter,
948 ctxInfo,
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.
956 TestHelper::addAccessFP addAccess,
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
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
979
980 helper.checkImage(reporter);
981 helper.checkImage(reporter, helper.snap1());
982}
983
985 reporter,
986 ctxInfo,
988 test_4_5(ctxInfo.directContext(), reporter,
990}
991
993 reporter,
994 ctxInfo,
996 test_4_5(ctxInfo.directContext(), reporter,
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
1003 TestHelper::addAccessFP addAccess,
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
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
1028}
1029
1031 reporter,
1032 ctxInfo,
1034 test_4_75(ctxInfo.directContext(), reporter,
1036}
1037
1039 reporter,
1040 ctxInfo,
1042 test_4_75(ctxInfo.directContext(), reporter,
1044}
1045
1046// Case 5: ensure that expanding the map works (esp. wrt custom data)
1048 TestHelper::addAccessFP addAccess,
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
1076 reporter,
1077 ctxInfo,
1079 test_5(ctxInfo.directContext(), reporter, &TestHelper::addViewAccess, &TestHelper::checkView);
1080}
1081
1083 reporter,
1084 ctxInfo,
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.
1092 TestHelper::addAccessFP addAccess,
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
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
1119
1120 REPORTER_ASSERT(reporter, (helper.*check)(nullptr, kImageWH,
1121 /*hits*/ 1, /*misses*/ 1, /*refs*/ 0, kNoID));
1122}
1123
1125 reporter,
1126 ctxInfo,
1128 test_6(ctxInfo.directContext(), reporter, &TestHelper::addViewAccess, &TestHelper::checkView);
1129}
1130
1132 reporter,
1133 ctxInfo,
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.
1141 TestHelper::addAccessFP addAccess,
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
1157
1158 helper.threadSafeCache()->dropUniqueRefs(nullptr);
1160
1161 ddl1 = nullptr;
1162
1163 helper.threadSafeCache()->dropUniqueRefs(nullptr);
1165 REPORTER_ASSERT(reporter, (helper.*check)(nullptr, 2*kImageWH,
1166 /*hits*/ 0, /*misses*/ 2, /*refs*/ 1, kNoID));
1167
1168 helper.threadSafeCache()->dropAllRefs();
1170
1171 ddl2 = nullptr;
1172}
1173
1175 reporter,
1176 ctxInfo,
1178 test_7(ctxInfo.directContext(), reporter, &TestHelper::addViewAccess, &TestHelper::checkView);
1179}
1180
1182 reporter,
1183 ctxInfo,
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.
1192 TestHelper::addAccessFP addAccess,
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
1214
1215 dContext->abandonContext(); // This should exercise dropAllRefs
1216
1217 sk_sp<GrDeferredDisplayList> ddl2 = helper.snap2();
1218
1220
1221 ddl1 = nullptr;
1222 ddl2 = nullptr;
1223}
1224
1226 reporter,
1227 ctxInfo,
1229 test_8(ctxInfo.directContext(), reporter, &TestHelper::addViewAccess, &TestHelper::checkView);
1230}
1231
1233 reporter,
1234 ctxInfo,
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.
1243 TestHelper::addAccessFP addAccess,
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
1265
1266 dContext->releaseResourcesAndAbandonContext(); // This should hit dropAllRefs
1267
1268 sk_sp<GrDeferredDisplayList> ddl2 = helper.snap2();
1269
1271
1272 ddl1 = nullptr;
1273 ddl2 = nullptr;
1274}
1275
1277 reporter,
1278 ctxInfo,
1280 test_9(ctxInfo.directContext(), reporter, &TestHelper::addViewAccess, &TestHelper::checkView);
1281}
1282
1284 reporter,
1285 ctxInfo,
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.
1294 TestHelper::addAccessFP addAccess,
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
1328
1329 ddl1 = nullptr;
1330 ddl2 = nullptr;
1331
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
1351
1352 REPORTER_ASSERT(reporter, (helper.*check)(helper.liveCanvas(), 2*kImageWH,
1353 /*hits*/ 2, /*misses*/ 2, /*refs*/ 0, kNoID));
1354}
1355
1357 reporter,
1358 ctxInfo,
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.
1375 TestHelper::addAccessFP addAccess,
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
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
1399
1401
1403
1405}
1406
1408 reporter,
1409 ctxInfo,
1412}
1413
1415 reporter,
1416 ctxInfo,
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.
1425 TestHelper::addAccessFP addAccess,
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
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
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
1461}
1462
1464 reporter,
1465 ctxInfo,
1468}
1469
1471 reporter,
1472 ctxInfo,
1475}
1476
1477// Case 13: Test out the 'msNotUsed' parameter to GrContext::performDeferredCleanup.
1479 TestHelper::addAccessFP addAccess,
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
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
1510 REPORTER_ASSERT(reporter, (helper.*check)(helper.liveCanvas(), 2*kImageWH,
1511 /*hits*/ 0, /*misses*/ 2, /*refs*/ 0, kNoID));
1512}
1513
1515 reporter,
1516 ctxInfo,
1519}
1520
1522 reporter,
1523 ctxInfo,
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.
1531 reporter,
1532 ctxInfo,
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
1569 TestHelper::addAccessFP addAccess,
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
1581
1583 (*create_key)(&key, kImageWH, kNoID);
1584
1586 /* inThreadSafeCache */ true);
1587
1589
1590 // This purge call is needed to process the invalidation messages
1592
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
1601
1602 helper.checkImage(reporter, std::move(ddl1));
1603 helper.checkImage(reporter, std::move(ddl2));
1604}
1605
1607 reporter,
1608 ctxInfo,
1612}
1613
1615 reporter,
1616 ctxInfo,
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
1628static bool newer_is_always_better(SkData* /* incumbent */, SkData* /* challenger */) {
1629 return true;
1630}
1631
1633 reporter,
1634 ctxInfo,
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);
1645 /*hits*/ 0, /*misses*/ 1, /*refs*/ 1, kNoID));
1646 sk_sp<GrDeferredDisplayList> ddl1 = helper.snap1();
1647
1648 {
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);
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 {
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}
reporter
#define DEFINE_OP_CLASS_ID
Definition GrOp.h:64
static void test_2(GrDirectContext *dContext, skiatest::Reporter *reporter, TestHelper::addAccessFP addAccess, TestHelper::checkFP check)
static constexpr auto kImageOrigin
static void test_11(GrDirectContext *dContext, skiatest::Reporter *reporter, TestHelper::addAccessFP addAccess, TestHelper::checkFP check)
static SkImageInfo default_ii(int wh)
static void test_8(GrDirectContext *dContext, skiatest::Reporter *reporter, TestHelper::addAccessFP addAccess, TestHelper::checkFP check)
static bool default_is_newer_better(SkData *incumbent, SkData *challenger)
static constexpr int kNoID
static void test_5(GrDirectContext *dContext, skiatest::Reporter *reporter, TestHelper::addAccessFP addAccess, TestHelper::checkFP check)
static void test_15(GrDirectContext *dContext, skiatest::Reporter *reporter, TestHelper::addAccessFP addAccess, TestHelper::checkFP check, void(*create_key)(skgpu::UniqueKey *, int wh, int id))
static void create_view_key(skgpu::UniqueKey *key, int wh, int id)
static void test_4_5(GrDirectContext *dContext, skiatest::Reporter *reporter, TestHelper::addAccessFP addAccess, TestHelper::checkFP check)
static void test_13(GrDirectContext *dContext, skiatest::Reporter *reporter, TestHelper::addAccessFP addAccess, TestHelper::checkFP check)
static bool newer_is_always_better(SkData *, SkData *)
static void test_6(GrDirectContext *dContext, skiatest::Reporter *reporter, TestHelper::addAccessFP addAccess, TestHelper::checkFP check)
static void test_12(GrDirectContext *dContext, skiatest::Reporter *reporter, TestHelper::addAccessFP addAccess, TestHelper::checkFP check)
static void test_7(GrDirectContext *dContext, skiatest::Reporter *reporter, TestHelper::addAccessFP addAccess, TestHelper::checkFP check)
static std::unique_ptr< skgpu::ganesh::SurfaceDrawContext > new_SDC(GrRecordingContext *rContext, int wh)
static void test_4_75(GrDirectContext *dContext, skiatest::Reporter *reporter, TestHelper::addAccessFP addAccess, TestHelper::checkFP check)
static constexpr int kImageWH
static void test_10(GrDirectContext *dContext, skiatest::Reporter *reporter, TestHelper::addAccessFP addAccess, TestHelper::checkFP check)
static SkBitmap create_bitmap(int wh)
static void test_1(GrDirectContext *dContext, skiatest::Reporter *reporter, TestHelper::addAccessFP addAccess, TestHelper::checkFP check)
static void test_9(GrDirectContext *dContext, skiatest::Reporter *reporter, TestHelper::addAccessFP addAccess, TestHelper::checkFP check)
static void create_vert_key(skgpu::UniqueKey *key, int wh, int id)
static void test_3(GrDirectContext *dContext, skiatest::Reporter *reporter, TestHelper::addAccessFP addAccess, TestHelper::checkFP check)
static void test_4(GrDirectContext *dContext, skiatest::Reporter *reporter, TestHelper::addAccessFP addAccess, TestHelper::checkFP check)
GrClampType
@ kStatic_GrAccessPattern
GrLoadOp
@ kBottomLeft_GrSurfaceOrigin
Definition GrTypes.h:149
GrXferBarrierFlags
SkColor4f color
#define check(reporter, ref, unref, make, kill)
@ kPremul_SkAlphaType
pixel components are premultiplied by alpha
Definition SkAlphaType.h:29
#define SkAssertResult(cond)
Definition SkAssert.h:123
#define SkASSERT(cond)
Definition SkAssert.h:116
@ kSrcOver
r = s + (1-sa)*d
@ kRGBA_8888_SkColorType
pixel with 8 bits for red, green, blue, alpha; in 32-bit word
Definition SkColorType.h:24
constexpr SkColor SK_ColorBLUE
Definition SkColor.h:135
constexpr SkColor SK_ColorWHITE
Definition SkColor.h:122
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
#define SkDEBUGCODE(...)
Definition SkDebug.h:23
static void * sk_malloc_throw(size_t size)
Definition SkMalloc.h:67
static SkPath clip(const SkPath &path, const SkHalfPlane &plane)
Definition SkPath.cpp:3824
#define INHERITED(method,...)
bool ComparePixels(const GrCPixmap &a, const GrCPixmap &b, const float tolRGBA[4], std::function< ComparePixmapsErrorReporter > &error)
#define REPORTER_ASSERT(r, cond,...)
Definition Test.h:286
#define ERRORF(r,...)
Definition Test.h:293
#define DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(name, reporter, context_info, ctsEnforcement)
Definition Test.h:434
Type::kYUV Type::kRGBA() int(0.7 *637)
static GrAppliedClip Disabled()
uint32_t contextID() const
const GrCaps * caps() const
skgpu::Swizzle getReadSwizzle(const GrBackendFormat &format, GrColorType colorType) const
Definition GrCaps.cpp:443
virtual GrDirectContext * asDirectContext()
SK_API GrBackendApi backend() const
SK_API GrBackendFormat defaultBackendFormat(SkColorType, GrRenderable) const
GrResourceProvider * resourceProvider()
GrResourceCache * getResourceCache()
bool submit(GrSyncCpu sync=GrSyncCpu::kNo)
void releaseResourcesAndAbandonContext()
GrSemaphoresSubmitted flush(const GrFlushInfo &info)
GrDirectContextPriv priv()
void purgeUnlockedResources(size_t bytesToPurge, bool preferScratchResources)
void performDeferredCleanup(std::chrono::milliseconds msNotUsed, GrPurgeResourceOptions opts=GrPurgeResourceOptions::kAllResources)
void abandonContext() override
void setResourceCacheLimit(size_t maxResourceBytes)
FixedFunctionFlags
Definition GrDrawOp.h:104
GrDirectContext * getContext()
Definition GrGpu.h:67
GrLoadOp colorLoadOp() const final
const GrDstProxyView & dstProxyView() const final
GrXferBarrierFlags renderPassBarriers() const final
SkArenaAlloc * allocator() override
void bindBuffers(sk_sp< const GrBuffer > indexBuffer, sk_sp< const GrBuffer > instanceBuffer, sk_sp< const GrBuffer > vertexBuffer, GrPrimitiveRestart primitiveRestart=GrPrimitiveRestart::kNo)
const GrSurfaceProxyView & writeView() const final
GrAppliedClip detachAppliedClip() final
const GrCaps & caps() const final
void bindPipeline(const GrProgramInfo &programInfo, const SkRect &drawBounds)
void draw(int vertexCount, int baseVertex)
bool usesMSAASurface() const final
Definition GrOp.h:70
std::unique_ptr< GrOp > Owner
Definition GrOp.h:72
void setBounds(const SkRect &newBounds, HasAABloat aabloat, IsHairline zeroArea)
Definition GrOp.h:279
static constexpr Analysis EmptySetAnalysis()
sk_sp< GrTextureProxy > findProxyByUniqueKey(const skgpu::UniqueKey &)
sk_sp< GrTextureProxy > createProxyFromBitmap(const SkBitmap &, skgpu::Mipmapped, SkBackingFit, skgpu::Budgeted)
GrProxyProvider * proxyProvider()
GrThreadSafeCache * threadSafeCache()
void recordProgramInfo(const GrProgramInfo *programInfo)
SkArenaAlloc * recordTimeAllocator()
GrRecordingContextPriv priv()
static const GrStyle & SimpleFill()
Definition GrStyle.h:30
skgpu::Swizzle swizzle() const
GrSurfaceOrigin origin() const
GrRenderTargetProxy * asRenderTargetProxy() const
static size_t ComputeSize(const GrBackendFormat &, SkISize dimensions, int colorSamplesPerPixel, skgpu::Mipmapped, bool binSize=false)
Definition GrSurface.cpp:21
void setGpuBuffer(sk_sp< GrGpuBuffer > gpuBuffer)
sk_sp< GrGpuBuffer > refGpuBuffer()
bool(* IsNewerBetter)(SkData *incumbent, SkData *challenger)
static std::tuple< GrSurfaceProxyView, sk_sp< Trampoline > > CreateLazyView(GrDirectContext *, GrColorType, SkISize dimensions, GrSurfaceOrigin, SkBackingFit)
SkDEBUGCODE(bool has(const skgpu::UniqueKey &) SK_EXCLUDES(fSpinLock);) GrSurfaceProxyView find(const skgpu std::tuple< GrSurfaceProxyView, sk_sp< SkData > > findWithData(const skgpu::UniqueKey &) SK_EXCLUDES(fSpinLock)
std::tuple< sk_sp< VertexData >, sk_sp< SkData > > findVertsWithData(const skgpu::UniqueKey &) SK_EXCLUDES(fSpinLock)
void remove(const skgpu::UniqueKey &) SK_EXCLUDES(fSpinLock)
std::tuple< GrSurfaceProxyView, sk_sp< SkData > > findOrAddWithData(const skgpu::UniqueKey &, const GrSurfaceProxyView &) SK_EXCLUDES(fSpinLock)
std::tuple< GrSurfaceProxyView, sk_sp< SkData > > addWithData(const skgpu::UniqueKey &, const GrSurfaceProxyView &) SK_EXCLUDES(fSpinLock)
void dropUniqueRefs(GrResourceCache *resourceCache) SK_EXCLUDES(fSpinLock)
static sk_sp< VertexData > MakeVertexData(const void *vertices, int vertexCount, size_t vertexSize)
void dropAllRefs() SK_EXCLUDES(fSpinLock)
FixedFunctionFlags fixedFunctionFlags() const override
const char * name() const override
void onPrePrepare(GrRecordingContext *rContext, const GrSurfaceProxyView &writeView, GrAppliedClip *clip, const GrDstProxyView &dstProxyView, GrXferBarrierFlags renderPassXferBarriers, GrLoadOp colorLoadOp) override
static DEFINE_OP_CLASS_ID GrOp::Owner Make(GrRecordingContext *rContext, TestHelper::Stats *stats, int wh, int id, bool failLookup, bool failFillingIn, GrThreadSafeCache::IsNewerBetter isNewerBetter)
void onPrepare(GrOpFlushState *flushState) override
GrProcessorSet::Analysis finalize(const GrCaps &, const GrAppliedClip *, GrClampType) override
const GrThreadSafeCache::VertexData * vertexData() const
void onExecute(GrOpFlushState *flushState, const SkRect &chainBounds) override
void allocPixels(const SkImageInfo &info, size_t rowBytes)
Definition SkBitmap.cpp:258
const SkPixmap & pixmap() const
Definition SkBitmap.h:133
void drawRect(const SkRect &rect, const SkPaint &paint)
virtual GrRecordingContext * recordingContext() const
@ kFast_SrcRectConstraint
sample outside bounds; faster
Definition SkCanvas.h:1543
void clear(SkColor color)
Definition SkCanvas.h:1199
static sk_sp< SkData > MakeWithCopy(const void *data, size_t length)
Definition SkData.cpp:111
static const SkMatrix & I()
static void Post(Message m)
void setColor(SkColor color)
Definition SkPaint.cpp:119
void setAntiAlias(bool aa)
Definition SkPaint.h:170
bool nextBool()
Definition SkRandom.h:117
SkCanvas * getCanvas()
Definition SkSurface.cpp:82
bool characterize(GrSurfaceCharacterization *characterization) const
bool checkVert(SkCanvas *canvas, int wh, int expectedHits, int expectedMisses, int expectedNumRefs, int expectedID)
SkCanvas * ddlCanvas1()
GrDirectContext * dContext()
void addVertAccess(SkCanvas *canvas, int wh, int id=kNoID, bool failLookup=false, bool failFillingIn=false)
SkCanvas * ddlCanvas2()
int numCacheEntries() const
bool checkImage(skiatest::Reporter *reporter, sk_sp< GrDeferredDisplayList > ddl)
GrThreadSafeCache * threadSafeCache()
void(TestHelper::* addAccessFP)(SkCanvas *, int wh, int id, bool failLookUp, bool failFillingIn)
TestHelper(GrDirectContext *dContext, GrThreadSafeCache::IsNewerBetter isNewerBetter=default_is_newer_better)
void addVertAccess(SkCanvas *canvas, int wh, int id, bool failLookup, bool failFillingIn, GrThreadSafeVertexTestOp **createdOp)
sk_sp< GrDeferredDisplayList > snap2()
void addViewAccess(SkCanvas *canvas, int wh, int id=kNoID, bool failLookup=false, bool failFillingIn=false)
bool checkImage(skiatest::Reporter *reporter)
bool checkView(SkCanvas *canvas, int wh, int expectedHits, int expectedMisses, int expectedNumRefs, int expectedID)
const GrThreadSafeCache * threadSafeCache() const
bool(TestHelper::* checkFP)(SkCanvas *, int wh, int expectedHits, int expectedMisses, int expectedNumRefs, int expectedID)
SkCanvas * liveCanvas()
sk_sp< GrDeferredDisplayList > snap1()
bool checkImage(skiatest::Reporter *reporter, const sk_sp< SkSurface > &s)
size_t gpuSize(int wh) const
T * get() const
Definition SkRefCnt.h:303
static Domain GenerateDomain()
static std::unique_ptr< SurfaceDrawContext > Make(GrRecordingContext *, GrColorType, sk_sp< GrSurfaceProxy >, sk_sp< SkColorSpace >, GrSurfaceOrigin, const SkSurfaceProps &)
const Paint & paint
struct MyStruct s
static const uint8_t buffer[]
const uint8_t uint32_t uint32_t GError ** error
GAsyncResult * result
uint32_t uint32_t * format
double y
double x
GrGeometryProcessor * MakeForDeviceSpace(SkArenaAlloc *, const Color &, const Coverage &, const LocalCoords &, const SkMatrix &viewMatrix)
constexpr SkColor4f kWhite
Definition SkColor.h:439
SK_API sk_sp< SkSurface > RenderTarget(GrRecordingContext *context, skgpu::Budgeted budgeted, const SkImageInfo &imageInfo, int sampleCount, GrSurfaceOrigin surfaceOrigin, const SkSurfaceProps *surfaceProps, bool shouldCreateWithMips=false, bool isProtected=false)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition switches.h:41
SurfaceDrawContext * TopDeviceSurfaceDrawContext(const SkCanvas *canvas)
Definition GrCanvas.cpp:20
SK_API bool DrawDDL(SkSurface *, sk_sp< const GrDeferredDisplayList > ddl)
static SkImageInfo Make(int width, int height, SkColorType ct, SkAlphaType at)
void set(float x, float y)
static SkRect MakeIWH(int w, int h)
Definition SkRect.h:623
static constexpr SkRect MakeWH(float w, float h)
Definition SkRect.h:609
const uintptr_t id