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 | |
14 | class GrAppliedHardClip; |
15 | class GrStencilPathShader; |
16 | class 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. |
21 | class GrPathTessellateOp : public GrDrawOp { |
22 | private: |
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 | |
161 | public: |
162 | // This serves as a base class for benchmarking individual methods on GrPathTessellateOp. |
163 | class TestingOnly_Benchmark; |
164 | }; |
165 | |
166 | #endif |
167 | |