1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2020 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/StrokeHardwareTessellator.h" 9cb93a386Sopenharmony_ci 10cb93a386Sopenharmony_ci#include "src/core/SkPathPriv.h" 11cb93a386Sopenharmony_ci#include "src/gpu/GrMeshDrawTarget.h" 12cb93a386Sopenharmony_ci#include "src/gpu/GrRecordingContextPriv.h" 13cb93a386Sopenharmony_ci#include "src/gpu/geometry/GrPathUtils.h" 14cb93a386Sopenharmony_ci#include "src/gpu/tessellate/WangsFormula.h" 15cb93a386Sopenharmony_ci#include "src/gpu/tessellate/shaders/GrTessellationShader.h" 16cb93a386Sopenharmony_ci 17cb93a386Sopenharmony_ci#if SK_GPU_V1 18cb93a386Sopenharmony_ci#include "src/gpu/GrOpFlushState.h" 19cb93a386Sopenharmony_ci#endif 20cb93a386Sopenharmony_ci 21cb93a386Sopenharmony_cinamespace skgpu { 22cb93a386Sopenharmony_ci 23cb93a386Sopenharmony_cinamespace { 24cb93a386Sopenharmony_ci 25cb93a386Sopenharmony_cifloat num_combined_segments(float numParametricSegments, float numRadialSegments) { 26cb93a386Sopenharmony_ci // The first and last edges are shared by both the parametric and radial sets of edges, so 27cb93a386Sopenharmony_ci // the total number of edges is: 28cb93a386Sopenharmony_ci // 29cb93a386Sopenharmony_ci // numCombinedEdges = numParametricEdges + numRadialEdges - 2 30cb93a386Sopenharmony_ci // 31cb93a386Sopenharmony_ci // It's also important to differentiate between the number of edges and segments in a strip: 32cb93a386Sopenharmony_ci // 33cb93a386Sopenharmony_ci // numCombinedSegments = numCombinedEdges - 1 34cb93a386Sopenharmony_ci // 35cb93a386Sopenharmony_ci // So the total number of segments in the combined strip is: 36cb93a386Sopenharmony_ci // 37cb93a386Sopenharmony_ci // numCombinedSegments = numParametricEdges + numRadialEdges - 2 - 1 38cb93a386Sopenharmony_ci // = numParametricSegments + 1 + numRadialSegments + 1 - 2 - 1 39cb93a386Sopenharmony_ci // = numParametricSegments + numRadialSegments - 1 40cb93a386Sopenharmony_ci // 41cb93a386Sopenharmony_ci return numParametricSegments + numRadialSegments - 1; 42cb93a386Sopenharmony_ci} 43cb93a386Sopenharmony_ci 44cb93a386Sopenharmony_cifloat2 pow4(float2 x) { 45cb93a386Sopenharmony_ci auto xx = x*x; 46cb93a386Sopenharmony_ci return xx*xx; 47cb93a386Sopenharmony_ci} 48cb93a386Sopenharmony_ci 49cb93a386Sopenharmony_ciclass PatchWriter { 50cb93a386Sopenharmony_cipublic: 51cb93a386Sopenharmony_ci enum class JoinType { 52cb93a386Sopenharmony_ci kMiter = SkPaint::kMiter_Join, 53cb93a386Sopenharmony_ci kRound = SkPaint::kRound_Join, 54cb93a386Sopenharmony_ci kBevel = SkPaint::kBevel_Join, 55cb93a386Sopenharmony_ci kBowtie = SkPaint::kLast_Join + 1 // Double sided round join. 56cb93a386Sopenharmony_ci }; 57cb93a386Sopenharmony_ci 58cb93a386Sopenharmony_ci PatchWriter(PatchAttribs attribs, 59cb93a386Sopenharmony_ci GrMeshDrawTarget* target, 60cb93a386Sopenharmony_ci const SkMatrix& viewMatrix, 61cb93a386Sopenharmony_ci float matrixMaxScale, 62cb93a386Sopenharmony_ci GrVertexChunkArray* patchChunks, 63cb93a386Sopenharmony_ci size_t patchStride, 64cb93a386Sopenharmony_ci int minPatchesPerChunk) 65cb93a386Sopenharmony_ci : fAttribs(attribs) 66cb93a386Sopenharmony_ci , fChunkBuilder(target, patchChunks, patchStride, minPatchesPerChunk) 67cb93a386Sopenharmony_ci // Subtract 2 because the tessellation shader chops every cubic at two locations, and 68cb93a386Sopenharmony_ci // each chop has the potential to introduce an extra segment. 69cb93a386Sopenharmony_ci , fMaxTessellationSegments(target->caps().shaderCaps()->maxTessellationSegments() - 2) 70cb93a386Sopenharmony_ci , fParametricPrecision(StrokeTolerances::CalcParametricPrecision(matrixMaxScale)) { 71cb93a386Sopenharmony_ci } 72cb93a386Sopenharmony_ci 73cb93a386Sopenharmony_ci // This is the precision value, adjusted for the view matrix, to use with Wang's formulas when 74cb93a386Sopenharmony_ci // determining how many parametric segments a curve will require. 75cb93a386Sopenharmony_ci float parametricPrecision() const { 76cb93a386Sopenharmony_ci return fParametricPrecision; 77cb93a386Sopenharmony_ci } 78cb93a386Sopenharmony_ci // Will a line and worst-case previous join both fit in a single patch together? 79cb93a386Sopenharmony_ci bool lineFitsInPatch_withJoin() { 80cb93a386Sopenharmony_ci return fMaxCombinedSegments_withJoin >= 1; 81cb93a386Sopenharmony_ci } 82cb93a386Sopenharmony_ci // Will a stroke with the given number of parametric segments and a worst-case rotation of 180 83cb93a386Sopenharmony_ci // degrees fit in a single patch? 84cb93a386Sopenharmony_ci bool stroke180FitsInPatch(float numParametricSegments_pow4) { 85cb93a386Sopenharmony_ci return numParametricSegments_pow4 <= fMaxParametricSegments_pow4[0]; 86cb93a386Sopenharmony_ci } 87cb93a386Sopenharmony_ci // Will a worst-case 180-degree stroke with the given number of parametric segments, and a 88cb93a386Sopenharmony_ci // worst-case join fit in a single patch together? 89cb93a386Sopenharmony_ci bool stroke180FitsInPatch_withJoin(float numParametricSegments_pow4) { 90cb93a386Sopenharmony_ci return numParametricSegments_pow4 <= fMaxParametricSegments_pow4_withJoin[0]; 91cb93a386Sopenharmony_ci } 92cb93a386Sopenharmony_ci // Will a stroke with the given number of parametric segments and a worst-case rotation of 360 93cb93a386Sopenharmony_ci // degrees fit in a single patch? 94cb93a386Sopenharmony_ci bool stroke360FitsInPatch(float numParametricSegments_pow4) { 95cb93a386Sopenharmony_ci return numParametricSegments_pow4 <= fMaxParametricSegments_pow4[1]; 96cb93a386Sopenharmony_ci } 97cb93a386Sopenharmony_ci // Will a worst-case 360-degree stroke with the given number of parametric segments, and a 98cb93a386Sopenharmony_ci // worst-case join fit in a single patch together? 99cb93a386Sopenharmony_ci bool stroke360FitsInPatch_withJoin(float numParametricSegments_pow4) { 100cb93a386Sopenharmony_ci return numParametricSegments_pow4 <= fMaxParametricSegments_pow4_withJoin[1]; 101cb93a386Sopenharmony_ci } 102cb93a386Sopenharmony_ci 103cb93a386Sopenharmony_ci void updateTolerances(float numRadialSegmentsPerRadian, SkPaint::Join joinType) { 104cb93a386Sopenharmony_ci fNumRadialSegmentsPerRadian = numRadialSegmentsPerRadian; 105cb93a386Sopenharmony_ci 106cb93a386Sopenharmony_ci // Calculate the worst-case numbers of parametric segments our hardware can support for the 107cb93a386Sopenharmony_ci // current stroke radius, in the event that there are also enough radial segments to rotate 108cb93a386Sopenharmony_ci // 180 and 360 degrees respectively. These are used for "quick accepts" that allow us to 109cb93a386Sopenharmony_ci // send almost all curves directly to the hardware without having to chop. 110cb93a386Sopenharmony_ci float2 numRadialSegments_180_360 = skvx::max(skvx::ceil( 111cb93a386Sopenharmony_ci float2{SK_ScalarPI, 2*SK_ScalarPI} * fNumRadialSegmentsPerRadian), 1); 112cb93a386Sopenharmony_ci // numEdges = numSegments + 1. See num_combined_segments(). 113cb93a386Sopenharmony_ci float maxTotalEdges = fMaxTessellationSegments + 1; 114cb93a386Sopenharmony_ci // numParametricSegments = numTotalEdges - numRadialSegments. See num_combined_segments(). 115cb93a386Sopenharmony_ci float2 maxParametricSegments = skvx::max(maxTotalEdges - numRadialSegments_180_360, 0); 116cb93a386Sopenharmony_ci float2 maxParametricSegments_pow4 = pow4(maxParametricSegments); 117cb93a386Sopenharmony_ci maxParametricSegments_pow4.store(fMaxParametricSegments_pow4); 118cb93a386Sopenharmony_ci 119cb93a386Sopenharmony_ci // Find the worst-case numbers of parametric segments if we are to integrate a join into the 120cb93a386Sopenharmony_ci // same patch as the curve. 121cb93a386Sopenharmony_ci float numRadialSegments180 = numRadialSegments_180_360[0]; 122cb93a386Sopenharmony_ci float worstCaseNumSegmentsInJoin; 123cb93a386Sopenharmony_ci switch (joinType) { 124cb93a386Sopenharmony_ci case SkPaint::kBevel_Join: worstCaseNumSegmentsInJoin = 1; break; 125cb93a386Sopenharmony_ci case SkPaint::kMiter_Join: worstCaseNumSegmentsInJoin = 2; break; 126cb93a386Sopenharmony_ci case SkPaint::kRound_Join: worstCaseNumSegmentsInJoin = numRadialSegments180; break; 127cb93a386Sopenharmony_ci } 128cb93a386Sopenharmony_ci 129cb93a386Sopenharmony_ci // Now calculate the worst-case numbers of parametric segments if we also want to combine a 130cb93a386Sopenharmony_ci // join with the patch. Subtract an extra 1 off the end because when we integrate a join, 131cb93a386Sopenharmony_ci // the tessellator has to add a redundant edge between the join and curve. 132cb93a386Sopenharmony_ci float2 maxParametricSegments_pow4_withJoin = pow4(skvx::max( 133cb93a386Sopenharmony_ci maxParametricSegments - worstCaseNumSegmentsInJoin - 1, 0)); 134cb93a386Sopenharmony_ci maxParametricSegments_pow4_withJoin.store(fMaxParametricSegments_pow4_withJoin); 135cb93a386Sopenharmony_ci 136cb93a386Sopenharmony_ci fMaxCombinedSegments_withJoin = fMaxTessellationSegments - worstCaseNumSegmentsInJoin - 1; 137cb93a386Sopenharmony_ci fSoloRoundJoinAlwaysFitsInPatch = (numRadialSegments180 <= fMaxTessellationSegments); 138cb93a386Sopenharmony_ci fStrokeJoinType = JoinType(joinType); 139cb93a386Sopenharmony_ci } 140cb93a386Sopenharmony_ci 141cb93a386Sopenharmony_ci void updateDynamicStroke(const SkStrokeRec& stroke) { 142cb93a386Sopenharmony_ci SkASSERT(fAttribs & PatchAttribs::kStrokeParams); 143cb93a386Sopenharmony_ci fDynamicStroke.set(stroke); 144cb93a386Sopenharmony_ci } 145cb93a386Sopenharmony_ci 146cb93a386Sopenharmony_ci void updateDynamicColor(const SkPMColor4f& color) { 147cb93a386Sopenharmony_ci SkASSERT(fAttribs & PatchAttribs::kColor); 148cb93a386Sopenharmony_ci bool wideColor = fAttribs & PatchAttribs::kWideColorIfEnabled; 149cb93a386Sopenharmony_ci SkASSERT(wideColor || color.fitsInBytes()); 150cb93a386Sopenharmony_ci fDynamicColor.set(color, wideColor); 151cb93a386Sopenharmony_ci } 152cb93a386Sopenharmony_ci 153cb93a386Sopenharmony_ci void moveTo(SkPoint pt) { 154cb93a386Sopenharmony_ci fCurrContourStartPoint = pt; 155cb93a386Sopenharmony_ci fHasLastControlPoint = false; 156cb93a386Sopenharmony_ci } 157cb93a386Sopenharmony_ci 158cb93a386Sopenharmony_ci // Writes out the given line, possibly chopping its previous join until the segments fit in 159cb93a386Sopenharmony_ci // tessellation patches. 160cb93a386Sopenharmony_ci void writeLineTo(SkPoint p0, SkPoint p1) { 161cb93a386Sopenharmony_ci this->writeLineTo(fStrokeJoinType, p0, p1); 162cb93a386Sopenharmony_ci } 163cb93a386Sopenharmony_ci void writeLineTo(JoinType prevJoinType, SkPoint p0, SkPoint p1) { 164cb93a386Sopenharmony_ci // Zero-length paths need special treatment because they are spec'd to behave differently. 165cb93a386Sopenharmony_ci if (p0 == p1) { 166cb93a386Sopenharmony_ci return; 167cb93a386Sopenharmony_ci } 168cb93a386Sopenharmony_ci SkPoint asPatch[4] = {p0, p0, p1, p1}; 169cb93a386Sopenharmony_ci this->internalPatchTo(prevJoinType, this->lineFitsInPatch_withJoin(), asPatch, p1); 170cb93a386Sopenharmony_ci } 171cb93a386Sopenharmony_ci 172cb93a386Sopenharmony_ci // Recursively chops the given conic and its previous join until the segments fit in 173cb93a386Sopenharmony_ci // tessellation patches. 174cb93a386Sopenharmony_ci void writeConicPatchesTo(const SkPoint p[3], float w) { 175cb93a386Sopenharmony_ci this->internalConicPatchesTo(fStrokeJoinType, p, w); 176cb93a386Sopenharmony_ci } 177cb93a386Sopenharmony_ci 178cb93a386Sopenharmony_ci // Chops the given cubic at points of inflection and 180-degree rotation, and then recursively 179cb93a386Sopenharmony_ci // chops the previous join and cubic sections as necessary until the segments fit in 180cb93a386Sopenharmony_ci // tessellation patches. 181cb93a386Sopenharmony_ci void writeCubicConvex180PatchesTo(const SkPoint p[4]) { 182cb93a386Sopenharmony_ci SkPoint chops[10]; 183cb93a386Sopenharmony_ci float chopT[2]; 184cb93a386Sopenharmony_ci bool areCusps; 185cb93a386Sopenharmony_ci int numChops = GrPathUtils::findCubicConvex180Chops(p, chopT, &areCusps); 186cb93a386Sopenharmony_ci if (numChops == 0) { 187cb93a386Sopenharmony_ci // The curve is already convex and rotates no more than 180 degrees. 188cb93a386Sopenharmony_ci this->internalCubicConvex180PatchesTo(fStrokeJoinType, p); 189cb93a386Sopenharmony_ci } else if (numChops == 1) { 190cb93a386Sopenharmony_ci SkChopCubicAt(p, chops, chopT[0]); 191cb93a386Sopenharmony_ci if (areCusps) { 192cb93a386Sopenharmony_ci // When chopping on a perfect cusp, these 3 points will be equal. 193cb93a386Sopenharmony_ci chops[2] = chops[4] = chops[3]; 194cb93a386Sopenharmony_ci } 195cb93a386Sopenharmony_ci this->internalCubicConvex180PatchesTo(fStrokeJoinType, chops); 196cb93a386Sopenharmony_ci this->internalCubicConvex180PatchesTo(JoinType::kBowtie, chops + 3); 197cb93a386Sopenharmony_ci } else { 198cb93a386Sopenharmony_ci SkASSERT(numChops == 2); 199cb93a386Sopenharmony_ci SkChopCubicAt(p, chops, chopT[0], chopT[1]); 200cb93a386Sopenharmony_ci // Two cusps are only possible on a flat line with two 180-degree turnarounds. 201cb93a386Sopenharmony_ci if (areCusps) { 202cb93a386Sopenharmony_ci this->writeLineTo(chops[0], chops[3]); 203cb93a386Sopenharmony_ci this->writeLineTo(JoinType::kBowtie, chops[3], chops[6]); 204cb93a386Sopenharmony_ci this->writeLineTo(JoinType::kBowtie, chops[6], chops[9]); 205cb93a386Sopenharmony_ci return; 206cb93a386Sopenharmony_ci } 207cb93a386Sopenharmony_ci this->internalCubicConvex180PatchesTo(fStrokeJoinType, chops); 208cb93a386Sopenharmony_ci this->internalCubicConvex180PatchesTo(JoinType::kBowtie, chops + 3); 209cb93a386Sopenharmony_ci this->internalCubicConvex180PatchesTo(JoinType::kBowtie, chops + 6); 210cb93a386Sopenharmony_ci } 211cb93a386Sopenharmony_ci } 212cb93a386Sopenharmony_ci 213cb93a386Sopenharmony_ci // Writes out the given stroke patch exactly as provided, without chopping or checking the 214cb93a386Sopenharmony_ci // number of segments. Possibly chops its previous join until the segments fit in tessellation 215cb93a386Sopenharmony_ci // patches. 216cb93a386Sopenharmony_ci SK_ALWAYS_INLINE void writePatchTo(bool prevJoinFitsInPatch, const SkPoint p[4], 217cb93a386Sopenharmony_ci SkPoint endControlPoint) { 218cb93a386Sopenharmony_ci SkASSERT(fStrokeJoinType != JoinType::kBowtie); 219cb93a386Sopenharmony_ci 220cb93a386Sopenharmony_ci if (!fHasLastControlPoint) { 221cb93a386Sopenharmony_ci // The first stroke doesn't have a previous join (yet). If the current contour ends up 222cb93a386Sopenharmony_ci // closing itself, we will add that join as its own patch. TODO: Consider deferring the 223cb93a386Sopenharmony_ci // first stroke until we know whether the contour will close. This will allow us to use 224cb93a386Sopenharmony_ci // the closing join as the first patch's previous join. 225cb93a386Sopenharmony_ci fHasLastControlPoint = true; 226cb93a386Sopenharmony_ci fCurrContourFirstControlPoint = (p[1] != p[0]) ? p[1] : p[2]; 227cb93a386Sopenharmony_ci fLastControlPoint = p[0]; // Disables the join section of this patch. 228cb93a386Sopenharmony_ci } else if (!prevJoinFitsInPatch) { 229cb93a386Sopenharmony_ci // There aren't enough guaranteed segments to fold the previous join into this patch. 230cb93a386Sopenharmony_ci // Emit the join in its own separate patch. 231cb93a386Sopenharmony_ci this->internalJoinTo(fStrokeJoinType, p[0], (p[1] != p[0]) ? p[1] : p[2]); 232cb93a386Sopenharmony_ci fLastControlPoint = p[0]; // Disables the join section of this patch. 233cb93a386Sopenharmony_ci } 234cb93a386Sopenharmony_ci 235cb93a386Sopenharmony_ci if (VertexWriter patchWriter = fChunkBuilder.appendVertex()) { 236cb93a386Sopenharmony_ci patchWriter << fLastControlPoint; 237cb93a386Sopenharmony_ci patchWriter.writeArray(p, 4); 238cb93a386Sopenharmony_ci this->writeDynamicAttribs(&patchWriter); 239cb93a386Sopenharmony_ci } 240cb93a386Sopenharmony_ci 241cb93a386Sopenharmony_ci fLastControlPoint = endControlPoint; 242cb93a386Sopenharmony_ci } 243cb93a386Sopenharmony_ci 244cb93a386Sopenharmony_ci void writeClose(SkPoint contourEndpoint, const SkMatrix& viewMatrix, 245cb93a386Sopenharmony_ci const SkStrokeRec& stroke) { 246cb93a386Sopenharmony_ci if (!fHasLastControlPoint) { 247cb93a386Sopenharmony_ci // Draw caps instead of closing if the subpath is zero length: 248cb93a386Sopenharmony_ci // 249cb93a386Sopenharmony_ci // "Any zero length subpath ... shall be stroked if the 'stroke-linecap' property has 250cb93a386Sopenharmony_ci // a value of round or square producing respectively a circle or a square." 251cb93a386Sopenharmony_ci // 252cb93a386Sopenharmony_ci // (https://www.w3.org/TR/SVG11/painting.html#StrokeProperties) 253cb93a386Sopenharmony_ci // 254cb93a386Sopenharmony_ci this->writeCaps(contourEndpoint, viewMatrix, stroke); 255cb93a386Sopenharmony_ci return; 256cb93a386Sopenharmony_ci } 257cb93a386Sopenharmony_ci 258cb93a386Sopenharmony_ci // Draw a line back to the beginning. (This will be discarded if 259cb93a386Sopenharmony_ci // contourEndpoint == fCurrContourStartPoint.) 260cb93a386Sopenharmony_ci this->writeLineTo(contourEndpoint, fCurrContourStartPoint); 261cb93a386Sopenharmony_ci this->internalJoinTo(fStrokeJoinType, fCurrContourStartPoint, fCurrContourFirstControlPoint); 262cb93a386Sopenharmony_ci 263cb93a386Sopenharmony_ci fHasLastControlPoint = false; 264cb93a386Sopenharmony_ci } 265cb93a386Sopenharmony_ci 266cb93a386Sopenharmony_ci void writeCaps(SkPoint contourEndpoint, const SkMatrix& viewMatrix, const SkStrokeRec& stroke) { 267cb93a386Sopenharmony_ci if (!fHasLastControlPoint) { 268cb93a386Sopenharmony_ci // We don't have any control points to orient the caps. In this case, square and round 269cb93a386Sopenharmony_ci // caps are specified to be drawn as an axis-aligned square or circle respectively. 270cb93a386Sopenharmony_ci // Assign default control points that achieve this. 271cb93a386Sopenharmony_ci SkVector outset; 272cb93a386Sopenharmony_ci if (!stroke.isHairlineStyle()) { 273cb93a386Sopenharmony_ci outset = {1, 0}; 274cb93a386Sopenharmony_ci } else { 275cb93a386Sopenharmony_ci // If the stroke is hairline, orient the square on the post-transform x-axis 276cb93a386Sopenharmony_ci // instead. We don't need to worry about the vector length since it will be 277cb93a386Sopenharmony_ci // normalized later. Since the matrix cannot have perspective, the below is 278cb93a386Sopenharmony_ci // equivalent to: 279cb93a386Sopenharmony_ci // 280cb93a386Sopenharmony_ci // outset = inverse(|a b|) * |1| * arbitrary_scale 281cb93a386Sopenharmony_ci // |c d| |0| 282cb93a386Sopenharmony_ci // 283cb93a386Sopenharmony_ci // == 1/det * | d -b| * |1| * arbitrary_scale 284cb93a386Sopenharmony_ci // |-c a| |0| 285cb93a386Sopenharmony_ci // 286cb93a386Sopenharmony_ci // == 1/det * | d| * arbitrary_scale 287cb93a386Sopenharmony_ci // |-c| 288cb93a386Sopenharmony_ci // 289cb93a386Sopenharmony_ci // == | d| 290cb93a386Sopenharmony_ci // |-c| 291cb93a386Sopenharmony_ci // 292cb93a386Sopenharmony_ci SkASSERT(!viewMatrix.hasPerspective()); 293cb93a386Sopenharmony_ci float c=viewMatrix.getSkewY(), d=viewMatrix.getScaleY(); 294cb93a386Sopenharmony_ci outset = {d, -c}; 295cb93a386Sopenharmony_ci } 296cb93a386Sopenharmony_ci fCurrContourFirstControlPoint = fCurrContourStartPoint - outset; 297cb93a386Sopenharmony_ci fLastControlPoint = fCurrContourStartPoint + outset; 298cb93a386Sopenharmony_ci fHasLastControlPoint = true; 299cb93a386Sopenharmony_ci contourEndpoint = fCurrContourStartPoint; 300cb93a386Sopenharmony_ci } 301cb93a386Sopenharmony_ci 302cb93a386Sopenharmony_ci switch (stroke.getCap()) { 303cb93a386Sopenharmony_ci case SkPaint::kButt_Cap: 304cb93a386Sopenharmony_ci break; 305cb93a386Sopenharmony_ci case SkPaint::kRound_Cap: { 306cb93a386Sopenharmony_ci // A round cap is the same thing as a 180-degree round join. 307cb93a386Sopenharmony_ci // If our join type isn't round we can alternatively use a bowtie. 308cb93a386Sopenharmony_ci JoinType roundCapJoinType = (stroke.getJoin() == SkPaint::kRound_Join) 309cb93a386Sopenharmony_ci ? JoinType::kRound : JoinType::kBowtie; 310cb93a386Sopenharmony_ci this->internalJoinTo(roundCapJoinType, contourEndpoint, fLastControlPoint); 311cb93a386Sopenharmony_ci this->internalMoveTo(fCurrContourStartPoint, fCurrContourFirstControlPoint); 312cb93a386Sopenharmony_ci this->internalJoinTo(roundCapJoinType, fCurrContourStartPoint, 313cb93a386Sopenharmony_ci fCurrContourFirstControlPoint); 314cb93a386Sopenharmony_ci break; 315cb93a386Sopenharmony_ci } 316cb93a386Sopenharmony_ci case SkPaint::kSquare_Cap: { 317cb93a386Sopenharmony_ci // A square cap is the same as appending lineTos. 318cb93a386Sopenharmony_ci auto strokeJoinType = JoinType(stroke.getJoin()); 319cb93a386Sopenharmony_ci SkVector lastTangent = contourEndpoint - fLastControlPoint; 320cb93a386Sopenharmony_ci if (!stroke.isHairlineStyle()) { 321cb93a386Sopenharmony_ci // Extend the cap by 1/2 stroke width. 322cb93a386Sopenharmony_ci lastTangent *= (.5f * stroke.getWidth()) / lastTangent.length(); 323cb93a386Sopenharmony_ci } else { 324cb93a386Sopenharmony_ci // Extend the cap by what will be 1/2 pixel after transformation. 325cb93a386Sopenharmony_ci lastTangent *= 326cb93a386Sopenharmony_ci .5f / viewMatrix.mapVector(lastTangent.fX, lastTangent.fY).length(); 327cb93a386Sopenharmony_ci } 328cb93a386Sopenharmony_ci this->writeLineTo(strokeJoinType, contourEndpoint, contourEndpoint + lastTangent); 329cb93a386Sopenharmony_ci this->internalMoveTo(fCurrContourStartPoint, fCurrContourFirstControlPoint); 330cb93a386Sopenharmony_ci SkVector firstTangent = fCurrContourFirstControlPoint - fCurrContourStartPoint; 331cb93a386Sopenharmony_ci if (!stroke.isHairlineStyle()) { 332cb93a386Sopenharmony_ci // Set the the cap back by 1/2 stroke width. 333cb93a386Sopenharmony_ci firstTangent *= (-.5f * stroke.getWidth()) / firstTangent.length(); 334cb93a386Sopenharmony_ci } else { 335cb93a386Sopenharmony_ci // Set the cap back by what will be 1/2 pixel after transformation. 336cb93a386Sopenharmony_ci firstTangent *= 337cb93a386Sopenharmony_ci -.5f / viewMatrix.mapVector(firstTangent.fX, firstTangent.fY).length(); 338cb93a386Sopenharmony_ci } 339cb93a386Sopenharmony_ci this->writeLineTo(strokeJoinType, fCurrContourStartPoint, 340cb93a386Sopenharmony_ci fCurrContourStartPoint + firstTangent); 341cb93a386Sopenharmony_ci break; 342cb93a386Sopenharmony_ci } 343cb93a386Sopenharmony_ci } 344cb93a386Sopenharmony_ci 345cb93a386Sopenharmony_ci fHasLastControlPoint = false; 346cb93a386Sopenharmony_ci } 347cb93a386Sopenharmony_ci 348cb93a386Sopenharmony_ciprivate: 349cb93a386Sopenharmony_ci void internalMoveTo(SkPoint pt, SkPoint lastControlPoint) { 350cb93a386Sopenharmony_ci fCurrContourStartPoint = pt; 351cb93a386Sopenharmony_ci fCurrContourFirstControlPoint = fLastControlPoint = lastControlPoint; 352cb93a386Sopenharmony_ci fHasLastControlPoint = true; 353cb93a386Sopenharmony_ci } 354cb93a386Sopenharmony_ci 355cb93a386Sopenharmony_ci // Recursively chops the given conic and its previous join until the segments fit in 356cb93a386Sopenharmony_ci // tessellation patches. 357cb93a386Sopenharmony_ci void internalConicPatchesTo(JoinType prevJoinType, const SkPoint p[3], float w, 358cb93a386Sopenharmony_ci int maxDepth = -1) { 359cb93a386Sopenharmony_ci // Zero-length paths need special treatment because they are spec'd to behave differently. 360cb93a386Sopenharmony_ci // If the control point is colocated on an endpoint then this might end up being the case. 361cb93a386Sopenharmony_ci // Fall back on a lineTo and let it make the final check. 362cb93a386Sopenharmony_ci if (p[1] == p[0] || p[1] == p[2] || w == 0) { 363cb93a386Sopenharmony_ci this->writeLineTo(prevJoinType, p[0], p[2]); 364cb93a386Sopenharmony_ci return; 365cb93a386Sopenharmony_ci } 366cb93a386Sopenharmony_ci 367cb93a386Sopenharmony_ci // Convert to a patch. 368cb93a386Sopenharmony_ci SkPoint asPatch[4]; 369cb93a386Sopenharmony_ci if (w == 1) { 370cb93a386Sopenharmony_ci GrPathUtils::convertQuadToCubic(p, asPatch); 371cb93a386Sopenharmony_ci } else { 372cb93a386Sopenharmony_ci GrTessellationShader::WriteConicPatch(p, w, asPatch); 373cb93a386Sopenharmony_ci } 374cb93a386Sopenharmony_ci 375cb93a386Sopenharmony_ci float numParametricSegments_pow4; 376cb93a386Sopenharmony_ci if (w == 1) { 377cb93a386Sopenharmony_ci numParametricSegments_pow4 = wangs_formula::quadratic_pow4(fParametricPrecision, p); 378cb93a386Sopenharmony_ci } else { 379cb93a386Sopenharmony_ci float n = wangs_formula::conic_pow2(fParametricPrecision, p, w); 380cb93a386Sopenharmony_ci numParametricSegments_pow4 = n*n; 381cb93a386Sopenharmony_ci } 382cb93a386Sopenharmony_ci if (this->stroke180FitsInPatch(numParametricSegments_pow4) || maxDepth == 0) { 383cb93a386Sopenharmony_ci this->internalPatchTo(prevJoinType, 384cb93a386Sopenharmony_ci this->stroke180FitsInPatch_withJoin(numParametricSegments_pow4), 385cb93a386Sopenharmony_ci asPatch, p[2]); 386cb93a386Sopenharmony_ci return; 387cb93a386Sopenharmony_ci } 388cb93a386Sopenharmony_ci 389cb93a386Sopenharmony_ci // We still might have enough tessellation segments to render the curve. Check again with 390cb93a386Sopenharmony_ci // the actual rotation. 391cb93a386Sopenharmony_ci float numRadialSegments = SkMeasureQuadRotation(p) * fNumRadialSegmentsPerRadian; 392cb93a386Sopenharmony_ci numRadialSegments = std::max(std::ceil(numRadialSegments), 1.f); 393cb93a386Sopenharmony_ci float numParametricSegments = wangs_formula::root4(numParametricSegments_pow4); 394cb93a386Sopenharmony_ci numParametricSegments = std::max(std::ceil(numParametricSegments), 1.f); 395cb93a386Sopenharmony_ci float numCombinedSegments = num_combined_segments(numParametricSegments, numRadialSegments); 396cb93a386Sopenharmony_ci if (numCombinedSegments > fMaxTessellationSegments) { 397cb93a386Sopenharmony_ci // The hardware doesn't support enough segments for this curve. Chop and recurse. 398cb93a386Sopenharmony_ci if (maxDepth < 0) { 399cb93a386Sopenharmony_ci // Decide on an extremely conservative upper bound for when to quit chopping. This 400cb93a386Sopenharmony_ci // is solely to protect us from infinite recursion in instances where FP error 401cb93a386Sopenharmony_ci // prevents us from chopping at the correct midtangent. 402cb93a386Sopenharmony_ci maxDepth = sk_float_nextlog2(numParametricSegments) + 403cb93a386Sopenharmony_ci sk_float_nextlog2(numRadialSegments) + 1; 404cb93a386Sopenharmony_ci maxDepth = std::max(maxDepth, 1); 405cb93a386Sopenharmony_ci } 406cb93a386Sopenharmony_ci if (w == 1) { 407cb93a386Sopenharmony_ci SkPoint chops[5]; 408cb93a386Sopenharmony_ci if (numParametricSegments >= numRadialSegments) { 409cb93a386Sopenharmony_ci SkChopQuadAtHalf(p, chops); 410cb93a386Sopenharmony_ci } else { 411cb93a386Sopenharmony_ci SkChopQuadAtMidTangent(p, chops); 412cb93a386Sopenharmony_ci } 413cb93a386Sopenharmony_ci this->internalConicPatchesTo(prevJoinType, chops, 1, maxDepth - 1); 414cb93a386Sopenharmony_ci this->internalConicPatchesTo(JoinType::kBowtie, chops + 2, 1, maxDepth - 1); 415cb93a386Sopenharmony_ci } else { 416cb93a386Sopenharmony_ci SkConic conic(p, w); 417cb93a386Sopenharmony_ci float chopT = (numParametricSegments >= numRadialSegments) ? .5f 418cb93a386Sopenharmony_ci : conic.findMidTangent(); 419cb93a386Sopenharmony_ci SkConic chops[2]; 420cb93a386Sopenharmony_ci if (conic.chopAt(chopT, chops)) { 421cb93a386Sopenharmony_ci this->internalConicPatchesTo(prevJoinType, chops[0].fPts, chops[0].fW, 422cb93a386Sopenharmony_ci maxDepth - 1); 423cb93a386Sopenharmony_ci this->internalConicPatchesTo(JoinType::kBowtie, chops[1].fPts, chops[1].fW, 424cb93a386Sopenharmony_ci maxDepth - 1); 425cb93a386Sopenharmony_ci } 426cb93a386Sopenharmony_ci } 427cb93a386Sopenharmony_ci return; 428cb93a386Sopenharmony_ci } 429cb93a386Sopenharmony_ci 430cb93a386Sopenharmony_ci this->internalPatchTo(prevJoinType, (numCombinedSegments <= fMaxCombinedSegments_withJoin), 431cb93a386Sopenharmony_ci asPatch, p[2]); 432cb93a386Sopenharmony_ci } 433cb93a386Sopenharmony_ci 434cb93a386Sopenharmony_ci // Recursively chops the given cubic and its previous join until the segments fit in 435cb93a386Sopenharmony_ci // tessellation patches. The cubic must be convex and must not rotate more than 180 degrees. 436cb93a386Sopenharmony_ci void internalCubicConvex180PatchesTo(JoinType prevJoinType, const SkPoint p[4], 437cb93a386Sopenharmony_ci int maxDepth = -1) { 438cb93a386Sopenharmony_ci // The stroke tessellation shader assigns special meaning to p0==p1==p2 and p1==p2==p3. If 439cb93a386Sopenharmony_ci // this is the case then we need to rewrite the cubic. 440cb93a386Sopenharmony_ci if (p[1] == p[2] && (p[1] == p[0] || p[1] == p[3])) { 441cb93a386Sopenharmony_ci this->writeLineTo(prevJoinType, p[0], p[3]); 442cb93a386Sopenharmony_ci return; 443cb93a386Sopenharmony_ci } 444cb93a386Sopenharmony_ci 445cb93a386Sopenharmony_ci float numParametricSegments_pow4 = wangs_formula::cubic_pow4(fParametricPrecision, p); 446cb93a386Sopenharmony_ci if (this->stroke180FitsInPatch(numParametricSegments_pow4) || maxDepth == 0) { 447cb93a386Sopenharmony_ci this->internalPatchTo(prevJoinType, 448cb93a386Sopenharmony_ci this->stroke180FitsInPatch_withJoin(numParametricSegments_pow4), 449cb93a386Sopenharmony_ci p, p[3]); 450cb93a386Sopenharmony_ci return; 451cb93a386Sopenharmony_ci } 452cb93a386Sopenharmony_ci 453cb93a386Sopenharmony_ci // We still might have enough tessellation segments to render the curve. Check again with 454cb93a386Sopenharmony_ci // its actual rotation. 455cb93a386Sopenharmony_ci float numRadialSegments = SkMeasureNonInflectCubicRotation(p) * fNumRadialSegmentsPerRadian; 456cb93a386Sopenharmony_ci numRadialSegments = std::max(std::ceil(numRadialSegments), 1.f); 457cb93a386Sopenharmony_ci float numParametricSegments = wangs_formula::root4(numParametricSegments_pow4); 458cb93a386Sopenharmony_ci numParametricSegments = std::max(std::ceil(numParametricSegments), 1.f); 459cb93a386Sopenharmony_ci float numCombinedSegments = num_combined_segments(numParametricSegments, numRadialSegments); 460cb93a386Sopenharmony_ci if (numCombinedSegments > fMaxTessellationSegments) { 461cb93a386Sopenharmony_ci // The hardware doesn't support enough segments for this curve. Chop and recurse. 462cb93a386Sopenharmony_ci SkPoint chops[7]; 463cb93a386Sopenharmony_ci if (maxDepth < 0) { 464cb93a386Sopenharmony_ci // Decide on an extremely conservative upper bound for when to quit chopping. This 465cb93a386Sopenharmony_ci // is solely to protect us from infinite recursion in instances where FP error 466cb93a386Sopenharmony_ci // prevents us from chopping at the correct midtangent. 467cb93a386Sopenharmony_ci maxDepth = sk_float_nextlog2(numParametricSegments) + 468cb93a386Sopenharmony_ci sk_float_nextlog2(numRadialSegments) + 1; 469cb93a386Sopenharmony_ci maxDepth = std::max(maxDepth, 1); 470cb93a386Sopenharmony_ci } 471cb93a386Sopenharmony_ci if (numParametricSegments >= numRadialSegments) { 472cb93a386Sopenharmony_ci SkChopCubicAtHalf(p, chops); 473cb93a386Sopenharmony_ci } else { 474cb93a386Sopenharmony_ci SkChopCubicAtMidTangent(p, chops); 475cb93a386Sopenharmony_ci } 476cb93a386Sopenharmony_ci this->internalCubicConvex180PatchesTo(prevJoinType, chops, maxDepth - 1); 477cb93a386Sopenharmony_ci this->internalCubicConvex180PatchesTo(JoinType::kBowtie, chops + 3, maxDepth - 1); 478cb93a386Sopenharmony_ci return; 479cb93a386Sopenharmony_ci } 480cb93a386Sopenharmony_ci 481cb93a386Sopenharmony_ci this->internalPatchTo(prevJoinType, (numCombinedSegments <= fMaxCombinedSegments_withJoin), 482cb93a386Sopenharmony_ci p, p[3]); 483cb93a386Sopenharmony_ci } 484cb93a386Sopenharmony_ci 485cb93a386Sopenharmony_ci // Writes out the given stroke patch exactly as provided, without chopping or checking the 486cb93a386Sopenharmony_ci // number of segments. Possibly chops its previous join until the segments fit in tessellation 487cb93a386Sopenharmony_ci // patches. It is valid for prevJoinType to be kBowtie. 488cb93a386Sopenharmony_ci void internalPatchTo(JoinType prevJoinType, bool prevJoinFitsInPatch, const SkPoint p[4], 489cb93a386Sopenharmony_ci SkPoint endPt) { 490cb93a386Sopenharmony_ci if (prevJoinType == JoinType::kBowtie) { 491cb93a386Sopenharmony_ci SkASSERT(fHasLastControlPoint); 492cb93a386Sopenharmony_ci // Bowtie joins are only used on internal chops, and internal chops almost always have 493cb93a386Sopenharmony_ci // continuous tangent angles (i.e., the ending tangent of the first chop and the 494cb93a386Sopenharmony_ci // beginning tangent of the second both point in the same direction). The tangents will 495cb93a386Sopenharmony_ci // only ever not point in the same direction if we chopped at a cusp point, so that's 496cb93a386Sopenharmony_ci // the only time we actually need a bowtie. 497cb93a386Sopenharmony_ci SkPoint nextControlPoint = (p[1] == p[0]) ? p[2] : p[1]; 498cb93a386Sopenharmony_ci SkVector a = p[0] - fLastControlPoint; 499cb93a386Sopenharmony_ci SkVector b = nextControlPoint - p[0]; 500cb93a386Sopenharmony_ci float ab_cosTheta = a.dot(b); 501cb93a386Sopenharmony_ci float ab_pow2 = a.dot(a) * b.dot(b); 502cb93a386Sopenharmony_ci // To check if tangents 'a' and 'b' do not point in the same direction, any of the 503cb93a386Sopenharmony_ci // following formulas work: 504cb93a386Sopenharmony_ci // 505cb93a386Sopenharmony_ci // 0 != theta 506cb93a386Sopenharmony_ci // 1 != cosTheta 507cb93a386Sopenharmony_ci // 1 != cosTheta * abs(cosTheta) [Still false when cosTheta == -1] 508cb93a386Sopenharmony_ci // 509cb93a386Sopenharmony_ci // Introducing a slop term for fuzzy equality gives: 510cb93a386Sopenharmony_ci // 511cb93a386Sopenharmony_ci // 1 !~= cosTheta * abs(cosTheta) [tolerance = epsilon] 512cb93a386Sopenharmony_ci // (ab)^2 !~= (ab)^2 * cosTheta * abs(cosTheta) [tolerance = (ab)^2 * epsilon] 513cb93a386Sopenharmony_ci // (ab)^2 !~= (ab * cosTheta) * (ab * abs(cosTheta)) [tolerance = (ab)^2 * epsilon] 514cb93a386Sopenharmony_ci // (ab)^2 !~= (ab * cosTheta) * abs(ab * cosTheta) [tolerance = (ab)^2 * epsilon] 515cb93a386Sopenharmony_ci // 516cb93a386Sopenharmony_ci // Since we also scale the tolerance, the formula is unaffected by the magnitude of the 517cb93a386Sopenharmony_ci // tangent vectors. (And we can fold "ab" in to the abs() because it's always positive.) 518cb93a386Sopenharmony_ci if (!SkScalarNearlyEqual(ab_pow2, ab_cosTheta * fabsf(ab_cosTheta), 519cb93a386Sopenharmony_ci ab_pow2 * SK_ScalarNearlyZero)) { 520cb93a386Sopenharmony_ci this->internalJoinTo(JoinType::kBowtie, p[0], nextControlPoint); 521cb93a386Sopenharmony_ci fLastControlPoint = p[0]; // Disables the join section of this patch. 522cb93a386Sopenharmony_ci prevJoinFitsInPatch = true; 523cb93a386Sopenharmony_ci } 524cb93a386Sopenharmony_ci } 525cb93a386Sopenharmony_ci 526cb93a386Sopenharmony_ci this->writePatchTo(prevJoinFitsInPatch, p, (p[2] != endPt) ? p[2] : p[1]); 527cb93a386Sopenharmony_ci } 528cb93a386Sopenharmony_ci 529cb93a386Sopenharmony_ci // Recursively chops the given join until the segments fit in tessellation patches. 530cb93a386Sopenharmony_ci void internalJoinTo(JoinType joinType, SkPoint junctionPoint, SkPoint nextControlPoint, 531cb93a386Sopenharmony_ci int maxDepth = -1) { 532cb93a386Sopenharmony_ci if (!fHasLastControlPoint) { 533cb93a386Sopenharmony_ci // The first stroke doesn't have a previous join. 534cb93a386Sopenharmony_ci return; 535cb93a386Sopenharmony_ci } 536cb93a386Sopenharmony_ci 537cb93a386Sopenharmony_ci if (!fSoloRoundJoinAlwaysFitsInPatch && maxDepth != 0 && 538cb93a386Sopenharmony_ci (joinType == JoinType::kRound || joinType == JoinType::kBowtie)) { 539cb93a386Sopenharmony_ci SkVector tan0 = junctionPoint - fLastControlPoint; 540cb93a386Sopenharmony_ci SkVector tan1 = nextControlPoint - junctionPoint; 541cb93a386Sopenharmony_ci float rotation = SkMeasureAngleBetweenVectors(tan0, tan1); 542cb93a386Sopenharmony_ci float numRadialSegments = rotation * fNumRadialSegmentsPerRadian; 543cb93a386Sopenharmony_ci if (numRadialSegments > fMaxTessellationSegments) { 544cb93a386Sopenharmony_ci // This is a round join that requires more segments than the tessellator supports. 545cb93a386Sopenharmony_ci // Split it and recurse. 546cb93a386Sopenharmony_ci if (maxDepth < 0) { 547cb93a386Sopenharmony_ci // Decide on an upper bound for when to quit chopping. This is solely to protect 548cb93a386Sopenharmony_ci // us from infinite recursion due to FP precision issues. 549cb93a386Sopenharmony_ci maxDepth = sk_float_nextlog2(numRadialSegments / fMaxTessellationSegments); 550cb93a386Sopenharmony_ci maxDepth = std::max(maxDepth, 1); 551cb93a386Sopenharmony_ci } 552cb93a386Sopenharmony_ci // Find the bisector so we can split the join in half. 553cb93a386Sopenharmony_ci SkPoint bisector = SkFindBisector(tan0, tan1); 554cb93a386Sopenharmony_ci // c0 will be the "next" control point for the first join half, and c1 will be the 555cb93a386Sopenharmony_ci // "previous" control point for the second join half. 556cb93a386Sopenharmony_ci SkPoint c0, c1; 557cb93a386Sopenharmony_ci // FIXME(skia:11347): This hack ensures "c0 - junctionPoint" gives the exact same 558cb93a386Sopenharmony_ci // ieee fp32 vector as "-(c1 - junctionPoint)". Tessellated stroking is becoming 559cb93a386Sopenharmony_ci // less experimental, so t's time to think of a cleaner method to avoid T-junctions 560cb93a386Sopenharmony_ci // when we chop joins. 561cb93a386Sopenharmony_ci int maxAttempts = 10; 562cb93a386Sopenharmony_ci do { 563cb93a386Sopenharmony_ci bisector = (junctionPoint + bisector) - (junctionPoint - bisector); 564cb93a386Sopenharmony_ci c0 = junctionPoint + bisector; 565cb93a386Sopenharmony_ci c1 = junctionPoint - bisector; 566cb93a386Sopenharmony_ci } while (c0 - junctionPoint != -(c1 - junctionPoint) && --maxAttempts); 567cb93a386Sopenharmony_ci // First join half. 568cb93a386Sopenharmony_ci this->internalJoinTo(joinType, junctionPoint, c0, maxDepth - 1); 569cb93a386Sopenharmony_ci fLastControlPoint = c1; 570cb93a386Sopenharmony_ci // Second join half. 571cb93a386Sopenharmony_ci this->internalJoinTo(joinType, junctionPoint, nextControlPoint, maxDepth - 1); 572cb93a386Sopenharmony_ci return; 573cb93a386Sopenharmony_ci } 574cb93a386Sopenharmony_ci } 575cb93a386Sopenharmony_ci 576cb93a386Sopenharmony_ci // We should never write out joins before the first curve. 577cb93a386Sopenharmony_ci SkASSERT(fHasLastControlPoint); 578cb93a386Sopenharmony_ci 579cb93a386Sopenharmony_ci if (VertexWriter patchWriter = fChunkBuilder.appendVertex()) { 580cb93a386Sopenharmony_ci patchWriter << fLastControlPoint << junctionPoint; 581cb93a386Sopenharmony_ci if (joinType == JoinType::kBowtie) { 582cb93a386Sopenharmony_ci // {prevControlPoint, [p0, p0, p0, p3]} is a reserved patch pattern that means this 583cb93a386Sopenharmony_ci // patch is a bowtie. The bowtie is anchored on p0 and its tangent angles go from 584cb93a386Sopenharmony_ci // (p0 - prevControlPoint) to (p3 - p0). 585cb93a386Sopenharmony_ci patchWriter << junctionPoint << junctionPoint; 586cb93a386Sopenharmony_ci } else { 587cb93a386Sopenharmony_ci // {prevControlPoint, [p0, p3, p3, p3]} is a reserved patch pattern that means this 588cb93a386Sopenharmony_ci // patch is a join only (no curve sections in the patch). The join is anchored on p0 589cb93a386Sopenharmony_ci // and its tangent angles go from (p0 - prevControlPoint) to (p3 - p0). 590cb93a386Sopenharmony_ci patchWriter << nextControlPoint << nextControlPoint; 591cb93a386Sopenharmony_ci } 592cb93a386Sopenharmony_ci patchWriter << (nextControlPoint); 593cb93a386Sopenharmony_ci this->writeDynamicAttribs(&patchWriter); 594cb93a386Sopenharmony_ci } 595cb93a386Sopenharmony_ci 596cb93a386Sopenharmony_ci fLastControlPoint = nextControlPoint; 597cb93a386Sopenharmony_ci } 598cb93a386Sopenharmony_ci 599cb93a386Sopenharmony_ci SK_ALWAYS_INLINE void writeDynamicAttribs(VertexWriter* patchWriter) { 600cb93a386Sopenharmony_ci if (fAttribs & PatchAttribs::kStrokeParams) { 601cb93a386Sopenharmony_ci *patchWriter << fDynamicStroke; 602cb93a386Sopenharmony_ci } 603cb93a386Sopenharmony_ci if (fAttribs & PatchAttribs::kColor) { 604cb93a386Sopenharmony_ci *patchWriter << fDynamicColor; 605cb93a386Sopenharmony_ci } 606cb93a386Sopenharmony_ci SkASSERT(!(fAttribs & PatchAttribs::kExplicitCurveType)); 607cb93a386Sopenharmony_ci } 608cb93a386Sopenharmony_ci 609cb93a386Sopenharmony_ci void discardStroke(const SkPoint p[], int numPoints) { 610cb93a386Sopenharmony_ci if (!fHasLastControlPoint) { 611cb93a386Sopenharmony_ci // This disables the first join, if any. (The first join gets added as a standalone 612cb93a386Sopenharmony_ci // patch during close(), but setting fCurrContourFirstControlPoint to p[0] causes us to 613cb93a386Sopenharmony_ci // skip that join if we attempt to add it later.) 614cb93a386Sopenharmony_ci fCurrContourFirstControlPoint = p[0]; 615cb93a386Sopenharmony_ci fHasLastControlPoint = true; 616cb93a386Sopenharmony_ci } 617cb93a386Sopenharmony_ci // Set fLastControlPoint to the next stroke's p0 (which will be equal to the final point of 618cb93a386Sopenharmony_ci // this stroke). This has the effect of disabling the next stroke's join. 619cb93a386Sopenharmony_ci fLastControlPoint = p[numPoints - 1]; 620cb93a386Sopenharmony_ci } 621cb93a386Sopenharmony_ci 622cb93a386Sopenharmony_ci const PatchAttribs fAttribs; 623cb93a386Sopenharmony_ci GrVertexChunkBuilder fChunkBuilder; 624cb93a386Sopenharmony_ci 625cb93a386Sopenharmony_ci // The maximum number of tessellation segments the hardware can emit for a single patch. 626cb93a386Sopenharmony_ci const int fMaxTessellationSegments; 627cb93a386Sopenharmony_ci 628cb93a386Sopenharmony_ci // This is the precision value, adjusted for the view matrix, to use with Wang's formulas when 629cb93a386Sopenharmony_ci // determining how many parametric segments a curve will require. 630cb93a386Sopenharmony_ci const float fParametricPrecision; 631cb93a386Sopenharmony_ci 632cb93a386Sopenharmony_ci // Number of radial segments required for each radian of rotation in order to look smooth with 633cb93a386Sopenharmony_ci // the current stroke radius. 634cb93a386Sopenharmony_ci float fNumRadialSegmentsPerRadian; 635cb93a386Sopenharmony_ci 636cb93a386Sopenharmony_ci // These arrays contain worst-case numbers of parametric segments, raised to the 4th power, that 637cb93a386Sopenharmony_ci // our hardware can support for the current stroke radius. They assume curve rotations of 180 638cb93a386Sopenharmony_ci // and 360 degrees respectively. These are used for "quick accepts" that allow us to send almost 639cb93a386Sopenharmony_ci // all curves directly to the hardware without having to chop. We raise to the 4th power because 640cb93a386Sopenharmony_ci // the "pow4" variants of Wang's formula are the quickest to evaluate. 641cb93a386Sopenharmony_ci float fMaxParametricSegments_pow4[2]; // Values for strokes that rotate 180 and 360 degrees. 642cb93a386Sopenharmony_ci float fMaxParametricSegments_pow4_withJoin[2]; // For strokes that rotate 180 and 360 degrees. 643cb93a386Sopenharmony_ci 644cb93a386Sopenharmony_ci // Maximum number of segments we can allocate for a stroke if we are stuffing it in a patch 645cb93a386Sopenharmony_ci // together with a worst-case join. 646cb93a386Sopenharmony_ci float fMaxCombinedSegments_withJoin; 647cb93a386Sopenharmony_ci 648cb93a386Sopenharmony_ci // Additional info on the current stroke radius/join type. 649cb93a386Sopenharmony_ci bool fSoloRoundJoinAlwaysFitsInPatch; 650cb93a386Sopenharmony_ci JoinType fStrokeJoinType; 651cb93a386Sopenharmony_ci 652cb93a386Sopenharmony_ci // Variables related to the specific contour that we are currently iterating during 653cb93a386Sopenharmony_ci // prepareBuffers(). 654cb93a386Sopenharmony_ci bool fHasLastControlPoint = false; 655cb93a386Sopenharmony_ci SkPoint fCurrContourStartPoint; 656cb93a386Sopenharmony_ci SkPoint fCurrContourFirstControlPoint; 657cb93a386Sopenharmony_ci SkPoint fLastControlPoint; 658cb93a386Sopenharmony_ci 659cb93a386Sopenharmony_ci // Values for the current dynamic state (if any) that will get written out with each patch. 660cb93a386Sopenharmony_ci StrokeParams fDynamicStroke; 661cb93a386Sopenharmony_ci GrVertexColor fDynamicColor; 662cb93a386Sopenharmony_ci}; 663cb93a386Sopenharmony_ci 664cb93a386Sopenharmony_ciSK_ALWAYS_INLINE bool cubic_has_cusp(const SkPoint p[4]) { 665cb93a386Sopenharmony_ci float2 p0 = skvx::bit_pun<float2>(p[0]); 666cb93a386Sopenharmony_ci float2 p1 = skvx::bit_pun<float2>(p[1]); 667cb93a386Sopenharmony_ci float2 p2 = skvx::bit_pun<float2>(p[2]); 668cb93a386Sopenharmony_ci float2 p3 = skvx::bit_pun<float2>(p[3]); 669cb93a386Sopenharmony_ci 670cb93a386Sopenharmony_ci // See GrPathUtils::findCubicConvex180Chops() for the math. 671cb93a386Sopenharmony_ci float2 C = p1 - p0; 672cb93a386Sopenharmony_ci float2 D = p2 - p1; 673cb93a386Sopenharmony_ci float2 E = p3 - p0; 674cb93a386Sopenharmony_ci float2 B = D - C; 675cb93a386Sopenharmony_ci float2 A = -3*D + E; 676cb93a386Sopenharmony_ci 677cb93a386Sopenharmony_ci float a = cross(A, B); 678cb93a386Sopenharmony_ci float b = cross(A, C); 679cb93a386Sopenharmony_ci float c = cross(B, C); 680cb93a386Sopenharmony_ci float discr = b*b - 4*a*c; 681cb93a386Sopenharmony_ci 682cb93a386Sopenharmony_ci // If -cuspThreshold <= discr <= cuspThreshold, it means the two roots are within a distance of 683cb93a386Sopenharmony_ci // 2^-11 from one another in parametric space. This is close enough for our purposes to take the 684cb93a386Sopenharmony_ci // slow codepath that knows how to handle cusps. 685cb93a386Sopenharmony_ci constexpr static float kEpsilon = 1.f / (1 << 11); 686cb93a386Sopenharmony_ci float cuspThreshold = (2*kEpsilon) * a; 687cb93a386Sopenharmony_ci cuspThreshold *= cuspThreshold; 688cb93a386Sopenharmony_ci 689cb93a386Sopenharmony_ci return fabsf(discr) <= cuspThreshold && 690cb93a386Sopenharmony_ci // The most common type of cusp we encounter is when p0==p1 or p2==p3. Unless the curve 691cb93a386Sopenharmony_ci // is a flat line (a==b==c==0), these don't actually need special treatment because the 692cb93a386Sopenharmony_ci // cusp occurs at t=0 or t=1. 693cb93a386Sopenharmony_ci (!(skvx::all(p0 == p1) || skvx::all(p2 == p3)) || (a == 0 && b == 0 && c == 0)); 694cb93a386Sopenharmony_ci} 695cb93a386Sopenharmony_ci 696cb93a386Sopenharmony_ci} // namespace 697cb93a386Sopenharmony_ci 698cb93a386Sopenharmony_ci 699cb93a386Sopenharmony_ciint StrokeHardwareTessellator::prepare(GrMeshDrawTarget* target, 700cb93a386Sopenharmony_ci const SkMatrix& shaderMatrix, 701cb93a386Sopenharmony_ci std::array<float,2> matrixMinMaxScales, 702cb93a386Sopenharmony_ci PathStrokeList* pathStrokeList, 703cb93a386Sopenharmony_ci int totalCombinedVerbCnt) { 704cb93a386Sopenharmony_ci using JoinType = PatchWriter::JoinType; 705cb93a386Sopenharmony_ci 706cb93a386Sopenharmony_ci // Over-allocate enough patches for 1 in 4 strokes to chop and for 8 extra caps. 707cb93a386Sopenharmony_ci int strokePreallocCount = totalCombinedVerbCnt * 5/4; 708cb93a386Sopenharmony_ci int capPreallocCount = 8; 709cb93a386Sopenharmony_ci int minPatchesPerChunk = strokePreallocCount + capPreallocCount; 710cb93a386Sopenharmony_ci PatchWriter patchWriter(fAttribs, 711cb93a386Sopenharmony_ci target, 712cb93a386Sopenharmony_ci shaderMatrix, 713cb93a386Sopenharmony_ci matrixMinMaxScales[1], 714cb93a386Sopenharmony_ci &fPatchChunks, 715cb93a386Sopenharmony_ci sizeof(SkPoint) * 5 + PatchAttribsStride(fAttribs), 716cb93a386Sopenharmony_ci minPatchesPerChunk); 717cb93a386Sopenharmony_ci 718cb93a386Sopenharmony_ci if (!(fAttribs & PatchAttribs::kStrokeParams)) { 719cb93a386Sopenharmony_ci // Strokes are static. Calculate tolerances once. 720cb93a386Sopenharmony_ci const SkStrokeRec& stroke = pathStrokeList->fStroke; 721cb93a386Sopenharmony_ci float localStrokeWidth = StrokeTolerances::GetLocalStrokeWidth(matrixMinMaxScales.data(), 722cb93a386Sopenharmony_ci stroke.getWidth()); 723cb93a386Sopenharmony_ci float numRadialSegmentsPerRadian = StrokeTolerances::CalcNumRadialSegmentsPerRadian( 724cb93a386Sopenharmony_ci patchWriter.parametricPrecision(), localStrokeWidth); 725cb93a386Sopenharmony_ci patchWriter.updateTolerances(numRadialSegmentsPerRadian, stroke.getJoin()); 726cb93a386Sopenharmony_ci } 727cb93a386Sopenharmony_ci 728cb93a386Sopenharmony_ci // Fast SIMD queue that buffers up values for "numRadialSegmentsPerRadian". Only used when we 729cb93a386Sopenharmony_ci // have dynamic strokes. 730cb93a386Sopenharmony_ci StrokeToleranceBuffer toleranceBuffer(patchWriter.parametricPrecision()); 731cb93a386Sopenharmony_ci 732cb93a386Sopenharmony_ci for (PathStrokeList* pathStroke = pathStrokeList; pathStroke; pathStroke = pathStroke->fNext) { 733cb93a386Sopenharmony_ci const SkStrokeRec& stroke = pathStroke->fStroke; 734cb93a386Sopenharmony_ci if (fAttribs & PatchAttribs::kStrokeParams) { 735cb93a386Sopenharmony_ci // Strokes are dynamic. Update tolerances with every new stroke. 736cb93a386Sopenharmony_ci patchWriter.updateTolerances(toleranceBuffer.fetchRadialSegmentsPerRadian(pathStroke), 737cb93a386Sopenharmony_ci stroke.getJoin()); 738cb93a386Sopenharmony_ci patchWriter.updateDynamicStroke(stroke); 739cb93a386Sopenharmony_ci } 740cb93a386Sopenharmony_ci if (fAttribs & PatchAttribs::kColor) { 741cb93a386Sopenharmony_ci patchWriter.updateDynamicColor(pathStroke->fColor); 742cb93a386Sopenharmony_ci } 743cb93a386Sopenharmony_ci 744cb93a386Sopenharmony_ci const SkPath& path = pathStroke->fPath; 745cb93a386Sopenharmony_ci bool contourIsEmpty = true; 746cb93a386Sopenharmony_ci for (auto [verb, p, w] : SkPathPriv::Iterate(path)) { 747cb93a386Sopenharmony_ci bool prevJoinFitsInPatch; 748cb93a386Sopenharmony_ci SkPoint scratchPts[4]; 749cb93a386Sopenharmony_ci const SkPoint* patchPts; 750cb93a386Sopenharmony_ci SkPoint endControlPoint; 751cb93a386Sopenharmony_ci switch (verb) { 752cb93a386Sopenharmony_ci case SkPathVerb::kMove: 753cb93a386Sopenharmony_ci // "A subpath ... consisting of a single moveto shall not be stroked." 754cb93a386Sopenharmony_ci // https://www.w3.org/TR/SVG11/painting.html#StrokeProperties 755cb93a386Sopenharmony_ci if (!contourIsEmpty) { 756cb93a386Sopenharmony_ci patchWriter.writeCaps(p[-1], shaderMatrix, stroke); 757cb93a386Sopenharmony_ci } 758cb93a386Sopenharmony_ci patchWriter.moveTo(p[0]); 759cb93a386Sopenharmony_ci contourIsEmpty = true; 760cb93a386Sopenharmony_ci continue; 761cb93a386Sopenharmony_ci case SkPathVerb::kClose: 762cb93a386Sopenharmony_ci patchWriter.writeClose(p[0], shaderMatrix, stroke); 763cb93a386Sopenharmony_ci contourIsEmpty = true; 764cb93a386Sopenharmony_ci continue; 765cb93a386Sopenharmony_ci case SkPathVerb::kLine: 766cb93a386Sopenharmony_ci // Set this to false first, before the upcoming continue might disrupt our flow. 767cb93a386Sopenharmony_ci contourIsEmpty = false; 768cb93a386Sopenharmony_ci if (p[0] == p[1]) { 769cb93a386Sopenharmony_ci continue; 770cb93a386Sopenharmony_ci } 771cb93a386Sopenharmony_ci prevJoinFitsInPatch = patchWriter.lineFitsInPatch_withJoin(); 772cb93a386Sopenharmony_ci scratchPts[0] = scratchPts[1] = p[0]; 773cb93a386Sopenharmony_ci scratchPts[2] = scratchPts[3] = p[1]; 774cb93a386Sopenharmony_ci patchPts = scratchPts; 775cb93a386Sopenharmony_ci endControlPoint = p[0]; 776cb93a386Sopenharmony_ci break; 777cb93a386Sopenharmony_ci case SkPathVerb::kQuad: { 778cb93a386Sopenharmony_ci contourIsEmpty = false; 779cb93a386Sopenharmony_ci if (p[1] == p[0] || p[1] == p[2]) { 780cb93a386Sopenharmony_ci // Zero-length paths need special treatment because they are spec'd to 781cb93a386Sopenharmony_ci // behave differently. If the control point is colocated on an endpoint then 782cb93a386Sopenharmony_ci // this might end up being the case. Fall back on a lineTo and let it make 783cb93a386Sopenharmony_ci // the final check. 784cb93a386Sopenharmony_ci patchWriter.writeLineTo(p[0], p[2]); 785cb93a386Sopenharmony_ci continue; 786cb93a386Sopenharmony_ci } 787cb93a386Sopenharmony_ci if (GrPathUtils::conicHasCusp(p)) { 788cb93a386Sopenharmony_ci // Cusps are rare, but the tessellation shader can't handle them. Chop the 789cb93a386Sopenharmony_ci // curve into segments that the shader can handle. 790cb93a386Sopenharmony_ci SkPoint cusp = SkEvalQuadAt(p, SkFindQuadMidTangent(p)); 791cb93a386Sopenharmony_ci patchWriter.writeLineTo(p[0], cusp); 792cb93a386Sopenharmony_ci patchWriter.writeLineTo(JoinType::kBowtie, cusp, p[2]); 793cb93a386Sopenharmony_ci continue; 794cb93a386Sopenharmony_ci } 795cb93a386Sopenharmony_ci float numParametricSegments_pow4 = 796cb93a386Sopenharmony_ci wangs_formula::quadratic_pow4(patchWriter.parametricPrecision(), p); 797cb93a386Sopenharmony_ci if (!patchWriter.stroke180FitsInPatch(numParametricSegments_pow4)) { 798cb93a386Sopenharmony_ci // The curve requires more tessellation segments than the hardware can 799cb93a386Sopenharmony_ci // support. This is rare. Recursively chop until each sub-curve fits. 800cb93a386Sopenharmony_ci patchWriter.writeConicPatchesTo(p, 1); 801cb93a386Sopenharmony_ci continue; 802cb93a386Sopenharmony_ci } 803cb93a386Sopenharmony_ci // The curve fits in a single tessellation patch. This is the most common case. 804cb93a386Sopenharmony_ci // Write it out directly. 805cb93a386Sopenharmony_ci prevJoinFitsInPatch = patchWriter.stroke180FitsInPatch_withJoin( 806cb93a386Sopenharmony_ci numParametricSegments_pow4); 807cb93a386Sopenharmony_ci GrPathUtils::convertQuadToCubic(p, scratchPts); 808cb93a386Sopenharmony_ci patchPts = scratchPts; 809cb93a386Sopenharmony_ci endControlPoint = patchPts[2]; 810cb93a386Sopenharmony_ci break; 811cb93a386Sopenharmony_ci } 812cb93a386Sopenharmony_ci case SkPathVerb::kConic: { 813cb93a386Sopenharmony_ci contourIsEmpty = false; 814cb93a386Sopenharmony_ci if (p[1] == p[0] || p[1] == p[2]) { 815cb93a386Sopenharmony_ci // Zero-length paths need special treatment because they are spec'd to 816cb93a386Sopenharmony_ci // behave differently. If the control point is colocated on an endpoint then 817cb93a386Sopenharmony_ci // this might end up being the case. Fall back on a lineTo and let it make 818cb93a386Sopenharmony_ci // the final check. 819cb93a386Sopenharmony_ci patchWriter.writeLineTo(p[0], p[2]); 820cb93a386Sopenharmony_ci continue; 821cb93a386Sopenharmony_ci } 822cb93a386Sopenharmony_ci if (GrPathUtils::conicHasCusp(p)) { 823cb93a386Sopenharmony_ci // Cusps are rare, but the tessellation shader can't handle them. Chop the 824cb93a386Sopenharmony_ci // curve into segments that the shader can handle. 825cb93a386Sopenharmony_ci SkConic conic(p, *w); 826cb93a386Sopenharmony_ci SkPoint cusp = conic.evalAt(conic.findMidTangent()); 827cb93a386Sopenharmony_ci patchWriter.writeLineTo(p[0], cusp); 828cb93a386Sopenharmony_ci patchWriter.writeLineTo(JoinType::kBowtie, cusp, p[2]); 829cb93a386Sopenharmony_ci continue; 830cb93a386Sopenharmony_ci } 831cb93a386Sopenharmony_ci // For now, the tessellation shader still uses Wang's quadratic formula when it 832cb93a386Sopenharmony_ci // draws conics. 833cb93a386Sopenharmony_ci // TODO: Update here when the shader starts using the real conic formula. 834cb93a386Sopenharmony_ci float n = wangs_formula::conic_pow2(patchWriter.parametricPrecision(), p, *w); 835cb93a386Sopenharmony_ci float numParametricSegments_pow4 = n*n; 836cb93a386Sopenharmony_ci if (!patchWriter.stroke180FitsInPatch(numParametricSegments_pow4)) { 837cb93a386Sopenharmony_ci // The curve requires more tessellation segments than the hardware can 838cb93a386Sopenharmony_ci // support. This is rare. Recursively chop until each sub-curve fits. 839cb93a386Sopenharmony_ci patchWriter.writeConicPatchesTo(p, *w); 840cb93a386Sopenharmony_ci continue; 841cb93a386Sopenharmony_ci } 842cb93a386Sopenharmony_ci // The curve fits in a single tessellation patch. This is the most common 843cb93a386Sopenharmony_ci // case. Write it out directly. 844cb93a386Sopenharmony_ci prevJoinFitsInPatch = patchWriter.stroke180FitsInPatch_withJoin( 845cb93a386Sopenharmony_ci numParametricSegments_pow4); 846cb93a386Sopenharmony_ci GrTessellationShader::WriteConicPatch(p, *w, scratchPts); 847cb93a386Sopenharmony_ci patchPts = scratchPts; 848cb93a386Sopenharmony_ci endControlPoint = p[1]; 849cb93a386Sopenharmony_ci break; 850cb93a386Sopenharmony_ci } 851cb93a386Sopenharmony_ci case SkPathVerb::kCubic: { 852cb93a386Sopenharmony_ci contourIsEmpty = false; 853cb93a386Sopenharmony_ci if (p[1] == p[2] && (p[1] == p[0] || p[1] == p[3])) { 854cb93a386Sopenharmony_ci // The stroke tessellation shader assigns special meaning to p0==p1==p2 and 855cb93a386Sopenharmony_ci // p1==p2==p3. If this is the case then we need to rewrite the cubic. 856cb93a386Sopenharmony_ci patchWriter.writeLineTo(p[0], p[3]); 857cb93a386Sopenharmony_ci continue; 858cb93a386Sopenharmony_ci } 859cb93a386Sopenharmony_ci float numParametricSegments_pow4 = 860cb93a386Sopenharmony_ci wangs_formula::cubic_pow4(patchWriter.parametricPrecision(), p); 861cb93a386Sopenharmony_ci if (!patchWriter.stroke360FitsInPatch(numParametricSegments_pow4) || 862cb93a386Sopenharmony_ci cubic_has_cusp(p)) { 863cb93a386Sopenharmony_ci // Either the curve requires more tessellation segments than the hardware 864cb93a386Sopenharmony_ci // can support, or it has cusp(s). Either case is rare. Chop it into 865cb93a386Sopenharmony_ci // sections that rotate 180 degrees or less (which will naturally be the 866cb93a386Sopenharmony_ci // cusp points if there are any), and then recursively chop each section 867cb93a386Sopenharmony_ci // until it fits. 868cb93a386Sopenharmony_ci patchWriter.writeCubicConvex180PatchesTo(p); 869cb93a386Sopenharmony_ci continue; 870cb93a386Sopenharmony_ci } 871cb93a386Sopenharmony_ci // The curve fits in a single tessellation patch. This is the most common case. 872cb93a386Sopenharmony_ci // Write it out directly. 873cb93a386Sopenharmony_ci prevJoinFitsInPatch = patchWriter.stroke360FitsInPatch_withJoin( 874cb93a386Sopenharmony_ci numParametricSegments_pow4); 875cb93a386Sopenharmony_ci patchPts = p; 876cb93a386Sopenharmony_ci endControlPoint = (p[2] != p[3]) ? p[2] : p[1]; 877cb93a386Sopenharmony_ci break; 878cb93a386Sopenharmony_ci } 879cb93a386Sopenharmony_ci } 880cb93a386Sopenharmony_ci patchWriter.writePatchTo(prevJoinFitsInPatch, patchPts, endControlPoint); 881cb93a386Sopenharmony_ci } 882cb93a386Sopenharmony_ci if (!contourIsEmpty) { 883cb93a386Sopenharmony_ci const SkPoint* p = SkPathPriv::PointData(path); 884cb93a386Sopenharmony_ci patchWriter.writeCaps(p[path.countPoints() - 1], shaderMatrix, stroke); 885cb93a386Sopenharmony_ci } 886cb93a386Sopenharmony_ci } 887cb93a386Sopenharmony_ci return 0; 888cb93a386Sopenharmony_ci} 889cb93a386Sopenharmony_ci 890cb93a386Sopenharmony_ci#if SK_GPU_V1 891cb93a386Sopenharmony_civoid StrokeHardwareTessellator::draw(GrOpFlushState* flushState) const { 892cb93a386Sopenharmony_ci for (const auto& vertexChunk : fPatchChunks) { 893cb93a386Sopenharmony_ci flushState->bindBuffers(nullptr, nullptr, vertexChunk.fBuffer); 894cb93a386Sopenharmony_ci flushState->draw(vertexChunk.fCount, vertexChunk.fBase); 895cb93a386Sopenharmony_ci } 896cb93a386Sopenharmony_ci} 897cb93a386Sopenharmony_ci#endif 898cb93a386Sopenharmony_ci 899cb93a386Sopenharmony_ci} // namespace skgpu 900