mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-08-23 06:56:40 +00:00
Use our own isinf/isnan over compiler provided ones that are unreliable
* Due to standards nonsense the availability of isinf/isnan in C++ is quite complex and varies a lot between compilers. Trying to access them reliably is quite brittle and they're easy to implement with bit-inspection of float patterns, so we do that instead.
This commit is contained in:
@@ -165,6 +165,98 @@ T RDCLERP(const T &a, const T &b, const T &step)
|
||||
return (1.0f - step) * a + step * b;
|
||||
}
|
||||
|
||||
inline bool RDCISNAN(float input)
|
||||
{
|
||||
union
|
||||
{
|
||||
uint32_t u;
|
||||
float f;
|
||||
} x;
|
||||
|
||||
x.f = input;
|
||||
|
||||
// ignore sign bit (0x80000000)
|
||||
// check that exponent (0x7f800000) is fully set
|
||||
// AND that mantissa (0x007fffff) is greater than 0 (if it's 0 then this is an inf)
|
||||
return (x.u & 0x7fffffffU) > 0x7f800000U;
|
||||
}
|
||||
|
||||
inline bool RDCISINF(float input)
|
||||
{
|
||||
union
|
||||
{
|
||||
uint32_t u;
|
||||
float f;
|
||||
} x;
|
||||
|
||||
x.f = input;
|
||||
|
||||
// ignore sign bit (0x80000000)
|
||||
// check that exponent (0x7f800000) is fully set
|
||||
// AND that mantissa (0x007fffff) is exactly than 0 (if it's non-0 then this is an nan)
|
||||
return (x.u & 0x7fffffffU) == 0x7f800000U;
|
||||
}
|
||||
|
||||
inline bool RDCISFINITE(float input)
|
||||
{
|
||||
union
|
||||
{
|
||||
uint32_t u;
|
||||
float f;
|
||||
} x;
|
||||
|
||||
x.f = input;
|
||||
|
||||
// ignore sign bit (0x80000000)
|
||||
// check that exponent (0x7f800000) is not fully set (if it's fully set then this is a
|
||||
// nan/inf)
|
||||
return (x.u & 0x7f800000U) != 0x7f800000U;
|
||||
}
|
||||
|
||||
// double variants
|
||||
|
||||
inline bool RDCISNAN(double input)
|
||||
{
|
||||
union
|
||||
{
|
||||
uint64_t u;
|
||||
double f;
|
||||
} x;
|
||||
|
||||
x.f = input;
|
||||
|
||||
// ignore sign bit (0x80000000)
|
||||
// check that exponent (0x7f800000) is fully set
|
||||
// AND that mantissa (0x007fffff) is greater than 0 (if it's 0 then this is an inf)
|
||||
return (x.u & 0x7fffffffffffffffULL) > 0x7ff0000000000000ULL;
|
||||
}
|
||||
|
||||
inline bool RDCISINF(double input)
|
||||
{
|
||||
union
|
||||
{
|
||||
uint64_t u;
|
||||
double f;
|
||||
} x;
|
||||
|
||||
x.f = input;
|
||||
|
||||
return (x.u & 0x7fffffffffffffffULL) == 0x7ff0000000000000ULL;
|
||||
}
|
||||
|
||||
inline bool RDCISFINITE(double input)
|
||||
{
|
||||
union
|
||||
{
|
||||
uint64_t u;
|
||||
double f;
|
||||
} x;
|
||||
|
||||
x.f = input;
|
||||
|
||||
return (x.u & 0x7ff0000000000000ULL) != 0x7ff0000000000000ULL;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T AlignUp4(T x)
|
||||
{
|
||||
|
||||
@@ -494,7 +494,7 @@ bool D3D11Replay::RenderTextureInternal(TextureDisplay cfg, TexDisplayFlags flag
|
||||
pixelData.RangeMinimum = cfg.rangeMin;
|
||||
pixelData.InverseRangeSize = 1.0f / (cfg.rangeMax - cfg.rangeMin);
|
||||
|
||||
if(_isnan(pixelData.InverseRangeSize) || !_finite(pixelData.InverseRangeSize))
|
||||
if(!RDCISFINITE(pixelData.InverseRangeSize))
|
||||
{
|
||||
pixelData.InverseRangeSize = FLT_MAX;
|
||||
}
|
||||
|
||||
@@ -1149,7 +1149,7 @@ bool D3D11DebugAPIWrapper::CalculateSampleGather(
|
||||
|
||||
for(uint32_t i = 0; i < ddxCalc.columns; i++)
|
||||
{
|
||||
if(_isnan(ddxCalc.value.fv[i]) || !_finite(ddxCalc.value.fv[i]))
|
||||
if(!RDCISFINITE(ddxCalc.value.fv[i]))
|
||||
{
|
||||
RDCWARN("NaN or Inf in texlookup");
|
||||
ddxCalc.value.fv[i] = 0.0f;
|
||||
@@ -1160,7 +1160,7 @@ bool D3D11DebugAPIWrapper::CalculateSampleGather(
|
||||
"texture lookup ddx - using 0.0 instead",
|
||||
m_instruction, opString));
|
||||
}
|
||||
if(_isnan(ddyCalc.value.fv[i]) || !_finite(ddyCalc.value.fv[i]))
|
||||
if(!RDCISFINITE(ddyCalc.value.fv[i]))
|
||||
{
|
||||
RDCWARN("NaN or Inf in texlookup");
|
||||
ddyCalc.value.fv[i] = 0.0f;
|
||||
@@ -1175,7 +1175,7 @@ bool D3D11DebugAPIWrapper::CalculateSampleGather(
|
||||
|
||||
for(uint32_t i = 0; i < uv.columns; i++)
|
||||
{
|
||||
if(texcoordType == 0 && (_isnan(uv.value.fv[i]) || !_finite(uv.value.fv[i])))
|
||||
if(texcoordType == 0 && (RDCISFINITE(uv.value.fv[i])))
|
||||
{
|
||||
RDCWARN("NaN or Inf in texlookup");
|
||||
uv.value.fv[i] = 0.0f;
|
||||
|
||||
@@ -380,7 +380,7 @@ bool D3D12Replay::RenderTextureInternal(D3D12_CPU_DESCRIPTOR_HANDLE rtv, Texture
|
||||
pixelData.RangeMinimum = cfg.rangeMin;
|
||||
pixelData.InverseRangeSize = 1.0f / (cfg.rangeMax - cfg.rangeMin);
|
||||
|
||||
if(_isnan(pixelData.InverseRangeSize) || !_finite(pixelData.InverseRangeSize))
|
||||
if(!RDCISFINITE(pixelData.InverseRangeSize))
|
||||
{
|
||||
pixelData.InverseRangeSize = FLT_MAX;
|
||||
}
|
||||
|
||||
@@ -1209,7 +1209,7 @@ bool D3D12DebugAPIWrapper::CalculateSampleGather(
|
||||
|
||||
for(uint32_t i = 0; i < ddxCalc.columns; i++)
|
||||
{
|
||||
if(_isnan(ddxCalc.value.fv[i]) || !_finite(ddxCalc.value.fv[i]))
|
||||
if(!RDCISFINITE(ddxCalc.value.fv[i]))
|
||||
{
|
||||
RDCWARN("NaN or Inf in texlookup");
|
||||
ddxCalc.value.fv[i] = 0.0f;
|
||||
@@ -1220,7 +1220,7 @@ bool D3D12DebugAPIWrapper::CalculateSampleGather(
|
||||
"texture lookup ddx - using 0.0 instead",
|
||||
m_instruction, opString));
|
||||
}
|
||||
if(_isnan(ddyCalc.value.fv[i]) || !_finite(ddyCalc.value.fv[i]))
|
||||
if(!RDCISFINITE(ddyCalc.value.fv[i]))
|
||||
{
|
||||
RDCWARN("NaN or Inf in texlookup");
|
||||
ddyCalc.value.fv[i] = 0.0f;
|
||||
@@ -1235,7 +1235,7 @@ bool D3D12DebugAPIWrapper::CalculateSampleGather(
|
||||
|
||||
for(uint32_t i = 0; i < uv.columns; i++)
|
||||
{
|
||||
if(texcoordType == 0 && (_isnan(uv.value.fv[i]) || !_finite(uv.value.fv[i])))
|
||||
if(texcoordType == 0 && (!RDCISFINITE(uv.value.fv[i])))
|
||||
{
|
||||
RDCWARN("NaN or Inf in texlookup");
|
||||
uv.value.fv[i] = 0.0f;
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace DXBCDebug
|
||||
{
|
||||
static float round_ne(float x)
|
||||
{
|
||||
if(!_finite(x) || _isnan(x))
|
||||
if(!RDCISFINITE(x))
|
||||
return x;
|
||||
|
||||
float rem = remainderf(x, 1.0f);
|
||||
@@ -770,10 +770,10 @@ ShaderVariable TypedUAVLoad(GlobalState::ViewFmt &fmt, const byte *d)
|
||||
|
||||
float dxbc_min(float a, float b)
|
||||
{
|
||||
if(_isnan(a))
|
||||
if(RDCISNAN(a))
|
||||
return b;
|
||||
|
||||
if(_isnan(b))
|
||||
if(RDCISNAN(b))
|
||||
return a;
|
||||
|
||||
return a < b ? a : b;
|
||||
@@ -781,10 +781,10 @@ float dxbc_min(float a, float b)
|
||||
|
||||
double dxbc_min(double a, double b)
|
||||
{
|
||||
if(_isnan(a))
|
||||
if(RDCISNAN(a))
|
||||
return b;
|
||||
|
||||
if(_isnan(b))
|
||||
if(RDCISNAN(b))
|
||||
return a;
|
||||
|
||||
return a < b ? a : b;
|
||||
@@ -792,10 +792,10 @@ double dxbc_min(double a, double b)
|
||||
|
||||
float dxbc_max(float a, float b)
|
||||
{
|
||||
if(_isnan(a))
|
||||
if(RDCISNAN(a))
|
||||
return b;
|
||||
|
||||
if(_isnan(b))
|
||||
if(RDCISNAN(b))
|
||||
return a;
|
||||
|
||||
return a >= b ? a : b;
|
||||
@@ -803,10 +803,10 @@ float dxbc_max(float a, float b)
|
||||
|
||||
double dxbc_max(double a, double b)
|
||||
{
|
||||
if(_isnan(a))
|
||||
if(RDCISNAN(a))
|
||||
return b;
|
||||
|
||||
if(_isnan(b))
|
||||
if(RDCISNAN(b))
|
||||
return a;
|
||||
|
||||
return a >= b ? a : b;
|
||||
@@ -1143,13 +1143,13 @@ ShaderEvents ThreadState::AssignValue(ShaderVariable &dst, uint32_t dstIndex,
|
||||
if(src.type == VarType::Float)
|
||||
{
|
||||
float ft = src.value.fv[srcIndex];
|
||||
if(!_finite(ft) || _isnan(ft))
|
||||
if(!RDCISFINITE(ft))
|
||||
flags |= ShaderEvents::GeneratedNanOrInf;
|
||||
}
|
||||
else if(src.type == VarType::Double)
|
||||
{
|
||||
double dt = src.value.dv[srcIndex];
|
||||
if(!_finite(dt) || _isnan(dt))
|
||||
if(!RDCISFINITE(dt))
|
||||
flags |= ShaderEvents::GeneratedNanOrInf;
|
||||
}
|
||||
|
||||
@@ -5548,7 +5548,7 @@ TEST_CASE("DXBC debugging helpers", "[program]")
|
||||
CHECK(dxbc_min(nan, neginf) == neginf);
|
||||
CHECK(dxbc_min(nan, a) == a);
|
||||
CHECK(dxbc_min(nan, posinf) == posinf);
|
||||
CHECK(_isnan(dxbc_min(nan, nan)));
|
||||
CHECK(RDCISNAN(dxbc_min(nan, nan)));
|
||||
};
|
||||
|
||||
SECTION("dxbc_max")
|
||||
@@ -5568,7 +5568,7 @@ TEST_CASE("DXBC debugging helpers", "[program]")
|
||||
CHECK(dxbc_max(nan, neginf) == neginf);
|
||||
CHECK(dxbc_max(nan, a) == a);
|
||||
CHECK(dxbc_max(nan, posinf) == posinf);
|
||||
CHECK(_isnan(dxbc_max(nan, nan)));
|
||||
CHECK(RDCISNAN(dxbc_max(nan, nan)));
|
||||
};
|
||||
|
||||
SECTION("sat/abs/neg on NaNs")
|
||||
@@ -5585,14 +5585,14 @@ TEST_CASE("DXBC debugging helpers", "[program]")
|
||||
v2 = neg(v, VarType::Float);
|
||||
|
||||
CHECK(v2.value.f.x == -b);
|
||||
CHECK(_isnan(v2.value.f.y));
|
||||
CHECK(RDCISNAN(v2.value.f.y));
|
||||
CHECK(v2.value.f.z == posinf);
|
||||
CHECK(v2.value.f.w == neginf);
|
||||
|
||||
v2 = abs(v, VarType::Float);
|
||||
|
||||
CHECK(v2.value.f.x == b);
|
||||
CHECK(_isnan(v2.value.f.y));
|
||||
CHECK(RDCISNAN(v2.value.f.y));
|
||||
CHECK(v2.value.f.z == posinf);
|
||||
CHECK(v2.value.f.w == posinf);
|
||||
};
|
||||
@@ -5607,7 +5607,7 @@ TEST_CASE("DXBC debugging helpers", "[program]")
|
||||
CHECK(flush_denorm(-foo) == -foo);
|
||||
|
||||
// check NaN/inf values
|
||||
CHECK(_isnan(flush_denorm(nan)));
|
||||
CHECK(RDCISNAN(flush_denorm(nan)));
|
||||
CHECK(flush_denorm(neginf) == neginf);
|
||||
CHECK(flush_denorm(posinf) == posinf);
|
||||
|
||||
|
||||
@@ -1726,7 +1726,7 @@ rdcstr Constant::toString(bool withType) const
|
||||
orig = val.fv[0];
|
||||
|
||||
// NaNs/infs are printed as hex to ensure we don't lose bits
|
||||
if(!isnan(orig) && !isinf(orig))
|
||||
if(RDCISFINITE(orig))
|
||||
{
|
||||
// check we can reparse precisely a float-formatted string. Otherwise we print as hex
|
||||
rdcstr flt = StringFormat::Fmt("%.6le", orig);
|
||||
|
||||
@@ -43,16 +43,16 @@ static bool ContainsNaNInf(const ShaderVariable &val)
|
||||
{
|
||||
for(int i = 0; i < count; i++)
|
||||
{
|
||||
ret |= isinf(val.value.fv[i]);
|
||||
ret |= isnan(val.value.fv[i]) != 0;
|
||||
ret |= RDCISINF(val.value.fv[i]);
|
||||
ret |= RDCISNAN(val.value.fv[i]) != 0;
|
||||
}
|
||||
}
|
||||
else if(val.type == VarType::Double)
|
||||
{
|
||||
for(int i = 0; i < count; i++)
|
||||
{
|
||||
ret |= isinf(val.value.dv[i]);
|
||||
ret |= isnan(val.value.dv[i]) != 0;
|
||||
ret |= RDCISINF(val.value.dv[i]);
|
||||
ret |= RDCISNAN(val.value.dv[i]) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1440,7 +1440,7 @@ void ThreadState::StepNext(ShaderDebugState *state, const rdcarray<ThreadState>
|
||||
ShaderVariable var = GetSrc(is.x);
|
||||
|
||||
for(uint8_t c = 0; c < var.columns; c++)
|
||||
var.value.uv[c] = isnan(var.value.fv[c]) ? 1 : 0;
|
||||
var.value.uv[c] = RDCISNAN(var.value.fv[c]) ? 1 : 0;
|
||||
|
||||
var.type = VarType::Bool;
|
||||
|
||||
@@ -1454,7 +1454,7 @@ void ThreadState::StepNext(ShaderDebugState *state, const rdcarray<ThreadState>
|
||||
ShaderVariable var = GetSrc(is.x);
|
||||
|
||||
for(uint8_t c = 0; c < var.columns; c++)
|
||||
var.value.uv[c] = isinf(var.value.fv[c]) ? 1 : 0;
|
||||
var.value.uv[c] = RDCISINF(var.value.fv[c]) ? 1 : 0;
|
||||
|
||||
var.type = VarType::Bool;
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ ShaderVariable RoundEven(ThreadState &state, uint32_t, const rdcarray<Id> ¶m
|
||||
for(uint32_t c = 0; c < var.columns; c++)
|
||||
{
|
||||
float x = var.value.fv[c];
|
||||
if(!isinf(x) && !isnan(x))
|
||||
if(RDCISFINITE(x))
|
||||
var.value.fv[c] = x - remainderf(x, 1.0f);
|
||||
}
|
||||
|
||||
@@ -854,8 +854,8 @@ ShaderVariable FindUMsb(ThreadState &state, uint32_t, const rdcarray<Id> ¶ms
|
||||
|
||||
static float GLSLNMax(float x, float y)
|
||||
{
|
||||
const bool xnan = isnan(x);
|
||||
const bool ynan = isnan(y);
|
||||
const bool xnan = RDCISNAN(x);
|
||||
const bool ynan = RDCISNAN(y);
|
||||
if(xnan && !ynan)
|
||||
return y;
|
||||
else if(!xnan && ynan)
|
||||
@@ -866,8 +866,8 @@ static float GLSLNMax(float x, float y)
|
||||
|
||||
static float GLSLNMin(float x, float y)
|
||||
{
|
||||
const bool xnan = isnan(x);
|
||||
const bool ynan = isnan(y);
|
||||
const bool xnan = RDCISNAN(x);
|
||||
const bool ynan = RDCISNAN(y);
|
||||
if(xnan && !ynan)
|
||||
return y;
|
||||
else if(!xnan && ynan)
|
||||
|
||||
@@ -1244,11 +1244,11 @@ TEST_CASE("Check format conversion", "[format]")
|
||||
{
|
||||
SECTION("Check half conversion is reflexive")
|
||||
{
|
||||
CHECK(std::isnan(ConvertFromHalf(ConvertToHalf(NAN))));
|
||||
CHECK(!std::isnan(ConvertFromHalf(ConvertToHalf(INFINITY))));
|
||||
CHECK(!std::isnan(ConvertFromHalf(ConvertToHalf(-INFINITY))));
|
||||
CHECK(!std::isfinite(ConvertFromHalf(ConvertToHalf(INFINITY))));
|
||||
CHECK(!std::isfinite(ConvertFromHalf(ConvertToHalf(-INFINITY))));
|
||||
CHECK(RDCISNAN(ConvertFromHalf(ConvertToHalf(NAN))));
|
||||
CHECK(!RDCISNAN(ConvertFromHalf(ConvertToHalf(INFINITY))));
|
||||
CHECK(!RDCISNAN(ConvertFromHalf(ConvertToHalf(-INFINITY))));
|
||||
CHECK(!RDCISFINITE(ConvertFromHalf(ConvertToHalf(INFINITY))));
|
||||
CHECK(!RDCISFINITE(ConvertFromHalf(ConvertToHalf(-INFINITY))));
|
||||
|
||||
for(uint16_t i = 0;; i++)
|
||||
{
|
||||
@@ -1257,15 +1257,15 @@ TEST_CASE("Check format conversion", "[format]")
|
||||
|
||||
// NaNs and infinites get mapped together. Just check that the value we get out is still a
|
||||
// nan/inf
|
||||
if(std::isnan(f))
|
||||
if(RDCISNAN(f))
|
||||
{
|
||||
float f2 = ConvertFromHalf(i2);
|
||||
CHECK(std::isnan(f2));
|
||||
CHECK(RDCISNAN(f2));
|
||||
}
|
||||
else if(!std::isfinite(f))
|
||||
else if(!RDCISFINITE(f))
|
||||
{
|
||||
float f2 = ConvertFromHalf(i2);
|
||||
CHECK(!std::isfinite(f2));
|
||||
CHECK(!RDCISFINITE(f2));
|
||||
}
|
||||
else if(i == 0x8000)
|
||||
{
|
||||
@@ -1328,15 +1328,15 @@ TEST_CASE("Check format conversion", "[format]")
|
||||
Vec3f vec = ConvertFromR11G11B10(input);
|
||||
uint32_t output = ConvertToR11G11B10(vec);
|
||||
|
||||
if(std::isnan(vec.x))
|
||||
if(RDCISNAN(vec.x))
|
||||
{
|
||||
Vec3f vec2 = ConvertFromR11G11B10(output);
|
||||
CHECK(std::isnan(vec2.x));
|
||||
CHECK(RDCISNAN(vec2.x));
|
||||
}
|
||||
else if(!std::isfinite(vec.x))
|
||||
else if(!RDCISFINITE(vec.x))
|
||||
{
|
||||
Vec3f vec2 = ConvertFromR11G11B10(output);
|
||||
CHECK(!std::isfinite(vec2.x));
|
||||
CHECK(!RDCISFINITE(vec2.x));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1351,15 +1351,15 @@ TEST_CASE("Check format conversion", "[format]")
|
||||
Vec3f vec = ConvertFromR11G11B10(input);
|
||||
uint32_t output = ConvertToR11G11B10(vec);
|
||||
|
||||
if(std::isnan(vec.z))
|
||||
if(RDCISNAN(vec.z))
|
||||
{
|
||||
Vec3f vec2 = ConvertFromR11G11B10(output);
|
||||
CHECK(std::isnan(vec2.z));
|
||||
CHECK(RDCISNAN(vec2.z));
|
||||
}
|
||||
else if(!std::isfinite(vec.z))
|
||||
else if(!RDCISFINITE(vec.z))
|
||||
{
|
||||
Vec3f vec2 = ConvertFromR11G11B10(output);
|
||||
CHECK(!std::isfinite(vec2.z));
|
||||
CHECK(!RDCISFINITE(vec2.z));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1456,21 +1456,21 @@ TEST_CASE("Check format conversion", "[format]")
|
||||
|
||||
Vec3f conv = ConvertFromR11G11B10(test.first);
|
||||
|
||||
if(std::isnan(conv.x))
|
||||
if(RDCISNAN(conv.x))
|
||||
{
|
||||
CHECK(std::isnan(test.second.x));
|
||||
CHECK(RDCISNAN(test.second.x));
|
||||
}
|
||||
else if(!std::isfinite(conv.x))
|
||||
else if(!RDCISFINITE(conv.x))
|
||||
{
|
||||
CHECK(!std::isfinite(test.second.x));
|
||||
CHECK(!RDCISFINITE(test.second.x));
|
||||
}
|
||||
else if(std::isnan(conv.z))
|
||||
else if(RDCISNAN(conv.z))
|
||||
{
|
||||
CHECK(std::isnan(test.second.z));
|
||||
CHECK(RDCISNAN(test.second.z));
|
||||
}
|
||||
else if(!std::isfinite(conv.z))
|
||||
else if(!RDCISFINITE(conv.z))
|
||||
{
|
||||
CHECK(!std::isfinite(test.second.z));
|
||||
CHECK(!RDCISFINITE(test.second.z));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -75,7 +75,7 @@ inline rdcstr ValueStr(SDObject *ptr)
|
||||
}
|
||||
else if(ptr->IsFloat())
|
||||
{
|
||||
if(isnan(ptr->data.basic.d))
|
||||
if(RDCISNAN(ptr->data.basic.d))
|
||||
ptr->data.basic.d = 1.0f;
|
||||
result = std::to_string(ptr->AsDouble()) + "f";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user