blob: 9ac425c987db80edb05f661d3910ddcce79e4a56 [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 Dalton4da70192018-06-18 09:51:36 -060020#include "GrTexture.h"
Chris Daltoncc604e52017-10-06 16:27:32 -060021#include "SkMatrix.h"
Chris Daltonfddb6c02017-11-04 15:22:22 -060022#include "SkPathPriv.h"
Chris Daltoncc604e52017-10-06 16:27:32 -060023#include "SkRect.h"
Chris Dalton4da70192018-06-18 09:51:36 -060024#include "sk_tool_utils.h"
Chris Daltoncc604e52017-10-06 16:27:32 -060025#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 {
Chris Dalton4c458b12018-06-16 17:22:59 -060038 out->addCoverageFP(fCCPR->makeClipProcessor(rtc->priv().testingOnly_getOpListID(), fPath,
Chris Daltona32a3c32017-12-05 10:05:21 -070039 SkIRect::MakeWH(rtc->width(), rtc->height()),
Chris Dalton4c458b12018-06-16 17:22:59 -060040 rtc->width(), rtc->height(),
41 *context->contextPriv().caps()));
Chris Daltona32a3c32017-12-05 10:05:21 -070042 return true;
43 }
44 bool quickContains(const SkRect&) const final { return false; }
45 bool isRRect(const SkRect& rtBounds, SkRRect* rr, GrAA*) const final { return false; }
46 void getConservativeBounds(int width, int height, SkIRect* rect, bool* iior) const final {
47 rect->set(0, 0, width, height);
48 if (iior) {
49 *iior = false;
50 }
51 }
52 GrCoverageCountingPathRenderer* const fCCPR;
53 const SkPath fPath;
54};
55
Chris Daltoncc604e52017-10-06 16:27:32 -060056class CCPRPathDrawer {
57public:
Chris Daltonfddb6c02017-11-04 15:22:22 -060058 CCPRPathDrawer(GrContext* ctx, skiatest::Reporter* reporter)
Chris Daltoncc604e52017-10-06 16:27:32 -060059 : fCtx(ctx)
Chris Daltonfddb6c02017-11-04 15:22:22 -060060 , fCCPR(fCtx->contextPriv().drawingManager()->getCoverageCountingPathRenderer())
Robert Phillips0c4b7b12018-03-06 08:20:37 -050061 , fRTC(fCtx->contextPriv().makeDeferredRenderTargetContext(
62 SkBackingFit::kExact, kCanvasSize,
Chris Daltoncc604e52017-10-06 16:27:32 -060063 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 Dalton4da70192018-06-18 09:51:36 -060073 GrContext* ctx() const { return fCtx; }
74 GrCoverageCountingPathRenderer* ccpr() const { return fCCPR; }
75
Chris Daltonfddb6c02017-11-04 15:22:22 -060076 bool valid() const { return fCCPR && fRTC; }
Chris Dalton344e9032017-12-11 15:42:09 -070077 void clear() const { fRTC->clear(nullptr, 0, GrRenderTargetContext::CanClearFullscreen::kYes); }
Chris Daltonfddb6c02017-11-04 15:22:22 -060078 void abandonGrContext() { fCtx = nullptr; fCCPR = nullptr; fRTC = nullptr; }
Chris Daltoncc604e52017-10-06 16:27:32 -060079
Chris Daltona2b5b642018-06-24 13:08:57 -060080 void drawPath(const SkPath& path, const SkMatrix& matrix = SkMatrix::I()) const {
Chris Daltonfddb6c02017-11-04 15:22:22 -060081 SkASSERT(this->valid());
Chris Daltoncc604e52017-10-06 16:27:32 -060082
Chris Daltoncc604e52017-10-06 16:27:32 -060083 GrPaint paint;
Chris Dalton4da70192018-06-18 09:51:36 -060084 paint.setColor4f(GrColor4f(0, 1, 0, 1));
Chris Daltonfddb6c02017-11-04 15:22:22 -060085
Chris Daltoncc604e52017-10-06 16:27:32 -060086 GrNoClip noClip;
87 SkIRect clipBounds = SkIRect::MakeWH(kCanvasSize, kCanvasSize);
Chris Daltonfddb6c02017-11-04 15:22:22 -060088
Chris Daltoncc604e52017-10-06 16:27:32 -060089 GrShape shape(path);
Chris Daltonfddb6c02017-11-04 15:22:22 -060090
Chris Daltona2b5b642018-06-24 13:08:57 -060091 fCCPR->testingOnly_drawPathDirectly({
92 fCtx, std::move(paint), &GrUserStencilSettings::kUnused, fRTC.get(), &noClip,
93 &clipBounds, &matrix, &shape, GrAAType::kCoverage, false});
Chris Daltoncc604e52017-10-06 16:27:32 -060094 }
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 Dalton4da70192018-06-18 09:51:36 -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;
Chris Dalton4da70192018-06-18 09:51:36 -0600126 mockOptions.fConfigOptions[kAlpha_8_GrPixelConfig].fRenderability =
127 GrMockOptions::ConfigOptions::Renderability::kNonMSAA;
128 mockOptions.fConfigOptions[kAlpha_8_GrPixelConfig].fTexturable = true;
Chris Daltonfddb6c02017-11-04 15:22:22 -0600129 mockOptions.fGeometryShaderSupport = true;
Chris Daltonfddb6c02017-11-04 15:22:22 -0600130 mockOptions.fIntegerSupport = true;
131 mockOptions.fFlatInterpolationSupport = true;
Chris Daltonfddb6c02017-11-04 15:22:22 -0600132
133 GrContextOptions ctxOptions;
134 ctxOptions.fAllowPathMaskCaching = false;
135 ctxOptions.fGpuPathRenderers = GpuPathRenderers::kCoverageCounting;
136
Chris Daltona2b5b642018-06-24 13:08:57 -0600137 this->customizeOptions(&mockOptions, &ctxOptions);
138
Chris Daltonfddb6c02017-11-04 15:22:22 -0600139 fMockContext = GrContext::MakeMock(&mockOptions, ctxOptions);
140 if (!fMockContext) {
141 ERRORF(reporter, "could not create mock context");
142 return;
143 }
144 if (!fMockContext->unique()) {
145 ERRORF(reporter, "mock context is not unique");
146 return;
147 }
148
149 CCPRPathDrawer ccpr(fMockContext.get(), reporter);
150 if (!ccpr.valid()) {
151 return;
152 }
153
154 fPath.moveTo(0, 0);
155 fPath.cubicTo(50, 50, 0, 50, 50, 0);
156 this->onRun(reporter, ccpr);
Chris Daltoncc604e52017-10-06 16:27:32 -0600157 }
158
Chris Daltonfddb6c02017-11-04 15:22:22 -0600159 virtual ~CCPRTest() {}
160
161protected:
Chris Daltona2b5b642018-06-24 13:08:57 -0600162 virtual void customizeOptions(GrMockOptions*, GrContextOptions*) {}
Chris Daltonfddb6c02017-11-04 15:22:22 -0600163 virtual void onRun(skiatest::Reporter* reporter, CCPRPathDrawer& ccpr) = 0;
164
Chris Dalton4da70192018-06-18 09:51:36 -0600165 sk_sp<GrContext> fMockContext;
166 SkPath fPath;
Chris Daltonfddb6c02017-11-04 15:22:22 -0600167};
168
Brian Salomondcfca432017-11-15 15:48:03 -0500169#define DEF_CCPR_TEST(name) \
170 DEF_GPUTEST(name, reporter, /* options */) { \
171 name test; \
172 test.run(reporter); \
Chris Daltoncc604e52017-10-06 16:27:32 -0600173 }
174
Chris Daltonfddb6c02017-11-04 15:22:22 -0600175class GrCCPRTest_cleanup : public CCPRTest {
176 void onRun(skiatest::Reporter* reporter, CCPRPathDrawer& ccpr) override {
177 REPORTER_ASSERT(reporter, SkPathPriv::TestingOnly_unique(fPath));
Chris Daltoncc604e52017-10-06 16:27:32 -0600178
Chris Daltonfddb6c02017-11-04 15:22:22 -0600179 // Ensure paths get unreffed.
180 for (int i = 0; i < 10; ++i) {
181 ccpr.drawPath(fPath);
Chris Dalton4bfb50b2018-05-21 09:10:53 -0600182 }
183 REPORTER_ASSERT(reporter, !SkPathPriv::TestingOnly_unique(fPath));
184 ccpr.flush();
185 REPORTER_ASSERT(reporter, SkPathPriv::TestingOnly_unique(fPath));
186
187 // Ensure clip paths get unreffed.
188 for (int i = 0; i < 10; ++i) {
Chris Daltona32a3c32017-12-05 10:05:21 -0700189 ccpr.clipFullscreenRect(fPath);
Chris Daltonfddb6c02017-11-04 15:22:22 -0600190 }
191 REPORTER_ASSERT(reporter, !SkPathPriv::TestingOnly_unique(fPath));
192 ccpr.flush();
193 REPORTER_ASSERT(reporter, SkPathPriv::TestingOnly_unique(fPath));
194
195 // Ensure paths get unreffed when we delete the context without flushing.
196 for (int i = 0; i < 10; ++i) {
197 ccpr.drawPath(fPath);
Chris Daltona32a3c32017-12-05 10:05:21 -0700198 ccpr.clipFullscreenRect(fPath);
Chris Daltonfddb6c02017-11-04 15:22:22 -0600199 }
200 ccpr.abandonGrContext();
201 REPORTER_ASSERT(reporter, !SkPathPriv::TestingOnly_unique(fPath));
202 fMockContext.reset();
203 REPORTER_ASSERT(reporter, SkPathPriv::TestingOnly_unique(fPath));
204 }
205};
206DEF_CCPR_TEST(GrCCPRTest_cleanup)
207
Chris Dalton91ab1552018-04-18 13:24:25 -0600208class GrCCPRTest_cleanupWithTexAllocFail : public GrCCPRTest_cleanup {
Chris Daltona2b5b642018-06-24 13:08:57 -0600209 void customizeOptions(GrMockOptions* mockOptions, GrContextOptions*) override {
210 mockOptions->fFailTextureAllocations = true;
Chris Dalton91ab1552018-04-18 13:24:25 -0600211 }
212};
213DEF_CCPR_TEST(GrCCPRTest_cleanupWithTexAllocFail)
214
Chris Dalton080baa42017-11-06 14:19:19 -0700215class GrCCPRTest_unregisterCulledOps : public CCPRTest {
216 void onRun(skiatest::Reporter* reporter, CCPRPathDrawer& ccpr) override {
217 REPORTER_ASSERT(reporter, SkPathPriv::TestingOnly_unique(fPath));
218
219 // Ensure Ops get unregistered from CCPR when culled early.
220 ccpr.drawPath(fPath);
221 REPORTER_ASSERT(reporter, !SkPathPriv::TestingOnly_unique(fPath));
222 ccpr.clear(); // Clear should delete the CCPR Op.
223 REPORTER_ASSERT(reporter, SkPathPriv::TestingOnly_unique(fPath));
224 ccpr.flush(); // Should not crash (DrawPathsOp should have unregistered itself).
225
226 // Ensure Op unregisters work when we delete the context without flushing.
227 ccpr.drawPath(fPath);
228 REPORTER_ASSERT(reporter, !SkPathPriv::TestingOnly_unique(fPath));
229 ccpr.clear(); // Clear should delete the CCPR DrawPathsOp.
230 REPORTER_ASSERT(reporter, SkPathPriv::TestingOnly_unique(fPath));
231 ccpr.abandonGrContext();
232 fMockContext.reset(); // Should not crash (DrawPathsOp should have unregistered itself).
233 }
234};
235DEF_CCPR_TEST(GrCCPRTest_unregisterCulledOps)
236
Chris Daltonc9c97b72017-11-27 15:34:26 -0700237class GrCCPRTest_parseEmptyPath : public CCPRTest {
238 void onRun(skiatest::Reporter* reporter, CCPRPathDrawer& ccpr) override {
239 REPORTER_ASSERT(reporter, SkPathPriv::TestingOnly_unique(fPath));
240
241 // Make a path large enough that ccpr chooses to crop it by the RT bounds, and ends up with
242 // an empty path.
243 SkPath largeOutsidePath;
244 largeOutsidePath.moveTo(-1e30f, -1e30f);
245 largeOutsidePath.lineTo(-1e30f, +1e30f);
246 largeOutsidePath.lineTo(-1e10f, +1e30f);
247 ccpr.drawPath(largeOutsidePath);
248
249 // Normally an empty path is culled before reaching ccpr, however we use a back door for
250 // testing so this path will make it.
251 SkPath emptyPath;
252 SkASSERT(emptyPath.isEmpty());
253 ccpr.drawPath(emptyPath);
254
255 // This is the test. It will exercise various internal asserts and verify we do not crash.
256 ccpr.flush();
Chris Daltona32a3c32017-12-05 10:05:21 -0700257
258 // Now try again with clips.
259 ccpr.clipFullscreenRect(largeOutsidePath);
260 ccpr.clipFullscreenRect(emptyPath);
261 ccpr.flush();
262
263 // ... and both.
264 ccpr.drawPath(largeOutsidePath);
265 ccpr.clipFullscreenRect(largeOutsidePath);
266 ccpr.drawPath(emptyPath);
267 ccpr.clipFullscreenRect(emptyPath);
268 ccpr.flush();
Chris Daltonc9c97b72017-11-27 15:34:26 -0700269 }
270};
271DEF_CCPR_TEST(GrCCPRTest_parseEmptyPath)
272
Chris Dalton4da70192018-06-18 09:51:36 -0600273// This test exercises CCPR's cache capabilities by drawing many paths with two different
274// transformation matrices. We then vary the matrices independently by whole and partial pixels,
275// and verify the caching behaved as expected.
276class GrCCPRTest_cache : public CCPRTest {
Chris Daltona2b5b642018-06-24 13:08:57 -0600277 void customizeOptions(GrMockOptions*, GrContextOptions* ctxOptions) override {
278 ctxOptions->fAllowPathMaskCaching = true;
279 }
280
Chris Dalton4da70192018-06-18 09:51:36 -0600281 void onRun(skiatest::Reporter* reporter, CCPRPathDrawer& ccpr) override {
282 static constexpr int kPathSize = 20;
283 SkRandom rand;
284
Chris Daltona8429cf2018-06-22 11:43:31 -0600285 SkPath paths[300];
Chris Dalton4da70192018-06-18 09:51:36 -0600286 int primes[11] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31};
287 for (size_t i = 0; i < SK_ARRAY_COUNT(paths); ++i) {
288 int numPts = rand.nextRangeU(GrShape::kMaxKeyFromDataVerbCnt + 1,
289 GrShape::kMaxKeyFromDataVerbCnt * 2);
290 paths[i] = sk_tool_utils::make_star(SkRect::MakeIWH(kPathSize, kPathSize), numPts,
291 primes[rand.nextU() % SK_ARRAY_COUNT(primes)]);
292 }
293
294 SkMatrix matrices[2] = {
295 SkMatrix::MakeTrans(5, 5),
296 SkMatrix::MakeTrans(kCanvasSize - kPathSize - 5, kCanvasSize - kPathSize - 5)
297 };
298
299 int firstAtlasID = -1;
300
Chris Daltona8429cf2018-06-22 11:43:31 -0600301 for (int iterIdx = 0; iterIdx < 10; ++iterIdx) {
302 static constexpr int kNumHitsBeforeStash = 2;
303 static const GrUniqueKey gInvalidUniqueKey;
304
305 // Draw all the paths then flush. Repeat until a new stash occurs.
306 const GrUniqueKey* stashedAtlasKey = &gInvalidUniqueKey;
307 for (int j = 0; j < kNumHitsBeforeStash; ++j) {
308 // Nothing should be stashed until its hit count reaches kNumHitsBeforeStash.
309 REPORTER_ASSERT(reporter, !stashedAtlasKey->isValid());
310
311 for (size_t i = 0; i < SK_ARRAY_COUNT(paths); ++i) {
Chris Daltona2b5b642018-06-24 13:08:57 -0600312 ccpr.drawPath(paths[i], matrices[i % 2]);
Chris Daltona8429cf2018-06-22 11:43:31 -0600313 }
314 ccpr.flush();
315
316 stashedAtlasKey = &ccpr.ccpr()->testingOnly_getStashedAtlasKey();
Chris Dalton4da70192018-06-18 09:51:36 -0600317 }
Chris Dalton4da70192018-06-18 09:51:36 -0600318
319 // Figure out the mock backend ID of the atlas texture stashed away by CCPR.
320 GrMockTextureInfo stashedAtlasInfo;
321 stashedAtlasInfo.fID = -1;
Chris Daltona8429cf2018-06-22 11:43:31 -0600322 if (stashedAtlasKey->isValid()) {
Chris Dalton4da70192018-06-18 09:51:36 -0600323 GrResourceProvider* rp = ccpr.ctx()->contextPriv().resourceProvider();
Chris Daltona8429cf2018-06-22 11:43:31 -0600324 sk_sp<GrSurface> stashedAtlas = rp->findByUniqueKey<GrSurface>(*stashedAtlasKey);
Chris Dalton4da70192018-06-18 09:51:36 -0600325 REPORTER_ASSERT(reporter, stashedAtlas);
326 if (stashedAtlas) {
327 const auto& backendTexture = stashedAtlas->asTexture()->getBackendTexture();
328 backendTexture.getMockTextureInfo(&stashedAtlasInfo);
329 }
330 }
331
Chris Daltona8429cf2018-06-22 11:43:31 -0600332 if (0 == iterIdx) {
333 // First iteration: just note the ID of the stashed atlas and continue.
334 REPORTER_ASSERT(reporter, stashedAtlasKey->isValid());
Chris Dalton4da70192018-06-18 09:51:36 -0600335 firstAtlasID = stashedAtlasInfo.fID;
336 continue;
337 }
338
Chris Daltona8429cf2018-06-22 11:43:31 -0600339 switch (iterIdx % 3) {
Chris Dalton4da70192018-06-18 09:51:36 -0600340 case 1:
341 // This draw should have gotten 100% cache hits; we only did integer translates
342 // last time (or none if it was the first flush). Therefore, no atlas should
343 // have been stashed away.
Chris Daltona8429cf2018-06-22 11:43:31 -0600344 REPORTER_ASSERT(reporter, !stashedAtlasKey->isValid());
Chris Dalton4da70192018-06-18 09:51:36 -0600345
346 // Invalidate even path masks.
347 matrices[0].preTranslate(1.6f, 1.4f);
348 break;
349
350 case 2:
351 // Even path masks were invalidated last iteration by a subpixel translate. They
352 // should have been re-rendered this time and stashed away in the CCPR atlas.
Chris Daltona8429cf2018-06-22 11:43:31 -0600353 REPORTER_ASSERT(reporter, stashedAtlasKey->isValid());
Chris Dalton4da70192018-06-18 09:51:36 -0600354
355 // 'firstAtlasID' should be kept as a scratch texture in the resource cache.
356 REPORTER_ASSERT(reporter, stashedAtlasInfo.fID == firstAtlasID);
357
358 // Invalidate odd path masks.
359 matrices[1].preTranslate(-1.4f, -1.6f);
360 break;
361
362 case 0:
363 // Odd path masks were invalidated last iteration by a subpixel translate. They
364 // should have been re-rendered this time and stashed away in the CCPR atlas.
Chris Daltona8429cf2018-06-22 11:43:31 -0600365 REPORTER_ASSERT(reporter, stashedAtlasKey->isValid());
Chris Dalton4da70192018-06-18 09:51:36 -0600366
367 // 'firstAtlasID' is the same texture that got stashed away last time (assuming
368 // no assertion failures). So if it also got stashed this time, it means we
369 // first copied the even paths out of it, then recycled the exact same texture
370 // to render the odd paths. This is the expected behavior.
371 REPORTER_ASSERT(reporter, stashedAtlasInfo.fID == firstAtlasID);
372
373 // Integer translates: all path masks stay valid.
374 matrices[0].preTranslate(-1, -1);
375 matrices[1].preTranslate(1, 1);
376 break;
377 }
378 }
379 }
380};
381DEF_CCPR_TEST(GrCCPRTest_cache)
382
Chris Daltonfddb6c02017-11-04 15:22:22 -0600383class CCPRRenderingTest {
384public:
385 void run(skiatest::Reporter* reporter, GrContext* ctx) const {
386 if (!ctx->contextPriv().drawingManager()->getCoverageCountingPathRenderer()) {
387 return; // CCPR is not enabled on this GPU.
388 }
389 CCPRPathDrawer ccpr(ctx, reporter);
390 if (!ccpr.valid()) {
391 return;
392 }
393 this->onRun(reporter, ccpr);
394 }
395
396 virtual ~CCPRRenderingTest() {}
397
398protected:
399 virtual void onRun(skiatest::Reporter* reporter, const CCPRPathDrawer& ccpr) const = 0;
400};
401
402#define DEF_CCPR_RENDERING_TEST(name) \
403 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(name, reporter, ctxInfo) { \
404 name test; \
405 test.run(reporter, ctxInfo.grContext()); \
406 }
407
408class GrCCPRTest_busyPath : public CCPRRenderingTest {
409 void onRun(skiatest::Reporter* reporter, const CCPRPathDrawer& ccpr) const override {
410 static constexpr int kNumBusyVerbs = 1 << 17;
411 ccpr.clear();
412 SkPath busyPath;
413 busyPath.moveTo(0, 0); // top left
414 busyPath.lineTo(kCanvasSize, kCanvasSize); // bottom right
415 for (int i = 2; i < kNumBusyVerbs; ++i) {
416 float offset = i * ((float)kCanvasSize / kNumBusyVerbs);
417 busyPath.lineTo(kCanvasSize - offset, kCanvasSize + offset); // offscreen
418 }
419 ccpr.drawPath(busyPath);
420
421 ccpr.flush(); // If this doesn't crash, the test passed.
422 // If it does, maybe fiddle with fMaxInstancesPerDrawArraysWithoutCrashing in
423 // your platform's GrGLCaps.
424 }
425};
426DEF_CCPR_RENDERING_TEST(GrCCPRTest_busyPath)