| 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 wangs_formula_cubic(vec2 p0, vec2 p1, vec2 p2, vec2 p3) { | 
|---|
| 20 |             float k = (3.0 * 2.0) / (8.0 * MAX_LINEARIZATION_ERROR); | 
|---|
| 21 |             float f = sqrt(k * length(max(abs(p2 - p1*2.0 + p0), | 
|---|
| 22 |                                           abs(p3 - p2*2.0 + p1)))); | 
|---|
| 23 |             return max(1.0, ceil(f)); | 
|---|
| 24 |         })"; | 
|---|
| 25 |  | 
|---|
| 26 | // Evaluate our point of interest using numerically stable mix() operations. | 
|---|
| 27 | constexpr static char kEvalCubicFn[] = R"( | 
|---|
| 28 |         vec2 eval_cubic(mat4x2 P, float T) { | 
|---|
| 29 |             vec2 ab = mix(P[0], P[1], T); | 
|---|
| 30 |             vec2 bc = mix(P[1], P[2], T); | 
|---|
| 31 |             vec2 cd = mix(P[2], P[3], T); | 
|---|
| 32 |             vec2 abc = mix(ab, bc, T); | 
|---|
| 33 |             vec2 bcd = mix(bc, cd, T); | 
|---|
| 34 |             return mix(abc, bcd, T); | 
|---|
| 35 |         })"; | 
|---|
| 36 |  | 
|---|
| 37 | class GrStencilPathShader::Impl : public GrGLSLGeometryProcessor { | 
|---|
| 38 | protected: | 
|---|
| 39 | void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { | 
|---|
| 40 | const auto& shader = args.fGP.cast<GrStencilPathShader>(); | 
|---|
| 41 | args.fVaryingHandler->emitAttributes(shader); | 
|---|
| 42 |  | 
|---|
| 43 | GrShaderVar vertexPos = (*shader.vertexAttributes().begin()).asShaderVar(); | 
|---|
| 44 | if (!shader.viewMatrix().isIdentity()) { | 
|---|
| 45 | const char* viewMatrix; | 
|---|
| 46 | fViewMatrixUniform = args.fUniformHandler->addUniform( | 
|---|
| 47 | nullptr, kVertex_GrShaderFlag, kFloat3x3_GrSLType, "view_matrix", &viewMatrix); | 
|---|
| 48 | args.fVertBuilder->codeAppendf( | 
|---|
| 49 | "float2 vertexpos = (%s * float3(inputPoint, 1)).xy;", viewMatrix); | 
|---|
| 50 | vertexPos.set(kFloat2_GrSLType, "vertexpos"); | 
|---|
| 51 | } | 
|---|
| 52 |  | 
|---|
| 53 | if (!shader.willUseTessellationShaders()) { | 
|---|
| 54 | gpArgs->fPositionVar = vertexPos; | 
|---|
| 55 | } else { | 
|---|
| 56 | args.fVertBuilder->declareGlobal(GrShaderVar( | 
|---|
| 57 | "P", kFloat2_GrSLType, GrShaderVar::TypeModifier::Out)); | 
|---|
| 58 | args.fVertBuilder->codeAppendf( "P = %s;", vertexPos.c_str()); | 
|---|
| 59 | } | 
|---|
| 60 |  | 
|---|
| 61 | // No fragment shader. | 
|---|
| 62 | } | 
|---|
| 63 |  | 
|---|
| 64 | void setData(const GrGLSLProgramDataManager& pdman, | 
|---|
| 65 | const GrPrimitiveProcessor& primProc) override { | 
|---|
| 66 | const auto& shader = primProc.cast<GrStencilPathShader>(); | 
|---|
| 67 | if (!shader.viewMatrix().isIdentity()) { | 
|---|
| 68 | pdman.setSkMatrix(fViewMatrixUniform, shader.viewMatrix()); | 
|---|
| 69 | } | 
|---|
| 70 | } | 
|---|
| 71 |  | 
|---|
| 72 | GrGLSLUniformHandler::UniformHandle fViewMatrixUniform; | 
|---|
| 73 | }; | 
|---|
| 74 |  | 
|---|
| 75 | GrGLSLPrimitiveProcessor* GrStencilPathShader::createGLSLInstance(const GrShaderCaps&) const { | 
|---|
| 76 | return new Impl; | 
|---|
| 77 | } | 
|---|
| 78 |  | 
|---|
| 79 | SkString GrCubicTessellateShader::getTessControlShaderGLSL(const GrGLSLPrimitiveProcessor*, | 
|---|
| 80 | const char* versionAndExtensionDecls, | 
|---|
| 81 | const GrGLSLUniformHandler&, | 
|---|
| 82 | const GrShaderCaps&) const { | 
|---|
| 83 | SkString code(versionAndExtensionDecls); | 
|---|
| 84 | code.append(kWangsFormulaCubicFn); | 
|---|
| 85 | code.append( R"( | 
|---|
| 86 |             layout(vertices = 1) out; | 
|---|
| 87 |  | 
|---|
| 88 |             in vec2 P[]; | 
|---|
| 89 |             out vec4 X[]; | 
|---|
| 90 |             out vec4 Y[]; | 
|---|
| 91 |  | 
|---|
| 92 |             void main() { | 
|---|
| 93 |                 // Chop the curve at T=1/2. | 
|---|
| 94 |                 vec2 ab = mix(P[0], P[1], .5); | 
|---|
| 95 |                 vec2 bc = mix(P[1], P[2], .5); | 
|---|
| 96 |                 vec2 cd = mix(P[2], P[3], .5); | 
|---|
| 97 |                 vec2 abc = mix(ab, bc, .5); | 
|---|
| 98 |                 vec2 bcd = mix(bc, cd, .5); | 
|---|
| 99 |                 vec2 abcd = mix(abc, bcd, .5); | 
|---|
| 100 |  | 
|---|
| 101 |                 // Calculate how many triangles we need to linearize each half of the curve. | 
|---|
| 102 |                 float l0 = wangs_formula_cubic(P[0], ab, abc, abcd); | 
|---|
| 103 |                 float l1 = wangs_formula_cubic(abcd, bcd, cd, P[3]); | 
|---|
| 104 |  | 
|---|
| 105 |                 gl_TessLevelOuter[0] = l1; | 
|---|
| 106 |                 gl_TessLevelOuter[1] = 1.0; | 
|---|
| 107 |                 gl_TessLevelOuter[2] = l0; | 
|---|
| 108 |  | 
|---|
| 109 |                 // Changing the inner level to 1 when l0 == l1 == 1 collapses the entire patch to a | 
|---|
| 110 |                 // single triangle. Otherwise, we need an inner level of 2 so our curve triangles | 
|---|
| 111 |                 // have an interior point to originate from. | 
|---|
| 112 |                 gl_TessLevelInner[0] = min(max(l0, l1), 2.0); | 
|---|
| 113 |  | 
|---|
| 114 |                 X[gl_InvocationID /*== 0*/] = vec4(P[0].x, P[1].x, P[2].x, P[3].x); | 
|---|
| 115 |                 Y[gl_InvocationID /*== 0*/] = vec4(P[0].y, P[1].y, P[2].y, P[3].y); | 
|---|
| 116 |             })"); | 
|---|
| 117 |  | 
|---|
| 118 | return code; | 
|---|
| 119 | } | 
|---|
| 120 |  | 
|---|
| 121 | SkString GrCubicTessellateShader::getTessEvaluationShaderGLSL( | 
|---|
| 122 | const GrGLSLPrimitiveProcessor*, const char* versionAndExtensionDecls, | 
|---|
| 123 | const GrGLSLUniformHandler&, const GrShaderCaps&) const { | 
|---|
| 124 | SkString code(versionAndExtensionDecls); | 
|---|
| 125 | code.append(kEvalCubicFn); | 
|---|
| 126 | code.append( R"( | 
|---|
| 127 |             layout(triangles, equal_spacing, ccw) in; | 
|---|
| 128 |  | 
|---|
| 129 |             uniform vec4 sk_RTAdjust; | 
|---|
| 130 |  | 
|---|
| 131 |             in vec4 X[]; | 
|---|
| 132 |             in vec4 Y[]; | 
|---|
| 133 |  | 
|---|
| 134 |             void main() { | 
|---|
| 135 |                 // Locate our parametric point of interest. T ramps from [0..1/2] on the left edge | 
|---|
| 136 |                 // of the triangle, and [1/2..1] on the right. If we are the patch's interior | 
|---|
| 137 |                 // vertex, then we want T=1/2. Since the barycentric coords are (1/3, 1/3, 1/3) at | 
|---|
| 138 |                 // the interior vertex, the below fma() works in all 3 scenarios. | 
|---|
| 139 |                 float T = fma(.5, gl_TessCoord.y, gl_TessCoord.z); | 
|---|
| 140 |  | 
|---|
| 141 |                 mat4x2 P = transpose(mat2x4(X[0], Y[0])); | 
|---|
| 142 |                 vec2 vertexpos = eval_cubic(P, T); | 
|---|
| 143 |                 if (all(notEqual(gl_TessCoord.xz, vec2(0)))) { | 
|---|
| 144 |                     // We are the interior point of the patch; center it inside [C(0), C(.5), C(1)]. | 
|---|
| 145 |                     vertexpos = (P[0] + vertexpos + P[3]) / 3.0; | 
|---|
| 146 |                 } | 
|---|
| 147 |  | 
|---|
| 148 |                 gl_Position = vec4(vertexpos * sk_RTAdjust.xz + sk_RTAdjust.yw, 0.0, 1.0); | 
|---|
| 149 |             })"); | 
|---|
| 150 |  | 
|---|
| 151 | return code; | 
|---|
| 152 | } | 
|---|
| 153 |  | 
|---|
| 154 | SkString GrWedgeTessellateShader::getTessControlShaderGLSL(const GrGLSLPrimitiveProcessor*, | 
|---|
| 155 | const char* versionAndExtensionDecls, | 
|---|
| 156 | const GrGLSLUniformHandler&, | 
|---|
| 157 | const GrShaderCaps&) const { | 
|---|
| 158 | SkString code(versionAndExtensionDecls); | 
|---|
| 159 | code.append(kWangsFormulaCubicFn); | 
|---|
| 160 | code.append( R"( | 
|---|
| 161 |             layout(vertices = 1) out; | 
|---|
| 162 |  | 
|---|
| 163 |             in vec2 P[]; | 
|---|
| 164 |             out vec4 X[]; | 
|---|
| 165 |             out vec4 Y[]; | 
|---|
| 166 |             out vec2 fanpoint[]; | 
|---|
| 167 |  | 
|---|
| 168 |             void main() { | 
|---|
| 169 |                 // Calculate how many triangles we need to linearize the curve. | 
|---|
| 170 |                 float num_segments = wangs_formula_cubic(P[0], P[1], P[2], P[3]); | 
|---|
| 171 |  | 
|---|
| 172 |                 // Tessellate the first side of the patch into num_segments triangles. | 
|---|
| 173 |                 gl_TessLevelOuter[0] = num_segments; | 
|---|
| 174 |  | 
|---|
| 175 |                 // Leave the other two sides of the patch as single segments. | 
|---|
| 176 |                 gl_TessLevelOuter[1] = 1.0; | 
|---|
| 177 |                 gl_TessLevelOuter[2] = 1.0; | 
|---|
| 178 |  | 
|---|
| 179 |                 // Changing the inner level to 1 when num_segments == 1 collapses the entire | 
|---|
| 180 |                 // patch to a single triangle. Otherwise, we need an inner level of 2 so our curve | 
|---|
| 181 |                 // triangles have an interior point to originate from. | 
|---|
| 182 |                 gl_TessLevelInner[0] = min(num_segments, 2.0); | 
|---|
| 183 |  | 
|---|
| 184 |                 X[gl_InvocationID /*== 0*/] = vec4(P[0].x, P[1].x, P[2].x, P[3].x); | 
|---|
| 185 |                 Y[gl_InvocationID /*== 0*/] = vec4(P[0].y, P[1].y, P[2].y, P[3].y); | 
|---|
| 186 |                 fanpoint[gl_InvocationID /*== 0*/] = P[4]; | 
|---|
| 187 |             })"); | 
|---|
| 188 |  | 
|---|
| 189 | return code; | 
|---|
| 190 | } | 
|---|
| 191 |  | 
|---|
| 192 | SkString GrWedgeTessellateShader::getTessEvaluationShaderGLSL( | 
|---|
| 193 | const GrGLSLPrimitiveProcessor*, const char* versionAndExtensionDecls, | 
|---|
| 194 | const GrGLSLUniformHandler&, const GrShaderCaps&) const { | 
|---|
| 195 | SkString code(versionAndExtensionDecls); | 
|---|
| 196 | code.append(kEvalCubicFn); | 
|---|
| 197 | code.append( R"( | 
|---|
| 198 |             layout(triangles, equal_spacing, ccw) in; | 
|---|
| 199 |  | 
|---|
| 200 |             uniform vec4 sk_RTAdjust; | 
|---|
| 201 |  | 
|---|
| 202 |             in vec4 X[]; | 
|---|
| 203 |             in vec4 Y[]; | 
|---|
| 204 |             in vec2 fanpoint[]; | 
|---|
| 205 |  | 
|---|
| 206 |             void main() { | 
|---|
| 207 |                 // Locate our parametric point of interest. It is equal to the barycentric | 
|---|
| 208 |                 // y-coordinate if we are a vertex on the tessellated edge of the triangle patch, | 
|---|
| 209 |                 // 0.5 if we are the patch's interior vertex, or N/A if we are the fan point. | 
|---|
| 210 |                 // NOTE: We are on the tessellated edge when the barycentric x-coordinate == 0. | 
|---|
| 211 |                 float T = (gl_TessCoord.x == 0.0) ? gl_TessCoord.y : 0.5; | 
|---|
| 212 |  | 
|---|
| 213 |                 mat4x2 P = transpose(mat2x4(X[0], Y[0])); | 
|---|
| 214 |                 vec2 vertexpos = eval_cubic(P, T); | 
|---|
| 215 |                 if (gl_TessCoord.x == 1.0) { | 
|---|
| 216 |                     // We are the anchor point that fans from the center of the curve's contour. | 
|---|
| 217 |                     vertexpos = fanpoint[0]; | 
|---|
| 218 |                 } else if (gl_TessCoord.x != 0.0) { | 
|---|
| 219 |                     // We are the interior point of the patch; center it inside [C(0), C(.5), C(1)]. | 
|---|
| 220 |                     vertexpos = (P[0] + vertexpos + P[3]) / 3.0; | 
|---|
| 221 |                 } | 
|---|
| 222 |  | 
|---|
| 223 |                 gl_Position = vec4(vertexpos * sk_RTAdjust.xz + sk_RTAdjust.yw, 0.0, 1.0); | 
|---|
| 224 |             })"); | 
|---|
| 225 |  | 
|---|
| 226 | return code; | 
|---|
| 227 | } | 
|---|
| 228 |  | 
|---|
| 229 | constexpr static int kMaxResolveLevel = GrTessellationPathRenderer::kMaxResolveLevel; | 
|---|
| 230 |  | 
|---|
| 231 | GR_DECLARE_STATIC_UNIQUE_KEY(gMiddleOutIndexBufferKey); | 
|---|
| 232 |  | 
|---|
| 233 | sk_sp<const GrGpuBuffer> GrMiddleOutCubicShader::FindOrMakeMiddleOutIndexBuffer( | 
|---|
| 234 | GrResourceProvider* resourceProvider) { | 
|---|
| 235 | GR_DEFINE_STATIC_UNIQUE_KEY(gMiddleOutIndexBufferKey); | 
|---|
| 236 | if (auto buffer = resourceProvider->findByUniqueKey<GrGpuBuffer>(gMiddleOutIndexBufferKey)) { | 
|---|
| 237 | return std::move(buffer); | 
|---|
| 238 | } | 
|---|
| 239 |  | 
|---|
| 240 | // One explicit triangle at index 0, and one middle-out cubic with kMaxResolveLevel line | 
|---|
| 241 | // segments beginning at index 3. | 
|---|
| 242 | constexpr static int kIndexCount = 3 + NumVerticesAtResolveLevel(kMaxResolveLevel); | 
|---|
| 243 | auto buffer = resourceProvider->createBuffer( | 
|---|
| 244 | kIndexCount * sizeof(uint16_t), GrGpuBufferType::kIndex, kStatic_GrAccessPattern); | 
|---|
| 245 | if (!buffer) { | 
|---|
| 246 | return nullptr; | 
|---|
| 247 | } | 
|---|
| 248 |  | 
|---|
| 249 | // We shouldn't bin and/or cache static buffers. | 
|---|
| 250 | SkASSERT(buffer->size() == kIndexCount * sizeof(uint16_t)); | 
|---|
| 251 | SkASSERT(!buffer->resourcePriv().getScratchKey().isValid()); | 
|---|
| 252 | auto indexData = static_cast<uint16_t*>(buffer->map()); | 
|---|
| 253 | SkAutoTMalloc<uint16_t> stagingBuffer; | 
|---|
| 254 | if (!indexData) { | 
|---|
| 255 | SkASSERT(!buffer->isMapped()); | 
|---|
| 256 | indexData = stagingBuffer.reset(kIndexCount); | 
|---|
| 257 | } | 
|---|
| 258 |  | 
|---|
| 259 | // Indices 0,1,2 contain special values that emit points P0, P1, and P2 respectively. (When the | 
|---|
| 260 | // vertex shader is fed an index value larger than (1 << kMaxResolveLevel), it emits | 
|---|
| 261 | // P[index % 4].) | 
|---|
| 262 | int i = 0; | 
|---|
| 263 | indexData[i++] = (1 << kMaxResolveLevel) + 4;  // % 4 == 0 | 
|---|
| 264 | indexData[i++] = (1 << kMaxResolveLevel) + 5;  // % 4 == 1 | 
|---|
| 265 | indexData[i++] = (1 << kMaxResolveLevel) + 6;  // % 4 == 2 | 
|---|
| 266 |  | 
|---|
| 267 | // Starting at index 3, we triangulate a cubic with 2^kMaxResolveLevel line segments. Each | 
|---|
| 268 | // index value corresponds to parametric value T=(index / 2^kMaxResolveLevel). Since the | 
|---|
| 269 | // triangles are arranged in "middle-out" order, we will be able to conveniently control the | 
|---|
| 270 | // resolveLevel by changing only the indexCount. | 
|---|
| 271 | for (uint16_t advance = 1 << (kMaxResolveLevel - 1); advance; advance >>= 1) { | 
|---|
| 272 | uint16_t T = 0; | 
|---|
| 273 | do { | 
|---|
| 274 | indexData[i++] = T; | 
|---|
| 275 | indexData[i++] = (T += advance); | 
|---|
| 276 | indexData[i++] = (T += advance); | 
|---|
| 277 | } while (T != (1 << kMaxResolveLevel)); | 
|---|
| 278 | } | 
|---|
| 279 | SkASSERT(i == kIndexCount); | 
|---|
| 280 |  | 
|---|
| 281 | if (buffer->isMapped()) { | 
|---|
| 282 | buffer->unmap(); | 
|---|
| 283 | } else { | 
|---|
| 284 | buffer->updateData(stagingBuffer, kIndexCount * sizeof(uint16_t)); | 
|---|
| 285 | } | 
|---|
| 286 | buffer->resourcePriv().setUniqueKey(gMiddleOutIndexBufferKey); | 
|---|
| 287 | return std::move(buffer); | 
|---|
| 288 | } | 
|---|
| 289 |  | 
|---|
| 290 | class GrMiddleOutCubicShader::Impl : public GrStencilPathShader::Impl { | 
|---|
| 291 | void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { | 
|---|
| 292 | const auto& shader = args.fGP.cast<GrMiddleOutCubicShader>(); | 
|---|
| 293 | args.fVaryingHandler->emitAttributes(shader); | 
|---|
| 294 | args.fVertBuilder->defineConstant( "kMaxResolveLevel", kMaxResolveLevel); | 
|---|
| 295 | args.fVertBuilder->codeAppend( R"( | 
|---|
| 296 |                 float4x2 P = float4x2(inputPoints_0_1, inputPoints_2_3); | 
|---|
| 297 |                 float2 point; | 
|---|
| 298 |                 if (sk_VertexID > (1 << kMaxResolveLevel)) { | 
|---|
| 299 |                     // This is a special index value that wants us to emit a specific point. | 
|---|
| 300 |                     point = P[sk_VertexID & 3]; | 
|---|
| 301 |                 } else {)"); | 
|---|
| 302 | // Evaluate the cubic at T=(sk_VertexID / 2^kMaxResolveLevel). | 
|---|
| 303 | if (args.fShaderCaps->fpManipulationSupport()) { | 
|---|
| 304 | args.fVertBuilder->codeAppend( R"( | 
|---|
| 305 |                     float T = ldexp(sk_VertexID, -kMaxResolveLevel);)"); | 
|---|
| 306 | } else { | 
|---|
| 307 | args.fVertBuilder->codeAppend( R"( | 
|---|
| 308 |                     float T = sk_VertexID / float(1 << kMaxResolveLevel);)"); | 
|---|
| 309 | } | 
|---|
| 310 | args.fVertBuilder->codeAppend( R"( | 
|---|
| 311 |                     float2 ab = mix(P[0], P[1], T); | 
|---|
| 312 |                     float2 bc = mix(P[1], P[2], T); | 
|---|
| 313 |                     float2 cd = mix(P[2], P[3], T); | 
|---|
| 314 |                     float2 abc = mix(ab, bc, T); | 
|---|
| 315 |                     float2 bcd = mix(bc, cd, T); | 
|---|
| 316 |                     point = mix(abc, bcd, T); | 
|---|
| 317 |                 })"); | 
|---|
| 318 |  | 
|---|
| 319 | GrShaderVar vertexPos( "point", kFloat2_GrSLType); | 
|---|
| 320 | if (!shader.viewMatrix().isIdentity()) { | 
|---|
| 321 | const char* viewMatrix; | 
|---|
| 322 | fViewMatrixUniform = args.fUniformHandler->addUniform( | 
|---|
| 323 | nullptr, kVertex_GrShaderFlag, kFloat3x3_GrSLType, "view_matrix", &viewMatrix); | 
|---|
| 324 | args.fVertBuilder->codeAppendf( R"( | 
|---|
| 325 |                     float2 transformedPoint = (%s * float3(point, 1)).xy;)", viewMatrix); | 
|---|
| 326 | vertexPos.set(kFloat2_GrSLType, "transformedPoint"); | 
|---|
| 327 | } | 
|---|
| 328 | gpArgs->fPositionVar = vertexPos; | 
|---|
| 329 | // No fragment shader. | 
|---|
| 330 | } | 
|---|
| 331 | }; | 
|---|
| 332 |  | 
|---|
| 333 | GrGLSLPrimitiveProcessor* GrMiddleOutCubicShader::createGLSLInstance(const GrShaderCaps&) const { | 
|---|
| 334 | return new Impl; | 
|---|
| 335 | } | 
|---|
| 336 |  | 
|---|