1 /* 2 * Copyright 2019 Google LLC. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #include "gm/gm.h" 9 10 #include "include/core/SkPath.h" 11 #include "include/gpu/GrContextOptions.h" 12 #include "include/gpu/GrRecordingContext.h" 13 #include "src/gpu/GrDirectContextPriv.h" 14 #include "src/gpu/GrDrawingManager.h" 15 #include "src/gpu/GrRecordingContextPriv.h" 16 #include "src/gpu/GrRenderTargetContext.h" 17 #include "src/gpu/ccpr/GrCCPathCache.h" 18 #include "src/gpu/ccpr/GrCoverageCountingPathRenderer.h" 19 #include "tools/ToolUtils.h" 20 21 namespace skiagm { 22 23 #define ERR_MSG_ASSERT(COND) \ 24 do { \ 25 if (!(COND)) { \ 26 errorMsg->printf("preservefillrule.cpp(%i): assert(%s)", \ 27 __LINE__, #COND); \ 28 return DrawResult::kFail; \ 29 } \ 30 } while (false) 31 32 33 /** 34 * This test ensures that the ccpr path cache preserves fill rules properly, both in the case where 35 * we copy paths into a8 literal coverage atlases, as well as in the case where we just reuse a 36 * stashed fp16 coverage count atlas. 37 */ 38 class PreserveFillRuleGM : public GpuGM { 39 public: 40 // fStarSize affects whether ccpr copies the paths to an a8 literal coverage atlas, or just 41 // leaves them stashed in an fp16 coverage count atlas. The threshold for copying to a8 is 42 // currently 256x256 total pixels copied. If this ever changes, there is code in onDraw that 43 // will detect the unexpected behavior and draw a failure message. PreserveFillRuleGM(bool literalCoverageAtlas)44 PreserveFillRuleGM(bool literalCoverageAtlas) 45 : fLiteralCoverageAtlas(literalCoverageAtlas) 46 , fStarSize((fLiteralCoverageAtlas) ? 200 : 20) { 47 } 48 49 private: onShortName()50 SkString onShortName() override { 51 SkString name("preservefillrule"); 52 name += (fLiteralCoverageAtlas) ? "_big" : "_little"; 53 return name; 54 } onISize()55 SkISize onISize() override { return SkISize::Make(fStarSize * 2, fStarSize * 2); } 56 modifyGrContextOptions(GrContextOptions * ctxOptions)57 void modifyGrContextOptions(GrContextOptions* ctxOptions) override { 58 ctxOptions->fGpuPathRenderers = GpuPathRenderers::kCoverageCounting; 59 ctxOptions->fAllowPathMaskCaching = true; 60 } 61 onDraw(GrRecordingContext * rContext,GrRenderTargetContext * rtc,SkCanvas * canvas,SkString * errorMsg)62 DrawResult onDraw(GrRecordingContext* rContext, GrRenderTargetContext* rtc, SkCanvas* canvas, 63 SkString* errorMsg) override { 64 using CoverageType = GrCCAtlas::CoverageType; 65 66 if (rtc->numSamples() > 1) { 67 errorMsg->set("ccpr is currently only used for coverage AA"); 68 return DrawResult::kSkip; 69 } 70 71 auto* ccpr = rContext->priv().drawingManager()->getCoverageCountingPathRenderer(); 72 if (!ccpr) { 73 errorMsg->set("ccpr only"); 74 return DrawResult::kSkip; 75 } 76 77 auto pathCache = ccpr->testingOnly_getPathCache(); 78 if (!pathCache) { 79 errorMsg->set("ccpr is not in caching mode. " 80 "Are you using viewer? Launch with \"--cachePathMasks true\"."); 81 return DrawResult::kFail; 82 } 83 84 auto dContext = GrAsDirectContext(rContext); 85 if (!dContext) { 86 *errorMsg = "Requires a direct context."; 87 return skiagm::DrawResult::kSkip; 88 } 89 90 auto starRect = SkRect::MakeWH(fStarSize, fStarSize); 91 SkPath star7_winding = ToolUtils::make_star(starRect, 7); 92 star7_winding.setFillType(SkPathFillType::kWinding); 93 94 SkPath star7_evenOdd = star7_winding; 95 star7_evenOdd.transform(SkMatrix::Translate(0, fStarSize)); 96 star7_evenOdd.setFillType(SkPathFillType::kEvenOdd); 97 98 SkPath star5_winding = ToolUtils::make_star(starRect, 5); 99 star5_winding.transform(SkMatrix::Translate(fStarSize, 0)); 100 star5_winding.setFillType(SkPathFillType::kWinding); 101 102 SkPath star5_evenOdd = star5_winding; 103 star5_evenOdd.transform(SkMatrix::Translate(0, fStarSize)); 104 star5_evenOdd.setFillType(SkPathFillType::kEvenOdd); 105 106 SkPaint paint; 107 paint.setColor(SK_ColorGREEN); 108 paint.setAntiAlias(true); 109 110 for (int i = 0; i < 3; ++i) { 111 canvas->clear(SK_ColorWHITE); 112 canvas->drawPath(star7_winding, paint); 113 canvas->drawPath(star7_evenOdd, paint); 114 canvas->drawPath(star5_winding, paint); 115 canvas->drawPath(star5_evenOdd, paint); 116 rtc->flush(SkSurface::BackendSurfaceAccess::kNoAccess, GrFlushInfo(), nullptr); 117 118 // Ensure the path cache is behaving in such a way that we are actually testing what we 119 // think we are. 120 int numCachedPaths = 0; 121 for (GrCCPathCacheEntry* entry : pathCache->testingOnly_getLRU()) { 122 if (0 == i) { 123 // We don't cache an atlas on the first hit. 124 ERR_MSG_ASSERT(!entry->cachedAtlas()); 125 } else { 126 // The stars should be cached in an atlas now. 127 ERR_MSG_ASSERT(entry->cachedAtlas()); 128 129 CoverageType atlasCoverageType = entry->cachedAtlas()->coverageType(); 130 if (i < 2) { 131 // We never copy to an a8 atlas before the second hit. 132 ERR_MSG_ASSERT(ccpr->coverageType() == atlasCoverageType); 133 } else if (fLiteralCoverageAtlas) { 134 // Verify fStarSize is large enough that the paths got copied to an a8 135 // atlas. 136 ERR_MSG_ASSERT(CoverageType::kA8_LiteralCoverage == atlasCoverageType); 137 } else { 138 // Verify fStarSize is small enough that the paths did *NOT* get copied to 139 // an a8 atlas. 140 ERR_MSG_ASSERT(ccpr->coverageType() == atlasCoverageType); 141 } 142 } 143 ++numCachedPaths; 144 } 145 146 if (dContext) { 147 // Verify all 4 paths are tracked by the path cache. 148 ERR_MSG_ASSERT(4 == numCachedPaths); 149 } 150 } 151 152 return DrawResult::kOk; 153 } 154 155 private: 156 const bool fLiteralCoverageAtlas; 157 const int fStarSize; 158 }; 159 160 DEF_GM( return new PreserveFillRuleGM(true); ) 161 DEF_GM( return new PreserveFillRuleGM(false); ) 162 163 } // namespace skiagm 164