• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2019 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 #include "src/gpu/tessellate/shaders/GrPathTessellationShader.h"
9 
10 #include "src/core/SkMathPriv.h"
11 #include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
12 #include "src/gpu/tessellate/PathTessellator.h"
13 #include "src/gpu/tessellate/Tessellation.h"
14 #include "src/gpu/tessellate/WangsFormula.h"
15 
16 using skgpu::PatchAttribs;
17 using skgpu::VertexWriter;
18 
19 namespace {
20 
21 // Uses instanced draws to triangulate standalone closed curves with a "middle-out" topology.
22 // Middle-out draws a triangle with vertices at T=[0, 1/2, 1] and then recurses breadth first:
23 //
24 //   depth=0: T=[0, 1/2, 1]
25 //   depth=1: T=[0, 1/4, 2/4], T=[2/4, 3/4, 1]
26 //   depth=2: T=[0, 1/8, 2/8], T=[2/8, 3/8, 4/8], T=[4/8, 5/8, 6/8], T=[6/8, 7/8, 1]
27 //   ...
28 //
29 // The shader determines how many segments are required to render each individual curve smoothly,
30 // and emits empty triangles at any vertices whose sk_VertexIDs are higher than necessary. It is the
31 // caller's responsibility to draw enough vertices per instance for the most complex curve in the
32 // batch to render smoothly (i.e., NumTrianglesAtResolveLevel() * 3).
33 class MiddleOutShader : public GrPathTessellationShader {
34 public:
MiddleOutShader(const GrShaderCaps & shaderCaps,const SkMatrix & viewMatrix,const SkPMColor4f & color,PatchAttribs attribs)35     MiddleOutShader(const GrShaderCaps& shaderCaps, const SkMatrix& viewMatrix,
36                     const SkPMColor4f& color, PatchAttribs attribs)
37             : GrPathTessellationShader(kTessellate_MiddleOutShader_ClassID,
38                                        GrPrimitiveType::kTriangles, 0, viewMatrix, color, attribs) {
39         fInstanceAttribs.emplace_back("p01", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
40         fInstanceAttribs.emplace_back("p23", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
41         if (fAttribs & PatchAttribs::kFanPoint) {
42             fInstanceAttribs.emplace_back("fanPointAttrib",
43                                           kFloat2_GrVertexAttribType,
44                                           kFloat2_GrSLType);
45         }
46         if (fAttribs & PatchAttribs::kColor) {
47             fInstanceAttribs.emplace_back("colorAttrib",
48                                           (fAttribs & PatchAttribs::kWideColorIfEnabled)
49                                                   ? kFloat4_GrVertexAttribType
50                                                   : kUByte4_norm_GrVertexAttribType,
51                                           kHalf4_GrSLType);
52         }
53         if (fAttribs & PatchAttribs::kExplicitCurveType) {
54             // A conic curve is written out with p3=[w,Infinity], but GPUs that don't support
55             // infinity can't detect this. On these platforms we also write out an extra float with
56             // each patch that explicitly tells the shader what type of curve it is.
57             fInstanceAttribs.emplace_back("curveType", kFloat_GrVertexAttribType, kFloat_GrSLType);
58         }
59         this->setInstanceAttributes(fInstanceAttribs.data(), fInstanceAttribs.count());
60         SkASSERT(fInstanceAttribs.count() <= kMaxInstanceAttribCount);
61         SkASSERT(this->instanceStride() ==
62                  sizeof(SkPoint) * 4 + skgpu::PatchAttribsStride(fAttribs));
63 
64         constexpr static Attribute kVertexAttrib("resolveLevel_and_idx", kFloat2_GrVertexAttribType,
65                                                  kFloat2_GrSLType);
66         this->setVertexAttributes(&kVertexAttrib, 1);
67     }
68 
maxTessellationSegments(const GrShaderCaps &) const69     int maxTessellationSegments(const GrShaderCaps&) const override {
70         return 1 << skgpu::PathTessellator::kMaxFixedResolveLevel;
71     }
72 
getShaderDfxInfo() const73     SkString getShaderDfxInfo() const override {
74         SkString format;
75         format.printf("ShaderDfx_MiddleOutShader_%d", fAttribs);
76         return format;
77     }
78 
79 private:
name() const80     const char* name() const final { return "tessellate_MiddleOutShader"; }
addToKey(const GrShaderCaps &,GrProcessorKeyBuilder * b) const81     void addToKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const final {
82         // When color is in a uniform, it's always wide so we need to ignore kWideColorIfEnabled.
83         // When color is in an attrib, its wideness is accounted for as part of the attrib key in
84         // GrGeometryProcessor::getAttributeKey().
85         // Either way, we get the correct key by ignoring .
86         b->add32((uint32_t)(fAttribs & ~PatchAttribs::kWideColorIfEnabled));
87     }
88     std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const final;
89 
90     constexpr static int kMaxInstanceAttribCount = 5;
91     SkSTArray<kMaxInstanceAttribCount, Attribute> fInstanceAttribs;
92 };
93 
makeProgramImpl(const GrShaderCaps &) const94 std::unique_ptr<GrGeometryProcessor::ProgramImpl> MiddleOutShader::makeProgramImpl(
95         const GrShaderCaps&) const {
96     class Impl : public GrPathTessellationShader::Impl {
97         void emitVertexCode(const GrShaderCaps& shaderCaps,
98                             const GrPathTessellationShader& shader,
99                             GrGLSLVertexBuilder* v,
100                             GrGLSLVaryingHandler* varyingHandler,
101                             GrGPArgs* gpArgs) override {
102             const MiddleOutShader& middleOutShader = shader.cast<MiddleOutShader>();
103             v->defineConstant("PRECISION", skgpu::kTessellationPrecision);
104             v->defineConstant("MAX_FIXED_RESOLVE_LEVEL",
105                               (float)skgpu::PathTessellator::kMaxFixedResolveLevel);
106             v->defineConstant("MAX_FIXED_SEGMENTS",
107                               (float)(1 << skgpu::PathTessellator::kMaxFixedResolveLevel));
108             v->insertFunction(skgpu::wangs_formula::as_sksl().c_str());
109             if (middleOutShader.fAttribs & PatchAttribs::kExplicitCurveType) {
110                 v->insertFunction(SkStringPrintf(R"(
111                 bool is_conic_curve() { return curveType != %g; })", kCubicCurveType).c_str());
112                 v->insertFunction(SkStringPrintf(R"(
113                 bool is_triangular_conic_curve() {
114                     return curveType == %g;
115                 })", kTriangularConicCurveType).c_str());
116             } else {
117                 SkASSERT(shaderCaps.infinitySupport());
118                 v->insertFunction(R"(
119                 bool is_conic_curve() { return isinf(p23.w); }
120                 bool is_triangular_conic_curve() { return isinf(p23.z); })");
121             }
122             if (shaderCaps.bitManipulationSupport()) {
123                 v->insertFunction(R"(
124                 float ldexp_portable(float x, float p) {
125                     return ldexp(x, int(p));
126                 })");
127             } else {
128                 v->insertFunction(R"(
129                 float ldexp_portable(float x, float p) {
130                     return x * exp2(p);
131                 })");
132             }
133             v->codeAppend(R"(
134             float resolveLevel = resolveLevel_and_idx.x;
135             float idxInResolveLevel = resolveLevel_and_idx.y;
136             float2 localcoord;)");
137             if (middleOutShader.fAttribs & PatchAttribs::kFanPoint) {
138                 v->codeAppend(R"(
139                 // A negative resolve level means this is the fan point.
140                 if (resolveLevel < 0) {
141                     localcoord = fanPointAttrib;
142                 } else)");  // Fall through to next if ().
143             }
144             v->codeAppend(R"(
145             if (is_triangular_conic_curve()) {
146                 // This patch is an exact triangle.
147                 localcoord = (resolveLevel != 0)      ? p01.zw
148                            : (idxInResolveLevel != 0) ? p23.xy
149                                                       : p01.xy;
150             } else {
151                 float2 p0=p01.xy, p1=p01.zw, p2=p23.xy, p3=p23.zw;
152                 float w = -1;  // w < 0 tells us to treat the instance as an integral cubic.
153                 float maxResolveLevel;
154                 if (is_conic_curve()) {
155                     // Conics are 3 points, with the weight in p3.
156                     w = p3.x;
157                     maxResolveLevel = wangs_formula_conic_log2(PRECISION, AFFINE_MATRIX * p0,
158                                                                           AFFINE_MATRIX * p1,
159                                                                           AFFINE_MATRIX * p2, w);
160                     p1 *= w;  // Unproject p1.
161                     p3 = p2;  // Duplicate the endpoint for shared code that also runs on cubics.
162                 } else {
163                     // The patch is an integral cubic.
164                     maxResolveLevel = wangs_formula_cubic_log2(PRECISION, p0, p1, p2, p3,
165                                                                AFFINE_MATRIX);
166                 }
167                 if (resolveLevel > maxResolveLevel) {
168                     // This vertex is at a higher resolve level than we need. Demote to a lower
169                     // resolveLevel, which will produce a degenerate triangle.
170                     idxInResolveLevel = floor(ldexp_portable(idxInResolveLevel,
171                                                              maxResolveLevel - resolveLevel));
172                     resolveLevel = maxResolveLevel;
173                 }
174                 // Promote our location to a discrete position in the maximum fixed resolve level.
175                 // This is extra paranoia to ensure we get the exact same fp32 coordinates for
176                 // colocated points from different resolve levels (e.g., the vertices T=3/4 and
177                 // T=6/8 should be exactly colocated).
178                 float fixedVertexID = floor(.5 + ldexp_portable(
179                         idxInResolveLevel, MAX_FIXED_RESOLVE_LEVEL - resolveLevel));
180                 if (0 < fixedVertexID && fixedVertexID < MAX_FIXED_SEGMENTS) {
181                     float T = fixedVertexID * (1 / MAX_FIXED_SEGMENTS);
182 
183                     // Evaluate at T. Use De Casteljau's for its accuracy and stability.
184                     float2 ab = mix(p0, p1, T);
185                     float2 bc = mix(p1, p2, T);
186                     float2 cd = mix(p2, p3, T);
187                     float2 abc = mix(ab, bc, T);
188                     float2 bcd = mix(bc, cd, T);
189                     float2 abcd = mix(abc, bcd, T);
190 
191                     // Evaluate the conic weight at T.
192                     float u = mix(1.0, w, T);
193                     float v = w + 1 - u;  // == mix(w, 1, T)
194                     float uv = mix(u, v, T);
195 
196                     localcoord = (w < 0) ? /*cubic*/ abcd : /*conic*/ abc/uv;
197                 } else {
198                     localcoord = (fixedVertexID == 0) ? p0.xy : p3.xy;
199                 }
200             }
201             float2 vertexpos = AFFINE_MATRIX * localcoord + TRANSLATE;)");
202             gpArgs->fLocalCoordVar.set(kFloat2_GrSLType, "localcoord");
203             gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertexpos");
204             if (middleOutShader.fAttribs & PatchAttribs::kColor) {
205                 GrGLSLVarying colorVarying(GrSLType::kHalf4_GrSLType);
206                 varyingHandler->addVarying("color",
207                                            &colorVarying,
208                                            GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
209                 v->codeAppendf("%s = colorAttrib;", colorVarying.vsOut());
210                 fVaryingColorName = colorVarying.fsIn();
211             }
212         }
213     };
214     return std::make_unique<Impl>();
215 }
216 
217 }  // namespace
218 
MakeMiddleOutFixedCountShader(const GrShaderCaps & shaderCaps,SkArenaAlloc * arena,const SkMatrix & viewMatrix,const SkPMColor4f & color,PatchAttribs attribs)219 GrPathTessellationShader* GrPathTessellationShader::MakeMiddleOutFixedCountShader(
220         const GrShaderCaps& shaderCaps,
221         SkArenaAlloc* arena,
222         const SkMatrix& viewMatrix,
223         const SkPMColor4f& color,
224         PatchAttribs attribs) {
225     // We should use explicit curve type when, and only when, there isn't infinity support.
226     // Otherwise the GPU can infer curve type based on infinity.
227     SkASSERT(shaderCaps.infinitySupport() != (attribs & PatchAttribs::kExplicitCurveType));
228     return arena->make<MiddleOutShader>(shaderCaps, viewMatrix, color, attribs);
229 }
230