1 /*
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 "src/gpu/ccpr/GrCCFiller.h"
9
10 #include "include/core/SkPath.h"
11 #include "include/core/SkPoint.h"
12 #include "src/core/SkMathPriv.h"
13 #include "src/core/SkPathPriv.h"
14 #include "src/gpu/GrCaps.h"
15 #include "src/gpu/GrGpuCommandBuffer.h"
16 #include "src/gpu/GrOnFlushResourceProvider.h"
17 #include "src/gpu/GrOpFlushState.h"
18 #include <stdlib.h>
19
20 using TriPointInstance = GrCCCoverageProcessor::TriPointInstance;
21 using QuadPointInstance = GrCCCoverageProcessor::QuadPointInstance;
22
GrCCFiller(Algorithm algorithm,int numPaths,int numSkPoints,int numSkVerbs,int numConicWeights)23 GrCCFiller::GrCCFiller(Algorithm algorithm, int numPaths, int numSkPoints, int numSkVerbs,
24 int numConicWeights)
25 : fAlgorithm(algorithm)
26 , fGeometry(numSkPoints, numSkVerbs, numConicWeights)
27 , fPathInfos(numPaths)
28 , fScissorSubBatches(numPaths)
29 , fTotalPrimitiveCounts{PrimitiveTallies(), PrimitiveTallies()} {
30 // Batches decide what to draw by looking where the previous one ended. Define initial batches
31 // that "end" at the beginning of the data. These will not be drawn, but will only be be read by
32 // the first actual batch.
33 fScissorSubBatches.push_back() = {PrimitiveTallies(), SkIRect::MakeEmpty()};
34 fBatches.push_back() = {PrimitiveTallies(), fScissorSubBatches.count(), PrimitiveTallies()};
35 }
36
parseDeviceSpaceFill(const SkPath & path,const SkPoint * deviceSpacePts,GrScissorTest scissorTest,const SkIRect & clippedDevIBounds,const SkIVector & devToAtlasOffset)37 void GrCCFiller::parseDeviceSpaceFill(const SkPath& path, const SkPoint* deviceSpacePts,
38 GrScissorTest scissorTest, const SkIRect& clippedDevIBounds,
39 const SkIVector& devToAtlasOffset) {
40 SkASSERT(!fInstanceBuffer); // Can't call after prepareToDraw().
41 SkASSERT(!path.isEmpty());
42
43 int currPathPointsIdx = fGeometry.points().count();
44 int currPathVerbsIdx = fGeometry.verbs().count();
45 PrimitiveTallies currPathPrimitiveCounts = PrimitiveTallies();
46
47 fGeometry.beginPath();
48
49 const float* conicWeights = SkPathPriv::ConicWeightData(path);
50 int ptsIdx = 0;
51 int conicWeightsIdx = 0;
52 bool insideContour = false;
53
54 for (SkPath::Verb verb : SkPathPriv::Verbs(path)) {
55 switch (verb) {
56 case SkPath::kMove_Verb:
57 if (insideContour) {
58 currPathPrimitiveCounts += fGeometry.endContour();
59 }
60 fGeometry.beginContour(deviceSpacePts[ptsIdx]);
61 ++ptsIdx;
62 insideContour = true;
63 continue;
64 case SkPath::kClose_Verb:
65 if (insideContour) {
66 currPathPrimitiveCounts += fGeometry.endContour();
67 }
68 insideContour = false;
69 continue;
70 case SkPath::kLine_Verb:
71 fGeometry.lineTo(&deviceSpacePts[ptsIdx - 1]);
72 ++ptsIdx;
73 continue;
74 case SkPath::kQuad_Verb:
75 fGeometry.quadraticTo(&deviceSpacePts[ptsIdx - 1]);
76 ptsIdx += 2;
77 continue;
78 case SkPath::kCubic_Verb:
79 fGeometry.cubicTo(&deviceSpacePts[ptsIdx - 1]);
80 ptsIdx += 3;
81 continue;
82 case SkPath::kConic_Verb:
83 fGeometry.conicTo(&deviceSpacePts[ptsIdx - 1], conicWeights[conicWeightsIdx]);
84 ptsIdx += 2;
85 ++conicWeightsIdx;
86 continue;
87 default:
88 SK_ABORT("Unexpected path verb.");
89 }
90 }
91 SkASSERT(ptsIdx == path.countPoints());
92 SkASSERT(conicWeightsIdx == SkPathPriv::ConicWeightCnt(path));
93
94 if (insideContour) {
95 currPathPrimitiveCounts += fGeometry.endContour();
96 }
97
98 fPathInfos.emplace_back(scissorTest, devToAtlasOffset);
99
100 // Tessellate fans from very large and/or simple paths, in order to reduce overdraw.
101 int numVerbs = fGeometry.verbs().count() - currPathVerbsIdx - 1;
102 int64_t tessellationWork = (int64_t)numVerbs * (32 - SkCLZ(numVerbs)); // N log N.
103 int64_t fanningWork = (int64_t)clippedDevIBounds.height() * clippedDevIBounds.width();
104 if (tessellationWork * (50*50) + (100*100) < fanningWork) { // Don't tessellate under 100x100.
105 fPathInfos.back().tessellateFan(
106 fAlgorithm, path, fGeometry, currPathVerbsIdx, currPathPointsIdx, clippedDevIBounds,
107 &currPathPrimitiveCounts);
108 }
109
110 fTotalPrimitiveCounts[(int)scissorTest] += currPathPrimitiveCounts;
111
112 if (GrScissorTest::kEnabled == scissorTest) {
113 fScissorSubBatches.push_back() = {fTotalPrimitiveCounts[(int)GrScissorTest::kEnabled],
114 clippedDevIBounds.makeOffset(devToAtlasOffset.fX,
115 devToAtlasOffset.fY)};
116 }
117 }
118
tessellateFan(Algorithm algorithm,const SkPath & originalPath,const GrCCFillGeometry & geometry,int verbsIdx,int ptsIdx,const SkIRect & clippedDevIBounds,PrimitiveTallies * newTriangleCounts)119 void GrCCFiller::PathInfo::tessellateFan(
120 Algorithm algorithm, const SkPath& originalPath, const GrCCFillGeometry& geometry,
121 int verbsIdx, int ptsIdx, const SkIRect& clippedDevIBounds,
122 PrimitiveTallies* newTriangleCounts) {
123 using Verb = GrCCFillGeometry::Verb;
124 SkASSERT(-1 == fFanTessellationCount);
125 SkASSERT(!fFanTessellation);
126
127 const SkTArray<Verb, true>& verbs = geometry.verbs();
128 const SkTArray<SkPoint, true>& pts = geometry.points();
129
130 newTriangleCounts->fTriangles =
131 newTriangleCounts->fWeightedTriangles = 0;
132
133 // Build an SkPath of the Redbook fan.
134 SkPath fan;
135 if (Algorithm::kCoverageCount == algorithm) {
136 // We use "winding" fill type right now because we are producing a coverage count, and must
137 // fill in every region that has non-zero wind. The path processor will convert coverage
138 // count to the appropriate fill type later.
139 fan.setFillType(SkPath::kWinding_FillType);
140 } else {
141 // When counting winding numbers in the stencil buffer, it works to just tessellate the
142 // Redbook fan with the same fill type as the path.
143 fan.setFillType(originalPath.getFillType());
144 }
145 SkASSERT(Verb::kBeginPath == verbs[verbsIdx]);
146 for (int i = verbsIdx + 1; i < verbs.count(); ++i) {
147 switch (verbs[i]) {
148 case Verb::kBeginPath:
149 SK_ABORT("Invalid GrCCFillGeometry");
150 continue;
151
152 case Verb::kBeginContour:
153 fan.moveTo(pts[ptsIdx++]);
154 continue;
155
156 case Verb::kLineTo:
157 fan.lineTo(pts[ptsIdx++]);
158 continue;
159
160 case Verb::kMonotonicQuadraticTo:
161 case Verb::kMonotonicConicTo:
162 fan.lineTo(pts[ptsIdx + 1]);
163 ptsIdx += 2;
164 continue;
165
166 case Verb::kMonotonicCubicTo:
167 fan.lineTo(pts[ptsIdx + 2]);
168 ptsIdx += 3;
169 continue;
170
171 case Verb::kEndClosedContour:
172 case Verb::kEndOpenContour:
173 fan.close();
174 continue;
175 }
176 }
177
178 GrTessellator::WindingVertex* vertices = nullptr;
179 fFanTessellationCount =
180 GrTessellator::PathToVertices(fan, std::numeric_limits<float>::infinity(),
181 SkRect::Make(clippedDevIBounds), &vertices);
182 if (fFanTessellationCount <= 0) {
183 SkASSERT(0 == fFanTessellationCount);
184 SkASSERT(nullptr == vertices);
185 return;
186 }
187
188 SkASSERT(0 == fFanTessellationCount % 3);
189 for (int i = 0; i < fFanTessellationCount; i += 3) {
190 int tessWinding = vertices[i].fWinding;
191 SkASSERT(tessWinding == vertices[i + 1].fWinding);
192 SkASSERT(tessWinding == vertices[i + 2].fWinding);
193
194 // Ensure this triangle's points actually wind in the same direction as tessWinding.
195 // CCPR shaders use the sign of wind to determine which direction to bloat, so even for
196 // "wound" triangles the winding sign and point ordering need to agree.
197 float ax = vertices[i].fPos.fX - vertices[i + 1].fPos.fX;
198 float ay = vertices[i].fPos.fY - vertices[i + 1].fPos.fY;
199 float bx = vertices[i].fPos.fX - vertices[i + 2].fPos.fX;
200 float by = vertices[i].fPos.fY - vertices[i + 2].fPos.fY;
201 float wind = ax*by - ay*bx;
202 if ((wind > 0) != (-tessWinding > 0)) { // Tessellator has opposite winding sense.
203 std::swap(vertices[i + 1].fPos, vertices[i + 2].fPos);
204 }
205
206 int weight = abs(tessWinding);
207 SkASSERT(SkPath::kEvenOdd_FillType != fan.getFillType() || weight == 1);
208 if (weight > 1 && Algorithm::kCoverageCount == algorithm) {
209 ++newTriangleCounts->fWeightedTriangles;
210 } else {
211 newTriangleCounts->fTriangles += weight;
212 }
213 }
214
215 fFanTessellation.reset(vertices);
216 }
217
closeCurrentBatch()218 GrCCFiller::BatchID GrCCFiller::closeCurrentBatch() {
219 SkASSERT(!fInstanceBuffer);
220 SkASSERT(!fBatches.empty());
221
222 const auto& lastBatch = fBatches.back();
223 int maxMeshes = 1 + fScissorSubBatches.count() - lastBatch.fEndScissorSubBatchIdx;
224 fMaxMeshesPerDraw = SkTMax(fMaxMeshesPerDraw, maxMeshes);
225
226 const auto& lastScissorSubBatch = fScissorSubBatches[lastBatch.fEndScissorSubBatchIdx - 1];
227 PrimitiveTallies batchTotalCounts = fTotalPrimitiveCounts[(int)GrScissorTest::kDisabled] -
228 lastBatch.fEndNonScissorIndices;
229 batchTotalCounts += fTotalPrimitiveCounts[(int)GrScissorTest::kEnabled] -
230 lastScissorSubBatch.fEndPrimitiveIndices;
231
232 // This will invalidate lastBatch.
233 fBatches.push_back() = {
234 fTotalPrimitiveCounts[(int)GrScissorTest::kDisabled],
235 fScissorSubBatches.count(),
236 batchTotalCounts
237 };
238 return fBatches.count() - 1;
239 }
240
241 // Emits a contour's triangle fan.
242 //
243 // Classic Redbook fanning would be the triangles: [0 1 2], [0 2 3], ..., [0 n-2 n-1].
244 //
245 // This function emits the triangle: [0 n/3 n*2/3], and then recurses on all three sides. The
246 // advantage to this approach is that for a convex-ish contour, it generates larger triangles.
247 // Classic fanning tends to generate long, skinny triangles, which are expensive to draw since they
248 // have a longer perimeter to rasterize and antialias.
249 //
250 // The indices array indexes the fan's points (think: glDrawElements), and must have at least log3
251 // elements past the end for this method to use as scratch space.
252 //
253 // Returns the next triangle instance after the final one emitted.
emit_recursive_fan(const SkTArray<SkPoint,true> & pts,SkTArray<int32_t,true> & indices,int firstIndex,int indexCount,const Sk2f & devToAtlasOffset,TriPointInstance::Ordering ordering,TriPointInstance out[])254 static TriPointInstance* emit_recursive_fan(
255 const SkTArray<SkPoint, true>& pts, SkTArray<int32_t, true>& indices, int firstIndex,
256 int indexCount, const Sk2f& devToAtlasOffset, TriPointInstance::Ordering ordering,
257 TriPointInstance out[]) {
258 if (indexCount < 3) {
259 return out;
260 }
261
262 int32_t oneThirdCount = indexCount / 3;
263 int32_t twoThirdsCount = (2 * indexCount) / 3;
264 out++->set(pts[indices[firstIndex]], pts[indices[firstIndex + oneThirdCount]],
265 pts[indices[firstIndex + twoThirdsCount]], devToAtlasOffset, ordering);
266
267 out = emit_recursive_fan(
268 pts, indices, firstIndex, oneThirdCount + 1, devToAtlasOffset, ordering, out);
269 out = emit_recursive_fan(
270 pts, indices, firstIndex + oneThirdCount, twoThirdsCount - oneThirdCount + 1,
271 devToAtlasOffset, ordering, out);
272
273 int endIndex = firstIndex + indexCount;
274 int32_t oldValue = indices[endIndex];
275 indices[endIndex] = indices[firstIndex];
276 out = emit_recursive_fan(
277 pts, indices, firstIndex + twoThirdsCount, indexCount - twoThirdsCount + 1,
278 devToAtlasOffset, ordering, out);
279 indices[endIndex] = oldValue;
280
281 return out;
282 }
283
emitTessellatedFan(const GrTessellator::WindingVertex * vertices,int numVertices,const Sk2f & devToAtlasOffset,TriPointInstance::Ordering ordering,TriPointInstance * triPointInstanceData,QuadPointInstance * quadPointInstanceData,GrCCFillGeometry::PrimitiveTallies * indices)284 void GrCCFiller::emitTessellatedFan(
285 const GrTessellator::WindingVertex* vertices, int numVertices, const Sk2f& devToAtlasOffset,
286 TriPointInstance::Ordering ordering, TriPointInstance* triPointInstanceData,
287 QuadPointInstance* quadPointInstanceData, GrCCFillGeometry::PrimitiveTallies* indices) {
288 for (int i = 0; i < numVertices; i += 3) {
289 int weight = abs(vertices[i].fWinding);
290 SkASSERT(weight >= 1);
291 if (weight > 1 && Algorithm::kStencilWindingCount != fAlgorithm) {
292 quadPointInstanceData[indices->fWeightedTriangles++].setW(
293 vertices[i].fPos, vertices[i+1].fPos, vertices[i + 2].fPos, devToAtlasOffset,
294 static_cast<float>(abs(vertices[i].fWinding)));
295 } else for (int j = 0; j < weight; ++j) {
296 // Unfortunately, there is not a way to increment stencil values by an amount larger
297 // than 1. Instead we draw the triangle 'weight' times.
298 triPointInstanceData[indices->fTriangles++].set(
299 vertices[i].fPos, vertices[i + 1].fPos, vertices[i + 2].fPos, devToAtlasOffset,
300 ordering);
301 }
302 }
303 }
304
prepareToDraw(GrOnFlushResourceProvider * onFlushRP)305 bool GrCCFiller::prepareToDraw(GrOnFlushResourceProvider* onFlushRP) {
306 using Verb = GrCCFillGeometry::Verb;
307 SkASSERT(!fInstanceBuffer);
308 SkASSERT(fBatches.back().fEndNonScissorIndices == // Call closeCurrentBatch().
309 fTotalPrimitiveCounts[(int)GrScissorTest::kDisabled]);
310 SkASSERT(fBatches.back().fEndScissorSubBatchIdx == fScissorSubBatches.count());
311
312 auto triangleOrdering = (Algorithm::kCoverageCount == fAlgorithm)
313 ? TriPointInstance::Ordering::kXYTransposed
314 : TriPointInstance::Ordering::kXYInterleaved;
315
316 // Here we build a single instance buffer to share with every internal batch.
317 //
318 // CCPR processs 3 different types of primitives: triangles, quadratics, cubics. Each primitive
319 // type is further divided into instances that require a scissor and those that don't. This
320 // leaves us with 3*2 = 6 independent instance arrays to build for the GPU.
321 //
322 // Rather than place each instance array in its own GPU buffer, we allocate a single
323 // megabuffer and lay them all out side-by-side. We can offset the "baseInstance" parameter in
324 // our draw calls to direct the GPU to the applicable elements within a given array.
325 //
326 // We already know how big to make each of the 6 arrays from fTotalPrimitiveCounts, so layout is
327 // straightforward. Start with triangles and quadratics. They both view the instance buffer as
328 // an array of TriPointInstance[], so we can begin at zero and lay them out one after the other.
329 fBaseInstances[0].fTriangles = 0;
330 fBaseInstances[1].fTriangles = fBaseInstances[0].fTriangles +
331 fTotalPrimitiveCounts[0].fTriangles;
332 fBaseInstances[0].fQuadratics = fBaseInstances[1].fTriangles +
333 fTotalPrimitiveCounts[1].fTriangles;
334 fBaseInstances[1].fQuadratics = fBaseInstances[0].fQuadratics +
335 fTotalPrimitiveCounts[0].fQuadratics;
336 int triEndIdx = fBaseInstances[1].fQuadratics + fTotalPrimitiveCounts[1].fQuadratics;
337
338 // Wound triangles and cubics both view the same instance buffer as an array of
339 // QuadPointInstance[]. So, reinterpreting the instance data as QuadPointInstance[], we start
340 // them on the first index that will not overwrite previous TriPointInstance data.
341 int quadBaseIdx =
342 GrSizeDivRoundUp(triEndIdx * sizeof(TriPointInstance), sizeof(QuadPointInstance));
343 fBaseInstances[0].fWeightedTriangles = quadBaseIdx;
344 fBaseInstances[1].fWeightedTriangles = fBaseInstances[0].fWeightedTriangles +
345 fTotalPrimitiveCounts[0].fWeightedTriangles;
346 fBaseInstances[0].fCubics = fBaseInstances[1].fWeightedTriangles +
347 fTotalPrimitiveCounts[1].fWeightedTriangles;
348 fBaseInstances[1].fCubics = fBaseInstances[0].fCubics + fTotalPrimitiveCounts[0].fCubics;
349 fBaseInstances[0].fConics = fBaseInstances[1].fCubics + fTotalPrimitiveCounts[1].fCubics;
350 fBaseInstances[1].fConics = fBaseInstances[0].fConics + fTotalPrimitiveCounts[0].fConics;
351 int quadEndIdx = fBaseInstances[1].fConics + fTotalPrimitiveCounts[1].fConics;
352
353 fInstanceBuffer =
354 onFlushRP->makeBuffer(GrGpuBufferType::kVertex, quadEndIdx * sizeof(QuadPointInstance));
355 if (!fInstanceBuffer) {
356 SkDebugf("WARNING: failed to allocate CCPR fill instance buffer.\n");
357 return false;
358 }
359
360 TriPointInstance* triPointInstanceData = static_cast<TriPointInstance*>(fInstanceBuffer->map());
361 QuadPointInstance* quadPointInstanceData =
362 reinterpret_cast<QuadPointInstance*>(triPointInstanceData);
363 SkASSERT(quadPointInstanceData);
364
365 PathInfo* nextPathInfo = fPathInfos.begin();
366 Sk2f devToAtlasOffset;
367 PrimitiveTallies instanceIndices[2] = {fBaseInstances[0], fBaseInstances[1]};
368 PrimitiveTallies* currIndices = nullptr;
369 SkSTArray<256, int32_t, true> currFan;
370 bool currFanIsTessellated = false;
371
372 const SkTArray<SkPoint, true>& pts = fGeometry.points();
373 int ptsIdx = -1;
374 int nextConicWeightIdx = 0;
375
376 // Expand the ccpr verbs into GPU instance buffers.
377 for (Verb verb : fGeometry.verbs()) {
378 switch (verb) {
379 case Verb::kBeginPath:
380 SkASSERT(currFan.empty());
381 currIndices = &instanceIndices[(int)nextPathInfo->scissorTest()];
382 devToAtlasOffset = Sk2f(static_cast<float>(nextPathInfo->devToAtlasOffset().fX),
383 static_cast<float>(nextPathInfo->devToAtlasOffset().fY));
384 currFanIsTessellated = nextPathInfo->hasFanTessellation();
385 if (currFanIsTessellated) {
386 this->emitTessellatedFan(
387 nextPathInfo->fanTessellation(), nextPathInfo->fanTessellationCount(),
388 devToAtlasOffset, triangleOrdering, triPointInstanceData,
389 quadPointInstanceData, currIndices);
390 }
391 ++nextPathInfo;
392 continue;
393
394 case Verb::kBeginContour:
395 SkASSERT(currFan.empty());
396 ++ptsIdx;
397 if (!currFanIsTessellated) {
398 currFan.push_back(ptsIdx);
399 }
400 continue;
401
402 case Verb::kLineTo:
403 ++ptsIdx;
404 if (!currFanIsTessellated) {
405 SkASSERT(!currFan.empty());
406 currFan.push_back(ptsIdx);
407 }
408 continue;
409
410 case Verb::kMonotonicQuadraticTo:
411 triPointInstanceData[currIndices->fQuadratics++].set(
412 &pts[ptsIdx], devToAtlasOffset, TriPointInstance::Ordering::kXYTransposed);
413 ptsIdx += 2;
414 if (!currFanIsTessellated) {
415 SkASSERT(!currFan.empty());
416 currFan.push_back(ptsIdx);
417 }
418 continue;
419
420 case Verb::kMonotonicCubicTo:
421 quadPointInstanceData[currIndices->fCubics++].set(
422 &pts[ptsIdx], devToAtlasOffset[0], devToAtlasOffset[1]);
423 ptsIdx += 3;
424 if (!currFanIsTessellated) {
425 SkASSERT(!currFan.empty());
426 currFan.push_back(ptsIdx);
427 }
428 continue;
429
430 case Verb::kMonotonicConicTo:
431 quadPointInstanceData[currIndices->fConics++].setW(
432 &pts[ptsIdx], devToAtlasOffset,
433 fGeometry.getConicWeight(nextConicWeightIdx));
434 ptsIdx += 2;
435 ++nextConicWeightIdx;
436 if (!currFanIsTessellated) {
437 SkASSERT(!currFan.empty());
438 currFan.push_back(ptsIdx);
439 }
440 continue;
441
442 case Verb::kEndClosedContour: // endPt == startPt.
443 if (!currFanIsTessellated) {
444 SkASSERT(!currFan.empty());
445 currFan.pop_back();
446 }
447 // fallthru.
448 case Verb::kEndOpenContour: // endPt != startPt.
449 SkASSERT(!currFanIsTessellated || currFan.empty());
450 if (!currFanIsTessellated && currFan.count() >= 3) {
451 int fanSize = currFan.count();
452 // Reserve space for emit_recursive_fan. Technically this can grow to
453 // fanSize + log3(fanSize), but we approximate with log2.
454 currFan.push_back_n(SkNextLog2(fanSize));
455 SkDEBUGCODE(TriPointInstance* end =) emit_recursive_fan(
456 pts, currFan, 0, fanSize, devToAtlasOffset, triangleOrdering,
457 triPointInstanceData + currIndices->fTriangles);
458 currIndices->fTriangles += fanSize - 2;
459 SkASSERT(triPointInstanceData + currIndices->fTriangles == end);
460 }
461 currFan.reset();
462 continue;
463 }
464 }
465
466 fInstanceBuffer->unmap();
467
468 SkASSERT(nextPathInfo == fPathInfos.end());
469 SkASSERT(ptsIdx == pts.count() - 1);
470 SkASSERT(instanceIndices[0].fTriangles == fBaseInstances[1].fTriangles);
471 SkASSERT(instanceIndices[1].fTriangles == fBaseInstances[0].fQuadratics);
472 SkASSERT(instanceIndices[0].fQuadratics == fBaseInstances[1].fQuadratics);
473 SkASSERT(instanceIndices[1].fQuadratics == triEndIdx);
474 SkASSERT(instanceIndices[0].fWeightedTriangles == fBaseInstances[1].fWeightedTriangles);
475 SkASSERT(instanceIndices[1].fWeightedTriangles == fBaseInstances[0].fCubics);
476 SkASSERT(instanceIndices[0].fCubics == fBaseInstances[1].fCubics);
477 SkASSERT(instanceIndices[1].fCubics == fBaseInstances[0].fConics);
478 SkASSERT(instanceIndices[0].fConics == fBaseInstances[1].fConics);
479 SkASSERT(instanceIndices[1].fConics == quadEndIdx);
480
481 fMeshesScratchBuffer.reserve(fMaxMeshesPerDraw);
482 fScissorRectScratchBuffer.reserve(fMaxMeshesPerDraw);
483
484 return true;
485 }
486
drawFills(GrOpFlushState * flushState,GrCCCoverageProcessor * proc,const GrPipeline & pipeline,BatchID batchID,const SkIRect & drawBounds) const487 void GrCCFiller::drawFills(
488 GrOpFlushState* flushState, GrCCCoverageProcessor* proc, const GrPipeline& pipeline,
489 BatchID batchID, const SkIRect& drawBounds) const {
490 using PrimitiveType = GrCCCoverageProcessor::PrimitiveType;
491
492 SkASSERT(fInstanceBuffer);
493
494 GrResourceProvider* rp = flushState->resourceProvider();
495 const PrimitiveTallies& batchTotalCounts = fBatches[batchID].fTotalPrimitiveCounts;
496
497 if (batchTotalCounts.fTriangles) {
498 proc->reset(PrimitiveType::kTriangles, rp);
499 this->drawPrimitives(
500 flushState, *proc, pipeline, batchID, &PrimitiveTallies::fTriangles, drawBounds);
501 }
502
503 if (batchTotalCounts.fWeightedTriangles) {
504 SkASSERT(Algorithm::kStencilWindingCount != fAlgorithm);
505 proc->reset(PrimitiveType::kWeightedTriangles, rp);
506 this->drawPrimitives(
507 flushState, *proc, pipeline, batchID, &PrimitiveTallies::fWeightedTriangles,
508 drawBounds);
509 }
510
511 if (batchTotalCounts.fQuadratics) {
512 proc->reset(PrimitiveType::kQuadratics, rp);
513 this->drawPrimitives(
514 flushState, *proc, pipeline, batchID, &PrimitiveTallies::fQuadratics, drawBounds);
515 }
516
517 if (batchTotalCounts.fCubics) {
518 proc->reset(PrimitiveType::kCubics, rp);
519 this->drawPrimitives(
520 flushState, *proc, pipeline, batchID, &PrimitiveTallies::fCubics, drawBounds);
521 }
522
523 if (batchTotalCounts.fConics) {
524 proc->reset(PrimitiveType::kConics, rp);
525 this->drawPrimitives(
526 flushState, *proc, pipeline, batchID, &PrimitiveTallies::fConics, drawBounds);
527 }
528 }
529
drawPrimitives(GrOpFlushState * flushState,const GrCCCoverageProcessor & proc,const GrPipeline & pipeline,BatchID batchID,int PrimitiveTallies::* instanceType,const SkIRect & drawBounds) const530 void GrCCFiller::drawPrimitives(
531 GrOpFlushState* flushState, const GrCCCoverageProcessor& proc, const GrPipeline& pipeline,
532 BatchID batchID, int PrimitiveTallies::*instanceType, const SkIRect& drawBounds) const {
533 SkASSERT(pipeline.isScissorEnabled());
534
535 // Don't call reset(), as that also resets the reserve count.
536 fMeshesScratchBuffer.pop_back_n(fMeshesScratchBuffer.count());
537 fScissorRectScratchBuffer.pop_back_n(fScissorRectScratchBuffer.count());
538
539 SkASSERT(batchID > 0);
540 SkASSERT(batchID < fBatches.count());
541 const Batch& previousBatch = fBatches[batchID - 1];
542 const Batch& batch = fBatches[batchID];
543 SkDEBUGCODE(int totalInstanceCount = 0);
544
545 if (int instanceCount = batch.fEndNonScissorIndices.*instanceType -
546 previousBatch.fEndNonScissorIndices.*instanceType) {
547 SkASSERT(instanceCount > 0);
548 int baseInstance = fBaseInstances[(int)GrScissorTest::kDisabled].*instanceType +
549 previousBatch.fEndNonScissorIndices.*instanceType;
550 proc.appendMesh(fInstanceBuffer, instanceCount, baseInstance, &fMeshesScratchBuffer);
551 fScissorRectScratchBuffer.push_back().setXYWH(0, 0, drawBounds.width(),
552 drawBounds.height());
553 SkDEBUGCODE(totalInstanceCount += instanceCount);
554 }
555
556 SkASSERT(previousBatch.fEndScissorSubBatchIdx > 0);
557 SkASSERT(batch.fEndScissorSubBatchIdx <= fScissorSubBatches.count());
558 int baseScissorInstance = fBaseInstances[(int)GrScissorTest::kEnabled].*instanceType;
559 for (int i = previousBatch.fEndScissorSubBatchIdx; i < batch.fEndScissorSubBatchIdx; ++i) {
560 const ScissorSubBatch& previousSubBatch = fScissorSubBatches[i - 1];
561 const ScissorSubBatch& scissorSubBatch = fScissorSubBatches[i];
562 int startIndex = previousSubBatch.fEndPrimitiveIndices.*instanceType;
563 int instanceCount = scissorSubBatch.fEndPrimitiveIndices.*instanceType - startIndex;
564 if (!instanceCount) {
565 continue;
566 }
567 SkASSERT(instanceCount > 0);
568 proc.appendMesh(fInstanceBuffer, instanceCount, baseScissorInstance + startIndex,
569 &fMeshesScratchBuffer);
570 fScissorRectScratchBuffer.push_back() = scissorSubBatch.fScissor;
571 SkDEBUGCODE(totalInstanceCount += instanceCount);
572 }
573
574 SkASSERT(fMeshesScratchBuffer.count() == fScissorRectScratchBuffer.count());
575 SkASSERT(fMeshesScratchBuffer.count() <= fMaxMeshesPerDraw);
576 SkASSERT(totalInstanceCount == batch.fTotalPrimitiveCounts.*instanceType);
577
578 if (!fMeshesScratchBuffer.empty()) {
579 proc.draw(flushState, pipeline, fScissorRectScratchBuffer.begin(),
580 fMeshesScratchBuffer.begin(), fMeshesScratchBuffer.count(),
581 SkRect::Make(drawBounds));
582 }
583 }
584