1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2021 Google LLC. 3cb93a386Sopenharmony_ci * 4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be 5cb93a386Sopenharmony_ci * found in the LICENSE file. 6cb93a386Sopenharmony_ci */ 7cb93a386Sopenharmony_ci 8cb93a386Sopenharmony_ci#include "src/gpu/tessellate/PathWedgeTessellator.h" 9cb93a386Sopenharmony_ci 10cb93a386Sopenharmony_ci#include "src/gpu/tessellate/AffineMatrix.h" 11cb93a386Sopenharmony_ci#include "src/gpu/tessellate/PatchWriter.h" 12cb93a386Sopenharmony_ci#include "src/gpu/tessellate/PathCurveTessellator.h" 13cb93a386Sopenharmony_ci#include "src/gpu/tessellate/WangsFormula.h" 14cb93a386Sopenharmony_ci 15cb93a386Sopenharmony_ci#if SK_GPU_V1 16cb93a386Sopenharmony_ci#include "src/gpu/GrMeshDrawTarget.h" 17cb93a386Sopenharmony_ci#include "src/gpu/GrOpFlushState.h" 18cb93a386Sopenharmony_ci#include "src/gpu/GrResourceProvider.h" 19cb93a386Sopenharmony_ci#endif 20cb93a386Sopenharmony_ci 21cb93a386Sopenharmony_cinamespace skgpu { 22cb93a386Sopenharmony_ci 23cb93a386Sopenharmony_ciusing CubicPatch = PatchWriter::CubicPatch; 24cb93a386Sopenharmony_ciusing ConicPatch = PatchWriter::ConicPatch; 25cb93a386Sopenharmony_ci 26cb93a386Sopenharmony_cinamespace { 27cb93a386Sopenharmony_ci 28cb93a386Sopenharmony_ci// Parses out each contour in a path and tracks the midpoint. Example usage: 29cb93a386Sopenharmony_ci// 30cb93a386Sopenharmony_ci// SkTPathContourParser parser; 31cb93a386Sopenharmony_ci// while (parser.parseNextContour()) { 32cb93a386Sopenharmony_ci// SkPoint midpoint = parser.currentMidpoint(); 33cb93a386Sopenharmony_ci// for (auto [verb, pts] : parser.currentContour()) { 34cb93a386Sopenharmony_ci// ... 35cb93a386Sopenharmony_ci// } 36cb93a386Sopenharmony_ci// } 37cb93a386Sopenharmony_ci// 38cb93a386Sopenharmony_ciclass MidpointContourParser { 39cb93a386Sopenharmony_cipublic: 40cb93a386Sopenharmony_ci MidpointContourParser(const SkPath& path) 41cb93a386Sopenharmony_ci : fPath(path) 42cb93a386Sopenharmony_ci , fVerbs(SkPathPriv::VerbData(fPath)) 43cb93a386Sopenharmony_ci , fNumRemainingVerbs(fPath.countVerbs()) 44cb93a386Sopenharmony_ci , fPoints(SkPathPriv::PointData(fPath)) 45cb93a386Sopenharmony_ci , fWeights(SkPathPriv::ConicWeightData(fPath)) {} 46cb93a386Sopenharmony_ci // Advances the internal state to the next contour in the path. Returns false if there are no 47cb93a386Sopenharmony_ci // more contours. 48cb93a386Sopenharmony_ci bool parseNextContour() { 49cb93a386Sopenharmony_ci bool hasGeometry = false; 50cb93a386Sopenharmony_ci for (; fVerbsIdx < fNumRemainingVerbs; ++fVerbsIdx) { 51cb93a386Sopenharmony_ci switch (fVerbs[fVerbsIdx]) { 52cb93a386Sopenharmony_ci case SkPath::kMove_Verb: 53cb93a386Sopenharmony_ci if (!hasGeometry) { 54cb93a386Sopenharmony_ci fMidpoint = {0,0}; 55cb93a386Sopenharmony_ci fMidpointWeight = 0; 56cb93a386Sopenharmony_ci this->advance(); // Resets fPtsIdx to 0 and advances fPoints. 57cb93a386Sopenharmony_ci fPtsIdx = 1; // Increment fPtsIdx past the kMove. 58cb93a386Sopenharmony_ci continue; 59cb93a386Sopenharmony_ci } 60cb93a386Sopenharmony_ci if (fPoints[0] != fPoints[fPtsIdx - 1]) { 61cb93a386Sopenharmony_ci // There's an implicit close at the end. Add the start point to our mean. 62cb93a386Sopenharmony_ci fMidpoint += fPoints[0]; 63cb93a386Sopenharmony_ci ++fMidpointWeight; 64cb93a386Sopenharmony_ci } 65cb93a386Sopenharmony_ci return true; 66cb93a386Sopenharmony_ci default: 67cb93a386Sopenharmony_ci continue; 68cb93a386Sopenharmony_ci case SkPath::kLine_Verb: 69cb93a386Sopenharmony_ci ++fPtsIdx; 70cb93a386Sopenharmony_ci break; 71cb93a386Sopenharmony_ci case SkPath::kConic_Verb: 72cb93a386Sopenharmony_ci ++fWtsIdx; 73cb93a386Sopenharmony_ci [[fallthrough]]; 74cb93a386Sopenharmony_ci case SkPath::kQuad_Verb: 75cb93a386Sopenharmony_ci fPtsIdx += 2; 76cb93a386Sopenharmony_ci break; 77cb93a386Sopenharmony_ci case SkPath::kCubic_Verb: 78cb93a386Sopenharmony_ci fPtsIdx += 3; 79cb93a386Sopenharmony_ci break; 80cb93a386Sopenharmony_ci } 81cb93a386Sopenharmony_ci fMidpoint += fPoints[fPtsIdx - 1]; 82cb93a386Sopenharmony_ci ++fMidpointWeight; 83cb93a386Sopenharmony_ci hasGeometry = true; 84cb93a386Sopenharmony_ci } 85cb93a386Sopenharmony_ci if (hasGeometry && fPoints[0] != fPoints[fPtsIdx - 1]) { 86cb93a386Sopenharmony_ci // There's an implicit close at the end. Add the start point to our mean. 87cb93a386Sopenharmony_ci fMidpoint += fPoints[0]; 88cb93a386Sopenharmony_ci ++fMidpointWeight; 89cb93a386Sopenharmony_ci } 90cb93a386Sopenharmony_ci return hasGeometry; 91cb93a386Sopenharmony_ci } 92cb93a386Sopenharmony_ci 93cb93a386Sopenharmony_ci // Allows for iterating the current contour using a range-for loop. 94cb93a386Sopenharmony_ci SkPathPriv::Iterate currentContour() { 95cb93a386Sopenharmony_ci return SkPathPriv::Iterate(fVerbs, fVerbs + fVerbsIdx, fPoints, fWeights); 96cb93a386Sopenharmony_ci } 97cb93a386Sopenharmony_ci 98cb93a386Sopenharmony_ci SkPoint currentMidpoint() { return fMidpoint * (1.f / fMidpointWeight); } 99cb93a386Sopenharmony_ci 100cb93a386Sopenharmony_ciprivate: 101cb93a386Sopenharmony_ci void advance() { 102cb93a386Sopenharmony_ci fVerbs += fVerbsIdx; 103cb93a386Sopenharmony_ci fNumRemainingVerbs -= fVerbsIdx; 104cb93a386Sopenharmony_ci fVerbsIdx = 0; 105cb93a386Sopenharmony_ci fPoints += fPtsIdx; 106cb93a386Sopenharmony_ci fPtsIdx = 0; 107cb93a386Sopenharmony_ci fWeights += fWtsIdx; 108cb93a386Sopenharmony_ci fWtsIdx = 0; 109cb93a386Sopenharmony_ci } 110cb93a386Sopenharmony_ci 111cb93a386Sopenharmony_ci const SkPath& fPath; 112cb93a386Sopenharmony_ci 113cb93a386Sopenharmony_ci const uint8_t* fVerbs; 114cb93a386Sopenharmony_ci int fNumRemainingVerbs = 0; 115cb93a386Sopenharmony_ci int fVerbsIdx = 0; 116cb93a386Sopenharmony_ci 117cb93a386Sopenharmony_ci const SkPoint* fPoints; 118cb93a386Sopenharmony_ci int fPtsIdx = 0; 119cb93a386Sopenharmony_ci 120cb93a386Sopenharmony_ci const float* fWeights; 121cb93a386Sopenharmony_ci int fWtsIdx = 0; 122cb93a386Sopenharmony_ci 123cb93a386Sopenharmony_ci SkPoint fMidpoint; 124cb93a386Sopenharmony_ci int fMidpointWeight; 125cb93a386Sopenharmony_ci}; 126cb93a386Sopenharmony_ci 127cb93a386Sopenharmony_ci} // namespace 128cb93a386Sopenharmony_ci 129cb93a386Sopenharmony_ciint PathWedgeTessellator::patchPreallocCount(int totalCombinedPathVerbCnt) const { 130cb93a386Sopenharmony_ci // Over-allocate enough wedges for 1 in 4 to chop. 131cb93a386Sopenharmony_ci int maxWedges = MaxCombinedFanEdgesInPathDrawList(totalCombinedPathVerbCnt); 132cb93a386Sopenharmony_ci return (maxWedges * 5 + 3) / 4; // i.e., ceil(maxWedges * 5/4) 133cb93a386Sopenharmony_ci} 134cb93a386Sopenharmony_ci 135cb93a386Sopenharmony_civoid PathWedgeTessellator::writePatches(PatchWriter& patchWriter, 136cb93a386Sopenharmony_ci int maxTessellationSegments, 137cb93a386Sopenharmony_ci const SkMatrix& shaderMatrix, 138cb93a386Sopenharmony_ci const PathDrawList& pathDrawList) { 139cb93a386Sopenharmony_ci float maxSegments_pow2 = pow2(maxTessellationSegments); 140cb93a386Sopenharmony_ci float maxSegments_pow4 = pow2(maxSegments_pow2); 141cb93a386Sopenharmony_ci 142cb93a386Sopenharmony_ci // If using fixed count, this is the number of segments we need to emit per instance. Always 143cb93a386Sopenharmony_ci // emit at least 1 segment. 144cb93a386Sopenharmony_ci float numFixedSegments_pow4 = 1; 145cb93a386Sopenharmony_ci 146cb93a386Sopenharmony_ci for (auto [pathMatrix, path, color] : pathDrawList) { 147cb93a386Sopenharmony_ci AffineMatrix m(pathMatrix); 148cb93a386Sopenharmony_ci wangs_formula::VectorXform totalXform(SkMatrix::Concat(shaderMatrix, pathMatrix)); 149cb93a386Sopenharmony_ci if (fAttribs & PatchAttribs::kColor) { 150cb93a386Sopenharmony_ci patchWriter.updateColorAttrib(color); 151cb93a386Sopenharmony_ci } 152cb93a386Sopenharmony_ci MidpointContourParser parser(path); 153cb93a386Sopenharmony_ci while (parser.parseNextContour()) { 154cb93a386Sopenharmony_ci patchWriter.updateFanPointAttrib(m.mapPoint(parser.currentMidpoint())); 155cb93a386Sopenharmony_ci SkPoint lastPoint = {0, 0}; 156cb93a386Sopenharmony_ci SkPoint startPoint = {0, 0}; 157cb93a386Sopenharmony_ci for (auto [verb, pts, w] : parser.currentContour()) { 158cb93a386Sopenharmony_ci switch (verb) { 159cb93a386Sopenharmony_ci case SkPathVerb::kMove: { 160cb93a386Sopenharmony_ci startPoint = lastPoint = pts[0]; 161cb93a386Sopenharmony_ci break; 162cb93a386Sopenharmony_ci } 163cb93a386Sopenharmony_ci 164cb93a386Sopenharmony_ci case SkPathVerb::kLine: { 165cb93a386Sopenharmony_ci CubicPatch(patchWriter) << LineToCubic{m.map2Points(pts)}; 166cb93a386Sopenharmony_ci lastPoint = pts[1]; 167cb93a386Sopenharmony_ci break; 168cb93a386Sopenharmony_ci } 169cb93a386Sopenharmony_ci 170cb93a386Sopenharmony_ci case SkPathVerb::kQuad: { 171cb93a386Sopenharmony_ci auto [p0, p1] = m.map2Points(pts); 172cb93a386Sopenharmony_ci auto p2 = m.map1Point(pts+2); 173cb93a386Sopenharmony_ci float n4 = wangs_formula::quadratic_pow4(kTessellationPrecision, 174cb93a386Sopenharmony_ci pts, 175cb93a386Sopenharmony_ci totalXform); 176cb93a386Sopenharmony_ci if (n4 <= maxSegments_pow4) { 177cb93a386Sopenharmony_ci // This quad already fits in "maxTessellationSegments". 178cb93a386Sopenharmony_ci CubicPatch(patchWriter) << QuadToCubic{p0, p1, p2}; 179cb93a386Sopenharmony_ci } else { 180cb93a386Sopenharmony_ci // Chop until each quad tessellation requires "maxSegments" or fewer. 181cb93a386Sopenharmony_ci int numPatches = 182cb93a386Sopenharmony_ci SkScalarCeilToInt(wangs_formula::root4(n4/maxSegments_pow4)); 183cb93a386Sopenharmony_ci patchWriter.chopAndWriteQuads(p0, p1, p2, numPatches); 184cb93a386Sopenharmony_ci } 185cb93a386Sopenharmony_ci numFixedSegments_pow4 = std::max(n4, numFixedSegments_pow4); 186cb93a386Sopenharmony_ci lastPoint = pts[2]; 187cb93a386Sopenharmony_ci break; 188cb93a386Sopenharmony_ci } 189cb93a386Sopenharmony_ci 190cb93a386Sopenharmony_ci case SkPathVerb::kConic: { 191cb93a386Sopenharmony_ci auto [p0, p1] = m.map2Points(pts); 192cb93a386Sopenharmony_ci auto p2 = m.map1Point(pts+2); 193cb93a386Sopenharmony_ci float n2 = wangs_formula::conic_pow2(kTessellationPrecision, 194cb93a386Sopenharmony_ci pts, 195cb93a386Sopenharmony_ci *w, 196cb93a386Sopenharmony_ci totalXform); 197cb93a386Sopenharmony_ci if (n2 <= maxSegments_pow2) { 198cb93a386Sopenharmony_ci // This conic already fits in "maxTessellationSegments". 199cb93a386Sopenharmony_ci ConicPatch(patchWriter) << p0 << p1 << p2 << *w; 200cb93a386Sopenharmony_ci } else { 201cb93a386Sopenharmony_ci // Chop until each conic tessellation requires "maxSegments" or fewer. 202cb93a386Sopenharmony_ci int numPatches = SkScalarCeilToInt(sqrtf(n2/maxSegments_pow2)); 203cb93a386Sopenharmony_ci patchWriter.chopAndWriteConics(p0, p1, p2, *w, numPatches); 204cb93a386Sopenharmony_ci } 205cb93a386Sopenharmony_ci numFixedSegments_pow4 = std::max(n2*n2, numFixedSegments_pow4); 206cb93a386Sopenharmony_ci lastPoint = pts[2]; 207cb93a386Sopenharmony_ci break; 208cb93a386Sopenharmony_ci } 209cb93a386Sopenharmony_ci 210cb93a386Sopenharmony_ci case SkPathVerb::kCubic: { 211cb93a386Sopenharmony_ci auto [p0, p1] = m.map2Points(pts); 212cb93a386Sopenharmony_ci auto [p2, p3] = m.map2Points(pts+2); 213cb93a386Sopenharmony_ci float n4 = wangs_formula::cubic_pow4(kTessellationPrecision, 214cb93a386Sopenharmony_ci pts, 215cb93a386Sopenharmony_ci totalXform); 216cb93a386Sopenharmony_ci if (n4 <= maxSegments_pow4) { 217cb93a386Sopenharmony_ci // This cubic already fits in "maxTessellationSegments". 218cb93a386Sopenharmony_ci CubicPatch(patchWriter) << p0 << p1 << p2 << p3; 219cb93a386Sopenharmony_ci } else { 220cb93a386Sopenharmony_ci // Chop until each cubic tessellation requires "maxSegments" or fewer. 221cb93a386Sopenharmony_ci int numPatches = 222cb93a386Sopenharmony_ci SkScalarCeilToInt(wangs_formula::root4(n4/maxSegments_pow4)); 223cb93a386Sopenharmony_ci patchWriter.chopAndWriteCubics(p0, p1, p2, p3, numPatches); 224cb93a386Sopenharmony_ci } 225cb93a386Sopenharmony_ci numFixedSegments_pow4 = std::max(n4, numFixedSegments_pow4); 226cb93a386Sopenharmony_ci lastPoint = pts[3]; 227cb93a386Sopenharmony_ci break; 228cb93a386Sopenharmony_ci } 229cb93a386Sopenharmony_ci 230cb93a386Sopenharmony_ci case SkPathVerb::kClose: { 231cb93a386Sopenharmony_ci break; // Ignore. We can assume an implicit close at the end. 232cb93a386Sopenharmony_ci } 233cb93a386Sopenharmony_ci } 234cb93a386Sopenharmony_ci } 235cb93a386Sopenharmony_ci if (lastPoint != startPoint) { 236cb93a386Sopenharmony_ci SkPoint pts[2] = {lastPoint, startPoint}; 237cb93a386Sopenharmony_ci CubicPatch(patchWriter) << LineToCubic{m.map2Points(pts)}; 238cb93a386Sopenharmony_ci } 239cb93a386Sopenharmony_ci } 240cb93a386Sopenharmony_ci } 241cb93a386Sopenharmony_ci 242cb93a386Sopenharmony_ci // log16(n^4) == log2(n). 243cb93a386Sopenharmony_ci // We already chopped curves to make sure none needed a higher resolveLevel than 244cb93a386Sopenharmony_ci // kMaxFixedResolveLevel. 245cb93a386Sopenharmony_ci fFixedResolveLevel = SkTPin(wangs_formula::nextlog16(numFixedSegments_pow4), 246cb93a386Sopenharmony_ci fFixedResolveLevel, 247cb93a386Sopenharmony_ci int(kMaxFixedResolveLevel)); 248cb93a386Sopenharmony_ci} 249cb93a386Sopenharmony_ci 250cb93a386Sopenharmony_civoid PathWedgeTessellator::WriteFixedVertexBuffer(VertexWriter vertexWriter, size_t bufferSize) { 251cb93a386Sopenharmony_ci SkASSERT(bufferSize >= sizeof(SkPoint)); 252cb93a386Sopenharmony_ci 253cb93a386Sopenharmony_ci // Start out with the fan point. A negative resolve level indicates the fan point. 254cb93a386Sopenharmony_ci vertexWriter << -1.f/*resolveLevel*/ << -1.f/*idx*/; 255cb93a386Sopenharmony_ci 256cb93a386Sopenharmony_ci // The rest is the same as for curves. 257cb93a386Sopenharmony_ci PathCurveTessellator::WriteFixedVertexBuffer(std::move(vertexWriter), 258cb93a386Sopenharmony_ci bufferSize - sizeof(SkPoint)); 259cb93a386Sopenharmony_ci} 260cb93a386Sopenharmony_ci 261cb93a386Sopenharmony_civoid PathWedgeTessellator::WriteFixedIndexBuffer(VertexWriter vertexWriter, size_t bufferSize) { 262cb93a386Sopenharmony_ci SkASSERT(bufferSize >= sizeof(uint16_t) * 3); 263cb93a386Sopenharmony_ci 264cb93a386Sopenharmony_ci // Start out with the fan triangle. 265cb93a386Sopenharmony_ci vertexWriter << (uint16_t)0 << (uint16_t)1 << (uint16_t)2; 266cb93a386Sopenharmony_ci 267cb93a386Sopenharmony_ci // The rest is the same as for curves, with a baseIndex of 1. 268cb93a386Sopenharmony_ci PathCurveTessellator::WriteFixedIndexBufferBaseIndex(std::move(vertexWriter), 269cb93a386Sopenharmony_ci bufferSize - sizeof(uint16_t) * 3, 270cb93a386Sopenharmony_ci 1); 271cb93a386Sopenharmony_ci} 272cb93a386Sopenharmony_ci 273cb93a386Sopenharmony_ci#if SK_GPU_V1 274cb93a386Sopenharmony_ci 275cb93a386Sopenharmony_ciGR_DECLARE_STATIC_UNIQUE_KEY(gFixedVertexBufferKey); 276cb93a386Sopenharmony_ciGR_DECLARE_STATIC_UNIQUE_KEY(gFixedIndexBufferKey); 277cb93a386Sopenharmony_ci 278cb93a386Sopenharmony_civoid PathWedgeTessellator::prepareFixedCountBuffers(GrMeshDrawTarget* target) { 279cb93a386Sopenharmony_ci GrResourceProvider* rp = target->resourceProvider(); 280cb93a386Sopenharmony_ci 281cb93a386Sopenharmony_ci GR_DEFINE_STATIC_UNIQUE_KEY(gFixedVertexBufferKey); 282cb93a386Sopenharmony_ci 283cb93a386Sopenharmony_ci fFixedVertexBuffer = rp->findOrMakeStaticBuffer(GrGpuBufferType::kVertex, 284cb93a386Sopenharmony_ci FixedVertexBufferSize(kMaxFixedResolveLevel), 285cb93a386Sopenharmony_ci gFixedVertexBufferKey, 286cb93a386Sopenharmony_ci WriteFixedVertexBuffer); 287cb93a386Sopenharmony_ci 288cb93a386Sopenharmony_ci GR_DEFINE_STATIC_UNIQUE_KEY(gFixedIndexBufferKey); 289cb93a386Sopenharmony_ci 290cb93a386Sopenharmony_ci fFixedIndexBuffer = rp->findOrMakeStaticBuffer(GrGpuBufferType::kIndex, 291cb93a386Sopenharmony_ci FixedIndexBufferSize(kMaxFixedResolveLevel), 292cb93a386Sopenharmony_ci gFixedIndexBufferKey, 293cb93a386Sopenharmony_ci WriteFixedIndexBuffer); 294cb93a386Sopenharmony_ci} 295cb93a386Sopenharmony_ci 296cb93a386Sopenharmony_civoid PathWedgeTessellator::drawTessellated(GrOpFlushState* flushState) const { 297cb93a386Sopenharmony_ci for (const GrVertexChunk& chunk : fVertexChunkArray) { 298cb93a386Sopenharmony_ci flushState->bindBuffers(nullptr, nullptr, chunk.fBuffer); 299cb93a386Sopenharmony_ci flushState->draw(chunk.fCount * 5, chunk.fBase * 5); 300cb93a386Sopenharmony_ci } 301cb93a386Sopenharmony_ci} 302cb93a386Sopenharmony_ci 303cb93a386Sopenharmony_civoid PathWedgeTessellator::drawFixedCount(GrOpFlushState* flushState) const { 304cb93a386Sopenharmony_ci if (!fFixedVertexBuffer || !fFixedIndexBuffer) { 305cb93a386Sopenharmony_ci return; 306cb93a386Sopenharmony_ci } 307cb93a386Sopenharmony_ci // Emit 3 vertices per curve triangle, plus 3 more for the fan triangle. 308cb93a386Sopenharmony_ci int fixedIndexCount = (NumCurveTrianglesAtResolveLevel(fFixedResolveLevel) + 1) * 3; 309cb93a386Sopenharmony_ci for (const GrVertexChunk& chunk : fVertexChunkArray) { 310cb93a386Sopenharmony_ci flushState->bindBuffers(fFixedIndexBuffer, chunk.fBuffer, fFixedVertexBuffer); 311cb93a386Sopenharmony_ci flushState->drawIndexedInstanced(fixedIndexCount, 0, chunk.fCount, chunk.fBase, 0); 312cb93a386Sopenharmony_ci } 313cb93a386Sopenharmony_ci} 314cb93a386Sopenharmony_ci 315cb93a386Sopenharmony_ci#endif 316cb93a386Sopenharmony_ci 317cb93a386Sopenharmony_ci} // namespace skgpu 318