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