1/*
2 * Copyright 2019 Google LLC.
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#ifndef GrPathTessellateOp_DEFINED
9#define GrPathTessellateOp_DEFINED
10
11#include "src/gpu/ops/GrMeshDrawOp.h"
12#include "src/gpu/tessellate/GrTessellationPathRenderer.h"
13
14class GrAppliedHardClip;
15class GrStencilPathShader;
16class GrResolveLevelCounter;
17
18// Renders paths using a hybrid "Red Book" (stencil, then cover) method. Curves get linearized by
19// either GPU tessellation shaders or indirect draws. This Op doesn't apply analytic AA, so it
20// requires a render target that supports either MSAA or mixed samples if AA is desired.
21class GrPathTessellateOp : public GrDrawOp {
22private:
23 DEFINE_OP_CLASS_ID
24
25 GrPathTessellateOp(const SkMatrix& viewMatrix, const SkPath& path, GrPaint&& paint,
26 GrAAType aaType, GrTessellationPathRenderer::OpFlags opFlags)
27 : GrDrawOp(ClassID())
28 , fOpFlags(opFlags)
29 , fViewMatrix(viewMatrix)
30 , fPath(path)
31 , fAAType(aaType)
32 , fColor(paint.getColor4f())
33 , fProcessors(std::move(paint)) {
34 SkRect devBounds;
35 fViewMatrix.mapRect(&devBounds, path.getBounds());
36 this->setBounds(devBounds, HasAABloat(GrAAType::kCoverage == fAAType), IsHairline::kNo);
37 }
38
39 const char* name() const override { return "GrPathTessellateOp"; }
40 void visitProxies(const VisitProxyFunc& fn) const override { fProcessors.visitProxies(fn); }
41 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
42 bool hasMixedSampledCoverage,
43 GrClampType clampType) override {
44 return fProcessors.finalize(
45 fColor, GrProcessorAnalysisCoverage::kNone, clip, &GrUserStencilSettings::kUnused,
46 hasMixedSampledCoverage, caps, clampType, &fColor);
47 }
48
49 FixedFunctionFlags fixedFunctionFlags() const override;
50 void onPrePrepare(GrRecordingContext*, const GrSurfaceProxyView*, GrAppliedClip*,
51 const GrXferProcessor::DstProxyView&) override;
52 void onPrepare(GrOpFlushState* state) override;
53
54 // Produces a non-overlapping triangulation of the path's inner polygon(s). The inner polygons
55 // connect the endpoints of each verb. (i.e., they are the path that would result from
56 // collapsing all curves to single lines.) If this succeeds, then we will be able to fill the
57 // triangles directly and bypass stencilling them.
58 //
59 // Returns false if the inner triangles do not form a simple polygon (e.g., self intersection,
60 // double winding). Non-simple polygons would need to split edges in order to avoid overlap,
61 // and this is not an option as it would introduce T-junctions with the outer cubics.
62 bool prepareNonOverlappingInnerTriangles(GrMeshDrawOp::Target*, int* numCountedCurves);
63
64 // Produces a "Red Book" style triangulation of the SkPath's inner polygon(s) using a
65 // "middle-out" topology (See GrMiddleOutPolygonTriangulator), and then prepares outer cubics in
66 // the cubic buffer. The inner triangles and outer cubics stencilled together define the
67 // complete path.
68 //
69 // If a resolveLevel counter is provided, this method resets it and uses it to count and
70 // prepares the outer cubics as indirect draws. Otherwise they are prepared as hardware
71 // tessellation patches.
72 //
73 // If drawTrianglesAsIndirectCubicDraw is true, then the resolveLevel counter must be non-null,
74 // and we express the inner triangles as an indirect cubic draw and sneak them in alongside the
75 // other cubic draws.
76 void prepareMiddleOutTrianglesAndCubics(GrMeshDrawOp::Target*, GrResolveLevelCounter* = nullptr,
77 bool drawTrianglesAsIndirectCubicDraw = false);
78
79 // Prepares a list of indirect draw commands and instance data for the path's "outer cubics",
80 // converting any quadratics to cubics. An outer cubic is an independent, 4-point closed contour
81 // consisting of a single cubic curve. Stencilled together with the inner triangles, these
82 // define the complete path.
83 void prepareIndirectOuterCubics(GrMeshDrawOp::Target*, const GrResolveLevelCounter&);
84
85 // For performance reasons we can often express triangles as an indirect cubic draw and sneak
86 // them in alongside the other indirect draws. This prepareIndirectOuterCubics variant allows
87 // the caller to provide a mapped cubic buffer with triangles already written into 4-point
88 // instances at the beginning. If numTrianglesAtBeginningOfData is nonzero, we add an extra
89 // indirect draw that renders these triangles.
90 void prepareIndirectOuterCubicsAndTriangles(GrMeshDrawOp::Target*, const GrResolveLevelCounter&,
91 SkPoint* cubicData,
92 int numTrianglesAtBeginningOfData);
93
94 // Writes an array of "outer cubic" tessellation patches from each bezier in the SkPath,
95 // converting any quadratics to cubics. An outer cubic is an independent, 4-point closed contour
96 // consisting of a single cubic curve. Stencilled together with the inner triangles, these
97 // define the complete path.
98 void prepareTessellatedOuterCubics(GrMeshDrawOp::Target*, int numCountedCurves);
99
100 // Writes an array of cubic "wedges" from the SkPath, converting any lines or quadratics to
101 // cubics. A wedge is an independent, 5-point closed contour consisting of 4 cubic control
102 // points plus an anchor point fanning from the center of the curve's resident contour. Once
103 // stencilled, these wedges alone define the complete path.
104 //
105 // TODO: Eventually we want to use rational cubic wedges in order to support conics.
106 void prepareTessellatedCubicWedges(GrMeshDrawOp::Target*);
107
108 void onExecute(GrOpFlushState*, const SkRect& chainBounds) override;
109 void drawStencilPass(GrOpFlushState*);
110 void drawCoverPass(GrOpFlushState*);
111
112 const GrTessellationPathRenderer::OpFlags fOpFlags;
113 const SkMatrix fViewMatrix;
114 const SkPath fPath;
115 const GrAAType fAAType;
116 SkPMColor4f fColor;
117 GrProcessorSet fProcessors;
118
119 sk_sp<const GrBuffer> fTriangleBuffer;
120 int fBaseTriangleVertex;
121 int fTriangleVertexCount;
122
123 // These switches specify how the above fTriangleBuffer should be drawn (if at all).
124 //
125 // If stencil=true and fill=false:
126 //
127 // We just stencil the triangles (with cubics) during the stencil step. The path gets filled
128 // later using a simple bounding box if needed.
129 //
130 // If stencil=true and fill=true:
131 //
132 // We still stencil the triangles and cubics normally, but during the *fill* step we fill
133 // the triangles plus local convex hulls around each cubic instead of a bounding box.
134 //
135 // If stencil=false and fill=true:
136 //
137 // This means that fTriangleBuffer contains non-overlapping geometry that can be filled
138 // directly to the final render target. We only need to stencil *curves*, and during the
139 // fill step we draw the triangles directly with a stencil test that accounts for curves
140 // (see drawCoverPass()), and then finally fill the curves with local convex hulls.
141 bool fDoStencilTriangleBuffer = false;
142 bool fDoFillTriangleBuffer = false;
143
144 // The cubic buffer defines either standalone cubics or wedges. These are stencilled by
145 // tessellation shaders, and may also be used do fill local convex hulls around each cubic.
146 sk_sp<const GrBuffer> fCubicBuffer;
147 int fBaseCubicVertex;
148 int fCubicVertexCount;
149 GrStencilPathShader* fStencilCubicsShader = nullptr;
150
151 // If fIndirectDrawBuffer is non-null, then we issue an indexed-indirect draw instead of using
152 // hardware tessellation. This is oftentimes faster than tessellation, and other times it serves
153 // as a polyfill when tessellation just isn't supported.
154 sk_sp<const GrBuffer> fIndirectDrawBuffer;
155 size_t fIndirectDrawOffset;
156 int fIndirectDrawCount;
157 sk_sp<const GrBuffer> fIndirectIndexBuffer;
158
159 friend class GrOpMemoryPool; // For ctor.
160
161public:
162 // This serves as a base class for benchmarking individual methods on GrPathTessellateOp.
163 class TestingOnly_Benchmark;
164};
165
166#endif
167