blob: 2a51c1b176bad863766e73b855b5406179b2b0ea [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 Daltonfddb6c02017-11-04 15:22:22 -0600129
130 GrContextOptions ctxOptions;
131 ctxOptions.fAllowPathMaskCaching = false;
132 ctxOptions.fGpuPathRenderers = GpuPathRenderers::kCoverageCounting;
133
134 fMockContext = GrContext::MakeMock(&mockOptions, ctxOptions);
135 if (!fMockContext) {
136 ERRORF(reporter, "could not create mock context");
137 return;
138 }
139 if (!fMockContext->unique()) {
140 ERRORF(reporter, "mock context is not unique");
141 return;
142 }
143
144 CCPRPathDrawer ccpr(fMockContext.get(), reporter);
145 if (!ccpr.valid()) {
146 return;
147 }
148
149 fPath.moveTo(0, 0);
150 fPath.cubicTo(50, 50, 0, 50, 50, 0);
151 this->onRun(reporter, ccpr);
Chris Daltoncc604e52017-10-06 16:27:32 -0600152 }
153
Chris Daltonfddb6c02017-11-04 15:22:22 -0600154 virtual ~CCPRTest() {}
155
156protected:
157 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 Daltona32a3c32017-12-05 10:05:21 -0700176 ccpr.clipFullscreenRect(fPath);
Chris Daltonfddb6c02017-11-04 15:22:22 -0600177 }
178 REPORTER_ASSERT(reporter, !SkPathPriv::TestingOnly_unique(fPath));
179 ccpr.flush();
180 REPORTER_ASSERT(reporter, SkPathPriv::TestingOnly_unique(fPath));
181
182 // Ensure paths get unreffed when we delete the context without flushing.
183 for (int i = 0; i < 10; ++i) {
184 ccpr.drawPath(fPath);
Chris Daltona32a3c32017-12-05 10:05:21 -0700185 ccpr.clipFullscreenRect(fPath);
Chris Daltonfddb6c02017-11-04 15:22:22 -0600186 }
187 ccpr.abandonGrContext();
188 REPORTER_ASSERT(reporter, !SkPathPriv::TestingOnly_unique(fPath));
189 fMockContext.reset();
190 REPORTER_ASSERT(reporter, SkPathPriv::TestingOnly_unique(fPath));
191 }
192};
193DEF_CCPR_TEST(GrCCPRTest_cleanup)
194
Chris Dalton080baa42017-11-06 14:19:19 -0700195class GrCCPRTest_unregisterCulledOps : public CCPRTest {
196 void onRun(skiatest::Reporter* reporter, CCPRPathDrawer& ccpr) override {
197 REPORTER_ASSERT(reporter, SkPathPriv::TestingOnly_unique(fPath));
198
199 // Ensure Ops get unregistered from CCPR when culled early.
200 ccpr.drawPath(fPath);
201 REPORTER_ASSERT(reporter, !SkPathPriv::TestingOnly_unique(fPath));
202 ccpr.clear(); // Clear should delete the CCPR Op.
203 REPORTER_ASSERT(reporter, SkPathPriv::TestingOnly_unique(fPath));
204 ccpr.flush(); // Should not crash (DrawPathsOp should have unregistered itself).
205
206 // Ensure Op unregisters work when we delete the context without flushing.
207 ccpr.drawPath(fPath);
208 REPORTER_ASSERT(reporter, !SkPathPriv::TestingOnly_unique(fPath));
209 ccpr.clear(); // Clear should delete the CCPR DrawPathsOp.
210 REPORTER_ASSERT(reporter, SkPathPriv::TestingOnly_unique(fPath));
211 ccpr.abandonGrContext();
212 fMockContext.reset(); // Should not crash (DrawPathsOp should have unregistered itself).
213 }
214};
215DEF_CCPR_TEST(GrCCPRTest_unregisterCulledOps)
216
Chris Daltonc9c97b72017-11-27 15:34:26 -0700217class GrCCPRTest_parseEmptyPath : public CCPRTest {
218 void onRun(skiatest::Reporter* reporter, CCPRPathDrawer& ccpr) override {
219 REPORTER_ASSERT(reporter, SkPathPriv::TestingOnly_unique(fPath));
220
221 // Make a path large enough that ccpr chooses to crop it by the RT bounds, and ends up with
222 // an empty path.
223 SkPath largeOutsidePath;
224 largeOutsidePath.moveTo(-1e30f, -1e30f);
225 largeOutsidePath.lineTo(-1e30f, +1e30f);
226 largeOutsidePath.lineTo(-1e10f, +1e30f);
227 ccpr.drawPath(largeOutsidePath);
228
229 // Normally an empty path is culled before reaching ccpr, however we use a back door for
230 // testing so this path will make it.
231 SkPath emptyPath;
232 SkASSERT(emptyPath.isEmpty());
233 ccpr.drawPath(emptyPath);
234
235 // This is the test. It will exercise various internal asserts and verify we do not crash.
236 ccpr.flush();
Chris Daltona32a3c32017-12-05 10:05:21 -0700237
238 // Now try again with clips.
239 ccpr.clipFullscreenRect(largeOutsidePath);
240 ccpr.clipFullscreenRect(emptyPath);
241 ccpr.flush();
242
243 // ... and both.
244 ccpr.drawPath(largeOutsidePath);
245 ccpr.clipFullscreenRect(largeOutsidePath);
246 ccpr.drawPath(emptyPath);
247 ccpr.clipFullscreenRect(emptyPath);
248 ccpr.flush();
Chris Daltonc9c97b72017-11-27 15:34:26 -0700249 }
250};
251DEF_CCPR_TEST(GrCCPRTest_parseEmptyPath)
252
Chris Daltonfddb6c02017-11-04 15:22:22 -0600253class CCPRRenderingTest {
254public:
255 void run(skiatest::Reporter* reporter, GrContext* ctx) const {
256 if (!ctx->contextPriv().drawingManager()->getCoverageCountingPathRenderer()) {
257 return; // CCPR is not enabled on this GPU.
258 }
259 CCPRPathDrawer ccpr(ctx, reporter);
260 if (!ccpr.valid()) {
261 return;
262 }
263 this->onRun(reporter, ccpr);
264 }
265
266 virtual ~CCPRRenderingTest() {}
267
268protected:
269 virtual void onRun(skiatest::Reporter* reporter, const CCPRPathDrawer& ccpr) const = 0;
270};
271
272#define DEF_CCPR_RENDERING_TEST(name) \
273 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(name, reporter, ctxInfo) { \
274 name test; \
275 test.run(reporter, ctxInfo.grContext()); \
276 }
277
278class GrCCPRTest_busyPath : public CCPRRenderingTest {
279 void onRun(skiatest::Reporter* reporter, const CCPRPathDrawer& ccpr) const override {
280 static constexpr int kNumBusyVerbs = 1 << 17;
281 ccpr.clear();
282 SkPath busyPath;
283 busyPath.moveTo(0, 0); // top left
284 busyPath.lineTo(kCanvasSize, kCanvasSize); // bottom right
285 for (int i = 2; i < kNumBusyVerbs; ++i) {
286 float offset = i * ((float)kCanvasSize / kNumBusyVerbs);
287 busyPath.lineTo(kCanvasSize - offset, kCanvasSize + offset); // offscreen
288 }
289 ccpr.drawPath(busyPath);
290
291 ccpr.flush(); // If this doesn't crash, the test passed.
292 // If it does, maybe fiddle with fMaxInstancesPerDrawArraysWithoutCrashing in
293 // your platform's GrGLCaps.
294 }
295};
296DEF_CCPR_RENDERING_TEST(GrCCPRTest_busyPath)
Chris Daltoncc604e52017-10-06 16:27:32 -0600297
298#endif