in src/sksl/codegen/SkSLGLSLCodeGenerator.cpp [696:951]
void GLSLCodeGenerator::writeFunctionCall(const FunctionCall& c) {
const FunctionDeclaration& function = c.function();
const ExpressionArray& arguments = c.arguments();
bool isTextureFunctionWithBias = false;
bool nameWritten = false;
const char* closingParen = ")";
switch (c.function().intrinsicKind()) {
case k_abs_IntrinsicKind: {
if (!fCaps.fEmulateAbsIntFunction)
break;
SkASSERT(arguments.size() == 1);
if (!arguments[0]->type().matches(*fContext.fTypes.fInt)) {
break;
}
// abs(int) on Intel OSX is incorrect, so emulate it:
this->write("_absemulation");
nameWritten = true;
if (!fWrittenAbsEmulation) {
fWrittenAbsEmulation = true;
fExtraFunctions.writeText("int _absemulation(int x) { return x * sign(x); }\n");
}
break;
}
case k_atan_IntrinsicKind:
if (fCaps.fMustForceNegatedAtanParamToFloat &&
arguments.size() == 2 &&
arguments[1]->is<PrefixExpression>()) {
const PrefixExpression& p = arguments[1]->as<PrefixExpression>();
if (p.getOperator().kind() == Operator::Kind::MINUS) {
this->write("atan(");
this->writeExpression(*arguments[0], Precedence::kSequence);
this->write(", -1.0 * ");
this->writeExpression(*p.operand(), Precedence::kMultiplicative);
this->write(")");
return;
}
}
break;
case k_ldexp_IntrinsicKind:
if (fCaps.fMustForceNegatedLdexpParamToMultiply &&
arguments.size() == 2 &&
arguments[1]->is<PrefixExpression>()) {
const PrefixExpression& p = arguments[1]->as<PrefixExpression>();
if (p.getOperator().kind() == Operator::Kind::MINUS) {
this->write("ldexp(");
this->writeExpression(*arguments[0], Precedence::kSequence);
this->write(", ");
this->writeExpression(*p.operand(), Precedence::kMultiplicative);
this->write(" * -1)");
return;
}
}
break;
case k_dFdy_IntrinsicKind:
// Flipping Y also negates the Y derivatives.
closingParen = "))";
this->write("(");
if (!fProgram.fConfig->fSettings.fForceNoRTFlip) {
this->write(SKSL_RTFLIP_NAME ".y * ");
}
this->write("dFdy");
nameWritten = true;
[[fallthrough]];
case k_dFdx_IntrinsicKind:
case k_fwidth_IntrinsicKind:
if (!fFoundDerivatives &&
fCaps.shaderDerivativeExtensionString()) {
this->writeExtension(fCaps.shaderDerivativeExtensionString());
fFoundDerivatives = true;
}
break;
case k_determinant_IntrinsicKind:
if (!fCaps.fBuiltinDeterminantSupport) {
SkASSERT(arguments.size() == 1);
this->writeDeterminantHack(*arguments[0]);
return;
}
break;
case k_fma_IntrinsicKind:
if (!fCaps.fBuiltinFMASupport) {
SkASSERT(arguments.size() == 3);
this->write("((");
this->writeExpression(*arguments[0], Precedence::kSequence);
this->write(") * (");
this->writeExpression(*arguments[1], Precedence::kSequence);
this->write(") + (");
this->writeExpression(*arguments[2], Precedence::kSequence);
this->write("))");
return;
}
break;
case k_fract_IntrinsicKind:
if (!fCaps.fCanUseFractForNegativeValues) {
SkASSERT(arguments.size() == 1);
this->write("(0.5 - sign(");
this->writeExpression(*arguments[0], Precedence::kSequence);
this->write(") * (0.5 - fract(abs(");
this->writeExpression(*arguments[0], Precedence::kSequence);
this->write("))))");
return;
}
break;
case k_inverse_IntrinsicKind:
if (fCaps.fGLSLGeneration < SkSL::GLSLGeneration::k140) {
SkASSERT(arguments.size() == 1);
this->writeInverseHack(*arguments[0]);
return;
}
break;
case k_inversesqrt_IntrinsicKind:
if (fCaps.fGLSLGeneration < SkSL::GLSLGeneration::k130) {
SkASSERT(arguments.size() == 1);
this->writeInverseSqrtHack(*arguments[0]);
return;
}
break;
case k_min_IntrinsicKind:
if (!fCaps.fCanUseMinAndAbsTogether) {
SkASSERT(arguments.size() == 2);
if (is_abs(*arguments[0])) {
this->writeMinAbsHack(*arguments[0], *arguments[1]);
return;
}
if (is_abs(*arguments[1])) {
// note that this violates the GLSL left-to-right evaluation semantics.
// I doubt it will ever end up mattering, but it's worth calling out.
this->writeMinAbsHack(*arguments[1], *arguments[0]);
return;
}
}
break;
case k_pow_IntrinsicKind:
if (!fCaps.fRemovePowWithConstantExponent) {
break;
}
// pow(x, y) on some NVIDIA drivers causes crashes if y is a constant.
// It's hard to tell what constitutes "constant" here, so just replace in all cases.
// Change pow(x, y) into exp2(y * log2(x))
this->write("exp2(");
this->writeExpression(*arguments[1], Precedence::kMultiplicative);
this->write(" * log2(");
this->writeExpression(*arguments[0], Precedence::kSequence);
this->write("))");
return;
case k_saturate_IntrinsicKind:
SkASSERT(arguments.size() == 1);
this->write("clamp(");
this->writeExpression(*arguments[0], Precedence::kSequence);
this->write(", 0.0, 1.0)");
return;
case k_sample_IntrinsicKind: {
const char* dim = "";
bool proj = false;
const Type& arg0Type = arguments[0]->type();
const Type& arg1Type = arguments[1]->type();
switch (arg0Type.dimensions()) {
case SpvDim1D:
dim = "1D";
isTextureFunctionWithBias = true;
if (arg1Type.matches(*fContext.fTypes.fFloat)) {
proj = false;
} else {
SkASSERT(arg1Type.matches(*fContext.fTypes.fFloat2));
proj = true;
}
break;
case SpvDim2D:
dim = "2D";
if (!arg0Type.matches(*fContext.fTypes.fSamplerExternalOES)) {
isTextureFunctionWithBias = true;
}
if (arg1Type.matches(*fContext.fTypes.fFloat2)) {
proj = false;
} else {
SkASSERT(arg1Type.matches(*fContext.fTypes.fFloat3));
proj = true;
}
break;
case SpvDim3D:
dim = "3D";
isTextureFunctionWithBias = true;
if (arg1Type.matches(*fContext.fTypes.fFloat3)) {
proj = false;
} else {
SkASSERT(arg1Type.matches(*fContext.fTypes.fFloat4));
proj = true;
}
break;
case SpvDimCube:
dim = "Cube";
isTextureFunctionWithBias = true;
proj = false;
break;
case SpvDimRect:
dim = "2DRect";
proj = false;
break;
case SpvDimBuffer:
SkASSERT(false); // doesn't exist
dim = "Buffer";
proj = false;
break;
case SpvDimSubpassData:
SkASSERT(false); // doesn't exist
dim = "SubpassData";
proj = false;
break;
}
this->write("texture");
if (fCaps.fGLSLGeneration < SkSL::GLSLGeneration::k130) {
this->write(dim);
}
if (proj) {
this->write("Proj");
}
nameWritten = true;
break;
}
case k_sampleGrad_IntrinsicKind: {
SkASSERT(arguments.size() == 4);
this->write("textureGrad");
nameWritten = true;
break;
}
case k_sampleLod_IntrinsicKind: {
SkASSERT(arguments.size() == 3);
this->write("textureLod");
nameWritten = true;
break;
}
case k_transpose_IntrinsicKind:
if (fCaps.fGLSLGeneration < SkSL::GLSLGeneration::k130) {
SkASSERT(arguments.size() == 1);
this->writeTransposeHack(*arguments[0]);
return;
}
break;
default:
break;
}
if (!nameWritten) {
this->writeIdentifier(function.mangledName());
}
this->write("(");
auto separator = SkSL::String::Separator();
for (const auto& arg : arguments) {
this->write(separator());
this->writeExpression(*arg, Precedence::kSequence);
}
if (fProgram.fConfig->fSettings.fSharpenTextures && isTextureFunctionWithBias) {
this->write(String::printf(", %g", kSharpenTexturesBias));
}
this->write(closingParen);
}