in src/sksl/codegen/SkSLRasterPipelineCodeGenerator.cpp [3100:3331]
bool Generator::pushIntrinsic(IntrinsicKind intrinsic, const Expression& arg0) {
switch (intrinsic) {
case IntrinsicKind::k_abs_IntrinsicKind:
if (arg0.type().componentType().isFloat()) {
// Perform abs(float) by masking off the sign bit.
if (!this->pushExpression(arg0)) {
return unsupported();
}
return this->pushAbsFloatIntrinsic(arg0.type().slotCount());
}
// We have a dedicated op for abs(int).
return this->pushIntrinsic(BuilderOp::abs_int, arg0);
case IntrinsicKind::k_any_IntrinsicKind:
if (!this->pushExpression(arg0)) {
return unsupported();
}
this->foldWithMultiOp(BuilderOp::bitwise_or_n_ints, arg0.type().slotCount());
return true;
case IntrinsicKind::k_all_IntrinsicKind:
if (!this->pushExpression(arg0)) {
return unsupported();
}
this->foldWithMultiOp(BuilderOp::bitwise_and_n_ints, arg0.type().slotCount());
return true;
case IntrinsicKind::k_acos_IntrinsicKind:
return this->pushIntrinsic(BuilderOp::acos_float, arg0);
case IntrinsicKind::k_asin_IntrinsicKind:
return this->pushIntrinsic(BuilderOp::asin_float, arg0);
case IntrinsicKind::k_atan_IntrinsicKind:
return this->pushIntrinsic(BuilderOp::atan_float, arg0);
case IntrinsicKind::k_ceil_IntrinsicKind:
return this->pushIntrinsic(BuilderOp::ceil_float, arg0);
case IntrinsicKind::k_cos_IntrinsicKind:
return this->pushIntrinsic(BuilderOp::cos_float, arg0);
case IntrinsicKind::k_degrees_IntrinsicKind: {
Literal lit180OverPi{Position{}, 57.2957795131f, &arg0.type().componentType()};
return this->pushBinaryExpression(arg0, OperatorKind::STAR, lit180OverPi);
}
case IntrinsicKind::k_floatBitsToInt_IntrinsicKind:
case IntrinsicKind::k_floatBitsToUint_IntrinsicKind:
case IntrinsicKind::k_intBitsToFloat_IntrinsicKind:
case IntrinsicKind::k_uintBitsToFloat_IntrinsicKind:
return this->pushExpression(arg0);
case IntrinsicKind::k_exp_IntrinsicKind:
return this->pushIntrinsic(BuilderOp::exp_float, arg0);
case IntrinsicKind::k_exp2_IntrinsicKind:
return this->pushIntrinsic(BuilderOp::exp2_float, arg0);
case IntrinsicKind::k_floor_IntrinsicKind:
return this->pushIntrinsic(BuilderOp::floor_float, arg0);
case IntrinsicKind::k_fract_IntrinsicKind:
// Implement fract as `x - floor(x)`.
if (!this->pushExpression(arg0)) {
return unsupported();
}
fBuilder.push_clone(arg0.type().slotCount());
fBuilder.unary_op(BuilderOp::floor_float, arg0.type().slotCount());
return this->binaryOp(arg0.type(), kSubtractOps);
case IntrinsicKind::k_inverse_IntrinsicKind:
SkASSERT(arg0.type().isMatrix());
SkASSERT(arg0.type().rows() == arg0.type().columns());
if (!this->pushExpression(arg0)) {
return unsupported();
}
fBuilder.inverse_matrix(arg0.type().rows());
return true;
case IntrinsicKind::k_inversesqrt_IntrinsicKind:
return this->pushIntrinsic(kInverseSqrtOps, arg0);
case IntrinsicKind::k_length_IntrinsicKind:
return this->pushExpression(arg0) &&
this->pushLengthIntrinsic(arg0.type().slotCount());
case IntrinsicKind::k_log_IntrinsicKind:
if (!this->pushExpression(arg0)) {
return unsupported();
}
fBuilder.unary_op(BuilderOp::log_float, arg0.type().slotCount());
return true;
case IntrinsicKind::k_log2_IntrinsicKind:
if (!this->pushExpression(arg0)) {
return unsupported();
}
fBuilder.unary_op(BuilderOp::log2_float, arg0.type().slotCount());
return true;
case IntrinsicKind::k_normalize_IntrinsicKind: {
// Implement normalize as `x / length(x)`. First, push the expression.
if (!this->pushExpression(arg0)) {
return unsupported();
}
int slotCount = arg0.type().slotCount();
if (slotCount > 1) {
#if defined(SK_USE_RSQRT_IN_RP_NORMALIZE)
// Instead of `x / sqrt(dot(x, x))`, we can get roughly the same result in less time
// by computing `x * invsqrt(dot(x, x))`.
fBuilder.push_clone(slotCount);
fBuilder.push_clone(slotCount);
fBuilder.dot_floats(slotCount);
// Compute `vec(inversesqrt(dot(x, x)))`.
fBuilder.unary_op(BuilderOp::invsqrt_float, 1);
fBuilder.push_duplicates(slotCount - 1);
// Return `x * vec(inversesqrt(dot(x, x)))`.
return this->binaryOp(arg0.type(), kMultiplyOps);
#else
// TODO: We can get roughly the same result in less time by using `invsqrt`, but
// that leads to more variance across architectures, which Chromium layout tests do
// not handle nicely.
fBuilder.push_clone(slotCount);
fBuilder.push_clone(slotCount);
fBuilder.dot_floats(slotCount);
// Compute `vec(sqrt(dot(x, x)))`.
fBuilder.unary_op(BuilderOp::sqrt_float, 1);
fBuilder.push_duplicates(slotCount - 1);
// Return `x / vec(sqrt(dot(x, x)))`.
return this->binaryOp(arg0.type(), kDivideOps);
#endif
} else {
// For single-slot normalization, we can simplify `sqrt(x * x)` into `abs(x)`.
fBuilder.push_clone(slotCount);
return this->pushAbsFloatIntrinsic(/*slots=*/1) &&
this->binaryOp(arg0.type(), kDivideOps);
}
}
case IntrinsicKind::k_not_IntrinsicKind:
return this->pushPrefixExpression(OperatorKind::LOGICALNOT, arg0);
case IntrinsicKind::k_radians_IntrinsicKind: {
Literal litPiOver180{Position{}, 0.01745329251f, &arg0.type().componentType()};
return this->pushBinaryExpression(arg0, OperatorKind::STAR, litPiOver180);
}
case IntrinsicKind::k_saturate_IntrinsicKind: {
// Implement saturate as clamp(arg, 0, 1).
Literal zeroLiteral{Position{}, 0.0, &arg0.type().componentType()};
Literal oneLiteral{Position{}, 1.0, &arg0.type().componentType()};
return this->pushIntrinsic(k_clamp_IntrinsicKind, arg0, zeroLiteral, oneLiteral);
}
case IntrinsicKind::k_sign_IntrinsicKind: {
// Implement floating-point sign() as `clamp(arg * FLT_MAX, -1, 1)`.
// FLT_MIN * FLT_MAX evaluates to 4, so multiplying any float value against FLT_MAX is
// sufficient to ensure that |value| is always 1 or greater (excluding zero and nan).
// Integer sign() doesn't need to worry about fractional values or nans, and can simply
// be `clamp(arg, -1, 1)`.
if (!this->pushExpression(arg0)) {
return unsupported();
}
if (arg0.type().componentType().isFloat()) {
Literal fltMaxLiteral{Position{}, FLT_MAX, &arg0.type().componentType()};
if (!this->pushVectorizedExpression(fltMaxLiteral, arg0.type())) {
return unsupported();
}
if (!this->binaryOp(arg0.type(), kMultiplyOps)) {
return unsupported();
}
}
Literal neg1Literal{Position{}, -1.0, &arg0.type().componentType()};
if (!this->pushVectorizedExpression(neg1Literal, arg0.type())) {
return unsupported();
}
if (!this->binaryOp(arg0.type(), kMaxOps)) {
return unsupported();
}
Literal pos1Literal{Position{}, 1.0, &arg0.type().componentType()};
if (!this->pushVectorizedExpression(pos1Literal, arg0.type())) {
return unsupported();
}
return this->binaryOp(arg0.type(), kMinOps);
}
case IntrinsicKind::k_sin_IntrinsicKind:
return this->pushIntrinsic(BuilderOp::sin_float, arg0);
case IntrinsicKind::k_sqrt_IntrinsicKind:
return this->pushIntrinsic(BuilderOp::sqrt_float, arg0);
case IntrinsicKind::k_tan_IntrinsicKind:
return this->pushIntrinsic(BuilderOp::tan_float, arg0);
case IntrinsicKind::k_transpose_IntrinsicKind:
SkASSERT(arg0.type().isMatrix());
if (!this->pushExpression(arg0)) {
return unsupported();
}
fBuilder.transpose(arg0.type().columns(), arg0.type().rows());
return true;
case IntrinsicKind::k_trunc_IntrinsicKind:
// Implement trunc as `float(int(x))`, since float-to-int rounds toward zero.
if (!this->pushExpression(arg0)) {
return unsupported();
}
fBuilder.unary_op(BuilderOp::cast_to_int_from_float, arg0.type().slotCount());
fBuilder.unary_op(BuilderOp::cast_to_float_from_int, arg0.type().slotCount());
return true;
case IntrinsicKind::k_fromLinearSrgb_IntrinsicKind:
case IntrinsicKind::k_toLinearSrgb_IntrinsicKind:
// The argument must be a half3.
SkASSERT(arg0.type().matches(*fContext.fTypes.fHalf3));
if (!this->pushExpression(arg0)) {
return unsupported();
}
if (intrinsic == IntrinsicKind::k_fromLinearSrgb_IntrinsicKind) {
fBuilder.invoke_from_linear_srgb();
} else {
fBuilder.invoke_to_linear_srgb();
}
return true;
default:
break;
}
return unsupported();
}