shader: add FP64 min, max and rounding

Co-authored-by: Çağatay Güçlü <33038942+defektu@users.noreply.github.com>
This commit is contained in:
nmzik
2026-10-04 23:08:09 +04:00
parent 96011cc5c9
commit 8274e517b2
9 changed files with 266 additions and 1 deletions
@@ -30,6 +30,28 @@ uint32_t CompareEqual64(EmitterState& state, uint32_t lhs_value, uint32_t rhs_va
return Unary(state, not_equal ? spv::OpAny : spv::OpAll, TypeBool(state), compare);
}
uint32_t EmitMinMaxF64(EmitterState& state, uint32_t lhs, uint32_t rhs, bool max_value) {
const auto bits_type = TypeU64(state);
const auto lhs_bits = Unary(state, spv::OpBitcast, bits_type, lhs);
const auto rhs_bits = Unary(state, spv::OpBitcast, bits_type, rhs);
const auto ordered = Binary(state, max_value ? spv::OpFOrdGreaterThan : spv::OpFOrdLessThan,
TypeBool(state), lhs, rhs);
auto result = Select(state, TypeF64(state), ordered, lhs, rhs);
// Equal values have identical bits except signed zero: min chooses -0, max chooses +0.
const auto equal = Binary(state, spv::OpFOrdEqual, TypeBool(state), lhs, rhs);
const auto equal_bits = Binary(state, max_value ? spv::OpBitwiseAnd : spv::OpBitwiseOr,
bits_type, lhs_bits, rhs_bits);
result = Select(state, TypeF64(state), equal,
Unary(state, spv::OpBitcast, TypeF64(state), equal_bits), result);
// Non-IEEE mode selects the other operand for NaN, including rhs when both are NaN.
result = Select(state, TypeF64(state), Unary(state, spv::OpIsNan, TypeBool(state), rhs),
lhs, result);
return Select(state, TypeF64(state), Unary(state, spv::OpIsNan, TypeBool(state), lhs),
rhs, result);
}
uint32_t CompareOrdered64(EmitterState& state, uint32_t lhs_value, uint32_t rhs_value,
spv::Op high_compare, spv::Op low_compare) {
const auto lhs = ExtractPair(state, lhs_value);
@@ -468,6 +490,14 @@ uint32_t EmitFPMax32(EmitterState& state, uint32_t arg0, uint32_t arg1) {
return EmitMinMaxF32Value(state, arg0, arg1, true);
}
uint32_t EmitFPMin64(EmitterState& state, uint32_t arg0, uint32_t arg1) {
return EmitMinMaxF64(state, arg0, arg1, false);
}
uint32_t EmitFPMax64(EmitterState& state, uint32_t arg0, uint32_t arg1) {
return EmitMinMaxF64(state, arg0, arg1, true);
}
uint32_t EmitFPMinTri32(EmitterState& state, uint32_t arg0, uint32_t arg1, uint32_t arg2) {
return EmitFMinMax3(state, arg0, arg1, arg2, false);
}
@@ -164,6 +164,8 @@ inline constexpr auto EmitFPFma32 =
EmitGlsl<GLSLstd450Fma, IR::Type::F32, uint32_t, uint32_t, uint32_t>;
uint32_t EmitFPMin32(EmitterState& state, uint32_t arg0, uint32_t arg1);
uint32_t EmitFPMax32(EmitterState& state, uint32_t arg0, uint32_t arg1);
uint32_t EmitFPMin64(EmitterState& state, uint32_t arg0, uint32_t arg1);
uint32_t EmitFPMax64(EmitterState& state, uint32_t arg0, uint32_t arg1);
uint32_t EmitFPMinTri32(EmitterState& state, uint32_t arg0, uint32_t arg1, uint32_t arg2);
uint32_t EmitFPMaxTri32(EmitterState& state, uint32_t arg0, uint32_t arg1, uint32_t arg2);
uint32_t EmitFPMedTri32(EmitterState& state, uint32_t arg0, uint32_t arg1, uint32_t arg2);
@@ -178,6 +180,9 @@ inline constexpr auto EmitFPRoundEven32 = EmitGlsl<GLSLstd450RoundEven, IR::Type
inline constexpr auto EmitFPFloor32 = EmitGlsl<GLSLstd450Floor, IR::Type::F32, uint32_t>;
inline constexpr auto EmitFPCeil32 = EmitGlsl<GLSLstd450Ceil, IR::Type::F32, uint32_t>;
inline constexpr auto EmitFPTrunc32 = EmitGlsl<GLSLstd450Trunc, IR::Type::F32, uint32_t>;
inline constexpr auto EmitFPFloor64 = EmitGlsl<GLSLstd450Floor, IR::Type::F64, uint32_t>;
inline constexpr auto EmitFPCeil64 = EmitGlsl<GLSLstd450Ceil, IR::Type::F64, uint32_t>;
inline constexpr auto EmitFPTrunc64 = EmitGlsl<GLSLstd450Trunc, IR::Type::F64, uint32_t>;
inline constexpr auto EmitFPFract32 = EmitGlsl<GLSLstd450Fract, IR::Type::F32, uint32_t>;
inline constexpr auto EmitFPFract64 = EmitGlsl<GLSLstd450Fract, IR::Type::F64, uint32_t>;
uint32_t EmitFPSin(EmitterState& state, uint32_t arg0);
@@ -188,9 +188,12 @@ enum class Opcode {
V_FRACT_F32,
V_FRACT_F64,
V_TRUNC_F32,
V_TRUNC_F64,
V_CEIL_F32,
V_CEIL_F64,
V_RNDNE_F32,
V_FLOOR_F32,
V_FLOOR_F64,
V_EXP_F32,
V_LOG_F32,
V_RSQ_F32,
@@ -248,6 +251,8 @@ enum class Opcode {
V_FMA_F64,
V_ADD_F64,
V_MUL_F64,
V_MIN_F64,
V_MAX_F64,
V_FMA_F32,
V_FMA_F16,
V_PACK_B32_F16,
@@ -91,6 +91,9 @@ constexpr OpcodeMap VOP1_OPCODE_LIST[] = {
{0x0fu, Opcode::V_CVT_F32_F64},
{0x10u, Opcode::V_CVT_F64_F32},
{0x16u, Opcode::V_CVT_F64_U32},
{0x17u, Opcode::V_TRUNC_F64},
{0x18u, Opcode::V_CEIL_F64},
{0x1au, Opcode::V_FLOOR_F64},
{0x2fu, Opcode::V_RCP_F64},
{0x05u, Opcode::V_CVT_F32_I32},
{0x06u, Opcode::V_CVT_F32_U32},
@@ -156,6 +159,9 @@ constexpr OpcodeMap VOP3_ENCODED_VOP1_OPCODE_LIST[] = {
{0x0fu, Opcode::V_CVT_F32_F64},
{0x10u, Opcode::V_CVT_F64_F32},
{0x16u, Opcode::V_CVT_F64_U32},
{0x17u, Opcode::V_TRUNC_F64},
{0x18u, Opcode::V_CEIL_F64},
{0x1au, Opcode::V_FLOOR_F64},
{0x2fu, Opcode::V_RCP_F64},
{0x05u, Opcode::V_CVT_F32_I32},
{0x06u, Opcode::V_CVT_F32_U32},
@@ -292,6 +298,8 @@ constexpr OpcodeMap VOP3_OPCODE_LIST[] = {
{0x14cu, Opcode::V_FMA_F64},
{0x164u, Opcode::V_ADD_F64},
{0x165u, Opcode::V_MUL_F64},
{0x166u, Opcode::V_MIN_F64},
{0x167u, Opcode::V_MAX_F64},
{0x148u, Opcode::V_BFE_U32},
{0x149u, Opcode::V_BFE_I32},
{0x14au, Opcode::V_BFI_B32},
@@ -478,7 +486,9 @@ bool IsVopcCompareExec(Opcode opcode);
bool IsVop1Float64Opcode(Opcode opcode) {
return opcode == Opcode::V_CVT_F64_I32 || opcode == Opcode::V_CVT_F32_F64 ||
opcode == Opcode::V_CVT_F64_F32 || opcode == Opcode::V_CVT_F64_U32 ||
opcode == Opcode::V_RCP_F64 || opcode == Opcode::V_FRACT_F64;
opcode == Opcode::V_RCP_F64 || opcode == Opcode::V_FRACT_F64 ||
opcode == Opcode::V_TRUNC_F64 || opcode == Opcode::V_CEIL_F64 ||
opcode == Opcode::V_FLOOR_F64;
}
bool IsVop1FloatSourceOpcode(Opcode opcode) {
@@ -487,6 +497,9 @@ bool IsVop1FloatSourceOpcode(Opcode opcode) {
case Opcode::V_CVT_F64_F32:
case Opcode::V_RCP_F64:
case Opcode::V_FRACT_F64:
case Opcode::V_TRUNC_F64:
case Opcode::V_CEIL_F64:
case Opcode::V_FLOOR_F64:
case Opcode::V_MOV_B32:
case Opcode::V_CVT_F32_F16:
case Opcode::V_CVT_U32_F32:
@@ -1235,6 +1248,8 @@ uint32_t NativeVop3SourceCount(Opcode opcode) {
switch (opcode) {
case Opcode::V_ADD_F64:
case Opcode::V_MUL_F64:
case Opcode::V_MIN_F64:
case Opcode::V_MAX_F64:
case Opcode::V_MUL_LO_U32:
case Opcode::V_MUL_HI_U32:
case Opcode::V_MUL_LO_I32:
@@ -1387,6 +1402,8 @@ bool SupportsNativeVop3SourceModifiers(Opcode opcode) {
case Opcode::V_MAD_F32:
case Opcode::V_ADD_F64:
case Opcode::V_MUL_F64:
case Opcode::V_MIN_F64:
case Opcode::V_MAX_F64:
case Opcode::V_FMA_F64:
case Opcode::V_FMA_F32:
case Opcode::V_PACK_B32_F16:
@@ -895,8 +895,13 @@ void IncludeInstructionVectorRegisters(const Decoder::Instruction& inst, uint32_
case Decoder::Opcode::V_CVT_F64_U32: include_vector(inst.dst, 2u); break;
case Decoder::Opcode::V_FMA_F64: include_vector(inst.src2, 2u); [[fallthrough]];
case Decoder::Opcode::V_ADD_F64:
case Decoder::Opcode::V_MIN_F64:
case Decoder::Opcode::V_MAX_F64:
case Decoder::Opcode::V_MUL_F64: include_vector(inst.src1, 2u); [[fallthrough]];
case Decoder::Opcode::V_RCP_F64:
case Decoder::Opcode::V_TRUNC_F64:
case Decoder::Opcode::V_CEIL_F64:
case Decoder::Opcode::V_FLOOR_F64:
case Decoder::Opcode::V_FRACT_F64: include_vector(inst.dst, 2u); [[fallthrough]];
case Decoder::Opcode::V_CVT_F32_F64: include_vector(inst.src0, 2u); break;
case Decoder::Opcode::V_CMP_EQ_F64:
@@ -456,9 +456,12 @@ void Translator::EmitVector(const Decoder::Instruction& inst) {
case O::V_FRACT_F32: return FloatUnary(inst, IR::ValueOpcode::FPFract32);
case O::V_FRACT_F64: return FloatUnary(inst, IR::ValueOpcode::FPFract64);
case O::V_TRUNC_F32: return FloatUnary(inst, IR::ValueOpcode::FPTrunc32);
case O::V_TRUNC_F64: return FloatUnary(inst, IR::ValueOpcode::FPTrunc64);
case O::V_CEIL_F32: return FloatUnary(inst, IR::ValueOpcode::FPCeil32);
case O::V_CEIL_F64: return FloatUnary(inst, IR::ValueOpcode::FPCeil64);
case O::V_RNDNE_F32: return FloatUnary(inst, IR::ValueOpcode::FPRoundEven32);
case O::V_FLOOR_F32: return FloatUnary(inst, IR::ValueOpcode::FPFloor32);
case O::V_FLOOR_F64: return FloatUnary(inst, IR::ValueOpcode::FPFloor64);
case O::V_EXP_F32: return FloatUnary(inst, IR::ValueOpcode::FPExp2);
case O::V_LOG_F32: return FloatUnary(inst, IR::ValueOpcode::FPLog2);
case O::V_RSQ_F32: return FloatUnary(inst, IR::ValueOpcode::FPRecipSqrt32);
@@ -471,6 +474,8 @@ void Translator::EmitVector(const Decoder::Instruction& inst) {
case O::V_MUL_F32: return FloatBinary(inst, IR::ValueOpcode::FPMul32, false);
case O::V_ADD_F64: return FloatBinary(inst, IR::ValueOpcode::FPAdd64, false);
case O::V_MUL_F64: return FloatBinary(inst, IR::ValueOpcode::FPMul64, false);
case O::V_MIN_F64: return FloatBinary(inst, IR::ValueOpcode::FPMin64, false);
case O::V_MAX_F64: return FloatBinary(inst, IR::ValueOpcode::FPMax64, false);
case O::V_MIN_F32: return FloatBinary(inst, IR::ValueOpcode::FPMin32, false);
case O::V_MAX_F32: return FloatBinary(inst, IR::ValueOpcode::FPMax32, false);
case O::V_LDEXP_F32: return FloatBinary(inst, IR::ValueOpcode::FPLdexp, false);
@@ -186,6 +186,8 @@ VALUE_OPCODE(FPFma64, F64, F64, F64, F64)
VALUE_OPCODE(FPRecip64, F64, F64)
VALUE_OPCODE(FPMin32, F32, F32, F32)
VALUE_OPCODE(FPMax32, F32, F32, F32)
VALUE_OPCODE(FPMin64, F64, F64, F64)
VALUE_OPCODE(FPMax64, F64, F64, F64)
VALUE_OPCODE(FPMinTri32, F32, F32, F32, F32)
VALUE_OPCODE(FPMaxTri32, F32, F32, F32, F32)
VALUE_OPCODE(FPMedTri32, F32, F32, F32, F32)
@@ -202,6 +204,9 @@ VALUE_OPCODE(FPRoundEven32, F32, F32)
VALUE_OPCODE(FPFloor32, F32, F32)
VALUE_OPCODE(FPCeil32, F32, F32)
VALUE_OPCODE(FPTrunc32, F32, F32)
VALUE_OPCODE(FPFloor64, F64, F64)
VALUE_OPCODE(FPCeil64, F64, F64)
VALUE_OPCODE(FPTrunc64, F64, F64)
VALUE_OPCODE(FPFract32, F32, F32)
VALUE_OPCODE(FPFract64, F64, F64)