From 95d3d9d3ad534f46e65c81c24c1889fc52107bad Mon Sep 17 00:00:00 2001 From: nmzik Date: Fri, 18 Sep 2026 02:30:46 +0400 Subject: [PATCH] shader: fix packed integer negation Co-authored-by: shadowbeat070 <70843316+shadowbeat070@users.noreply.github.com> --- .../recompiler/frontend/translate/Convert.cpp | 4 +- .../frontend/translate/Translate.cpp | 8 ++-- .../frontend/translate/Translator.h | 2 +- tests/ShaderRecompilerComputeTests.cpp | 37 +++++++++++++++++++ 4 files changed, 43 insertions(+), 8 deletions(-) diff --git a/src/graphics/shader/recompiler/frontend/translate/Convert.cpp b/src/graphics/shader/recompiler/frontend/translate/Convert.cpp index 1369a9418..90c2c64d3 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Convert.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Convert.cpp @@ -166,9 +166,9 @@ void Translator::V_CVT_PK_U8_F32(const Decoder::Instruction& inst) { } void Translator::V_PACK_B32_F16(const Decoder::Instruction& inst) { - const auto low = ReadF16LaneBits(inst.src0, false); + const auto low = Read16LaneBits(inst.src0, false); const auto high = - ir.ShiftLeftLogical(ReadF16LaneBits(inst.src1, false), IR::U32(IR::Value(16u))); + ir.ShiftLeftLogical(Read16LaneBits(inst.src1, false), IR::U32(IR::Value(16u))); WriteOperand(DestinationOperand(inst), ir.BitwiseOr(low, high)); } diff --git a/src/graphics/shader/recompiler/frontend/translate/Translate.cpp b/src/graphics/shader/recompiler/frontend/translate/Translate.cpp index 892278c23..a83ac27f0 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Translate.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Translate.cpp @@ -541,10 +541,7 @@ IR::U32 Translator::ReadU16LaneRaw(const Decoder::Operand& operand, bool high_la IR::U32 Translator::ReadU16LaneAsU32(const Decoder::Operand& operand, bool high_lane, bool sign_extend) { - auto value = ReadU16LaneRaw(operand, high_lane); - if (high_lane ? operand.negate_hi : operand.negate) { - value = ir.BitwiseAnd(ir.ISub(IR::U32(IR::Value(0u)), value), IR::U32(IR::Value(0xffffu))); - } + auto value = Read16LaneBits(operand, high_lane); if (sign_extend || operand.sdwa_sext) { value = IR::U32( ir.Emit(IR::ValueOpcode::BitFieldSExtract, {value, IR::Value(0u), IR::Value(16u)})); @@ -556,12 +553,13 @@ IR::U32 Translator::ReadU16AsU32(const Decoder::Operand& operand, bool sign_exte return ReadU16LaneAsU32(operand, false, sign_extend); } -IR::U32 Translator::ReadF16LaneBits(const Decoder::Operand& operand, bool high_lane) { +IR::U32 Translator::Read16LaneBits(const Decoder::Operand& operand, bool high_lane) { auto value = ReadU16LaneRaw(operand, high_lane); if (operand.absolute) { value = ir.BitwiseAnd(value, IR::U32(IR::Value(0x7fffu))); } if (high_lane ? operand.negate_hi : operand.negate) { + // RDNA2 source NEG flips the sign bit, including packed integer operands. value = ir.BitwiseXor(value, IR::U32(IR::Value(0x8000u))); } return value; diff --git a/src/graphics/shader/recompiler/frontend/translate/Translator.h b/src/graphics/shader/recompiler/frontend/translate/Translator.h index 5c54ca8b9..733640a80 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Translator.h +++ b/src/graphics/shader/recompiler/frontend/translate/Translator.h @@ -44,7 +44,7 @@ private: IR::U32 ReadU16LaneRaw(const Decoder::Operand& operand, bool high_lane); IR::U32 ReadU16LaneAsU32(const Decoder::Operand& operand, bool high_lane, bool sign_extend); IR::U32 ReadU16AsU32(const Decoder::Operand& operand, bool sign_extend); - IR::U32 ReadF16LaneBits(const Decoder::Operand& operand, bool high_lane); + IR::U32 Read16LaneBits(const Decoder::Operand& operand, bool high_lane); std::array ExtractU64(IR::U64 value); void WriteU32Pair(const Decoder::Operand& operand, const std::array& value); IR::U32 ConditionBit(const Decoder::Operand& operand); diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 82770b912..ded176287 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -19000,6 +19000,37 @@ TestCase Vop3pOpselHiUsesArchitecturalSourceBits() { {O::V_MOV_B32, O::V_PK_FMA_F16, O::BUFFER_STORE_DWORD, O::S_ENDPGM}}; } +TestCase Vop3pIntegerNegationCapturedAndSelectedHalves() { + using O = ShaderOpcode; + + std::vector code; + AppendVMovLiteral(&code, 6, 0xfed4ff38u); // (-200, -300) + AppendVMovLiteral(&code, 3, 0x012c00c8u); // (200, 300) + // Captured temporal shader: NEG_LO/HI turns 0x7fff into 0xffff (-1). + // Adding that clamped offset must select (199,299), not collapse to (0,0). + code.insert(code.end(), {0xcc074107u, 0x30020cffu, 0x00007fffu}); + code.insert(code.end(), {0xcc0a4010u, 0x18020f03u}); + AppendStoreVgpr(&code, 7, 0); + AppendStoreVgpr(&code, 16, 1); + + AppendVMovLiteral(&code, 0, 0x80011234u); + AppendVMovLiteral(&code, 1, 0xbeef5678u); + // Independently negate src0 low and src1 high, then reverse both the + // modified sources and src1 half selection. NEG flips the selected sign bit. + AppendVop3p(&code, 0x0a, 10, Vgpr(0), Vgpr(1), 0, 0x3, 0, 0x2, 0x1); + AppendVop3p(&code, 0x0a, 11, Vgpr(0), Vgpr(1), 0, 0x1, 0x2, 0x1, 0x2); + AppendStoreVgpr(&code, 10, 2); + AppendStoreVgpr(&code, 11, 3); + AppendEnd(&code); + + return {"Vop3pIntegerNegationCapturedAndSelectedHalves", + code, + {}, + {0xffffffffu, 0x012b00c7u, 0xbef0e8acu, 0x56795123u}, + {O::V_MOV_B32, O::V_PK_MAX_I16, O::V_PK_ADD_U16, + O::BUFFER_STORE_DWORD, O::S_ENDPGM}}; +} + TestCase CvtPkU8F32PacksSelectedByte() { using O = ShaderOpcode; @@ -26908,6 +26939,7 @@ std::vector MakeCases() { AddCase(Vop2PkFmacF16AccumulatesPackedHalvesIndependently); AddCase(Vop2PkFmacF16DppNegatesBothPackedHalves); AddCase(Vop3pOpselHiUsesArchitecturalSourceBits); + AddCase(Vop3pIntegerNegationCapturedAndSelectedHalves); AddCase(CvtPkU8F32PacksSelectedByte); AddCase(CvtPkrtzF16F32SubnormalRoundsTowardZero); AddCase(CvtPkrtzF16F32SdwaAndOutputModifiers); @@ -31628,6 +31660,11 @@ int main(int argc, char **argv) { std::setvbuf(stdout, nullptr, _IONBF, 0); EnsureConfigInitialized(); CheckLeastRecentlyUsedCacheOrdering(); + if (argc == 2 && std::strcmp(argv[1], "--packed-integer-neg-only") == 0) { + VulkanHarness vulkan; + RunCase(&vulkan, Vop3pIntegerNegationCapturedAndSelectedHalves()); + return 0; + } if (argc == 2 && std::strcmp(argv[1], "--dpp-only") == 0) { VulkanHarness vulkan; RunCase(&vulkan, VectorDpp8Captured(false));