Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions core/libs/prx/libSceAgcDriver/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -862,6 +862,15 @@ if(BUILD_TESTING)
set_tests_properties(scalar_saveexec PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
add_test_executable(vector_interp_e64_tests ${LIBS_INCLUDE_DIR}/tests/VectorInterpE64.cpp)
target_include_directories(vector_interp_e64_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(vector_interp_e64_tests PRIVATE shader_recompiler_agc_driver libc)
configure_windows_unwind(vector_interp_e64_tests)
add_test(NAME vector_interp_e64 COMMAND vector_interp_e64_tests)
if(WIN32)
set_tests_properties(vector_interp_e64 PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
add_test_executable(scalar_bitcount64_tests ${LIBS_INCLUDE_DIR}/tests/ScalarBitCount64.cpp)
target_link_libraries(scalar_bitcount64_tests PRIVATE shader_recompiler_agc_driver libc)
configure_windows_unwind(scalar_bitcount64_tests)
Expand Down
67 changes: 67 additions & 0 deletions core/libs/tests/VectorInterpE64.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#include "RdnaDecoder/RdnaInstructionDecoder.hpp"
#include "RdnaDecoder/RdnaVectorOpDecoder.hpp"
#include "Translation/TranslationContext.hpp"
#include <array>
#include <cstdio>
#include <stdexcept>
#include <string>
using namespace ShaderRecompiler;
static void Require(bool condition) {
if (!condition) throw std::runtime_error("VOP3 interpolation regression");
}
static std::string Translate(const RdnaInstruction& instruction, bool barycentric) {
IrProgram program;
auto& block = program.CreateBlock();
program.SetEntryBlock(block);
TranslationContext context(program, block, 256);
context.SetPixelInput(nullptr, barycentric);
context.TranslateInstruction(instruction);
return ProgramToString(program);
}
static void Reject(std::array<std::uint32_t, 2> code) {
bool rejected = false;
try { (void)DecodeRdnaVop3(0, code, 0); }
catch (const std::invalid_argument&) { rejected = true; }
Require(rejected);
}
int main() try {
std::uint32_t checks = 0;
for (std::uint32_t op = 0; op < 3; ++op) {
for (std::uint32_t attr : {0u, 1u, 31u, 32u, 63u}) {
for (std::uint32_t chan = 0; chan < 4; ++chan) {
for (std::uint32_t dest : {0u, 5u, 255u}) {
for (std::uint32_t source : {0u, 1u, 2u, 255u}) {
if (op == 2 && source > 2) continue;
const auto encodedSource = op == 2 ? source : source + 256u;
const std::array<std::uint32_t, 2> wide{
0xd6000000u | (op << 16u) | dest,
0x02000000u | (encodedSource << 9u) | (chan << 6u) | attr};
const std::array<std::uint32_t, 1> compact{
0xc8000000u | (dest << 18u) | (op << 16u) | (attr << 10u) | (chan << 8u) | source};
const auto a = DecodeRdnaVop3(0, wide, 0);
const auto b = DecodeRdnaVintrp(0, compact, 0);
Require(a.family == RdnaInstructionFamily::VOP3 && a.wordCount == 2 && a.opcodeId == 0x200u + op);
Require(a.op == b.op && a.destination.reg == b.destination.reg);
Require(a.source0.kind == b.source0.kind && a.source0.reg == b.source0.reg && a.source0.value == b.source0.value);
Require(a.source1.value == attr && a.source2.value == chan && a.sourceCount == 3);
for (bool barycentric : {false, true}) Require(Translate(a, barycentric) == Translate(b, barycentric));
++checks;
}
}
}
}
}
for (std::uint32_t bit : {8u, 9u, 10u, 11u, 12u, 13u, 14u, 15u}) Reject({0xd6000005u | (1u << bit), 0x02020400u});
for (std::uint32_t bit : {8u, 27u, 28u, 29u, 30u, 31u}) Reject({0xd6000005u, 0x02020400u | (1u << bit)});
Reject({0xd6000005u, 0x02000400u});
Reject({0xd6020005u, 0x02000600u});
Reject({0xd6000005u, 0x02060400u});
bool truncated = false;
try { const std::array<std::uint32_t, 1> code{0xd6000005u}; (void)DecodeRdnaVop3(0, code, 0); }
catch (const std::out_of_range&) { truncated = true; }
Require(truncated);
std::printf("PASS: %u VOP3 interpolation encodings; E32/E64 IR parity in both barycentric modes; malformed/modifier/truncation guards\n", checks);
} catch (const std::exception& error) {
std::fprintf(stderr, "FAIL: %s\n", error.what());
return 1;
}
30 changes: 30 additions & 0 deletions core/shader/recompiler/RdnaDecoder/src/RdnaVectorOpDecoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,9 @@ constexpr VopcOpcodeInfo vopcOpcodes[] = {
};

constexpr VectorOpcodeInfo vop3Opcodes[] = {
{0x200u, RdnaOpcode::VInterpP1F32},
{0x201u, RdnaOpcode::VInterpP2F32},
{0x202u, RdnaOpcode::VInterpMovF32},
{0x140u, RdnaOpcode::VMadLegacyF32},
{0x141u, RdnaOpcode::VMadF32},
{0x142u, RdnaOpcode::VMadI32I24},
Expand Down Expand Up @@ -2233,6 +2236,33 @@ RdnaInstruction DecodeRdnaVop3(std::uint32_t programCounter, std::span<const std
instruction.op = lookupVop3Opcode(opcode);
SetRdnaRawWords(instruction, code, wordIndex, 2);

if (opcode >= 0x200u && opcode <= 0x202u) {
if (abs != 0u || opSel != 0u || clamp != 0u || omod != 0u || neg != 0u) {
throw std::invalid_argument("VOP3 interpolation modifiers are not implemented");
}
if (src0 > 0xffu || src2 != 128u) {
throw std::invalid_argument("VOP3 interpolation requires canonical attribute and unused-source fields");
}
instruction.destination = DecodeRdnaVectorGpr(vdst);
if (instruction.op == RdnaOpcode::VInterpMovF32) {
if (src1 > 2u) throw std::invalid_argument("VOP3 interpolation parameter selector is reserved");
instruction.source0.kind = RdnaOperandKind::IntegerInlineConstant;
instruction.source0.value = src1;
instruction.source0.signedVal = static_cast<std::int32_t>(src1);
} else {
if (src1 < 256u) throw std::invalid_argument("VOP3 interpolation requires a vector source");
instruction.source0 = DecodeRdnaVectorGpr(src1 - 256u);
}
instruction.source1.kind = RdnaOperandKind::IntegerInlineConstant;
instruction.source1.value = src0 & 0x3fu;
instruction.source1.signedVal = static_cast<std::int32_t>(instruction.source1.value);
instruction.source2.kind = RdnaOperandKind::IntegerInlineConstant;
instruction.source2.value = (src0 >> 6u) & 3u;
instruction.source2.signedVal = static_cast<std::int32_t>(instruction.source2.value);
instruction.sourceCount = 3;
return instruction;
}

const bool carryInOut = (instruction.op == RdnaOpcode::VAddcU32 && opcode == 0x128u) ||
(instruction.op == RdnaOpcode::VSubCoCiU32 && opcode == 0x129u) ||
(instruction.op == RdnaOpcode::VSubrevCoCiU32 && opcode == 0x12au);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,13 @@ void TranslationContext::TranslateInstruction(const RdnaInstruction& decoded) {
break;
case RdnaInstructionFamily::VOP1:
case RdnaInstructionFamily::VOP2:
case RdnaInstructionFamily::VOP3:
case RdnaInstructionFamily::VOP3P:
case RdnaInstructionFamily::VOPC:
translated = emitVector(instruction);
break;
case RdnaInstructionFamily::VOP3:
translated = emitInterpolation(instruction) || emitVector(instruction);
break;
case RdnaInstructionFamily::SMEM:
case RdnaInstructionFamily::MUBUF:
case RdnaInstructionFamily::MTBUF:
Expand Down
4 changes: 4 additions & 0 deletions docs/dev/TechnicalDebt.md
Original file line number Diff line number Diff line change
Expand Up @@ -325,4 +325,8 @@ Throughout the project, every function at every stage either **does exactly what
- [sceImeKeyboardGetResourceId](../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) reports that no USB keyboard is connected (`0x80bc0004`, as [shadPS4](https://github.com/shadps4-emu/shadPS4) does) because IME keyboard events are not delivered to the handler, although [libSceKeyboard](../../core/libs/prx/libSceKeyboard/src/keyboard_impl.cpp) exposes the host keyboard
- [Player review dialog](../../core/libs/prx/libSceCdlgPlayerReview/Export.cpp) (libSceCdlgPlayerReview) shows nothing: `scePlayerReviewDialogOpen` finishes at once and the result reports that the user canceled, as `sceWebBrowserDialogOpen` does
- [NP reachability callbacks](../../core/libs/prx/libSceNpManager/Export.cpp) retain one callback and its user data; duplicate registration and unregistering an absent callback return the errors used by [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/945dbc3cc3eee80ac3e053b438502ed936fa6bb2/src/core/libraries/np/np_manager.cpp). No callback is delivered because the existing NP model remains signed out with reachability unavailable.

## VOP3 F32 interpolation encodings

- `V_INTERP_P1_F32`, `V_INTERP_P2_F32`, `V_INTERP_MOV_F32` VOP3 encodings use LLVM's `VOP3Interp_gfx10` layout in [VOPInstructions.td](https://github.com/llvm/llvm-project/blob/main/llvm/lib/Target/AMDGPU/VOPInstructions.td), opcode assignments in [VOP3Instructions.td](https://github.com/llvm/llvm-project/blob/main/llvm/lib/Target/AMDGPU/VOP3Instructions.td), and byte fixtures in [gfx10_asm_vop3.s](https://github.com/llvm/llvm-project/blob/main/llvm/test/MC/AMDGPU/gfx10_asm_vop3.s). They lower to the existing VINTRP semantics; no hardware validation was performed. Only unmodified forms with LLVM's canonical unused source 128 are accepted. Source and output modifiers, the attribute high bit and other unused-source encodings throw. These are additional encodings, not additional unique opcodes for the progress counter.
- [Temporary app-content storage](../../core/libs/prx/libSceAppContent/Export.cpp) tracks one `/temp0` mount, preserves its files on unmount, and rejects new path resolutions until remount. Mount, format, space-query and unmount errors follow [fpPS4](https://github.com/red-prig/fpPS4/blob/04cefd43e6fddd1ab033e7980cd356d14c964905/sys/sys_path.pas); the pointer ABI is also declared by [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/AppContent/AppContent.cs). Existing open host descriptors remain usable after unmount; console behaviour with open files is unverified. Host filesystem failures still throw.