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