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