Flutter Engine
The Flutter Engine
DefaultPathRenderer.cpp
Go to the documentation of this file.
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
9
12#include "src/base/SkTLazy.h"
13#include "src/core/SkGeometry.h"
33
34using namespace skia_private;
35
36////////////////////////////////////////////////////////////////////////////////
37// Helpers for drawPath
38
39namespace {
40
41#define STENCIL_OFF 0 // Always disable stencil (even when needed)
42
43inline 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
61class PathGeoBuilder {
62public:
63 PathGeoBuilder(GrPrimitiveType primitiveType,
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
76 ~PathGeoBuilder() {
77 this->createMeshAndPutBackReserve();
78 }
79
80 /**
81 * Path verbs
82 */
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
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
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
126 void addConic(SkScalar weight, const SkPoint pts[], SkScalar srcSpaceTolSqd,
127 SkScalar srcSpaceTol) {
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
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
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) {
165 this->moveTo(pts[0]);
166 break;
168 this->addLine(pts);
169 break;
171 this->addConic(iter.conicWeight(), pts, srcSpaceTolSqd, srcSpaceTol);
172 break;
174 this->addQuad(pts, srcSpaceTolSqd, srcSpaceTol);
175 break;
177 this->addCubic(pts, srcSpaceTolSqd, srcSpaceTol);
178 break;
180 break;
182 done = true;
183 }
184 }
185 }
186
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
203private:
204 /**
205 * Derived properties
206 * TODO: Cache some of these for better performance, rather than re-computing?
207 */
208 bool isIndexed() const {
209 return GrPrimitiveType::kLines == fPrimitiveType ||
210 GrPrimitiveType::kTriangles == fPrimitiveType;
211 }
212 bool isHairline() const {
213 return GrPrimitiveType::kLines == fPrimitiveType ||
214 GrPrimitiveType::kLineStrip == fPrimitiveType;
215 }
216 int indexScale() const {
217 switch (fPrimitiveType) {
219 return 2;
221 return 3;
222 default:
223 return 0;
224 }
225 }
226
227 uint16_t currentIndex() const { return fCurVert - fVertices; }
228
229 // Allocate vertex and (possibly) index buffers
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,
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
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
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
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
372 int fFirstVertex;
373 int fVerticesInChunk;
374 SkPoint* fVertices;
375 SkPoint* fCurVert;
376
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;
387};
388
389class DefaultPathOp final : public GrMeshDrawOp {
390private:
392
393public:
395
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
411 const char* name() const override { return "DefaultPathOp"; }
412
413 void visitProxies(const GrVisitProxyFunc& func) const override {
414 if (fProgramInfo) {
415 fProgramInfo->visitFPProxies(func);
416 } else {
417 fHelper.visitProxies(func);
418 }
419 }
420
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)
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
438 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
439
441 GrClampType clampType) override {
442 GrProcessorAnalysisCoverage gpCoverage =
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
449private:
450 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
459 }
460
462 }
463
464 GrProgramInfo* programInfo() override { return fProgramInfo; }
465
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 {
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);
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
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
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
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)
551 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
562 const SkPMColor4f& color() const { return fColor; }
563 uint8_t coverage() const { return fCoverage; }
564 const SkMatrix& viewMatrix() const { return fViewMatrix; }
565 bool isHairline() const { return fIsHairline; }
566
567 struct PathData {
569 SkScalar fTolerance;
570 };
571
573 Helper fHelper;
574 SkPMColor4f fColor;
575 uint8_t fCoverage;
577 bool fIsHairline;
578
580 GrProgramInfo* fProgramInfo = nullptr;
581
582 using INHERITED = GrMeshDrawOp;
583};
584
585} // anonymous namespace
586
587///////////////////////////////////////////////////////////////////////////////////////////////////
588
589#if defined(GR_TEST_UTILS)
590
591GR_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.
597 const SkPath& path = GrTest::TestPath(random);
598
599 // Compute srcSpaceTol
600 SkRect bounds = path.getBounds();
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
618namespace skgpu::ganesh {
619
620bool 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()) {
669 reverse = true;
670 [[fallthrough]];
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
688 reverse = true;
689 [[fallthrough]];
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
713 SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, path.getBounds());
714
715 SkRect devBounds;
717 viewMatrix, &devBounds);
718
719 for (int p = 0; p < passCount; ++p) {
720 if (lastPassIsBounds && (p == passCount-1)) {
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;
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
766DefaultPathRenderer::onGetStencilSupport(const GrStyledShape& shape) const {
767 if (single_pass_shape(shape)) {
769 } else {
771 }
772}
773
774PathRenderer::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.
798}
799
800bool DefaultPathRenderer::onDrawPath(const DrawPathArgs& args) {
801 GR_AUDIT_TRAIL_AUTO_FRAME(args.fContext->priv().auditTrail(),
802 "DefaultPathRenderer::onDrawPath");
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
810void DefaultPathRenderer::onStencilPath(const StencilPathArgs& args) {
811 GR_AUDIT_TRAIL_AUTO_FRAME(args.fContext->priv().auditTrail(),
812 "DefaultPathRenderer::onStencilPath");
813 SkASSERT(!args.fShape->inverseFilled());
814
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
SkPath fPath
SkMatrix fViewMatrix
GrSimpleMesh * fMeshes
static void done(const char *config, const char *src, const char *srcOptions, const char *name)
Definition: DM.cpp:263
float fCoverage
#define GR_AUDIT_TRAIL_AUTO_FRAME(audit_trail, framename)
Definition: GrAuditTrail.h:167
#define DEFINE_OP_CLASS_ID
Definition: GrOp.h:64
static constexpr GrUserStencilSettings gEOStencilPass(GrUserStencilSettings::StaticInit< 0xffff, GrUserStencilTest::kAlwaysIfInClip, 0xffff, GrUserStencilOp::kInvert, GrUserStencilOp::kKeep, 0xffff >())
static constexpr GrUserStencilSettings gWindColorPass(GrUserStencilSettings::StaticInit< 0x0000, GrUserStencilTest::kLessIfInClip, 0xffff, GrUserStencilOp::kZero, GrUserStencilOp::kZero, 0xffff >())
static constexpr GrUserStencilSettings gInvEOColorPass(GrUserStencilSettings::StaticInit< 0x0000, GrUserStencilTest::kEqualIfInClip, 0xffff, GrUserStencilOp::kZero, GrUserStencilOp::kZero, 0xffff >())
static constexpr GrUserStencilSettings gInvWindColorPass(GrUserStencilSettings::StaticInit< 0x0000, GrUserStencilTest::kEqualIfInClip, 0xffff, GrUserStencilOp::kZero, GrUserStencilOp::kZero, 0xffff >())
static constexpr GrUserStencilSettings gDirectToStencil(GrUserStencilSettings::StaticInit< 0x0000, GrUserStencilTest::kAlwaysIfInClip, 0xffff, GrUserStencilOp::kZero, GrUserStencilOp::kIncMaybeClamp, 0xffff >())
static constexpr GrUserStencilSettings gEOColorPass(GrUserStencilSettings::StaticInit< 0x0000, GrUserStencilTest::kNotEqual, 0xffff, GrUserStencilOp::kZero, GrUserStencilOp::kZero, 0xffff >())
static constexpr GrUserStencilSettings gWindStencilPass(GrUserStencilSettings::StaticInitSeparate< 0xffff, 0xffff, GrUserStencilTest::kAlwaysIfInClip, GrUserStencilTest::kAlwaysIfInClip, 0xffff, 0xffff, GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap, GrUserStencilOp::kKeep, GrUserStencilOp::kKeep, 0xffff, 0xffff >())
GrProcessorAnalysisCoverage
GrClampType
Definition: GrTypesPriv.h:228
std::function< void(GrSurfaceProxy *, skgpu::Mipmapped)> GrVisitProxyFunc
Definition: GrTypesPriv.h:943
GrPrimitiveType
Definition: GrTypesPriv.h:43
GrAAType
Definition: GrTypesPriv.h:200
GrAA
Definition: GrTypesPriv.h:173
GrLoadOp
Definition: GrTypesPriv.h:155
bool GrIsStrokeHairlineOrEquivalent(const GrStyle &style, const SkMatrix &matrix, SkScalar *outCoverage)
Definition: GrUtil.cpp:65
GrXferBarrierFlags
#define SkDEBUGFAIL(message)
Definition: SkAssert.h:118
#define SkASSERT(cond)
Definition: SkAssert.h:116
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
static SkPath clip(const SkPath &path, const SkHalfPlane &plane)
Definition: SkPath.cpp:3892
#define INHERITED(method,...)
Definition: SkRecorder.cpp:128
#define SkScalarRoundToInt(x)
Definition: SkScalar.h:37
SK_API SkString SkStringPrintf(const char *format,...) SK_PRINTF_LIKE(1
Creates a new string and writes into it using a printf()-style format.
static constexpr bool SkToBool(const T &x)
Definition: SkTo.h:35
sk_sp< const GrBuffer > fIndexBuffer
sk_sp< const GrBuffer > fVertexBuffer
Definition: GrCaps.h:57
Definition: GrClip.h:29
static const GrDisableColorXPFactory * Get()
friend class GrSimpleMeshDrawOpHelperWithStencil
Definition: GrDrawOp.h:119
virtual FixedFunctionFlags fixedFunctionFlags() const
Definition: GrDrawOp.h:112
virtual GrProcessorSet::Analysis finalize(const GrCaps &, const GrAppliedClip *, GrClampType)=0
size_t vertexStride() const
virtual GrProgramInfo * programInfo()=0
void createProgramInfo(const GrCaps *caps, SkArenaAlloc *arena, const GrSurfaceProxyView &writeView, bool usesMSAASurface, GrAppliedClip &&appliedClip, const GrDstProxyView &dstProxyView, GrXferBarrierFlags renderPassXferBarriers, GrLoadOp colorLoadOp)
Definition: GrMeshDrawOp.h:39
virtual void onCreateProgramInfo(const GrCaps *, SkArenaAlloc *, const GrSurfaceProxyView &writeView, bool usesMSAASurface, GrAppliedClip &&, const GrDstProxyView &, GrXferBarrierFlags renderPassXferBarriers, GrLoadOp colorLoadOp)=0
virtual void onPrepareDraws(GrMeshDrawTarget *)=0
void drawMesh(const GrSimpleMesh &mesh)
void bindPipelineAndScissorClip(const GrProgramInfo &programInfo, const SkRect &drawBounds)
void bindTextures(const GrGeometryProcessor &geomProc, const GrSurfaceProxy &singleGeomProcTexture, const GrPipeline &pipeline)
Definition: GrOp.h:70
virtual void onExecute(GrOpFlushState *, const SkRect &chainBounds)=0
static Owner Make(GrRecordingContext *context, Args &&... args)
Definition: GrOp.h:75
std::unique_ptr< GrOp > Owner
Definition: GrOp.h:72
virtual const char * name() const =0
const T & cast() const
Definition: GrOp.h:148
virtual void visitProxies(const GrVisitProxyFunc &) const
Definition: GrOp.h:95
void setBounds(const SkRect &newBounds, HasAABloat aabloat, IsHairline zeroArea)
Definition: GrOp.h:279
virtual CombineResult onCombineIfPossible(GrOp *, SkArenaAlloc *, const GrCaps &)
Definition: GrOp.h:305
void setXPFactory(const GrXPFactory *xpFactory)
Definition: GrPaint.h:53
const GrPipeline & pipeline() const
Definition: GrProgramInfo.h:39
const GrGeometryProcessor & geomProc() const
Definition: GrProgramInfo.h:40
void visitFPProxies(const GrVisitProxyFunc &func) const
Definition: GrProgramInfo.h:64
GrProcessorSet::Analysis finalizeProcessors(const GrCaps &caps, const GrAppliedClip *clip, GrClampType clampType, GrProcessorAnalysisCoverage geometryCoverage, GrProcessorAnalysisColor *geometryColor)
void visitProxies(const GrVisitProxyFunc &func) const
GrDrawOp::FixedFunctionFlags fixedFunctionFlags() const
bool isCompatible(const GrSimpleMeshDrawOpHelperWithStencil &that, const GrCaps &, const SkRect &thisBounds, const SkRect &thatBounds, bool ignoreAAType=false) const
GrProgramInfo * createProgramInfoWithStencil(const GrCaps *, SkArenaAlloc *, const GrSurfaceProxyView &writeView, bool usesMSAASurface, GrAppliedClip &&, const GrDstProxyView &, GrGeometryProcessor *, GrPrimitiveType, GrXferBarrierFlags renderPassXferBarriers, GrLoadOp colorLoadOp)
bool isSimpleFill() const
Definition: GrStyle.h:114
void asPath(SkPath *out) const
bool knownToBeConvex() const
bool inverseFilled() const
const GrStyle & style() const
SkISize backingStoreDimensions() const
static bool CheapEqual(const SkMatrix &a, const SkMatrix &b)
Definition: SkMatrixPriv.h:181
bool invert(SkMatrix *inverse) const
Definition: SkMatrix.h:1206
static const SkMatrix & I()
Definition: SkMatrix.cpp:1544
bool hasPerspective() const
Definition: SkMatrix.h:312
bool mapRect(SkRect *dst, const SkRect &src, SkApplyPerspectiveClip pc=SkApplyPerspectiveClip::kYes) const
Definition: SkMatrix.cpp:1141
Definition: SkPath.h:59
@ kClose_Verb
Definition: SkPath.h:1471
@ kMove_Verb
Definition: SkPath.h:1466
@ kConic_Verb
Definition: SkPath.h:1469
@ kDone_Verb
Definition: SkPath.h:1472
@ kCubic_Verb
Definition: SkPath.h:1470
@ kQuad_Verb
Definition: SkPath.h:1468
@ kLine_Verb
Definition: SkPath.h:1467
@ kHairline_InitStyle
Definition: SkStrokeRec.h:25
static void GetPathDevBounds(const SkPath &path, SkISize devSize, const SkMatrix &matrix, SkRect *bounds)
GrRenderTargetProxy * asRenderTargetProxy()
GrRecordingContext * recordingContext() const
void addDrawOp(const GrClip *, GrOp::Owner, const std::function< WillAddOpFn > &=std::function< WillAddOpFn >())
void stencilRect(const GrClip *clip, const GrUserStencilSettings *ss, GrPaint &&paint, GrAA doStencilMSAA, const SkMatrix &viewMatrix, const SkRect &rect, const SkMatrix *localMatrix=nullptr)
const Paint & paint
Definition: color_source.cc:38
DlColor color
float SkScalar
Definition: extension.cpp:12
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
uint32_t * target
GrGeometryProcessor * Make(SkArenaAlloc *, const Color &, const Coverage &, const LocalCoords &, const SkMatrix &viewMatrix)
uint32_t generateCubicPoints(const SkPoint &p0, const SkPoint &p1, const SkPoint &p2, const SkPoint &p3, SkScalar tolSqd, SkPoint **points, uint32_t pointsLeft)
uint32_t quadraticPointCount(const SkPoint points[], SkScalar tol)
Definition: GrPathUtils.cpp:73
static const SkScalar kDefaultTolerance
Definition: GrPathUtils.h:32
uint32_t cubicPointCount(const SkPoint points[], SkScalar tol)
SkScalar scaleToleranceToSrc(SkScalar devTol, const SkMatrix &viewM, const SkRect &pathBounds)
Definition: GrPathUtils.cpp:41
static const int kMaxPointsPerCurve
Definition: GrPathUtils.h:35
uint32_t generateQuadraticPoints(const SkPoint &p0, const SkPoint &p1, const SkPoint &p2, SkScalar tolSqd, SkPoint **points, uint32_t pointsLeft)
Definition: GrPathUtils.cpp:78
SK_API sk_sp< SkDocument > Make(SkWStream *dst, const SkSerialProcs *=nullptr, std::function< void(const SkPicture *)> onEndPage=nullptr)
Optional< SkRect > bounds
Definition: SkRecords.h:189
SkMesh mesh
Definition: SkRecords.h:345
string converter
Definition: cacheimages.py:19
void Helper(uword arg)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
static const GrUserStencilSettings & kUnused