mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 02:47:02 +02:00
Fix HITMAN 3 boot, layered presentation, and socket imports (#851)
* videoout: synchronize layered flips with premultiplied composition * shader: support unsigned FFBH subword sources * shader: apply cube-face result scaling * shader: prune buffer reads proven out of bounds * net: implement send and receive exports with shared socket handling --------- Co-authored-by: nmzik <Nmzik@mail.ru>
This commit is contained in:
@@ -1208,6 +1208,7 @@ struct TestCase {
|
||||
std::vector<std::pair<std::string, size_t>> decoded_counts;
|
||||
std::vector<std::pair<std::string, size_t>> ir_counts;
|
||||
u32 expected_storage_mip_descriptors = 0;
|
||||
std::optional<std::vector<u32>> expected_buffer_resources;
|
||||
};
|
||||
|
||||
struct GraphicsCase {
|
||||
@@ -1241,7 +1242,9 @@ struct CompiledShader {
|
||||
|
||||
std::array<u32, 64> MakeNativeUserData(const std::array<u32, 64> *source) {
|
||||
std::array<u32, 64> data{};
|
||||
data[3] = 3u << 28u;
|
||||
data[50] = 1u << 20u;
|
||||
data[51] = 3u << 28u;
|
||||
if (source != nullptr) {
|
||||
data = *source;
|
||||
}
|
||||
@@ -1564,18 +1567,26 @@ CompiledShader CompileCase(const TestCase &test, u32 host_subgroup_size = 64) {
|
||||
"recompiler returned empty SPIR-V");
|
||||
ValidateSpirv(test.name, result.spirv);
|
||||
CheckSpirvText(test, result.spirv);
|
||||
const auto *buffer_binding = ShaderRecompiler::IR::FindBinding(
|
||||
result.program.bindings, ShaderRecompiler::IR::DescriptorBindingKind::Buffers);
|
||||
if (test.expected_buffer_resources) {
|
||||
Require(test.name, "live buffer bindings",
|
||||
buffer_binding != nullptr &&
|
||||
buffer_binding->resources == *test.expected_buffer_resources,
|
||||
"dead buffer changed the surviving descriptor order");
|
||||
}
|
||||
std::vector<u32> packed_user_data;
|
||||
for (const auto reg : result.program.bindings.user_data_registers) {
|
||||
packed_user_data.push_back(
|
||||
resources.user_data[reg - result.program.user_data_base]);
|
||||
}
|
||||
packed_user_data.resize(result.program.bindings.ShaderDataDwords());
|
||||
const auto buffer_count =
|
||||
static_cast<u32>(result.program.info.buffers.size());
|
||||
const auto buffer_count = result.program.bindings.memory_offset_count;
|
||||
for (u32 i = 0; i < buffer_count; i++) {
|
||||
u32 offset = 0;
|
||||
if (i < test.storage_buffer_offsets.size()) {
|
||||
offset = test.storage_buffer_offsets[i];
|
||||
const auto resource = buffer_binding->resources[i];
|
||||
if (resource < test.storage_buffer_offsets.size()) {
|
||||
offset = test.storage_buffer_offsets[resource];
|
||||
}
|
||||
Require(test.name, "shader data", offset % sizeof(u32) == 0 && offset < 256,
|
||||
"storage buffer offset is not representable");
|
||||
@@ -1593,7 +1604,8 @@ std::array<u32, 64> MakeStructuredStorageBufferData(u32 stride_bytes,
|
||||
std::array<u32, 64> data{};
|
||||
data[1] = (stride_bytes & 0x3fffu) << 16u;
|
||||
data[2] = num_records;
|
||||
data[3] = DstSel(4, 5, 6, 7) | (1u << 24u);
|
||||
data[3] = DstSel(4, 5, 6, 7) | (1u << 24u) |
|
||||
(stride_bytes == 0 ? 3u << 28u : 0u);
|
||||
if (add_tid) {
|
||||
data[3] |= 1u << 23u;
|
||||
}
|
||||
@@ -13233,8 +13245,9 @@ public:
|
||||
info.offset = 0;
|
||||
info.range = buffer.size;
|
||||
if (test.storage_buffer_range_dwords != 0) {
|
||||
const auto offset = i < test.storage_buffer_offsets.size()
|
||||
? test.storage_buffer_offsets[i]
|
||||
const auto resource = buffers->resources[i];
|
||||
const auto offset = resource < test.storage_buffer_offsets.size()
|
||||
? test.storage_buffer_offsets[resource]
|
||||
: 0u;
|
||||
info.range = static_cast<vk::DeviceSize>(
|
||||
test.storage_buffer_range_dwords * sizeof(u32) + offset);
|
||||
@@ -18876,6 +18889,31 @@ TestCase VectorFfbhI32NativeAndVop3OnGpu() {
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase Vop1SdwaFfbhCapturedScalarLowWordSource() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
constexpr std::array inputs = {0xdead0000u, 0xffff0001u, 0x00008000u,
|
||||
0x00010008u, 0xffff4000u, 0x0000ffffu};
|
||||
std::vector<u32> code;
|
||||
for (u32 i = 0; i < inputs.size(); i++) {
|
||||
AppendSmemLoadOpcode(&code, 0x08, 39, i * 4u);
|
||||
code.insert(code.end(), {0x7e0072f9u, 0x00840627u}); // v_ffbh_u32 v0, s39.word0
|
||||
AppendStoreVgpr(&code, 0, i);
|
||||
}
|
||||
AppendEnd(&code);
|
||||
|
||||
TestCase test;
|
||||
test.name = "Vop1SdwaFfbhCapturedScalarLowWordSource";
|
||||
test.code = std::move(code);
|
||||
test.initial.assign(inputs.begin(), inputs.end());
|
||||
test.expected = {0xffffffffu, 31u, 16u, 28u, 17u, 16u};
|
||||
test.opcodes = {O::S_BUFFER_LOAD_DWORD, O::V_FFBH_U32, O::V_MOV_B32,
|
||||
O::BUFFER_STORE_DWORD, O::S_ENDPGM};
|
||||
test.decoded_counts = {{"V_FFBH_U32 v0, s39.sdwa(sel=4,sext=0)", inputs.size()}};
|
||||
test.required_spirv = {"OpBitFieldUExtract", "FindUMsb"};
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase Vop1SdwaFfblCapturedHighWordSource() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
@@ -21192,6 +21230,37 @@ TestCase Vop1MoveRelDestination() {
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase CubeIdCapturedNegationAndOutputScale() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
constexpr std::array directions = {
|
||||
std::array{4.0f, 2.0f, 1.0f}, std::array{-4.0f, 2.0f, 1.0f},
|
||||
std::array{1.0f, -4.0f, 2.0f}, std::array{1.0f, 4.0f, 2.0f},
|
||||
std::array{1.0f, 2.0f, -4.0f}, std::array{1.0f, 2.0f, 4.0f}};
|
||||
constexpr std::array registers = {47u, 49u, 48u};
|
||||
TestCase test;
|
||||
test.name = "CubeIdCapturedNegationAndOutputScale";
|
||||
for (u32 i = 0; i < directions.size(); i++) {
|
||||
for (u32 component = 0; component < registers.size(); component++) {
|
||||
test.initial.push_back(std::bit_cast<u32>(directions[i][component]));
|
||||
AppendVMovU32(&test.code, 30, (i * 3u + component) * 4u);
|
||||
AppendBufferLoadDword(&test.code, registers[component], 30);
|
||||
}
|
||||
// The captured instruction negates Y/Z and divides the floating face ID by two.
|
||||
test.code.insert(test.code.end(), {0xd544000bu, 0xdcc2632fu});
|
||||
AppendStoreVgpr(&test.code, 11, i);
|
||||
}
|
||||
AppendEnd(&test.code);
|
||||
test.expected = {0x00000000u, 0x3f000000u, 0x3f800000u,
|
||||
0x3fc00000u, 0x40000000u, 0x40200000u};
|
||||
test.opcodes = {O::V_MOV_B32, O::BUFFER_LOAD_DWORD, O::V_CUBEID_F32,
|
||||
O::BUFFER_STORE_DWORD, O::S_ENDPGM};
|
||||
test.decoded_counts = {
|
||||
{"V_CUBEID_F32 v11.omod(3), v47, v49.neg, v48.neg", directions.size()}};
|
||||
test.required_spirv = {"OpFNegate", "OpFMul"};
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase VectorFloatSpecialOps() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
@@ -24503,6 +24572,113 @@ TestCase BufferStoreFormatXDropsOutOfRangeRecord() {
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase TBufferCapturedZeroStrideOob(bool raw_bounds = false, bool scalar = false) {
|
||||
using O = ShaderOpcode;
|
||||
TestCase test;
|
||||
test.name = scalar ? "TBufferZeroStrideOobPreservesScalarRead"
|
||||
: raw_bounds ? "TBufferZeroStrideRawBoundsStillLoads"
|
||||
: "TBufferCapturedZeroStrideOob";
|
||||
test.initial = {0x11223344u, 0x55667788u, 0x99aabbccu, 0xddeeff00u,
|
||||
0x12345678u, 0xdeadbeefu, 0xdeadbeefu};
|
||||
test.expected = test.initial;
|
||||
for (u32 i = 0; i < 4; i++) {
|
||||
test.expected[1 + i] = raw_bounds ? test.initial[i] : 0u;
|
||||
}
|
||||
test.expected[5] = test.initial[4];
|
||||
if (scalar) test.expected[6] = test.initial[0];
|
||||
test.user_data = MakeStructuredStorageBufferData(4, test.initial.size());
|
||||
std::copy_n(test.user_data.begin(), 4, test.user_data.begin() + 4);
|
||||
test.user_data[4] = 0x2000u;
|
||||
// Captured HITMAN descriptor in s[32:35]: mode 0, zero stride, arbitrary base.
|
||||
test.user_data[32] = 0x4a398620u;
|
||||
test.user_data[33] = 0;
|
||||
test.user_data[34] = 0x2b5u;
|
||||
test.user_data[35] = 0x0004d000u | (raw_bounds ? 3u << 28u : 0u);
|
||||
test.has_user_data = true;
|
||||
test.storage_buffer_offsets = {0, 0, 16};
|
||||
test.expected_buffer_resources = raw_bounds || scalar
|
||||
? std::vector<u32>{0, 1, 2}
|
||||
: std::vector<u32>{0, 2};
|
||||
auto &code = test.code;
|
||||
AppendVMovLiteral(&code, 0, test.initial[0]);
|
||||
AppendStoreVgpr(&code, 0, 0); // Keep resource zero before the removed middle slot.
|
||||
AppendVMovU32(&code, 30, 0);
|
||||
code.push_back(0xea6b2000u);
|
||||
code.push_back(0x8008191eu); // Exact pc 0x47c: typed float4, s[32:35], v30.
|
||||
AppendVMovU32(&code, 20, 0);
|
||||
code.push_back(EncodeMubuf0(0x0cu));
|
||||
code.push_back(EncodeMubuf1(33, 1, 20));
|
||||
if (scalar) {
|
||||
code.push_back(EncodeSmem0(0x08u, 40, 16));
|
||||
code.push_back(EncodeSmem1(0));
|
||||
code.push_back(EncodeVop1(0x01u, 32, 40));
|
||||
}
|
||||
for (u32 i = 0; i < 4; i++) AppendStoreVgpr(&code, 25 + i, 1 + i);
|
||||
AppendStoreVgpr(&code, 33, 5);
|
||||
if (scalar) AppendStoreVgpr(&code, 32, 6);
|
||||
AppendEnd(&code);
|
||||
test.opcodes = {O::V_MOV_B32, O::TBUFFER_LOAD_FORMAT_XYZW,
|
||||
O::BUFFER_LOAD_DWORD, O::BUFFER_STORE_DWORD, O::S_ENDPGM};
|
||||
if (scalar) test.opcodes.push_back(O::S_BUFFER_LOAD_DWORD);
|
||||
test.ir_counts = {{"LoadBufferU32x4", raw_bounds ? 1u : 0u}};
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase BufferZeroStrideOobFormatsAndWidths() {
|
||||
using O = ShaderOpcode;
|
||||
TestCase test;
|
||||
test.name = "BufferZeroStrideOobFormatsAndWidths";
|
||||
test.initial = std::vector<u32>(20, 0xdeadbeefu);
|
||||
test.expected = std::vector<u32>(20, 0);
|
||||
test.expected[10] = 0x3f800000u;
|
||||
test.expected[12] = 0x3f800000u;
|
||||
test.expected[14] = 1;
|
||||
test.expected[16] = 1;
|
||||
test.expected[18] = 0xabcdef01u;
|
||||
test.expected[19] = 0xdeadbeefu;
|
||||
test.user_data = MakeStructuredStorageBufferData(4, 20);
|
||||
for (u32 base : {4u, 8u}) {
|
||||
test.user_data[base] = 0x4a398620u;
|
||||
test.user_data[base + 2] = 0x2b5u;
|
||||
const u32 format = base == 4 ? 77u : 75u; // Four 32-bit float or unsigned integer components.
|
||||
test.user_data[base + 3] = (format << 12u) | DstSel(1, 4, 1, 0);
|
||||
}
|
||||
test.has_user_data = true;
|
||||
test.expected_buffer_resources = std::vector<u32>{0};
|
||||
auto &code = test.code;
|
||||
AppendVMovU32(&code, 0, 0);
|
||||
AppendStoreVgpr(&code, 0, 0);
|
||||
AppendVMovU32(&code, 20, 0);
|
||||
u32 destination = 0;
|
||||
for (const auto [opcode, width] :
|
||||
{std::pair{0x08u, 1u}, {0x0au, 1u}, {0x0cu, 1u}, {0x0du, 2u}, {0x0fu, 3u}}) {
|
||||
code.push_back(EncodeMubuf0(opcode));
|
||||
code.push_back(EncodeMubuf1(destination, 1, 20));
|
||||
destination += width;
|
||||
}
|
||||
AppendVMovU32(&code, 8, 0);
|
||||
AppendVMovU32(&code, 9, 0);
|
||||
for (u32 base : {1u, 2u}) {
|
||||
code.push_back(EncodeMubuf0(0x03u));
|
||||
code.push_back(EncodeMubuf1(base == 1 ? 10 : 14, base, 20));
|
||||
}
|
||||
AppendVMovLiteral(&code, 18, 0xabcdef01u);
|
||||
code.push_back(EncodeSop1(0x04u, 60, 126u)); // Preserve the partial wave's active lanes.
|
||||
code.push_back(EncodeSop1(0x04u, 126, InlineU32(0)));
|
||||
code.push_back(EncodeMubuf0(0x00u));
|
||||
code.push_back(EncodeMubuf1(18, 1, 20));
|
||||
code.push_back(EncodeSop1(0x04u, 126, 60u));
|
||||
for (u32 i = 0; i < 19; i++) AppendStoreVgpr(&code, i, i);
|
||||
AppendEnd(&code);
|
||||
test.opcodes = {O::V_MOV_B32, O::S_MOV_B64, O::BUFFER_LOAD_UBYTE,
|
||||
O::BUFFER_LOAD_USHORT, O::BUFFER_LOAD_DWORD,
|
||||
O::BUFFER_LOAD_DWORDX2, O::BUFFER_LOAD_DWORDX3,
|
||||
O::BUFFER_LOAD_FORMAT_X, O::BUFFER_LOAD_FORMAT_XYZW,
|
||||
O::BUFFER_STORE_DWORD, O::S_ENDPGM};
|
||||
test.ir_counts = {{"LoadBuffer", 0}};
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase TBufferLoadVariants() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
@@ -29001,6 +29177,7 @@ std::vector<TestCase> MakeCases() {
|
||||
AddCase(VectorVop3MoveAppliesFloatSourceModifiers);
|
||||
AddCase(VectorIntegerOps);
|
||||
AddCase(VectorFfbhI32NativeAndVop3OnGpu);
|
||||
AddCase(Vop1SdwaFfbhCapturedScalarLowWordSource);
|
||||
AddCase(Vop1SdwaFfblCapturedHighWordSource);
|
||||
AddCase(Vop1SdwaNotCapturedByte0Source);
|
||||
AddCase(Vop1SdwaNotPreservesHighWordDestination);
|
||||
@@ -29072,6 +29249,7 @@ std::vector<TestCase> MakeCases() {
|
||||
AddCase(Vop1MoveRelSource);
|
||||
AddCase(Vop1MoveRelDestination);
|
||||
AddCase(VectorFloatSpecialOps);
|
||||
AddCase(CubeIdCapturedNegationAndOutputScale);
|
||||
AddCase(MadMixF16LiteralHalfSourceUsesOpsel);
|
||||
AddCase(MadMixF16NegHiIsAbsAndNegIsIndependent);
|
||||
AddCase(VectorVop3FmaF16UsesRdna2Opcode34b);
|
||||
@@ -29173,6 +29351,10 @@ std::vector<TestCase> MakeCases() {
|
||||
AddCase(BufferStoreFormatXAddTidUsesLaneIndex);
|
||||
AddCase(BufferStoreFormatXDropsOutOfRangeRecord);
|
||||
AddCase(TBufferLoadVariants);
|
||||
AddCase([] { return TBufferCapturedZeroStrideOob(); });
|
||||
AddCase([] { return TBufferCapturedZeroStrideOob(true); });
|
||||
AddCase([] { return TBufferCapturedZeroStrideOob(false, true); });
|
||||
AddCase(BufferZeroStrideOobFormatsAndWidths);
|
||||
AddCase(TBufferLoadFormatXyzwSnapshotsOverlappingAddress);
|
||||
AddCase(TBufferLoadFormatXyzwPackedSnapshotsOverlappingAddress);
|
||||
AddCase(TBufferLoadFormatX8UintZeroExtendsByte);
|
||||
@@ -34160,6 +34342,28 @@ int main(int argc, char **argv) {
|
||||
RunCase(&vulkan, Vop1SdwaMovByteDestinations());
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && std::strcmp(argv[1], "--sdwa-ffbh-only") == 0) {
|
||||
VulkanHarness vulkan;
|
||||
RunCase(&vulkan, Vop1SdwaFfbhCapturedScalarLowWordSource());
|
||||
RunCase(&vulkan, VectorFfbhI32NativeAndVop3OnGpu());
|
||||
RunCase(&vulkan, Vop1SdwaFfblCapturedHighWordSource());
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && std::strcmp(argv[1], "--cubeid-omod-only") == 0) {
|
||||
VulkanHarness vulkan;
|
||||
RunCase(&vulkan, CubeIdCapturedNegationAndOutputScale());
|
||||
RunCase(&vulkan, VectorFloatSpecialOps());
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && std::strcmp(argv[1], "--zero-stride-oob-only") == 0) {
|
||||
VulkanHarness vulkan;
|
||||
RunCase(&vulkan, TBufferCapturedZeroStrideOob());
|
||||
RunCase(&vulkan, TBufferCapturedZeroStrideOob(true));
|
||||
RunCase(&vulkan, TBufferCapturedZeroStrideOob(false, true));
|
||||
RunCase(&vulkan, BufferZeroStrideOobFormatsAndWidths());
|
||||
RunCase(&vulkan, TBufferLoadFormatXIdxenUsesDescriptorStride());
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && std::strcmp(argv[1], "--sdwa-ashr-only") == 0) {
|
||||
VulkanHarness vulkan;
|
||||
RunCase(&vulkan, Vop2SdwaAshrrevCapturedWord0SignExtends());
|
||||
|
||||
Reference in New Issue
Block a user