1 /* 2 * Copyright 2013 Google Inc. 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 "GrDistanceFieldGeoProc.h" 9 10 #include "GrTexture.h" 11 #include "SkDistanceFieldGen.h" 12 #include "glsl/GrGLSLFragmentShaderBuilder.h" 13 #include "glsl/GrGLSLGeometryProcessor.h" 14 #include "glsl/GrGLSLProgramDataManager.h" 15 #include "glsl/GrGLSLUniformHandler.h" 16 #include "glsl/GrGLSLUtil.h" 17 #include "glsl/GrGLSLVarying.h" 18 #include "glsl/GrGLSLVertexShaderBuilder.h" 19 20 // Assuming a radius of a little less than the diagonal of the fragment 21 #define SK_DistanceFieldAAFactor "0.65" 22 23 class GrGLDistanceFieldA8TextGeoProc : public GrGLSLGeometryProcessor { 24 public: 25 GrGLDistanceFieldA8TextGeoProc() 26 : fViewMatrix(SkMatrix::InvalidMatrix()) 27 #ifdef SK_GAMMA_APPLY_TO_A8 28 , fDistanceAdjust(-1.0f) 29 #endif 30 {} 31 32 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{ 33 const GrDistanceFieldA8TextGeoProc& dfTexEffect = 34 args.fGP.cast<GrDistanceFieldA8TextGeoProc>(); 35 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder; 36 37 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; 38 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; 39 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; 40 41 // emit attributes 42 varyingHandler->emitAttributes(dfTexEffect); 43 44 #ifdef SK_GAMMA_APPLY_TO_A8 45 // adjust based on gamma 46 const char* distanceAdjustUniName = nullptr; 47 // width, height, 1/(3*width) 48 fDistanceAdjustUni = uniformHandler->addUniform(kFragment_GrShaderFlag, 49 kFloat_GrSLType, kDefault_GrSLPrecision, 50 "DistanceAdjust", &distanceAdjustUniName); 51 #endif 52 53 // Setup pass through color 54 varyingHandler->addPassThroughAttribute(dfTexEffect.inColor(), args.fOutputColor); 55 56 // Setup position 57 this->setupPosition(vertBuilder, 58 uniformHandler, 59 gpArgs, 60 dfTexEffect.inPosition()->fName, 61 dfTexEffect.viewMatrix(), 62 &fViewMatrixUniform); 63 64 // emit transforms 65 this->emitTransforms(vertBuilder, 66 varyingHandler, 67 uniformHandler, 68 gpArgs->fPositionVar, 69 dfTexEffect.inPosition()->fName, 70 args.fFPCoordTransformHandler); 71 72 // add varyings 73 GrGLSLVertToFrag recipScale(kFloat_GrSLType); 74 GrGLSLVertToFrag uv(kVec2f_GrSLType); 75 bool isUniformScale = (dfTexEffect.getFlags() & kUniformScale_DistanceFieldEffectMask) == 76 kUniformScale_DistanceFieldEffectMask; 77 bool isSimilarity = SkToBool(dfTexEffect.getFlags() & kSimilarity_DistanceFieldEffectFlag); 78 bool isGammaCorrect = 79 SkToBool(dfTexEffect.getFlags() & kGammaCorrect_DistanceFieldEffectFlag); 80 bool isAliased = 81 SkToBool(dfTexEffect.getFlags() & kAliased_DistanceFieldEffectFlag); 82 varyingHandler->addVarying("TextureCoords", &uv, kHigh_GrSLPrecision); 83 vertBuilder->codeAppendf("%s = %s;", uv.vsOut(), dfTexEffect.inTextureCoords()->fName); 84 85 // compute numbers to be hardcoded to convert texture coordinates from float to int 86 SkASSERT(dfTexEffect.numTextureSamplers() == 1); 87 GrTexture* atlas = dfTexEffect.textureSampler(0).peekTexture(); 88 SkASSERT(atlas && SkIsPow2(atlas->width()) && SkIsPow2(atlas->height())); 89 90 GrGLSLVertToFrag st(kVec2f_GrSLType); 91 varyingHandler->addVarying("IntTextureCoords", &st, kHigh_GrSLPrecision); 92 vertBuilder->codeAppendf("%s = vec2(%d, %d) * %s;", st.vsOut(), 93 atlas->width(), atlas->height(), 94 dfTexEffect.inTextureCoords()->fName); 95 96 // Use highp to work around aliasing issues 97 fragBuilder->codeAppendf("highp vec2 uv = %s;\n", uv.fsIn()); 98 99 fragBuilder->codeAppend("\tfloat texColor = "); 100 fragBuilder->appendTextureLookup(args.fTexSamplers[0], 101 "uv", 102 kVec2f_GrSLType); 103 fragBuilder->codeAppend(".r;\n"); 104 fragBuilder->codeAppend("\tfloat distance = " 105 SK_DistanceFieldMultiplier "*(texColor - " SK_DistanceFieldThreshold ");"); 106 #ifdef SK_GAMMA_APPLY_TO_A8 107 // adjust width based on gamma 108 fragBuilder->codeAppendf("distance -= %s;", distanceAdjustUniName); 109 #endif 110 111 fragBuilder->codeAppend("float afwidth;"); 112 if (isUniformScale) { 113 // For uniform scale, we adjust for the effect of the transformation on the distance 114 // by using the length of the gradient of the t coordinate in the y direction. 115 // We use st coordinates to ensure we're mapping 1:1 from texel space to pixel space. 116 117 // this gives us a smooth step across approximately one fragment 118 #ifdef SK_VULKAN 119 fragBuilder->codeAppendf("afwidth = abs(" SK_DistanceFieldAAFactor "*dFdx(%s.x));", 120 st.fsIn()); 121 #else 122 // We use the y gradient because there is a bug in the Mali 400 in the x direction. 123 fragBuilder->codeAppendf("afwidth = abs(" SK_DistanceFieldAAFactor "*dFdy(%s.y));", 124 st.fsIn()); 125 #endif 126 } else if (isSimilarity) { 127 // For similarity transform, we adjust the effect of the transformation on the distance 128 // by using the length of the gradient of the texture coordinates. We use st coordinates 129 // to ensure we're mapping 1:1 from texel space to pixel space. 130 // We use the y gradient because there is a bug in the Mali 400 in the x direction. 131 132 // this gives us a smooth step across approximately one fragment 133 #ifdef SK_VULKAN 134 fragBuilder->codeAppendf("float st_grad_len = length(dFdx(%s));", st.fsIn()); 135 #else 136 // We use the y gradient because there is a bug in the Mali 400 in the x direction. 137 fragBuilder->codeAppendf("float st_grad_len = length(dFdy(%s));", st.fsIn()); 138 #endif 139 fragBuilder->codeAppend("afwidth = abs(" SK_DistanceFieldAAFactor "*st_grad_len);"); 140 } else { 141 // For general transforms, to determine the amount of correction we multiply a unit 142 // vector pointing along the SDF gradient direction by the Jacobian of the st coords 143 // (which is the inverse transform for this fragment) and take the length of the result. 144 fragBuilder->codeAppend("vec2 dist_grad = vec2(dFdx(distance), dFdy(distance));"); 145 // the length of the gradient may be 0, so we need to check for this 146 // this also compensates for the Adreno, which likes to drop tiles on division by 0 147 fragBuilder->codeAppend("float dg_len2 = dot(dist_grad, dist_grad);"); 148 fragBuilder->codeAppend("if (dg_len2 < 0.0001) {"); 149 fragBuilder->codeAppend("dist_grad = vec2(0.7071, 0.7071);"); 150 fragBuilder->codeAppend("} else {"); 151 fragBuilder->codeAppend("dist_grad = dist_grad*inversesqrt(dg_len2);"); 152 fragBuilder->codeAppend("}"); 153 154 fragBuilder->codeAppendf("vec2 Jdx = dFdx(%s);", st.fsIn()); 155 fragBuilder->codeAppendf("vec2 Jdy = dFdy(%s);", st.fsIn()); 156 fragBuilder->codeAppend("vec2 grad = vec2(dist_grad.x*Jdx.x + dist_grad.y*Jdy.x,"); 157 fragBuilder->codeAppend(" dist_grad.x*Jdx.y + dist_grad.y*Jdy.y);"); 158 159 // this gives us a smooth step across approximately one fragment 160 fragBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*length(grad);"); 161 } 162 163 if (isAliased) { 164 fragBuilder->codeAppend("float val = distance > 0 ? 1.0 : 0.0;"); 165 } else if (isGammaCorrect) { 166 // The smoothstep falloff compensates for the non-linear sRGB response curve. If we are 167 // doing gamma-correct rendering (to an sRGB or F16 buffer), then we actually want 168 // distance mapped linearly to coverage, so use a linear step: 169 fragBuilder->codeAppend( 170 "float val = clamp((distance + afwidth) / (2.0 * afwidth), 0.0, 1.0);"); 171 } else { 172 fragBuilder->codeAppend("float val = smoothstep(-afwidth, afwidth, distance);"); 173 } 174 175 fragBuilder->codeAppendf("%s = vec4(val);", args.fOutputCoverage); 176 } 177 178 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc, 179 FPCoordTransformIter&& transformIter) override { 180 #ifdef SK_GAMMA_APPLY_TO_A8 181 const GrDistanceFieldA8TextGeoProc& dfTexEffect = proc.cast<GrDistanceFieldA8TextGeoProc>(); 182 float distanceAdjust = dfTexEffect.getDistanceAdjust(); 183 if (distanceAdjust != fDistanceAdjust) { 184 pdman.set1f(fDistanceAdjustUni, distanceAdjust); 185 fDistanceAdjust = distanceAdjust; 186 } 187 #endif 188 const GrDistanceFieldA8TextGeoProc& dfa8gp = proc.cast<GrDistanceFieldA8TextGeoProc>(); 189 190 if (!dfa8gp.viewMatrix().isIdentity() && !fViewMatrix.cheapEqualTo(dfa8gp.viewMatrix())) { 191 fViewMatrix = dfa8gp.viewMatrix(); 192 float viewMatrix[3 * 3]; 193 GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix); 194 pdman.setMatrix3f(fViewMatrixUniform, viewMatrix); 195 } 196 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter); 197 } 198 199 static inline void GenKey(const GrGeometryProcessor& gp, 200 const GrShaderCaps&, 201 GrProcessorKeyBuilder* b) { 202 const GrDistanceFieldA8TextGeoProc& dfTexEffect = gp.cast<GrDistanceFieldA8TextGeoProc>(); 203 uint32_t key = dfTexEffect.getFlags(); 204 key |= ComputePosKey(dfTexEffect.viewMatrix()) << 16; 205 b->add32(key); 206 207 // Currently we hardcode numbers to convert atlas coordinates to normalized floating point 208 SkASSERT(gp.numTextureSamplers() == 1); 209 GrTextureProxy* atlas = gp.textureSampler(0).proxy(); 210 if (atlas) { 211 b->add32(atlas->width()); 212 b->add32(atlas->height()); 213 } 214 } 215 216 private: 217 SkMatrix fViewMatrix; 218 UniformHandle fViewMatrixUniform; 219 #ifdef SK_GAMMA_APPLY_TO_A8 220 float fDistanceAdjust; 221 UniformHandle fDistanceAdjustUni; 222 #endif 223 224 typedef GrGLSLGeometryProcessor INHERITED; 225 }; 226 227 /////////////////////////////////////////////////////////////////////////////// 228 229 GrDistanceFieldA8TextGeoProc::GrDistanceFieldA8TextGeoProc(GrColor color, 230 const SkMatrix& viewMatrix, 231 sk_sp<GrTextureProxy> proxy, 232 const GrSamplerParams& params, 233 #ifdef SK_GAMMA_APPLY_TO_A8 234 float distanceAdjust, 235 #endif 236 uint32_t flags, 237 bool usesLocalCoords) 238 : fColor(color) 239 , fViewMatrix(viewMatrix) 240 , fTextureSampler(std::move(proxy), params) 241 #ifdef SK_GAMMA_APPLY_TO_A8 242 , fDistanceAdjust(distanceAdjust) 243 #endif 244 , fFlags(flags & kNonLCD_DistanceFieldEffectMask) 245 , fInColor(nullptr) 246 , fUsesLocalCoords(usesLocalCoords) { 247 SkASSERT(!(flags & ~kNonLCD_DistanceFieldEffectMask)); 248 this->initClassID<GrDistanceFieldA8TextGeoProc>(); 249 fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType, 250 kHigh_GrSLPrecision); 251 fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType); 252 fInTextureCoords = &this->addVertexAttrib("inTextureCoords", kVec2us_GrVertexAttribType, 253 kHigh_GrSLPrecision); 254 this->addTextureSampler(&fTextureSampler); 255 } 256 257 void GrDistanceFieldA8TextGeoProc::getGLSLProcessorKey(const GrShaderCaps& caps, 258 GrProcessorKeyBuilder* b) const { 259 GrGLDistanceFieldA8TextGeoProc::GenKey(*this, caps, b); 260 } 261 262 GrGLSLPrimitiveProcessor* 263 GrDistanceFieldA8TextGeoProc::createGLSLInstance(const GrShaderCaps&) const { 264 return new GrGLDistanceFieldA8TextGeoProc(); 265 } 266 267 /////////////////////////////////////////////////////////////////////////////// 268 269 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(GrDistanceFieldA8TextGeoProc); 270 271 #if GR_TEST_UTILS 272 sk_sp<GrGeometryProcessor> GrDistanceFieldA8TextGeoProc::TestCreate(GrProcessorTestData* d) { 273 int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx 274 : GrProcessorUnitTest::kAlphaTextureIdx; 275 sk_sp<GrTextureProxy> proxy = d->textureProxy(texIdx); 276 277 static const SkShader::TileMode kTileModes[] = { 278 SkShader::kClamp_TileMode, 279 SkShader::kRepeat_TileMode, 280 SkShader::kMirror_TileMode, 281 }; 282 SkShader::TileMode tileModes[] = { 283 kTileModes[d->fRandom->nextULessThan(SK_ARRAY_COUNT(kTileModes))], 284 kTileModes[d->fRandom->nextULessThan(SK_ARRAY_COUNT(kTileModes))], 285 }; 286 GrSamplerParams params(tileModes, d->fRandom->nextBool() ? GrSamplerParams::kBilerp_FilterMode 287 : GrSamplerParams::kNone_FilterMode); 288 289 uint32_t flags = 0; 290 flags |= d->fRandom->nextBool() ? kSimilarity_DistanceFieldEffectFlag : 0; 291 if (flags & kSimilarity_DistanceFieldEffectFlag) { 292 flags |= d->fRandom->nextBool() ? kScaleOnly_DistanceFieldEffectFlag : 0; 293 } 294 295 return GrDistanceFieldA8TextGeoProc::Make(GrRandomColor(d->fRandom), 296 GrTest::TestMatrix(d->fRandom), 297 std::move(proxy), params, 298 #ifdef SK_GAMMA_APPLY_TO_A8 299 d->fRandom->nextF(), 300 #endif 301 flags, 302 d->fRandom->nextBool()); 303 } 304 #endif 305 306 /////////////////////////////////////////////////////////////////////////////// 307 308 class GrGLDistanceFieldPathGeoProc : public GrGLSLGeometryProcessor { 309 public: 310 GrGLDistanceFieldPathGeoProc() 311 : fViewMatrix(SkMatrix::InvalidMatrix()) 312 , fTextureSize(SkISize::Make(-1, -1)) {} 313 314 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{ 315 const GrDistanceFieldPathGeoProc& dfTexEffect = args.fGP.cast<GrDistanceFieldPathGeoProc>(); 316 317 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder; 318 319 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; 320 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; 321 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; 322 323 // emit attributes 324 varyingHandler->emitAttributes(dfTexEffect); 325 326 GrGLSLVertToFrag v(kVec2f_GrSLType); 327 varyingHandler->addVarying("TextureCoords", &v, kHigh_GrSLPrecision); 328 329 // setup pass through color 330 varyingHandler->addPassThroughAttribute(dfTexEffect.inColor(), args.fOutputColor); 331 vertBuilder->codeAppendf("%s = %s;", v.vsOut(), dfTexEffect.inTextureCoords()->fName); 332 333 // Setup position 334 this->setupPosition(vertBuilder, 335 uniformHandler, 336 gpArgs, 337 dfTexEffect.inPosition()->fName, 338 dfTexEffect.viewMatrix(), 339 &fViewMatrixUniform); 340 341 // emit transforms 342 this->emitTransforms(vertBuilder, 343 varyingHandler, 344 uniformHandler, 345 gpArgs->fPositionVar, 346 dfTexEffect.inPosition()->fName, 347 args.fFPCoordTransformHandler); 348 349 const char* textureSizeUniName = nullptr; 350 fTextureSizeUni = uniformHandler->addUniform(kFragment_GrShaderFlag, 351 kVec2f_GrSLType, kDefault_GrSLPrecision, 352 "TextureSize", &textureSizeUniName); 353 354 // Use highp to work around aliasing issues 355 fragBuilder->codeAppendf("highp vec2 uv = %s;", v.fsIn()); 356 357 fragBuilder->codeAppend("float texColor = "); 358 fragBuilder->appendTextureLookup(args.fTexSamplers[0], 359 "uv", 360 kVec2f_GrSLType); 361 fragBuilder->codeAppend(".r;"); 362 fragBuilder->codeAppend("float distance = " 363 SK_DistanceFieldMultiplier "*(texColor - " SK_DistanceFieldThreshold ");"); 364 365 fragBuilder->codeAppendf("highp vec2 st = uv*%s;", textureSizeUniName); 366 fragBuilder->codeAppend("float afwidth;"); 367 bool isUniformScale = (dfTexEffect.getFlags() & kUniformScale_DistanceFieldEffectMask) == 368 kUniformScale_DistanceFieldEffectMask; 369 bool isSimilarity = SkToBool(dfTexEffect.getFlags() & kSimilarity_DistanceFieldEffectFlag); 370 bool isGammaCorrect = 371 SkToBool(dfTexEffect.getFlags() & kGammaCorrect_DistanceFieldEffectFlag); 372 if (isUniformScale) { 373 // For uniform scale, we adjust for the effect of the transformation on the distance 374 // by using the length of the gradient of the t coordinate in the y direction. 375 // We use st coordinates to ensure we're mapping 1:1 from texel space to pixel space. 376 377 // this gives us a smooth step across approximately one fragment 378 #ifdef SK_VULKAN 379 fragBuilder->codeAppend("afwidth = abs(" SK_DistanceFieldAAFactor "*dFdx(st.x));"); 380 #else 381 // We use the y gradient because there is a bug in the Mali 400 in the x direction. 382 fragBuilder->codeAppend("afwidth = abs(" SK_DistanceFieldAAFactor "*dFdy(st.y));"); 383 #endif 384 } else if (isSimilarity) { 385 // For similarity transform, we adjust the effect of the transformation on the distance 386 // by using the length of the gradient of the texture coordinates. We use st coordinates 387 // to ensure we're mapping 1:1 from texel space to pixel space. 388 389 // this gives us a smooth step across approximately one fragment 390 #ifdef SK_VULKAN 391 fragBuilder->codeAppend("float st_grad_len = length(dFdx(st));"); 392 #else 393 // We use the y gradient because there is a bug in the Mali 400 in the x direction. 394 fragBuilder->codeAppend("float st_grad_len = length(dFdy(st));"); 395 #endif 396 fragBuilder->codeAppend("afwidth = abs(" SK_DistanceFieldAAFactor "*st_grad_len);"); 397 } else { 398 // For general transforms, to determine the amount of correction we multiply a unit 399 // vector pointing along the SDF gradient direction by the Jacobian of the st coords 400 // (which is the inverse transform for this fragment) and take the length of the result. 401 fragBuilder->codeAppend("vec2 dist_grad = vec2(dFdx(distance), dFdy(distance));"); 402 // the length of the gradient may be 0, so we need to check for this 403 // this also compensates for the Adreno, which likes to drop tiles on division by 0 404 fragBuilder->codeAppend("float dg_len2 = dot(dist_grad, dist_grad);"); 405 fragBuilder->codeAppend("if (dg_len2 < 0.0001) {"); 406 fragBuilder->codeAppend("dist_grad = vec2(0.7071, 0.7071);"); 407 fragBuilder->codeAppend("} else {"); 408 fragBuilder->codeAppend("dist_grad = dist_grad*inversesqrt(dg_len2);"); 409 fragBuilder->codeAppend("}"); 410 411 fragBuilder->codeAppend("vec2 Jdx = dFdx(st);"); 412 fragBuilder->codeAppend("vec2 Jdy = dFdy(st);"); 413 fragBuilder->codeAppend("vec2 grad = vec2(dist_grad.x*Jdx.x + dist_grad.y*Jdy.x,"); 414 fragBuilder->codeAppend(" dist_grad.x*Jdx.y + dist_grad.y*Jdy.y);"); 415 416 // this gives us a smooth step across approximately one fragment 417 fragBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*length(grad);"); 418 } 419 // The smoothstep falloff compensates for the non-linear sRGB response curve. If we are 420 // doing gamma-correct rendering (to an sRGB or F16 buffer), then we actually want distance 421 // mapped linearly to coverage, so use a linear step: 422 if (isGammaCorrect) { 423 fragBuilder->codeAppend( 424 "float val = clamp((distance + afwidth) / (2.0 * afwidth), 0.0, 1.0);"); 425 } else { 426 fragBuilder->codeAppend("float val = smoothstep(-afwidth, afwidth, distance);"); 427 } 428 429 fragBuilder->codeAppendf("%s = vec4(val);", args.fOutputCoverage); 430 } 431 432 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc, 433 FPCoordTransformIter&& transformIter) override { 434 SkASSERT(fTextureSizeUni.isValid()); 435 436 GrTexture* texture = proc.textureSampler(0).peekTexture(); 437 438 if (texture->width() != fTextureSize.width() || 439 texture->height() != fTextureSize.height()) { 440 fTextureSize = SkISize::Make(texture->width(), texture->height()); 441 pdman.set2f(fTextureSizeUni, 442 SkIntToScalar(fTextureSize.width()), 443 SkIntToScalar(fTextureSize.height())); 444 } 445 446 const GrDistanceFieldPathGeoProc& dfpgp = proc.cast<GrDistanceFieldPathGeoProc>(); 447 448 if (!dfpgp.viewMatrix().isIdentity() && !fViewMatrix.cheapEqualTo(dfpgp.viewMatrix())) { 449 fViewMatrix = dfpgp.viewMatrix(); 450 float viewMatrix[3 * 3]; 451 GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix); 452 pdman.setMatrix3f(fViewMatrixUniform, viewMatrix); 453 } 454 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter); 455 } 456 457 static inline void GenKey(const GrGeometryProcessor& gp, 458 const GrShaderCaps&, 459 GrProcessorKeyBuilder* b) { 460 const GrDistanceFieldPathGeoProc& dfTexEffect = gp.cast<GrDistanceFieldPathGeoProc>(); 461 462 uint32_t key = dfTexEffect.getFlags(); 463 key |= ComputePosKey(dfTexEffect.viewMatrix()) << 16; 464 b->add32(key); 465 } 466 467 private: 468 UniformHandle fTextureSizeUni; 469 UniformHandle fViewMatrixUniform; 470 SkMatrix fViewMatrix; 471 SkISize fTextureSize; 472 473 typedef GrGLSLGeometryProcessor INHERITED; 474 }; 475 476 /////////////////////////////////////////////////////////////////////////////// 477 GrDistanceFieldPathGeoProc::GrDistanceFieldPathGeoProc(GrColor color, 478 const SkMatrix& viewMatrix, 479 sk_sp<GrTextureProxy> proxy, 480 const GrSamplerParams& params, 481 uint32_t flags, 482 bool usesLocalCoords) 483 : fColor(color) 484 , fViewMatrix(viewMatrix) 485 , fTextureSampler(std::move(proxy), params) 486 , fFlags(flags & kNonLCD_DistanceFieldEffectMask) 487 , fInColor(nullptr) 488 , fUsesLocalCoords(usesLocalCoords) { 489 SkASSERT(!(flags & ~kNonLCD_DistanceFieldEffectMask)); 490 this->initClassID<GrDistanceFieldPathGeoProc>(); 491 fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType, 492 kHigh_GrSLPrecision); 493 fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType); 494 fInTextureCoords = &this->addVertexAttrib("inTextureCoords", kVec2us_GrVertexAttribType); 495 this->addTextureSampler(&fTextureSampler); 496 } 497 498 void GrDistanceFieldPathGeoProc::getGLSLProcessorKey(const GrShaderCaps& caps, 499 GrProcessorKeyBuilder* b) const { 500 GrGLDistanceFieldPathGeoProc::GenKey(*this, caps, b); 501 } 502 503 GrGLSLPrimitiveProcessor* 504 GrDistanceFieldPathGeoProc::createGLSLInstance(const GrShaderCaps&) const { 505 return new GrGLDistanceFieldPathGeoProc(); 506 } 507 508 /////////////////////////////////////////////////////////////////////////////// 509 510 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(GrDistanceFieldPathGeoProc); 511 512 #if GR_TEST_UTILS 513 sk_sp<GrGeometryProcessor> GrDistanceFieldPathGeoProc::TestCreate(GrProcessorTestData* d) { 514 int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx 515 : GrProcessorUnitTest::kAlphaTextureIdx; 516 sk_sp<GrTextureProxy> proxy = d->textureProxy(texIdx); 517 518 static const SkShader::TileMode kTileModes[] = { 519 SkShader::kClamp_TileMode, 520 SkShader::kRepeat_TileMode, 521 SkShader::kMirror_TileMode, 522 }; 523 SkShader::TileMode tileModes[] = { 524 kTileModes[d->fRandom->nextULessThan(SK_ARRAY_COUNT(kTileModes))], 525 kTileModes[d->fRandom->nextULessThan(SK_ARRAY_COUNT(kTileModes))], 526 }; 527 GrSamplerParams params(tileModes, d->fRandom->nextBool() ? GrSamplerParams::kBilerp_FilterMode 528 : GrSamplerParams::kNone_FilterMode); 529 530 uint32_t flags = 0; 531 flags |= d->fRandom->nextBool() ? kSimilarity_DistanceFieldEffectFlag : 0; 532 if (flags & kSimilarity_DistanceFieldEffectFlag) { 533 flags |= d->fRandom->nextBool() ? kScaleOnly_DistanceFieldEffectFlag : 0; 534 } 535 536 return GrDistanceFieldPathGeoProc::Make(GrRandomColor(d->fRandom), 537 GrTest::TestMatrix(d->fRandom), 538 std::move(proxy), 539 params, 540 flags, 541 d->fRandom->nextBool()); 542 } 543 #endif 544 545 /////////////////////////////////////////////////////////////////////////////// 546 547 class GrGLDistanceFieldLCDTextGeoProc : public GrGLSLGeometryProcessor { 548 public: 549 GrGLDistanceFieldLCDTextGeoProc() 550 : fViewMatrix(SkMatrix::InvalidMatrix()) { 551 fDistanceAdjust = GrDistanceFieldLCDTextGeoProc::DistanceAdjust::Make(1.0f, 1.0f, 1.0f); 552 } 553 554 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{ 555 const GrDistanceFieldLCDTextGeoProc& dfTexEffect = 556 args.fGP.cast<GrDistanceFieldLCDTextGeoProc>(); 557 558 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; 559 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; 560 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; 561 562 // emit attributes 563 varyingHandler->emitAttributes(dfTexEffect); 564 565 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder; 566 567 // setup pass through color 568 varyingHandler->addPassThroughAttribute(dfTexEffect.inColor(), args.fOutputColor); 569 570 // Setup position 571 this->setupPosition(vertBuilder, 572 uniformHandler, 573 gpArgs, 574 dfTexEffect.inPosition()->fName, 575 dfTexEffect.viewMatrix(), 576 &fViewMatrixUniform); 577 578 // emit transforms 579 this->emitTransforms(vertBuilder, 580 varyingHandler, 581 uniformHandler, 582 gpArgs->fPositionVar, 583 dfTexEffect.inPosition()->fName, 584 args.fFPCoordTransformHandler); 585 586 // set up varyings 587 bool isUniformScale = (dfTexEffect.getFlags() & kUniformScale_DistanceFieldEffectMask) == 588 kUniformScale_DistanceFieldEffectMask; 589 bool isSimilarity = SkToBool(dfTexEffect.getFlags() & kSimilarity_DistanceFieldEffectFlag); 590 bool isGammaCorrect = 591 SkToBool(dfTexEffect.getFlags() & kGammaCorrect_DistanceFieldEffectFlag); 592 GrGLSLVertToFrag recipScale(kFloat_GrSLType); 593 GrGLSLVertToFrag uv(kVec2f_GrSLType); 594 varyingHandler->addVarying("TextureCoords", &uv, kHigh_GrSLPrecision); 595 vertBuilder->codeAppendf("%s = %s;", uv.vsOut(), dfTexEffect.inTextureCoords()->fName); 596 597 // compute numbers to be hardcoded to convert texture coordinates from float to int 598 SkASSERT(dfTexEffect.numTextureSamplers() == 1); 599 GrTexture* atlas = dfTexEffect.textureSampler(0).peekTexture(); 600 SkASSERT(atlas && SkIsPow2(atlas->width()) && SkIsPow2(atlas->height())); 601 602 GrGLSLVertToFrag st(kVec2f_GrSLType); 603 varyingHandler->addVarying("IntTextureCoords", &st, kHigh_GrSLPrecision); 604 vertBuilder->codeAppendf("%s = vec2(%d, %d) * %s;", st.vsOut(), 605 atlas->width(), atlas->height(), 606 dfTexEffect.inTextureCoords()->fName); 607 608 // add frag shader code 609 610 // create LCD offset adjusted by inverse of transform 611 // Use highp to work around aliasing issues 612 fragBuilder->codeAppendf("highp vec2 uv = %s;\n", uv.fsIn()); 613 614 SkScalar lcdDelta = 1.0f / (3.0f * atlas->width()); 615 if (dfTexEffect.getFlags() & kBGR_DistanceFieldEffectFlag) { 616 fragBuilder->codeAppendf("highp float delta = -%.*f;\n", SK_FLT_DECIMAL_DIG, lcdDelta); 617 } else { 618 fragBuilder->codeAppendf("highp float delta = %.*f;\n", SK_FLT_DECIMAL_DIG, lcdDelta); 619 } 620 if (isUniformScale) { 621 #ifdef SK_VULKAN 622 fragBuilder->codeAppendf("float st_grad_len = abs(dFdx(%s.x));", st.fsIn()); 623 #else 624 // We use the y gradient because there is a bug in the Mali 400 in the x direction. 625 fragBuilder->codeAppendf("float st_grad_len = abs(dFdy(%s.y));", st.fsIn()); 626 #endif 627 fragBuilder->codeAppend("vec2 offset = vec2(st_grad_len*delta, 0.0);"); 628 } else if (isSimilarity) { 629 // For a similarity matrix with rotation, the gradient will not be aligned 630 // with the texel coordinate axes, so we need to calculate it. 631 #ifdef SK_VULKAN 632 fragBuilder->codeAppendf("vec2 st_grad = dFdx(%s);", st.fsIn()); 633 fragBuilder->codeAppend("vec2 offset = delta*st_grad;"); 634 #else 635 // We use dFdy because of a Mali 400 bug, and rotate -90 degrees to 636 // get the gradient in the x direction. 637 fragBuilder->codeAppendf("vec2 st_grad = dFdy(%s);", st.fsIn()); 638 fragBuilder->codeAppend("vec2 offset = delta*vec2(st_grad.y, -st_grad.x);"); 639 #endif 640 fragBuilder->codeAppend("float st_grad_len = length(st_grad);"); 641 } else { 642 fragBuilder->codeAppendf("vec2 st = %s;\n", st.fsIn()); 643 644 fragBuilder->codeAppend("vec2 Jdx = dFdx(st);"); 645 fragBuilder->codeAppend("vec2 Jdy = dFdy(st);"); 646 fragBuilder->codeAppend("vec2 offset = delta*Jdx;"); 647 } 648 649 // green is distance to uv center 650 fragBuilder->codeAppend("\tvec4 texColor = "); 651 fragBuilder->appendTextureLookup(args.fTexSamplers[0], "uv", kVec2f_GrSLType); 652 fragBuilder->codeAppend(";\n"); 653 fragBuilder->codeAppend("\tvec3 distance;\n"); 654 fragBuilder->codeAppend("\tdistance.y = texColor.r;\n"); 655 // red is distance to left offset 656 fragBuilder->codeAppend("\tvec2 uv_adjusted = uv - offset;\n"); 657 fragBuilder->codeAppend("\ttexColor = "); 658 fragBuilder->appendTextureLookup(args.fTexSamplers[0], "uv_adjusted", kVec2f_GrSLType); 659 fragBuilder->codeAppend(";\n"); 660 fragBuilder->codeAppend("\tdistance.x = texColor.r;\n"); 661 // blue is distance to right offset 662 fragBuilder->codeAppend("\tuv_adjusted = uv + offset;\n"); 663 fragBuilder->codeAppend("\ttexColor = "); 664 fragBuilder->appendTextureLookup(args.fTexSamplers[0], "uv_adjusted", kVec2f_GrSLType); 665 fragBuilder->codeAppend(";\n"); 666 fragBuilder->codeAppend("\tdistance.z = texColor.r;\n"); 667 668 fragBuilder->codeAppend("\tdistance = " 669 "vec3(" SK_DistanceFieldMultiplier ")*(distance - vec3(" SK_DistanceFieldThreshold"));"); 670 671 // adjust width based on gamma 672 const char* distanceAdjustUniName = nullptr; 673 fDistanceAdjustUni = uniformHandler->addUniform(kFragment_GrShaderFlag, 674 kVec3f_GrSLType, kDefault_GrSLPrecision, 675 "DistanceAdjust", &distanceAdjustUniName); 676 fragBuilder->codeAppendf("distance -= %s;", distanceAdjustUniName); 677 678 // To be strictly correct, we should compute the anti-aliasing factor separately 679 // for each color component. However, this is only important when using perspective 680 // transformations, and even then using a single factor seems like a reasonable 681 // trade-off between quality and speed. 682 fragBuilder->codeAppend("float afwidth;"); 683 if (isSimilarity) { 684 // For similarity transform (uniform scale-only is a subset of this), we adjust for the 685 // effect of the transformation on the distance by using the length of the gradient of 686 // the texture coordinates. We use st coordinates to ensure we're mapping 1:1 from texel 687 // space to pixel space. 688 689 // this gives us a smooth step across approximately one fragment 690 fragBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*st_grad_len;"); 691 } else { 692 // For general transforms, to determine the amount of correction we multiply a unit 693 // vector pointing along the SDF gradient direction by the Jacobian of the st coords 694 // (which is the inverse transform for this fragment) and take the length of the result. 695 fragBuilder->codeAppend("vec2 dist_grad = vec2(dFdx(distance.r), dFdy(distance.r));"); 696 // the length of the gradient may be 0, so we need to check for this 697 // this also compensates for the Adreno, which likes to drop tiles on division by 0 698 fragBuilder->codeAppend("float dg_len2 = dot(dist_grad, dist_grad);"); 699 fragBuilder->codeAppend("if (dg_len2 < 0.0001) {"); 700 fragBuilder->codeAppend("dist_grad = vec2(0.7071, 0.7071);"); 701 fragBuilder->codeAppend("} else {"); 702 fragBuilder->codeAppend("dist_grad = dist_grad*inversesqrt(dg_len2);"); 703 fragBuilder->codeAppend("}"); 704 fragBuilder->codeAppend("vec2 grad = vec2(dist_grad.x*Jdx.x + dist_grad.y*Jdy.x,"); 705 fragBuilder->codeAppend(" dist_grad.x*Jdx.y + dist_grad.y*Jdy.y);"); 706 707 // this gives us a smooth step across approximately one fragment 708 fragBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*length(grad);"); 709 } 710 711 // The smoothstep falloff compensates for the non-linear sRGB response curve. If we are 712 // doing gamma-correct rendering (to an sRGB or F16 buffer), then we actually want distance 713 // mapped linearly to coverage, so use a linear step: 714 if (isGammaCorrect) { 715 fragBuilder->codeAppendf("%s = " 716 "vec4(clamp((distance + vec3(afwidth)) / vec3(2.0 * afwidth), 0.0, 1.0), 1.0);", 717 args.fOutputCoverage); 718 } else { 719 fragBuilder->codeAppendf( 720 "%s = vec4(smoothstep(vec3(-afwidth), vec3(afwidth), distance), 1.0);", 721 args.fOutputCoverage); 722 } 723 } 724 725 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& processor, 726 FPCoordTransformIter&& transformIter) override { 727 SkASSERT(fDistanceAdjustUni.isValid()); 728 729 const GrDistanceFieldLCDTextGeoProc& dflcd = processor.cast<GrDistanceFieldLCDTextGeoProc>(); 730 GrDistanceFieldLCDTextGeoProc::DistanceAdjust wa = dflcd.getDistanceAdjust(); 731 if (wa != fDistanceAdjust) { 732 pdman.set3f(fDistanceAdjustUni, 733 wa.fR, 734 wa.fG, 735 wa.fB); 736 fDistanceAdjust = wa; 737 } 738 739 if (!dflcd.viewMatrix().isIdentity() && !fViewMatrix.cheapEqualTo(dflcd.viewMatrix())) { 740 fViewMatrix = dflcd.viewMatrix(); 741 float viewMatrix[3 * 3]; 742 GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix); 743 pdman.setMatrix3f(fViewMatrixUniform, viewMatrix); 744 } 745 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter); 746 } 747 748 static inline void GenKey(const GrGeometryProcessor& gp, 749 const GrShaderCaps&, 750 GrProcessorKeyBuilder* b) { 751 const GrDistanceFieldLCDTextGeoProc& dfTexEffect = gp.cast<GrDistanceFieldLCDTextGeoProc>(); 752 753 uint32_t key = dfTexEffect.getFlags(); 754 key |= ComputePosKey(dfTexEffect.viewMatrix()) << 16; 755 b->add32(key); 756 757 // Currently we hardcode numbers to convert atlas coordinates to normalized floating point 758 SkASSERT(gp.numTextureSamplers() == 1); 759 GrTextureProxy* atlas = gp.textureSampler(0).proxy(); 760 if (atlas) { 761 b->add32(atlas->width()); 762 b->add32(atlas->height()); 763 } 764 } 765 766 private: 767 SkMatrix fViewMatrix; 768 UniformHandle fViewMatrixUniform; 769 UniformHandle fColorUniform; 770 GrDistanceFieldLCDTextGeoProc::DistanceAdjust fDistanceAdjust; 771 UniformHandle fDistanceAdjustUni; 772 773 typedef GrGLSLGeometryProcessor INHERITED; 774 }; 775 776 /////////////////////////////////////////////////////////////////////////////// 777 GrDistanceFieldLCDTextGeoProc::GrDistanceFieldLCDTextGeoProc( 778 GrColor color, const SkMatrix& viewMatrix, 779 sk_sp<GrTextureProxy> proxy, 780 const GrSamplerParams& params, 781 DistanceAdjust distanceAdjust, 782 uint32_t flags, bool usesLocalCoords) 783 : fColor(color) 784 , fViewMatrix(viewMatrix) 785 , fTextureSampler(std::move(proxy), params) 786 , fDistanceAdjust(distanceAdjust) 787 , fFlags(flags & kLCD_DistanceFieldEffectMask) 788 , fUsesLocalCoords(usesLocalCoords) { 789 SkASSERT(!(flags & ~kLCD_DistanceFieldEffectMask) && (flags & kUseLCD_DistanceFieldEffectFlag)); 790 this->initClassID<GrDistanceFieldLCDTextGeoProc>(); 791 fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType, 792 kHigh_GrSLPrecision); 793 fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType); 794 fInTextureCoords = &this->addVertexAttrib("inTextureCoords", kVec2us_GrVertexAttribType, 795 kHigh_GrSLPrecision); 796 this->addTextureSampler(&fTextureSampler); 797 } 798 799 void GrDistanceFieldLCDTextGeoProc::getGLSLProcessorKey(const GrShaderCaps& caps, 800 GrProcessorKeyBuilder* b) const { 801 GrGLDistanceFieldLCDTextGeoProc::GenKey(*this, caps, b); 802 } 803 804 GrGLSLPrimitiveProcessor* GrDistanceFieldLCDTextGeoProc::createGLSLInstance(const GrShaderCaps&) const { 805 return new GrGLDistanceFieldLCDTextGeoProc(); 806 } 807 808 /////////////////////////////////////////////////////////////////////////////// 809 810 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(GrDistanceFieldLCDTextGeoProc); 811 812 #if GR_TEST_UTILS 813 sk_sp<GrGeometryProcessor> GrDistanceFieldLCDTextGeoProc::TestCreate(GrProcessorTestData* d) { 814 int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx : 815 GrProcessorUnitTest::kAlphaTextureIdx; 816 sk_sp<GrTextureProxy> proxy = d->textureProxy(texIdx); 817 818 static const SkShader::TileMode kTileModes[] = { 819 SkShader::kClamp_TileMode, 820 SkShader::kRepeat_TileMode, 821 SkShader::kMirror_TileMode, 822 }; 823 SkShader::TileMode tileModes[] = { 824 kTileModes[d->fRandom->nextULessThan(SK_ARRAY_COUNT(kTileModes))], 825 kTileModes[d->fRandom->nextULessThan(SK_ARRAY_COUNT(kTileModes))], 826 }; 827 GrSamplerParams params(tileModes, d->fRandom->nextBool() ? GrSamplerParams::kBilerp_FilterMode 828 : GrSamplerParams::kNone_FilterMode); 829 DistanceAdjust wa = { 0.0f, 0.1f, -0.1f }; 830 uint32_t flags = kUseLCD_DistanceFieldEffectFlag; 831 flags |= d->fRandom->nextBool() ? kSimilarity_DistanceFieldEffectFlag : 0; 832 if (flags & kSimilarity_DistanceFieldEffectFlag) { 833 flags |= d->fRandom->nextBool() ? kScaleOnly_DistanceFieldEffectFlag : 0; 834 } 835 flags |= d->fRandom->nextBool() ? kBGR_DistanceFieldEffectFlag : 0; 836 return GrDistanceFieldLCDTextGeoProc::Make(GrRandomColor(d->fRandom), 837 GrTest::TestMatrix(d->fRandom), 838 std::move(proxy), params, 839 wa, 840 flags, 841 d->fRandom->nextBool()); 842 } 843 #endif 844