blob: 6f9b2240d7a4426e749d65d6dda437d0e910e814 [file] [log] [blame]
Chris Dalton9ca27842018-01-18 12:24:50 -07001/*
2 * Copyright 2017 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 "GrCCPathParser.h"
9
10#include "GrCaps.h"
11#include "GrGpuCommandBuffer.h"
12#include "GrOnFlushResourceProvider.h"
13#include "GrOpFlushState.h"
14#include "SkMathPriv.h"
15#include "SkPath.h"
16#include "SkPathPriv.h"
17#include "SkPoint.h"
18#include "ccpr/GrCCGeometry.h"
Chris Dalton84403d72018-02-13 21:46:17 -050019#include <stdlib.h>
Chris Dalton9ca27842018-01-18 12:24:50 -070020
Chris Dalton84403d72018-02-13 21:46:17 -050021using TriPointInstance = GrCCCoverageProcessor::TriPointInstance;
22using QuadPointInstance = GrCCCoverageProcessor::QuadPointInstance;
Chris Dalton9ca27842018-01-18 12:24:50 -070023
Chris Dalton3917b1e2018-05-09 00:40:52 -060024GrCCPathParser::GrCCPathParser(int numPaths, const PathStats& pathStats)
25 // Overallocate by one point to accomodate for overflow with Sk4f. (See parsePath.)
26 : fLocalDevPtsBuffer(pathStats.fMaxPointsPerPath + 1)
27 , fGeometry(pathStats.fNumTotalSkPoints, pathStats.fNumTotalSkVerbs,
28 pathStats.fNumTotalConicWeights)
29 , fPathsInfo(numPaths)
30 , fScissorSubBatches(numPaths)
Chris Dalton9ca27842018-01-18 12:24:50 -070031 , fTotalPrimitiveCounts{PrimitiveTallies(), PrimitiveTallies()} {
32 // Batches decide what to draw by looking where the previous one ended. Define initial batches
33 // that "end" at the beginning of the data. These will not be drawn, but will only be be read by
34 // the first actual batch.
35 fScissorSubBatches.push_back() = {PrimitiveTallies(), SkIRect::MakeEmpty()};
Chris Dalton84403d72018-02-13 21:46:17 -050036 fCoverageCountBatches.push_back() = {PrimitiveTallies(), fScissorSubBatches.count(),
37 PrimitiveTallies()};
Chris Dalton9ca27842018-01-18 12:24:50 -070038}
39
40void GrCCPathParser::parsePath(const SkMatrix& m, const SkPath& path, SkRect* devBounds,
41 SkRect* devBounds45) {
42 const SkPoint* pts = SkPathPriv::PointData(path);
43 int numPts = path.countPoints();
44 SkASSERT(numPts + 1 <= fLocalDevPtsBuffer.count());
45
46 if (!numPts) {
47 devBounds->setEmpty();
48 devBounds45->setEmpty();
49 this->parsePath(path, nullptr);
50 return;
51 }
52
53 // m45 transforms path points into "45 degree" device space. A bounding box in this space gives
54 // the circumscribing octagon's diagonals. We could use SK_ScalarRoot2Over2, but an orthonormal
55 // transform is not necessary as long as the shader uses the correct inverse.
56 SkMatrix m45;
57 m45.setSinCos(1, 1);
58 m45.preConcat(m);
59
60 // X,Y,T are two parallel view matrices that accumulate two bounding boxes as they map points:
61 // device-space bounds and "45 degree" device-space bounds (| 1 -1 | * devCoords).
62 // | 1 1 |
63 Sk4f X = Sk4f(m.getScaleX(), m.getSkewY(), m45.getScaleX(), m45.getSkewY());
64 Sk4f Y = Sk4f(m.getSkewX(), m.getScaleY(), m45.getSkewX(), m45.getScaleY());
65 Sk4f T = Sk4f(m.getTranslateX(), m.getTranslateY(), m45.getTranslateX(), m45.getTranslateY());
66
67 // Map the path's points to device space and accumulate bounding boxes.
68 Sk4f devPt = SkNx_fma(Y, Sk4f(pts[0].y()), T);
69 devPt = SkNx_fma(X, Sk4f(pts[0].x()), devPt);
70 Sk4f topLeft = devPt;
71 Sk4f bottomRight = devPt;
72
73 // Store all 4 values [dev.x, dev.y, dev45.x, dev45.y]. We are only interested in the first two,
74 // and will overwrite [dev45.x, dev45.y] with the next point. This is why the dst buffer must
75 // be at least one larger than the number of points.
76 devPt.store(&fLocalDevPtsBuffer[0]);
77
78 for (int i = 1; i < numPts; ++i) {
79 devPt = SkNx_fma(Y, Sk4f(pts[i].y()), T);
80 devPt = SkNx_fma(X, Sk4f(pts[i].x()), devPt);
81 topLeft = Sk4f::Min(topLeft, devPt);
82 bottomRight = Sk4f::Max(bottomRight, devPt);
83 devPt.store(&fLocalDevPtsBuffer[i]);
84 }
85
86 SkPoint topLeftPts[2], bottomRightPts[2];
87 topLeft.store(topLeftPts);
88 bottomRight.store(bottomRightPts);
89 devBounds->setLTRB(topLeftPts[0].x(), topLeftPts[0].y(), bottomRightPts[0].x(),
90 bottomRightPts[0].y());
91 devBounds45->setLTRB(topLeftPts[1].x(), topLeftPts[1].y(), bottomRightPts[1].x(),
92 bottomRightPts[1].y());
93
94 this->parsePath(path, fLocalDevPtsBuffer.get());
95}
96
97void GrCCPathParser::parseDeviceSpacePath(const SkPath& deviceSpacePath) {
98 this->parsePath(deviceSpacePath, SkPathPriv::PointData(deviceSpacePath));
99}
100
101void GrCCPathParser::parsePath(const SkPath& path, const SkPoint* deviceSpacePts) {
102 SkASSERT(!fInstanceBuffer); // Can't call after finalize().
103 SkASSERT(!fParsingPath); // Call saveParsedPath() or discardParsedPath() for the last one first.
104 SkDEBUGCODE(fParsingPath = true);
105 SkASSERT(path.isEmpty() || deviceSpacePts);
106
107 fCurrPathPointsIdx = fGeometry.points().count();
108 fCurrPathVerbsIdx = fGeometry.verbs().count();
109 fCurrPathPrimitiveCounts = PrimitiveTallies();
110
111 fGeometry.beginPath();
112
113 if (path.isEmpty()) {
114 return;
115 }
116
Chris Dalton9f2dab02018-04-18 14:07:03 -0600117 const float* conicWeights = SkPathPriv::ConicWeightData(path);
Chris Dalton9ca27842018-01-18 12:24:50 -0700118 int ptsIdx = 0;
Chris Dalton9f2dab02018-04-18 14:07:03 -0600119 int conicWeightsIdx = 0;
Chris Dalton9ca27842018-01-18 12:24:50 -0700120 bool insideContour = false;
121
122 for (SkPath::Verb verb : SkPathPriv::Verbs(path)) {
123 switch (verb) {
124 case SkPath::kMove_Verb:
125 this->endContourIfNeeded(insideContour);
126 fGeometry.beginContour(deviceSpacePts[ptsIdx]);
127 ++ptsIdx;
128 insideContour = true;
129 continue;
130 case SkPath::kClose_Verb:
131 this->endContourIfNeeded(insideContour);
132 insideContour = false;
133 continue;
134 case SkPath::kLine_Verb:
Chris Dalton6f5e77a2018-04-23 21:14:42 -0600135 fGeometry.lineTo(&deviceSpacePts[ptsIdx - 1]);
Chris Dalton9ca27842018-01-18 12:24:50 -0700136 ++ptsIdx;
137 continue;
138 case SkPath::kQuad_Verb:
Chris Dalton7ca3b7b2018-04-10 00:21:19 -0600139 fGeometry.quadraticTo(&deviceSpacePts[ptsIdx - 1]);
Chris Dalton9ca27842018-01-18 12:24:50 -0700140 ptsIdx += 2;
141 continue;
142 case SkPath::kCubic_Verb:
Chris Dalton7ca3b7b2018-04-10 00:21:19 -0600143 fGeometry.cubicTo(&deviceSpacePts[ptsIdx - 1]);
Chris Dalton9ca27842018-01-18 12:24:50 -0700144 ptsIdx += 3;
145 continue;
146 case SkPath::kConic_Verb:
Chris Dalton9f2dab02018-04-18 14:07:03 -0600147 fGeometry.conicTo(&deviceSpacePts[ptsIdx - 1], conicWeights[conicWeightsIdx]);
148 ptsIdx += 2;
149 ++conicWeightsIdx;
150 continue;
Chris Dalton9ca27842018-01-18 12:24:50 -0700151 default:
152 SK_ABORT("Unexpected path verb.");
153 }
154 }
Chris Dalton9f2dab02018-04-18 14:07:03 -0600155 SkASSERT(ptsIdx == path.countPoints());
156 SkASSERT(conicWeightsIdx == SkPathPriv::ConicWeightCnt(path));
Chris Dalton9ca27842018-01-18 12:24:50 -0700157
158 this->endContourIfNeeded(insideContour);
159}
160
161void GrCCPathParser::endContourIfNeeded(bool insideContour) {
162 if (insideContour) {
163 fCurrPathPrimitiveCounts += fGeometry.endContour();
164 }
165}
166
167void GrCCPathParser::saveParsedPath(ScissorMode scissorMode, const SkIRect& clippedDevIBounds,
Chris Dalton9414c962018-06-14 10:14:50 -0600168 const SkIVector& devToAtlasOffset) {
Chris Dalton9ca27842018-01-18 12:24:50 -0700169 SkASSERT(fParsingPath);
170
Chris Dalton9414c962018-06-14 10:14:50 -0600171 fPathsInfo.emplace_back(scissorMode, devToAtlasOffset);
Chris Dalton84403d72018-02-13 21:46:17 -0500172
173 // Tessellate fans from very large and/or simple paths, in order to reduce overdraw.
174 int numVerbs = fGeometry.verbs().count() - fCurrPathVerbsIdx - 1;
175 int64_t tessellationWork = (int64_t)numVerbs * (32 - SkCLZ(numVerbs)); // N log N.
176 int64_t fanningWork = (int64_t)clippedDevIBounds.height() * clippedDevIBounds.width();
177 if (tessellationWork * (50*50) + (100*100) < fanningWork) { // Don't tessellate under 100x100.
178 fCurrPathPrimitiveCounts.fTriangles =
Chris Dalton703b4762018-04-06 16:11:48 -0600179 fCurrPathPrimitiveCounts.fWeightedTriangles = 0;
Chris Dalton84403d72018-02-13 21:46:17 -0500180
181 const SkTArray<GrCCGeometry::Verb, true>& verbs = fGeometry.verbs();
182 const SkTArray<SkPoint, true>& pts = fGeometry.points();
183 int ptsIdx = fCurrPathPointsIdx;
184
Chris Dalton45e46602018-02-15 12:27:29 -0700185 // Build an SkPath of the Redbook fan. We use "winding" fill type right now because we are
186 // producing a coverage count, and must fill in every region that has non-zero wind. The
187 // path processor will convert coverage count to the appropriate fill type later.
Chris Dalton84403d72018-02-13 21:46:17 -0500188 SkPath fan;
Chris Dalton45e46602018-02-15 12:27:29 -0700189 fan.setFillType(SkPath::kWinding_FillType);
Chris Dalton84403d72018-02-13 21:46:17 -0500190 SkASSERT(GrCCGeometry::Verb::kBeginPath == verbs[fCurrPathVerbsIdx]);
191 for (int i = fCurrPathVerbsIdx + 1; i < fGeometry.verbs().count(); ++i) {
192 switch (verbs[i]) {
193 case GrCCGeometry::Verb::kBeginPath:
194 SK_ABORT("Invalid GrCCGeometry");
195 continue;
196
197 case GrCCGeometry::Verb::kBeginContour:
198 fan.moveTo(pts[ptsIdx++]);
199 continue;
200
201 case GrCCGeometry::Verb::kLineTo:
202 fan.lineTo(pts[ptsIdx++]);
203 continue;
204
205 case GrCCGeometry::Verb::kMonotonicQuadraticTo:
Chris Dalton9f2dab02018-04-18 14:07:03 -0600206 case GrCCGeometry::Verb::kMonotonicConicTo:
Chris Dalton84403d72018-02-13 21:46:17 -0500207 fan.lineTo(pts[ptsIdx + 1]);
208 ptsIdx += 2;
209 continue;
210
211 case GrCCGeometry::Verb::kMonotonicCubicTo:
212 fan.lineTo(pts[ptsIdx + 2]);
213 ptsIdx += 3;
214 continue;
215
216 case GrCCGeometry::Verb::kEndClosedContour:
217 case GrCCGeometry::Verb::kEndOpenContour:
218 fan.close();
219 continue;
220 }
221 }
222 GrTessellator::WindingVertex* vertices = nullptr;
223 int count = GrTessellator::PathToVertices(fan, std::numeric_limits<float>::infinity(),
224 SkRect::Make(clippedDevIBounds), &vertices);
225 SkASSERT(0 == count % 3);
226 for (int i = 0; i < count; i += 3) {
Chris Dalton45e46602018-02-15 12:27:29 -0700227 int tessWinding = vertices[i].fWinding;
228 SkASSERT(tessWinding == vertices[i + 1].fWinding);
229 SkASSERT(tessWinding == vertices[i + 2].fWinding);
230
231 // Ensure this triangle's points actually wind in the same direction as tessWinding.
232 // CCPR shaders use the sign of wind to determine which direction to bloat, so even for
233 // "wound" triangles the winding sign and point ordering need to agree.
234 float ax = vertices[i].fPos.fX - vertices[i + 1].fPos.fX;
235 float ay = vertices[i].fPos.fY - vertices[i + 1].fPos.fY;
236 float bx = vertices[i].fPos.fX - vertices[i + 2].fPos.fX;
237 float by = vertices[i].fPos.fY - vertices[i + 2].fPos.fY;
238 float wind = ax*by - ay*bx;
239 if ((wind > 0) != (-tessWinding > 0)) { // Tessellator has opposite winding sense.
240 std::swap(vertices[i + 1].fPos, vertices[i + 2].fPos);
241 }
242
243 if (1 == abs(tessWinding)) {
Chris Dalton84403d72018-02-13 21:46:17 -0500244 ++fCurrPathPrimitiveCounts.fTriangles;
245 } else {
Chris Dalton703b4762018-04-06 16:11:48 -0600246 ++fCurrPathPrimitiveCounts.fWeightedTriangles;
Chris Dalton84403d72018-02-13 21:46:17 -0500247 }
248 }
249
Chris Daltonad065442018-03-08 22:41:33 -0700250 fPathsInfo.back().adoptFanTessellation(vertices, count);
Chris Dalton84403d72018-02-13 21:46:17 -0500251 }
252
Chris Dalton9ca27842018-01-18 12:24:50 -0700253 fTotalPrimitiveCounts[(int)scissorMode] += fCurrPathPrimitiveCounts;
254
255 if (ScissorMode::kScissored == scissorMode) {
256 fScissorSubBatches.push_back() = {fTotalPrimitiveCounts[(int)ScissorMode::kScissored],
Chris Dalton9414c962018-06-14 10:14:50 -0600257 clippedDevIBounds.makeOffset(devToAtlasOffset.fX,
258 devToAtlasOffset.fY)};
Chris Dalton9ca27842018-01-18 12:24:50 -0700259 }
260
261 SkDEBUGCODE(fParsingPath = false);
262}
263
264void GrCCPathParser::discardParsedPath() {
265 SkASSERT(fParsingPath);
266 fGeometry.resize_back(fCurrPathPointsIdx, fCurrPathVerbsIdx);
267 SkDEBUGCODE(fParsingPath = false);
268}
269
270GrCCPathParser::CoverageCountBatchID GrCCPathParser::closeCurrentBatch() {
271 SkASSERT(!fInstanceBuffer);
272 SkASSERT(!fCoverageCountBatches.empty());
273
Chris Daltona883aca2018-03-08 23:05:30 -0700274 const auto& lastBatch = fCoverageCountBatches.back();
275 int maxMeshes = 1 + fScissorSubBatches.count() - lastBatch.fEndScissorSubBatchIdx;
276 fMaxMeshesPerDraw = SkTMax(fMaxMeshesPerDraw, maxMeshes);
277
278 const auto& lastScissorSubBatch = fScissorSubBatches[lastBatch.fEndScissorSubBatchIdx - 1];
Chris Dalton84403d72018-02-13 21:46:17 -0500279 PrimitiveTallies batchTotalCounts = fTotalPrimitiveCounts[(int)ScissorMode::kNonScissored] -
280 lastBatch.fEndNonScissorIndices;
281 batchTotalCounts += fTotalPrimitiveCounts[(int)ScissorMode::kScissored] -
282 lastScissorSubBatch.fEndPrimitiveIndices;
Chris Dalton9ca27842018-01-18 12:24:50 -0700283
Chris Daltona883aca2018-03-08 23:05:30 -0700284 // This will invalidate lastBatch.
Chris Dalton9ca27842018-01-18 12:24:50 -0700285 fCoverageCountBatches.push_back() = {
286 fTotalPrimitiveCounts[(int)ScissorMode::kNonScissored],
Chris Dalton84403d72018-02-13 21:46:17 -0500287 fScissorSubBatches.count(),
288 batchTotalCounts
Chris Dalton9ca27842018-01-18 12:24:50 -0700289 };
290 return fCoverageCountBatches.count() - 1;
291}
292
293// Emits a contour's triangle fan.
294//
295// Classic Redbook fanning would be the triangles: [0 1 2], [0 2 3], ..., [0 n-2 n-1].
296//
297// This function emits the triangle: [0 n/3 n*2/3], and then recurses on all three sides. The
298// advantage to this approach is that for a convex-ish contour, it generates larger triangles.
299// Classic fanning tends to generate long, skinny triangles, which are expensive to draw since they
300// have a longer perimeter to rasterize and antialias.
301//
302// The indices array indexes the fan's points (think: glDrawElements), and must have at least log3
303// elements past the end for this method to use as scratch space.
304//
305// Returns the next triangle instance after the final one emitted.
Chris Dalton84403d72018-02-13 21:46:17 -0500306static TriPointInstance* emit_recursive_fan(const SkTArray<SkPoint, true>& pts,
Chris Dalton9ca27842018-01-18 12:24:50 -0700307 SkTArray<int32_t, true>& indices, int firstIndex,
Chris Dalton9414c962018-06-14 10:14:50 -0600308 int indexCount, const Sk2f& devToAtlasOffset,
Chris Dalton84403d72018-02-13 21:46:17 -0500309 TriPointInstance out[]) {
Chris Dalton9ca27842018-01-18 12:24:50 -0700310 if (indexCount < 3) {
311 return out;
312 }
313
314 int32_t oneThirdCount = indexCount / 3;
315 int32_t twoThirdsCount = (2 * indexCount) / 3;
316 out++->set(pts[indices[firstIndex]], pts[indices[firstIndex + oneThirdCount]],
Chris Dalton9414c962018-06-14 10:14:50 -0600317 pts[indices[firstIndex + twoThirdsCount]], devToAtlasOffset);
Chris Dalton9ca27842018-01-18 12:24:50 -0700318
Chris Dalton9414c962018-06-14 10:14:50 -0600319 out = emit_recursive_fan(pts, indices, firstIndex, oneThirdCount + 1, devToAtlasOffset, out);
Chris Dalton9ca27842018-01-18 12:24:50 -0700320 out = emit_recursive_fan(pts, indices, firstIndex + oneThirdCount,
Chris Dalton9414c962018-06-14 10:14:50 -0600321 twoThirdsCount - oneThirdCount + 1, devToAtlasOffset, out);
Chris Dalton9ca27842018-01-18 12:24:50 -0700322
323 int endIndex = firstIndex + indexCount;
324 int32_t oldValue = indices[endIndex];
325 indices[endIndex] = indices[firstIndex];
326 out = emit_recursive_fan(pts, indices, firstIndex + twoThirdsCount,
Chris Dalton9414c962018-06-14 10:14:50 -0600327 indexCount - twoThirdsCount + 1, devToAtlasOffset, out);
Chris Dalton9ca27842018-01-18 12:24:50 -0700328 indices[endIndex] = oldValue;
329
330 return out;
331}
332
Chris Dalton84403d72018-02-13 21:46:17 -0500333static void emit_tessellated_fan(const GrTessellator::WindingVertex* vertices, int numVertices,
Chris Dalton9414c962018-06-14 10:14:50 -0600334 const Sk2f& devToAtlasOffset,
335 TriPointInstance* triPointInstanceData,
Chris Dalton84403d72018-02-13 21:46:17 -0500336 QuadPointInstance* quadPointInstanceData,
337 GrCCGeometry::PrimitiveTallies* indices) {
338 for (int i = 0; i < numVertices; i += 3) {
339 if (1 == abs(vertices[i].fWinding)) {
340 triPointInstanceData[indices->fTriangles++].set(vertices[i].fPos, vertices[i + 1].fPos,
Chris Dalton9414c962018-06-14 10:14:50 -0600341 vertices[i + 2].fPos, devToAtlasOffset);
Chris Dalton84403d72018-02-13 21:46:17 -0500342 } else {
Chris Dalton703b4762018-04-06 16:11:48 -0600343 quadPointInstanceData[indices->fWeightedTriangles++].setW(
Chris Dalton9414c962018-06-14 10:14:50 -0600344 vertices[i].fPos, vertices[i+1].fPos, vertices[i + 2].fPos, devToAtlasOffset,
Chris Dalton6f5e77a2018-04-23 21:14:42 -0600345 static_cast<float>(abs(vertices[i].fWinding)));
Chris Dalton84403d72018-02-13 21:46:17 -0500346 }
347 }
348}
349
Chris Dalton9ca27842018-01-18 12:24:50 -0700350bool GrCCPathParser::finalize(GrOnFlushResourceProvider* onFlushRP) {
351 SkASSERT(!fParsingPath); // Call saveParsedPath() or discardParsedPath().
352 SkASSERT(fCoverageCountBatches.back().fEndNonScissorIndices == // Call closeCurrentBatch().
353 fTotalPrimitiveCounts[(int)ScissorMode::kNonScissored]);
354 SkASSERT(fCoverageCountBatches.back().fEndScissorSubBatchIdx == fScissorSubBatches.count());
355
356 // Here we build a single instance buffer to share with every internal batch.
357 //
358 // CCPR processs 3 different types of primitives: triangles, quadratics, cubics. Each primitive
359 // type is further divided into instances that require a scissor and those that don't. This
360 // leaves us with 3*2 = 6 independent instance arrays to build for the GPU.
361 //
362 // Rather than place each instance array in its own GPU buffer, we allocate a single
363 // megabuffer and lay them all out side-by-side. We can offset the "baseInstance" parameter in
364 // our draw calls to direct the GPU to the applicable elements within a given array.
365 //
366 // We already know how big to make each of the 6 arrays from fTotalPrimitiveCounts, so layout is
367 // straightforward. Start with triangles and quadratics. They both view the instance buffer as
Chris Dalton84403d72018-02-13 21:46:17 -0500368 // an array of TriPointInstance[], so we can begin at zero and lay them out one after the other.
Chris Dalton9ca27842018-01-18 12:24:50 -0700369 fBaseInstances[0].fTriangles = 0;
370 fBaseInstances[1].fTriangles = fBaseInstances[0].fTriangles +
371 fTotalPrimitiveCounts[0].fTriangles;
372 fBaseInstances[0].fQuadratics = fBaseInstances[1].fTriangles +
373 fTotalPrimitiveCounts[1].fTriangles;
374 fBaseInstances[1].fQuadratics = fBaseInstances[0].fQuadratics +
375 fTotalPrimitiveCounts[0].fQuadratics;
376 int triEndIdx = fBaseInstances[1].fQuadratics + fTotalPrimitiveCounts[1].fQuadratics;
377
Chris Dalton84403d72018-02-13 21:46:17 -0500378 // Wound triangles and cubics both view the same instance buffer as an array of
379 // QuadPointInstance[]. So, reinterpreting the instance data as QuadPointInstance[], we start
380 // them on the first index that will not overwrite previous TriPointInstance data.
381 int quadBaseIdx =
382 GR_CT_DIV_ROUND_UP(triEndIdx * sizeof(TriPointInstance), sizeof(QuadPointInstance));
Chris Dalton703b4762018-04-06 16:11:48 -0600383 fBaseInstances[0].fWeightedTriangles = quadBaseIdx;
384 fBaseInstances[1].fWeightedTriangles = fBaseInstances[0].fWeightedTriangles +
385 fTotalPrimitiveCounts[0].fWeightedTriangles;
386 fBaseInstances[0].fCubics = fBaseInstances[1].fWeightedTriangles +
387 fTotalPrimitiveCounts[1].fWeightedTriangles;
Chris Dalton9ca27842018-01-18 12:24:50 -0700388 fBaseInstances[1].fCubics = fBaseInstances[0].fCubics + fTotalPrimitiveCounts[0].fCubics;
Chris Dalton9f2dab02018-04-18 14:07:03 -0600389 fBaseInstances[0].fConics = fBaseInstances[1].fCubics + fTotalPrimitiveCounts[1].fCubics;
390 fBaseInstances[1].fConics = fBaseInstances[0].fConics + fTotalPrimitiveCounts[0].fConics;
391 int quadEndIdx = fBaseInstances[1].fConics + fTotalPrimitiveCounts[1].fConics;
Chris Dalton9ca27842018-01-18 12:24:50 -0700392
393 fInstanceBuffer = onFlushRP->makeBuffer(kVertex_GrBufferType,
Chris Dalton84403d72018-02-13 21:46:17 -0500394 quadEndIdx * sizeof(QuadPointInstance));
Chris Dalton9ca27842018-01-18 12:24:50 -0700395 if (!fInstanceBuffer) {
396 return false;
397 }
398
Chris Dalton84403d72018-02-13 21:46:17 -0500399 TriPointInstance* triPointInstanceData = static_cast<TriPointInstance*>(fInstanceBuffer->map());
400 QuadPointInstance* quadPointInstanceData =
401 reinterpret_cast<QuadPointInstance*>(triPointInstanceData);
402 SkASSERT(quadPointInstanceData);
Chris Dalton9ca27842018-01-18 12:24:50 -0700403
Chris Dalton84403d72018-02-13 21:46:17 -0500404 PathInfo* nextPathInfo = fPathsInfo.begin();
Chris Dalton9414c962018-06-14 10:14:50 -0600405 Sk2f devToAtlasOffset;
Chris Dalton9ca27842018-01-18 12:24:50 -0700406 PrimitiveTallies instanceIndices[2] = {fBaseInstances[0], fBaseInstances[1]};
407 PrimitiveTallies* currIndices = nullptr;
408 SkSTArray<256, int32_t, true> currFan;
Chris Dalton84403d72018-02-13 21:46:17 -0500409 bool currFanIsTessellated = false;
Chris Dalton9ca27842018-01-18 12:24:50 -0700410
411 const SkTArray<SkPoint, true>& pts = fGeometry.points();
Chris Dalton84403d72018-02-13 21:46:17 -0500412 int ptsIdx = -1;
Chris Dalton9f2dab02018-04-18 14:07:03 -0600413 int nextConicWeightIdx = 0;
Chris Dalton9ca27842018-01-18 12:24:50 -0700414
415 // Expand the ccpr verbs into GPU instance buffers.
416 for (GrCCGeometry::Verb verb : fGeometry.verbs()) {
417 switch (verb) {
418 case GrCCGeometry::Verb::kBeginPath:
419 SkASSERT(currFan.empty());
Chris Daltonad065442018-03-08 22:41:33 -0700420 currIndices = &instanceIndices[(int)nextPathInfo->scissorMode()];
Chris Dalton9414c962018-06-14 10:14:50 -0600421 devToAtlasOffset = Sk2f(static_cast<float>(nextPathInfo->devToAtlasOffset().fX),
422 static_cast<float>(nextPathInfo->devToAtlasOffset().fY));
Chris Daltonad065442018-03-08 22:41:33 -0700423 currFanIsTessellated = nextPathInfo->hasFanTessellation();
Chris Dalton84403d72018-02-13 21:46:17 -0500424 if (currFanIsTessellated) {
Chris Daltonad065442018-03-08 22:41:33 -0700425 emit_tessellated_fan(nextPathInfo->fanTessellation(),
Chris Dalton9414c962018-06-14 10:14:50 -0600426 nextPathInfo->fanTessellationCount(), devToAtlasOffset,
Chris Dalton84403d72018-02-13 21:46:17 -0500427 triPointInstanceData, quadPointInstanceData, currIndices);
428 }
429 ++nextPathInfo;
Chris Dalton9ca27842018-01-18 12:24:50 -0700430 continue;
431
432 case GrCCGeometry::Verb::kBeginContour:
433 SkASSERT(currFan.empty());
Chris Dalton84403d72018-02-13 21:46:17 -0500434 ++ptsIdx;
435 if (!currFanIsTessellated) {
436 currFan.push_back(ptsIdx);
437 }
Chris Dalton9ca27842018-01-18 12:24:50 -0700438 continue;
439
440 case GrCCGeometry::Verb::kLineTo:
Chris Dalton84403d72018-02-13 21:46:17 -0500441 ++ptsIdx;
442 if (!currFanIsTessellated) {
443 SkASSERT(!currFan.empty());
444 currFan.push_back(ptsIdx);
445 }
Chris Dalton9ca27842018-01-18 12:24:50 -0700446 continue;
447
448 case GrCCGeometry::Verb::kMonotonicQuadraticTo:
Chris Dalton9414c962018-06-14 10:14:50 -0600449 triPointInstanceData[currIndices->fQuadratics++].set(&pts[ptsIdx],
450 devToAtlasOffset);
Chris Dalton84403d72018-02-13 21:46:17 -0500451 ptsIdx += 2;
452 if (!currFanIsTessellated) {
453 SkASSERT(!currFan.empty());
454 currFan.push_back(ptsIdx);
455 }
Chris Dalton9ca27842018-01-18 12:24:50 -0700456 continue;
457
458 case GrCCGeometry::Verb::kMonotonicCubicTo:
Chris Dalton9414c962018-06-14 10:14:50 -0600459 quadPointInstanceData[currIndices->fCubics++].set(&pts[ptsIdx], devToAtlasOffset[0],
460 devToAtlasOffset[1]);
Chris Dalton84403d72018-02-13 21:46:17 -0500461 ptsIdx += 3;
462 if (!currFanIsTessellated) {
463 SkASSERT(!currFan.empty());
464 currFan.push_back(ptsIdx);
465 }
Chris Dalton9ca27842018-01-18 12:24:50 -0700466 continue;
467
Chris Dalton9f2dab02018-04-18 14:07:03 -0600468 case GrCCGeometry::Verb::kMonotonicConicTo:
469 quadPointInstanceData[currIndices->fConics++].setW(
Chris Dalton9414c962018-06-14 10:14:50 -0600470 &pts[ptsIdx], devToAtlasOffset,
471 fGeometry.getConicWeight(nextConicWeightIdx));
Chris Dalton9f2dab02018-04-18 14:07:03 -0600472 ptsIdx += 2;
473 ++nextConicWeightIdx;
474 if (!currFanIsTessellated) {
475 SkASSERT(!currFan.empty());
476 currFan.push_back(ptsIdx);
477 }
478 continue;
479
Chris Dalton9ca27842018-01-18 12:24:50 -0700480 case GrCCGeometry::Verb::kEndClosedContour: // endPt == startPt.
Chris Dalton84403d72018-02-13 21:46:17 -0500481 if (!currFanIsTessellated) {
482 SkASSERT(!currFan.empty());
483 currFan.pop_back();
484 }
Chris Dalton9ca27842018-01-18 12:24:50 -0700485 // fallthru.
486 case GrCCGeometry::Verb::kEndOpenContour: // endPt != startPt.
Chris Dalton84403d72018-02-13 21:46:17 -0500487 SkASSERT(!currFanIsTessellated || currFan.empty());
488 if (!currFanIsTessellated && currFan.count() >= 3) {
Chris Dalton9ca27842018-01-18 12:24:50 -0700489 int fanSize = currFan.count();
490 // Reserve space for emit_recursive_fan. Technically this can grow to
491 // fanSize + log3(fanSize), but we approximate with log2.
492 currFan.push_back_n(SkNextLog2(fanSize));
Chris Dalton84403d72018-02-13 21:46:17 -0500493 SkDEBUGCODE(TriPointInstance* end =)
Chris Dalton9414c962018-06-14 10:14:50 -0600494 emit_recursive_fan(pts, currFan, 0, fanSize, devToAtlasOffset,
Chris Dalton84403d72018-02-13 21:46:17 -0500495 triPointInstanceData + currIndices->fTriangles);
Chris Dalton9ca27842018-01-18 12:24:50 -0700496 currIndices->fTriangles += fanSize - 2;
Chris Dalton84403d72018-02-13 21:46:17 -0500497 SkASSERT(triPointInstanceData + currIndices->fTriangles == end);
Chris Dalton9ca27842018-01-18 12:24:50 -0700498 }
499 currFan.reset();
500 continue;
501 }
502 }
503
504 fInstanceBuffer->unmap();
505
Chris Dalton84403d72018-02-13 21:46:17 -0500506 SkASSERT(nextPathInfo == fPathsInfo.end());
Chris Dalton9ca27842018-01-18 12:24:50 -0700507 SkASSERT(ptsIdx == pts.count() - 1);
508 SkASSERT(instanceIndices[0].fTriangles == fBaseInstances[1].fTriangles);
509 SkASSERT(instanceIndices[1].fTriangles == fBaseInstances[0].fQuadratics);
510 SkASSERT(instanceIndices[0].fQuadratics == fBaseInstances[1].fQuadratics);
511 SkASSERT(instanceIndices[1].fQuadratics == triEndIdx);
Chris Dalton703b4762018-04-06 16:11:48 -0600512 SkASSERT(instanceIndices[0].fWeightedTriangles == fBaseInstances[1].fWeightedTriangles);
513 SkASSERT(instanceIndices[1].fWeightedTriangles == fBaseInstances[0].fCubics);
Chris Dalton9ca27842018-01-18 12:24:50 -0700514 SkASSERT(instanceIndices[0].fCubics == fBaseInstances[1].fCubics);
Chris Dalton9f2dab02018-04-18 14:07:03 -0600515 SkASSERT(instanceIndices[1].fCubics == fBaseInstances[0].fConics);
516 SkASSERT(instanceIndices[0].fConics == fBaseInstances[1].fConics);
517 SkASSERT(instanceIndices[1].fConics == quadEndIdx);
Chris Dalton9ca27842018-01-18 12:24:50 -0700518
519 fMeshesScratchBuffer.reserve(fMaxMeshesPerDraw);
520 fDynamicStatesScratchBuffer.reserve(fMaxMeshesPerDraw);
521
522 return true;
523}
524
525void GrCCPathParser::drawCoverageCount(GrOpFlushState* flushState, CoverageCountBatchID batchID,
526 const SkIRect& drawBounds) const {
Chris Dalton8dfc70f2018-03-26 19:15:22 -0600527 using PrimitiveType = GrCCCoverageProcessor::PrimitiveType;
Chris Dalton9ca27842018-01-18 12:24:50 -0700528
529 SkASSERT(fInstanceBuffer);
530
Chris Dalton84403d72018-02-13 21:46:17 -0500531 const PrimitiveTallies& batchTotalCounts = fCoverageCountBatches[batchID].fTotalPrimitiveCounts;
532
Chris Dalton9ca27842018-01-18 12:24:50 -0700533 GrPipeline pipeline(flushState->drawOpArgs().fProxy, GrPipeline::ScissorState::kEnabled,
534 SkBlendMode::kPlus);
535
Chris Dalton84403d72018-02-13 21:46:17 -0500536 if (batchTotalCounts.fTriangles) {
Chris Dalton8dfc70f2018-03-26 19:15:22 -0600537 this->drawPrimitives(flushState, pipeline, batchID, PrimitiveType::kTriangles,
Chris Dalton703b4762018-04-06 16:11:48 -0600538 &PrimitiveTallies::fTriangles, drawBounds);
Chris Dalton84403d72018-02-13 21:46:17 -0500539 }
Chris Dalton9ca27842018-01-18 12:24:50 -0700540
Chris Dalton703b4762018-04-06 16:11:48 -0600541 if (batchTotalCounts.fWeightedTriangles) {
542 this->drawPrimitives(flushState, pipeline, batchID, PrimitiveType::kWeightedTriangles,
543 &PrimitiveTallies::fWeightedTriangles, drawBounds);
Chris Dalton84403d72018-02-13 21:46:17 -0500544 }
Chris Dalton9ca27842018-01-18 12:24:50 -0700545
Chris Dalton84403d72018-02-13 21:46:17 -0500546 if (batchTotalCounts.fQuadratics) {
Chris Dalton8dfc70f2018-03-26 19:15:22 -0600547 this->drawPrimitives(flushState, pipeline, batchID, PrimitiveType::kQuadratics,
Chris Dalton703b4762018-04-06 16:11:48 -0600548 &PrimitiveTallies::fQuadratics, drawBounds);
Chris Dalton84403d72018-02-13 21:46:17 -0500549 }
550
551 if (batchTotalCounts.fCubics) {
Chris Dalton8dfc70f2018-03-26 19:15:22 -0600552 this->drawPrimitives(flushState, pipeline, batchID, PrimitiveType::kCubics,
Chris Dalton703b4762018-04-06 16:11:48 -0600553 &PrimitiveTallies::fCubics, drawBounds);
Chris Dalton84403d72018-02-13 21:46:17 -0500554 }
Chris Dalton9f2dab02018-04-18 14:07:03 -0600555
556 if (batchTotalCounts.fConics) {
557 this->drawPrimitives(flushState, pipeline, batchID, PrimitiveType::kConics,
558 &PrimitiveTallies::fConics, drawBounds);
559 }
Chris Dalton9ca27842018-01-18 12:24:50 -0700560}
561
Chris Dalton8dfc70f2018-03-26 19:15:22 -0600562void GrCCPathParser::drawPrimitives(GrOpFlushState* flushState, const GrPipeline& pipeline,
Chris Dalton9ca27842018-01-18 12:24:50 -0700563 CoverageCountBatchID batchID,
Chris Dalton8dfc70f2018-03-26 19:15:22 -0600564 GrCCCoverageProcessor::PrimitiveType primitiveType,
Chris Dalton9ca27842018-01-18 12:24:50 -0700565 int PrimitiveTallies::*instanceType,
566 const SkIRect& drawBounds) const {
567 SkASSERT(pipeline.getScissorState().enabled());
568
Chris Dalton9ca27842018-01-18 12:24:50 -0700569 // Don't call reset(), as that also resets the reserve count.
570 fMeshesScratchBuffer.pop_back_n(fMeshesScratchBuffer.count());
571 fDynamicStatesScratchBuffer.pop_back_n(fDynamicStatesScratchBuffer.count());
572
Chris Dalton703b4762018-04-06 16:11:48 -0600573 GrCCCoverageProcessor proc(flushState->resourceProvider(), primitiveType);
Chris Dalton9ca27842018-01-18 12:24:50 -0700574
575 SkASSERT(batchID > 0);
576 SkASSERT(batchID < fCoverageCountBatches.count());
577 const CoverageCountBatch& previousBatch = fCoverageCountBatches[batchID - 1];
578 const CoverageCountBatch& batch = fCoverageCountBatches[batchID];
Chris Dalton84403d72018-02-13 21:46:17 -0500579 SkDEBUGCODE(int totalInstanceCount = 0);
Chris Dalton9ca27842018-01-18 12:24:50 -0700580
581 if (int instanceCount = batch.fEndNonScissorIndices.*instanceType -
582 previousBatch.fEndNonScissorIndices.*instanceType) {
583 SkASSERT(instanceCount > 0);
584 int baseInstance = fBaseInstances[(int)ScissorMode::kNonScissored].*instanceType +
585 previousBatch.fEndNonScissorIndices.*instanceType;
586 proc.appendMesh(fInstanceBuffer.get(), instanceCount, baseInstance, &fMeshesScratchBuffer);
587 fDynamicStatesScratchBuffer.push_back().fScissorRect.setXYWH(0, 0, drawBounds.width(),
588 drawBounds.height());
Chris Dalton84403d72018-02-13 21:46:17 -0500589 SkDEBUGCODE(totalInstanceCount += instanceCount);
Chris Dalton9ca27842018-01-18 12:24:50 -0700590 }
591
592 SkASSERT(previousBatch.fEndScissorSubBatchIdx > 0);
593 SkASSERT(batch.fEndScissorSubBatchIdx <= fScissorSubBatches.count());
594 int baseScissorInstance = fBaseInstances[(int)ScissorMode::kScissored].*instanceType;
595 for (int i = previousBatch.fEndScissorSubBatchIdx; i < batch.fEndScissorSubBatchIdx; ++i) {
596 const ScissorSubBatch& previousSubBatch = fScissorSubBatches[i - 1];
597 const ScissorSubBatch& scissorSubBatch = fScissorSubBatches[i];
598 int startIndex = previousSubBatch.fEndPrimitiveIndices.*instanceType;
599 int instanceCount = scissorSubBatch.fEndPrimitiveIndices.*instanceType - startIndex;
600 if (!instanceCount) {
601 continue;
602 }
603 SkASSERT(instanceCount > 0);
604 proc.appendMesh(fInstanceBuffer.get(), instanceCount,
605 baseScissorInstance + startIndex, &fMeshesScratchBuffer);
606 fDynamicStatesScratchBuffer.push_back().fScissorRect = scissorSubBatch.fScissor;
Chris Dalton84403d72018-02-13 21:46:17 -0500607 SkDEBUGCODE(totalInstanceCount += instanceCount);
Chris Dalton9ca27842018-01-18 12:24:50 -0700608 }
609
610 SkASSERT(fMeshesScratchBuffer.count() == fDynamicStatesScratchBuffer.count());
611 SkASSERT(fMeshesScratchBuffer.count() <= fMaxMeshesPerDraw);
Chris Dalton84403d72018-02-13 21:46:17 -0500612 SkASSERT(totalInstanceCount == batch.fTotalPrimitiveCounts.*instanceType);
Chris Dalton9ca27842018-01-18 12:24:50 -0700613
614 if (!fMeshesScratchBuffer.empty()) {
Chris Dalton8dfc70f2018-03-26 19:15:22 -0600615 proc.draw(flushState, pipeline, fMeshesScratchBuffer.begin(),
616 fDynamicStatesScratchBuffer.begin(), fMeshesScratchBuffer.count(),
617 SkRect::Make(drawBounds));
Chris Dalton9ca27842018-01-18 12:24:50 -0700618 }
619}