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 GrStrokeTessellationShader_DEFINED 9 #define GrStrokeTessellationShader_DEFINED 10 11 #include "src/gpu/tessellate/shaders/GrTessellationShader.h" 12 13 #include "include/core/SkStrokeRec.h" 14 #include "src/gpu/GrVx.h" 15 #include "src/gpu/glsl/GrGLSLVarying.h" 16 #include "src/gpu/tessellate/Tessellation.h" 17 18 // Tessellates a batch of stroke patches directly to the canvas. Tessellated stroking works by 19 // creating stroke-width, orthogonal edges at set locations along the curve and then connecting them 20 // with a quad strip. These orthogonal edges come from two different sets: "parametric edges" and 21 // "radial edges". Parametric edges are spaced evenly in the parametric sense, and radial edges 22 // divide the curve's _rotation_ into even steps. The tessellation shader evaluates both sets of 23 // edges and sorts them into a single quad strip. With this combined set of edges we can stroke any 24 // curve, regardless of curvature. 25 class GrStrokeTessellationShader : public GrTessellationShader { 26 using PatchAttribs = skgpu::PatchAttribs; 27 28 public: 29 // Are we using hardware tessellation or indirect draws? 30 enum class Mode : int8_t { 31 kHardwareTessellation, 32 kLog2Indirect, 33 kFixedCount 34 }; 35 36 // 'viewMatrix' is applied to the geometry post tessellation. It cannot have perspective. 37 GrStrokeTessellationShader(const GrShaderCaps&, Mode, PatchAttribs, const SkMatrix& viewMatrix, 38 const SkStrokeRec&, SkPMColor4f, int8_t maxParametricSegments_log2); 39 mode()40 Mode mode() const { return fMode; } attribs()41 PatchAttribs attribs() const { return fPatchAttribs; } hasDynamicStroke()42 bool hasDynamicStroke() const { return fPatchAttribs & PatchAttribs::kStrokeParams; } hasDynamicColor()43 bool hasDynamicColor() const { return fPatchAttribs & PatchAttribs::kColor; } hasExplicitCurveType()44 bool hasExplicitCurveType() const { return fPatchAttribs & PatchAttribs::kExplicitCurveType; } stroke()45 const SkStrokeRec& stroke() const { return fStroke;} maxParametricSegments_log2()46 int8_t maxParametricSegments_log2() const { return fMaxParametricSegments_log2; } fixedCountNumTotalEdges()47 float fixedCountNumTotalEdges() const { return fFixedCountNumTotalEdges;} 48 49 // Used by GrFixedCountTessellator to configure the uniform value that tells the shader how many 50 // total edges are in the triangle strip. setFixedCountNumTotalEdges(int value)51 void setFixedCountNumTotalEdges(int value) { 52 SkASSERT(fMode == Mode::kFixedCount); 53 fFixedCountNumTotalEdges = value; 54 } 55 56 SkString getShaderDfxInfo() const override; 57 58 private: name()59 const char* name() const override { 60 switch (fMode) { 61 case Mode::kHardwareTessellation: 62 return "GrStrokeTessellationShader_HardwareImpl"; 63 case Mode::kLog2Indirect: 64 case Mode::kFixedCount: 65 return "GrStrokeTessellationShader_InstancedImpl"; 66 } 67 SkUNREACHABLE; 68 } 69 void addToKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override; 70 std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const final; 71 72 const Mode fMode; 73 const PatchAttribs fPatchAttribs; 74 const SkStrokeRec fStroke; 75 const int8_t fMaxParametricSegments_log2; 76 77 constexpr static int kMaxAttribCount = 6; 78 SkSTArray<kMaxAttribCount, Attribute> fAttribs; 79 80 // This is a uniform value used when fMode is kFixedCount that tells the shader how many total 81 // edges are in the triangle strip. 82 float fFixedCountNumTotalEdges = 0; 83 84 class Impl; 85 class HardwareImpl; 86 class InstancedImpl; 87 }; 88 89 // This common base class emits shader code for our parametric/radial stroke tessellation algorithm 90 // described above. The subclass emits its own specific setup code before calling into 91 // emitTessellationCode and emitFragment code. 92 class GrStrokeTessellationShader::Impl : public ProgramImpl { 93 protected: 94 // float cosine_between_vectors(float2 a, float2 b) { ... 95 // 96 // Returns dot(a, b) / (length(a) * length(b)). 97 static const char* kCosineBetweenVectorsFn; 98 99 // float miter_extent(float cosTheta, float miterLimit) { ... 100 // 101 // Extends the middle radius to either the miter point, or the bevel edge if we surpassed the 102 // miter limit and need to revert to a bevel join. 103 static const char* kMiterExtentFn; 104 105 // float num_radial_segments_per_radian(float parametricPrecision, float strokeRadius) { ... 106 // 107 // Returns the number of radial segments required for each radian of rotation, in order for the 108 // curve to appear "smooth" as defined by the parametricPrecision. 109 static const char* kNumRadialSegmentsPerRadianFn; 110 111 // float<N> unchecked_mix(float<N> a, float<N> b, float<N> T) { ... 112 // 113 // Unlike mix(), this does not return b when t==1. But it otherwise seems to get better 114 // precision than "a*(1 - t) + b*t" for things like chopping cubics on exact cusp points. 115 // We override this result anyway when t==1 so it shouldn't be a problem. 116 static const char* kUncheckedMixFn; 117 118 // Emits code that calculates the vertex position and any other inputs to the fragment shader. 119 // The subclass is responsible to define the following symbols before calling this method: 120 // 121 // // Functions. 122 // float2 unchecked_mix(float2, float2, float); 123 // float unchecked_mix(float, float, float); 124 // 125 // // Values provided by either uniforms or attribs. 126 // float2 p0, p1, p2, p3; 127 // float w; 128 // float STROKE_RADIUS; 129 // float 2x2 AFFINE_MATRIX; 130 // float2 TRANSLATE; 131 // 132 // // Values calculated by the specific subclass. 133 // float combinedEdgeID; 134 // bool isFinalEdge; 135 // float numParametricSegments; 136 // float radsPerSegment; 137 // float2 tan0; 138 // float2 tan1; 139 // float strokeOutset; 140 // 141 void emitTessellationCode(const GrStrokeTessellationShader& shader, SkString* code, 142 GrGPArgs* gpArgs, const GrShaderCaps& shaderCaps) const; 143 144 // Emits all necessary fragment code. If using dynamic color, the impl is responsible to set up 145 // a half4 varying for color and provide its name in 'fDynamicColorName'. 146 void emitFragmentCode(const GrStrokeTessellationShader&, const EmitArgs&); 147 148 void setData(const GrGLSLProgramDataManager& pdman, const GrShaderCaps&, 149 const GrGeometryProcessor&) final; 150 151 GrGLSLUniformHandler::UniformHandle fTessControlArgsUniform; 152 GrGLSLUniformHandler::UniformHandle fTranslateUniform; 153 GrGLSLUniformHandler::UniformHandle fAffineMatrixUniform; 154 GrGLSLUniformHandler::UniformHandle fEdgeCountUniform; 155 GrGLSLUniformHandler::UniformHandle fColorUniform; 156 SkString fDynamicColorName; 157 }; 158 159 class GrStrokeTessellationShader::InstancedImpl : public GrStrokeTessellationShader::Impl { 160 void onEmitCode(EmitArgs&, GrGPArgs*) override; 161 }; 162 163 class GrStrokeTessellationShader::HardwareImpl : public GrStrokeTessellationShader::Impl { 164 void onEmitCode(EmitArgs&, GrGPArgs*) override; 165 SkString getTessControlShaderGLSL(const GrGeometryProcessor&, 166 const char* versionAndExtensionDecls, 167 const GrGLSLUniformHandler&, 168 const GrShaderCaps&) const override; 169 SkString getTessEvaluationShaderGLSL(const GrGeometryProcessor&, 170 const char* versionAndExtensionDecls, 171 const GrGLSLUniformHandler&, 172 const GrShaderCaps&) const override; 173 }; 174 175 #endif 176