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