| 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 | |