Fix underflow/overflow issues in skvm::approx_pow2.
The skvm implementation of pow2 uses a clever bit-twiddling trick to generate a floating-point value that can be cast to integer, then bit- punned to float, to generate its result. However, the bit trick fails for large inputs, and the bit-punning step generates a nonsense result. This is now fixed by using a well-positioned clamp. Change-Id: I55143a98324f5f518d0875149a0b6ce6d734ded0 Bug: skia:12847 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/497283 Auto-Submit: John Stiles <johnstiles@google.com> Reviewed-by: Brian Osman <brianosman@google.com> Commit-Queue: Brian Osman <brianosman@google.com>
This commit is contained in:
parent
39dc2a02fd
commit
f6bb619a16
@ -847,12 +847,16 @@ namespace skvm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
F32 Builder::approx_pow2(F32 x) {
|
F32 Builder::approx_pow2(F32 x) {
|
||||||
|
constexpr float kInfinityBits = 0x7f800000;
|
||||||
|
|
||||||
F32 f = fract(x);
|
F32 f = fract(x);
|
||||||
F32 approx = add(x, 121.274057500f);
|
F32 approx = add(x, 121.274057500f);
|
||||||
approx = sub(approx, mul( 1.490129070f, f));
|
approx = sub(approx, mul( 1.490129070f, f));
|
||||||
approx = add(approx, div(27.728023300f, sub(4.84252568f, f)));
|
approx = add(approx, div(27.728023300f, sub(4.84252568f, f)));
|
||||||
|
approx = mul(1.0f * (1<<23), approx);
|
||||||
|
approx = clamp(approx, 0, kInfinityBits); // guard against underflow/overflow
|
||||||
|
|
||||||
return pun_to_F32(round(mul(1.0f * (1<<23), approx)));
|
return pun_to_F32(round(approx));
|
||||||
}
|
}
|
||||||
|
|
||||||
F32 Builder::approx_powf(F32 x, F32 y) {
|
F32 Builder::approx_powf(F32 x, F32 y) {
|
||||||
|
@ -2134,7 +2134,9 @@ DEF_TEST(SkVM_approx_math, r) {
|
|||||||
|
|
||||||
auto compare = [r](int N, const float values[], const float expected[]) {
|
auto compare = [r](int N, const float values[], const float expected[]) {
|
||||||
for (int i = 0; i < N; ++i) {
|
for (int i = 0; i < N; ++i) {
|
||||||
REPORTER_ASSERT(r, SkScalarNearlyEqual(values[i], expected[i], 0.001f));
|
REPORTER_ASSERT(r, (values[i] == expected[i]) ||
|
||||||
|
SkScalarNearlyEqual(values[i], expected[i], 0.001f),
|
||||||
|
"evaluated to %g, but expected %g", values[i], expected[i]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2151,12 +2153,12 @@ DEF_TEST(SkVM_approx_math, r) {
|
|||||||
|
|
||||||
// pow2
|
// pow2
|
||||||
{
|
{
|
||||||
float values[] = {-2, -1, 0, 1, 2, 3};
|
float values[] = {-80, -5, -2, -1, 0, 1, 2, 3, 5, 160};
|
||||||
constexpr int N = SK_ARRAY_COUNT(values);
|
constexpr int N = SK_ARRAY_COUNT(values);
|
||||||
eval(N, values, [](skvm::Builder* b, skvm::F32 v) {
|
eval(N, values, [](skvm::Builder* b, skvm::F32 v) {
|
||||||
return b->approx_pow2(v);
|
return b->approx_pow2(v);
|
||||||
});
|
});
|
||||||
const float expected[] = {0.25f, 0.5f, 1, 2, 4, 8};
|
const float expected[] = {0, 0.03125f, 0.25f, 0.5f, 1, 2, 4, 8, 32, INFINITY};
|
||||||
compare(N, values, expected);
|
compare(N, values, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user