Flutter Engine
The Flutter Engine
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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
96static SkImageInfo default_ii(int wh) {
98}
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,
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,
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 {
447
449 {wh, wh},
450 /*colorSamplesPerPixel=*/1,
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
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"; }
510 FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
511 GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*, GrClampType) override {
513 }
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
610 void onPrePrepare(GrRecordingContext* rContext,
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(
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
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.
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();
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
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();
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();
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();
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
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);
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
Definition: FontMgrTest.cpp:39
SkAssertResult(font.textToGlyphs("Hello", 5, SkTextEncoding::kUTF8, glyphs, std::size(glyphs))==count)
#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)
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(GrThreadSafeCache1View, reporter, ctxInfo, CtsEnforcement::kNever)
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
Definition: GrTypesPriv.h:228
@ kStatic_GrAccessPattern
Definition: GrTypesPriv.h:428
GrLoadOp
Definition: GrTypesPriv.h:155
@ kBottomLeft_GrSurfaceOrigin
Definition: GrTypes.h:149
GrXferBarrierFlags
#define check(reporter, ref, unref, make, kill)
Definition: RefCntTest.cpp:85
@ kPremul_SkAlphaType
pixel components are premultiplied by alpha
Definition: SkAlphaType.h:29
#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
static void * sk_malloc_throw(size_t size)
Definition: SkMalloc.h:67
static SkPath clip(const SkPath &path, const SkHalfPlane &plane)
Definition: SkPath.cpp:3892
#define INHERITED(method,...)
Definition: SkRecorder.cpp:128
SkDEBUGCODE(SK_SPI) SkThreadID SkGetThreadID()
bool ComparePixels(const GrCPixmap &a, const GrCPixmap &b, const float tolRGBA[4], std::function< ComparePixmapsErrorReporter > &error)
Definition: TestUtils.cpp:142
#define REPORTER_ASSERT(r, cond,...)
Definition: Test.h:286
#define ERRORF(r,...)
Definition: Test.h:293
uint32_t contextID() const
const GrCaps * caps() const
Definition: GrCaps.h:57
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)
GrDrawOp(uint32_t classID)
Definition: GrDrawOp.h:31
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
virtual void onExecute(GrOpFlushState *, const SkRect &chainBounds)=0
std::unique_ptr< GrOp > Owner
Definition: GrOp.h:72
virtual void onPrepare(GrOpFlushState *)=0
virtual const char * name() const =0
virtual void onPrePrepare(GrRecordingContext *, const GrSurfaceProxyView &writeView, GrAppliedClip *, const GrDstProxyView &, GrXferBarrierFlags renderPassXferBarriers, GrLoadOp colorLoadOp)=0
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
const GrBackendFormat & backendFormat() 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)
static DEFINE_OP_CLASS_ID GrOp::Owner Make(GrRecordingContext *rContext, TestHelper::Stats *stats, int wh, int id, bool failLookup, bool failFillingIn, GrThreadSafeCache::IsNewerBetter isNewerBetter)
const GrThreadSafeCache::VertexData * vertexData() const
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)
Definition: SkCanvas.cpp:1673
virtual GrRecordingContext * recordingContext() const
Definition: SkCanvas.cpp:1637
@ kFast_SrcRectConstraint
sample outside bounds; faster
Definition: SkCanvas.h:1543
void clear(SkColor color)
Definition: SkCanvas.h:1199
Definition: SkData.h:25
static sk_sp< SkData > MakeWithCopy(const void *data, size_t length)
Definition: SkData.cpp:111
static const SkMatrix & I()
Definition: SkMatrix.cpp:1544
static void Post(Message m)
Definition: SkMessageBus.h:130
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
Definition: SkSurface.cpp:239
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()
Definition: ResourceKey.cpp:27
static std::unique_ptr< SurfaceDrawContext > Make(GrRecordingContext *, GrColorType, sk_sp< GrSurfaceProxy >, sk_sp< SkColorSpace >, GrSurfaceOrigin, const SkSurfaceProps &)
const Paint & paint
Definition: color_source.cc:38
DlColor color
struct MyStruct s
if(end==-1)
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)
Definition: bitmap.py:1
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace Enable an endless trace buffer The default is a ring buffer This is useful when very old events need to viewed For during application launch Memory usage will continue to grow indefinitely however Start app with an specific route defined on the framework flutter assets Path to the Flutter assets directory enable service port Allow the VM service to fallback to automatic port selection if binding to a specified port fails trace Trace early application lifecycle Automatically switches to an endless trace buffer trace skia Filters out all Skia trace event categories except those that are specified in this comma separated list dump skp on shader Automatically dump the skp that triggers new shader compilations This is useful for writing custom ShaderWarmUp to reduce jank By this is not enabled to reduce the overhead purge persistent cache
Definition: switches.h:191
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
Definition: switches.h:126
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
dictionary stats
Definition: malisc.py:20
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)
Definition: SkPoint_impl.h:200
static SkRect MakeIWH(int w, int h)
Definition: SkRect.h:623
static constexpr SkRect MakeWH(float w, float h)
Definition: SkRect.h:609
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63
const uintptr_t id