mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 10:56:58 +02:00
Linux: port the emulator to a working state on Linux (#117)
Linux: port the emulator to a working state
This commit is contained in:
@@ -18,6 +18,13 @@
|
||||
#include <windows.h>
|
||||
#undef min
|
||||
#undef max
|
||||
#else
|
||||
#include <csignal>
|
||||
#include <map>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/wait.h>
|
||||
#include <ucontext.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
@@ -39,6 +46,123 @@ void Check(bool value, const char *text) {
|
||||
}
|
||||
}
|
||||
|
||||
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS
|
||||
// POSIX shims for the shared test body.
|
||||
using DWORD = uint32_t;
|
||||
constexpr uint32_t PAGE_NOACCESS = 1;
|
||||
constexpr uint32_t PAGE_READONLY = 2;
|
||||
constexpr uint32_t PAGE_READWRITE = 3;
|
||||
constexpr uint32_t MEM_RESERVE = 0;
|
||||
constexpr uint32_t MEM_COMMIT = 0;
|
||||
constexpr uint32_t MEM_RELEASE = 0;
|
||||
|
||||
int ToHostProt(uint32_t protection) {
|
||||
switch (protection) {
|
||||
case PAGE_NOACCESS:
|
||||
return PROT_NONE;
|
||||
case PAGE_READONLY:
|
||||
return PROT_READ;
|
||||
default:
|
||||
return PROT_READ | PROT_WRITE;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t Protection(const void *address) {
|
||||
const auto addr = reinterpret_cast<uintptr_t>(address);
|
||||
std::FILE *maps = std::fopen("/proc/self/maps", "r");
|
||||
Check(maps != nullptr, "open /proc/self/maps failed");
|
||||
char line[512];
|
||||
uint32_t result = 0; // 0 => not mapped at all
|
||||
while (std::fgets(line, sizeof(line), maps) != nullptr) {
|
||||
unsigned long start = 0;
|
||||
unsigned long end = 0;
|
||||
char perms[8]{};
|
||||
if (std::sscanf(line, "%lx-%lx %7s", &start, &end, perms) != 3) {
|
||||
continue;
|
||||
}
|
||||
if (addr >= start && addr < end) {
|
||||
result = perms[1] == 'w' ? PAGE_READWRITE
|
||||
: perms[0] == 'r' ? PAGE_READONLY
|
||||
: PAGE_NOACCESS;
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::fclose(maps);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool IsWritable(const void *address) { return Protection(address) == PAGE_READWRITE; }
|
||||
|
||||
// munmap needs a length where VirtualFree's callers pass 0, so sizes are remembered here.
|
||||
std::map<void *, size_t> &AllocationSizes() {
|
||||
static std::map<void *, size_t> sizes;
|
||||
return sizes;
|
||||
}
|
||||
|
||||
void *VirtualAlloc(void *address, size_t size, DWORD /*type*/, uint32_t protection) {
|
||||
const int extra = address != nullptr ? MAP_FIXED_NOREPLACE : 0;
|
||||
void *raw = ::mmap(address, size, ToHostProt(protection),
|
||||
MAP_PRIVATE | MAP_ANONYMOUS | extra, -1, 0);
|
||||
if (raw == MAP_FAILED) {
|
||||
return nullptr;
|
||||
}
|
||||
AllocationSizes()[raw] = size;
|
||||
return raw;
|
||||
}
|
||||
|
||||
int VirtualFree(void *address, size_t /*size*/, DWORD /*type*/) {
|
||||
auto &sizes = AllocationSizes();
|
||||
auto it = sizes.find(address);
|
||||
if (it == sizes.end()) {
|
||||
return 0;
|
||||
}
|
||||
const int ok = ::munmap(address, it->second) == 0 ? 1 : 0;
|
||||
sizes.erase(it);
|
||||
return ok;
|
||||
}
|
||||
|
||||
[[maybe_unused]] int VirtualProtect(void *address, size_t size, uint32_t protection,
|
||||
DWORD *old_protection) {
|
||||
if (old_protection != nullptr) {
|
||||
*old_protection = Protection(address);
|
||||
}
|
||||
return ::mprotect(address, size, ToHostProt(protection)) == 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
// Two views of one anonymous shared object.
|
||||
class SharedPage final {
|
||||
public:
|
||||
SharedPage(uintptr_t address, uint64_t size) : size_(static_cast<size_t>(size)) {
|
||||
fd_ = static_cast<int>(::syscall(SYS_memfd_create, "kyty-shared-page", 0u));
|
||||
Check(fd_ >= 0, "memfd_create failed");
|
||||
Check(::ftruncate(fd_, static_cast<off_t>(size)) == 0, "ftruncate failed");
|
||||
guest = static_cast<uint8_t *>(::mmap(reinterpret_cast<void *>(address), size_,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED | MAP_FIXED_NOREPLACE, fd_, 0));
|
||||
Check(guest == reinterpret_cast<void *>(address), "fixed shared view failed");
|
||||
backing = static_cast<uint8_t *>(
|
||||
::mmap(nullptr, size_, PROT_READ | PROT_WRITE, MAP_SHARED, fd_, 0));
|
||||
Check(backing != MAP_FAILED && backing != nullptr, "shared backing view failed");
|
||||
}
|
||||
|
||||
~SharedPage() {
|
||||
Check(::munmap(backing, size_) == 0, "shared backing unmap failed");
|
||||
Check(::munmap(guest, size_) == 0, "shared guest unmap failed");
|
||||
Check(::close(fd_) == 0, "shared mapping close failed");
|
||||
}
|
||||
|
||||
SharedPage(const SharedPage &) = delete;
|
||||
SharedPage &operator=(const SharedPage &) = delete;
|
||||
|
||||
uint8_t *guest = nullptr;
|
||||
uint8_t *backing = nullptr;
|
||||
|
||||
private:
|
||||
size_t size_ = 0;
|
||||
int fd_ = -1;
|
||||
};
|
||||
#endif
|
||||
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
bool IsWritable(const void *address) {
|
||||
MEMORY_BASIC_INFORMATION info{};
|
||||
@@ -84,6 +208,8 @@ class SharedPage final {
|
||||
private:
|
||||
HANDLE mapping_ = nullptr;
|
||||
};
|
||||
#endif
|
||||
#if 1
|
||||
|
||||
bool DummyFault(void *, PageFaultAccess, uint64_t, uint64_t, PageFaultPhase) noexcept {
|
||||
return true;
|
||||
@@ -246,6 +372,7 @@ void UnmapContended() noexcept {
|
||||
g_unmap_contended.store(true, std::memory_order_release);
|
||||
}
|
||||
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
LONG CALLBACK NativeTrackerFaultHandler(EXCEPTION_POINTERS *exception) {
|
||||
if (exception == nullptr || exception->ExceptionRecord == nullptr ||
|
||||
exception->ExceptionRecord->ExceptionCode != EXCEPTION_ACCESS_VIOLATION) {
|
||||
@@ -266,6 +393,45 @@ LONG CALLBACK NativeTrackerFaultHandler(EXCEPTION_POINTERS *exception) {
|
||||
? EXCEPTION_CONTINUE_EXECUTION
|
||||
: EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
#else
|
||||
// SIGSEGV stands in for the vectored exception handler.
|
||||
void NativeTrackerFaultHandler(int signal_number, siginfo_t *info, void *native_context) {
|
||||
auto *context = static_cast<ucontext_t *>(native_context);
|
||||
const auto error_code = static_cast<uint64_t>(context->uc_mcontext.gregs[REG_ERR]);
|
||||
const auto access = (error_code & 0x10u) != 0 ? PageFaultAccess::Execute
|
||||
: (error_code & 0x02u) != 0 ? PageFaultAccess::Write
|
||||
: PageFaultAccess::Read;
|
||||
auto *page_manager = g_native_page_manager.load(std::memory_order_acquire);
|
||||
if (page_manager != nullptr) {
|
||||
g_native_fault_entered.store(true, std::memory_order_release);
|
||||
if (page_manager->HandleFault(access, reinterpret_cast<uint64_t>(info->si_addr))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
struct sigaction restore {};
|
||||
restore.sa_handler = SIG_DFL;
|
||||
sigemptyset(&restore.sa_mask);
|
||||
::sigaction(signal_number, &restore, nullptr);
|
||||
}
|
||||
|
||||
struct sigaction g_saved_segv_action {};
|
||||
|
||||
void *AddVectoredExceptionHandler(unsigned long /*first*/,
|
||||
void (*handler)(int, siginfo_t *, void *)) {
|
||||
struct sigaction action {};
|
||||
action.sa_sigaction = handler;
|
||||
sigemptyset(&action.sa_mask);
|
||||
action.sa_flags = SA_SIGINFO;
|
||||
if (::sigaction(SIGSEGV, &action, &g_saved_segv_action) != 0) {
|
||||
return nullptr;
|
||||
}
|
||||
return reinterpret_cast<void *>(handler);
|
||||
}
|
||||
|
||||
int RemoveVectoredExceptionHandler(void * /*token*/) {
|
||||
return ::sigaction(SIGSEGV, &g_saved_segv_action, nullptr) == 0 ? 1 : 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
void TestPendingFaultBlocksUploadConsumption() {
|
||||
constexpr uintptr_t base = 0x0000000200010000ull;
|
||||
@@ -978,14 +1144,17 @@ void TestCrossRegionUpload() {
|
||||
}
|
||||
|
||||
void TestFatalPaths() {
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
char path[MAX_PATH]{};
|
||||
Check(GetModuleFileNameA(nullptr, path, MAX_PATH) != 0,
|
||||
"GetModuleFileName failed");
|
||||
#endif
|
||||
for (const char *name : {"gpu-dirty-fault", "gpu-dirty-read", "virtual-gpu-read",
|
||||
"gpu-dirty-explicit-cpu",
|
||||
"unmapped", "reentrant-upload",
|
||||
"writable-upload-race", "gpu-dirty-unmap-race",
|
||||
"missing-download-bytes"}) {
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
std::string command = std::string("\"") + path + "\" --death " + name;
|
||||
std::vector<char> mutable_command(command.begin(), command.end());
|
||||
mutable_command.push_back('\0');
|
||||
@@ -1004,6 +1173,21 @@ void TestFatalPaths() {
|
||||
"MemoryTracker death path used the wrong exit");
|
||||
CloseHandle(process.hThread);
|
||||
CloseHandle(process.hProcess);
|
||||
#else
|
||||
const pid_t pid = ::fork();
|
||||
Check(pid >= 0, "fork failed");
|
||||
if (pid == 0) {
|
||||
::execl("/proc/self/exe", "MemoryTrackerTests", "--death", name, nullptr);
|
||||
std::_Exit(0x7e);
|
||||
}
|
||||
int status = 0;
|
||||
Check(::waitpid(pid, &status, 0) == pid, "waitpid failed");
|
||||
// Exit status carries only the low 8 bits.
|
||||
const bool fatal_exit = WIFEXITED(status) && (WEXITSTATUS(status) == (321 & 0xff) ||
|
||||
WEXITSTATUS(status) == (322 & 0xff));
|
||||
Check(fatal_exit || WIFSIGNALED(status),
|
||||
"MemoryTracker death path used the wrong exit");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1011,7 +1195,7 @@ void TestFatalPaths() {
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
#if 1
|
||||
if (argc == 3 && std::strcmp(argv[1], "--death") == 0) {
|
||||
RunDeathCase(argv[2]);
|
||||
}
|
||||
|
||||
+150
-1
@@ -17,6 +17,13 @@
|
||||
#include <windows.h>
|
||||
#undef min
|
||||
#undef max
|
||||
#else
|
||||
#include <csignal>
|
||||
#include <map>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/wait.h>
|
||||
#include <ucontext.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
@@ -32,6 +39,76 @@ void Check(bool value, const char *text) {
|
||||
}
|
||||
}
|
||||
|
||||
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS
|
||||
// POSIX shims for the shared test body.
|
||||
using DWORD = uint32_t;
|
||||
constexpr uint32_t PAGE_NOACCESS = 1;
|
||||
constexpr uint32_t PAGE_READONLY = 2;
|
||||
constexpr uint32_t PAGE_READWRITE = 3;
|
||||
constexpr uint32_t MEM_RELEASE = 0;
|
||||
|
||||
int ToHostProt(uint32_t protection) {
|
||||
switch (protection) {
|
||||
case PAGE_NOACCESS:
|
||||
return PROT_NONE;
|
||||
case PAGE_READONLY:
|
||||
return PROT_READ;
|
||||
default:
|
||||
return PROT_READ | PROT_WRITE;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t Protection(const void *address) {
|
||||
const auto addr = reinterpret_cast<uintptr_t>(address);
|
||||
std::FILE *maps = std::fopen("/proc/self/maps", "r");
|
||||
Check(maps != nullptr, "open /proc/self/maps failed");
|
||||
char line[512];
|
||||
uint32_t result = 0; // 0 => not mapped at all
|
||||
while (std::fgets(line, sizeof(line), maps) != nullptr) {
|
||||
unsigned long start = 0;
|
||||
unsigned long end = 0;
|
||||
char perms[8]{};
|
||||
if (std::sscanf(line, "%lx-%lx %7s", &start, &end, perms) != 3) {
|
||||
continue;
|
||||
}
|
||||
if (addr >= start && addr < end) {
|
||||
result = perms[1] == 'w' ? PAGE_READWRITE
|
||||
: perms[0] == 'r' ? PAGE_READONLY
|
||||
: PAGE_NOACCESS;
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::fclose(maps);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool IsWritable(const void *address) { return Protection(address) == PAGE_READWRITE; }
|
||||
|
||||
// munmap needs the length that VirtualFree's callers pass as 0, so sizes are remembered here.
|
||||
std::map<void *, size_t> &AllocationSizes() {
|
||||
static std::map<void *, size_t> sizes;
|
||||
return sizes;
|
||||
}
|
||||
|
||||
int VirtualFree(void *address, size_t /*size*/, DWORD /*type*/) {
|
||||
auto &sizes = AllocationSizes();
|
||||
auto it = sizes.find(address);
|
||||
if (it == sizes.end()) {
|
||||
return 0;
|
||||
}
|
||||
const int ok = ::munmap(address, it->second) == 0 ? 1 : 0;
|
||||
sizes.erase(it);
|
||||
return ok;
|
||||
}
|
||||
|
||||
int VirtualProtect(void *address, size_t size, uint32_t protection, DWORD *old_protection) {
|
||||
if (old_protection != nullptr) {
|
||||
*old_protection = Protection(address);
|
||||
}
|
||||
return ::mprotect(address, size, ToHostProt(protection)) == 0 ? 1 : 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
bool IsWritable(const void *address) {
|
||||
MEMORY_BASIC_INFORMATION info{};
|
||||
@@ -44,6 +121,8 @@ uint32_t Protection(const void *address) {
|
||||
Check(VirtualQuery(address, &info, sizeof(info)) != 0, "VirtualQuery failed");
|
||||
return info.Protect;
|
||||
}
|
||||
#endif
|
||||
#if 1
|
||||
|
||||
struct FaultContext {
|
||||
PageManager *manager = nullptr;
|
||||
@@ -61,6 +140,7 @@ std::atomic_bool g_delay_native_fault{false};
|
||||
std::atomic_bool g_native_fault_entered{false};
|
||||
std::atomic_bool g_release_native_fault{false};
|
||||
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
LONG CALLBACK NativeFaultHandler(EXCEPTION_POINTERS *exception) {
|
||||
if (exception == nullptr || exception->ExceptionRecord == nullptr ||
|
||||
exception->ExceptionRecord->ExceptionCode != EXCEPTION_ACCESS_VIOLATION) {
|
||||
@@ -86,6 +166,50 @@ LONG CALLBACK NativeFaultHandler(EXCEPTION_POINTERS *exception) {
|
||||
? EXCEPTION_CONTINUE_EXECUTION
|
||||
: EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
#else
|
||||
// SIGSEGV stands in for the vectored exception handler.
|
||||
void NativeFaultHandler(int signal_number, siginfo_t *info, void *native_context) {
|
||||
auto *context = static_cast<ucontext_t *>(native_context);
|
||||
const auto error_code = static_cast<uint64_t>(context->uc_mcontext.gregs[REG_ERR]);
|
||||
const auto access = (error_code & 0x10u) != 0 ? PageFaultAccess::Execute
|
||||
: (error_code & 0x02u) != 0 ? PageFaultAccess::Write
|
||||
: PageFaultAccess::Read;
|
||||
auto *manager = g_native_fault_manager.load(std::memory_order_acquire);
|
||||
if (manager != nullptr) {
|
||||
if (g_delay_native_fault.load(std::memory_order_acquire)) {
|
||||
g_native_fault_entered.store(true, std::memory_order_release);
|
||||
while (!g_release_native_fault.load(std::memory_order_acquire)) {
|
||||
std::this_thread::yield();
|
||||
}
|
||||
}
|
||||
if (manager->HandleFault(access, reinterpret_cast<uint64_t>(info->si_addr))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
struct sigaction restore {};
|
||||
restore.sa_handler = SIG_DFL;
|
||||
sigemptyset(&restore.sa_mask);
|
||||
::sigaction(signal_number, &restore, nullptr);
|
||||
}
|
||||
|
||||
struct sigaction g_saved_segv_action {};
|
||||
|
||||
void *AddVectoredExceptionHandler(unsigned long /*first*/,
|
||||
void (*handler)(int, siginfo_t *, void *)) {
|
||||
struct sigaction action {};
|
||||
action.sa_sigaction = handler;
|
||||
sigemptyset(&action.sa_mask);
|
||||
action.sa_flags = SA_SIGINFO;
|
||||
if (::sigaction(SIGSEGV, &action, &g_saved_segv_action) != 0) {
|
||||
return nullptr;
|
||||
}
|
||||
return reinterpret_cast<void *>(handler);
|
||||
}
|
||||
|
||||
int RemoveVectoredExceptionHandler(void * /*token*/) {
|
||||
return ::sigaction(SIGSEGV, &g_saved_segv_action, nullptr) == 0 ? 1 : 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool InvalidateFault(void *context, Libs::Graphics::PageFaultAccess, uint64_t vaddr, uint64_t size,
|
||||
Libs::Graphics::PageFaultPhase phase) noexcept {
|
||||
@@ -112,11 +236,20 @@ bool InvalidateFault(void *context, Libs::Graphics::PageFaultAccess, uint64_t va
|
||||
|
||||
uint8_t *Allocate(uint64_t size, uint32_t protection = PAGE_READWRITE) {
|
||||
constexpr uintptr_t test_address = 0x0000000200010000ull;
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
auto *memory = static_cast<uint8_t *>(
|
||||
VirtualAlloc(reinterpret_cast<void *>(test_address), size,
|
||||
MEM_RESERVE | MEM_COMMIT, protection));
|
||||
Check(memory == reinterpret_cast<void *>(test_address),
|
||||
"fixed low VirtualAlloc failed");
|
||||
#else
|
||||
// Do not overwrite a leaked mapping from an earlier case.
|
||||
void *raw = ::mmap(reinterpret_cast<void *>(test_address), size, ToHostProt(protection),
|
||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED_NOREPLACE, -1, 0);
|
||||
Check(raw == reinterpret_cast<void *>(test_address), "fixed low mmap failed");
|
||||
auto *memory = static_cast<uint8_t *>(raw);
|
||||
AllocationSizes()[raw] = static_cast<size_t>(size);
|
||||
#endif
|
||||
return memory;
|
||||
}
|
||||
|
||||
@@ -497,6 +630,7 @@ void TestCrossRegionRange() {
|
||||
}
|
||||
|
||||
void CheckDeathCase(const char *name) {
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
char path[MAX_PATH]{};
|
||||
Check(GetModuleFileNameA(nullptr, path, MAX_PATH) != 0,
|
||||
"GetModuleFileName failed");
|
||||
@@ -518,6 +652,21 @@ void CheckDeathCase(const char *name) {
|
||||
"death case did not use the PageManager fatal exit");
|
||||
CloseHandle(process.hThread);
|
||||
CloseHandle(process.hProcess);
|
||||
#else
|
||||
const pid_t pid = ::fork();
|
||||
Check(pid >= 0, "fork failed");
|
||||
if (pid == 0) {
|
||||
::execl("/proc/self/exe", "PageManagerTests", "--death", name, nullptr);
|
||||
std::_Exit(0x7e);
|
||||
}
|
||||
int status = 0;
|
||||
Check(::waitpid(pid, &status, 0) == pid, "waitpid failed");
|
||||
// Exit status carries only the low 8 bits.
|
||||
const bool fatal_exit = WIFEXITED(status) && WEXITSTATUS(status) == (322 & 0xff);
|
||||
const bool fatal_signal = WIFSIGNALED(status);
|
||||
Check(fatal_exit || fatal_signal,
|
||||
"death case did not use the PageManager fatal exit");
|
||||
#endif
|
||||
}
|
||||
|
||||
void TestFatalPaths() {
|
||||
@@ -631,7 +780,7 @@ void TestGpuAccessPermissions() {
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
#if 1
|
||||
if (argc == 3 && std::strcmp(argv[1], "--death") == 0) {
|
||||
RunDeathCase(argv[2]);
|
||||
}
|
||||
|
||||
@@ -1507,7 +1507,7 @@ public:
|
||||
ordered_finished.load(),
|
||||
"submission barrier did not drain prior PM4 work");
|
||||
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
|
||||
constexpr uintptr_t fault_base = 0x0000000200500000ull;
|
||||
constexpr uint64_t fault_size = 0x10000;
|
||||
int64_t fault_direct_offset = -1;
|
||||
@@ -1689,7 +1689,7 @@ public:
|
||||
Libs::LibKernel::Memory::KernelReleaseDirectMemory(
|
||||
fault_direct_offset, fault_size) == 0,
|
||||
"processor-fault direct-memory allocation release failed");
|
||||
#endif
|
||||
|
||||
|
||||
std::binary_semaphore command_entered{0};
|
||||
std::binary_semaphore release_command{0};
|
||||
@@ -1896,7 +1896,7 @@ public:
|
||||
std::printf("[host] %-32s ok\n", name);
|
||||
}
|
||||
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
|
||||
void CheckBufferCacheDirtyGarbageCollection() {
|
||||
constexpr const char *name = "BufferCacheDirtyGarbageCollection";
|
||||
constexpr uintptr_t base = 0x0000000200700000ull;
|
||||
@@ -6117,7 +6117,7 @@ public:
|
||||
"descriptor discovery direct-memory allocation release failed");
|
||||
std::printf("[host] %-32s ok\n", name);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
Buffer CreateStorageBuffer(const char *shader_name,
|
||||
const std::vector<u32> &initial,
|
||||
@@ -15071,7 +15071,6 @@ void CheckEmbeddedFetchVertexOffset() {
|
||||
std::printf("[host] %-32s ok\n", "EmbeddedFetchVertexOffset");
|
||||
}
|
||||
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
struct CacheFaultContext {
|
||||
TextureCache *texture = nullptr;
|
||||
};
|
||||
@@ -15088,6 +15087,7 @@ bool CacheFault(void *opaque, PageFaultAccess access, uint64_t vaddr,
|
||||
std::_Exit(0x7f);
|
||||
}
|
||||
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
void CheckReverseRenderTargetFormatContract() {
|
||||
const auto format = TextureGetRenderTargetFormat(12u, 7u, 2u);
|
||||
Require("ReverseRenderTarget", "exact format",
|
||||
@@ -15129,6 +15129,7 @@ void CheckReverseRenderTargetFormatContract() {
|
||||
"adjacent unproven render-target tuple did not retain the fatal guard");
|
||||
std::printf("[host] %-32s ok\n", "ReverseRenderTargetFormat");
|
||||
}
|
||||
#endif
|
||||
|
||||
[[noreturn]] void RunImageViewDeathCase(const char *kind) {
|
||||
if (std::strcmp(kind, "sampled-invalid-selector") == 0) {
|
||||
@@ -15211,6 +15212,7 @@ void CheckReverseRenderTargetFormatContract() {
|
||||
std::_Exit(0x7f);
|
||||
}
|
||||
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
void CheckSampledColorViews() {
|
||||
Require("SampledColorViews", "identity",
|
||||
SelectSampledColorView(vk::Format::eR8G8B8A8Unorm,
|
||||
@@ -15446,6 +15448,7 @@ void CheckSampledColorViews() {
|
||||
}
|
||||
std::printf("[host] %-32s ok\n", "SampledColorRenderTargetViews");
|
||||
}
|
||||
#endif
|
||||
|
||||
void CheckSampledDepthResource() {
|
||||
ShaderRecompiler::IR::ImageResource resource{};
|
||||
@@ -16209,6 +16212,7 @@ ShaderTextureResource BasicUintVolumeStorageTextureDescriptor() {
|
||||
std::_Exit(0x7f);
|
||||
}
|
||||
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
void CheckBasicStorageTextureDescriptor() {
|
||||
const auto descriptor = BasicStorageTextureDescriptor();
|
||||
Require("BasicStorageTexture", "descriptor",
|
||||
@@ -16567,6 +16571,7 @@ void CheckBasicStorageTextureDescriptor() {
|
||||
}
|
||||
std::printf("[host] %-32s ok\n", "BasicStorageTextureDescriptor");
|
||||
}
|
||||
#endif
|
||||
|
||||
void CheckStorageTextureLinearUploadLayout() {
|
||||
constexpr uint32_t format =
|
||||
@@ -16835,6 +16840,7 @@ void CheckStandard64RenderTargetTileRoundTrip() {
|
||||
std::printf("[host] %-32s ok\n", "Standard64RenderTarget");
|
||||
}
|
||||
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
void CheckStorageTextureGpuOwnedRebindState() {
|
||||
constexpr uintptr_t base = 0x0000000200200000ull;
|
||||
constexpr uint64_t size = 0x10000;
|
||||
@@ -16904,6 +16910,7 @@ void CheckStorageTextureGpuOwnedRebindState() {
|
||||
VirtualFree(memory, 0, MEM_RELEASE) != 0, "VirtualFree failed");
|
||||
std::printf("[host] %-32s ok\n", "StorageTextureGpuOwnedRebind");
|
||||
}
|
||||
#endif
|
||||
|
||||
void CheckNativeMsaaState() {
|
||||
Require("NativeMsaaState", "sample encoding",
|
||||
@@ -17407,7 +17414,7 @@ void CheckSharedFenceResourceLifetime() {
|
||||
std::printf("[host] %-32s ok\n", "SharedFenceResourceLifetime");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
void CheckEmbeddedFetchLaneSpill() {
|
||||
std::vector<u32> code;
|
||||
@@ -17683,18 +17690,6 @@ int main(int argc, char **argv) {
|
||||
vulkan.CheckGpuCommandLane();
|
||||
return 0;
|
||||
}
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
if (argc == 2 && std::strcmp(argv[1], "--reverse-rt-death") == 0) {
|
||||
RunReverseRenderTargetDeathCase();
|
||||
}
|
||||
if (argc == 2 && std::strcmp(argv[1], "--reverse-rt-only") == 0) {
|
||||
CheckReverseRenderTargetFormatContract();
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && std::strcmp(argv[1], "--standard64-rt-only") == 0) {
|
||||
CheckStandard64RenderTargetTileRoundTrip();
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && std::strcmp(argv[1], "--image-overlap-only") == 0) {
|
||||
CheckDepthAttachmentWrites();
|
||||
CheckDynamicRenderingState();
|
||||
@@ -17714,17 +17709,48 @@ int main(int argc, char **argv) {
|
||||
vulkan.CheckUnifiedImageViewCache();
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && std::strcmp(argv[1], "--image-view-cache-only") == 0) {
|
||||
VulkanHarness vulkan;
|
||||
vulkan.CheckUnifiedImageViewCache();
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && std::strcmp(argv[1], "--storage-sampled-only") == 0) {
|
||||
VulkanHarness vulkan;
|
||||
vulkan.CheckUnifiedImageViewCache();
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && std::strcmp(argv[1], "--depth-readback-only") == 0) {
|
||||
CheckDepthTargetFootprints();
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && std::strcmp(argv[1], "--buffer-cache-range-only") == 0) {
|
||||
VulkanHarness vulkan;
|
||||
vulkan.CheckUnifiedTextureCacheFlow();
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && std::strcmp(argv[1], "--buffer-cache-gc-only") == 0) {
|
||||
VulkanHarness vulkan;
|
||||
vulkan.CheckBufferCacheDirtyGarbageCollection();
|
||||
return 0;
|
||||
}
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
if (argc == 2 && std::strcmp(argv[1], "--reverse-rt-death") == 0) {
|
||||
RunReverseRenderTargetDeathCase();
|
||||
}
|
||||
if (argc == 2 && std::strcmp(argv[1], "--reverse-rt-only") == 0) {
|
||||
CheckReverseRenderTargetFormatContract();
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && std::strcmp(argv[1], "--standard64-rt-only") == 0) {
|
||||
CheckStandard64RenderTargetTileRoundTrip();
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && std::strcmp(argv[1], "--image-view-only") == 0) {
|
||||
VulkanHarness vulkan;
|
||||
CheckSampledColorViews();
|
||||
CheckSampledVideoOutView(vulkan.RuntimeRenderer());
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && std::strcmp(argv[1], "--image-view-cache-only") == 0) {
|
||||
VulkanHarness vulkan;
|
||||
vulkan.CheckUnifiedImageViewCache();
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && std::strcmp(argv[1], "--image-transition-only") == 0) {
|
||||
VulkanHarness vulkan;
|
||||
CheckImageTransitionState(vulkan.RuntimeRenderer());
|
||||
@@ -17736,16 +17762,6 @@ int main(int argc, char **argv) {
|
||||
CheckSampledDepthDescriptor(vulkan.RuntimeRenderer());
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && std::strcmp(argv[1], "--buffer-cache-range-only") == 0) {
|
||||
VulkanHarness vulkan;
|
||||
vulkan.CheckUnifiedTextureCacheFlow();
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && std::strcmp(argv[1], "--buffer-cache-gc-only") == 0) {
|
||||
VulkanHarness vulkan;
|
||||
vulkan.CheckBufferCacheDirtyGarbageCollection();
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && std::strcmp(argv[1], "--storage-bgra-only") == 0) {
|
||||
CheckSampledColorViews();
|
||||
CheckBasicStorageTextureDescriptor();
|
||||
@@ -17762,15 +17778,6 @@ int main(int argc, char **argv) {
|
||||
RunCase(&vulkan, ImageStoreYzwxUsesInverseSwizzle());
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && std::strcmp(argv[1], "--storage-sampled-only") == 0) {
|
||||
VulkanHarness vulkan;
|
||||
vulkan.CheckUnifiedImageViewCache();
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && std::strcmp(argv[1], "--depth-readback-only") == 0) {
|
||||
CheckDepthTargetFootprints();
|
||||
return 0;
|
||||
}
|
||||
if (argc == 3 && std::strcmp(argv[1], "--image-view-death") == 0) {
|
||||
RunImageViewDeathCase(argv[2]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user