using CoalesceFn = double (*)(double, double, double); using FinalizeFn = double (*)(double);
static std::unique_ptr<Expression> coalesce_n_way_vector(const Expression* arg0, const Expression* arg1, double startingState, const Type& returnType,
CoalesceFn coalesce,
FinalizeFn finalize) { // Takes up to two vector or scalar arguments and coalesces them in sequence: // scalar = startingState; // scalar = coalesce(scalar, arg0.x, arg1.x); // scalar = coalesce(scalar, arg0.y, arg1.y); // scalar = coalesce(scalar, arg0.z, arg1.z); // scalar = coalesce(scalar, arg0.w, arg1.w); // scalar = finalize(scalar); // // If an argument is null, zero is passed to the coalesce function. If the arguments are a mix // of scalars and vectors, the scalars are interpreted as a vector containing the same value for // every component.
if (value >= minimumValue && value <= maximumValue) { // This result will fit inside the return type.
} else { // The value is outside the float range or is NaN (all if-checks fail); do not optimize. return nullptr;
}
}
using EvaluateFn = double (*)(double, double, double);
static std::unique_ptr<Expression> evaluate_n_way_intrinsic(const Context& context, const Expression* arg0, const Expression* arg1, const Expression* arg2, const Type& returnType,
EvaluateFn eval) { // Takes up to three arguments and evaluates all of them, left-to-right, in tandem. // Equivalent to constructing a new compound value containing the results from: // eval(arg0.x, arg1.x, arg2.x), // eval(arg0.y, arg1.y, arg2.y), // eval(arg0.z, arg1.z, arg2.z), // eval(arg0.w, arg1.w, arg2.w) // // If an argument is null, zero is passed to the evaluation function. If the arguments are a mix // of scalars and compounds, scalars are interpreted as a compound containing the same value for // every component.
if (array[index] >= minimumValue && array[index] <= maximumValue) { // This result will fit inside the return type.
} else { // The value is outside the float range or is NaN (all if-checks fail); do not optimize. return nullptr;
}
}
template <typename T1, typename T2> staticdouble pun_value(double val) { // Interpret `val` as a value of type T1.
static_assert(sizeof(T1) == sizeof(T2));
T1 inputValue = (T1)val; // Reinterpret those bits as a value of type T2.
T2 outputValue;
memcpy(&outputValue, &inputValue, sizeof(T2)); // Return the value-of-type-T2 as a double. (Non-finite values will prohibit optimization.) return (double)outputValue;
}
// Helper functions for optimizing all of our intrinsics. namespace Intrinsics { namespace {
double coalesce_length(double a, double b, double) { return a + (b * b); } double finalize_length(double a) { return std::sqrt(a); }
double coalesce_distance(double a, double b, double c) { b -= c; return a + (b * b); } double finalize_distance(double a) { return std::sqrt(a); }
double coalesce_dot(double a, double b, double c) { return a + (b * c); } double coalesce_any(double a, double b, double) { return a || b; } double coalesce_all(double a, double b, double) { return a && b; }
bool compare_lessThan(double a, double b) { return a < b; } bool compare_lessThanEqual(double a, double b) { return a <= b; } bool compare_greaterThan(double a, double b) { return a > b; } bool compare_greaterThanEqual(double a, double b) { return a >= b; } bool compare_equal(double a, double b) { return a == b; } bool compare_notEqual(double a, double b) { return a != b; }
double evaluate_radians(double a, double, double) { return a * 0.0174532925; } double evaluate_degrees(double a, double, double) { return a * 57.2957795; } double evaluate_sin(double a, double, double) { return std::sin(a); } double evaluate_cos(double a, double, double) { return std::cos(a); } double evaluate_tan(double a, double, double) { return std::tan(a); } double evaluate_asin(double a, double, double) { return std::asin(a); } double evaluate_acos(double a, double, double) { return std::acos(a); } double evaluate_atan(double a, double, double) { return std::atan(a); } double evaluate_atan2(double a, double b, double) { return std::atan2(a, b); } double evaluate_asinh(double a, double, double) { return std::asinh(a); } double evaluate_acosh(double a, double, double) { return std::acosh(a); } double evaluate_atanh(double a, double, double) { return std::atanh(a); }
double evaluate_pow(double a, double b, double) { return std::pow(a, b); } double evaluate_exp(double a, double, double) { return std::exp(a); } double evaluate_log(double a, double, double) { return std::log(a); } double evaluate_exp2(double a, double, double) { return std::exp2(a); } double evaluate_log2(double a, double, double) { return std::log2(a); } double evaluate_sqrt(double a, double, double) { return std::sqrt(a); } double evaluate_inversesqrt(double a, double, double) { return sk_ieee_double_divide(1.0, std::sqrt(a));
}
double evaluate_add(double a, double b, double) { return a + b; } double evaluate_sub(double a, double b, double) { return a - b; } double evaluate_mul(double a, double b, double) { return a * b; } double evaluate_div(double a, double b, double) { return sk_ieee_double_divide(a, b); } double evaluate_abs(double a, double, double) { return std::abs(a); } double evaluate_sign(double a, double, double) { return (a > 0) - (a < 0); } double evaluate_opposite_sign(double a,double, double) { return (a < 0) - (a > 0); } double evaluate_floor(double a, double, double) { return std::floor(a); } double evaluate_ceil(double a, double, double) { return std::ceil(a); } double evaluate_fract(double a, double, double) { return a - std::floor(a); } double evaluate_min(double a, double b, double) { return (a < b) ? a : b; } double evaluate_max(double a, double b, double) { return (a > b) ? a : b; } double evaluate_clamp(double x, double l, double h) { return (x < l) ? l : (x > h) ? h : x; } double evaluate_fma(double a, double b, double c) { return a * b + c; } double evaluate_saturate(double a, double, double) { return (a < 0) ? 0 : (a > 1) ? 1 : a; } double evaluate_mix(double x, double y, double a) { return x * (1 - a) + y * a; } double evaluate_step(double e, double x, double) { return (x < e) ? 0 : 1; } double evaluate_mod(double a, double b, double) { return a - b * std::floor(sk_ieee_double_divide(a, b));
} double evaluate_smoothstep(double edge0, double edge1, double x) { double t = sk_ieee_double_divide(x - edge0, edge1 - edge0);
t = (t < 0) ? 0 : (t > 1) ? 1 : t; return t * t * (3.0 - 2.0 * t);
}
double evaluate_matrixCompMult(double x, double y, double) { return x * y; }
double evaluate_not(double a, double, double) { return !a; } double evaluate_sinh(double a, double, double) { return std::sinh(a); } double evaluate_cosh(double a, double, double) { return std::cosh(a); } double evaluate_tanh(double a, double, double) { return std::tanh(a); } double evaluate_trunc(double a, double, double) { return std::trunc(a); } double evaluate_round(double a, double, double) { // The semantics of std::remainder guarantee a rounded-to-even result here, regardless of the // current float-rounding mode. return a - std::remainder(a, 1.0);
} double evaluate_floatBitsToInt(double a, double, double) { return pun_value<float, int32_t> (a); } double evaluate_floatBitsToUint(double a, double, double) { return pun_value<float, uint32_t>(a); } double evaluate_intBitsToFloat(double a, double, double) { return pun_value<int32_t, float>(a); } double evaluate_uintBitsToFloat(double a, double, double) { return pun_value<uint32_t, float>(a); }
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
std::string FunctionCall::description(OperatorPrecedence) const {
std::string result = std::string(this->function().name()) + "("; auto separator = SkSL::String::Separator(); for (const std::unique_ptr<Expression>& arg : this->arguments()) {
result += separator();
result += arg->description(OperatorPrecedence::kSequence);
}
result += ")"; return result;
}
staticbool argument_and_parameter_flags_match(const Expression& argument, const Variable& parameter) { // If the function parameter has a pixel format, the argument being passed in must have a // matching pixel format.
LayoutFlags paramPixelFormat = parameter.layout().fFlags & LayoutFlag::kAllPixelFormats; if (paramPixelFormat != LayoutFlag::kNone) { // The only SkSL type that supports pixel-format qualifiers is a storage texture. if (parameter.type().isStorageTexture()) { // Storage textures are opaquely typed, so there's no way to specify one other than by // directly accessing a variable. if (!argument.is<VariableReference>()) { returnfalse;
}
// The variable's pixel-format flags must match. (Only one pixel-format bit can be set.) const Variable& var = *argument.as<VariableReference>().variable(); if ((var.layout().fFlags & LayoutFlag::kAllPixelFormats) != paramPixelFormat) { returnfalse;
}
}
}
// The only other supported parameter flags are `const` and `in/out`, which do not allow // multiple overloads. returntrue;
}
/** * Used to determine the best overload for a function call by calculating the cost of coercing the * arguments of the function to the required types. Cost has no particular meaning other than "lower * costs are preferred". Returns CoercionCost::Impossible() if the call is not valid. This is never * called for functions with only one definition.
*/ static CoercionCost call_cost(const Context& context, const FunctionDeclaration& function, const ExpressionArray& arguments) { // Strict-ES2 programs can never call an `$es3` function. if (context.fConfig->strictES2Mode() && function.modifierFlags().isES3()) { return CoercionCost::Impossible();
} // Functions with the wrong number of parameters are never a match. if (function.parameters().size() != SkToSizeT(arguments.size())) { return CoercionCost::Impossible();
} // If the arguments cannot be coerced to the parameter types, the function is never a match.
FunctionDeclaration::ParamTypes types; const Type* ignored; if (!function.determineFinalTypes(arguments, &types, &ignored)) { return CoercionCost::Impossible();
} // If the arguments do not match the parameter types due to mismatched modifiers, the function // is never a match. for (int i = 0; i < arguments.size(); i++) { const Expression& arg = *arguments[i]; const Variable& param = *function.parameters()[i]; if (!argument_and_parameter_flags_match(arg, param)) { return CoercionCost::Impossible();
}
} // Return the sum of coercion costs of each argument.
CoercionCost total = CoercionCost::Free(); for (int i = 0; i < arguments.size(); i++) {
total = total + arguments[i]->coercionCost(*types[i]);
} return total;
}
const FunctionDeclaration* FunctionCall::FindBestFunctionForCall( const Context& context, const FunctionDeclaration* overloadChain, const ExpressionArray& arguments) { if (!overloadChain->nextOverload()) { return overloadChain;
}
CoercionCost bestCost = CoercionCost::Impossible(); const FunctionDeclaration* best = nullptr; for (const FunctionDeclaration* f = overloadChain; f != nullptr; f = f->nextOverload()) {
CoercionCost cost = call_cost(context, *f, arguments); if (cost <= bestCost) {
bestCost = cost;
best = f;
}
} return bestCost.fImpossible ? nullptr : best;
}
static std::string build_argument_type_list(SkSpan<const std::unique_ptr<Expression>> arguments) {
std::string result = "("; auto separator = SkSL::String::Separator(); for (const std::unique_ptr<Expression>& arg : arguments) {
result += separator();
result += arg->type().displayName();
} return result + ")";
}
std::unique_ptr<Expression> FunctionCall::Convert(const Context& context,
Position pos,
std::unique_ptr<Expression> functionValue,
ExpressionArray arguments) { switch (functionValue->kind()) { case Expression::Kind::kTypeReference: return Constructor::Convert(context,
pos,
functionValue->as<TypeReference>().value(),
std::move(arguments)); case Expression::Kind::kFunctionReference: { const FunctionReference& ref = functionValue->as<FunctionReference>(); const FunctionDeclaration* best = FindBestFunctionForCall(context, ref.overloadChain(),
arguments); if (best) { return FunctionCall::Convert(context, pos, *best, std::move(arguments));
}
std::string msg = "no match for " + std::string(ref.overloadChain()->name()) +
build_argument_type_list(arguments);
context.fErrors->error(pos, msg); return nullptr;
} case Expression::Kind::kMethodReference: {
MethodReference& ref = functionValue->as<MethodReference>();
arguments.push_back(std::move(ref.self()));
const FunctionDeclaration* best = FindBestFunctionForCall(context, ref.overloadChain(),
arguments); if (best) { return FunctionCall::Convert(context, pos, *best, std::move(arguments));
}
std::string msg = "no match for " + arguments.back()->type().displayName() + "::" + std::string(ref.overloadChain()->name().substr(1)) +
build_argument_type_list(SkSpan(arguments).first(arguments.size() - 1));
context.fErrors->error(pos, msg); return nullptr;
} case Expression::Kind::kPoison:
functionValue->fPosition = pos; return functionValue; default:
context.fErrors->error(pos, "not a function"); return nullptr;
}
}
std::unique_ptr<Expression> FunctionCall::Convert(const Context& context,
Position pos, const FunctionDeclaration& function,
ExpressionArray arguments) { // Reject ES3 function calls in strict ES2 mode. if (context.fConfig->strictES2Mode() && function.modifierFlags().isES3()) {
context.fErrors->error(pos, "call to '" + function.description() + "' is not supported"); return nullptr;
}
// Reject function calls with the wrong number of arguments. if (function.parameters().size() != SkToSizeT(arguments.size())) {
std::string msg = "call to '" + std::string(function.name()) + "' expected " +
std::to_string(function.parameters().size()) + " argument"; if (function.parameters().size() != 1) {
msg += "s";
}
msg += ", but found " + std::to_string(arguments.size());
context.fErrors->error(pos, msg); return nullptr;
}
// If the arguments do not match the parameter types due to mismatched modifiers, reject the // function call. for (int i = 0; i < arguments.size(); i++) { const Expression& arg = *arguments[i]; const Variable& param = *function.parameters()[i]; if (!argument_and_parameter_flags_match(arg, param)) {
context.fErrors->error(arg.position(), "expected argument of type '" +
param.layout().paddedDescription() +
param.modifierFlags().paddedDescription() +
param.type().description() + "'"); return nullptr;
}
}
// Resolve generic types.
FunctionDeclaration::ParamTypes types; const Type* returnType; if (!function.determineFinalTypes(arguments, &types, &returnType)) {
std::string msg = "no match for " + std::string(function.name()) +
build_argument_type_list(arguments);
context.fErrors->error(pos, msg); return nullptr;
}
for (int i = 0; i < arguments.size(); i++) { // Coerce each argument to the proper type.
arguments[i] = types[i]->coerceExpression(std::move(arguments[i]), context); if (!arguments[i]) { return nullptr;
} // Update the refKind on out-parameters, and ensure that they are actually assignable.
ModifierFlags paramFlags = function.parameters()[i]->modifierFlags(); if (paramFlags & ModifierFlag::kOut) { const VariableRefKind refKind = (paramFlags & ModifierFlag::kIn)
? VariableReference::RefKind::kReadWrite
: VariableReference::RefKind::kPointer; if (!Analysis::UpdateVariableRefKind(arguments[i].get(), refKind, context.fErrors)) { return nullptr;
}
}
}
if (function.isMain()) {
context.fErrors->error(pos, "call to 'main' is not allowed"); return nullptr;
}
if (function.intrinsicKind() == k_eval_IntrinsicKind) { // This is a method call on an effect child. Translate it into a ChildCall, which simplifies // handling in the generators and analysis code. const Variable& child = *arguments.back()->as<VariableReference>().variable();
arguments.pop_back(); return ChildCall::Make(context, pos, returnType, child, std::move(arguments));
}
// We might be able to optimize built-in intrinsics. if (function.isIntrinsic() && has_compile_time_constant_arguments(arguments)) { // The function is an intrinsic and all inputs are compile-time constants. Optimize it. if (std::unique_ptr<Expression> expr = optimize_intrinsic_call(context,
pos,
function.intrinsicKind(),
arguments,
*returnType)) {
expr->fPosition = pos; return expr;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.