Commit d55d9973 by Nicolas Capens Committed by Nicolas Capens

Fix clamping of NaN values.

We pass integer uniforms as floating-point ones, which can cause an exception when converting them to fixed-point values. For example an integer value of -1 would be 0xFFFFFFFF which is Not-a-Number in IEEE-754 floating-point and can't be cast to an integer. In this case we don't actually care about the result because the fixed- point number is only used by the fixed-function pipeline. A safe but still fast way to compare floating-point numbers including NaNs is to treat them as one's complement integers, which can easily be converted into two's complement representation. Also rename bitCast<> to bit_cast<> to match the C++20 function. Change-Id: Id588d25ab70d31eda2800c24a8df539d6a3411d4 Reviewed-on: https://swiftshader-review.googlesource.com/c/21708Tested-by: 's avatarNicolas Capens <nicolascapens@google.com> Reviewed-by: 's avatarAlexis Hétu <sugoi@google.com>
parent 9e4d040c
......@@ -75,7 +75,7 @@ namespace sw
}
template <typename destType, typename sourceType>
destType bitCast(const sourceType &source)
destType bit_cast(const sourceType &source)
{
union
{
......@@ -196,6 +196,26 @@ namespace sw
return clamp(x, 0.0f, 1.0f);
}
// Bit-cast of a floating-point value into a two's complement integer representation.
// This makes floating-point values comparable as integers.
inline int32_t float_as_twos_complement(float f)
{
// IEEE-754 floating-point numbers are sorted by magnitude in the same way as integers,
// except negative values are like one's complement integers. Convert them to two's complement.
int32_t i = bit_cast<int32_t>(f);
return (i < 0) ? (0x7FFFFFFF - i) : i;
}
// 'Safe' clamping operation which always returns a value between min and max (inclusive).
inline float clamp_s(float x, float min, float max)
{
// NaN values can't be compared directly
if(float_as_twos_complement(x) < float_as_twos_complement(min)) x = min;
if(float_as_twos_complement(x) > float_as_twos_complement(max)) x = max;
return x;
}
inline int ceilPow2(int x)
{
int i = 1;
......
......@@ -1974,7 +1974,7 @@ TIntermTyped* TIntermConstantUnion::fold(TOperator op, TIntermTyped* constantNod
case EOpFloatBitsToInt:
switch(basicType) {
case EbtFloat:
tempConstArray[i].setIConst(sw::bitCast<int>(unionArray[i].getFConst()));
tempConstArray[i].setIConst(sw::bit_cast<int>(unionArray[i].getFConst()));
type.setBasicType(EbtInt);
break;
default:
......@@ -1986,7 +1986,7 @@ TIntermTyped* TIntermConstantUnion::fold(TOperator op, TIntermTyped* constantNod
case EOpFloatBitsToUint:
switch(basicType) {
case EbtFloat:
tempConstArray[i].setUConst(sw::bitCast<unsigned int>(unionArray[i].getFConst()));
tempConstArray[i].setUConst(sw::bit_cast<unsigned int>(unionArray[i].getFConst()));
type.setBasicType(EbtUInt);
break;
default:
......@@ -1997,7 +1997,7 @@ TIntermTyped* TIntermConstantUnion::fold(TOperator op, TIntermTyped* constantNod
case EOpIntBitsToFloat:
switch(basicType) {
case EbtInt:
tempConstArray[i].setFConst(sw::bitCast<float>(unionArray[i].getIConst()));
tempConstArray[i].setFConst(sw::bit_cast<float>(unionArray[i].getIConst()));
type.setBasicType(EbtFloat);
break;
default:
......@@ -2008,7 +2008,7 @@ TIntermTyped* TIntermConstantUnion::fold(TOperator op, TIntermTyped* constantNod
case EOpUintBitsToFloat:
switch(basicType) {
case EbtUInt:
tempConstArray[i].setFConst(sw::bitCast<float>(unionArray[i].getUConst()));
tempConstArray[i].setFConst(sw::bit_cast<float>(unionArray[i].getUConst()));
type.setBasicType(EbtFloat);
break;
default:
......
......@@ -95,11 +95,11 @@ namespace sw
if(index < 8) // ps_1_x constants
{
// FIXME: Compact into generic function
short x = iround(4095 * clamp(value[0], -1.0f, 1.0f));
short y = iround(4095 * clamp(value[1], -1.0f, 1.0f));
short z = iround(4095 * clamp(value[2], -1.0f, 1.0f));
short w = iround(4095 * clamp(value[3], -1.0f, 1.0f));
// TODO: Compact into generic function
short x = iround(4095 * clamp_s(value[0], -1.0f, 1.0f));
short y = iround(4095 * clamp_s(value[1], -1.0f, 1.0f));
short z = iround(4095 * clamp_s(value[2], -1.0f, 1.0f));
short w = iround(4095 * clamp_s(value[3], -1.0f, 1.0f));
cW[index][0][0] = x;
cW[index][0][1] = x;
......
......@@ -75,7 +75,7 @@ namespace sw
}
template <typename destType, typename sourceType>
destType bitCast(const sourceType &source)
destType bit_cast(const sourceType &source)
{
union
{
......@@ -196,6 +196,26 @@ namespace sw
return clamp(x, 0.0f, 1.0f);
}
// Bit-cast of a floating-point value into a two's complement integer representation.
// This makes floating-point values comparable as integers.
inline int32_t float_as_twos_complement(float f)
{
// IEEE-754 floating-point numbers are sorted by magnitude in the same way as integers,
// except negative values are like one's complement integers. Convert them to two's complement.
int32_t i = bit_cast<int32_t>(f);
return (i < 0) ? (0x7FFFFFFF - i) : i;
}
// 'Safe' clamping operation which always returns a value between min and max (inclusive).
inline float clamp_s(float x, float min, float max)
{
// NaN values can't be compared directly
if(float_as_twos_complement(x) < float_as_twos_complement(min)) x = min;
if(float_as_twos_complement(x) > float_as_twos_complement(max)) x = max;
return x;
}
inline int ceilPow2(int x)
{
int i = 1;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment