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