mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-23 04:16:57 +02:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d7da244475 | |||
| 549495575c | |||
| c3cc8454b9 | |||
| b8ad1d8c4b | |||
| cfb110e0aa |
@@ -43,9 +43,6 @@ jobs:
|
||||
- name: Setup Visual Studio environment
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
|
||||
- name: Setup Ninja
|
||||
uses: seanmiddleditch/gha-setup-ninja@v5
|
||||
|
||||
- name: Locate vcpkg
|
||||
id: vcpkg
|
||||
shell: pwsh
|
||||
@@ -155,7 +152,7 @@ jobs:
|
||||
|
||||
- name: Install build dependencies
|
||||
shell: bash
|
||||
run: brew install glslang ninja
|
||||
run: brew install glslang
|
||||
|
||||
- name: Install Qt 6.10.3
|
||||
uses: jurplel/install-qt-action@v4
|
||||
@@ -318,7 +315,6 @@ jobs:
|
||||
libxrandr-dev \
|
||||
libxss-dev \
|
||||
lld \
|
||||
ninja-build \
|
||||
pkg-config \
|
||||
wayland-protocols
|
||||
|
||||
|
||||
@@ -12,6 +12,22 @@ endif()
|
||||
|
||||
project(Kyty)
|
||||
|
||||
find_program(SCCACHE_PROGRAM sccache)
|
||||
find_program(CCACHE_PROGRAM ccache)
|
||||
|
||||
foreach(lang IN ITEMS C CXX)
|
||||
# An explicitly empty launcher disables caching for this language.
|
||||
if(NOT DEFINED CMAKE_${lang}_COMPILER_LAUNCHER)
|
||||
if(SCCACHE_PROGRAM)
|
||||
set(CMAKE_${lang}_COMPILER_LAUNCHER "${SCCACHE_PROGRAM}")
|
||||
message(STATUS "sccache enabled for ${lang}: ${SCCACHE_PROGRAM}")
|
||||
elseif(CCACHE_PROGRAM)
|
||||
set(CMAKE_${lang}_COMPILER_LAUNCHER "${CCACHE_PROGRAM}")
|
||||
message(STATUS "ccache enabled for ${lang}: ${CCACHE_PROGRAM}")
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
set(KYTY_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src")
|
||||
set(KYTY_TESTS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/tests")
|
||||
set(KYTY_THIRD_PARTY_DIR "${CMAKE_CURRENT_SOURCE_DIR}/3rdparty")
|
||||
|
||||
@@ -445,45 +445,6 @@ bool File::SetLastAccessAndWriteTimeUTC(const std::filesystem::path& name, const
|
||||
return SysFileSetLastAccessAndWriteTimeUtc(name, af, wf);
|
||||
}
|
||||
|
||||
std::vector<File::FindInfo> File::FindFiles(const std::filesystem::path& path) {
|
||||
std::vector<sys_file_find_t> files;
|
||||
|
||||
SysFileFindFiles(path, files);
|
||||
|
||||
const auto normalized_path = path.lexically_normal();
|
||||
std::vector<File::FindInfo> ret;
|
||||
ret.reserve(files.size());
|
||||
|
||||
for (const auto& f: files) {
|
||||
File::FindInfo r {};
|
||||
|
||||
r.path_with_name = f.path_with_name;
|
||||
r.rel_path_with_name = f.path_with_name.lexically_normal().lexically_relative(normalized_path);
|
||||
r.size = f.size;
|
||||
|
||||
SysTimeStruct at {};
|
||||
SysTimeStruct wt {};
|
||||
|
||||
SysFileToSystemTimeUtc(f.last_access_time, at);
|
||||
SysFileToSystemTimeUtc(f.last_write_time, wt);
|
||||
|
||||
if (!at.is_invalid && !wt.is_invalid) {
|
||||
r.last_access_time = DateTime(Date(at.Year, at.Month, at.Day),
|
||||
Time(at.Hour, at.Minute, at.Second, at.Milliseconds));
|
||||
r.last_write_time = DateTime(Date(wt.Year, wt.Month, wt.Day),
|
||||
Time(wt.Hour, wt.Minute, wt.Second, wt.Milliseconds));
|
||||
}
|
||||
|
||||
// printf("%s, %s, %" PRIu64", %s, %s\n", r.path_with_name.c_str(),
|
||||
// r.rel_path_with_name.c_str(), r.size, r.last_access_time.ToString().c_str(),
|
||||
// r.last_write_time.ToString().c_str());
|
||||
|
||||
ret.push_back(std::move(r));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<File::DirEntry> File::GetDirEntries(const std::filesystem::path& path) {
|
||||
std::vector<sys_dir_entry_t> files;
|
||||
|
||||
|
||||
@@ -30,14 +30,6 @@ class File {
|
||||
public:
|
||||
enum class Mode { Read, Write, ReadWrite, WriteRead };
|
||||
|
||||
struct FindInfo {
|
||||
std::filesystem::path path_with_name;
|
||||
std::filesystem::path rel_path_with_name;
|
||||
DateTime last_access_time;
|
||||
DateTime last_write_time;
|
||||
uint64_t size;
|
||||
};
|
||||
|
||||
struct DirEntry {
|
||||
std::string name;
|
||||
bool is_file;
|
||||
@@ -98,7 +90,6 @@ public:
|
||||
static bool SetLastAccessAndWriteTimeUTC(const std::filesystem::path& name,
|
||||
const DateTime& access, const DateTime& write);
|
||||
|
||||
static std::vector<FindInfo> FindFiles(const std::filesystem::path& path);
|
||||
static std::vector<DirEntry> GetDirEntries(const std::filesystem::path& path);
|
||||
static bool CopyFile(const std::filesystem::path& src, const std::filesystem::path& dst);
|
||||
static bool RenameFile(const std::filesystem::path& src, const std::filesystem::path& dst);
|
||||
|
||||
@@ -18,14 +18,6 @@ enum sys_file_cache_type_t {
|
||||
SYS_FILE_CACHE_SEQUENTIAL_SCAN = 2 // NOLINT(readability-identifier-naming)
|
||||
};
|
||||
|
||||
// NOLINTNEXTLINE(readability-identifier-naming)
|
||||
struct sys_file_find_t {
|
||||
std::filesystem::path path_with_name;
|
||||
SysFileTimeStruct last_access_time;
|
||||
SysFileTimeStruct last_write_time;
|
||||
uint64_t size;
|
||||
};
|
||||
|
||||
// NOLINTNEXTLINE(readability-identifier-naming)
|
||||
struct sys_dir_entry_t {
|
||||
std::string name;
|
||||
@@ -75,8 +67,6 @@ bool SysFileSetLastWriteTimeUtc(const std::filesystem::path& name, SysFileTimeSt
|
||||
bool SysFileSetLastAccessAndWriteTimeUtc(const std::filesystem::path& name,
|
||||
SysFileTimeStruct& access, SysFileTimeStruct& write);
|
||||
// NOLINTNEXTLINE(google-runtime-references)
|
||||
void SysFileFindFiles(const std::filesystem::path& path, std::vector<sys_file_find_t>& out);
|
||||
// NOLINTNEXTLINE(google-runtime-references)
|
||||
void SysFileGetDents(const std::filesystem::path& path, std::vector<sys_dir_entry_t>& out);
|
||||
bool SysFileCopyFile(const std::filesystem::path& src, const std::filesystem::path& dst);
|
||||
bool SysFileRenameFile(const std::filesystem::path& src, const std::filesystem::path& dst);
|
||||
|
||||
@@ -610,51 +610,6 @@ bool SysFileSetLastAccessAndWriteTimeUtc(const std::filesystem::path& name,
|
||||
// }
|
||||
}
|
||||
|
||||
// Recursively collect regular files.
|
||||
void SysFileFindFiles(const std::filesystem::path& path, std::vector<sys_file_find_t>& out) {
|
||||
auto real_path = get_internal_name(path);
|
||||
|
||||
DIR* dir = opendir(real_path.string().c_str());
|
||||
if (dir == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const dirent* entry = readdir(dir); entry != nullptr; entry = readdir(dir)) {
|
||||
const std::string file_name(entry->d_name);
|
||||
|
||||
if (file_name == "." || file_name == "..") {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto child = real_path / file_name;
|
||||
struct stat s {};
|
||||
|
||||
// lstat, so a symlink is never followed into a cycle during the recursive walk.
|
||||
if (0 != lstat(child.string().c_str(), &s)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (S_ISDIR(s.st_mode)) {
|
||||
SysFileFindFiles(child, out);
|
||||
} else if (S_ISREG(s.st_mode)) {
|
||||
sys_file_find_t r {};
|
||||
|
||||
r.path_with_name = child;
|
||||
r.size = static_cast<uint64_t>(s.st_size);
|
||||
r.last_access_time.is_invalid = false;
|
||||
r.last_access_time.time = s.st_atime;
|
||||
r.last_access_time.nanos = KYTY_STAT_ATIME_NS(s);
|
||||
r.last_write_time.is_invalid = false;
|
||||
r.last_write_time.time = s.st_mtime;
|
||||
r.last_write_time.nanos = KYTY_STAT_MTIME_NS(s);
|
||||
|
||||
out.push_back(r);
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
// Keep "." and ".." to match FindFirstFileW.
|
||||
void SysFileGetDents(const std::filesystem::path& path, std::vector<sys_dir_entry_t>& out) {
|
||||
auto real_path = get_internal_name(path);
|
||||
|
||||
@@ -57,10 +57,10 @@ static DWORD GetCacheAccessType(sys_file_cache_type_t t) {
|
||||
}
|
||||
|
||||
if (t == SYS_FILE_CACHE_SEQUENTIAL_SCAN) {
|
||||
return SYS_FILE_CACHE_SEQUENTIAL_SCAN;
|
||||
return FILE_FLAG_SEQUENTIAL_SCAN;
|
||||
}
|
||||
|
||||
return SYS_FILE_CACHE_AUTO;
|
||||
return FILE_ATTRIBUTE_NORMAL;
|
||||
}
|
||||
|
||||
void SysFileRead(void* data, uint32_t size, sys_file_t& f, uint32_t* bytes_read) {
|
||||
@@ -476,46 +476,6 @@ bool SysFileSetLastAccessAndWriteTimeUtc(const std::filesystem::path& name,
|
||||
return ok;
|
||||
}
|
||||
|
||||
void SysFileFindFiles(const std::filesystem::path& path, std::vector<sys_file_find_t>& out) {
|
||||
const auto pattern = path / L"*";
|
||||
|
||||
WIN32_FIND_DATAW data {};
|
||||
HANDLE h = FindFirstFileW(pattern.c_str(), &data);
|
||||
|
||||
if (h == INVALID_HANDLE_VALUE) {
|
||||
return;
|
||||
}
|
||||
|
||||
do {
|
||||
std::filesystem::path file_name(data.cFileName);
|
||||
|
||||
if (file_name == L"." || file_name == L"..") {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto full_path = path / file_name;
|
||||
|
||||
if ((data.dwFileAttributes & static_cast<DWORD>(FILE_ATTRIBUTE_DIRECTORY)) != 0u) {
|
||||
SysFileFindFiles(full_path, out);
|
||||
} else {
|
||||
sys_file_find_t r {};
|
||||
|
||||
r.path_with_name = full_path;
|
||||
r.size = (static_cast<uint64_t>(data.nFileSizeHigh) << 32u) +
|
||||
static_cast<uint64_t>(data.nFileSizeLow);
|
||||
r.last_access_time.is_invalid = false;
|
||||
r.last_access_time.time = data.ftLastAccessTime;
|
||||
r.last_write_time.is_invalid = false;
|
||||
r.last_write_time.time = data.ftLastWriteTime;
|
||||
|
||||
out.push_back(std::move(r));
|
||||
}
|
||||
|
||||
} while (FindNextFileW(h, &data) != 0);
|
||||
|
||||
FindClose(h);
|
||||
}
|
||||
|
||||
void SysFileGetDents(const std::filesystem::path& path, std::vector<sys_dir_entry_t>& out) {
|
||||
const auto pattern = path / L"*";
|
||||
|
||||
|
||||
@@ -74,7 +74,8 @@ constexpr OpcodeMap SOPC_OPCODE_LIST[] = {
|
||||
{0x03u, Opcode::S_CMP_GE_I32}, {0x04u, Opcode::S_CMP_LT_I32}, {0x05u, Opcode::S_CMP_LE_I32},
|
||||
{0x06u, Opcode::S_CMP_EQ_U32}, {0x07u, Opcode::S_CMP_LG_U32}, {0x08u, Opcode::S_CMP_GT_U32},
|
||||
{0x09u, Opcode::S_CMP_GE_U32}, {0x0au, Opcode::S_CMP_LT_U32}, {0x0bu, Opcode::S_CMP_LE_U32},
|
||||
{0x0cu, Opcode::S_BITCMP0_B32}, {0x0du, Opcode::S_BITCMP1_B32}, {0x12u, Opcode::S_CMP_EQ_U64},
|
||||
{0x0cu, Opcode::S_BITCMP0_B32}, {0x0du, Opcode::S_BITCMP1_B32},
|
||||
{0x0eu, Opcode::S_BITCMP0_B64}, {0x0fu, Opcode::S_BITCMP1_B64}, {0x12u, Opcode::S_CMP_EQ_U64},
|
||||
{0x13u, Opcode::S_CMP_LG_U64},
|
||||
};
|
||||
|
||||
|
||||
@@ -73,6 +73,8 @@ enum class Opcode {
|
||||
S_SUB_I32,
|
||||
S_BITCMP0_B32,
|
||||
S_BITCMP1_B32,
|
||||
S_BITCMP0_B64,
|
||||
S_BITCMP1_B64,
|
||||
S_BITSET0_B32,
|
||||
S_BITSET1_B32,
|
||||
S_BITSET0_B64,
|
||||
|
||||
@@ -521,7 +521,8 @@ constexpr Vop1SdwaRule VOP1_SDWA_RULES[] = {
|
||||
{Opcode::V_CVT_F32_U32, SdwaSelBytes() | SdwaSelWords() | SdwaSelFull(), 0, 0, false},
|
||||
{Opcode::V_CVT_F32_I32, SdwaSelBytes() | SdwaSelWords() | SdwaSelFull(), 0, 0, false},
|
||||
{Opcode::V_CVT_F32_UBYTE0, SdwaSelBytes() | SdwaSelWords() | SdwaSelFull(), 0, 0, false},
|
||||
{Opcode::V_NOT_B32, SdwaSelBytes() | SdwaSelWords() | SdwaSelFull(), 0, 0, false},
|
||||
{Opcode::V_NOT_B32, SdwaSelBytes() | SdwaSelWords() | SdwaSelFull(), SdwaSelBytes() | SdwaSelWords(),
|
||||
SdwaSelFull(), false},
|
||||
{Opcode::V_FFBL_B32, SdwaSelBytes() | SdwaSelWords() | SdwaSelFull(), 0, 0, false},
|
||||
{Opcode::V_CVT_F32_F16, SdwaSelWords() | SdwaSelFull(), 0, 0, true},
|
||||
{Opcode::V_CVT_F16_F32, SdwaSelBytes() | SdwaSelWords() | SdwaSelFull(), SdwaSelWords(),
|
||||
|
||||
@@ -507,6 +507,18 @@ bool Translator::S_BITCMP_B32(const Decoder::Instruction& inst, bool expected) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Translator::S_BITCMP_B64(const Decoder::Instruction& inst, bool expected) {
|
||||
const auto value = ReadU32Pair(inst.src0);
|
||||
const auto offset = ir.BitwiseAnd(ReadU32(inst.src1), IR::U32(IR::Value(63u)));
|
||||
const auto word_bit = ir.BitwiseAnd(offset, IR::U32(IR::Value(31u)));
|
||||
const auto word =
|
||||
ir.Select(ir.ULessThan(offset, IR::U32(IR::Value(32u))), value[0], value[1]);
|
||||
const auto bit =
|
||||
IR::U32(ir.Emit(IR::ValueOpcode::BitFieldUExtract, {word, word_bit, IR::Value(1u)}));
|
||||
WriteCompareResult(inst.dst, ir.IEqual(bit, IR::U32(IR::Value(expected ? 1u : 0u))));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Translator::V_ALIGNBIT_B32(const Decoder::Instruction& inst) {
|
||||
const auto hi = ReadU32(inst.src0);
|
||||
const auto lo = ReadU32(inst.src1);
|
||||
|
||||
@@ -208,6 +208,8 @@ bool Translator::EmitScalar(const Decoder::Instruction& inst) {
|
||||
case O::S_BFE_U64: return S_BFE_U64(inst);
|
||||
case O::S_BITCMP0_B32: return S_BITCMP_B32(inst, false);
|
||||
case O::S_BITCMP1_B32: return S_BITCMP_B32(inst, true);
|
||||
case O::S_BITCMP0_B64: return S_BITCMP_B64(inst, false);
|
||||
case O::S_BITCMP1_B64: return S_BITCMP_B64(inst, true);
|
||||
case O::S_PACK_LL_B32_B16: return PackB16(inst, false, false);
|
||||
case O::S_PACK_LH_B32_B16: return PackB16(inst, false, true);
|
||||
case O::S_PACK_HH_B32_B16: return PackB16(inst, true, true);
|
||||
|
||||
@@ -205,6 +205,7 @@ private:
|
||||
bool V_BFE_U32(const Decoder::Instruction& inst, bool sign);
|
||||
bool V_BFI_B32(const Decoder::Instruction& inst);
|
||||
bool S_BITCMP_B32(const Decoder::Instruction& inst, bool expected);
|
||||
bool S_BITCMP_B64(const Decoder::Instruction& inst, bool expected);
|
||||
bool V_ALIGNBIT_B32(const Decoder::Instruction& inst);
|
||||
bool V_ALIGNBYTE_B32(const Decoder::Instruction& inst);
|
||||
bool V_LSHL_ADD_U32(const Decoder::Instruction& inst);
|
||||
|
||||
@@ -189,20 +189,6 @@ void CheckUnicodePaths(const std::filesystem::path &root) {
|
||||
Check(fixture.Create(native_file), "create Unicode filename");
|
||||
fixture.Close();
|
||||
|
||||
for (const auto &directory : {unicode_root, std::filesystem::relative(unicode_root)}) {
|
||||
// A Windows temporary directory may be on a different drive from the working directory.
|
||||
if (directory.empty()) {
|
||||
continue;
|
||||
}
|
||||
const auto files = Common::File::FindFiles(directory);
|
||||
const auto relative_file =
|
||||
std::filesystem::path(u8"nested/asset-\u00e9-\u65e5\u672c\u8a9e.bin");
|
||||
Check(std::any_of(files.begin(), files.end(), [&](const auto &file) {
|
||||
return file.path_with_name.lexically_normal() ==
|
||||
(directory / relative_file).lexically_normal() &&
|
||||
file.rel_path_with_name == relative_file;
|
||||
}), "recursive enumeration preserves native and relative Unicode paths");
|
||||
}
|
||||
const auto entries = Common::File::GetDirEntries(nested_root);
|
||||
Check(std::any_of(entries.begin(), entries.end(), [&](const auto &entry) {
|
||||
return entry.is_file && entry.name == GuestFilename;
|
||||
|
||||
@@ -17057,6 +17057,117 @@ TestCase ScalarGetpcWritesNextInstructionPc() {
|
||||
{O::S_GETPC_B64, O::V_MOV_B32, O::BUFFER_STORE_DWORD, O::S_ENDPGM}};
|
||||
}
|
||||
|
||||
TestCase ScalarBitcmpB64DynamicOperands() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
struct Input {
|
||||
u32 low;
|
||||
u32 high;
|
||||
u32 index;
|
||||
};
|
||||
const Input inputs[] = {
|
||||
{1u, 0u, 0u}, {0x80000000u, 0u, 31u}, {0u, 1u, 32u},
|
||||
{0u, 0x80000000u, 63u}, {1u, 0u, 64u},
|
||||
{0x80000000u, 0u, 95u}, {0u, 1u, 96u},
|
||||
{0u, 0x80000000u, 127u}, {1u, 0u, 128u},
|
||||
{0u, 0x80000000u, 0xffffffffu}, {0u, 0u, 17u},
|
||||
{0xffffffffu, 0xffffffffu, 42u},
|
||||
{0x89abcdefu, 0x76543210u, 4u},
|
||||
{0x89abcdefu, 0x76543210u, 36u},
|
||||
};
|
||||
TestCase test;
|
||||
test.name = "ScalarBitcmpB64DynamicOperands";
|
||||
for (const auto &input : inputs) {
|
||||
test.initial.insert(test.initial.end(), {input.low, input.high, input.index});
|
||||
}
|
||||
test.expected = test.initial;
|
||||
for (u32 i = 0; i < std::size(inputs); ++i) {
|
||||
// Load the value and index on the GPU so the compares cannot constant-fold.
|
||||
for (u32 component = 0; component < 3; ++component) {
|
||||
AppendVMovU32(&test.code, 30, (i * 3 + component) * 4);
|
||||
AppendBufferLoadDword(&test.code, 0, 30);
|
||||
test.code.push_back(EncodeVop1(0x02, 20 + component, Vgpr(0)));
|
||||
}
|
||||
const uint64_t value = uint64_t{inputs[i].low} | (uint64_t{inputs[i].high} << 32);
|
||||
const u32 bit = static_cast<u32>((value >> (inputs[i].index & 63u)) & 1u);
|
||||
for (u32 expected_bit = 0; expected_bit < 2; ++expected_bit) {
|
||||
const bool scc = bit == expected_bit;
|
||||
test.code.push_back(EncodeSopc(0x0e + expected_bit, 20, 22));
|
||||
test.code.push_back(EncodeSop2(0x0a, 24, InlineU32(1), InlineU32(0)));
|
||||
AppendStoreSgpr(&test.code, 24, static_cast<u32>(test.expected.size()));
|
||||
test.expected.push_back(scc ? 1u : 0u);
|
||||
|
||||
// Exercise SCC-driven control flow as well as S_CSELECT_B32.
|
||||
test.code.push_back(EncodeSopp(0x05, 2)); // s_cbranch_scc1 to the true arm.
|
||||
test.code.push_back(EncodeSMovB32(24, InlineU32(3)));
|
||||
test.code.push_back(EncodeSopp(0x02, 1)); // s_branch past the true arm.
|
||||
test.code.push_back(EncodeSMovB32(24, InlineU32(7)));
|
||||
AppendStoreSgpr(&test.code, 24, static_cast<u32>(test.expected.size()));
|
||||
test.expected.push_back(scc ? 7u : 3u);
|
||||
}
|
||||
}
|
||||
AppendEnd(&test.code);
|
||||
test.initial.resize(test.expected.size());
|
||||
test.opcodes = {O::V_MOV_B32, O::BUFFER_LOAD_DWORD, O::V_READFIRSTLANE_B32,
|
||||
O::S_BITCMP0_B64, O::S_BITCMP1_B64, O::S_CSELECT_B32,
|
||||
O::S_CBRANCH_SCC1, O::S_BRANCH, O::S_MOV_B32,
|
||||
O::BUFFER_STORE_DWORD, O::S_ENDPGM};
|
||||
test.required_spirv = {"OpBitFieldUExtract", "OpULessThan", "OpSelect"};
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase ScalarBitcmpB64IntegerConstants() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
struct Source {
|
||||
u32 encoding;
|
||||
uint64_t bits;
|
||||
};
|
||||
// Negative integer inlines sign-extend; B64 literals zero-extend.
|
||||
const Source sources[] = {
|
||||
{128u, 0u}, {129u, 1u}, {192u, 64u},
|
||||
{193u, 0xffffffffffffffffull}, {208u, 0xfffffffffffffff0ull},
|
||||
{255u, 0xffffffffu}, {255u, 0x80000000u},
|
||||
};
|
||||
const u32 indices[] = {0u, 4u, 6u, 31u, 32u, 63u, 64u, 127u};
|
||||
TestCase test;
|
||||
test.name = "ScalarBitcmpB64IntegerConstants";
|
||||
test.initial.assign(std::begin(indices), std::end(indices));
|
||||
test.expected = test.initial;
|
||||
for (u32 i = 0; i < std::size(indices); ++i) {
|
||||
AppendVMovU32(&test.code, 30, i * 4);
|
||||
AppendBufferLoadDword(&test.code, 0, 30);
|
||||
test.code.push_back(EncodeVop1(0x02, 22, Vgpr(0)));
|
||||
for (const auto &source : sources) {
|
||||
const u32 bit = static_cast<u32>((source.bits >> (indices[i] & 63u)) & 1u);
|
||||
for (u32 expected_bit = 0; expected_bit < 2; ++expected_bit) {
|
||||
test.code.push_back(EncodeSopc(0x0e + expected_bit, source.encoding, 22));
|
||||
if (source.encoding == 255u) {
|
||||
test.code.push_back(static_cast<u32>(source.bits));
|
||||
}
|
||||
test.code.push_back(EncodeSop2(0x0a, 24, InlineU32(1), InlineU32(0)));
|
||||
AppendStoreSgpr(&test.code, 24, static_cast<u32>(test.expected.size()));
|
||||
test.expected.push_back(bit == expected_bit ? 1u : 0u);
|
||||
}
|
||||
}
|
||||
}
|
||||
// An instruction has one literal word even when both sources select it.
|
||||
for (u32 expected_bit = 0; expected_bit < 2; ++expected_bit) {
|
||||
test.code.push_back(EncodeSopc(0x0e + expected_bit, 255u, 255u));
|
||||
test.code.push_back(0x8000001fu); // Select bit 31 of the same zero-extended literal.
|
||||
test.code.push_back(EncodeSop2(0x0a, 24, InlineU32(1), InlineU32(0)));
|
||||
AppendStoreSgpr(&test.code, 24, static_cast<u32>(test.expected.size()));
|
||||
test.expected.push_back(expected_bit);
|
||||
}
|
||||
AppendEnd(&test.code);
|
||||
test.initial.resize(test.expected.size());
|
||||
test.opcodes = {O::V_MOV_B32, O::BUFFER_LOAD_DWORD, O::V_READFIRSTLANE_B32,
|
||||
O::S_BITCMP0_B64, O::S_BITCMP1_B64, O::S_CSELECT_B32,
|
||||
O::BUFFER_STORE_DWORD, O::S_ENDPGM};
|
||||
test.required_spirv = {"OpBitFieldUExtract"};
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase ScalarBitfieldPack() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
@@ -18065,6 +18176,29 @@ TestCase Vop1SdwaNotCapturedByte0Source() {
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase Vop1SdwaNotPreservesHighWordDestination() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
std::vector<u32> code;
|
||||
AppendBufferLoadDword(&code, 0, 30);
|
||||
AppendVMovLiteral(&code, 3, 0xabcd5555u);
|
||||
code.push_back(0x7e066ef9u);
|
||||
code.push_back(0x00061400u);
|
||||
AppendStoreVgpr(&code, 3, 0);
|
||||
AppendEnd(&code);
|
||||
|
||||
TestCase test;
|
||||
test.name = "Vop1SdwaNotPreservesHighWordDestination";
|
||||
test.code = std::move(code);
|
||||
test.initial = {0x12345678u};
|
||||
test.expected = {0xabcda987u};
|
||||
test.opcodes = {O::BUFFER_LOAD_DWORD, O::V_MOV_B32, O::V_NOT_B32,
|
||||
O::BUFFER_STORE_DWORD, O::S_ENDPGM};
|
||||
test.ir_counts = {{" = BitwiseNot32 ", 1}};
|
||||
test.required_spirv = {"OpNot"};
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase Vop1SdwaMovByteDestinations() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
@@ -27149,6 +27283,8 @@ std::vector<TestCase> MakeCases() {
|
||||
cases.push_back(ScalarSubvectorLoops(64));
|
||||
AddCase(ScalarGetpcWritesNextInstructionPc);
|
||||
AddCase(ScalarBitfieldPack);
|
||||
AddCase(ScalarBitcmpB64DynamicOperands);
|
||||
AddCase(ScalarBitcmpB64IntegerConstants);
|
||||
AddCase(ScalarBrevB32PreservesScc);
|
||||
AddCase(ScalarBfeI32CapturedRawSignExtends);
|
||||
AddCase(BitfieldExtractWidthPastEndEdges);
|
||||
@@ -27176,6 +27312,7 @@ std::vector<TestCase> MakeCases() {
|
||||
AddCase(VectorFfbhI32NativeAndVop3OnGpu);
|
||||
AddCase(Vop1SdwaFfblCapturedHighWordSource);
|
||||
AddCase(Vop1SdwaNotCapturedByte0Source);
|
||||
AddCase(Vop1SdwaNotPreservesHighWordDestination);
|
||||
AddCase(Vop1SdwaMovByteDestinations);
|
||||
AddCase(Vop2SdwaSubNcExactByte2Destination);
|
||||
AddCase(Vop2SdwaAddNcCapturedHighWordDestination);
|
||||
|
||||
@@ -3435,6 +3435,62 @@ void TestNewShaderRecompilerCapturedVop1SdwaByteConvert() {
|
||||
CheckSpirvBinaryValidates(result.spirv);
|
||||
}
|
||||
|
||||
void TestNewShaderRecompilerVop1SdwaNotDestination() {
|
||||
auto options = MakeCompileOptions(ShaderType::Pixel);
|
||||
|
||||
const auto check_destination = [&](uint32_t modifier, uint32_t dst_sel,
|
||||
uint32_t dst_unused) {
|
||||
const uint32_t shader[] = {
|
||||
0x7e066ef9u, modifier, EncodeExp0(0x00, 0x1),
|
||||
EncodeExp1(3, 0, 0, 0), EncodeSopp(0x01),
|
||||
};
|
||||
|
||||
ShaderRecompiler::Decoder::Instruction decoded;
|
||||
ShaderRecompiler::Decoder::DecodeInstruction(shader, 0, decoded);
|
||||
Check(decoded.opcode == ShaderRecompiler::Decoder::Opcode::V_NOT_B32,
|
||||
"V_NOT_B32 SDWA opcode did not decode");
|
||||
Check(decoded.word_count == 2u,
|
||||
"V_NOT_B32 SDWA instruction has the wrong word count");
|
||||
Check(decoded.dst.kind == ShaderRecompiler::Decoder::OperandKind::Vgpr &&
|
||||
decoded.dst.reg == 3u && decoded.dst.sdwa_sel == dst_sel &&
|
||||
decoded.dst.sdwa_dst_unused == dst_unused &&
|
||||
decoded.dst.explicit_sdwa_dst,
|
||||
"V_NOT_B32 SDWA destination metadata is incorrect");
|
||||
Check(decoded.src0.kind == ShaderRecompiler::Decoder::OperandKind::Vgpr &&
|
||||
decoded.src0.reg == 0u && decoded.src0.sdwa_sel == 6u &&
|
||||
!decoded.src0.negate && !decoded.src0.absolute,
|
||||
"V_NOT_B32 SDWA source metadata is incorrect");
|
||||
|
||||
auto result = RecompileForTest(shader, options);
|
||||
CheckSpirvBinaryValidates(result.spirv);
|
||||
};
|
||||
|
||||
check_destination(0x00061400u, 4u, 2u);
|
||||
check_destination(0x00060500u, 5u, 0u);
|
||||
check_destination(0x00060000u, 0u, 0u);
|
||||
|
||||
const auto check_rejected = [&](uint32_t modifier, const char *message) {
|
||||
const uint32_t instruction[] = {0x7e066ef9u, modifier};
|
||||
|
||||
ShaderRecompiler::Decoder::Instruction decoded;
|
||||
ShaderRecompiler::Decoder::DecodeInstruction(instruction, 0, decoded);
|
||||
Check(decoded.word_count == 2u &&
|
||||
decoded.opcode == ShaderRecompiler::Decoder::Opcode::UNSUPPORTED,
|
||||
message);
|
||||
};
|
||||
|
||||
check_rejected(0x00060700u, "V_NOT_B32 SDWA accepted destination selector 7");
|
||||
check_rejected(0x00070600u, "V_NOT_B32 SDWA accepted source selector 7");
|
||||
check_rejected(0x00061c00u,
|
||||
"V_NOT_B32 SDWA accepted destination unused value 3");
|
||||
check_rejected(0x00160400u, "V_NOT_B32 SDWA accepted source negation");
|
||||
check_rejected(0x00260400u, "V_NOT_B32 SDWA accepted source absolute");
|
||||
check_rejected(0x00062400u, "V_NOT_B32 SDWA accepted clamp");
|
||||
check_rejected(0x00064400u, "V_NOT_B32 SDWA accepted output modifier");
|
||||
check_rejected(0x00000400u,
|
||||
"V_NOT_B32 SDWA partial destination accepted a byte source");
|
||||
}
|
||||
|
||||
void TestNewShaderRecompilerBootB16PackedAndSdwaOpcodes() {
|
||||
const uint32_t shader[] = {
|
||||
0xd7070001u,
|
||||
@@ -13397,6 +13453,7 @@ int main() {
|
||||
TestNewShaderRecompilerDsReadWrite2Translation();
|
||||
TestNewShaderRecompilerDsWideAndAtomicTranslation();
|
||||
TestNewShaderRecompilerCapturedVop1SdwaByteConvert();
|
||||
TestNewShaderRecompilerVop1SdwaNotDestination();
|
||||
TestNewShaderRecompilerScalarMemoryBindingDomains();
|
||||
// Opcode semantics and optimized SPIR-V are exercised by
|
||||
// ShaderRecompilerComputeTests; keep the distinct decoder contract checks
|
||||
|
||||
Reference in New Issue
Block a user