in src/sksl/ir/SkSLFunctionCall.cpp [635:1003]
static std::unique_ptr<Expression> optimize_intrinsic_call(const Context& context,
Position pos,
IntrinsicKind intrinsic,
const ExpressionArray& argArray,
const Type& returnType) {
// Replace constant variables with their literal values.
IntrinsicArguments arguments = {};
SkASSERT(SkToSizeT(argArray.size()) <= arguments.size());
for (int index = 0; index < argArray.size(); ++index) {
arguments[index] = ConstantFolder::GetConstantValueForVariable(*argArray[index]);
}
auto Get = [&](int idx, int col) -> float {
return *arguments[idx]->getConstantValue(col);
};
switch (intrinsic) {
// 8.1 : Angle and Trigonometry Functions
case k_radians_IntrinsicKind:
return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_radians);
case k_degrees_IntrinsicKind:
return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_degrees);
case k_sin_IntrinsicKind:
return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_sin);
case k_cos_IntrinsicKind:
return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_cos);
case k_tan_IntrinsicKind:
return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_tan);
case k_sinh_IntrinsicKind:
return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_sinh);
case k_cosh_IntrinsicKind:
return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_cosh);
case k_tanh_IntrinsicKind:
return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_tanh);
case k_asin_IntrinsicKind:
return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_asin);
case k_acos_IntrinsicKind:
return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_acos);
case k_atan_IntrinsicKind:
if (argArray.size() == 1) {
return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_atan);
} else {
return evaluate_pairwise_intrinsic(context, arguments, returnType,
Intrinsics::evaluate_atan2);
}
case k_asinh_IntrinsicKind:
return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_asinh);
case k_acosh_IntrinsicKind:
return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_acosh);
case k_atanh_IntrinsicKind:
return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_atanh);
// 8.2 : Exponential Functions
case k_pow_IntrinsicKind:
return evaluate_pairwise_intrinsic(context, arguments, returnType,
Intrinsics::evaluate_pow);
case k_exp_IntrinsicKind:
return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_exp);
case k_log_IntrinsicKind:
return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_log);
case k_exp2_IntrinsicKind:
return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_exp2);
case k_log2_IntrinsicKind:
return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_log2);
case k_sqrt_IntrinsicKind:
return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_sqrt);
case k_inversesqrt_IntrinsicKind:
return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_inversesqrt);
// 8.3 : Common Functions
case k_abs_IntrinsicKind:
return evaluate_intrinsic_numeric(context, arguments, returnType,
Intrinsics::evaluate_abs);
case k_sign_IntrinsicKind:
return Intrinsics::evaluate_sign(context, arguments);
case k_floor_IntrinsicKind:
return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_floor);
case k_ceil_IntrinsicKind:
return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_ceil);
case k_fract_IntrinsicKind:
return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_fract);
case k_mod_IntrinsicKind:
return evaluate_pairwise_intrinsic(context, arguments, returnType,
Intrinsics::evaluate_mod);
case k_min_IntrinsicKind:
return evaluate_pairwise_intrinsic(context, arguments, returnType,
Intrinsics::evaluate_min);
case k_max_IntrinsicKind:
return evaluate_pairwise_intrinsic(context, arguments, returnType,
Intrinsics::evaluate_max);
case k_clamp_IntrinsicKind:
return evaluate_3_way_intrinsic(context, arguments, returnType,
Intrinsics::evaluate_clamp);
case k_fma_IntrinsicKind:
return evaluate_3_way_intrinsic(context, arguments, returnType,
Intrinsics::evaluate_fma);
case k_saturate_IntrinsicKind:
return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_saturate);
case k_mix_IntrinsicKind:
if (arguments[2]->type().componentType().isBoolean()) {
const SkSL::Type& numericType = arguments[0]->type().componentType();
if (numericType.isFloat()) {
type_check_expression<float>(*arguments[0]);
type_check_expression<float>(*arguments[1]);
} else if (numericType.isInteger()) {
type_check_expression<SKSL_INT>(*arguments[0]);
type_check_expression<SKSL_INT>(*arguments[1]);
} else if (numericType.isBoolean()) {
type_check_expression<bool>(*arguments[0]);
type_check_expression<bool>(*arguments[1]);
} else {
SkDEBUGFAILF("unsupported type %s", numericType.description().c_str());
return nullptr;
}
return evaluate_n_way_intrinsic(context, arguments[0], arguments[1], arguments[2],
returnType, Intrinsics::evaluate_mix);
} else {
return evaluate_3_way_intrinsic(context, arguments, returnType,
Intrinsics::evaluate_mix);
}
case k_step_IntrinsicKind:
return evaluate_pairwise_intrinsic(context, arguments, returnType,
Intrinsics::evaluate_step);
case k_smoothstep_IntrinsicKind:
return evaluate_3_way_intrinsic(context, arguments, returnType,
Intrinsics::evaluate_smoothstep);
case k_trunc_IntrinsicKind:
return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_trunc);
case k_round_IntrinsicKind: // GLSL `round` documents its rounding mode as unspecified
case k_roundEven_IntrinsicKind: // and is allowed to behave identically to `roundEven`.
return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_round);
case k_floatBitsToInt_IntrinsicKind:
return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_floatBitsToInt);
case k_floatBitsToUint_IntrinsicKind:
return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_floatBitsToUint);
case k_intBitsToFloat_IntrinsicKind:
return evaluate_intrinsic<SKSL_INT>(context, arguments, returnType,
Intrinsics::evaluate_intBitsToFloat);
case k_uintBitsToFloat_IntrinsicKind:
return evaluate_intrinsic<SKSL_INT>(context, arguments, returnType,
Intrinsics::evaluate_uintBitsToFloat);
// 8.4 : Floating-Point Pack and Unpack Functions
case k_packUnorm2x16_IntrinsicKind: {
auto Pack = [&](int n) -> unsigned int {
float x = Get(0, n);
return (int)std::round(Intrinsics::evaluate_clamp(x, 0.0, 1.0) * 65535.0);
};
const double packed = ((Pack(0) << 0) & 0x0000FFFF) |
((Pack(1) << 16) & 0xFFFF0000);
return ConstructorCompound::MakeFromConstants(context, arguments[0]->fPosition,
*context.fTypes.fUInt, &packed);
}
case k_packSnorm2x16_IntrinsicKind: {
auto Pack = [&](int n) -> unsigned int {
float x = Get(0, n);
return (int)std::round(Intrinsics::evaluate_clamp(x, -1.0, 1.0) * 32767.0);
};
const double packed = ((Pack(0) << 0) & 0x0000FFFF) |
((Pack(1) << 16) & 0xFFFF0000);
return ConstructorCompound::MakeFromConstants(context, arguments[0]->fPosition,
*context.fTypes.fUInt, &packed);
}
case k_packHalf2x16_IntrinsicKind: {
auto Pack = [&](int n) -> unsigned int {
return SkFloatToHalf(Get(0, n));
};
const double packed = ((Pack(0) << 0) & 0x0000FFFF) |
((Pack(1) << 16) & 0xFFFF0000);
return ConstructorCompound::MakeFromConstants(context, arguments[0]->fPosition,
*context.fTypes.fUInt, &packed);
}
case k_unpackUnorm2x16_IntrinsicKind: {
SKSL_INT x = *arguments[0]->getConstantValue(0);
uint16_t a = ((x >> 0) & 0x0000FFFF);
uint16_t b = ((x >> 16) & 0x0000FFFF);
const double unpacked[2] = {double(a) / 65535.0,
double(b) / 65535.0};
return ConstructorCompound::MakeFromConstants(context, arguments[0]->fPosition,
*context.fTypes.fFloat2, unpacked);
}
case k_unpackSnorm2x16_IntrinsicKind: {
SKSL_INT x = *arguments[0]->getConstantValue(0);
int16_t a = ((x >> 0) & 0x0000FFFF);
int16_t b = ((x >> 16) & 0x0000FFFF);
const double unpacked[2] = {Intrinsics::evaluate_clamp(double(a) / 32767.0, -1.0, 1.0),
Intrinsics::evaluate_clamp(double(b) / 32767.0, -1.0, 1.0)};
return ConstructorCompound::MakeFromConstants(context, arguments[0]->fPosition,
*context.fTypes.fFloat2, unpacked);
}
case k_unpackHalf2x16_IntrinsicKind: {
SKSL_INT x = *arguments[0]->getConstantValue(0);
uint16_t a = ((x >> 0) & 0x0000FFFF);
uint16_t b = ((x >> 16) & 0x0000FFFF);
const double unpacked[2] = {SkHalfToFloat(a),
SkHalfToFloat(b)};
return ConstructorCompound::MakeFromConstants(context, arguments[0]->fPosition,
*context.fTypes.fFloat2, unpacked);
}
// 8.5 : Geometric Functions
case k_length_IntrinsicKind:
return Intrinsics::evaluate_length(arguments);
case k_distance_IntrinsicKind:
return Intrinsics::evaluate_distance(arguments);
case k_dot_IntrinsicKind:
return Intrinsics::evaluate_dot(arguments);
case k_cross_IntrinsicKind: {
auto X = [&](int n) -> float { return Get(0, n); };
auto Y = [&](int n) -> float { return Get(1, n); };
SkASSERT(arguments[0]->type().columns() == 3); // the vec2 form is not a real intrinsic
double vec[3] = {X(1) * Y(2) - Y(1) * X(2),
X(2) * Y(0) - Y(2) * X(0),
X(0) * Y(1) - Y(0) * X(1)};
return ConstructorCompound::MakeFromConstants(context, arguments[0]->fPosition,
returnType, vec);
}
case k_normalize_IntrinsicKind:
return Intrinsics::evaluate_normalize(context, arguments);
case k_faceforward_IntrinsicKind:
return Intrinsics::evaluate_faceforward(context, arguments);
case k_reflect_IntrinsicKind:
return Intrinsics::evaluate_reflect(context, arguments);
case k_refract_IntrinsicKind:
return Intrinsics::evaluate_refract(context, arguments);
// 8.6 : Matrix Functions
case k_matrixCompMult_IntrinsicKind:
return evaluate_pairwise_intrinsic(context, arguments, returnType,
Intrinsics::evaluate_matrixCompMult);
case k_transpose_IntrinsicKind: {
double mat[16];
int index = 0;
for (int c = 0; c < returnType.columns(); ++c) {
for (int r = 0; r < returnType.rows(); ++r) {
mat[index++] = Get(0, (returnType.columns() * r) + c);
}
}
return ConstructorCompound::MakeFromConstants(context, arguments[0]->fPosition,
returnType, mat);
}
case k_outerProduct_IntrinsicKind: {
double mat[16];
int index = 0;
for (int c = 0; c < returnType.columns(); ++c) {
for (int r = 0; r < returnType.rows(); ++r) {
mat[index++] = Get(0, r) * Get(1, c);
}
}
return ConstructorCompound::MakeFromConstants(context, arguments[0]->fPosition,
returnType, mat);
}
case k_determinant_IntrinsicKind: {
float mat[16];
extract_matrix(arguments[0], mat);
float determinant;
switch (arguments[0]->type().slotCount()) {
case 4:
determinant = SkInvert2x2Matrix(mat, /*outMatrix=*/nullptr);
break;
case 9:
determinant = SkInvert3x3Matrix(mat, /*outMatrix=*/nullptr);
break;
case 16:
determinant = SkInvert4x4Matrix(mat, /*outMatrix=*/nullptr);
break;
default:
SkDEBUGFAILF("unsupported type %s", arguments[0]->type().description().c_str());
return nullptr;
}
return Literal::MakeFloat(arguments[0]->fPosition, determinant, &returnType);
}
case k_inverse_IntrinsicKind: {
float mat[16] = {};
extract_matrix(arguments[0], mat);
switch (arguments[0]->type().slotCount()) {
case 4:
if (SkInvert2x2Matrix(mat, mat) == 0.0f) {
return nullptr;
}
break;
case 9:
if (SkInvert3x3Matrix(mat, mat) == 0.0f) {
return nullptr;
}
break;
case 16:
if (SkInvert4x4Matrix(mat, mat) == 0.0f) {
return nullptr;
}
break;
default:
SkDEBUGFAILF("unsupported type %s", arguments[0]->type().description().c_str());
return nullptr;
}
double dmat[16];
std::copy(mat, mat + std::size(mat), dmat);
return ConstructorCompound::MakeFromConstants(context, arguments[0]->fPosition,
returnType, dmat);
}
// 8.7 : Vector Relational Functions
case k_lessThan_IntrinsicKind:
return optimize_comparison(context, arguments, Intrinsics::compare_lessThan);
case k_lessThanEqual_IntrinsicKind:
return optimize_comparison(context, arguments, Intrinsics::compare_lessThanEqual);
case k_greaterThan_IntrinsicKind:
return optimize_comparison(context, arguments, Intrinsics::compare_greaterThan);
case k_greaterThanEqual_IntrinsicKind:
return optimize_comparison(context, arguments, Intrinsics::compare_greaterThanEqual);
case k_equal_IntrinsicKind:
return optimize_comparison(context, arguments, Intrinsics::compare_equal);
case k_notEqual_IntrinsicKind:
return optimize_comparison(context, arguments, Intrinsics::compare_notEqual);
case k_any_IntrinsicKind:
return coalesce_vector<bool>(arguments, /*startingState=*/false, returnType,
Intrinsics::coalesce_any,
/*finalize=*/nullptr);
case k_all_IntrinsicKind:
return coalesce_vector<bool>(arguments, /*startingState=*/true, returnType,
Intrinsics::coalesce_all,
/*finalize=*/nullptr);
case k_not_IntrinsicKind:
return evaluate_intrinsic<bool>(context, arguments, returnType,
Intrinsics::evaluate_not);
default:
return nullptr;
}
}