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(¢er) + 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