Chris Dalton | d9bdc32 | 2021-06-01 19:22:05 -0600 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2021 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 | #include "src/gpu/tessellate/GrPathWedgeTessellator.h" |
| 9 | |
Robert Phillips | 06273bc | 2021-08-11 15:43:50 -0400 | [diff] [blame] | 10 | #include "src/gpu/GrMeshDrawTarget.h" |
Robert Phillips | 1a82a4e | 2021-07-01 10:27:44 -0400 | [diff] [blame] | 11 | #include "src/gpu/GrResourceProvider.h" |
Chris Dalton | d9bdc32 | 2021-06-01 19:22:05 -0600 | [diff] [blame] | 12 | #include "src/gpu/geometry/GrPathUtils.h" |
| 13 | #include "src/gpu/geometry/GrWangsFormula.h" |
| 14 | #include "src/gpu/tessellate/GrCullTest.h" |
Chris Dalton | 6966981 | 2021-07-27 10:00:12 -0600 | [diff] [blame] | 15 | #include "src/gpu/tessellate/GrPathXform.h" |
Chris Dalton | d9bdc32 | 2021-06-01 19:22:05 -0600 | [diff] [blame] | 16 | #include "src/gpu/tessellate/shaders/GrPathTessellationShader.h" |
| 17 | |
| 18 | namespace { |
| 19 | |
Chris Dalton | e1f7237 | 2021-06-29 16:45:49 -0600 | [diff] [blame] | 20 | constexpr static float kPrecision = GrTessellationShader::kLinearizationPrecision; |
Chris Dalton | d9bdc32 | 2021-06-01 19:22:05 -0600 | [diff] [blame] | 21 | |
| 22 | // Parses out each contour in a path and tracks the midpoint. Example usage: |
| 23 | // |
| 24 | // SkTPathContourParser parser; |
| 25 | // while (parser.parseNextContour()) { |
| 26 | // SkPoint midpoint = parser.currentMidpoint(); |
| 27 | // for (auto [verb, pts] : parser.currentContour()) { |
| 28 | // ... |
| 29 | // } |
| 30 | // } |
| 31 | // |
| 32 | class MidpointContourParser { |
| 33 | public: |
| 34 | MidpointContourParser(const SkPath& path) |
| 35 | : fPath(path) |
| 36 | , fVerbs(SkPathPriv::VerbData(fPath)) |
| 37 | , fNumRemainingVerbs(fPath.countVerbs()) |
| 38 | , fPoints(SkPathPriv::PointData(fPath)) |
| 39 | , fWeights(SkPathPriv::ConicWeightData(fPath)) {} |
| 40 | // Advances the internal state to the next contour in the path. Returns false if there are no |
| 41 | // more contours. |
| 42 | bool parseNextContour() { |
| 43 | bool hasGeometry = false; |
| 44 | for (; fVerbsIdx < fNumRemainingVerbs; ++fVerbsIdx) { |
| 45 | switch (fVerbs[fVerbsIdx]) { |
| 46 | case SkPath::kMove_Verb: |
| 47 | if (!hasGeometry) { |
| 48 | fMidpoint = fPoints[fPtsIdx]; |
| 49 | fMidpointWeight = 1; |
| 50 | this->advance(); |
| 51 | ++fPtsIdx; |
| 52 | continue; |
| 53 | } |
| 54 | return true; |
| 55 | default: |
| 56 | continue; |
| 57 | case SkPath::kLine_Verb: |
| 58 | ++fPtsIdx; |
| 59 | break; |
| 60 | case SkPath::kConic_Verb: |
| 61 | ++fWtsIdx; |
| 62 | [[fallthrough]]; |
| 63 | case SkPath::kQuad_Verb: |
| 64 | fPtsIdx += 2; |
| 65 | break; |
| 66 | case SkPath::kCubic_Verb: |
| 67 | fPtsIdx += 3; |
| 68 | break; |
| 69 | } |
| 70 | fMidpoint += fPoints[fPtsIdx - 1]; |
| 71 | ++fMidpointWeight; |
| 72 | hasGeometry = true; |
| 73 | } |
| 74 | return hasGeometry; |
| 75 | } |
| 76 | |
| 77 | // Allows for iterating the current contour using a range-for loop. |
| 78 | SkPathPriv::Iterate currentContour() { |
| 79 | return SkPathPriv::Iterate(fVerbs, fVerbs + fVerbsIdx, fPoints, fWeights); |
| 80 | } |
| 81 | |
| 82 | SkPoint currentMidpoint() { return fMidpoint * (1.f / fMidpointWeight); } |
| 83 | |
| 84 | private: |
| 85 | void advance() { |
| 86 | fVerbs += fVerbsIdx; |
| 87 | fNumRemainingVerbs -= fVerbsIdx; |
| 88 | fVerbsIdx = 0; |
| 89 | fPoints += fPtsIdx; |
| 90 | fPtsIdx = 0; |
| 91 | fWeights += fWtsIdx; |
| 92 | fWtsIdx = 0; |
| 93 | } |
| 94 | |
| 95 | const SkPath& fPath; |
| 96 | |
| 97 | const uint8_t* fVerbs; |
| 98 | int fNumRemainingVerbs = 0; |
| 99 | int fVerbsIdx = 0; |
| 100 | |
| 101 | const SkPoint* fPoints; |
| 102 | int fPtsIdx = 0; |
| 103 | |
| 104 | const float* fWeights; |
| 105 | int fWtsIdx = 0; |
| 106 | |
| 107 | SkPoint fMidpoint; |
| 108 | int fMidpointWeight; |
| 109 | }; |
| 110 | |
| 111 | // Writes out wedge patches, chopping as necessary so none require more segments than are supported |
| 112 | // by the hardware. |
| 113 | class WedgeWriter { |
| 114 | public: |
Chris Dalton | 17eaf62 | 2021-07-28 09:43:52 -0600 | [diff] [blame] | 115 | WedgeWriter(GrMeshDrawTarget* target, |
| 116 | GrVertexChunkArray* vertexChunkArray, |
| 117 | size_t patchStride, |
| 118 | int initialPatchAllocCount, |
Chris Dalton | 6966981 | 2021-07-27 10:00:12 -0600 | [diff] [blame] | 119 | int maxSegments) |
Chris Dalton | 17eaf62 | 2021-07-28 09:43:52 -0600 | [diff] [blame] | 120 | : fChunker(target, vertexChunkArray, patchStride, initialPatchAllocCount) |
Chris Dalton | d2b8ba3 | 2021-06-09 00:12:59 -0600 | [diff] [blame] | 121 | , fMaxSegments_pow2(maxSegments * maxSegments) |
| 122 | , fMaxSegments_pow4(fMaxSegments_pow2 * fMaxSegments_pow2) { |
Chris Dalton | d9bdc32 | 2021-06-01 19:22:05 -0600 | [diff] [blame] | 123 | } |
| 124 | |
Chris Dalton | 17eaf62 | 2021-07-28 09:43:52 -0600 | [diff] [blame] | 125 | void setMatrices(const SkRect& cullBounds, |
| 126 | const SkMatrix& shaderMatrix, |
| 127 | const SkMatrix& pathMatrix) { |
| 128 | SkMatrix totalMatrix; |
| 129 | totalMatrix.setConcat(shaderMatrix, pathMatrix); |
| 130 | fCullTest.set(cullBounds, totalMatrix); |
| 131 | fTotalVectorXform = totalMatrix; |
| 132 | fPathXform = pathMatrix; |
| 133 | } |
| 134 | |
Chris Dalton | 6966981 | 2021-07-27 10:00:12 -0600 | [diff] [blame] | 135 | const GrPathXform& pathXform() const { return fPathXform; } |
| 136 | |
Chris Dalton | 50516f3 | 2021-07-18 22:26:27 -0600 | [diff] [blame] | 137 | SK_ALWAYS_INLINE void writeFlatWedge(const GrShaderCaps& shaderCaps, |
Chris Dalton | 17eaf62 | 2021-07-28 09:43:52 -0600 | [diff] [blame] | 138 | SkPoint p0, |
| 139 | SkPoint p1, |
Chris Dalton | d9bdc32 | 2021-06-01 19:22:05 -0600 | [diff] [blame] | 140 | SkPoint midpoint) { |
Chris Dalton | 17eaf62 | 2021-07-28 09:43:52 -0600 | [diff] [blame] | 141 | if (GrVertexWriter vertexWriter = fChunker.appendVertex()) { |
Chris Dalton | 6966981 | 2021-07-27 10:00:12 -0600 | [diff] [blame] | 142 | fPathXform.mapLineToCubic(&vertexWriter, p0, p1); |
Chris Dalton | d9bdc32 | 2021-06-01 19:22:05 -0600 | [diff] [blame] | 143 | vertexWriter.write(midpoint); |
Chris Dalton | 50516f3 | 2021-07-18 22:26:27 -0600 | [diff] [blame] | 144 | vertexWriter.write(GrVertexWriter::If(!shaderCaps.infinitySupport(), |
| 145 | GrTessellationShader::kCubicCurveType)); |
Chris Dalton | d9bdc32 | 2021-06-01 19:22:05 -0600 | [diff] [blame] | 146 | } |
| 147 | } |
| 148 | |
Chris Dalton | 50516f3 | 2021-07-18 22:26:27 -0600 | [diff] [blame] | 149 | SK_ALWAYS_INLINE void writeQuadraticWedge(const GrShaderCaps& shaderCaps, |
Chris Dalton | 17eaf62 | 2021-07-28 09:43:52 -0600 | [diff] [blame] | 150 | const SkPoint p[3], |
Chris Dalton | d9bdc32 | 2021-06-01 19:22:05 -0600 | [diff] [blame] | 151 | SkPoint midpoint) { |
Chris Dalton | 6966981 | 2021-07-27 10:00:12 -0600 | [diff] [blame] | 152 | float numSegments_pow4 = GrWangsFormula::quadratic_pow4(kPrecision, p, fTotalVectorXform); |
Chris Dalton | d2b8ba3 | 2021-06-09 00:12:59 -0600 | [diff] [blame] | 153 | if (numSegments_pow4 > fMaxSegments_pow4) { |
Chris Dalton | 17eaf62 | 2021-07-28 09:43:52 -0600 | [diff] [blame] | 154 | this->chopAndWriteQuadraticWedges(shaderCaps, p, midpoint); |
Chris Dalton | d9bdc32 | 2021-06-01 19:22:05 -0600 | [diff] [blame] | 155 | return; |
| 156 | } |
Chris Dalton | 17eaf62 | 2021-07-28 09:43:52 -0600 | [diff] [blame] | 157 | if (GrVertexWriter vertexWriter = fChunker.appendVertex()) { |
Chris Dalton | 6966981 | 2021-07-27 10:00:12 -0600 | [diff] [blame] | 158 | fPathXform.mapQuadToCubic(&vertexWriter, p); |
Chris Dalton | d9bdc32 | 2021-06-01 19:22:05 -0600 | [diff] [blame] | 159 | vertexWriter.write(midpoint); |
Chris Dalton | 50516f3 | 2021-07-18 22:26:27 -0600 | [diff] [blame] | 160 | vertexWriter.write(GrVertexWriter::If(!shaderCaps.infinitySupport(), |
| 161 | GrTessellationShader::kCubicCurveType)); |
Chris Dalton | d9bdc32 | 2021-06-01 19:22:05 -0600 | [diff] [blame] | 162 | } |
Chris Dalton | d2b8ba3 | 2021-06-09 00:12:59 -0600 | [diff] [blame] | 163 | fNumFixedSegments_pow4 = std::max(numSegments_pow4, fNumFixedSegments_pow4); |
Chris Dalton | d9bdc32 | 2021-06-01 19:22:05 -0600 | [diff] [blame] | 164 | } |
| 165 | |
Chris Dalton | 50516f3 | 2021-07-18 22:26:27 -0600 | [diff] [blame] | 166 | SK_ALWAYS_INLINE void writeConicWedge(const GrShaderCaps& shaderCaps, |
Chris Dalton | 17eaf62 | 2021-07-28 09:43:52 -0600 | [diff] [blame] | 167 | const SkPoint p[3], |
| 168 | float w, |
| 169 | SkPoint midpoint) { |
Chris Dalton | 6966981 | 2021-07-27 10:00:12 -0600 | [diff] [blame] | 170 | float numSegments_pow2 = GrWangsFormula::conic_pow2(kPrecision, p, w, fTotalVectorXform); |
| 171 | if (numSegments_pow2 > fMaxSegments_pow2) { |
Chris Dalton | 17eaf62 | 2021-07-28 09:43:52 -0600 | [diff] [blame] | 172 | this->chopAndWriteConicWedges(shaderCaps, {p, w}, midpoint); |
Chris Dalton | d9bdc32 | 2021-06-01 19:22:05 -0600 | [diff] [blame] | 173 | return; |
| 174 | } |
Chris Dalton | 17eaf62 | 2021-07-28 09:43:52 -0600 | [diff] [blame] | 175 | if (GrVertexWriter vertexWriter = fChunker.appendVertex()) { |
Chris Dalton | 6966981 | 2021-07-27 10:00:12 -0600 | [diff] [blame] | 176 | fPathXform.mapConicToPatch(&vertexWriter, p, w); |
Chris Dalton | d9bdc32 | 2021-06-01 19:22:05 -0600 | [diff] [blame] | 177 | vertexWriter.write(midpoint); |
Chris Dalton | 50516f3 | 2021-07-18 22:26:27 -0600 | [diff] [blame] | 178 | vertexWriter.write(GrVertexWriter::If(!shaderCaps.infinitySupport(), |
| 179 | GrTessellationShader::kConicCurveType)); |
Chris Dalton | d9bdc32 | 2021-06-01 19:22:05 -0600 | [diff] [blame] | 180 | } |
Chris Dalton | d2b8ba3 | 2021-06-09 00:12:59 -0600 | [diff] [blame] | 181 | fNumFixedSegments_pow4 = std::max(numSegments_pow2 * numSegments_pow2, |
| 182 | fNumFixedSegments_pow4); |
Chris Dalton | d9bdc32 | 2021-06-01 19:22:05 -0600 | [diff] [blame] | 183 | } |
| 184 | |
Chris Dalton | 50516f3 | 2021-07-18 22:26:27 -0600 | [diff] [blame] | 185 | SK_ALWAYS_INLINE void writeCubicWedge(const GrShaderCaps& shaderCaps, |
Chris Dalton | 17eaf62 | 2021-07-28 09:43:52 -0600 | [diff] [blame] | 186 | const SkPoint p[4], |
Chris Dalton | d9bdc32 | 2021-06-01 19:22:05 -0600 | [diff] [blame] | 187 | SkPoint midpoint) { |
Chris Dalton | 6966981 | 2021-07-27 10:00:12 -0600 | [diff] [blame] | 188 | float numSegments_pow4 = GrWangsFormula::cubic_pow4(kPrecision, p, fTotalVectorXform); |
Chris Dalton | d2b8ba3 | 2021-06-09 00:12:59 -0600 | [diff] [blame] | 189 | if (numSegments_pow4 > fMaxSegments_pow4) { |
Chris Dalton | 17eaf62 | 2021-07-28 09:43:52 -0600 | [diff] [blame] | 190 | this->chopAndWriteCubicWedges(shaderCaps, p, midpoint); |
Chris Dalton | d9bdc32 | 2021-06-01 19:22:05 -0600 | [diff] [blame] | 191 | return; |
| 192 | } |
Chris Dalton | 17eaf62 | 2021-07-28 09:43:52 -0600 | [diff] [blame] | 193 | if (GrVertexWriter vertexWriter = fChunker.appendVertex()) { |
Chris Dalton | 6966981 | 2021-07-27 10:00:12 -0600 | [diff] [blame] | 194 | fPathXform.map4Points(&vertexWriter, p); |
Chris Dalton | d9bdc32 | 2021-06-01 19:22:05 -0600 | [diff] [blame] | 195 | vertexWriter.write(midpoint); |
Chris Dalton | 50516f3 | 2021-07-18 22:26:27 -0600 | [diff] [blame] | 196 | vertexWriter.write(GrVertexWriter::If(!shaderCaps.infinitySupport(), |
| 197 | GrTessellationShader::kCubicCurveType)); |
Chris Dalton | d9bdc32 | 2021-06-01 19:22:05 -0600 | [diff] [blame] | 198 | } |
Chris Dalton | d2b8ba3 | 2021-06-09 00:12:59 -0600 | [diff] [blame] | 199 | fNumFixedSegments_pow4 = std::max(numSegments_pow4, fNumFixedSegments_pow4); |
Chris Dalton | d9bdc32 | 2021-06-01 19:22:05 -0600 | [diff] [blame] | 200 | } |
| 201 | |
Chris Dalton | d2b8ba3 | 2021-06-09 00:12:59 -0600 | [diff] [blame] | 202 | int numFixedSegments_pow4() const { return fNumFixedSegments_pow4; } |
| 203 | |
Chris Dalton | d9bdc32 | 2021-06-01 19:22:05 -0600 | [diff] [blame] | 204 | private: |
Chris Dalton | 17eaf62 | 2021-07-28 09:43:52 -0600 | [diff] [blame] | 205 | void chopAndWriteQuadraticWedges(const GrShaderCaps& shaderCaps, |
| 206 | const SkPoint p[3], |
| 207 | SkPoint midpoint) { |
Chris Dalton | d9bdc32 | 2021-06-01 19:22:05 -0600 | [diff] [blame] | 208 | SkPoint chops[5]; |
| 209 | SkChopQuadAtHalf(p, chops); |
| 210 | for (int i = 0; i < 2; ++i) { |
| 211 | const SkPoint* q = chops + i*2; |
| 212 | if (fCullTest.areVisible3(q)) { |
Chris Dalton | 17eaf62 | 2021-07-28 09:43:52 -0600 | [diff] [blame] | 213 | this->writeQuadraticWedge(shaderCaps, q, midpoint); |
Chris Dalton | d9bdc32 | 2021-06-01 19:22:05 -0600 | [diff] [blame] | 214 | } else { |
Chris Dalton | 17eaf62 | 2021-07-28 09:43:52 -0600 | [diff] [blame] | 215 | this->writeFlatWedge(shaderCaps, q[0], q[2], midpoint); |
Chris Dalton | d9bdc32 | 2021-06-01 19:22:05 -0600 | [diff] [blame] | 216 | } |
| 217 | } |
| 218 | } |
| 219 | |
Chris Dalton | 17eaf62 | 2021-07-28 09:43:52 -0600 | [diff] [blame] | 220 | void chopAndWriteConicWedges(const GrShaderCaps& shaderCaps, |
| 221 | const SkConic& conic, |
| 222 | SkPoint midpoint) { |
Chris Dalton | d9bdc32 | 2021-06-01 19:22:05 -0600 | [diff] [blame] | 223 | SkConic chops[2]; |
| 224 | if (!conic.chopAt(.5, chops)) { |
| 225 | return; |
| 226 | } |
| 227 | for (int i = 0; i < 2; ++i) { |
| 228 | if (fCullTest.areVisible3(chops[i].fPts)) { |
Chris Dalton | 17eaf62 | 2021-07-28 09:43:52 -0600 | [diff] [blame] | 229 | this->writeConicWedge(shaderCaps, chops[i].fPts, chops[i].fW, midpoint); |
Chris Dalton | d9bdc32 | 2021-06-01 19:22:05 -0600 | [diff] [blame] | 230 | } else { |
Chris Dalton | 17eaf62 | 2021-07-28 09:43:52 -0600 | [diff] [blame] | 231 | this->writeFlatWedge(shaderCaps, chops[i].fPts[0], chops[i].fPts[2], midpoint); |
Chris Dalton | d9bdc32 | 2021-06-01 19:22:05 -0600 | [diff] [blame] | 232 | } |
| 233 | } |
| 234 | } |
| 235 | |
Chris Dalton | 17eaf62 | 2021-07-28 09:43:52 -0600 | [diff] [blame] | 236 | void chopAndWriteCubicWedges(const GrShaderCaps& shaderCaps, |
| 237 | const SkPoint p[4], |
| 238 | SkPoint midpoint) { |
Chris Dalton | d9bdc32 | 2021-06-01 19:22:05 -0600 | [diff] [blame] | 239 | SkPoint chops[7]; |
| 240 | SkChopCubicAtHalf(p, chops); |
| 241 | for (int i = 0; i < 2; ++i) { |
| 242 | const SkPoint* c = chops + i*3; |
| 243 | if (fCullTest.areVisible4(c)) { |
Chris Dalton | 17eaf62 | 2021-07-28 09:43:52 -0600 | [diff] [blame] | 244 | this->writeCubicWedge(shaderCaps, c, midpoint); |
Chris Dalton | d9bdc32 | 2021-06-01 19:22:05 -0600 | [diff] [blame] | 245 | } else { |
Chris Dalton | 17eaf62 | 2021-07-28 09:43:52 -0600 | [diff] [blame] | 246 | this->writeFlatWedge(shaderCaps, c[0], c[3], midpoint); |
Chris Dalton | d9bdc32 | 2021-06-01 19:22:05 -0600 | [diff] [blame] | 247 | } |
| 248 | } |
| 249 | } |
| 250 | |
Chris Dalton | 17eaf62 | 2021-07-28 09:43:52 -0600 | [diff] [blame] | 251 | GrVertexChunkBuilder fChunker; |
Chris Dalton | d9bdc32 | 2021-06-01 19:22:05 -0600 | [diff] [blame] | 252 | GrCullTest fCullTest; |
Chris Dalton | 6966981 | 2021-07-27 10:00:12 -0600 | [diff] [blame] | 253 | GrVectorXform fTotalVectorXform; |
| 254 | GrPathXform fPathXform; |
Chris Dalton | d2b8ba3 | 2021-06-09 00:12:59 -0600 | [diff] [blame] | 255 | const float fMaxSegments_pow2; |
| 256 | const float fMaxSegments_pow4; |
| 257 | |
| 258 | // If using fixed count, this is the max number of curve segments we need to draw per instance. |
| 259 | float fNumFixedSegments_pow4 = 1; |
Chris Dalton | d9bdc32 | 2021-06-01 19:22:05 -0600 | [diff] [blame] | 260 | }; |
| 261 | |
| 262 | } // namespace |
| 263 | |
| 264 | |
| 265 | GrPathTessellator* GrPathWedgeTessellator::Make(SkArenaAlloc* arena, const SkMatrix& viewMatrix, |
Chris Dalton | d2b8ba3 | 2021-06-09 00:12:59 -0600 | [diff] [blame] | 266 | const SkPMColor4f& color, int numPathVerbs, |
Chris Dalton | 198ac15 | 2021-06-09 13:49:43 -0600 | [diff] [blame] | 267 | const GrPipeline& pipeline, const GrCaps& caps) { |
Chris Dalton | d2b8ba3 | 2021-06-09 00:12:59 -0600 | [diff] [blame] | 268 | using PatchType = GrPathTessellationShader::PatchType; |
| 269 | GrPathTessellationShader* shader; |
| 270 | if (caps.shaderCaps()->tessellationSupport() && |
Chris Dalton | 50516f3 | 2021-07-18 22:26:27 -0600 | [diff] [blame] | 271 | caps.shaderCaps()->infinitySupport() && // The hw tessellation shaders use infinity. |
Brian Salomon | 66b500a | 2021-08-02 12:37:14 -0400 | [diff] [blame] | 272 | !pipeline.usesLocalCoords() && // Our tessellation back door doesn't handle varyings. |
Chris Dalton | d2b8ba3 | 2021-06-09 00:12:59 -0600 | [diff] [blame] | 273 | numPathVerbs >= caps.minPathVerbsForHwTessellation()) { |
| 274 | shader = GrPathTessellationShader::MakeHardwareTessellationShader(arena, viewMatrix, color, |
| 275 | PatchType::kWedges); |
| 276 | } else { |
Chris Dalton | 50516f3 | 2021-07-18 22:26:27 -0600 | [diff] [blame] | 277 | shader = GrPathTessellationShader::MakeMiddleOutFixedCountShader(*caps.shaderCaps(), arena, |
| 278 | viewMatrix, color, |
Chris Dalton | d2b8ba3 | 2021-06-09 00:12:59 -0600 | [diff] [blame] | 279 | PatchType::kWedges); |
| 280 | } |
| 281 | return arena->make([=](void* objStart) { |
| 282 | return new(objStart) GrPathWedgeTessellator(shader); |
| 283 | }); |
Chris Dalton | d9bdc32 | 2021-06-01 19:22:05 -0600 | [diff] [blame] | 284 | } |
| 285 | |
Chris Dalton | e1f7237 | 2021-06-29 16:45:49 -0600 | [diff] [blame] | 286 | GR_DECLARE_STATIC_UNIQUE_KEY(gFixedCountVertexBufferKey); |
| 287 | GR_DECLARE_STATIC_UNIQUE_KEY(gFixedCountIndexBufferKey); |
| 288 | |
Chris Dalton | 6966981 | 2021-07-27 10:00:12 -0600 | [diff] [blame] | 289 | void GrPathWedgeTessellator::prepare(GrMeshDrawTarget* target, |
| 290 | const SkRect& cullBounds, |
Chris Dalton | 17eaf62 | 2021-07-28 09:43:52 -0600 | [diff] [blame] | 291 | const PathDrawList& pathDrawList, |
| 292 | int totalCombinedPathVerbCnt) { |
Chris Dalton | d9bdc32 | 2021-06-01 19:22:05 -0600 | [diff] [blame] | 293 | SkASSERT(fVertexChunkArray.empty()); |
| 294 | |
Chris Dalton | 50516f3 | 2021-07-18 22:26:27 -0600 | [diff] [blame] | 295 | const GrShaderCaps& shaderCaps = *target->caps().shaderCaps(); |
| 296 | |
Chris Dalton | d9bdc32 | 2021-06-01 19:22:05 -0600 | [diff] [blame] | 297 | // Over-allocate enough wedges for 1 in 4 to chop. |
Chris Dalton | 17eaf62 | 2021-07-28 09:43:52 -0600 | [diff] [blame] | 298 | int maxWedges = MaxCombinedFanEdgesInPathDrawList(totalCombinedPathVerbCnt); |
Chris Dalton | d9bdc32 | 2021-06-01 19:22:05 -0600 | [diff] [blame] | 299 | int wedgeAllocCount = (maxWedges * 5 + 3) / 4; // i.e., ceil(maxWedges * 5/4) |
| 300 | if (!wedgeAllocCount) { |
| 301 | return; |
| 302 | } |
Chris Dalton | 50516f3 | 2021-07-18 22:26:27 -0600 | [diff] [blame] | 303 | size_t patchStride = fShader->willUseTessellationShaders() ? fShader->vertexStride() * 5 |
| 304 | : fShader->instanceStride(); |
Chris Dalton | d9bdc32 | 2021-06-01 19:22:05 -0600 | [diff] [blame] | 305 | |
Chris Dalton | d2b8ba3 | 2021-06-09 00:12:59 -0600 | [diff] [blame] | 306 | int maxSegments; |
| 307 | if (fShader->willUseTessellationShaders()) { |
Chris Dalton | 50516f3 | 2021-07-18 22:26:27 -0600 | [diff] [blame] | 308 | maxSegments = shaderCaps.maxTessellationSegments(); |
Chris Dalton | d2b8ba3 | 2021-06-09 00:12:59 -0600 | [diff] [blame] | 309 | } else { |
Chris Dalton | e1f7237 | 2021-06-29 16:45:49 -0600 | [diff] [blame] | 310 | maxSegments = GrPathTessellationShader::kMaxFixedCountSegments; |
Chris Dalton | d2b8ba3 | 2021-06-09 00:12:59 -0600 | [diff] [blame] | 311 | } |
| 312 | |
Chris Dalton | 17eaf62 | 2021-07-28 09:43:52 -0600 | [diff] [blame] | 313 | WedgeWriter wedgeWriter(target, &fVertexChunkArray, patchStride, wedgeAllocCount, maxSegments); |
| 314 | for (auto [pathMatrix, path] : pathDrawList) { |
| 315 | wedgeWriter.setMatrices(cullBounds, fShader->viewMatrix(), pathMatrix); |
| 316 | MidpointContourParser parser(path); |
| 317 | while (parser.parseNextContour()) { |
| 318 | SkPoint midpoint = wedgeWriter.pathXform().mapPoint(parser.currentMidpoint()); |
| 319 | SkPoint startPoint = {0, 0}; |
| 320 | SkPoint lastPoint = startPoint; |
| 321 | for (auto [verb, pts, w] : parser.currentContour()) { |
| 322 | switch (verb) { |
| 323 | case SkPathVerb::kMove: |
| 324 | startPoint = lastPoint = pts[0]; |
| 325 | break; |
| 326 | case SkPathVerb::kClose: |
| 327 | break; // Ignore. We can assume an implicit close at the end. |
| 328 | case SkPathVerb::kLine: |
| 329 | wedgeWriter.writeFlatWedge(shaderCaps, pts[0], pts[1], midpoint); |
| 330 | lastPoint = pts[1]; |
| 331 | break; |
| 332 | case SkPathVerb::kQuad: |
| 333 | wedgeWriter.writeQuadraticWedge(shaderCaps, pts, midpoint); |
| 334 | lastPoint = pts[2]; |
| 335 | break; |
| 336 | case SkPathVerb::kConic: |
| 337 | wedgeWriter.writeConicWedge(shaderCaps, pts, *w, midpoint); |
| 338 | lastPoint = pts[2]; |
| 339 | break; |
| 340 | case SkPathVerb::kCubic: |
| 341 | wedgeWriter.writeCubicWedge(shaderCaps, pts, midpoint); |
| 342 | lastPoint = pts[3]; |
| 343 | break; |
| 344 | } |
Chris Dalton | d9bdc32 | 2021-06-01 19:22:05 -0600 | [diff] [blame] | 345 | } |
Chris Dalton | 17eaf62 | 2021-07-28 09:43:52 -0600 | [diff] [blame] | 346 | if (lastPoint != startPoint) { |
| 347 | wedgeWriter.writeFlatWedge(shaderCaps, lastPoint, startPoint, midpoint); |
| 348 | } |
Chris Dalton | d9bdc32 | 2021-06-01 19:22:05 -0600 | [diff] [blame] | 349 | } |
| 350 | } |
Chris Dalton | d2b8ba3 | 2021-06-09 00:12:59 -0600 | [diff] [blame] | 351 | |
| 352 | if (!fShader->willUseTessellationShaders()) { |
| 353 | // log2(n) == log16(n^4). |
| 354 | int fixedResolveLevel = GrWangsFormula::nextlog16(wedgeWriter.numFixedSegments_pow4()); |
| 355 | int numCurveTriangles = |
| 356 | GrPathTessellationShader::NumCurveTrianglesAtResolveLevel(fixedResolveLevel); |
| 357 | // Emit 3 vertices per curve triangle, plus 3 more for the fan triangle. |
Chris Dalton | e1f7237 | 2021-06-29 16:45:49 -0600 | [diff] [blame] | 358 | fFixedIndexCount = numCurveTriangles * 3 + 3; |
| 359 | |
| 360 | GR_DEFINE_STATIC_UNIQUE_KEY(gFixedCountVertexBufferKey); |
| 361 | |
| 362 | fFixedCountVertexBuffer = target->resourceProvider()->findOrMakeStaticBuffer( |
| 363 | GrGpuBufferType::kVertex, |
| 364 | GrPathTessellationShader::SizeOfVertexBufferForMiddleOutWedges(), |
| 365 | gFixedCountVertexBufferKey, |
| 366 | GrPathTessellationShader::InitializeVertexBufferForMiddleOutWedges); |
| 367 | |
| 368 | GR_DEFINE_STATIC_UNIQUE_KEY(gFixedCountIndexBufferKey); |
| 369 | |
| 370 | fFixedCountIndexBuffer = target->resourceProvider()->findOrMakeStaticBuffer( |
| 371 | GrGpuBufferType::kIndex, |
| 372 | GrPathTessellationShader::SizeOfIndexBufferForMiddleOutWedges(), |
| 373 | gFixedCountIndexBufferKey, |
| 374 | GrPathTessellationShader::InitializeIndexBufferForMiddleOutWedges); |
Chris Dalton | d2b8ba3 | 2021-06-09 00:12:59 -0600 | [diff] [blame] | 375 | } |
Chris Dalton | d9bdc32 | 2021-06-01 19:22:05 -0600 | [diff] [blame] | 376 | } |
| 377 | |
Robert Phillips | f0d21cf | 2021-08-13 10:26:37 -0400 | [diff] [blame] | 378 | #if SK_GPU_V1 |
| 379 | #include "src/gpu/GrOpFlushState.h" |
| 380 | |
Chris Dalton | d9bdc32 | 2021-06-01 19:22:05 -0600 | [diff] [blame] | 381 | void GrPathWedgeTessellator::draw(GrOpFlushState* flushState) const { |
Chris Dalton | d2b8ba3 | 2021-06-09 00:12:59 -0600 | [diff] [blame] | 382 | if (fShader->willUseTessellationShaders()) { |
| 383 | for (const GrVertexChunk& chunk : fVertexChunkArray) { |
| 384 | flushState->bindBuffers(nullptr, nullptr, chunk.fBuffer); |
| 385 | flushState->draw(chunk.fCount * 5, chunk.fBase * 5); |
| 386 | } |
| 387 | } else { |
| 388 | SkASSERT(fShader->hasInstanceAttributes()); |
| 389 | for (const GrVertexChunk& chunk : fVertexChunkArray) { |
Chris Dalton | e1f7237 | 2021-06-29 16:45:49 -0600 | [diff] [blame] | 390 | flushState->bindBuffers(fFixedCountIndexBuffer, chunk.fBuffer, fFixedCountVertexBuffer); |
| 391 | flushState->drawIndexedInstanced(fFixedIndexCount, 0, chunk.fCount, chunk.fBase, 0); |
Chris Dalton | d2b8ba3 | 2021-06-09 00:12:59 -0600 | [diff] [blame] | 392 | } |
Chris Dalton | d9bdc32 | 2021-06-01 19:22:05 -0600 | [diff] [blame] | 393 | } |
| 394 | } |
Robert Phillips | f0d21cf | 2021-08-13 10:26:37 -0400 | [diff] [blame] | 395 | #endif |