Reduce template usage in vector-coalesce functionality.

The main loop of vector-coalesce is now a single function that operates
exclusively on double values, rather than being templated on its input
types.

A similar approach should work equally well on evaluate-n-way-intrinsic.

Change-Id: Ic2848b52365ed5e38f6432f881b2ac593668a148
Bug: skia:12233
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/437917
Auto-Submit: John Stiles <johnstiles@google.com>
Commit-Queue: John Stiles <johnstiles@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
This commit is contained in:
John Stiles 2021-08-10 11:51:24 -04:00 committed by SkCQ
parent 464809bc69
commit b0074aa7c2

View File

@ -30,12 +30,55 @@ static bool has_compile_time_constant_arguments(const ExpressionArray& arguments
return true; return true;
} }
static double as_double(const Expression* expr) {
if (expr) {
if (expr->is<IntLiteral>()) {
return (double)expr->as<IntLiteral>().value();
}
if (expr->is<FloatLiteral>()) {
return (double)expr->as<FloatLiteral>().value();
}
if (expr->is<BoolLiteral>()) {
return (double)expr->as<BoolLiteral>().value();
}
SkDEBUGFAILF("unexpected expression kind %d", (int)expr->kind());
}
return 0.0;
}
template <typename T> template <typename T>
static void type_check_expression(const Expression& expr);
template <>
void type_check_expression<float>(const Expression& expr) {
SkASSERT(expr.type().componentType().isFloat());
}
template <>
[[maybe_unused]] void type_check_expression<SKSL_INT>(const Expression& expr) {
SkASSERT(expr.type().componentType().isInteger());
}
template <>
void type_check_expression<bool>(const Expression& expr) {
SkASSERT(expr.type().componentType().isBoolean());
}
template <typename T>
static std::unique_ptr<Expression> make_literal(int offset, double value, const Type* type) {
return Literal<T>::Make(offset, (T)value, type);
}
using CoalesceFn = double (*)(double, double, double);
using FinalizeFn = double (*)(double);
using MakeLiteralFn = std::unique_ptr<Expression> (*)(int, double, const Type*);
static std::unique_ptr<Expression> coalesce_n_way_vector(const Expression* arg0, static std::unique_ptr<Expression> coalesce_n_way_vector(const Expression* arg0,
const Expression* arg1, const Expression* arg1,
T startingState, double startingState,
const std::function<T(T, T, T)>& coalesce, CoalesceFn coalesce,
const std::function<T(T)>& finalize) { FinalizeFn finalize,
MakeLiteralFn makeLiteral) {
// Takes up to two vector or scalar arguments and coalesces them in sequence: // Takes up to two vector or scalar arguments and coalesces them in sequence:
// scalar = startingState; // scalar = startingState;
// scalar = coalesce(scalar, arg0.x, arg1.x); // scalar = coalesce(scalar, arg0.x, arg1.x);
@ -48,6 +91,8 @@ static std::unique_ptr<Expression> coalesce_n_way_vector(const Expression* arg0,
// of scalars and vectors, the scalars is interpreted as a vector containing the same value for // of scalars and vectors, the scalars is interpreted as a vector containing the same value for
// every component. // every component.
int offset = arg0->fOffset;
arg0 = ConstantFolder::GetConstantValueForVariable(*arg0); arg0 = ConstantFolder::GetConstantValueForVariable(*arg0);
SkASSERT(arg0); SkASSERT(arg0);
@ -62,7 +107,7 @@ static std::unique_ptr<Expression> coalesce_n_way_vector(const Expression* arg0,
SkASSERT(arg1->type().componentType() == vecType.componentType()); SkASSERT(arg1->type().componentType() == vecType.componentType());
} }
T value = startingState; double value = startingState;
int arg0Index = 0; int arg0Index = 0;
int arg1Index = 0; int arg1Index = 0;
for (int index = 0; index < vecType.columns(); ++index) { for (int index = 0; index < vecType.columns(); ++index) {
@ -77,15 +122,11 @@ static std::unique_ptr<Expression> coalesce_n_way_vector(const Expression* arg0,
SkASSERT(arg1Subexpr); SkASSERT(arg1Subexpr);
} }
value = coalesce(value, value = coalesce(value, as_double(arg0Subexpr), as_double(arg1Subexpr));
arg0Subexpr->as<Literal<T>>().value(),
arg1Subexpr ? arg1Subexpr->as<Literal<T>>().value() : T{});
if constexpr (std::is_floating_point<T>::value) { // If coalescing the intrinsic yields a non-finite value, do not optimize.
// If coalescing the intrinsic yields a non-finite value, do not optimize. if (!std::isfinite(value)) {
if (!std::isfinite(value)) { return nullptr;
return nullptr;
}
} }
} }
@ -93,43 +134,32 @@ static std::unique_ptr<Expression> coalesce_n_way_vector(const Expression* arg0,
value = finalize(value); value = finalize(value);
} }
return Literal<T>::Make(arg0->fOffset, value, &vecType.componentType()); return makeLiteral(offset, value, &arg0->type().componentType());
} }
template <typename T> template <typename T>
static std::unique_ptr<Expression> coalesce_vector(const ExpressionArray& arguments, static std::unique_ptr<Expression> coalesce_vector(const ExpressionArray& arguments,
T startingState, double startingState,
const std::function<T(T, T)>& coalesce, CoalesceFn coalesce,
const std::function<T(T)>& finalize) { FinalizeFn finalize) {
SkASSERT(arguments.size() == 1); SkASSERT(arguments.size() == 1);
if constexpr (std::is_same<T, bool>::value) { type_check_expression<T>(*arguments[0]);
SkASSERT(arguments.front()->type().componentType().isBoolean());
}
if constexpr (std::is_same<T, float>::value) {
SkASSERT(arguments.front()->type().componentType().isFloat());
}
return coalesce_n_way_vector<T>(arguments.front().get(), /*arg1=*/nullptr, startingState, return coalesce_n_way_vector(arguments[0].get(), /*arg1=*/nullptr,
[&coalesce](T a, T b, T) { return coalesce(a, b); }, (double)startingState, coalesce, finalize, make_literal<T>);
finalize);
} }
template <typename T> template <typename T>
static std::unique_ptr<Expression> coalesce_pairwise_vectors( static std::unique_ptr<Expression> coalesce_pairwise_vectors(const ExpressionArray& arguments,
const ExpressionArray& arguments, double startingState,
T startingState, CoalesceFn coalesce,
const std::function<T(T, T, T)>& coalesce, FinalizeFn finalize) {
const std::function<T(T)>& finalize) {
SkASSERT(arguments.size() == 2); SkASSERT(arguments.size() == 2);
const Type& type = arguments.front()->type().componentType(); type_check_expression<T>(*arguments[0]);
type_check_expression<T>(*arguments[1]);
if (type.isFloat()) { return coalesce_n_way_vector(arguments[0].get(), arguments[1].get(),
return coalesce_n_way_vector<float>(arguments[0].get(), arguments[1].get(), startingState, (double)startingState, coalesce, finalize, make_literal<T>);
coalesce, finalize);
}
SkDEBUGFAILF("unsupported type %s", type.description().c_str());
return nullptr;
} }
template <typename LITERAL, typename FN> template <typename LITERAL, typename FN>
@ -339,6 +369,21 @@ static std::unique_ptr<Expression> evaluate_3_way_intrinsic(const Context& conte
return nullptr; return nullptr;
} }
// Helper functions for optimizing all of our intrinsics.
namespace Intrinsics {
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; }
}
static std::unique_ptr<Expression> optimize_intrinsic_call(const Context& context, static std::unique_ptr<Expression> optimize_intrinsic_call(const Context& context,
IntrinsicKind intrinsic, IntrinsicKind intrinsic,
const ExpressionArray& arguments) { const ExpressionArray& arguments) {
@ -466,18 +511,16 @@ static std::unique_ptr<Expression> optimize_intrinsic_call(const Context& contex
// 8.4 : Geometric Functions // 8.4 : Geometric Functions
case k_length_IntrinsicKind: case k_length_IntrinsicKind:
return coalesce_vector<float>(arguments, /*startingState=*/0, return coalesce_vector<float>(arguments, /*startingState=*/0,
[](float a, float b) { return a + (b * b); }, Intrinsics::coalesce_length,
[](float a) { return std::sqrt(a); }); Intrinsics::finalize_length);
case k_distance_IntrinsicKind: case k_distance_IntrinsicKind:
return coalesce_pairwise_vectors<float>( return coalesce_pairwise_vectors<float>(arguments, /*startingState=*/0,
arguments, /*startingState=*/0, Intrinsics::coalesce_distance,
[](float a, float b, float c) { b -= c; return a + (b * b); }, Intrinsics::finalize_distance);
[](float a) { return std::sqrt(a); });
case k_dot_IntrinsicKind: case k_dot_IntrinsicKind:
return coalesce_pairwise_vectors<float>( return coalesce_pairwise_vectors<float>(arguments, /*startingState=*/0,
arguments, /*startingState=*/0, Intrinsics::coalesce_dot,
[](float a, float b, float c) { return a + (b * c); }, /*finalize=*/nullptr);
/*finalize=*/nullptr);
case k_cross_IntrinsicKind: { case k_cross_IntrinsicKind: {
auto Value = [&](int a, int n) -> float { auto Value = [&](int a, int n) -> float {
return arguments[a]->getConstantSubexpression(n)->as<FloatLiteral>().value(); return arguments[a]->getConstantSubexpression(n)->as<FloatLiteral>().value();
@ -596,11 +639,11 @@ static std::unique_ptr<Expression> optimize_intrinsic_call(const Context& contex
case k_any_IntrinsicKind: case k_any_IntrinsicKind:
return coalesce_vector<bool>(arguments, /*startingState=*/false, return coalesce_vector<bool>(arguments, /*startingState=*/false,
[](bool a, bool b) { return a || b; }, Intrinsics::coalesce_any,
/*finalize=*/nullptr); /*finalize=*/nullptr);
case k_all_IntrinsicKind: case k_all_IntrinsicKind:
return coalesce_vector<bool>(arguments, /*startingState=*/true, return coalesce_vector<bool>(arguments, /*startingState=*/true,
[](bool a, bool b) { return a && b; }, Intrinsics::coalesce_all,
/*finalize=*/nullptr); /*finalize=*/nullptr);
case k_not_IntrinsicKind: case k_not_IntrinsicKind:
return evaluate_intrinsic<bool>(context, arguments, [](bool a) { return !a; }); return evaluate_intrinsic<bool>(context, arguments, [](bool a) { return !a; });