summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/core/core.cpp7
-rw-r--r--src/core/file_sys/fsmitm_romfsbuild.cpp84
-rw-r--r--src/core/file_sys/patch_manager.cpp8
-rw-r--r--src/core/file_sys/registered_cache.cpp3
-rw-r--r--src/core/file_sys/romfs.cpp5
-rw-r--r--src/core/file_sys/vfs_cached.cpp6
-rw-r--r--src/core/file_sys/vfs_cached.h2
-rw-r--r--src/core/file_sys/vfs_concat.cpp27
-rw-r--r--src/core/file_sys/vfs_concat.h12
-rw-r--r--src/core/file_sys/vfs_layered.cpp8
-rw-r--r--src/core/hle/service/acc/acc.cpp12
-rw-r--r--src/core/hle/service/hle_ipc.cpp58
-rw-r--r--src/core/hle/service/hle_ipc.h6
-rw-r--r--src/core/hle/service/mii/types/core_data.cpp1
-rw-r--r--src/core/hle/service/prepo/prepo.cpp40
-rw-r--r--src/tests/common/unique_function.cpp6
-rw-r--r--src/video_core/buffer_cache/buffer_cache_base.h4
-rw-r--r--src/video_core/host_shaders/CMakeLists.txt1
-rw-r--r--src/video_core/host_shaders/convert_abgr8_to_d32f.frag15
-rw-r--r--src/video_core/host_shaders/convert_d24s8_to_abgr8.frag8
-rw-r--r--src/video_core/host_shaders/convert_d32f_to_abgr8.frag2
-rw-r--r--src/video_core/host_shaders/convert_s8d24_to_abgr8.frag8
-rw-r--r--src/video_core/renderer_vulkan/blit_image.cpp9
-rw-r--r--src/video_core/renderer_vulkan/blit_image.h4
-rw-r--r--src/video_core/renderer_vulkan/vk_query_cache.cpp1
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp13
-rw-r--r--src/video_core/renderer_vulkan/vk_render_pass_cache.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp23
-rw-r--r--src/video_core/texture_cache/formatter.cpp8
-rw-r--r--src/video_core/texture_cache/samples_helper.h2
30 files changed, 237 insertions, 148 deletions
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 0ab2e3b76..d7e2efbd7 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -116,11 +116,8 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
}
}
- if (concat.empty()) {
- return nullptr;
- }
-
- return FileSys::ConcatenatedVfsFile::MakeConcatenatedFile(concat, dir->GetName());
+ return FileSys::ConcatenatedVfsFile::MakeConcatenatedFile(dir->GetName(),
+ std::move(concat));
}
if (Common::FS::IsDir(path)) {
diff --git a/src/core/file_sys/fsmitm_romfsbuild.cpp b/src/core/file_sys/fsmitm_romfsbuild.cpp
index e39c7b62b..f1d3e4129 100644
--- a/src/core/file_sys/fsmitm_romfsbuild.cpp
+++ b/src/core/file_sys/fsmitm_romfsbuild.cpp
@@ -107,62 +107,56 @@ static u64 romfs_get_hash_table_count(u64 num_entries) {
void RomFSBuildContext::VisitDirectory(VirtualDir romfs_dir, VirtualDir ext_dir,
std::shared_ptr<RomFSBuildDirectoryContext> parent) {
- std::vector<std::shared_ptr<RomFSBuildDirectoryContext>> child_dirs;
+ for (auto& child_romfs_file : romfs_dir->GetFiles()) {
+ const auto name = child_romfs_file->GetName();
+ const auto child = std::make_shared<RomFSBuildFileContext>();
+ // Set child's path.
+ child->cur_path_ofs = parent->path_len + 1;
+ child->path_len = child->cur_path_ofs + static_cast<u32>(name.size());
+ child->path = parent->path + "/" + name;
+
+ if (ext_dir != nullptr && ext_dir->GetFile(name + ".stub") != nullptr) {
+ continue;
+ }
- const auto entries = romfs_dir->GetEntries();
+ // Sanity check on path_len
+ ASSERT(child->path_len < FS_MAX_PATH);
- for (const auto& kv : entries) {
- if (kv.second == VfsEntryType::Directory) {
- const auto child = std::make_shared<RomFSBuildDirectoryContext>();
- // Set child's path.
- child->cur_path_ofs = parent->path_len + 1;
- child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size());
- child->path = parent->path + "/" + kv.first;
+ child->source = std::move(child_romfs_file);
- if (ext_dir != nullptr && ext_dir->GetFile(kv.first + ".stub") != nullptr) {
- continue;
+ if (ext_dir != nullptr) {
+ if (const auto ips = ext_dir->GetFile(name + ".ips")) {
+ if (auto patched = PatchIPS(child->source, ips)) {
+ child->source = std::move(patched);
+ }
}
+ }
- // Sanity check on path_len
- ASSERT(child->path_len < FS_MAX_PATH);
-
- if (AddDirectory(parent, child)) {
- child_dirs.push_back(child);
- }
- } else {
- const auto child = std::make_shared<RomFSBuildFileContext>();
- // Set child's path.
- child->cur_path_ofs = parent->path_len + 1;
- child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size());
- child->path = parent->path + "/" + kv.first;
-
- if (ext_dir != nullptr && ext_dir->GetFile(kv.first + ".stub") != nullptr) {
- continue;
- }
+ child->size = child->source->GetSize();
- // Sanity check on path_len
- ASSERT(child->path_len < FS_MAX_PATH);
+ AddFile(parent, child);
+ }
- child->source = romfs_dir->GetFile(kv.first);
+ for (auto& child_romfs_dir : romfs_dir->GetSubdirectories()) {
+ const auto name = child_romfs_dir->GetName();
+ const auto child = std::make_shared<RomFSBuildDirectoryContext>();
+ // Set child's path.
+ child->cur_path_ofs = parent->path_len + 1;
+ child->path_len = child->cur_path_ofs + static_cast<u32>(name.size());
+ child->path = parent->path + "/" + name;
- if (ext_dir != nullptr) {
- if (const auto ips = ext_dir->GetFile(kv.first + ".ips")) {
- if (auto patched = PatchIPS(child->source, ips)) {
- child->source = std::move(patched);
- }
- }
- }
+ if (ext_dir != nullptr && ext_dir->GetFile(name + ".stub") != nullptr) {
+ continue;
+ }
- child->size = child->source->GetSize();
+ // Sanity check on path_len
+ ASSERT(child->path_len < FS_MAX_PATH);
- AddFile(parent, child);
+ if (!AddDirectory(parent, child)) {
+ continue;
}
- }
- for (auto& child : child_dirs) {
- auto subdir_name = std::string_view(child->path).substr(child->cur_path_ofs);
- auto child_romfs_dir = romfs_dir->GetSubdirectory(subdir_name);
- auto child_ext_dir = ext_dir != nullptr ? ext_dir->GetSubdirectory(subdir_name) : nullptr;
+ auto child_ext_dir = ext_dir != nullptr ? ext_dir->GetSubdirectory(name) : nullptr;
this->VisitDirectory(child_romfs_dir, child_ext_dir, child);
}
}
@@ -293,7 +287,7 @@ std::multimap<u64, VirtualFile> RomFSBuildContext::Build() {
cur_entry.name_size = name_size;
- out.emplace(cur_file->offset + ROMFS_FILEPARTITION_OFS, cur_file->source);
+ out.emplace(cur_file->offset + ROMFS_FILEPARTITION_OFS, std::move(cur_file->source));
std::memcpy(file_table.data() + cur_file->entry_offset, &cur_entry, sizeof(RomFSFileEntry));
std::memset(file_table.data() + cur_file->entry_offset + sizeof(RomFSFileEntry), 0,
Common::AlignUp(cur_entry.name_size, 4));
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index 8e475f25a..0bca05587 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -377,16 +377,16 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t
auto romfs_dir = FindSubdirectoryCaseless(subdir, "romfs");
if (romfs_dir != nullptr)
- layers.push_back(std::make_shared<CachedVfsDirectory>(romfs_dir));
+ layers.emplace_back(std::make_shared<CachedVfsDirectory>(std::move(romfs_dir)));
auto ext_dir = FindSubdirectoryCaseless(subdir, "romfs_ext");
if (ext_dir != nullptr)
- layers_ext.push_back(std::make_shared<CachedVfsDirectory>(ext_dir));
+ layers_ext.emplace_back(std::make_shared<CachedVfsDirectory>(std::move(ext_dir)));
if (type == ContentRecordType::HtmlDocument) {
auto manual_dir = FindSubdirectoryCaseless(subdir, "manual_html");
if (manual_dir != nullptr)
- layers.push_back(std::make_shared<CachedVfsDirectory>(manual_dir));
+ layers.emplace_back(std::make_shared<CachedVfsDirectory>(std::move(manual_dir)));
}
}
@@ -400,7 +400,7 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t
return;
}
- layers.push_back(std::move(extracted));
+ layers.emplace_back(std::move(extracted));
auto layered = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers));
if (layered == nullptr) {
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index 04da93d5c..1cc77ad14 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -322,7 +322,8 @@ VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& open_di
return nullptr;
}
- return ConcatenatedVfsFile::MakeConcatenatedFile(concat, concat.front()->GetName());
+ auto name = concat.front()->GetName();
+ return ConcatenatedVfsFile::MakeConcatenatedFile(std::move(name), std::move(concat));
}
VirtualFile RegisteredCache::GetFileAtID(NcaID id) const {
diff --git a/src/core/file_sys/romfs.cpp b/src/core/file_sys/romfs.cpp
index 614da2130..1c580de57 100644
--- a/src/core/file_sys/romfs.cpp
+++ b/src/core/file_sys/romfs.cpp
@@ -133,7 +133,7 @@ VirtualDir ExtractRomFS(VirtualFile file, RomFSExtractionType type) {
out = out->GetSubdirectories().front();
}
- return std::make_shared<CachedVfsDirectory>(out);
+ return std::make_shared<CachedVfsDirectory>(std::move(out));
}
VirtualFile CreateRomFS(VirtualDir dir, VirtualDir ext) {
@@ -141,8 +141,7 @@ VirtualFile CreateRomFS(VirtualDir dir, VirtualDir ext) {
return nullptr;
RomFSBuildContext ctx{dir, ext};
- auto file_map = ctx.Build();
- return ConcatenatedVfsFile::MakeConcatenatedFile(0, file_map, dir->GetName());
+ return ConcatenatedVfsFile::MakeConcatenatedFile(0, dir->GetName(), ctx.Build());
}
} // namespace FileSys
diff --git a/src/core/file_sys/vfs_cached.cpp b/src/core/file_sys/vfs_cached.cpp
index c3154ee81..7ee5300e5 100644
--- a/src/core/file_sys/vfs_cached.cpp
+++ b/src/core/file_sys/vfs_cached.cpp
@@ -6,13 +6,13 @@
namespace FileSys {
-CachedVfsDirectory::CachedVfsDirectory(VirtualDir& source_dir)
+CachedVfsDirectory::CachedVfsDirectory(VirtualDir&& source_dir)
: name(source_dir->GetName()), parent(source_dir->GetParentDirectory()) {
for (auto& dir : source_dir->GetSubdirectories()) {
- dirs.emplace(dir->GetName(), std::make_shared<CachedVfsDirectory>(dir));
+ dirs.emplace(dir->GetName(), std::make_shared<CachedVfsDirectory>(std::move(dir)));
}
for (auto& file : source_dir->GetFiles()) {
- files.emplace(file->GetName(), file);
+ files.emplace(file->GetName(), std::move(file));
}
}
diff --git a/src/core/file_sys/vfs_cached.h b/src/core/file_sys/vfs_cached.h
index 113acac12..1e5300784 100644
--- a/src/core/file_sys/vfs_cached.h
+++ b/src/core/file_sys/vfs_cached.h
@@ -11,7 +11,7 @@ namespace FileSys {
class CachedVfsDirectory : public ReadOnlyVfsDirectory {
public:
- CachedVfsDirectory(VirtualDir& source_directory);
+ CachedVfsDirectory(VirtualDir&& source_directory);
~CachedVfsDirectory() override;
VirtualFile GetFile(std::string_view file_name) const override;
diff --git a/src/core/file_sys/vfs_concat.cpp b/src/core/file_sys/vfs_concat.cpp
index 311a59e5f..168b9cbec 100644
--- a/src/core/file_sys/vfs_concat.cpp
+++ b/src/core/file_sys/vfs_concat.cpp
@@ -10,7 +10,7 @@
namespace FileSys {
-ConcatenatedVfsFile::ConcatenatedVfsFile(ConcatenationMap&& concatenation_map_, std::string&& name_)
+ConcatenatedVfsFile::ConcatenatedVfsFile(std::string&& name_, ConcatenationMap&& concatenation_map_)
: concatenation_map(std::move(concatenation_map_)), name(std::move(name_)) {
DEBUG_ASSERT(this->VerifyContinuity());
}
@@ -30,8 +30,8 @@ bool ConcatenatedVfsFile::VerifyContinuity() const {
ConcatenatedVfsFile::~ConcatenatedVfsFile() = default;
-VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(const std::vector<VirtualFile>& files,
- std::string&& name) {
+VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(std::string&& name,
+ std::vector<VirtualFile>&& files) {
// Fold trivial cases.
if (files.empty()) {
return nullptr;
@@ -46,20 +46,21 @@ VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(const std::vector<VirtualF
u64 last_offset = 0;
for (auto& file : files) {
+ const auto size = file->GetSize();
+
concatenation_map.emplace_back(ConcatenationEntry{
.offset = last_offset,
- .file = file,
+ .file = std::move(file),
});
- last_offset += file->GetSize();
+ last_offset += size;
}
- return VirtualFile(new ConcatenatedVfsFile(std::move(concatenation_map), std::move(name)));
+ return VirtualFile(new ConcatenatedVfsFile(std::move(name), std::move(concatenation_map)));
}
-VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(u8 filler_byte,
- const std::multimap<u64, VirtualFile>& files,
- std::string&& name) {
+VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(u8 filler_byte, std::string&& name,
+ std::multimap<u64, VirtualFile>&& files) {
// Fold trivial cases.
if (files.empty()) {
return nullptr;
@@ -76,6 +77,8 @@ VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(u8 filler_byte,
// Iteration of a multimap is ordered, so offset will be strictly non-decreasing.
for (auto& [offset, file] : files) {
+ const auto size = file->GetSize();
+
if (offset > last_offset) {
concatenation_map.emplace_back(ConcatenationEntry{
.offset = last_offset,
@@ -85,13 +88,13 @@ VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(u8 filler_byte,
concatenation_map.emplace_back(ConcatenationEntry{
.offset = offset,
- .file = file,
+ .file = std::move(file),
});
- last_offset = offset + file->GetSize();
+ last_offset = offset + size;
}
- return VirtualFile(new ConcatenatedVfsFile(std::move(concatenation_map), std::move(name)));
+ return VirtualFile(new ConcatenatedVfsFile(std::move(name), std::move(concatenation_map)));
}
std::string ConcatenatedVfsFile::GetName() const {
diff --git a/src/core/file_sys/vfs_concat.h b/src/core/file_sys/vfs_concat.h
index 6b329d545..cbddd12bd 100644
--- a/src/core/file_sys/vfs_concat.h
+++ b/src/core/file_sys/vfs_concat.h
@@ -24,22 +24,20 @@ private:
};
using ConcatenationMap = std::vector<ConcatenationEntry>;
- explicit ConcatenatedVfsFile(std::vector<ConcatenationEntry>&& concatenation_map,
- std::string&& name);
+ explicit ConcatenatedVfsFile(std::string&& name,
+ std::vector<ConcatenationEntry>&& concatenation_map);
bool VerifyContinuity() const;
public:
~ConcatenatedVfsFile() override;
/// Wrapper function to allow for more efficient handling of files.size() == 0, 1 cases.
- static VirtualFile MakeConcatenatedFile(const std::vector<VirtualFile>& files,
- std::string&& name);
+ static VirtualFile MakeConcatenatedFile(std::string&& name, std::vector<VirtualFile>&& files);
/// Convenience function that turns a map of offsets to files into a concatenated file, filling
/// gaps with a given filler byte.
- static VirtualFile MakeConcatenatedFile(u8 filler_byte,
- const std::multimap<u64, VirtualFile>& files,
- std::string&& name);
+ static VirtualFile MakeConcatenatedFile(u8 filler_byte, std::string&& name,
+ std::multimap<u64, VirtualFile>&& files);
std::string GetName() const override;
std::size_t GetSize() const override;
diff --git a/src/core/file_sys/vfs_layered.cpp b/src/core/file_sys/vfs_layered.cpp
index 3e6426afc..08daca397 100644
--- a/src/core/file_sys/vfs_layered.cpp
+++ b/src/core/file_sys/vfs_layered.cpp
@@ -38,7 +38,7 @@ VirtualDir LayeredVfsDirectory::GetDirectoryRelative(std::string_view path) cons
for (const auto& layer : dirs) {
auto dir = layer->GetDirectoryRelative(path);
if (dir != nullptr) {
- out.push_back(std::move(dir));
+ out.emplace_back(std::move(dir));
}
}
@@ -62,11 +62,11 @@ std::vector<VirtualFile> LayeredVfsDirectory::GetFiles() const {
std::set<std::string, std::less<>> out_names;
for (const auto& layer : dirs) {
- for (const auto& file : layer->GetFiles()) {
+ for (auto& file : layer->GetFiles()) {
auto file_name = file->GetName();
if (!out_names.contains(file_name)) {
out_names.emplace(std::move(file_name));
- out.push_back(file);
+ out.emplace_back(std::move(file));
}
}
}
@@ -86,7 +86,7 @@ std::vector<VirtualDir> LayeredVfsDirectory::GetSubdirectories() const {
std::vector<VirtualDir> out;
out.reserve(names.size());
for (const auto& subdir : names)
- out.push_back(GetSubdirectory(subdir));
+ out.emplace_back(GetSubdirectory(subdir));
return out;
}
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index b7d14060c..1b1c8190e 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -407,13 +407,13 @@ protected:
IPC::RequestParser rp{ctx};
const auto base = rp.PopRaw<ProfileBase>();
- const auto user_data = ctx.ReadBuffer();
- const auto image_data = ctx.ReadBuffer(1);
+ const auto image_data = ctx.ReadBufferA(0);
+ const auto user_data = ctx.ReadBufferX(0);
- LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid=0x{}",
- Common::StringFromFixedZeroTerminatedBuffer(
- reinterpret_cast<const char*>(base.username.data()), base.username.size()),
- base.timestamp, base.user_uuid.RawString());
+ LOG_INFO(Service_ACC, "called, username='{}', timestamp={:016X}, uuid=0x{}",
+ Common::StringFromFixedZeroTerminatedBuffer(
+ reinterpret_cast<const char*>(base.username.data()), base.username.size()),
+ base.timestamp, base.user_uuid.RawString());
if (user_data.size() < sizeof(UserData)) {
LOG_ERROR(Service_ACC, "UserData buffer too small!");
diff --git a/src/core/hle/service/hle_ipc.cpp b/src/core/hle/service/hle_ipc.cpp
index f6a1e54f2..6f3ae3cc4 100644
--- a/src/core/hle/service/hle_ipc.cpp
+++ b/src/core/hle/service/hle_ipc.cpp
@@ -23,6 +23,17 @@
#include "core/hle/service/ipc_helpers.h"
#include "core/memory.h"
+namespace {
+static thread_local std::array read_buffer_data_a{
+ Common::ScratchBuffer<u8>(),
+ Common::ScratchBuffer<u8>(),
+};
+static thread_local std::array read_buffer_data_x{
+ Common::ScratchBuffer<u8>(),
+ Common::ScratchBuffer<u8>(),
+};
+} // Anonymous namespace
+
namespace Service {
SessionRequestHandler::SessionRequestHandler(Kernel::KernelCore& kernel_, const char* service_name_)
@@ -328,26 +339,57 @@ std::vector<u8> HLERequestContext::ReadBufferCopy(std::size_t buffer_index) cons
}
}
-std::span<const u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const {
+std::span<const u8> HLERequestContext::ReadBufferA(std::size_t buffer_index) const {
static thread_local std::array read_buffer_a{
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0),
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0),
};
- static thread_local std::array read_buffer_data_a{
- Common::ScratchBuffer<u8>(),
- Common::ScratchBuffer<u8>(),
- };
+
+ ASSERT_OR_EXECUTE_MSG(
+ BufferDescriptorA().size() > buffer_index, { return {}; },
+ "BufferDescriptorA invalid buffer_index {}", buffer_index);
+ auto& read_buffer = read_buffer_a[buffer_index];
+ return read_buffer.Read(BufferDescriptorA()[buffer_index].Address(),
+ BufferDescriptorA()[buffer_index].Size(),
+ &read_buffer_data_a[buffer_index]);
+}
+
+std::span<const u8> HLERequestContext::ReadBufferX(std::size_t buffer_index) const {
static thread_local std::array read_buffer_x{
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0),
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0),
};
- static thread_local std::array read_buffer_data_x{
- Common::ScratchBuffer<u8>(),
- Common::ScratchBuffer<u8>(),
+
+ ASSERT_OR_EXECUTE_MSG(
+ BufferDescriptorX().size() > buffer_index, { return {}; },
+ "BufferDescriptorX invalid buffer_index {}", buffer_index);
+ auto& read_buffer = read_buffer_x[buffer_index];
+ return read_buffer.Read(BufferDescriptorX()[buffer_index].Address(),
+ BufferDescriptorX()[buffer_index].Size(),
+ &read_buffer_data_x[buffer_index]);
+}
+
+std::span<const u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const {
+ static thread_local std::array read_buffer_a{
+ Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0),
+ Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0),
+ };
+ static thread_local std::array read_buffer_x{
+ Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0),
+ Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0),
};
const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&
BufferDescriptorA()[buffer_index].Size()};
+ const bool is_buffer_x{BufferDescriptorX().size() > buffer_index &&
+ BufferDescriptorX()[buffer_index].Size()};
+
+ if (is_buffer_a && is_buffer_x) {
+ LOG_WARNING(Input, "Both buffer descriptors are available a.size={}, x.size={}",
+ BufferDescriptorA()[buffer_index].Size(),
+ BufferDescriptorX()[buffer_index].Size());
+ }
+
if (is_buffer_a) {
ASSERT_OR_EXECUTE_MSG(
BufferDescriptorA().size() > buffer_index, { return {}; },
diff --git a/src/core/hle/service/hle_ipc.h b/src/core/hle/service/hle_ipc.h
index 4bd24c899..ad5259a5c 100644
--- a/src/core/hle/service/hle_ipc.h
+++ b/src/core/hle/service/hle_ipc.h
@@ -253,6 +253,12 @@ public:
return domain_message_header.has_value();
}
+ /// Helper function to get a span of a buffer using the buffer descriptor A
+ [[nodiscard]] std::span<const u8> ReadBufferA(std::size_t buffer_index = 0) const;
+
+ /// Helper function to get a span of a buffer using the buffer descriptor X
+ [[nodiscard]] std::span<const u8> ReadBufferX(std::size_t buffer_index = 0) const;
+
/// Helper function to get a span of a buffer using the appropriate buffer descriptor
[[nodiscard]] std::span<const u8> ReadBuffer(std::size_t buffer_index = 0) const;
diff --git a/src/core/hle/service/mii/types/core_data.cpp b/src/core/hle/service/mii/types/core_data.cpp
index 970c748ca..ba1da76ba 100644
--- a/src/core/hle/service/mii/types/core_data.cpp
+++ b/src/core/hle/service/mii/types/core_data.cpp
@@ -41,6 +41,7 @@ void CoreData::BuildRandom(Age age, Gender gender, Race race) {
}
}
+ SetDefault();
SetGender(gender);
SetFavoriteColor(MiiUtil::GetRandomValue(FavoriteColor::Max));
SetRegionMove(0);
diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp
index ec4a84989..14e8df63a 100644
--- a/src/core/hle/service/prepo/prepo.cpp
+++ b/src/core/hle/service/prepo/prepo.cpp
@@ -58,14 +58,8 @@ private:
IPC::RequestParser rp{ctx};
const auto process_id = rp.PopRaw<u64>();
- const auto data1 = ctx.ReadBuffer(0);
- const auto data2 = [&ctx] {
- if (ctx.CanReadBuffer(1)) {
- return ctx.ReadBuffer(1);
- }
-
- return std::span<const u8>{};
- }();
+ const auto data1 = ctx.ReadBufferA(0);
+ const auto data2 = ctx.ReadBufferX(0);
LOG_DEBUG(Service_PREPO,
"called, type={:02X}, process_id={:016X}, data1_size={:016X}, data2_size={:016X}",
@@ -85,14 +79,8 @@ private:
const auto user_id = rp.PopRaw<u128>();
const auto process_id = rp.PopRaw<u64>();
- const auto data1 = ctx.ReadBuffer(0);
- const auto data2 = [&ctx] {
- if (ctx.CanReadBuffer(1)) {
- return ctx.ReadBuffer(1);
- }
-
- return std::span<const u8>{};
- }();
+ const auto data1 = ctx.ReadBufferA(0);
+ const auto data2 = ctx.ReadBufferX(0);
LOG_DEBUG(Service_PREPO,
"called, type={:02X}, user_id={:016X}{:016X}, process_id={:016X}, "
@@ -137,14 +125,8 @@ private:
IPC::RequestParser rp{ctx};
const auto title_id = rp.PopRaw<u64>();
- const auto data1 = ctx.ReadBuffer(0);
- const auto data2 = [&ctx] {
- if (ctx.CanReadBuffer(1)) {
- return ctx.ReadBuffer(1);
- }
-
- return std::span<const u8>{};
- }();
+ const auto data1 = ctx.ReadBufferA(0);
+ const auto data2 = ctx.ReadBufferX(0);
LOG_DEBUG(Service_PREPO, "called, title_id={:016X}, data1_size={:016X}, data2_size={:016X}",
title_id, data1.size(), data2.size());
@@ -161,14 +143,8 @@ private:
const auto user_id = rp.PopRaw<u128>();
const auto title_id = rp.PopRaw<u64>();
- const auto data1 = ctx.ReadBuffer(0);
- const auto data2 = [&ctx] {
- if (ctx.CanReadBuffer(1)) {
- return ctx.ReadBuffer(1);
- }
-
- return std::span<const u8>{};
- }();
+ const auto data1 = ctx.ReadBufferA(0);
+ const auto data2 = ctx.ReadBufferX(0);
LOG_DEBUG(Service_PREPO,
"called, user_id={:016X}{:016X}, title_id={:016X}, data1_size={:016X}, "
diff --git a/src/tests/common/unique_function.cpp b/src/tests/common/unique_function.cpp
index f7a23e876..42e6ade09 100644
--- a/src/tests/common/unique_function.cpp
+++ b/src/tests/common/unique_function.cpp
@@ -46,8 +46,8 @@ TEST_CASE("UniqueFunction", "[common]") {
Noisy noisy;
REQUIRE(noisy.state == "Default constructed");
- Common::UniqueFunction<void> func = [noisy = std::move(noisy)] {
- REQUIRE(noisy.state == "Move constructed");
+ Common::UniqueFunction<void> func = [noisy_inner = std::move(noisy)] {
+ REQUIRE(noisy_inner.state == "Move constructed");
};
REQUIRE(noisy.state == "Moved away");
func();
@@ -101,7 +101,7 @@ TEST_CASE("UniqueFunction", "[common]") {
};
Foo object{&num_destroyed};
{
- Common::UniqueFunction<void> func = [object = std::move(object)] {};
+ Common::UniqueFunction<void> func = [object_inner = std::move(object)] {};
REQUIRE(num_destroyed == 0);
}
REQUIRE(num_destroyed == 1);
diff --git a/src/video_core/buffer_cache/buffer_cache_base.h b/src/video_core/buffer_cache/buffer_cache_base.h
index c4f6e8d12..eed267361 100644
--- a/src/video_core/buffer_cache/buffer_cache_base.h
+++ b/src/video_core/buffer_cache/buffer_cache_base.h
@@ -62,7 +62,11 @@ using BufferId = SlotId;
using VideoCore::Surface::PixelFormat;
using namespace Common::Literals;
+#ifdef __APPLE__
+constexpr u32 NUM_VERTEX_BUFFERS = 16;
+#else
constexpr u32 NUM_VERTEX_BUFFERS = 32;
+#endif
constexpr u32 NUM_TRANSFORM_FEEDBACK_BUFFERS = 4;
constexpr u32 NUM_GRAPHICS_UNIFORM_BUFFERS = 18;
constexpr u32 NUM_COMPUTE_UNIFORM_BUFFERS = 8;
diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt
index 8bb429578..cd2549232 100644
--- a/src/video_core/host_shaders/CMakeLists.txt
+++ b/src/video_core/host_shaders/CMakeLists.txt
@@ -19,6 +19,7 @@ set(SHADER_FILES
block_linear_unswizzle_2d.comp
block_linear_unswizzle_3d.comp
convert_abgr8_to_d24s8.frag
+ convert_abgr8_to_d32f.frag
convert_d32f_to_abgr8.frag
convert_d24s8_to_abgr8.frag
convert_depth_to_float.frag
diff --git a/src/video_core/host_shaders/convert_abgr8_to_d32f.frag b/src/video_core/host_shaders/convert_abgr8_to_d32f.frag
new file mode 100644
index 000000000..095b910c2
--- /dev/null
+++ b/src/video_core/host_shaders/convert_abgr8_to_d32f.frag
@@ -0,0 +1,15 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#version 450
+
+layout(binding = 0) uniform sampler2D color_texture;
+
+void main() {
+ ivec2 coord = ivec2(gl_FragCoord.xy);
+ vec4 color = texelFetch(color_texture, coord, 0).abgr;
+
+ float value = color.a * (color.r + color.g + color.b) / 3.0f;
+
+ gl_FragDepth = value;
+}
diff --git a/src/video_core/host_shaders/convert_d24s8_to_abgr8.frag b/src/video_core/host_shaders/convert_d24s8_to_abgr8.frag
index d33131d7c..b81a54056 100644
--- a/src/video_core/host_shaders/convert_d24s8_to_abgr8.frag
+++ b/src/video_core/host_shaders/convert_d24s8_to_abgr8.frag
@@ -3,16 +3,16 @@
#version 450
+precision mediump int;
+precision highp float;
+
layout(binding = 0) uniform sampler2D depth_tex;
-layout(binding = 1) uniform isampler2D stencil_tex;
+layout(binding = 1) uniform usampler2D stencil_tex;
layout(location = 0) out vec4 color;
void main() {
ivec2 coord = ivec2(gl_FragCoord.xy);
- uint depth = uint(textureLod(depth_tex, coord, 0).r * (exp2(24.0) - 1.0f));
- uint stencil = uint(textureLod(stencil_tex, coord, 0).r);
-
highp uint depth_val =
uint(textureLod(depth_tex, coord, 0).r * (exp2(32.0) - 1.0));
lowp uint stencil_val = textureLod(stencil_tex, coord, 0).r;
diff --git a/src/video_core/host_shaders/convert_d32f_to_abgr8.frag b/src/video_core/host_shaders/convert_d32f_to_abgr8.frag
index 04cfef8b5..4e5a9f955 100644
--- a/src/video_core/host_shaders/convert_d32f_to_abgr8.frag
+++ b/src/video_core/host_shaders/convert_d32f_to_abgr8.frag
@@ -9,6 +9,6 @@ layout(location = 0) out vec4 color;
void main() {
ivec2 coord = ivec2(gl_FragCoord.xy);
- float depth = textureLod(depth_tex, coord, 0).r;
+ float depth = texelFetch(depth_tex, coord, 0).r;
color = vec4(depth, depth, depth, 1.0);
}
diff --git a/src/video_core/host_shaders/convert_s8d24_to_abgr8.frag b/src/video_core/host_shaders/convert_s8d24_to_abgr8.frag
index 31db7d426..6a457981d 100644
--- a/src/video_core/host_shaders/convert_s8d24_to_abgr8.frag
+++ b/src/video_core/host_shaders/convert_s8d24_to_abgr8.frag
@@ -3,16 +3,16 @@
#version 450
+precision mediump int;
+precision highp float;
+
layout(binding = 0) uniform sampler2D depth_tex;
-layout(binding = 1) uniform isampler2D stencil_tex;
+layout(binding = 1) uniform usampler2D stencil_tex;
layout(location = 0) out vec4 color;
void main() {
ivec2 coord = ivec2(gl_FragCoord.xy);
- uint depth = uint(textureLod(depth_tex, coord, 0).r * (exp2(24.0) - 1.0f));
- uint stencil = uint(textureLod(stencil_tex, coord, 0).r);
-
highp uint depth_val =
uint(textureLod(depth_tex, coord, 0).r * (exp2(32.0) - 1.0));
lowp uint stencil_val = textureLod(stencil_tex, coord, 0).r;
diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp
index f01d2394e..c3db09424 100644
--- a/src/video_core/renderer_vulkan/blit_image.cpp
+++ b/src/video_core/renderer_vulkan/blit_image.cpp
@@ -8,6 +8,7 @@
#include "common/settings.h"
#include "video_core/host_shaders/blit_color_float_frag_spv.h"
#include "video_core/host_shaders/convert_abgr8_to_d24s8_frag_spv.h"
+#include "video_core/host_shaders/convert_abgr8_to_d32f_frag_spv.h"
#include "video_core/host_shaders/convert_d24s8_to_abgr8_frag_spv.h"
#include "video_core/host_shaders/convert_d32f_to_abgr8_frag_spv.h"
#include "video_core/host_shaders/convert_depth_to_float_frag_spv.h"
@@ -434,6 +435,7 @@ BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_,
convert_depth_to_float_frag(BuildShader(device, CONVERT_DEPTH_TO_FLOAT_FRAG_SPV)),
convert_float_to_depth_frag(BuildShader(device, CONVERT_FLOAT_TO_DEPTH_FRAG_SPV)),
convert_abgr8_to_d24s8_frag(BuildShader(device, CONVERT_ABGR8_TO_D24S8_FRAG_SPV)),
+ convert_abgr8_to_d32f_frag(BuildShader(device, CONVERT_ABGR8_TO_D32F_FRAG_SPV)),
convert_d32f_to_abgr8_frag(BuildShader(device, CONVERT_D32F_TO_ABGR8_FRAG_SPV)),
convert_d24s8_to_abgr8_frag(BuildShader(device, CONVERT_D24S8_TO_ABGR8_FRAG_SPV)),
convert_s8d24_to_abgr8_frag(BuildShader(device, CONVERT_S8D24_TO_ABGR8_FRAG_SPV)),
@@ -559,6 +561,13 @@ void BlitImageHelper::ConvertABGR8ToD24S8(const Framebuffer* dst_framebuffer,
Convert(*convert_abgr8_to_d24s8_pipeline, dst_framebuffer, src_image_view);
}
+void BlitImageHelper::ConvertABGR8ToD32F(const Framebuffer* dst_framebuffer,
+ const ImageView& src_image_view) {
+ ConvertPipelineDepthTargetEx(convert_abgr8_to_d32f_pipeline, dst_framebuffer->RenderPass(),
+ convert_abgr8_to_d32f_frag);
+ Convert(*convert_abgr8_to_d32f_pipeline, dst_framebuffer, src_image_view);
+}
+
void BlitImageHelper::ConvertD32FToABGR8(const Framebuffer* dst_framebuffer,
ImageView& src_image_view) {
ConvertPipelineColorTargetEx(convert_d32f_to_abgr8_pipeline, dst_framebuffer->RenderPass(),
diff --git a/src/video_core/renderer_vulkan/blit_image.h b/src/video_core/renderer_vulkan/blit_image.h
index a032c71fb..b2104a59e 100644
--- a/src/video_core/renderer_vulkan/blit_image.h
+++ b/src/video_core/renderer_vulkan/blit_image.h
@@ -67,6 +67,8 @@ public:
void ConvertABGR8ToD24S8(const Framebuffer* dst_framebuffer, const ImageView& src_image_view);
+ void ConvertABGR8ToD32F(const Framebuffer* dst_framebuffer, const ImageView& src_image_view);
+
void ConvertD32FToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view);
void ConvertD24S8ToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view);
@@ -130,6 +132,7 @@ private:
vk::ShaderModule convert_depth_to_float_frag;
vk::ShaderModule convert_float_to_depth_frag;
vk::ShaderModule convert_abgr8_to_d24s8_frag;
+ vk::ShaderModule convert_abgr8_to_d32f_frag;
vk::ShaderModule convert_d32f_to_abgr8_frag;
vk::ShaderModule convert_d24s8_to_abgr8_frag;
vk::ShaderModule convert_s8d24_to_abgr8_frag;
@@ -149,6 +152,7 @@ private:
vk::Pipeline convert_d16_to_r16_pipeline;
vk::Pipeline convert_r16_to_d16_pipeline;
vk::Pipeline convert_abgr8_to_d24s8_pipeline;
+ vk::Pipeline convert_abgr8_to_d32f_pipeline;
vk::Pipeline convert_d32f_to_abgr8_pipeline;
vk::Pipeline convert_d24s8_to_abgr8_pipeline;
vk::Pipeline convert_s8d24_to_abgr8_pipeline;
diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp
index 2edaafa7e..66c03bf17 100644
--- a/src/video_core/renderer_vulkan/vk_query_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp
@@ -1436,6 +1436,7 @@ void QueryCacheRuntime::Barriers(bool is_prebarrier) {
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,
};
+ impl->scheduler.RequestOutsideRenderPassOperationContext();
if (is_prebarrier) {
impl->scheduler.Record([](vk::CommandBuffer cmdbuf) {
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 23696fac4..465eac37e 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -977,6 +977,19 @@ void RasterizerVulkan::UpdateScissorsState(Tegra::Engines::Maxwell3D::Regs& regs
if (!state_tracker.TouchScissors()) {
return;
}
+ if (!regs.viewport_scale_offset_enabled) {
+ const auto x = static_cast<float>(regs.surface_clip.x);
+ const auto y = static_cast<float>(regs.surface_clip.y);
+ const auto width = static_cast<float>(regs.surface_clip.width);
+ const auto height = static_cast<float>(regs.surface_clip.height);
+ VkRect2D scissor;
+ scissor.offset.x = static_cast<u32>(x);
+ scissor.offset.y = static_cast<u32>(y);
+ scissor.extent.width = static_cast<u32>(width != 0.0f ? width : 1.0f);
+ scissor.extent.height = static_cast<u32>(height != 0.0f ? height : 1.0f);
+ scheduler.Record([scissor](vk::CommandBuffer cmdbuf) { cmdbuf.SetScissor(0, scissor); });
+ return;
+ }
u32 up_scale = 1;
u32 down_shift = 0;
if (texture_cache.IsRescaling()) {
diff --git a/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp b/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp
index ae9f1de64..7746a88d3 100644
--- a/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp
@@ -19,7 +19,7 @@ VkAttachmentDescription AttachmentDescription(const Device& device, PixelFormat
VkSampleCountFlagBits samples) {
using MaxwellToVK::SurfaceFormat;
return {
- .flags = VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT,
+ .flags = {},
.format = SurfaceFormat(device, FormatType::Optimal, true, format).format,
.samples = samples,
.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD,
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index 93773a69f..de34f6d49 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -1194,6 +1194,11 @@ void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, Im
return blit_image_helper.ConvertD16ToR16(dst, src_view);
}
break;
+ case PixelFormat::A8B8G8R8_SRGB:
+ if (src_view.format == PixelFormat::D32_FLOAT) {
+ return blit_image_helper.ConvertD32FToABGR8(dst, src_view);
+ }
+ break;
case PixelFormat::A8B8G8R8_UNORM:
if (src_view.format == PixelFormat::S8_UINT_D24_UNORM) {
return blit_image_helper.ConvertD24S8ToABGR8(dst, src_view);
@@ -1205,6 +1210,16 @@ void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, Im
return blit_image_helper.ConvertD32FToABGR8(dst, src_view);
}
break;
+ case PixelFormat::B8G8R8A8_SRGB:
+ if (src_view.format == PixelFormat::D32_FLOAT) {
+ return blit_image_helper.ConvertD32FToABGR8(dst, src_view);
+ }
+ break;
+ case PixelFormat::B8G8R8A8_UNORM:
+ if (src_view.format == PixelFormat::D32_FLOAT) {
+ return blit_image_helper.ConvertD32FToABGR8(dst, src_view);
+ }
+ break;
case PixelFormat::R32_FLOAT:
if (src_view.format == PixelFormat::D32_FLOAT) {
return blit_image_helper.ConvertD32ToR32(dst, src_view);
@@ -1222,6 +1237,12 @@ void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, Im
}
break;
case PixelFormat::D32_FLOAT:
+ if (src_view.format == PixelFormat::A8B8G8R8_UNORM ||
+ src_view.format == PixelFormat::B8G8R8A8_UNORM ||
+ src_view.format == PixelFormat::A8B8G8R8_SRGB ||
+ src_view.format == PixelFormat::B8G8R8A8_SRGB) {
+ return blit_image_helper.ConvertABGR8ToD32F(dst, src_view);
+ }
if (src_view.format == PixelFormat::R32_FLOAT) {
return blit_image_helper.ConvertR32ToD32(dst, src_view);
}
@@ -2034,7 +2055,7 @@ void TextureCacheRuntime::TransitionImageLayout(Image& image) {
},
};
scheduler.RequestOutsideRenderPassOperationContext();
- scheduler.Record([barrier = barrier](vk::CommandBuffer cmdbuf) {
+ scheduler.Record([barrier](vk::CommandBuffer cmdbuf) {
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, barrier);
});
diff --git a/src/video_core/texture_cache/formatter.cpp b/src/video_core/texture_cache/formatter.cpp
index 6279d8e9e..2b7e0df72 100644
--- a/src/video_core/texture_cache/formatter.cpp
+++ b/src/video_core/texture_cache/formatter.cpp
@@ -10,19 +10,23 @@
#include "video_core/texture_cache/image_info.h"
#include "video_core/texture_cache/image_view_base.h"
#include "video_core/texture_cache/render_targets.h"
+#include "video_core/texture_cache/samples_helper.h"
namespace VideoCommon {
std::string Name(const ImageBase& image) {
const GPUVAddr gpu_addr = image.gpu_addr;
const ImageInfo& info = image.info;
- const u32 width = info.size.width;
- const u32 height = info.size.height;
+ u32 width = info.size.width;
+ u32 height = info.size.height;
const u32 depth = info.size.depth;
const u32 num_layers = image.info.resources.layers;
const u32 num_levels = image.info.resources.levels;
std::string resource;
if (image.info.num_samples > 1) {
+ const auto [samples_x, samples_y] = VideoCommon::SamplesLog2(image.info.num_samples);
+ width >>= samples_x;
+ height >>= samples_y;
resource += fmt::format(":{}xMSAA", image.info.num_samples);
}
if (num_layers > 1) {
diff --git a/src/video_core/texture_cache/samples_helper.h b/src/video_core/texture_cache/samples_helper.h
index 203ac1b11..2ee2f8312 100644
--- a/src/video_core/texture_cache/samples_helper.h
+++ b/src/video_core/texture_cache/samples_helper.h
@@ -24,7 +24,7 @@ namespace VideoCommon {
return {2, 2};
}
ASSERT_MSG(false, "Invalid number of samples={}", num_samples);
- return {1, 1};
+ return {0, 0};
}
[[nodiscard]] inline int NumSamples(Tegra::Texture::MsaaMode msaa_mode) {