1 /* 2 * Copyright 2020 Google LLC. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #ifndef GrStrokeTessellateShader_DEFINED 9 #define GrStrokeTessellateShader_DEFINED 10 11 #include "src/gpu/tessellate/GrPathShader.h" 12 13 #include "include/core/SkStrokeRec.h" 14 #include "src/gpu/GrVx.h" 15 #include "src/gpu/tessellate/GrTessellationPathRenderer.h" 16 #include <array> 17 18 class GrGLSLUniformHandler; 19 20 // Tessellates a batch of stroke patches directly to the canvas. Tessellated stroking works by 21 // creating stroke-width, orthogonal edges at set locations along the curve and then connecting them 22 // with a quad strip. These orthogonal edges come from two different sets: "parametric edges" and 23 // "radial edges". Parametric edges are spaced evenly in the parametric sense, and radial edges 24 // divide the curve's _rotation_ into even steps. The tessellation shader evaluates both sets of 25 // edges and sorts them into a single quad strip. With this combined set of edges we can stroke any 26 // curve, regardless of curvature. 27 class GrStrokeTessellateShader : public GrPathShader { 28 public: 29 // Are we using hardware tessellation or indirect draws? 30 enum class Mode { 31 kHardwareTessellation, 32 kLog2Indirect, 33 kFixedCount 34 }; 35 36 enum class ShaderFlags { 37 kNone = 0, 38 kHasConics = 1 << 0, 39 kWideColor = 1 << 1, 40 kDynamicStroke = 1 << 2, // Each patch or instance has its own stroke width and join type. 41 kDynamicColor = 1 << 3, // Each patch or instance has its own color. 42 }; 43 44 GR_DECL_BITFIELD_CLASS_OPS_FRIENDS(ShaderFlags); 45 46 // Returns the fixed number of edges that are always emitted with the given join type. If the 47 // join is round, the caller needs to account for the additional radial edges on their own. 48 // Specifically, each join always emits: 49 // 50 // * Two colocated edges at the beginning (a full-width edge to seam with the preceding stroke 51 // and a half-width edge to begin the join). 52 // 53 // * An extra edge in the middle for miter joins, or else a variable number of radial edges 54 // for round joins (the caller is responsible for counting radial edges from round joins). 55 // 56 // * A half-width edge at the end of the join that will be colocated with the first 57 // (full-width) edge of the stroke. 58 // NumFixedEdgesInJoin(SkPaint::Join joinType)59 constexpr static int NumFixedEdgesInJoin(SkPaint::Join joinType) { 60 switch (joinType) { 61 case SkPaint::kMiter_Join: 62 return 4; 63 case SkPaint::kRound_Join: 64 // The caller is responsible for counting the variable number of middle, radial 65 // segments on round joins. 66 [[fallthrough]]; 67 case SkPaint::kBevel_Join: 68 return 3; 69 } 70 SkUNREACHABLE; 71 } 72 73 // We encode all of a join's information in a single float value: 74 // 75 // Negative => Round Join 76 // Zero => Bevel Join 77 // Positive => Miter join, and the value is also the miter limit 78 // GetJoinType(const SkStrokeRec & stroke)79 static float GetJoinType(const SkStrokeRec& stroke) { 80 switch (stroke.getJoin()) { 81 case SkPaint::kRound_Join: return -1; 82 case SkPaint::kBevel_Join: return 0; 83 case SkPaint::kMiter_Join: SkASSERT(stroke.getMiter() >= 0); return stroke.getMiter(); 84 } 85 SkUNREACHABLE; 86 } 87 88 // This struct gets written out to each patch or instance if kDynamicStroke is enabled. 89 struct DynamicStroke { StrokesHaveEqualDynamicStateDynamicStroke90 static bool StrokesHaveEqualDynamicState(const SkStrokeRec& a, const SkStrokeRec& b) { 91 return a.getWidth() == b.getWidth() && a.getJoin() == b.getJoin() && 92 (a.getJoin() != SkPaint::kMiter_Join || a.getMiter() == b.getMiter()); 93 } setDynamicStroke94 void set(const SkStrokeRec& stroke) { 95 fRadius = stroke.getWidth() * .5f; 96 fJoinType = GetJoinType(stroke); 97 } 98 float fRadius; 99 float fJoinType; // See GetJoinType(). 100 }; 101 102 // 'viewMatrix' is applied to the geometry post tessellation. It cannot have perspective. GrStrokeTessellateShader(Mode mode,ShaderFlags shaderFlags,const SkMatrix & viewMatrix,const SkStrokeRec & stroke,SkPMColor4f color)103 GrStrokeTessellateShader(Mode mode, ShaderFlags shaderFlags, const SkMatrix& viewMatrix, 104 const SkStrokeRec& stroke, SkPMColor4f color) 105 : GrPathShader(kTessellate_GrStrokeTessellateShader_ClassID, viewMatrix, 106 (mode == Mode::kHardwareTessellation) ? 107 GrPrimitiveType::kPatches : GrPrimitiveType::kTriangleStrip, 108 (mode == Mode::kHardwareTessellation) ? 1 : 0) 109 , fMode(mode) 110 , fShaderFlags(shaderFlags) 111 , fStroke(stroke) 112 , fColor(color) { 113 if (fMode == Mode::kHardwareTessellation) { 114 // A join calculates its starting angle using prevCtrlPtAttr. 115 fAttribs.emplace_back("prevCtrlPtAttr", kFloat2_GrVertexAttribType, kFloat2_GrSLType); 116 // pts 0..3 define the stroke as a cubic bezier. If p3.y is infinity, then it's a conic 117 // with w=p3.x. 118 // 119 // If p0 == prevCtrlPtAttr, then no join is emitted. 120 // 121 // pts=[p0, p3, p3, p3] is a reserved pattern that means this patch is a join only, 122 // whose start and end tangents are (p0 - inputPrevCtrlPt) and (p3 - p0). 123 // 124 // pts=[p0, p0, p0, p3] is a reserved pattern that means this patch is a "bowtie", or 125 // double-sided round join, anchored on p0 and rotating from (p0 - prevCtrlPtAttr) to 126 // (p3 - p0). 127 fAttribs.emplace_back("pts01Attr", kFloat4_GrVertexAttribType, kFloat4_GrSLType); 128 fAttribs.emplace_back("pts23Attr", kFloat4_GrVertexAttribType, kFloat4_GrSLType); 129 } else { 130 // pts 0..3 define the stroke as a cubic bezier. If p3.y is infinity, then it's a conic 131 // with w=p3.x. 132 // 133 // An empty stroke (p0==p1==p2==p3) is a special case that denotes a circle, or 134 // 180-degree point stroke. 135 fAttribs.emplace_back("pts01Attr", kFloat4_GrVertexAttribType, kFloat4_GrSLType); 136 fAttribs.emplace_back("pts23Attr", kFloat4_GrVertexAttribType, kFloat4_GrSLType); 137 if (fMode == Mode::kLog2Indirect) { 138 // argsAttr.xy contains the lastControlPoint for setting up the join. 139 // 140 // "argsAttr.z=numTotalEdges" tells the shader the literal number of edges in the 141 // triangle strip being rendered (i.e., it should be vertexCount/2). If 142 // numTotalEdges is negative and the join type is "kRound", it also instructs the 143 // shader to only allocate one segment the preceding round join. 144 fAttribs.emplace_back("argsAttr", kFloat3_GrVertexAttribType, kFloat3_GrSLType); 145 } else { 146 SkASSERT(fMode == Mode::kFixedCount); 147 // argsAttr contains the lastControlPoint for setting up the join. 148 fAttribs.emplace_back("argsAttr", kFloat2_GrVertexAttribType, kFloat2_GrSLType); 149 } 150 } 151 if (fShaderFlags & ShaderFlags::kDynamicStroke) { 152 fAttribs.emplace_back("dynamicStrokeAttr", kFloat2_GrVertexAttribType, 153 kFloat2_GrSLType); 154 } 155 if (fShaderFlags & ShaderFlags::kDynamicColor) { 156 fAttribs.emplace_back("dynamicColorAttr", 157 (fShaderFlags & ShaderFlags::kWideColor) 158 ? kFloat4_GrVertexAttribType 159 : kUByte4_norm_GrVertexAttribType, 160 kHalf4_GrSLType); 161 } 162 if (fMode == Mode::kHardwareTessellation) { 163 this->setVertexAttributes(fAttribs.data(), fAttribs.count()); 164 } else { 165 this->setInstanceAttributes(fAttribs.data(), fAttribs.count()); 166 } 167 SkASSERT(fAttribs.count() <= kMaxAttribCount); 168 } 169 hasConics()170 bool hasConics() const { return fShaderFlags & ShaderFlags::kHasConics; } hasDynamicStroke()171 bool hasDynamicStroke() const { return fShaderFlags & ShaderFlags::kDynamicStroke; } hasDynamicColor()172 bool hasDynamicColor() const { return fShaderFlags & ShaderFlags::kDynamicColor; } 173 174 // Used by GrFixedCountTessellator to configure the uniform value that tells the shader how many 175 // total edges are in the triangle strip. setFixedCountNumTotalEdges(int value)176 void setFixedCountNumTotalEdges(int value) { 177 SkASSERT(fMode == Mode::kFixedCount); 178 fFixedCountNumTotalEdges = value; 179 } 180 181 private: name()182 const char* name() const override { return "GrStrokeTessellateShader"; } 183 void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override; 184 GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const final; 185 186 const Mode fMode; 187 const ShaderFlags fShaderFlags; 188 const SkStrokeRec fStroke; 189 const SkPMColor4f fColor; 190 191 constexpr static int kMaxAttribCount = 5; 192 SkSTArray<kMaxAttribCount, Attribute> fAttribs; 193 194 // This is a uniform value used when fMode is kFixedCount that tells the shader how many total 195 // edges are in the triangle strip. 196 float fFixedCountNumTotalEdges = 0; 197 198 class TessellationImpl; 199 class InstancedImpl; 200 }; 201 202 GR_MAKE_BITFIELD_CLASS_OPS(GrStrokeTessellateShader::ShaderFlags); 203 204 #endif 205