• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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