mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-29 06:16:55 +01:00
shader: support unsigned 16-bit multiply-add
This commit is contained in:
@@ -698,6 +698,8 @@ if(BUILD_TESTING)
|
|||||||
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --quadmask-only)
|
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --quadmask-only)
|
||||||
add_test(NAME shader_recompiler_integer16_ternary
|
add_test(NAME shader_recompiler_integer16_ternary
|
||||||
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --integer16-ternary-only)
|
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --integer16-ternary-only)
|
||||||
|
add_test(NAME shader_recompiler_mad_u16
|
||||||
|
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --mad-u16-only)
|
||||||
add_test(NAME shader_recompiler_alignbyte
|
add_test(NAME shader_recompiler_alignbyte
|
||||||
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --alignbyte-only)
|
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --alignbyte-only)
|
||||||
add_test(NAME graphics_pipeline_rasterization
|
add_test(NAME graphics_pipeline_rasterization
|
||||||
|
|||||||
@@ -258,6 +258,7 @@ enum class Opcode {
|
|||||||
V_CVT_PK_U8_F32,
|
V_CVT_PK_U8_F32,
|
||||||
V_MAD_F32,
|
V_MAD_F32,
|
||||||
V_MAD_I16,
|
V_MAD_I16,
|
||||||
|
V_MAD_U16,
|
||||||
V_MAD_I32_I24,
|
V_MAD_I32_I24,
|
||||||
V_MAD_U32_U24,
|
V_MAD_U32_U24,
|
||||||
V_MAD_U64_U32,
|
V_MAD_U64_U32,
|
||||||
|
|||||||
@@ -380,6 +380,7 @@ constexpr OpcodeMap VOP3_OPCODE_LIST[] = {
|
|||||||
{0x311u, Opcode::V_PACK_B32_F16},
|
{0x311u, Opcode::V_PACK_B32_F16},
|
||||||
{0x314u, Opcode::V_LSHLREV_B16},
|
{0x314u, Opcode::V_LSHLREV_B16},
|
||||||
{0x319u, Opcode::V_SUBREV_I32},
|
{0x319u, Opcode::V_SUBREV_I32},
|
||||||
|
{0x340u, Opcode::V_MAD_U16},
|
||||||
{0x345u, Opcode::V_XAD_U32},
|
{0x345u, Opcode::V_XAD_U32},
|
||||||
{0x346u, Opcode::V_LSHL_ADD_U32},
|
{0x346u, Opcode::V_LSHL_ADD_U32},
|
||||||
{0x347u, Opcode::V_ADD_LSHL_U32},
|
{0x347u, Opcode::V_ADD_LSHL_U32},
|
||||||
@@ -501,7 +502,7 @@ bool IsNativeVop3B16TernaryOpcode(Opcode opcode) {
|
|||||||
return opcode == Opcode::V_MIN3_I16 || opcode == Opcode::V_MIN3_U16 ||
|
return opcode == Opcode::V_MIN3_I16 || opcode == Opcode::V_MIN3_U16 ||
|
||||||
opcode == Opcode::V_MAX3_I16 || opcode == Opcode::V_MAX3_U16 ||
|
opcode == Opcode::V_MAX3_I16 || opcode == Opcode::V_MAX3_U16 ||
|
||||||
opcode == Opcode::V_MED3_I16 || opcode == Opcode::V_MED3_U16 ||
|
opcode == Opcode::V_MED3_I16 || opcode == Opcode::V_MED3_U16 ||
|
||||||
opcode == Opcode::V_MAD_I16;
|
opcode == Opcode::V_MAD_I16 || opcode == Opcode::V_MAD_U16;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsNativeVop3B16BinaryOpcode(Opcode opcode) {
|
bool IsNativeVop3B16BinaryOpcode(Opcode opcode) {
|
||||||
@@ -1536,9 +1537,9 @@ bool SupportsNativeVop3ResultModifiers(Opcode opcode) {
|
|||||||
|
|
||||||
bool SupportsNativeVop3Clamp(Opcode opcode) {
|
bool SupportsNativeVop3Clamp(Opcode opcode) {
|
||||||
return SupportsNativeVop3ResultModifiers(opcode) || UsesInexactClampControl(opcode) ||
|
return SupportsNativeVop3ResultModifiers(opcode) || UsesInexactClampControl(opcode) ||
|
||||||
opcode == Opcode::V_MAD_I16 || opcode == Opcode::V_MIN3_I16 ||
|
opcode == Opcode::V_MAD_I16 || opcode == Opcode::V_MAD_U16 ||
|
||||||
opcode == Opcode::V_MAX3_I16 || opcode == Opcode::V_MAX3_U16 ||
|
opcode == Opcode::V_MIN3_I16 || opcode == Opcode::V_MAX3_I16 ||
|
||||||
opcode == Opcode::V_MED3_U16;
|
opcode == Opcode::V_MAX3_U16 || opcode == Opcode::V_MED3_U16;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HasUnsupportedNativeVop3Modifiers(Opcode opcode, bool permlane, bool mad_mix,
|
bool HasUnsupportedNativeVop3Modifiers(Opcode opcode, bool permlane, bool mad_mix,
|
||||||
|
|||||||
@@ -20,12 +20,12 @@ void Translator::Integer16Binary(const Decoder::Instruction& inst, IR::ValueOpco
|
|||||||
Write16Bits(DestinationOperand(inst), result);
|
Write16Bits(DestinationOperand(inst), result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Translator::V_MAD_I16(const Decoder::Instruction& inst) {
|
void Translator::Integer16Mad(const Decoder::Instruction& inst, bool sign) {
|
||||||
const auto lhs = ReadU16AsU32(inst.src0, true);
|
const auto lhs = ReadU16AsU32(inst.src0, sign);
|
||||||
const auto rhs = ReadU16AsU32(inst.src1, true);
|
const auto rhs = ReadU16AsU32(inst.src1, sign);
|
||||||
auto result = ir.IAdd(ir.IMul(lhs, rhs), ReadU16AsU32(inst.src2, true));
|
auto result = ir.IAdd(ir.IMul(lhs, rhs), ReadU16AsU32(inst.src2, sign));
|
||||||
if (inst.dst.clamp) {
|
if (inst.dst.clamp) {
|
||||||
result = Saturate16(result, true);
|
result = Saturate16(result, sign);
|
||||||
}
|
}
|
||||||
Write16Bits(DestinationOperand(inst), result);
|
Write16Bits(DestinationOperand(inst), result);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ private:
|
|||||||
void Integer16Shift(const Decoder::Instruction& inst, IR::ValueOpcode opcode, bool arithmetic);
|
void Integer16Shift(const Decoder::Instruction& inst, IR::ValueOpcode opcode, bool arithmetic);
|
||||||
void Integer16Binary(const Decoder::Instruction& inst, IR::ValueOpcode opcode, bool sign);
|
void Integer16Binary(const Decoder::Instruction& inst, IR::ValueOpcode opcode, bool sign);
|
||||||
void Integer16Ternary(const Decoder::Instruction& inst, IR::ValueOpcode opcode, bool sign);
|
void Integer16Ternary(const Decoder::Instruction& inst, IR::ValueOpcode opcode, bool sign);
|
||||||
void V_MAD_I16(const Decoder::Instruction& inst);
|
void Integer16Mad(const Decoder::Instruction& inst, bool sign);
|
||||||
void PackedInteger16Shift(const Decoder::Instruction& inst, IR::ValueOpcode opcode,
|
void PackedInteger16Shift(const Decoder::Instruction& inst, IR::ValueOpcode opcode,
|
||||||
bool arithmetic);
|
bool arithmetic);
|
||||||
void PackedInteger16Binary(const Decoder::Instruction& inst, IR::ValueOpcode opcode);
|
void PackedInteger16Binary(const Decoder::Instruction& inst, IR::ValueOpcode opcode);
|
||||||
|
|||||||
@@ -375,7 +375,8 @@ void Translator::EmitVector(const Decoder::Instruction& inst) {
|
|||||||
case O::V_SUB_NC_U16:
|
case O::V_SUB_NC_U16:
|
||||||
case O::V_SUB_NC_I16: return Integer16Binary(inst, IR::ValueOpcode::ISub32, false);
|
case O::V_SUB_NC_I16: return Integer16Binary(inst, IR::ValueOpcode::ISub32, false);
|
||||||
case O::V_MUL_LO_U16: return Integer16Binary(inst, IR::ValueOpcode::IMul32, false);
|
case O::V_MUL_LO_U16: return Integer16Binary(inst, IR::ValueOpcode::IMul32, false);
|
||||||
case O::V_MAD_I16: return V_MAD_I16(inst);
|
case O::V_MAD_I16: return Integer16Mad(inst, true);
|
||||||
|
case O::V_MAD_U16: return Integer16Mad(inst, false);
|
||||||
case O::V_MED3_I16: return Integer16Ternary(inst, IR::ValueOpcode::SMedTri32, true);
|
case O::V_MED3_I16: return Integer16Ternary(inst, IR::ValueOpcode::SMedTri32, true);
|
||||||
case O::V_MIN3_U16: return Integer16Ternary(inst, IR::ValueOpcode::UMinTri32, false);
|
case O::V_MIN3_U16: return Integer16Ternary(inst, IR::ValueOpcode::UMinTri32, false);
|
||||||
case O::V_MIN3_I16: return Integer16Ternary(inst, IR::ValueOpcode::SMinTri32, true);
|
case O::V_MIN3_I16: return Integer16Ternary(inst, IR::ValueOpcode::SMinTri32, true);
|
||||||
|
|||||||
@@ -26752,6 +26752,60 @@ TestCase Vop3F32ClampFlushesNaN() {
|
|||||||
return test;
|
return test;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TestCase Vop3MadU16(u32 wave_size) {
|
||||||
|
using O = ShaderOpcode;
|
||||||
|
namespace D = ShaderRecompiler::Decoder;
|
||||||
|
constexpr std::array<std::array<u32, 3>, 4> inputs{{
|
||||||
|
{0xffff0002u, 0x80000003u, 0x00010004u},
|
||||||
|
{0xffffffffu, 0xffffffffu, 0xffffffffu},
|
||||||
|
{0x0000ffffu, 0x00010001u, 0x00010000u},
|
||||||
|
{0x7fff8000u, 0x80007fffu, 0x8000ffffu}}};
|
||||||
|
TestCase test;
|
||||||
|
test.name = wave_size == 32 ? "Vop3MadU16Wave32" : "Vop3MadU16Wave64";
|
||||||
|
for (const auto &input : inputs) test.initial.insert(test.initial.end(), input.begin(), input.end());
|
||||||
|
test.expected = test.initial;
|
||||||
|
auto &code = test.code;
|
||||||
|
for (u32 modifier = 0; modifier < 9; ++modifier) {
|
||||||
|
const u32 words[] = {
|
||||||
|
EncodeVop3Word0(0x340, 9, modifier < 3 ? 1u << modifier : 0),
|
||||||
|
EncodeVop3Word1(Vgpr(2), Vgpr(3), Vgpr(4), modifier >= 6 ? modifier - 5 : 0,
|
||||||
|
modifier >= 3 && modifier < 6 ? 1u << (modifier - 3) : 0)};
|
||||||
|
D::Instruction decoded;
|
||||||
|
D::DecodeInstruction(words, 0, decoded);
|
||||||
|
Require(test.name, "integer modifiers", decoded.opcode == O::UNSUPPORTED,
|
||||||
|
"unsigned 16-bit multiply-add accepted a floating-point modifier");
|
||||||
|
}
|
||||||
|
for (u32 i = 0; i < inputs.size(); ++i) {
|
||||||
|
for (u32 variant = 0; variant < 32; ++variant) {
|
||||||
|
const u32 selectors = variant & 15u;
|
||||||
|
const bool clamp = variant >= 16;
|
||||||
|
std::array<u32, 3> selected{};
|
||||||
|
for (u32 operand = 0; operand < 3; ++operand) {
|
||||||
|
AppendVMovU32(&code, 30, (i * 3 + operand) * sizeof(u32));
|
||||||
|
AppendBufferLoadDword(&code, 2 + operand, 30);
|
||||||
|
selected[operand] = (inputs[i][operand] >> (((selectors >> operand) & 1u) * 16)) & 0xffffu;
|
||||||
|
}
|
||||||
|
const u32 dst = selectors % 4 < 3 ? 2 + selectors % 4 : 9;
|
||||||
|
AppendVMovLiteral(&code, 9, 0xa5a5beefu);
|
||||||
|
AppendVop3(&code, 0x340, dst, Vgpr(2), Vgpr(3), i == 3 ? 255 : Vgpr(4), 0,
|
||||||
|
selectors, clamp);
|
||||||
|
if (i == 3) code.push_back(inputs[i][2]);
|
||||||
|
AppendStoreVgpr(&code, dst, static_cast<u32>(test.expected.size()));
|
||||||
|
const uint64_t wide = uint64_t{selected[0]} * selected[1] + selected[2];
|
||||||
|
const u32 result = clamp && wide > 0xffffu ? 0xffffu : static_cast<u32>(wide) & 0xffffu;
|
||||||
|
const u32 old = dst == 9 ? 0xa5a5beefu : inputs[i][dst - 2];
|
||||||
|
test.expected.push_back(selectors & 8u ? (old & 0xffffu) | (result << 16)
|
||||||
|
: (old & 0xffff0000u) | result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AppendEnd(&code);
|
||||||
|
test.opcodes = {O::V_MOV_B32, O::BUFFER_LOAD_DWORD, O::V_MAD_U16,
|
||||||
|
O::BUFFER_STORE_DWORD, O::S_ENDPGM};
|
||||||
|
test.compute_info.wave_size = wave_size;
|
||||||
|
test.has_compute_info = true;
|
||||||
|
return test;
|
||||||
|
}
|
||||||
|
|
||||||
TestCase Vop3MadI16CapturedSelectorsAndSaturation() {
|
TestCase Vop3MadI16CapturedSelectorsAndSaturation() {
|
||||||
using O = ShaderOpcode;
|
using O = ShaderOpcode;
|
||||||
struct MadCase {
|
struct MadCase {
|
||||||
@@ -46237,6 +46291,8 @@ std::vector<TestCase> MakeCases() {
|
|||||||
AddCase(Vop3CvtPkI16I32Captured);
|
AddCase(Vop3CvtPkI16I32Captured);
|
||||||
AddCase(Vop3CvtPk16Saturates);
|
AddCase(Vop3CvtPk16Saturates);
|
||||||
AddCase(Vop3MulLoU16CapturedAndSelectors);
|
AddCase(Vop3MulLoU16CapturedAndSelectors);
|
||||||
|
cases.push_back(Vop3MadU16(32));
|
||||||
|
cases.push_back(Vop3MadU16(64));
|
||||||
AddCase(Vop3MadI16CapturedSelectorsAndSaturation);
|
AddCase(Vop3MadI16CapturedSelectorsAndSaturation);
|
||||||
AddCase(Vop3F32ClampFlushesNaN);
|
AddCase(Vop3F32ClampFlushesNaN);
|
||||||
cases.push_back(Vop3Integer16Ternary(32));
|
cases.push_back(Vop3Integer16Ternary(32));
|
||||||
@@ -52671,6 +52727,13 @@ int main(int argc, char **argv) {
|
|||||||
std::setvbuf(stdout, nullptr, _IONBF, 0);
|
std::setvbuf(stdout, nullptr, _IONBF, 0);
|
||||||
EnsureConfigInitialized();
|
EnsureConfigInitialized();
|
||||||
CheckLeastRecentlyUsedCacheOrdering();
|
CheckLeastRecentlyUsedCacheOrdering();
|
||||||
|
if (argc == 2 && std::strcmp(argv[1], "--mad-u16-only") == 0) {
|
||||||
|
VulkanHarness vulkan;
|
||||||
|
RunCase(&vulkan, Vop3MadU16(32));
|
||||||
|
RunCase(&vulkan, Vop3MadU16(64));
|
||||||
|
RunCase(&vulkan, Vop3MadI16CapturedSelectorsAndSaturation());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
if (argc == 2 && std::strcmp(argv[1], "--integer16-ternary-only") == 0) {
|
if (argc == 2 && std::strcmp(argv[1], "--integer16-ternary-only") == 0) {
|
||||||
VulkanHarness vulkan;
|
VulkanHarness vulkan;
|
||||||
RunCase(&vulkan, Vop3Integer16Ternary(32));
|
RunCase(&vulkan, Vop3Integer16Ternary(32));
|
||||||
|
|||||||
Reference in New Issue
Block a user