1 /*
2 * Copyright 2011 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/ganesh/ops/DefaultPathRenderer.h"
9
10 #include "include/core/SkString.h"
11 #include "include/core/SkStrokeRec.h"
12 #include "src/base/SkTLazy.h"
13 #include "src/core/SkGeometry.h"
14 #include "src/core/SkMatrixPriv.h"
15 #include "src/core/SkTraceEvent.h"
16 #include "src/gpu/ganesh/GrAuditTrail.h"
17 #include "src/gpu/ganesh/GrCaps.h"
18 #include "src/gpu/ganesh/GrClip.h"
19 #include "src/gpu/ganesh/GrDefaultGeoProcFactory.h"
20 #include "src/gpu/ganesh/GrDrawOpTest.h"
21 #include "src/gpu/ganesh/GrOpFlushState.h"
22 #include "src/gpu/ganesh/GrProgramInfo.h"
23 #include "src/gpu/ganesh/GrSimpleMesh.h"
24 #include "src/gpu/ganesh/GrStyle.h"
25 #include "src/gpu/ganesh/GrUtil.h"
26 #include "src/gpu/ganesh/SurfaceDrawContext.h"
27 #include "src/gpu/ganesh/effects/GrDisableColorXP.h"
28 #include "src/gpu/ganesh/geometry/GrPathUtils.h"
29 #include "src/gpu/ganesh/geometry/GrStyledShape.h"
30 #include "src/gpu/ganesh/ops/GrMeshDrawOp.h"
31 #include "src/gpu/ganesh/ops/GrPathStencilSettings.h"
32 #include "src/gpu/ganesh/ops/GrSimpleMeshDrawOpHelperWithStencil.h"
33
34 using namespace skia_private;
35
36 ////////////////////////////////////////////////////////////////////////////////
37 // Helpers for drawPath
38
39 namespace {
40
41 #define STENCIL_OFF 0 // Always disable stencil (even when needed)
42
single_pass_shape(const GrStyledShape & shape)43 inline bool single_pass_shape(const GrStyledShape& shape) {
44 #if STENCIL_OFF
45 return true;
46 #else
47 // Inverse fill is always two pass.
48 if (shape.inverseFilled()) {
49 return false;
50 }
51 // This path renderer only accepts simple fill paths or stroke paths that are either hairline
52 // or have a stroke width small enough to treat as hairline. Hairline paths are always single
53 // pass. Filled paths are single pass if they're convex.
54 if (shape.style().isSimpleFill()) {
55 return shape.knownToBeConvex();
56 }
57 return true;
58 #endif
59 }
60
61 class PathGeoBuilder {
62 public:
PathGeoBuilder(GrPrimitiveType primitiveType,GrMeshDrawTarget * target,SkTDArray<GrSimpleMesh * > * meshes)63 PathGeoBuilder(GrPrimitiveType primitiveType,
64 GrMeshDrawTarget* target,
65 SkTDArray<GrSimpleMesh*>* meshes)
66 : fPrimitiveType(primitiveType)
67 , fTarget(target)
68 , fVertexStride(sizeof(SkPoint))
69 , fFirstIndex(0)
70 , fIndicesInChunk(0)
71 , fIndices(nullptr)
72 , fMeshes(meshes) {
73 this->allocNewBuffers();
74 }
75
~PathGeoBuilder()76 ~PathGeoBuilder() {
77 this->createMeshAndPutBackReserve();
78 }
79
80 /**
81 * Path verbs
82 */
moveTo(const SkPoint & p)83 void moveTo(const SkPoint& p) {
84 if (!this->ensureSpace(1)) {
85 return;
86 }
87
88 if (!this->isHairline()) {
89 fSubpathIndexStart = this->currentIndex();
90 fSubpathStartPoint = p;
91 }
92 *(fCurVert++) = p;
93 }
94
addLine(const SkPoint pts[])95 void addLine(const SkPoint pts[]) {
96 if (!this->ensureSpace(1, this->indexScale(), &pts[0])) {
97 return;
98 }
99
100 if (this->isIndexed()) {
101 uint16_t prevIdx = this->currentIndex() - 1;
102 this->appendCountourEdgeIndices(prevIdx);
103 }
104 *(fCurVert++) = pts[1];
105 }
106
addQuad(const SkPoint pts[],SkScalar srcSpaceTolSqd,SkScalar srcSpaceTol)107 void addQuad(const SkPoint pts[], SkScalar srcSpaceTolSqd, SkScalar srcSpaceTol) {
108 if (!this->ensureSpace(GrPathUtils::kMaxPointsPerCurve,
109 GrPathUtils::kMaxPointsPerCurve * this->indexScale(),
110 &pts[0])) {
111 return;
112 }
113
114 // First pt of quad is the pt we ended on in previous step
115 uint16_t firstQPtIdx = this->currentIndex() - 1;
116 uint16_t numPts = (uint16_t)GrPathUtils::generateQuadraticPoints(
117 pts[0], pts[1], pts[2], srcSpaceTolSqd, &fCurVert,
118 GrPathUtils::quadraticPointCount(pts, srcSpaceTol));
119 if (this->isIndexed()) {
120 for (uint16_t i = 0; i < numPts; ++i) {
121 this->appendCountourEdgeIndices(firstQPtIdx + i);
122 }
123 }
124 }
125
addConic(SkScalar weight,const SkPoint pts[],SkScalar srcSpaceTolSqd,SkScalar srcSpaceTol)126 void addConic(SkScalar weight, const SkPoint pts[], SkScalar srcSpaceTolSqd,
127 SkScalar srcSpaceTol) {
128 SkAutoConicToQuads converter;
129 const SkPoint* quadPts = converter.computeQuads(pts, weight, srcSpaceTol);
130 for (int i = 0; i < converter.countQuads(); ++i) {
131 this->addQuad(quadPts + i * 2, srcSpaceTolSqd, srcSpaceTol);
132 }
133 }
134
addCubic(const SkPoint pts[],SkScalar srcSpaceTolSqd,SkScalar srcSpaceTol)135 void addCubic(const SkPoint pts[], SkScalar srcSpaceTolSqd, SkScalar srcSpaceTol) {
136 if (!this->ensureSpace(GrPathUtils::kMaxPointsPerCurve,
137 GrPathUtils::kMaxPointsPerCurve * this->indexScale(),
138 &pts[0])) {
139 return;
140 }
141
142 // First pt of cubic is the pt we ended on in previous step
143 uint16_t firstCPtIdx = this->currentIndex() - 1;
144 uint16_t numPts = (uint16_t) GrPathUtils::generateCubicPoints(
145 pts[0], pts[1], pts[2], pts[3], srcSpaceTolSqd, &fCurVert,
146 GrPathUtils::cubicPointCount(pts, srcSpaceTol));
147 if (this->isIndexed()) {
148 for (uint16_t i = 0; i < numPts; ++i) {
149 this->appendCountourEdgeIndices(firstCPtIdx + i);
150 }
151 }
152 }
153
addPath(const SkPath & path,SkScalar srcSpaceTol)154 void addPath(const SkPath& path, SkScalar srcSpaceTol) {
155 SkScalar srcSpaceTolSqd = srcSpaceTol * srcSpaceTol;
156
157 SkPath::Iter iter(path, false);
158 SkPoint pts[4];
159
160 bool done = false;
161 while (!done) {
162 SkPath::Verb verb = iter.next(pts);
163 switch (verb) {
164 case SkPath::kMove_Verb:
165 this->moveTo(pts[0]);
166 break;
167 case SkPath::kLine_Verb:
168 this->addLine(pts);
169 break;
170 case SkPath::kConic_Verb:
171 this->addConic(iter.conicWeight(), pts, srcSpaceTolSqd, srcSpaceTol);
172 break;
173 case SkPath::kQuad_Verb:
174 this->addQuad(pts, srcSpaceTolSqd, srcSpaceTol);
175 break;
176 case SkPath::kCubic_Verb:
177 this->addCubic(pts, srcSpaceTolSqd, srcSpaceTol);
178 break;
179 case SkPath::kClose_Verb:
180 break;
181 case SkPath::kDone_Verb:
182 done = true;
183 }
184 }
185 }
186
PathHasMultipleSubpaths(const SkPath & path)187 static bool PathHasMultipleSubpaths(const SkPath& path) {
188 bool first = true;
189
190 SkPath::Iter iter(path, false);
191 SkPath::Verb verb;
192
193 SkPoint pts[4];
194 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
195 if (SkPath::kMove_Verb == verb && !first) {
196 return true;
197 }
198 first = false;
199 }
200 return false;
201 }
202
203 private:
204 /**
205 * Derived properties
206 * TODO: Cache some of these for better performance, rather than re-computing?
207 */
isIndexed() const208 bool isIndexed() const {
209 return GrPrimitiveType::kLines == fPrimitiveType ||
210 GrPrimitiveType::kTriangles == fPrimitiveType;
211 }
isHairline() const212 bool isHairline() const {
213 return GrPrimitiveType::kLines == fPrimitiveType ||
214 GrPrimitiveType::kLineStrip == fPrimitiveType;
215 }
indexScale() const216 int indexScale() const {
217 switch (fPrimitiveType) {
218 case GrPrimitiveType::kLines:
219 return 2;
220 case GrPrimitiveType::kTriangles:
221 return 3;
222 default:
223 return 0;
224 }
225 }
226
currentIndex() const227 uint16_t currentIndex() const { return fCurVert - fVertices; }
228
229 // Allocate vertex and (possibly) index buffers
allocNewBuffers()230 void allocNewBuffers() {
231 SkASSERT(fValid);
232
233 // Ensure that we always get enough verts for a worst-case quad/cubic, plus leftover points
234 // from previous mesh piece (up to two verts to continue fanning). If we can't get that
235 // many, ask for a much larger number. This needs to be fairly big to handle quads/cubics,
236 // which have a worst-case of 1k points.
237 static const int kMinVerticesPerChunk = GrPathUtils::kMaxPointsPerCurve + 2;
238 static const int kFallbackVerticesPerChunk = 16384;
239
240 fVertices = static_cast<SkPoint*>(fTarget->makeVertexSpaceAtLeast(fVertexStride,
241 kMinVerticesPerChunk,
242 kFallbackVerticesPerChunk,
243 &fVertexBuffer,
244 &fFirstVertex,
245 &fVerticesInChunk));
246 if (!fVertices) {
247 SkDebugf("WARNING: Failed to allocate vertex buffer for DefaultPathRenderer.\n");
248 fCurVert = nullptr;
249 fCurIdx = fIndices = nullptr;
250 fSubpathIndexStart = 0;
251 fValid = false;
252 return;
253 }
254
255 if (this->isIndexed()) {
256 // Similar to above: Ensure we get enough indices for one worst-case quad/cubic.
257 // No extra indices are needed for stitching, though. If we can't get that many, ask
258 // for enough to match our large vertex request.
259 const int kMinIndicesPerChunk = GrPathUtils::kMaxPointsPerCurve * this->indexScale();
260 const int kFallbackIndicesPerChunk = kFallbackVerticesPerChunk * this->indexScale();
261
262 fIndices = fTarget->makeIndexSpaceAtLeast(kMinIndicesPerChunk, kFallbackIndicesPerChunk,
263 &fIndexBuffer, &fFirstIndex,
264 &fIndicesInChunk);
265 if (!fIndices) {
266 SkDebugf("WARNING: Failed to allocate index buffer for DefaultPathRenderer.\n");
267 fVertices = nullptr;
268 fValid = false;
269 }
270 }
271
272 fCurVert = fVertices;
273 fCurIdx = fIndices;
274 fSubpathIndexStart = 0;
275 }
276
appendCountourEdgeIndices(uint16_t edgeV0Idx)277 void appendCountourEdgeIndices(uint16_t edgeV0Idx) {
278 SkASSERT(fCurIdx);
279
280 // When drawing lines we're appending line segments along the countour. When applying the
281 // other fill rules we're drawing triangle fans around the start of the current (sub)path.
282 if (!this->isHairline()) {
283 *(fCurIdx++) = fSubpathIndexStart;
284 }
285 *(fCurIdx++) = edgeV0Idx;
286 *(fCurIdx++) = edgeV0Idx + 1;
287 }
288
289 // Emits a single draw with all accumulated vertex/index data
createMeshAndPutBackReserve()290 void createMeshAndPutBackReserve() {
291 if (!fValid) {
292 return;
293 }
294
295 int vertexCount = fCurVert - fVertices;
296 int indexCount = fCurIdx - fIndices;
297 SkASSERT(vertexCount <= fVerticesInChunk);
298 SkASSERT(indexCount <= fIndicesInChunk);
299
300 GrSimpleMesh* mesh = nullptr;
301 if (this->isIndexed() ? SkToBool(indexCount) : SkToBool(vertexCount)) {
302 mesh = fTarget->allocMesh();
303 if (!this->isIndexed()) {
304 mesh->set(std::move(fVertexBuffer), vertexCount, fFirstVertex);
305 } else {
306 mesh->setIndexed(std::move(fIndexBuffer), indexCount, fFirstIndex, 0,
307 vertexCount - 1, GrPrimitiveRestart::kNo, std::move(fVertexBuffer),
308 fFirstVertex);
309 }
310 }
311
312 fTarget->putBackIndices((size_t)(fIndicesInChunk - indexCount));
313 fTarget->putBackVertices((size_t)(fVerticesInChunk - vertexCount), fVertexStride);
314
315 if (mesh) {
316 fMeshes->push_back(mesh);
317 }
318 }
319
ensureSpace(int vertsNeeded,int indicesNeeded=0,const SkPoint * lastPoint=nullptr)320 bool ensureSpace(int vertsNeeded, int indicesNeeded = 0, const SkPoint* lastPoint = nullptr) {
321 if (!fValid) {
322 return false;
323 }
324
325 if (fCurVert + vertsNeeded > fVertices + fVerticesInChunk ||
326 fCurIdx + indicesNeeded > fIndices + fIndicesInChunk) {
327 // We are about to run out of space (possibly)
328
329 #ifdef SK_DEBUG
330 // To maintain continuity, we need to remember one or two points from the current mesh.
331 // Lines only need the last point, fills need the first point from the current contour.
332 // We always grab both here, and append the ones we need at the end of this process.
333 SkASSERT(fSubpathIndexStart < fVerticesInChunk);
334 // This assert is reading from the gpu buffer fVertices and will be slow, but for debug
335 // that is okay.
336 if (!this->isHairline()) {
337 SkASSERT(fSubpathStartPoint == fVertices[fSubpathIndexStart]);
338 }
339 if (lastPoint) {
340 SkASSERT(*(fCurVert - 1) == *lastPoint);
341 }
342 #endif
343
344 // Draw the mesh we've accumulated, and put back any unused space
345 this->createMeshAndPutBackReserve();
346
347 // Get new buffers
348 this->allocNewBuffers();
349 if (!fValid) {
350 return false;
351 }
352
353 // On moves we don't need to copy over any points to the new buffer and we pass in a
354 // null lastPoint.
355 if (lastPoint) {
356 // Append copies of the points we saved so the two meshes will weld properly
357 if (!this->isHairline()) {
358 *(fCurVert++) = fSubpathStartPoint;
359 }
360 *(fCurVert++) = *lastPoint;
361 }
362 }
363
364 return true;
365 }
366
367 GrPrimitiveType fPrimitiveType;
368 GrMeshDrawTarget* fTarget;
369 size_t fVertexStride;
370
371 sk_sp<const GrBuffer> fVertexBuffer;
372 int fFirstVertex;
373 int fVerticesInChunk;
374 SkPoint* fVertices;
375 SkPoint* fCurVert;
376
377 sk_sp<const GrBuffer> fIndexBuffer;
378 int fFirstIndex;
379 int fIndicesInChunk;
380 uint16_t* fIndices;
381 uint16_t* fCurIdx;
382 uint16_t fSubpathIndexStart;
383 SkPoint fSubpathStartPoint;
384
385 bool fValid = true;
386 SkTDArray<GrSimpleMesh*>* fMeshes;
387 };
388
389 class DefaultPathOp final : public GrMeshDrawOp {
390 private:
391 using Helper = GrSimpleMeshDrawOpHelperWithStencil;
392
393 public:
394 DEFINE_OP_CLASS_ID
395
Make(GrRecordingContext * context,GrPaint && paint,const SkPath & path,SkScalar tolerance,uint8_t coverage,const SkMatrix & viewMatrix,bool isHairline,GrAAType aaType,const SkRect & devBounds,const GrUserStencilSettings * stencilSettings)396 static GrOp::Owner Make(GrRecordingContext* context,
397 GrPaint&& paint,
398 const SkPath& path,
399 SkScalar tolerance,
400 uint8_t coverage,
401 const SkMatrix& viewMatrix,
402 bool isHairline,
403 GrAAType aaType,
404 const SkRect& devBounds,
405 const GrUserStencilSettings* stencilSettings) {
406 return Helper::FactoryHelper<DefaultPathOp>(context, std::move(paint), path, tolerance,
407 coverage, viewMatrix, isHairline, aaType,
408 devBounds, stencilSettings);
409 }
410
name() const411 const char* name() const override { return "DefaultPathOp"; }
412
visitProxies(const GrVisitProxyFunc & func) const413 void visitProxies(const GrVisitProxyFunc& func) const override {
414 if (fProgramInfo) {
415 fProgramInfo->visitFPProxies(func);
416 } else {
417 fHelper.visitProxies(func);
418 }
419 }
420
DefaultPathOp(GrProcessorSet * processorSet,const SkPMColor4f & color,const SkPath & path,SkScalar tolerance,uint8_t coverage,const SkMatrix & viewMatrix,bool isHairline,GrAAType aaType,const SkRect & devBounds,const GrUserStencilSettings * stencilSettings)421 DefaultPathOp(GrProcessorSet* processorSet, const SkPMColor4f& color, const SkPath& path,
422 SkScalar tolerance, uint8_t coverage, const SkMatrix& viewMatrix, bool isHairline,
423 GrAAType aaType, const SkRect& devBounds,
424 const GrUserStencilSettings* stencilSettings)
425 : INHERITED(ClassID())
426 , fHelper(processorSet, aaType, stencilSettings)
427 , fColor(color)
428 , fCoverage(coverage)
429 , fViewMatrix(viewMatrix)
430 , fIsHairline(isHairline) {
431 fPaths.emplace_back(PathData{path, tolerance});
432
433 HasAABloat aaBloat = (aaType == GrAAType::kNone) ? HasAABloat ::kNo : HasAABloat::kYes;
434 this->setBounds(devBounds, aaBloat,
435 isHairline ? IsHairline::kYes : IsHairline::kNo);
436 }
437
fixedFunctionFlags() const438 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
439
finalize(const GrCaps & caps,const GrAppliedClip * clip,GrClampType clampType)440 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
441 GrClampType clampType) override {
442 GrProcessorAnalysisCoverage gpCoverage =
443 this->coverage() == 0xFF ? GrProcessorAnalysisCoverage::kNone
444 : GrProcessorAnalysisCoverage::kSingleChannel;
445 // This Op uses uniform (not vertex) color, so doesn't need to track wide color.
446 return fHelper.finalizeProcessors(caps, clip, clampType, gpCoverage, &fColor, nullptr);
447 }
448
449 private:
primType() const450 GrPrimitiveType primType() const {
451 if (this->isHairline()) {
452 int instanceCount = fPaths.size();
453
454 // We avoid indices when we have a single hairline contour.
455 bool isIndexed = instanceCount > 1 ||
456 PathGeoBuilder::PathHasMultipleSubpaths(fPaths[0].fPath);
457
458 return isIndexed ? GrPrimitiveType::kLines : GrPrimitiveType::kLineStrip;
459 }
460
461 return GrPrimitiveType::kTriangles;
462 }
463
programInfo()464 GrProgramInfo* programInfo() override { return fProgramInfo; }
465
onCreateProgramInfo(const GrCaps * caps,SkArenaAlloc * arena,const GrSurfaceProxyView & writeView,bool usesMSAASurface,GrAppliedClip && appliedClip,const GrDstProxyView & dstProxyView,GrXferBarrierFlags renderPassXferBarriers,GrLoadOp colorLoadOp)466 void onCreateProgramInfo(const GrCaps* caps,
467 SkArenaAlloc* arena,
468 const GrSurfaceProxyView& writeView,
469 bool usesMSAASurface,
470 GrAppliedClip&& appliedClip,
471 const GrDstProxyView& dstProxyView,
472 GrXferBarrierFlags renderPassXferBarriers,
473 GrLoadOp colorLoadOp) override {
474 GrGeometryProcessor* gp;
475 {
476 using namespace GrDefaultGeoProcFactory;
477 Color color(this->color());
478 Coverage coverage(this->coverage());
479 LocalCoords localCoords(fHelper.usesLocalCoords() ? LocalCoords::kUsePosition_Type
480 : LocalCoords::kUnused_Type);
481 gp = GrDefaultGeoProcFactory::Make(arena,
482 color,
483 coverage,
484 localCoords,
485 this->viewMatrix());
486 }
487
488 SkASSERT(gp->vertexStride() == sizeof(SkPoint));
489
490 fProgramInfo = fHelper.createProgramInfoWithStencil(caps, arena, writeView,
491 usesMSAASurface,
492 std::move(appliedClip), dstProxyView,
493 gp, this->primType(),
494 renderPassXferBarriers, colorLoadOp);
495
496 }
497
onPrepareDraws(GrMeshDrawTarget * target)498 void onPrepareDraws(GrMeshDrawTarget* target) override {
499 PathGeoBuilder pathGeoBuilder(this->primType(), target, &fMeshes);
500
501 // fill buffers
502 for (int i = 0; i < fPaths.size(); i++) {
503 const PathData& args = fPaths[i];
504 pathGeoBuilder.addPath(args.fPath, args.fTolerance);
505 }
506 }
507
onExecute(GrOpFlushState * flushState,const SkRect & chainBounds)508 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
509 if (!fProgramInfo) {
510 this->createProgramInfo(flushState);
511 }
512
513 if (!fProgramInfo || fMeshes.empty()) {
514 return;
515 }
516
517 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
518 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
519 for (int i = 0; i < fMeshes.size(); ++i) {
520 flushState->drawMesh(*fMeshes[i]);
521 }
522 }
523
onCombineIfPossible(GrOp * t,SkArenaAlloc *,const GrCaps & caps)524 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
525 DefaultPathOp* that = t->cast<DefaultPathOp>();
526 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
527 return CombineResult::kCannotCombine;
528 }
529
530 if (this->color() != that->color()) {
531 return CombineResult::kCannotCombine;
532 }
533
534 if (this->coverage() != that->coverage()) {
535 return CombineResult::kCannotCombine;
536 }
537
538 if (!SkMatrixPriv::CheapEqual(this->viewMatrix(), that->viewMatrix())) {
539 return CombineResult::kCannotCombine;
540 }
541
542 if (this->isHairline() != that->isHairline()) {
543 return CombineResult::kCannotCombine;
544 }
545
546 fPaths.push_back_n(that->fPaths.size(), that->fPaths.begin());
547 return CombineResult::kMerged;
548 }
549
550 #if defined(GR_TEST_UTILS)
onDumpInfo() const551 SkString onDumpInfo() const override {
552 SkString string = SkStringPrintf("Color: 0x%08x Count: %d\n",
553 fColor.toBytes_RGBA(), fPaths.size());
554 for (const auto& path : fPaths) {
555 string.appendf("Tolerance: %.2f\n", path.fTolerance);
556 }
557 string += fHelper.dumpInfo();
558 return string;
559 }
560 #endif
561
color() const562 const SkPMColor4f& color() const { return fColor; }
coverage() const563 uint8_t coverage() const { return fCoverage; }
viewMatrix() const564 const SkMatrix& viewMatrix() const { return fViewMatrix; }
isHairline() const565 bool isHairline() const { return fIsHairline; }
566
567 struct PathData {
568 SkPath fPath;
569 SkScalar fTolerance;
570 };
571
572 STArray<1, PathData, true> fPaths;
573 Helper fHelper;
574 SkPMColor4f fColor;
575 uint8_t fCoverage;
576 SkMatrix fViewMatrix;
577 bool fIsHairline;
578
579 SkTDArray<GrSimpleMesh*> fMeshes;
580 GrProgramInfo* fProgramInfo = nullptr;
581
582 using INHERITED = GrMeshDrawOp;
583 };
584
585 } // anonymous namespace
586
587 ///////////////////////////////////////////////////////////////////////////////////////////////////
588
589 #if defined(GR_TEST_UTILS)
590
GR_DRAW_OP_TEST_DEFINE(DefaultPathOp)591 GR_DRAW_OP_TEST_DEFINE(DefaultPathOp) {
592 SkMatrix viewMatrix = GrTest::TestMatrix(random);
593
594 // For now just hairlines because the other types of draws require two ops.
595 // TODO we should figure out a way to combine the stencil and cover steps into one op.
596 GrStyle style(SkStrokeRec::kHairline_InitStyle);
597 const SkPath& path = GrTest::TestPath(random);
598
599 // Compute srcSpaceTol
600 SkRect bounds = path.getBounds();
601 SkScalar tol = GrPathUtils::kDefaultTolerance;
602 SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, bounds);
603
604 viewMatrix.mapRect(&bounds);
605 uint8_t coverage = GrTest::RandomCoverage(random);
606 GrAAType aaType = GrAAType::kNone;
607 if (numSamples > 1 && random->nextBool()) {
608 aaType = GrAAType::kMSAA;
609 }
610 return DefaultPathOp::Make(context, std::move(paint), path, srcSpaceTol, coverage, viewMatrix,
611 true, aaType, bounds, GrGetRandomStencil(random, context));
612 }
613
614 #endif
615
616 ///////////////////////////////////////////////////////////////////////////////////////////////////
617
618 namespace skgpu::ganesh {
619
internalDrawPath(skgpu::ganesh::SurfaceDrawContext * sdc,GrPaint && paint,GrAAType aaType,const GrUserStencilSettings & userStencilSettings,const GrClip * clip,const SkMatrix & viewMatrix,const GrStyledShape & shape,bool stencilOnly)620 bool DefaultPathRenderer::internalDrawPath(skgpu::ganesh::SurfaceDrawContext* sdc,
621 GrPaint&& paint,
622 GrAAType aaType,
623 const GrUserStencilSettings& userStencilSettings,
624 const GrClip* clip,
625 const SkMatrix& viewMatrix,
626 const GrStyledShape& shape,
627 bool stencilOnly) {
628 auto context = sdc->recordingContext();
629
630 SkASSERT(GrAAType::kCoverage != aaType);
631 SkPath path;
632 shape.asPath(&path);
633
634 SkScalar hairlineCoverage;
635 uint8_t newCoverage = 0xff;
636 bool isHairline = false;
637 if (GrIsStrokeHairlineOrEquivalent(shape.style(), viewMatrix, &hairlineCoverage)) {
638 newCoverage = SkScalarRoundToInt(hairlineCoverage * 0xff);
639 isHairline = true;
640 } else {
641 SkASSERT(shape.style().isSimpleFill());
642 }
643
644 int passCount = 0;
645 const GrUserStencilSettings* passes[2];
646 bool reverse = false;
647 bool lastPassIsBounds;
648
649 if (isHairline) {
650 passCount = 1;
651 if (stencilOnly) {
652 passes[0] = &gDirectToStencil;
653 } else {
654 passes[0] = &userStencilSettings;
655 }
656 lastPassIsBounds = false;
657 } else {
658 if (single_pass_shape(shape)) {
659 passCount = 1;
660 if (stencilOnly) {
661 passes[0] = &gDirectToStencil;
662 } else {
663 passes[0] = &userStencilSettings;
664 }
665 lastPassIsBounds = false;
666 } else {
667 switch (path.getFillType()) {
668 case SkPathFillType::kInverseEvenOdd:
669 reverse = true;
670 [[fallthrough]];
671 case SkPathFillType::kEvenOdd:
672 passes[0] = &gEOStencilPass;
673 if (stencilOnly) {
674 passCount = 1;
675 lastPassIsBounds = false;
676 } else {
677 passCount = 2;
678 lastPassIsBounds = true;
679 if (reverse) {
680 passes[1] = &gInvEOColorPass;
681 } else {
682 passes[1] = &gEOColorPass;
683 }
684 }
685 break;
686
687 case SkPathFillType::kInverseWinding:
688 reverse = true;
689 [[fallthrough]];
690 case SkPathFillType::kWinding:
691 passes[0] = &gWindStencilPass;
692 passCount = 2;
693 if (stencilOnly) {
694 lastPassIsBounds = false;
695 --passCount;
696 } else {
697 lastPassIsBounds = true;
698 if (reverse) {
699 passes[passCount-1] = &gInvWindColorPass;
700 } else {
701 passes[passCount-1] = &gWindColorPass;
702 }
703 }
704 break;
705 default:
706 SkDEBUGFAIL("Unknown path fFill!");
707 return false;
708 }
709 }
710 }
711
712 SkScalar tol = GrPathUtils::kDefaultTolerance;
713 SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, path.getBounds());
714
715 SkRect devBounds;
716 GetPathDevBounds(path, sdc->asRenderTargetProxy()->backingStoreDimensions(),
717 viewMatrix, &devBounds);
718
719 for (int p = 0; p < passCount; ++p) {
720 if (lastPassIsBounds && (p == passCount-1)) {
721 SkRect bounds;
722 SkMatrix localMatrix = SkMatrix::I();
723 if (reverse) {
724 // draw over the dev bounds (which will be the whole dst surface for inv fill).
725 bounds = devBounds;
726 SkMatrix vmi;
727 // mapRect through persp matrix may not be correct
728 if (!viewMatrix.hasPerspective() && viewMatrix.invert(&vmi)) {
729 vmi.mapRect(&bounds);
730 } else {
731 if (!viewMatrix.invert(&localMatrix)) {
732 return false;
733 }
734 }
735 } else {
736 bounds = path.getBounds();
737 }
738 const SkMatrix& viewM = (reverse && viewMatrix.hasPerspective()) ? SkMatrix::I() :
739 viewMatrix;
740 // This is a non-coverage aa rect op since we assert aaType != kCoverage at the start
741 assert_alive(paint);
742 sdc->stencilRect(clip, passes[p], std::move(paint),
743 GrAA(aaType == GrAAType::kMSAA), viewM, bounds,
744 &localMatrix);
745 } else {
746 bool stencilPass = stencilOnly || passCount > 1;
747 GrOp::Owner op;
748 if (stencilPass) {
749 GrPaint stencilPaint;
750 stencilPaint.setXPFactory(GrDisableColorXPFactory::Get());
751 op = DefaultPathOp::Make(context, std::move(stencilPaint), path, srcSpaceTol,
752 newCoverage, viewMatrix, isHairline, aaType, devBounds,
753 passes[p]);
754 } else {
755 assert_alive(paint);
756 op = DefaultPathOp::Make(context, std::move(paint), path, srcSpaceTol, newCoverage,
757 viewMatrix, isHairline, aaType, devBounds, passes[p]);
758 }
759 sdc->addDrawOp(clip, std::move(op));
760 }
761 }
762 return true;
763 }
764
765 PathRenderer::StencilSupport
onGetStencilSupport(const GrStyledShape & shape) const766 DefaultPathRenderer::onGetStencilSupport(const GrStyledShape& shape) const {
767 if (single_pass_shape(shape)) {
768 return kNoRestriction_StencilSupport;
769 } else {
770 return kStencilOnly_StencilSupport;
771 }
772 }
773
onCanDrawPath(const CanDrawPathArgs & args) const774 PathRenderer::CanDrawPath DefaultPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
775 bool isHairline = GrIsStrokeHairlineOrEquivalent(
776 args.fShape->style(), *args.fViewMatrix, nullptr);
777 // If we aren't a single_pass_shape or hairline, we require stencil buffers.
778 if (!(single_pass_shape(*args.fShape) || isHairline) &&
779 !args.fProxy->canUseStencil(*args.fCaps)) {
780 return CanDrawPath::kNo;
781 }
782 // If antialiasing is required, we only support MSAA.
783 if (GrAAType::kNone != args.fAAType && GrAAType::kMSAA != args.fAAType) {
784 return CanDrawPath::kNo;
785 }
786 // This can draw any path with any simple fill style.
787 if (!args.fShape->style().isSimpleFill() && !isHairline) {
788 return CanDrawPath::kNo;
789 }
790 // Don't try to draw hairlines with DefaultPathRenderer if avoidLineDraws is true.
791 // Alternatively, we could try to implement hairline draws without line primitives in
792 // DefaultPathRenderer, but this is simpler.
793 if (args.fCaps->avoidLineDraws() && isHairline) {
794 return CanDrawPath::kNo;
795 }
796 // This is the fallback renderer for when a path is too complicated for the others to draw.
797 return CanDrawPath::kAsBackup;
798 }
799
onDrawPath(const DrawPathArgs & args)800 bool DefaultPathRenderer::onDrawPath(const DrawPathArgs& args) {
801 GR_AUDIT_TRAIL_AUTO_FRAME(args.fContext->priv().auditTrail(),
802 "DefaultPathRenderer::onDrawPath");
803 GrAAType aaType = (GrAAType::kNone != args.fAAType) ? GrAAType::kMSAA : GrAAType::kNone;
804
805 return this->internalDrawPath(
806 args.fSurfaceDrawContext, std::move(args.fPaint), aaType, *args.fUserStencilSettings,
807 args.fClip, *args.fViewMatrix, *args.fShape, false);
808 }
809
onStencilPath(const StencilPathArgs & args)810 void DefaultPathRenderer::onStencilPath(const StencilPathArgs& args) {
811 GR_AUDIT_TRAIL_AUTO_FRAME(args.fContext->priv().auditTrail(),
812 "DefaultPathRenderer::onStencilPath");
813 SkASSERT(!args.fShape->inverseFilled());
814
815 GrPaint paint;
816 paint.setXPFactory(GrDisableColorXPFactory::Get());
817
818 auto aaType = (GrAA::kYes == args.fDoStencilMSAA) ? GrAAType::kMSAA : GrAAType::kNone;
819
820 this->internalDrawPath(
821 args.fSurfaceDrawContext, std::move(paint), aaType, GrUserStencilSettings::kUnused,
822 args.fClip, *args.fViewMatrix, *args.fShape, true);
823 }
824
825 } // namespace skgpu::ganesh
826