• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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