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/StrokeFixedCountTessellator.h" 9cb93a386Sopenharmony_ci 10cb93a386Sopenharmony_ci#include "src/core/SkGeometry.h" 11cb93a386Sopenharmony_ci#include "src/gpu/GrMeshDrawTarget.h" 12cb93a386Sopenharmony_ci#include "src/gpu/GrResourceProvider.h" 13cb93a386Sopenharmony_ci#include "src/gpu/geometry/GrPathUtils.h" 14cb93a386Sopenharmony_ci#include "src/gpu/tessellate/StrokeIterator.h" 15cb93a386Sopenharmony_ci#include "src/gpu/tessellate/WangsFormula.h" 16cb93a386Sopenharmony_ci#include "src/gpu/tessellate/shaders/GrTessellationShader.h" 17cb93a386Sopenharmony_ci 18cb93a386Sopenharmony_ci#if SK_GPU_V1 19cb93a386Sopenharmony_ci#include "src/gpu/GrOpFlushState.h" 20cb93a386Sopenharmony_ci#endif 21cb93a386Sopenharmony_ci 22cb93a386Sopenharmony_cinamespace skgpu { 23cb93a386Sopenharmony_ci 24cb93a386Sopenharmony_cinamespace { 25cb93a386Sopenharmony_ci 26cb93a386Sopenharmony_ciconstexpr static float kMaxParametricSegments_pow4 = 27cb93a386Sopenharmony_ci StrokeFixedCountTessellator::kMaxParametricSegments_pow4; 28cb93a386Sopenharmony_ci 29cb93a386Sopenharmony_ci// Writes out strokes to the given instance chunk array, chopping if necessary so that all instances 30cb93a386Sopenharmony_ci// require 32 parametric segments or less. (We don't consider radial segments here. The tessellator 31cb93a386Sopenharmony_ci// will just add enough additional segments to handle a worst-case 180 degree stroke.) 32cb93a386Sopenharmony_ciclass InstanceWriter { 33cb93a386Sopenharmony_cipublic: 34cb93a386Sopenharmony_ci InstanceWriter(PatchAttribs attribs, 35cb93a386Sopenharmony_ci GrMeshDrawTarget* target, 36cb93a386Sopenharmony_ci float matrixMaxScale, 37cb93a386Sopenharmony_ci const SkMatrix& viewMatrix, 38cb93a386Sopenharmony_ci GrVertexChunkArray* patchChunks, 39cb93a386Sopenharmony_ci size_t instanceStride, 40cb93a386Sopenharmony_ci int minInstancesPerChunk) 41cb93a386Sopenharmony_ci : fAttribs(attribs) 42cb93a386Sopenharmony_ci , fChunkBuilder(target, patchChunks, instanceStride, minInstancesPerChunk) 43cb93a386Sopenharmony_ci , fParametricPrecision(StrokeTolerances::CalcParametricPrecision(matrixMaxScale)) { 44cb93a386Sopenharmony_ci } 45cb93a386Sopenharmony_ci 46cb93a386Sopenharmony_ci float parametricPrecision() const { return fParametricPrecision; } 47cb93a386Sopenharmony_ci 48cb93a386Sopenharmony_ci // maxParametricSegments^4, or the number of parametric segments, raised to the 4th power, 49cb93a386Sopenharmony_ci // that are required by the single instance we've written that requires the most segments. 50cb93a386Sopenharmony_ci float maxParametricSegments_pow4() const { return fMaxParametricSegments_pow4; } 51cb93a386Sopenharmony_ci 52cb93a386Sopenharmony_ci // Updates the dynamic stroke state that we will write out with each instance. 53cb93a386Sopenharmony_ci void updateDynamicStroke(const SkStrokeRec& stroke) { 54cb93a386Sopenharmony_ci SkASSERT(!fHasDeferredFirstStroke); 55cb93a386Sopenharmony_ci SkASSERT(fAttribs & PatchAttribs::kStrokeParams); 56cb93a386Sopenharmony_ci fDynamicStroke.set(stroke); 57cb93a386Sopenharmony_ci } 58cb93a386Sopenharmony_ci 59cb93a386Sopenharmony_ci // Updates the dynamic color state that we will write out with each instance. 60cb93a386Sopenharmony_ci void updateDynamicColor(const SkPMColor4f& color) { 61cb93a386Sopenharmony_ci SkASSERT(!fHasDeferredFirstStroke); 62cb93a386Sopenharmony_ci SkASSERT(fAttribs & PatchAttribs::kColor); 63cb93a386Sopenharmony_ci bool wideColor = fAttribs & PatchAttribs::kWideColorIfEnabled; 64cb93a386Sopenharmony_ci SkASSERT(wideColor || color.fitsInBytes()); 65cb93a386Sopenharmony_ci fDynamicColor.set(color, wideColor); 66cb93a386Sopenharmony_ci } 67cb93a386Sopenharmony_ci 68cb93a386Sopenharmony_ci SK_ALWAYS_INLINE void lineTo(SkPoint start, SkPoint end) { 69cb93a386Sopenharmony_ci SkPoint cubic[] = {start, start, end, end}; 70cb93a386Sopenharmony_ci SkPoint endControlPoint = start; 71cb93a386Sopenharmony_ci this->writeStroke(cubic, endControlPoint, GrTessellationShader::kCubicCurveType); 72cb93a386Sopenharmony_ci } 73cb93a386Sopenharmony_ci 74cb93a386Sopenharmony_ci SK_ALWAYS_INLINE void quadraticTo(const SkPoint p[3]) { 75cb93a386Sopenharmony_ci float numParametricSegments_pow4 = wangs_formula::quadratic_pow4(fParametricPrecision, p); 76cb93a386Sopenharmony_ci if (numParametricSegments_pow4 > kMaxParametricSegments_pow4) { 77cb93a386Sopenharmony_ci this->chopQuadraticTo(p); 78cb93a386Sopenharmony_ci return; 79cb93a386Sopenharmony_ci } 80cb93a386Sopenharmony_ci SkPoint cubic[4]; 81cb93a386Sopenharmony_ci GrPathUtils::convertQuadToCubic(p, cubic); 82cb93a386Sopenharmony_ci SkPoint endControlPoint = cubic[2]; 83cb93a386Sopenharmony_ci this->writeStroke(cubic, endControlPoint, GrTessellationShader::kCubicCurveType); 84cb93a386Sopenharmony_ci fMaxParametricSegments_pow4 = std::max(numParametricSegments_pow4, 85cb93a386Sopenharmony_ci fMaxParametricSegments_pow4); 86cb93a386Sopenharmony_ci } 87cb93a386Sopenharmony_ci 88cb93a386Sopenharmony_ci SK_ALWAYS_INLINE void conicTo(const SkPoint p[3], float w) { 89cb93a386Sopenharmony_ci float n = wangs_formula::conic_pow2(fParametricPrecision, p, w); 90cb93a386Sopenharmony_ci float numParametricSegments_pow4 = n*n; 91cb93a386Sopenharmony_ci if (numParametricSegments_pow4 > kMaxParametricSegments_pow4) { 92cb93a386Sopenharmony_ci this->chopConicTo({p, w}); 93cb93a386Sopenharmony_ci return; 94cb93a386Sopenharmony_ci } 95cb93a386Sopenharmony_ci SkPoint conic[4]; 96cb93a386Sopenharmony_ci GrTessellationShader::WriteConicPatch(p, w, conic); 97cb93a386Sopenharmony_ci SkPoint endControlPoint = conic[1]; 98cb93a386Sopenharmony_ci this->writeStroke(conic, endControlPoint, GrTessellationShader::kConicCurveType); 99cb93a386Sopenharmony_ci fMaxParametricSegments_pow4 = std::max(numParametricSegments_pow4, 100cb93a386Sopenharmony_ci fMaxParametricSegments_pow4); 101cb93a386Sopenharmony_ci } 102cb93a386Sopenharmony_ci 103cb93a386Sopenharmony_ci SK_ALWAYS_INLINE void cubicConvex180To(const SkPoint p[4]) { 104cb93a386Sopenharmony_ci float numParametricSegments_pow4 = wangs_formula::cubic_pow4(fParametricPrecision, p); 105cb93a386Sopenharmony_ci if (numParametricSegments_pow4 > kMaxParametricSegments_pow4) { 106cb93a386Sopenharmony_ci this->chopCubicConvex180To(p); 107cb93a386Sopenharmony_ci return; 108cb93a386Sopenharmony_ci } 109cb93a386Sopenharmony_ci SkPoint endControlPoint = (p[3] != p[2]) ? p[2] : (p[2] != p[1]) ? p[1] : p[0]; 110cb93a386Sopenharmony_ci this->writeStroke(p, endControlPoint, GrTessellationShader::kCubicCurveType); 111cb93a386Sopenharmony_ci fMaxParametricSegments_pow4 = std::max(numParametricSegments_pow4, 112cb93a386Sopenharmony_ci fMaxParametricSegments_pow4); 113cb93a386Sopenharmony_ci } 114cb93a386Sopenharmony_ci 115cb93a386Sopenharmony_ci // Called when we encounter the verb "kMoveWithinContour". Moves invalidate the previous control 116cb93a386Sopenharmony_ci // point. The stroke iterator tells us the new value to use for the previous control point. 117cb93a386Sopenharmony_ci void setLastControlPoint(SkPoint newLastControlPoint) { 118cb93a386Sopenharmony_ci fLastControlPoint = newLastControlPoint; 119cb93a386Sopenharmony_ci fHasLastControlPoint = true; 120cb93a386Sopenharmony_ci } 121cb93a386Sopenharmony_ci 122cb93a386Sopenharmony_ci // Draws a circle whose diameter is equal to the stroke width. We emit circles at cusp points 123cb93a386Sopenharmony_ci // round caps, and empty strokes that are specified to be drawn as circles. 124cb93a386Sopenharmony_ci void writeCircle(SkPoint location) { 125cb93a386Sopenharmony_ci if (VertexWriter writer = fChunkBuilder.appendVertex()) { 126cb93a386Sopenharmony_ci // The shader interprets an empty stroke + empty join as a special case that denotes a 127cb93a386Sopenharmony_ci // circle, or 180-degree point stroke. 128cb93a386Sopenharmony_ci writer.fill(location, 5); 129cb93a386Sopenharmony_ci this->writeDynamicAttribs(&writer, GrTessellationShader::kCubicCurveType); 130cb93a386Sopenharmony_ci } 131cb93a386Sopenharmony_ci } 132cb93a386Sopenharmony_ci 133cb93a386Sopenharmony_ci void finishContour() { 134cb93a386Sopenharmony_ci if (fHasDeferredFirstStroke) { 135cb93a386Sopenharmony_ci // We deferred the first stroke because we didn't know the previous control point to use 136cb93a386Sopenharmony_ci // for its join. We write it out now. 137cb93a386Sopenharmony_ci SkASSERT(fHasLastControlPoint); 138cb93a386Sopenharmony_ci this->writeStroke(fDeferredFirstStroke, SkPoint(), 139cb93a386Sopenharmony_ci fDeferredCurveTypeIfUnsupportedInfinity); 140cb93a386Sopenharmony_ci fHasDeferredFirstStroke = false; 141cb93a386Sopenharmony_ci } 142cb93a386Sopenharmony_ci fHasLastControlPoint = false; 143cb93a386Sopenharmony_ci } 144cb93a386Sopenharmony_ci 145cb93a386Sopenharmony_ciprivate: 146cb93a386Sopenharmony_ci void chopQuadraticTo(const SkPoint p[3]) { 147cb93a386Sopenharmony_ci SkPoint chops[5]; 148cb93a386Sopenharmony_ci SkChopQuadAtHalf(p, chops); 149cb93a386Sopenharmony_ci this->quadraticTo(chops); 150cb93a386Sopenharmony_ci this->quadraticTo(chops + 2); 151cb93a386Sopenharmony_ci } 152cb93a386Sopenharmony_ci 153cb93a386Sopenharmony_ci void chopConicTo(const SkConic& conic) { 154cb93a386Sopenharmony_ci SkConic chops[2]; 155cb93a386Sopenharmony_ci if (!conic.chopAt(.5f, chops)) { 156cb93a386Sopenharmony_ci return; 157cb93a386Sopenharmony_ci } 158cb93a386Sopenharmony_ci this->conicTo(chops[0].fPts, chops[0].fW); 159cb93a386Sopenharmony_ci this->conicTo(chops[1].fPts, chops[1].fW); 160cb93a386Sopenharmony_ci } 161cb93a386Sopenharmony_ci 162cb93a386Sopenharmony_ci void chopCubicConvex180To(const SkPoint p[4]) { 163cb93a386Sopenharmony_ci SkPoint chops[7]; 164cb93a386Sopenharmony_ci SkChopCubicAtHalf(p, chops); 165cb93a386Sopenharmony_ci this->cubicConvex180To(chops); 166cb93a386Sopenharmony_ci this->cubicConvex180To(chops + 3); 167cb93a386Sopenharmony_ci } 168cb93a386Sopenharmony_ci 169cb93a386Sopenharmony_ci SK_ALWAYS_INLINE void writeStroke(const SkPoint p[4], SkPoint endControlPoint, 170cb93a386Sopenharmony_ci float curveTypeIfUnsupportedInfinity) { 171cb93a386Sopenharmony_ci if (!fHasLastControlPoint) { 172cb93a386Sopenharmony_ci // We don't know the previous control point yet to use for the join. Defer writing out 173cb93a386Sopenharmony_ci // this stroke until the end. 174cb93a386Sopenharmony_ci memcpy(fDeferredFirstStroke, p, sizeof(fDeferredFirstStroke)); 175cb93a386Sopenharmony_ci fDeferredCurveTypeIfUnsupportedInfinity = curveTypeIfUnsupportedInfinity; 176cb93a386Sopenharmony_ci fHasDeferredFirstStroke = true; 177cb93a386Sopenharmony_ci fHasLastControlPoint = true; 178cb93a386Sopenharmony_ci } else if (VertexWriter writer = fChunkBuilder.appendVertex()) { 179cb93a386Sopenharmony_ci writer.writeArray(p, 4); 180cb93a386Sopenharmony_ci writer << fLastControlPoint; 181cb93a386Sopenharmony_ci this->writeDynamicAttribs(&writer, curveTypeIfUnsupportedInfinity); 182cb93a386Sopenharmony_ci } 183cb93a386Sopenharmony_ci fLastControlPoint = endControlPoint; 184cb93a386Sopenharmony_ci } 185cb93a386Sopenharmony_ci 186cb93a386Sopenharmony_ci SK_ALWAYS_INLINE void writeDynamicAttribs(VertexWriter* writer, 187cb93a386Sopenharmony_ci float curveTypeIfUnsupportedInfinity) { 188cb93a386Sopenharmony_ci if (fAttribs & PatchAttribs::kStrokeParams) { 189cb93a386Sopenharmony_ci *writer << fDynamicStroke; 190cb93a386Sopenharmony_ci } 191cb93a386Sopenharmony_ci if (fAttribs & PatchAttribs::kColor) { 192cb93a386Sopenharmony_ci *writer << fDynamicColor; 193cb93a386Sopenharmony_ci } 194cb93a386Sopenharmony_ci if (fAttribs & PatchAttribs::kExplicitCurveType) { 195cb93a386Sopenharmony_ci *writer << curveTypeIfUnsupportedInfinity; 196cb93a386Sopenharmony_ci } 197cb93a386Sopenharmony_ci } 198cb93a386Sopenharmony_ci 199cb93a386Sopenharmony_ci void discardStroke(const SkPoint p[], int numPts) { 200cb93a386Sopenharmony_ci // Set fLastControlPoint to the next stroke's p0 (which will be equal to the final point of 201cb93a386Sopenharmony_ci // this stroke). This has the effect of disabling the next stroke's join. 202cb93a386Sopenharmony_ci fLastControlPoint = p[numPts - 1]; 203cb93a386Sopenharmony_ci fHasLastControlPoint = true; 204cb93a386Sopenharmony_ci } 205cb93a386Sopenharmony_ci 206cb93a386Sopenharmony_ci const PatchAttribs fAttribs; 207cb93a386Sopenharmony_ci GrVertexChunkBuilder fChunkBuilder; 208cb93a386Sopenharmony_ci const float fParametricPrecision; 209cb93a386Sopenharmony_ci float fMaxParametricSegments_pow4 = 1; 210cb93a386Sopenharmony_ci 211cb93a386Sopenharmony_ci // We can't write out the first stroke until we know the previous control point for its join. 212cb93a386Sopenharmony_ci SkPoint fDeferredFirstStroke[4]; 213cb93a386Sopenharmony_ci float fDeferredCurveTypeIfUnsupportedInfinity; 214cb93a386Sopenharmony_ci SkPoint fLastControlPoint; // Used to configure the joins in the instance data. 215cb93a386Sopenharmony_ci bool fHasDeferredFirstStroke = false; 216cb93a386Sopenharmony_ci bool fHasLastControlPoint = false; 217cb93a386Sopenharmony_ci 218cb93a386Sopenharmony_ci // Values for the current dynamic state (if any) that will get written out with each instance. 219cb93a386Sopenharmony_ci StrokeParams fDynamicStroke; 220cb93a386Sopenharmony_ci GrVertexColor fDynamicColor; 221cb93a386Sopenharmony_ci}; 222cb93a386Sopenharmony_ci 223cb93a386Sopenharmony_ci// Returns the worst-case number of edges we will need in order to draw a join of the given type. 224cb93a386Sopenharmony_ciint worst_case_edges_in_join(SkPaint::Join joinType, float numRadialSegmentsPerRadian) { 225cb93a386Sopenharmony_ci int numEdges = StrokeFixedCountTessellator::NumFixedEdgesInJoin(joinType); 226cb93a386Sopenharmony_ci if (joinType == SkPaint::kRound_Join) { 227cb93a386Sopenharmony_ci // For round joins we need to count the radial edges on our own. Account for a worst-case 228cb93a386Sopenharmony_ci // join of 180 degrees (SK_ScalarPI radians). 229cb93a386Sopenharmony_ci numEdges += std::max(SkScalarCeilToInt(numRadialSegmentsPerRadian * SK_ScalarPI) - 1, 0); 230cb93a386Sopenharmony_ci } 231cb93a386Sopenharmony_ci return numEdges; 232cb93a386Sopenharmony_ci} 233cb93a386Sopenharmony_ci 234cb93a386Sopenharmony_ci} // namespace 235cb93a386Sopenharmony_ci 236cb93a386Sopenharmony_ci 237cb93a386Sopenharmony_ciGR_DECLARE_STATIC_UNIQUE_KEY(gVertexIDFallbackBufferKey); 238cb93a386Sopenharmony_ci 239cb93a386Sopenharmony_ciint StrokeFixedCountTessellator::prepare(GrMeshDrawTarget* target, 240cb93a386Sopenharmony_ci const SkMatrix& shaderMatrix, 241cb93a386Sopenharmony_ci std::array<float,2> matrixMinMaxScales, 242cb93a386Sopenharmony_ci PathStrokeList* pathStrokeList, 243cb93a386Sopenharmony_ci int totalCombinedVerbCnt) { 244cb93a386Sopenharmony_ci int maxEdgesInJoin = 0; 245cb93a386Sopenharmony_ci float maxRadialSegmentsPerRadian = 0; 246cb93a386Sopenharmony_ci 247cb93a386Sopenharmony_ci // Over-allocate enough patches for each stroke to chop once, and for 8 extra caps. Since we 248cb93a386Sopenharmony_ci // have to chop at inflections, points of 180 degree rotation, and anywhere a stroke requires 249cb93a386Sopenharmony_ci // too many parametric segments, many strokes will end up getting choppped. 250cb93a386Sopenharmony_ci int strokePreallocCount = totalCombinedVerbCnt * 2; 251cb93a386Sopenharmony_ci int capPreallocCount = 8; 252cb93a386Sopenharmony_ci int minInstancesPerChunk = strokePreallocCount + capPreallocCount; 253cb93a386Sopenharmony_ci InstanceWriter instanceWriter(fAttribs, 254cb93a386Sopenharmony_ci target, 255cb93a386Sopenharmony_ci matrixMinMaxScales[1], 256cb93a386Sopenharmony_ci shaderMatrix, 257cb93a386Sopenharmony_ci &fInstanceChunks, 258cb93a386Sopenharmony_ci sizeof(SkPoint) * 5 + PatchAttribsStride(fAttribs), 259cb93a386Sopenharmony_ci minInstancesPerChunk); 260cb93a386Sopenharmony_ci 261cb93a386Sopenharmony_ci if (!(fAttribs & PatchAttribs::kStrokeParams)) { 262cb93a386Sopenharmony_ci // Strokes are static. Calculate tolerances once. 263cb93a386Sopenharmony_ci const SkStrokeRec& stroke = pathStrokeList->fStroke; 264cb93a386Sopenharmony_ci float localStrokeWidth = StrokeTolerances::GetLocalStrokeWidth(matrixMinMaxScales.data(), 265cb93a386Sopenharmony_ci stroke.getWidth()); 266cb93a386Sopenharmony_ci float numRadialSegmentsPerRadian = StrokeTolerances::CalcNumRadialSegmentsPerRadian( 267cb93a386Sopenharmony_ci instanceWriter.parametricPrecision(), localStrokeWidth); 268cb93a386Sopenharmony_ci maxEdgesInJoin = worst_case_edges_in_join(stroke.getJoin(), numRadialSegmentsPerRadian); 269cb93a386Sopenharmony_ci maxRadialSegmentsPerRadian = numRadialSegmentsPerRadian; 270cb93a386Sopenharmony_ci } 271cb93a386Sopenharmony_ci 272cb93a386Sopenharmony_ci // Fast SIMD queue that buffers up values for "numRadialSegmentsPerRadian". Only used when we 273cb93a386Sopenharmony_ci // have dynamic stroke. 274cb93a386Sopenharmony_ci StrokeToleranceBuffer toleranceBuffer(instanceWriter.parametricPrecision()); 275cb93a386Sopenharmony_ci 276cb93a386Sopenharmony_ci for (PathStrokeList* pathStroke = pathStrokeList; pathStroke; pathStroke = pathStroke->fNext) { 277cb93a386Sopenharmony_ci const SkStrokeRec& stroke = pathStroke->fStroke; 278cb93a386Sopenharmony_ci if (fAttribs & PatchAttribs::kStrokeParams) { 279cb93a386Sopenharmony_ci // Strokes are dynamic. Calculate tolerances every time. 280cb93a386Sopenharmony_ci float numRadialSegmentsPerRadian = 281cb93a386Sopenharmony_ci toleranceBuffer.fetchRadialSegmentsPerRadian(pathStroke); 282cb93a386Sopenharmony_ci maxEdgesInJoin = std::max( 283cb93a386Sopenharmony_ci worst_case_edges_in_join(stroke.getJoin(), numRadialSegmentsPerRadian), 284cb93a386Sopenharmony_ci maxEdgesInJoin); 285cb93a386Sopenharmony_ci maxRadialSegmentsPerRadian = std::max(numRadialSegmentsPerRadian, 286cb93a386Sopenharmony_ci maxRadialSegmentsPerRadian); 287cb93a386Sopenharmony_ci instanceWriter.updateDynamicStroke(stroke); 288cb93a386Sopenharmony_ci } 289cb93a386Sopenharmony_ci if (fAttribs & PatchAttribs::kColor) { 290cb93a386Sopenharmony_ci instanceWriter.updateDynamicColor(pathStroke->fColor); 291cb93a386Sopenharmony_ci } 292cb93a386Sopenharmony_ci StrokeIterator strokeIter(pathStroke->fPath, &pathStroke->fStroke, &shaderMatrix); 293cb93a386Sopenharmony_ci while (strokeIter.next()) { 294cb93a386Sopenharmony_ci const SkPoint* p = strokeIter.pts(); 295cb93a386Sopenharmony_ci switch (strokeIter.verb()) { 296cb93a386Sopenharmony_ci using Verb = StrokeIterator::Verb; 297cb93a386Sopenharmony_ci int numChops; 298cb93a386Sopenharmony_ci case Verb::kContourFinished: 299cb93a386Sopenharmony_ci instanceWriter.finishContour(); 300cb93a386Sopenharmony_ci break; 301cb93a386Sopenharmony_ci case Verb::kCircle: 302cb93a386Sopenharmony_ci // Round cap or else an empty stroke that is specified to be drawn as a circle. 303cb93a386Sopenharmony_ci instanceWriter.writeCircle(p[0]); 304cb93a386Sopenharmony_ci [[fallthrough]]; 305cb93a386Sopenharmony_ci case Verb::kMoveWithinContour: 306cb93a386Sopenharmony_ci instanceWriter.setLastControlPoint(p[0]); 307cb93a386Sopenharmony_ci break; 308cb93a386Sopenharmony_ci case Verb::kLine: 309cb93a386Sopenharmony_ci instanceWriter.lineTo(p[0], p[1]); 310cb93a386Sopenharmony_ci break; 311cb93a386Sopenharmony_ci case Verb::kQuad: 312cb93a386Sopenharmony_ci if (GrPathUtils::conicHasCusp(p)) { 313cb93a386Sopenharmony_ci // The cusp is always at the midtandent. 314cb93a386Sopenharmony_ci SkPoint cusp = SkEvalQuadAt(p, SkFindQuadMidTangent(p)); 315cb93a386Sopenharmony_ci instanceWriter.writeCircle(cusp); 316cb93a386Sopenharmony_ci // A quad can only have a cusp if it's flat with a 180-degree turnaround. 317cb93a386Sopenharmony_ci instanceWriter.lineTo(p[0], cusp); 318cb93a386Sopenharmony_ci instanceWriter.lineTo(cusp, p[2]); 319cb93a386Sopenharmony_ci } else { 320cb93a386Sopenharmony_ci instanceWriter.quadraticTo(p); 321cb93a386Sopenharmony_ci } 322cb93a386Sopenharmony_ci break; 323cb93a386Sopenharmony_ci case Verb::kConic: 324cb93a386Sopenharmony_ci if (GrPathUtils::conicHasCusp(p)) { 325cb93a386Sopenharmony_ci // The cusp is always at the midtandent. 326cb93a386Sopenharmony_ci SkConic conic(p, strokeIter.w()); 327cb93a386Sopenharmony_ci SkPoint cusp = conic.evalAt(conic.findMidTangent()); 328cb93a386Sopenharmony_ci instanceWriter.writeCircle(cusp); 329cb93a386Sopenharmony_ci // A conic can only have a cusp if it's flat with a 180-degree turnaround. 330cb93a386Sopenharmony_ci instanceWriter.lineTo(p[0], cusp); 331cb93a386Sopenharmony_ci instanceWriter.lineTo(cusp, p[2]); 332cb93a386Sopenharmony_ci } else { 333cb93a386Sopenharmony_ci instanceWriter.conicTo(p, strokeIter.w()); 334cb93a386Sopenharmony_ci } 335cb93a386Sopenharmony_ci break; 336cb93a386Sopenharmony_ci case Verb::kCubic: 337cb93a386Sopenharmony_ci SkPoint chops[10]; 338cb93a386Sopenharmony_ci float T[2]; 339cb93a386Sopenharmony_ci bool areCusps; 340cb93a386Sopenharmony_ci numChops = GrPathUtils::findCubicConvex180Chops(p, T, &areCusps); 341cb93a386Sopenharmony_ci if (numChops == 0) { 342cb93a386Sopenharmony_ci instanceWriter.cubicConvex180To(p); 343cb93a386Sopenharmony_ci } else if (numChops == 1) { 344cb93a386Sopenharmony_ci SkChopCubicAt(p, chops, T[0]); 345cb93a386Sopenharmony_ci if (areCusps) { 346cb93a386Sopenharmony_ci instanceWriter.writeCircle(chops[3]); 347cb93a386Sopenharmony_ci // In a perfect world, these 3 points would be be equal after chopping 348cb93a386Sopenharmony_ci // on a cusp. 349cb93a386Sopenharmony_ci chops[2] = chops[4] = chops[3]; 350cb93a386Sopenharmony_ci } 351cb93a386Sopenharmony_ci instanceWriter.cubicConvex180To(chops); 352cb93a386Sopenharmony_ci instanceWriter.cubicConvex180To(chops + 3); 353cb93a386Sopenharmony_ci } else { 354cb93a386Sopenharmony_ci SkASSERT(numChops == 2); 355cb93a386Sopenharmony_ci SkChopCubicAt(p, chops, T[0], T[1]); 356cb93a386Sopenharmony_ci if (areCusps) { 357cb93a386Sopenharmony_ci instanceWriter.writeCircle(chops[3]); 358cb93a386Sopenharmony_ci instanceWriter.writeCircle(chops[6]); 359cb93a386Sopenharmony_ci // Two cusps are only possible if it's a flat line with two 180-degree 360cb93a386Sopenharmony_ci // turnarounds. 361cb93a386Sopenharmony_ci instanceWriter.lineTo(chops[0], chops[3]); 362cb93a386Sopenharmony_ci instanceWriter.lineTo(chops[3], chops[6]); 363cb93a386Sopenharmony_ci instanceWriter.lineTo(chops[6], chops[9]); 364cb93a386Sopenharmony_ci } else { 365cb93a386Sopenharmony_ci instanceWriter.cubicConvex180To(chops); 366cb93a386Sopenharmony_ci instanceWriter.cubicConvex180To(chops + 3); 367cb93a386Sopenharmony_ci instanceWriter.cubicConvex180To(chops + 6); 368cb93a386Sopenharmony_ci } 369cb93a386Sopenharmony_ci } 370cb93a386Sopenharmony_ci break; 371cb93a386Sopenharmony_ci } 372cb93a386Sopenharmony_ci } 373cb93a386Sopenharmony_ci } 374cb93a386Sopenharmony_ci 375cb93a386Sopenharmony_ci // The maximum rotation we can have in a stroke is 180 degrees (SK_ScalarPI radians). 376cb93a386Sopenharmony_ci int maxRadialSegmentsInStroke = 377cb93a386Sopenharmony_ci std::max(SkScalarCeilToInt(maxRadialSegmentsPerRadian * SK_ScalarPI), 1); 378cb93a386Sopenharmony_ci 379cb93a386Sopenharmony_ci int maxParametricSegmentsInStroke = SkScalarCeilToInt(sqrtf(sqrtf( 380cb93a386Sopenharmony_ci instanceWriter.maxParametricSegments_pow4()))); 381cb93a386Sopenharmony_ci SkASSERT(maxParametricSegmentsInStroke >= 1); // maxParametricSegments_pow4 is always >= 1. 382cb93a386Sopenharmony_ci 383cb93a386Sopenharmony_ci // Now calculate the maximum number of edges we will need in the stroke portion of the instance. 384cb93a386Sopenharmony_ci // The first and last edges in a stroke are shared by both the parametric and radial sets of 385cb93a386Sopenharmony_ci // edges, so the total number of edges is: 386cb93a386Sopenharmony_ci // 387cb93a386Sopenharmony_ci // numCombinedEdges = numParametricEdges + numRadialEdges - 2 388cb93a386Sopenharmony_ci // 389cb93a386Sopenharmony_ci // It's also important to differentiate between the number of edges and segments in a strip: 390cb93a386Sopenharmony_ci // 391cb93a386Sopenharmony_ci // numSegments = numEdges - 1 392cb93a386Sopenharmony_ci // 393cb93a386Sopenharmony_ci // So the total number of combined edges in the stroke is: 394cb93a386Sopenharmony_ci // 395cb93a386Sopenharmony_ci // numEdgesInStroke = numParametricSegments + 1 + numRadialSegments + 1 - 2 396cb93a386Sopenharmony_ci // = numParametricSegments + numRadialSegments 397cb93a386Sopenharmony_ci // 398cb93a386Sopenharmony_ci int maxEdgesInStroke = maxRadialSegmentsInStroke + maxParametricSegmentsInStroke; 399cb93a386Sopenharmony_ci 400cb93a386Sopenharmony_ci // Each triangle strip has two sections: It starts with a join then transitions to a stroke. The 401cb93a386Sopenharmony_ci // number of edges in an instance is the sum of edges from the join and stroke sections both. 402cb93a386Sopenharmony_ci // NOTE: The final join edge and the first stroke edge are co-located, however we still need to 403cb93a386Sopenharmony_ci // emit both because the join's edge is half-width and the stroke's is full-width. 404cb93a386Sopenharmony_ci fFixedEdgeCount = maxEdgesInJoin + maxEdgesInStroke; 405cb93a386Sopenharmony_ci 406cb93a386Sopenharmony_ci#if !SK_GPU_V1 407cb93a386Sopenharmony_ci // Don't draw more vertices than can be indexed by a signed short. We just have to draw the line 408cb93a386Sopenharmony_ci // somewhere and this seems reasonable enough. (There are two vertices per edge, so 2^14 edges 409cb93a386Sopenharmony_ci // make 2^15 vertices.) 410cb93a386Sopenharmony_ci fFixedEdgeCount = std::min(fFixedEdgeCount, (1 << 14) - 1); 411cb93a386Sopenharmony_ci#else 412cb93a386Sopenharmony_ci // Decrease the upper limit of edge/vertex numbers to avoid Vulkan GPU OOM and page fault with stream id 0x3 413cb93a386Sopenharmony_ci fFixedEdgeCount = std::min(fFixedEdgeCount, (1 << 10)); 414cb93a386Sopenharmony_ci#endif 415cb93a386Sopenharmony_ci 416cb93a386Sopenharmony_ci if (!target->caps().shaderCaps()->vertexIDSupport()) { 417cb93a386Sopenharmony_ci // Our shader won't be able to use sk_VertexID. Bind a fallback vertex buffer with the IDs 418cb93a386Sopenharmony_ci // in it instead. 419cb93a386Sopenharmony_ci constexpr static int kMaxVerticesInFallbackBuffer = 2048; 420cb93a386Sopenharmony_ci fFixedEdgeCount = std::min(fFixedEdgeCount, kMaxVerticesInFallbackBuffer/2); 421cb93a386Sopenharmony_ci 422cb93a386Sopenharmony_ci GR_DEFINE_STATIC_UNIQUE_KEY(gVertexIDFallbackBufferKey); 423cb93a386Sopenharmony_ci 424cb93a386Sopenharmony_ci fVertexBufferIfNoIDSupport = target->resourceProvider()->findOrMakeStaticBuffer( 425cb93a386Sopenharmony_ci GrGpuBufferType::kVertex, 426cb93a386Sopenharmony_ci kMaxVerticesInFallbackBuffer * sizeof(float), 427cb93a386Sopenharmony_ci gVertexIDFallbackBufferKey, 428cb93a386Sopenharmony_ci InitializeVertexIDFallbackBuffer); 429cb93a386Sopenharmony_ci } 430cb93a386Sopenharmony_ci 431cb93a386Sopenharmony_ci return fFixedEdgeCount; 432cb93a386Sopenharmony_ci} 433cb93a386Sopenharmony_ci 434cb93a386Sopenharmony_civoid StrokeFixedCountTessellator::InitializeVertexIDFallbackBuffer(VertexWriter vertexWriter, 435cb93a386Sopenharmony_ci size_t bufferSize) { 436cb93a386Sopenharmony_ci SkASSERT(bufferSize % (sizeof(float) * 2) == 0); 437cb93a386Sopenharmony_ci int edgeCount = bufferSize / (sizeof(float) * 2); 438cb93a386Sopenharmony_ci for (int i = 0; i < edgeCount; ++i) { 439cb93a386Sopenharmony_ci vertexWriter << (float)i << (float)-i; 440cb93a386Sopenharmony_ci } 441cb93a386Sopenharmony_ci} 442cb93a386Sopenharmony_ci 443cb93a386Sopenharmony_ci#if SK_GPU_V1 444cb93a386Sopenharmony_civoid StrokeFixedCountTessellator::draw(GrOpFlushState* flushState) const { 445cb93a386Sopenharmony_ci if (fInstanceChunks.empty() || fFixedEdgeCount <= 0) { 446cb93a386Sopenharmony_ci return; 447cb93a386Sopenharmony_ci } 448cb93a386Sopenharmony_ci if (!flushState->caps().shaderCaps()->vertexIDSupport() && 449cb93a386Sopenharmony_ci !fVertexBufferIfNoIDSupport) { 450cb93a386Sopenharmony_ci return; 451cb93a386Sopenharmony_ci } 452cb93a386Sopenharmony_ci for (const auto& instanceChunk : fInstanceChunks) { 453cb93a386Sopenharmony_ci flushState->bindBuffers(nullptr, instanceChunk.fBuffer, fVertexBufferIfNoIDSupport); 454cb93a386Sopenharmony_ci flushState->drawInstanced(instanceChunk.fCount, 455cb93a386Sopenharmony_ci instanceChunk.fBase, 456cb93a386Sopenharmony_ci fFixedEdgeCount * 2, 457cb93a386Sopenharmony_ci 0); 458cb93a386Sopenharmony_ci } 459cb93a386Sopenharmony_ci} 460cb93a386Sopenharmony_ci#endif 461cb93a386Sopenharmony_ci 462cb93a386Sopenharmony_ci} // namespace skgpu 463