Compare commits

...

6 commits

Author SHA1 Message Date
yuzubot 2f2ef1dacd "Merge Tagged PR 12173" 2024-02-22 13:03:06 +00:00
yuzubot d01745f346 "Merge Tagged PR 12749" 2024-02-22 13:03:05 +00:00
yuzubot ddca3d63cd "Merge Tagged PR 12982" 2024-02-22 13:03:05 +00:00
yuzubot 40494d6f59 "Merge Tagged PR 13000" 2024-02-22 13:03:04 +00:00
yuzubot 5e88e705b2 "Merge Tagged PR 13001" 2024-02-22 13:03:04 +00:00
yuzubot 02a9dde5f5 "Merge Tagged PR 13075" 2024-02-22 13:03:03 +00:00
29 changed files with 819 additions and 439 deletions

View file

@ -384,6 +384,12 @@ struct Values {
AstcRecompression::Bc3, AstcRecompression::Bc3,
"astc_recompression", "astc_recompression",
Category::RendererAdvanced}; Category::RendererAdvanced};
SwitchableSetting<VramUsageMode, true> vram_usage_mode{linkage,
VramUsageMode::Conservative,
VramUsageMode::Conservative,
VramUsageMode::Aggressive,
"vram_usage_mode",
Category::RendererAdvanced};
SwitchableSetting<bool> async_presentation{linkage, SwitchableSetting<bool> async_presentation{linkage,
#ifdef ANDROID #ifdef ANDROID
true, true,

View file

@ -122,6 +122,8 @@ ENUM(AstcRecompression, Uncompressed, Bc1, Bc3);
ENUM(VSyncMode, Immediate, Mailbox, Fifo, FifoRelaxed); ENUM(VSyncMode, Immediate, Mailbox, Fifo, FifoRelaxed);
ENUM(VramUsageMode, Conservative, Aggressive);
ENUM(RendererBackend, OpenGL, Vulkan, Null); ENUM(RendererBackend, OpenGL, Vulkan, Null);
ENUM(ShaderBackend, Glsl, Glasm, SpirV); ENUM(ShaderBackend, Glsl, Glasm, SpirV);

View file

@ -59,8 +59,12 @@ add_library(core STATIC
file_sys/fs_path.h file_sys/fs_path.h
file_sys/fs_path_utility.h file_sys/fs_path_utility.h
file_sys/fs_string_util.h file_sys/fs_string_util.h
file_sys/fsa/fs_i_directory.h
file_sys/fsa/fs_i_file.h
file_sys/fsa/fs_i_filesystem.h
file_sys/fsmitm_romfsbuild.cpp file_sys/fsmitm_romfsbuild.cpp
file_sys/fsmitm_romfsbuild.h file_sys/fsmitm_romfsbuild.h
file_sys/fssrv/fssrv_sf_path.h
file_sys/fssystem/fs_i_storage.h file_sys/fssystem/fs_i_storage.h
file_sys/fssystem/fs_types.h file_sys/fssystem/fs_types.h
file_sys/fssystem/fssystem_aes_ctr_counter_extended_storage.cpp file_sys/fssystem/fssystem_aes_ctr_counter_extended_storage.cpp

View file

@ -522,13 +522,17 @@ void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size
auto* memory_device_inter = registered_processes[asid.id]; auto* memory_device_inter = registered_processes[asid.id];
const auto release_pending = [&] { const auto release_pending = [&] {
if (uncache_bytes > 0) { if (uncache_bytes > 0) {
if (memory_device_inter != nullptr) {
MarkRegionCaching(memory_device_inter, uncache_begin << Memory::YUZU_PAGEBITS, MarkRegionCaching(memory_device_inter, uncache_begin << Memory::YUZU_PAGEBITS,
uncache_bytes, false); uncache_bytes, false);
}
uncache_bytes = 0; uncache_bytes = 0;
} }
if (cache_bytes > 0) { if (cache_bytes > 0) {
if (memory_device_inter != nullptr) {
MarkRegionCaching(memory_device_inter, cache_begin << Memory::YUZU_PAGEBITS, MarkRegionCaching(memory_device_inter, cache_begin << Memory::YUZU_PAGEBITS,
cache_bytes, true); cache_bytes, true);
}
cache_bytes = 0; cache_bytes = 0;
} }
}; };

View file

@ -23,6 +23,8 @@ enum class OpenDirectoryMode : u64 {
File = (1 << 1), File = (1 << 1),
All = (Directory | File), All = (Directory | File),
NotRequireFileSize = (1ULL << 31),
}; };
DECLARE_ENUM_FLAG_OPERATORS(OpenDirectoryMode) DECLARE_ENUM_FLAG_OPERATORS(OpenDirectoryMode)
@ -36,4 +38,29 @@ enum class CreateOption : u8 {
BigFile = (1 << 0), BigFile = (1 << 0),
}; };
struct FileSystemAttribute {
u8 dir_entry_name_length_max_defined;
u8 file_entry_name_length_max_defined;
u8 dir_path_name_length_max_defined;
u8 file_path_name_length_max_defined;
INSERT_PADDING_BYTES_NOINIT(0x5);
u8 utf16_dir_entry_name_length_max_defined;
u8 utf16_file_entry_name_length_max_defined;
u8 utf16_dir_path_name_length_max_defined;
u8 utf16_file_path_name_length_max_defined;
INSERT_PADDING_BYTES_NOINIT(0x18);
s32 dir_entry_name_length_max;
s32 file_entry_name_length_max;
s32 dir_path_name_length_max;
s32 file_path_name_length_max;
INSERT_PADDING_WORDS_NOINIT(0x5);
s32 utf16_dir_entry_name_length_max;
s32 utf16_file_entry_name_length_max;
s32 utf16_dir_path_name_length_max;
s32 utf16_file_path_name_length_max;
INSERT_PADDING_WORDS_NOINIT(0x18);
INSERT_PADDING_WORDS_NOINIT(0x1);
};
static_assert(sizeof(FileSystemAttribute) == 0xC0, "FileSystemAttribute has incorrect size");
} // namespace FileSys } // namespace FileSys

View file

@ -10,7 +10,7 @@ namespace FileSys {
constexpr size_t RequiredAlignment = alignof(u64); constexpr size_t RequiredAlignment = alignof(u64);
void* AllocateUnsafe(size_t size) { inline void* AllocateUnsafe(size_t size) {
// Allocate // Allocate
void* const ptr = ::operator new(size, std::align_val_t{RequiredAlignment}); void* const ptr = ::operator new(size, std::align_val_t{RequiredAlignment});
@ -21,16 +21,16 @@ void* AllocateUnsafe(size_t size) {
return ptr; return ptr;
} }
void DeallocateUnsafe(void* ptr, size_t size) { inline void DeallocateUnsafe(void* ptr, size_t size) {
// Deallocate the pointer // Deallocate the pointer
::operator delete(ptr, std::align_val_t{RequiredAlignment}); ::operator delete(ptr, std::align_val_t{RequiredAlignment});
} }
void* Allocate(size_t size) { inline void* Allocate(size_t size) {
return AllocateUnsafe(size); return AllocateUnsafe(size);
} }
void Deallocate(void* ptr, size_t size) { inline void Deallocate(void* ptr, size_t size) {
// If the pointer is non-null, deallocate it // If the pointer is non-null, deallocate it
if (ptr != nullptr) { if (ptr != nullptr) {
DeallocateUnsafe(ptr, size); DeallocateUnsafe(ptr, size);

View file

@ -381,7 +381,7 @@ public:
// Check that it's possible for us to remove a child // Check that it's possible for us to remove a child
auto* p = m_write_buffer.Get(); auto* p = m_write_buffer.Get();
s32 len = std::strlen(p); s32 len = static_cast<s32>(std::strlen(p));
R_UNLESS(len != 1 || (p[0] != '/' && p[0] != '.'), ResultNotImplemented); R_UNLESS(len != 1 || (p[0] != '/' && p[0] != '.'), ResultNotImplemented);
// Handle a trailing separator // Handle a trailing separator

View file

@ -426,8 +426,9 @@ public:
R_SUCCEED(); R_SUCCEED();
} }
static Result Normalize(char* dst, size_t* out_len, const char* path, size_t max_out_size, static constexpr Result Normalize(char* dst, size_t* out_len, const char* path,
bool is_windows_path, bool is_drive_relative_path, size_t max_out_size, bool is_windows_path,
bool is_drive_relative_path,
bool allow_all_characters = false) { bool allow_all_characters = false) {
// Use StringTraits names for remainder of scope // Use StringTraits names for remainder of scope
using namespace StringTraits; using namespace StringTraits;

View file

@ -19,6 +19,11 @@ constexpr int Strlen(const T* str) {
return length; return length;
} }
template <typename T>
constexpr int Strnlen(const T* str, std::size_t count) {
return Strnlen(str, static_cast<int>(count));
}
template <typename T> template <typename T>
constexpr int Strnlen(const T* str, int count) { constexpr int Strnlen(const T* str, int count) {
ASSERT(str != nullptr); ASSERT(str != nullptr);
@ -32,6 +37,11 @@ constexpr int Strnlen(const T* str, int count) {
return length; return length;
} }
template <typename T>
constexpr int Strncmp(const T* lhs, const T* rhs, std::size_t count) {
return Strncmp(lhs, rhs, static_cast<int>(count));
}
template <typename T> template <typename T>
constexpr int Strncmp(const T* lhs, const T* rhs, int count) { constexpr int Strncmp(const T* lhs, const T* rhs, int count) {
ASSERT(lhs != nullptr); ASSERT(lhs != nullptr);
@ -51,6 +61,11 @@ constexpr int Strncmp(const T* lhs, const T* rhs, int count) {
return l - r; return l - r;
} }
template <typename T>
static constexpr int Strlcpy(T* dst, const T* src, std::size_t count) {
return Strlcpy<T>(dst, src, static_cast<int>(count));
}
template <typename T> template <typename T>
static constexpr int Strlcpy(T* dst, const T* src, int count) { static constexpr int Strlcpy(T* dst, const T* src, int count) {
ASSERT(dst != nullptr); ASSERT(dst != nullptr);

View file

@ -0,0 +1,91 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
#include "core/file_sys/errors.h"
#include "core/file_sys/fs_directory.h"
#include "core/file_sys/fs_file.h"
#include "core/file_sys/fs_filesystem.h"
#include "core/file_sys/savedata_factory.h"
#include "core/file_sys/vfs/vfs.h"
#include "core/hle/result.h"
namespace FileSys::Fsa {
class IDirectory {
public:
explicit IDirectory(VirtualDir backend_, OpenDirectoryMode mode)
: backend(std::move(backend_)) {
// TODO(DarkLordZach): Verify that this is the correct behavior.
// Build entry index now to save time later.
if (True(mode & OpenDirectoryMode::Directory)) {
BuildEntryIndex(backend->GetSubdirectories(), DirectoryEntryType::Directory);
}
if (True(mode & OpenDirectoryMode::File)) {
BuildEntryIndex(backend->GetFiles(), DirectoryEntryType::File);
}
}
virtual ~IDirectory() {}
Result Read(s64* out_count, DirectoryEntry* out_entries, s64 max_entries) {
R_UNLESS(out_count != nullptr, ResultNullptrArgument);
if (max_entries == 0) {
*out_count = 0;
R_SUCCEED();
}
R_UNLESS(out_entries != nullptr, ResultNullptrArgument);
R_UNLESS(max_entries > 0, ResultInvalidArgument);
R_RETURN(this->DoRead(out_count, out_entries, max_entries));
}
Result GetEntryCount(s64* out) {
R_UNLESS(out != nullptr, ResultNullptrArgument);
R_RETURN(this->DoGetEntryCount(out));
}
private:
Result DoRead(s64* out_count, DirectoryEntry* out_entries, s64 max_entries) {
const u64 actual_entries =
std::min(static_cast<u64>(max_entries), entries.size() - next_entry_index);
const auto* begin = reinterpret_cast<u8*>(entries.data() + next_entry_index);
const auto* end = reinterpret_cast<u8*>(entries.data() + next_entry_index + actual_entries);
const auto range_size = static_cast<std::size_t>(std::distance(begin, end));
next_entry_index += actual_entries;
*out_count = actual_entries;
std::memcpy(out_entries, begin, range_size);
R_SUCCEED();
}
Result DoGetEntryCount(s64* out) {
*out = entries.size() - next_entry_index;
R_SUCCEED();
}
// TODO: Remove this when VFS is gone
template <typename T>
void BuildEntryIndex(const std::vector<T>& new_data, DirectoryEntryType type) {
entries.reserve(entries.size() + new_data.size());
for (const auto& new_entry : new_data) {
auto name = new_entry->GetName();
if (type == DirectoryEntryType::File && name == GetSaveDataSizeFileName()) {
continue;
}
entries.emplace_back(name, static_cast<s8>(type),
type == DirectoryEntryType::Directory ? 0 : new_entry->GetSize());
}
}
VirtualDir backend;
std::vector<DirectoryEntry> entries;
u64 next_entry_index = 0;
};
} // namespace FileSys::Fsa

View file

@ -0,0 +1,167 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/overflow.h"
#include "core/file_sys/errors.h"
#include "core/file_sys/fs_file.h"
#include "core/file_sys/fs_filesystem.h"
#include "core/file_sys/fs_operate_range.h"
#include "core/file_sys/vfs/vfs.h"
#include "core/file_sys/vfs/vfs_types.h"
#include "core/hle/result.h"
namespace FileSys::Fsa {
class IFile {
public:
explicit IFile(VirtualFile backend_) : backend(std::move(backend_)) {}
virtual ~IFile() {}
Result Read(size_t* out, s64 offset, void* buffer, size_t size, const ReadOption& option) {
// Check that we have an output pointer
R_UNLESS(out != nullptr, ResultNullptrArgument);
// If we have nothing to read, just succeed
if (size == 0) {
*out = 0;
R_SUCCEED();
}
// Check that the read is valid
R_UNLESS(buffer != nullptr, ResultNullptrArgument);
R_UNLESS(offset >= 0, ResultOutOfRange);
R_UNLESS(Common::CanAddWithoutOverflow<s64>(offset, size), ResultOutOfRange);
// Do the read
R_RETURN(this->DoRead(out, offset, buffer, size, option));
}
Result Read(size_t* out, s64 offset, void* buffer, size_t size) {
R_RETURN(this->Read(out, offset, buffer, size, ReadOption::None));
}
Result GetSize(s64* out) {
R_UNLESS(out != nullptr, ResultNullptrArgument);
R_RETURN(this->DoGetSize(out));
}
Result Flush() {
R_RETURN(this->DoFlush());
}
Result Write(s64 offset, const void* buffer, size_t size, const WriteOption& option) {
// Handle the zero-size case
if (size == 0) {
if (option.HasFlushFlag()) {
R_TRY(this->Flush());
}
R_SUCCEED();
}
// Check the write is valid
R_UNLESS(buffer != nullptr, ResultNullptrArgument);
R_UNLESS(offset >= 0, ResultOutOfRange);
R_UNLESS(Common::CanAddWithoutOverflow<s64>(offset, size), ResultOutOfRange);
R_RETURN(this->DoWrite(offset, buffer, size, option));
}
Result SetSize(s64 size) {
R_UNLESS(size >= 0, ResultOutOfRange);
R_RETURN(this->DoSetSize(size));
}
Result OperateRange(void* dst, size_t dst_size, OperationId op_id, s64 offset, s64 size,
const void* src, size_t src_size) {
R_RETURN(this->DoOperateRange(dst, dst_size, op_id, offset, size, src, src_size));
}
Result OperateRange(OperationId op_id, s64 offset, s64 size) {
R_RETURN(this->DoOperateRange(nullptr, 0, op_id, offset, size, nullptr, 0));
}
protected:
Result DryRead(size_t* out, s64 offset, size_t size, const ReadOption& option,
OpenMode open_mode) {
// Check that we can read
R_UNLESS(static_cast<u32>(open_mode & OpenMode::Read) != 0, ResultReadNotPermitted);
// Get the file size, and validate our offset
s64 file_size = 0;
R_TRY(this->DoGetSize(std::addressof(file_size)));
R_UNLESS(offset <= file_size, ResultOutOfRange);
*out = static_cast<size_t>(std::min(file_size - offset, static_cast<s64>(size)));
R_SUCCEED();
}
Result DrySetSize(s64 size, OpenMode open_mode) {
// Check that we can write
R_UNLESS(static_cast<u32>(open_mode & OpenMode::Write) != 0, ResultWriteNotPermitted);
R_SUCCEED();
}
Result DryWrite(bool* out_append, s64 offset, size_t size, const WriteOption& option,
OpenMode open_mode) {
// Check that we can write
R_UNLESS(static_cast<u32>(open_mode & OpenMode::Write) != 0, ResultWriteNotPermitted);
// Get the file size
s64 file_size = 0;
R_TRY(this->DoGetSize(&file_size));
// Determine if we need to append
*out_append = false;
if (file_size < offset + static_cast<s64>(size)) {
R_UNLESS(static_cast<u32>(open_mode & OpenMode::AllowAppend) != 0,
ResultFileExtensionWithoutOpenModeAllowAppend);
*out_append = true;
}
R_SUCCEED();
}
private:
Result DoRead(size_t* out, s64 offset, void* buffer, size_t size, const ReadOption& option) {
const auto read_size = backend->Read(static_cast<u8*>(buffer), size, offset);
*out = read_size;
R_SUCCEED();
}
Result DoGetSize(s64* out) {
*out = backend->GetSize();
R_SUCCEED();
}
Result DoFlush() {
// Exists for SDK compatibiltity -- No need to flush file.
R_SUCCEED();
}
Result DoWrite(s64 offset, const void* buffer, size_t size, const WriteOption& option) {
const std::size_t written = backend->Write(static_cast<const u8*>(buffer), size, offset);
ASSERT_MSG(written == size,
"Could not write all bytes to file (requested={:016X}, actual={:016X}).", size,
written);
R_SUCCEED();
}
Result DoSetSize(s64 size) {
backend->Resize(size);
R_SUCCEED();
}
Result DoOperateRange(void* dst, size_t dst_size, OperationId op_id, s64 offset, s64 size,
const void* src, size_t src_size) {
R_THROW(ResultNotImplemented);
}
VirtualFile backend;
};
} // namespace FileSys::Fsa

View file

@ -0,0 +1,206 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/file_sys/errors.h"
#include "core/file_sys/fs_filesystem.h"
#include "core/file_sys/fs_path.h"
#include "core/file_sys/vfs/vfs_types.h"
#include "core/hle/result.h"
#include "core/hle/service/filesystem/filesystem.h"
namespace FileSys::Fsa {
class IFile;
class IDirectory;
enum class QueryId : u32 {
SetConcatenationFileAttribute = 0,
UpdateMac = 1,
IsSignedSystemPartitionOnSdCardValid = 2,
QueryUnpreparedFileInformation = 3,
};
class IFileSystem {
public:
explicit IFileSystem(VirtualDir backend_) : backend{std::move(backend_)} {}
virtual ~IFileSystem() {}
Result CreateFile(const Path& path, s64 size, CreateOption option) {
R_UNLESS(size >= 0, ResultOutOfRange);
R_RETURN(this->DoCreateFile(path, size, static_cast<int>(option)));
}
Result CreateFile(const Path& path, s64 size) {
R_RETURN(this->CreateFile(path, size, CreateOption::None));
}
Result DeleteFile(const Path& path) {
R_RETURN(this->DoDeleteFile(path));
}
Result CreateDirectory(const Path& path) {
R_RETURN(this->DoCreateDirectory(path));
}
Result DeleteDirectory(const Path& path) {
R_RETURN(this->DoDeleteDirectory(path));
}
Result DeleteDirectoryRecursively(const Path& path) {
R_RETURN(this->DoDeleteDirectoryRecursively(path));
}
Result RenameFile(const Path& old_path, const Path& new_path) {
R_RETURN(this->DoRenameFile(old_path, new_path));
}
Result RenameDirectory(const Path& old_path, const Path& new_path) {
R_RETURN(this->DoRenameDirectory(old_path, new_path));
}
Result GetEntryType(DirectoryEntryType* out, const Path& path) {
R_RETURN(this->DoGetEntryType(out, path));
}
Result OpenFile(VirtualFile* out_file, const Path& path, OpenMode mode) {
R_UNLESS(out_file != nullptr, ResultNullptrArgument);
R_UNLESS(static_cast<u32>(mode & OpenMode::ReadWrite) != 0, ResultInvalidOpenMode);
R_UNLESS(static_cast<u32>(mode & ~OpenMode::All) == 0, ResultInvalidOpenMode);
R_RETURN(this->DoOpenFile(out_file, path, mode));
}
Result OpenDirectory(VirtualDir* out_dir, const Path& path, OpenDirectoryMode mode) {
R_UNLESS(out_dir != nullptr, ResultNullptrArgument);
R_UNLESS(static_cast<u64>(mode & OpenDirectoryMode::All) != 0, ResultInvalidOpenMode);
R_UNLESS(static_cast<u64>(
mode & ~(OpenDirectoryMode::All | OpenDirectoryMode::NotRequireFileSize)) == 0,
ResultInvalidOpenMode);
R_RETURN(this->DoOpenDirectory(out_dir, path, mode));
}
Result Commit() {
R_RETURN(this->DoCommit());
}
Result GetFreeSpaceSize(s64* out, const Path& path) {
R_UNLESS(out != nullptr, ResultNullptrArgument);
R_RETURN(this->DoGetFreeSpaceSize(out, path));
}
Result GetTotalSpaceSize(s64* out, const Path& path) {
R_UNLESS(out != nullptr, ResultNullptrArgument);
R_RETURN(this->DoGetTotalSpaceSize(out, path));
}
Result CleanDirectoryRecursively(const Path& path) {
R_RETURN(this->DoCleanDirectoryRecursively(path));
}
Result GetFileTimeStampRaw(FileTimeStampRaw* out, const Path& path) {
R_UNLESS(out != nullptr, ResultNullptrArgument);
R_RETURN(this->DoGetFileTimeStampRaw(out, path));
}
Result QueryEntry(char* dst, size_t dst_size, const char* src, size_t src_size, QueryId query,
const Path& path) {
R_RETURN(this->DoQueryEntry(dst, dst_size, src, src_size, query, path));
}
// These aren't accessible as commands
Result CommitProvisionally(s64 counter) {
R_RETURN(this->DoCommitProvisionally(counter));
}
Result Rollback() {
R_RETURN(this->DoRollback());
}
Result Flush() {
R_RETURN(this->DoFlush());
}
private:
Result DoCreateFile(const Path& path, s64 size, int flags) {
R_RETURN(backend.CreateFile(path.GetString(), size));
}
Result DoDeleteFile(const Path& path) {
R_RETURN(backend.DeleteFile(path.GetString()));
}
Result DoCreateDirectory(const Path& path) {
R_RETURN(backend.CreateDirectory(path.GetString()));
}
Result DoDeleteDirectory(const Path& path) {
R_RETURN(backend.DeleteDirectory(path.GetString()));
}
Result DoDeleteDirectoryRecursively(const Path& path) {
R_RETURN(backend.DeleteDirectoryRecursively(path.GetString()));
}
Result DoRenameFile(const Path& old_path, const Path& new_path) {
R_RETURN(backend.RenameFile(old_path.GetString(), new_path.GetString()));
}
Result DoRenameDirectory(const Path& old_path, const Path& new_path) {
R_RETURN(backend.RenameDirectory(old_path.GetString(), new_path.GetString()));
}
Result DoGetEntryType(DirectoryEntryType* out, const Path& path) {
R_RETURN(backend.GetEntryType(out, path.GetString()));
}
Result DoOpenFile(VirtualFile* out_file, const Path& path, OpenMode mode) {
R_RETURN(backend.OpenFile(out_file, path.GetString(), mode));
}
Result DoOpenDirectory(VirtualDir* out_directory, const Path& path, OpenDirectoryMode mode) {
R_RETURN(backend.OpenDirectory(out_directory, path.GetString()));
}
Result DoCommit() {
R_THROW(ResultNotImplemented);
}
Result DoGetFreeSpaceSize(s64* out, const Path& path) {
R_THROW(ResultNotImplemented);
}
Result DoGetTotalSpaceSize(s64* out, const Path& path) {
R_THROW(ResultNotImplemented);
}
Result DoCleanDirectoryRecursively(const Path& path) {
R_RETURN(backend.CleanDirectoryRecursively(path.GetString()));
}
Result DoGetFileTimeStampRaw(FileTimeStampRaw* out, const Path& path) {
R_RETURN(backend.GetFileTimeStampRaw(out, path.GetString()));
}
Result DoQueryEntry(char* dst, size_t dst_size, const char* src, size_t src_size, QueryId query,
const Path& path) {
R_THROW(ResultNotImplemented);
}
// These aren't accessible as commands
Result DoCommitProvisionally(s64 counter) {
R_THROW(ResultNotImplemented);
}
Result DoRollback() {
R_THROW(ResultNotImplemented);
}
Result DoFlush() {
R_THROW(ResultNotImplemented);
}
Service::FileSystem::VfsDirectoryServiceWrapper backend;
};
} // namespace FileSys::Fsa

View file

@ -0,0 +1,36 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/file_sys/fs_directory.h"
namespace FileSys::Sf {
struct Path {
char str[EntryNameLengthMax + 1];
static constexpr Path Encode(const char* p) {
Path path = {};
for (size_t i = 0; i < sizeof(path) - 1; i++) {
path.str[i] = p[i];
if (p[i] == '\x00') {
break;
}
}
return path;
}
static constexpr size_t GetPathLength(const Path& path) {
size_t len = 0;
for (size_t i = 0; i < sizeof(path) - 1 && path.str[i] != '\x00'; i++) {
len++;
}
return len;
}
};
static_assert(std::is_trivially_copyable_v<Path>, "Path must be trivially copyable.");
using FspPath = Path;
} // namespace FileSys::Sf

View file

@ -3,82 +3,34 @@
#include "core/file_sys/fs_filesystem.h" #include "core/file_sys/fs_filesystem.h"
#include "core/file_sys/savedata_factory.h" #include "core/file_sys/savedata_factory.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/filesystem/fsp/fs_i_directory.h" #include "core/hle/service/filesystem/fsp/fs_i_directory.h"
#include "core/hle/service/ipc_helpers.h"
namespace Service::FileSystem { namespace Service::FileSystem {
template <typename T> IDirectory::IDirectory(Core::System& system_, FileSys::VirtualDir directory_,
static void BuildEntryIndex(std::vector<FileSys::DirectoryEntry>& entries,
const std::vector<T>& new_data, FileSys::DirectoryEntryType type) {
entries.reserve(entries.size() + new_data.size());
for (const auto& new_entry : new_data) {
auto name = new_entry->GetName();
if (type == FileSys::DirectoryEntryType::File &&
name == FileSys::GetSaveDataSizeFileName()) {
continue;
}
entries.emplace_back(name, static_cast<s8>(type),
type == FileSys::DirectoryEntryType::Directory ? 0
: new_entry->GetSize());
}
}
IDirectory::IDirectory(Core::System& system_, FileSys::VirtualDir backend_,
FileSys::OpenDirectoryMode mode) FileSys::OpenDirectoryMode mode)
: ServiceFramework{system_, "IDirectory"}, backend(std::move(backend_)) { : ServiceFramework{system_, "IDirectory"},
backend(std::make_unique<FileSys::Fsa::IDirectory>(directory_, mode)) {
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{0, &IDirectory::Read, "Read"}, {0, D<&IDirectory::Read>, "Read"},
{1, &IDirectory::GetEntryCount, "GetEntryCount"}, {1, D<&IDirectory::GetEntryCount>, "GetEntryCount"},
}; };
RegisterHandlers(functions); RegisterHandlers(functions);
// TODO(DarkLordZach): Verify that this is the correct behavior.
// Build entry index now to save time later.
if (True(mode & FileSys::OpenDirectoryMode::Directory)) {
BuildEntryIndex(entries, backend->GetSubdirectories(),
FileSys::DirectoryEntryType::Directory);
}
if (True(mode & FileSys::OpenDirectoryMode::File)) {
BuildEntryIndex(entries, backend->GetFiles(), FileSys::DirectoryEntryType::File);
}
} }
void IDirectory::Read(HLERequestContext& ctx) { Result IDirectory::Read(
Out<s64> out_count,
const OutArray<FileSys::DirectoryEntry, BufferAttr_HipcMapAlias> out_entries) {
LOG_DEBUG(Service_FS, "called."); LOG_DEBUG(Service_FS, "called.");
// Calculate how many entries we can fit in the output buffer R_RETURN(backend->Read(out_count, out_entries.data(), out_entries.size()));
const u64 count_entries = ctx.GetWriteBufferNumElements<FileSys::DirectoryEntry>();
// Cap at total number of entries.
const u64 actual_entries = std::min(count_entries, entries.size() - next_entry_index);
// Determine data start and end
const auto* begin = reinterpret_cast<u8*>(entries.data() + next_entry_index);
const auto* end = reinterpret_cast<u8*>(entries.data() + next_entry_index + actual_entries);
const auto range_size = static_cast<std::size_t>(std::distance(begin, end));
next_entry_index += actual_entries;
// Write the data to memory
ctx.WriteBuffer(begin, range_size);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push(actual_entries);
} }
void IDirectory::GetEntryCount(HLERequestContext& ctx) { Result IDirectory::GetEntryCount(Out<s64> out_count) {
LOG_DEBUG(Service_FS, "called"); LOG_DEBUG(Service_FS, "called");
u64 count = entries.size() - next_entry_index; R_RETURN(backend->GetEntryCount(out_count));
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push(count);
} }
} // namespace Service::FileSystem } // namespace Service::FileSystem

View file

@ -3,7 +3,9 @@
#pragma once #pragma once
#include "core/file_sys/fsa/fs_i_directory.h"
#include "core/file_sys/vfs/vfs.h" #include "core/file_sys/vfs/vfs.h"
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/service.h" #include "core/hle/service/service.h"
@ -15,16 +17,15 @@ namespace Service::FileSystem {
class IDirectory final : public ServiceFramework<IDirectory> { class IDirectory final : public ServiceFramework<IDirectory> {
public: public:
explicit IDirectory(Core::System& system_, FileSys::VirtualDir backend_, explicit IDirectory(Core::System& system_, FileSys::VirtualDir directory_,
FileSys::OpenDirectoryMode mode); FileSys::OpenDirectoryMode mode);
private: private:
FileSys::VirtualDir backend; std::unique_ptr<FileSys::Fsa::IDirectory> backend;
std::vector<FileSys::DirectoryEntry> entries;
u64 next_entry_index = 0;
void Read(HLERequestContext& ctx); Result Read(Out<s64> out_count,
void GetEntryCount(HLERequestContext& ctx); const OutArray<FileSys::DirectoryEntry, BufferAttr_HipcMapAlias> out_entries);
Result GetEntryCount(Out<s64> out_count);
}; };
} // namespace Service::FileSystem } // namespace Service::FileSystem

View file

@ -2,126 +2,64 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "core/file_sys/errors.h" #include "core/file_sys/errors.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/filesystem/fsp/fs_i_file.h" #include "core/hle/service/filesystem/fsp/fs_i_file.h"
#include "core/hle/service/ipc_helpers.h"
namespace Service::FileSystem { namespace Service::FileSystem {
IFile::IFile(Core::System& system_, FileSys::VirtualFile backend_) IFile::IFile(Core::System& system_, FileSys::VirtualFile file_)
: ServiceFramework{system_, "IFile"}, backend(std::move(backend_)) { : ServiceFramework{system_, "IFile"}, backend{std::make_unique<FileSys::Fsa::IFile>(file_)} {
// clang-format off
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{0, &IFile::Read, "Read"}, {0, D<&IFile::Read>, "Read"},
{1, &IFile::Write, "Write"}, {1, D<&IFile::Write>, "Write"},
{2, &IFile::Flush, "Flush"}, {2, D<&IFile::Flush>, "Flush"},
{3, &IFile::SetSize, "SetSize"}, {3, D<&IFile::SetSize>, "SetSize"},
{4, &IFile::GetSize, "GetSize"}, {4, D<&IFile::GetSize>, "GetSize"},
{5, nullptr, "OperateRange"}, {5, nullptr, "OperateRange"},
{6, nullptr, "OperateRangeWithBuffer"}, {6, nullptr, "OperateRangeWithBuffer"},
}; };
// clang-format on
RegisterHandlers(functions); RegisterHandlers(functions);
} }
void IFile::Read(HLERequestContext& ctx) { Result IFile::Read(
IPC::RequestParser rp{ctx}; FileSys::ReadOption option, Out<s64> out_size, s64 offset,
const u64 option = rp.Pop<u64>(); const OutBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_buffer,
const s64 offset = rp.Pop<s64>(); s64 size) {
const s64 length = rp.Pop<s64>(); LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option.value, offset,
size);
LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option, offset, length);
// Error checking
if (length < 0) {
LOG_ERROR(Service_FS, "Length is less than 0, length={}", length);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(FileSys::ResultInvalidSize);
return;
}
if (offset < 0) {
LOG_ERROR(Service_FS, "Offset is less than 0, offset={}", offset);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(FileSys::ResultInvalidOffset);
return;
}
// Read the data from the Storage backend // Read the data from the Storage backend
std::vector<u8> output = backend->ReadBytes(length, offset); R_RETURN(
backend->Read(reinterpret_cast<size_t*>(out_size.Get()), offset, out_buffer.data(), size));
// Write the data to memory
ctx.WriteBuffer(output);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push(static_cast<u64>(output.size()));
} }
void IFile::Write(HLERequestContext& ctx) { Result IFile::Write(
IPC::RequestParser rp{ctx}; const InBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> buffer,
const u64 option = rp.Pop<u64>(); FileSys::WriteOption option, s64 offset, s64 size) {
const s64 offset = rp.Pop<s64>(); LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option.value, offset,
const s64 length = rp.Pop<s64>(); size);
LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option, offset, length); R_RETURN(backend->Write(offset, buffer.data(), size, option));
// Error checking
if (length < 0) {
LOG_ERROR(Service_FS, "Length is less than 0, length={}", length);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(FileSys::ResultInvalidSize);
return;
}
if (offset < 0) {
LOG_ERROR(Service_FS, "Offset is less than 0, offset={}", offset);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(FileSys::ResultInvalidOffset);
return;
} }
const auto data = ctx.ReadBuffer(); Result IFile::Flush() {
ASSERT_MSG(static_cast<s64>(data.size()) <= length,
"Attempting to write more data than requested (requested={:016X}, actual={:016X}).",
length, data.size());
// Write the data to the Storage backend
const auto write_size =
static_cast<std::size_t>(std::distance(data.begin(), data.begin() + length));
const std::size_t written = backend->Write(data.data(), write_size, offset);
ASSERT_MSG(static_cast<s64>(written) == length,
"Could not write all bytes to file (requested={:016X}, actual={:016X}).", length,
written);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IFile::Flush(HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called"); LOG_DEBUG(Service_FS, "called");
// Exists for SDK compatibiltity -- No need to flush file. R_RETURN(backend->Flush());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
} }
void IFile::SetSize(HLERequestContext& ctx) { Result IFile::SetSize(s64 size) {
IPC::RequestParser rp{ctx};
const u64 size = rp.Pop<u64>();
LOG_DEBUG(Service_FS, "called, size={}", size); LOG_DEBUG(Service_FS, "called, size={}", size);
backend->Resize(size); R_RETURN(backend->SetSize(size));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
} }
void IFile::GetSize(HLERequestContext& ctx) { Result IFile::GetSize(Out<s64> out_size) {
const u64 size = backend->GetSize(); LOG_DEBUG(Service_FS, "called");
LOG_DEBUG(Service_FS, "called, size={}", size);
IPC::ResponseBuilder rb{ctx, 4}; R_RETURN(backend->GetSize(out_size));
rb.Push(ResultSuccess);
rb.Push<u64>(size);
} }
} // namespace Service::FileSystem } // namespace Service::FileSystem

View file

@ -3,6 +3,8 @@
#pragma once #pragma once
#include "core/file_sys/fsa/fs_i_file.h"
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/service.h" #include "core/hle/service/service.h"
@ -10,16 +12,21 @@ namespace Service::FileSystem {
class IFile final : public ServiceFramework<IFile> { class IFile final : public ServiceFramework<IFile> {
public: public:
explicit IFile(Core::System& system_, FileSys::VirtualFile backend_); explicit IFile(Core::System& system_, FileSys::VirtualFile file_);
private: private:
FileSys::VirtualFile backend; std::unique_ptr<FileSys::Fsa::IFile> backend;
void Read(HLERequestContext& ctx); Result Read(FileSys::ReadOption option, Out<s64> out_size, s64 offset,
void Write(HLERequestContext& ctx); const OutBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure>
void Flush(HLERequestContext& ctx); out_buffer,
void SetSize(HLERequestContext& ctx); s64 size);
void GetSize(HLERequestContext& ctx); Result Write(
const InBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> buffer,
FileSys::WriteOption option, s64 offset, s64 size);
Result Flush();
Result SetSize(s64 size);
Result GetSize(Out<s64> out_size);
}; };
} // namespace Service::FileSystem } // namespace Service::FileSystem

View file

@ -2,261 +2,172 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "common/string_util.h" #include "common/string_util.h"
#include "core/file_sys/fssrv/fssrv_sf_path.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/filesystem/fsp/fs_i_directory.h" #include "core/hle/service/filesystem/fsp/fs_i_directory.h"
#include "core/hle/service/filesystem/fsp/fs_i_file.h" #include "core/hle/service/filesystem/fsp/fs_i_file.h"
#include "core/hle/service/filesystem/fsp/fs_i_filesystem.h" #include "core/hle/service/filesystem/fsp/fs_i_filesystem.h"
#include "core/hle/service/ipc_helpers.h"
namespace Service::FileSystem { namespace Service::FileSystem {
IFileSystem::IFileSystem(Core::System& system_, FileSys::VirtualDir backend_, SizeGetter size_) IFileSystem::IFileSystem(Core::System& system_, FileSys::VirtualDir dir_, SizeGetter size_getter_)
: ServiceFramework{system_, "IFileSystem"}, backend{std::move(backend_)}, size{std::move( : ServiceFramework{system_, "IFileSystem"}, backend{std::make_unique<FileSys::Fsa::IFileSystem>(
size_)} { dir_)},
size_getter{std::move(size_getter_)} {
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{0, &IFileSystem::CreateFile, "CreateFile"}, {0, D<&IFileSystem::CreateFile>, "CreateFile"},
{1, &IFileSystem::DeleteFile, "DeleteFile"}, {1, D<&IFileSystem::DeleteFile>, "DeleteFile"},
{2, &IFileSystem::CreateDirectory, "CreateDirectory"}, {2, D<&IFileSystem::CreateDirectory>, "CreateDirectory"},
{3, &IFileSystem::DeleteDirectory, "DeleteDirectory"}, {3, D<&IFileSystem::DeleteDirectory>, "DeleteDirectory"},
{4, &IFileSystem::DeleteDirectoryRecursively, "DeleteDirectoryRecursively"}, {4, D<&IFileSystem::DeleteDirectoryRecursively>, "DeleteDirectoryRecursively"},
{5, &IFileSystem::RenameFile, "RenameFile"}, {5, D<&IFileSystem::RenameFile>, "RenameFile"},
{6, nullptr, "RenameDirectory"}, {6, nullptr, "RenameDirectory"},
{7, &IFileSystem::GetEntryType, "GetEntryType"}, {7, D<&IFileSystem::GetEntryType>, "GetEntryType"},
{8, &IFileSystem::OpenFile, "OpenFile"}, {8, D<&IFileSystem::OpenFile>, "OpenFile"},
{9, &IFileSystem::OpenDirectory, "OpenDirectory"}, {9, D<&IFileSystem::OpenDirectory>, "OpenDirectory"},
{10, &IFileSystem::Commit, "Commit"}, {10, D<&IFileSystem::Commit>, "Commit"},
{11, &IFileSystem::GetFreeSpaceSize, "GetFreeSpaceSize"}, {11, D<&IFileSystem::GetFreeSpaceSize>, "GetFreeSpaceSize"},
{12, &IFileSystem::GetTotalSpaceSize, "GetTotalSpaceSize"}, {12, D<&IFileSystem::GetTotalSpaceSize>, "GetTotalSpaceSize"},
{13, &IFileSystem::CleanDirectoryRecursively, "CleanDirectoryRecursively"}, {13, D<&IFileSystem::CleanDirectoryRecursively>, "CleanDirectoryRecursively"},
{14, &IFileSystem::GetFileTimeStampRaw, "GetFileTimeStampRaw"}, {14, D<&IFileSystem::GetFileTimeStampRaw>, "GetFileTimeStampRaw"},
{15, nullptr, "QueryEntry"}, {15, nullptr, "QueryEntry"},
{16, &IFileSystem::GetFileSystemAttribute, "GetFileSystemAttribute"}, {16, D<&IFileSystem::GetFileSystemAttribute>, "GetFileSystemAttribute"},
}; };
RegisterHandlers(functions); RegisterHandlers(functions);
} }
void IFileSystem::CreateFile(HLERequestContext& ctx) { Result IFileSystem::CreateFile(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path,
IPC::RequestParser rp{ctx}; s32 option, s64 size) {
LOG_DEBUG(Service_FS, "called. file={}, option=0x{:X}, size=0x{:08X}", path->str, option, size);
const auto file_buffer = ctx.ReadBuffer(); R_RETURN(backend->CreateFile(FileSys::Path(path->str), size));
const std::string name = Common::StringFromBuffer(file_buffer);
const u64 file_mode = rp.Pop<u64>();
const u32 file_size = rp.Pop<u32>();
LOG_DEBUG(Service_FS, "called. file={}, mode=0x{:X}, size=0x{:08X}", name, file_mode,
file_size);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(backend.CreateFile(name, file_size));
} }
void IFileSystem::DeleteFile(HLERequestContext& ctx) { Result IFileSystem::DeleteFile(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
const auto file_buffer = ctx.ReadBuffer(); LOG_DEBUG(Service_FS, "called. file={}", path->str);
const std::string name = Common::StringFromBuffer(file_buffer);
LOG_DEBUG(Service_FS, "called. file={}", name); R_RETURN(backend->DeleteFile(FileSys::Path(path->str)));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(backend.DeleteFile(name));
} }
void IFileSystem::CreateDirectory(HLERequestContext& ctx) { Result IFileSystem::CreateDirectory(
const auto file_buffer = ctx.ReadBuffer(); const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
const std::string name = Common::StringFromBuffer(file_buffer); LOG_DEBUG(Service_FS, "called. directory={}", path->str);
LOG_DEBUG(Service_FS, "called. directory={}", name); R_RETURN(backend->CreateDirectory(FileSys::Path(path->str)));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(backend.CreateDirectory(name));
} }
void IFileSystem::DeleteDirectory(HLERequestContext& ctx) { Result IFileSystem::DeleteDirectory(
const auto file_buffer = ctx.ReadBuffer(); const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
const std::string name = Common::StringFromBuffer(file_buffer); LOG_DEBUG(Service_FS, "called. directory={}", path->str);
LOG_DEBUG(Service_FS, "called. directory={}", name); R_RETURN(backend->DeleteDirectory(FileSys::Path(path->str)));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(backend.DeleteDirectory(name));
} }
void IFileSystem::DeleteDirectoryRecursively(HLERequestContext& ctx) { Result IFileSystem::DeleteDirectoryRecursively(
const auto file_buffer = ctx.ReadBuffer(); const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
const std::string name = Common::StringFromBuffer(file_buffer); LOG_DEBUG(Service_FS, "called. directory={}", path->str);
LOG_DEBUG(Service_FS, "called. directory={}", name); R_RETURN(backend->DeleteDirectoryRecursively(FileSys::Path(path->str)));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(backend.DeleteDirectoryRecursively(name));
} }
void IFileSystem::CleanDirectoryRecursively(HLERequestContext& ctx) { Result IFileSystem::CleanDirectoryRecursively(
const auto file_buffer = ctx.ReadBuffer(); const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
const std::string name = Common::StringFromBuffer(file_buffer); LOG_DEBUG(Service_FS, "called. Directory: {}", path->str);
LOG_DEBUG(Service_FS, "called. Directory: {}", name); R_RETURN(backend->CleanDirectoryRecursively(FileSys::Path(path->str)));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(backend.CleanDirectoryRecursively(name));
} }
void IFileSystem::RenameFile(HLERequestContext& ctx) { Result IFileSystem::RenameFile(
const std::string src_name = Common::StringFromBuffer(ctx.ReadBuffer(0)); const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> old_path,
const std::string dst_name = Common::StringFromBuffer(ctx.ReadBuffer(1)); const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> new_path) {
LOG_DEBUG(Service_FS, "called. file '{}' to file '{}'", old_path->str, new_path->str);
LOG_DEBUG(Service_FS, "called. file '{}' to file '{}'", src_name, dst_name); R_RETURN(backend->RenameFile(FileSys::Path(old_path->str), FileSys::Path(new_path->str)));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(backend.RenameFile(src_name, dst_name));
} }
void IFileSystem::OpenFile(HLERequestContext& ctx) { Result IFileSystem::OpenFile(OutInterface<IFile> out_interface,
IPC::RequestParser rp{ctx}; const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path,
u32 mode) {
const auto file_buffer = ctx.ReadBuffer(); LOG_DEBUG(Service_FS, "called. file={}, mode={}", path->str, mode);
const std::string name = Common::StringFromBuffer(file_buffer);
const auto mode = static_cast<FileSys::OpenMode>(rp.Pop<u32>());
LOG_DEBUG(Service_FS, "called. file={}, mode={}", name, mode);
FileSys::VirtualFile vfs_file{}; FileSys::VirtualFile vfs_file{};
auto result = backend.OpenFile(&vfs_file, name, mode); R_TRY(backend->OpenFile(&vfs_file, FileSys::Path(path->str),
if (result != ResultSuccess) { static_cast<FileSys::OpenMode>(mode)));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result); *out_interface = std::make_shared<IFile>(system, vfs_file);
return; R_SUCCEED();
} }
auto file = std::make_shared<IFile>(system, vfs_file); Result IFileSystem::OpenDirectory(OutInterface<IDirectory> out_interface,
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path,
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; u32 mode) {
rb.Push(ResultSuccess); LOG_DEBUG(Service_FS, "called. directory={}, mode={}", path->str, mode);
rb.PushIpcInterface<IFile>(std::move(file));
}
void IFileSystem::OpenDirectory(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto file_buffer = ctx.ReadBuffer();
const std::string name = Common::StringFromBuffer(file_buffer);
const auto mode = rp.PopRaw<FileSys::OpenDirectoryMode>();
LOG_DEBUG(Service_FS, "called. directory={}, mode={}", name, mode);
FileSys::VirtualDir vfs_dir{}; FileSys::VirtualDir vfs_dir{};
auto result = backend.OpenDirectory(&vfs_dir, name); R_TRY(backend->OpenDirectory(&vfs_dir, FileSys::Path(path->str),
if (result != ResultSuccess) { static_cast<FileSys::OpenDirectoryMode>(mode)));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result); *out_interface = std::make_shared<IDirectory>(system, vfs_dir,
return; static_cast<FileSys::OpenDirectoryMode>(mode));
R_SUCCEED();
} }
auto directory = std::make_shared<IDirectory>(system, vfs_dir, mode); Result IFileSystem::GetEntryType(
Out<u32> out_type, const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; LOG_DEBUG(Service_FS, "called. file={}", path->str);
rb.Push(ResultSuccess);
rb.PushIpcInterface<IDirectory>(std::move(directory));
}
void IFileSystem::GetEntryType(HLERequestContext& ctx) {
const auto file_buffer = ctx.ReadBuffer();
const std::string name = Common::StringFromBuffer(file_buffer);
LOG_DEBUG(Service_FS, "called. file={}", name);
FileSys::DirectoryEntryType vfs_entry_type{}; FileSys::DirectoryEntryType vfs_entry_type{};
auto result = backend.GetEntryType(&vfs_entry_type, name); R_TRY(backend->GetEntryType(&vfs_entry_type, FileSys::Path(path->str)));
if (result != ResultSuccess) {
IPC::ResponseBuilder rb{ctx, 2}; *out_type = static_cast<u32>(vfs_entry_type);
rb.Push(result); R_SUCCEED();
return;
} }
IPC::ResponseBuilder rb{ctx, 3}; Result IFileSystem::Commit() {
rb.Push(ResultSuccess);
rb.Push<u32>(static_cast<u32>(vfs_entry_type));
}
void IFileSystem::Commit(HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called"); LOG_WARNING(Service_FS, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2}; R_SUCCEED();
rb.Push(ResultSuccess);
} }
void IFileSystem::GetFreeSpaceSize(HLERequestContext& ctx) { Result IFileSystem::GetFreeSpaceSize(
Out<s64> out_size, const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
LOG_DEBUG(Service_FS, "called"); LOG_DEBUG(Service_FS, "called");
IPC::ResponseBuilder rb{ctx, 4}; *out_size = size_getter.get_free_size();
rb.Push(ResultSuccess); R_SUCCEED();
rb.Push(size.get_free_size());
} }
void IFileSystem::GetTotalSpaceSize(HLERequestContext& ctx) { Result IFileSystem::GetTotalSpaceSize(
Out<s64> out_size, const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
LOG_DEBUG(Service_FS, "called"); LOG_DEBUG(Service_FS, "called");
IPC::ResponseBuilder rb{ctx, 4}; *out_size = size_getter.get_total_size();
rb.Push(ResultSuccess); R_SUCCEED();
rb.Push(size.get_total_size());
} }
void IFileSystem::GetFileTimeStampRaw(HLERequestContext& ctx) { Result IFileSystem::GetFileTimeStampRaw(
const auto file_buffer = ctx.ReadBuffer(); Out<FileSys::FileTimeStampRaw> out_timestamp,
const std::string name = Common::StringFromBuffer(file_buffer); const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
LOG_WARNING(Service_FS, "(Partial Implementation) called. file={}", path->str);
LOG_WARNING(Service_FS, "(Partial Implementation) called. file={}", name);
FileSys::FileTimeStampRaw vfs_timestamp{}; FileSys::FileTimeStampRaw vfs_timestamp{};
auto result = backend.GetFileTimeStampRaw(&vfs_timestamp, name); R_TRY(backend->GetFileTimeStampRaw(&vfs_timestamp, FileSys::Path(path->str)));
if (result != ResultSuccess) {
IPC::ResponseBuilder rb{ctx, 2}; *out_timestamp = vfs_timestamp;
rb.Push(result); R_SUCCEED();
return;
} }
IPC::ResponseBuilder rb{ctx, 10}; Result IFileSystem::GetFileSystemAttribute(Out<FileSys::FileSystemAttribute> out_attribute) {
rb.Push(ResultSuccess);
rb.PushRaw(vfs_timestamp);
}
void IFileSystem::GetFileSystemAttribute(HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called"); LOG_WARNING(Service_FS, "(STUBBED) called");
struct FileSystemAttribute { FileSys::FileSystemAttribute savedata_attribute{};
u8 dir_entry_name_length_max_defined;
u8 file_entry_name_length_max_defined;
u8 dir_path_name_length_max_defined;
u8 file_path_name_length_max_defined;
INSERT_PADDING_BYTES_NOINIT(0x5);
u8 utf16_dir_entry_name_length_max_defined;
u8 utf16_file_entry_name_length_max_defined;
u8 utf16_dir_path_name_length_max_defined;
u8 utf16_file_path_name_length_max_defined;
INSERT_PADDING_BYTES_NOINIT(0x18);
s32 dir_entry_name_length_max;
s32 file_entry_name_length_max;
s32 dir_path_name_length_max;
s32 file_path_name_length_max;
INSERT_PADDING_WORDS_NOINIT(0x5);
s32 utf16_dir_entry_name_length_max;
s32 utf16_file_entry_name_length_max;
s32 utf16_dir_path_name_length_max;
s32 utf16_file_path_name_length_max;
INSERT_PADDING_WORDS_NOINIT(0x18);
INSERT_PADDING_WORDS_NOINIT(0x1);
};
static_assert(sizeof(FileSystemAttribute) == 0xc0, "FileSystemAttribute has incorrect size");
FileSystemAttribute savedata_attribute{};
savedata_attribute.dir_entry_name_length_max_defined = true; savedata_attribute.dir_entry_name_length_max_defined = true;
savedata_attribute.file_entry_name_length_max_defined = true; savedata_attribute.file_entry_name_length_max_defined = true;
savedata_attribute.dir_entry_name_length_max = 0x40; savedata_attribute.dir_entry_name_length_max = 0x40;
savedata_attribute.file_entry_name_length_max = 0x40; savedata_attribute.file_entry_name_length_max = 0x40;
IPC::ResponseBuilder rb{ctx, 50}; *out_attribute = savedata_attribute;
rb.Push(ResultSuccess); R_SUCCEED();
rb.PushRaw(savedata_attribute);
} }
} // namespace Service::FileSystem } // namespace Service::FileSystem

View file

@ -3,36 +3,58 @@
#pragma once #pragma once
#include "common/common_funcs.h"
#include "core/file_sys/fs_filesystem.h"
#include "core/file_sys/fsa/fs_i_filesystem.h"
#include "core/file_sys/vfs/vfs.h" #include "core/file_sys/vfs/vfs.h"
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/filesystem/fsp/fsp_util.h" #include "core/hle/service/filesystem/fsp/fsp_util.h"
#include "core/hle/service/service.h" #include "core/hle/service/service.h"
namespace FileSys::Sf {
struct Path;
}
namespace Service::FileSystem { namespace Service::FileSystem {
class IFile;
class IDirectory;
class IFileSystem final : public ServiceFramework<IFileSystem> { class IFileSystem final : public ServiceFramework<IFileSystem> {
public: public:
explicit IFileSystem(Core::System& system_, FileSys::VirtualDir backend_, SizeGetter size_); explicit IFileSystem(Core::System& system_, FileSys::VirtualDir dir_, SizeGetter size_getter_);
void CreateFile(HLERequestContext& ctx); Result CreateFile(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path, s32 option,
void DeleteFile(HLERequestContext& ctx); s64 size);
void CreateDirectory(HLERequestContext& ctx); Result DeleteFile(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
void DeleteDirectory(HLERequestContext& ctx); Result CreateDirectory(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
void DeleteDirectoryRecursively(HLERequestContext& ctx); Result DeleteDirectory(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
void CleanDirectoryRecursively(HLERequestContext& ctx); Result DeleteDirectoryRecursively(
void RenameFile(HLERequestContext& ctx); const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
void OpenFile(HLERequestContext& ctx); Result CleanDirectoryRecursively(
void OpenDirectory(HLERequestContext& ctx); const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
void GetEntryType(HLERequestContext& ctx); Result RenameFile(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> old_path,
void Commit(HLERequestContext& ctx); const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> new_path);
void GetFreeSpaceSize(HLERequestContext& ctx); Result OpenFile(OutInterface<IFile> out_interface,
void GetTotalSpaceSize(HLERequestContext& ctx); const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path, u32 mode);
void GetFileTimeStampRaw(HLERequestContext& ctx); Result OpenDirectory(OutInterface<IDirectory> out_interface,
void GetFileSystemAttribute(HLERequestContext& ctx); const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path,
u32 mode);
Result GetEntryType(Out<u32> out_type,
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
Result Commit();
Result GetFreeSpaceSize(Out<s64> out_size,
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
Result GetTotalSpaceSize(Out<s64> out_size,
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
Result GetFileTimeStampRaw(Out<FileSys::FileTimeStampRaw> out_timestamp,
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
Result GetFileSystemAttribute(Out<FileSys::FileSystemAttribute> out_attribute);
private: private:
VfsDirectoryServiceWrapper backend; std::unique_ptr<FileSys::Fsa::IFileSystem> backend;
SizeGetter size; SizeGetter size_getter;
}; };
} // namespace Service::FileSystem } // namespace Service::FileSystem

View file

@ -261,7 +261,9 @@ void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) {
case Stage::Geometry: case Stage::Geometry:
execution_model = spv::ExecutionModel::Geometry; execution_model = spv::ExecutionModel::Geometry;
ctx.AddCapability(spv::Capability::Geometry); ctx.AddCapability(spv::Capability::Geometry);
if (ctx.profile.support_geometry_streams) {
ctx.AddCapability(spv::Capability::GeometryStreams); ctx.AddCapability(spv::Capability::GeometryStreams);
}
switch (ctx.runtime_info.input_topology) { switch (ctx.runtime_info.input_topology) {
case InputTopology::Points: case InputTopology::Points:
ctx.AddExecutionMode(main, spv::ExecutionMode::InputPoints); ctx.AddExecutionMode(main, spv::ExecutionMode::InputPoints);

View file

@ -129,7 +129,9 @@ void EmitEmitVertex(EmitContext& ctx, const IR::Value& stream) {
if (ctx.runtime_info.convert_depth_mode && !ctx.profile.support_native_ndc) { if (ctx.runtime_info.convert_depth_mode && !ctx.profile.support_native_ndc) {
ConvertDepthMode(ctx); ConvertDepthMode(ctx);
} }
if (stream.IsImmediate()) { if (!ctx.profile.support_geometry_streams) {
throw NotImplementedException("Geometry streams");
} else if (stream.IsImmediate()) {
ctx.OpEmitStreamVertex(ctx.Def(stream)); ctx.OpEmitStreamVertex(ctx.Def(stream));
} else { } else {
LOG_WARNING(Shader_SPIRV, "Stream is not immediate"); LOG_WARNING(Shader_SPIRV, "Stream is not immediate");
@ -140,7 +142,9 @@ void EmitEmitVertex(EmitContext& ctx, const IR::Value& stream) {
} }
void EmitEndPrimitive(EmitContext& ctx, const IR::Value& stream) { void EmitEndPrimitive(EmitContext& ctx, const IR::Value& stream) {
if (stream.IsImmediate()) { if (!ctx.profile.support_geometry_streams) {
throw NotImplementedException("Geometry streams");
} else if (stream.IsImmediate()) {
ctx.OpEndStreamPrimitive(ctx.Def(stream)); ctx.OpEndStreamPrimitive(ctx.Def(stream));
} else { } else {
LOG_WARNING(Shader_SPIRV, "Stream is not immediate"); LOG_WARNING(Shader_SPIRV, "Stream is not immediate");

View file

@ -44,6 +44,7 @@ struct Profile {
bool support_gl_derivative_control{}; bool support_gl_derivative_control{};
bool support_scaled_attributes{}; bool support_scaled_attributes{};
bool support_multi_viewport{}; bool support_multi_viewport{};
bool support_geometry_streams{};
bool warp_size_potentially_larger_than_guest{}; bool warp_size_potentially_larger_than_guest{};

View file

@ -1488,7 +1488,10 @@ void BufferCache<P>::ImmediateUploadMemory([[maybe_unused]] Buffer& buffer,
std::span<const u8> upload_span; std::span<const u8> upload_span;
const DAddr device_addr = buffer.CpuAddr() + copy.dst_offset; const DAddr device_addr = buffer.CpuAddr() + copy.dst_offset;
if (IsRangeGranular(device_addr, copy.size)) { if (IsRangeGranular(device_addr, copy.size)) {
upload_span = std::span(device_memory.GetPointer<u8>(device_addr), copy.size); auto* const ptr = device_memory.GetPointer<u8>(device_addr);
if (ptr != nullptr) {
upload_span = std::span(ptr, copy.size);
}
} else { } else {
if (immediate_buffer.empty()) { if (immediate_buffer.empty()) {
immediate_buffer = ImmediateBuffer(largest_copy); immediate_buffer = ImmediateBuffer(largest_copy);

View file

@ -352,6 +352,7 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
.support_native_ndc = device.IsExtDepthClipControlSupported(), .support_native_ndc = device.IsExtDepthClipControlSupported(),
.support_scaled_attributes = !device.MustEmulateScaledFormats(), .support_scaled_attributes = !device.MustEmulateScaledFormats(),
.support_multi_viewport = device.SupportsMultiViewport(), .support_multi_viewport = device.SupportsMultiViewport(),
.support_geometry_streams = device.AreTransformFeedbackGeometryStreamsSupported(),
.warp_size_potentially_larger_than_guest = device.IsWarpSizePotentiallyBiggerThanGuest(), .warp_size_potentially_larger_than_guest = device.IsWarpSizePotentiallyBiggerThanGuest(),

View file

@ -1064,8 +1064,6 @@ public:
} }
}); });
} }
auto* ptr = device_memory.GetPointer<u8>(new_query->dependant_address);
ASSERT(ptr != nullptr);
new_query->dependant_manage = must_manage_dependance; new_query->dependant_manage = must_manage_dependance;
pending_flush_queries.push_back(index); pending_flush_queries.push_back(index);
@ -1104,10 +1102,12 @@ public:
tfb_streamer.Free(query->dependant_index); tfb_streamer.Free(query->dependant_index);
} else { } else {
u8* pointer = device_memory.GetPointer<u8>(query->dependant_address); u8* pointer = device_memory.GetPointer<u8>(query->dependant_address);
if (pointer != nullptr) {
u32 result; u32 result;
std::memcpy(&result, pointer, sizeof(u32)); std::memcpy(&result, pointer, sizeof(u32));
num_vertices = static_cast<u64>(result) / query->stride; num_vertices = static_cast<u64>(result) / query->stride;
} }
}
query->value = [&]() -> u64 { query->value = [&]() -> u64 {
switch (query->topology) { switch (query->topology) {
case Maxwell3D::Regs::PrimitiveTopology::Points: case Maxwell3D::Regs::PrimitiveTopology::Points:
@ -1360,7 +1360,9 @@ bool QueryCacheRuntime::HostConditionalRenderingCompareValues(VideoCommon::Looku
const auto check_value = [&](DAddr address) { const auto check_value = [&](DAddr address) {
u8* ptr = impl->device_memory.GetPointer<u8>(address); u8* ptr = impl->device_memory.GetPointer<u8>(address);
u64 value{}; u64 value{};
if (ptr != nullptr) {
std::memcpy(&value, ptr, sizeof(value)); std::memcpy(&value, ptr, sizeof(value));
}
return value == 0; return value == 0;
}; };
std::array<VideoCommon::LookupData*, 2> objects{&object_1, &object_2}; std::array<VideoCommon::LookupData*, 2> objects{&object_1, &object_2};

View file

@ -1054,37 +1054,16 @@ void RasterizerVulkan::UpdateDepthBias(Tegra::Engines::Maxwell3D::Regs& regs) {
regs.zeta.format == Tegra::DepthFormat::X8Z24_UNORM || regs.zeta.format == Tegra::DepthFormat::X8Z24_UNORM ||
regs.zeta.format == Tegra::DepthFormat::S8Z24_UNORM || regs.zeta.format == Tegra::DepthFormat::S8Z24_UNORM ||
regs.zeta.format == Tegra::DepthFormat::V8Z24_UNORM; regs.zeta.format == Tegra::DepthFormat::V8Z24_UNORM;
bool force_unorm = ([&] { if (is_d24 && !device.SupportsD24DepthBuffer() &&
if (!is_d24 || device.SupportsD24DepthBuffer()) { Settings::values.renderer_amdvlk_depth_bias_workaround) {
return false;
}
if (device.IsExtDepthBiasControlSupported()) {
return true;
}
if (!Settings::values.renderer_amdvlk_depth_bias_workaround) {
return false;
}
// the base formulas can be obtained from here: // the base formulas can be obtained from here:
// https://docs.microsoft.com/en-us/windows/win32/direct3d11/d3d10-graphics-programming-guide-output-merger-stage-depth-bias // https://docs.microsoft.com/en-us/windows/win32/direct3d11/d3d10-graphics-programming-guide-output-merger-stage-depth-bias
const double rescale_factor = const double rescale_factor =
static_cast<double>(1ULL << (32 - 24)) / (static_cast<double>(0x1.ep+127)); static_cast<double>(1ULL << (32 - 24)) / (static_cast<double>(0x1.ep+127));
units = static_cast<float>(static_cast<double>(units) * rescale_factor); units = static_cast<float>(static_cast<double>(units) * rescale_factor);
return false;
})();
scheduler.Record([constant = units, clamp = regs.depth_bias_clamp,
factor = regs.slope_scale_depth_bias, force_unorm,
precise = device.HasExactDepthBiasControl()](vk::CommandBuffer cmdbuf) {
if (force_unorm) {
VkDepthBiasRepresentationInfoEXT info{
.sType = VK_STRUCTURE_TYPE_DEPTH_BIAS_REPRESENTATION_INFO_EXT,
.pNext = nullptr,
.depthBiasRepresentation =
VK_DEPTH_BIAS_REPRESENTATION_LEAST_REPRESENTABLE_VALUE_FORCE_UNORM_EXT,
.depthBiasExact = precise ? VK_TRUE : VK_FALSE,
};
cmdbuf.SetDepthBias(constant, clamp, factor, &info);
return;
} }
scheduler.Record([constant = units, clamp = regs.depth_bias_clamp,
factor = regs.slope_scale_depth_bias](vk::CommandBuffer cmdbuf) {
cmdbuf.SetDepthBias(constant, clamp, factor); cmdbuf.SetDepthBias(constant, clamp, factor);
}); });
} }

View file

@ -1137,13 +1137,6 @@ void Device::RemoveUnsuitableExtensions() {
RemoveExtensionFeatureIfUnsuitable(extensions.custom_border_color, features.custom_border_color, RemoveExtensionFeatureIfUnsuitable(extensions.custom_border_color, features.custom_border_color,
VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME); VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME);
// VK_EXT_depth_bias_control
extensions.depth_bias_control =
features.depth_bias_control.depthBiasControl &&
features.depth_bias_control.leastRepresentableValueForceUnormRepresentation;
RemoveExtensionFeatureIfUnsuitable(extensions.depth_bias_control, features.depth_bias_control,
VK_EXT_DEPTH_BIAS_CONTROL_EXTENSION_NAME);
// VK_EXT_depth_clip_control // VK_EXT_depth_clip_control
extensions.depth_clip_control = features.depth_clip_control.depthClipControl; extensions.depth_clip_control = features.depth_clip_control.depthClipControl;
RemoveExtensionFeatureIfUnsuitable(extensions.depth_clip_control, features.depth_clip_control, RemoveExtensionFeatureIfUnsuitable(extensions.depth_clip_control, features.depth_clip_control,
@ -1297,10 +1290,6 @@ u64 Device::GetDeviceMemoryUsage() const {
} }
void Device::CollectPhysicalMemoryInfo() { void Device::CollectPhysicalMemoryInfo() {
// Account for resolution scaling in memory limits
const size_t normal_memory = 6_GiB;
const size_t scaler_memory = 1_GiB * Settings::values.resolution_info.ScaleUp(1);
// Calculate limits using memory budget // Calculate limits using memory budget
VkPhysicalDeviceMemoryBudgetPropertiesEXT budget{}; VkPhysicalDeviceMemoryBudgetPropertiesEXT budget{};
budget.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT; budget.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT;
@ -1331,7 +1320,15 @@ void Device::CollectPhysicalMemoryInfo() {
if (!is_integrated) { if (!is_integrated) {
const u64 reserve_memory = std::min<u64>(device_access_memory / 8, 1_GiB); const u64 reserve_memory = std::min<u64>(device_access_memory / 8, 1_GiB);
device_access_memory -= reserve_memory; device_access_memory -= reserve_memory;
device_access_memory = std::min<u64>(device_access_memory, normal_memory + scaler_memory);
if (Settings::values.vram_usage_mode.GetValue() != Settings::VramUsageMode::Aggressive) {
// Account for resolution scaling in memory limits
const size_t normal_memory = 6_GiB;
const size_t scaler_memory = 1_GiB * Settings::values.resolution_info.ScaleUp(1);
device_access_memory =
std::min<u64>(device_access_memory, normal_memory + scaler_memory);
}
return; return;
} }
const s64 available_memory = static_cast<s64>(device_access_memory - device_initial_usage); const s64 available_memory = static_cast<s64>(device_access_memory - device_initial_usage);

View file

@ -41,7 +41,6 @@ VK_DEFINE_HANDLE(VmaAllocator)
// Define all features which may be used by the implementation and require an extension here. // Define all features which may be used by the implementation and require an extension here.
#define FOR_EACH_VK_FEATURE_EXT(FEATURE) \ #define FOR_EACH_VK_FEATURE_EXT(FEATURE) \
FEATURE(EXT, CustomBorderColor, CUSTOM_BORDER_COLOR, custom_border_color) \ FEATURE(EXT, CustomBorderColor, CUSTOM_BORDER_COLOR, custom_border_color) \
FEATURE(EXT, DepthBiasControl, DEPTH_BIAS_CONTROL, depth_bias_control) \
FEATURE(EXT, DepthClipControl, DEPTH_CLIP_CONTROL, depth_clip_control) \ FEATURE(EXT, DepthClipControl, DEPTH_CLIP_CONTROL, depth_clip_control) \
FEATURE(EXT, ExtendedDynamicState, EXTENDED_DYNAMIC_STATE, extended_dynamic_state) \ FEATURE(EXT, ExtendedDynamicState, EXTENDED_DYNAMIC_STATE, extended_dynamic_state) \
FEATURE(EXT, ExtendedDynamicState2, EXTENDED_DYNAMIC_STATE_2, extended_dynamic_state2) \ FEATURE(EXT, ExtendedDynamicState2, EXTENDED_DYNAMIC_STATE_2, extended_dynamic_state2) \
@ -97,7 +96,6 @@ VK_DEFINE_HANDLE(VmaAllocator)
#define FOR_EACH_VK_RECOMMENDED_EXTENSION(EXTENSION_NAME) \ #define FOR_EACH_VK_RECOMMENDED_EXTENSION(EXTENSION_NAME) \
EXTENSION_NAME(VK_EXT_CONDITIONAL_RENDERING_EXTENSION_NAME) \ EXTENSION_NAME(VK_EXT_CONDITIONAL_RENDERING_EXTENSION_NAME) \
EXTENSION_NAME(VK_EXT_CONSERVATIVE_RASTERIZATION_EXTENSION_NAME) \ EXTENSION_NAME(VK_EXT_CONSERVATIVE_RASTERIZATION_EXTENSION_NAME) \
EXTENSION_NAME(VK_EXT_DEPTH_BIAS_CONTROL_EXTENSION_NAME) \
EXTENSION_NAME(VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME) \ EXTENSION_NAME(VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME) \
EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME) \ EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME) \
EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME) \ EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME) \
@ -150,9 +148,6 @@ VK_DEFINE_HANDLE(VmaAllocator)
// Define features where the absence of the feature may result in a degraded experience. // Define features where the absence of the feature may result in a degraded experience.
#define FOR_EACH_VK_RECOMMENDED_FEATURE(FEATURE_NAME) \ #define FOR_EACH_VK_RECOMMENDED_FEATURE(FEATURE_NAME) \
FEATURE_NAME(custom_border_color, customBorderColors) \ FEATURE_NAME(custom_border_color, customBorderColors) \
FEATURE_NAME(depth_bias_control, depthBiasControl) \
FEATURE_NAME(depth_bias_control, leastRepresentableValueForceUnormRepresentation) \
FEATURE_NAME(depth_bias_control, depthBiasExact) \
FEATURE_NAME(extended_dynamic_state, extendedDynamicState) \ FEATURE_NAME(extended_dynamic_state, extendedDynamicState) \
FEATURE_NAME(format_a4b4g4r4, formatA4B4G4R4) \ FEATURE_NAME(format_a4b4g4r4, formatA4B4G4R4) \
FEATURE_NAME(index_type_uint8, indexTypeUint8) \ FEATURE_NAME(index_type_uint8, indexTypeUint8) \
@ -479,11 +474,6 @@ public:
return extensions.depth_clip_control; return extensions.depth_clip_control;
} }
/// Returns true if the device supports VK_EXT_depth_bias_control.
bool IsExtDepthBiasControlSupported() const {
return extensions.depth_bias_control;
}
/// Returns true if the device supports VK_EXT_shader_viewport_index_layer. /// Returns true if the device supports VK_EXT_shader_viewport_index_layer.
bool IsExtShaderViewportIndexLayerSupported() const { bool IsExtShaderViewportIndexLayerSupported() const {
return extensions.shader_viewport_index_layer; return extensions.shader_viewport_index_layer;
@ -499,6 +489,11 @@ public:
return extensions.transform_feedback; return extensions.transform_feedback;
} }
/// Returns true if the device supports VK_EXT_transform_feedback properly.
bool AreTransformFeedbackGeometryStreamsSupported() const {
return features.transform_feedback.geometryStreams;
}
/// Returns true if the device supports VK_EXT_custom_border_color. /// Returns true if the device supports VK_EXT_custom_border_color.
bool IsExtCustomBorderColorSupported() const { bool IsExtCustomBorderColorSupported() const {
return extensions.custom_border_color; return extensions.custom_border_color;
@ -649,10 +644,6 @@ public:
return features.robustness2.nullDescriptor; return features.robustness2.nullDescriptor;
} }
bool HasExactDepthBiasControl() const {
return features.depth_bias_control.depthBiasExact;
}
u32 GetMaxVertexInputAttributes() const { u32 GetMaxVertexInputAttributes() const {
return properties.properties.limits.maxVertexInputAttributes; return properties.properties.limits.maxVertexInputAttributes;
} }

View file

@ -164,6 +164,11 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
"the emulator to decompress to an intermediate format any card supports, RGBA8.\n" "the emulator to decompress to an intermediate format any card supports, RGBA8.\n"
"This option recompresses RGBA8 to either the BC1 or BC3 format, saving VRAM but " "This option recompresses RGBA8 to either the BC1 or BC3 format, saving VRAM but "
"negatively affecting image quality.")); "negatively affecting image quality."));
INSERT(Settings, vram_usage_mode, tr("VRAM Usage Mode:"),
tr("Selects whether the emulator should prefer to conserve memory or make maximum usage "
"of available video memory for performance. Has no effect on integrated graphics. "
"Aggressive mode may severely impact the performance of other applications such as "
"recording software."));
INSERT( INSERT(
Settings, vsync_mode, tr("VSync Mode:"), Settings, vsync_mode, tr("VSync Mode:"),
tr("FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen " tr("FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen "
@ -315,6 +320,11 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent) {
PAIR(AstcRecompression, Bc1, tr("BC1 (Low quality)")), PAIR(AstcRecompression, Bc1, tr("BC1 (Low quality)")),
PAIR(AstcRecompression, Bc3, tr("BC3 (Medium quality)")), PAIR(AstcRecompression, Bc3, tr("BC3 (Medium quality)")),
}}); }});
translations->insert({Settings::EnumMetadata<Settings::VramUsageMode>::Index(),
{
PAIR(VramUsageMode, Conservative, tr("Conservative")),
PAIR(VramUsageMode, Aggressive, tr("Aggressive")),
}});
translations->insert({Settings::EnumMetadata<Settings::RendererBackend>::Index(), translations->insert({Settings::EnumMetadata<Settings::RendererBackend>::Index(),
{ {
#ifdef HAS_OPENGL #ifdef HAS_OPENGL