mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-29 00:33:06 +00:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b95ea4bb5f | ||
|
|
e93b4b0378 | ||
|
|
0fd41ae206 |
No files matched your search
+1
-1
@@ -350,7 +350,7 @@ set(inc_headers
|
||||
${KYTY_THIRD_PARTY_DIR}/SPIRV-Tools/include
|
||||
${KYTY_THIRD_PARTY_DIR}/SPIRV-Headers/include
|
||||
${KYTY_THIRD_PARTY_DIR}/fmt/include
|
||||
${KYTY_THIRD_PARTY_DIR}/xxhash
|
||||
${KYTY_THIRD_PARTY_DIR}/xxHash
|
||||
${KYTY_THIRD_PARTY_DIR}/cpuinfo/include
|
||||
${KYTY_THIRD_PARTY_DIR}/renderdoc
|
||||
${KYTY_THIRD_PARTY_DIR}/stb
|
||||
|
||||
@@ -326,6 +326,7 @@ inline constexpr auto EmitSharedAtomicSMin32 = EmitAtomic32;
|
||||
inline constexpr auto EmitSharedAtomicUMin32 = EmitAtomic32;
|
||||
inline constexpr auto EmitSharedAtomicSMax32 = EmitAtomic32;
|
||||
inline constexpr auto EmitSharedAtomicUMax64 = EmitSharedAtomic64;
|
||||
inline constexpr auto EmitSharedAtomicCmpSwap32 = EmitAtomic32;
|
||||
inline constexpr auto EmitSharedAtomicCmpSwap64 = EmitSharedAtomic64;
|
||||
inline constexpr auto EmitSharedAtomicUMax32 = EmitAtomic32;
|
||||
inline constexpr auto EmitSharedAtomicAnd32 = EmitAtomic32;
|
||||
|
||||
@@ -504,6 +504,7 @@ uint32_t EncodePackedColorComponent(EmitterState& state, const Format::BufferFor
|
||||
|
||||
spv::Op SpirvAtomicOpcode(IR::ValueOpcode opcode) {
|
||||
switch (opcode) {
|
||||
case IR::ValueOpcode::SharedAtomicCmpSwap32:
|
||||
case IR::ValueOpcode::SharedAtomicCmpSwap64:
|
||||
case IR::ValueOpcode::ImageAtomicCompareSwap32:
|
||||
case IR::ValueOpcode::BufferAtomicCmpSwap32: return spv::OpAtomicCompareExchange;
|
||||
@@ -561,8 +562,12 @@ uint32_t EmitAtomicOperation(ValueEmitContext& ctx, const IR::Inst& inst, uint32
|
||||
const bool wide = inst.GetType() == IR::Type::U64;
|
||||
const auto type = wide ? TypeU64(ctx.state) : TypeU32(ctx.state);
|
||||
if (opcode == spv::OpAtomicCompareExchange) {
|
||||
const auto desired = ctx.Arg(inst, inst.NumArgs() - 3);
|
||||
const auto comparator = ctx.Arg(inst, inst.NumArgs() - 2);
|
||||
auto desired = ctx.Arg(inst, inst.NumArgs() - 3);
|
||||
auto comparator = ctx.Arg(inst, inst.NumArgs() - 2);
|
||||
if (inst.GetOpcode() == IR::ValueOpcode::SharedAtomicCmpSwap32) {
|
||||
// RDNA2 DS compares DATA and stores DATA2, opposite to BUFFER atomics.
|
||||
std::swap(desired, comparator);
|
||||
}
|
||||
ctx.state.builder.AddFunction(
|
||||
spv::OpAtomicCompareExchange, TypeU32(ctx.state), old, pointer,
|
||||
ConstantU32(ctx.state, scope), ConstantU32(ctx.state, spv::MemorySemanticsMaskNone),
|
||||
|
||||
@@ -115,6 +115,7 @@ constexpr MemoryOpcodeInfo DS_OPCODE_LIST[] = {
|
||||
{0x27u, Opcode::DS_MIN_RTN_U32, 1, 32}, {0x28u, Opcode::DS_MAX_RTN_U32, 1, 32},
|
||||
{0x29u, Opcode::DS_AND_RTN_B32, 1, 32}, {0x2au, Opcode::DS_OR_RTN_B32, 1, 32},
|
||||
{0x2bu, Opcode::DS_XOR_RTN_B32, 1, 32}, {0x2du, Opcode::DS_WRXCHG_RTN_B32, 1, 32},
|
||||
{0x30u, Opcode::DS_CMPST_RTN_B32, 1, 32},
|
||||
{0x35u, Opcode::DS_SWIZZLE_B32, 1, 32}, {0x36u, Opcode::DS_READ_B32, 1, 32},
|
||||
{0x37u, Opcode::DS_READ2_B32, 2, 32}, {0x38u, Opcode::DS_READ2ST64_B32, 2, 32},
|
||||
{0x39u, Opcode::DS_READ_I8, 1, 8, true}, {0x3au, Opcode::DS_READ_U8, 1, 8},
|
||||
@@ -201,6 +202,7 @@ bool IsDsAtomicOpcode(Opcode opcode) {
|
||||
case Opcode::DS_MAX_U32:
|
||||
case Opcode::DS_MAX_RTN_U32:
|
||||
case Opcode::DS_MAX_RTN_U64:
|
||||
case Opcode::DS_CMPST_RTN_B32:
|
||||
case Opcode::DS_CMPST_RTN_B64:
|
||||
case Opcode::DS_AND_B32:
|
||||
case Opcode::DS_AND_RTN_B32:
|
||||
@@ -221,6 +223,7 @@ uint32_t DsSourceCount(Opcode opcode) {
|
||||
case Opcode::DS_WRITE2ST64_B32:
|
||||
case Opcode::DS_WRITE2_B64:
|
||||
case Opcode::DS_WRITE2ST64_B64:
|
||||
case Opcode::DS_CMPST_RTN_B32:
|
||||
case Opcode::DS_CMPST_RTN_B64:
|
||||
case Opcode::DS_MSKOR_B32: return 3u;
|
||||
case Opcode::DS_MIN_F32:
|
||||
@@ -455,6 +458,9 @@ void DecodeDs(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
|
||||
if (inst.opcode == Opcode::DS_SWIZZLE_B32 && inst.offset >= 0xe000u) {
|
||||
SetUnsupported(inst, Family::DS, opcode, "DS swizzle FFT mode is not implemented");
|
||||
}
|
||||
if (inst.gds && inst.opcode == Opcode::DS_CMPST_RTN_B32) {
|
||||
SetUnsupported(inst, Family::DS, opcode, "DS compare-store return B32 GDS variant is not implemented");
|
||||
}
|
||||
if (inst.gds &&
|
||||
(inst.opcode == Opcode::DS_SWIZZLE_B32 || inst.opcode == Opcode::DS_PERMUTE_B32 ||
|
||||
inst.opcode == Opcode::DS_BPERMUTE_B32 ||
|
||||
|
||||
@@ -718,6 +718,7 @@ std::string InstructionToString(const Instruction& inst) {
|
||||
case Opcode::DS_MAX_U32:
|
||||
case Opcode::DS_MAX_RTN_U32:
|
||||
case Opcode::DS_MAX_RTN_U64:
|
||||
case Opcode::DS_CMPST_RTN_B32:
|
||||
case Opcode::DS_CMPST_RTN_B64:
|
||||
case Opcode::DS_AND_B32:
|
||||
case Opcode::DS_AND_RTN_B32:
|
||||
|
||||
@@ -632,6 +632,7 @@ enum class Opcode {
|
||||
DS_MAX_U32,
|
||||
DS_MAX_RTN_U32,
|
||||
DS_MAX_RTN_U64,
|
||||
DS_CMPST_RTN_B32,
|
||||
DS_CMPST_RTN_B64,
|
||||
DS_AND_B32,
|
||||
DS_AND_RTN_B32,
|
||||
|
||||
@@ -1085,6 +1085,8 @@ void Translator::EmitMemory(const Decoder::Instruction& inst) {
|
||||
return DS_ATOMIC(inst, IR::ValueOpcode::SharedAtomicUMax32, true);
|
||||
case Decoder::Opcode::DS_MAX_RTN_U64:
|
||||
return DS_ATOMIC(inst, IR::ValueOpcode::SharedAtomicUMax64, true);
|
||||
case Decoder::Opcode::DS_CMPST_RTN_B32:
|
||||
return DS_ATOMIC(inst, IR::ValueOpcode::SharedAtomicCmpSwap32, true);
|
||||
case Decoder::Opcode::DS_CMPST_RTN_B64:
|
||||
return DS_ATOMIC(inst, IR::ValueOpcode::SharedAtomicCmpSwap64, true);
|
||||
case Decoder::Opcode::DS_AND_B32:
|
||||
|
||||
@@ -178,6 +178,7 @@ SharedAccess SharedAccessOf(ValueOpcode opcode) {
|
||||
case ValueOpcode::SharedAtomicSMax32:
|
||||
case ValueOpcode::SharedAtomicUMax32:
|
||||
case ValueOpcode::SharedAtomicUMax64:
|
||||
case ValueOpcode::SharedAtomicCmpSwap32:
|
||||
case ValueOpcode::SharedAtomicCmpSwap64:
|
||||
case ValueOpcode::SharedAtomicAnd32:
|
||||
case ValueOpcode::SharedAtomicOr32:
|
||||
|
||||
@@ -307,6 +307,7 @@ VALUE_OPCODE(SharedAtomicUMin32, U32, U32, U32, U1)
|
||||
VALUE_OPCODE(SharedAtomicSMax32, U32, U32, U32, U1)
|
||||
VALUE_OPCODE(SharedAtomicUMax32, U32, U32, U32, U1)
|
||||
VALUE_OPCODE(SharedAtomicUMax64, U64, U32, U64, U1)
|
||||
VALUE_OPCODE(SharedAtomicCmpSwap32, U32, U32, U32, U32, U1)
|
||||
VALUE_OPCODE(SharedAtomicCmpSwap64, U64, U32, U64, U64, U1)
|
||||
VALUE_OPCODE(SharedAtomicAnd32, U32, U32, U32, U1)
|
||||
VALUE_OPCODE(SharedAtomicOr32, U32, U32, U32, U1)
|
||||
|
||||
@@ -338,14 +338,20 @@ static bool HasWindowsForbiddenFilenameCharacter(const std::string& relative_pat
|
||||
std::filesystem::path MountPoints::ResolvePath(const std::string& mounted_name) {
|
||||
Common::LockGuard lock(m_mutex);
|
||||
|
||||
// Guests join mount points and names with extra slashes (e.g. "/addcont0//file").
|
||||
std::string normalized = Common::FixFilenameSlash(mounted_name);
|
||||
normalized.erase(std::unique(normalized.begin(), normalized.end(),
|
||||
[](char a, char b) { return a == '/' && b == '/'; }),
|
||||
normalized.end());
|
||||
|
||||
// Match the entire guest path so a mount root works with or without a trailing slash.
|
||||
const auto mounted_path = Common::FixDirectorySlash(mounted_name);
|
||||
const auto mounted_path = Common::FixDirectorySlash(normalized);
|
||||
const auto it = std::find_if(
|
||||
m_mount_pairs.begin(), m_mount_pairs.end(),
|
||||
[&mounted_path](const MountPair& p) { return mounted_path.starts_with(p.point); });
|
||||
if (it != m_mount_pairs.end()) {
|
||||
const auto& p = *it;
|
||||
auto rel_path = Common::RemoveFirst(Common::FixFilenameSlash(mounted_name), p.point.size());
|
||||
auto rel_path = Common::RemoveFirst(normalized, p.point.size());
|
||||
while (rel_path.starts_with('/')) {
|
||||
rel_path = Common::RemoveFirst(rel_path, 1);
|
||||
}
|
||||
|
||||
@@ -25,6 +25,9 @@
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <atomic>
|
||||
#include <cinttypes>
|
||||
#include <iterator>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
@@ -176,6 +179,14 @@ static int GetHostPathStat(const std::filesystem::path& host_path, LibKernel::Fi
|
||||
return OK;
|
||||
}
|
||||
|
||||
// Mounts can appear after a failed lookup (e.g. additional content), so forget negative results.
|
||||
void InvalidateFailedResolves() {
|
||||
std::scoped_lock lock(g_mutex);
|
||||
for (auto it = g_resolved_paths.begin(); it != g_resolved_paths.end();) {
|
||||
it = it->second.result != OK ? g_resolved_paths.erase(it) : std::next(it);
|
||||
}
|
||||
}
|
||||
|
||||
static int ResolveOnePath(const char* guest_path, uint32_t* id, uint64_t* size) {
|
||||
if (guest_path == nullptr || guest_path[0] == '\0') {
|
||||
return LibKernel::KERNEL_ERROR_EINVAL;
|
||||
@@ -225,6 +236,10 @@ static int ResolveOnePath(const char* guest_path, uint32_t* id, uint64_t* size)
|
||||
log_missing = true;
|
||||
}
|
||||
}
|
||||
if (!log_missing && info.result == OK && path.starts_with("/addcont")) {
|
||||
LOGF("\tAPR resolve: %s -> %s (%" PRIu64 " bytes)\n", guest_path,
|
||||
Common::PathToString(info.host_path).c_str(), info.file_size);
|
||||
}
|
||||
if (log_missing) {
|
||||
LOGF("\tAPR resolve missing path: %s -> %s\n", guest_path, Common::PathToString(info.host_path).c_str());
|
||||
}
|
||||
@@ -1181,6 +1196,13 @@ static int ExecuteCommand(const CommandBufferState::Command& entry) {
|
||||
uint64_t bytes_read = 0;
|
||||
auto result = ReadHostFileToGuest(host_path, command.file_offset, command.destination,
|
||||
command.size, &bytes_read);
|
||||
static std::atomic<uint32_t> s_logged_dlc_reads {0};
|
||||
if (host_path.filename() == "re_dlc_000.pak" && s_logged_dlc_reads.fetch_add(1) < 32) {
|
||||
LOGF("\tAPR read dlc: id=0x%08" PRIx32 ", offset=0x%016" PRIx64 ", size=0x%016" PRIx64
|
||||
", read=0x%016" PRIx64 ", result=0x%08" PRIx32 "\n",
|
||||
command.file_id, command.file_offset, command.size, bytes_read,
|
||||
static_cast<uint32_t>(result));
|
||||
}
|
||||
if (result != OK) {
|
||||
LOGF("\tAPR submit read failed: id=0x%08" PRIx32 ", result=0x%08" PRIx32 ", path=%s\n",
|
||||
command.file_id, static_cast<uint32_t>(result), Common::PathToString(host_path).c_str());
|
||||
|
||||
@@ -22,13 +22,16 @@
|
||||
|
||||
namespace Libs {
|
||||
|
||||
namespace AprShared {
|
||||
void InvalidateFailedResolves();
|
||||
}
|
||||
|
||||
LIB_VERSION("AppContent", 1, "AppContentUtil", 1, 1);
|
||||
|
||||
namespace AppContent {
|
||||
|
||||
constexpr uint32_t APP_CONTENT_APPPARAM_SKU_FLAG_FULL = 3;
|
||||
constexpr int APP_CONTENT_ERROR_PARAMETER = -2133262334; /* 0x80D90002 */
|
||||
constexpr int APP_CONTENT_ERROR_BUSY = -2133262333; /* 0x80D90003 */
|
||||
constexpr int APP_CONTENT_ERROR_NOT_MOUNTED = -2133262332; /* 0x80D90004 */
|
||||
constexpr int APP_CONTENT_ERROR_NOT_FOUND = -2133262331; /* 0x80D90005 */
|
||||
constexpr int APP_CONTENT_ERROR_MOUNT_FULL = -2133262330; /* 0x80D90006 */
|
||||
@@ -207,7 +210,10 @@ int KYTY_SYSV_ABI AppContentAddcontMount(uint32_t servic
|
||||
|
||||
for (const auto& m: g_addcont_mounts) {
|
||||
if (m.label == entry->entitlement_label) {
|
||||
return APP_CONTENT_ERROR_BUSY;
|
||||
const auto guest_point = m.point.substr(0, m.point.size() - 1);
|
||||
std::strncpy(mount_point->data, guest_point.c_str(), sizeof(mount_point->data) - 1);
|
||||
LOGF("\t already mounted %s\n", guest_point.c_str());
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,7 +238,15 @@ int KYTY_SYSV_ABI AppContentAddcontMount(uint32_t servic
|
||||
LOGF("\t mounted %s -> %s\n", mount.point.c_str(),
|
||||
Common::PathToString(entry->mount_dir).c_str());
|
||||
|
||||
std::strncpy(mount_point->data, mount.point.c_str(), sizeof(mount_point->data) - 1);
|
||||
AprShared::InvalidateFailedResolves();
|
||||
|
||||
// Titles scan /download0/pdlc for downloaded content; it must exist even when empty.
|
||||
if (const auto pdlc = LibKernel::FileSystem::GetRealFilename("/download0/pdlc"); !pdlc.empty()) {
|
||||
std::filesystem::create_directories(pdlc, ec);
|
||||
}
|
||||
|
||||
const auto guest_point = mount.point.substr(0, mount.point.size() - 1);
|
||||
std::strncpy(mount_point->data, guest_point.c_str(), sizeof(mount_point->data) - 1);
|
||||
g_addcont_mounts.push_back(std::move(mount));
|
||||
|
||||
return OK;
|
||||
@@ -251,8 +265,8 @@ int KYTY_SYSV_ABI AppContentAddcontUnmount(const AppContentMountPoint* mount_poi
|
||||
return APP_CONTENT_ERROR_NOT_MOUNTED;
|
||||
}
|
||||
|
||||
const std::string point(mount_point->data,
|
||||
strnlen(mount_point->data, sizeof(mount_point->data)));
|
||||
const std::string point = Common::FixDirectorySlash(
|
||||
std::string_view(mount_point->data, strnlen(mount_point->data, sizeof(mount_point->data))));
|
||||
|
||||
std::lock_guard lock(g_addcont_mutex);
|
||||
|
||||
@@ -263,6 +277,7 @@ int KYTY_SYSV_ABI AppContentAddcontUnmount(const AppContentMountPoint* mount_poi
|
||||
}
|
||||
LibKernel::FileSystem::Umount(point);
|
||||
g_addcont_mounts.erase(it);
|
||||
AprShared::InvalidateFailedResolves();
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
@@ -221,11 +221,17 @@ int KYTY_SYSV_ABI PlayGoGetChunkId(int handle, uint16_t* out_chunk_id_list,
|
||||
if (out_entries == nullptr) {
|
||||
return PLAYGO_ERROR_BAD_POINTER;
|
||||
}
|
||||
if (number_of_entries != 0 && out_chunk_id_list == nullptr) {
|
||||
return PLAYGO_ERROR_BAD_POINTER;
|
||||
if (out_chunk_id_list != nullptr && number_of_entries == 0) {
|
||||
return PLAYGO_ERROR_BAD_SIZE;
|
||||
}
|
||||
ensure_chunks_loaded();
|
||||
|
||||
// A null output buffer asks for the total chunk count.
|
||||
if (out_chunk_id_list == nullptr) {
|
||||
*out_entries = g_chunks_num;
|
||||
return OK;
|
||||
}
|
||||
|
||||
const uint32_t entries = std::min(number_of_entries, g_chunks_num);
|
||||
for (uint32_t i = 0; i < entries; i++) {
|
||||
out_chunk_id_list[i] = static_cast<uint16_t>(i);
|
||||
|
||||
@@ -413,7 +413,9 @@ void TestAddContent(const std::filesystem::path& root) {
|
||||
Check(mount(0, nullptr, &point) == static_cast<int>(0x80d90002u) &&
|
||||
list(0, nullptr, 0, nullptr) == static_cast<int>(0x817d0002u),
|
||||
"null add-on parameters return parameter errors");
|
||||
Check(mount(0, &info[3].label, &point) == OK, "mount owned add-on");
|
||||
Check(mount(0, &info[3].label, &point) == OK &&
|
||||
!std::string_view(point.data).ends_with('/'),
|
||||
"mount owned add-on without trailing slash");
|
||||
const std::string mounted_file = std::string(point.data) + "/payload.txt";
|
||||
const int fd = FileSystem::KernelOpen(mounted_file.c_str(), 0, 0);
|
||||
std::array<char, 11> payload {};
|
||||
@@ -422,8 +424,9 @@ void TestAddContent(const std::filesystem::path& root) {
|
||||
FileSystem::KernelClose(fd) == OK,
|
||||
"guest reads files from the selected title's add-on");
|
||||
MountPoint repeated {};
|
||||
Check(mount(0, &info[3].label, &repeated) == static_cast<int>(0x80d90003u),
|
||||
"duplicate mount returns BUSY");
|
||||
Check(mount(0, &info[3].label, &repeated) == OK &&
|
||||
std::strcmp(repeated.data, point.data) == 0,
|
||||
"duplicate mount returns the same mount point");
|
||||
Check(unmount(&point) == OK && FileSystem::GetRealFilename(mounted_file).empty() &&
|
||||
unmount(&point) == static_cast<int>(0x80d90004u),
|
||||
"unmount removes mapping and repeated unmount reports NOT_MOUNTED");
|
||||
|
||||
@@ -21015,6 +21015,7 @@ CoverageClass ClassifyOpcode(ShaderOpcode opcode,
|
||||
case Opcode::DS_ADD_U32:
|
||||
case Opcode::DS_ADD_U64:
|
||||
case Opcode::DS_MAX_RTN_U64:
|
||||
case Opcode::DS_CMPST_RTN_B32:
|
||||
case Opcode::DS_CMPST_RTN_B64:
|
||||
case Opcode::DS_ADD_RTN_U32:
|
||||
case Opcode::DS_SUB_U32:
|
||||
@@ -34698,6 +34699,60 @@ TestCase DsAtomicReturn64() {
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase DsCompareStoreReturn32() {
|
||||
using O = ShaderOpcode;
|
||||
TestCase test;
|
||||
test.name = "DsCompareStoreReturn32";
|
||||
auto &code = test.code;
|
||||
const auto result = [&](u32 reg, u32 value) {
|
||||
AppendStoreVgpr(&code, reg, static_cast<u32>(test.expected.size()));
|
||||
test.expected.push_back(value);
|
||||
};
|
||||
struct Case { u32 before, compare, replacement; };
|
||||
constexpr std::array cases{Case{0x12345678u, 0x12345678u, 0x9abcdef0u},
|
||||
Case{0x12345678u, 0x9abcdef0u, 0x12345678u},
|
||||
Case{0x80000000u, 0u, 0x7fffffffu},
|
||||
Case{UINT32_MAX, UINT32_MAX, 0u}};
|
||||
AppendVMovU32(&code, 7, 0);
|
||||
for (const auto &item : cases) {
|
||||
AppendVMovLiteral(&code, 2, item.before);
|
||||
code.push_back(EncodeDs0(0x0d, 8));
|
||||
code.push_back(EncodeDs1(0, 2, 7));
|
||||
AppendVMovLiteral(&code, 5, item.compare);
|
||||
AppendVMovLiteral(&code, 19, item.replacement);
|
||||
AppendVMovU32(&code, 4, 8);
|
||||
// CS04b812daaacd532f pc 0x3e8: DATA compares, DATA2 replaces, VDst aliases ADDR.
|
||||
code.insert(code.end(), {0xd8c00000u, 0x04130504u});
|
||||
result(4, item.before);
|
||||
code.push_back(EncodeDs0(0x36, 8));
|
||||
code.push_back(EncodeDs1(8, 0, 7));
|
||||
result(8, item.before == item.compare ? item.replacement : item.before);
|
||||
}
|
||||
AppendVMovU32(&code, 5, 0);
|
||||
AppendVMovU32(&code, 4, 12); // Past the three LDS DWORDs.
|
||||
code.insert(code.end(), {0xd8c00000u, 0x04130504u});
|
||||
result(4, 0);
|
||||
AppendVMovLiteral(&code, 2, 0x12345678u);
|
||||
code.push_back(EncodeDs0(0x0d, 8));
|
||||
code.push_back(EncodeDs1(0, 2, 7));
|
||||
AppendVMovLiteral(&code, 5, 0x12345678u);
|
||||
AppendVMovU32(&code, 4, 8);
|
||||
code.push_back(EncodeSMovB32(126, InlineU32(0)));
|
||||
code.insert(code.end(), {0xd8c00000u, 0x04130504u});
|
||||
code.push_back(EncodeSMovB32(126, InlineU32(1)));
|
||||
result(4, 8); // An inactive lane keeps its destination and leaves memory alone.
|
||||
code.push_back(EncodeDs0(0x36, 8));
|
||||
code.push_back(EncodeDs1(8, 0, 7));
|
||||
result(8, 0x12345678u);
|
||||
AppendEnd(&code);
|
||||
test.compute_info.lds_size_dwords = 3;
|
||||
test.opcodes = {O::V_MOV_B32, O::S_MOV_B32, O::DS_WRITE_B32, O::DS_CMPST_RTN_B32,
|
||||
O::DS_READ_B32, O::BUFFER_STORE_DWORD, O::S_ENDPGM};
|
||||
test.required_spirv = {"OpAtomicCompareExchange %uint"};
|
||||
test.forbidden_spirv = {"OpLoopMerge"};
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase DsAtomic64Contention(bool add, u32 wave_size) {
|
||||
using O = ShaderOpcode;
|
||||
TestCase test;
|
||||
@@ -42276,6 +42331,7 @@ std::vector<TestCase> MakeCases() {
|
||||
}
|
||||
AddCase(DsAtomicReturnVariants);
|
||||
AddCase(DsAtomicReturn64);
|
||||
AddCase(DsCompareStoreReturn32);
|
||||
for (bool add : {false, true}) {
|
||||
cases.push_back(DsAtomic64Bounds(add));
|
||||
for (u32 wave_size : {32, 64}) {
|
||||
@@ -47800,7 +47856,8 @@ int main(int argc, char **argv) {
|
||||
RunCase(&vulkan, DsWideLdsPartialBounds());
|
||||
RunCase(&vulkan, DsAtomic64Bounds(false));
|
||||
RunCase(&vulkan, DsAtomic64Bounds(true));
|
||||
for (auto test : {DsWideLdsPartialBounds(), DsAtomic64Bounds(false), DsAtomic64Bounds(true), DsAtomicReturn64()}) {
|
||||
for (auto test : {DsWideLdsPartialBounds(), DsAtomic64Bounds(false), DsAtomic64Bounds(true), DsAtomicReturn64(),
|
||||
DsCompareStoreReturn32()}) {
|
||||
test.compute_info.lds_storage = true;
|
||||
std::erase(test.required_spirv, "WorkgroupMemoryExplicitLayoutKHR");
|
||||
test.required_spirv.push_back("StorageBuffer");
|
||||
|
||||
@@ -8119,6 +8119,24 @@ void TestDsReturningAtomic64Decoder() {
|
||||
"captured DS_CMPST_RTN_B64 lost its comparison/replacement pairs or aliases");
|
||||
}
|
||||
|
||||
void TestDsCompareStore32Decoder() {
|
||||
using namespace ShaderRecompiler::Decoder;
|
||||
// CS04b812daaacd532f pc 0x3e8 (AC Valhalla, #281).
|
||||
const uint32_t words[] = {0xd8c00000u, 0x04130504u};
|
||||
Instruction decoded;
|
||||
DecodeInstruction(words, 0u, decoded);
|
||||
Check(decoded.opcode == Opcode::DS_CMPST_RTN_B32 && decoded.word_count == 2u &&
|
||||
decoded.data_dwords == 1u && decoded.src_count == 3u && decoded.src0.reg == 4u &&
|
||||
decoded.src1.reg == 5u && decoded.src2.reg == 19u && decoded.dst.reg == 4u &&
|
||||
decoded.offset == 0u && !decoded.gds,
|
||||
"captured DS_CMPST_RTN_B32 lost its comparison/replacement order or aliases");
|
||||
|
||||
const uint32_t gds_words[] = {words[0] | (1u << 17u), words[1]};
|
||||
DecodeInstruction(gds_words, 0u, decoded);
|
||||
Check(decoded.opcode == Opcode::UNSUPPORTED && decoded.word_count == 2u && decoded.gds,
|
||||
"DS_CMPST_RTN_B32 must reject the unimplemented GDS variant");
|
||||
}
|
||||
|
||||
void TestDsFloatMinMaxDecoder() {
|
||||
using namespace ShaderRecompiler::Decoder;
|
||||
for (bool max_value : {false, true}) {
|
||||
@@ -16480,6 +16498,7 @@ int main() {
|
||||
TestScalarAshrI64Decoder();
|
||||
TestImageAtomicWidthDecoder();
|
||||
TestDsReturningAtomic64Decoder();
|
||||
TestDsCompareStore32Decoder();
|
||||
TestDsFloatMinMaxDecoder();
|
||||
TestNewShaderDecoderArchitecture();
|
||||
TestImageAddressOperands();
|
||||
|
||||
Reference in new issue
Block a user