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