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#ifndef tessellate_StrokeTessellator_DEFINED 9cb93a386Sopenharmony_ci#define tessellate_StrokeTessellator_DEFINED 10cb93a386Sopenharmony_ci 11cb93a386Sopenharmony_ci#include "include/core/SkPath.h" 12cb93a386Sopenharmony_ci#include "include/core/SkStrokeRec.h" 13cb93a386Sopenharmony_ci#include "include/private/SkColorData.h" 14cb93a386Sopenharmony_ci#include "src/gpu/tessellate/Tessellation.h" 15cb93a386Sopenharmony_ci 16cb93a386Sopenharmony_ciclass GrMeshDrawTarget; 17cb93a386Sopenharmony_ciclass GrOpFlushState; 18cb93a386Sopenharmony_ci 19cb93a386Sopenharmony_cinamespace skgpu { 20cb93a386Sopenharmony_ci 21cb93a386Sopenharmony_ci// Prepares GPU data for, and then draws a stroke's tessellated geometry. 22cb93a386Sopenharmony_ciclass StrokeTessellator { 23cb93a386Sopenharmony_cipublic: 24cb93a386Sopenharmony_ci struct PathStrokeList { 25cb93a386Sopenharmony_ci PathStrokeList(const SkPath& path, const SkStrokeRec& stroke, const SkPMColor4f& color) 26cb93a386Sopenharmony_ci : fPath(path), fStroke(stroke), fColor(color) {} 27cb93a386Sopenharmony_ci SkPath fPath; 28cb93a386Sopenharmony_ci SkStrokeRec fStroke; 29cb93a386Sopenharmony_ci SkPMColor4f fColor; 30cb93a386Sopenharmony_ci PathStrokeList* fNext = nullptr; 31cb93a386Sopenharmony_ci }; 32cb93a386Sopenharmony_ci 33cb93a386Sopenharmony_ci StrokeTessellator(PatchAttribs attribs) : fAttribs(attribs) {} 34cb93a386Sopenharmony_ci 35cb93a386Sopenharmony_ci // Called before draw(). Prepares GPU buffers containing the geometry to tessellate. 36cb93a386Sopenharmony_ci // 37cb93a386Sopenharmony_ci // Returns the fixed number of edges the tessellator will draw per patch, if using fixed-count 38cb93a386Sopenharmony_ci // rendering, otherwise 0. 39cb93a386Sopenharmony_ci virtual int prepare(GrMeshDrawTarget*, 40cb93a386Sopenharmony_ci const SkMatrix& shaderMatrix, 41cb93a386Sopenharmony_ci std::array<float,2> matrixMinMaxScales, 42cb93a386Sopenharmony_ci PathStrokeList*, 43cb93a386Sopenharmony_ci int totalCombinedVerbCnt) = 0; 44cb93a386Sopenharmony_ci 45cb93a386Sopenharmony_ci#if SK_GPU_V1 46cb93a386Sopenharmony_ci // Issues draw calls for the tessellated stroke. The caller is responsible for creating and 47cb93a386Sopenharmony_ci // binding a pipeline that uses this class's shader() before calling draw(). 48cb93a386Sopenharmony_ci virtual void draw(GrOpFlushState*) const = 0; 49cb93a386Sopenharmony_ci#endif 50cb93a386Sopenharmony_ci 51cb93a386Sopenharmony_ci virtual ~StrokeTessellator() {} 52cb93a386Sopenharmony_ci 53cb93a386Sopenharmony_ciprotected: 54cb93a386Sopenharmony_ci PatchAttribs fAttribs; 55cb93a386Sopenharmony_ci}; 56cb93a386Sopenharmony_ci 57cb93a386Sopenharmony_ci// These tolerances decide the number of parametric and radial segments the tessellator will 58cb93a386Sopenharmony_ci// linearize strokes into. These decisions are made in (pre-viewMatrix) local path space. 59cb93a386Sopenharmony_cistruct StrokeTolerances { 60cb93a386Sopenharmony_ci // Decides the number of parametric segments the tessellator adds for each curve. (Uniform 61cb93a386Sopenharmony_ci // steps in parametric space.) The tessellator will add enough parametric segments so that, 62cb93a386Sopenharmony_ci // once transformed into device space, they never deviate by more than 63cb93a386Sopenharmony_ci // 1/kTessellationPrecision pixels from the true curve. 64cb93a386Sopenharmony_ci constexpr static float CalcParametricPrecision(float matrixMaxScale) { 65cb93a386Sopenharmony_ci return matrixMaxScale * kTessellationPrecision; 66cb93a386Sopenharmony_ci } 67cb93a386Sopenharmony_ci // Decides the number of radial segments the tessellator adds for each curve. (Uniform steps 68cb93a386Sopenharmony_ci // in tangent angle.) The tessellator will add this number of radial segments for each 69cb93a386Sopenharmony_ci // radian of rotation in local path space. 70cb93a386Sopenharmony_ci static float CalcNumRadialSegmentsPerRadian(float parametricPrecision, 71cb93a386Sopenharmony_ci float strokeWidth) { 72cb93a386Sopenharmony_ci return .5f / acosf(std::max(1 - 2 / (parametricPrecision * strokeWidth), -1.f)); 73cb93a386Sopenharmony_ci } 74cb93a386Sopenharmony_ci template<int N> static vec<N> ApproxNumRadialSegmentsPerRadian(float parametricPrecision, 75cb93a386Sopenharmony_ci vec<N> strokeWidths) { 76cb93a386Sopenharmony_ci vec<N> cosTheta = skvx::max(1 - 2 / (parametricPrecision * strokeWidths), -1); 77cb93a386Sopenharmony_ci // Subtract SKVX_APPROX_ACOS_MAX_ERROR so we never account for too few segments. 78cb93a386Sopenharmony_ci return .5f / (skvx::approx_acos(cosTheta) - SKVX_APPROX_ACOS_MAX_ERROR); 79cb93a386Sopenharmony_ci } 80cb93a386Sopenharmony_ci // Returns the equivalent stroke width in (pre-viewMatrix) local path space that the 81cb93a386Sopenharmony_ci // tessellator will use when rendering this stroke. This only differs from the actual stroke 82cb93a386Sopenharmony_ci // width for hairlines. 83cb93a386Sopenharmony_ci static float GetLocalStrokeWidth(const float matrixMinMaxScales[2], float strokeWidth) { 84cb93a386Sopenharmony_ci SkASSERT(strokeWidth >= 0); 85cb93a386Sopenharmony_ci float localStrokeWidth = strokeWidth; 86cb93a386Sopenharmony_ci if (localStrokeWidth == 0) { // Is the stroke a hairline? 87cb93a386Sopenharmony_ci float matrixMinScale = matrixMinMaxScales[0]; 88cb93a386Sopenharmony_ci float matrixMaxScale = matrixMinMaxScales[1]; 89cb93a386Sopenharmony_ci // If the stroke is hairline then the tessellator will operate in post-transform 90cb93a386Sopenharmony_ci // space instead. But for the sake of CPU methods that need to conservatively 91cb93a386Sopenharmony_ci // approximate the number of segments to emit, we use 92cb93a386Sopenharmony_ci // localStrokeWidth ~= 1/matrixMinScale. 93cb93a386Sopenharmony_ci float approxScale = matrixMinScale; 94cb93a386Sopenharmony_ci // If the matrix has strong skew, don't let the scale shoot off to infinity. (This 95cb93a386Sopenharmony_ci // does not affect the tessellator; only the CPU methods that approximate the number 96cb93a386Sopenharmony_ci // of segments to emit.) 97cb93a386Sopenharmony_ci approxScale = std::max(matrixMinScale, matrixMaxScale * .25f); 98cb93a386Sopenharmony_ci localStrokeWidth = 1/approxScale; 99cb93a386Sopenharmony_ci if (localStrokeWidth == 0) { 100cb93a386Sopenharmony_ci // We just can't accidentally return zero from this method because zero means 101cb93a386Sopenharmony_ci // "hairline". Otherwise return whatever we calculated above. 102cb93a386Sopenharmony_ci localStrokeWidth = SK_ScalarNearlyZero; 103cb93a386Sopenharmony_ci } 104cb93a386Sopenharmony_ci } 105cb93a386Sopenharmony_ci return localStrokeWidth; 106cb93a386Sopenharmony_ci } 107cb93a386Sopenharmony_ci static StrokeTolerances Make(const float matrixMinMaxScales[2], float strokeWidth) { 108cb93a386Sopenharmony_ci return MakeNonHairline(matrixMinMaxScales[1], 109cb93a386Sopenharmony_ci GetLocalStrokeWidth(matrixMinMaxScales, strokeWidth)); 110cb93a386Sopenharmony_ci } 111cb93a386Sopenharmony_ci static StrokeTolerances MakeNonHairline(float matrixMaxScale, float strokeWidth) { 112cb93a386Sopenharmony_ci SkASSERT(strokeWidth > 0); 113cb93a386Sopenharmony_ci float parametricPrecision = CalcParametricPrecision(matrixMaxScale); 114cb93a386Sopenharmony_ci return {parametricPrecision, 115cb93a386Sopenharmony_ci CalcNumRadialSegmentsPerRadian(parametricPrecision, strokeWidth)}; 116cb93a386Sopenharmony_ci } 117cb93a386Sopenharmony_ci float fParametricPrecision; 118cb93a386Sopenharmony_ci float fNumRadialSegmentsPerRadian; 119cb93a386Sopenharmony_ci}; 120cb93a386Sopenharmony_ci 121cb93a386Sopenharmony_ci// Calculates and buffers up future values for "numRadialSegmentsPerRadian" using SIMD. 122cb93a386Sopenharmony_ciclass alignas(sizeof(float) * 4) StrokeToleranceBuffer { 123cb93a386Sopenharmony_cipublic: 124cb93a386Sopenharmony_ci using PathStrokeList = StrokeTessellator::PathStrokeList; 125cb93a386Sopenharmony_ci 126cb93a386Sopenharmony_ci StrokeToleranceBuffer(float parametricPrecision) : fParametricPrecision(parametricPrecision) {} 127cb93a386Sopenharmony_ci 128cb93a386Sopenharmony_ci float fetchRadialSegmentsPerRadian(PathStrokeList* head) { 129cb93a386Sopenharmony_ci // StrokeTessellateOp::onCombineIfPossible does not allow hairlines to become dynamic. If 130cb93a386Sopenharmony_ci // this changes, we will need to call StrokeTolerances::GetLocalStrokeWidth() for each 131cb93a386Sopenharmony_ci // stroke. 132cb93a386Sopenharmony_ci SkASSERT(!head->fStroke.isHairlineStyle()); 133cb93a386Sopenharmony_ci if (fBufferIdx == 4) { 134cb93a386Sopenharmony_ci // We ran out of values. Peek ahead and buffer up 4 more. 135cb93a386Sopenharmony_ci PathStrokeList* peekAhead = head; 136cb93a386Sopenharmony_ci int i = 0; 137cb93a386Sopenharmony_ci do { 138cb93a386Sopenharmony_ci fStrokeWidths[i++] = peekAhead->fStroke.getWidth(); 139cb93a386Sopenharmony_ci } while ((peekAhead = peekAhead->fNext) && i < 4); 140cb93a386Sopenharmony_ci auto tol = StrokeTolerances::ApproxNumRadialSegmentsPerRadian(fParametricPrecision, 141cb93a386Sopenharmony_ci fStrokeWidths); 142cb93a386Sopenharmony_ci tol.store(fNumRadialSegmentsPerRadian); 143cb93a386Sopenharmony_ci fBufferIdx = 0; 144cb93a386Sopenharmony_ci } 145cb93a386Sopenharmony_ci SkASSERT(0 <= fBufferIdx && fBufferIdx < 4); 146cb93a386Sopenharmony_ci SkASSERT(fStrokeWidths[fBufferIdx] == head->fStroke.getWidth()); 147cb93a386Sopenharmony_ci return fNumRadialSegmentsPerRadian[fBufferIdx++]; 148cb93a386Sopenharmony_ci } 149cb93a386Sopenharmony_ci 150cb93a386Sopenharmony_ciprivate: 151cb93a386Sopenharmony_ci float4 fStrokeWidths{}; // Must be first for alignment purposes. 152cb93a386Sopenharmony_ci float fNumRadialSegmentsPerRadian[4]; 153cb93a386Sopenharmony_ci const float fParametricPrecision; 154cb93a386Sopenharmony_ci int fBufferIdx = 4; // Initialize the buffer as "empty"; 155cb93a386Sopenharmony_ci}; 156cb93a386Sopenharmony_ci 157cb93a386Sopenharmony_ci} // namespace skgpu 158cb93a386Sopenharmony_ci 159cb93a386Sopenharmony_ci#endif // tessellate_StrokeTessellator_DEFINED 160