mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-19 18:36:53 +02:00
shader: support FP64 conversions used by FighterZ (#884)
shader: support float and unsigned widening to FP64 Co-authored-by: nmzik <Nmzik@mail.ru>
This commit is contained in:
committed by
GitHub
parent
0791f921f6
commit
0a5774be7b
@@ -307,6 +307,11 @@ uint32_t EmitConvertF32F64(EmitterState& state, uint32_t arg0) {
|
||||
Unary(state, spv::OpBitcast, TypeF32(state), clamped), converted));
|
||||
}
|
||||
|
||||
uint32_t EmitConvertF64F32(EmitterState& state, uint32_t arg0) {
|
||||
return EmitNative<spv::OpFConvert, IR::Type::F64>(state,
|
||||
EmitFlushF32DenormToSignedZero(state, arg0));
|
||||
}
|
||||
|
||||
uint32_t EmitCompositeExtractU64(EmitterState& state, uint32_t arg0, IR::Value arg1) {
|
||||
return EmitNative<spv::OpCompositeExtract, IR::Type::U32>(state, arg0, arg1.U32());
|
||||
}
|
||||
|
||||
@@ -31,7 +31,9 @@ uint32_t EmitConvertSigned32ToFloat(EmitterState& state, uint32_t arg0) {
|
||||
inline constexpr auto EmitConvertF32S32 = EmitConvertSigned32ToFloat<IR::Type::F32>;
|
||||
inline constexpr auto EmitConvertF64S32 = EmitConvertSigned32ToFloat<IR::Type::F64>;
|
||||
uint32_t EmitConvertF32F64(EmitterState& state, uint32_t arg0);
|
||||
uint32_t EmitConvertF64F32(EmitterState& state, uint32_t arg0);
|
||||
EMIT_NATIVE(ConvertF32U32, OpConvertUToF, F32, uint32_t)
|
||||
EMIT_NATIVE(ConvertF64U32, OpConvertUToF, F64, uint32_t)
|
||||
EMIT_NATIVE(CompositeConstructU64, OpCompositeConstruct, U64, uint32_t, uint32_t)
|
||||
EMIT_NATIVE(CompositeConstructU32x2, OpCompositeConstruct, U32x2, uint32_t, uint32_t)
|
||||
EMIT_NATIVE(CompositeConstructU32x3, OpCompositeConstruct, U32x3, uint32_t, uint32_t, uint32_t)
|
||||
|
||||
@@ -684,8 +684,8 @@ void DefineModule(EmitterState& state) {
|
||||
state.builder.AddExecutionMode(state.main_func, spv::ExecutionModeSignedZeroInfNanPreserve,
|
||||
32u);
|
||||
if (state.requirements.float64) {
|
||||
EXIT_NOT_IMPLEMENTED(state.program.stage != ShaderType::Compute);
|
||||
EXIT_NOT_IMPLEMENTED(state.input_info.compute->float_mode != 0xc0);
|
||||
EXIT_NOT_IMPLEMENTED(state.program.stage == ShaderType::Compute &&
|
||||
state.input_info.compute->float_mode != 0xc0);
|
||||
// MODE=0xc0 uses round-to-nearest-even and preserves FP64 input/output denormals.
|
||||
state.builder.RequireCapability(spv::CapabilityFloat64);
|
||||
state.builder.RequireCapability(spv::CapabilityRoundingModeRTE);
|
||||
|
||||
@@ -156,6 +156,8 @@ enum class Opcode {
|
||||
V_DOT2C_F32_F16,
|
||||
V_CVT_F64_I32,
|
||||
V_CVT_F32_F64,
|
||||
V_CVT_F64_F32,
|
||||
V_CVT_F64_U32,
|
||||
V_CVT_F32_I32,
|
||||
V_CVT_F32_U32,
|
||||
V_CVT_U32_F32,
|
||||
|
||||
@@ -89,6 +89,8 @@ constexpr OpcodeMap VOP1_OPCODE_LIST[] = {
|
||||
{0x02u, Opcode::V_READFIRSTLANE_B32},
|
||||
{0x04u, Opcode::V_CVT_F64_I32},
|
||||
{0x0fu, Opcode::V_CVT_F32_F64},
|
||||
{0x10u, Opcode::V_CVT_F64_F32},
|
||||
{0x16u, Opcode::V_CVT_F64_U32},
|
||||
{0x2fu, Opcode::V_RCP_F64},
|
||||
{0x05u, Opcode::V_CVT_F32_I32},
|
||||
{0x06u, Opcode::V_CVT_F32_U32},
|
||||
@@ -151,6 +153,8 @@ constexpr OpcodeMap VOP3_ENCODED_VOP1_OPCODE_LIST[] = {
|
||||
{0x02u, Opcode::V_READFIRSTLANE_B32},
|
||||
{0x04u, Opcode::V_CVT_F64_I32},
|
||||
{0x0fu, Opcode::V_CVT_F32_F64},
|
||||
{0x10u, Opcode::V_CVT_F64_F32},
|
||||
{0x16u, Opcode::V_CVT_F64_U32},
|
||||
{0x2fu, Opcode::V_RCP_F64},
|
||||
{0x05u, Opcode::V_CVT_F32_I32},
|
||||
{0x06u, Opcode::V_CVT_F32_U32},
|
||||
@@ -461,6 +465,7 @@ bool IsVopcCompareExec(Opcode opcode);
|
||||
bool IsVop1FloatSourceOpcode(Opcode opcode) {
|
||||
switch (opcode) {
|
||||
case Opcode::V_CVT_F32_F64:
|
||||
case Opcode::V_CVT_F64_F32:
|
||||
case Opcode::V_RCP_F64:
|
||||
case Opcode::V_MOV_B32:
|
||||
case Opcode::V_CVT_F32_F16:
|
||||
@@ -721,6 +726,7 @@ void ApplyDppModifier(Operand& operand, uint32_t modifier, uint32_t encoding) {
|
||||
void DecodeVop1Dpp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
|
||||
uint32_t opcode, uint32_t vdst, Instruction& inst) {
|
||||
if (inst.opcode == Opcode::V_CVT_F64_I32 || inst.opcode == Opcode::V_CVT_F32_F64 ||
|
||||
inst.opcode == Opcode::V_CVT_F64_F32 || inst.opcode == Opcode::V_CVT_F64_U32 ||
|
||||
inst.opcode == Opcode::V_RCP_F64) {
|
||||
SetUnsupported(inst, Family::VOP1, opcode, "FP64 instructions do not support DPP");
|
||||
return;
|
||||
|
||||
@@ -876,7 +876,9 @@ void IncludeInstructionVectorRegisters(const Decoder::Instruction& inst, uint32_
|
||||
include_vector(inst.src2);
|
||||
include_vector(inst.src3);
|
||||
switch (inst.opcode) {
|
||||
case Decoder::Opcode::V_CVT_F64_I32: include_vector(inst.dst, 2u); break;
|
||||
case Decoder::Opcode::V_CVT_F64_I32:
|
||||
case Decoder::Opcode::V_CVT_F64_F32:
|
||||
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_MUL_F64: include_vector(inst.src1, 2u); [[fallthrough]];
|
||||
case Decoder::Opcode::V_RCP_F64: include_vector(inst.dst, 2u); [[fallthrough]];
|
||||
|
||||
@@ -299,6 +299,8 @@ void Translator::EmitVector(const Decoder::Instruction& inst) {
|
||||
case O::V_CVT_F32_I32: V_CVT_F32_I32(inst); return;
|
||||
case O::V_CVT_F64_I32: return FloatUnary(inst, IR::ValueOpcode::ConvertF64S32);
|
||||
case O::V_CVT_F32_F64: return FloatUnary(inst, IR::ValueOpcode::ConvertF32F64);
|
||||
case O::V_CVT_F64_F32: return FloatUnary(inst, IR::ValueOpcode::ConvertF64F32);
|
||||
case O::V_CVT_F64_U32: return FloatUnary(inst, IR::ValueOpcode::ConvertF64U32);
|
||||
case O::V_CVT_U32_F32: V_CVT_U32_F32(inst); return;
|
||||
case O::V_CVT_I32_F32: V_CVT_I32_F32(inst); return;
|
||||
case O::V_CVT_F16_F32: V_CVT_F16_F32(inst); return;
|
||||
|
||||
@@ -66,7 +66,9 @@ VALUE_OPCODE(ConvertU32F32, U32, F32)
|
||||
VALUE_OPCODE(ConvertF32S32, F32, U32)
|
||||
VALUE_OPCODE(ConvertF64S32, F64, U32)
|
||||
VALUE_OPCODE(ConvertF32F64, F32, F64)
|
||||
VALUE_OPCODE(ConvertF64F32, F64, F32)
|
||||
VALUE_OPCODE(ConvertF32U32, F32, U32)
|
||||
VALUE_OPCODE(ConvertF64U32, F64, U32)
|
||||
VALUE_OPCODE(CompositeConstructU64, U64, U32, U32)
|
||||
VALUE_OPCODE(CompositeConstructU32x2, U32x2, U32, U32)
|
||||
VALUE_OPCODE(CompositeConstructU32x3, U32x3, U32, U32, U32)
|
||||
|
||||
@@ -16709,6 +16709,8 @@ CoverageClass ClassifyOpcode(ShaderOpcode opcode,
|
||||
case Opcode::V_DOT2C_F32_F16:
|
||||
case Opcode::V_CVT_F64_I32:
|
||||
case Opcode::V_CVT_F32_F64:
|
||||
case Opcode::V_CVT_F64_F32:
|
||||
case Opcode::V_CVT_F64_U32:
|
||||
case Opcode::V_RCP_F64:
|
||||
case Opcode::V_MUL_F64:
|
||||
case Opcode::V_FMA_F64:
|
||||
@@ -22122,6 +22124,83 @@ TestCase VectorF64CapturedScreenSpaceShadows() {
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase VectorF64WideningConversions() {
|
||||
using O = ShaderOpcode;
|
||||
TestCase test;
|
||||
test.name = "VectorF64WideningConversions";
|
||||
const auto store_pair = [&](u32 reg, uint64_t bits) {
|
||||
AppendStoreVgpr(&test.code, reg, static_cast<u32>(test.expected.size()));
|
||||
AppendStoreVgpr(&test.code, reg + 1,
|
||||
static_cast<u32>(test.expected.size() + 1));
|
||||
test.expected.insert(test.expected.end(), {static_cast<u32>(bits),
|
||||
static_cast<u32>(bits >> 32)});
|
||||
};
|
||||
for (const u32 bits :
|
||||
{0u, 0x80000000u, 0x3f800001u, 0xbf800001u, 0x00800000u, 0x7f7fffffu,
|
||||
0xff7fffffu, 0x7f800000u, 0xff800000u, 1u, 0x80000001u}) {
|
||||
AppendSMovLiteral(&test.code, 8, bits);
|
||||
test.code.push_back(0x7e002008u);
|
||||
const auto source = (bits & 0x7f800000u) == 0 ? bits & 0x80000000u : bits;
|
||||
store_pair(0, std::bit_cast<uint64_t>(
|
||||
static_cast<double>(std::bit_cast<float>(source))));
|
||||
}
|
||||
for (const u32 bits : {0x7fc12345u, 0x7f812345u}) {
|
||||
AppendSMovLiteral(&test.code, 8, bits);
|
||||
test.code.push_back(0x7e002008u);
|
||||
AppendVMovLiteral(&test.code, 3, 0x7ff80000u);
|
||||
test.code.push_back(EncodeVop2(0x1b, 1, Vgpr(3), 1));
|
||||
AppendStoreVgpr(&test.code, 1, static_cast<u32>(test.expected.size()));
|
||||
test.expected.push_back(0x7ff80000u);
|
||||
}
|
||||
AppendVMovLiteral(&test.code, 1, std::bit_cast<u32>(-3.5f));
|
||||
AppendVop3(&test.code, 0x190, 1, Vgpr(1), 0, 0, 1, 0, false, 0, 1);
|
||||
store_pair(1, std::bit_cast<uint64_t>(-3.5));
|
||||
for (const u32 value :
|
||||
{0u, 1u, 16777217u, 0x7fffffffu, 0x80000000u, 0xffffffffu}) {
|
||||
AppendVMovLiteral(&test.code, 1, value);
|
||||
test.code.push_back(EncodeVop1(0x16, 1, Vgpr(1)));
|
||||
store_pair(1, std::bit_cast<uint64_t>(static_cast<double>(value)));
|
||||
}
|
||||
AppendSMovLiteral(&test.code, 8, 0xffffffffu);
|
||||
AppendVop3(&test.code, 0x196, 1, 8u, 0);
|
||||
store_pair(1, std::bit_cast<uint64_t>(4294967295.0));
|
||||
test.code.push_back(EncodeSop1(0x04, 12, 126));
|
||||
test.code.push_back(EncodeSop1(0x04, 126, InlineU32(0)));
|
||||
test.code.push_back(EncodeVop1(0x10, 1, InlineU32(0)));
|
||||
test.code.push_back(EncodeVop1(0x16, 1, InlineU32(0)));
|
||||
test.code.push_back(EncodeSop1(0x04, 126, 12));
|
||||
store_pair(1, std::bit_cast<uint64_t>(4294967295.0));
|
||||
AppendEnd(&test.code);
|
||||
test.opcodes = {O::S_MOV_B32, O::S_MOV_B64, O::V_MOV_B32,
|
||||
O::V_AND_B32, O::V_CVT_F64_F32, O::V_CVT_F64_U32,
|
||||
O::BUFFER_STORE_DWORD, O::S_ENDPGM};
|
||||
test.required_spirv = {"OpCapability Float64", "OpTypeFloat 64", "OpFConvert",
|
||||
"OpConvertUToF"};
|
||||
|
||||
std::vector<u32> vertex_code;
|
||||
AppendSMovLiteral(&vertex_code, 8, std::bit_cast<u32>(2.0f));
|
||||
vertex_code.insert(vertex_code.end(),
|
||||
{0x7e002008u, 0x7e005f00u, 0x7e401f00u});
|
||||
vertex_code.push_back(EncodeExp0(0x0c, 0xf));
|
||||
vertex_code.push_back(EncodeExp1(32, 32, 32, 32));
|
||||
AppendEnd(&vertex_code);
|
||||
ShaderVertexInputInfo vertex{};
|
||||
ShaderRecompiler::CompileOptions options;
|
||||
options.stage = ShaderType::Vertex;
|
||||
options.input_info.vertex = &vertex;
|
||||
auto translated = ShaderRecompiler::TranslateProgram(vertex_code, options);
|
||||
auto result =
|
||||
ShaderRecompiler::CompileProgram(std::move(translated), options, {});
|
||||
ValidateSpirv(test.name, result.spirv);
|
||||
Require(test.name, "captured vertex conversion chain",
|
||||
result.ir_dump.find("ConvertF64F32") != std::string::npos &&
|
||||
result.ir_dump.find("FPRecip64") != std::string::npos &&
|
||||
result.ir_dump.find("ConvertF32F64") != std::string::npos,
|
||||
"vertex compilation lost the captured widening/reciprocal/narrowing "
|
||||
"sequence");
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase VectorF64ModesModifiersAndExec() {
|
||||
using O = ShaderOpcode;
|
||||
TestCase test;
|
||||
@@ -29876,6 +29955,7 @@ std::vector<TestCase> MakeCases() {
|
||||
AddCase(VectorRcpIflagF32IntegerReciprocal);
|
||||
AddCase(VectorF64CapturedScreenSpaceShadows);
|
||||
AddCase(VectorF64ModesModifiersAndExec);
|
||||
AddCase(VectorF64WideningConversions);
|
||||
AddCase(VectorSinCosMaxFiniteSpecialCases);
|
||||
AddCase(VectorCompareF32ExposureGuard);
|
||||
for (const auto mode : {0xc0u, 0xe0u}) {
|
||||
|
||||
Reference in New Issue
Block a user