From e58e2f060532dcba99ec4acedb28dc265cc60fc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Franch?= Date: Sun, 4 Oct 2026 11:45:59 +0200 Subject: [PATCH] feat(shader): accept float modifiers on v_cndmask_b32, v_mul_legacy_f32 and v_ldexp_f16 - v_cndmask_b32: neg/abs on DPP; neg/abs applied on the bits, as the hardware does - v_mul_legacy_f32: neg/abs on DPP and SDWA, clamp/omod on SDWA and VOP3 - v_ldexp_f16: neg/abs on src0 for VOP3, DPP and SDWA; NaN result chosen on the bits Co-Authored-By: Claude --- .../tests/execution/Vop2ModifierForms.cpp | 192 ++++++++++++++++++ .../RdnaDecoder/src/RdnaVectorOpDecoder.cpp | 29 ++- .../Translation/src/FloatInstructions.cpp | 9 +- .../Translation/src/IntegerInstructions.cpp | 14 +- 4 files changed, 225 insertions(+), 19 deletions(-) create mode 100644 core/libs/prx/libSceAgcDriver/tests/execution/Vop2ModifierForms.cpp diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/Vop2ModifierForms.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/Vop2ModifierForms.cpp new file mode 100644 index 000000000..9b07c7f68 --- /dev/null +++ b/core/libs/prx/libSceAgcDriver/tests/execution/Vop2ModifierForms.cpp @@ -0,0 +1,192 @@ +#include "prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp" +#include "prx/libSceAgcDriver/Graphics/include/Draw.hpp" +#include "Recompiler.hpp" +#include "VulkanTestDevice.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + +using AgcDriver::Graphics::Require; +using ShaderRecompiler::ShaderStage; + +constexpr std::uint32_t Threads = 32; +constexpr std::uint32_t Inputs = 4; +constexpr std::uint32_t Results = 16; +alignas(256) std::array Input{}; +alignas(256) std::array Output{}; + +alignas(256) constexpr std::array Code{ + 0x34020084, 0x34060086, 0xe0381000, 0x80000401, 0xbf8c3f70, 0x7d8a0c80, 0x7e1402ff, 0xdeadbeef, + 0x7e1602ff, 0xdeadbeef, 0x7e1802ff, 0xdeadbeef, 0x7e1a02ff, 0xdeadbeef, 0x7e1c02ff, 0xdeadbeef, + 0x7e1e02ff, 0xdeadbeef, 0x7e2002ff, 0xdeadbeef, 0x7e2202ff, 0xdeadbeef, 0x7e2402ff, 0xdeadbeef, + 0x7e2602ff, 0xdeadbeef, 0x7e2802ff, 0xdeadbeef, 0x7e2a02ff, 0xdeadbeef, 0x7e2c02ff, 0xdeadbeef, + 0x7e2e02ff, 0xdeadbeef, 0x7e3002ff, 0xdeadbeef, 0x7e3202ff, 0xdeadbeef, 0x02140afa, 0xff10e404, + 0x02160afa, 0xffe0e404, 0xd501020c, 0x21aa0b04, 0x021a0af9, 0x16360604, 0x0e1c0afa, 0xff90e404, + 0x0e1e0af9, 0x16360604, 0x0e200af9, 0x06062604, 0x0e220af9, 0x16064604, 0xd5078012, 0x1a020b04, + 0xd5070013, 0x32020b04, 0xd5078314, 0x42020b04, 0xd53b0015, 0x22020b04, 0x762c0afa, 0xff20e404, + 0x762e0af9, 0x06360604, 0xd53b0118, 0x02020b04, 0xe0781000, 0x80010a03, 0xe0781010, 0x80010e03, + 0xe0781020, 0x80011203, 0xe0781030, 0x80011603, 0xbf810000, +}; + +constexpr std::uint32_t Rows[32][4] = { + {0x00000000u, 0x00000000u, 0x00000001u, 0x00000000u}, + {0x00000000u, 0x7f800000u, 0x00000001u, 0x00000000u}, + {0x3fc00000u, 0x3fc00000u, 0x00000001u, 0x00000000u}, + {0x3fc00000u, 0x7f800000u, 0x00000000u, 0x00000000u}, + {0xbf800000u, 0x3f800000u, 0x00000001u, 0x00000000u}, + {0x3fc00000u, 0x00000000u, 0x00000000u, 0x00000000u}, + {0xc0000000u, 0xabcdbc00u, 0x00000000u, 0x00000000u}, + {0x3fc00000u, 0xff800000u, 0x00000000u, 0x00000000u}, + {0xc0000000u, 0xff61b1e6u, 0x00000000u, 0x00000000u}, + {0x3e800000u, 0xc0000000u, 0x00000001u, 0x00000000u}, + {0x3e800000u, 0xbf800000u, 0x00000001u, 0x00000000u}, + {0x3f800000u, 0xbf800000u, 0x00000000u, 0x00000000u}, + {0xc0000000u, 0x7f800000u, 0x00000001u, 0x00000000u}, + {0x3e800000u, 0x3f800000u, 0x00000000u, 0x00000000u}, + {0x00000000u, 0x80000000u, 0x00000000u, 0x00000000u}, + {0x00000000u, 0x3fc00000u, 0x00000001u, 0x00000000u}, + {0x807fffffu, 0x00000000u, 0x00000001u, 0x00000000u}, + {0x3f19999au, 0x00000001u, 0x00000001u, 0x00000000u}, + {0x3f19999au, 0x00003555u, 0x00000000u, 0x00000000u}, + {0x3f19999au, 0xffffffe2u, 0x00000001u, 0x00000000u}, + {0x00003c00u, 0x00000000u, 0x00000001u, 0x00000000u}, + {0x00003c00u, 0x00003c00u, 0x00000000u, 0x00000000u}, + {0x00007e01u, 0x00000000u, 0x00000000u, 0x00000000u}, + {0x00000000u, 0x807fffffu, 0x00000001u, 0x00000000u}, + {0x00000000u, 0x7fc00001u, 0x00000001u, 0x00000000u}, + {0x80000000u, 0x00000000u, 0x00000000u, 0x00000000u}, + {0x80000000u, 0xc0000000u, 0x00000001u, 0x00000000u}, + {0x80000000u, 0x00000001u, 0x00000000u, 0x00000000u}, + {0x80000000u, 0x807fffffu, 0x00000000u, 0x00000000u}, + {0x80000000u, 0x7fc00001u, 0x00000001u, 0x00000000u}, + {0x80000000u, 0xffc00000u, 0x00000000u, 0x00000000u}, + {0x80000000u, 0x00007e01u, 0x00000001u, 0x00000000u} +}; +constexpr std::uint32_t Expected[32][15] = { + {0x00000000u, 0x80000000u, 0x00000000u, 0x80000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0xdead8000u, 0xdead0000u, 0x00008000u, 0xdead0000u}, + {0x7f800000u, 0xff800000u, 0x7f800000u, 0xff800000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0xdead8000u, 0xdead0000u, 0x00008000u, 0xdead0000u}, + {0x3fc00000u, 0xbfc00000u, 0x3fc00000u, 0xbfc00000u, 0xc0100000u, 0x40100000u, 0x3f800000u, 0xc0900000u, 0x3f800000u, 0xc1100000u, 0x00000000u, 0xdead8000u, 0xdead0000u, 0x00008000u, 0xdead0000u}, + {0xbfc00000u, 0x3fc00000u, 0xbfc00000u, 0xbfc00000u, 0xff800000u, 0x7f800000u, 0x3f800000u, 0xff800000u, 0x3f800000u, 0xff800000u, 0x00000000u, 0xdead8000u, 0xdead0000u, 0x00008000u, 0xdead0000u}, + {0x3f800000u, 0xbf800000u, 0x3f800000u, 0xbf800000u, 0x3f800000u, 0x3f800000u, 0x00000000u, 0x40000000u, 0x00000000u, 0x40800000u, 0x00000000u, 0xdead8000u, 0xdead0000u, 0x00008000u, 0xdead0000u}, + {0xbfc00000u, 0x3fc00000u, 0xbfc00000u, 0xbfc00000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0xdead8000u, 0xdead0000u, 0x00008000u, 0xdead0000u}, + {0x40000000u, 0x40000000u, 0x40000000u, 0xc0000000u, 0x2c4dbc00u, 0xac4dbc00u, 0x2c4dbc00u, 0xaccdbc00u, 0x2bcdbc00u, 0xad4dbc00u, 0x00000000u, 0xdead8000u, 0xdead0000u, 0x00008000u, 0xdead0000u}, + {0xbfc00000u, 0x3fc00000u, 0xbfc00000u, 0xbfc00000u, 0xff800000u, 0xff800000u, 0x00000000u, 0x7f800000u, 0x00000000u, 0x7f800000u, 0x00000000u, 0xdead8000u, 0xdead0000u, 0x00008000u, 0xdead0000u}, + {0x40000000u, 0x40000000u, 0x40000000u, 0xc0000000u, 0x7f800000u, 0xff800000u, 0x3f800000u, 0xff800000u, 0x3f800000u, 0xff800000u, 0x00000000u, 0xdead8000u, 0xdead0000u, 0x00008000u, 0xdead0000u}, + {0xc0000000u, 0xc0000000u, 0x40000000u, 0x40000000u, 0xbf000000u, 0xbf000000u, 0x00000000u, 0x3f800000u, 0x00000000u, 0x40000000u, 0x00000000u, 0xdead8000u, 0xdead0000u, 0x00008000u, 0xdead0000u}, + {0xbf800000u, 0xbf800000u, 0x3f800000u, 0x3f800000u, 0xbe800000u, 0xbe800000u, 0x00000000u, 0x3f000000u, 0x00000000u, 0x3f800000u, 0x00000000u, 0xdead8000u, 0xdead0000u, 0x00008000u, 0xdead0000u}, + {0xbf800000u, 0x3f800000u, 0xbf800000u, 0xbf800000u, 0xbf800000u, 0xbf800000u, 0x00000000u, 0x40000000u, 0x00000000u, 0x40800000u, 0x00000000u, 0xdead8000u, 0xdead0000u, 0x00008000u, 0xdead0000u}, + {0x7f800000u, 0xff800000u, 0x7f800000u, 0xff800000u, 0x7f800000u, 0x7f800000u, 0x00000000u, 0x7f800000u, 0x00000000u, 0x7f800000u, 0x00000000u, 0xdead8000u, 0xdead0000u, 0x00008000u, 0xdead0000u}, + {0xbe800000u, 0x3e800000u, 0xbe800000u, 0xbe800000u, 0xbe800000u, 0x3e800000u, 0x3e800000u, 0xbf000000u, 0x3e000000u, 0xbf800000u, 0x00000000u, 0xdead8000u, 0xdead0000u, 0x00008000u, 0xdead0000u}, + {0x80000000u, 0x00000000u, 0x80000000u, 0x80000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0xdead8000u, 0xdead0000u, 0x00008000u, 0xdead0000u}, + {0x3fc00000u, 0xbfc00000u, 0x3fc00000u, 0xbfc00000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0xdead8000u, 0xdead0000u, 0x00008000u, 0xdead0000u}, + {0x00000000u, 0x80000000u, 0x00000000u, 0x80000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0xdead7fffu, 0xdead7fffu, 0x0000ffffu, 0xdead7fffu}, + {0x00000001u, 0x80000001u, 0x00000001u, 0x80000001u, 0x80000001u, 0x00000001u, 0x00000001u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0xdead1d9au, 0xdead1d9au, 0x00009d9au, 0xdead1d9au}, + {0xbf19999au, 0x3f19999au, 0xbf19999au, 0xbf19999au, 0x80002000u, 0x00002000u, 0x00002000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0xdead7c00u, 0xdead7c00u, 0x0000fc00u, 0xdead7c00u}, + {0xffffffe2u, 0xffffffe2u, 0x7fffffe2u, 0x7fffffe2u, 0x7fffffe2u, 0x7fffffe2u, 0x00000000u, 0x7fffffe2u, 0x00000000u, 0xffffffe2u, 0x00000000u, 0xdead0000u, 0xdead0000u, 0x00008000u, 0xdead0000u}, + {0x00000000u, 0x80000000u, 0x00000000u, 0x80000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0xdeadbc00u, 0xdead3c00u, 0x0000bc00u, 0xdead3c00u}, + {0x80003c00u, 0x00003c00u, 0x80003c00u, 0x80003c00u, 0x80000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0xdeadfc00u, 0xdead7c00u, 0x0000fc00u, 0xdead7c00u}, + {0x80007e01u, 0x00007e01u, 0x80007e01u, 0x80007e01u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0xdeadfe01u, 0xdead7e01u, 0x0000fe01u, 0xdead7e01u}, + {0x807fffffu, 0x807fffffu, 0x007fffffu, 0x007fffffu, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0xdead8000u, 0xdead0000u, 0x00008000u, 0xdead0000u}, + {0x7fc00001u, 0xffc00001u, 0x7fc00001u, 0xffc00001u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0xdead8000u, 0xdead0000u, 0x00008000u, 0xdead0000u}, + {0x00000000u, 0x00000000u, 0x00000000u, 0x80000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0xdead8000u, 0xdead0000u, 0x00008000u, 0xdead0000u}, + {0xc0000000u, 0xc0000000u, 0x40000000u, 0x40000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0xdead8000u, 0xdead0000u, 0x00008000u, 0xdead0000u}, + {0x00000000u, 0x00000000u, 0x00000000u, 0x80000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0xdead8000u, 0xdead0000u, 0x00008000u, 0xdead0000u}, + {0x00000000u, 0x00000000u, 0x00000000u, 0x80000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0xdead8000u, 0xdead0000u, 0x00008000u, 0xdead0000u}, + {0x7fc00001u, 0xffc00001u, 0x7fc00001u, 0xffc00001u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0xdead8000u, 0xdead0000u, 0x00008000u, 0xdead0000u}, + {0x00000000u, 0x00000000u, 0x00000000u, 0x80000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0xdead8000u, 0xdead0000u, 0x00008000u, 0xdead0000u}, + {0x00007e01u, 0x80007e01u, 0x00007e01u, 0x80007e01u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0xdead8000u, 0xdead0000u, 0x00008000u, 0xdead0000u} +}; +constexpr std::uint32_t Checked[32] = {0x7fffu, 0x7fffu, 0x7fffu, 0x7fffu, 0x7fffu, 0x7fffu, 0x7fffu, 0x7fffu, 0x7fffu, 0x7fffu, 0x7fffu, 0x7fffu, 0x7fffu, 0x7fffu, 0x7fffu, 0x7fffu, 0x780fu, 0x780fu, 0x780fu, 0x780fu, 0x780fu, 0x780fu, 0x780fu, 0x780fu, 0x780fu, 0x7fffu, 0x7fffu, 0x780fu, 0x780fu, 0x780fu, 0x780fu, 0x780fu}; +constexpr const char* Names[15] = { + "v_cndmask_b32_dpp v10, -v4, v5, vcc_lo", + "v_cndmask_b32_dpp v11, |v4|, -|v5|, vcc_lo", + "v_cndmask_b32_e64 v12, -v4, |v5|, vcc_lo", + "v_cndmask_b32_sdwa v13, -|v4|, -v5, vcc_lo", + "v_mul_legacy_f32_dpp v14, -v4, |v5|", + "v_mul_legacy_f32_sdwa v15, -|v4|, -v5", + "v_mul_legacy_f32_sdwa v16, v4, v5 clamp", + "v_mul_legacy_f32_sdwa v17, v4, -v5 mul:2", + "v_mul_legacy_f32_e64 v18, v4, v5 clamp div:2", + "v_mul_legacy_f32_e64 v19, -v4, v5 mul:4", + "v_mul_legacy_f32_e64 v20, |v4|, -|v5| clamp", + "v_ldexp_f16_e64 v21, -v4, v5", + "v_ldexp_f16_dpp v22, |v4|, v5", + "v_ldexp_f16_sdwa v23, -|v4|, v5", + "v_ldexp_f16_e64 v24, |v4|, v5", +}; + +void Fill(std::uint32_t tid, std::uint32_t* words) { + std::copy(std::begin(Rows[tid]), std::end(Rows[tid]), words); +} + +std::array BufferDescriptor(const void* data, std::uint32_t bytes) { + const auto address = reinterpret_cast(data); + return {static_cast(address), static_cast((address >> 32u) & 0xffffu), bytes, 0x01016facu}; +} + +std::string Hex(std::uint32_t value) { + char text[16]; + std::snprintf(text, sizeof(text), "0x%08x", value); + return text; +} + +void Expect(std::uint32_t tid, std::uint32_t actual, std::uint32_t expected, const char* name) { + Require(actual == expected, std::string("vop2 modifier forms: lane ") + std::to_string(tid) + " " + name + " is " + Hex(actual) + ", expected " + Hex(expected)); +} + +void Run(AgcDriver::VulkanDevice& device) { + for (std::uint32_t tid = 0; tid < Threads; ++tid) Fill(tid, &Input[tid * Inputs]); + Output.fill(0xdeadbeefu); + std::vector userData(8, 0u); + const auto input = BufferDescriptor(Input.data(), static_cast(Input.size() * 4u)); + const auto output = BufferDescriptor(Output.data(), static_cast(Output.size() * 4u)); + std::copy(input.begin(), input.end(), userData.begin()); + std::copy(output.begin(), output.end(), userData.begin() + 4); + const std::span code(Code); + const std::array memory{{{reinterpret_cast(code.data()), std::as_bytes(code)}}}; + const ShaderRecompiler::ShaderComputeStageInfo compute{{Threads, 1, 1}, 0u, {false, false, false}, false, 1}; + ShaderRecompiler::RecompileRequest request{ + {ShaderStage::Compute, reinterpret_cast(code.data()), code, 0, {}}, + {32, 0, userData, compute, std::nullopt, std::nullopt, memory}, + device.Target(), + {0, 0, 0, 128} + }; + request.useCache = false; + const auto result = ShaderRecompiler::Recompile(request); + device.Dispatch(result, 1, 1, 1, {}, reinterpret_cast(code.data())); + device.WaitIdle(); +} + +void Check() { + for (std::uint32_t tid = 0; tid < Threads; ++tid) { + const std::uint32_t* in = &Input[tid * Inputs]; + const std::uint32_t* out = &Output[tid * Results]; + for (std::uint32_t i = 0; i < 15; ++i) { + if ((Checked[tid] >> i) & 1u) Expect(tid, out[i], Expected[tid][i], Names[i]); + } + } +} + +} + +int main() { + try { + const auto device = OpenVulkanTestDevice(); + if (!device) return VulkanTestSkipped; + Run(*device); + Check(); + std::puts("vop2 modifier forms tests passed"); + return 0; + } catch (const std::exception& error) { + std::cerr << error.what() << '\n'; + return 1; + } +} diff --git a/core/shader/recompiler/RdnaDecoder/src/RdnaVectorOpDecoder.cpp b/core/shader/recompiler/RdnaDecoder/src/RdnaVectorOpDecoder.cpp index 3cf02f268..df2e1cc2f 100644 --- a/core/shader/recompiler/RdnaDecoder/src/RdnaVectorOpDecoder.cpp +++ b/core/shader/recompiler/RdnaDecoder/src/RdnaVectorOpDecoder.cpp @@ -1026,11 +1026,22 @@ bool isVop2FloatOpcode(RdnaOpcode opcode) { case RdnaOpcode::VFmamkF16: case RdnaOpcode::VFmaakF16: case RdnaOpcode::VMaxF16: - case RdnaOpcode::VMinF16: return true; + case RdnaOpcode::VMinF16: + case RdnaOpcode::VMulLegacyF32: return true; default: return false; } } +bool hasUnsupportedVop2SourceModifiers(RdnaOpcode opcode, bool src0Modified, bool src1Modified) { + if (isVop2FloatOpcode(opcode) || opcode == RdnaOpcode::VCndmaskB32) { + return false; + } + if (opcode == RdnaOpcode::VLdexpF16) { + return src1Modified; + } + return src0Modified || src1Modified; +} + bool usesInexactClampControl(RdnaOpcode opcode) { return opcode == RdnaOpcode::VCvtU32F32 || opcode == RdnaOpcode::VCvtI32F32 || opcode == RdnaOpcode::VCvtU16F16 || opcode == RdnaOpcode::VCvtI16F16; @@ -1329,8 +1340,7 @@ void validateVop2Sdwa(const RdnaInstruction& instruction, std::uint32_t opcode, throw std::invalid_argument("VOP2 SDWA output modifiers are not supported"); } if (isFullWidthVop2Sdwa(fields)) { - if (!isVop2FloatOpcode(instruction.op) && instruction.op != RdnaOpcode::VCndmaskB32 && - (fields.src0Neg != 0u || fields.src0Abs != 0u || fields.src1Neg != 0u || fields.src1Abs != 0u)) { + if (hasUnsupportedVop2SourceModifiers(instruction.op, fields.src0Neg != 0u || fields.src0Abs != 0u, fields.src1Neg != 0u || fields.src1Abs != 0u)) { throw std::invalid_argument("VOP2 SDWA source modifiers are not supported"); } return; @@ -1440,8 +1450,7 @@ void decodeVop2Dpp(std::uint32_t programCounter, std::span instruction.source1.negateHi = instruction.source1.negate; } - if (!isVop2FloatOpcode(instruction.op) && !packedFmac && - (instruction.source0.negate || instruction.source0.absolute || instruction.source1.negate || instruction.source1.absolute)) { + if (!packedFmac && hasUnsupportedVop2SourceModifiers(instruction.op, instruction.source0.negate || instruction.source0.absolute, instruction.source1.negate || instruction.source1.absolute)) { throw std::invalid_argument("VOP2 DPP integer source modifiers are not supported"); } finalizeVop2Instruction(code, wordIndex, instruction); @@ -1863,7 +1872,8 @@ bool supportsNativeVop3SourceModifiers(RdnaOpcode opcode) { case RdnaOpcode::VMinF64: case RdnaOpcode::VMaxF64: case RdnaOpcode::VLdexpF64: - case RdnaOpcode::VTrigPreopF64: return true; + case RdnaOpcode::VTrigPreopF64: + case RdnaOpcode::VLdexpF16: return true; default: return false; } } @@ -1877,6 +1887,7 @@ bool supportsNativeVop3ResultModifiers(RdnaOpcode opcode) { case RdnaOpcode::VSubF32: case RdnaOpcode::VSubrevF32: case RdnaOpcode::VMulF32: + case RdnaOpcode::VMulLegacyF32: case RdnaOpcode::VMinF32: case RdnaOpcode::VMaxF32: case RdnaOpcode::VMacF32: @@ -1947,6 +1958,12 @@ void checkNativeVop3Modifiers(RdnaOpcode opcode, bool permlane, bool carryInOut, } return; } + if (opcode == RdnaOpcode::VLdexpF16) { + if ((abs & ~1u) != 0u || opSel != 0u || clamp != 0u || omod != 0u || (neg & ~1u) != 0u) { + throw std::invalid_argument("VOP3 source modifiers are not implemented"); + } + return; + } if (opcode == RdnaOpcode::VCndmaskB32) { if ((abs & ~0x3u) != 0u || opSel != 0u || clamp != 0u || omod != 0u || (neg & ~0x3u) != 0u) { throw std::invalid_argument("VOP3 source modifiers are not implemented"); diff --git a/core/shader/recompiler/Translation/src/FloatInstructions.cpp b/core/shader/recompiler/Translation/src/FloatInstructions.cpp index 079497ea8..5513e8198 100644 --- a/core/shader/recompiler/Translation/src/FloatInstructions.cpp +++ b/core/shader/recompiler/Translation/src/FloatInstructions.cpp @@ -173,11 +173,16 @@ IrU32 TranslationContext::normF16(IrU32 bits, bool signedValue) { } bool TranslationContext::vLdexpF16(const RdnaInstruction& inst) { - const IrF32 value = readF16AsF32(sourceAt(inst, 0u)); + const IrU32 bits = readF16Bits(sourceAt(inst, 0u)); + const IrF16 half(ir.Emit(IrOpcode::BitCastF16U16, IrType::F16, {&ir.Emit(IrOpcode::ConvertU16U32, IrType::U16, {&bits.Value()})})); + const IrF32 value(ir.Emit(IrOpcode::ConvertF32F16, IrType::F32, {&half.Value()})); const IrU32 exponent(ir.Emit(IrOpcode::BitFieldSExtract, IrType::U32, {&readU32(sourceAt(inst, 1u)).Value(), &ir.Constant(0u), &ir.Constant(16u)})); const IrU32 clamped(ir.Emit(IrOpcode::SMax32, IrType::U32, {&ir.Emit(IrOpcode::SMin32, IrType::U32, {&exponent.Value(), &ir.Constant(64u)}), &ir.Constant(static_cast(-64))})); IrValue& power = ir.BitCastF32(ir.ShiftLeftLogical(ir.IAdd(clamped.Value(), ir.Constant(127u)), ir.Constant(23u))); - writeF16(inst.destination, IrF32(ir.Emit(IrOpcode::FPMul32, IrType::F32, {&value.Value(), &power}))); + const IrF16 scaled(ir.Emit(IrOpcode::ConvertF16F32, IrType::F16, {&ir.Emit(IrOpcode::FPMul32, IrType::F32, {&value.Value(), &power})})); + const IrU32 result(ir.Emit(IrOpcode::ConvertU32U16, IrType::U32, {&ir.Emit(IrOpcode::BitCastU16F16, IrType::U16, {&scaled.Value()})})); + const IrU1 nan(ir.UGreaterThan(ir.BitwiseAnd(bits.Value(), ir.Constant(0x7fffu)), ir.Constant(0x7c00u))); + write16Bits(inst.destination, IrU32(ir.Select(nan.Value(), ir.BitwiseOr(bits.Value(), ir.Constant(0x200u)), result.Value()))); return true; } diff --git a/core/shader/recompiler/Translation/src/IntegerInstructions.cpp b/core/shader/recompiler/Translation/src/IntegerInstructions.cpp index 81974fcbe..447f28c23 100644 --- a/core/shader/recompiler/Translation/src/IntegerInstructions.cpp +++ b/core/shader/recompiler/Translation/src/IntegerInstructions.cpp @@ -802,17 +802,9 @@ bool TranslationContext::vCndmaskB32(const RdnaInstruction& inst) { const IrU1 condition = readMask(maskOperand); const RdnaOperand& falseOperand = sourceAt(inst, 0u); const RdnaOperand& trueOperand = sourceAt(inst, 1u); - IrValue* result = nullptr; - if (falseOperand.negate || falseOperand.absolute || trueOperand.negate || trueOperand.absolute) { - IrValue* falseValue = readOperand(falseOperand, IrType::F32); - IrValue* trueValue = readOperand(trueOperand, IrType::F32); - result = &ir.Emit(IrOpcode::SelectF32, IrType::F32, {&condition.Value(), trueValue, falseValue}); - } else { - const IrU32 falseValue = readU32(falseOperand); - const IrU32 trueValue = readU32(trueOperand); - result = &ir.Select(condition.Value(), trueValue.Value(), falseValue.Value()); - } - writeOperand(inst.destination, result); + const IrU32 falseValue = readU32(falseOperand); + const IrU32 trueValue = readU32(trueOperand); + writeOperand(inst.destination, &ir.Select(condition.Value(), trueValue.Value(), falseValue.Value())); return true; }