blob: 6f0d8852d96eb107a385ea7ca3e1c83189e2f07a [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 Daltona32a3c32017-12-05 10:05:21 -0700178 ccpr.clipFullscreenRect(fPath);
Chris Daltonfddb6c02017-11-04 15:22:22 -0600179 }
180 REPORTER_ASSERT(reporter, !SkPathPriv::TestingOnly_unique(fPath));
181 ccpr.flush();
182 REPORTER_ASSERT(reporter, SkPathPriv::TestingOnly_unique(fPath));
183
184 // Ensure paths get unreffed when we delete the context without flushing.
185 for (int i = 0; i < 10; ++i) {
186 ccpr.drawPath(fPath);
Chris Daltona32a3c32017-12-05 10:05:21 -0700187 ccpr.clipFullscreenRect(fPath);
Chris Daltonfddb6c02017-11-04 15:22:22 -0600188 }
189 ccpr.abandonGrContext();
190 REPORTER_ASSERT(reporter, !SkPathPriv::TestingOnly_unique(fPath));
191 fMockContext.reset();
192 REPORTER_ASSERT(reporter, SkPathPriv::TestingOnly_unique(fPath));
193 }
194};
195DEF_CCPR_TEST(GrCCPRTest_cleanup)
196
Chris Dalton91ab1552018-04-18 13:24:25 -0600197class GrCCPRTest_cleanupWithTexAllocFail : public GrCCPRTest_cleanup {
198 void customizeMockOptions(GrMockOptions* options) override {
199 options->fFailTextureAllocations = true;
200 }
201};
202DEF_CCPR_TEST(GrCCPRTest_cleanupWithTexAllocFail)
203
Chris Dalton080baa42017-11-06 14:19:19 -0700204class GrCCPRTest_unregisterCulledOps : public CCPRTest {
205 void onRun(skiatest::Reporter* reporter, CCPRPathDrawer& ccpr) override {
206 REPORTER_ASSERT(reporter, SkPathPriv::TestingOnly_unique(fPath));
207
208 // Ensure Ops get unregistered from CCPR when culled early.
209 ccpr.drawPath(fPath);
210 REPORTER_ASSERT(reporter, !SkPathPriv::TestingOnly_unique(fPath));
211 ccpr.clear(); // Clear should delete the CCPR Op.
212 REPORTER_ASSERT(reporter, SkPathPriv::TestingOnly_unique(fPath));
213 ccpr.flush(); // Should not crash (DrawPathsOp should have unregistered itself).
214
215 // Ensure Op unregisters work when we delete the context without flushing.
216 ccpr.drawPath(fPath);
217 REPORTER_ASSERT(reporter, !SkPathPriv::TestingOnly_unique(fPath));
218 ccpr.clear(); // Clear should delete the CCPR DrawPathsOp.
219 REPORTER_ASSERT(reporter, SkPathPriv::TestingOnly_unique(fPath));
220 ccpr.abandonGrContext();
221 fMockContext.reset(); // Should not crash (DrawPathsOp should have unregistered itself).
222 }
223};
224DEF_CCPR_TEST(GrCCPRTest_unregisterCulledOps)
225
Chris Daltonc9c97b72017-11-27 15:34:26 -0700226class GrCCPRTest_parseEmptyPath : public CCPRTest {
227 void onRun(skiatest::Reporter* reporter, CCPRPathDrawer& ccpr) override {
228 REPORTER_ASSERT(reporter, SkPathPriv::TestingOnly_unique(fPath));
229
230 // Make a path large enough that ccpr chooses to crop it by the RT bounds, and ends up with
231 // an empty path.
232 SkPath largeOutsidePath;
233 largeOutsidePath.moveTo(-1e30f, -1e30f);
234 largeOutsidePath.lineTo(-1e30f, +1e30f);
235 largeOutsidePath.lineTo(-1e10f, +1e30f);
236 ccpr.drawPath(largeOutsidePath);
237
238 // Normally an empty path is culled before reaching ccpr, however we use a back door for
239 // testing so this path will make it.
240 SkPath emptyPath;
241 SkASSERT(emptyPath.isEmpty());
242 ccpr.drawPath(emptyPath);
243
244 // This is the test. It will exercise various internal asserts and verify we do not crash.
245 ccpr.flush();
Chris Daltona32a3c32017-12-05 10:05:21 -0700246
247 // Now try again with clips.
248 ccpr.clipFullscreenRect(largeOutsidePath);
249 ccpr.clipFullscreenRect(emptyPath);
250 ccpr.flush();
251
252 // ... and both.
253 ccpr.drawPath(largeOutsidePath);
254 ccpr.clipFullscreenRect(largeOutsidePath);
255 ccpr.drawPath(emptyPath);
256 ccpr.clipFullscreenRect(emptyPath);
257 ccpr.flush();
Chris Daltonc9c97b72017-11-27 15:34:26 -0700258 }
259};
260DEF_CCPR_TEST(GrCCPRTest_parseEmptyPath)
261
Chris Daltonfddb6c02017-11-04 15:22:22 -0600262class CCPRRenderingTest {
263public:
264 void run(skiatest::Reporter* reporter, GrContext* ctx) const {
265 if (!ctx->contextPriv().drawingManager()->getCoverageCountingPathRenderer()) {
266 return; // CCPR is not enabled on this GPU.
267 }
268 CCPRPathDrawer ccpr(ctx, reporter);
269 if (!ccpr.valid()) {
270 return;
271 }
272 this->onRun(reporter, ccpr);
273 }
274
275 virtual ~CCPRRenderingTest() {}
276
277protected:
278 virtual void onRun(skiatest::Reporter* reporter, const CCPRPathDrawer& ccpr) const = 0;
279};
280
281#define DEF_CCPR_RENDERING_TEST(name) \
282 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(name, reporter, ctxInfo) { \
283 name test; \
284 test.run(reporter, ctxInfo.grContext()); \
285 }
286
287class GrCCPRTest_busyPath : public CCPRRenderingTest {
288 void onRun(skiatest::Reporter* reporter, const CCPRPathDrawer& ccpr) const override {
289 static constexpr int kNumBusyVerbs = 1 << 17;
290 ccpr.clear();
291 SkPath busyPath;
292 busyPath.moveTo(0, 0); // top left
293 busyPath.lineTo(kCanvasSize, kCanvasSize); // bottom right
294 for (int i = 2; i < kNumBusyVerbs; ++i) {
295 float offset = i * ((float)kCanvasSize / kNumBusyVerbs);
296 busyPath.lineTo(kCanvasSize - offset, kCanvasSize + offset); // offscreen
297 }
298 ccpr.drawPath(busyPath);
299
300 ccpr.flush(); // If this doesn't crash, the test passed.
301 // If it does, maybe fiddle with fMaxInstancesPerDrawArraysWithoutCrashing in
302 // your platform's GrGLCaps.
303 }
304};
305DEF_CCPR_RENDERING_TEST(GrCCPRTest_busyPath)
Chris Daltoncc604e52017-10-06 16:27:32 -0600306
307#endif