From 514f45105a756980afea74ffc6a86f85a1b883fb Mon Sep 17 00:00:00 2001 From: Jake Turner Date: Fri, 20 Sep 2024 16:22:30 +0100 Subject: [PATCH] DXIL Debugger extended support for FOrd*, FUnord* instructions Added support (including half, float, double variants) for: FOrdGreaterEqual FOrdLess FOrdLessEqual FOrdNotEqual FOrd FOrdTrue FUnord FUnordEqual FUnordGreater FUnordGreaterEqual FUnordLess FUnordLessEqual FUnordNotEqual Extended existing support of FOrdEqual, FOrdNotEqual to handle half and double variants --- renderdoc/driver/shaders/dxil/dxil_debug.cpp | 177 ++++++++++++++---- .../driver/shaders/dxil/dxil_reflect.cpp | 14 -- 2 files changed, 145 insertions(+), 46 deletions(-) diff --git a/renderdoc/driver/shaders/dxil/dxil_debug.cpp b/renderdoc/driver/shaders/dxil/dxil_debug.cpp index 0065c80ed..b2ca97564 100644 --- a/renderdoc/driver/shaders/dxil/dxil_debug.cpp +++ b/renderdoc/driver/shaders/dxil/dxil_debug.cpp @@ -27,6 +27,7 @@ #include "dxil_debug.h" #include "common/formatting.h" #include "maths/formatpacking.h" +#include "replay/common/var_dispatch_helpers.h" using namespace DXIL; using namespace DXDebug; @@ -2605,28 +2606,154 @@ bool ThreadState::ExecuteInstruction(DebugAPIWrapper *apiWrapper, result.value.f32v[0] = a.value.f32v[0] / b.value.f32v[0]; break; } + case Operation::FOrdFalse: case Operation::FOrdEqual: + case Operation::FOrdGreater: + case Operation::FOrdGreaterEqual: + case Operation::FOrdLess: + case Operation::FOrdLessEqual: case Operation::FOrdNotEqual: + case Operation::FOrd: + case Operation::FOrdTrue: + case Operation::FUnord: + case Operation::FUnordEqual: + case Operation::FUnordGreater: + case Operation::FUnordGreaterEqual: + case Operation::FUnordLess: + case Operation::FUnordLessEqual: + case Operation::FUnordNotEqual: { - // TODO: handle different bitwidths - // TODO: support F16, F64 - RDCASSERTEQUAL(inst.args[0]->type->type, Type::TypeKind::Scalar); - RDCASSERTEQUAL(inst.args[0]->type->scalarType, Type::Float); - RDCASSERTEQUAL(inst.args[1]->type->type, Type::TypeKind::Scalar); - RDCASSERTEQUAL(inst.args[1]->type->scalarType, Type::Float); - ShaderVariable a; - ShaderVariable b; - RDCASSERT(GetShaderVariable(inst.args[0], opCode, dxOpCode, a)); - RDCASSERT(GetShaderVariable(inst.args[1], opCode, dxOpCode, b)); - uint32_t res = ~0U; - if(opCode == Operation::FOrdEqual) - res = (a.value.s32v[0] == b.value.f32v[0]) ? 1 : 0; - else if(opCode == Operation::FOrdNotEqual) - res = (a.value.s32v[0] != b.value.f32v[0]) ? 1 : 0; - - RDCASSERTNOTEQUAL(res, ~0U); RDCASSERTEQUAL(result.type, VarType::Bool); - result.value.u32v[0] = res; + + if(opCode == Operation::FOrdFalse) + result.value.u32v[0] = 0; + else if(opCode == Operation::FOrdTrue) + result.value.u32v[0] = 1; + else + { + RDCASSERTEQUAL(inst.args[0]->type->type, Type::TypeKind::Scalar); + RDCASSERTEQUAL(inst.args[0]->type->scalarType, Type::Float); + RDCASSERTEQUAL(inst.args[1]->type->type, Type::TypeKind::Scalar); + RDCASSERTEQUAL(inst.args[1]->type->scalarType, Type::Float); + ShaderVariable a; + ShaderVariable b; + RDCASSERT(GetShaderVariable(inst.args[0], opCode, dxOpCode, a)); + RDCASSERT(GetShaderVariable(inst.args[1], opCode, dxOpCode, b)); + RDCASSERTEQUAL(a.type, b.type); + const uint32_t c = 0; + + // FOrd are all floating-point comparison where both operands are guaranteed to be ordered + // Using normal comparison operators will give the correct result + if(opCode == Operation::FOrdEqual) + { +#undef _IMPL +#define _IMPL(T) comp(result, c) = (comp(a, c) == comp(b, c)) ? 1 : 0 + + IMPL_FOR_FLOAT_TYPES_FOR_TYPE(_IMPL, a.type); + } + else if(opCode == Operation::FOrdGreater) + { +#undef _IMPL +#define _IMPL(T) comp(result, c) = (comp(a, c) > comp(b, c)) ? 1 : 0 + + IMPL_FOR_FLOAT_TYPES_FOR_TYPE(_IMPL, a.type); + } + else if(opCode == Operation::FOrdGreaterEqual) + { +#undef _IMPL +#define _IMPL(T) comp(result, c) = (comp(a, c) >= comp(b, c)) ? 1 : 0 + + IMPL_FOR_FLOAT_TYPES_FOR_TYPE(_IMPL, a.type); + } + else if(opCode == Operation::FOrdLess) + { +#undef _IMPL +#define _IMPL(T) comp(result, c) = (comp(a, c) < comp(b, c)) ? 1 : 0 + + IMPL_FOR_FLOAT_TYPES_FOR_TYPE(_IMPL, a.type); + } + else if(opCode == Operation::FOrdLessEqual) + { +#undef _IMPL +#define _IMPL(T) comp(result, c) = (comp(a, c) <= comp(b, c)) ? 1 : 0 + + IMPL_FOR_FLOAT_TYPES_FOR_TYPE(_IMPL, a.type); + } + else if(opCode == Operation::FOrdNotEqual) + { +#undef _IMPL +#define _IMPL(T) comp(result, c) = (comp(a, c) != comp(b, c)) ? 1 : 0 + + IMPL_FOR_FLOAT_TYPES_FOR_TYPE(_IMPL, a.type); + } + else if(opCode == Operation::FOrd) + { + // Both operands are ordered (not NaN) +#undef _IMPL +#define _IMPL(T) comp(result, c) = !RDCISNAN(comp(a, c)) && !RDCISNAN(comp(b, c)); + + IMPL_FOR_FLOAT_TYPES_FOR_TYPE(_IMPL, a.type); + } + // FUnord are all floating-point comparison where any operands may be unordered + // Any comparison with unordered comparisons will return false. Since we want + // 'or are unordered' then we want to negate the comparison so that unordered comparisons + // will always return true. So we negate and invert the actual comparison so that the + // comparison will be unchanged effectively. + else if(opCode == Operation::FUnord) + { + // Either operand is unordered (NaN) +#undef _IMPL +#define _IMPL(T) comp(result, c) = RDCISNAN(comp(a, c)) || RDCISNAN(comp(b, c)); + + IMPL_FOR_FLOAT_TYPES_FOR_TYPE(_IMPL, a.type); + } + else if(opCode == Operation::FUnordEqual) + { +#undef _IMPL +#define _IMPL(T) comp(result, c) = (comp(a, c) != comp(b, c)) ? 0 : 1 + + IMPL_FOR_FLOAT_TYPES_FOR_TYPE(_IMPL, a.type); + } + else if(opCode == Operation::FUnordGreater) + { +#undef _IMPL +#define _IMPL(T) comp(result, c) = (comp(a, c) <= comp(b, c)) ? 0 : 1 + + IMPL_FOR_FLOAT_TYPES_FOR_TYPE(_IMPL, a.type); + } + else if(opCode == Operation::FUnordGreaterEqual) + { +#undef _IMPL +#define _IMPL(T) comp(result, c) = (comp(a, c) < comp(b, c)) ? 0 : 1 + + IMPL_FOR_FLOAT_TYPES_FOR_TYPE(_IMPL, a.type); + } + else if(opCode == Operation::FUnordLess) + { +#undef _IMPL +#define _IMPL(T) comp(result, c) = (comp(a, c) >= comp(b, c)) ? 0 : 1 + + IMPL_FOR_FLOAT_TYPES_FOR_TYPE(_IMPL, a.type); + } + else if(opCode == Operation::FUnordLessEqual) + { +#undef _IMPL +#define _IMPL(T) comp(result, c) = (comp(a, c) > comp(b, c)) ? 0 : 1 + + IMPL_FOR_FLOAT_TYPES_FOR_TYPE(_IMPL, a.type); + } + else if(opCode == Operation::FUnordNotEqual) + { +#undef _IMPL +#define _IMPL(T) comp(result, c) = (comp(a, c) == comp(b, c)) ? 0 : 1 + + IMPL_FOR_FLOAT_TYPES_FOR_TYPE(_IMPL, a.type); + } + else + { + RDCERR("Unhandled opCode %s", ToStr(opCode).c_str()); + } + } break; } case Operation::IEqual: @@ -2769,20 +2896,6 @@ bool ThreadState::ExecuteInstruction(DebugAPIWrapper *apiWrapper, case Operation::SDiv: case Operation::URem: case Operation::SRem: - case Operation::FOrdFalse: - case Operation::FOrdGreater: - case Operation::FOrdGreaterEqual: - case Operation::FOrdLess: - case Operation::FOrdLessEqual: - case Operation::FOrd: - case Operation::FUnord: - case Operation::FUnordEqual: - case Operation::FUnordGreater: - case Operation::FUnordGreaterEqual: - case Operation::FUnordLess: - case Operation::FUnordLessEqual: - case Operation::FUnordNotEqual: - case Operation::FOrdTrue: case Operation::UGreater: case Operation::UGreaterEqual: case Operation::ULess: diff --git a/renderdoc/driver/shaders/dxil/dxil_reflect.cpp b/renderdoc/driver/shaders/dxil/dxil_reflect.cpp index 9358d36a9..704eabccf 100644 --- a/renderdoc/driver/shaders/dxil/dxil_reflect.cpp +++ b/renderdoc/driver/shaders/dxil/dxil_reflect.cpp @@ -1778,20 +1778,6 @@ rdcstr Program::GetDebugStatus() case Operation::SDiv: case Operation::URem: case Operation::SRem: - case Operation::FOrdFalse: - case Operation::FOrdGreater: - case Operation::FOrdGreaterEqual: - case Operation::FOrdLess: - case Operation::FOrdLessEqual: - case Operation::FOrd: - case Operation::FUnord: - case Operation::FUnordEqual: - case Operation::FUnordGreater: - case Operation::FUnordGreaterEqual: - case Operation::FUnordLess: - case Operation::FUnordLessEqual: - case Operation::FUnordNotEqual: - case Operation::FOrdTrue: case Operation::UGreater: case Operation::UGreaterEqual: case Operation::ULess: