17
17
#include " src/gpu/tessellate/GrVectorXform.h"
18
18
#include " src/gpu/tessellate/GrWangsFormula.h"
19
19
20
- constexpr static float kStandardCubicType = GrStrokeTessellateShader::kStandardCubicType ;
21
- constexpr static float kDoubleSidedRoundJoinType = -GrStrokeTessellateShader::kRoundJoinType ;
20
+ using Patch = GrStrokeTessellateShader::Patch;
21
+
22
+ constexpr static float kDoubleSidedRoundJoinType = -Patch::kRoundJoinType ;
22
23
23
24
static SkPoint lerp (const SkPoint& a, const SkPoint& b, float T) {
24
25
SkASSERT (1 != T); // The below does not guarantee lerp(a, b, 1) === b.
25
26
return (b - a) * T + a;
26
27
}
27
28
28
- void GrStrokePatchBuilder::allocVertexChunk (int minVertexAllocCount) {
29
- VertexChunk* chunk = &fVertexChunkArray ->push_back ();
30
- fCurrChunkVertexData = (SkPoint*)fTarget ->makeVertexSpaceAtLeast (
31
- sizeof (SkPoint), minVertexAllocCount, minVertexAllocCount, &chunk->fVertexBuffer ,
32
- &chunk->fBaseVertex , &fCurrChunkVertexCapacity );
33
- fCurrChunkMinVertexAllocCount = minVertexAllocCount;
29
+ void GrStrokePatchBuilder::allocPatchChunkAtLeast (int minPatchAllocCount) {
30
+ PatchChunk* chunk = &fPatchChunkArray ->push_back ();
31
+ fCurrChunkPatchData = (Patch*)fTarget ->makeVertexSpaceAtLeast (sizeof (Patch), minPatchAllocCount,
32
+ minPatchAllocCount,
33
+ &chunk->fPatchBuffer ,
34
+ &chunk->fBasePatch ,
35
+ &fCurrChunkPatchCapacity );
36
+ fCurrChunkMinPatchAllocCount = minPatchAllocCount;
34
37
}
35
38
36
- SkPoint* GrStrokePatchBuilder::reservePatch () {
37
- constexpr static int kNumVerticesPerPatch = GrStrokeTessellateShader::kNumVerticesPerPatch ;
38
- if (fVertexChunkArray ->back ().fVertexCount + kNumVerticesPerPatch > fCurrChunkVertexCapacity ) {
39
+ Patch* GrStrokePatchBuilder::reservePatch () {
40
+ if (fPatchChunkArray ->back ().fPatchCount >= fCurrChunkPatchCapacity ) {
39
41
// The current chunk is full. Time to allocate a new one. (And no need to put back vertices;
40
42
// the buffer is full.)
41
- this ->allocVertexChunk ( fCurrChunkMinVertexAllocCount * 2 );
43
+ this ->allocPatchChunkAtLeast ( fCurrChunkMinPatchAllocCount * 2 );
42
44
}
43
- if (!fCurrChunkVertexData ) {
45
+ if (!fCurrChunkPatchData ) {
44
46
SkDebugf (" WARNING: Failed to allocate vertex buffer for tessellated stroke." );
45
47
return nullptr ;
46
48
}
47
- SkASSERT (fVertexChunkArray ->back ().fVertexCount + kNumVerticesPerPatch <=
48
- fCurrChunkVertexCapacity );
49
- SkPoint* patch = fCurrChunkVertexData + fVertexChunkArray ->back ().fVertexCount ;
50
- fVertexChunkArray ->back ().fVertexCount += kNumVerticesPerPatch ;
49
+ SkASSERT (fPatchChunkArray ->back ().fPatchCount <= fCurrChunkPatchCapacity );
50
+ Patch* patch = fCurrChunkPatchData + fPatchChunkArray ->back ().fPatchCount ;
51
+ ++fPatchChunkArray ->back ().fPatchCount ;
51
52
return patch;
52
53
}
53
54
54
- void GrStrokePatchBuilder::writeCubicSegment (float prevJoinType, const SkPoint pts[4 ]) {
55
+ void GrStrokePatchBuilder::writeCubicSegment (float prevJoinType, const SkPoint pts[4 ],
56
+ float cubicType) {
55
57
SkPoint c1 = (pts[1 ] == pts[0 ]) ? pts[2 ] : pts[1 ];
56
58
SkPoint c2 = (pts[2 ] == pts[3 ]) ? pts[1 ] : pts[2 ];
57
59
@@ -62,9 +64,11 @@ void GrStrokePatchBuilder::writeCubicSegment(float prevJoinType, const SkPoint p
62
64
fHasPreviousSegment = true ;
63
65
}
64
66
65
- if (SkPoint* patch = this ->reservePatch ()) {
66
- memcpy (patch, pts, sizeof (SkPoint) * 4 );
67
- patch[4 ].set (kStandardCubicType , fCurrStrokeRadius );
67
+ SkASSERT (cubicType == Patch::kStandardCubicType || cubicType == Patch::kFlatLineType );
68
+ if (Patch* patch = this ->reservePatch ()) {
69
+ memcpy (patch->fPts .data (), pts, sizeof (patch->fPts ));
70
+ patch->fPatchType = cubicType;
71
+ patch->fStrokeRadius = fCurrStrokeRadius ;
68
72
}
69
73
70
74
fLastControlPoint = c2;
@@ -73,12 +77,12 @@ void GrStrokePatchBuilder::writeCubicSegment(float prevJoinType, const SkPoint p
73
77
74
78
void GrStrokePatchBuilder::writeJoin (float joinType, const SkPoint& prevControlPoint,
75
79
const SkPoint& anchorPoint, const SkPoint& nextControlPoint) {
76
- if (SkPoint* joinPatch = this -> reservePatch ()) {
77
- joinPatch[ 0 ] = prevControlPoint ;
78
- joinPatch[ 1 ] = anchorPoint;
79
- joinPatch[ 2 ] = anchorPoint;
80
- joinPatch[ 3 ] = nextControlPoint ;
81
- joinPatch[ 4 ]. set (joinType, fCurrStrokeRadius ) ;
80
+ SkASSERT (joinType == Patch:: kRoundJoinType || joinType == Patch:: kMiterJoinType ||
81
+ joinType == Patch:: kRoundJoinType || joinType == kDoubleSidedRoundJoinType ) ;
82
+ if (Patch* joinPatch = this -> reservePatch ()) {
83
+ joinPatch-> fPts = {{prevControlPoint, anchorPoint, anchorPoint, nextControlPoint}} ;
84
+ joinPatch-> fPatchType = joinType ;
85
+ joinPatch-> fStrokeRadius = fCurrStrokeRadius ;
82
86
}
83
87
}
84
88
@@ -89,12 +93,10 @@ void GrStrokePatchBuilder::writeSquareCap(const SkPoint& endPoint, const SkPoint
89
93
// Add a join to guarantee we get water tight seaming. Make the join type negative so it's
90
94
// double sided.
91
95
this ->writeJoin (-fCurrStrokeJoinType , controlPoint, endPoint, capPoint);
92
- if (SkPoint* capPatch = this ->reservePatch ()) {
93
- capPatch[0 ] = endPoint;
94
- capPatch[1 ] = endPoint;
95
- capPatch[2 ] = capPoint;
96
- capPatch[3 ] = capPoint;
97
- capPatch[4 ].set (kStandardCubicType , fCurrStrokeRadius );
96
+ if (Patch* capPatch = this ->reservePatch ()) {
97
+ capPatch->fPts = {{endPoint, endPoint, capPoint, capPoint}};
98
+ capPatch->fPatchType = Patch::kFlatLineType ;
99
+ capPatch->fStrokeRadius = fCurrStrokeRadius ;
98
100
}
99
101
}
100
102
@@ -113,10 +115,10 @@ void GrStrokePatchBuilder::writeCaps(SkPaint::Cap capType) {
113
115
break ;
114
116
case SkPaint::kRound_Cap :
115
117
// A round cap is the same thing as a 180-degree round join.
116
- this ->writeJoin (GrStrokeTessellateShader ::kRoundJoinType , fCurrContourFirstControlPoint ,
118
+ this ->writeJoin (Patch ::kRoundJoinType , fCurrContourFirstControlPoint ,
117
119
fCurrContourStartPoint , fCurrContourFirstControlPoint );
118
- this ->writeJoin (GrStrokeTessellateShader ::kRoundJoinType , fLastControlPoint ,
119
- fCurrentPoint , fLastControlPoint );
120
+ this ->writeJoin (Patch ::kRoundJoinType , fLastControlPoint , fCurrentPoint ,
121
+ fLastControlPoint );
120
122
break ;
121
123
case SkPaint::kSquare_Cap :
122
124
this ->writeSquareCap (fCurrContourStartPoint , fCurrContourFirstControlPoint );
@@ -128,11 +130,11 @@ void GrStrokePatchBuilder::writeCaps(SkPaint::Cap capType) {
128
130
static float join_type_from_join (SkPaint::Join join) {
129
131
switch (join) {
130
132
case SkPaint::kBevel_Join :
131
- return GrStrokeTessellateShader::kBevelJoinType ;
133
+ return GrStrokeTessellateShader::Patch:: kBevelJoinType ;
132
134
case SkPaint::kMiter_Join :
133
- return GrStrokeTessellateShader::kMiterJoinType ;
135
+ return GrStrokeTessellateShader::Patch:: kMiterJoinType ;
134
136
case SkPaint::kRound_Join :
135
- return GrStrokeTessellateShader::kRoundJoinType ;
137
+ return GrStrokeTessellateShader::Patch:: kRoundJoinType ;
136
138
}
137
139
SkUNREACHABLE;
138
140
}
@@ -210,7 +212,7 @@ void GrStrokePatchBuilder::lineTo(float prevJoinType, const SkPoint& p0, const S
210
212
}
211
213
212
214
SkPoint cubic[4 ] = {p0, p0, p1, p1};
213
- this ->writeCubicSegment (prevJoinType, cubic);
215
+ this ->writeCubicSegment (prevJoinType, cubic, Patch:: kFlatLineType );
214
216
}
215
217
216
218
void GrStrokePatchBuilder::quadraticTo (float prevJoinType, const SkPoint p[3 ], int maxDepth) {
@@ -257,44 +259,11 @@ void GrStrokePatchBuilder::quadraticTo(float prevJoinType, const SkPoint p[3], i
257
259
}
258
260
259
261
SkPoint cubic[4 ] = {p[0 ], lerp (p[0 ], p[1 ], 2 /3 .f ), lerp (p[1 ], p[2 ], 1 /3 .f ), p[2 ]};
260
- this ->writeCubicSegment (prevJoinType, cubic);
261
- }
262
-
263
- void GrStrokePatchBuilder::cubicTo (float prevJoinType, const SkPoint inputPts[4 ]) {
264
- const SkPoint* p = inputPts;
265
- int numCubics = 1 ;
266
- SkPoint chopped[10 ];
267
- double tt[2 ], ss[2 ];
268
- if (SkClassifyCubic (p, tt, ss) == SkCubicType::kSerpentine ) {
269
- // TEMPORARY: Don't allow cubics to have inflection points.
270
- // TODO: This will soon be moved into the GPU tessellation pipeline and handled more
271
- // elegantly.
272
- float t[2 ] = {(float )(tt[0 ]/ss[0 ]), (float )(tt[1 ]/ss[1 ])};
273
- const float * begin = (t[0 ] > 0 && t[0 ] < 1 ) ? t : t+1 ;
274
- const float * end = (t[1 ] > 0 && t[1 ] > t[0 ] && t[1 ] < 1 ) ? t+2 : t+1 ;
275
- numCubics = (end - begin) + 1 ;
276
- if (numCubics > 1 ) {
277
- SkChopCubicAt (p, chopped, begin, end - begin);
278
- p = chopped;
279
- }
280
- } else if (SkMeasureNonInflectCubicRotation (p) > SK_ScalarPI*15 /16 ) {
281
- // TEMPORARY: Don't allow cubics to turn more than 180 degrees. We chop them when they get
282
- // close, just to be sure.
283
- // TODO: This will soon be moved into the GPU tessellation pipeline and handled more
284
- // elegantly.
285
- SkChopCubicAtMidTangent (p, chopped);
286
- p = chopped;
287
- numCubics = 2 ;
288
- }
289
- for (int i = 0 ; i < numCubics; ++i) {
290
- this ->nonInflectCubicTo (prevJoinType, p + i*3 );
291
- // Use kDoubleSidedRoundJoinType in case we happened to chop at an exact cusp or turnaround
292
- // point of a flat cubic, in which case we would lose 180 degrees of rotation.
293
- prevJoinType = kDoubleSidedRoundJoinType ;
294
- }
262
+ this ->writeCubicSegment (prevJoinType, cubic, Patch::kStandardCubicType );
295
263
}
296
264
297
- void GrStrokePatchBuilder::nonInflectCubicTo (float prevJoinType, const SkPoint p[4 ], int maxDepth) {
265
+ void GrStrokePatchBuilder::cubicTo (float prevJoinType, const SkPoint p[4 ], int maxDepth,
266
+ bool mightInflect) {
298
267
// The stroker relies on p1 and p2 to find tangents at the endpoints. (We have to treat the
299
268
// endpoint tangents carefully in order to get water tight seams with the join segments.) If p0
300
269
// and p1 are both colocated on an endpoint then we need to draw this cubic as a line instead.
@@ -303,46 +272,62 @@ void GrStrokePatchBuilder::nonInflectCubicTo(float prevJoinType, const SkPoint p
303
272
return ;
304
273
}
305
274
306
- // Ensure our hardware supports enough tessellation segments to render the curve. The first
307
- // branch assumes a worst-case rotation of 360 degrees and checks if even then we have enough.
308
- // In practice it is rare to take even the first branch.
309
- //
310
- // NOTE: We could technically assume a worst-case rotation of 180 because cubicTo() chops at
311
- // midtangents and inflections. However, this is only temporary so we leave it at 360 where it
312
- // will arrive at in the future.
275
+ // Early-out if by conservative estimate we can ensure our hardware supports enough tessellation
276
+ // segments to render the curve. In practice we almost always take this branch.
313
277
float numParametricSegments = GrWangsFormula::cubic (fLinearizationIntolerance , p);
314
- if (numParametricSegments > fMaxParametricSegments360 && maxDepth != 0 ) {
315
- // We still might have enough tessellation segments to render the curve. Check again with
316
- // the actual rotation.
317
- float numRadialSegments = SkMeasureNonInflectCubicRotation (p) * fNumRadialSegmentsPerRad ;
318
- numRadialSegments = std::max (std::ceil (numRadialSegments), 1 .f );
319
- numParametricSegments = std::max (std::ceil (numParametricSegments), 1 .f );
320
- if (numParametricSegments + numRadialSegments - 1 > fMaxTessellationSegments ) {
321
- // The hardware doesn't support enough segments for this curve. Chop and recurse.
322
- if (maxDepth < 0 ) {
323
- // Decide on an extremely conservative upper bound for when to quit chopping. This
324
- // is solely to protect us from infinite recursion in instances where FP error
325
- // prevents us from chopping at the correct midtangent.
326
- maxDepth = sk_float_nextlog2 (numParametricSegments) +
327
- sk_float_nextlog2 (numRadialSegments) + 1 ;
328
- SkASSERT (maxDepth >= 1 );
329
- }
330
- SkPoint chopped[7 ];
331
- if (numParametricSegments >= numRadialSegments) {
332
- SkChopCubicAtHalf (p, chopped);
333
- } else {
334
- SkChopCubicAtMidTangent (p, chopped);
278
+ if (numParametricSegments <= fMaxParametricSegments360 || maxDepth == 0 ) {
279
+ this ->writeCubicSegment (prevJoinType, p, Patch::kStandardCubicType );
280
+ return ;
281
+ }
282
+
283
+ // Ensure the curve does not inflect before we attempt to measure its rotation.
284
+ SkPoint chopped[10 ];
285
+ if (mightInflect) {
286
+ float inflectT[2 ];
287
+ if (int n = SkFindCubicInflections (p, inflectT)) {
288
+ SkChopCubicAt (p, chopped, inflectT, n);
289
+ for (int i = 0 ; i <= n; ++i) {
290
+ this ->cubicTo (prevJoinType, chopped + i*3 , maxDepth, false );
291
+ // Switch to kDoubleSidedRoundJoinType in case we happened to chop at an exact cusp
292
+ // or turnaround point of a flat cubic, in which case we would lose 180 degrees of
293
+ // rotation.
294
+ prevJoinType = kDoubleSidedRoundJoinType ;
335
295
}
336
- this ->nonInflectCubicTo (prevJoinType, chopped, maxDepth - 1 );
337
- // Use kDoubleSidedRoundJoinType in case we happened to chop at an exact cusp or
338
- // turnaround point of a flat cubic, in which case we would lose 180 degrees of
339
- // rotation.
340
- this ->nonInflectCubicTo (kDoubleSidedRoundJoinType , chopped + 3 , maxDepth - 1 );
341
296
return ;
342
297
}
343
298
}
344
299
345
- this ->writeCubicSegment (prevJoinType, p);
300
+ // We still might have enough tessellation segments to render the curve. Check again with
301
+ // its actual rotation.
302
+ float numRadialSegments = SkMeasureNonInflectCubicRotation (p) * fNumRadialSegmentsPerRad ;
303
+ numRadialSegments = std::max (std::ceil (numRadialSegments), 1 .f );
304
+ numParametricSegments = std::max (std::ceil (numParametricSegments), 1 .f );
305
+ if (numParametricSegments + numRadialSegments - 1 <= fMaxTessellationSegments ) {
306
+ this ->writeCubicSegment (prevJoinType, p, Patch::kStandardCubicType );
307
+ return ;
308
+ }
309
+
310
+ // The hardware doesn't support enough segments to tessellate this curve. Chop and recurse.
311
+ if (numParametricSegments >= numRadialSegments) {
312
+ SkChopCubicAtHalf (p, chopped);
313
+ } else {
314
+ SkChopCubicAtMidTangent (p, chopped);
315
+ }
316
+
317
+ if (maxDepth < 0 ) {
318
+ // Decide on an extremely conservative upper bound for when to quit chopping. This
319
+ // is solely to protect us from infinite recursion in instances where FP error
320
+ // prevents us from chopping at the correct midtangent.
321
+ maxDepth = sk_float_nextlog2 (numParametricSegments) +
322
+ sk_float_nextlog2 (numRadialSegments) + 1 ;
323
+ SkASSERT (maxDepth >= 1 );
324
+ }
325
+
326
+ this ->cubicTo (prevJoinType, chopped, maxDepth - 1 , false );
327
+ // Use kDoubleSidedRoundJoinType in case we happened to chop at an exact cusp or
328
+ // turnaround point of a flat cubic, in which case we would lose 180 degrees of
329
+ // rotation.
330
+ this ->cubicTo (kDoubleSidedRoundJoinType , chopped + 3 , maxDepth - 1 , false );
346
331
}
347
332
348
333
void GrStrokePatchBuilder::close (SkPaint::Cap capType) {
0 commit comments