From ec6627af03d3cab1e2a0e397b0c13978dba83853 Mon Sep 17 00:00:00 2001 From: baldurk Date: Tue, 14 Jul 2020 13:24:36 +0100 Subject: [PATCH] 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. --- renderdoc/common/common.h | 92 +++++++++++++++++++ .../driver/d3d11/d3d11_rendertexture.cpp | 2 +- renderdoc/driver/d3d11/d3d11_shaderdebug.cpp | 6 +- .../driver/d3d12/d3d12_rendertexture.cpp | 2 +- renderdoc/driver/d3d12/d3d12_shaderdebug.cpp | 6 +- renderdoc/driver/shaders/dxbc/dxbc_debug.cpp | 32 +++---- .../driver/shaders/dxil/dxil_disassemble.cpp | 2 +- .../driver/shaders/spirv/spirv_debug.cpp | 12 +-- .../shaders/spirv/spirv_debug_glsl450.cpp | 10 +- renderdoc/maths/formatpacking.cpp | 50 +++++----- .../serialise/codecs/vk_cpp_codec_common.h | 2 +- 11 files changed, 154 insertions(+), 62 deletions(-) diff --git a/renderdoc/common/common.h b/renderdoc/common/common.h index e5fcd59e9..219cff260 100644 --- a/renderdoc/common/common.h +++ b/renderdoc/common/common.h @@ -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 inline T AlignUp4(T x) { diff --git a/renderdoc/driver/d3d11/d3d11_rendertexture.cpp b/renderdoc/driver/d3d11/d3d11_rendertexture.cpp index 445599979..c17b5723e 100644 --- a/renderdoc/driver/d3d11/d3d11_rendertexture.cpp +++ b/renderdoc/driver/d3d11/d3d11_rendertexture.cpp @@ -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; } diff --git a/renderdoc/driver/d3d11/d3d11_shaderdebug.cpp b/renderdoc/driver/d3d11/d3d11_shaderdebug.cpp index 5d6236e2b..ea78354ae 100644 --- a/renderdoc/driver/d3d11/d3d11_shaderdebug.cpp +++ b/renderdoc/driver/d3d11/d3d11_shaderdebug.cpp @@ -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; diff --git a/renderdoc/driver/d3d12/d3d12_rendertexture.cpp b/renderdoc/driver/d3d12/d3d12_rendertexture.cpp index 72f79c10a..e533a8366 100644 --- a/renderdoc/driver/d3d12/d3d12_rendertexture.cpp +++ b/renderdoc/driver/d3d12/d3d12_rendertexture.cpp @@ -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; } diff --git a/renderdoc/driver/d3d12/d3d12_shaderdebug.cpp b/renderdoc/driver/d3d12/d3d12_shaderdebug.cpp index 42944d5bb..ba67678ed 100644 --- a/renderdoc/driver/d3d12/d3d12_shaderdebug.cpp +++ b/renderdoc/driver/d3d12/d3d12_shaderdebug.cpp @@ -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; diff --git a/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp b/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp index ad801f266..071f23307 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp +++ b/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp @@ -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); diff --git a/renderdoc/driver/shaders/dxil/dxil_disassemble.cpp b/renderdoc/driver/shaders/dxil/dxil_disassemble.cpp index e01f140a7..b78f96604 100644 --- a/renderdoc/driver/shaders/dxil/dxil_disassemble.cpp +++ b/renderdoc/driver/shaders/dxil/dxil_disassemble.cpp @@ -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); diff --git a/renderdoc/driver/shaders/spirv/spirv_debug.cpp b/renderdoc/driver/shaders/spirv/spirv_debug.cpp index 87dc1f14d..01bba9559 100644 --- a/renderdoc/driver/shaders/spirv/spirv_debug.cpp +++ b/renderdoc/driver/shaders/spirv/spirv_debug.cpp @@ -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 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 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; diff --git a/renderdoc/driver/shaders/spirv/spirv_debug_glsl450.cpp b/renderdoc/driver/shaders/spirv/spirv_debug_glsl450.cpp index 46df06f98..3c42985c3 100644 --- a/renderdoc/driver/shaders/spirv/spirv_debug_glsl450.cpp +++ b/renderdoc/driver/shaders/spirv/spirv_debug_glsl450.cpp @@ -49,7 +49,7 @@ ShaderVariable RoundEven(ThreadState &state, uint32_t, const rdcarray ¶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 ¶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) diff --git a/renderdoc/maths/formatpacking.cpp b/renderdoc/maths/formatpacking.cpp index 11b0a0ff9..f7c9af787 100644 --- a/renderdoc/maths/formatpacking.cpp +++ b/renderdoc/maths/formatpacking.cpp @@ -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 { diff --git a/renderdoc/serialise/codecs/vk_cpp_codec_common.h b/renderdoc/serialise/codecs/vk_cpp_codec_common.h index 8e0df3319..92ba29360 100644 --- a/renderdoc/serialise/codecs/vk_cpp_codec_common.h +++ b/renderdoc/serialise/codecs/vk_cpp_codec_common.h @@ -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"; }