• 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/GrStencilPathShader.h"
9 
10 #include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
11 #include "src/gpu/glsl/GrGLSLVarying.h"
12 #include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
13 
14 // Wang's formula for cubics (1985) gives us the number of evenly spaced (in the
15 // parametric sense) line segments that are guaranteed to be within a distance of
16 // "MAX_LINEARIZATION_ERROR" from the actual curve.
17 constexpr static char kWangsFormulaCubicFn[] = R"(
18 #define MAX_LINEARIZATION_ERROR 0.25  // 1/4 pixel
19 float length_pow2(vec2 v) {
20     return dot(v, v);
21 }
22 float wangs_formula_cubic(vec2 p0, vec2 p1, vec2 p2, vec2 p3) {
23     float k = (3.0 * 2.0) / (8.0 * MAX_LINEARIZATION_ERROR);
24     float m = max(length_pow2(-2.0*p1 + p2 + p0),
25                   length_pow2(-2.0*p2 + p3 + p1));
26     return max(1.0, ceil(sqrt(k * sqrt(m))));
27 })";
28 
29 constexpr static char kSkSLTypeDefs[] = R"(
30 #define float4x3 mat4x3
31 #define float3 vec3
32 #define float2 vec2
33 )";
34 
35 // Converts a 4-point input patch into the rational cubic it intended to represent.
36 constexpr static char kUnpackRationalCubicFn[] = R"(
37 float4x3 unpack_rational_cubic(float2 p0, float2 p1, float2 p2, float2 p3) {
38     float4x3 P = float4x3(p0,1, p1,1, p2,1, p3,1);
39     if (isinf(P[3].y)) {
40         // This patch is actually a conic. Convert to a rational cubic.
41         float w = P[3].x;
42         float3 c = P[1] * ((2.0/3.0) * w);
43         P = float4x3(P[0], fma(P[0], float3(1.0/3.0), c), fma(P[2], float3(1.0/3.0), c), P[2]);
44     }
45     return P;
46 })";
47 
48 // Evaluate our point of interest using numerically stable linear interpolations. We add our own
49 // "safe_mix" method to guarantee we get exactly "b" when T=1. The builtin mix() function seems
50 // spec'd to behave this way, but empirical results results have shown it does not always.
51 constexpr static char kEvalRationalCubicFn[] = R"(
52 float3 safe_mix(float3 a, float3 b, float T, float one_minus_T) {
53     return a*one_minus_T + b*T;
54 }
55 float2 eval_rational_cubic(float4x3 P, float T) {
56     float one_minus_T = 1.0 - T;
57     float3 ab = safe_mix(P[0], P[1], T, one_minus_T);
58     float3 bc = safe_mix(P[1], P[2], T, one_minus_T);
59     float3 cd = safe_mix(P[2], P[3], T, one_minus_T);
60     float3 abc = safe_mix(ab, bc, T, one_minus_T);
61     float3 bcd = safe_mix(bc, cd, T, one_minus_T);
62     float3 abcd = safe_mix(abc, bcd, T, one_minus_T);
63     return abcd.xy / abcd.z;
64 })";
65 
66 class GrStencilPathShader::Impl : public GrGLSLGeometryProcessor {
67 protected:
onEmitCode(EmitArgs & args,GrGPArgs * gpArgs)68     void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
69         const auto& shader = args.fGeomProc.cast<GrStencilPathShader>();
70         args.fVaryingHandler->emitAttributes(shader);
71         auto v = args.fVertBuilder;
72 
73         GrShaderVar vertexPos = (*shader.vertexAttributes().begin()).asShaderVar();
74         if (!shader.viewMatrix().isIdentity()) {
75             const char* viewMatrix;
76             fViewMatrixUniform = args.fUniformHandler->addUniform(
77                     nullptr, kVertex_GrShaderFlag, kFloat3x3_GrSLType, "view_matrix", &viewMatrix);
78             v->codeAppendf("float2 vertexpos = (%s * float3(inputPoint, 1)).xy;", viewMatrix);
79             if (shader.willUseTessellationShaders()) {
80                 // If y is infinity then x is a conic weight. Don't transform.
81                 v->codeAppendf("vertexpos = (isinf(vertexpos.y)) ? inputPoint : vertexpos;");
82             }
83             vertexPos.set(kFloat2_GrSLType, "vertexpos");
84         }
85 
86         if (!shader.willUseTessellationShaders()) {  // This is the case for the triangle shader.
87             gpArgs->fPositionVar = vertexPos;
88         } else {
89             v->declareGlobal(GrShaderVar("vsPt", kFloat2_GrSLType, GrShaderVar::TypeModifier::Out));
90             v->codeAppendf("vsPt = %s;", vertexPos.c_str());
91         }
92 
93         // No fragment shader.
94     }
95 
setData(const GrGLSLProgramDataManager & pdman,const GrShaderCaps &,const GrGeometryProcessor & geomProc)96     void setData(const GrGLSLProgramDataManager& pdman,
97                  const GrShaderCaps&,
98                  const GrGeometryProcessor& geomProc) override {
99         const auto& shader = geomProc.cast<GrStencilPathShader>();
100         if (!shader.viewMatrix().isIdentity()) {
101             pdman.setSkMatrix(fViewMatrixUniform, shader.viewMatrix());
102         }
103     }
104 
105     GrGLSLUniformHandler::UniformHandle fViewMatrixUniform;
106 };
107 
createGLSLInstance(const GrShaderCaps &) const108 GrGLSLGeometryProcessor* GrStencilPathShader::createGLSLInstance(const GrShaderCaps&) const {
109     return new Impl;
110 }
111 
createGLSLInstance(const GrShaderCaps &) const112 GrGLSLGeometryProcessor* GrCubicTessellateShader::createGLSLInstance(const GrShaderCaps&) const {
113     class CubicImpl : public GrStencilPathShader::Impl {
114         SkString getTessControlShaderGLSL(const GrGeometryProcessor&,
115                                           const char* versionAndExtensionDecls,
116                                           const GrGLSLUniformHandler&,
117                                           const GrShaderCaps&) const override {
118             SkString code(versionAndExtensionDecls);
119             code.append(kWangsFormulaCubicFn);
120             code.append(kSkSLTypeDefs);
121             code.append(kUnpackRationalCubicFn);
122             code.append(R"(
123             layout(vertices = 1) out;
124 
125             in vec2 vsPt[];
126             out vec4 X[];
127             out vec4 Y[];
128             out float w[];
129 
130             void main() {
131                 mat4x3 P = unpack_rational_cubic(vsPt[0], vsPt[1], vsPt[2], vsPt[3]);
132 
133                 // Chop the curve at T=1/2. Here we take advantage of the fact that a uniform scalar
134                 // has no effect on homogeneous coordinates in order to eval quickly at .5:
135                 //
136                 //    mix(p0, p1, .5) / mix(w0, w1, .5)
137                 //    == ((p0 + p1) * .5) / ((w0 + w1) * .5)
138                 //    == (p0 + p1) / (w0 + w1)
139                 //
140                 vec3 ab = P[0] + P[1];
141                 vec3 bc = P[1] + P[2];
142                 vec3 cd = P[2] + P[3];
143                 vec3 abc = ab + bc;
144                 vec3 bcd = bc + cd;
145                 vec3 abcd = abc + bcd;
146 
147                 // Calculate how many triangles we need to linearize each half of the curve. We
148                 // simply call Wang's formula for integral cubics with the down-projected points.
149                 // This appears to be an upper bound on what the actual number of subdivisions would
150                 // have been.
151                 float w0 = wangs_formula_cubic(P[0].xy, ab.xy/ab.z, abc.xy/abc.z, abcd.xy/abcd.z);
152                 float w1 = wangs_formula_cubic(abcd.xy/abcd.z, bcd.xy/bcd.z, cd.xy/cd.z, P[3].xy);
153 
154                 gl_TessLevelOuter[0] = w1;
155                 gl_TessLevelOuter[1] = 1.0;
156                 gl_TessLevelOuter[2] = w0;
157 
158                 // Changing the inner level to 1 when w0 == w1 == 1 collapses the entire patch to a
159                 // single triangle. Otherwise, we need an inner level of 2 so our curve triangles
160                 // have an interior point to originate from.
161                 gl_TessLevelInner[0] = min(max(w0, w1), 2.0);
162 
163                 X[gl_InvocationID /*== 0*/] = vec4(P[0].x, P[1].x, P[2].x, P[3].x);
164                 Y[gl_InvocationID /*== 0*/] = vec4(P[0].y, P[1].y, P[2].y, P[3].y);
165                 w[gl_InvocationID /*== 0*/] = P[1].z;
166             })");
167 
168             return code;
169         }
170 
171         SkString getTessEvaluationShaderGLSL(const GrGeometryProcessor&,
172                                              const char* versionAndExtensionDecls,
173                                              const GrGLSLUniformHandler&,
174                                              const GrShaderCaps&) const override {
175             SkString code(versionAndExtensionDecls);
176             code.append(kSkSLTypeDefs);
177             code.append(kEvalRationalCubicFn);
178             code.append(R"(
179             layout(triangles, equal_spacing, ccw) in;
180 
181             uniform vec4 sk_RTAdjust;
182 
183             in vec4 X[];
184             in vec4 Y[];
185             in float w[];
186 
187             void main() {
188                 // Locate our parametric point of interest. T ramps from [0..1/2] on the left edge
189                 // of the triangle, and [1/2..1] on the right. If we are the patch's interior
190                 // vertex, then we want T=1/2. Since the barycentric coords are (1/3, 1/3, 1/3) at
191                 // the interior vertex, the below fma() works in all 3 scenarios.
192                 float T = fma(.5, gl_TessCoord.y, gl_TessCoord.z);
193 
194                 mat4x3 P = transpose(mat3x4(X[0], Y[0], 1,w[0],w[0],1));
195                 vec2 vertexpos = eval_rational_cubic(P, T);
196                 if (all(notEqual(gl_TessCoord.xz, vec2(0)))) {
197                     // We are the interior point of the patch; center it inside [C(0), C(.5), C(1)].
198                     vertexpos = (P[0].xy + vertexpos + P[3].xy) / 3.0;
199                 }
200 
201                 gl_Position = vec4(vertexpos * sk_RTAdjust.xz + sk_RTAdjust.yw, 0.0, 1.0);
202             })");
203 
204             return code;
205         }
206     };
207 
208     return new CubicImpl;
209 }
210 
createGLSLInstance(const GrShaderCaps &) const211 GrGLSLGeometryProcessor* GrWedgeTessellateShader::createGLSLInstance(const GrShaderCaps&) const {
212     class WedgeImpl : public GrStencilPathShader::Impl {
213         SkString getTessControlShaderGLSL(const GrGeometryProcessor&,
214                                           const char* versionAndExtensionDecls,
215                                           const GrGLSLUniformHandler&,
216                                           const GrShaderCaps&) const override {
217             SkString code(versionAndExtensionDecls);
218             code.append(kWangsFormulaCubicFn);
219             code.append(kSkSLTypeDefs);
220             code.append(kUnpackRationalCubicFn);
221             code.append(R"(
222             layout(vertices = 1) out;
223 
224             in vec2 vsPt[];
225             out vec4 X[];
226             out vec4 Y[];
227             out float w[];
228             out vec2 fanpoint[];
229 
230             void main() {
231                 mat4x3 P = unpack_rational_cubic(vsPt[0], vsPt[1], vsPt[2], vsPt[3]);
232 
233                 // Figure out how many segments to divide the curve into. To do this we simply call
234                 // Wang's formula for integral cubics with the down-projected points. This appears
235                 // to be an upper bound on what the actual number of subdivisions would have been.
236                 float num_segments = wangs_formula_cubic(P[0].xy, P[1].xy/P[1].z, P[2].xy/P[2].z,
237                                                          P[3].xy);
238 
239                 // Tessellate the first side of the patch into num_segments triangles.
240                 gl_TessLevelOuter[0] = num_segments;
241 
242                 // Leave the other two sides of the patch as single segments.
243                 gl_TessLevelOuter[1] = 1.0;
244                 gl_TessLevelOuter[2] = 1.0;
245 
246                 // Changing the inner level to 1 when num_segments == 1 collapses the entire
247                 // patch to a single triangle. Otherwise, we need an inner level of 2 so our curve
248                 // triangles have an interior point to originate from.
249                 gl_TessLevelInner[0] = min(num_segments, 2.0);
250 
251                 X[gl_InvocationID /*== 0*/] = vec4(P[0].x, P[1].x, P[2].x, P[3].x);
252                 Y[gl_InvocationID /*== 0*/] = vec4(P[0].y, P[1].y, P[2].y, P[3].y);
253                 w[gl_InvocationID /*== 0*/] = P[1].z;
254                 fanpoint[gl_InvocationID /*== 0*/] = vsPt[4];
255             })");
256 
257             return code;
258         }
259 
260         SkString getTessEvaluationShaderGLSL(const GrGeometryProcessor&,
261                                              const char* versionAndExtensionDecls,
262                                              const GrGLSLUniformHandler&,
263                                              const GrShaderCaps&) const override {
264             SkString code(versionAndExtensionDecls);
265             code.append(kSkSLTypeDefs);
266             code.append(kEvalRationalCubicFn);
267             code.append(R"(
268             layout(triangles, equal_spacing, ccw) in;
269 
270             uniform vec4 sk_RTAdjust;
271 
272             in vec4 X[];
273             in vec4 Y[];
274             in float w[];
275             in vec2 fanpoint[];
276 
277             void main() {
278                 // Locate our parametric point of interest. It is equal to the barycentric
279                 // y-coordinate if we are a vertex on the tessellated edge of the triangle patch,
280                 // 0.5 if we are the patch's interior vertex, or N/A if we are the fan point.
281                 // NOTE: We are on the tessellated edge when the barycentric x-coordinate == 0.
282                 float T = (gl_TessCoord.x == 0.0) ? gl_TessCoord.y : 0.5;
283 
284                 mat4x3 P = transpose(mat3x4(X[0], Y[0], 1,w[0],w[0],1));
285                 vec2 vertexpos = eval_rational_cubic(P, T);
286 
287                 if (gl_TessCoord.x == 1.0) {
288                     // We are the anchor point that fans from the center of the curve's contour.
289                     vertexpos = fanpoint[0];
290                 } else if (gl_TessCoord.x != 0.0) {
291                     // We are the interior point of the patch; center it inside [C(0), C(.5), C(1)].
292                     vertexpos = (P[0].xy + vertexpos + P[3].xy) / 3.0;
293                 }
294 
295                 gl_Position = vec4(vertexpos * sk_RTAdjust.xz + sk_RTAdjust.yw, 0.0, 1.0);
296             })");
297 
298             return code;
299         }
300     };
301 
302     return new WedgeImpl;
303 }
304 
305 constexpr static int kMaxResolveLevel = GrTessellationPathRenderer::kMaxResolveLevel;
306 
307 GR_DECLARE_STATIC_UNIQUE_KEY(gMiddleOutIndexBufferKey);
308 
FindOrMakeMiddleOutIndexBuffer(GrResourceProvider * resourceProvider)309 sk_sp<const GrGpuBuffer> GrMiddleOutCubicShader::FindOrMakeMiddleOutIndexBuffer(
310         GrResourceProvider* resourceProvider) {
311     GR_DEFINE_STATIC_UNIQUE_KEY(gMiddleOutIndexBufferKey);
312     if (auto buffer = resourceProvider->findByUniqueKey<GrGpuBuffer>(gMiddleOutIndexBufferKey)) {
313         return std::move(buffer);
314     }
315 
316     // One explicit triangle at index 0, and one middle-out cubic with kMaxResolveLevel line
317     // segments beginning at index 3.
318     constexpr static int kIndexCount = 3 + NumVerticesAtResolveLevel(kMaxResolveLevel);
319     auto buffer = resourceProvider->createBuffer(
320             kIndexCount * sizeof(uint16_t), GrGpuBufferType::kIndex, kStatic_GrAccessPattern);
321     if (!buffer) {
322         return nullptr;
323     }
324 
325     // We shouldn't bin and/or cache static buffers.
326     SkASSERT(buffer->size() == kIndexCount * sizeof(uint16_t));
327     SkASSERT(!buffer->resourcePriv().getScratchKey().isValid());
328     auto indexData = static_cast<uint16_t*>(buffer->map());
329     SkAutoTMalloc<uint16_t> stagingBuffer;
330     if (!indexData) {
331         SkASSERT(!buffer->isMapped());
332         indexData = stagingBuffer.reset(kIndexCount);
333     }
334 
335     // Indices 0,1,2 contain special values that emit points P0, P1, and P2 respectively. (When the
336     // vertex shader is fed an index value larger than (1 << kMaxResolveLevel), it emits
337     // P[index % 4].)
338     int i = 0;
339     indexData[i++] = (1 << kMaxResolveLevel) + 4;  // % 4 == 0
340     indexData[i++] = (1 << kMaxResolveLevel) + 5;  // % 4 == 1
341     indexData[i++] = (1 << kMaxResolveLevel) + 6;  // % 4 == 2
342 
343     // Starting at index 3, we triangulate a cubic with 2^kMaxResolveLevel line segments. Each
344     // index value corresponds to parametric value T=(index / 2^kMaxResolveLevel). Since the
345     // triangles are arranged in "middle-out" order, we will be able to conveniently control the
346     // resolveLevel by changing only the indexCount.
347     for (uint16_t advance = 1 << (kMaxResolveLevel - 1); advance; advance >>= 1) {
348         uint16_t T = 0;
349         do {
350             indexData[i++] = T;
351             indexData[i++] = (T += advance);
352             indexData[i++] = (T += advance);
353         } while (T != (1 << kMaxResolveLevel));
354     }
355     SkASSERT(i == kIndexCount);
356 
357     if (buffer->isMapped()) {
358         buffer->unmap();
359     } else {
360         buffer->updateData(stagingBuffer, kIndexCount * sizeof(uint16_t));
361     }
362     buffer->resourcePriv().setUniqueKey(gMiddleOutIndexBufferKey);
363     return std::move(buffer);
364 }
365 
366 class GrMiddleOutCubicShader::Impl : public GrStencilPathShader::Impl {
onEmitCode(EmitArgs & args,GrGPArgs * gpArgs)367     void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
368         const auto& shader = args.fGeomProc.cast<GrMiddleOutCubicShader>();
369         args.fVaryingHandler->emitAttributes(shader);
370         args.fVertBuilder->defineConstantf("int", "kMaxVertexID", "%i", 1 << kMaxResolveLevel);
371         args.fVertBuilder->defineConstantf("float", "kInverseMaxVertexID",
372                                            "(1.0 / float(kMaxVertexID))");
373         args.fVertBuilder->insertFunction(kUnpackRationalCubicFn);
374         args.fVertBuilder->insertFunction(kEvalRationalCubicFn);
375         args.fVertBuilder->codeAppend(R"(
376         float2 pos;
377         if (sk_VertexID > kMaxVertexID) {
378             // This is a special index value that instructs us to emit a specific point.
379             pos = ((sk_VertexID & 3) == 0) ? inputPoints_0_1.xy :
380                   ((sk_VertexID & 2) == 0) ? inputPoints_0_1.zw : inputPoints_2_3.xy;
381         } else {
382             // Evaluate the cubic at T = (sk_VertexID / 2^kMaxResolveLevel).
383             float T = float(sk_VertexID) * kInverseMaxVertexID;
384             float4x3 P = unpack_rational_cubic(inputPoints_0_1.xy, inputPoints_0_1.zw,
385                                                inputPoints_2_3.xy, inputPoints_2_3.zw);
386             pos = eval_rational_cubic(P, T);
387         })");
388 
389         GrShaderVar vertexPos("pos", kFloat2_GrSLType);
390         if (!shader.viewMatrix().isIdentity()) {
391             const char* viewMatrix;
392             fViewMatrixUniform = args.fUniformHandler->addUniform(
393                     nullptr, kVertex_GrShaderFlag, kFloat3x3_GrSLType, "view_matrix", &viewMatrix);
394             args.fVertBuilder->codeAppendf(R"(
395             float2 transformedPoint = (%s * float3(pos, 1)).xy;)", viewMatrix);
396             vertexPos.set(kFloat2_GrSLType, "transformedPoint");
397         }
398         gpArgs->fPositionVar = vertexPos;
399         // No fragment shader.
400     }
401 };
402 
createGLSLInstance(const GrShaderCaps &) const403 GrGLSLGeometryProcessor* GrMiddleOutCubicShader::createGLSLInstance(const GrShaderCaps&) const {
404     return new Impl;
405 }
406