blob: 8cde797f437acdc37141a26b39a17c8866f69b85 [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
11#if SK_SUPPORT_GPU
12
13#include "GrContext.h"
14#include "GrContextPriv.h"
15#include "GrClip.h"
Chris Daltonfddb6c02017-11-04 15:22:22 -060016#include "GrDrawingManager.h"
17#include "GrPathRenderer.h"
18#include "GrPaint.h"
Chris Daltoncc604e52017-10-06 16:27:32 -060019#include "GrRenderTargetContext.h"
20#include "GrRenderTargetContextPriv.h"
21#include "GrShape.h"
Chris Daltoncc604e52017-10-06 16:27:32 -060022#include "SkMatrix.h"
Chris Daltonfddb6c02017-11-04 15:22:22 -060023#include "SkPathPriv.h"
Chris Daltoncc604e52017-10-06 16:27:32 -060024#include "SkRect.h"
25#include "ccpr/GrCoverageCountingPathRenderer.h"
Chris Daltonfddb6c02017-11-04 15:22:22 -060026#include "mock/GrMockTypes.h"
Chris Daltoncc604e52017-10-06 16:27:32 -060027#include <cmath>
28
29static constexpr int kCanvasSize = 100;
30
Chris Daltona32a3c32017-12-05 10:05:21 -070031class CCPRClip : public GrClip {
32public:
33 CCPRClip(GrCoverageCountingPathRenderer* ccpr, const SkPath& path) : fCCPR(ccpr), fPath(path) {}
34
35private:
Robert Phillips777707b2018-01-17 11:40:14 -050036 bool apply(GrContext* context, GrRenderTargetContext* rtc, bool, bool, GrAppliedClip* out,
Chris Daltona32a3c32017-12-05 10:05:21 -070037 SkRect* bounds) const override {
Robert Phillips777707b2018-01-17 11:40:14 -050038 GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider();
39 out->addCoverageFP(fCCPR->makeClipProcessor(proxyProvider,
40 rtc->priv().testingOnly_getOpListID(), fPath,
Chris Daltona32a3c32017-12-05 10:05:21 -070041 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 Daltoncc604e52017-10-06 16:27:32 -060057class CCPRPathDrawer {
58public:
Chris Daltonfddb6c02017-11-04 15:22:22 -060059 CCPRPathDrawer(GrContext* ctx, skiatest::Reporter* reporter)
Chris Daltoncc604e52017-10-06 16:27:32 -060060 : fCtx(ctx)
Chris Daltonfddb6c02017-11-04 15:22:22 -060061 , fCCPR(fCtx->contextPriv().drawingManager()->getCoverageCountingPathRenderer())
Robert Phillips0c4b7b12018-03-06 08:20:37 -050062 , fRTC(fCtx->contextPriv().makeDeferredRenderTargetContext(
63 SkBackingFit::kExact, kCanvasSize,
Chris Daltoncc604e52017-10-06 16:27:32 -060064 kCanvasSize, kRGBA_8888_GrPixelConfig,
65 nullptr)) {
Chris Daltonfddb6c02017-11-04 15:22:22 -060066 if (!fCCPR) {
67 ERRORF(reporter, "ccpr not enabled in GrContext for ccpr tests");
68 }
69 if (!fRTC) {
70 ERRORF(reporter, "failed to create GrRenderTargetContext for ccpr tests");
Chris Daltoncc604e52017-10-06 16:27:32 -060071 }
72 }
73
Chris Daltonfddb6c02017-11-04 15:22:22 -060074 bool valid() const { return fCCPR && fRTC; }
Chris Dalton344e9032017-12-11 15:42:09 -070075 void clear() const { fRTC->clear(nullptr, 0, GrRenderTargetContext::CanClearFullscreen::kYes); }
Chris Daltonfddb6c02017-11-04 15:22:22 -060076 void abandonGrContext() { fCtx = nullptr; fCCPR = nullptr; fRTC = nullptr; }
Chris Daltoncc604e52017-10-06 16:27:32 -060077
Chris Daltonfddb6c02017-11-04 15:22:22 -060078 void drawPath(SkPath path, GrColor4f color = GrColor4f(0, 1, 0, 1)) const {
79 SkASSERT(this->valid());
Chris Daltoncc604e52017-10-06 16:27:32 -060080
Chris Daltoncc604e52017-10-06 16:27:32 -060081 GrPaint paint;
82 paint.setColor4f(color);
Chris Daltonfddb6c02017-11-04 15:22:22 -060083
Chris Daltoncc604e52017-10-06 16:27:32 -060084 GrNoClip noClip;
85 SkIRect clipBounds = SkIRect::MakeWH(kCanvasSize, kCanvasSize);
Chris Daltonfddb6c02017-11-04 15:22:22 -060086
Chris Daltoncc604e52017-10-06 16:27:32 -060087 SkMatrix matrix = SkMatrix::I();
Chris Daltonfddb6c02017-11-04 15:22:22 -060088
89 path.setIsVolatile(true);
Chris Daltoncc604e52017-10-06 16:27:32 -060090 GrShape shape(path);
Chris Daltonfddb6c02017-11-04 15:22:22 -060091
Chris Daltoncc604e52017-10-06 16:27:32 -060092 fCCPR->drawPath({fCtx, std::move(paint), &GrUserStencilSettings::kUnused, fRTC.get(),
93 &noClip, &clipBounds, &matrix, &shape, GrAAType::kCoverage, false});
94 }
95
Chris Daltona32a3c32017-12-05 10:05:21 -070096 void clipFullscreenRect(SkPath clipPath, GrColor4f color = GrColor4f(0, 1, 0, 1)) {
97 SkASSERT(this->valid());
98
99 GrPaint paint;
100 paint.setColor4f(color);
101
102 fRTC->drawRect(CCPRClip(fCCPR, clipPath), std::move(paint), GrAA::kYes, SkMatrix::I(),
103 SkRect::MakeIWH(kCanvasSize, kCanvasSize));
104 }
105
Chris Daltonfddb6c02017-11-04 15:22:22 -0600106 void flush() const {
107 SkASSERT(this->valid());
Chris Daltoncc604e52017-10-06 16:27:32 -0600108 fCtx->flush();
109 }
110
111private:
Chris Daltonfddb6c02017-11-04 15:22:22 -0600112 GrContext* fCtx;
113 GrCoverageCountingPathRenderer* fCCPR;
114 sk_sp<GrRenderTargetContext> fRTC;
Chris Daltoncc604e52017-10-06 16:27:32 -0600115};
116
Chris Daltonfddb6c02017-11-04 15:22:22 -0600117class CCPRTest {
118public:
119 void run(skiatest::Reporter* reporter) {
120 GrMockOptions mockOptions;
121 mockOptions.fInstanceAttribSupport = true;
122 mockOptions.fMapBufferFlags = GrCaps::kCanMap_MapFlag;
Brian Salomonbdecacf2018-02-02 20:32:49 -0500123 mockOptions.fConfigOptions[kAlpha_half_GrPixelConfig].fRenderability =
124 GrMockOptions::ConfigOptions::Renderability::kNonMSAA;
Chris Daltonfddb6c02017-11-04 15:22:22 -0600125 mockOptions.fConfigOptions[kAlpha_half_GrPixelConfig].fTexturable = true;
126 mockOptions.fGeometryShaderSupport = true;
Chris Daltonfddb6c02017-11-04 15:22:22 -0600127 mockOptions.fIntegerSupport = true;
128 mockOptions.fFlatInterpolationSupport = true;
Chris Dalton91ab1552018-04-18 13:24:25 -0600129 this->customizeMockOptions(&mockOptions);
Chris Daltonfddb6c02017-11-04 15:22:22 -0600130
131 GrContextOptions ctxOptions;
132 ctxOptions.fAllowPathMaskCaching = false;
133 ctxOptions.fGpuPathRenderers = GpuPathRenderers::kCoverageCounting;
134
135 fMockContext = GrContext::MakeMock(&mockOptions, ctxOptions);
136 if (!fMockContext) {
137 ERRORF(reporter, "could not create mock context");
138 return;
139 }
140 if (!fMockContext->unique()) {
141 ERRORF(reporter, "mock context is not unique");
142 return;
143 }
144
145 CCPRPathDrawer ccpr(fMockContext.get(), reporter);
146 if (!ccpr.valid()) {
147 return;
148 }
149
150 fPath.moveTo(0, 0);
151 fPath.cubicTo(50, 50, 0, 50, 50, 0);
152 this->onRun(reporter, ccpr);
Chris Daltoncc604e52017-10-06 16:27:32 -0600153 }
154
Chris Daltonfddb6c02017-11-04 15:22:22 -0600155 virtual ~CCPRTest() {}
156
157protected:
Chris Dalton91ab1552018-04-18 13:24:25 -0600158 virtual void customizeMockOptions(GrMockOptions*) {}
Chris Daltonfddb6c02017-11-04 15:22:22 -0600159 virtual void onRun(skiatest::Reporter* reporter, CCPRPathDrawer& ccpr) = 0;
160
161 sk_sp<GrContext> fMockContext;
162 SkPath fPath;
163};
164
Brian Salomondcfca432017-11-15 15:48:03 -0500165#define DEF_CCPR_TEST(name) \
166 DEF_GPUTEST(name, reporter, /* options */) { \
167 name test; \
168 test.run(reporter); \
Chris Daltoncc604e52017-10-06 16:27:32 -0600169 }
170
Chris Daltonfddb6c02017-11-04 15:22:22 -0600171class GrCCPRTest_cleanup : public CCPRTest {
172 void onRun(skiatest::Reporter* reporter, CCPRPathDrawer& ccpr) override {
173 REPORTER_ASSERT(reporter, SkPathPriv::TestingOnly_unique(fPath));
Chris Daltoncc604e52017-10-06 16:27:32 -0600174
Chris Daltonfddb6c02017-11-04 15:22:22 -0600175 // Ensure paths get unreffed.
176 for (int i = 0; i < 10; ++i) {
177 ccpr.drawPath(fPath);
Chris Dalton4bfb50b2018-05-21 09:10:53 -0600178 }
179 REPORTER_ASSERT(reporter, !SkPathPriv::TestingOnly_unique(fPath));
180 ccpr.flush();
181 REPORTER_ASSERT(reporter, SkPathPriv::TestingOnly_unique(fPath));
182
183 // Ensure clip paths get unreffed.
184 for (int i = 0; i < 10; ++i) {
Chris Daltona32a3c32017-12-05 10:05:21 -0700185 ccpr.clipFullscreenRect(fPath);
Chris Daltonfddb6c02017-11-04 15:22:22 -0600186 }
187 REPORTER_ASSERT(reporter, !SkPathPriv::TestingOnly_unique(fPath));
188 ccpr.flush();
189 REPORTER_ASSERT(reporter, SkPathPriv::TestingOnly_unique(fPath));
190
191 // Ensure paths get unreffed when we delete the context without flushing.
192 for (int i = 0; i < 10; ++i) {
193 ccpr.drawPath(fPath);
Chris Daltona32a3c32017-12-05 10:05:21 -0700194 ccpr.clipFullscreenRect(fPath);
Chris Daltonfddb6c02017-11-04 15:22:22 -0600195 }
196 ccpr.abandonGrContext();
197 REPORTER_ASSERT(reporter, !SkPathPriv::TestingOnly_unique(fPath));
198 fMockContext.reset();
199 REPORTER_ASSERT(reporter, SkPathPriv::TestingOnly_unique(fPath));
200 }
201};
202DEF_CCPR_TEST(GrCCPRTest_cleanup)
203
Chris Dalton91ab1552018-04-18 13:24:25 -0600204class GrCCPRTest_cleanupWithTexAllocFail : public GrCCPRTest_cleanup {
205 void customizeMockOptions(GrMockOptions* options) override {
206 options->fFailTextureAllocations = true;
207 }
208};
209DEF_CCPR_TEST(GrCCPRTest_cleanupWithTexAllocFail)
210
Chris Dalton080baa42017-11-06 14:19:19 -0700211class GrCCPRTest_unregisterCulledOps : public CCPRTest {
212 void onRun(skiatest::Reporter* reporter, CCPRPathDrawer& ccpr) override {
213 REPORTER_ASSERT(reporter, SkPathPriv::TestingOnly_unique(fPath));
214
215 // Ensure Ops get unregistered from CCPR when culled early.
216 ccpr.drawPath(fPath);
217 REPORTER_ASSERT(reporter, !SkPathPriv::TestingOnly_unique(fPath));
218 ccpr.clear(); // Clear should delete the CCPR Op.
219 REPORTER_ASSERT(reporter, SkPathPriv::TestingOnly_unique(fPath));
220 ccpr.flush(); // Should not crash (DrawPathsOp should have unregistered itself).
221
222 // Ensure Op unregisters work when we delete the context without flushing.
223 ccpr.drawPath(fPath);
224 REPORTER_ASSERT(reporter, !SkPathPriv::TestingOnly_unique(fPath));
225 ccpr.clear(); // Clear should delete the CCPR DrawPathsOp.
226 REPORTER_ASSERT(reporter, SkPathPriv::TestingOnly_unique(fPath));
227 ccpr.abandonGrContext();
228 fMockContext.reset(); // Should not crash (DrawPathsOp should have unregistered itself).
229 }
230};
231DEF_CCPR_TEST(GrCCPRTest_unregisterCulledOps)
232
Chris Daltonc9c97b72017-11-27 15:34:26 -0700233class GrCCPRTest_parseEmptyPath : public CCPRTest {
234 void onRun(skiatest::Reporter* reporter, CCPRPathDrawer& ccpr) override {
235 REPORTER_ASSERT(reporter, SkPathPriv::TestingOnly_unique(fPath));
236
237 // Make a path large enough that ccpr chooses to crop it by the RT bounds, and ends up with
238 // an empty path.
239 SkPath largeOutsidePath;
240 largeOutsidePath.moveTo(-1e30f, -1e30f);
241 largeOutsidePath.lineTo(-1e30f, +1e30f);
242 largeOutsidePath.lineTo(-1e10f, +1e30f);
243 ccpr.drawPath(largeOutsidePath);
244
245 // Normally an empty path is culled before reaching ccpr, however we use a back door for
246 // testing so this path will make it.
247 SkPath emptyPath;
248 SkASSERT(emptyPath.isEmpty());
249 ccpr.drawPath(emptyPath);
250
251 // This is the test. It will exercise various internal asserts and verify we do not crash.
252 ccpr.flush();
Chris Daltona32a3c32017-12-05 10:05:21 -0700253
254 // Now try again with clips.
255 ccpr.clipFullscreenRect(largeOutsidePath);
256 ccpr.clipFullscreenRect(emptyPath);
257 ccpr.flush();
258
259 // ... and both.
260 ccpr.drawPath(largeOutsidePath);
261 ccpr.clipFullscreenRect(largeOutsidePath);
262 ccpr.drawPath(emptyPath);
263 ccpr.clipFullscreenRect(emptyPath);
264 ccpr.flush();
Chris Daltonc9c97b72017-11-27 15:34:26 -0700265 }
266};
267DEF_CCPR_TEST(GrCCPRTest_parseEmptyPath)
268
Chris Daltonfddb6c02017-11-04 15:22:22 -0600269class CCPRRenderingTest {
270public:
271 void run(skiatest::Reporter* reporter, GrContext* ctx) const {
272 if (!ctx->contextPriv().drawingManager()->getCoverageCountingPathRenderer()) {
273 return; // CCPR is not enabled on this GPU.
274 }
275 CCPRPathDrawer ccpr(ctx, reporter);
276 if (!ccpr.valid()) {
277 return;
278 }
279 this->onRun(reporter, ccpr);
280 }
281
282 virtual ~CCPRRenderingTest() {}
283
284protected:
285 virtual void onRun(skiatest::Reporter* reporter, const CCPRPathDrawer& ccpr) const = 0;
286};
287
288#define DEF_CCPR_RENDERING_TEST(name) \
289 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(name, reporter, ctxInfo) { \
290 name test; \
291 test.run(reporter, ctxInfo.grContext()); \
292 }
293
294class GrCCPRTest_busyPath : public CCPRRenderingTest {
295 void onRun(skiatest::Reporter* reporter, const CCPRPathDrawer& ccpr) const override {
296 static constexpr int kNumBusyVerbs = 1 << 17;
297 ccpr.clear();
298 SkPath busyPath;
299 busyPath.moveTo(0, 0); // top left
300 busyPath.lineTo(kCanvasSize, kCanvasSize); // bottom right
301 for (int i = 2; i < kNumBusyVerbs; ++i) {
302 float offset = i * ((float)kCanvasSize / kNumBusyVerbs);
303 busyPath.lineTo(kCanvasSize - offset, kCanvasSize + offset); // offscreen
304 }
305 ccpr.drawPath(busyPath);
306
307 ccpr.flush(); // If this doesn't crash, the test passed.
308 // If it does, maybe fiddle with fMaxInstancesPerDrawArraysWithoutCrashing in
309 // your platform's GrGLCaps.
310 }
311};
312DEF_CCPR_RENDERING_TEST(GrCCPRTest_busyPath)
Chris Daltoncc604e52017-10-06 16:27:32 -0600313
314#endif