From 4202d9d621fea5fd5686656b296b97d64e3f3582 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Wed, 30 Apr 2025 23:37:37 -0700 Subject: [PATCH 1/4] fix: Add missing ctime includes. --- src/core/devices/random_device.cpp | 1 + src/core/devices/srandom_device.cpp | 1 + src/core/devices/urandom_device.cpp | 1 + src/core/devtools/widget/frame_dump.cpp | 1 + 4 files changed, 4 insertions(+) diff --git a/src/core/devices/random_device.cpp b/src/core/devices/random_device.cpp index 50934e3b8..b2754fe58 100644 --- a/src/core/devices/random_device.cpp +++ b/src/core/devices/random_device.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include "common/logging/log.h" #include "random_device.h" diff --git a/src/core/devices/srandom_device.cpp b/src/core/devices/srandom_device.cpp index ab78ddbe2..5e51b1c39 100644 --- a/src/core/devices/srandom_device.cpp +++ b/src/core/devices/srandom_device.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include "common/logging/log.h" #include "srandom_device.h" diff --git a/src/core/devices/urandom_device.cpp b/src/core/devices/urandom_device.cpp index c001aab83..7318a6ff7 100644 --- a/src/core/devices/urandom_device.cpp +++ b/src/core/devices/urandom_device.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include "common/logging/log.h" #include "urandom_device.h" diff --git a/src/core/devtools/widget/frame_dump.cpp b/src/core/devtools/widget/frame_dump.cpp index 646ccb6d6..2445bdcb5 100644 --- a/src/core/devtools/widget/frame_dump.cpp +++ b/src/core/devtools/widget/frame_dump.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include #include #include From b0e4e87ff3f7206df63988eab7593163253d987b Mon Sep 17 00:00:00 2001 From: Mahmoud Adel <94652220+AboMedoz@users.noreply.github.com> Date: Thu, 1 May 2025 12:12:15 +0300 Subject: [PATCH 2/4] Implement SnormNz conversion (#2841) * + * + * Unpack Snorm 2x16 * + * SintToSnormNz * all is broken ig.... * review changes * my stupid ass messed all while trying to resolve the conflicts.. * + * + * fix rebase * clang-format fix (1) * clang-format fix (2) --------- Co-authored-by: squidbus <175574877+squidbus@users.noreply.github.com> --- CMakeLists.txt | 0 .../ir/passes/lower_buffer_format_to_raw.cpp | 3 ++- src/shader_recompiler/ir/reinterpret.h | 26 +++++++++++++++++++ src/video_core/amdgpu/liverpool.h | 2 +- src/video_core/amdgpu/resource.h | 4 +-- src/video_core/amdgpu/types.h | 21 +++++++++++++-- 6 files changed, 50 insertions(+), 6 deletions(-) mode change 100755 => 100644 CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt old mode 100755 new mode 100644 diff --git a/src/shader_recompiler/ir/passes/lower_buffer_format_to_raw.cpp b/src/shader_recompiler/ir/passes/lower_buffer_format_to_raw.cpp index 658a495bc..65be02541 100644 --- a/src/shader_recompiler/ir/passes/lower_buffer_format_to_raw.cpp +++ b/src/shader_recompiler/ir/passes/lower_buffer_format_to_raw.cpp @@ -206,7 +206,8 @@ static void LowerBufferFormatInst(IR::Block& block, IR::Inst& inst, Info& info) .swizzle = is_inst_typed ? AmdGpu::RemapSwizzle(flags.inst_data_fmt.Value(), AmdGpu::IdentityMapping) : buffer.DstSelect(), - .num_conversion = is_inst_typed ? AmdGpu::MapNumberConversion(flags.inst_num_fmt.Value()) + .num_conversion = is_inst_typed ? AmdGpu::MapNumberConversion(flags.inst_num_fmt.Value(), + flags.inst_data_fmt.Value()) : buffer.GetNumberConversion(), .num_components = AmdGpu::NumComponents(data_format), }; diff --git a/src/shader_recompiler/ir/reinterpret.h b/src/shader_recompiler/ir/reinterpret.h index b65b19928..99819cbb9 100644 --- a/src/shader_recompiler/ir/reinterpret.h +++ b/src/shader_recompiler/ir/reinterpret.h @@ -34,6 +34,18 @@ inline F32 ApplyReadNumberConversion(IREmitter& ir, const F32& value, case AmdGpu::NumberConversion::UnormToUbnorm: // Convert 0...1 to -1...1 return ir.FPSub(ir.FPMul(value, ir.Imm32(2.f)), ir.Imm32(1.f)); + case AmdGpu::NumberConversion::Sint8ToSnormNz: { + const IR::U32 additon = ir.IAdd(ir.IMul(ir.BitCast(value), ir.Imm32(2)), ir.Imm32(1)); + const IR::F32 left = ir.ConvertSToF(32, 32, additon); + const IR::F32 max = ir.Imm32(float(std::numeric_limits::max())); + return ir.FPDiv(left, max); + } + case AmdGpu::NumberConversion::Sint16ToSnormNz: { + const IR::U32 additon = ir.IAdd(ir.IMul(ir.BitCast(value), ir.Imm32(2)), ir.Imm32(1)); + const IR::F32 left = ir.ConvertSToF(32, 32, additon); + const IR::F32 max = ir.Imm32(float(std::numeric_limits::max())); + return ir.FPDiv(left, max); + } default: UNREACHABLE(); } @@ -66,6 +78,20 @@ inline F32 ApplyWriteNumberConversion(IREmitter& ir, const F32& value, case AmdGpu::NumberConversion::UnormToUbnorm: // Convert -1...1 to 0...1 return ir.FPDiv(ir.FPAdd(value, ir.Imm32(1.f)), ir.Imm32(2.f)); + case AmdGpu::NumberConversion::Sint8ToSnormNz: { + const IR::F32 max = ir.Imm32(float(std::numeric_limits::max())); + const IR::F32 mul = ir.FPMul(ir.FPClamp(value, ir.Imm32(-1.f), ir.Imm32(1.f)), max); + const IR::F32 left = ir.FPSub(mul, ir.Imm32(1.f)); + const IR::U32 raw = ir.ConvertFToS(32, ir.FPDiv(left, ir.Imm32(2.f))); + return ir.BitCast(raw); + } + case AmdGpu::NumberConversion::Sint16ToSnormNz: { + const IR::F32 max = ir.Imm32(float(std::numeric_limits::max())); + const IR::F32 mul = ir.FPMul(ir.FPClamp(value, ir.Imm32(-1.f), ir.Imm32(1.f)), max); + const IR::F32 left = ir.FPSub(mul, ir.Imm32(1.f)); + const IR::U32 raw = ir.ConvertFToS(32, ir.FPDiv(left, ir.Imm32(2.f))); + return ir.BitCast(raw); + } default: UNREACHABLE(); } diff --git a/src/video_core/amdgpu/liverpool.h b/src/video_core/amdgpu/liverpool.h index 5928a6313..c4bebd05f 100644 --- a/src/video_core/amdgpu/liverpool.h +++ b/src/video_core/amdgpu/liverpool.h @@ -928,7 +928,7 @@ struct Liverpool { } [[nodiscard]] NumberConversion GetNumberConversion() const { - return MapNumberConversion(GetFixedNumberFormat()); + return MapNumberConversion(GetFixedNumberFormat(), info.format); } [[nodiscard]] CompMapping Swizzle() const { diff --git a/src/video_core/amdgpu/resource.h b/src/video_core/amdgpu/resource.h index 64a85c812..c387c7bf2 100644 --- a/src/video_core/amdgpu/resource.h +++ b/src/video_core/amdgpu/resource.h @@ -68,7 +68,7 @@ struct Buffer { } NumberConversion GetNumberConversion() const noexcept { - return MapNumberConversion(NumberFormat(num_format)); + return MapNumberConversion(NumberFormat(num_format), DataFormat(data_format)); } u32 GetStride() const noexcept { @@ -292,7 +292,7 @@ struct Image { } NumberConversion GetNumberConversion() const noexcept { - return MapNumberConversion(NumberFormat(num_format)); + return MapNumberConversion(NumberFormat(num_format), DataFormat(data_format)); } TilingMode GetTilingMode() const { diff --git a/src/video_core/amdgpu/types.h b/src/video_core/amdgpu/types.h index d1cf19076..ab0df689e 100644 --- a/src/video_core/amdgpu/types.h +++ b/src/video_core/amdgpu/types.h @@ -197,6 +197,8 @@ enum class NumberConversion : u32 { UintToUscaled = 1, SintToSscaled = 2, UnormToUbnorm = 3, + Sint8ToSnormNz = 5, + Sint16ToSnormNz = 6, }; struct CompMapping { @@ -287,6 +289,7 @@ inline NumberFormat RemapNumberFormat(const NumberFormat format, const DataForma case NumberFormat::Uscaled: return NumberFormat::Uint; case NumberFormat::Sscaled: + case NumberFormat::SnormNz: return NumberFormat::Sint; case NumberFormat::Ubnorm: return NumberFormat::Unorm; @@ -336,14 +339,28 @@ inline CompMapping RemapSwizzle(const DataFormat format, const CompMapping swizz } } -inline NumberConversion MapNumberConversion(const NumberFormat format) { - switch (format) { +inline NumberConversion MapNumberConversion(const NumberFormat num_fmt, const DataFormat data_fmt) { + switch (num_fmt) { case NumberFormat::Uscaled: return NumberConversion::UintToUscaled; case NumberFormat::Sscaled: return NumberConversion::SintToSscaled; case NumberFormat::Ubnorm: return NumberConversion::UnormToUbnorm; + case NumberFormat::SnormNz: { + switch (data_fmt) { + case DataFormat::Format8: + case DataFormat::Format8_8: + case DataFormat::Format8_8_8_8: + return NumberConversion::Sint8ToSnormNz; + case DataFormat::Format16: + case DataFormat::Format16_16: + case DataFormat::Format16_16_16_16: + return NumberConversion::Sint16ToSnormNz; + default: + UNREACHABLE_MSG("data_fmt = {}", u32(data_fmt)); + } + } default: return NumberConversion::None; } From 6c39bf229c006b76ad43f5c79e527ff383f4dc80 Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Thu, 1 May 2025 06:47:43 -0500 Subject: [PATCH 3/4] libkernel: Various filesystem fixes (#2868) * Proper handling of whence 3 & 4 * Accurate directory handling in open Directories can be opened, and can be created in open, these changes should handle that more accurately. * Mount /app0 as read only On real hardware, it's read only. * Proper directory flag handling. Even when directory is specified, it will still succeed to open non-directories. * Check for read only directories * Earlier ro check in posix_rmdir Hardware tests suggest these checks are in a different order * Clear temp folder on boot My tests rely on this, and some games do too. Two birds with one stone * Clang * Add missing DeleteHandle calls Whoops * Final flags adjustment in sceKernelOpen All my current tests are now hardware accurate. * Fix truncates Host ftruncate consistently fails on EINVAL, I'll need to test if this issue affected Windows too. * Windows hacks Windows is more limiting about how folders are opened and things like that. For now, pretend these calls didn't error. Also fixes compilation for Windows * Final touch-ups After expanding my test suite further, I found a couple more edge cases that needed addressing. Bloodborne audio is still broken, I'll look into that soon. * Remove hacky read-only behavior in posix_stat Bloodborne apparently uses the mode parameter here when querying it's audio files, and the mode we returned led to it disabling audio entirely. * Clang * Cleaner code * Combine fsync and sync flags According to FreeBSD docs, the "sync" flag is synonymous with the fsync flag, and is only included to meet the POSIX spec. * Log if any currently unhandled flags are encountered. These are rare and probably not too important, but log a warning when they're seen. * Update file_system.cpp * Update file_system.cpp * Clang * Revert truncate fix Using ftruncate works fine after moving the call to before the proper file opening code. * Truncate before open Open the file as read-write, then try truncating. This fixes read | truncate flag behavior on Windows. * Slightly adjust check for invalid flags Any open call with invalid flags should return EINVAL, regardless of other errors parameters might cause. --- src/common/io_file.cpp | 4 +- src/common/io_file.h | 2 - src/core/libraries/kernel/file_system.cpp | 198 ++++++++++++++-------- src/emulator.cpp | 14 +- 4 files changed, 133 insertions(+), 85 deletions(-) diff --git a/src/common/io_file.cpp b/src/common/io_file.cpp index 3efadc6ea..6fa9062a7 100644 --- a/src/common/io_file.cpp +++ b/src/common/io_file.cpp @@ -131,9 +131,7 @@ namespace { case SeekOrigin::End: return SEEK_END; default: - LOG_ERROR(Common_Filesystem, "Unsupported origin {}, defaulting to SEEK_SET", - static_cast(origin)); - return SEEK_SET; + UNREACHABLE_MSG("Impossible SeekOrigin {}", static_cast(origin)); } } diff --git a/src/common/io_file.h b/src/common/io_file.h index fb20a2bc5..45787a092 100644 --- a/src/common/io_file.h +++ b/src/common/io_file.h @@ -61,8 +61,6 @@ enum class SeekOrigin : u32 { SetOrigin, // Seeks from the start of the file. CurrentPosition, // Seeks from the current file pointer position. End, // Seeks from the end of the file. - SeekHole, // Seeks from the start of the next hole in the file. - SeekData, // Seeks from the start of the next non-hole region in the file. }; class IOFile final { diff --git a/src/core/libraries/kernel/file_system.cpp b/src/core/libraries/kernel/file_system.cpp index bcfa15a62..cb1fd14a2 100644 --- a/src/core/libraries/kernel/file_system.cpp +++ b/src/core/libraries/kernel/file_system.cpp @@ -67,10 +67,16 @@ s32 PS4_SYSV_ABI open(const char* raw_path, s32 flags, u16 mode) { bool write = (flags & 0x3) == ORBIS_KERNEL_O_WRONLY; bool rdwr = (flags & 0x3) == ORBIS_KERNEL_O_RDWR; + if (!read && !write && !rdwr) { + // Start by checking for invalid flags. + *__Error() = POSIX_EINVAL; + return -1; + } + bool nonblock = (flags & ORBIS_KERNEL_O_NONBLOCK) != 0; bool append = (flags & ORBIS_KERNEL_O_APPEND) != 0; - bool fsync = (flags & ORBIS_KERNEL_O_FSYNC) != 0; - bool sync = (flags & ORBIS_KERNEL_O_SYNC) != 0; + // Flags fsync and sync behave the same + bool sync = (flags & ORBIS_KERNEL_O_SYNC) != 0 || (flags & ORBIS_KERNEL_O_FSYNC) != 0; bool create = (flags & ORBIS_KERNEL_O_CREAT) != 0; bool truncate = (flags & ORBIS_KERNEL_O_TRUNC) != 0; bool excl = (flags & ORBIS_KERNEL_O_EXCL) != 0; @@ -78,6 +84,10 @@ s32 PS4_SYSV_ABI open(const char* raw_path, s32 flags, u16 mode) { bool direct = (flags & ORBIS_KERNEL_O_DIRECT) != 0; bool directory = (flags & ORBIS_KERNEL_O_DIRECTORY) != 0; + if (sync || direct || dsync || nonblock) { + LOG_WARNING(Kernel_Fs, "flags {:#x} not fully handled", flags); + } + std::string_view path{raw_path}; u32 handle = h->CreateHandle(); auto* file = h->GetFile(handle); @@ -94,84 +104,126 @@ s32 PS4_SYSV_ABI open(const char* raw_path, s32 flags, u16 mode) { } } - if (directory) { - file->type = Core::FileSys::FileType::Directory; - file->m_guest_name = path; - file->m_host_name = mnt->GetHostPath(file->m_guest_name); - if (!std::filesystem::is_directory(file->m_host_name)) { // directory doesn't exist + bool read_only = false; + file->m_guest_name = path; + file->m_host_name = mnt->GetHostPath(file->m_guest_name, &read_only); + bool exists = std::filesystem::exists(file->m_host_name); + s32 e = 0; + + if (create) { + if (excl && exists) { + // Error if file exists h->DeleteHandle(handle); - *__Error() = POSIX_ENOENT; + *__Error() = POSIX_EEXIST; + return -1; + } + + if (read_only) { + // Can't create files in a read only directory + h->DeleteHandle(handle); + *__Error() = POSIX_EROFS; + return -1; + } + // Create a file if it doesn't exist + Common::FS::IOFile out(file->m_host_name, Common::FS::FileAccessMode::Write); + } else if (!exists) { + // If we're not creating a file, and it doesn't exist, return ENOENT + h->DeleteHandle(handle); + *__Error() = POSIX_ENOENT; + return -1; + } + + if (std::filesystem::is_directory(file->m_host_name) || directory) { + // Directories can be opened even if the directory flag isn't set. + // In these cases, error behavior is identical to the directory code path. + directory = true; + } + + if (directory) { + if (!std::filesystem::is_directory(file->m_host_name)) { + // If the opened file is not a directory, return ENOTDIR. + // This will trigger when create & directory is specified, this is expected. + h->DeleteHandle(handle); + *__Error() = POSIX_ENOTDIR; + return -1; + } + + file->type = Core::FileSys::FileType::Directory; + + // Populate directory contents + mnt->IterateDirectory(file->m_guest_name, + [&file](const auto& ent_path, const auto ent_is_file) { + auto& dir_entry = file->dirents.emplace_back(); + dir_entry.name = ent_path.filename().string(); + dir_entry.isFile = ent_is_file; + }); + file->dirents_index = 0; + + if (read) { + e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Read); + } else if (write || rdwr) { + // Cannot open directories with any type of write access + h->DeleteHandle(handle); + *__Error() = POSIX_EISDIR; + return -1; + } + + if (e == EACCES) { + // Hack to bypass some platform limitations, ignore the error and continue as normal. + LOG_WARNING(Kernel_Fs, "Opening directories is not fully supported on this platform"); + e = 0; + } + + if (truncate) { + // Cannot open directories with truncate + h->DeleteHandle(handle); + *__Error() = POSIX_EISDIR; return -1; - } else { - if (create) { - return handle; // dir already exists - } else { - mnt->IterateDirectory(file->m_guest_name, - [&file](const auto& ent_path, const auto ent_is_file) { - auto& dir_entry = file->dirents.emplace_back(); - dir_entry.name = ent_path.filename().string(); - dir_entry.isFile = ent_is_file; - }); - file->dirents_index = 0; - } } } else { - file->m_guest_name = path; - file->m_host_name = mnt->GetHostPath(file->m_guest_name); - bool exists = std::filesystem::exists(file->m_host_name); - int e = 0; + // Start by opening as read-write so we can truncate regardless of flags. + // Since open starts by closing the file, this won't interfere with later open calls. + e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::ReadWrite); - if (create) { - if (excl && exists) { - // Error if file exists - h->DeleteHandle(handle); - *__Error() = POSIX_EEXIST; - return -1; - } - // Create file if it doesn't exist - Common::FS::IOFile out(file->m_host_name, Common::FS::FileAccessMode::Write); - } else if (!exists) { - // File to open doesn't exist, return ENOENT + file->type = Core::FileSys::FileType::Regular; + + if (truncate && read_only) { + // Can't open files with truncate flag in a read only directory h->DeleteHandle(handle); - *__Error() = POSIX_ENOENT; + *__Error() = POSIX_EROFS; return -1; + } else if (truncate && e == 0) { + // If the file was opened successfully and truncate was enabled, reduce size to 0 + file->f.SetSize(0); } if (read) { // Read only e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Read); + } else if (read_only) { + // Can't open files with write/read-write access in a read only directory + h->DeleteHandle(handle); + *__Error() = POSIX_EROFS; + return -1; + } else if (append) { + // Append can be specified with rdwr or write, but we treat it as a separate mode. + e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Append); } else if (write) { // Write only - if (append) { - e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Append); - } else { - e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Write); - } + e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Write); } else if (rdwr) { // Read and write - if (append) { - e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Append); - } else { - e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::ReadWrite); - } - } else { - // Invalid flags - *__Error() = POSIX_EINVAL; - return -1; - } - - if (truncate && e == 0) { - // If the file was opened successfully and truncate was enabled, reduce size to 0 - file->f.SetSize(0); - } - - if (e != 0) { - // Open failed in platform-specific code, errno needs to be converted. - h->DeleteHandle(handle); - SetPosixErrno(e); - return -1; + e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::ReadWrite); } } + + if (e != 0) { + // Open failed in platform-specific code, errno needs to be converted. + h->DeleteHandle(handle); + SetPosixErrno(e); + return -1; + } + file->is_opened = true; return handle; } @@ -365,10 +417,10 @@ s64 PS4_SYSV_ABI posix_lseek(s32 fd, s64 offset, s32 whence) { origin = Common::FS::SeekOrigin::CurrentPosition; } else if (whence == 2) { origin = Common::FS::SeekOrigin::End; - } else if (whence == 3) { - origin = Common::FS::SeekOrigin::SeekHole; - } else if (whence == 4) { - origin = Common::FS::SeekOrigin::SeekData; + } else if (whence == 3 || whence == 4) { + // whence parameter belongs to an unsupported POSIX extension + *__Error() = POSIX_ENOTTY; + return -1; } else { // whence parameter is invalid *__Error() = POSIX_EINVAL; @@ -486,13 +538,13 @@ s32 PS4_SYSV_ABI posix_rmdir(const char* path) { const std::filesystem::path dir_name = mnt->GetHostPath(path, &ro); - if (dir_name.empty() || !std::filesystem::is_directory(dir_name)) { - *__Error() = POSIX_ENOTDIR; + if (ro) { + *__Error() = POSIX_EROFS; return -1; } - if (ro) { - *__Error() = POSIX_EROFS; + if (dir_name.empty() || !std::filesystem::is_directory(dir_name)) { + *__Error() = POSIX_ENOTDIR; return -1; } @@ -523,8 +575,7 @@ s32 PS4_SYSV_ABI sceKernelRmdir(const char* path) { s32 PS4_SYSV_ABI posix_stat(const char* path, OrbisKernelStat* sb) { LOG_INFO(Kernel_Fs, "(PARTIAL) path = {}", path); auto* mnt = Common::Singleton::Instance(); - bool ro = false; - const auto path_name = mnt->GetHostPath(path, &ro); + const auto path_name = mnt->GetHostPath(path); std::memset(sb, 0, sizeof(OrbisKernelStat)); const bool is_dir = std::filesystem::is_directory(path_name); const bool is_file = std::filesystem::is_regular_file(path_name); @@ -545,9 +596,6 @@ s32 PS4_SYSV_ABI posix_stat(const char* path, OrbisKernelStat* sb) { sb->st_blocks = (sb->st_size + 511) / 512; // TODO incomplete } - if (ro) { - sb->st_mode &= ~0000555u; - } return ORBIS_OK; } diff --git a/src/emulator.cpp b/src/emulator.cpp index 5c20353df..448b8aad4 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -98,9 +98,9 @@ void Emulator::Run(const std::filesystem::path& file, const std::vector::Instance(); - mnt->Mount(game_folder, "/app0"); + mnt->Mount(game_folder, "/app0", true); // Certain games may use /hostapp as well such as CUSA001100 - mnt->Mount(game_folder, "/hostapp"); + mnt->Mount(game_folder, "/hostapp", true); auto& game_info = Common::ElfInfo::Instance(); @@ -231,11 +231,15 @@ void Emulator::Run(const std::filesystem::path& file, const std::vectorMount(mount_data_dir, "/data"); // should just exist, manually create with game serial + + // Mounting temp folders const auto& mount_temp_dir = Common::FS::GetUserPath(Common::FS::PathType::TempDataDir) / id; - if (!std::filesystem::exists(mount_temp_dir)) { - std::filesystem::create_directory(mount_temp_dir); + if (std::filesystem::exists(mount_temp_dir)) { + // Temp folder should be cleared on each boot. + std::filesystem::remove_all(mount_temp_dir); } - mnt->Mount(mount_temp_dir, "/temp0"); // called in app_content ==> stat/mkdir + std::filesystem::create_directory(mount_temp_dir); + mnt->Mount(mount_temp_dir, "/temp0"); mnt->Mount(mount_temp_dir, "/temp"); const auto& mount_download_dir = From eb09c4ccce4ca6ab8ed08f9e53926c52dd52d8a5 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Thu, 1 May 2025 20:10:42 -0700 Subject: [PATCH 4/4] vk_presenter: Use correct format for output frame image and view. (#2871) --- .../renderer_vulkan/vk_presenter.cpp | 22 +++++++++++++++++-- src/video_core/renderer_vulkan/vk_presenter.h | 8 ++++--- src/video_core/texture_cache/image_info.cpp | 7 +++--- 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_presenter.cpp b/src/video_core/renderer_vulkan/vk_presenter.cpp index 6bd4b26fa..09dd23cb6 100644 --- a/src/video_core/renderer_vulkan/vk_presenter.cpp +++ b/src/video_core/renderer_vulkan/vk_presenter.cpp @@ -270,7 +270,25 @@ Frame* Presenter::PrepareLastFrame() { return frame; } -Frame* Presenter::PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop) { +static vk::Format GetFrameViewFormat(const Libraries::VideoOut::PixelFormat format) { + switch (format) { + case Libraries::VideoOut::PixelFormat::A8B8G8R8Srgb: + return vk::Format::eR8G8B8A8Srgb; + case Libraries::VideoOut::PixelFormat::A8R8G8B8Srgb: + return vk::Format::eB8G8R8A8Srgb; + case Libraries::VideoOut::PixelFormat::A2R10G10B10: + case Libraries::VideoOut::PixelFormat::A2R10G10B10Srgb: + case Libraries::VideoOut::PixelFormat::A2R10G10B10Bt2020Pq: + return vk::Format::eA2R10G10B10UnormPack32; + default: + break; + } + UNREACHABLE_MSG("Unknown format={}", static_cast(format)); + return {}; +} + +Frame* Presenter::PrepareFrameInternal(VideoCore::ImageId image_id, + const Libraries::VideoOut::PixelFormat format, bool is_eop) { // Request a free presentation frame. Frame* frame = GetRenderFrame(); @@ -324,7 +342,7 @@ Frame* Presenter::PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop) cmdbuf); VideoCore::ImageViewInfo info{}; - info.format = image.info.pixel_format; + info.format = GetFrameViewFormat(format); // Exclude alpha from output frame to avoid blending with UI. info.mapping = vk::ComponentMapping{ .r = vk::ComponentSwizzle::eIdentity, diff --git a/src/video_core/renderer_vulkan/vk_presenter.h b/src/video_core/renderer_vulkan/vk_presenter.h index ad2708474..8ed2052ee 100644 --- a/src/video_core/renderer_vulkan/vk_presenter.h +++ b/src/video_core/renderer_vulkan/vk_presenter.h @@ -70,11 +70,12 @@ public: auto desc = VideoCore::TextureCache::VideoOutDesc{attribute, cpu_address}; const auto image_id = texture_cache.FindImage(desc); texture_cache.UpdateImage(image_id, is_eop ? nullptr : &flip_scheduler); - return PrepareFrameInternal(image_id, is_eop); + return PrepareFrameInternal(image_id, attribute.attrib.pixel_format, is_eop); } Frame* PrepareBlankFrame(bool is_eop) { - return PrepareFrameInternal(VideoCore::NULL_IMAGE_ID, is_eop); + return PrepareFrameInternal(VideoCore::NULL_IMAGE_ID, + Libraries::VideoOut::PixelFormat::Unknown, is_eop); } VideoCore::Image& RegisterVideoOutSurface( @@ -119,7 +120,8 @@ public: } private: - Frame* PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop = true); + Frame* PrepareFrameInternal(VideoCore::ImageId image_id, + Libraries::VideoOut::PixelFormat format, bool is_eop = true); Frame* GetRenderFrame(); void SetExpectedGameSize(s32 width, s32 height); diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp index 26928eaf7..39322f449 100644 --- a/src/video_core/texture_cache/image_info.cpp +++ b/src/video_core/texture_cache/image_info.cpp @@ -16,14 +16,15 @@ using VideoOutFormat = Libraries::VideoOut::PixelFormat; static vk::Format ConvertPixelFormat(const VideoOutFormat format) { switch (format) { - case VideoOutFormat::A8R8G8B8Srgb: - return vk::Format::eB8G8R8A8Srgb; case VideoOutFormat::A8B8G8R8Srgb: + // Remaining formats are mapped to RGBA for internal consistency and changed to BGRA in the + // frame image view. + case VideoOutFormat::A8R8G8B8Srgb: return vk::Format::eR8G8B8A8Srgb; case VideoOutFormat::A2R10G10B10: case VideoOutFormat::A2R10G10B10Srgb: case VideoOutFormat::A2R10G10B10Bt2020Pq: - return vk::Format::eA2R10G10B10UnormPack32; + return vk::Format::eA2B10G10R10UnormPack32; default: break; }