fix(shader): decode the scalar aperture sources 235-238 instead of failing the shader
This commit is contained in:
parent
a29ef59219
commit
b7e32b3648
|
|
@ -1225,6 +1225,14 @@ if(BUILD_TESTING)
|
|||
set_tests_properties(scalar_scratch_load PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
|
||||
endif()
|
||||
add_test_executable(aperture_sources_tests ${LIBS_INCLUDE_DIR}/tests/ApertureSources.cpp)
|
||||
target_link_libraries(aperture_sources_tests PRIVATE shader_recompiler_agc_driver libc)
|
||||
configure_windows_unwind(aperture_sources_tests)
|
||||
add_test(NAME aperture_sources COMMAND aperture_sources_tests)
|
||||
if(WIN32)
|
||||
set_tests_properties(aperture_sources PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
|
||||
endif()
|
||||
add_test_executable(scalar_call_tests ${LIBS_INCLUDE_DIR}/tests/ScalarCall.cpp)
|
||||
target_link_libraries(scalar_call_tests PRIVATE shader_recompiler_agc_driver libc)
|
||||
configure_windows_unwind(scalar_call_tests)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,89 @@
|
|||
#include "IntermediateRepresentation/IrProgram.hpp"
|
||||
#include "RdnaDecoder/RdnaInstructionDecoder.hpp"
|
||||
#include "RdnaDecoder/RdnaScalarOpDecoder.hpp"
|
||||
#include "Translation/TranslationContext.hpp"
|
||||
#include <array>
|
||||
#include <cstdio>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
using namespace ShaderRecompiler;
|
||||
|
||||
namespace {
|
||||
|
||||
struct ApertureSource {
|
||||
std::uint32_t code;
|
||||
RdnaOperandKind kind;
|
||||
const char* name;
|
||||
};
|
||||
|
||||
const std::array<ApertureSource, 4> ApertureSources{{
|
||||
{235u, RdnaOperandKind::SrcSharedBase, "src_shared_base"},
|
||||
{236u, RdnaOperandKind::SrcSharedLimit, "src_shared_limit"},
|
||||
{237u, RdnaOperandKind::SrcPrivateBase, "src_private_base"},
|
||||
{238u, RdnaOperandKind::SrcPrivateLimit, "src_private_limit"},
|
||||
}};
|
||||
|
||||
void Require(bool value, const std::string& message) {
|
||||
if (!value) throw std::runtime_error(message);
|
||||
}
|
||||
|
||||
// The refusal must name the source it refused: a bare "not modelled" cannot be told from a decode gap.
|
||||
template <typename Fn>
|
||||
void ExpectRefusal(const char* source, Fn translate) {
|
||||
try {
|
||||
translate();
|
||||
} catch (const std::exception& error) {
|
||||
if (std::string(error.what()).find(source) == std::string::npos) {
|
||||
throw std::runtime_error(std::string("the refusal does not name ") + source + ": " + error.what());
|
||||
}
|
||||
return;
|
||||
}
|
||||
throw std::runtime_error(std::string("reading ") + source + " was accepted");
|
||||
}
|
||||
|
||||
// s_mov_b32 s0, <source>: SOP1, format 0x7d, opcode 0x03, the source code in the low byte.
|
||||
RdnaInstruction ScalarMove(std::uint32_t source) {
|
||||
const std::array<std::uint32_t, 2> words{0x80000000u | (0x7du << 23u) | (0x03u << 8u) | source, 0u};
|
||||
return DecodeRdnaSop1(0u, words, 0u);
|
||||
}
|
||||
|
||||
void Translate(const RdnaInstruction& instruction) {
|
||||
IrProgram program;
|
||||
auto& block = program.CreateBlock();
|
||||
program.SetEntryBlock(block);
|
||||
program.BlockOrder() = {&block};
|
||||
program.Metadata().blockInfo.resize(1);
|
||||
TranslationContext context(program, block, 256);
|
||||
context.TranslateInstruction(instruction);
|
||||
}
|
||||
|
||||
void CheckDecode() {
|
||||
for (const auto& source : ApertureSources) {
|
||||
const auto operand = DecodeRdnaScalarSource(source.code, 0u);
|
||||
Require(operand.kind == source.kind,
|
||||
"scalar source " + std::to_string(source.code) + " decoded to the wrong operand kind");
|
||||
Require(RdnaOperandToString(operand) == source.name,
|
||||
"scalar source " + std::to_string(source.code) + " is named " + RdnaOperandToString(operand));
|
||||
}
|
||||
}
|
||||
|
||||
// Translating the move reads the source as a value, which is the path a title that carries the
|
||||
// aperture into an address does not take and one that reads it does.
|
||||
void CheckReadRefuses() {
|
||||
for (const auto& source : ApertureSources) {
|
||||
const RdnaInstruction move = ScalarMove(source.code);
|
||||
Require(move.op == RdnaOpcode::SMovB32, "the scalar move decoded to another opcode");
|
||||
Require(move.source0.kind == source.kind, "the scalar move decodes source0 as the wrong operand kind");
|
||||
ExpectRefusal(source.name, [&] { Translate(move); });
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main() {
|
||||
CheckDecode();
|
||||
CheckReadRefuses();
|
||||
std::puts("aperture sources tests passed");
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -53,7 +53,11 @@ enum class RdnaOperandKind {
|
|||
VccZ,
|
||||
ExecZ,
|
||||
M0,
|
||||
PopsExitingWaveId
|
||||
PopsExitingWaveId,
|
||||
SrcSharedBase,
|
||||
SrcSharedLimit,
|
||||
SrcPrivateBase,
|
||||
SrcPrivateLimit
|
||||
};
|
||||
|
||||
enum class RdnaImageDimension : std::uint32_t {
|
||||
|
|
|
|||
|
|
@ -217,6 +217,10 @@ RdnaOperand DecodeRdnaScalarSource(std::uint32_t code, std::uint32_t programCoun
|
|||
case 125u: operand.kind = RdnaOperandKind::Null; return operand;
|
||||
case 126u: operand.kind = RdnaOperandKind::ExecLo; return operand;
|
||||
case 127u: operand.kind = RdnaOperandKind::ExecHi; return operand;
|
||||
case 235u: operand.kind = RdnaOperandKind::SrcSharedBase; return operand;
|
||||
case 236u: operand.kind = RdnaOperandKind::SrcSharedLimit; return operand;
|
||||
case 237u: operand.kind = RdnaOperandKind::SrcPrivateBase; return operand;
|
||||
case 238u: operand.kind = RdnaOperandKind::SrcPrivateLimit; return operand;
|
||||
case 239u: operand.kind = RdnaOperandKind::PopsExitingWaveId; return operand;
|
||||
case 248u:
|
||||
operand.kind = RdnaOperandKind::FloatInlineConstant;
|
||||
|
|
@ -315,6 +319,10 @@ std::string RdnaOperandToString(const RdnaOperand& operand) {
|
|||
case RdnaOperandKind::ExecZ: return "execz";
|
||||
case RdnaOperandKind::M0: return "m0";
|
||||
case RdnaOperandKind::PopsExitingWaveId: return "pops_exiting_wave_id";
|
||||
case RdnaOperandKind::SrcSharedBase: return "src_shared_base";
|
||||
case RdnaOperandKind::SrcSharedLimit: return "src_shared_limit";
|
||||
case RdnaOperandKind::SrcPrivateBase: return "src_private_base";
|
||||
case RdnaOperandKind::SrcPrivateLimit: return "src_private_limit";
|
||||
}
|
||||
|
||||
throw std::invalid_argument("unsupported operand kind for string conversion");
|
||||
|
|
|
|||
|
|
@ -436,6 +436,10 @@ RdnaOperand scalarSource(std::uint32_t code) {
|
|||
case 125u: operand.kind = RdnaOperandKind::Null; return operand;
|
||||
case 126u: operand.kind = RdnaOperandKind::ExecLo; return operand;
|
||||
case 127u: operand.kind = RdnaOperandKind::ExecHi; return operand;
|
||||
case 235u: operand.kind = RdnaOperandKind::SrcSharedBase; return operand;
|
||||
case 236u: operand.kind = RdnaOperandKind::SrcSharedLimit; return operand;
|
||||
case 237u: operand.kind = RdnaOperandKind::SrcPrivateBase; return operand;
|
||||
case 238u: operand.kind = RdnaOperandKind::SrcPrivateLimit; return operand;
|
||||
case 239u: operand.kind = RdnaOperandKind::PopsExitingWaveId; return operand;
|
||||
case 248u: operand.kind = RdnaOperandKind::FloatInlineConstant; operand.value = floatBits(0.15915494309189535f); return operand;
|
||||
case 251u: operand.kind = RdnaOperandKind::VccZ; return operand;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
#include "RdnaDecoder/RdnaInstructionDecoder.hpp"
|
||||
#include "Translation/TranslationContext.hpp"
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
namespace ShaderRecompiler {
|
||||
|
||||
|
|
@ -418,6 +420,11 @@ IrU32 TranslationContext::readScalarCode(std::uint32_t code) {
|
|||
const std::array<IrU32, 2> mask = ballotMask(IrU1(ir.GetExec()));
|
||||
return mask[code - 126u];
|
||||
}
|
||||
case 235u:
|
||||
case 236u:
|
||||
case 237u:
|
||||
case 238u:
|
||||
throw std::runtime_error("TranslationContext::readScalarCode: the scalar aperture source " + RdnaOperandToString(DecodeRdnaScalarSource(code, 0u)) + " is not modelled");
|
||||
default: return IrU32(ir.Constant(0u));
|
||||
}
|
||||
}
|
||||
|
|
@ -447,6 +454,11 @@ IrU32 TranslationContext::readRawU32(const RdnaOperand& operand) {
|
|||
const IrU32 zero(ir.Constant(0u));
|
||||
return IrU32(ir.Select(ir.IEqual(mask.Value(), zero.Value()), ir.Constant(1u), zero.Value()));
|
||||
}
|
||||
case RdnaOperandKind::SrcSharedBase:
|
||||
case RdnaOperandKind::SrcSharedLimit:
|
||||
case RdnaOperandKind::SrcPrivateBase:
|
||||
case RdnaOperandKind::SrcPrivateLimit:
|
||||
throw std::runtime_error("TranslationContext::readRawU32: the scalar aperture source " + RdnaOperandToString(operand) + " is not modelled");
|
||||
default: throw std::runtime_error("TranslationContext::readRawU32 invalid decoded operand used as a raw U32 source");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -448,6 +448,7 @@ Throughout the project, every function at every stage either **does exactly what
|
|||
- [Font writing lines](../../core/libs/prx/libSceFont/src/WritingLine.cpp) (libSceFont) - `sceFontCreateWritingLine`, `sceFontWritingLineWritesOrder`, `sceFontWritingLineRefersRenderStep`, `sceFontWritingLineGetRenderMetrics` and `sceFontWritingLineClear` follow [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/main/src/libs/libFont.cpp): runs are placed left to right, each at the line's advance so far, with no spacing or adjustment. Only horizontal and left-to-right lines, writing attribute 0 and non-null run metrics are modelled; other values, the metrics of an empty line and unknown or null handles throw, since no reference gives their result or error code. `sceFontWritingLineGetOrderingSpace` still throws (KytyPS5's values are placeholders). Lines are allocated on the host, not in the font memory the title passes to the library: FreeType needs about 1 MiB per CJK face, which the title's font memory is not sized for.
|
||||
- [s_swappc_b64](../../core/shader/recompiler/Translation/src/ControlFlowInstructions.cpp) writes the link pair with the same model as `s_getpc_b64`: the guest return address `pc + 4` as a byte offset over `GetShaderBase`. Any other use of the link pair (arithmetic, compare, hash, store, or a second `s_swappc_b64` sharing it) throws; constant-offset folding over the link is not modeled. The special encoding `s_swappc_b64` with SDST 125 still decodes as `s_setpc_b64`, which matches the hardware: measured on RDNA2 (gfx1036) in wave32 and wave64, it branches to the source pair and leaves a marker SGPR pair untouched, and with an SGPR destination the link is `PC + 4`.
|
||||
- The [s_swappc_b64 fetch-shader call at vertex/local/tessellation/mesh entry](../../core/shader/recompiler/ControlFlow/src/GraphBuilder.cpp) assumes the called fetch-shader blob ends in `s_setpc_b64` to the link pair (the GNM fetch-shader shape shadPS4's `EmitFetch` copies); the blob is not part of the shader program, so its return closes the pairing by assumption and any in-program reader of the link pair throws. Attribute fetches are inlined from the request's parsed fetch-shader plan through the same `GetAttribute` path as the embedded fetch analyzer, honoring the buffer resource `dst_sel`; an attribute writing more than four registers, or an entry call without a parsed plan, throws. The vertex/instance offset accumulator inside the fetch blob is not replayed for external blobs.
|
||||
- The scalar aperture sources [src_shared_base, src_shared_limit, src_private_base, src_private_limit](../../core/shader/recompiler/RdnaDecoder/src/RdnaInstructionDecoder.cpp) (core/shader/recompiler) decode to their own operand kinds (scalar source codes 235 to 238, named as in LLVM's `SIRegisterInfo.td`), but their value is not modelled: reading one refuses with the source it refused named, because the wave's shared and private aperture bases and limits are unknown. Both the scalar-code read and the raw-operand read refuse; a shader that only carries the value into an address still prepares, one that reads it does not
|
||||
- [VertexBufferReadSize](../../core/libs/prx/libSceAgcDriver/Graphics/include/VertexInput.hpp) and the [vertex staging copy](../../core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp) (libSceAgcDriver) - a vertex fetch whose index is at or past the descriptor's `num_records` no longer throws for `OOB_SELECT` 0 and 1. The copy still spans the whole fetch, but the bytes from `num_records * stride` onward read as zeros, so the out-of-range vertex is `(0, 0, 0, 0)`. Measured with `buffer_load_format_xyzw` (`idxen`) through the [hardware oracle](HW_ORACLE.md) on RDNA2 gfx1036 for PPSA01325's word 3 `0x0004dfac` (`OOB_SELECT` 0) in #2241: indices 3, 4 and 5 over 3 records each return `00000000 00000000 00000000 00000000`, W included, unlike Vulkan `robustBufferAccess`, which returns `(0, 0, 0, 1)`. The zeros come from the record check, not from memory: stored values placed past the records are ignored, and a `num_records` 6 control returns them. `RESOURCE_LEVEL` 1, `OOB_SELECT` 1 and FORMAT 74 `32_32_32_FLOAT` give the same zeros, in wave32 and wave64; gfx1013 (the console) is not measured. `OOB_SELECT` 2 returns the stored values past the records and `OOB_SELECT` 3 compares `num_records` against the byte offset, both measured on the same setup in #2241; the two keep throwing past `num_records` here, since each needs its own rule. Zero-stride descriptors (`stride` 0) keep their separate byte-count check and are not covered here. Each out-of-range fetch is copied alone and outside the draw-snapshot cache, which is neither read nor written for it: a reused snapshot would carry another draw's bytes into the zero region, and a stored zero-padded copy would hand zeros to a later in-range draw at the same address. The valid prefix is still flushed for GPU writes before it is read.
|
||||
- [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
|
||||
|
|
|
|||
Loading…
Reference in New Issue