blob: 35e3a1fde74ab101431aa05fe85bdc7115fcce80 [file] [log] [blame]
Chris Daltoncc604e52017-10-06 16:27:32 -06001/*
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
Chris Daltoncc604e52017-10-06 16:27:32 -060011#include "GrContext.h"
12#include "GrContextPriv.h"
13#include "GrClip.h"
Chris Daltonfddb6c02017-11-04 15:22:22 -060014#include "GrDrawingManager.h"
15#include "GrPathRenderer.h"
16#include "GrPaint.h"
Chris Daltoncc604e52017-10-06 16:27:32 -060017#include "GrRenderTargetContext.h"
18#include "GrRenderTargetContextPriv.h"
19#include "GrShape.h"
Chris Daltoncc604e52017-10-06 16:27:32 -060020#include "SkMatrix.h"
Chris Daltonfddb6c02017-11-04 15:22:22 -060021#include "SkPathPriv.h"
Chris Daltoncc604e52017-10-06 16:27:32 -060022#include "SkRect.h"
23#include "ccpr/GrCoverageCountingPathRenderer.h"
Chris Daltonfddb6c02017-11-04 15:22:22 -060024#include "mock/GrMockTypes.h"
Chris Daltoncc604e52017-10-06 16:27:32 -060025#include <cmath>
26
27static constexpr int kCanvasSize = 100;
28
Chris Daltona32a3c32017-12-05 10:05:21 -070029class CCPRClip : public GrClip {
30public:
31 CCPRClip(GrCoverageCountingPathRenderer* ccpr, const SkPath& path) : fCCPR(ccpr), fPath(path) {}
32
33private:
Robert Phillips777707b2018-01-17 11:40:14 -050034 bool apply(GrContext* context, GrRenderTargetContext* rtc, bool, bool, GrAppliedClip* out,
Chris Daltona32a3c32017-12-05 10:05:21 -070035 SkRect* bounds) const override {
Robert Phillips777707b2018-01-17 11:40:14 -050036 GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider();
37 out->addCoverageFP(fCCPR->makeClipProcessor(proxyProvider,
38 rtc->priv().testingOnly_getOpListID(), fPath,
Chris Daltona32a3c32017-12-05 10:05:21 -070039 SkIRect::MakeWH(rtc->width(), rtc->height()),
40 rtc->width(), rtc->height()));
41 return true;
42 }
43 bool quickContains(const SkRect&) const final { return false; }
44 bool isRRect(const SkRect& rtBounds, SkRRect* rr, GrAA*) const final { return false; }
45 void getConservativeBounds(int width, int height, SkIRect* rect, bool* iior) const final {
46 rect->set(0, 0, width, height);
47 if (iior) {
48 *iior = false;
49 }
50 }
51 GrCoverageCountingPathRenderer* const fCCPR;
52 const SkPath fPath;
53};
54
Chris Daltoncc604e52017-10-06 16:27:32 -060055class CCPRPathDrawer {
56public:
Chris Daltonfddb6c02017-11-04 15:22:22 -060057 CCPRPathDrawer(GrContext* ctx, skiatest::Reporter* reporter)
Chris Daltoncc604e52017-10-06 16:27:32 -060058 : fCtx(ctx)
Chris Daltonfddb6c02017-11-04 15:22:22 -060059 , fCCPR(fCtx->contextPriv().drawingManager()->getCoverageCountingPathRenderer())
Robert Phillips0c4b7b12018-03-06 08:20:37 -050060 , fRTC(fCtx->contextPriv().makeDeferredRenderTargetContext(
61 SkBackingFit::kExact, kCanvasSize,
Chris Daltoncc604e52017-10-06 16:27:32 -060062 kCanvasSize, kRGBA_8888_GrPixelConfig,
63 nullptr)) {
Chris Daltonfddb6c02017-11-04 15:22:22 -060064 if (!fCCPR) {
65 ERRORF(reporter, "ccpr not enabled in GrContext for ccpr tests");
66 }
67 if (!fRTC) {
68 ERRORF(reporter, "failed to create GrRenderTargetContext for ccpr tests");
Chris Daltoncc604e52017-10-06 16:27:32 -060069 }
70 }
71
Chris Daltonfddb6c02017-11-04 15:22:22 -060072 bool valid() const { return fCCPR && fRTC; }
Chris Dalton344e9032017-12-11 15:42:09 -070073 void clear() const { fRTC->clear(nullptr, 0, GrRenderTargetContext::CanClearFullscreen::kYes); }
Chris Daltonfddb6c02017-11-04 15:22:22 -060074 void abandonGrContext() { fCtx = nullptr; fCCPR = nullptr; fRTC = nullptr; }
Chris Daltoncc604e52017-10-06 16:27:32 -060075
Chris Daltonfddb6c02017-11-04 15:22:22 -060076 void drawPath(SkPath path, GrColor4f color = GrColor4f(0, 1, 0, 1)) const {
77 SkASSERT(this->valid());
Chris Daltoncc604e52017-10-06 16:27:32 -060078
Chris Daltoncc604e52017-10-06 16:27:32 -060079 GrPaint paint;
80 paint.setColor4f(color);
Chris Daltonfddb6c02017-11-04 15:22:22 -060081
Chris Daltoncc604e52017-10-06 16:27:32 -060082 GrNoClip noClip;
83 SkIRect clipBounds = SkIRect::MakeWH(kCanvasSize, kCanvasSize);
Chris Daltonfddb6c02017-11-04 15:22:22 -060084
Chris Daltoncc604e52017-10-06 16:27:32 -060085 SkMatrix matrix = SkMatrix::I();
Chris Daltonfddb6c02017-11-04 15:22:22 -060086
87 path.setIsVolatile(true);
Chris Daltoncc604e52017-10-06 16:27:32 -060088 GrShape shape(path);
Chris Daltonfddb6c02017-11-04 15:22:22 -060089
Chris Daltoncc604e52017-10-06 16:27:32 -060090 fCCPR->drawPath({fCtx, std::move(paint), &GrUserStencilSettings::kUnused, fRTC.get(),
91 &noClip, &clipBounds, &matrix, &shape, GrAAType::kCoverage, false});
92 }
93
Chris Daltona32a3c32017-12-05 10:05:21 -070094 void clipFullscreenRect(SkPath clipPath, GrColor4f color = GrColor4f(0, 1, 0, 1)) {
95 SkASSERT(this->valid());
96
97 GrPaint paint;
98 paint.setColor4f(color);
99
100 fRTC->drawRect(CCPRClip(fCCPR, clipPath), std::move(paint), GrAA::kYes, SkMatrix::I(),
101 SkRect::MakeIWH(kCanvasSize, kCanvasSize));
102 }
103
Chris Daltonfddb6c02017-11-04 15:22:22 -0600104 void flush() const {
105 SkASSERT(this->valid());
Chris Daltoncc604e52017-10-06 16:27:32 -0600106 fCtx->flush();
107 }
108
109private:
Chris Daltonfddb6c02017-11-04 15:22:22 -0600110 GrContext* fCtx;
111 GrCoverageCountingPathRenderer* fCCPR;
112 sk_sp<GrRenderTargetContext> fRTC;
Chris Daltoncc604e52017-10-06 16:27:32 -0600113};
114
Chris Daltonfddb6c02017-11-04 15:22:22 -0600115class CCPRTest {
116public:
117 void run(skiatest::Reporter* reporter) {
118 GrMockOptions mockOptions;
119 mockOptions.fInstanceAttribSupport = true;
120 mockOptions.fMapBufferFlags = GrCaps::kCanMap_MapFlag;
Brian Salomonbdecacf2018-02-02 20:32:49 -0500121 mockOptions.fConfigOptions[kAlpha_half_GrPixelConfig].fRenderability =
122 GrMockOptions::ConfigOptions::Renderability::kNonMSAA;
Chris Daltonfddb6c02017-11-04 15:22:22 -0600123 mockOptions.fConfigOptions[kAlpha_half_GrPixelConfig].fTexturable = true;
124 mockOptions.fGeometryShaderSupport = true;
Chris Daltonfddb6c02017-11-04 15:22:22 -0600125 mockOptions.fIntegerSupport = true;
126 mockOptions.fFlatInterpolationSupport = true;
Chris Dalton91ab1552018-04-18 13:24:25 -0600127 this->customizeMockOptions(&mockOptions);
Chris Daltonfddb6c02017-11-04 15:22:22 -0600128
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 Daltoncc604e52017-10-06 16:27:32 -0600151 }
152
Chris Daltonfddb6c02017-11-04 15:22:22 -0600153 virtual ~CCPRTest() {}
154
155protected:
Chris Dalton91ab1552018-04-18 13:24:25 -0600156 virtual void customizeMockOptions(GrMockOptions*) {}
Chris Daltonfddb6c02017-11-04 15:22:22 -0600157 virtual void onRun(skiatest::Reporter* reporter, CCPRPathDrawer& ccpr) = 0;
158
159 sk_sp<GrContext> fMockContext;
160 SkPath fPath;
161};
162
Brian Salomondcfca432017-11-15 15:48:03 -0500163#define DEF_CCPR_TEST(name) \
164 DEF_GPUTEST(name, reporter, /* options */) { \
165 name test; \
166 test.run(reporter); \
Chris Daltoncc604e52017-10-06 16:27:32 -0600167 }
168
Chris Daltonfddb6c02017-11-04 15:22:22 -0600169class GrCCPRTest_cleanup : public CCPRTest {
170 void onRun(skiatest::Reporter* reporter, CCPRPathDrawer& ccpr) override {
171 REPORTER_ASSERT(reporter, SkPathPriv::TestingOnly_unique(fPath));
Chris Daltoncc604e52017-10-06 16:27:32 -0600172
Chris Daltonfddb6c02017-11-04 15:22:22 -0600173 // Ensure paths get unreffed.
174 for (int i = 0; i < 10; ++i) {
175 ccpr.drawPath(fPath);
Chris Dalton4bfb50b2018-05-21 09:10:53 -0600176 }
177 REPORTER_ASSERT(reporter, !SkPathPriv::TestingOnly_unique(fPath));
178 ccpr.flush();
179 REPORTER_ASSERT(reporter, SkPathPriv::TestingOnly_unique(fPath));
180
181 // Ensure clip paths get unreffed.
182 for (int i = 0; i < 10; ++i) {
Chris Daltona32a3c32017-12-05 10:05:21 -0700183 ccpr.clipFullscreenRect(fPath);
Chris Daltonfddb6c02017-11-04 15:22:22 -0600184 }
185 REPORTER_ASSERT(reporter, !SkPathPriv::TestingOnly_unique(fPath));
186 ccpr.flush();
187 REPORTER_ASSERT(reporter, SkPathPriv::TestingOnly_unique(fPath));
188
189 // Ensure paths get unreffed when we delete the context without flushing.
190 for (int i = 0; i < 10; ++i) {
191 ccpr.drawPath(fPath);
Chris Daltona32a3c32017-12-05 10:05:21 -0700192 ccpr.clipFullscreenRect(fPath);
Chris Daltonfddb6c02017-11-04 15:22:22 -0600193 }
194 ccpr.abandonGrContext();
195 REPORTER_ASSERT(reporter, !SkPathPriv::TestingOnly_unique(fPath));
196 fMockContext.reset();
197 REPORTER_ASSERT(reporter, SkPathPriv::TestingOnly_unique(fPath));
198 }
199};
200DEF_CCPR_TEST(GrCCPRTest_cleanup)
201
Chris Dalton91ab1552018-04-18 13:24:25 -0600202class GrCCPRTest_cleanupWithTexAllocFail : public GrCCPRTest_cleanup {
203 void customizeMockOptions(GrMockOptions* options) override {
204 options->fFailTextureAllocations = true;
205 }
206};
207DEF_CCPR_TEST(GrCCPRTest_cleanupWithTexAllocFail)
208
Chris Dalton080baa42017-11-06 14:19:19 -0700209class GrCCPRTest_unregisterCulledOps : public CCPRTest {
210 void onRun(skiatest::Reporter* reporter, CCPRPathDrawer& ccpr) override {
211 REPORTER_ASSERT(reporter, SkPathPriv::TestingOnly_unique(fPath));
212
213 // Ensure Ops get unregistered from CCPR when culled early.
214 ccpr.drawPath(fPath);
215 REPORTER_ASSERT(reporter, !SkPathPriv::TestingOnly_unique(fPath));
216 ccpr.clear(); // Clear should delete the CCPR Op.
217 REPORTER_ASSERT(reporter, SkPathPriv::TestingOnly_unique(fPath));
218 ccpr.flush(); // Should not crash (DrawPathsOp should have unregistered itself).
219
220 // Ensure Op unregisters work when we delete the context without flushing.
221 ccpr.drawPath(fPath);
222 REPORTER_ASSERT(reporter, !SkPathPriv::TestingOnly_unique(fPath));
223 ccpr.clear(); // Clear should delete the CCPR DrawPathsOp.
224 REPORTER_ASSERT(reporter, SkPathPriv::TestingOnly_unique(fPath));
225 ccpr.abandonGrContext();
226 fMockContext.reset(); // Should not crash (DrawPathsOp should have unregistered itself).
227 }
228};
229DEF_CCPR_TEST(GrCCPRTest_unregisterCulledOps)
230
Chris Daltonc9c97b72017-11-27 15:34:26 -0700231class GrCCPRTest_parseEmptyPath : public CCPRTest {
232 void onRun(skiatest::Reporter* reporter, CCPRPathDrawer& ccpr) override {
233 REPORTER_ASSERT(reporter, SkPathPriv::TestingOnly_unique(fPath));
234
235 // Make a path large enough that ccpr chooses to crop it by the RT bounds, and ends up with
236 // an empty path.
237 SkPath largeOutsidePath;
238 largeOutsidePath.moveTo(-1e30f, -1e30f);
239 largeOutsidePath.lineTo(-1e30f, +1e30f);
240 largeOutsidePath.lineTo(-1e10f, +1e30f);
241 ccpr.drawPath(largeOutsidePath);
242
243 // Normally an empty path is culled before reaching ccpr, however we use a back door for
244 // testing so this path will make it.
245 SkPath emptyPath;
246 SkASSERT(emptyPath.isEmpty());
247 ccpr.drawPath(emptyPath);
248
249 // This is the test. It will exercise various internal asserts and verify we do not crash.
250 ccpr.flush();
Chris Daltona32a3c32017-12-05 10:05:21 -0700251
252 // Now try again with clips.
253 ccpr.clipFullscreenRect(largeOutsidePath);
254 ccpr.clipFullscreenRect(emptyPath);
255 ccpr.flush();
256
257 // ... and both.
258 ccpr.drawPath(largeOutsidePath);
259 ccpr.clipFullscreenRect(largeOutsidePath);
260 ccpr.drawPath(emptyPath);
261 ccpr.clipFullscreenRect(emptyPath);
262 ccpr.flush();
Chris Daltonc9c97b72017-11-27 15:34:26 -0700263 }
264};
265DEF_CCPR_TEST(GrCCPRTest_parseEmptyPath)
266
Chris Daltonfddb6c02017-11-04 15:22:22 -0600267class CCPRRenderingTest {
268public:
269 void run(skiatest::Reporter* reporter, GrContext* ctx) const {
270 if (!ctx->contextPriv().drawingManager()->getCoverageCountingPathRenderer()) {
271 return; // CCPR is not enabled on this GPU.
272 }
273 CCPRPathDrawer ccpr(ctx, reporter);
274 if (!ccpr.valid()) {
275 return;
276 }
277 this->onRun(reporter, ccpr);
278 }
279
280 virtual ~CCPRRenderingTest() {}
281
282protected:
283 virtual void onRun(skiatest::Reporter* reporter, const CCPRPathDrawer& ccpr) const = 0;
284};
285
286#define DEF_CCPR_RENDERING_TEST(name) \
287 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(name, reporter, ctxInfo) { \
288 name test; \
289 test.run(reporter, ctxInfo.grContext()); \
290 }
291
292class GrCCPRTest_busyPath : public CCPRRenderingTest {
293 void onRun(skiatest::Reporter* reporter, const CCPRPathDrawer& ccpr) const override {
294 static constexpr int kNumBusyVerbs = 1 << 17;
295 ccpr.clear();
296 SkPath busyPath;
297 busyPath.moveTo(0, 0); // top left
298 busyPath.lineTo(kCanvasSize, kCanvasSize); // bottom right
299 for (int i = 2; i < kNumBusyVerbs; ++i) {
300 float offset = i * ((float)kCanvasSize / kNumBusyVerbs);
301 busyPath.lineTo(kCanvasSize - offset, kCanvasSize + offset); // offscreen
302 }
303 ccpr.drawPath(busyPath);
304
305 ccpr.flush(); // If this doesn't crash, the test passed.
306 // If it does, maybe fiddle with fMaxInstancesPerDrawArraysWithoutCrashing in
307 // your platform's GrGLCaps.
308 }
309};
310DEF_CCPR_RENDERING_TEST(GrCCPRTest_busyPath)