diff --git a/CMakeLists.txt b/CMakeLists.txt index b77247e8d..8e2cdcc31 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -558,6 +558,7 @@ set(VIDEO_CORE src/video_core/amdgpu/liverpool.cpp src/video_core/texture_cache/tile_manager.cpp src/video_core/texture_cache/tile_manager.h src/video_core/texture_cache/types.h + src/video_core/texture_cache/host_compatibility.h src/video_core/page_manager.cpp src/video_core/page_manager.h src/video_core/multi_level_page_table.h diff --git a/src/core/address_space.cpp b/src/core/address_space.cpp index 235113700..0dd7a76f2 100644 --- a/src/core/address_space.cpp +++ b/src/core/address_space.cpp @@ -15,6 +15,7 @@ #include #include #endif +#include "libraries/error_codes.h" #ifdef __APPLE__ // Reserve space for the system address space using a zerofill section. @@ -231,27 +232,36 @@ struct AddressSpace::Impl { void Protect(VAddr virtual_addr, size_t size, bool read, bool write, bool execute) { DWORD new_flags{}; - if (read && write) { + + if (read && write && execute) { + new_flags = PAGE_EXECUTE_READWRITE; + } else if (read && write) { new_flags = PAGE_READWRITE; } else if (read && !write) { new_flags = PAGE_READONLY; - } else if (!read && !write) { + } else if (execute && !read && not write) { + new_flags = PAGE_EXECUTE; + } else if (!read && !write && !execute) { new_flags = PAGE_NOACCESS; } else { - UNIMPLEMENTED_MSG("Protection flag combination read={} write={}", read, write); + LOG_CRITICAL(Common_Memory, + "Unsupported protection flag combination for address {:#x}, size {}", + virtual_addr, size); + return; } - const VAddr virtual_end = virtual_addr + size; - auto [it, end] = placeholders.equal_range({virtual_addr, virtual_end}); - while (it != end) { - const size_t offset = std::max(it->lower(), virtual_addr); - const size_t protect_length = std::min(it->upper(), virtual_end) - offset; - DWORD old_flags{}; - if (!VirtualProtect(virtual_base + offset, protect_length, new_flags, &old_flags)) { - LOG_CRITICAL(Common_Memory, "Failed to change virtual memory protect rules"); - } - ++it; + DWORD old_flags{}; + bool success = + VirtualProtect(reinterpret_cast(virtual_addr), size, new_flags, &old_flags); + + if (!success) { + LOG_ERROR(Common_Memory, + "Failed to change virtual memory protection for address {:#x}, size {}", + virtual_addr, size); } + + // Use assert to ensure success in debug builds + DEBUG_ASSERT(success && "Failed to change virtual memory protection"); } HANDLE process{}; @@ -493,7 +503,10 @@ void AddressSpace::Unmap(VAddr virtual_addr, size_t size, VAddr start_in_vma, VA } void AddressSpace::Protect(VAddr virtual_addr, size_t size, MemoryPermission perms) { - return impl->Protect(virtual_addr, size, true, true, true); + const bool read = True(perms & MemoryPermission::Read); + const bool write = True(perms & MemoryPermission::Write); + const bool execute = True(perms & MemoryPermission::Execute); + return impl->Protect(virtual_addr, size, read, write, execute); } } // namespace Core diff --git a/src/core/libraries/kernel/event_flag/event_flag.cpp b/src/core/libraries/kernel/event_flag/event_flag.cpp index 0fd0c3bb7..ec5d6ded2 100644 --- a/src/core/libraries/kernel/event_flag/event_flag.cpp +++ b/src/core/libraries/kernel/event_flag/event_flag.cpp @@ -145,7 +145,7 @@ int PS4_SYSV_ABI sceKernelPollEventFlag(OrbisKernelEventFlag ef, u64 bitPattern, } int PS4_SYSV_ABI sceKernelWaitEventFlag(OrbisKernelEventFlag ef, u64 bitPattern, u32 waitMode, u64* pResultPat, OrbisKernelUseconds* pTimeout) { - LOG_INFO(Kernel_Event, "called bitPattern = {:#x} waitMode = {:#x}", bitPattern, waitMode); + LOG_DEBUG(Kernel_Event, "called bitPattern = {:#x} waitMode = {:#x}", bitPattern, waitMode); if (ef == nullptr) { return ORBIS_KERNEL_ERROR_ESRCH; } diff --git a/src/core/libraries/kernel/libkernel.cpp b/src/core/libraries/kernel/libkernel.cpp index 2634e25c8..d56f4dc41 100644 --- a/src/core/libraries/kernel/libkernel.cpp +++ b/src/core/libraries/kernel/libkernel.cpp @@ -454,6 +454,8 @@ void LibKernel_Register(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("F6e0kwo4cnk", "libkernel", 1, "libkernel", 1, 1, sceKernelTriggerUserEvent); LIB_FUNCTION("LJDwdSNTnDg", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteUserEvent); LIB_FUNCTION("mJ7aghmgvfc", "libkernel", 1, "libkernel", 1, 1, sceKernelGetEventId); + LIB_FUNCTION("9bfdLIyuwCY", "libkernel", 1, "libkernel", 1, 1, sceKernelMTypeProtect); + LIB_FUNCTION("vSMAm3cxYTY", "libkernel", 1, "libkernel", 1, 1, sceKernelMProtect); LIB_FUNCTION("23CPPI1tyBY", "libkernel", 1, "libkernel", 1, 1, sceKernelGetEventFilter); // misc diff --git a/src/core/libraries/kernel/memory_management.cpp b/src/core/libraries/kernel/memory_management.cpp index a5288a656..af3542912 100644 --- a/src/core/libraries/kernel/memory_management.cpp +++ b/src/core/libraries/kernel/memory_management.cpp @@ -7,6 +7,7 @@ #include "common/assert.h" #include "common/logging/log.h" #include "common/singleton.h" +#include "core/address_space.h" #include "core/libraries/error_codes.h" #include "core/libraries/kernel/memory_management.h" #include "core/linker.h" @@ -218,6 +219,19 @@ int PS4_SYSV_ABI sceKernelQueryMemoryProtection(void* addr, void** start, void** return memory->QueryProtection(std::bit_cast(addr), start, end, prot); } +int PS4_SYSV_ABI sceKernelMProtect(const void* addr, size_t size, int prot) { + Core::MemoryManager* memory_manager = Core::Memory::Instance(); + Core::MemoryProt protection_flags = static_cast(prot); + return memory_manager->Protect(std::bit_cast(addr), size, protection_flags); +} + +int PS4_SYSV_ABI sceKernelMTypeProtect(const void* addr, size_t size, int mtype, int prot) { + Core::MemoryManager* memory_manager = Core::Memory::Instance(); + Core::MemoryProt protection_flags = static_cast(prot); + return memory_manager->MTypeProtect(std::bit_cast(addr), size, + static_cast(mtype), protection_flags); +} + int PS4_SYSV_ABI sceKernelDirectMemoryQuery(u64 offset, int flags, OrbisQueryInfo* query_info, size_t infoSize) { LOG_WARNING(Kernel_Vmm, "called offset = {:#x}, flags = {:#x}", offset, flags); @@ -282,6 +296,12 @@ s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEn entries[i].operation, entries[i].length, result); break; } + case MemoryOpTypes::ORBIS_KERNEL_MAP_OP_PROTECT: { + result = sceKernelMProtect(entries[i].start, entries[i].length, entries[i].protection); + LOG_INFO(Kernel_Vmm, "entry = {}, operation = {}, len = {:#x}, result = {}", i, + entries[i].operation, entries[i].length, result); + break; + } case MemoryOpTypes::ORBIS_KERNEL_MAP_OP_MAP_FLEXIBLE: { result = sceKernelMapNamedFlexibleMemory(&entries[i].start, entries[i].length, entries[i].protection, flags, ""); @@ -292,11 +312,10 @@ s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEn break; } case MemoryOpTypes::ORBIS_KERNEL_MAP_OP_TYPE_PROTECT: { - // By now, ignore protection and log it instead - LOG_WARNING(Kernel_Vmm, - "entry = {}, operation = {}, len = {:#x}, type = {} " - "is UNSUPPORTED and skipped", - i, entries[i].operation, entries[i].length, (u8)entries[i].type); + result = sceKernelMTypeProtect(entries[i].start, entries[i].length, entries[i].type, + entries[i].protection); + LOG_INFO(Kernel_Vmm, "entry = {}, operation = {}, len = {:#x}, result = {}", i, + entries[i].operation, entries[i].length, result); break; } default: { diff --git a/src/core/libraries/kernel/memory_management.h b/src/core/libraries/kernel/memory_management.h index 761cb0844..205b2274f 100644 --- a/src/core/libraries/kernel/memory_management.h +++ b/src/core/libraries/kernel/memory_management.h @@ -95,6 +95,10 @@ s32 PS4_SYSV_ABI sceKernelMapFlexibleMemory(void** addr_in_out, std::size_t len, int flags); int PS4_SYSV_ABI sceKernelQueryMemoryProtection(void* addr, void** start, void** end, u32* prot); +int PS4_SYSV_ABI sceKernelMProtect(const void* addr, size_t size, int prot); + +int PS4_SYSV_ABI sceKernelMTypeProtect(const void* addr, size_t size, int mtype, int prot); + int PS4_SYSV_ABI sceKernelDirectMemoryQuery(u64 offset, int flags, OrbisQueryInfo* query_info, size_t infoSize); s32 PS4_SYSV_ABI sceKernelAvailableFlexibleMemorySize(size_t* sizeOut); diff --git a/src/core/libraries/kernel/thread_management.cpp b/src/core/libraries/kernel/thread_management.cpp index 11d472a49..919afcb47 100644 --- a/src/core/libraries/kernel/thread_management.cpp +++ b/src/core/libraries/kernel/thread_management.cpp @@ -414,11 +414,6 @@ ScePthreadMutex* createMutex(ScePthreadMutex* addr) { if (addr == nullptr || *addr != nullptr) { return addr; } - static std::mutex mutex; - std::scoped_lock lk{mutex}; - if (*addr != nullptr) { - return addr; - } const VAddr vaddr = reinterpret_cast(addr); std::string name = fmt::format("mutex{:#x}", vaddr); scePthreadMutexInit(addr, nullptr, name.c_str()); @@ -584,8 +579,7 @@ int PS4_SYSV_ABI scePthreadMutexLock(ScePthreadMutex* mutex) { } int PS4_SYSV_ABI scePthreadMutexUnlock(ScePthreadMutex* mutex) { - mutex = createMutex(mutex); - if (mutex == nullptr) { + if (mutex == nullptr || *mutex == nullptr) { return SCE_KERNEL_ERROR_EINVAL; } diff --git a/src/core/libraries/network/net.h b/src/core/libraries/network/net.h index 965b76809..eababdb67 100644 --- a/src/core/libraries/network/net.h +++ b/src/core/libraries/network/net.h @@ -10,8 +10,12 @@ class SymbolsResolver; } // Define our own htonll and ntohll because its not available in some systems/platforms +#ifndef HTONLL #define HTONLL(x) (((uint64_t)htonl((x) & 0xFFFFFFFFUL)) << 32) | htonl((uint32_t)((x) >> 32)) +#endif +#ifndef NTOHLL #define NTOHLL(x) (((uint64_t)ntohl((x) & 0xFFFFFFFFUL)) << 32) | ntohl((uint32_t)((x) >> 32)) +#endif namespace Libraries::Net { diff --git a/src/core/libraries/pad/pad.cpp b/src/core/libraries/pad/pad.cpp index cb0da552a..b8a27fdc2 100644 --- a/src/core/libraries/pad/pad.cpp +++ b/src/core/libraries/pad/pad.cpp @@ -250,7 +250,7 @@ int PS4_SYSV_ABI scePadOpen(s32 userId, s32 type, s32 index, const OrbisPadOpenP if (type != ORBIS_PAD_PORT_TYPE_SPECIAL) return ORBIS_PAD_ERROR_DEVICE_NOT_CONNECTED; } else { - if (type != ORBIS_PAD_PORT_TYPE_STANDARD) + if (type != ORBIS_PAD_PORT_TYPE_STANDARD && type != ORBIS_PAD_PORT_TYPE_REMOTE_CONTROL) return ORBIS_PAD_ERROR_DEVICE_NOT_CONNECTED; } return 1; // dummy @@ -263,7 +263,7 @@ int PS4_SYSV_ABI scePadOpenExt(s32 userId, s32 type, s32 index, if (type != ORBIS_PAD_PORT_TYPE_SPECIAL) return ORBIS_PAD_ERROR_DEVICE_NOT_CONNECTED; } else { - if (type != ORBIS_PAD_PORT_TYPE_STANDARD) + if (type != ORBIS_PAD_PORT_TYPE_STANDARD && type != ORBIS_PAD_PORT_TYPE_REMOTE_CONTROL) return ORBIS_PAD_ERROR_DEVICE_NOT_CONNECTED; } return 1; // dummy diff --git a/src/core/libraries/pad/pad.h b/src/core/libraries/pad/pad.h index b18bbc355..f94a642cf 100644 --- a/src/core/libraries/pad/pad.h +++ b/src/core/libraries/pad/pad.h @@ -16,6 +16,7 @@ constexpr int ORBIS_PAD_MAX_DEVICE_UNIQUE_DATA_SIZE = 12; constexpr int ORBIS_PAD_PORT_TYPE_STANDARD = 0; constexpr int ORBIS_PAD_PORT_TYPE_SPECIAL = 2; +constexpr int ORBIS_PAD_PORT_TYPE_REMOTE_CONTROL = 16; enum OrbisPadDeviceClass { ORBIS_PAD_DEVICE_CLASS_INVALID = -1, diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 640751477..44f96a001 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -7,6 +7,7 @@ #include "core/libraries/error_codes.h" #include "core/libraries/kernel/memory_management.h" #include "core/memory.h" +#include "video_core/renderer_vulkan/vk_instance.h" #include "video_core/renderer_vulkan/vk_rasterizer.h" namespace Core { @@ -292,6 +293,118 @@ int MemoryManager::QueryProtection(VAddr addr, void** start, void** end, u32* pr return ORBIS_OK; } +int MemoryManager::Protect(VAddr addr, size_t size, MemoryProt prot) { + std::scoped_lock lk{mutex}; + + // Find the virtual memory area that contains the specified address range. + auto it = FindVMA(addr); + if (it == vma_map.end() || !it->second.Contains(addr, size)) { + LOG_ERROR(Core, "Address range not mapped"); + return ORBIS_KERNEL_ERROR_EINVAL; + } + + VirtualMemoryArea& vma = it->second; + if (vma.type == VMAType::Free) { + LOG_ERROR(Core, "Cannot change protection on free memory region"); + return ORBIS_KERNEL_ERROR_EINVAL; + } + + // Validate protection flags + constexpr static MemoryProt valid_flags = MemoryProt::NoAccess | MemoryProt::CpuRead | + MemoryProt::CpuReadWrite | MemoryProt::GpuRead | + MemoryProt::GpuWrite | MemoryProt::GpuReadWrite; + + MemoryProt invalid_flags = prot & ~valid_flags; + if (u32(invalid_flags) != 0 && u32(invalid_flags) != u32(MemoryProt::NoAccess)) { + LOG_ERROR(Core, "Invalid protection flags: prot = {:#x}, invalid flags = {:#x}", u32(prot), + u32(invalid_flags)); + return ORBIS_KERNEL_ERROR_EINVAL; + } + + // Change protection + vma.prot = prot; + + // Set permissions + Core::MemoryPermission perms{}; + + if (True(prot & MemoryProt::CpuRead)) { + perms |= Core::MemoryPermission::Read; + } + if (True(prot & MemoryProt::CpuReadWrite)) { + perms |= Core::MemoryPermission::ReadWrite; + } + if (True(prot & MemoryProt::GpuRead)) { + perms |= Core::MemoryPermission::Read; + } + if (True(prot & MemoryProt::GpuWrite)) { + perms |= Core::MemoryPermission::Write; + } + if (True(prot & MemoryProt::GpuReadWrite)) { + perms |= Core::MemoryPermission::ReadWrite; + } + + impl.Protect(addr, size, perms); + + return ORBIS_OK; +} + +int MemoryManager::MTypeProtect(VAddr addr, size_t size, VMAType mtype, MemoryProt prot) { + std::scoped_lock lk{mutex}; + + // Find the virtual memory area that contains the specified address range. + auto it = FindVMA(addr); + if (it == vma_map.end() || !it->second.Contains(addr, size)) { + LOG_ERROR(Core, "Address range not mapped"); + return ORBIS_KERNEL_ERROR_EINVAL; + } + + VirtualMemoryArea& vma = it->second; + + if (vma.type == VMAType::Free) { + LOG_ERROR(Core, "Cannot change protection on free memory region"); + return ORBIS_KERNEL_ERROR_EINVAL; + } + + // Validate protection flags + constexpr static MemoryProt valid_flags = MemoryProt::NoAccess | MemoryProt::CpuRead | + MemoryProt::CpuReadWrite | MemoryProt::GpuRead | + MemoryProt::GpuWrite | MemoryProt::GpuReadWrite; + + MemoryProt invalid_flags = prot & ~valid_flags; + if (u32(invalid_flags) != 0 && u32(invalid_flags) != u32(MemoryProt::NoAccess)) { + LOG_ERROR(Core, "Invalid protection flags: prot = {:#x}, invalid flags = {:#x}", u32(prot), + u32(invalid_flags)); + return ORBIS_KERNEL_ERROR_EINVAL; + } + + // Change type and protection + vma.type = mtype; + vma.prot = prot; + + // Set permissions + Core::MemoryPermission perms{}; + + if (True(prot & MemoryProt::CpuRead)) { + perms |= Core::MemoryPermission::Read; + } + if (True(prot & MemoryProt::CpuReadWrite)) { + perms |= Core::MemoryPermission::ReadWrite; + } + if (True(prot & MemoryProt::GpuRead)) { + perms |= Core::MemoryPermission::Read; + } + if (True(prot & MemoryProt::GpuWrite)) { + perms |= Core::MemoryPermission::Write; + } + if (True(prot & MemoryProt::GpuReadWrite)) { + perms |= Core::MemoryPermission::ReadWrite; + } + + impl.Protect(addr, size, perms); + + return ORBIS_OK; +} + int MemoryManager::VirtualQuery(VAddr addr, int flags, ::Libraries::Kernel::OrbisVirtualQueryInfo* info) { std::scoped_lock lk{mutex}; diff --git a/src/core/memory.h b/src/core/memory.h index 919995b0c..d0935ffb7 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -30,6 +30,7 @@ enum class MemoryProt : u32 { GpuWrite = 32, GpuReadWrite = 38, }; +DECLARE_ENUM_FLAG_OPERATORS(MemoryProt) enum class MemoryMapFlags : u32 { NoFlags = 0, @@ -163,6 +164,10 @@ public: int QueryProtection(VAddr addr, void** start, void** end, u32* prot); + int Protect(VAddr addr, size_t size, MemoryProt prot); + + int MTypeProtect(VAddr addr, size_t size, VMAType mtype, MemoryProt prot); + int VirtualQuery(VAddr addr, int flags, ::Libraries::Kernel::OrbisVirtualQueryInfo* info); int DirectMemoryQuery(PAddr addr, bool find_next, diff --git a/src/qt_gui/game_list_frame.cpp b/src/qt_gui/game_list_frame.cpp index f5c2facbe..f8f6c6c78 100644 --- a/src/qt_gui/game_list_frame.cpp +++ b/src/qt_gui/game_list_frame.cpp @@ -31,14 +31,8 @@ GameListFrame::GameListFrame(std::shared_ptr game_info_get, QWidg this->setColumnWidth(5, 90); // Size this->setColumnWidth(6, 90); // Version QStringList headers; - headers << "Icon" - << "Name" - << "Serial" - << "Region" - << "Firmware" - << "Size" - << "Version" - << "Path"; + headers << tr("Icon") << tr("Name") << tr("Serial") << tr("Region") << tr("Firmware") + << tr("Size") << tr("Version") << tr("Path"); this->setHorizontalHeaderLabels(headers); this->horizontalHeader()->setSortIndicatorShown(true); this->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents); diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index 988e01a50..93969100d 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -34,12 +34,12 @@ bool MainWindow::Init() { CreateActions(); CreateRecentGameActions(); ConfigureGuiFromSettings(); + LoadTranslation(); CreateDockWindows(); CreateConnects(); SetLastUsedTheme(); SetLastIconSizeBullet(); GetPhysicalDevices(); - LoadTranslation(); // show ui setMinimumSize(350, minimumSizeHint().height()); setWindowTitle(QString::fromStdString("shadPS4 v" + std::string(Common::VERSION))); @@ -103,7 +103,7 @@ void MainWindow::CreateDockWindows() { QWidget* phCentralWidget = new QWidget(this); setCentralWidget(phCentralWidget); - m_dock_widget.reset(new QDockWidget("Game List", this)); + m_dock_widget.reset(new QDockWidget(tr("Game List"), this)); m_game_list_frame.reset(new GameListFrame(m_game_info, this)); m_game_list_frame->setObjectName("gamelist"); m_game_grid_frame.reset(new GameGridFrame(m_game_info, this)); diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index d572915c6..4206c4463 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -80,9 +80,13 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge } }); - connect(ui->tabWidgetSettings, &QTabWidget::currentChanged, this, [this]() { - ui->buttonBox->button(QDialogButtonBox::StandardButton::Close)->setFocus(); - }); + ui->buttonBox->button(QDialogButtonBox::Save)->setText(tr("Save")); + ui->buttonBox->button(QDialogButtonBox::Apply)->setText(tr("Apply")); + ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)->setText(tr("Restore Defaults")); + ui->buttonBox->button(QDialogButtonBox::Close)->setText(tr("Close")); + + connect(ui->tabWidgetSettings, &QTabWidget::currentChanged, this, + [this]() { ui->buttonBox->button(QDialogButtonBox::Close)->setFocus(); }); // GENERAL TAB { diff --git a/src/qt_gui/translations/ar.ts b/src/qt_gui/translations/ar.ts index d35bbbf86..12b1e7ba6 100644 --- a/src/qt_gui/translations/ar.ts +++ b/src/qt_gui/translations/ar.ts @@ -502,6 +502,11 @@ MainWindow + + + Game List + ققائمة الألعاب + * Unsupported Vulkan Version @@ -864,7 +869,7 @@ DownloadComplete_MSG - تم تنزيل التصحيحات بنجاح! تم تنزيل جميع التصحيحات لجميع الألعاب، ولا داعي لتنزيلها بشكل فردي لكل لعبة كما هو الحال مع الغش. + تم تنزيل التصحيحات بنجاح! تم تنزيل جميع التصحيحات لجميع الألعاب، ولا داعي لتنزيلها بشكل فردي لكل لعبة كما هو الحال مع الغش. إذا لم يظهر التحديث، قد يكون السبب أنه غير متوفر للإصدار وسيريال اللعبة المحدد. قد تحتاج إلى تحديث اللعبة. @@ -911,5 +916,76 @@ Name: :الاسم + + + Can't apply cheats before the game is started + لا يمكن تطبيق الغش قبل بدء اللعبة. + + + SettingsDialog + + + Save + حفظ + + + + Apply + تطبيق + + + + Restore Defaults + استعادة الإعدادات الافتراضية + + + + Close + إغلاق + + + + GameListFrame + + + Icon + أيقونة + + + + Name + اسم + + + + Serial + سيريال + + + + Region + منطقة + + + + Firmware + البرمجيات الثابتة + + + + Size + حجم + + + + Version + إصدار + + + + Path + مسار + + diff --git a/src/qt_gui/translations/da_DK.ts b/src/qt_gui/translations/da_DK.ts index c67d29b1d..bb405ec0a 100644 --- a/src/qt_gui/translations/da_DK.ts +++ b/src/qt_gui/translations/da_DK.ts @@ -500,6 +500,11 @@ MainWindow + + + Game List + Spiloversigt + * Unsupported Vulkan Version @@ -851,7 +856,7 @@ DownloadComplete_MSG - Patcher hentet med succes! Alle patches til alle spil er blevet hentet, der er ikke behov for at hente dem individuelt for hvert spil, som det sker med snyd. + Patcher hentet med succes! Alle patches til alle spil er blevet hentet, der er ikke behov for at hente dem individuelt for hvert spil, som det sker med snyd. Hvis opdateringen ikke vises, kan det være, at den ikke findes for den specifikke serie og version af spillet. Det kan være nødvendigt at opdatere spillet. @@ -898,5 +903,76 @@ Name: Navn: + + + Can't apply cheats before the game is started + Kan ikke anvende snyd før spillet er startet. + + + SettingsDialog + + + Save + Gem + + + + Apply + Anvend + + + + Restore Defaults + Gendan standardindstillinger + + + + Close + Luk + + + + GameListFrame + + + Icon + Ikon + + + + Name + Navn + + + + Serial + Seriel + + + + Region + Region + + + + Firmware + Firmware + + + + Size + Størrelse + + + + Version + Version + + + + Path + Sti + + \ No newline at end of file diff --git a/src/qt_gui/translations/de.ts b/src/qt_gui/translations/de.ts index c208ad441..1482686ce 100644 --- a/src/qt_gui/translations/de.ts +++ b/src/qt_gui/translations/de.ts @@ -500,6 +500,11 @@ MainWindow + + + Game List + Spieleliste + * Unsupported Vulkan Version @@ -851,7 +856,7 @@ DownloadComplete_MSG - Patches erfolgreich heruntergeladen! Alle Patches für alle Spiele wurden heruntergeladen, es ist nicht notwendig, sie einzeln für jedes Spiel herunterzuladen, wie es bei Cheats der Fall ist. + Patches erfolgreich heruntergeladen! Alle Patches für alle Spiele wurden heruntergeladen, es ist nicht notwendig, sie einzeln für jedes Spiel herunterzuladen, wie es bei Cheats der Fall ist. Wenn der Patch nicht angezeigt wird, könnte es sein, dass er für die spezifische Seriennummer und Version des Spiels nicht existiert. Möglicherweise müssen Sie das Spiel aktualisieren. @@ -898,5 +903,76 @@ Name: Name: + + + Can't apply cheats before the game is started + Kann keine Cheats anwenden, bevor das Spiel gestartet ist. + + + SettingsDialog + + + Save + Speichern + + + + Apply + Übernehmen + + + + Restore Defaults + Werkseinstellungen wiederherstellen + + + + Close + Schließen + + + + GameListFrame + + + Icon + Symbol + + + + Name + Name + + + + Serial + Seriennummer + + + + Region + Region + + + + Firmware + Firmware + + + + Size + Größe + + + + Version + Version + + + + Path + Pfad + + \ No newline at end of file diff --git a/src/qt_gui/translations/el.ts b/src/qt_gui/translations/el.ts index ef831fb09..4a3aa54ff 100644 --- a/src/qt_gui/translations/el.ts +++ b/src/qt_gui/translations/el.ts @@ -500,6 +500,11 @@ MainWindow + + + Game List + Λίστα παιχνιδιών + * Unsupported Vulkan Version @@ -851,7 +856,7 @@ DownloadComplete_MSG - Τα Patches κατεβάστηκαν επιτυχώς! Όλα τα Patches για όλα τα παιχνίδια έχουν κατέβει, δεν είναι απαραίτητο να τα κατεβάσετε ένα-ένα για κάθε παιχνίδι, όπως με τα Cheats. + Τα Patches κατεβάστηκαν επιτυχώς! Όλα τα Patches για όλα τα παιχνίδια έχουν κατέβει, δεν είναι απαραίτητο να τα κατεβάσετε ένα-ένα για κάθε παιχνίδι, όπως με τα Cheats. Εάν η ενημέρωση δεν εμφανίζεται, μπορεί να μην υπάρχει για τον συγκεκριμένο σειριακό αριθμό και έκδοση του παιχνιδιού. Μπορεί να χρειαστεί να ενημερώσετε το παιχνίδι. @@ -898,5 +903,76 @@ Name: Όνομα: + + + Can't apply cheats before the game is started + Δεν μπορείτε να εφαρμόσετε cheats πριν ξεκινήσει το παιχνίδι. + + + SettingsDialog + + + Save + Αποθήκευση + + + + Apply + Εφαρμογή + + + + Restore Defaults + Επαναφορά Προεπιλογών + + + + Close + Κλείσιμο + + + + GameListFrame + + + Icon + Εικονίδιο + + + + Name + Όνομα + + + + Serial + Σειριακός αριθμός + + + + Region + Περιοχή + + + + Firmware + Λογισμικό + + + + Size + Μέγεθος + + + + Version + Έκδοση + + + + Path + Διαδρομή + + \ No newline at end of file diff --git a/src/qt_gui/translations/en.ts b/src/qt_gui/translations/en.ts index b3c3b699b..9696610bc 100644 --- a/src/qt_gui/translations/en.ts +++ b/src/qt_gui/translations/en.ts @@ -500,6 +500,11 @@ MainWindow + + + Game List + Game List + * Unsupported Vulkan Version @@ -851,7 +856,7 @@ DownloadComplete_MSG - Patches Downloaded Successfully! All Patches available for all games have been downloaded, there is no need to download them individually for each game as happens in Cheats. + Patches Downloaded Successfully! All Patches available for all games have been downloaded, there is no need to download them individually for each game as happens in Cheats. If the patch does not appear, it may be that it does not exist for the specific serial and version of the game. It may be necessary to update the game. @@ -898,5 +903,76 @@ Name: Name: - + + + Can't apply cheats before the game is started + Can't apply cheats before the game is started. + + + + SettingsDialog + + + Save + Save + + + + Apply + Apply + + + + Restore Defaults + Restore Defaults + + + + Close + Close + + + + GameListFrame + + + Icon + Icon + + + + Name + Name + + + + Serial + Serial + + + + Region + Region + + + + Firmware + Firmware + + + + Size + Size + + + + Version + Version + + + + Path + Path + + \ No newline at end of file diff --git a/src/qt_gui/translations/es_ES.ts b/src/qt_gui/translations/es_ES.ts index c34dc3d44..e1bc91809 100644 --- a/src/qt_gui/translations/es_ES.ts +++ b/src/qt_gui/translations/es_ES.ts @@ -500,6 +500,11 @@ MainWindow + + + Game List + Lista de juegos + * Unsupported Vulkan Version @@ -851,7 +856,7 @@ DownloadComplete_MSG - ¡Parches descargados exitosamente! Todos los parches disponibles para todos los juegos han sido descargados, no es necesario descargarlos individualmente para cada juego como ocurre con los trucos. + ¡Parches descargados exitosamente! Todos los parches disponibles para todos los juegos han sido descargados, no es necesario descargarlos individualmente para cada juego como ocurre con los trucos. Si el parche no aparece, puede ser que no exista para el número de serie y versión específicos del juego. Puede ser necesario actualizar el juego. @@ -898,5 +903,76 @@ Name: Nombre: + + + Can't apply cheats before the game is started + No se pueden aplicar trucos antes de que se inicie el juego. + + + + SettingsDialog + + + Save + Guardar + + + + Apply + Aplicar + + + + Restore Defaults + Restaurar Valores Predeterminados + + + + Close + Cerrar + + + + GameListFrame + + + Icon + Ícono + + + + Name + Nombre + + + + Serial + Serie + + + + Region + Región + + + + Firmware + Firmware + + + + Size + Tamaño + + + + Version + Versión + + + + Path + Ruta + \ No newline at end of file diff --git a/src/qt_gui/translations/fa_IR.ts b/src/qt_gui/translations/fa_IR.ts index 129d54792..089653069 100644 --- a/src/qt_gui/translations/fa_IR.ts +++ b/src/qt_gui/translations/fa_IR.ts @@ -1,899 +1,978 @@ - - AboutDialog - - - About shadPS4 - درباره ShadPS4 - - - - shadPS4 - ShadPS4 - - - - shadPS4 is an experimental open-source emulator for the PlayStation 4. - یک شبیه ساز متن باز برای پلی استیشن 4 است. - - - - This software should not be used to play games you have not legally obtained. - این برنامه نباید برای بازی هایی که شما به صورت غیرقانونی به دست آوردید استفاده شود. - - - - ElfViewer - - - Open Folder - فولدر را بازکن - - - - GameInfoClass - - - Loading game list, please wait :3 - درحال بارگیری لیست بازی ها,لطفا کمی صبرکنید :3 - - - - Cancel - لغو - - - - Loading... - ...درحال بارگیری - - - - GameInstallDialog - - - shadPS4 - Choose directory - ShadPS4 - انتخاب محل نصب بازی - - - - Directory to install games - محل نصب بازی ها - - - - Browse - انتخاب دستی - - - - Error - ارور - - - - The value for location to install games is not valid. - .مکان داده شده برای نصب بازی درست نمی باشد - - - - GuiContextMenus - - - Create Shortcut - ساخت شورتکات - - - - Open Game Folder - بازکردن محل نصب بازی - - - - Cheats / Patches - چیت/پچ ها - - - - SFO Viewer - SFO مشاهده - - - - Trophy Viewer - مشاهده تروفی ها - - - - Copy info - کپی کردن اطلاعات - - - - Copy Name - کپی کردن نام - - - - Copy Serial - کپی کردن سریال - - - - Copy All - کپی کردن تمامی مقادیر - - - - Shortcut creation - سازنده شورتکات - - - - Shortcut created successfully!\n %1 - شورتکات با موفقیت ساخته شد! \n %1 - - - - Error - ارور - - - - Error creating shortcut!\n %1 - مشکلی در هنگام ساخت شورتکات بوجود آمد!\n %1 - - - - Install PKG - نصب PKG - - - - MainWindow - - - Open/Add Elf Folder - ELF بازکردن/ساختن پوشه - - - - Install Packages (PKG) - نصب بسته (PKG) - - - - Boot Game - اجرای بازی - - - - About shadPS4 - ShadPS4 درباره - - - - Configure... - ...تنظیمات - - - - Install application from a .pkg file - .PKG نصب بازی از فایل - - - - Recent Games - بازی های اخیر - - - - Exit - خروج - - - - Exit shadPS4 - ShadPS4 بستن - - - - Exit the application. - بستن برنامه - - - - Show Game List - نشان دادن بازی ها - - - - Game List Refresh - رفرش لیست بازی ها - - - - Tiny - کوچک ترین - - - - Small - کوچک - - - - Medium - متوسط - - - - Large - بزرگ - - - - List View - لیستی - - - - Grid View - شبکه ای (چهارخونه) - - - - Elf Viewer - Elf Viewer - - - - Game Install Directory - محل نصب بازی - - - - Download Cheats/Patches - دانلود چیت/پچ - - - - Dump Game List - استخراج لیست بازی ها - - - - PKG Viewer - PKG مشاهده گر - - - - Search... - جست و جو... - - - - File - فایل - - - - View - شخصی سازی - - - - Game List Icons - آیکون ها - - - - Game List Mode - حالت نمایش لیست بازی ها - - - - Settings - تنظیمات - - - - Utils - ابزارها - - - - Themes - تم ها - - - - About - درباره ما - - - - Dark - تیره - - - - Light - روشن - - - - Green - سبز - - - - Blue - آبی - - - - Violet - بنفش - - - - toolBar - نوار ابزار - - - - * Unsupported Vulkan Version - شما پشتیبانی نمیشود Vulkan ورژن* - - - - Download Cheats For All Installed Games - دانلود چیت برای همه بازی ها - - - - Download Patches For All Games - دانلود پچ برای همه بازی ها - - - - Download Complete - دانلود کامل شد✅ - - - - You have downloaded cheats for all the games you have installed. - چیت برای همه بازی های شما دانلودشد✅ - - - - Patches Downloaded Successfully! - پچ ها با موفقیت دانلود شد✅ - - - - All Patches available for all games have been downloaded. - ✅تمام پچ های موجود برای همه بازی های شما دانلود شد - - - - Games: - بازی ها: - - - - PKG File (*.PKG) - PKG فایل (*.PKG) - - - - ELF files (*.bin *.elf *.oelf) - ELF فایل های (*.bin *.elf *.oelf) - - - - Game Boot - اجرای بازی - - - - Only one file can be selected! - فقط یک فایل انتخاب کنید! - - - - PKG Extraction - PKG استخراج فایل - - - - Patch detected! - پچ شناسایی شد! - - - - PKG and Game versions match: - و نسخه بازی همخوانی دارد PKG فایل: - - - - Would you like to overwrite? - آیا مایل به جایگزینی فایل هستید؟ - - - - PKG Version %1 is older than installed version: - نسخه فایل PKG %1 قدیمی تر از نسخه نصب شده است: - - - - Game is installed: - بازی نصب شد: - - - - Would you like to install Patch: - آیا مایل به نصب پچ هستید: - - - - DLC Installation - نصب DLC - - - - Would you like to install DLC: %1? - آیا مایل به نصب DLC هستید: %1 - - - - DLC already installed: - قبلا نصب شده DLC این: - - - - Game already installed - این بازی قبلا نصب شده - - - - PKG is a patch, please install the game first! - فایل انتخاب شده یک پچ است, لطفا اول بازی را نصب کنید - - - - PKG ERROR - PKG ارور فایل - - - - Extracting PKG %1/%2 - درحال استخراج PKG %1/%2 - - - - Extraction Finished - استخراج به پایان رسید - - - - Game successfully installed at %1 - بازی با موفقیت در %1 نصب شد - - - - File doesn't appear to be a valid PKG file - این فایل یک PKG درست به نظر نمی آید - - - - PKGViewer - - - Open Folder - بازکردن پوشه - - - - TrophyViewer - - - Trophy Viewer - تروفی ها - - - - SettingsDialog - - - Settings - تنظیمات - - - - General - عمومی - - - - System - سیستم - - - - Console Language - زبان کنسول - - - - Emulator Language - زبان شبیه ساز - - - - Emulator - شبیه ساز - - - - Enable Fullscreen - تمام صفحه - - - - Show Splash - Splash نمایش - - - - Is PS4 Pro - PS4 Pro حالت - - - - Username - نام کاربری - - - - Logger - Logger - - - - Log Type - Log نوع - - - - Log Filter - Log فیلتر - - - - Graphics - گرافیک - - - - Graphics Device - کارت گرافیک مورداستفاده - - - - Width - عرض - - - - Height - طول - - - - Vblank Divider - Vblank Divider - - - - Advanced - ...بیشتر - - - - Enable Shaders Dumping - Shaders Dumping فعال کردن - - - - Enable NULL GPU - NULL GPU فعال کردن - - - - Enable PM4 Dumping - PM4 Dumping فعال کردن - - - - Debug - Debug - - - - Enable Debug Dumping - Debug Dumping - - - - Enable Vulkan Validation Layers - Vulkan Validation Layers - - - - Enable Vulkan Synchronization Validation - Vulkan Synchronization Validation - - - - Enable RenderDoc Debugging - RenderDoc Debugging - - - - CheatsPatches - - - Cheats / Patches - چیت / پچ ها - - - - defaultTextEdit_MSG - defaultTextEdit_MSG - - - - No Image Available - تصویری موجود نمی باشد - - - - Serial: - سریال: - - - - Version: - ورژن: - - - - Size: - حجم: - - - - Select Cheat File: - فایل چیت را انتخاب کنید: - - - - Repository: - :منبع - - - - Download Cheats - دانلود چیت ها - - - - Delete File - پاک کردن فایل - - - - No files selected. - فایلی انتخاب نشده. - - - - You can delete the cheats you don't want after downloading them. - شما میتوانید بعد از دانلود چیت هایی که نمیخواهید را پاک کنید - - - - Do you want to delete the selected file?\n%1 - آیا میخواهید فایل های انتخاب شده را پاک کنید؟ \n%1 - - - - Select Patch File: - فایل پچ را انتخاب کنید - - - - Download Patches - دانلود کردن پچ ها - - - - Save - ذخیره - - - - Cheats - چیت ها - - - - Patches - پچ ها - - - - Error - ارور - - - - No patch selected. - هیچ پچ انتخاب نشده - - - - Unable to open files.json for reading. - .json مشکل در خواندن فایل - - - - No patch file found for the current serial. - هیچ فایل پچ برای سریال بازی شما پیدا نشد. - - - - Unable to open the file for reading. - خطا در خواندن فایل - - - - Unable to open the file for writing. - خطا در نوشتن فایل - - - - Failed to parse XML: - انجام نشد XML تجزیه فایل: - - - - Success - عملیات موفق بود - - - - Options saved successfully. - تغییرات با موفقیت ذخیره شد✅ - - - - Invalid Source - منبع نامعتبر❌ - - - - The selected source is invalid. - منبع انتخاب شده نامعتبر است - - - - File Exists - فایل وجود دارد - - - - File already exists. Do you want to replace it? - فایل از قبل وجود دارد. آیا می خواهید آن را جایگزین کنید؟ - - - - Failed to save file: - ذخیره فایل موفقیت آمیز نبود: - - - - Failed to download file: - خطا در دانلود فایل: - - - - Cheats Not Found - چیت یافت نشد - - - - CheatsNotFound_MSG - متاسفانه هیچ چیتی از منبع انتخاب شده پیدا نشد! شما میتوانید منابع دیگری را برای دانلود انتخاب و یا چیت های خود را به صورت دستی واردکنید. - - - - Cheats Downloaded Successfully - دانلود چیت ها موفقیت آمیز بود✅ - - - - CheatsDownloadedSuccessfully_MSG - تمامی چیت های موجود برای این بازی از منبع انتخاب شده دانلود شد! شما همچنان میتوانید چیت های دیگری را ازمنابع مختلف دانلود کنید و درصورت موجود بودن از آنها استفاده کنید. - - - - Failed to save: - خطا در ذخیره اطلاعات: - - - - Failed to download: - خطا در دانلود❌ - - - - Download Complete - دانلود موفقیت آمیز بود✅ - - - - DownloadComplete_MSG - دانلود با موفقیت به اتمام رسید✅ - - - - Failed to parse JSON data from HTML. - HTML از JSON خطا در تجزیه اطلاعات. - - - - Failed to retrieve HTML page. - HTML خطا دربازیابی صفحه - - - - Failed to open file: - خطا در اجرای فایل: - - - - XML ERROR: - XML ERROR: - - - - Failed to open files.json for writing - .json خطا در نوشتن فایل - - - - Author: - تولید کننده: - - - - Directory does not exist: - پوشه وجود ندارد: - - - - Failed to open files.json for reading. - .json خطا در خواندن فایل - - - - Name: - نام: - - - + + AboutDialog + + + About shadPS4 + درباره ShadPS4 + + + + shadPS4 + ShadPS4 + + + + shadPS4 is an experimental open-source emulator for the PlayStation 4. + یک شبیه ساز متن باز برای پلی استیشن 4 است. + + + + This software should not be used to play games you have not legally obtained. + این برنامه نباید برای بازی هایی که شما به صورت غیرقانونی به دست آوردید استفاده شود. + + + + ElfViewer + + + Open Folder + فولدر را بازکن + + + + GameInfoClass + + + Loading game list, please wait :3 + درحال بارگیری لیست بازی ها,لطفا کمی صبرکنید :3 + + + + Cancel + لغو + + + + Loading... + ...درحال بارگیری + + + + GameInstallDialog + + + shadPS4 - Choose directory + ShadPS4 - انتخاب محل نصب بازی + + + + Directory to install games + محل نصب بازی ها + + + + Browse + انتخاب دستی + + + + Error + ارور + + + + The value for location to install games is not valid. + .مکان داده شده برای نصب بازی درست نمی باشد + + + + GuiContextMenus + + + Create Shortcut + ساخت شورتکات + + + + Open Game Folder + بازکردن محل نصب بازی + + + + Cheats / Patches + چیت/پچ ها + + + + SFO Viewer + SFO مشاهده + + + + Trophy Viewer + مشاهده تروفی ها + + + + Copy info + کپی کردن اطلاعات + + + + Copy Name + کپی کردن نام + + + + Copy Serial + کپی کردن سریال + + + + Copy All + کپی کردن تمامی مقادیر + + + + Shortcut creation + سازنده شورتکات + + + + Shortcut created successfully!\n %1 + شورتکات با موفقیت ساخته شد! \n %1 + + + + Error + ارور + + + + Error creating shortcut!\n %1 + مشکلی در هنگام ساخت شورتکات بوجود آمد!\n %1 + + + + Install PKG + نصب PKG + + + + MainWindow + + + Open/Add Elf Folder + ELF بازکردن/ساختن پوشه + + + + Install Packages (PKG) + نصب بسته (PKG) + + + + Boot Game + اجرای بازی + + + + About shadPS4 + ShadPS4 درباره + + + + Configure... + ...تنظیمات + + + + Install application from a .pkg file + .PKG نصب بازی از فایل + + + + Recent Games + بازی های اخیر + + + + Exit + خروج + + + + Exit shadPS4 + ShadPS4 بستن + + + + Exit the application. + بستن برنامه + + + + Show Game List + نشان دادن بازی ها + + + + Game List Refresh + رفرش لیست بازی ها + + + + Tiny + کوچک ترین + + + + Small + کوچک + + + + Medium + متوسط + + + + Large + بزرگ + + + + List View + لیستی + + + + Grid View + شبکه ای (چهارخونه) + + + + Elf Viewer + Elf Viewer + + + + Game Install Directory + محل نصب بازی + + + + Download Cheats/Patches + دانلود چیت/پچ + + + + Dump Game List + استخراج لیست بازی ها + + + + PKG Viewer + PKG مشاهده گر + + + + Search... + جست و جو... + + + + File + فایل + + + + View + شخصی سازی + + + + Game List Icons + آیکون ها + + + + Game List Mode + حالت نمایش لیست بازی ها + + + + Settings + تنظیمات + + + + Utils + ابزارها + + + + Themes + تم ها + + + + About + درباره ما + + + + Dark + تیره + + + + Light + روشن + + + + Green + سبز + + + + Blue + آبی + + + + Violet + بنفش + + + + toolBar + نوار ابزار + + + + PKGViewer + + + Open Folder + بازکردن پوشه + + + + TrophyViewer + + + Trophy Viewer + تروفی ها + + + + SettingsDialog + + + Settings + تنظیمات + + + + General + عمومی + + + + System + سیستم + + + + Console Language + زبان کنسول + + + + Emulator Language + زبان شبیه ساز + + + + Emulator + شبیه ساز + + + + Enable Fullscreen + تمام صفحه + + + + Show Splash + Splash نمایش + + + + Is PS4 Pro + PS4 Pro حالت + + + + Username + نام کاربری + + + + Logger + Logger + + + + Log Type + Log نوع + + + + Log Filter + Log فیلتر + + + + Graphics + گرافیک + + + + Graphics Device + کارت گرافیک مورداستفاده + + + + Width + عرض + + + + Height + طول + + + + Vblank Divider + Vblank Divider + + + + Advanced + ...بیشتر + + + + Enable Shaders Dumping + Shaders Dumping فعال کردن + + + + Enable NULL GPU + NULL GPU فعال کردن + + + + Enable PM4 Dumping + PM4 Dumping فعال کردن + + + + Debug + Debug + + + + Enable Debug Dumping + Debug Dumping + + + + Enable Vulkan Validation Layers + Vulkan Validation Layers + + + + Enable Vulkan Synchronization Validation + Vulkan Synchronization Validation + + + + Enable RenderDoc Debugging + RenderDoc Debugging + + + + MainWindow + + + Game List + لیست بازی + + + + * Unsupported Vulkan Version + شما پشتیبانی نمیشود Vulkan ورژن* + + + + Download Cheats For All Installed Games + دانلود چیت برای همه بازی ها + + + + Download Patches For All Games + دانلود پچ برای همه بازی ها + + + + Download Complete + دانلود کامل شد✅ + + + + You have downloaded cheats for all the games you have installed. + چیت برای همه بازی های شما دانلودشد✅ + + + + Patches Downloaded Successfully! + پچ ها با موفقیت دانلود شد✅ + + + + All Patches available for all games have been downloaded. + ✅تمام پچ های موجود برای همه بازی های شما دانلود شد + + + + Games: + بازی ها: + + + + PKG File (*.PKG) + PKG فایل (*.PKG) + + + + ELF files (*.bin *.elf *.oelf) + ELF فایل های (*.bin *.elf *.oelf) + + + + Game Boot + اجرای بازی + + + + Only one file can be selected! + فقط یک فایل انتخاب کنید! + + + + PKG Extraction + PKG استخراج فایل + + + + Patch detected! + پچ شناسایی شد! + + + + PKG and Game versions match: + و نسخه بازی همخوانی دارد PKG فایل: + + + + Would you like to overwrite? + آیا مایل به جایگزینی فایل هستید؟ + + + + PKG Version %1 is older than installed version: + نسخه فایل PKG %1 قدیمی تر از نسخه نصب شده است: + + + + Game is installed: + بازی نصب شد: + + + + Would you like to install Patch: + آیا مایل به نصب پچ هستید: + + + + DLC Installation + نصب DLC + + + + Would you like to install DLC: %1? + آیا مایل به نصب DLC هستید: %1 + + + + DLC already installed: + قبلا نصب شده DLC این: + + + + Game already installed + این بازی قبلا نصب شده + + + + PKG is a patch, please install the game first! + فایل انتخاب شده یک پچ است, لطفا اول بازی را نصب کنید + + + + PKG ERROR + PKG ارور فایل + + + + Extracting PKG %1/%2 + درحال استخراج PKG %1/%2 + + + + Extraction Finished + استخراج به پایان رسید + + + + Game successfully installed at %1 + بازی با موفقیت در %1 نصب شد + + + + File doesn't appear to be a valid PKG file + این فایل یک PKG درست به نظر نمی آید + + + + CheatsPatches + + + Cheats / Patches + چیت / پچ ها + + + + defaultTextEdit_MSG + defaultTextEdit_MSG + + + + No Image Available + تصویری موجود نمی باشد + + + + Serial: + سریال: + + + + Version: + ورژن: + + + + Size: + حجم: + + + + Select Cheat File: + فایل چیت را انتخاب کنید: + + + + Repository: + :منبع + + + + Download Cheats + دانلود چیت ها + + + + Delete File + پاک کردن فایل + + + + No files selected. + فایلی انتخاب نشده. + + + + You can delete the cheats you don't want after downloading them. + شما میتوانید بعد از دانلود چیت هایی که نمیخواهید را پاک کنید + + + + Do you want to delete the selected file?\n%1 + آیا میخواهید فایل های انتخاب شده را پاک کنید؟ \n%1 + + + + Select Patch File: + فایل پچ را انتخاب کنید + + + + Download Patches + دانلود کردن پچ ها + + + + Save + ذخیره + + + + Cheats + چیت ها + + + + Patches + پچ ها + + + + Error + ارور + + + + No patch selected. + هیچ پچ انتخاب نشده + + + + Unable to open files.json for reading. + .json مشکل در خواندن فایل + + + + No patch file found for the current serial. + هیچ فایل پچ برای سریال بازی شما پیدا نشد. + + + + Unable to open the file for reading. + خطا در خواندن فایل + + + + Unable to open the file for writing. + خطا در نوشتن فایل + + + + Failed to parse XML: + انجام نشد XML تجزیه فایل: + + + + Success + عملیات موفق بود + + + + Options saved successfully. + تغییرات با موفقیت ذخیره شد✅ + + + + Invalid Source + منبع نامعتبر❌ + + + + The selected source is invalid. + منبع انتخاب شده نامعتبر است + + + + File Exists + فایل وجود دارد + + + + File already exists. Do you want to replace it? + فایل از قبل وجود دارد. آیا می خواهید آن را جایگزین کنید؟ + + + + Failed to save file: + ذخیره فایل موفقیت آمیز نبود: + + + + Failed to download file: + خطا در دانلود فایل: + + + + Cheats Not Found + چیت یافت نشد + + + + CheatsNotFound_MSG + متاسفانه هیچ چیتی از منبع انتخاب شده پیدا نشد! شما میتوانید منابع دیگری را برای دانلود انتخاب و یا چیت های خود را به صورت دستی واردکنید. + + + + Cheats Downloaded Successfully + دانلود چیت ها موفقیت آمیز بود✅ + + + + CheatsDownloadedSuccessfully_MSG + تمامی چیت های موجود برای این بازی از منبع انتخاب شده دانلود شد! شما همچنان میتوانید چیت های دیگری را ازمنابع مختلف دانلود کنید و درصورت موجود بودن از آنها استفاده کنید. + + + + Failed to save: + خطا در ذخیره اطلاعات: + + + + Failed to download: + خطا در دانلود❌ + + + + Download Complete + پچ ها با موفقیت بارگیری شدند! تمام وصله های موجود برای همه بازی ها دانلود شده اند، نیازی به دانلود جداگانه آنها برای هر بازی نیست، همانطور که در Cheats اتفاق می افتد. اگر پچ ظاهر نشد، ممکن است برای سریال و نسخه خاصی از بازی وجود نداشته باشد. ممکن است نیاز به آپدیت بازی باشد.✅ + + + + DownloadComplete_MSG + دانلود با موفقیت به اتمام رسید✅ + + + + Failed to parse JSON data from HTML. + HTML از JSON خطا در تجزیه اطلاعات. + + + + Failed to retrieve HTML page. + HTML خطا دربازیابی صفحه + + + + Failed to open file: + خطا در اجرای فایل: + + + + XML ERROR: + XML ERROR: + + + + Failed to open files.json for writing + .json خطا در نوشتن فایل + + + + Author: + تولید کننده: + + + + Directory does not exist: + پوشه وجود ندارد: + + + + Failed to open files.json for reading. + .json خطا در خواندن فایل + + + + Name: + نام: + + + + Can't apply cheats before the game is started + قبل از شروع بازی نمی توانید تقلب ها را اعمال کنید. + + + + SettingsDialog + + + Save + ذخیره + + + + Apply + اعمال + + + + Restore Defaults + بازیابی پیش فرض ها + + + + Close + بستن + + + + GameListFrame + + + Icon + آیکون + + + + Name + نام + + + + Serial + سریال + + + + Region + منطقه + + + + Firmware + فریمور + + + + Size + اندازه + + + + Version + نسخه + + + + Path + مسیر + + + \ No newline at end of file diff --git a/src/qt_gui/translations/fi.ts b/src/qt_gui/translations/fi.ts index d667dd379..50e24fce6 100644 --- a/src/qt_gui/translations/fi.ts +++ b/src/qt_gui/translations/fi.ts @@ -500,6 +500,11 @@ MainWindow + + + Game List + Pelilista + * Unsupported Vulkan Version @@ -851,7 +856,7 @@ DownloadComplete_MSG - Korjaukset ladattu onnistuneesti! Kaikki saatavilla olevat korjaukset kaikille peleille on ladattu, eikä niitä tarvitse ladata yksittäin jokaiselle pelille kuten huijauksissa. + Korjaukset ladattu onnistuneesti! Kaikki saatavilla olevat korjaukset kaikille peleille on ladattu, eikä niitä tarvitse ladata yksittäin jokaiselle pelille kuten huijauksissa. Jos päivitystä ei näy, se saattaa olla, että sitä ei ole saatavilla tietylle sarjanumerolle ja peliversiolle. Saattaa olla tarpeen päivittää peli. @@ -898,5 +903,76 @@ Name: Nimi: + + + Can't apply cheats before the game is started + Ei voi käyttää huijauksia ennen kuin peli on aloitettu. + + + SettingsDialog + + + Save + Tallenna + + + + Apply + Ota käyttöön + + + + Restore Defaults + Palauta oletukset + + + + Close + Sulje + + + + GameListFrame + + + Icon + Ikoni + + + + Name + Nimi + + + + Serial + Sarjanumero + + + + Region + Alue + + + + Firmware + Ohjelmisto + + + + Size + Koko + + + + Version + Versio + + + + Path + Polku + + \ No newline at end of file diff --git a/src/qt_gui/translations/fr.ts b/src/qt_gui/translations/fr.ts index 388912d23..33e2990c0 100644 --- a/src/qt_gui/translations/fr.ts +++ b/src/qt_gui/translations/fr.ts @@ -500,6 +500,11 @@ MainWindow + + + Game List + Liste de jeux + * Unsupported Vulkan Version @@ -851,7 +856,7 @@ DownloadComplete_MSG - Patchs téléchargés avec succès ! Tous les patches disponibles pour tous les jeux ont été téléchargés, il n'est pas nécessaire de les télécharger individuellement pour chaque jeu comme c'est le cas pour les Cheats. + Patchs téléchargés avec succès ! Tous les patches disponibles pour tous les jeux ont été téléchargés, il n'est pas nécessaire de les télécharger individuellement pour chaque jeu comme c'est le cas pour les Cheats. Si le correctif n'apparaît pas, il se peut qu'il n'existe pas pour le numéro de série et la version spécifiques du jeu. Il peut être nécessaire de mettre à jour le jeu. @@ -898,5 +903,76 @@ Name: Nom : + + + Can't apply cheats before the game is started + Impossible d'appliquer les triches avant que le jeu ne commence. + - + + SettingsDialog + + + Save + Enregistrer + + + + Apply + Appliquer + + + + Restore Defaults + Restaurer les paramètres par défaut + + + + Close + Fermer + + + + GameListFrame + + + Icon + Icône + + + + Name + Nom + + + + Serial + Série + + + + Region + Région + + + + Firmware + Firmware + + + + Size + Taille + + + + Version + Version + + + + Path + Chemin + + + \ No newline at end of file diff --git a/src/qt_gui/translations/hu_HU.ts b/src/qt_gui/translations/hu_HU.ts index e5fb25a5d..0f69822e7 100644 --- a/src/qt_gui/translations/hu_HU.ts +++ b/src/qt_gui/translations/hu_HU.ts @@ -500,6 +500,11 @@ MainWindow + + + Game List + Játéklista + * Unsupported Vulkan Version @@ -851,7 +856,7 @@ DownloadComplete_MSG - Frissítések sikeresen letöltve! Minden elérhető frissítés letöltésre került, nem szükséges egyesével letölteni őket minden játékhoz, mint a csalások esetében. + Frissítések sikeresen letöltve! Minden elérhető frissítés letöltésre került, nem szükséges egyesével letölteni őket minden játékhoz, mint a csalások esetében. Ha a javítás nem jelenik meg, lehet, hogy nem létezik a játék adott sorozatszámához és verziójához. Lehet, hogy frissítenie kell a játékot. @@ -898,5 +903,76 @@ Name: Név: + + + Can't apply cheats before the game is started + Nem lehet csalásokat alkalmazni, mielőtt a játék elindul. + + + + SettingsDialog + + + Save + Mentés + + + + Apply + Alkalmaz + + + + Restore Defaults + Alapértelmezett értékek visszaállítása + + + + Close + Bezárás + + + + GameListFrame + + + Icon + Ikon + + + + Name + Név + + + + Serial + Sorozatszám + + + + Region + Régió + + + + Firmware + Firmware + + + + Size + Méret + + + + Version + Verzió + + + + Path + Útvonal + diff --git a/src/qt_gui/translations/id.ts b/src/qt_gui/translations/id.ts index b8ce27cde..6108ffa20 100644 --- a/src/qt_gui/translations/id.ts +++ b/src/qt_gui/translations/id.ts @@ -500,6 +500,11 @@ MainWindow + + + Game List + Daftar game + * Unsupported Vulkan Version @@ -851,7 +856,7 @@ DownloadComplete_MSG - Patch Berhasil Diunduh! Semua Patch yang tersedia untuk semua game telah diunduh, tidak perlu mengunduhnya satu per satu seperti yang terjadi pada Cheat. + Patch Berhasil Diunduh! Semua Patch yang tersedia untuk semua game telah diunduh, tidak perlu mengunduhnya satu per satu seperti yang terjadi pada Cheat. Jika patch tidak muncul, mungkin patch tersebut tidak ada untuk nomor seri dan versi game yang spesifik. Mungkin perlu memperbarui game. @@ -898,5 +903,76 @@ Name: Nama: + + + Can't apply cheats before the game is started + Tidak bisa menerapkan cheat sebelum permainan dimulai. + + + + SettingsDialog + + + Save + Simpan + + + + Apply + Terapkan + + + + Restore Defaults + Kembalikan Pengaturan Default + + + + Close + Tutup + + + + GameListFrame + + + Icon + Ikon + + + + Name + Nama + + + + Serial + Serial + + + + Region + Wilayah + + + + Firmware + Firmware + + + + Size + Ukuran + + + + Version + Versi + + + + Path + Jalur + \ No newline at end of file diff --git a/src/qt_gui/translations/it.ts b/src/qt_gui/translations/it.ts index 380a8e43b..39cb35dd3 100644 --- a/src/qt_gui/translations/it.ts +++ b/src/qt_gui/translations/it.ts @@ -500,6 +500,11 @@ MainWindow + + + Game List + Elenco giochi + * Unsupported Vulkan Version @@ -851,7 +856,7 @@ DownloadComplete_MSG - Patch scaricata con successo! Vengono scaricate tutte le patch disponibili per tutti i giochi, non è necessario scaricarle singolarmente per ogni gioco come nel caso dei trucchi. + Patch scaricata con successo! Vengono scaricate tutte le patch disponibili per tutti i giochi, non è necessario scaricarle singolarmente per ogni gioco come nel caso dei trucchi. Se la patch non appare, potrebbe essere che non esista per il numero di serie e la versione specifica del gioco. Potrebbe essere necessario aggiornare il gioco. @@ -898,5 +903,76 @@ Name: Nome: + + + Can't apply cheats before the game is started + Non è possibile applicare i trucchi prima dell'inizio del gioco. + + + SettingsDialog + + + Save + Salva + + + + Apply + Applica + + + + Restore Defaults + Ripristina Impostazioni Predefinite + + + + Close + Chiudi + + + + GameListFrame + + + Icon + Icona + + + + Name + Nome + + + + Serial + Seriale + + + + Region + Regione + + + + Firmware + Firmware + + + + Size + Dimensione + + + + Version + Versione + + + + Path + Percorso + + diff --git a/src/qt_gui/translations/ja_JP.ts b/src/qt_gui/translations/ja_JP.ts index 3d62de0de..680d4ebd5 100644 --- a/src/qt_gui/translations/ja_JP.ts +++ b/src/qt_gui/translations/ja_JP.ts @@ -500,6 +500,11 @@ MainWindow + + + Game List + ゲームリスト + * Unsupported Vulkan Version @@ -851,7 +856,7 @@ DownloadComplete_MSG - パッチが正常にダウンロードされました! すべてのゲームに利用可能なパッチがダウンロードされました。チートとは異なり、各ゲームごとに個別にダウンロードする必要はありません。 + パッチが正常にダウンロードされました! すべてのゲームに利用可能なパッチがダウンロードされました。チートとは異なり、各ゲームごとに個別にダウンロードする必要はありません。 パッチが表示されない場合、特定のシリアル番号とバージョンのゲームには存在しない可能性があります。ゲームを更新する必要があるかもしれません。 @@ -898,5 +903,76 @@ Name: 名前: - + + + Can't apply cheats before the game is started + ゲームが開始される前にチートを適用することはできません。 + + + + SettingsDialog + + + Save + 保存 + + + + Apply + 適用 + + + + Restore Defaults + デフォルトに戻す + + + + Close + 閉じる + + + + GameListFrame + + + Icon + アイコン + + + + Name + 名前 + + + + Serial + シリアル + + + + Region + 地域 + + + + Firmware + ファームウェア + + + + Size + サイズ + + + + Version + バージョン + + + + Path + パス + + \ No newline at end of file diff --git a/src/qt_gui/translations/ko_KR.ts b/src/qt_gui/translations/ko_KR.ts index f7f171dc6..a167311b9 100644 --- a/src/qt_gui/translations/ko_KR.ts +++ b/src/qt_gui/translations/ko_KR.ts @@ -500,6 +500,11 @@ MainWindow + + + Game List + Game List + * Unsupported Vulkan Version @@ -851,7 +856,7 @@ DownloadComplete_MSG - Patches Downloaded Successfully! All Patches available for all games have been downloaded, there is no need to download them individually for each game as happens in Cheats. + Patches Downloaded Successfully! All Patches available for all games have been downloaded, there is no need to download them individually for each game as happens in Cheats. If the patch does not appear, it may be that it does not exist for the specific serial and version of the game. It may be necessary to update the game. @@ -898,5 +903,76 @@ Name: Name: - + + + Can't apply cheats before the game is started + Can't apply cheats before the game is started. + + + + SettingsDialog + + + Save + Save + + + + Apply + Apply + + + + Restore Defaults + Restore Defaults + + + + Close + Close + + + + GameListFrame + + + Icon + Icon + + + + Name + Name + + + + Serial + Serial + + + + Region + Region + + + + Firmware + Firmware + + + + Size + Size + + + + Version + Version + + + + Path + Path + + \ No newline at end of file diff --git a/src/qt_gui/translations/lt_LT.ts b/src/qt_gui/translations/lt_LT.ts index 7aa4402e6..2c86ec0a0 100644 --- a/src/qt_gui/translations/lt_LT.ts +++ b/src/qt_gui/translations/lt_LT.ts @@ -500,6 +500,11 @@ MainWindow + + + Game List + Žaidimų sąrašas + * Unsupported Vulkan Version @@ -851,7 +856,7 @@ DownloadComplete_MSG - Pataisos sėkmingai atsisiųstos! Visos pataisos visiems žaidimams buvo atsisiųstos, nebėra reikalo jas atsisiųsti atskirai kiekvienam žaidimui, kaip tai vyksta su sukčiavimais. + Pataisos sėkmingai atsisiųstos! Visos pataisos visiems žaidimams buvo atsisiųstos, nebėra reikalo jas atsisiųsti atskirai kiekvienam žaidimui, kaip tai vyksta su sukčiavimais. Jei pleistras nepasirodo, gali būti, kad jo nėra tam tikram žaidimo serijos numeriui ir versijai. Gali prireikti atnaujinti žaidimą. @@ -898,5 +903,76 @@ Name: Pavadinimas: + + + Can't apply cheats before the game is started + Negalima taikyti sukčiavimų prieš pradedant žaidimą. + + + + SettingsDialog + + + Save + Įrašyti + + + + Apply + Taikyti + + + + Restore Defaults + Atkurti numatytuosius nustatymus + + + + Close + Uždaryti + + + + GameListFrame + + + Icon + Ikona + + + + Name + Vardas + + + + Serial + Serijinis numeris + + + + Region + Regionas + + + + Firmware + Firmvare + + + + Size + Dydis + + + + Version + Versija + + + + Path + Kelias + \ No newline at end of file diff --git a/src/qt_gui/translations/nb.ts b/src/qt_gui/translations/nb.ts index 76cad45b2..b62791e05 100644 --- a/src/qt_gui/translations/nb.ts +++ b/src/qt_gui/translations/nb.ts @@ -500,6 +500,11 @@ MainWindow + + + Game List + Spilliste + * Unsupported Vulkan Version @@ -851,7 +856,7 @@ DownloadComplete_MSG - Oppdateringer lastet ned vellykket! Alle oppdateringer tilgjengelige for alle spill har blitt lastet ned, det er ikke nødvendig å laste dem ned individuelt for hvert spill som skjer med jukser. + Oppdateringer lastet ned vellykket! Alle oppdateringer tilgjengelige for alle spill har blitt lastet ned, det er ikke nødvendig å laste dem ned individuelt for hvert spill som skjer med jukser. Hvis oppdateringen ikke vises, kan det hende at den ikke finnes for den spesifikke serienummeret og versjonen av spillet. Det kan være nødvendig å oppdatere spillet. @@ -898,5 +903,76 @@ Name: Navn: - + + + Can't apply cheats before the game is started + Kan ikke bruke juksetriks før spillet er startet. + + + + SettingsDialog + + + Save + Lag + + + + Apply + Bruk + + + + Restore Defaults + Gjenopprett standardinnstillinger + + + + Close + Lukk + + + + GameListFrame + + + Icon + Ikon + + + + Name + Navn + + + + Serial + Serienummer + + + + Region + Region + + + + Firmware + Firmware + + + + Size + Størrelse + + + + Version + Versjon + + + + Path + Sti + + \ No newline at end of file diff --git a/src/qt_gui/translations/nl.ts b/src/qt_gui/translations/nl.ts index b00460479..3d5edfc5d 100644 --- a/src/qt_gui/translations/nl.ts +++ b/src/qt_gui/translations/nl.ts @@ -500,6 +500,11 @@ MainWindow + + + Game List + Lijst met spellen + * Unsupported Vulkan Version @@ -851,7 +856,7 @@ DownloadComplete_MSG - Patches succesvol gedownload! Alle beschikbare patches voor alle spellen zijn gedownload. Het is niet nodig om ze afzonderlijk te downloaden voor elk spel dat cheats heeft. + Patches succesvol gedownload! Alle beschikbare patches voor alle spellen zijn gedownload. Het is niet nodig om ze afzonderlijk te downloaden voor elk spel dat cheats heeft. Als de patch niet verschijnt, kan het zijn dat deze niet bestaat voor het specifieke serienummer en de versie van het spel. Het kan nodig zijn om het spel bij te werken. @@ -898,5 +903,76 @@ Name: Naam: + + + Can't apply cheats before the game is started + Je kunt geen cheats toepassen voordat het spel is gestart. + + + SettingsDialog + + + Save + Opslaan + + + + Apply + Toepassen + + + + Restore Defaults + Standaardinstellingen herstellen + + + + Close + Sluiten + + + + GameListFrame + + + Icon + Pictogram + + + + Name + Naam + + + + Serial + Serienummer + + + + Region + Regio + + + + Firmware + Firmware + + + + Size + Grootte + + + + Version + Versie + + + + Path + Pad + + \ No newline at end of file diff --git a/src/qt_gui/translations/pl_PL.ts b/src/qt_gui/translations/pl_PL.ts index deaab42fb..af8330bbd 100644 --- a/src/qt_gui/translations/pl_PL.ts +++ b/src/qt_gui/translations/pl_PL.ts @@ -251,7 +251,7 @@ Game Install Directory - Katalog zainstalowanych gry + Katalog zainstalowanych gier @@ -500,6 +500,11 @@ MainWindow + + + Game List + Lista gier + * Unsupported Vulkan Version @@ -851,7 +856,7 @@ DownloadComplete_MSG - Poprawki zostały pomyślnie pobrane! Wszystkie dostępne poprawki dla wszystkich gier zostały pobrane. Nie ma potrzeby pobierania ich osobno dla każdej gry, która ma kody. + Poprawki zostały pomyślnie pobrane! Wszystkie dostępne poprawki dla wszystkich gier zostały pobrane. Nie ma potrzeby pobierania ich osobno dla każdej gry, która ma kody. Jeśli poprawka się nie pojawia, możliwe, że nie istnieje dla konkretnego numeru seryjnego i wersji gry. Może być konieczne zaktualizowanie gry. @@ -898,5 +903,76 @@ Failed to parse JSON: Nie udało się przeanlizować JSON: + + + Can't apply cheats before the game is started + Nie można zastosować kodów przed uruchomieniem gry. + + + SettingsDialog + + + Save + Zapisz + + + + Apply + Zastosuj + + + + Restore Defaults + Przywróć ustawienia domyślne + + + + Close + Zamknij + + + + GameListFrame + + + Icon + Ikona + + + + Name + Nazwa + + + + Serial + Numer seryjny + + + + Region + Region + + + + Firmware + Oprogramowanie + + + + Size + Rozmiar + + + + Version + Wersja + + + + Path + Ścieżka + + diff --git a/src/qt_gui/translations/pt_BR.ts b/src/qt_gui/translations/pt_BR.ts index 8b4538b9d..e774a30b4 100644 --- a/src/qt_gui/translations/pt_BR.ts +++ b/src/qt_gui/translations/pt_BR.ts @@ -500,6 +500,11 @@ MainWindow + + + Game List + Lista de Jogos + * Unsupported Vulkan Version @@ -851,7 +856,7 @@ DownloadComplete_MSG - Patches Baixados com Sucesso! Todos os patches disponíveis para todos os jogos foram baixados, não é necessário baixá-los individualmente para cada jogo como acontece com os Cheats. + Patches Baixados com Sucesso! Todos os patches disponíveis para todos os jogos foram baixados, não é necessário baixá-los individualmente para cada jogo como acontece com os Cheats. Se o patch não aparecer, pode ser que ele não exista para o número de série e a versão específicos do jogo. Pode ser necessário atualizar o jogo. @@ -897,6 +902,77 @@ Name: Nome: + + + + Can't apply cheats before the game is started + Não é possível aplicar cheats antes que o jogo comece. + + + + SettingsDialog + + + Save + Salvar + + + + Apply + Aplicar + + + + Restore Defaults + Restaurar Padrões + + + + Close + Fechar + + + + GameListFrame + + + Icon + Icone + + + + Name + Nome + + + + Serial + Serial + + + + Region + Região + + + + Firmware + Firmware + + + + Size + Tamanho + + + + Version + Versão + + + + Path + Diretório \ No newline at end of file diff --git a/src/qt_gui/translations/ro_RO.ts b/src/qt_gui/translations/ro_RO.ts index 8b2fda0c1..56df113f5 100644 --- a/src/qt_gui/translations/ro_RO.ts +++ b/src/qt_gui/translations/ro_RO.ts @@ -499,6 +499,11 @@ + + + Game List + Lista jocurilor + MainWindow @@ -851,7 +856,7 @@ DownloadComplete_MSG - Patches descărcate cu succes! Toate Patches disponibile pentru toate jocurile au fost descărcate; nu este nevoie să le descarci individual pentru fiecare joc, așa cum se întâmplă cu Cheats. + Patches descărcate cu succes! Toate Patches disponibile pentru toate jocurile au fost descărcate; nu este nevoie să le descarci individual pentru fiecare joc, așa cum se întâmplă cu Cheats. Dacă patch-ul nu apare, este posibil să nu existe pentru seria și versiunea specifică a jocului. Poate fi necesar să actualizați jocul. @@ -898,5 +903,76 @@ Name: Nume: + + + Can't apply cheats before the game is started + Nu poți aplica cheats înainte ca jocul să înceapă. + + + SettingsDialog + + + Save + Salvează + + + + Apply + Aplică + + + + Restore Defaults + Restabilește Impozitivele + + + + Close + Închide + + + + GameListFrame + + + Icon + Icon + + + + Name + Nume + + + + Serial + Serie + + + + Region + Regiune + + + + Firmware + Firmware + + + + Size + Dimensiune + + + + Version + Versiune + + + + Path + Drum + + \ No newline at end of file diff --git a/src/qt_gui/translations/ru_RU.ts b/src/qt_gui/translations/ru_RU.ts index 9e3446ad4..1eac3e515 100644 --- a/src/qt_gui/translations/ru_RU.ts +++ b/src/qt_gui/translations/ru_RU.ts @@ -500,6 +500,11 @@ MainWindow + + + Game List + Список игр + * Unsupported Vulkan Version @@ -851,7 +856,7 @@ DownloadComplete_MSG - Патчи успешно скачаны! Все доступные патчи для всех игр были скачаны, нет необходимости скачивать их по отдельности для каждой игры, как это происходит с читами. + Патчи успешно скачаны! Все доступные патчи для всех игр были скачаны, нет необходимости скачивать их по отдельности для каждой игры, как это происходит с читами. Если патч не появляется, возможно, его не существует для конкретного серийного номера и версии игры. Возможно, потребуется обновить игру. @@ -898,5 +903,76 @@ Name: Имя: + + + Can't apply cheats before the game is started + Невозможно применить читы до начала игрыs + + + + SettingsDialog + + + Save + Сохранить + + + + Apply + Применить + + + + Restore Defaults + Восстановить умолчания + + + + Close + Закрыть + + + + GameListFrame + + + Icon + Иконка + + + + Name + Название + + + + Serial + Серийный номер + + + + Region + Регион + + + + Firmware + Прошивка + + + + Size + Размер + + + + Version + Версия + + + + Path + Путь + \ No newline at end of file diff --git a/src/qt_gui/translations/sq.ts b/src/qt_gui/translations/sq.ts index cd1ee74fb..f5dd55949 100644 --- a/src/qt_gui/translations/sq.ts +++ b/src/qt_gui/translations/sq.ts @@ -500,6 +500,11 @@ MainWindow + + + Game List + Lista e lojërave + * Unsupported Vulkan Version @@ -851,7 +856,7 @@ DownloadComplete_MSG - Arnat u shkarkuan me sukses! Të gjitha arnat e ofruara për të gjitha lojërat janë shkarkuar, nuk ka nevojë t'i shkarkosh ato individualisht për secilën lojë siç ndodh me Mashtrimet. + Arnat u shkarkuan me sukses! Të gjitha arnat e ofruara për të gjitha lojërat janë shkarkuar, nuk ka nevojë t'i shkarkosh ato individualisht për secilën lojë siç ndodh me Mashtrimet. Nëse patch-i nuk shfaqet, mund të mos ekzistojë për numrin e serisë dhe versionin specifik të lojës. Mund të jetë e nevojshme të përditësoni lojën. @@ -898,5 +903,76 @@ Name: Emri: + + + Can't apply cheats before the game is started + Nuk mund të aplikoni mashtrime para se të fillojë loja. + + + SettingsDialog + + + Save + Ruaj + + + + Apply + Përdor + + + + Restore Defaults + Rikthe parazgjedhjet + + + + Close + Mbyll + + + + GameListFrame + + + Icon + Ikonë + + + + Name + Emri + + + + Serial + Seri + + + + Region + Rajoni + + + + Firmware + Firmware + + + + Size + Madësia + + + + Version + Versioni + + + + Path + Rrugë + + diff --git a/src/qt_gui/translations/tr_TR.ts b/src/qt_gui/translations/tr_TR.ts index e11a2d960..83f62c85e 100644 --- a/src/qt_gui/translations/tr_TR.ts +++ b/src/qt_gui/translations/tr_TR.ts @@ -500,6 +500,11 @@ MainWindow + + + Game List + Oyun Listesi + * Unsupported Vulkan Version @@ -851,7 +856,7 @@ DownloadComplete_MSG - Yamalar başarıyla indirildi! Tüm oyunlar için mevcut tüm yamalar indirildi, her oyun için ayrı ayrı indirme yapmanız gerekmez, hilelerle olduğu gibi. + Yamalar başarıyla indirildi! Tüm oyunlar için mevcut tüm yamalar indirildi, her oyun için ayrı ayrı indirme yapmanız gerekmez, hilelerle olduğu gibi. Yamanın görünmemesi durumunda, belirli seri numarası ve oyun sürümü için mevcut olmayabilir. Oyunu güncellemeniz gerekebilir. @@ -969,5 +974,76 @@ Apply Changes Değişiklikleri Uygula + + + Can't apply cheats before the game is started + Hileleri oyuna başlamadan önce uygulayamazsınız. + + + SettingsDialog + + + Save + Kaydet + + + + Apply + Uygula + + + + Restore Defaults + Varsayılanları Geri Yükle + + + + Close + Kapat + + + + GameListFrame + + + Icon + Simge + + + + Name + Ad + + + + Serial + Seri Numarası + + + + Region + Bölge + + + + Firmware + Yazılım + + + + Size + Boyut + + + + Version + Sürüm + + + + Path + Yol + + \ No newline at end of file diff --git a/src/qt_gui/translations/vi_VN.ts b/src/qt_gui/translations/vi_VN.ts index aead45a63..017e61af5 100644 --- a/src/qt_gui/translations/vi_VN.ts +++ b/src/qt_gui/translations/vi_VN.ts @@ -500,6 +500,11 @@ MainWindow + + + Game List + Danh sách trò chơi + * Unsupported Vulkan Version @@ -851,7 +856,7 @@ DownloadComplete_MSG - Bản vá đã tải xuống thành công! Tất cả các bản vá có sẵn cho tất cả các trò chơi đã được tải xuống, không cần tải xuống riêng lẻ cho mỗi trò chơi như trong Cheat. + Bản vá đã tải xuống thành công! Tất cả các bản vá có sẵn cho tất cả các trò chơi đã được tải xuống, không cần tải xuống riêng lẻ cho mỗi trò chơi như trong Cheat. Nếu bản vá không xuất hiện, có thể là nó không tồn tại cho số seri và phiên bản cụ thể của trò chơi. Có thể bạn cần phải cập nhật trò chơi. @@ -898,5 +903,76 @@ Name: Tên: + + + Can't apply cheats before the game is started + Không thể áp dụng cheat trước khi trò chơi bắt đầu. + + + SettingsDialog + + + Save + Lưu + + + + Apply + Áp dụng + + + + Restore Defaults + Khôi phục cài đặt mặc định + + + + Close + Đóng + + + + GameListFrame + + + Icon + Biểu tượng + + + + Name + Tên + + + + Serial + Số seri + + + + Region + Khu vực + + + + Firmware + Phần mềm + + + + Size + Kích thước + + + + Version + Phiên bản + + + + Path + Đường dẫn + + \ No newline at end of file diff --git a/src/qt_gui/translations/zh_CN.ts b/src/qt_gui/translations/zh_CN.ts index 7584fd5ea..f8675ed01 100644 --- a/src/qt_gui/translations/zh_CN.ts +++ b/src/qt_gui/translations/zh_CN.ts @@ -500,6 +500,11 @@ MainWindow + + + Game List + 游戏列表 + * Unsupported Vulkan Version @@ -851,7 +856,7 @@ DownloadComplete_MSG - 补丁下载成功!所有可用的补丁已下载完成,无需像作弊码那样单独下载每个游戏的补丁。 + 补丁下载成功!所有可用的补丁已下载完成,无需像作弊码那样单独下载每个游戏的补丁。如果补丁没有出现,可能是该补丁不存在于特定的序列号和游戏版本中。可能需要更新游戏。 @@ -898,5 +903,76 @@ Name: 名称: - + + + Can't apply cheats before the game is started + 在游戏开始之前无法应用作弊。 + + + + SettingsDialog + + + Save + 保存 + + + + Apply + 应用 + + + + Restore Defaults + 恢复默认 + + + + Close + 关闭 + + + + GameListFrame + + + Icon + 图标 + + + + Name + 名称 + + + + Serial + 序列号 + + + + Region + 区域 + + + + Firmware + 固件 + + + + Size + 大小 + + + + Version + 版本 + + + + Path + 路径 + + \ No newline at end of file diff --git a/src/qt_gui/translations/zh_TW.ts b/src/qt_gui/translations/zh_TW.ts index 3836ed18a..77fe691fe 100644 --- a/src/qt_gui/translations/zh_TW.ts +++ b/src/qt_gui/translations/zh_TW.ts @@ -500,6 +500,11 @@ MainWindow + + + Game List + 遊戲列表 + * Unsupported Vulkan Version @@ -851,7 +856,7 @@ DownloadComplete_MSG - 修補檔下載成功!所有遊戲的修補檔已下載完成,無需像作弊碼那樣為每個遊戲單獨下載。 + 修補檔下載成功!所有遊戲的修補檔已下載完成,無需像作弊碼那樣為每個遊戲單獨下載。如果補丁未顯示,可能是該補丁不適用於特定的序號和遊戲版本。可能需要更新遊戲。 @@ -898,5 +903,76 @@ Name: 名稱: + + + Can't apply cheats before the game is started + 在遊戲開始之前無法應用作弊。 + + + SettingsDialog + + + Save + 儲存 + + + + Apply + 應用 + + + + Restore Defaults + 還原預設值 + + + + Close + 關閉 + + + + GameListFrame + + + Icon + 圖示 + + + + Name + 名稱 + + + + Serial + 序號 + + + + Region + 區域 + + + + Firmware + 固件 + + + + Size + 大小 + + + + Version + 版本 + + + + Path + 路徑 + + \ No newline at end of file diff --git a/src/shader_recompiler/frontend/decode.cpp b/src/shader_recompiler/frontend/decode.cpp index b5c02d747..4452b4fda 100644 --- a/src/shader_recompiler/frontend/decode.cpp +++ b/src/shader_recompiler/frontend/decode.cpp @@ -5,6 +5,8 @@ #include "common/assert.h" #include "shader_recompiler/frontend/decode.h" +#include "magic_enum.hpp" + namespace Shader::Gcn { namespace bit { @@ -253,7 +255,9 @@ void GcnDecodeContext::updateInstructionMeta(InstEncoding encoding) { ASSERT_MSG(instFormat.src_type != ScalarType::Undefined && instFormat.dst_type != ScalarType::Undefined, - "TODO: Instruction format table not complete, please fix it manually."); + "Instruction format table incomplete for opcode {} ({}, encoding = {})", + magic_enum::enum_name(m_instruction.opcode), u32(m_instruction.opcode), + magic_enum::enum_name(encoding)); m_instruction.inst_class = instFormat.inst_class; m_instruction.category = instFormat.inst_category; diff --git a/src/shader_recompiler/frontend/format.cpp b/src/shader_recompiler/frontend/format.cpp index 8df3ac364..34bd618cc 100644 --- a/src/shader_recompiler/frontend/format.cpp +++ b/src/shader_recompiler/frontend/format.cpp @@ -1786,8 +1786,7 @@ constexpr std::array InstructionFormatVOP3 = {{ constexpr std::array InstructionFormatVOP1 = {{ // 0 = V_NOP - {InstClass::VectorMisc, InstCategory::VectorALU, 0, 1, ScalarType::Undefined, - ScalarType::Undefined}, + {InstClass::VectorMisc, InstCategory::VectorALU, 0, 1, ScalarType::Any, ScalarType::Any}, // 1 = V_MOV_B32 {InstClass::VectorRegMov, InstCategory::VectorALU, 1, 1, ScalarType::Uint32, ScalarType::Uint32}, @@ -3603,8 +3602,8 @@ constexpr std::array InstructionFormatMIMG = {{ {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Undefined, ScalarType::Undefined}, // 79 = IMAGE_GATHER4_C_LZ - {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Undefined, - ScalarType::Undefined}, + {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Uint32, + ScalarType::Uint32}, // 80 = IMAGE_GATHER4_O {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Undefined, ScalarType::Undefined}, @@ -3656,8 +3655,8 @@ constexpr std::array InstructionFormatMIMG = {{ {}, {}, // 104 = IMAGE_SAMPLE_CD - {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Undefined, - ScalarType::Undefined}, + {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Float32, + ScalarType::Float32}, // 105 = IMAGE_SAMPLE_CD_CL {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Undefined, ScalarType::Undefined}, diff --git a/src/shader_recompiler/frontend/translate/data_share.cpp b/src/shader_recompiler/frontend/translate/data_share.cpp index 7c23c7284..c0f0fa274 100644 --- a/src/shader_recompiler/frontend/translate/data_share.cpp +++ b/src/shader_recompiler/frontend/translate/data_share.cpp @@ -171,9 +171,9 @@ void Translator::V_READFIRSTLANE_B32(const GcnInst& inst) { const IR::U32 value{GetSrc(inst.src[0])}; if (info.stage != Stage::Compute) { - ir.SetScalarReg(dst, value); + SetDst(inst.dst[0], value); } else { - ir.SetScalarReg(dst, ir.ReadFirstLane(value)); + SetDst(inst.dst[0], ir.ReadFirstLane(value)); } } diff --git a/src/video_core/buffer_cache/buffer.cpp b/src/video_core/buffer_cache/buffer.cpp index adcea000b..702958034 100644 --- a/src/video_core/buffer_cache/buffer.cpp +++ b/src/video_core/buffer_cache/buffer.cpp @@ -91,10 +91,10 @@ void UniqueBuffer::Create(const vk::BufferCreateInfo& buffer_ci, MemoryUsage usa buffer = vk::Buffer{unsafe_buffer}; } -Buffer::Buffer(const Vulkan::Instance& instance_, MemoryUsage usage_, VAddr cpu_addr_, - vk::BufferUsageFlags flags, u64 size_bytes_) - : cpu_addr{cpu_addr_}, size_bytes{size_bytes_}, instance{&instance_}, usage{usage_}, - buffer{instance->GetDevice(), instance->GetAllocator()} { +Buffer::Buffer(const Vulkan::Instance& instance_, Vulkan::Scheduler& scheduler_, MemoryUsage usage_, + VAddr cpu_addr_, vk::BufferUsageFlags flags, u64 size_bytes_) + : cpu_addr{cpu_addr_}, size_bytes{size_bytes_}, instance{&instance_}, scheduler{&scheduler_}, + usage{usage_}, buffer{instance->GetDevice(), instance->GetAllocator()} { // Create buffer object. const vk::BufferCreateInfo buffer_ci = { .size = size_bytes, @@ -117,13 +117,6 @@ Buffer::Buffer(const Vulkan::Instance& instance_, MemoryUsage usage_, VAddr cpu_ vk::BufferView Buffer::View(u32 offset, u32 size, bool is_written, AmdGpu::DataFormat dfmt, AmdGpu::NumberFormat nfmt) { - const auto it{std::ranges::find_if(views, [=](const BufferView& view) { - return offset == view.offset && size == view.size && is_written == view.is_written && - dfmt == view.dfmt && nfmt == view.nfmt; - })}; - if (it != views.end()) { - return *it->handle; - } const vk::BufferUsageFlags2CreateInfoKHR usage_flags = { .usage = is_written ? vk::BufferUsageFlagBits2KHR::eStorageTexelBuffer : vk::BufferUsageFlagBits2KHR::eUniformTexelBuffer, @@ -135,23 +128,18 @@ vk::BufferView Buffer::View(u32 offset, u32 size, bool is_written, AmdGpu::DataF .offset = offset, .range = size, }; - views.push_back({ - .offset = offset, - .size = size, - .is_written = is_written, - .dfmt = dfmt, - .nfmt = nfmt, - .handle = instance->GetDevice().createBufferViewUnique(view_ci), - }); - return *views.back().handle; + const auto view = instance->GetDevice().createBufferView(view_ci); + scheduler->DeferOperation( + [view, device = instance->GetDevice()] { device.destroyBufferView(view); }); + return view; } constexpr u64 WATCHES_INITIAL_RESERVE = 0x4000; constexpr u64 WATCHES_RESERVE_CHUNK = 0x1000; -StreamBuffer::StreamBuffer(const Vulkan::Instance& instance, Vulkan::Scheduler& scheduler_, +StreamBuffer::StreamBuffer(const Vulkan::Instance& instance, Vulkan::Scheduler& scheduler, MemoryUsage usage, u64 size_bytes) - : Buffer{instance, usage, 0, AllFlags, size_bytes}, scheduler{scheduler_} { + : Buffer{instance, scheduler, usage, 0, AllFlags, size_bytes} { ReserveWatches(current_watches, WATCHES_INITIAL_RESERVE); ReserveWatches(previous_watches, WATCHES_INITIAL_RESERVE); const auto device = instance.GetDevice(); @@ -206,7 +194,7 @@ void StreamBuffer::Commit() { auto& watch = current_watches[current_watch_cursor++]; watch.upper_bound = offset; - watch.tick = scheduler.CurrentTick(); + watch.tick = scheduler->CurrentTick(); } void StreamBuffer::ReserveWatches(std::vector& watches, std::size_t grow_size) { @@ -220,7 +208,7 @@ void StreamBuffer::WaitPendingOperations(u64 requested_upper_bound) { while (requested_upper_bound > wait_bound && wait_cursor < *invalidation_mark) { auto& watch = previous_watches[wait_cursor]; wait_bound = watch.upper_bound; - scheduler.Wait(watch.tick); + scheduler->Wait(watch.tick); ++wait_cursor; } } diff --git a/src/video_core/buffer_cache/buffer.h b/src/video_core/buffer_cache/buffer.h index 26d48eaef..403d4ed85 100644 --- a/src/video_core/buffer_cache/buffer.h +++ b/src/video_core/buffer_cache/buffer.h @@ -73,8 +73,9 @@ struct UniqueBuffer { class Buffer { public: - explicit Buffer(const Vulkan::Instance& instance, MemoryUsage usage, VAddr cpu_addr_, - vk::BufferUsageFlags flags, u64 size_bytes_); + explicit Buffer(const Vulkan::Instance& instance, Vulkan::Scheduler& scheduler, + MemoryUsage usage, VAddr cpu_addr_, vk::BufferUsageFlags flags, + u64 size_bytes_); Buffer& operator=(const Buffer&) = delete; Buffer(const Buffer&) = delete; @@ -118,6 +119,25 @@ public: return buffer; } + std::optional GetBarrier(vk::AccessFlagBits2 dst_acess_mask, + vk::PipelineStageFlagBits2 dst_stage) { + if (dst_acess_mask == access_mask && stage == dst_stage) { + return {}; + } + + auto barrier = vk::BufferMemoryBarrier2{ + .srcStageMask = stage, + .srcAccessMask = access_mask, + .dstStageMask = dst_stage, + .dstAccessMask = dst_acess_mask, + .buffer = buffer.buffer, + .size = size_bytes, + }; + access_mask = dst_acess_mask; + stage = dst_stage; + return barrier; + } + public: VAddr cpu_addr = 0; bool is_picked{}; @@ -125,18 +145,12 @@ public: int stream_score = 0; size_t size_bytes = 0; std::span mapped_data; - const Vulkan::Instance* instance{}; + const Vulkan::Instance* instance; + Vulkan::Scheduler* scheduler; MemoryUsage usage; UniqueBuffer buffer; - struct BufferView { - u32 offset; - u32 size; - bool is_written; - AmdGpu::DataFormat dfmt; - AmdGpu::NumberFormat nfmt; - vk::UniqueBufferView handle; - }; - std::vector views; + vk::AccessFlagBits2 access_mask{vk::AccessFlagBits2::eNone}; + vk::PipelineStageFlagBits2 stage{vk::PipelineStageFlagBits2::eNone}; }; class StreamBuffer : public Buffer { @@ -175,7 +189,6 @@ private: void WaitPendingOperations(u64 requested_upper_bound); private: - Vulkan::Scheduler& scheduler; u64 offset{}; u64 mapped_size{}; std::vector current_watches; diff --git a/src/video_core/buffer_cache/buffer_cache.cpp b/src/video_core/buffer_cache/buffer_cache.cpp index 93e05085d..89032e990 100644 --- a/src/video_core/buffer_cache/buffer_cache.cpp +++ b/src/video_core/buffer_cache/buffer_cache.cpp @@ -10,20 +10,24 @@ #include "video_core/renderer_vulkan/liverpool_to_vk.h" #include "video_core/renderer_vulkan/vk_instance.h" #include "video_core/renderer_vulkan/vk_scheduler.h" +#include "video_core/texture_cache/texture_cache.h" namespace VideoCore { +static constexpr size_t NumVertexBuffers = 32; static constexpr size_t StagingBufferSize = 512_MB; static constexpr size_t UboStreamBufferSize = 64_MB; BufferCache::BufferCache(const Vulkan::Instance& instance_, Vulkan::Scheduler& scheduler_, - const AmdGpu::Liverpool* liverpool_, PageManager& tracker_) - : instance{instance_}, scheduler{scheduler_}, liverpool{liverpool_}, tracker{tracker_}, + const AmdGpu::Liverpool* liverpool_, TextureCache& texture_cache_, + PageManager& tracker_) + : instance{instance_}, scheduler{scheduler_}, liverpool{liverpool_}, + texture_cache{texture_cache_}, tracker{tracker_}, staging_buffer{instance, scheduler, MemoryUsage::Upload, StagingBufferSize}, stream_buffer{instance, scheduler, MemoryUsage::Stream, UboStreamBufferSize}, memory_tracker{&tracker} { // Ensure the first slot is used for the null buffer - void(slot_buffers.insert(instance, MemoryUsage::DeviceLocal, 0, ReadFlags, 1)); + void(slot_buffers.insert(instance, scheduler, MemoryUsage::DeviceLocal, 0, ReadFlags, 1)); } BufferCache::~BufferCache() = default; @@ -100,9 +104,9 @@ bool BufferCache::BindVertexBuffers(const Shader::Info& vs_info) { return false; } - std::array host_buffers; - std::array host_offsets; - boost::container::static_vector guest_buffers; + std::array host_buffers; + std::array host_offsets; + boost::container::static_vector guest_buffers; struct BufferRange { VAddr base_address; @@ -117,7 +121,7 @@ bool BufferCache::BindVertexBuffers(const Shader::Info& vs_info) { // Calculate buffers memory overlaps bool has_step_rate = false; - boost::container::static_vector ranges{}; + boost::container::static_vector ranges{}; for (const auto& input : vs_info.vs_inputs) { if (input.instance_step_rate == Shader::Info::VsInput::InstanceIdType::OverStepRate0 || input.instance_step_rate == Shader::Info::VsInput::InstanceIdType::OverStepRate1) { @@ -152,7 +156,7 @@ bool BufferCache::BindVertexBuffers(const Shader::Info& vs_info) { return lhv.base_address < rhv.base_address; }); - boost::container::static_vector ranges_merged{ranges[0]}; + boost::container::static_vector ranges_merged{ranges[0]}; for (auto range : ranges) { auto& prev_range = ranges_merged.back(); if (prev_range.end_address < range.base_address) { @@ -232,7 +236,7 @@ std::pair BufferCache::ObtainBuffer(VAddr device_addr, u32 size, b bool is_texel_buffer) { static constexpr u64 StreamThreshold = CACHING_PAGESIZE; const bool is_gpu_dirty = memory_tracker.IsRegionGpuModified(device_addr, size); - if (!is_written && !is_texel_buffer && size <= StreamThreshold && !is_gpu_dirty) { + if (!is_written && size <= StreamThreshold && !is_gpu_dirty) { // For small uniform buffers that have not been modified by gpu // use device local stream buffer to reduce renderpass breaks. const u64 offset = stream_buffer.Copy(device_addr, size, instance.UniformMinAlignment()); @@ -241,18 +245,18 @@ std::pair BufferCache::ObtainBuffer(VAddr device_addr, u32 size, b const BufferId buffer_id = FindBuffer(device_addr, size); Buffer& buffer = slot_buffers[buffer_id]; - SynchronizeBuffer(buffer, device_addr, size); + SynchronizeBuffer(buffer, device_addr, size, is_texel_buffer); if (is_written) { memory_tracker.MarkRegionAsGpuModified(device_addr, size); } return {&buffer, buffer.Offset(device_addr)}; } -std::pair BufferCache::ObtainTempBuffer(VAddr gpu_addr, u32 size) { +std::pair BufferCache::ObtainTempBuffer(VAddr gpu_addr, u32 size) { const u64 page = gpu_addr >> CACHING_PAGEBITS; const BufferId buffer_id = page_table[page]; if (buffer_id) { - const Buffer& buffer = slot_buffers[buffer_id]; + Buffer& buffer = slot_buffers[buffer_id]; if (buffer.IsInBounds(gpu_addr, size)) { return {&buffer, buffer.Offset(gpu_addr)}; } @@ -420,8 +424,8 @@ BufferId BufferCache::CreateBuffer(VAddr device_addr, u32 wanted_size) { wanted_size = static_cast(device_addr_end - device_addr); const OverlapResult overlap = ResolveOverlaps(device_addr, wanted_size); const u32 size = static_cast(overlap.end - overlap.begin); - const BufferId new_buffer_id = - slot_buffers.insert(instance, MemoryUsage::DeviceLocal, overlap.begin, AllFlags, size); + const BufferId new_buffer_id = slot_buffers.insert( + instance, scheduler, MemoryUsage::DeviceLocal, overlap.begin, AllFlags, size); auto& new_buffer = slot_buffers[new_buffer_id]; const size_t size_bytes = new_buffer.SizeBytes(); const auto cmdbuf = scheduler.CommandBuffer(); @@ -459,7 +463,8 @@ void BufferCache::ChangeRegister(BufferId buffer_id) { } } -bool BufferCache::SynchronizeBuffer(Buffer& buffer, VAddr device_addr, u32 size) { +void BufferCache::SynchronizeBuffer(Buffer& buffer, VAddr device_addr, u32 size, + bool is_texel_buffer) { std::scoped_lock lk{mutex}; boost::container::small_vector copies; u64 total_size_bytes = 0; @@ -479,8 +484,13 @@ bool BufferCache::SynchronizeBuffer(Buffer& buffer, VAddr device_addr, u32 size) // Prevent uploading to gpu modified regions. // gpu_modified_ranges.ForEachNotInRange(device_addr_out, range_size, add_copy); }); + SCOPE_EXIT { + if (is_texel_buffer) { + SynchronizeBufferFromImage(buffer, device_addr, size); + } + }; if (total_size_bytes == 0) { - return true; + return; } vk::Buffer src_buffer = staging_buffer.Handle(); if (total_size_bytes < StagingBufferSize) { @@ -496,7 +506,11 @@ bool BufferCache::SynchronizeBuffer(Buffer& buffer, VAddr device_addr, u32 size) } else { // For large one time transfers use a temporary host buffer. // RenderDoc can lag quite a bit if the stream buffer is too large. - Buffer temp_buffer{instance, MemoryUsage::Upload, 0, vk::BufferUsageFlagBits::eTransferSrc, + Buffer temp_buffer{instance, + scheduler, + MemoryUsage::Upload, + 0, + vk::BufferUsageFlagBits::eTransferSrc, total_size_bytes}; src_buffer = temp_buffer.Handle(); u8* const staging = temp_buffer.mapped_data.data(); @@ -524,7 +538,68 @@ bool BufferCache::SynchronizeBuffer(Buffer& buffer, VAddr device_addr, u32 size) cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eAllCommands, vk::DependencyFlagBits::eByRegion, WRITE_BARRIER, {}, {}); - return false; +} + +bool BufferCache::SynchronizeBufferFromImage(Buffer& buffer, VAddr device_addr, u32 size) { + boost::container::small_vector image_ids; + const u32 inv_size = std::min(size, MaxInvalidateDist); + texture_cache.ForEachImageInRegion(device_addr, inv_size, [&](ImageId image_id, Image& image) { + // Only consider GPU modified images, i.e render targets or storage images. + // Also avoid any CPU modified images as the image data is likely to be stale. + if (True(image.flags & ImageFlagBits::CpuModified) || + False(image.flags & ImageFlagBits::GpuModified)) { + return; + } + // Image must fully overlap with the provided buffer range. + if (image.cpu_addr < device_addr || image.cpu_addr_end > device_addr + size) { + return; + } + image_ids.push_back(image_id); + }); + if (image_ids.empty()) { + return false; + } + // Sort images by modification tick. If there are overlaps we want to + // copy from least to most recently modified. + std::ranges::sort(image_ids, [&](ImageId lhs_id, ImageId rhs_id) { + const Image& lhs = texture_cache.GetImage(lhs_id); + const Image& rhs = texture_cache.GetImage(rhs_id); + return lhs.tick_accessed_last < rhs.tick_accessed_last; + }); + boost::container::small_vector copies; + for (const ImageId image_id : image_ids) { + copies.clear(); + Image& image = texture_cache.GetImage(image_id); + u32 offset = buffer.Offset(image.cpu_addr); + const u32 num_layers = image.info.resources.layers; + for (u32 m = 0; m < image.info.resources.levels; m++) { + const u32 width = std::max(image.info.size.width >> m, 1u); + const u32 height = std::max(image.info.size.height >> m, 1u); + const u32 depth = + image.info.props.is_volume ? std::max(image.info.size.depth >> m, 1u) : 1u; + const auto& [mip_size, mip_pitch, mip_height, mip_ofs] = image.info.mips_layout[m]; + copies.push_back({ + .bufferOffset = offset, + .bufferRowLength = static_cast(mip_pitch), + .bufferImageHeight = static_cast(mip_height), + .imageSubresource{ + .aspectMask = image.aspect_mask & ~vk::ImageAspectFlagBits::eStencil, + .mipLevel = m, + .baseArrayLayer = 0, + .layerCount = num_layers, + }, + .imageOffset = {0, 0, 0}, + .imageExtent = {width, height, depth}, + }); + offset += mip_ofs * num_layers; + } + scheduler.EndRendering(); + image.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits::eTransferRead); + const auto cmdbuf = scheduler.CommandBuffer(); + cmdbuf.copyImageToBuffer(image.image, vk::ImageLayout::eTransferSrcOptimal, buffer.buffer, + copies); + } + return true; } void BufferCache::DeleteBuffer(BufferId buffer_id, bool do_not_mark) { diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index b9002cea2..b38b00f07 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -28,7 +28,7 @@ using BufferId = Common::SlotId; static constexpr BufferId NULL_BUFFER_ID{0}; -static constexpr u32 NUM_VERTEX_BUFFERS = 32; +class TextureCache; class BufferCache { public: @@ -53,7 +53,8 @@ public: public: explicit BufferCache(const Vulkan::Instance& instance, Vulkan::Scheduler& scheduler, - const AmdGpu::Liverpool* liverpool, PageManager& tracker); + const AmdGpu::Liverpool* liverpool, TextureCache& texture_cache, + PageManager& tracker); ~BufferCache(); /// Invalidates any buffer in the logical page range. @@ -70,7 +71,7 @@ public: bool is_texel_buffer = false); /// Obtains a temporary buffer for usage in texture cache. - [[nodiscard]] std::pair ObtainTempBuffer(VAddr gpu_addr, u32 size); + [[nodiscard]] std::pair ObtainTempBuffer(VAddr gpu_addr, u32 size); /// Return true when a region is registered on the cache [[nodiscard]] bool IsRegionRegistered(VAddr addr, size_t size); @@ -116,13 +117,16 @@ private: template void ChangeRegister(BufferId buffer_id); - bool SynchronizeBuffer(Buffer& buffer, VAddr device_addr, u32 size); + void SynchronizeBuffer(Buffer& buffer, VAddr device_addr, u32 size, bool is_texel_buffer); + + bool SynchronizeBufferFromImage(Buffer& buffer, VAddr device_addr, u32 size); void DeleteBuffer(BufferId buffer_id, bool do_not_mark = false); const Vulkan::Instance& instance; Vulkan::Scheduler& scheduler; const AmdGpu::Liverpool* liverpool; + TextureCache& texture_cache; PageManager& tracker; StreamBuffer staging_buffer; StreamBuffer stream_buffer; diff --git a/src/video_core/renderer_vulkan/vk_common.h b/src/video_core/renderer_vulkan/vk_common.h index 3e048749f..a2f9cbcaf 100644 --- a/src/video_core/renderer_vulkan/vk_common.h +++ b/src/video_core/renderer_vulkan/vk_common.h @@ -13,6 +13,7 @@ #define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 1 #define VULKAN_HPP_NO_CONSTRUCTORS #define VULKAN_HPP_NO_STRUCT_SETTERS +#define VULKAN_HPP_HAS_SPACESHIP_OPERATOR #include #define VMA_STATIC_VULKAN_FUNCTIONS 0 diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp index 1d9001238..b87d3c915 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp @@ -104,6 +104,7 @@ bool ComputePipeline::BindResources(VideoCore::BufferCache& buffer_cache, boost::container::static_vector buffer_infos; boost::container::static_vector image_infos; boost::container::small_vector set_writes; + boost::container::small_vector buffer_barriers; Shader::PushData push_data{}; u32 binding{}; @@ -153,9 +154,9 @@ bool ComputePipeline::BindResources(VideoCore::BufferCache& buffer_cache, for (const auto& desc : info->texture_buffers) { const auto vsharp = desc.GetSharp(*info); vk::BufferView& buffer_view = buffer_views.emplace_back(VK_NULL_HANDLE); - if (vsharp.GetDataFmt() != AmdGpu::DataFormat::FormatInvalid) { + const u32 size = vsharp.GetSize(); + if (vsharp.GetDataFmt() != AmdGpu::DataFormat::FormatInvalid && size != 0) { const VAddr address = vsharp.base_address; - const u32 size = vsharp.GetSize(); if (desc.is_written) { if (texture_cache.TouchMeta(address, true)) { LOG_TRACE(Render_Vulkan, "Metadata update skipped"); @@ -166,9 +167,6 @@ bool ComputePipeline::BindResources(VideoCore::BufferCache& buffer_cache, LOG_WARNING(Render_Vulkan, "Unexpected metadata read by a CS shader (buffer)"); } } - if (desc.is_written) { - texture_cache.InvalidateMemory(address, size); - } const u32 alignment = instance.TexelBufferMinAlignment(); const auto [vk_buffer, offset] = buffer_cache.ObtainBuffer(address, size, desc.is_written, true); @@ -183,6 +181,15 @@ bool ComputePipeline::BindResources(VideoCore::BufferCache& buffer_cache, } buffer_view = vk_buffer->View(offset_aligned, size + adjust, desc.is_written, vsharp.GetDataFmt(), vsharp.GetNumberFmt()); + if (auto barrier = + vk_buffer->GetBarrier(desc.is_written ? vk::AccessFlagBits2::eShaderWrite + : vk::AccessFlagBits2::eShaderRead, + vk::PipelineStageFlagBits2::eComputeShader)) { + buffer_barriers.emplace_back(*barrier); + } + if (desc.is_written) { + texture_cache.InvalidateMemory(address, size); + } } set_writes.push_back({ .dstSet = VK_NULL_HANDLE, @@ -198,7 +205,7 @@ bool ComputePipeline::BindResources(VideoCore::BufferCache& buffer_cache, for (const auto& image_desc : info->images) { const auto tsharp = image_desc.GetSharp(*info); if (tsharp.GetDataFmt() != AmdGpu::DataFormat::FormatInvalid) { - VideoCore::ImageInfo image_info{tsharp}; + VideoCore::ImageInfo image_info{tsharp, image_desc.is_depth}; VideoCore::ImageViewInfo view_info{tsharp, image_desc.is_storage}; const auto& image_view = texture_cache.FindTexture(image_info, view_info); const auto& image = texture_cache.GetImage(image_view.image_id); @@ -222,6 +229,9 @@ bool ComputePipeline::BindResources(VideoCore::BufferCache& buffer_cache, } for (const auto& sampler : info->samplers) { const auto ssharp = sampler.GetSharp(*info); + if (ssharp.force_degamma) { + LOG_WARNING(Render_Vulkan, "Texture requires gamma correction"); + } const auto vk_sampler = texture_cache.GetSampler(ssharp); image_infos.emplace_back(vk_sampler, VK_NULL_HANDLE, vk::ImageLayout::eGeneral); set_writes.push_back({ @@ -239,6 +249,17 @@ bool ComputePipeline::BindResources(VideoCore::BufferCache& buffer_cache, } const auto cmdbuf = scheduler.CommandBuffer(); + + if (!buffer_barriers.empty()) { + const auto dependencies = vk::DependencyInfo{ + .dependencyFlags = vk::DependencyFlagBits::eByRegion, + .bufferMemoryBarrierCount = u32(buffer_barriers.size()), + .pBufferMemoryBarriers = buffer_barriers.data(), + }; + scheduler.EndRendering(); + cmdbuf.pipelineBarrier2(dependencies); + } + cmdbuf.pushConstants(*pipeline_layout, vk::ShaderStageFlagBits::eCompute, 0u, sizeof(push_data), &push_data); cmdbuf.pushDescriptorSetKHR(vk::PipelineBindPoint::eCompute, *pipeline_layout, 0, set_writes); diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index f03e5d5de..6ac4dcf14 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -359,6 +359,7 @@ void GraphicsPipeline::BindResources(const Liverpool::Regs& regs, boost::container::static_vector buffer_infos; boost::container::static_vector image_infos; boost::container::small_vector set_writes; + boost::container::small_vector buffer_barriers; Shader::PushData push_data{}; u32 binding{}; @@ -404,15 +405,15 @@ void GraphicsPipeline::BindResources(const Liverpool::Regs& regs, }); } - for (const auto& tex_buffer : stage->texture_buffers) { - const auto vsharp = tex_buffer.GetSharp(*stage); + for (const auto& desc : stage->texture_buffers) { + const auto vsharp = desc.GetSharp(*stage); vk::BufferView& buffer_view = buffer_views.emplace_back(VK_NULL_HANDLE); - if (vsharp.GetDataFmt() != AmdGpu::DataFormat::FormatInvalid) { + const u32 size = vsharp.GetSize(); + if (vsharp.GetDataFmt() != AmdGpu::DataFormat::FormatInvalid && size != 0) { const VAddr address = vsharp.base_address; - const u32 size = vsharp.GetSize(); const u32 alignment = instance.TexelBufferMinAlignment(); const auto [vk_buffer, offset] = - buffer_cache.ObtainBuffer(address, size, tex_buffer.is_written, true); + buffer_cache.ObtainBuffer(address, size, desc.is_written, true); const u32 fmt_stride = AmdGpu::NumBits(vsharp.GetDataFmt()) >> 3; ASSERT_MSG(fmt_stride == vsharp.GetStride(), "Texel buffer stride must match format stride"); @@ -422,16 +423,25 @@ void GraphicsPipeline::BindResources(const Liverpool::Regs& regs, ASSERT(adjust % fmt_stride == 0); push_data.AddOffset(binding, adjust / fmt_stride); } - buffer_view = vk_buffer->View(offset_aligned, size + adjust, tex_buffer.is_written, + buffer_view = vk_buffer->View(offset_aligned, size + adjust, desc.is_written, vsharp.GetDataFmt(), vsharp.GetNumberFmt()); + const auto dst_access = desc.is_written ? vk::AccessFlagBits2::eShaderWrite + : vk::AccessFlagBits2::eShaderRead; + if (auto barrier = vk_buffer->GetBarrier( + dst_access, vk::PipelineStageFlagBits2::eVertexShader)) { + buffer_barriers.emplace_back(*barrier); + } + if (desc.is_written) { + texture_cache.InvalidateMemory(address, size); + } } set_writes.push_back({ .dstSet = VK_NULL_HANDLE, .dstBinding = binding++, .dstArrayElement = 0, .descriptorCount = 1, - .descriptorType = tex_buffer.is_written ? vk::DescriptorType::eStorageTexelBuffer - : vk::DescriptorType::eUniformTexelBuffer, + .descriptorType = desc.is_written ? vk::DescriptorType::eStorageTexelBuffer + : vk::DescriptorType::eUniformTexelBuffer, .pTexelBufferView = &buffer_view, }); } @@ -441,7 +451,7 @@ void GraphicsPipeline::BindResources(const Liverpool::Regs& regs, const auto tsharp = image_desc.GetSharp(*stage); if (tsharp) { tsharps.emplace_back(tsharp); - VideoCore::ImageInfo image_info{tsharp}; + VideoCore::ImageInfo image_info{tsharp, image_desc.is_depth}; VideoCore::ImageViewInfo view_info{tsharp, image_desc.is_storage}; const auto& image_view = texture_cache.FindTexture(image_info, view_info); const auto& image = texture_cache.GetImage(image_view.image_id); @@ -465,6 +475,9 @@ void GraphicsPipeline::BindResources(const Liverpool::Regs& regs, } for (const auto& sampler : stage->samplers) { auto ssharp = sampler.GetSharp(*stage); + if (ssharp.force_degamma) { + LOG_WARNING(Render_Vulkan, "Texture requires gamma correction"); + } if (sampler.disable_aniso) { const auto& tsharp = tsharps[sampler.associated_image]; if (tsharp.base_level == 0 && tsharp.last_level == 0) { @@ -485,6 +498,17 @@ void GraphicsPipeline::BindResources(const Liverpool::Regs& regs, } const auto cmdbuf = scheduler.CommandBuffer(); + + if (!buffer_barriers.empty()) { + const auto dependencies = vk::DependencyInfo{ + .dependencyFlags = vk::DependencyFlagBits::eByRegion, + .bufferMemoryBarrierCount = u32(buffer_barriers.size()), + .pBufferMemoryBarriers = buffer_barriers.data(), + }; + scheduler.EndRendering(); + cmdbuf.pipelineBarrier2(dependencies); + } + if (!set_writes.empty()) { cmdbuf.pushDescriptorSetKHR(vk::PipelineBindPoint::eGraphics, *pipeline_layout, 0, set_writes); diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index 395f71981..a055cf3bc 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -228,6 +228,7 @@ bool Instance::CreateDevice() { const bool maintenance5 = add_extension(VK_KHR_MAINTENANCE_5_EXTENSION_NAME); add_extension(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME); add_extension(VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_EXTENSION_NAME); + add_extension(VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME); #ifdef __APPLE__ // Required by Vulkan spec if supported. @@ -296,6 +297,7 @@ bool Instance::CreateDevice() { .shaderFloat16 = vk12_features.shaderFloat16, .scalarBlockLayout = vk12_features.scalarBlockLayout, .uniformBufferStandardLayout = vk12_features.uniformBufferStandardLayout, + .separateDepthStencilLayouts = vk12_features.separateDepthStencilLayouts, .hostQueryReset = vk12_features.hostQueryReset, .timelineSemaphore = vk12_features.timelineSemaphore, }, diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 6a8e0f137..b5435af1f 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -290,6 +290,11 @@ bool PipelineCache::RefreshGraphicsKey() { } const auto stage = Shader::StageFromIndex(i); const auto params = Liverpool::GetParams(*pgm); + + if (stage != Shader::Stage::Vertex && stage != Shader::Stage::Fragment) { + return false; + } + std::tie(infos[i], modules[i], key.stage_hashes[i]) = GetProgram(stage, params, binding); } return true; diff --git a/src/video_core/renderer_vulkan/vk_platform.cpp b/src/video_core/renderer_vulkan/vk_platform.cpp index 2318bb247..feadda96c 100644 --- a/src/video_core/renderer_vulkan/vk_platform.cpp +++ b/src/video_core/renderer_vulkan/vk_platform.cpp @@ -42,6 +42,8 @@ static VKAPI_ATTR VkBool32 VKAPI_CALL DebugUtilsCallback( switch (static_cast(callback_data->messageIdNumber)) { case 0x609a13b: // Vertex attribute at location not consumed by shader case 0xc81ad50e: + case 0xb7c39078: + case 0x32868fde: // vkCreateBufferView(): pCreateInfo->range does not equal VK_WHOLE_SIZE case 0x92d66fc1: // `pMultisampleState is NULL` for depth only passes (confirmed VL error) return VK_FALSE; default: diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 5a20899db..9f72d0448 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -17,7 +17,7 @@ namespace Vulkan { Rasterizer::Rasterizer(const Instance& instance_, Scheduler& scheduler_, AmdGpu::Liverpool* liverpool_) : instance{instance_}, scheduler{scheduler_}, page_manager{this}, - buffer_cache{instance, scheduler, liverpool_, page_manager}, + buffer_cache{instance, scheduler, liverpool_, texture_cache, page_manager}, texture_cache{instance, scheduler, buffer_cache, page_manager}, liverpool{liverpool_}, memory{Core::Memory::Instance()}, pipeline_cache{instance, scheduler, liverpool} { if (!Config::nullGpu()) { @@ -179,6 +179,10 @@ void Rasterizer::BeginRendering() { const auto& regs = liverpool->regs; RenderState state; + if (regs.color_control.degamma_enable) { + LOG_WARNING(Render_Vulkan, "Color buffers require gamma correction"); + } + for (auto col_buf_id = 0u; col_buf_id < Liverpool::NumColorBuffers; ++col_buf_id) { const auto& col_buf = regs.color_buffers[col_buf_id]; if (!col_buf) { diff --git a/src/video_core/texture_cache/host_compatibility.h b/src/video_core/texture_cache/host_compatibility.h new file mode 100644 index 000000000..a73f7e6be --- /dev/null +++ b/src/video_core/texture_cache/host_compatibility.h @@ -0,0 +1,392 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// Copyright © 2023 Skyline Team and Contributors (https://github.com/skyline-emu/) +// Copyright © 2015-2023 The Khronos Group Inc. +// Copyright © 2015-2023 Valve Corporation +// Copyright © 2015-2023 LunarG, Inc. + +#pragma once + +#include +#include "video_core/renderer_vulkan/vk_common.h" + +namespace VideoCore { +/** + * @brief All classes of format compatibility according to the Vulkan specification + * @url + * https://github.com/KhronosGroup/Vulkan-ValidationLayers/blob/d37c676f75f545a3e5a98d7dfb89864391a1db1e/layers/generated/vk_format_utils.h#L47-L131 + * @note This is copied directly from Vulkan Validation Layers and doesn't follow the Skyline naming + * conventions + */ +enum class FORMAT_COMPATIBILITY_CLASS { + NONE = 0, + _10BIT_2PLANE_420, + _10BIT_2PLANE_422, + _10BIT_2PLANE_444, + _10BIT_3PLANE_420, + _10BIT_3PLANE_422, + _10BIT_3PLANE_444, + _12BIT_2PLANE_420, + _12BIT_2PLANE_422, + _12BIT_2PLANE_444, + _12BIT_3PLANE_420, + _12BIT_3PLANE_422, + _12BIT_3PLANE_444, + _128BIT, + _16BIT, + _16BIT_2PLANE_420, + _16BIT_2PLANE_422, + _16BIT_2PLANE_444, + _16BIT_3PLANE_420, + _16BIT_3PLANE_422, + _16BIT_3PLANE_444, + _192BIT, + _24BIT, + _256BIT, + _32BIT, + _32BIT_B8G8R8G8, + _32BIT_G8B8G8R8, + _48BIT, + _64BIT, + _64BIT_B10G10R10G10, + _64BIT_B12G12R12G12, + _64BIT_B16G16R16G16, + _64BIT_G10B10G10R10, + _64BIT_G12B12G12R12, + _64BIT_G16B16G16R16, + _64BIT_R10G10B10A10, + _64BIT_R12G12B12A12, + _8BIT, + _8BIT_2PLANE_420, + _8BIT_2PLANE_422, + _8BIT_2PLANE_444, + _8BIT_3PLANE_420, + _8BIT_3PLANE_422, + _8BIT_3PLANE_444, + _96BIT, + ASTC_10X10, + ASTC_10X5, + ASTC_10X6, + ASTC_10X8, + ASTC_12X10, + ASTC_12X12, + ASTC_4X4, + ASTC_5X4, + ASTC_5X5, + ASTC_6X5, + ASTC_6X6, + ASTC_8X5, + ASTC_8X6, + ASTC_8X8, + BC1_RGB, + BC1_RGBA, + BC2, + BC3, + BC4, + BC5, + BC6H, + BC7, + D16, + D16S8, + D24, + D24S8, + D32, + D32S8, + EAC_R, + EAC_RG, + ETC2_EAC_RGBA, + ETC2_RGB, + ETC2_RGBA, + PVRTC1_2BPP, + PVRTC1_4BPP, + PVRTC2_2BPP, + PVRTC2_4BPP, + S8 +}; + +/** + * @brief The format compatibility class according to the Vulkan specification + * @url + * https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#formats-compatibility-classes + * @url + * https://github.com/KhronosGroup/Vulkan-ValidationLayers/blob/d37c676f75f545a3e5a98d7dfb89864391a1db1e/layers/generated/vk_format_utils.cpp#L70-L812 + * @note This is copied directly from Vulkan Validation Layers and doesn't follow the Skyline naming + * conventions + */ +static const std::unordered_map vkFormatClassTable{ + {VK_FORMAT_A1R5G5B5_UNORM_PACK16, FORMAT_COMPATIBILITY_CLASS::_16BIT}, + {VK_FORMAT_A2B10G10R10_SINT_PACK32, FORMAT_COMPATIBILITY_CLASS::_32BIT}, + {VK_FORMAT_A2B10G10R10_SNORM_PACK32, FORMAT_COMPATIBILITY_CLASS::_32BIT}, + {VK_FORMAT_A2B10G10R10_SSCALED_PACK32, FORMAT_COMPATIBILITY_CLASS::_32BIT}, + {VK_FORMAT_A2B10G10R10_UINT_PACK32, FORMAT_COMPATIBILITY_CLASS::_32BIT}, + {VK_FORMAT_A2B10G10R10_UNORM_PACK32, FORMAT_COMPATIBILITY_CLASS::_32BIT}, + {VK_FORMAT_A2B10G10R10_USCALED_PACK32, FORMAT_COMPATIBILITY_CLASS::_32BIT}, + {VK_FORMAT_A2R10G10B10_SINT_PACK32, FORMAT_COMPATIBILITY_CLASS::_32BIT}, + {VK_FORMAT_A2R10G10B10_SNORM_PACK32, FORMAT_COMPATIBILITY_CLASS::_32BIT}, + {VK_FORMAT_A2R10G10B10_SSCALED_PACK32, FORMAT_COMPATIBILITY_CLASS::_32BIT}, + {VK_FORMAT_A2R10G10B10_UINT_PACK32, FORMAT_COMPATIBILITY_CLASS::_32BIT}, + {VK_FORMAT_A2R10G10B10_UNORM_PACK32, FORMAT_COMPATIBILITY_CLASS::_32BIT}, + {VK_FORMAT_A2R10G10B10_USCALED_PACK32, FORMAT_COMPATIBILITY_CLASS::_32BIT}, + {VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT, FORMAT_COMPATIBILITY_CLASS::_16BIT}, + {VK_FORMAT_A4R4G4B4_UNORM_PACK16_EXT, FORMAT_COMPATIBILITY_CLASS::_16BIT}, + {VK_FORMAT_A8B8G8R8_SINT_PACK32, FORMAT_COMPATIBILITY_CLASS::_32BIT}, + {VK_FORMAT_A8B8G8R8_SNORM_PACK32, FORMAT_COMPATIBILITY_CLASS::_32BIT}, + {VK_FORMAT_A8B8G8R8_SRGB_PACK32, FORMAT_COMPATIBILITY_CLASS::_32BIT}, + {VK_FORMAT_A8B8G8R8_SSCALED_PACK32, FORMAT_COMPATIBILITY_CLASS::_32BIT}, + {VK_FORMAT_A8B8G8R8_UINT_PACK32, FORMAT_COMPATIBILITY_CLASS::_32BIT}, + {VK_FORMAT_A8B8G8R8_UNORM_PACK32, FORMAT_COMPATIBILITY_CLASS::_32BIT}, + {VK_FORMAT_A8B8G8R8_USCALED_PACK32, FORMAT_COMPATIBILITY_CLASS::_32BIT}, + {VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT, FORMAT_COMPATIBILITY_CLASS::ASTC_10X10}, + {VK_FORMAT_ASTC_10x10_SRGB_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_10X10}, + {VK_FORMAT_ASTC_10x10_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_10X10}, + {VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK_EXT, FORMAT_COMPATIBILITY_CLASS::ASTC_10X5}, + {VK_FORMAT_ASTC_10x5_SRGB_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_10X5}, + {VK_FORMAT_ASTC_10x5_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_10X5}, + {VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK_EXT, FORMAT_COMPATIBILITY_CLASS::ASTC_10X6}, + {VK_FORMAT_ASTC_10x6_SRGB_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_10X6}, + {VK_FORMAT_ASTC_10x6_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_10X6}, + {VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK_EXT, FORMAT_COMPATIBILITY_CLASS::ASTC_10X8}, + {VK_FORMAT_ASTC_10x8_SRGB_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_10X8}, + {VK_FORMAT_ASTC_10x8_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_10X8}, + {VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT, FORMAT_COMPATIBILITY_CLASS::ASTC_12X10}, + {VK_FORMAT_ASTC_12x10_SRGB_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_12X10}, + {VK_FORMAT_ASTC_12x10_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_12X10}, + {VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT, FORMAT_COMPATIBILITY_CLASS::ASTC_12X12}, + {VK_FORMAT_ASTC_12x12_SRGB_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_12X12}, + {VK_FORMAT_ASTC_12x12_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_12X12}, + {VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT, FORMAT_COMPATIBILITY_CLASS::ASTC_4X4}, + {VK_FORMAT_ASTC_4x4_SRGB_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_4X4}, + {VK_FORMAT_ASTC_4x4_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_4X4}, + {VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK_EXT, FORMAT_COMPATIBILITY_CLASS::ASTC_5X4}, + {VK_FORMAT_ASTC_5x4_SRGB_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_5X4}, + {VK_FORMAT_ASTC_5x4_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_5X4}, + {VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK_EXT, FORMAT_COMPATIBILITY_CLASS::ASTC_5X5}, + {VK_FORMAT_ASTC_5x5_SRGB_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_5X5}, + {VK_FORMAT_ASTC_5x5_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_5X5}, + {VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK_EXT, FORMAT_COMPATIBILITY_CLASS::ASTC_6X5}, + {VK_FORMAT_ASTC_6x5_SRGB_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_6X5}, + {VK_FORMAT_ASTC_6x5_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_6X5}, + {VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT, FORMAT_COMPATIBILITY_CLASS::ASTC_6X6}, + {VK_FORMAT_ASTC_6x6_SRGB_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_6X6}, + {VK_FORMAT_ASTC_6x6_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_6X6}, + {VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK_EXT, FORMAT_COMPATIBILITY_CLASS::ASTC_8X5}, + {VK_FORMAT_ASTC_8x5_SRGB_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_8X5}, + {VK_FORMAT_ASTC_8x5_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_8X5}, + {VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK_EXT, FORMAT_COMPATIBILITY_CLASS::ASTC_8X6}, + {VK_FORMAT_ASTC_8x6_SRGB_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_8X6}, + {VK_FORMAT_ASTC_8x6_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_8X6}, + {VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK_EXT, FORMAT_COMPATIBILITY_CLASS::ASTC_8X8}, + {VK_FORMAT_ASTC_8x8_SRGB_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_8X8}, + {VK_FORMAT_ASTC_8x8_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_8X8}, + {VK_FORMAT_B10G11R11_UFLOAT_PACK32, FORMAT_COMPATIBILITY_CLASS::_32BIT}, + {VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16, + FORMAT_COMPATIBILITY_CLASS::_64BIT_B10G10R10G10}, + {VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16, + FORMAT_COMPATIBILITY_CLASS::_64BIT_B12G12R12G12}, + {VK_FORMAT_B16G16R16G16_422_UNORM, FORMAT_COMPATIBILITY_CLASS::_64BIT_B16G16R16G16}, + {VK_FORMAT_B4G4R4A4_UNORM_PACK16, FORMAT_COMPATIBILITY_CLASS::_16BIT}, + {VK_FORMAT_B5G5R5A1_UNORM_PACK16, FORMAT_COMPATIBILITY_CLASS::_16BIT}, + {VK_FORMAT_B5G6R5_UNORM_PACK16, FORMAT_COMPATIBILITY_CLASS::_16BIT}, + {VK_FORMAT_B8G8R8A8_SINT, FORMAT_COMPATIBILITY_CLASS::_32BIT}, + {VK_FORMAT_B8G8R8A8_SNORM, FORMAT_COMPATIBILITY_CLASS::_32BIT}, + {VK_FORMAT_B8G8R8A8_SRGB, FORMAT_COMPATIBILITY_CLASS::_32BIT}, + {VK_FORMAT_B8G8R8A8_SSCALED, FORMAT_COMPATIBILITY_CLASS::_32BIT}, + {VK_FORMAT_B8G8R8A8_UINT, FORMAT_COMPATIBILITY_CLASS::_32BIT}, + {VK_FORMAT_B8G8R8A8_UNORM, FORMAT_COMPATIBILITY_CLASS::_32BIT}, + {VK_FORMAT_B8G8R8A8_USCALED, FORMAT_COMPATIBILITY_CLASS::_32BIT}, + {VK_FORMAT_B8G8R8G8_422_UNORM, FORMAT_COMPATIBILITY_CLASS::_32BIT_B8G8R8G8}, + {VK_FORMAT_B8G8R8_SINT, FORMAT_COMPATIBILITY_CLASS::_24BIT}, + {VK_FORMAT_B8G8R8_SNORM, FORMAT_COMPATIBILITY_CLASS::_24BIT}, + {VK_FORMAT_B8G8R8_SRGB, FORMAT_COMPATIBILITY_CLASS::_24BIT}, + {VK_FORMAT_B8G8R8_SSCALED, FORMAT_COMPATIBILITY_CLASS::_24BIT}, + {VK_FORMAT_B8G8R8_UINT, FORMAT_COMPATIBILITY_CLASS::_24BIT}, + {VK_FORMAT_B8G8R8_UNORM, FORMAT_COMPATIBILITY_CLASS::_24BIT}, + {VK_FORMAT_B8G8R8_USCALED, FORMAT_COMPATIBILITY_CLASS::_24BIT}, + {VK_FORMAT_BC1_RGBA_SRGB_BLOCK, FORMAT_COMPATIBILITY_CLASS::BC1_RGBA}, + {VK_FORMAT_BC1_RGBA_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::BC1_RGBA}, + {VK_FORMAT_BC1_RGB_SRGB_BLOCK, FORMAT_COMPATIBILITY_CLASS::BC1_RGB}, + {VK_FORMAT_BC1_RGB_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::BC1_RGB}, + {VK_FORMAT_BC2_SRGB_BLOCK, FORMAT_COMPATIBILITY_CLASS::BC2}, + {VK_FORMAT_BC2_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::BC2}, + {VK_FORMAT_BC3_SRGB_BLOCK, FORMAT_COMPATIBILITY_CLASS::BC3}, + {VK_FORMAT_BC3_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::BC3}, + {VK_FORMAT_BC4_SNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::BC4}, + {VK_FORMAT_BC4_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::BC4}, + {VK_FORMAT_BC5_SNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::BC5}, + {VK_FORMAT_BC5_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::BC5}, + {VK_FORMAT_BC6H_SFLOAT_BLOCK, FORMAT_COMPATIBILITY_CLASS::BC6H}, + {VK_FORMAT_BC6H_UFLOAT_BLOCK, FORMAT_COMPATIBILITY_CLASS::BC6H}, + {VK_FORMAT_BC7_SRGB_BLOCK, FORMAT_COMPATIBILITY_CLASS::BC7}, + {VK_FORMAT_BC7_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::BC7}, + {VK_FORMAT_D16_UNORM, FORMAT_COMPATIBILITY_CLASS::D16}, + {VK_FORMAT_D16_UNORM_S8_UINT, FORMAT_COMPATIBILITY_CLASS::D16S8}, + {VK_FORMAT_D24_UNORM_S8_UINT, FORMAT_COMPATIBILITY_CLASS::D24S8}, + {VK_FORMAT_D32_SFLOAT, FORMAT_COMPATIBILITY_CLASS::D32}, + {VK_FORMAT_D32_SFLOAT_S8_UINT, FORMAT_COMPATIBILITY_CLASS::D32S8}, + {VK_FORMAT_E5B9G9R9_UFLOAT_PACK32, FORMAT_COMPATIBILITY_CLASS::_32BIT}, + {VK_FORMAT_EAC_R11G11_SNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::EAC_RG}, + {VK_FORMAT_EAC_R11G11_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::EAC_RG}, + {VK_FORMAT_EAC_R11_SNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::EAC_R}, + {VK_FORMAT_EAC_R11_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::EAC_R}, + {VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK, FORMAT_COMPATIBILITY_CLASS::ETC2_RGBA}, + {VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::ETC2_RGBA}, + {VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK, FORMAT_COMPATIBILITY_CLASS::ETC2_EAC_RGBA}, + {VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::ETC2_EAC_RGBA}, + {VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK, FORMAT_COMPATIBILITY_CLASS::ETC2_RGB}, + {VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::ETC2_RGB}, + {VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16, + FORMAT_COMPATIBILITY_CLASS::_64BIT_G10B10G10R10}, + {VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16, + FORMAT_COMPATIBILITY_CLASS::_10BIT_2PLANE_420}, + {VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16, + FORMAT_COMPATIBILITY_CLASS::_10BIT_2PLANE_422}, + {VK_FORMAT_G10X6_B10X6R10X6_2PLANE_444_UNORM_3PACK16_EXT, + FORMAT_COMPATIBILITY_CLASS::_10BIT_2PLANE_444}, + {VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16, + FORMAT_COMPATIBILITY_CLASS::_10BIT_3PLANE_420}, + {VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16, + FORMAT_COMPATIBILITY_CLASS::_10BIT_3PLANE_422}, + {VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16, + FORMAT_COMPATIBILITY_CLASS::_10BIT_3PLANE_444}, + {VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16, + FORMAT_COMPATIBILITY_CLASS::_64BIT_G12B12G12R12}, + {VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16, + FORMAT_COMPATIBILITY_CLASS::_12BIT_2PLANE_420}, + {VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16, + FORMAT_COMPATIBILITY_CLASS::_12BIT_2PLANE_422}, + {VK_FORMAT_G12X4_B12X4R12X4_2PLANE_444_UNORM_3PACK16_EXT, + FORMAT_COMPATIBILITY_CLASS::_12BIT_2PLANE_444}, + {VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16, + FORMAT_COMPATIBILITY_CLASS::_12BIT_3PLANE_420}, + {VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16, + FORMAT_COMPATIBILITY_CLASS::_12BIT_3PLANE_422}, + {VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16, + FORMAT_COMPATIBILITY_CLASS::_12BIT_3PLANE_444}, + {VK_FORMAT_G16B16G16R16_422_UNORM, FORMAT_COMPATIBILITY_CLASS::_64BIT_G16B16G16R16}, + {VK_FORMAT_G16_B16R16_2PLANE_420_UNORM, FORMAT_COMPATIBILITY_CLASS::_16BIT_2PLANE_420}, + {VK_FORMAT_G16_B16R16_2PLANE_422_UNORM, FORMAT_COMPATIBILITY_CLASS::_16BIT_2PLANE_422}, + {VK_FORMAT_G16_B16R16_2PLANE_444_UNORM_EXT, FORMAT_COMPATIBILITY_CLASS::_16BIT_2PLANE_444}, + {VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM, FORMAT_COMPATIBILITY_CLASS::_16BIT_3PLANE_420}, + {VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM, FORMAT_COMPATIBILITY_CLASS::_16BIT_3PLANE_422}, + {VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM, FORMAT_COMPATIBILITY_CLASS::_16BIT_3PLANE_444}, + {VK_FORMAT_G8B8G8R8_422_UNORM, FORMAT_COMPATIBILITY_CLASS::_32BIT_G8B8G8R8}, + {VK_FORMAT_G8_B8R8_2PLANE_420_UNORM, FORMAT_COMPATIBILITY_CLASS::_8BIT_2PLANE_420}, + {VK_FORMAT_G8_B8R8_2PLANE_422_UNORM, FORMAT_COMPATIBILITY_CLASS::_8BIT_2PLANE_422}, + {VK_FORMAT_G8_B8R8_2PLANE_444_UNORM_EXT, FORMAT_COMPATIBILITY_CLASS::_8BIT_2PLANE_444}, + {VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM, FORMAT_COMPATIBILITY_CLASS::_8BIT_3PLANE_420}, + {VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM, FORMAT_COMPATIBILITY_CLASS::_8BIT_3PLANE_422}, + {VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM, FORMAT_COMPATIBILITY_CLASS::_8BIT_3PLANE_444}, + {VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG, FORMAT_COMPATIBILITY_CLASS::PVRTC1_2BPP}, + {VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG, FORMAT_COMPATIBILITY_CLASS::PVRTC1_2BPP}, + {VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG, FORMAT_COMPATIBILITY_CLASS::PVRTC1_4BPP}, + {VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG, FORMAT_COMPATIBILITY_CLASS::PVRTC1_4BPP}, + {VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG, FORMAT_COMPATIBILITY_CLASS::PVRTC2_2BPP}, + {VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG, FORMAT_COMPATIBILITY_CLASS::PVRTC2_2BPP}, + {VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG, FORMAT_COMPATIBILITY_CLASS::PVRTC2_4BPP}, + {VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG, FORMAT_COMPATIBILITY_CLASS::PVRTC2_4BPP}, + {VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16, FORMAT_COMPATIBILITY_CLASS::_64BIT_R10G10B10A10}, + {VK_FORMAT_R10X6G10X6_UNORM_2PACK16, FORMAT_COMPATIBILITY_CLASS::_32BIT}, + {VK_FORMAT_R10X6_UNORM_PACK16, FORMAT_COMPATIBILITY_CLASS::_16BIT}, + {VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16, FORMAT_COMPATIBILITY_CLASS::_64BIT_R12G12B12A12}, + {VK_FORMAT_R12X4G12X4_UNORM_2PACK16, FORMAT_COMPATIBILITY_CLASS::_32BIT}, + {VK_FORMAT_R12X4_UNORM_PACK16, FORMAT_COMPATIBILITY_CLASS::_16BIT}, + {VK_FORMAT_R16G16B16A16_SFLOAT, FORMAT_COMPATIBILITY_CLASS::_64BIT}, + {VK_FORMAT_R16G16B16A16_SINT, FORMAT_COMPATIBILITY_CLASS::_64BIT}, + {VK_FORMAT_R16G16B16A16_SNORM, FORMAT_COMPATIBILITY_CLASS::_64BIT}, + {VK_FORMAT_R16G16B16A16_SSCALED, FORMAT_COMPATIBILITY_CLASS::_64BIT}, + {VK_FORMAT_R16G16B16A16_UINT, FORMAT_COMPATIBILITY_CLASS::_64BIT}, + {VK_FORMAT_R16G16B16A16_UNORM, FORMAT_COMPATIBILITY_CLASS::_64BIT}, + {VK_FORMAT_R16G16B16A16_USCALED, FORMAT_COMPATIBILITY_CLASS::_64BIT}, + {VK_FORMAT_R16G16B16_SFLOAT, FORMAT_COMPATIBILITY_CLASS::_48BIT}, + {VK_FORMAT_R16G16B16_SINT, FORMAT_COMPATIBILITY_CLASS::_48BIT}, + {VK_FORMAT_R16G16B16_SNORM, FORMAT_COMPATIBILITY_CLASS::_48BIT}, + {VK_FORMAT_R16G16B16_SSCALED, FORMAT_COMPATIBILITY_CLASS::_48BIT}, + {VK_FORMAT_R16G16B16_UINT, FORMAT_COMPATIBILITY_CLASS::_48BIT}, + {VK_FORMAT_R16G16B16_UNORM, FORMAT_COMPATIBILITY_CLASS::_48BIT}, + {VK_FORMAT_R16G16B16_USCALED, FORMAT_COMPATIBILITY_CLASS::_48BIT}, + {VK_FORMAT_R16G16_SFLOAT, FORMAT_COMPATIBILITY_CLASS::_32BIT}, + {VK_FORMAT_R16G16_SINT, FORMAT_COMPATIBILITY_CLASS::_32BIT}, + {VK_FORMAT_R16G16_SNORM, FORMAT_COMPATIBILITY_CLASS::_32BIT}, + {VK_FORMAT_R16G16_SSCALED, FORMAT_COMPATIBILITY_CLASS::_32BIT}, + {VK_FORMAT_R16G16_UINT, FORMAT_COMPATIBILITY_CLASS::_32BIT}, + {VK_FORMAT_R16G16_UNORM, FORMAT_COMPATIBILITY_CLASS::_32BIT}, + {VK_FORMAT_R16G16_USCALED, FORMAT_COMPATIBILITY_CLASS::_32BIT}, + {VK_FORMAT_R16_SFLOAT, FORMAT_COMPATIBILITY_CLASS::_16BIT}, + {VK_FORMAT_R16_SINT, FORMAT_COMPATIBILITY_CLASS::_16BIT}, + {VK_FORMAT_R16_SNORM, FORMAT_COMPATIBILITY_CLASS::_16BIT}, + {VK_FORMAT_R16_SSCALED, FORMAT_COMPATIBILITY_CLASS::_16BIT}, + {VK_FORMAT_R16_UINT, FORMAT_COMPATIBILITY_CLASS::_16BIT}, + {VK_FORMAT_R16_UNORM, FORMAT_COMPATIBILITY_CLASS::_16BIT}, + {VK_FORMAT_R16_USCALED, FORMAT_COMPATIBILITY_CLASS::_16BIT}, + {VK_FORMAT_R32G32B32A32_SFLOAT, FORMAT_COMPATIBILITY_CLASS::_128BIT}, + {VK_FORMAT_R32G32B32A32_SINT, FORMAT_COMPATIBILITY_CLASS::_128BIT}, + {VK_FORMAT_R32G32B32A32_UINT, FORMAT_COMPATIBILITY_CLASS::_128BIT}, + {VK_FORMAT_R32G32B32_SFLOAT, FORMAT_COMPATIBILITY_CLASS::_96BIT}, + {VK_FORMAT_R32G32B32_SINT, FORMAT_COMPATIBILITY_CLASS::_96BIT}, + {VK_FORMAT_R32G32B32_UINT, FORMAT_COMPATIBILITY_CLASS::_96BIT}, + {VK_FORMAT_R32G32_SFLOAT, FORMAT_COMPATIBILITY_CLASS::_64BIT}, + {VK_FORMAT_R32G32_SINT, FORMAT_COMPATIBILITY_CLASS::_64BIT}, + {VK_FORMAT_R32G32_UINT, FORMAT_COMPATIBILITY_CLASS::_64BIT}, + {VK_FORMAT_R32_SFLOAT, FORMAT_COMPATIBILITY_CLASS::_32BIT}, + {VK_FORMAT_R32_SINT, FORMAT_COMPATIBILITY_CLASS::_32BIT}, + {VK_FORMAT_R32_UINT, FORMAT_COMPATIBILITY_CLASS::_32BIT}, + {VK_FORMAT_R4G4B4A4_UNORM_PACK16, FORMAT_COMPATIBILITY_CLASS::_16BIT}, + {VK_FORMAT_R4G4_UNORM_PACK8, FORMAT_COMPATIBILITY_CLASS::_8BIT}, + {VK_FORMAT_R5G5B5A1_UNORM_PACK16, FORMAT_COMPATIBILITY_CLASS::_16BIT}, + {VK_FORMAT_R5G6B5_UNORM_PACK16, FORMAT_COMPATIBILITY_CLASS::_16BIT}, + {VK_FORMAT_R64G64B64A64_SFLOAT, FORMAT_COMPATIBILITY_CLASS::_256BIT}, + {VK_FORMAT_R64G64B64A64_SINT, FORMAT_COMPATIBILITY_CLASS::_256BIT}, + {VK_FORMAT_R64G64B64A64_UINT, FORMAT_COMPATIBILITY_CLASS::_256BIT}, + {VK_FORMAT_R64G64B64_SFLOAT, FORMAT_COMPATIBILITY_CLASS::_192BIT}, + {VK_FORMAT_R64G64B64_SINT, FORMAT_COMPATIBILITY_CLASS::_192BIT}, + {VK_FORMAT_R64G64B64_UINT, FORMAT_COMPATIBILITY_CLASS::_192BIT}, + {VK_FORMAT_R64G64_SFLOAT, FORMAT_COMPATIBILITY_CLASS::_128BIT}, + {VK_FORMAT_R64G64_SINT, FORMAT_COMPATIBILITY_CLASS::_128BIT}, + {VK_FORMAT_R64G64_UINT, FORMAT_COMPATIBILITY_CLASS::_128BIT}, + {VK_FORMAT_R64_SFLOAT, FORMAT_COMPATIBILITY_CLASS::_64BIT}, + {VK_FORMAT_R64_SINT, FORMAT_COMPATIBILITY_CLASS::_64BIT}, + {VK_FORMAT_R64_UINT, FORMAT_COMPATIBILITY_CLASS::_64BIT}, + {VK_FORMAT_R8G8B8A8_SINT, FORMAT_COMPATIBILITY_CLASS::_32BIT}, + {VK_FORMAT_R8G8B8A8_SNORM, FORMAT_COMPATIBILITY_CLASS::_32BIT}, + {VK_FORMAT_R8G8B8A8_SRGB, FORMAT_COMPATIBILITY_CLASS::_32BIT}, + {VK_FORMAT_R8G8B8A8_SSCALED, FORMAT_COMPATIBILITY_CLASS::_32BIT}, + {VK_FORMAT_R8G8B8A8_UINT, FORMAT_COMPATIBILITY_CLASS::_32BIT}, + {VK_FORMAT_R8G8B8A8_UNORM, FORMAT_COMPATIBILITY_CLASS::_32BIT}, + {VK_FORMAT_R8G8B8A8_USCALED, FORMAT_COMPATIBILITY_CLASS::_32BIT}, + {VK_FORMAT_R8G8B8_SINT, FORMAT_COMPATIBILITY_CLASS::_24BIT}, + {VK_FORMAT_R8G8B8_SNORM, FORMAT_COMPATIBILITY_CLASS::_24BIT}, + {VK_FORMAT_R8G8B8_SRGB, FORMAT_COMPATIBILITY_CLASS::_24BIT}, + {VK_FORMAT_R8G8B8_SSCALED, FORMAT_COMPATIBILITY_CLASS::_24BIT}, + {VK_FORMAT_R8G8B8_UINT, FORMAT_COMPATIBILITY_CLASS::_24BIT}, + {VK_FORMAT_R8G8B8_UNORM, FORMAT_COMPATIBILITY_CLASS::_24BIT}, + {VK_FORMAT_R8G8B8_USCALED, FORMAT_COMPATIBILITY_CLASS::_24BIT}, + {VK_FORMAT_R8G8_SINT, FORMAT_COMPATIBILITY_CLASS::_16BIT}, + {VK_FORMAT_R8G8_SNORM, FORMAT_COMPATIBILITY_CLASS::_16BIT}, + {VK_FORMAT_R8G8_SRGB, FORMAT_COMPATIBILITY_CLASS::_16BIT}, + {VK_FORMAT_R8G8_SSCALED, FORMAT_COMPATIBILITY_CLASS::_16BIT}, + {VK_FORMAT_R8G8_UINT, FORMAT_COMPATIBILITY_CLASS::_16BIT}, + {VK_FORMAT_R8G8_UNORM, FORMAT_COMPATIBILITY_CLASS::_16BIT}, + {VK_FORMAT_R8G8_USCALED, FORMAT_COMPATIBILITY_CLASS::_16BIT}, + {VK_FORMAT_R8_SINT, FORMAT_COMPATIBILITY_CLASS::_8BIT}, + {VK_FORMAT_R8_SNORM, FORMAT_COMPATIBILITY_CLASS::_8BIT}, + {VK_FORMAT_R8_SRGB, FORMAT_COMPATIBILITY_CLASS::_8BIT}, + {VK_FORMAT_R8_SSCALED, FORMAT_COMPATIBILITY_CLASS::_8BIT}, + {VK_FORMAT_R8_UINT, FORMAT_COMPATIBILITY_CLASS::_8BIT}, + {VK_FORMAT_R8_UNORM, FORMAT_COMPATIBILITY_CLASS::_8BIT}, + {VK_FORMAT_R8_USCALED, FORMAT_COMPATIBILITY_CLASS::_8BIT}, + {VK_FORMAT_S8_UINT, FORMAT_COMPATIBILITY_CLASS::S8}, + {VK_FORMAT_X8_D24_UNORM_PACK32, FORMAT_COMPATIBILITY_CLASS::D24}, + {VK_FORMAT_UNDEFINED, FORMAT_COMPATIBILITY_CLASS::NONE}, +}; + +/** + * @return If the two formats are compatible according to Vulkan's format compatibility rules + * @url + * https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#formats-compatibility + */ +static bool IsVulkanFormatCompatible(vk::Format lhs, vk::Format rhs) { + if (lhs == rhs) { + return true; + } + return vkFormatClassTable.at(VkFormat(lhs)) == vkFormatClassTable.at(VkFormat(rhs)); +} +} // namespace VideoCore diff --git a/src/video_core/texture_cache/image.cpp b/src/video_core/texture_cache/image.cpp index 0d20eaeab..2a5c4c434 100644 --- a/src/video_core/texture_cache/image.cpp +++ b/src/video_core/texture_cache/image.cpp @@ -166,8 +166,9 @@ Image::Image(const Vulkan::Instance& instance_, Vulkan::Scheduler& scheduler_, image.Create(image_ci); - Vulkan::SetObjectName(instance->GetDevice(), (vk::Image)image, "Image {:#x}:{:#x}", - info.guest_address, info.guest_size_bytes); + Vulkan::SetObjectName(instance->GetDevice(), (vk::Image)image, "Image {}x{}x{} {:#x}:{:#x}", + info.size.width, info.size.height, info.size.depth, info.guest_address, + info.guest_size_bytes); } void Image::Transit(vk::ImageLayout dst_layout, vk::Flags dst_mask, @@ -242,6 +243,74 @@ void Image::Upload(vk::Buffer buffer, u64 offset) { vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eTransferRead); } +void Image::CopyImage(const Image& image) { + scheduler->EndRendering(); + Transit(vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits::eTransferWrite); + + auto cmdbuf = scheduler->CommandBuffer(); + + boost::container::small_vector image_copy{}; + for (u32 m = 0; m < image.info.resources.levels; ++m) { + const auto mip_w = std::max(info.size.width >> m, 1u); + const auto mip_h = std::max(info.size.height >> m, 1u); + const auto mip_d = std::max(info.size.depth >> m, 1u); + + image_copy.emplace_back(vk::ImageCopy{ + .srcSubresource{ + .aspectMask = image.aspect_mask, + .mipLevel = m, + .baseArrayLayer = 0, + .layerCount = image.info.resources.layers, + }, + .dstSubresource{ + .aspectMask = image.aspect_mask, + .mipLevel = m, + .baseArrayLayer = 0, + .layerCount = image.info.resources.layers, + }, + .extent = {mip_w, mip_h, mip_d}, + }); + } + cmdbuf.copyImage(image.image, image.layout, this->image, this->layout, image_copy); + + Transit(vk::ImageLayout::eGeneral, + vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eTransferRead); +} + +void Image::CopyMip(const Image& image, u32 mip) { + scheduler->EndRendering(); + Transit(vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits::eTransferWrite); + + auto cmdbuf = scheduler->CommandBuffer(); + + const auto mip_w = std::max(info.size.width >> mip, 1u); + const auto mip_h = std::max(info.size.height >> mip, 1u); + const auto mip_d = std::max(info.size.depth >> mip, 1u); + + ASSERT(mip_w == image.info.size.width); + ASSERT(mip_h == image.info.size.height); + + const vk::ImageCopy image_copy{ + .srcSubresource{ + .aspectMask = image.aspect_mask, + .mipLevel = 0, + .baseArrayLayer = 0, + .layerCount = image.info.resources.layers, + }, + .dstSubresource{ + .aspectMask = image.aspect_mask, + .mipLevel = mip, + .baseArrayLayer = 0, + .layerCount = info.resources.layers, + }, + .extent = {mip_w, mip_h, mip_d}, + }; + cmdbuf.copyImage(image.image, image.layout, this->image, this->layout, image_copy); + + Transit(vk::ImageLayout::eGeneral, + vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eTransferRead); +} + Image::~Image() = default; } // namespace VideoCore diff --git a/src/video_core/texture_cache/image.h b/src/video_core/texture_cache/image.h index 3df8ddb70..f932b25a0 100644 --- a/src/video_core/texture_cache/image.h +++ b/src/video_core/texture_cache/image.h @@ -32,6 +32,7 @@ enum ImageFlagBits : u32 { Registered = 1 << 6, ///< True when the image is registered Picked = 1 << 7, ///< Temporary flag to mark the image as picked MetaRegistered = 1 << 8, ///< True when metadata for this surface is known and registered + Deleted = 1 << 9, ///< Indicates that images was marked for deletion once frame is done }; DECLARE_ENUM_FLAG_OPERATORS(ImageFlagBits) @@ -95,6 +96,9 @@ struct Image { vk::CommandBuffer cmdbuf = {}); void Upload(vk::Buffer buffer, u64 offset); + void CopyImage(const Image& image); + void CopyMip(const Image& image, u32 mip); + const Vulkan::Instance* instance; Vulkan::Scheduler* scheduler; ImageInfo info; @@ -112,6 +116,7 @@ struct Image { vk::Flags access_mask = vk::AccessFlagBits::eNone; vk::ImageLayout layout = vk::ImageLayout::eUndefined; boost::container::small_vector mip_hashes; + u64 tick_accessed_last{0}; }; } // namespace VideoCore diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp index 4ac4aee8f..66fde5c83 100644 --- a/src/video_core/texture_cache/image_info.cpp +++ b/src/video_core/texture_cache/image_info.cpp @@ -174,6 +174,7 @@ ImageInfo::ImageInfo(const AmdGpu::Liverpool::ColorBuffer& buffer, const auto color_slice_sz = buffer.GetColorSliceSize(); guest_size_bytes = color_slice_sz * buffer.NumSlices(); mips_layout.emplace_back(color_slice_sz, pitch, 0); + tiling_idx = static_cast(buffer.attrib.tile_mode_index.Value()); } ImageInfo::ImageInfo(const AmdGpu::Liverpool::DepthBuffer& buffer, u32 num_slices, @@ -186,7 +187,7 @@ ImageInfo::ImageInfo(const AmdGpu::Liverpool::DepthBuffer& buffer, u32 num_slice size.width = hint.Valid() ? hint.width : buffer.Pitch(); size.height = hint.Valid() ? hint.height : buffer.Height(); size.depth = 1; - pitch = size.width; + pitch = buffer.Pitch(); resources.layers = num_slices; meta_info.htile_addr = buffer.z_info.tile_surface_en ? htile_address : 0; usage.depth_target = true; @@ -199,9 +200,19 @@ ImageInfo::ImageInfo(const AmdGpu::Liverpool::DepthBuffer& buffer, u32 num_slice mips_layout.emplace_back(depth_slice_sz, pitch, 0); } -ImageInfo::ImageInfo(const AmdGpu::Image& image) noexcept { +ImageInfo::ImageInfo(const AmdGpu::Image& image, bool force_depth /*= false*/) noexcept { tiling_mode = image.GetTilingMode(); pixel_format = LiverpoolToVK::SurfaceFormat(image.GetDataFmt(), image.GetNumberFmt()); + // Override format if image is forced to be a depth target + if (force_depth || tiling_mode == AmdGpu::TilingMode::Depth_MacroTiled) { + if (pixel_format == vk::Format::eR32Sfloat) { + pixel_format = vk::Format::eD32SfloatS8Uint; + } else if (pixel_format == vk::Format::eR16Unorm) { + pixel_format = vk::Format::eD16UnormS8Uint; + } else { + UNREACHABLE(); + } + } type = ConvertImageType(image.GetType()); props.is_tiled = image.IsTiled(); props.is_cube = image.GetType() == AmdGpu::ImageType::Cube; @@ -287,4 +298,74 @@ void ImageInfo::UpdateSize() { guest_size_bytes *= resources.layers; } +bool ImageInfo::IsMipOf(const ImageInfo& info) const { + if (!IsCompatible(info)) { + return false; + } + + // Currently we expect only on level to be copied. + if (resources.levels != 1) { + return false; + } + + const int mip = info.resources.levels - resources.levels; + if (mip < 1) { + return false; + } + + const auto mip_w = std::max(info.size.width >> mip, 1u); + const auto mip_h = std::max(info.size.height >> mip, 1u); + if ((size.width != mip_w) || (size.height != mip_h)) { + return false; + } + + const auto mip_d = std::max(info.size.depth >> mip, 1u); + if (info.type == vk::ImageType::e3D && type == vk::ImageType::e2D) { + // In case of 2D array to 3D copy, make sure we have proper number of layers. + if (resources.layers != mip_d) { + return false; + } + } else { + if (type != info.type) { + return false; + } + } + + // Check if the mip has correct size. + if (info.mips_layout.size() <= mip || info.mips_layout[mip].size != guest_size_bytes) { + return false; + } + + return true; +} + +bool ImageInfo::IsSliceOf(const ImageInfo& info) const { + if (!IsCompatible(info)) { + return false; + } + + // Array slices should be of the same type. + if (type != info.type) { + return false; + } + + // 2D dimensions of both images should be the same. + if ((size.width != info.size.width) || (size.height != info.size.height)) { + return false; + } + + // Check for size alignment. + const bool slice_size = info.guest_size_bytes / info.resources.layers; + if (guest_size_bytes % slice_size != 0) { + return false; + } + + // Ensure that address is aligned too. + if (((info.guest_address - guest_address) % guest_size_bytes) != 0) { + return false; + } + + return true; +} + } // namespace VideoCore diff --git a/src/video_core/texture_cache/image_info.h b/src/video_core/texture_cache/image_info.h index ddad318d9..ba8985b8f 100644 --- a/src/video_core/texture_cache/image_info.h +++ b/src/video_core/texture_cache/image_info.h @@ -3,7 +3,6 @@ #pragma once -#include "common/enum.h" #include "common/types.h" #include "core/libraries/videoout/buffer.h" #include "video_core/amdgpu/liverpool.h" @@ -20,7 +19,7 @@ struct ImageInfo { const AmdGpu::Liverpool::CbDbExtent& hint = {}) noexcept; ImageInfo(const AmdGpu::Liverpool::DepthBuffer& buffer, u32 num_slices, VAddr htile_address, const AmdGpu::Liverpool::CbDbExtent& hint = {}) noexcept; - ImageInfo(const AmdGpu::Image& image) noexcept; + ImageInfo(const AmdGpu::Image& image, bool force_depth = false) noexcept; bool IsTiled() const { return tiling_mode != AmdGpu::TilingMode::Display_Linear; @@ -29,6 +28,15 @@ struct ImageInfo { bool IsPacked() const; bool IsDepthStencil() const; + bool IsMipOf(const ImageInfo& info) const; + bool IsSliceOf(const ImageInfo& info) const; + + /// Verifies if images are compatible for subresource merging. + bool IsCompatible(const ImageInfo& info) const { + return (pixel_format == info.pixel_format && tiling_idx == info.tiling_idx && + num_samples == info.num_samples && num_bits == info.num_bits); + } + void UpdateSize(); struct { diff --git a/src/video_core/texture_cache/image_view.cpp b/src/video_core/texture_cache/image_view.cpp index bcdc11ad9..e554bad7e 100644 --- a/src/video_core/texture_cache/image_view.cpp +++ b/src/video_core/texture_cache/image_view.cpp @@ -123,7 +123,8 @@ ImageView::ImageView(const Vulkan::Instance& instance, const ImageViewInfo& info // When sampling D32 texture from shader, the T# specifies R32 Float format so adjust it. vk::Format format = info.format; vk::ImageAspectFlags aspect = image.aspect_mask; - if (image.aspect_mask & vk::ImageAspectFlagBits::eDepth && format == vk::Format::eR32Sfloat) { + if (image.aspect_mask & vk::ImageAspectFlagBits::eDepth && + (format == vk::Format::eR32Sfloat || format == vk::Format::eD32Sfloat)) { format = image.info.pixel_format; aspect = vk::ImageAspectFlagBits::eDepth; } diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp index 3354a8ecb..0d0c81f5d 100644 --- a/src/video_core/texture_cache/texture_cache.cpp +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -1,18 +1,21 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include #include "common/assert.h" #include "video_core/buffer_cache/buffer_cache.h" #include "video_core/page_manager.h" #include "video_core/renderer_vulkan/vk_instance.h" #include "video_core/renderer_vulkan/vk_scheduler.h" +#include "video_core/texture_cache/host_compatibility.h" #include "video_core/texture_cache/texture_cache.h" #include "video_core/texture_cache/tile_manager.h" namespace VideoCore { static constexpr u64 PageShift = 12; +static constexpr u64 NumFramesBeforeRemoval = 32; TextureCache::TextureCache(const Vulkan::Instance& instance_, Vulkan::Scheduler& scheduler_, BufferCache& buffer_cache_, PageManager& tracker_) @@ -35,15 +38,16 @@ TextureCache::TextureCache(const Vulkan::Instance& instance_, Vulkan::Scheduler& TextureCache::~TextureCache() = default; void TextureCache::InvalidateMemory(VAddr address, size_t size) { - std::unique_lock lock{mutex}; + std::scoped_lock lock{mutex}; ForEachImageInRegion(address, size, [&](ImageId image_id, Image& image) { - if (!image.Overlaps(address, size)) { - return; + const size_t image_dist = + image.cpu_addr > address ? image.cpu_addr - address : address - image.cpu_addr; + if (image_dist < MaxInvalidateDist) { + // Ensure image is reuploaded when accessed again. + image.flags |= ImageFlagBits::CpuModified; } - // Ensure image is reuploaded when accessed again. - image.flags |= ImageFlagBits::CpuModified; // Untrack image, so the range is unprotected and the guest can write freely. - UntrackImage(image, image_id); + UntrackImage(image_id); }); } @@ -53,46 +57,170 @@ void TextureCache::UnmapMemory(VAddr cpu_addr, size_t size) { boost::container::small_vector deleted_images; ForEachImageInRegion(cpu_addr, size, [&](ImageId id, Image&) { deleted_images.push_back(id); }); for (const ImageId id : deleted_images) { - Image& image = slot_images[id]; - if (True(image.flags & ImageFlagBits::Tracked)) { - UntrackImage(image, id); - } // TODO: Download image data back to host. - UnregisterImage(id); - DeleteImage(id); + FreeImage(id); } } -ImageId TextureCache::FindImage(const ImageInfo& info) { +ImageId TextureCache::ResolveDepthOverlap(const ImageInfo& requested_info, ImageId cache_image_id) { + const auto& cache_info = slot_images[cache_image_id].info; + + const bool was_bound_as_texture = + !cache_info.usage.depth_target && (cache_info.usage.texture || cache_info.usage.storage); + if (requested_info.usage.depth_target && was_bound_as_texture) { + auto new_image_id = slot_images.insert(instance, scheduler, requested_info); + RegisterImage(new_image_id); + + // auto& new_image = slot_images[new_image_id]; + // TODO: need to run a helper for depth copy here + + FreeImage(cache_image_id); + return new_image_id; + } + + const bool should_bind_as_texture = + !requested_info.usage.depth_target && + (requested_info.usage.texture || requested_info.usage.storage); + if (cache_info.usage.depth_target && should_bind_as_texture) { + return cache_image_id; + } + + return {}; +} + +ImageId TextureCache::ResolveOverlap(const ImageInfo& image_info, ImageId cache_image_id, + ImageId merged_image_id) { + auto& tex_cache_image = slot_images[cache_image_id]; + + if (image_info.guest_address == tex_cache_image.info.guest_address) { // Equal address + if (image_info.size != tex_cache_image.info.size) { + // Very likely this kind of overlap is caused by allocation from a pool. We can assume + // it is safe to delete the image if it wasn't accessed in some amount of frames. + if (scheduler.CurrentTick() - tex_cache_image.tick_accessed_last > + NumFramesBeforeRemoval) { + + FreeImage(cache_image_id); + } + return merged_image_id; + } + + if (auto depth_image_id = ResolveDepthOverlap(image_info, cache_image_id)) { + return depth_image_id; + } + + if (image_info.pixel_format != tex_cache_image.info.pixel_format || + image_info.size != tex_cache_image.info.size || + image_info.guest_size_bytes <= tex_cache_image.info.guest_size_bytes) { + return merged_image_id ? merged_image_id : cache_image_id; + } + + ImageId new_image_id{}; + if (image_info.type == tex_cache_image.info.type) { + new_image_id = ExpandImage(image_info, cache_image_id); + } else { + UNREACHABLE(); + } + return new_image_id; + } + + // Right overlap, the image requested is a possible subresource of the image from cache. + if (image_info.guest_address > tex_cache_image.info.guest_address) { + // Should be handled by view. No additional actions needed. + } else { + // Left overlap, the image from cache is a possible subresource of the image requested + if (!merged_image_id) { + // We need to have a larger, already allocated image to copy this one into + return {}; + } + + if (tex_cache_image.info.IsMipOf(image_info)) { + tex_cache_image.Transit(vk::ImageLayout::eTransferSrcOptimal, + vk::AccessFlagBits::eTransferRead); + + const auto num_mips_to_copy = tex_cache_image.info.resources.levels; + ASSERT(num_mips_to_copy == 1); + + auto& merged_image = slot_images[merged_image_id]; + merged_image.CopyMip(tex_cache_image, image_info.resources.levels - 1); + + FreeImage(cache_image_id); + } + } + + return merged_image_id; +} + +ImageId TextureCache::ExpandImage(const ImageInfo& info, ImageId image_id) { + const auto new_image_id = slot_images.insert(instance, scheduler, info); + RegisterImage(new_image_id); + + auto& src_image = slot_images[image_id]; + auto& new_image = slot_images[new_image_id]; + + src_image.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits::eTransferRead); + new_image.CopyImage(src_image); + + FreeImage(image_id); + + TrackImage(new_image_id); + new_image.flags &= ~ImageFlagBits::CpuModified; + return new_image_id; +} + +ImageId TextureCache::FindImage(const ImageInfo& info, FindFlags flags) { if (info.guest_address == 0) [[unlikely]] { return NULL_IMAGE_VIEW_ID; } - std::unique_lock lock{mutex}; - boost::container::small_vector image_ids; - ForEachImageInRegion( - info.guest_address, info.guest_size_bytes, [&](ImageId image_id, Image& image) { - // Address and width must match. - if (image.cpu_addr != info.guest_address || image.info.size.width != info.size.width) { - return; - } - if (info.IsDepthStencil() != image.info.IsDepthStencil() && - info.pixel_format != vk::Format::eR32Sfloat) { - return; - } - image_ids.push_back(image_id); - }); - - // ASSERT_MSG(image_ids.size() <= 1, "Overlapping images not allowed!"); + std::scoped_lock lock{mutex}; + boost::container::small_vector image_ids; + ForEachImageInRegion(info.guest_address, info.guest_size_bytes, + [&](ImageId image_id, Image& image) { image_ids.push_back(image_id); }); ImageId image_id{}; - if (image_ids.empty()) { + + // Check for a perfect match first + for (const auto& cache_id : image_ids) { + auto& cache_image = slot_images[cache_id]; + if (cache_image.info.guest_address != info.guest_address) { + continue; + } + if (False(flags & FindFlags::RelaxSize) && + cache_image.info.guest_size_bytes != info.guest_size_bytes) { + continue; + } + if (False(flags & FindFlags::RelaxDim) && cache_image.info.size != info.size) { + continue; + } + if (False(flags & FindFlags::RelaxFmt) && + !IsVulkanFormatCompatible(info.pixel_format, cache_image.info.pixel_format)) { + continue; + } + ASSERT(cache_image.info.type == info.type); + image_id = cache_id; + } + + // Try to resolve overlaps (if any) + if (!image_id) { + for (const auto& cache_id : image_ids) { + const auto& merged_info = image_id ? slot_images[image_id].info : info; + image_id = ResolveOverlap(merged_info, cache_id, image_id); + } + } + + if (True(flags & FindFlags::NoCreate) && !image_id) { + return {}; + } + + // Create and register a new image + if (!image_id) { image_id = slot_images.insert(instance, scheduler, info); RegisterImage(image_id); - } else { - image_id = image_ids[image_ids.size() > 1 ? 1 : 0]; } + Image& image = slot_images[image_id]; + image.tick_accessed_last = scheduler.CurrentTick(); + return image_id; } @@ -119,8 +247,11 @@ ImageView& TextureCache::RegisterImageView(ImageId image_id, const ImageViewInfo ImageView& TextureCache::FindTexture(const ImageInfo& info, const ImageViewInfo& view_info) { const ImageId image_id = FindImage(info); - UpdateImage(image_id); Image& image = slot_images[image_id]; + if (view_info.is_storage) { + image.flags |= ImageFlagBits::GpuModified; + } + UpdateImage(image_id); auto& usage = image.info.usage; if (view_info.is_storage) { @@ -135,31 +266,7 @@ ImageView& TextureCache::FindTexture(const ImageInfo& info, const ImageViewInfo& usage.texture = true; } - // These changes are temporary and should be removed once texture cache will handle subresources - // merging - auto view_info_tmp = view_info; - if (view_info_tmp.range.base.level > image.info.resources.levels - 1 || - view_info_tmp.range.base.layer > image.info.resources.layers - 1 || - view_info_tmp.range.extent.levels > image.info.resources.levels || - view_info_tmp.range.extent.layers > image.info.resources.layers) { - - LOG_DEBUG(Render_Vulkan, - "Subresource range ({}~{},{}~{}) exceeds base image extents ({},{})", - view_info_tmp.range.base.level, view_info_tmp.range.extent.levels, - view_info_tmp.range.base.layer, view_info_tmp.range.extent.layers, - image.info.resources.levels, image.info.resources.layers); - - view_info_tmp.range.base.level = - std::min(view_info_tmp.range.base.level, image.info.resources.levels - 1); - view_info_tmp.range.base.layer = - std::min(view_info_tmp.range.base.layer, image.info.resources.layers - 1); - view_info_tmp.range.extent.levels = - std::min(view_info_tmp.range.extent.levels, image.info.resources.levels); - view_info_tmp.range.extent.layers = - std::min(view_info_tmp.range.extent.layers, image.info.resources.layers); - } - - return RegisterImageView(image_id, view_info_tmp); + return RegisterImageView(image_id, view_info); } ImageView& TextureCache::FindRenderTarget(const ImageInfo& image_info, @@ -204,10 +311,18 @@ ImageView& TextureCache::FindDepthTarget(const ImageInfo& image_info, Image& image = slot_images[image_id]; image.flags |= ImageFlagBits::GpuModified; image.flags &= ~ImageFlagBits::CpuModified; - image.aspect_mask = vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil; + image.aspect_mask = vk::ImageAspectFlagBits::eDepth; - const auto new_layout = view_info.is_storage ? vk::ImageLayout::eDepthStencilAttachmentOptimal - : vk::ImageLayout::eDepthStencilReadOnlyOptimal; + const bool has_stencil = image_info.usage.stencil; + if (has_stencil) { + image.aspect_mask |= vk::ImageAspectFlagBits::eStencil; + } + + const auto new_layout = view_info.is_storage + ? has_stencil ? vk::ImageLayout::eDepthStencilAttachmentOptimal + : vk::ImageLayout::eDepthAttachmentOptimal + : has_stencil ? vk::ImageLayout::eDepthStencilReadOnlyOptimal + : vk::ImageLayout::eDepthReadOnlyOptimal; image.Transit(new_layout, vk::AccessFlagBits::eDepthStencilAttachmentWrite | vk::AccessFlagBits::eDepthStencilAttachmentRead); @@ -224,11 +339,16 @@ ImageView& TextureCache::FindDepthTarget(const ImageInfo& image_info, // Update tracked image usage image.info.usage.depth_target = true; + image.info.usage.stencil = has_stencil; return RegisterImageView(image_id, view_info); } void TextureCache::RefreshImage(Image& image, Vulkan::Scheduler* custom_scheduler /*= nullptr*/) { + if (False(image.flags & ImageFlagBits::CpuModified)) { + return; + } + // Mark image as validated. image.flags &= ~ImageFlagBits::CpuModified; @@ -260,7 +380,7 @@ void TextureCache::RefreshImage(Image& image, Vulkan::Scheduler* custom_schedule .bufferRowLength = static_cast(mip_pitch), .bufferImageHeight = static_cast(mip_height), .imageSubresource{ - .aspectMask = vk::ImageAspectFlagBits::eColor, + .aspectMask = image.aspect_mask & ~vk::ImageAspectFlagBits::eStencil, .mipLevel = m, .baseArrayLayer = 0, .layerCount = num_layers, @@ -282,16 +402,20 @@ void TextureCache::RefreshImage(Image& image, Vulkan::Scheduler* custom_schedule const VAddr image_addr = image.info.guest_address; const size_t image_size = image.info.guest_size_bytes; - vk::Buffer buffer{}; - u32 offset{}; - if (auto upload_buffer = tile_manager.TryDetile(image); upload_buffer) { - buffer = *upload_buffer; - } else { - const auto [vk_buffer, buf_offset] = buffer_cache.ObtainTempBuffer(image_addr, image_size); - buffer = vk_buffer->Handle(); - offset = buf_offset; + const auto [vk_buffer, buf_offset] = buffer_cache.ObtainTempBuffer(image_addr, image_size); + // The obtained buffer may be written by a shader so we need to emit a barrier to prevent RAW + // hazard + if (auto barrier = vk_buffer->GetBarrier(vk::AccessFlagBits2::eTransferRead, + vk::PipelineStageFlagBits2::eTransfer)) { + const auto dependencies = vk::DependencyInfo{ + .dependencyFlags = vk::DependencyFlagBits::eByRegion, + .bufferMemoryBarrierCount = 1, + .pBufferMemoryBarriers = &barrier.value(), + }; + cmdbuf.pipelineBarrier2(dependencies); } + const auto [buffer, offset] = tile_manager.TryDetile(vk_buffer->Handle(), buf_offset, image); for (auto& copy : image_copy) { copy.bufferOffset += offset; } @@ -335,7 +459,8 @@ void TextureCache::UnregisterImage(ImageId image_id) { }); } -void TextureCache::TrackImage(Image& image, ImageId image_id) { +void TextureCache::TrackImage(ImageId image_id) { + auto& image = slot_images[image_id]; if (True(image.flags & ImageFlagBits::Tracked)) { return; } @@ -343,7 +468,8 @@ void TextureCache::TrackImage(Image& image, ImageId image_id) { tracker.UpdatePagesCachedCount(image.cpu_addr, image.info.guest_size_bytes, 1); } -void TextureCache::UntrackImage(Image& image, ImageId image_id) { +void TextureCache::UntrackImage(ImageId image_id) { + auto& image = slot_images[image_id]; if (False(image.flags & ImageFlagBits::Tracked)) { return; } @@ -356,6 +482,8 @@ void TextureCache::DeleteImage(ImageId image_id) { ASSERT_MSG(False(image.flags & ImageFlagBits::Tracked), "Image was not untracked"); ASSERT_MSG(False(image.flags & ImageFlagBits::Registered), "Image was not unregistered"); + image.flags |= ImageFlagBits::Deleted; + // Remove any registered meta areas. const auto& meta_info = image.info.meta_info; if (meta_info.cmask_addr) { diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 31b1e3939..44bc2b431 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -23,6 +23,16 @@ namespace VideoCore { class BufferCache; class PageManager; +enum class FindFlags { + NoCreate = 1 << 0, ///< Do not create an image if searching for one fails. + RelaxDim = 1 << 1, ///< Do not check the dimentions of image, only address. + RelaxSize = 1 << 2, ///< Do not check that the size matches exactly. + RelaxFmt = 1 << 3, ///< Do not check that format is compatible. +}; +DECLARE_ENUM_FLAG_OPERATORS(FindFlags) + +static constexpr u32 MaxInvalidateDist = 12_MB; + class TextureCache { struct Traits { using Entry = boost::container::small_vector; @@ -44,7 +54,7 @@ public: void UnmapMemory(VAddr cpu_addr, size_t size); /// Retrieves the image handle of the image with the provided attributes. - [[nodiscard]] ImageId FindImage(const ImageInfo& info); + [[nodiscard]] ImageId FindImage(const ImageInfo& info, FindFlags flags = {}); /// Retrieves an image view with the properties of the specified image descriptor. [[nodiscard]] ImageView& FindTexture(const ImageInfo& image_info, @@ -61,13 +71,19 @@ public: /// Updates image contents if it was modified by CPU. void UpdateImage(ImageId image_id, Vulkan::Scheduler* custom_scheduler = nullptr) { Image& image = slot_images[image_id]; - if (False(image.flags & ImageFlagBits::CpuModified)) { - return; - } + TrackImage(image_id); RefreshImage(image, custom_scheduler); - TrackImage(image, image_id); } + [[nodiscard]] ImageId ResolveOverlap(const ImageInfo& info, ImageId cache_img_id, + ImageId merged_image_id); + + /// Resolves depth overlap and either re-creates the image or returns existing one + [[nodiscard]] ImageId ResolveDepthOverlap(const ImageInfo& requested_info, + ImageId cache_img_id); + + [[nodiscard]] ImageId ExpandImage(const ImageInfo& info, ImageId image_id); + /// Reuploads image contents. void RefreshImage(Image& image, Vulkan::Scheduler* custom_scheduler = nullptr); @@ -100,31 +116,12 @@ public: return false; } -private: - ImageView& RegisterImageView(ImageId image_id, const ImageViewInfo& view_info); - - /// Iterate over all page indices in a range - template - static void ForEachPage(PAddr addr, size_t size, Func&& func) { - static constexpr bool RETURNS_BOOL = std::is_same_v, bool>; - const u64 page_end = (addr + size - 1) >> Traits::PageBits; - for (u64 page = addr >> Traits::PageBits; page <= page_end; ++page) { - if constexpr (RETURNS_BOOL) { - if (func(page)) { - break; - } - } else { - func(page); - } - } - } - template void ForEachImageInRegion(VAddr cpu_addr, size_t size, Func&& func) { using FuncReturn = typename std::invoke_result::type; static constexpr bool BOOL_BREAK = std::is_same_v; boost::container::small_vector images; - ForEachPage(cpu_addr, size, [this, &images, func](u64 page) { + ForEachPage(cpu_addr, size, [this, &images, cpu_addr, size, func](u64 page) { const auto it = page_table.find(page); if (it == nullptr) { if constexpr (BOOL_BREAK) { @@ -138,6 +135,9 @@ private: if (image.flags & ImageFlagBits::Picked) { continue; } + if (!image.Overlaps(cpu_addr, size)) { + continue; + } image.flags |= ImageFlagBits::Picked; images.push_back(image_id); if constexpr (BOOL_BREAK) { @@ -157,6 +157,26 @@ private: } } +private: + /// Iterate over all page indices in a range + template + static void ForEachPage(PAddr addr, size_t size, Func&& func) { + static constexpr bool RETURNS_BOOL = std::is_same_v, bool>; + const u64 page_end = (addr + size - 1) >> Traits::PageBits; + for (u64 page = addr >> Traits::PageBits; page <= page_end; ++page) { + if constexpr (RETURNS_BOOL) { + if (func(page)) { + break; + } + } else { + func(page); + } + } + } + + /// Registers an image view for provided image + ImageView& RegisterImageView(ImageId image_id, const ImageViewInfo& view_info); + /// Create an image from the given parameters [[nodiscard]] ImageId InsertImage(const ImageInfo& info, VAddr cpu_addr); @@ -167,14 +187,20 @@ private: void UnregisterImage(ImageId image); /// Track CPU reads and writes for image - void TrackImage(Image& image, ImageId image_id); + void TrackImage(ImageId image_id); /// Stop tracking CPU reads and writes for image - void UntrackImage(Image& image, ImageId image_id); + void UntrackImage(ImageId image_id); /// Removes the image and any views/surface metas that reference it. void DeleteImage(ImageId image_id); + void FreeImage(ImageId image_id) { + UntrackImage(image_id); + UnregisterImage(image_id); + DeleteImage(image_id); + } + private: const Vulkan::Instance& instance; Vulkan::Scheduler& scheduler; diff --git a/src/video_core/texture_cache/tile_manager.cpp b/src/video_core/texture_cache/tile_manager.cpp index 5f3ed0f89..7e06291e7 100644 --- a/src/video_core/texture_cache/tile_manager.cpp +++ b/src/video_core/texture_cache/tile_manager.cpp @@ -254,11 +254,8 @@ struct DetilerParams { u32 sizes[14]; }; -static constexpr size_t StreamBufferSize = 1_GB; - TileManager::TileManager(const Vulkan::Instance& instance, Vulkan::Scheduler& scheduler) - : instance{instance}, scheduler{scheduler}, - stream_buffer{instance, scheduler, MemoryUsage::Upload, StreamBufferSize} { + : instance{instance}, scheduler{scheduler} { static const std::array detiler_shaders{ HostShaders::DETILE_M8X1_COMP, HostShaders::DETILE_M8X2_COMP, HostShaders::DETILE_M32X1_COMP, HostShaders::DETILE_M32X2_COMP, @@ -380,35 +377,23 @@ void TileManager::FreeBuffer(ScratchBuffer buffer) { vmaDestroyBuffer(instance.GetAllocator(), buffer.first, buffer.second); } -std::optional TileManager::TryDetile(Image& image) { +std::pair TileManager::TryDetile(vk::Buffer in_buffer, u32 in_offset, + Image& image) { if (!image.info.props.is_tiled) { - return std::nullopt; + return {in_buffer, in_offset}; } const auto* detiler = GetDetiler(image); if (!detiler) { - if (image.info.tiling_mode != AmdGpu::TilingMode::Texture_MacroTiled) { + if (image.info.tiling_mode != AmdGpu::TilingMode::Texture_MacroTiled && + image.info.tiling_mode != AmdGpu::TilingMode::Display_MacroTiled) { LOG_ERROR(Render_Vulkan, "Unsupported tiled image: {} ({})", vk::to_string(image.info.pixel_format), NameOf(image.info.tiling_mode)); } - return std::nullopt; + return {in_buffer, in_offset}; } - // Prepare input buffer const u32 image_size = image.info.guest_size_bytes; - const auto [in_buffer, in_offset] = [&] -> std::pair { - // Use stream buffer for smaller textures. - if (image_size <= stream_buffer.GetFreeSize()) { - u32 offset = stream_buffer.Copy(image.info.guest_address, image_size); - return {stream_buffer.Handle(), offset}; - } - // Request temporary host buffer for larger sizes. - auto in_buffer = AllocBuffer(image_size); - const auto addr = reinterpret_cast(image.info.guest_address); - Upload(in_buffer, addr, image_size); - scheduler.DeferOperation([=, this]() { FreeBuffer(in_buffer); }); - return {in_buffer.first, 0}; - }(); // Prepare output buffer auto out_buffer = AllocBuffer(image_size, true); @@ -479,7 +464,7 @@ std::optional TileManager::TryDetile(Image& image) { vk::PipelineStageFlagBits::eTransfer, vk::DependencyFlagBits::eByRegion, {}, post_barrier, {}); - return {out_buffer.first}; + return {out_buffer.first, 0}; } } // namespace VideoCore diff --git a/src/video_core/texture_cache/tile_manager.h b/src/video_core/texture_cache/tile_manager.h index 00765b1f8..d0e5eb0f3 100644 --- a/src/video_core/texture_cache/tile_manager.h +++ b/src/video_core/texture_cache/tile_manager.h @@ -39,7 +39,7 @@ public: TileManager(const Vulkan::Instance& instance, Vulkan::Scheduler& scheduler); ~TileManager(); - std::optional TryDetile(Image& image); + std::pair TryDetile(vk::Buffer in_buffer, u32 in_offset, Image& image); ScratchBuffer AllocBuffer(u32 size, bool is_storage = false); void Upload(ScratchBuffer buffer, const void* data, size_t size); @@ -51,7 +51,6 @@ private: private: const Vulkan::Instance& instance; Vulkan::Scheduler& scheduler; - StreamBuffer stream_buffer; std::array detilers; }; diff --git a/src/video_core/texture_cache/types.h b/src/video_core/texture_cache/types.h index 45ffe2511..bcef19355 100644 --- a/src/video_core/texture_cache/types.h +++ b/src/video_core/texture_cache/types.h @@ -36,6 +36,8 @@ struct Extent3D { u32 width; u32 height; u32 depth; + + auto operator<=>(const Extent3D&) const = default; }; struct SubresourceLayers {