diff --git a/core/libs/prx/libSceAgcDriver/CMakeLists.txt b/core/libs/prx/libSceAgcDriver/CMakeLists.txt index 7173257e7..da61330ed 100644 --- a/core/libs/prx/libSceAgcDriver/CMakeLists.txt +++ b/core/libs/prx/libSceAgcDriver/CMakeLists.txt @@ -910,12 +910,14 @@ if(BUILD_TESTING) endif() endforeach() endforeach() - foreach(preparedMode IN ITEMS indirect uncached debug debug_failure deferred) + foreach(preparedMode IN ITEMS indirect uncached debug debug_failure deferred deferred_undecodable) set(preparedTest "agc_driver_prepared_shaders_${preparedMode}") if(preparedMode STREQUAL "indirect") add_test(NAME ${preparedTest} COMMAND agc_driver_prepared_shaders_tests --indirect) elseif(preparedMode STREQUAL "deferred") add_test(NAME ${preparedTest} COMMAND agc_driver_prepared_shaders_tests --deferred) + elseif(preparedMode STREQUAL "deferred_undecodable") + add_test(NAME ${preparedTest} COMMAND agc_driver_prepared_shaders_tests --deferred-undecodable) elseif(preparedMode STREQUAL "debug_failure") add_test(NAME ${preparedTest} COMMAND agc_driver_prepared_shaders_tests --fail-before-registration) set_tests_properties(${preparedTest} PROPERTIES WILL_FAIL TRUE TIMEOUT 30) diff --git a/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp b/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp index e3e92b1a5..585355b3b 100644 --- a/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp +++ b/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp @@ -368,7 +368,13 @@ std::vector PrepareRegistered(const ShaderSnapshot& snap if (address < snapshot.codeAddress || address - snapshot.codeAddress >= snapshot.code.size() * 4u) throw std::runtime_error("AGC driver: registered entry point is outside shader code"); const auto codeOffset = static_cast((address - snapshot.codeAddress) / 4u); const auto code = std::span(snapshot.code).subspan(codeOffset); - const auto decoded = ShaderRecompiler::RdnaInstructionDecoder{}.Decode(code); + ShaderRecompiler::RdnaProgram decoded; + try { + decoded = ShaderRecompiler::RdnaInstructionDecoder{}.Decode(code); + } catch (const ShaderRecompiler::UnsupportedInstructionError& error) { + if (deferred == nullptr) throw; + return defer(error.what()); + } std::optional compute; std::optional pixel; std::optional vertex; diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/PreparedShaders.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/PreparedShaders.cpp index 47c49c205..883ce2786 100644 --- a/core/libs/prx/libSceAgcDriver/tests/execution/PreparedShaders.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/execution/PreparedShaders.cpp @@ -362,17 +362,50 @@ void DeferredRegistration() { ExpectFailure([] { AgcDriverShutdown_nid_postfix(); }, "not statically resolvable"); } +void DeferredUndecodableRegistration() { + alignas(256) std::array code{0xbe842104u, 0xf1949f05u, 0x00060018u}; + struct Header { + Shader shader{}; + std::array registers{}; + ShaderSpecialRegs specials{}; + } header; + const auto address = reinterpret_cast(code.data()); + header.shader.file_header = 0x34333231u; + header.shader.version = 0x18; + header.shader.header_size = sizeof(header); + header.shader.shader_size = sizeof(code); + header.shader.code = code.data(); + header.shader.sh_registers = header.registers.data(); + header.shader.num_sh_registers = header.registers.size(); + header.shader.specials = &header.specials; + header.specials.dispatch_modifier = 0x8000; + header.registers = {{{0x20c, static_cast(address >> 8u)}, {0x20d, static_cast(address >> 40u)}, {0x207, 1}, {0x208, 1}, {0x209, 1}, {0x212, 0}, {0x213, 12}, {0x207, 1}}}; + AgcDriverRegisterShader_nid_postfix(&header.shader); + std::vector commands; + for (const auto reg : header.registers) commands.insert(commands.end(), {0xc0017600u, reg.offset, reg.value}); + commands.insert(commands.end(), {0xc0031500u, 1, 1, 1, 0x8041}); + Packet packet{commands.data(), static_cast(commands.size()), 0, {}}; + sceAgcDriverSubmitAcb(0x20, &packet); + ExpectFailure([] { AgcDriverWaitIdle_nid_postfix(); }, "unsupported MIMG opcode"); + ExpectFailure([] { AgcDriverShutdown_nid_postfix(); }, "unsupported MIMG opcode"); +} + } int main(int argc, char** argv) { try { - Require(argc == 1 || (argc == 2 && (std::string_view(argv[1]) == "--indirect" || std::string_view(argv[1]) == "--fail-before-registration" || std::string_view(argv[1]) == "--deferred")), "invalid test arguments"); + Require(argc == 1 || (argc == 2 && (std::string_view(argv[1]) == "--indirect" || std::string_view(argv[1]) == "--fail-before-registration" || std::string_view(argv[1]) == "--deferred" || std::string_view(argv[1]) == "--deferred-undecodable")), "invalid test arguments"); if (argc == 2 && std::string_view(argv[1]) == "--deferred") { if (!OpenVulkanTestDevice()) return VulkanTestSkipped; DeferredRegistration(); std::cout << "deferred shader preparation tests passed\n"; return 0; } + if (argc == 2 && std::string_view(argv[1]) == "--deferred-undecodable") { + DeferredUndecodableRegistration(); + std::cout << "deferred undecodable shader tests passed\n"; + return 0; + } auto device = OpenVulkanTestDevice(); if (!device) return VulkanTestSkipped; Run(*device); diff --git a/core/shader/recompiler/RdnaDecoder/include/RdnaDecoder/RdnaInstruction.hpp b/core/shader/recompiler/RdnaDecoder/include/RdnaDecoder/RdnaInstruction.hpp index 1c7598815..f038fdc0f 100644 --- a/core/shader/recompiler/RdnaDecoder/include/RdnaDecoder/RdnaInstruction.hpp +++ b/core/shader/recompiler/RdnaDecoder/include/RdnaDecoder/RdnaInstruction.hpp @@ -4,10 +4,16 @@ #include "RdnaDecoder/RdnaOpcode.hpp" #include #include +#include #include namespace ShaderRecompiler { +class UnsupportedInstructionError : public std::runtime_error { +public: + using std::runtime_error::runtime_error; +}; + enum class RdnaInstructionFamily { Unknown, SOP1, diff --git a/core/shader/recompiler/RdnaDecoder/src/RdnaImageOpDecoder.cpp b/core/shader/recompiler/RdnaDecoder/src/RdnaImageOpDecoder.cpp index 82d125695..0ab4c4645 100644 --- a/core/shader/recompiler/RdnaDecoder/src/RdnaImageOpDecoder.cpp +++ b/core/shader/recompiler/RdnaDecoder/src/RdnaImageOpDecoder.cpp @@ -164,7 +164,7 @@ const ImageOpcodeInfo& lookupOpcode(std::uint32_t opcode) { } char message[48]; std::snprintf(message, sizeof(message), "unsupported MIMG opcode 0x%02x", opcode); - throw std::runtime_error(message); + throw UnsupportedInstructionError(message); } void validateFlags(std::uint32_t flags) { diff --git a/core/shader/recompiler/RdnaDecoder/src/RdnaScalarOpDecoder.cpp b/core/shader/recompiler/RdnaDecoder/src/RdnaScalarOpDecoder.cpp index 02abfe5a4..c85b8d563 100644 --- a/core/shader/recompiler/RdnaDecoder/src/RdnaScalarOpDecoder.cpp +++ b/core/shader/recompiler/RdnaDecoder/src/RdnaScalarOpDecoder.cpp @@ -74,7 +74,7 @@ RdnaOpcode decodeSop1Opcode(std::uint32_t opcode) { case 0x46u: return RdnaOpcode::SAndn1WrexecB32; case 0x47u: return RdnaOpcode::SAndn2WrexecB32; case 0x49u: return RdnaOpcode::SMovrelsd2B32; - default: throw std::invalid_argument("unsupported SOP1 opcode " + std::to_string(opcode)); + default: throw UnsupportedInstructionError("unsupported SOP1 opcode " + std::to_string(opcode)); } } @@ -131,7 +131,7 @@ RdnaOpcode decodeSop2Opcode(std::uint32_t opcode) { case 0x34u: return RdnaOpcode::SPackHhB32B16; case 0x35u: return RdnaOpcode::SMulHiU32; case 0x36u: return RdnaOpcode::SMulHiI32; - default: throw std::invalid_argument("unsupported SOP2 opcode " + std::to_string(opcode)); + default: throw UnsupportedInstructionError("unsupported SOP2 opcode " + std::to_string(opcode)); } } @@ -155,7 +155,7 @@ RdnaOpcode decodeSopcOpcode(std::uint32_t opcode) { case 0x0fu: return RdnaOpcode::SBitcmp1B64; case 0x12u: return RdnaOpcode::SCmpEqU64; case 0x13u: return RdnaOpcode::SCmpLgU64; - default: throw std::invalid_argument("unsupported SOPC opcode " + std::to_string(opcode)); + default: throw UnsupportedInstructionError("unsupported SOPC opcode " + std::to_string(opcode)); } } @@ -188,7 +188,7 @@ RdnaOpcode decodeSopkOpcode(std::uint32_t opcode) { case 0x1au: return RdnaOpcode::SWaitcnt; case 0x1bu: return RdnaOpcode::SSubvectorLoopBegin; case 0x1cu: return RdnaOpcode::SSubvectorLoopEnd; - default: throw std::invalid_argument("unsupported SOPK opcode " + std::to_string(opcode)); + default: throw UnsupportedInstructionError("unsupported SOPK opcode " + std::to_string(opcode)); } } @@ -231,7 +231,7 @@ RdnaOpcode decodeSoppOpcode(std::uint32_t opcode) { case 0x24u: return RdnaOpcode::SRoundMode; case 0x25u: return RdnaOpcode::SDenormMode; case 0x28u: return RdnaOpcode::STtracedata; - default: throw std::invalid_argument("unsupported SOPP opcode " + std::to_string(opcode)); + default: throw UnsupportedInstructionError("unsupported SOPP opcode " + std::to_string(opcode)); } }