blob: cba94b4bf59b55422f25c17a17900441a5f041ab [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())
Chris Daltoncc604e52017-10-06 16:27:32 -060062 , fRTC(fCtx->makeDeferredRenderTargetContext(SkBackingFit::kExact, kCanvasSize,
63 kCanvasSize, kRGBA_8888_GrPixelConfig,
64 nullptr)) {
Chris Daltonfddb6c02017-11-04 15:22:22 -060065 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 Daltoncc604e52017-10-06 16:27:32 -060070 }
71 }
72
Chris Daltonfddb6c02017-11-04 15:22:22 -060073 bool valid() const { return fCCPR && fRTC; }
Chris Dalton344e9032017-12-11 15:42:09 -070074 void clear() const { fRTC->clear(nullptr, 0, GrRenderTargetContext::CanClearFullscreen::kYes); }
Chris Daltonfddb6c02017-11-04 15:22:22 -060075 void abandonGrContext() { fCtx = nullptr; fCCPR = nullptr; fRTC = nullptr; }
Chris Daltoncc604e52017-10-06 16:27:32 -060076
Chris Daltonfddb6c02017-11-04 15:22:22 -060077 void drawPath(SkPath path, GrColor4f color = GrColor4f(0, 1, 0, 1)) const {
78 SkASSERT(this->valid());
Chris Daltoncc604e52017-10-06 16:27:32 -060079
Chris Daltoncc604e52017-10-06 16:27:32 -060080 GrPaint paint;
81 paint.setColor4f(color);
Chris Daltonfddb6c02017-11-04 15:22:22 -060082
Chris Daltoncc604e52017-10-06 16:27:32 -060083 GrNoClip noClip;
84 SkIRect clipBounds = SkIRect::MakeWH(kCanvasSize, kCanvasSize);
Chris Daltonfddb6c02017-11-04 15:22:22 -060085
Chris Daltoncc604e52017-10-06 16:27:32 -060086 SkMatrix matrix = SkMatrix::I();
Chris Daltonfddb6c02017-11-04 15:22:22 -060087
88 path.setIsVolatile(true);
Chris Daltoncc604e52017-10-06 16:27:32 -060089 GrShape shape(path);
Chris Daltonfddb6c02017-11-04 15:22:22 -060090
Chris Daltoncc604e52017-10-06 16:27:32 -060091 fCCPR->drawPath({fCtx, std::move(paint), &GrUserStencilSettings::kUnused, fRTC.get(),
92 &noClip, &clipBounds, &matrix, &shape, GrAAType::kCoverage, false});
93 }
94
Chris Daltona32a3c32017-12-05 10:05:21 -070095 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 Daltonfddb6c02017-11-04 15:22:22 -0600105 void flush() const {
106 SkASSERT(this->valid());
Chris Daltoncc604e52017-10-06 16:27:32 -0600107 fCtx->flush();
108 }
109
110private:
Chris Daltonfddb6c02017-11-04 15:22:22 -0600111 GrContext* fCtx;
112 GrCoverageCountingPathRenderer* fCCPR;
113 sk_sp<GrRenderTargetContext> fRTC;
Chris Daltoncc604e52017-10-06 16:27:32 -0600114};
115
Chris Daltonfddb6c02017-11-04 15:22:22 -0600116class CCPRTest {
117public:
118 void run(skiatest::Reporter* reporter) {
119 GrMockOptions mockOptions;
120 mockOptions.fInstanceAttribSupport = true;
121 mockOptions.fMapBufferFlags = GrCaps::kCanMap_MapFlag;
Brian Salomond653cac2018-02-01 13:58:00 -0500122 mockOptions.fConfigOptions[kAlpha_half_GrPixelConfig].fRenderability =
123 GrMockOptions::ConfigOptions::Renderability::kNonMSAA;
Chris Daltonfddb6c02017-11-04 15:22:22 -0600124 mockOptions.fConfigOptions[kAlpha_half_GrPixelConfig].fTexturable = true;
125 mockOptions.fGeometryShaderSupport = true;
Chris Daltonfddb6c02017-11-04 15:22:22 -0600126 mockOptions.fIntegerSupport = true;
127 mockOptions.fFlatInterpolationSupport = true;
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:
156 virtual void onRun(skiatest::Reporter* reporter, CCPRPathDrawer& ccpr) = 0;
157
158 sk_sp<GrContext> fMockContext;
159 SkPath fPath;
160};
161
Brian Salomondcfca432017-11-15 15:48:03 -0500162#define DEF_CCPR_TEST(name) \
163 DEF_GPUTEST(name, reporter, /* options */) { \
164 name test; \
165 test.run(reporter); \
Chris Daltoncc604e52017-10-06 16:27:32 -0600166 }
167
Chris Daltonfddb6c02017-11-04 15:22:22 -0600168class GrCCPRTest_cleanup : public CCPRTest {
169 void onRun(skiatest::Reporter* reporter, CCPRPathDrawer& ccpr) override {
170 REPORTER_ASSERT(reporter, SkPathPriv::TestingOnly_unique(fPath));
Chris Daltoncc604e52017-10-06 16:27:32 -0600171
Chris Daltonfddb6c02017-11-04 15:22:22 -0600172 // Ensure paths get unreffed.
173 for (int i = 0; i < 10; ++i) {
174 ccpr.drawPath(fPath);
Chris Daltona32a3c32017-12-05 10:05:21 -0700175 ccpr.clipFullscreenRect(fPath);
Chris Daltonfddb6c02017-11-04 15:22:22 -0600176 }
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 Daltona32a3c32017-12-05 10:05:21 -0700184 ccpr.clipFullscreenRect(fPath);
Chris Daltonfddb6c02017-11-04 15:22:22 -0600185 }
186 ccpr.abandonGrContext();
187 REPORTER_ASSERT(reporter, !SkPathPriv::TestingOnly_unique(fPath));
188 fMockContext.reset();
189 REPORTER_ASSERT(reporter, SkPathPriv::TestingOnly_unique(fPath));
190 }
191};
192DEF_CCPR_TEST(GrCCPRTest_cleanup)
193
Chris Dalton080baa42017-11-06 14:19:19 -0700194class 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};
214DEF_CCPR_TEST(GrCCPRTest_unregisterCulledOps)
215
Chris Daltonc9c97b72017-11-27 15:34:26 -0700216class 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 Daltona32a3c32017-12-05 10:05:21 -0700236
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 Daltonc9c97b72017-11-27 15:34:26 -0700248 }
249};
250DEF_CCPR_TEST(GrCCPRTest_parseEmptyPath)
251
Chris Daltonfddb6c02017-11-04 15:22:22 -0600252class CCPRRenderingTest {
253public:
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
267protected:
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
277class 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};
295DEF_CCPR_RENDERING_TEST(GrCCPRTest_busyPath)
Chris Daltoncc604e52017-10-06 16:27:32 -0600296
297#endif