1 /* 2 * Copyright 2017 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #include "SkTypes.h" 9 #include "Test.h" 10 11 #if SK_SUPPORT_GPU 12 13 #include "GrContext.h" 14 #include "GrContextPriv.h" 15 #include "GrClip.h" 16 #include "GrDrawingManager.h" 17 #include "GrPathRenderer.h" 18 #include "GrPaint.h" 19 #include "GrRenderTargetContext.h" 20 #include "GrRenderTargetContextPriv.h" 21 #include "GrShape.h" 22 #include "SkMatrix.h" 23 #include "SkPathPriv.h" 24 #include "SkRect.h" 25 #include "ccpr/GrCoverageCountingPathRenderer.h" 26 #include "mock/GrMockTypes.h" 27 #include <cmath> 28 29 static constexpr int kCanvasSize = 100; 30 31 class CCPRClip : public GrClip { 32 public: CCPRClip(GrCoverageCountingPathRenderer * ccpr,const SkPath & path)33 CCPRClip(GrCoverageCountingPathRenderer* ccpr, const SkPath& path) : fCCPR(ccpr), fPath(path) {} 34 35 private: apply(GrContext * context,GrRenderTargetContext * rtc,bool,bool,GrAppliedClip * out,SkRect * bounds) const36 bool apply(GrContext* context, GrRenderTargetContext* rtc, bool, bool, GrAppliedClip* out, 37 SkRect* bounds) const override { 38 GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider(); 39 out->addCoverageFP(fCCPR->makeClipProcessor(proxyProvider, 40 rtc->priv().testingOnly_getOpListID(), fPath, 41 SkIRect::MakeWH(rtc->width(), rtc->height()), 42 rtc->width(), rtc->height())); 43 return true; 44 } quickContains(const SkRect &) const45 bool quickContains(const SkRect&) const final { return false; } isRRect(const SkRect & rtBounds,SkRRect * rr,GrAA *) const46 bool isRRect(const SkRect& rtBounds, SkRRect* rr, GrAA*) const final { return false; } getConservativeBounds(int width,int height,SkIRect * rect,bool * iior) const47 void getConservativeBounds(int width, int height, SkIRect* rect, bool* iior) const final { 48 rect->set(0, 0, width, height); 49 if (iior) { 50 *iior = false; 51 } 52 } 53 GrCoverageCountingPathRenderer* const fCCPR; 54 const SkPath fPath; 55 }; 56 57 class CCPRPathDrawer { 58 public: CCPRPathDrawer(GrContext * ctx,skiatest::Reporter * reporter)59 CCPRPathDrawer(GrContext* ctx, skiatest::Reporter* reporter) 60 : fCtx(ctx) 61 , fCCPR(fCtx->contextPriv().drawingManager()->getCoverageCountingPathRenderer()) 62 , fRTC(fCtx->makeDeferredRenderTargetContext(SkBackingFit::kExact, kCanvasSize, 63 kCanvasSize, kRGBA_8888_GrPixelConfig, 64 nullptr)) { 65 if (!fCCPR) { 66 ERRORF(reporter, "ccpr not enabled in GrContext for ccpr tests"); 67 } 68 if (!fRTC) { 69 ERRORF(reporter, "failed to create GrRenderTargetContext for ccpr tests"); 70 } 71 } 72 valid() const73 bool valid() const { return fCCPR && fRTC; } clear() const74 void clear() const { fRTC->clear(nullptr, 0, GrRenderTargetContext::CanClearFullscreen::kYes); } abandonGrContext()75 void abandonGrContext() { fCtx = nullptr; fCCPR = nullptr; fRTC = nullptr; } 76 drawPath(SkPath path,GrColor4f color=GrColor4f (0,1,0,1)) const77 void drawPath(SkPath path, GrColor4f color = GrColor4f(0, 1, 0, 1)) const { 78 SkASSERT(this->valid()); 79 80 GrPaint paint; 81 paint.setColor4f(color); 82 83 GrNoClip noClip; 84 SkIRect clipBounds = SkIRect::MakeWH(kCanvasSize, kCanvasSize); 85 86 SkMatrix matrix = SkMatrix::I(); 87 88 path.setIsVolatile(true); 89 GrShape shape(path); 90 91 fCCPR->drawPath({fCtx, std::move(paint), &GrUserStencilSettings::kUnused, fRTC.get(), 92 &noClip, &clipBounds, &matrix, &shape, GrAAType::kCoverage, false}); 93 } 94 clipFullscreenRect(SkPath clipPath,GrColor4f color=GrColor4f (0,1,0,1))95 void clipFullscreenRect(SkPath clipPath, GrColor4f color = GrColor4f(0, 1, 0, 1)) { 96 SkASSERT(this->valid()); 97 98 GrPaint paint; 99 paint.setColor4f(color); 100 101 fRTC->drawRect(CCPRClip(fCCPR, clipPath), std::move(paint), GrAA::kYes, SkMatrix::I(), 102 SkRect::MakeIWH(kCanvasSize, kCanvasSize)); 103 } 104 flush() const105 void flush() const { 106 SkASSERT(this->valid()); 107 fCtx->flush(); 108 } 109 110 private: 111 GrContext* fCtx; 112 GrCoverageCountingPathRenderer* fCCPR; 113 sk_sp<GrRenderTargetContext> fRTC; 114 }; 115 116 class CCPRTest { 117 public: run(skiatest::Reporter * reporter)118 void run(skiatest::Reporter* reporter) { 119 GrMockOptions mockOptions; 120 mockOptions.fInstanceAttribSupport = true; 121 mockOptions.fMapBufferFlags = GrCaps::kCanMap_MapFlag; 122 mockOptions.fConfigOptions[kAlpha_half_GrPixelConfig].fRenderable[0] = true; 123 mockOptions.fConfigOptions[kAlpha_half_GrPixelConfig].fTexturable = true; 124 mockOptions.fGeometryShaderSupport = true; 125 mockOptions.fIntegerSupport = true; 126 mockOptions.fFlatInterpolationSupport = true; 127 128 GrContextOptions ctxOptions; 129 ctxOptions.fAllowPathMaskCaching = false; 130 ctxOptions.fGpuPathRenderers = GpuPathRenderers::kCoverageCounting; 131 132 fMockContext = GrContext::MakeMock(&mockOptions, ctxOptions); 133 if (!fMockContext) { 134 ERRORF(reporter, "could not create mock context"); 135 return; 136 } 137 if (!fMockContext->unique()) { 138 ERRORF(reporter, "mock context is not unique"); 139 return; 140 } 141 142 CCPRPathDrawer ccpr(fMockContext.get(), reporter); 143 if (!ccpr.valid()) { 144 return; 145 } 146 147 fPath.moveTo(0, 0); 148 fPath.cubicTo(50, 50, 0, 50, 50, 0); 149 this->onRun(reporter, ccpr); 150 } 151 ~CCPRTest()152 virtual ~CCPRTest() {} 153 154 protected: 155 virtual void onRun(skiatest::Reporter* reporter, CCPRPathDrawer& ccpr) = 0; 156 157 sk_sp<GrContext> fMockContext; 158 SkPath fPath; 159 }; 160 161 #define DEF_CCPR_TEST(name) \ 162 DEF_GPUTEST(name, reporter, /* options */) { \ 163 name test; \ 164 test.run(reporter); \ 165 } 166 167 class GrCCPRTest_cleanup : public CCPRTest { onRun(skiatest::Reporter * reporter,CCPRPathDrawer & ccpr)168 void onRun(skiatest::Reporter* reporter, CCPRPathDrawer& ccpr) override { 169 REPORTER_ASSERT(reporter, SkPathPriv::TestingOnly_unique(fPath)); 170 171 // Ensure paths get unreffed. 172 for (int i = 0; i < 10; ++i) { 173 ccpr.drawPath(fPath); 174 ccpr.clipFullscreenRect(fPath); 175 } 176 REPORTER_ASSERT(reporter, !SkPathPriv::TestingOnly_unique(fPath)); 177 ccpr.flush(); 178 REPORTER_ASSERT(reporter, SkPathPriv::TestingOnly_unique(fPath)); 179 180 // Ensure paths get unreffed when we delete the context without flushing. 181 for (int i = 0; i < 10; ++i) { 182 ccpr.drawPath(fPath); 183 ccpr.clipFullscreenRect(fPath); 184 } 185 ccpr.abandonGrContext(); 186 REPORTER_ASSERT(reporter, !SkPathPriv::TestingOnly_unique(fPath)); 187 fMockContext.reset(); 188 REPORTER_ASSERT(reporter, SkPathPriv::TestingOnly_unique(fPath)); 189 } 190 }; 191 DEF_CCPR_TEST(GrCCPRTest_cleanup) 192 193 class GrCCPRTest_unregisterCulledOps : public CCPRTest { onRun(skiatest::Reporter * reporter,CCPRPathDrawer & ccpr)194 void onRun(skiatest::Reporter* reporter, CCPRPathDrawer& ccpr) override { 195 REPORTER_ASSERT(reporter, SkPathPriv::TestingOnly_unique(fPath)); 196 197 // Ensure Ops get unregistered from CCPR when culled early. 198 ccpr.drawPath(fPath); 199 REPORTER_ASSERT(reporter, !SkPathPriv::TestingOnly_unique(fPath)); 200 ccpr.clear(); // Clear should delete the CCPR Op. 201 REPORTER_ASSERT(reporter, SkPathPriv::TestingOnly_unique(fPath)); 202 ccpr.flush(); // Should not crash (DrawPathsOp should have unregistered itself). 203 204 // Ensure Op unregisters work when we delete the context without flushing. 205 ccpr.drawPath(fPath); 206 REPORTER_ASSERT(reporter, !SkPathPriv::TestingOnly_unique(fPath)); 207 ccpr.clear(); // Clear should delete the CCPR DrawPathsOp. 208 REPORTER_ASSERT(reporter, SkPathPriv::TestingOnly_unique(fPath)); 209 ccpr.abandonGrContext(); 210 fMockContext.reset(); // Should not crash (DrawPathsOp should have unregistered itself). 211 } 212 }; 213 DEF_CCPR_TEST(GrCCPRTest_unregisterCulledOps) 214 215 class GrCCPRTest_parseEmptyPath : public CCPRTest { onRun(skiatest::Reporter * reporter,CCPRPathDrawer & ccpr)216 void onRun(skiatest::Reporter* reporter, CCPRPathDrawer& ccpr) override { 217 REPORTER_ASSERT(reporter, SkPathPriv::TestingOnly_unique(fPath)); 218 219 // Make a path large enough that ccpr chooses to crop it by the RT bounds, and ends up with 220 // an empty path. 221 SkPath largeOutsidePath; 222 largeOutsidePath.moveTo(-1e30f, -1e30f); 223 largeOutsidePath.lineTo(-1e30f, +1e30f); 224 largeOutsidePath.lineTo(-1e10f, +1e30f); 225 ccpr.drawPath(largeOutsidePath); 226 227 // Normally an empty path is culled before reaching ccpr, however we use a back door for 228 // testing so this path will make it. 229 SkPath emptyPath; 230 SkASSERT(emptyPath.isEmpty()); 231 ccpr.drawPath(emptyPath); 232 233 // This is the test. It will exercise various internal asserts and verify we do not crash. 234 ccpr.flush(); 235 236 // Now try again with clips. 237 ccpr.clipFullscreenRect(largeOutsidePath); 238 ccpr.clipFullscreenRect(emptyPath); 239 ccpr.flush(); 240 241 // ... and both. 242 ccpr.drawPath(largeOutsidePath); 243 ccpr.clipFullscreenRect(largeOutsidePath); 244 ccpr.drawPath(emptyPath); 245 ccpr.clipFullscreenRect(emptyPath); 246 ccpr.flush(); 247 } 248 }; 249 DEF_CCPR_TEST(GrCCPRTest_parseEmptyPath) 250 251 class CCPRRenderingTest { 252 public: run(skiatest::Reporter * reporter,GrContext * ctx) const253 void run(skiatest::Reporter* reporter, GrContext* ctx) const { 254 if (!ctx->contextPriv().drawingManager()->getCoverageCountingPathRenderer()) { 255 return; // CCPR is not enabled on this GPU. 256 } 257 CCPRPathDrawer ccpr(ctx, reporter); 258 if (!ccpr.valid()) { 259 return; 260 } 261 this->onRun(reporter, ccpr); 262 } 263 ~CCPRRenderingTest()264 virtual ~CCPRRenderingTest() {} 265 266 protected: 267 virtual void onRun(skiatest::Reporter* reporter, const CCPRPathDrawer& ccpr) const = 0; 268 }; 269 270 #define DEF_CCPR_RENDERING_TEST(name) \ 271 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(name, reporter, ctxInfo) { \ 272 name test; \ 273 test.run(reporter, ctxInfo.grContext()); \ 274 } 275 276 class GrCCPRTest_busyPath : public CCPRRenderingTest { onRun(skiatest::Reporter * reporter,const CCPRPathDrawer & ccpr) const277 void onRun(skiatest::Reporter* reporter, const CCPRPathDrawer& ccpr) const override { 278 static constexpr int kNumBusyVerbs = 1 << 17; 279 ccpr.clear(); 280 SkPath busyPath; 281 busyPath.moveTo(0, 0); // top left 282 busyPath.lineTo(kCanvasSize, kCanvasSize); // bottom right 283 for (int i = 2; i < kNumBusyVerbs; ++i) { 284 float offset = i * ((float)kCanvasSize / kNumBusyVerbs); 285 busyPath.lineTo(kCanvasSize - offset, kCanvasSize + offset); // offscreen 286 } 287 ccpr.drawPath(busyPath); 288 289 ccpr.flush(); // If this doesn't crash, the test passed. 290 // If it does, maybe fiddle with fMaxInstancesPerDrawArraysWithoutCrashing in 291 // your platform's GrGLCaps. 292 } 293 }; 294 DEF_CCPR_RENDERING_TEST(GrCCPRTest_busyPath) 295 296 #endif 297