diff --git a/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp b/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp index 28ce76c5a..68a7d4a22 100644 --- a/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp @@ -21,6 +21,7 @@ constexpr MemoryOpcodeInfo SMEM_OPCODE_LIST[] = { {0x04u, Opcode::S_LOAD_DWORDX16, 16, 32}, {0x08u, Opcode::S_BUFFER_LOAD_DWORD, 1, 32}, {0x09u, Opcode::S_BUFFER_LOAD_DWORDX2, 2, 32}, {0x0au, Opcode::S_BUFFER_LOAD_DWORDX4, 4, 32}, {0x0bu, Opcode::S_BUFFER_LOAD_DWORDX8, 8, 32}, {0x0cu, Opcode::S_BUFFER_LOAD_DWORDX16, 16, 32}, + {0x25u, Opcode::S_MEMREALTIME, 2, 32}, }; constexpr MemoryOpcodeInfo MUBUF_OPCODE_LIST[] = { @@ -245,6 +246,10 @@ void DecodeSmem(uint32_t pc, std::span code, uint32_t word_index } DecodeScalarDestination(sdst, pc, inst.dst); + if (inst.opcode == Opcode::S_MEMREALTIME) { + inst.src_count = 0; + return; + } // SMEM encodes SBASE in SGPR pairs. Scalar-buffer loads still use the same // pair index; their descriptor operand consumes four SGPRs from that base. DecodeScalarSource(sbase * 2u, pc, inst.src0); diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h index 0d01f6a18..1a67a5d67 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h @@ -439,6 +439,7 @@ enum class Opcode { S_BUFFER_LOAD_DWORDX4, S_BUFFER_LOAD_DWORDX8, S_BUFFER_LOAD_DWORDX16, + S_MEMREALTIME, BUFFER_LOAD_FORMAT_X, BUFFER_LOAD_FORMAT_XY, BUFFER_LOAD_FORMAT_XYZ, diff --git a/src/graphics/shader/recompiler/frontend/translate/Memory.cpp b/src/graphics/shader/recompiler/frontend/translate/Memory.cpp index bec08ef4c..bb6941571 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Memory.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Memory.cpp @@ -1,8 +1,10 @@ +#include "common/logging/log.h" #include "graphics/shader/recompiler/frontend/translate/Translator.h" #include "graphics/shader/recompiler/frontend/decode/ImageOps.h" #include #include +#include namespace Libs::Graphics::ShaderRecompiler::Frontend { @@ -891,6 +893,17 @@ bool Translator::EmitMemory(const Decoder::Instruction& inst) { case Decoder::Opcode::S_BUFFER_LOAD_DWORDX4: case Decoder::Opcode::S_BUFFER_LOAD_DWORDX8: case Decoder::Opcode::S_BUFFER_LOAD_DWORDX16: return S_LOAD(inst, false); + case Decoder::Opcode::S_MEMREALTIME: { + static std::atomic_flag warned = ATOMIC_FLAG_INIT; + if (!warned.test_and_set(std::memory_order_relaxed)) { + Log::WriteToConsoleAndLog( + "Warning: S_MEMREALTIME uses placeholder UINT64_MAX; real-time clock not implemented.\n"); + } + for (uint32_t component = 0; component < 2; component++) { + WriteOperand(ScalarDestinationOperand(inst.dst, component), IR::Value(UINT32_MAX)); + } + return true; + } case Decoder::Opcode::BUFFER_LOAD_UBYTE: case Decoder::Opcode::BUFFER_LOAD_SBYTE: diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index ee54d51e7..84367e170 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -22783,6 +22783,31 @@ TestCase BranchVccnzUsesWaveMask() { return test; } +TestCase ScalarMemRealtimeCapturedPlaceholder() { + using O = ShaderOpcode; + namespace D = ShaderRecompiler::Decoder; + + std::vector code = {0xf4940300u, 0xfa000000u}; + D::Instruction decoded; + D::DecodeInstruction(code, 0, decoded); + Require("ScalarMemRealtimeCapturedPlaceholder", "decode", + decoded.opcode == O::S_MEMREALTIME && decoded.word_count == 2 && + decoded.dst.kind == D::OperandKind::Sgpr && decoded.dst.reg == 12 && + decoded.data_dwords == 2 && decoded.src_count == 0 && + decoded.src0.kind == D::OperandKind::Unknown && + decoded.src1.kind == D::OperandKind::Unknown, + "captured clock instruction must write s12:s13 without memory operands"); + AppendStoreSgprPair(&code, 12, 0); + code.insert(code.end(), {0xf4940300u, 0xfa000000u}); + AppendStoreSgprPair(&code, 12, 2); + AppendEnd(&code); + return {"ScalarMemRealtimeCapturedPlaceholder", + code, + {}, + {UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX}, + {O::S_MEMREALTIME, O::V_MOV_B32, O::BUFFER_STORE_DWORD, O::S_ENDPGM}}; +} + TestCase ScalarMemoryLoadVariants() { using O = ShaderOpcode; @@ -28436,6 +28461,7 @@ std::vector MakeCases() { AddCase(SharedReturnKeepsSelectedValues); AddCase(BranchVccnzUsesWaveMask); AddCase(BranchVccnzUsesCarryProducedWaveMask); + AddCase(ScalarMemRealtimeCapturedPlaceholder); AddCase(ScalarMemoryLoadVariants); AddCase(ScalarLoadSignedImmediateOffsetAddsSoffset); AddCase(ScalarLoadAlignsComponentsAndMasksAddress); @@ -33172,6 +33198,11 @@ int main(int argc, char **argv) { std::setvbuf(stdout, nullptr, _IONBF, 0); EnsureConfigInitialized(); CheckLeastRecentlyUsedCacheOrdering(); + if (argc == 2 && std::strcmp(argv[1], "--s-memrealtime-only") == 0) { + VulkanHarness vulkan; + RunCase(&vulkan, ScalarMemRealtimeCapturedPlaceholder()); + return 0; + } if (argc == 2 && std::strcmp(argv[1], "--packed-integer-neg-only") == 0) { VulkanHarness vulkan; RunCase(&vulkan, Vop3pIntegerNegationCapturedAndSelectedHalves());