Merge branch 'main' into net3

This commit is contained in:
georgemoralis 2025-05-02 09:44:32 +03:00 committed by GitHub
commit d6758d0662
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 216 additions and 99 deletions

0
CMakeLists.txt Executable file → Normal file
View File

View File

@ -131,9 +131,7 @@ namespace {
case SeekOrigin::End:
return SEEK_END;
default:
LOG_ERROR(Common_Filesystem, "Unsupported origin {}, defaulting to SEEK_SET",
static_cast<u32>(origin));
return SEEK_SET;
UNREACHABLE_MSG("Impossible SeekOrigin {}", static_cast<u32>(origin));
}
}

View File

@ -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 {

View File

@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstdlib>
#include <ctime>
#include "common/logging/log.h"
#include "random_device.h"

View File

@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstdlib>
#include <ctime>
#include "common/logging/log.h"
#include "srandom_device.h"

View File

@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstdlib>
#include <ctime>
#include "common/logging/log.h"
#include "urandom_device.h"

View File

@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstdio>
#include <ctime>
#include <fmt/chrono.h>
#include <imgui.h>
#include <magic_enum/magic_enum.hpp>

View File

@ -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<Core::FileSys::MntPoints>::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;
}

View File

@ -98,9 +98,9 @@ void Emulator::Run(const std::filesystem::path& file, const std::vector<std::str
// Applications expect to be run from /app0 so mount the file's parent path as app0.
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::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::vector<std::str
std::filesystem::create_directory(mount_data_dir);
}
mnt->Mount(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 =

View File

@ -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),
};

View File

@ -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<U32>(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<u8>::max()));
return ir.FPDiv(left, max);
}
case AmdGpu::NumberConversion::Sint16ToSnormNz: {
const IR::U32 additon = ir.IAdd(ir.IMul(ir.BitCast<U32>(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<u16>::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<u8>::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<F32>(raw);
}
case AmdGpu::NumberConversion::Sint16ToSnormNz: {
const IR::F32 max = ir.Imm32(float(std::numeric_limits<u16>::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<F32>(raw);
}
default:
UNREACHABLE();
}

View File

@ -928,7 +928,7 @@ struct Liverpool {
}
[[nodiscard]] NumberConversion GetNumberConversion() const {
return MapNumberConversion(GetFixedNumberFormat());
return MapNumberConversion(GetFixedNumberFormat(), info.format);
}
[[nodiscard]] CompMapping Swizzle() const {

View File

@ -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 {

View File

@ -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;
}

View File

@ -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<u32>(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,

View File

@ -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);

View File

@ -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;
}