1 /*
2 * Copyright 2018 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 "GrCCStroker.h"
9
10 #include "GrGpuCommandBuffer.h"
11 #include "GrOnFlushResourceProvider.h"
12 #include "SkPathPriv.h"
13 #include "SkStrokeRec.h"
14 #include "ccpr/GrCCCoverageProcessor.h"
15 #include "glsl/GrGLSLFragmentShaderBuilder.h"
16 #include "glsl/GrGLSLVertexGeoBuilder.h"
17
18 static constexpr int kMaxNumLinearSegmentsLog2 = GrCCStrokeGeometry::kMaxNumLinearSegmentsLog2;
19 using TriangleInstance = GrCCCoverageProcessor::TriPointInstance;
20 using ConicInstance = GrCCCoverageProcessor::QuadPointInstance;
21
22 namespace {
23
24 struct LinearStrokeInstance {
25 float fEndpoints[4];
26 float fStrokeRadius;
27
28 inline void set(const SkPoint[2], float dx, float dy, float strokeRadius);
29 };
30
set(const SkPoint P[2],float dx,float dy,float strokeRadius)31 inline void LinearStrokeInstance::set(const SkPoint P[2], float dx, float dy, float strokeRadius) {
32 Sk2f X, Y;
33 Sk2f::Load2(P, &X, &Y);
34 Sk2f::Store2(fEndpoints, X + dx, Y + dy);
35 fStrokeRadius = strokeRadius;
36 }
37
38 struct CubicStrokeInstance {
39 float fX[4];
40 float fY[4];
41 float fStrokeRadius;
42 float fNumSegments;
43
44 inline void set(const SkPoint[4], float dx, float dy, float strokeRadius, int numSegments);
45 inline void set(const Sk4f& X, const Sk4f& Y, float dx, float dy, float strokeRadius,
46 int numSegments);
47 };
48
set(const SkPoint P[4],float dx,float dy,float strokeRadius,int numSegments)49 inline void CubicStrokeInstance::set(const SkPoint P[4], float dx, float dy, float strokeRadius,
50 int numSegments) {
51 Sk4f X, Y;
52 Sk4f::Load2(P, &X, &Y);
53 this->set(X, Y, dx, dy, strokeRadius, numSegments);
54 }
55
set(const Sk4f & X,const Sk4f & Y,float dx,float dy,float strokeRadius,int numSegments)56 inline void CubicStrokeInstance::set(const Sk4f& X, const Sk4f& Y, float dx, float dy,
57 float strokeRadius, int numSegments) {
58 (X + dx).store(&fX);
59 (Y + dy).store(&fY);
60 fStrokeRadius = strokeRadius;
61 fNumSegments = static_cast<float>(numSegments);
62 }
63
64 // This class draws stroked lines in post-transform device space (a.k.a. rectangles). Rigid-body
65 // transforms can be achieved by transforming the line ahead of time and adjusting the stroke
66 // width. Skews of the stroke itself are not yet supported.
67 //
68 // Corner coverage is AA-correct, meaning, n^2 attenuation along the diagonals. This is important
69 // for seamless integration with the connecting geometry.
70 class LinearStrokeProcessor : public GrGeometryProcessor {
71 public:
LinearStrokeProcessor()72 LinearStrokeProcessor() : GrGeometryProcessor(kLinearStrokeProcessor_ClassID) {
73 this->setInstanceAttributes(kInstanceAttribs, 2);
74 #ifdef SK_DEBUG
75 using Instance = LinearStrokeInstance;
76 SkASSERT(this->instanceStride() == sizeof(Instance));
77 #endif
78 }
79
80 private:
name() const81 const char* name() const override { return "LinearStrokeProcessor"; }
getGLSLProcessorKey(const GrShaderCaps &,GrProcessorKeyBuilder *) const82 void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {}
83
84 static constexpr Attribute kInstanceAttribs[2] = {
85 {"endpts", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
86 {"stroke_radius", kFloat_GrVertexAttribType, kFloat_GrSLType}
87 };
88
89 class Impl : public GrGLSLGeometryProcessor {
setData(const GrGLSLProgramDataManager &,const GrPrimitiveProcessor &,FPCoordTransformIter &&)90 void setData(const GrGLSLProgramDataManager&, const GrPrimitiveProcessor&,
91 FPCoordTransformIter&&) override {}
92 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override;
93 };
94
createGLSLInstance(const GrShaderCaps &) const95 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
96 return new Impl();
97 }
98 };
99
onEmitCode(EmitArgs & args,GrGPArgs * gpArgs)100 void LinearStrokeProcessor::Impl::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) {
101 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
102 GrGLSLUniformHandler* uniHandler = args.fUniformHandler;
103
104 varyingHandler->emitAttributes(args.fGP.cast<LinearStrokeProcessor>());
105
106 GrGLSLVertexBuilder* v = args.fVertBuilder;
107 v->codeAppend ("float2 tan = normalize(endpts.zw - endpts.xy);");
108 v->codeAppend ("float2 n = float2(tan.y, -tan.x);");
109 v->codeAppend ("float nwidth = abs(n.x) + abs(n.y);");
110
111 // Outset the vertex position for AA butt caps.
112 v->codeAppend ("float2 outset = tan*nwidth/2;");
113 v->codeAppend ("float2 position = (sk_VertexID < 2) "
114 "? endpts.xy - outset : endpts.zw + outset;");
115
116 // Calculate Manhattan distance from both butt caps, where distance=0 on the actual endpoint and
117 // distance=-.5 on the outset edge.
118 GrGLSLVarying edgeDistances(kFloat4_GrSLType);
119 varyingHandler->addVarying("edge_distances", &edgeDistances);
120 v->codeAppendf("%s.xz = float2(-.5, dot(endpts.zw - endpts.xy, tan) / nwidth + .5);",
121 edgeDistances.vsOut());
122 v->codeAppendf("%s.xz = (sk_VertexID < 2) ? %s.xz : %s.zx;",
123 edgeDistances.vsOut(), edgeDistances.vsOut(), edgeDistances.vsOut());
124
125 // Outset the vertex position for stroke radius plus edge AA.
126 v->codeAppend ("outset = n * (stroke_radius + nwidth/2);");
127 v->codeAppend ("position += (0 == (sk_VertexID & 1)) ? +outset : -outset;");
128
129 // Calculate Manhattan distance from both edges, where distance=0 on the actual edge and
130 // distance=-.5 on the outset.
131 v->codeAppendf("%s.yw = float2(-.5, 2*stroke_radius / nwidth + .5);", edgeDistances.vsOut());
132 v->codeAppendf("%s.yw = (0 == (sk_VertexID & 1)) ? %s.yw : %s.wy;",
133 edgeDistances.vsOut(), edgeDistances.vsOut(), edgeDistances.vsOut());
134
135 gpArgs->fPositionVar.set(kFloat2_GrSLType, "position");
136 this->emitTransforms(v, varyingHandler, uniHandler, GrShaderVar("position", kFloat2_GrSLType),
137 SkMatrix::I(), args.fFPCoordTransformHandler);
138
139 // Use the 4 edge distances to calculate coverage in the fragment shader.
140 GrGLSLFPFragmentBuilder* f = args.fFragBuilder;
141 f->codeAppendf("half2 coverages = min(%s.xy, .5) + min(%s.zw, .5);",
142 edgeDistances.fsIn(), edgeDistances.fsIn());
143 f->codeAppendf("%s = half4(coverages.x * coverages.y);", args.fOutputColor);
144
145 // This shader doesn't use the built-in Ganesh coverage.
146 f->codeAppendf("%s = half4(1);", args.fOutputCoverage);
147 }
148
149 constexpr GrPrimitiveProcessor::Attribute LinearStrokeProcessor::kInstanceAttribs[];
150
151 // This class draws stroked cubics in post-transform device space. Rigid-body transforms can be
152 // achieved by transforming the curve ahead of time and adjusting the stroke width. Skews of the
153 // stroke itself are not yet supported. Quadratics can be drawn by converting them to cubics.
154 //
155 // This class works by finding stroke-width line segments orthogonal to the curve at a
156 // pre-determined number of evenly spaced points along the curve (evenly spaced in the parametric
157 // sense). It then connects the segments with a triangle strip. As for common in CCPR, clockwise-
158 // winding triangles from the strip emit positive coverage, counter-clockwise triangles emit
159 // negative, and we use SkBlendMode::kPlus.
160 class CubicStrokeProcessor : public GrGeometryProcessor {
161 public:
CubicStrokeProcessor()162 CubicStrokeProcessor() : GrGeometryProcessor(kCubicStrokeProcessor_ClassID) {
163 this->setInstanceAttributes(kInstanceAttribs, 3);
164 #ifdef SK_DEBUG
165 using Instance = CubicStrokeInstance;
166 SkASSERT(this->instanceStride() == sizeof(Instance));
167 #endif
168 }
169
170 private:
name() const171 const char* name() const override { return "CubicStrokeProcessor"; }
getGLSLProcessorKey(const GrShaderCaps &,GrProcessorKeyBuilder *) const172 void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {}
173
174 static constexpr Attribute kInstanceAttribs[3] = {
175 {"X", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
176 {"Y", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
177 {"stroke_info", kFloat2_GrVertexAttribType, kFloat2_GrSLType}
178 };
179
180 class Impl : public GrGLSLGeometryProcessor {
setData(const GrGLSLProgramDataManager &,const GrPrimitiveProcessor &,FPCoordTransformIter &&)181 void setData(const GrGLSLProgramDataManager&, const GrPrimitiveProcessor&,
182 FPCoordTransformIter&&) override {}
183 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override;
184 };
185
createGLSLInstance(const GrShaderCaps &) const186 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
187 return new Impl();
188 }
189 };
190
onEmitCode(EmitArgs & args,GrGPArgs * gpArgs)191 void CubicStrokeProcessor::Impl::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) {
192 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
193 GrGLSLUniformHandler* uniHandler = args.fUniformHandler;
194
195 varyingHandler->emitAttributes(args.fGP.cast<CubicStrokeProcessor>());
196
197 GrGLSLVertexBuilder* v = args.fVertBuilder;
198 v->codeAppend ("float4x2 P = transpose(float2x4(X, Y));");
199 v->codeAppend ("float stroke_radius = stroke_info[0];");
200 v->codeAppend ("float num_segments = stroke_info[1];");
201
202 // Find the parametric T value at which we will emit our orthogonal line segment. We emit two
203 // line segments at T=0 and double at T=1 as well for AA butt caps.
204 v->codeAppend ("float point_id = float(sk_VertexID/2);");
205 v->codeAppend ("float T = max((point_id - 1) / num_segments, 0);");
206 v->codeAppend ("T = (point_id >= num_segments + 1) ? 1 : T;"); // In case x/x !== 1.
207
208 // Use De Casteljau's algorithm to find the position and tangent for our orthogonal line
209 // segment. De Casteljau's is more numerically stable than evaluating the curve and derivative
210 // directly.
211 v->codeAppend ("float2 ab = mix(P[0], P[1], T);");
212 v->codeAppend ("float2 bc = mix(P[1], P[2], T);");
213 v->codeAppend ("float2 cd = mix(P[2], P[3], T);");
214 v->codeAppend ("float2 abc = mix(ab, bc, T);");
215 v->codeAppend ("float2 bcd = mix(bc, cd, T);");
216 v->codeAppend ("float2 position = mix(abc, bcd, T);");
217 v->codeAppend ("float2 tan = bcd - abc;");
218
219 // Find actual tangents for the corner cases when De Casteljau's yields tan=0. (We shouldn't
220 // encounter other numerically unstable cases where tan ~= 0, because GrCCStrokeGeometry snaps
221 // control points to endpoints in curves where they are almost equal.)
222 v->codeAppend ("if (0 == T && P[0] == P[1]) {");
223 v->codeAppend ( "tan = P[2] - P[0];");
224 v->codeAppend ("}");
225 v->codeAppend ("if (1 == T && P[2] == P[3]) {");
226 v->codeAppend ( "tan = P[3] - P[1];");
227 v->codeAppend ("}");
228 v->codeAppend ("tan = normalize(tan);");
229 v->codeAppend ("float2 n = float2(tan.y, -tan.x);");
230 v->codeAppend ("float nwidth = abs(n.x) + abs(n.y);");
231
232 // Outset the vertex position for stroke radius plus edge AA.
233 v->codeAppend ("float2 outset = n * (stroke_radius + nwidth/2);");
234 v->codeAppend ("position += (0 == (sk_VertexID & 1)) ? -outset : +outset;");
235
236 // Calculate the Manhattan distance from both edges, where distance=0 on the actual edge and
237 // distance=-.5 on the outset.
238 GrGLSLVarying coverages(kFloat3_GrSLType);
239 varyingHandler->addVarying("coverages", &coverages);
240 v->codeAppendf("%s.xy = float2(-.5, 2*stroke_radius / nwidth + .5);", coverages.vsOut());
241 v->codeAppendf("%s.xy = (0 == (sk_VertexID & 1)) ? %s.xy : %s.yx;",
242 coverages.vsOut(), coverages.vsOut(), coverages.vsOut());
243
244 // Adjust the orthogonal line segments on the endpoints so they straddle the actual endpoint
245 // at a Manhattan distance of .5 on either side.
246 v->codeAppend ("if (0 == point_id || num_segments+1 == point_id) {");
247 v->codeAppend ( "position -= tan*nwidth/2;");
248 v->codeAppend ("}");
249 v->codeAppend ("if (1 == point_id || num_segments+2 == point_id) {");
250 v->codeAppend ( "position += tan*nwidth/2;");
251 v->codeAppend ("}");
252
253 // Interpolate coverage for butt cap AA from 0 on the outer segment to 1 on the inner.
254 v->codeAppendf("%s.z = (0 == point_id || num_segments+2 == point_id) ? 0 : 1;",
255 coverages.vsOut());
256
257 gpArgs->fPositionVar.set(kFloat2_GrSLType, "position");
258 this->emitTransforms(v, varyingHandler, uniHandler, GrShaderVar("position", kFloat2_GrSLType),
259 SkMatrix::I(), args.fFPCoordTransformHandler);
260
261 // Use the 2 edge distances and interpolated butt cap AA to calculate fragment coverage.
262 GrGLSLFPFragmentBuilder* f = args.fFragBuilder;
263 f->codeAppendf("half2 edge_coverages = min(%s.xy, .5);", coverages.fsIn());
264 f->codeAppend ("half coverage = edge_coverages.x + edge_coverages.y;");
265 f->codeAppendf("coverage *= %s.z;", coverages.fsIn()); // Butt cap AA.
266
267 // As is common for CCPR, clockwise-winding triangles from the strip emit positive coverage, and
268 // counter-clockwise triangles emit negative.
269 f->codeAppendf("%s = half4(sk_Clockwise ? +coverage : -coverage);", args.fOutputColor);
270
271 // This shader doesn't use the built-in Ganesh coverage.
272 f->codeAppendf("%s = half4(1);", args.fOutputCoverage);
273 }
274
275 constexpr GrPrimitiveProcessor::Attribute CubicStrokeProcessor::kInstanceAttribs[];
276
277 } // anonymous namespace
278
parseDeviceSpaceStroke(const SkPath & path,const SkPoint * deviceSpacePts,const SkStrokeRec & stroke,float strokeDevWidth,GrScissorTest scissorTest,const SkIRect & clippedDevIBounds,const SkIVector & devToAtlasOffset)279 void GrCCStroker::parseDeviceSpaceStroke(const SkPath& path, const SkPoint* deviceSpacePts,
280 const SkStrokeRec& stroke, float strokeDevWidth,
281 GrScissorTest scissorTest,
282 const SkIRect& clippedDevIBounds,
283 const SkIVector& devToAtlasOffset) {
284 SkASSERT(SkStrokeRec::kStroke_Style == stroke.getStyle() ||
285 SkStrokeRec::kHairline_Style == stroke.getStyle());
286 SkASSERT(!fInstanceBuffer);
287 SkASSERT(!path.isEmpty());
288
289 if (!fHasOpenBatch) {
290 fBatches.emplace_back(&fTalliesAllocator, *fInstanceCounts[(int)GrScissorTest::kDisabled],
291 fScissorSubBatches.count());
292 fInstanceCounts[(int)GrScissorTest::kDisabled] = fBatches.back().fNonScissorEndInstances;
293 fHasOpenBatch = true;
294 }
295
296 InstanceTallies* currStrokeEndIndices;
297 if (GrScissorTest::kEnabled == scissorTest) {
298 SkASSERT(fBatches.back().fEndScissorSubBatch == fScissorSubBatches.count());
299 fScissorSubBatches.emplace_back(
300 &fTalliesAllocator, *fInstanceCounts[(int)GrScissorTest::kEnabled],
301 clippedDevIBounds.makeOffset(devToAtlasOffset.x(), devToAtlasOffset.y()));
302 fBatches.back().fEndScissorSubBatch = fScissorSubBatches.count();
303 fInstanceCounts[(int)GrScissorTest::kEnabled] =
304 currStrokeEndIndices = fScissorSubBatches.back().fEndInstances;
305 } else {
306 currStrokeEndIndices = fBatches.back().fNonScissorEndInstances;
307 }
308
309 fGeometry.beginPath(stroke, strokeDevWidth, currStrokeEndIndices);
310
311 fPathInfos.push_back() = {devToAtlasOffset, strokeDevWidth/2, scissorTest};
312
313 int devPtsIdx = 0;
314 SkPath::Verb previousVerb = SkPath::kClose_Verb;
315
316 for (SkPath::Verb verb : SkPathPriv::Verbs(path)) {
317 SkASSERT(SkPath::kDone_Verb != previousVerb);
318 const SkPoint* P = &deviceSpacePts[devPtsIdx - 1];
319 switch (verb) {
320 case SkPath::kMove_Verb:
321 if (devPtsIdx > 0 && SkPath::kClose_Verb != previousVerb) {
322 fGeometry.capContourAndExit();
323 }
324 fGeometry.moveTo(deviceSpacePts[devPtsIdx]);
325 ++devPtsIdx;
326 break;
327 case SkPath::kClose_Verb:
328 SkASSERT(SkPath::kClose_Verb != previousVerb);
329 fGeometry.closeContour();
330 break;
331 case SkPath::kLine_Verb:
332 SkASSERT(SkPath::kClose_Verb != previousVerb);
333 fGeometry.lineTo(P[1]);
334 ++devPtsIdx;
335 break;
336 case SkPath::kQuad_Verb:
337 SkASSERT(SkPath::kClose_Verb != previousVerb);
338 fGeometry.quadraticTo(P);
339 devPtsIdx += 2;
340 break;
341 case SkPath::kCubic_Verb: {
342 SkASSERT(SkPath::kClose_Verb != previousVerb);
343 fGeometry.cubicTo(P);
344 devPtsIdx += 3;
345 break;
346 }
347 case SkPath::kConic_Verb:
348 SkASSERT(SkPath::kClose_Verb != previousVerb);
349 SK_ABORT("Stroked conics not supported.");
350 break;
351 case SkPath::kDone_Verb:
352 break;
353 }
354 previousVerb = verb;
355 }
356
357 if (devPtsIdx > 0 && SkPath::kClose_Verb != previousVerb) {
358 fGeometry.capContourAndExit();
359 }
360 }
361
362 // This class encapsulates the process of expanding ready-to-draw geometry from GrCCStrokeGeometry
363 // directly into GPU instance buffers.
364 class GrCCStroker::InstanceBufferBuilder {
365 public:
InstanceBufferBuilder(GrOnFlushResourceProvider * onFlushRP,GrCCStroker * stroker)366 InstanceBufferBuilder(GrOnFlushResourceProvider* onFlushRP, GrCCStroker* stroker) {
367 memcpy(fNextInstances, stroker->fBaseInstances, sizeof(fNextInstances));
368 #ifdef SK_DEBUG
369 fEndInstances[0] = stroker->fBaseInstances[0] + *stroker->fInstanceCounts[0];
370 fEndInstances[1] = stroker->fBaseInstances[1] + *stroker->fInstanceCounts[1];
371 #endif
372
373 int endConicsIdx = stroker->fBaseInstances[1].fConics +
374 stroker->fInstanceCounts[1]->fConics;
375 fInstanceBuffer = onFlushRP->makeBuffer(kVertex_GrBufferType,
376 endConicsIdx * sizeof(ConicInstance));
377 if (!fInstanceBuffer) {
378 SkDebugf("WARNING: failed to allocate CCPR stroke instance buffer.\n");
379 return;
380 }
381 fInstanceBufferData = fInstanceBuffer->map();
382 }
383
isMapped() const384 bool isMapped() const { return SkToBool(fInstanceBufferData); }
385
updateCurrentInfo(const PathInfo & pathInfo)386 void updateCurrentInfo(const PathInfo& pathInfo) {
387 SkASSERT(this->isMapped());
388 fCurrDX = static_cast<float>(pathInfo.fDevToAtlasOffset.x());
389 fCurrDY = static_cast<float>(pathInfo.fDevToAtlasOffset.y());
390 fCurrStrokeRadius = pathInfo.fStrokeRadius;
391 fCurrNextInstances = &fNextInstances[(int)pathInfo.fScissorTest];
392 SkDEBUGCODE(fCurrEndInstances = &fEndInstances[(int)pathInfo.fScissorTest]);
393 }
394
appendLinearStroke(const SkPoint endpts[2])395 void appendLinearStroke(const SkPoint endpts[2]) {
396 SkASSERT(this->isMapped());
397 this->appendLinearStrokeInstance().set(endpts, fCurrDX, fCurrDY, fCurrStrokeRadius);
398 }
399
appendQuadraticStroke(const SkPoint P[3],int numLinearSegmentsLog2)400 void appendQuadraticStroke(const SkPoint P[3], int numLinearSegmentsLog2) {
401 SkASSERT(this->isMapped());
402 SkASSERT(numLinearSegmentsLog2 > 0);
403
404 Sk4f ptsT[2];
405 Sk2f p0 = Sk2f::Load(P);
406 Sk2f p1 = Sk2f::Load(P+1);
407 Sk2f p2 = Sk2f::Load(P+2);
408
409 // Convert the quadratic to cubic.
410 Sk2f c1 = SkNx_fma(Sk2f(2/3.f), p1 - p0, p0);
411 Sk2f c2 = SkNx_fma(Sk2f(1/3.f), p2 - p1, p1);
412 Sk2f::Store4(ptsT, p0, c1, c2, p2);
413
414 this->appendCubicStrokeInstance(numLinearSegmentsLog2).set(
415 ptsT[0], ptsT[1], fCurrDX, fCurrDY, fCurrStrokeRadius, 1 << numLinearSegmentsLog2);
416 }
417
appendCubicStroke(const SkPoint P[3],int numLinearSegmentsLog2)418 void appendCubicStroke(const SkPoint P[3], int numLinearSegmentsLog2) {
419 SkASSERT(this->isMapped());
420 SkASSERT(numLinearSegmentsLog2 > 0);
421 this->appendCubicStrokeInstance(numLinearSegmentsLog2).set(
422 P, fCurrDX, fCurrDY, fCurrStrokeRadius, 1 << numLinearSegmentsLog2);
423 }
424
appendJoin(Verb joinVerb,const SkPoint & center,const SkVector & leftNorm,const SkVector & rightNorm,float miterCapHeightOverWidth,float conicWeight)425 void appendJoin(Verb joinVerb, const SkPoint& center, const SkVector& leftNorm,
426 const SkVector& rightNorm, float miterCapHeightOverWidth, float conicWeight) {
427 SkASSERT(this->isMapped());
428
429 Sk2f offset = Sk2f::Load(¢er) + Sk2f(fCurrDX, fCurrDY);
430 Sk2f n0 = Sk2f::Load(&leftNorm);
431 Sk2f n1 = Sk2f::Load(&rightNorm);
432
433 // Identify the outer edge.
434 Sk2f cross = n0 * SkNx_shuffle<1,0>(n1);
435 if (cross[0] < cross[1]) {
436 Sk2f tmp = n0;
437 n0 = -n1;
438 n1 = -tmp;
439 }
440
441 if (!GrCCStrokeGeometry::IsInternalJoinVerb(joinVerb)) {
442 // Normal joins are a triangle that connects the outer corners of two adjoining strokes.
443 this->appendTriangleInstance().set(n1 * fCurrStrokeRadius, Sk2f(0, 0),
444 n0 * fCurrStrokeRadius, offset);
445 if (Verb::kBevelJoin == joinVerb) {
446 return;
447 }
448 } else {
449 // Internal joins are coverage-counted, self-intersecting quadrilaterals that tie the
450 // four corners of two adjoining strokes together a like a shoelace. Coverage is
451 // negative on the inside half. We implement this geometry with a pair of triangles.
452 this->appendTriangleInstance().set(-n0 * fCurrStrokeRadius, n0 * fCurrStrokeRadius,
453 n1 * fCurrStrokeRadius, offset);
454 this->appendTriangleInstance().set(-n0 * fCurrStrokeRadius, n1 * fCurrStrokeRadius,
455 -n1 * fCurrStrokeRadius, offset);
456 if (Verb::kInternalBevelJoin == joinVerb) {
457 return;
458 }
459 }
460
461 // For miter and round joins, we place an additional triangle cap on top of the bevel. This
462 // triangle is literal for miters and is conic control points for round joins.
463 SkASSERT(miterCapHeightOverWidth >= 0 || SkScalarIsNaN(miterCapHeightOverWidth));
464 Sk2f base = n1 - n0;
465 Sk2f baseNorm = Sk2f(base[1], -base[0]);
466 Sk2f c = (n0 + n1) * .5f + baseNorm * miterCapHeightOverWidth;
467
468 if (Verb::kMiterJoin == joinVerb) {
469 this->appendTriangleInstance().set(n0 * fCurrStrokeRadius, c * fCurrStrokeRadius,
470 n1 * fCurrStrokeRadius, offset);
471 } else {
472 SkASSERT(Verb::kRoundJoin == joinVerb || Verb::kInternalRoundJoin == joinVerb);
473 this->appendConicInstance().setW(n0 * fCurrStrokeRadius, c * fCurrStrokeRadius,
474 n1 * fCurrStrokeRadius, offset, conicWeight);
475 if (Verb::kInternalRoundJoin == joinVerb) {
476 this->appendConicInstance().setW(-n1 * fCurrStrokeRadius, c * -fCurrStrokeRadius,
477 -n0 * fCurrStrokeRadius, offset, conicWeight);
478 }
479 }
480 }
481
appendCap(Verb capType,const SkPoint & pt,const SkVector & norm)482 void appendCap(Verb capType, const SkPoint& pt, const SkVector& norm) {
483 SkASSERT(this->isMapped());
484
485 Sk2f n = Sk2f::Load(&norm) * fCurrStrokeRadius;
486 Sk2f v = Sk2f(-n[1], n[0]);
487 Sk2f offset = Sk2f::Load(&pt) + Sk2f(fCurrDX, fCurrDY);
488
489 if (Verb::kSquareCap == capType) {
490 SkPoint endPts[2] = {{0, 0}, {v[0], v[1]}};
491 this->appendLinearStrokeInstance().set(endPts, offset[0], offset[1], fCurrStrokeRadius);
492 } else {
493 SkASSERT(Verb::kRoundCap == capType);
494 this->appendTriangleInstance().set(n, v, -n, offset);
495 this->appendConicInstance().setW(n, n + v, v, offset, SK_ScalarRoot2Over2);
496 this->appendConicInstance().setW(v, v - n, -n, offset, SK_ScalarRoot2Over2);
497 }
498 }
499
finish()500 sk_sp<GrBuffer> finish() {
501 SkASSERT(this->isMapped());
502 SkASSERT(!memcmp(fNextInstances, fEndInstances, sizeof(fNextInstances)));
503 fInstanceBuffer->unmap();
504 fInstanceBufferData = nullptr;
505 SkASSERT(!this->isMapped());
506 return std::move(fInstanceBuffer);
507 }
508
509 private:
appendLinearStrokeInstance()510 LinearStrokeInstance& appendLinearStrokeInstance() {
511 int instanceIdx = fCurrNextInstances->fStrokes[0]++;
512 SkASSERT(instanceIdx < fCurrEndInstances->fStrokes[0]);
513
514 return reinterpret_cast<LinearStrokeInstance*>(fInstanceBufferData)[instanceIdx];
515 }
516
appendCubicStrokeInstance(int numLinearSegmentsLog2)517 CubicStrokeInstance& appendCubicStrokeInstance(int numLinearSegmentsLog2) {
518 SkASSERT(numLinearSegmentsLog2 > 0);
519 SkASSERT(numLinearSegmentsLog2 <= kMaxNumLinearSegmentsLog2);
520
521 int instanceIdx = fCurrNextInstances->fStrokes[numLinearSegmentsLog2]++;
522 SkASSERT(instanceIdx < fCurrEndInstances->fStrokes[numLinearSegmentsLog2]);
523
524 return reinterpret_cast<CubicStrokeInstance*>(fInstanceBufferData)[instanceIdx];
525 }
526
appendTriangleInstance()527 TriangleInstance& appendTriangleInstance() {
528 int instanceIdx = fCurrNextInstances->fTriangles++;
529 SkASSERT(instanceIdx < fCurrEndInstances->fTriangles);
530
531 return reinterpret_cast<TriangleInstance*>(fInstanceBufferData)[instanceIdx];
532 }
533
appendConicInstance()534 ConicInstance& appendConicInstance() {
535 int instanceIdx = fCurrNextInstances->fConics++;
536 SkASSERT(instanceIdx < fCurrEndInstances->fConics);
537
538 return reinterpret_cast<ConicInstance*>(fInstanceBufferData)[instanceIdx];
539 }
540
541 float fCurrDX, fCurrDY;
542 float fCurrStrokeRadius;
543 InstanceTallies* fCurrNextInstances;
544 SkDEBUGCODE(const InstanceTallies* fCurrEndInstances);
545
546 sk_sp<GrBuffer> fInstanceBuffer;
547 void* fInstanceBufferData = nullptr;
548 InstanceTallies fNextInstances[2];
549 SkDEBUGCODE(InstanceTallies fEndInstances[2]);
550 };
551
closeCurrentBatch()552 GrCCStroker::BatchID GrCCStroker::closeCurrentBatch() {
553 if (!fHasOpenBatch) {
554 return kEmptyBatchID;
555 }
556 int start = (fBatches.count() < 2) ? 0 : fBatches[fBatches.count() - 2].fEndScissorSubBatch;
557 int end = fBatches.back().fEndScissorSubBatch;
558 fMaxNumScissorSubBatches = SkTMax(fMaxNumScissorSubBatches, end - start);
559 fHasOpenBatch = false;
560 return fBatches.count() - 1;
561 }
562
prepareToDraw(GrOnFlushResourceProvider * onFlushRP)563 bool GrCCStroker::prepareToDraw(GrOnFlushResourceProvider* onFlushRP) {
564 SkASSERT(!fInstanceBuffer);
565 SkASSERT(!fHasOpenBatch); // Call closeCurrentBatch() first.
566
567 // Here we layout a single instance buffer to share with every internal batch.
568 //
569 // Rather than place each instance array in its own GPU buffer, we allocate a single
570 // megabuffer and lay them all out side-by-side. We can offset the "baseInstance" parameter in
571 // our draw calls to direct the GPU to the applicable elements within a given array.
572 fBaseInstances[0].fStrokes[0] = 0;
573 fBaseInstances[1].fStrokes[0] = fInstanceCounts[0]->fStrokes[0];
574 int endLinearStrokesIdx = fBaseInstances[1].fStrokes[0] + fInstanceCounts[1]->fStrokes[0];
575
576 int cubicStrokesIdx = GR_CT_DIV_ROUND_UP(endLinearStrokesIdx * sizeof(LinearStrokeInstance),
577 sizeof(CubicStrokeInstance));
578 for (int i = 1; i <= kMaxNumLinearSegmentsLog2; ++i) {
579 for (int j = 0; j < kNumScissorModes; ++j) {
580 fBaseInstances[j].fStrokes[i] = cubicStrokesIdx;
581 cubicStrokesIdx += fInstanceCounts[j]->fStrokes[i];
582 }
583 }
584
585 int trianglesIdx = GR_CT_DIV_ROUND_UP(cubicStrokesIdx * sizeof(CubicStrokeInstance),
586 sizeof(TriangleInstance));
587 fBaseInstances[0].fTriangles = trianglesIdx;
588 fBaseInstances[1].fTriangles =
589 fBaseInstances[0].fTriangles + fInstanceCounts[0]->fTriangles;
590 int endTrianglesIdx =
591 fBaseInstances[1].fTriangles + fInstanceCounts[1]->fTriangles;
592
593 int conicsIdx = GR_CT_DIV_ROUND_UP(endTrianglesIdx * sizeof(TriangleInstance),
594 sizeof(ConicInstance));
595 fBaseInstances[0].fConics = conicsIdx;
596 fBaseInstances[1].fConics = fBaseInstances[0].fConics + fInstanceCounts[0]->fConics;
597
598 InstanceBufferBuilder builder(onFlushRP, this);
599 if (!builder.isMapped()) {
600 return false; // Buffer allocation failed.
601 }
602
603 // Now parse the GrCCStrokeGeometry and expand it into the instance buffer.
604 int pathIdx = 0;
605 int ptsIdx = 0;
606 int paramsIdx = 0;
607 int normalsIdx = 0;
608
609 const SkTArray<GrCCStrokeGeometry::Parameter, true>& params = fGeometry.params();
610 const SkTArray<SkPoint, true>& pts = fGeometry.points();
611 const SkTArray<SkVector, true>& normals = fGeometry.normals();
612
613 float miterCapHeightOverWidth=0, conicWeight=0;
614
615 for (Verb verb : fGeometry.verbs()) {
616 switch (verb) {
617 case Verb::kBeginPath:
618 builder.updateCurrentInfo(fPathInfos[pathIdx]);
619 ++pathIdx;
620 continue;
621
622 case Verb::kLinearStroke:
623 builder.appendLinearStroke(&pts[ptsIdx]);
624 ++ptsIdx;
625 continue;
626 case Verb::kQuadraticStroke:
627 builder.appendQuadraticStroke(&pts[ptsIdx],
628 params[paramsIdx++].fNumLinearSegmentsLog2);
629 ptsIdx += 2;
630 ++normalsIdx;
631 continue;
632 case Verb::kCubicStroke:
633 builder.appendCubicStroke(&pts[ptsIdx], params[paramsIdx++].fNumLinearSegmentsLog2);
634 ptsIdx += 3;
635 ++normalsIdx;
636 continue;
637
638 case Verb::kRoundJoin:
639 case Verb::kInternalRoundJoin:
640 conicWeight = params[paramsIdx++].fConicWeight;
641 // fallthru
642 case Verb::kMiterJoin:
643 miterCapHeightOverWidth = params[paramsIdx++].fMiterCapHeightOverWidth;
644 // fallthru
645 case Verb::kBevelJoin:
646 case Verb::kInternalBevelJoin:
647 builder.appendJoin(verb, pts[ptsIdx], normals[normalsIdx], normals[normalsIdx + 1],
648 miterCapHeightOverWidth, conicWeight);
649 ++normalsIdx;
650 continue;
651
652 case Verb::kSquareCap:
653 case Verb::kRoundCap:
654 builder.appendCap(verb, pts[ptsIdx], normals[normalsIdx]);
655 continue;
656
657 case Verb::kEndContour:
658 ++ptsIdx;
659 ++normalsIdx;
660 continue;
661 }
662 SK_ABORT("Invalid CCPR stroke element.");
663 }
664
665 fInstanceBuffer = builder.finish();
666 SkASSERT(fPathInfos.count() == pathIdx);
667 SkASSERT(pts.count() == ptsIdx);
668 SkASSERT(normals.count() == normalsIdx);
669
670 fMeshesBuffer.reserve((1 + fMaxNumScissorSubBatches) * kMaxNumLinearSegmentsLog2);
671 fScissorsBuffer.reserve((1 + fMaxNumScissorSubBatches) * kMaxNumLinearSegmentsLog2);
672 return true;
673 }
674
drawStrokes(GrOpFlushState * flushState,BatchID batchID,const SkIRect & drawBounds) const675 void GrCCStroker::drawStrokes(GrOpFlushState* flushState, BatchID batchID,
676 const SkIRect& drawBounds) const {
677 using PrimitiveType = GrCCCoverageProcessor::PrimitiveType;
678 SkASSERT(fInstanceBuffer);
679
680 if (kEmptyBatchID == batchID) {
681 return;
682 }
683 const Batch& batch = fBatches[batchID];
684 int startScissorSubBatch = (!batchID) ? 0 : fBatches[batchID - 1].fEndScissorSubBatch;
685
686 const InstanceTallies* startIndices[2];
687 startIndices[(int)GrScissorTest::kDisabled] = (!batchID)
688 ? &fZeroTallies : fBatches[batchID - 1].fNonScissorEndInstances;
689 startIndices[(int)GrScissorTest::kEnabled] = (!startScissorSubBatch)
690 ? &fZeroTallies : fScissorSubBatches[startScissorSubBatch - 1].fEndInstances;
691
692 GrPipeline pipeline(GrScissorTest::kEnabled, SkBlendMode::kPlus);
693
694 // Draw linear strokes.
695 this->appendStrokeMeshesToBuffers(0, batch, startIndices, startScissorSubBatch, drawBounds);
696 if (!fMeshesBuffer.empty()) {
697 LinearStrokeProcessor linearProc;
698 this->flushBufferedMeshesAsStrokes(linearProc, flushState, pipeline, drawBounds);
699 }
700
701 // Draw cubic strokes. (Quadratics were converted to cubics for GPU processing.)
702 for (int i = 1; i <= kMaxNumLinearSegmentsLog2; ++i) {
703 this->appendStrokeMeshesToBuffers(i, batch, startIndices, startScissorSubBatch, drawBounds);
704 }
705 if (!fMeshesBuffer.empty()) {
706 CubicStrokeProcessor cubicProc;
707 this->flushBufferedMeshesAsStrokes(cubicProc, flushState, pipeline, drawBounds);
708 }
709
710 // Draw triangles.
711 GrCCCoverageProcessor triProc(flushState->resourceProvider(), PrimitiveType::kTriangles);
712 this->drawConnectingGeometry<&InstanceTallies::fTriangles>(
713 flushState, pipeline, triProc, batch, startIndices, startScissorSubBatch, drawBounds);
714
715 // Draw conics.
716 GrCCCoverageProcessor conicProc(flushState->resourceProvider(), PrimitiveType::kConics);
717 this->drawConnectingGeometry<&InstanceTallies::fConics>(
718 flushState, pipeline, conicProc, batch, startIndices, startScissorSubBatch, drawBounds);
719 }
720
appendStrokeMeshesToBuffers(int numSegmentsLog2,const Batch & batch,const InstanceTallies * startIndices[2],int startScissorSubBatch,const SkIRect & drawBounds) const721 void GrCCStroker::appendStrokeMeshesToBuffers(int numSegmentsLog2, const Batch& batch,
722 const InstanceTallies* startIndices[2],
723 int startScissorSubBatch,
724 const SkIRect& drawBounds) const {
725 // Linear strokes draw a quad. Cubic strokes emit a strip with normals at "numSegments"
726 // evenly-spaced points along the curve, plus one more for the final endpoint, plus two more for
727 // AA butt caps. (i.e., 2 vertices * (numSegments + 3).)
728 int numStripVertices = (0 == numSegmentsLog2) ? 4 : ((1 << numSegmentsLog2) + 3) * 2;
729
730 // Append non-scissored meshes.
731 int baseInstance = fBaseInstances[(int)GrScissorTest::kDisabled].fStrokes[numSegmentsLog2];
732 int startIdx = startIndices[(int)GrScissorTest::kDisabled]->fStrokes[numSegmentsLog2];
733 int endIdx = batch.fNonScissorEndInstances->fStrokes[numSegmentsLog2];
734 SkASSERT(endIdx >= startIdx);
735 if (int instanceCount = endIdx - startIdx) {
736 GrMesh& mesh = fMeshesBuffer.emplace_back(GrPrimitiveType::kTriangleStrip);
737 mesh.setInstanced(fInstanceBuffer, instanceCount, baseInstance + startIdx,
738 numStripVertices);
739 fScissorsBuffer.push_back(drawBounds);
740 }
741
742 // Append scissored meshes.
743 baseInstance = fBaseInstances[(int)GrScissorTest::kEnabled].fStrokes[numSegmentsLog2];
744 startIdx = startIndices[(int)GrScissorTest::kEnabled]->fStrokes[numSegmentsLog2];
745 for (int i = startScissorSubBatch; i < batch.fEndScissorSubBatch; ++i) {
746 const ScissorSubBatch& subBatch = fScissorSubBatches[i];
747 endIdx = subBatch.fEndInstances->fStrokes[numSegmentsLog2];
748 SkASSERT(endIdx >= startIdx);
749 if (int instanceCount = endIdx - startIdx) {
750 GrMesh& mesh = fMeshesBuffer.emplace_back(GrPrimitiveType::kTriangleStrip);
751 mesh.setInstanced(fInstanceBuffer, instanceCount, baseInstance + startIdx,
752 numStripVertices);
753 fScissorsBuffer.push_back(subBatch.fScissor);
754 startIdx = endIdx;
755 }
756 }
757 }
758
flushBufferedMeshesAsStrokes(const GrPrimitiveProcessor & processor,GrOpFlushState * flushState,const GrPipeline & pipeline,const SkIRect & drawBounds) const759 void GrCCStroker::flushBufferedMeshesAsStrokes(const GrPrimitiveProcessor& processor,
760 GrOpFlushState* flushState,
761 const GrPipeline& pipeline,
762 const SkIRect& drawBounds) const {
763 SkASSERT(fMeshesBuffer.count() == fScissorsBuffer.count());
764 GrPipeline::DynamicStateArrays dynamicStateArrays;
765 dynamicStateArrays.fScissorRects = fScissorsBuffer.begin();
766 flushState->rtCommandBuffer()->draw(processor, pipeline, nullptr, &dynamicStateArrays,
767 fMeshesBuffer.begin(), fMeshesBuffer.count(),
768 SkRect::Make(drawBounds));
769 // Don't call reset(), as that also resets the reserve count.
770 fMeshesBuffer.pop_back_n(fMeshesBuffer.count());
771 fScissorsBuffer.pop_back_n(fScissorsBuffer.count());
772 }
773
774 template<int GrCCStrokeGeometry::InstanceTallies::* InstanceType>
drawConnectingGeometry(GrOpFlushState * flushState,const GrPipeline & pipeline,const GrCCCoverageProcessor & processor,const Batch & batch,const InstanceTallies * startIndices[2],int startScissorSubBatch,const SkIRect & drawBounds) const775 void GrCCStroker::drawConnectingGeometry(GrOpFlushState* flushState, const GrPipeline& pipeline,
776 const GrCCCoverageProcessor& processor,
777 const Batch& batch, const InstanceTallies* startIndices[2],
778 int startScissorSubBatch,
779 const SkIRect& drawBounds) const {
780 // Append non-scissored meshes.
781 int baseInstance = fBaseInstances[(int)GrScissorTest::kDisabled].*InstanceType;
782 int startIdx = startIndices[(int)GrScissorTest::kDisabled]->*InstanceType;
783 int endIdx = batch.fNonScissorEndInstances->*InstanceType;
784 SkASSERT(endIdx >= startIdx);
785 if (int instanceCount = endIdx - startIdx) {
786 processor.appendMesh(fInstanceBuffer, instanceCount, baseInstance + startIdx,
787 &fMeshesBuffer);
788 fScissorsBuffer.push_back(drawBounds);
789 }
790
791 // Append scissored meshes.
792 baseInstance = fBaseInstances[(int)GrScissorTest::kEnabled].*InstanceType;
793 startIdx = startIndices[(int)GrScissorTest::kEnabled]->*InstanceType;
794 for (int i = startScissorSubBatch; i < batch.fEndScissorSubBatch; ++i) {
795 const ScissorSubBatch& subBatch = fScissorSubBatches[i];
796 endIdx = subBatch.fEndInstances->*InstanceType;
797 SkASSERT(endIdx >= startIdx);
798 if (int instanceCount = endIdx - startIdx) {
799 processor.appendMesh(fInstanceBuffer, instanceCount, baseInstance + startIdx,
800 &fMeshesBuffer);
801 fScissorsBuffer.push_back(subBatch.fScissor);
802 startIdx = endIdx;
803 }
804 }
805
806 // Flush the geometry.
807 if (!fMeshesBuffer.empty()) {
808 SkASSERT(fMeshesBuffer.count() == fScissorsBuffer.count());
809 processor.draw(flushState, pipeline, fScissorsBuffer.begin(), fMeshesBuffer.begin(),
810 fMeshesBuffer.count(), SkRect::Make(drawBounds));
811 // Don't call reset(), as that also resets the reserve count.
812 fMeshesBuffer.pop_back_n(fMeshesBuffer.count());
813 fScissorsBuffer.pop_back_n(fScissorsBuffer.count());
814 }
815 }
816