void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { const GrRRectShadowGeoProc& rsgp = args.fGP.cast<GrRRectShadowGeoProc>(); GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder; // emit attributes varyingHandler->emitAttributes(rsgp); fragBuilder->codeAppend("vec4 shadowParams;"); varyingHandler->addPassThroughAttribute(rsgp.inShadowParams(), "shadowParams"); // setup pass through color varyingHandler->addPassThroughAttribute(rsgp.inColor(), args.fOutputColor); // Setup position this->setupPosition(vertBuilder, gpArgs, rsgp.inPosition()->fName); // emit transforms this->emitTransforms(vertBuilder, varyingHandler, uniformHandler, gpArgs->fPositionVar, rsgp.inPosition()->fName, args.fFPCoordTransformHandler); fragBuilder->codeAppend("float d = length(shadowParams.xy);"); fragBuilder->codeAppend("float distance = shadowParams.z * (1.0 - d);"); fragBuilder->codeAppend("float factor = 1.0 - clamp(distance, 0.0, shadowParams.w);"); fragBuilder->codeAppend("factor = exp(-factor * factor * 4.0) - 0.018;"); fragBuilder->codeAppendf("%s = vec4(factor);", args.fOutputCoverage); }
void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { const MSAAQuadProcessor& qp = args.fGP.cast<MSAAQuadProcessor>(); GrGLSLVertexBuilder* vsBuilder = args.fVertBuilder; GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; // emit attributes varyingHandler->emitAttributes(qp); varyingHandler->addPassThroughAttribute(qp.inColor(), args.fOutputColor); GrGLSLVertToFrag uv(kVec2f_GrSLType); varyingHandler->addVarying("uv", &uv, kHigh_GrSLPrecision); vsBuilder->codeAppendf("%s = %s;", uv.vsOut(), qp.inUV()->fName); // Setup position this->setupPosition(vsBuilder, uniformHandler, gpArgs, qp.inPosition()->fName, qp.viewMatrix(), &fViewMatrixUniform); // emit transforms this->emitTransforms(vsBuilder, varyingHandler, uniformHandler, gpArgs->fPositionVar, qp.inPosition()->fName, SkMatrix::I(), args.fTransformsIn, args.fTransformsOut); GrGLSLPPFragmentBuilder* fsBuilder = args.fFragBuilder; fsBuilder->codeAppendf("if (%s.x * %s.x >= %s.y) discard;", uv.fsIn(), uv.fsIn(), uv.fsIn()); fsBuilder->codeAppendf("%s = vec4(1.0);", args.fOutputCoverage); }
void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { const GrBitmapTextGeoProc& btgp = args.fGP.cast<GrBitmapTextGeoProc>(); GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; // emit attributes varyingHandler->emitAttributes(btgp); const char* atlasSizeInvName; fAtlasSizeInvUniform = uniformHandler->addUniform(kVertex_GrShaderFlag, kFloat2_GrSLType, kHigh_GrSLPrecision, "AtlasSizeInv", &atlasSizeInvName); GrGLSLVarying uv(kFloat2_GrSLType); GrSLType texIdxType = args.fShaderCaps->integerSupport() ? kInt_GrSLType : kFloat_GrSLType; GrGLSLVarying texIdx(texIdxType); append_index_uv_varyings(args, btgp.inTextureCoords().name(), atlasSizeInvName, &uv, &texIdx, nullptr); GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; // Setup pass through color if (btgp.hasVertexColor()) { varyingHandler->addPassThroughAttribute(btgp.inColor(), args.fOutputColor); } else { this->setupUniformColor(fragBuilder, uniformHandler, args.fOutputColor, &fColorUniform); } // Setup position gpArgs->fPositionVar = btgp.inPosition().asShaderVar(); // emit transforms this->emitTransforms(vertBuilder, varyingHandler, uniformHandler, btgp.inPosition().asShaderVar(), btgp.localMatrix(), args.fFPCoordTransformHandler); fragBuilder->codeAppend("half4 texColor;"); append_multitexture_lookup(args, btgp.numTextureSamplers(), texIdx, uv.fsIn(), "texColor"); if (btgp.maskFormat() == kARGB_GrMaskFormat) { // modulate by color fragBuilder->codeAppendf("%s = %s * texColor;", args.fOutputColor, args.fOutputColor); fragBuilder->codeAppendf("%s = half4(1);", args.fOutputCoverage); } else { fragBuilder->codeAppendf("%s = texColor;", args.fOutputCoverage); } }
void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) final { const GrPipelineDynamicStateTestProcessor& mp = args.fGP.cast<GrPipelineDynamicStateTestProcessor>(); GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; varyingHandler->emitAttributes(mp); varyingHandler->addPassThroughAttribute(&mp.fColor, args.fOutputColor); GrGLSLVertexBuilder* v = args.fVertBuilder; v->codeAppendf("vec2 vertex = %s;", mp.fVertex.fName); gpArgs->fPositionVar.set(kVec2f_GrSLType, "vertex"); GrGLSLPPFragmentBuilder* f = args.fFragBuilder; f->codeAppendf("%s = vec4(1);", args.fOutputCoverage); }
void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{ const GrDistanceFieldLCDTextGeoProc& dfTexEffect = args.fGP.cast<GrDistanceFieldLCDTextGeoProc>(); GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; // emit attributes varyingHandler->emitAttributes(dfTexEffect); GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder; // setup pass through color if (!dfTexEffect.colorIgnored()) { varyingHandler->addPassThroughAttribute(dfTexEffect.inColor(), args.fOutputColor); } // Setup position this->setupPosition(vertBuilder, uniformHandler, gpArgs, dfTexEffect.inPosition()->fName, dfTexEffect.viewMatrix(), &fViewMatrixUniform); // emit transforms this->emitTransforms(vertBuilder, varyingHandler, uniformHandler, gpArgs->fPositionVar, dfTexEffect.inPosition()->fName, args.fTransformsIn, args.fTransformsOut); // set up varyings bool isUniformScale = (dfTexEffect.getFlags() & kUniformScale_DistanceFieldEffectMask) == kUniformScale_DistanceFieldEffectMask; bool isSimilarity = SkToBool(dfTexEffect.getFlags() & kSimilarity_DistanceFieldEffectFlag); GrGLSLVertToFrag recipScale(kFloat_GrSLType); GrGLSLVertToFrag uv(kVec2f_GrSLType); varyingHandler->addVarying("TextureCoords", &uv, kHigh_GrSLPrecision); vertBuilder->codeAppendf("%s = %s;", uv.vsOut(), dfTexEffect.inTextureCoords()->fName); // compute numbers to be hardcoded to convert texture coordinates from float to int SkASSERT(dfTexEffect.numTextures() == 1); GrTexture* atlas = dfTexEffect.textureAccess(0).getTexture(); SkASSERT(atlas && SkIsPow2(atlas->width()) && SkIsPow2(atlas->height())); GrGLSLVertToFrag st(kVec2f_GrSLType); varyingHandler->addVarying("IntTextureCoords", &st, kHigh_GrSLPrecision); vertBuilder->codeAppendf("%s = vec2(%d, %d) * %s;", st.vsOut(), atlas->width(), atlas->height(), dfTexEffect.inTextureCoords()->fName); // add frag shader code SkAssertResult(fragBuilder->enableFeature( GrGLSLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature)); // create LCD offset adjusted by inverse of transform // Use highp to work around aliasing issues fragBuilder->codeAppend(GrGLSLShaderVar::PrecisionString(args.fGLSLCaps, kHigh_GrSLPrecision)); fragBuilder->codeAppendf("vec2 uv = %s;\n", uv.fsIn()); fragBuilder->codeAppend(GrGLSLShaderVar::PrecisionString(args.fGLSLCaps, kHigh_GrSLPrecision)); SkScalar lcdDelta = 1.0f / (3.0f * atlas->width()); if (dfTexEffect.getFlags() & kBGR_DistanceFieldEffectFlag) { fragBuilder->codeAppendf("float delta = -%.*f;\n", SK_FLT_DECIMAL_DIG, lcdDelta); } else { fragBuilder->codeAppendf("float delta = %.*f;\n", SK_FLT_DECIMAL_DIG, lcdDelta); } if (isUniformScale) { fragBuilder->codeAppendf("float st_grad_len = abs(dFdy(%s.y));", st.fsIn()); fragBuilder->codeAppend("vec2 offset = vec2(st_grad_len*delta, 0.0);"); } else if (isSimilarity) { // For a similarity matrix with rotation, the gradient will not be aligned // with the texel coordinate axes, so we need to calculate it. // We use dFdy because of a Mali 400 bug, and rotate -90 degrees to // get the gradient in the x direction. fragBuilder->codeAppendf("vec2 st_grad = dFdy(%s);", st.fsIn()); fragBuilder->codeAppend("float st_grad_len = length(st_grad);"); fragBuilder->codeAppend("vec2 offset = delta*vec2(st_grad.y, -st_grad.x);"); } else { fragBuilder->codeAppendf("vec2 st = %s;\n", st.fsIn()); fragBuilder->codeAppend("vec2 Jdx = dFdx(st);"); fragBuilder->codeAppend("vec2 Jdy = dFdy(st);"); fragBuilder->codeAppend("vec2 offset = delta*Jdx;"); } // green is distance to uv center fragBuilder->codeAppend("\tvec4 texColor = "); fragBuilder->appendTextureLookup(args.fSamplers[0], "uv", kVec2f_GrSLType); fragBuilder->codeAppend(";\n"); fragBuilder->codeAppend("\tvec3 distance;\n"); fragBuilder->codeAppend("\tdistance.y = texColor.r;\n"); // red is distance to left offset fragBuilder->codeAppend("\tvec2 uv_adjusted = uv - offset;\n"); fragBuilder->codeAppend("\ttexColor = "); fragBuilder->appendTextureLookup(args.fSamplers[0], "uv_adjusted", kVec2f_GrSLType); fragBuilder->codeAppend(";\n"); fragBuilder->codeAppend("\tdistance.x = texColor.r;\n"); // blue is distance to right offset fragBuilder->codeAppend("\tuv_adjusted = uv + offset;\n"); fragBuilder->codeAppend("\ttexColor = "); fragBuilder->appendTextureLookup(args.fSamplers[0], "uv_adjusted", kVec2f_GrSLType); fragBuilder->codeAppend(";\n"); fragBuilder->codeAppend("\tdistance.z = texColor.r;\n"); fragBuilder->codeAppend("\tdistance = " "vec3(" SK_DistanceFieldMultiplier ")*(distance - vec3(" SK_DistanceFieldThreshold"));"); // adjust width based on gamma const char* distanceAdjustUniName = nullptr; fDistanceAdjustUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kVec3f_GrSLType, kDefault_GrSLPrecision, "DistanceAdjust", &distanceAdjustUniName); fragBuilder->codeAppendf("distance -= %s;", distanceAdjustUniName); // To be strictly correct, we should compute the anti-aliasing factor separately // for each color component. However, this is only important when using perspective // transformations, and even then using a single factor seems like a reasonable // trade-off between quality and speed. fragBuilder->codeAppend("float afwidth;"); if (isSimilarity) { // For similarity transform (uniform scale-only is a subset of this), we adjust for the // effect of the transformation on the distance by using the length of the gradient of // the texture coordinates. We use st coordinates to ensure we're mapping 1:1 from texel // space to pixel space. // this gives us a smooth step across approximately one fragment fragBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*st_grad_len;"); } else { // For general transforms, to determine the amount of correction we multiply a unit // vector pointing along the SDF gradient direction by the Jacobian of the st coords // (which is the inverse transform for this fragment) and take the length of the result. fragBuilder->codeAppend("vec2 dist_grad = vec2(dFdx(distance.r), dFdy(distance.r));"); // the length of the gradient may be 0, so we need to check for this // this also compensates for the Adreno, which likes to drop tiles on division by 0 fragBuilder->codeAppend("float dg_len2 = dot(dist_grad, dist_grad);"); fragBuilder->codeAppend("if (dg_len2 < 0.0001) {"); fragBuilder->codeAppend("dist_grad = vec2(0.7071, 0.7071);"); fragBuilder->codeAppend("} else {"); fragBuilder->codeAppend("dist_grad = dist_grad*inversesqrt(dg_len2);"); fragBuilder->codeAppend("}"); fragBuilder->codeAppend("vec2 grad = vec2(dist_grad.x*Jdx.x + dist_grad.y*Jdy.x,"); fragBuilder->codeAppend(" dist_grad.x*Jdx.y + dist_grad.y*Jdy.y);"); // this gives us a smooth step across approximately one fragment fragBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*length(grad);"); } fragBuilder->codeAppend( "vec4 val = vec4(smoothstep(vec3(-afwidth), vec3(afwidth), distance), 1.0);"); // set alpha to be max of rgb coverage fragBuilder->codeAppend("val.a = max(max(val.r, val.g), val.b);"); fragBuilder->codeAppendf("%s = val;", args.fOutputCoverage); }
void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{ const GrDistanceFieldA8TextGeoProc& dfTexEffect = args.fGP.cast<GrDistanceFieldA8TextGeoProc>(); GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder; SkAssertResult(fragBuilder->enableFeature( GrGLSLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature)); GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; // emit attributes varyingHandler->emitAttributes(dfTexEffect); #ifdef SK_GAMMA_APPLY_TO_A8 // adjust based on gamma const char* distanceAdjustUniName = nullptr; // width, height, 1/(3*width) fDistanceAdjustUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kFloat_GrSLType, kDefault_GrSLPrecision, "DistanceAdjust", &distanceAdjustUniName); #endif // Setup pass through color if (!dfTexEffect.colorIgnored()) { varyingHandler->addPassThroughAttribute(dfTexEffect.inColor(), args.fOutputColor); } // Setup position this->setupPosition(vertBuilder, uniformHandler, gpArgs, dfTexEffect.inPosition()->fName, dfTexEffect.viewMatrix(), &fViewMatrixUniform); // emit transforms this->emitTransforms(vertBuilder, varyingHandler, uniformHandler, gpArgs->fPositionVar, dfTexEffect.inPosition()->fName, args.fTransformsIn, args.fTransformsOut); // add varyings GrGLSLVertToFrag recipScale(kFloat_GrSLType); GrGLSLVertToFrag uv(kVec2f_GrSLType); bool isUniformScale = (dfTexEffect.getFlags() & kUniformScale_DistanceFieldEffectMask) == kUniformScale_DistanceFieldEffectMask; bool isSimilarity = SkToBool(dfTexEffect.getFlags() & kSimilarity_DistanceFieldEffectFlag); varyingHandler->addVarying("TextureCoords", &uv, kHigh_GrSLPrecision); vertBuilder->codeAppendf("%s = %s;", uv.vsOut(), dfTexEffect.inTextureCoords()->fName); // compute numbers to be hardcoded to convert texture coordinates from float to int SkASSERT(dfTexEffect.numTextures() == 1); GrTexture* atlas = dfTexEffect.textureAccess(0).getTexture(); SkASSERT(atlas && SkIsPow2(atlas->width()) && SkIsPow2(atlas->height())); GrGLSLVertToFrag st(kVec2f_GrSLType); varyingHandler->addVarying("IntTextureCoords", &st, kHigh_GrSLPrecision); vertBuilder->codeAppendf("%s = vec2(%d, %d) * %s;", st.vsOut(), atlas->width(), atlas->height(), dfTexEffect.inTextureCoords()->fName); // Use highp to work around aliasing issues fragBuilder->codeAppend(GrGLSLShaderVar::PrecisionString(args.fGLSLCaps, kHigh_GrSLPrecision)); fragBuilder->codeAppendf("vec2 uv = %s;\n", uv.fsIn()); fragBuilder->codeAppend("\tfloat texColor = "); fragBuilder->appendTextureLookup(args.fSamplers[0], "uv", kVec2f_GrSLType); fragBuilder->codeAppend(".r;\n"); fragBuilder->codeAppend("\tfloat distance = " SK_DistanceFieldMultiplier "*(texColor - " SK_DistanceFieldThreshold ");"); #ifdef SK_GAMMA_APPLY_TO_A8 // adjust width based on gamma fragBuilder->codeAppendf("distance -= %s;", distanceAdjustUniName); #endif fragBuilder->codeAppend("float afwidth;"); if (isUniformScale) { // For uniform scale, we adjust for the effect of the transformation on the distance // by using the length of the gradient of the t coordinate in the y direction. // We use st coordinates to ensure we're mapping 1:1 from texel space to pixel space. // We use the y gradient because there is a bug in the Mali 400 in the x direction. // this gives us a smooth step across approximately one fragment fragBuilder->codeAppendf("afwidth = abs(" SK_DistanceFieldAAFactor "*dFdy(%s.y));", st.fsIn()); } else if (isSimilarity) { // For similarity transform, we adjust the effect of the transformation on the distance // by using the length of the gradient of the texture coordinates. We use st coordinates // to ensure we're mapping 1:1 from texel space to pixel space. // We use the y gradient because there is a bug in the Mali 400 in the x direction. // this gives us a smooth step across approximately one fragment fragBuilder->codeAppendf("float st_grad_len = length(dFdy(%s));", st.fsIn()); fragBuilder->codeAppend("afwidth = abs(" SK_DistanceFieldAAFactor "*st_grad_len);"); } else { // For general transforms, to determine the amount of correction we multiply a unit // vector pointing along the SDF gradient direction by the Jacobian of the st coords // (which is the inverse transform for this fragment) and take the length of the result. fragBuilder->codeAppend("vec2 dist_grad = vec2(dFdx(distance), dFdy(distance));"); // the length of the gradient may be 0, so we need to check for this // this also compensates for the Adreno, which likes to drop tiles on division by 0 fragBuilder->codeAppend("float dg_len2 = dot(dist_grad, dist_grad);"); fragBuilder->codeAppend("if (dg_len2 < 0.0001) {"); fragBuilder->codeAppend("dist_grad = vec2(0.7071, 0.7071);"); fragBuilder->codeAppend("} else {"); fragBuilder->codeAppend("dist_grad = dist_grad*inversesqrt(dg_len2);"); fragBuilder->codeAppend("}"); fragBuilder->codeAppendf("vec2 Jdx = dFdx(%s);", st.fsIn()); fragBuilder->codeAppendf("vec2 Jdy = dFdy(%s);", st.fsIn()); fragBuilder->codeAppend("vec2 grad = vec2(dist_grad.x*Jdx.x + dist_grad.y*Jdy.x,"); fragBuilder->codeAppend(" dist_grad.x*Jdx.y + dist_grad.y*Jdy.y);"); // this gives us a smooth step across approximately one fragment fragBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*length(grad);"); } fragBuilder->codeAppend("float val = smoothstep(-afwidth, afwidth, distance);"); fragBuilder->codeAppendf("%s = vec4(val);", args.fOutputCoverage); }
void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { const DefaultGeoProc& gp = args.fGP.cast<DefaultGeoProc>(); GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; // emit attributes varyingHandler->emitAttributes(gp); // Setup pass through color if (gp.hasVertexColor()) { GrGLSLVarying varying(kHalf4_GrSLType); varyingHandler->addVarying("color", &varying); // There are several optional steps to process the color. Start with the attribute: vertBuilder->codeAppendf("half4 color = %s;", gp.inColor()->fName); // Linearize if (gp.linearizeColor()) { SkString srgbFuncName; static const GrShaderVar gSrgbArgs[] = { GrShaderVar("x", kHalf_GrSLType), }; vertBuilder->emitFunction(kHalf_GrSLType, "srgb_to_linear", SK_ARRAY_COUNT(gSrgbArgs), gSrgbArgs, "return (x <= 0.04045) ? (x / 12.92) " ": pow((x + 0.055) / 1.055, 2.4);", &srgbFuncName); vertBuilder->codeAppendf("color = half4(%s(%s.r), %s(%s.g), %s(%s.b), %s.a);", srgbFuncName.c_str(), gp.inColor()->fName, srgbFuncName.c_str(), gp.inColor()->fName, srgbFuncName.c_str(), gp.inColor()->fName, gp.inColor()->fName); } // For SkColor, do a red/blue swap and premul if (gp.fFlags & kColorAttributeIsSkColor_GPFlag) { vertBuilder->codeAppend("color = half4(color.a * color.bgr, color.a);"); } // Do color-correction to destination gamut if (gp.linearizeColor()) { fColorSpaceHelper.emitCode(uniformHandler, gp.fColorSpaceXform.get(), kVertex_GrShaderFlag); if (fColorSpaceHelper.isValid()) { SkString xformedColor; vertBuilder->appendColorGamutXform(&xformedColor, "color", &fColorSpaceHelper); vertBuilder->codeAppendf("color = %s;", xformedColor.c_str()); } } vertBuilder->codeAppendf("%s = color;\n", varying.vsOut()); fragBuilder->codeAppendf("%s = %s;", args.fOutputColor, varying.fsIn()); } else { this->setupUniformColor(fragBuilder, uniformHandler, args.fOutputColor, &fColorUniform); } // Setup position this->writeOutputPosition(vertBuilder, uniformHandler, gpArgs, gp.inPosition()->fName, gp.viewMatrix(), &fViewMatrixUniform); if (gp.hasExplicitLocalCoords()) { // emit transforms with explicit local coords this->emitTransforms(vertBuilder, varyingHandler, uniformHandler, gp.inLocalCoords()->asShaderVar(), gp.localMatrix(), args.fFPCoordTransformHandler); } else { // emit transforms with position this->emitTransforms(vertBuilder, varyingHandler, uniformHandler, gp.inPosition()->asShaderVar(), gp.localMatrix(), args.fFPCoordTransformHandler); } // Setup coverage as pass through if (gp.hasVertexCoverage()) { fragBuilder->codeAppendf("half alpha = 1.0;"); varyingHandler->addPassThroughAttribute(gp.inCoverage(), "alpha"); fragBuilder->codeAppendf("%s = half4(alpha);", args.fOutputCoverage); } else if (gp.coverage() == 0xff) { fragBuilder->codeAppendf("%s = half4(1);", args.fOutputCoverage); } else { const char* fragCoverage; fCoverageUniform = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf_GrSLType, "Coverage", &fragCoverage); fragBuilder->codeAppendf("%s = half4(%s);", args.fOutputCoverage, fragCoverage); } }
void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { const DefaultGeoProc& gp = args.fGP.cast<DefaultGeoProc>(); GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder; GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; // emit attributes varyingHandler->emitAttributes(gp); // Setup pass through color if (!gp.colorIgnored()) { if (gp.hasVertexColor()) { varyingHandler->addPassThroughAttribute(gp.inColor(), args.fOutputColor); } else { this->setupUniformColor(fragBuilder, uniformHandler, args.fOutputColor, &fColorUniform); } } // Setup position this->setupPosition(vertBuilder, uniformHandler, gpArgs, gp.inPosition()->fName, gp.viewMatrix(), &fViewMatrixUniform); if (gp.hasExplicitLocalCoords()) { // emit transforms with explicit local coords this->emitTransforms(vertBuilder, varyingHandler, uniformHandler, gpArgs->fPositionVar, gp.inLocalCoords()->fName, gp.localMatrix(), args.fTransformsIn, args.fTransformsOut); } else if(gp.hasTransformedLocalCoords()) { // transforms have already been applied to vertex attributes on the cpu this->emitTransforms(vertBuilder, varyingHandler, gp.inLocalCoords()->fName, args.fTransformsIn, args.fTransformsOut); } else { // emit transforms with position this->emitTransforms(vertBuilder, varyingHandler, uniformHandler, gpArgs->fPositionVar, gp.inPosition()->fName, gp.localMatrix(), args.fTransformsIn, args.fTransformsOut); } // Setup coverage as pass through if (!gp.coverageWillBeIgnored()) { if (gp.hasVertexCoverage()) { fragBuilder->codeAppendf("float alpha = 1.0;"); varyingHandler->addPassThroughAttribute(gp.inCoverage(), "alpha"); fragBuilder->codeAppendf("%s = vec4(alpha);", args.fOutputCoverage); } else if (gp.coverage() == 0xff) { fragBuilder->codeAppendf("%s = vec4(1);", args.fOutputCoverage); } else { const char* fragCoverage; fCoverageUniform = uniformHandler->addUniform(kFragment_GrShaderFlag, kFloat_GrSLType, kDefault_GrSLPrecision, "Coverage", &fragCoverage); fragBuilder->codeAppendf("%s = vec4(%s);", args.fOutputCoverage, fragCoverage); } } }
void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { const GrBitmapTextGeoProc& cte = args.fGP.cast<GrBitmapTextGeoProc>(); GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; // emit attributes varyingHandler->emitAttributes(cte); // compute numbers to be hardcoded to convert texture coordinates from int to float SkASSERT(cte.numTextures() == 1); SkDEBUGCODE(GrTexture* atlas = cte.textureAccess(0).getTexture()); SkASSERT(atlas && SkIsPow2(atlas->width()) && SkIsPow2(atlas->height())); GrGLSLVertToFrag v(kVec2f_GrSLType); varyingHandler->addVarying("TextureCoords", &v, kHigh_GrSLPrecision); vertBuilder->codeAppendf("%s = %s;", v.vsOut(), cte.inTextureCoords()->fName); GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder; // Setup pass through color if (!cte.colorIgnored()) { if (cte.hasVertexColor()) { varyingHandler->addPassThroughAttribute(cte.inColor(), args.fOutputColor); } else { this->setupUniformColor(fragBuilder, uniformHandler, args.fOutputColor, &fColorUniform); } } // Setup position this->setupPosition(vertBuilder, gpArgs, cte.inPosition()->fName); // emit transforms this->emitTransforms(vertBuilder, varyingHandler, uniformHandler, gpArgs->fPositionVar, cte.inPosition()->fName, cte.localMatrix(), args.fTransformsIn, args.fTransformsOut); if (cte.maskFormat() == kARGB_GrMaskFormat) { fragBuilder->codeAppendf("%s = ", args.fOutputColor); fragBuilder->appendTextureLookupAndModulate(args.fOutputColor, args.fTexSamplers[0], v.fsIn(), kVec2f_GrSLType); fragBuilder->codeAppend(";"); fragBuilder->codeAppendf("%s = vec4(1);", args.fOutputCoverage); } else { fragBuilder->codeAppendf("%s = ", args.fOutputCoverage); fragBuilder->appendTextureLookup(args.fTexSamplers[0], v.fsIn(), kVec2f_GrSLType); fragBuilder->codeAppend(";"); if (cte.maskFormat() == kA565_GrMaskFormat) { // set alpha to be max of rgb coverage fragBuilder->codeAppendf("%s.a = max(max(%s.r, %s.g), %s.b);", args.fOutputCoverage, args.fOutputCoverage, args.fOutputCoverage, args.fOutputCoverage); } } }
void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{ const GrDistanceFieldPathGeoProc& dfTexEffect = args.fGP.cast<GrDistanceFieldPathGeoProc>(); GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; SkAssertResult(fragBuilder->enableFeature( GrGLSLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature)); GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; // emit attributes varyingHandler->emitAttributes(dfTexEffect); GrGLSLVertToFrag v(kVec2f_GrSLType); varyingHandler->addVarying("TextureCoords", &v, kHigh_GrSLPrecision); // setup pass through color if (!dfTexEffect.colorIgnored()) { varyingHandler->addPassThroughAttribute(dfTexEffect.inColor(), args.fOutputColor); } vertBuilder->codeAppendf("%s = %s;", v.vsOut(), dfTexEffect.inTextureCoords()->fName); // Setup position this->setupPosition(vertBuilder, uniformHandler, gpArgs, dfTexEffect.inPosition()->fName, dfTexEffect.viewMatrix(), &fViewMatrixUniform); // emit transforms this->emitTransforms(vertBuilder, varyingHandler, uniformHandler, gpArgs->fPositionVar, dfTexEffect.inPosition()->fName, args.fTransformsIn, args.fTransformsOut); const char* textureSizeUniName = nullptr; fTextureSizeUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, kVec2f_GrSLType, kDefault_GrSLPrecision, "TextureSize", &textureSizeUniName); // Use highp to work around aliasing issues fragBuilder->codeAppend(GrGLSLShaderVar::PrecisionString(args.fGLSLCaps, kHigh_GrSLPrecision)); fragBuilder->codeAppendf("vec2 uv = %s;", v.fsIn()); fragBuilder->codeAppend("float texColor = "); fragBuilder->appendTextureLookup(args.fSamplers[0], "uv", kVec2f_GrSLType); fragBuilder->codeAppend(".r;"); fragBuilder->codeAppend("float distance = " SK_DistanceFieldMultiplier "*(texColor - " SK_DistanceFieldThreshold ");"); fragBuilder->codeAppend(GrGLSLShaderVar::PrecisionString(args.fGLSLCaps, kHigh_GrSLPrecision)); fragBuilder->codeAppendf("vec2 st = uv*%s;", textureSizeUniName); fragBuilder->codeAppend("float afwidth;"); if (dfTexEffect.getFlags() & kSimilarity_DistanceFieldEffectFlag) { // For uniform scale, we adjust for the effect of the transformation on the distance // by using the length of the gradient of the texture coordinates. We use st coordinates // to ensure we're mapping 1:1 from texel space to pixel space. // this gives us a smooth step across approximately one fragment fragBuilder->codeAppend("afwidth = abs(" SK_DistanceFieldAAFactor "*dFdy(st.y));"); } else { // For general transforms, to determine the amount of correction we multiply a unit // vector pointing along the SDF gradient direction by the Jacobian of the st coords // (which is the inverse transform for this fragment) and take the length of the result. fragBuilder->codeAppend("vec2 dist_grad = vec2(dFdx(distance), dFdy(distance));"); // the length of the gradient may be 0, so we need to check for this // this also compensates for the Adreno, which likes to drop tiles on division by 0 fragBuilder->codeAppend("float dg_len2 = dot(dist_grad, dist_grad);"); fragBuilder->codeAppend("if (dg_len2 < 0.0001) {"); fragBuilder->codeAppend("dist_grad = vec2(0.7071, 0.7071);"); fragBuilder->codeAppend("} else {"); fragBuilder->codeAppend("dist_grad = dist_grad*inversesqrt(dg_len2);"); fragBuilder->codeAppend("}"); fragBuilder->codeAppend("vec2 Jdx = dFdx(st);"); fragBuilder->codeAppend("vec2 Jdy = dFdy(st);"); fragBuilder->codeAppend("vec2 grad = vec2(dist_grad.x*Jdx.x + dist_grad.y*Jdy.x,"); fragBuilder->codeAppend(" dist_grad.x*Jdx.y + dist_grad.y*Jdy.y);"); // this gives us a smooth step across approximately one fragment fragBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*length(grad);"); } fragBuilder->codeAppend("float val = smoothstep(-afwidth, afwidth, distance);"); fragBuilder->codeAppendf("%s = vec4(val);", args.fOutputCoverage); }
void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{ const GrDistanceFieldPathGeoProc& dfTexEffect = args.fGP.cast<GrDistanceFieldPathGeoProc>(); GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder; SkAssertResult(fragBuilder->enableFeature( GrGLSLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature)); GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; // emit attributes varyingHandler->emitAttributes(dfTexEffect); GrGLSLVertToFrag v(kVec2f_GrSLType); varyingHandler->addVarying("TextureCoords", &v, kHigh_GrSLPrecision); // setup pass through color if (!dfTexEffect.colorIgnored()) { varyingHandler->addPassThroughAttribute(dfTexEffect.inColor(), args.fOutputColor); } vertBuilder->codeAppendf("%s = %s;", v.vsOut(), dfTexEffect.inTextureCoords()->fName); // Setup position this->setupPosition(vertBuilder, uniformHandler, gpArgs, dfTexEffect.inPosition()->fName, dfTexEffect.viewMatrix(), &fViewMatrixUniform); // emit transforms this->emitTransforms(vertBuilder, varyingHandler, uniformHandler, gpArgs->fPositionVar, dfTexEffect.inPosition()->fName, args.fFPCoordTransformHandler); const char* textureSizeUniName = nullptr; fTextureSizeUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kVec2f_GrSLType, kDefault_GrSLPrecision, "TextureSize", &textureSizeUniName); // Use highp to work around aliasing issues fragBuilder->appendPrecisionModifier(kHigh_GrSLPrecision); fragBuilder->codeAppendf("vec2 uv = %s;", v.fsIn()); fragBuilder->codeAppend("float texColor = "); fragBuilder->appendTextureLookup(args.fTexSamplers[0], "uv", kVec2f_GrSLType); fragBuilder->codeAppend(".r;"); fragBuilder->codeAppend("float distance = " SK_DistanceFieldMultiplier "*(texColor - " SK_DistanceFieldThreshold ");"); fragBuilder->appendPrecisionModifier(kHigh_GrSLPrecision); fragBuilder->codeAppendf("vec2 st = uv*%s;", textureSizeUniName); fragBuilder->codeAppend("float afwidth;"); bool isUniformScale = (dfTexEffect.getFlags() & kUniformScale_DistanceFieldEffectMask) == kUniformScale_DistanceFieldEffectMask; bool isSimilarity = SkToBool(dfTexEffect.getFlags() & kSimilarity_DistanceFieldEffectFlag); bool isGammaCorrect = SkToBool(dfTexEffect.getFlags() & kGammaCorrect_DistanceFieldEffectFlag); if (isUniformScale) { // For uniform scale, we adjust for the effect of the transformation on the distance // by using the length of the gradient of the t coordinate in the y direction. // We use st coordinates to ensure we're mapping 1:1 from texel space to pixel space. // this gives us a smooth step across approximately one fragment #ifdef SK_VULKAN fragBuilder->codeAppend("afwidth = abs(" SK_DistanceFieldAAFactor "*dFdx(st.x));"); #else // We use the y gradient because there is a bug in the Mali 400 in the x direction. fragBuilder->codeAppend("afwidth = abs(" SK_DistanceFieldAAFactor "*dFdy(st.y));"); #endif } else if (isSimilarity) { // For similarity transform, we adjust the effect of the transformation on the distance // by using the length of the gradient of the texture coordinates. We use st coordinates // to ensure we're mapping 1:1 from texel space to pixel space. // this gives us a smooth step across approximately one fragment #ifdef SK_VULKAN fragBuilder->codeAppend("float st_grad_len = length(dFdx(st));"); #else // We use the y gradient because there is a bug in the Mali 400 in the x direction. fragBuilder->codeAppend("float st_grad_len = length(dFdy(st));"); #endif fragBuilder->codeAppend("afwidth = abs(" SK_DistanceFieldAAFactor "*st_grad_len);"); } else { // For general transforms, to determine the amount of correction we multiply a unit // vector pointing along the SDF gradient direction by the Jacobian of the st coords // (which is the inverse transform for this fragment) and take the length of the result. fragBuilder->codeAppend("vec2 dist_grad = vec2(dFdx(distance), dFdy(distance));"); // the length of the gradient may be 0, so we need to check for this // this also compensates for the Adreno, which likes to drop tiles on division by 0 fragBuilder->codeAppend("float dg_len2 = dot(dist_grad, dist_grad);"); fragBuilder->codeAppend("if (dg_len2 < 0.0001) {"); fragBuilder->codeAppend("dist_grad = vec2(0.7071, 0.7071);"); fragBuilder->codeAppend("} else {"); fragBuilder->codeAppend("dist_grad = dist_grad*inversesqrt(dg_len2);"); fragBuilder->codeAppend("}"); fragBuilder->codeAppend("vec2 Jdx = dFdx(st);"); fragBuilder->codeAppend("vec2 Jdy = dFdy(st);"); fragBuilder->codeAppend("vec2 grad = vec2(dist_grad.x*Jdx.x + dist_grad.y*Jdy.x,"); fragBuilder->codeAppend(" dist_grad.x*Jdx.y + dist_grad.y*Jdy.y);"); // this gives us a smooth step across approximately one fragment fragBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*length(grad);"); } // The smoothstep falloff compensates for the non-linear sRGB response curve. If we are // doing gamma-correct rendering (to an sRGB or F16 buffer), then we actually want distance // mapped linearly to coverage, so use a linear step: if (isGammaCorrect) { fragBuilder->codeAppend( "float val = clamp(distance + afwidth / (2.0 * afwidth), 0.0, 1.0);"); } else { fragBuilder->codeAppend("float val = smoothstep(-afwidth, afwidth, distance);"); } fragBuilder->codeAppendf("%s = vec4(val);", args.fOutputCoverage); }
void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{ const GrDistanceFieldLCDTextGeoProc& dfTexEffect = args.fGP.cast<GrDistanceFieldLCDTextGeoProc>(); GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; // emit attributes varyingHandler->emitAttributes(dfTexEffect); const char* atlasSizeInvName; fAtlasSizeInvUniform = uniformHandler->addUniform(kVertex_GrShaderFlag, kFloat2_GrSLType, kHigh_GrSLPrecision, "AtlasSizeInv", &atlasSizeInvName); GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; // setup pass through color varyingHandler->addPassThroughAttribute(dfTexEffect.inColor(), args.fOutputColor); // Setup position gpArgs->fPositionVar = dfTexEffect.inPosition()->asShaderVar(); // emit transforms this->emitTransforms(vertBuilder, varyingHandler, uniformHandler, dfTexEffect.inPosition()->asShaderVar(), dfTexEffect.localMatrix(), args.fFPCoordTransformHandler); // set up varyings GrGLSLVarying uv(kFloat2_GrSLType); GrSLType texIdxType = args.fShaderCaps->integerSupport() ? kInt_GrSLType : kFloat_GrSLType; GrGLSLVarying texIdx(texIdxType); GrGLSLVarying st(kFloat2_GrSLType); append_index_uv_varyings(args, dfTexEffect.inTextureCoords()->fName, atlasSizeInvName, &uv, &texIdx, &st); GrGLSLVarying delta(kFloat_GrSLType); varyingHandler->addVarying("Delta", &delta); if (dfTexEffect.getFlags() & kBGR_DistanceFieldEffectFlag) { vertBuilder->codeAppendf("%s = -%s.x/3.0;", delta.vsOut(), atlasSizeInvName); } else { vertBuilder->codeAppendf("%s = %s.x/3.0;", delta.vsOut(), atlasSizeInvName); } // add frag shader code bool isUniformScale = (dfTexEffect.getFlags() & kUniformScale_DistanceFieldEffectMask) == kUniformScale_DistanceFieldEffectMask; bool isSimilarity = SkToBool(dfTexEffect.getFlags() & kSimilarity_DistanceFieldEffectFlag); bool isGammaCorrect = SkToBool(dfTexEffect.getFlags() & kGammaCorrect_DistanceFieldEffectFlag); // create LCD offset adjusted by inverse of transform // Use highp to work around aliasing issues fragBuilder->codeAppendf("float2 uv = %s;\n", uv.fsIn()); if (isUniformScale) { #ifdef SK_VULKAN fragBuilder->codeAppendf("half st_grad_len = abs(dFdx(%s.x));", st.fsIn()); #else // We use the y gradient because there is a bug in the Mali 400 in the x direction. fragBuilder->codeAppendf("half st_grad_len = abs(dFdy(%s.y));", st.fsIn()); #endif fragBuilder->codeAppendf("half2 offset = half2(st_grad_len*%s, 0.0);", delta.fsIn()); } else if (isSimilarity) { // For a similarity matrix with rotation, the gradient will not be aligned // with the texel coordinate axes, so we need to calculate it. #ifdef SK_VULKAN fragBuilder->codeAppendf("half2 st_grad = dFdx(%s);", st.fsIn()); fragBuilder->codeAppendf("half2 offset = %s*st_grad;", delta.fsIn()); #else // We use dFdy because of a Mali 400 bug, and rotate -90 degrees to // get the gradient in the x direction. fragBuilder->codeAppendf("half2 st_grad = dFdy(%s);", st.fsIn()); fragBuilder->codeAppendf("half2 offset = %s*half2(st_grad.y, -st_grad.x);", delta.fsIn()); #endif fragBuilder->codeAppend("half st_grad_len = length(st_grad);"); } else { fragBuilder->codeAppendf("half2 st = %s;\n", st.fsIn()); fragBuilder->codeAppend("half2 Jdx = dFdx(st);"); fragBuilder->codeAppend("half2 Jdy = dFdy(st);"); fragBuilder->codeAppendf("half2 offset = %s*Jdx;", delta.fsIn()); } // sample the texture by index fragBuilder->codeAppend("half4 texColor;"); append_multitexture_lookup(args, dfTexEffect.numTextureSamplers(), texIdx, "uv", "texColor"); // green is distance to uv center fragBuilder->codeAppend("half3 distance;"); fragBuilder->codeAppend("distance.y = texColor.r;"); // red is distance to left offset fragBuilder->codeAppend("half2 uv_adjusted = uv - offset;"); append_multitexture_lookup(args, dfTexEffect.numTextureSamplers(), texIdx, "uv_adjusted", "texColor"); fragBuilder->codeAppend("distance.x = texColor.r;"); // blue is distance to right offset fragBuilder->codeAppend("uv_adjusted = uv + offset;"); append_multitexture_lookup(args, dfTexEffect.numTextureSamplers(), texIdx, "uv_adjusted", "texColor"); fragBuilder->codeAppend("distance.z = texColor.r;"); fragBuilder->codeAppend("distance = " "half3(" SK_DistanceFieldMultiplier ")*(distance - half3(" SK_DistanceFieldThreshold"));"); // adjust width based on gamma const char* distanceAdjustUniName = nullptr; fDistanceAdjustUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf3_GrSLType, "DistanceAdjust", &distanceAdjustUniName); fragBuilder->codeAppendf("distance -= %s;", distanceAdjustUniName); // To be strictly correct, we should compute the anti-aliasing factor separately // for each color component. However, this is only important when using perspective // transformations, and even then using a single factor seems like a reasonable // trade-off between quality and speed. fragBuilder->codeAppend("half afwidth;"); if (isSimilarity) { // For similarity transform (uniform scale-only is a subset of this), we adjust for the // effect of the transformation on the distance by using the length of the gradient of // the texture coordinates. We use st coordinates to ensure we're mapping 1:1 from texel // space to pixel space. // this gives us a smooth step across approximately one fragment fragBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*st_grad_len;"); } else { // For general transforms, to determine the amount of correction we multiply a unit // vector pointing along the SDF gradient direction by the Jacobian of the st coords // (which is the inverse transform for this fragment) and take the length of the result. fragBuilder->codeAppend("half2 dist_grad = half2(dFdx(distance.r), dFdy(distance.r));"); // the length of the gradient may be 0, so we need to check for this // this also compensates for the Adreno, which likes to drop tiles on division by 0 fragBuilder->codeAppend("half dg_len2 = dot(dist_grad, dist_grad);"); fragBuilder->codeAppend("if (dg_len2 < 0.0001) {"); fragBuilder->codeAppend("dist_grad = half2(0.7071, 0.7071);"); fragBuilder->codeAppend("} else {"); fragBuilder->codeAppend("dist_grad = dist_grad*inversesqrt(dg_len2);"); fragBuilder->codeAppend("}"); fragBuilder->codeAppend("half2 grad = half2(dist_grad.x*Jdx.x + dist_grad.y*Jdy.x,"); fragBuilder->codeAppend(" dist_grad.x*Jdx.y + dist_grad.y*Jdy.y);"); // this gives us a smooth step across approximately one fragment fragBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*length(grad);"); } // The smoothstep falloff compensates for the non-linear sRGB response curve. If we are // doing gamma-correct rendering (to an sRGB or F16 buffer), then we actually want distance // mapped linearly to coverage, so use a linear step: if (isGammaCorrect) { fragBuilder->codeAppendf("%s = " "half4(clamp((distance + half3(afwidth)) / half3(2.0 * afwidth), 0.0, 1.0), 1.0);", args.fOutputCoverage); } else { fragBuilder->codeAppendf( "%s = half4(smoothstep(half3(-afwidth), half3(afwidth), distance), 1.0);", args.fOutputCoverage); } }
void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{ const GrDistanceFieldPathGeoProc& dfPathEffect = args.fGP.cast<GrDistanceFieldPathGeoProc>(); GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; // emit attributes varyingHandler->emitAttributes(dfPathEffect); const char* atlasSizeInvName; fAtlasSizeInvUniform = uniformHandler->addUniform(kVertex_GrShaderFlag, kFloat2_GrSLType, kHigh_GrSLPrecision, "AtlasSizeInv", &atlasSizeInvName); GrGLSLVarying uv(kFloat2_GrSLType); GrSLType texIdxType = args.fShaderCaps->integerSupport() ? kInt_GrSLType : kFloat_GrSLType; GrGLSLVarying texIdx(texIdxType); GrGLSLVarying st(kFloat2_GrSLType); append_index_uv_varyings(args, dfPathEffect.inTextureCoords()->fName, atlasSizeInvName, &uv, &texIdx, &st); // setup pass through color varyingHandler->addPassThroughAttribute(dfPathEffect.inColor(), args.fOutputColor); if (dfPathEffect.matrix().hasPerspective()) { // Setup position this->writeOutputPosition(vertBuilder, uniformHandler, gpArgs, dfPathEffect.inPosition()->fName, dfPathEffect.matrix(), &fMatrixUniform); // emit transforms this->emitTransforms(vertBuilder, varyingHandler, uniformHandler, dfPathEffect.inPosition()->asShaderVar(), args.fFPCoordTransformHandler); } else { // Setup position this->writeOutputPosition(vertBuilder, gpArgs, dfPathEffect.inPosition()->fName); // emit transforms this->emitTransforms(vertBuilder, varyingHandler, uniformHandler, dfPathEffect.inPosition()->asShaderVar(), dfPathEffect.matrix(), args.fFPCoordTransformHandler); } // Use highp to work around aliasing issues fragBuilder->codeAppendf("float2 uv = %s;", uv.fsIn()); fragBuilder->codeAppend("half4 texColor;"); append_multitexture_lookup(args, dfPathEffect.numTextureSamplers(), texIdx, "uv", "texColor"); fragBuilder->codeAppend("half distance = " SK_DistanceFieldMultiplier "*(texColor.r - " SK_DistanceFieldThreshold ");"); fragBuilder->codeAppend("half afwidth;"); bool isUniformScale = (dfPathEffect.getFlags() & kUniformScale_DistanceFieldEffectMask) == kUniformScale_DistanceFieldEffectMask; bool isSimilarity = SkToBool(dfPathEffect.getFlags() & kSimilarity_DistanceFieldEffectFlag); bool isGammaCorrect = SkToBool(dfPathEffect.getFlags() & kGammaCorrect_DistanceFieldEffectFlag); if (isUniformScale) { // For uniform scale, we adjust for the effect of the transformation on the distance // by using the length of the gradient of the t coordinate in the y direction. // We use st coordinates to ensure we're mapping 1:1 from texel space to pixel space. // this gives us a smooth step across approximately one fragment #ifdef SK_VULKAN fragBuilder->codeAppendf("afwidth = abs(" SK_DistanceFieldAAFactor "*dFdx(%s.x));", st.fsIn()); #else // We use the y gradient because there is a bug in the Mali 400 in the x direction. fragBuilder->codeAppendf("afwidth = abs(" SK_DistanceFieldAAFactor "*dFdy(%s.y));", st.fsIn()); #endif } else if (isSimilarity) { // For similarity transform, we adjust the effect of the transformation on the distance // by using the length of the gradient of the texture coordinates. We use st coordinates // to ensure we're mapping 1:1 from texel space to pixel space. // this gives us a smooth step across approximately one fragment #ifdef SK_VULKAN fragBuilder->codeAppendf("half st_grad_len = length(dFdx(%s));", st.fsIn()); #else // We use the y gradient because there is a bug in the Mali 400 in the x direction. fragBuilder->codeAppendf("half st_grad_len = length(dFdy(%s));", st.fsIn()); #endif fragBuilder->codeAppend("afwidth = abs(" SK_DistanceFieldAAFactor "*st_grad_len);"); } else { // For general transforms, to determine the amount of correction we multiply a unit // vector pointing along the SDF gradient direction by the Jacobian of the st coords // (which is the inverse transform for this fragment) and take the length of the result. fragBuilder->codeAppend("half2 dist_grad = half2(dFdx(distance), dFdy(distance));"); // the length of the gradient may be 0, so we need to check for this // this also compensates for the Adreno, which likes to drop tiles on division by 0 fragBuilder->codeAppend("half dg_len2 = dot(dist_grad, dist_grad);"); fragBuilder->codeAppend("if (dg_len2 < 0.0001) {"); fragBuilder->codeAppend("dist_grad = half2(0.7071, 0.7071);"); fragBuilder->codeAppend("} else {"); fragBuilder->codeAppend("dist_grad = dist_grad*inversesqrt(dg_len2);"); fragBuilder->codeAppend("}"); fragBuilder->codeAppendf("half2 Jdx = dFdx(%s);", st.fsIn()); fragBuilder->codeAppendf("half2 Jdy = dFdy(%s);", st.fsIn()); fragBuilder->codeAppend("half2 grad = half2(dist_grad.x*Jdx.x + dist_grad.y*Jdy.x,"); fragBuilder->codeAppend(" dist_grad.x*Jdx.y + dist_grad.y*Jdy.y);"); // this gives us a smooth step across approximately one fragment fragBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*length(grad);"); } // The smoothstep falloff compensates for the non-linear sRGB response curve. If we are // doing gamma-correct rendering (to an sRGB or F16 buffer), then we actually want distance // mapped linearly to coverage, so use a linear step: if (isGammaCorrect) { fragBuilder->codeAppend( "half val = clamp((distance + afwidth) / (2.0 * afwidth), 0.0, 1.0);"); } else { fragBuilder->codeAppend("half val = smoothstep(-afwidth, afwidth, distance);"); } fragBuilder->codeAppendf("%s = half4(val);", args.fOutputCoverage); }
void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { const auto& proc = args.fGP.cast<Processor>(); bool useHWDerivatives = (proc.fFlags & Flags::kUseHWDerivatives); bool hasPerspective = (proc.fFlags & Flags::kHasPerspective); bool hasLocalCoords = (proc.fFlags & Flags::kHasLocalCoords); SkASSERT(useHWDerivatives == hasPerspective); SkASSERT(proc.vertexStride() == sizeof(MSAAVertex)); // Emit the vertex shader. GrGLSLVertexBuilder* v = args.fVertBuilder; GrGLSLVaryingHandler* varyings = args.fVaryingHandler; varyings->emitAttributes(proc); varyings->addPassThroughAttribute(*proc.fColorAttrib, args.fOutputColor, GrGLSLVaryingHandler::Interpolation::kCanBeFlat); // Unpack vertex attribs. v->codeAppendf("float2 corner = corner_and_radius_outsets.xy;"); v->codeAppendf("float2 radius_outset = corner_and_radius_outsets.zw;"); // Identify our radii. v->codeAppend("float2 radii;"); v->codeAppend("radii.x = dot(radii_selector, radii_x);"); v->codeAppend("radii.y = dot(radii_selector, radii_y);"); v->codeAppendf("bool is_arc_section = (radii.x > 0);"); v->codeAppendf("radii = abs(radii);"); // Find our vertex position, adjusted for radii. Our rect is drawn in normalized // [-1,-1,+1,+1] space. v->codeAppend("float2 vertexpos = corner + radius_outset * radii;"); // Emit transforms. GrShaderVar localCoord("", kFloat2_GrSLType); if (hasLocalCoords) { v->codeAppend("float2 localcoord = (local_rect.xy * (1 - vertexpos) + " "local_rect.zw * (1 + vertexpos)) * .5;"); localCoord.set(kFloat2_GrSLType, "localcoord"); } this->emitTransforms(v, varyings, args.fUniformHandler, localCoord, args.fFPCoordTransformHandler); // Transform to device space. if (!hasPerspective) { v->codeAppend("float2x2 skewmatrix = float2x2(skew.xy, skew.zw);"); v->codeAppend("float2 devcoord = vertexpos * skewmatrix + translate;"); gpArgs->fPositionVar.set(kFloat2_GrSLType, "devcoord"); } else { v->codeAppend("float3x3 persp_matrix = float3x3(persp_x, persp_y, persp_z);"); v->codeAppend("float3 devcoord = float3(vertexpos, 1) * persp_matrix;"); gpArgs->fPositionVar.set(kFloat3_GrSLType, "devcoord"); } // Determine normalized arc coordinates for the implicit function. GrGLSLVarying arcCoord((useHWDerivatives) ? kFloat2_GrSLType : kFloat4_GrSLType); varyings->addVarying("arccoord", &arcCoord); v->codeAppendf("if (is_arc_section) {"); v->codeAppendf( "%s.xy = 1 - abs(radius_outset);", arcCoord.vsOut()); if (!useHWDerivatives) { // The gradient is order-1: Interpolate it across arccoord.zw. // This doesn't work with perspective. SkASSERT(!hasPerspective); v->codeAppendf("float2x2 derivatives = inverse(skewmatrix);"); v->codeAppendf("%s.zw = derivatives * (%s.xy/radii * corner * 2);", arcCoord.vsOut(), arcCoord.vsOut()); } v->codeAppendf("} else {"); if (useHWDerivatives) { v->codeAppendf("%s = float2(0);", arcCoord.vsOut()); } else { v->codeAppendf("%s = float4(0);", arcCoord.vsOut()); } v->codeAppendf("}"); // Emit the fragment shader. GrGLSLFPFragmentBuilder* f = args.fFragBuilder; f->codeAppendf("%s = half4(1);", args.fOutputCoverage); // If x,y == 0, then we are drawing a triangle that does not track an arc. f->codeAppendf("if (float2(0) != %s.xy) {", arcCoord.fsIn()); f->codeAppendf( "float fn = dot(%s.xy, %s.xy) - 1;", arcCoord.fsIn(), arcCoord.fsIn()); if (GrAAType::kMSAA == proc.fAAType) { using ScopeFlags = GrGLSLFPFragmentBuilder::ScopeFlags; if (!useHWDerivatives) { f->codeAppendf("float2 grad = %s.zw;", arcCoord.fsIn()); f->applyFnToMultisampleMask("fn", "grad", ScopeFlags::kInsidePerPrimitiveBranch); } else { f->applyFnToMultisampleMask("fn", nullptr, ScopeFlags::kInsidePerPrimitiveBranch); } } else { f->codeAppendf("if (fn > 0) {"); f->codeAppendf( "%s = half4(0);", args.fOutputCoverage); f->codeAppendf("}"); } f->codeAppendf("}"); }
void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { const auto& proc = args.fGP.cast<Processor>(); bool useHWDerivatives = (proc.fFlags & Flags::kUseHWDerivatives); SkASSERT(proc.vertexStride() == sizeof(CoverageVertex)); GrGLSLVaryingHandler* varyings = args.fVaryingHandler; varyings->emitAttributes(proc); varyings->addPassThroughAttribute(*proc.fColorAttrib, args.fOutputColor, GrGLSLVaryingHandler::Interpolation::kCanBeFlat); // Emit the vertex shader. GrGLSLVertexBuilder* v = args.fVertBuilder; // Unpack vertex attribs. v->codeAppend("float2 corner = corner_and_radius_outsets.xy;"); v->codeAppend("float2 radius_outset = corner_and_radius_outsets.zw;"); v->codeAppend("float2 aa_bloat_direction = aa_bloat_and_coverage.xy;"); v->codeAppend("float coverage = aa_bloat_and_coverage.z;"); v->codeAppend("float is_linear_coverage = aa_bloat_and_coverage.w;"); // Find the amount to bloat each edge for AA (in source space). v->codeAppend("float2 pixellength = inversesqrt(" "float2(dot(skew.xz, skew.xz), dot(skew.yw, skew.yw)));"); v->codeAppend("float4 normalized_axis_dirs = skew * pixellength.xyxy;"); v->codeAppend("float2 axiswidths = (abs(normalized_axis_dirs.xy) + " "abs(normalized_axis_dirs.zw));"); v->codeAppend("float2 aa_bloatradius = axiswidths * pixellength * .5;"); // Identify our radii. v->codeAppend("float4 radii_and_neighbors = radii_selector" "* float4x4(radii_x, radii_y, radii_x.yxwz, radii_y.wzyx);"); v->codeAppend("float2 radii = radii_and_neighbors.xy;"); v->codeAppend("float2 neighbor_radii = radii_and_neighbors.zw;"); v->codeAppend("if (any(greaterThan(aa_bloatradius, float2(1)))) {"); // The rrect is more narrow than an AA coverage ramp. We can't draw as-is // or else opposite AA borders will overlap. Instead, fudge the size up to // the width of a coverage ramp, and then reduce total coverage to make // the rect appear more thin. v->codeAppend( "corner = max(abs(corner), aa_bloatradius) * sign(corner);"); v->codeAppend( "coverage /= max(aa_bloatradius.x, 1) * max(aa_bloatradius.y, 1);"); // Set radii to zero to ensure we take the "linear coverage" codepath. // (The "coverage" variable only has effect in the linear codepath.) v->codeAppend( "radii = float2(0);"); v->codeAppend("}"); v->codeAppend("if (any(lessThan(radii, aa_bloatradius * 1.25))) {"); // The radii are very small. Demote this arc to a sharp 90 degree corner. v->codeAppend( "radii = aa_bloatradius;"); // Snap octagon vertices to the corner of the bounding box. v->codeAppend( "radius_outset = floor(abs(radius_outset)) * radius_outset;"); v->codeAppend( "is_linear_coverage = 1;"); v->codeAppend("} else {"); // Don't let radii get smaller than a pixel. v->codeAppend( "radii = clamp(radii, pixellength, 2 - pixellength);"); v->codeAppend( "neighbor_radii = clamp(neighbor_radii, pixellength, 2 - pixellength);"); // Don't let neighboring radii get closer together than 1/16 pixel. v->codeAppend( "float2 spacing = 2 - radii - neighbor_radii;"); v->codeAppend( "float2 extra_pad = max(pixellength * .0625 - spacing, float2(0));"); v->codeAppend( "radii -= extra_pad * .5;"); v->codeAppend("}"); // Find our vertex position, adjusted for radii and bloated for AA. Our rect is drawn in // normalized [-1,-1,+1,+1] space. v->codeAppend("float2 aa_outset = aa_bloat_direction.xy * aa_bloatradius;"); v->codeAppend("float2 vertexpos = corner + radius_outset * radii + aa_outset;"); // Emit transforms. GrShaderVar localCoord("", kFloat2_GrSLType); if (proc.fFlags & Flags::kHasLocalCoords) { v->codeAppend("float2 localcoord = (local_rect.xy * (1 - vertexpos) + " "local_rect.zw * (1 + vertexpos)) * .5;"); localCoord.set(kFloat2_GrSLType, "localcoord"); } this->emitTransforms(v, varyings, args.fUniformHandler, localCoord, args.fFPCoordTransformHandler); // Transform to device space. SkASSERT(!(proc.fFlags & Flags::kHasPerspective)); v->codeAppend("float2x2 skewmatrix = float2x2(skew.xy, skew.zw);"); v->codeAppend("float2 devcoord = vertexpos * skewmatrix + translate;"); gpArgs->fPositionVar.set(kFloat2_GrSLType, "devcoord"); // Setup interpolants for coverage. GrGLSLVarying arcCoord(useHWDerivatives ? kFloat2_GrSLType : kFloat4_GrSLType); varyings->addVarying("arccoord", &arcCoord); v->codeAppend("if (0 != is_linear_coverage) {"); // We are a non-corner piece: Set x=0 to indicate built-in coverage, and // interpolate linear coverage across y. v->codeAppendf( "%s.xy = float2(0, coverage);", arcCoord.vsOut()); v->codeAppend("} else {"); // Find the normalized arc coordinates for our corner ellipse. // (i.e., the coordinate system where x^2 + y^2 == 1). v->codeAppend( "float2 arccoord = 1 - abs(radius_outset) + aa_outset/radii * corner;"); // We are a corner piece: Interpolate the arc coordinates for coverage. // Emit x+1 to ensure no pixel in the arc has a x value of 0 (since x=0 // instructs the fragment shader to use linear coverage). v->codeAppendf( "%s.xy = float2(arccoord.x+1, arccoord.y);", arcCoord.vsOut()); if (!useHWDerivatives) { // The gradient is order-1: Interpolate it across arccoord.zw. v->codeAppendf("float2x2 derivatives = inverse(skewmatrix);"); v->codeAppendf("%s.zw = derivatives * (arccoord/radii * 2);", arcCoord.vsOut()); } v->codeAppend("}"); // Emit the fragment shader. GrGLSLFPFragmentBuilder* f = args.fFragBuilder; f->codeAppendf("float x_plus_1=%s.x, y=%s.y;", arcCoord.fsIn(), arcCoord.fsIn()); f->codeAppendf("half coverage;"); f->codeAppendf("if (0 == x_plus_1) {"); f->codeAppendf( "coverage = half(y);"); // We are a non-arc pixel (linear coverage). f->codeAppendf("} else {"); f->codeAppendf( "float fn = x_plus_1 * (x_plus_1 - 2);"); // fn = (x+1)*(x-1) = x^2-1 f->codeAppendf( "fn = fma(y,y, fn);"); // fn = x^2 + y^2 - 1 if (useHWDerivatives) { f->codeAppendf("float fnwidth = fwidth(fn);"); } else { // The gradient is interpolated across arccoord.zw. f->codeAppendf("float gx=%s.z, gy=%s.w;", arcCoord.fsIn(), arcCoord.fsIn()); f->codeAppendf("float fnwidth = abs(gx) + abs(gy);"); } f->codeAppendf( "half d = half(fn/fnwidth);"); f->codeAppendf( "coverage = clamp(.5 - d, 0, 1);"); f->codeAppendf("}"); f->codeAppendf("%s = half4(coverage);", args.fOutputCoverage); }