summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/settings.h2
-rw-r--r--src/common/uuid.h5
-rw-r--r--src/core/core.cpp13
-rw-r--r--src/core/core.h2
-rw-r--r--src/core/file_sys/card_image.cpp18
-rw-r--r--src/core/file_sys/card_image.h3
-rw-r--r--src/core/file_sys/submission_package.cpp71
-rw-r--r--src/core/file_sys/submission_package.h11
-rw-r--r--src/core/hle/service/am/applets/applet_controller.cpp21
-rw-r--r--src/core/hle/service/am/applets/applet_controller.h15
-rw-r--r--src/core/loader/loader.cpp13
-rw-r--r--src/core/loader/loader.h13
-rw-r--r--src/core/loader/nsp.cpp34
-rw-r--r--src/core/loader/nsp.h4
-rw-r--r--src/core/loader/xci.cpp14
-rw-r--r--src/core/loader/xci.h3
-rw-r--r--src/input_common/mouse/mouse_poller.cpp2
-rw-r--r--src/input_common/sdl/sdl_impl.cpp59
-rw-r--r--src/input_common/sdl/sdl_impl.h8
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h78
-rw-r--r--src/video_core/engines/maxwell_dma.cpp20
-rw-r--r--src/video_core/engines/maxwell_dma.h2
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.cpp6
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.h2
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp5
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h2
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.h2
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.cpp55
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.h8
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pass.cpp33
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp5
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.h2
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp6
-rw-r--r--src/video_core/texture_cache/texture_cache.h6
-rw-r--r--src/video_core/texture_cache/util.cpp86
-rw-r--r--src/yuzu/bootmanager.cpp15
-rw-r--r--src/yuzu/configuration/configure_audio.cpp12
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp3
-rw-r--r--src/yuzu/game_list.cpp8
-rw-r--r--src/yuzu/game_list.h5
-rw-r--r--src/yuzu/game_list_worker.cpp44
-rw-r--r--src/yuzu/main.cpp17
-rw-r--r--src/yuzu/main.h6
-rw-r--r--src/yuzu_cmd/CMakeLists.txt5
-rw-r--r--src/yuzu_cmd/config.cpp24
-rw-r--r--src/yuzu_cmd/default_ini.h142
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp20
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp8
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp34
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h2
-rw-r--r--src/yuzu_cmd/yuzu.cpp2
51 files changed, 650 insertions, 326 deletions
diff --git a/src/common/settings.h b/src/common/settings.h
index 832358036..ce1bc647d 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -368,7 +368,7 @@ struct Values {
368 "udp_input_servers"}; 368 "udp_input_servers"};
369 369
370 BasicSetting<bool> mouse_panning{false, "mouse_panning"}; 370 BasicSetting<bool> mouse_panning{false, "mouse_panning"};
371 BasicSetting<u8> mouse_panning_sensitivity{1, "mouse_panning_sensitivity"}; 371 BasicSetting<u8> mouse_panning_sensitivity{10, "mouse_panning_sensitivity"};
372 BasicSetting<bool> mouse_enabled{false, "mouse_enabled"}; 372 BasicSetting<bool> mouse_enabled{false, "mouse_enabled"};
373 std::string mouse_device; 373 std::string mouse_device;
374 MouseButtonsRaw mouse_buttons; 374 MouseButtonsRaw mouse_buttons;
diff --git a/src/common/uuid.h b/src/common/uuid.h
index 2e7a18405..0ffa37e7c 100644
--- a/src/common/uuid.h
+++ b/src/common/uuid.h
@@ -20,12 +20,11 @@ struct UUID {
20 constexpr explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {} 20 constexpr explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {}
21 21
22 [[nodiscard]] constexpr explicit operator bool() const { 22 [[nodiscard]] constexpr explicit operator bool() const {
23 return uuid[0] != INVALID_UUID[0] && uuid[1] != INVALID_UUID[1]; 23 return uuid != INVALID_UUID;
24 } 24 }
25 25
26 [[nodiscard]] constexpr bool operator==(const UUID& rhs) const { 26 [[nodiscard]] constexpr bool operator==(const UUID& rhs) const {
27 // TODO(DarkLordZach): Replace with uuid == rhs.uuid with C++20 27 return uuid == rhs.uuid;
28 return uuid[0] == rhs.uuid[0] && uuid[1] == rhs.uuid[1];
29 } 28 }
30 29
31 [[nodiscard]] constexpr bool operator!=(const UUID& rhs) const { 30 [[nodiscard]] constexpr bool operator!=(const UUID& rhs) const {
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 406320ed6..15226cf41 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -216,9 +216,9 @@ struct System::Impl {
216 } 216 }
217 217
218 ResultStatus Load(System& system, Frontend::EmuWindow& emu_window, const std::string& filepath, 218 ResultStatus Load(System& system, Frontend::EmuWindow& emu_window, const std::string& filepath,
219 std::size_t program_index) { 219 u64 program_id, std::size_t program_index) {
220 app_loader = Loader::GetLoader(system, GetGameFileFromPath(virtual_filesystem, filepath), 220 app_loader = Loader::GetLoader(system, GetGameFileFromPath(virtual_filesystem, filepath),
221 program_index); 221 program_id, program_index);
222 222
223 if (!app_loader) { 223 if (!app_loader) {
224 LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath); 224 LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
@@ -269,11 +269,10 @@ struct System::Impl {
269 } 269 }
270 } 270 }
271 271
272 u64 title_id{0}; 272 if (app_loader->ReadProgramId(program_id) != Loader::ResultStatus::Success) {
273 if (app_loader->ReadProgramId(title_id) != Loader::ResultStatus::Success) {
274 LOG_ERROR(Core, "Failed to find title id for ROM (Error {})", load_result); 273 LOG_ERROR(Core, "Failed to find title id for ROM (Error {})", load_result);
275 } 274 }
276 perf_stats = std::make_unique<PerfStats>(title_id); 275 perf_stats = std::make_unique<PerfStats>(program_id);
277 // Reset counters and set time origin to current frame 276 // Reset counters and set time origin to current frame
278 GetAndResetPerfStats(); 277 GetAndResetPerfStats();
279 perf_stats->BeginSystemFrame(); 278 perf_stats->BeginSystemFrame();
@@ -459,8 +458,8 @@ void System::Shutdown() {
459} 458}
460 459
461System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath, 460System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath,
462 std::size_t program_index) { 461 u64 program_id, std::size_t program_index) {
463 return impl->Load(*this, emu_window, filepath, program_index); 462 return impl->Load(*this, emu_window, filepath, program_id, program_index);
464} 463}
465 464
466bool System::IsPoweredOn() const { 465bool System::IsPoweredOn() const {
diff --git a/src/core/core.h b/src/core/core.h
index 8b93ba998..b93c32e60 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -175,7 +175,7 @@ public:
175 * @returns ResultStatus code, indicating if the operation succeeded. 175 * @returns ResultStatus code, indicating if the operation succeeded.
176 */ 176 */
177 [[nodiscard]] ResultStatus Load(Frontend::EmuWindow& emu_window, const std::string& filepath, 177 [[nodiscard]] ResultStatus Load(Frontend::EmuWindow& emu_window, const std::string& filepath,
178 std::size_t program_index = 0); 178 u64 program_id = 0, std::size_t program_index = 0);
179 179
180 /** 180 /**
181 * Indicates if the emulated system is powered on (all subsystems initialized and able to run an 181 * Indicates if the emulated system is powered on (all subsystems initialized and able to run an
diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp
index db2f6a955..755d3303e 100644
--- a/src/core/file_sys/card_image.cpp
+++ b/src/core/file_sys/card_image.cpp
@@ -29,7 +29,7 @@ constexpr std::array partition_names{
29 "logo", 29 "logo",
30}; 30};
31 31
32XCI::XCI(VirtualFile file_, std::size_t program_index) 32XCI::XCI(VirtualFile file_, u64 program_id, size_t program_index)
33 : file(std::move(file_)), program_nca_status{Loader::ResultStatus::ErrorXCIMissingProgramNCA}, 33 : file(std::move(file_)), program_nca_status{Loader::ResultStatus::ErrorXCIMissingProgramNCA},
34 partitions(partition_names.size()), 34 partitions(partition_names.size()),
35 partitions_raw(partition_names.size()), keys{Core::Crypto::KeyManager::Instance()} { 35 partitions_raw(partition_names.size()), keys{Core::Crypto::KeyManager::Instance()} {
@@ -63,12 +63,12 @@ XCI::XCI(VirtualFile file_, std::size_t program_index)
63 63
64 secure_partition = std::make_shared<NSP>( 64 secure_partition = std::make_shared<NSP>(
65 main_hfs.GetFile(partition_names[static_cast<std::size_t>(XCIPartition::Secure)]), 65 main_hfs.GetFile(partition_names[static_cast<std::size_t>(XCIPartition::Secure)]),
66 program_index); 66 program_id, program_index);
67 67
68 ncas = secure_partition->GetNCAsCollapsed(); 68 ncas = secure_partition->GetNCAsCollapsed();
69 program = 69 program =
70 secure_partition->GetNCA(secure_partition->GetProgramTitleID(), ContentRecordType::Program); 70 secure_partition->GetNCA(secure_partition->GetProgramTitleID(), ContentRecordType::Program);
71 program_nca_status = secure_partition->GetProgramStatus(secure_partition->GetProgramTitleID()); 71 program_nca_status = secure_partition->GetProgramStatus();
72 if (program_nca_status == Loader::ResultStatus::ErrorNSPMissingProgramNCA) { 72 if (program_nca_status == Loader::ResultStatus::ErrorNSPMissingProgramNCA) {
73 program_nca_status = Loader::ResultStatus::ErrorXCIMissingProgramNCA; 73 program_nca_status = Loader::ResultStatus::ErrorXCIMissingProgramNCA;
74 } 74 }
@@ -174,6 +174,10 @@ u64 XCI::GetProgramTitleID() const {
174 return secure_partition->GetProgramTitleID(); 174 return secure_partition->GetProgramTitleID();
175} 175}
176 176
177std::vector<u64> XCI::GetProgramTitleIDs() const {
178 return secure_partition->GetProgramTitleIDs();
179}
180
177u32 XCI::GetSystemUpdateVersion() { 181u32 XCI::GetSystemUpdateVersion() {
178 const auto update = GetPartition(XCIPartition::Update); 182 const auto update = GetPartition(XCIPartition::Update);
179 if (update == nullptr) { 183 if (update == nullptr) {
@@ -229,9 +233,11 @@ const std::vector<std::shared_ptr<NCA>>& XCI::GetNCAs() const {
229} 233}
230 234
231std::shared_ptr<NCA> XCI::GetNCAByType(NCAContentType type) const { 235std::shared_ptr<NCA> XCI::GetNCAByType(NCAContentType type) const {
232 const auto iter = 236 const auto program_id = secure_partition->GetProgramTitleID();
233 std::find_if(ncas.begin(), ncas.end(), 237 const auto iter = std::find_if(
234 [type](const std::shared_ptr<NCA>& nca) { return nca->GetType() == type; }); 238 ncas.begin(), ncas.end(), [this, type, program_id](const std::shared_ptr<NCA>& nca) {
239 return nca->GetType() == type && nca->GetTitleId() == program_id;
240 });
235 return iter == ncas.end() ? nullptr : *iter; 241 return iter == ncas.end() ? nullptr : *iter;
236} 242}
237 243
diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h
index 4960e90fe..0fd9fa87c 100644
--- a/src/core/file_sys/card_image.h
+++ b/src/core/file_sys/card_image.h
@@ -78,7 +78,7 @@ enum class XCIPartition : u8 { Update, Normal, Secure, Logo };
78 78
79class XCI : public ReadOnlyVfsDirectory { 79class XCI : public ReadOnlyVfsDirectory {
80public: 80public:
81 explicit XCI(VirtualFile file, std::size_t program_index = 0); 81 explicit XCI(VirtualFile file, u64 program_id = 0, size_t program_index = 0);
82 ~XCI() override; 82 ~XCI() override;
83 83
84 Loader::ResultStatus GetStatus() const; 84 Loader::ResultStatus GetStatus() const;
@@ -104,6 +104,7 @@ public:
104 VirtualFile GetLogoPartitionRaw() const; 104 VirtualFile GetLogoPartitionRaw() const;
105 105
106 u64 GetProgramTitleID() const; 106 u64 GetProgramTitleID() const;
107 std::vector<u64> GetProgramTitleIDs() const;
107 u32 GetSystemUpdateVersion(); 108 u32 GetSystemUpdateVersion();
108 u64 GetSystemUpdateTitleID() const; 109 u64 GetSystemUpdateTitleID() const;
109 110
diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp
index d51d469e3..f192dffa5 100644
--- a/src/core/file_sys/submission_package.cpp
+++ b/src/core/file_sys/submission_package.cpp
@@ -20,8 +20,9 @@
20 20
21namespace FileSys { 21namespace FileSys {
22 22
23NSP::NSP(VirtualFile file_, std::size_t program_index_) 23NSP::NSP(VirtualFile file_, u64 title_id_, std::size_t program_index_)
24 : file(std::move(file_)), program_index(program_index_), status{Loader::ResultStatus::Success}, 24 : file(std::move(file_)), expected_program_id(title_id_),
25 program_index(program_index_), status{Loader::ResultStatus::Success},
25 pfs(std::make_shared<PartitionFilesystem>(file)), keys{Core::Crypto::KeyManager::Instance()} { 26 pfs(std::make_shared<PartitionFilesystem>(file)), keys{Core::Crypto::KeyManager::Instance()} {
26 if (pfs->GetStatus() != Loader::ResultStatus::Success) { 27 if (pfs->GetStatus() != Loader::ResultStatus::Success) {
27 status = pfs->GetStatus(); 28 status = pfs->GetStatus();
@@ -46,60 +47,59 @@ Loader::ResultStatus NSP::GetStatus() const {
46 return status; 47 return status;
47} 48}
48 49
49Loader::ResultStatus NSP::GetProgramStatus(u64 title_id) const { 50Loader::ResultStatus NSP::GetProgramStatus() const {
50 if (IsExtractedType() && GetExeFS() != nullptr && FileSys::IsDirectoryExeFS(GetExeFS())) { 51 if (IsExtractedType() && GetExeFS() != nullptr && FileSys::IsDirectoryExeFS(GetExeFS())) {
51 return Loader::ResultStatus::Success; 52 return Loader::ResultStatus::Success;
52 } 53 }
53 54
54 const auto iter = program_status.find(title_id); 55 const auto iter = program_status.find(GetProgramTitleID());
55 if (iter == program_status.end()) 56 if (iter == program_status.end())
56 return Loader::ResultStatus::ErrorNSPMissingProgramNCA; 57 return Loader::ResultStatus::ErrorNSPMissingProgramNCA;
57 return iter->second; 58 return iter->second;
58} 59}
59 60
60u64 NSP::GetFirstTitleID() const {
61 if (IsExtractedType()) {
62 return GetProgramTitleID();
63 }
64
65 if (program_status.empty())
66 return 0;
67 return program_status.begin()->first;
68}
69
70u64 NSP::GetProgramTitleID() const { 61u64 NSP::GetProgramTitleID() const {
71 if (IsExtractedType()) { 62 if (IsExtractedType()) {
72 if (GetExeFS() == nullptr || !IsDirectoryExeFS(GetExeFS())) { 63 return GetExtractedTitleID() + program_index;
73 return 0; 64 }
74 }
75 65
76 ProgramMetadata meta; 66 auto program_id = expected_program_id;
77 if (meta.Load(GetExeFS()->GetFile("main.npdm")) == Loader::ResultStatus::Success) { 67 if (program_id == 0) {
78 return meta.GetTitleID(); 68 if (!program_status.empty()) {
79 } else { 69 program_id = program_status.begin()->first;
80 return 0;
81 } 70 }
82 } 71 }
83 72
84 const auto out = GetFirstTitleID(); 73 program_id = program_id + program_index;
85 if ((out & 0x800) == 0) 74 if (program_status.find(program_id) != program_status.end()) {
86 return out; 75 return program_id;
76 }
87 77
88 const auto ids = GetTitleIDs(); 78 const auto ids = GetProgramTitleIDs();
89 const auto iter = 79 const auto iter =
90 std::find_if(ids.begin(), ids.end(), [](u64 tid) { return (tid & 0x800) == 0; }); 80 std::find_if(ids.begin(), ids.end(), [](u64 tid) { return (tid & 0x800) == 0; });
91 return iter == ids.end() ? out : *iter; 81 return iter == ids.end() ? 0 : *iter;
82}
83
84u64 NSP::GetExtractedTitleID() const {
85 if (GetExeFS() == nullptr || !IsDirectoryExeFS(GetExeFS())) {
86 return 0;
87 }
88
89 ProgramMetadata meta;
90 if (meta.Load(GetExeFS()->GetFile("main.npdm")) == Loader::ResultStatus::Success) {
91 return meta.GetTitleID();
92 } else {
93 return 0;
94 }
92} 95}
93 96
94std::vector<u64> NSP::GetTitleIDs() const { 97std::vector<u64> NSP::GetProgramTitleIDs() const {
95 if (IsExtractedType()) { 98 if (IsExtractedType()) {
96 return {GetProgramTitleID()}; 99 return {GetExtractedTitleID()};
97 } 100 }
98 101
99 std::vector<u64> out; 102 std::vector<u64> out{program_ids.cbegin(), program_ids.cend()};
100 out.reserve(ncas.size());
101 for (const auto& kv : ncas)
102 out.push_back(kv.first);
103 return out; 103 return out;
104} 104}
105 105
@@ -146,7 +146,7 @@ std::shared_ptr<NCA> NSP::GetNCA(u64 title_id, ContentRecordType type, TitleType
146 if (extracted) 146 if (extracted)
147 LOG_WARNING(Service_FS, "called on an NSP that is of type extracted."); 147 LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
148 148
149 const auto title_id_iter = ncas.find(title_id + program_index); 149 const auto title_id_iter = ncas.find(title_id);
150 if (title_id_iter == ncas.end()) 150 if (title_id_iter == ncas.end())
151 return nullptr; 151 return nullptr;
152 152
@@ -160,7 +160,7 @@ std::shared_ptr<NCA> NSP::GetNCA(u64 title_id, ContentRecordType type, TitleType
160VirtualFile NSP::GetNCAFile(u64 title_id, ContentRecordType type, TitleType title_type) const { 160VirtualFile NSP::GetNCAFile(u64 title_id, ContentRecordType type, TitleType title_type) const {
161 if (extracted) 161 if (extracted)
162 LOG_WARNING(Service_FS, "called on an NSP that is of type extracted."); 162 LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
163 const auto nca = GetNCA(title_id, type); 163 const auto nca = GetNCA(title_id, type, title_type);
164 if (nca != nullptr) 164 if (nca != nullptr)
165 return nca->GetBaseFile(); 165 return nca->GetBaseFile();
166 return nullptr; 166 return nullptr;
@@ -286,6 +286,7 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
286 286
287 if (next_nca->GetType() == NCAContentType::Program) { 287 if (next_nca->GetType() == NCAContentType::Program) {
288 program_status[next_nca->GetTitleId()] = next_nca->GetStatus(); 288 program_status[next_nca->GetTitleId()] = next_nca->GetStatus();
289 program_ids.insert(next_nca->GetTitleId() & 0xFFFFFFFFFFFFF000);
289 } 290 }
290 291
291 if (next_nca->GetStatus() != Loader::ResultStatus::Success && 292 if (next_nca->GetStatus() != Loader::ResultStatus::Success &&
diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h
index ecb3b6f15..030f36c09 100644
--- a/src/core/file_sys/submission_package.h
+++ b/src/core/file_sys/submission_package.h
@@ -6,6 +6,7 @@
6 6
7#include <map> 7#include <map>
8#include <memory> 8#include <memory>
9#include <set>
9#include <vector> 10#include <vector>
10#include "common/common_types.h" 11#include "common/common_types.h"
11#include "core/file_sys/vfs.h" 12#include "core/file_sys/vfs.h"
@@ -27,15 +28,15 @@ enum class ContentRecordType : u8;
27 28
28class NSP : public ReadOnlyVfsDirectory { 29class NSP : public ReadOnlyVfsDirectory {
29public: 30public:
30 explicit NSP(VirtualFile file_, std::size_t program_index_ = 0); 31 explicit NSP(VirtualFile file_, u64 title_id = 0, std::size_t program_index_ = 0);
31 ~NSP() override; 32 ~NSP() override;
32 33
33 Loader::ResultStatus GetStatus() const; 34 Loader::ResultStatus GetStatus() const;
34 Loader::ResultStatus GetProgramStatus(u64 title_id) const; 35 Loader::ResultStatus GetProgramStatus() const;
35 // Should only be used when one title id can be assured. 36 // Should only be used when one title id can be assured.
36 u64 GetFirstTitleID() const;
37 u64 GetProgramTitleID() const; 37 u64 GetProgramTitleID() const;
38 std::vector<u64> GetTitleIDs() const; 38 u64 GetExtractedTitleID() const;
39 std::vector<u64> GetProgramTitleIDs() const;
39 40
40 bool IsExtractedType() const; 41 bool IsExtractedType() const;
41 42
@@ -69,6 +70,7 @@ private:
69 70
70 VirtualFile file; 71 VirtualFile file;
71 72
73 const u64 expected_program_id;
72 const std::size_t program_index; 74 const std::size_t program_index;
73 75
74 bool extracted = false; 76 bool extracted = false;
@@ -78,6 +80,7 @@ private:
78 std::shared_ptr<PartitionFilesystem> pfs; 80 std::shared_ptr<PartitionFilesystem> pfs;
79 // Map title id -> {map type -> NCA} 81 // Map title id -> {map type -> NCA}
80 std::map<u64, std::map<std::pair<TitleType, ContentRecordType>, std::shared_ptr<NCA>>> ncas; 82 std::map<u64, std::map<std::pair<TitleType, ContentRecordType>, std::shared_ptr<NCA>>> ncas;
83 std::set<u64> program_ids;
81 std::vector<VirtualFile> ticket_files; 84 std::vector<VirtualFile> ticket_files;
82 85
83 Core::Crypto::KeyManager& keys; 86 Core::Crypto::KeyManager& keys;
diff --git a/src/core/hle/service/am/applets/applet_controller.cpp b/src/core/hle/service/am/applets/applet_controller.cpp
index 12682effe..2721679c1 100644
--- a/src/core/hle/service/am/applets/applet_controller.cpp
+++ b/src/core/hle/service/am/applets/applet_controller.cpp
@@ -87,6 +87,10 @@ void Controller::Initialize() {
87 case sizeof(ControllerUpdateFirmwareArg): 87 case sizeof(ControllerUpdateFirmwareArg):
88 controller_private_arg.mode = ControllerSupportMode::ShowControllerFirmwareUpdate; 88 controller_private_arg.mode = ControllerSupportMode::ShowControllerFirmwareUpdate;
89 break; 89 break;
90 case sizeof(ControllerKeyRemappingArg):
91 controller_private_arg.mode =
92 ControllerSupportMode::ShowControllerKeyRemappingForSystem;
93 break;
90 default: 94 default:
91 UNIMPLEMENTED_MSG("Unknown ControllerPrivateArg mode={} with arg_size={}", 95 UNIMPLEMENTED_MSG("Unknown ControllerPrivateArg mode={} with arg_size={}",
92 controller_private_arg.mode, controller_private_arg.arg_size); 96 controller_private_arg.mode, controller_private_arg.arg_size);
@@ -99,7 +103,9 @@ void Controller::Initialize() {
99 // This is always 0 (Application) except with ShowControllerFirmwareUpdateForSystem. 103 // This is always 0 (Application) except with ShowControllerFirmwareUpdateForSystem.
100 if (controller_private_arg.caller >= ControllerSupportCaller::MaxControllerSupportCaller) { 104 if (controller_private_arg.caller >= ControllerSupportCaller::MaxControllerSupportCaller) {
101 if (controller_private_arg.flag_1 && 105 if (controller_private_arg.flag_1 &&
102 controller_private_arg.mode == ControllerSupportMode::ShowControllerFirmwareUpdate) { 106 (controller_private_arg.mode == ControllerSupportMode::ShowControllerFirmwareUpdate ||
107 controller_private_arg.mode ==
108 ControllerSupportMode::ShowControllerKeyRemappingForSystem)) {
103 controller_private_arg.caller = ControllerSupportCaller::System; 109 controller_private_arg.caller = ControllerSupportCaller::System;
104 } else { 110 } else {
105 controller_private_arg.caller = ControllerSupportCaller::Application; 111 controller_private_arg.caller = ControllerSupportCaller::Application;
@@ -121,6 +127,7 @@ void Controller::Initialize() {
121 std::memcpy(&controller_user_arg_old, user_arg.data(), user_arg.size()); 127 std::memcpy(&controller_user_arg_old, user_arg.data(), user_arg.size());
122 break; 128 break;
123 case ControllerAppletVersion::Version7: 129 case ControllerAppletVersion::Version7:
130 case ControllerAppletVersion::Version8:
124 ASSERT(user_arg.size() == sizeof(ControllerSupportArgNew)); 131 ASSERT(user_arg.size() == sizeof(ControllerSupportArgNew));
125 std::memcpy(&controller_user_arg_new, user_arg.data(), user_arg.size()); 132 std::memcpy(&controller_user_arg_new, user_arg.data(), user_arg.size());
126 break; 133 break;
@@ -143,6 +150,16 @@ void Controller::Initialize() {
143 std::memcpy(&controller_update_arg, update_arg.data(), update_arg.size()); 150 std::memcpy(&controller_update_arg, update_arg.data(), update_arg.size());
144 break; 151 break;
145 } 152 }
153 case ControllerSupportMode::ShowControllerKeyRemappingForSystem: {
154 const auto remapping_arg_storage = broker.PopNormalDataToApplet();
155 ASSERT(remapping_arg_storage != nullptr);
156
157 const auto& remapping_arg = remapping_arg_storage->GetData();
158 ASSERT(remapping_arg.size() == sizeof(ControllerKeyRemappingArg));
159
160 std::memcpy(&controller_key_remapping_arg, remapping_arg.data(), remapping_arg.size());
161 break;
162 }
146 default: { 163 default: {
147 UNIMPLEMENTED_MSG("Unimplemented ControllerSupportMode={}", controller_private_arg.mode); 164 UNIMPLEMENTED_MSG("Unimplemented ControllerSupportMode={}", controller_private_arg.mode);
148 break; 165 break;
@@ -179,6 +196,7 @@ void Controller::Execute() {
179 std::vector<ExplainText>(controller_user_arg_old.explain_text.begin(), 196 std::vector<ExplainText>(controller_user_arg_old.explain_text.begin(),
180 controller_user_arg_old.explain_text.end())); 197 controller_user_arg_old.explain_text.end()));
181 case ControllerAppletVersion::Version7: 198 case ControllerAppletVersion::Version7:
199 case ControllerAppletVersion::Version8:
182 default: 200 default:
183 return ConvertToFrontendParameters( 201 return ConvertToFrontendParameters(
184 controller_private_arg, controller_user_arg_new.header, 202 controller_private_arg, controller_user_arg_new.header,
@@ -210,6 +228,7 @@ void Controller::Execute() {
210 } 228 }
211 case ControllerSupportMode::ShowControllerStrapGuide: 229 case ControllerSupportMode::ShowControllerStrapGuide:
212 case ControllerSupportMode::ShowControllerFirmwareUpdate: 230 case ControllerSupportMode::ShowControllerFirmwareUpdate:
231 case ControllerSupportMode::ShowControllerKeyRemappingForSystem:
213 UNIMPLEMENTED_MSG("ControllerSupportMode={} is not implemented", 232 UNIMPLEMENTED_MSG("ControllerSupportMode={} is not implemented",
214 controller_private_arg.mode); 233 controller_private_arg.mode);
215 ConfigurationComplete(); 234 ConfigurationComplete();
diff --git a/src/core/hle/service/am/applets/applet_controller.h b/src/core/hle/service/am/applets/applet_controller.h
index 20617e91f..0a34c4fc0 100644
--- a/src/core/hle/service/am/applets/applet_controller.h
+++ b/src/core/hle/service/am/applets/applet_controller.h
@@ -25,13 +25,15 @@ enum class ControllerAppletVersion : u32_le {
25 Version3 = 0x3, // 1.0.0 - 2.3.0 25 Version3 = 0x3, // 1.0.0 - 2.3.0
26 Version4 = 0x4, // 3.0.0 - 5.1.0 26 Version4 = 0x4, // 3.0.0 - 5.1.0
27 Version5 = 0x5, // 6.0.0 - 7.0.1 27 Version5 = 0x5, // 6.0.0 - 7.0.1
28 Version7 = 0x7, // 8.0.0+ 28 Version7 = 0x7, // 8.0.0 - 10.2.0
29 Version8 = 0x8, // 11.0.0+
29}; 30};
30 31
31enum class ControllerSupportMode : u8 { 32enum class ControllerSupportMode : u8 {
32 ShowControllerSupport, 33 ShowControllerSupport,
33 ShowControllerStrapGuide, 34 ShowControllerStrapGuide,
34 ShowControllerFirmwareUpdate, 35 ShowControllerFirmwareUpdate,
36 ShowControllerKeyRemappingForSystem,
35 37
36 MaxControllerSupportMode, 38 MaxControllerSupportMode,
37}; 39};
@@ -78,7 +80,7 @@ struct ControllerSupportArgOld {
78static_assert(sizeof(ControllerSupportArgOld) == 0x21C, 80static_assert(sizeof(ControllerSupportArgOld) == 0x21C,
79 "ControllerSupportArgOld has incorrect size."); 81 "ControllerSupportArgOld has incorrect size.");
80 82
81// LibraryAppletVersion 0x7 83// LibraryAppletVersion 0x7, 0x8
82struct ControllerSupportArgNew { 84struct ControllerSupportArgNew {
83 ControllerSupportArgHeader header{}; 85 ControllerSupportArgHeader header{};
84 std::array<IdentificationColor, 8> identification_colors{}; 86 std::array<IdentificationColor, 8> identification_colors{};
@@ -95,6 +97,14 @@ struct ControllerUpdateFirmwareArg {
95static_assert(sizeof(ControllerUpdateFirmwareArg) == 0x4, 97static_assert(sizeof(ControllerUpdateFirmwareArg) == 0x4,
96 "ControllerUpdateFirmwareArg has incorrect size."); 98 "ControllerUpdateFirmwareArg has incorrect size.");
97 99
100struct ControllerKeyRemappingArg {
101 u64 unknown{};
102 u32 unknown_2{};
103 INSERT_PADDING_WORDS(1);
104};
105static_assert(sizeof(ControllerKeyRemappingArg) == 0x10,
106 "ControllerKeyRemappingArg has incorrect size.");
107
98struct ControllerSupportResultInfo { 108struct ControllerSupportResultInfo {
99 s8 player_count{}; 109 s8 player_count{};
100 INSERT_PADDING_BYTES(3); 110 INSERT_PADDING_BYTES(3);
@@ -128,6 +138,7 @@ private:
128 ControllerSupportArgOld controller_user_arg_old; 138 ControllerSupportArgOld controller_user_arg_old;
129 ControllerSupportArgNew controller_user_arg_new; 139 ControllerSupportArgNew controller_user_arg_new;
130 ControllerUpdateFirmwareArg controller_update_arg; 140 ControllerUpdateFirmwareArg controller_update_arg;
141 ControllerKeyRemappingArg controller_key_remapping_arg;
131 bool complete{false}; 142 bool complete{false};
132 ResultCode status{ResultSuccess}; 143 ResultCode status{ResultSuccess};
133 bool is_single_mode{false}; 144 bool is_single_mode{false};
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index 228dc6389..199e69e89 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -206,7 +206,8 @@ AppLoader::~AppLoader() = default;
206 * @return std::unique_ptr<AppLoader> a pointer to a loader object; nullptr for unsupported type 206 * @return std::unique_ptr<AppLoader> a pointer to a loader object; nullptr for unsupported type
207 */ 207 */
208static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::VirtualFile file, 208static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::VirtualFile file,
209 FileType type, std::size_t program_index) { 209 FileType type, u64 program_id,
210 std::size_t program_index) {
210 switch (type) { 211 switch (type) {
211 // Standard ELF file format. 212 // Standard ELF file format.
212 case FileType::ELF: 213 case FileType::ELF:
@@ -227,7 +228,8 @@ static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::V
227 // NX XCI (nX Card Image) file format. 228 // NX XCI (nX Card Image) file format.
228 case FileType::XCI: 229 case FileType::XCI:
229 return std::make_unique<AppLoader_XCI>(std::move(file), system.GetFileSystemController(), 230 return std::make_unique<AppLoader_XCI>(std::move(file), system.GetFileSystemController(),
230 system.GetContentProvider(), program_index); 231 system.GetContentProvider(), program_id,
232 program_index);
231 233
232 // NX NAX (NintendoAesXts) file format. 234 // NX NAX (NintendoAesXts) file format.
233 case FileType::NAX: 235 case FileType::NAX:
@@ -236,7 +238,8 @@ static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::V
236 // NX NSP (Nintendo Submission Package) file format 238 // NX NSP (Nintendo Submission Package) file format
237 case FileType::NSP: 239 case FileType::NSP:
238 return std::make_unique<AppLoader_NSP>(std::move(file), system.GetFileSystemController(), 240 return std::make_unique<AppLoader_NSP>(std::move(file), system.GetFileSystemController(),
239 system.GetContentProvider(), program_index); 241 system.GetContentProvider(), program_id,
242 program_index);
240 243
241 // NX KIP (Kernel Internal Process) file format 244 // NX KIP (Kernel Internal Process) file format
242 case FileType::KIP: 245 case FileType::KIP:
@@ -252,7 +255,7 @@ static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::V
252} 255}
253 256
254std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file, 257std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file,
255 std::size_t program_index) { 258 u64 program_id, std::size_t program_index) {
256 FileType type = IdentifyFile(file); 259 FileType type = IdentifyFile(file);
257 const FileType filename_type = GuessFromFilename(file->GetName()); 260 const FileType filename_type = GuessFromFilename(file->GetName());
258 261
@@ -266,7 +269,7 @@ std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile
266 269
267 LOG_DEBUG(Loader, "Loading file {} as {}...", file->GetName(), GetFileTypeString(type)); 270 LOG_DEBUG(Loader, "Loading file {} as {}...", file->GetName(), GetFileTypeString(type));
268 271
269 return GetFileLoader(system, std::move(file), type, program_index); 272 return GetFileLoader(system, std::move(file), type, program_id, program_index);
270} 273}
271 274
272} // namespace Loader 275} // namespace Loader
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index edc8bb257..7b1bac3f7 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -227,6 +227,17 @@ public:
227 } 227 }
228 228
229 /** 229 /**
230 * Get the program ids of the application
231 *
232 * @param[out] out_program_ids Reference to store program ids into
233 *
234 * @return ResultStatus result of function
235 */
236 virtual ResultStatus ReadProgramIds(std::vector<u64>& out_program_ids) {
237 return ResultStatus::ErrorNotImplemented;
238 }
239
240 /**
230 * Get the RomFS of the application 241 * Get the RomFS of the application
231 * Since the RomFS can be huge, we return a file reference instead of copying to a buffer 242 * Since the RomFS can be huge, we return a file reference instead of copying to a buffer
232 * 243 *
@@ -324,6 +335,6 @@ protected:
324 * @return the best loader for this file. 335 * @return the best loader for this file.
325 */ 336 */
326std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file, 337std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file,
327 std::size_t program_index = 0); 338 u64 program_id = 0, std::size_t program_index = 0);
328 339
329} // namespace Loader 340} // namespace Loader
diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp
index d815a7cd3..8b167ad3c 100644
--- a/src/core/loader/nsp.cpp
+++ b/src/core/loader/nsp.cpp
@@ -23,10 +23,9 @@ namespace Loader {
23 23
24AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file_, 24AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file_,
25 const Service::FileSystem::FileSystemController& fsc, 25 const Service::FileSystem::FileSystemController& fsc,
26 const FileSys::ContentProvider& content_provider, 26 const FileSys::ContentProvider& content_provider, u64 program_id,
27 std::size_t program_index) 27 std::size_t program_index)
28 : AppLoader(file_), nsp(std::make_unique<FileSys::NSP>(file_, program_index)), 28 : AppLoader(file_), nsp(std::make_unique<FileSys::NSP>(file_, program_id, program_index)) {
29 title_id(nsp->GetProgramTitleID()) {
30 29
31 if (nsp->GetStatus() != ResultStatus::Success) { 30 if (nsp->GetStatus() != ResultStatus::Success) {
32 return; 31 return;
@@ -46,12 +45,8 @@ AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file_,
46 return pm.ParseControlNCA(*control_nca); 45 return pm.ParseControlNCA(*control_nca);
47 }(); 46 }();
48 47
49 if (title_id == 0) {
50 return;
51 }
52
53 secondary_loader = std::make_unique<AppLoader_NCA>( 48 secondary_loader = std::make_unique<AppLoader_NCA>(
54 nsp->GetNCAFile(title_id, FileSys::ContentRecordType::Program)); 49 nsp->GetNCAFile(nsp->GetProgramTitleID(), FileSys::ContentRecordType::Program));
55 } 50 }
56} 51}
57 52
@@ -68,10 +63,11 @@ FileType AppLoader_NSP::IdentifyType(const FileSys::VirtualFile& nsp_file) {
68 } 63 }
69 64
70 // Non-Extracted Type case 65 // Non-Extracted Type case
66 const auto program_id = nsp.GetProgramTitleID();
71 if (!nsp.IsExtractedType() && 67 if (!nsp.IsExtractedType() &&
72 nsp.GetNCA(nsp.GetFirstTitleID(), FileSys::ContentRecordType::Program) != nullptr && 68 nsp.GetNCA(program_id, FileSys::ContentRecordType::Program) != nullptr &&
73 AppLoader_NCA::IdentifyType(nsp.GetNCAFile( 69 AppLoader_NCA::IdentifyType(
74 nsp.GetFirstTitleID(), FileSys::ContentRecordType::Program)) == FileType::NCA) { 70 nsp.GetNCAFile(program_id, FileSys::ContentRecordType::Program)) == FileType::NCA) {
75 return FileType::NSP; 71 return FileType::NSP;
76 } 72 }
77 } 73 }
@@ -84,6 +80,8 @@ AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::KProcess& process, Core::S
84 return {ResultStatus::ErrorAlreadyLoaded, {}}; 80 return {ResultStatus::ErrorAlreadyLoaded, {}};
85 } 81 }
86 82
83 const auto title_id = nsp->GetProgramTitleID();
84
87 if (!nsp->IsExtractedType() && title_id == 0) { 85 if (!nsp->IsExtractedType() && title_id == 0) {
88 return {ResultStatus::ErrorNSPMissingProgramNCA, {}}; 86 return {ResultStatus::ErrorNSPMissingProgramNCA, {}};
89 } 87 }
@@ -93,7 +91,7 @@ AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::KProcess& process, Core::S
93 return {nsp_status, {}}; 91 return {nsp_status, {}};
94 } 92 }
95 93
96 const auto nsp_program_status = nsp->GetProgramStatus(title_id); 94 const auto nsp_program_status = nsp->GetProgramStatus();
97 if (nsp_program_status != ResultStatus::Success) { 95 if (nsp_program_status != ResultStatus::Success) {
98 return {nsp_program_status, {}}; 96 return {nsp_program_status, {}};
99 } 97 }
@@ -134,8 +132,8 @@ ResultStatus AppLoader_NSP::ReadUpdateRaw(FileSys::VirtualFile& out_file) {
134 return ResultStatus::ErrorNoPackedUpdate; 132 return ResultStatus::ErrorNoPackedUpdate;
135 } 133 }
136 134
137 const auto read = 135 const auto read = nsp->GetNCAFile(FileSys::GetUpdateTitleID(nsp->GetProgramTitleID()),
138 nsp->GetNCAFile(FileSys::GetUpdateTitleID(title_id), FileSys::ContentRecordType::Program); 136 FileSys::ContentRecordType::Program);
139 137
140 if (read == nullptr) { 138 if (read == nullptr) {
141 return ResultStatus::ErrorNoPackedUpdate; 139 return ResultStatus::ErrorNoPackedUpdate;
@@ -151,11 +149,15 @@ ResultStatus AppLoader_NSP::ReadUpdateRaw(FileSys::VirtualFile& out_file) {
151} 149}
152 150
153ResultStatus AppLoader_NSP::ReadProgramId(u64& out_program_id) { 151ResultStatus AppLoader_NSP::ReadProgramId(u64& out_program_id) {
154 if (title_id == 0) { 152 out_program_id = nsp->GetProgramTitleID();
153 if (out_program_id == 0) {
155 return ResultStatus::ErrorNotInitialized; 154 return ResultStatus::ErrorNotInitialized;
156 } 155 }
156 return ResultStatus::Success;
157}
157 158
158 out_program_id = title_id; 159ResultStatus AppLoader_NSP::ReadProgramIds(std::vector<u64>& out_program_ids) {
160 out_program_ids = nsp->GetProgramTitleIDs();
159 return ResultStatus::Success; 161 return ResultStatus::Success;
160} 162}
161 163
diff --git a/src/core/loader/nsp.h b/src/core/loader/nsp.h
index 644c0ff58..50406a92e 100644
--- a/src/core/loader/nsp.h
+++ b/src/core/loader/nsp.h
@@ -28,7 +28,7 @@ class AppLoader_NSP final : public AppLoader {
28public: 28public:
29 explicit AppLoader_NSP(FileSys::VirtualFile file_, 29 explicit AppLoader_NSP(FileSys::VirtualFile file_,
30 const Service::FileSystem::FileSystemController& fsc, 30 const Service::FileSystem::FileSystemController& fsc,
31 const FileSys::ContentProvider& content_provider, 31 const FileSys::ContentProvider& content_provider, u64 program_id,
32 std::size_t program_index); 32 std::size_t program_index);
33 ~AppLoader_NSP() override; 33 ~AppLoader_NSP() override;
34 34
@@ -51,6 +51,7 @@ public:
51 u64 ReadRomFSIVFCOffset() const override; 51 u64 ReadRomFSIVFCOffset() const override;
52 ResultStatus ReadUpdateRaw(FileSys::VirtualFile& out_file) override; 52 ResultStatus ReadUpdateRaw(FileSys::VirtualFile& out_file) override;
53 ResultStatus ReadProgramId(u64& out_program_id) override; 53 ResultStatus ReadProgramId(u64& out_program_id) override;
54 ResultStatus ReadProgramIds(std::vector<u64>& out_program_ids) override;
54 ResultStatus ReadIcon(std::vector<u8>& buffer) override; 55 ResultStatus ReadIcon(std::vector<u8>& buffer) override;
55 ResultStatus ReadTitle(std::string& title) override; 56 ResultStatus ReadTitle(std::string& title) override;
56 ResultStatus ReadControlData(FileSys::NACP& nacp) override; 57 ResultStatus ReadControlData(FileSys::NACP& nacp) override;
@@ -67,7 +68,6 @@ private:
67 68
68 FileSys::VirtualFile icon_file; 69 FileSys::VirtualFile icon_file;
69 std::unique_ptr<FileSys::NACP> nacp_file; 70 std::unique_ptr<FileSys::NACP> nacp_file;
70 u64 title_id;
71}; 71};
72 72
73} // namespace Loader 73} // namespace Loader
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp
index 635d6ae15..269603eef 100644
--- a/src/core/loader/xci.cpp
+++ b/src/core/loader/xci.cpp
@@ -22,9 +22,9 @@ namespace Loader {
22 22
23AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file_, 23AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file_,
24 const Service::FileSystem::FileSystemController& fsc, 24 const Service::FileSystem::FileSystemController& fsc,
25 const FileSys::ContentProvider& content_provider, 25 const FileSys::ContentProvider& content_provider, u64 program_id,
26 std::size_t program_index) 26 std::size_t program_index)
27 : AppLoader(file_), xci(std::make_unique<FileSys::XCI>(file_, program_index)), 27 : AppLoader(file_), xci(std::make_unique<FileSys::XCI>(file_, program_id, program_index)),
28 nca_loader(std::make_unique<AppLoader_NCA>(xci->GetProgramNCAFile())) { 28 nca_loader(std::make_unique<AppLoader_NCA>(xci->GetProgramNCAFile())) {
29 if (xci->GetStatus() != ResultStatus::Success) { 29 if (xci->GetStatus() != ResultStatus::Success) {
30 return; 30 return;
@@ -121,6 +121,11 @@ ResultStatus AppLoader_XCI::ReadProgramId(u64& out_program_id) {
121 return nca_loader->ReadProgramId(out_program_id); 121 return nca_loader->ReadProgramId(out_program_id);
122} 122}
123 123
124ResultStatus AppLoader_XCI::ReadProgramIds(std::vector<u64>& out_program_ids) {
125 out_program_ids = xci->GetProgramTitleIDs();
126 return ResultStatus::Success;
127}
128
124ResultStatus AppLoader_XCI::ReadIcon(std::vector<u8>& buffer) { 129ResultStatus AppLoader_XCI::ReadIcon(std::vector<u8>& buffer) {
125 if (icon_file == nullptr) { 130 if (icon_file == nullptr) {
126 return ResultStatus::ErrorNoControl; 131 return ResultStatus::ErrorNoControl;
@@ -149,8 +154,9 @@ ResultStatus AppLoader_XCI::ReadControlData(FileSys::NACP& control) {
149} 154}
150 155
151ResultStatus AppLoader_XCI::ReadManualRomFS(FileSys::VirtualFile& out_file) { 156ResultStatus AppLoader_XCI::ReadManualRomFS(FileSys::VirtualFile& out_file) {
152 const auto nca = xci->GetSecurePartitionNSP()->GetNCA(xci->GetProgramTitleID(), 157 const auto nca =
153 FileSys::ContentRecordType::HtmlDocument); 158 xci->GetSecurePartitionNSP()->GetNCA(xci->GetSecurePartitionNSP()->GetProgramTitleID(),
159 FileSys::ContentRecordType::HtmlDocument);
154 if (xci->GetStatus() != ResultStatus::Success || nca == nullptr) { 160 if (xci->GetStatus() != ResultStatus::Success || nca == nullptr) {
155 return ResultStatus::ErrorXCIMissingPartition; 161 return ResultStatus::ErrorXCIMissingPartition;
156 } 162 }
diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h
index 708155c30..30caaf90e 100644
--- a/src/core/loader/xci.h
+++ b/src/core/loader/xci.h
@@ -28,7 +28,7 @@ class AppLoader_XCI final : public AppLoader {
28public: 28public:
29 explicit AppLoader_XCI(FileSys::VirtualFile file_, 29 explicit AppLoader_XCI(FileSys::VirtualFile file_,
30 const Service::FileSystem::FileSystemController& fsc, 30 const Service::FileSystem::FileSystemController& fsc,
31 const FileSys::ContentProvider& content_provider, 31 const FileSys::ContentProvider& content_provider, u64 program_id,
32 std::size_t program_index); 32 std::size_t program_index);
33 ~AppLoader_XCI() override; 33 ~AppLoader_XCI() override;
34 34
@@ -51,6 +51,7 @@ public:
51 u64 ReadRomFSIVFCOffset() const override; 51 u64 ReadRomFSIVFCOffset() const override;
52 ResultStatus ReadUpdateRaw(FileSys::VirtualFile& out_file) override; 52 ResultStatus ReadUpdateRaw(FileSys::VirtualFile& out_file) override;
53 ResultStatus ReadProgramId(u64& out_program_id) override; 53 ResultStatus ReadProgramId(u64& out_program_id) override;
54 ResultStatus ReadProgramIds(std::vector<u64>& out_program_ids) override;
54 ResultStatus ReadIcon(std::vector<u8>& buffer) override; 55 ResultStatus ReadIcon(std::vector<u8>& buffer) override;
55 ResultStatus ReadTitle(std::string& title) override; 56 ResultStatus ReadTitle(std::string& title) override;
56 ResultStatus ReadControlData(FileSys::NACP& control) override; 57 ResultStatus ReadControlData(FileSys::NACP& control) override;
diff --git a/src/input_common/mouse/mouse_poller.cpp b/src/input_common/mouse/mouse_poller.cpp
index 1e84eaddd..efcdd85d2 100644
--- a/src/input_common/mouse/mouse_poller.cpp
+++ b/src/input_common/mouse/mouse_poller.cpp
@@ -84,7 +84,7 @@ public:
84 std::lock_guard lock{mutex}; 84 std::lock_guard lock{mutex};
85 const auto axis_value = 85 const auto axis_value =
86 static_cast<float>(mouse_input->GetMouseState(button).axis.at(axis)); 86 static_cast<float>(mouse_input->GetMouseState(button).axis.at(axis));
87 const float sensitivity = Settings::values.mouse_panning_sensitivity.GetValue() * 0.15f; 87 const float sensitivity = Settings::values.mouse_panning_sensitivity.GetValue() * 0.10f;
88 return axis_value * sensitivity / (100.0f * range); 88 return axis_value * sensitivity / (100.0f * range);
89 } 89 }
90 90
diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp
index 2f7ccdd37..70a0ba09c 100644
--- a/src/input_common/sdl/sdl_impl.cpp
+++ b/src/input_common/sdl/sdl_impl.cpp
@@ -115,6 +115,41 @@ public:
115 return state.buttons.at(button); 115 return state.buttons.at(button);
116 } 116 }
117 117
118 bool ToggleButton(int button) {
119 std::lock_guard lock{mutex};
120
121 if (!state.toggle_buttons.contains(button) || !state.lock_buttons.contains(button)) {
122 state.toggle_buttons.insert_or_assign(button, false);
123 state.lock_buttons.insert_or_assign(button, false);
124 }
125
126 const bool button_state = state.toggle_buttons.at(button);
127 const bool button_lock = state.lock_buttons.at(button);
128
129 if (button_lock) {
130 return button_state;
131 }
132
133 state.lock_buttons.insert_or_assign(button, true);
134
135 if (button_state) {
136 state.toggle_buttons.insert_or_assign(button, false);
137 } else {
138 state.toggle_buttons.insert_or_assign(button, true);
139 }
140
141 return !button_state;
142 }
143
144 bool UnlockButton(int button) {
145 std::lock_guard lock{mutex};
146 if (!state.toggle_buttons.contains(button)) {
147 return false;
148 }
149 state.lock_buttons.insert_or_assign(button, false);
150 return state.toggle_buttons.at(button);
151 }
152
118 void SetAxis(int axis, Sint16 value) { 153 void SetAxis(int axis, Sint16 value) {
119 std::lock_guard lock{mutex}; 154 std::lock_guard lock{mutex};
120 state.axes.insert_or_assign(axis, value); 155 state.axes.insert_or_assign(axis, value);
@@ -130,10 +165,10 @@ public:
130 165
131 if (sdl_controller) { 166 if (sdl_controller) {
132 return SDL_GameControllerRumble(sdl_controller.get(), amp_low, amp_high, 167 return SDL_GameControllerRumble(sdl_controller.get(), amp_low, amp_high,
133 rumble_max_duration_ms) == 0; 168 rumble_max_duration_ms) != -1;
134 } else if (sdl_joystick) { 169 } else if (sdl_joystick) {
135 return SDL_JoystickRumble(sdl_joystick.get(), amp_low, amp_high, 170 return SDL_JoystickRumble(sdl_joystick.get(), amp_low, amp_high,
136 rumble_max_duration_ms) == 0; 171 rumble_max_duration_ms) != -1;
137 } 172 }
138 173
139 return false; 174 return false;
@@ -241,6 +276,8 @@ public:
241private: 276private:
242 struct State { 277 struct State {
243 std::unordered_map<int, bool> buttons; 278 std::unordered_map<int, bool> buttons;
279 std::unordered_map<int, bool> toggle_buttons{};
280 std::unordered_map<int, bool> lock_buttons{};
244 std::unordered_map<int, Sint16> axes; 281 std::unordered_map<int, Sint16> axes;
245 std::unordered_map<int, Uint8> hats; 282 std::unordered_map<int, Uint8> hats;
246 } state; 283 } state;
@@ -402,16 +439,25 @@ void SDLState::CloseJoysticks() {
402 439
403class SDLButton final : public Input::ButtonDevice { 440class SDLButton final : public Input::ButtonDevice {
404public: 441public:
405 explicit SDLButton(std::shared_ptr<SDLJoystick> joystick_, int button_) 442 explicit SDLButton(std::shared_ptr<SDLJoystick> joystick_, int button_, bool toggle_)
406 : joystick(std::move(joystick_)), button(button_) {} 443 : joystick(std::move(joystick_)), button(button_), toggle(toggle_) {}
407 444
408 bool GetStatus() const override { 445 bool GetStatus() const override {
409 return joystick->GetButton(button); 446 const bool button_state = joystick->GetButton(button);
447 if (!toggle) {
448 return button_state;
449 }
450
451 if (button_state) {
452 return joystick->ToggleButton(button);
453 }
454 return joystick->UnlockButton(button);
410 } 455 }
411 456
412private: 457private:
413 std::shared_ptr<SDLJoystick> joystick; 458 std::shared_ptr<SDLJoystick> joystick;
414 int button; 459 int button;
460 bool toggle;
415}; 461};
416 462
417class SDLDirectionButton final : public Input::ButtonDevice { 463class SDLDirectionButton final : public Input::ButtonDevice {
@@ -635,6 +681,7 @@ public:
635 std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override { 681 std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override {
636 const std::string guid = params.Get("guid", "0"); 682 const std::string guid = params.Get("guid", "0");
637 const int port = params.Get("port", 0); 683 const int port = params.Get("port", 0);
684 const auto toggle = params.Get("toggle", false);
638 685
639 auto joystick = state.GetSDLJoystickByGUID(guid, port); 686 auto joystick = state.GetSDLJoystickByGUID(guid, port);
640 687
@@ -680,7 +727,7 @@ public:
680 const int button = params.Get("button", 0); 727 const int button = params.Get("button", 0);
681 // This is necessary so accessing GetButton with button won't crash 728 // This is necessary so accessing GetButton with button won't crash
682 joystick->SetButton(button, false); 729 joystick->SetButton(button, false);
683 return std::make_unique<SDLButton>(joystick, button); 730 return std::make_unique<SDLButton>(joystick, button, toggle);
684 } 731 }
685 732
686private: 733private:
diff --git a/src/input_common/sdl/sdl_impl.h b/src/input_common/sdl/sdl_impl.h
index b77afcbd8..7a9ad6346 100644
--- a/src/input_common/sdl/sdl_impl.h
+++ b/src/input_common/sdl/sdl_impl.h
@@ -10,15 +10,7 @@
10#include <thread> 10#include <thread>
11#include <unordered_map> 11#include <unordered_map>
12 12
13// Ignore -Wimplicit-fallthrough due to https://github.com/libsdl-org/SDL/issues/4307
14#ifdef __GNUC__
15#pragma GCC diagnostic push
16#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
17#endif
18#include <SDL.h> 13#include <SDL.h>
19#ifdef __GNUC__
20#pragma GCC diagnostic pop
21#endif
22 14
23#include "common/common_types.h" 15#include "common/common_types.h"
24#include "common/threadsafe_queue.h" 16#include "common/threadsafe_queue.h"
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index 2871682f6..7373cb62d 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -164,11 +164,16 @@ public:
164 /// Pop asynchronous downloads 164 /// Pop asynchronous downloads
165 void PopAsyncFlushes(); 165 void PopAsyncFlushes();
166 166
167 [[nodiscard]] bool DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount); 167 bool DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount);
168
169 bool DMAClear(GPUVAddr src_address, u64 amount, u32 value);
168 170
169 /// Return true when a CPU region is modified from the GPU 171 /// Return true when a CPU region is modified from the GPU
170 [[nodiscard]] bool IsRegionGpuModified(VAddr addr, size_t size); 172 [[nodiscard]] bool IsRegionGpuModified(VAddr addr, size_t size);
171 173
174 /// Return true when a region is registered on the cache
175 [[nodiscard]] bool IsRegionRegistered(VAddr addr, size_t size);
176
172 /// Return true when a CPU region is modified from the CPU 177 /// Return true when a CPU region is modified from the CPU
173 [[nodiscard]] bool IsRegionCpuModified(VAddr addr, size_t size); 178 [[nodiscard]] bool IsRegionCpuModified(VAddr addr, size_t size);
174 179
@@ -324,6 +329,8 @@ private:
324 329
325 [[nodiscard]] bool HasFastUniformBufferBound(size_t stage, u32 binding_index) const noexcept; 330 [[nodiscard]] bool HasFastUniformBufferBound(size_t stage, u32 binding_index) const noexcept;
326 331
332 void ClearDownload(IntervalType subtract_interval);
333
327 VideoCore::RasterizerInterface& rasterizer; 334 VideoCore::RasterizerInterface& rasterizer;
328 Tegra::Engines::Maxwell3D& maxwell3d; 335 Tegra::Engines::Maxwell3D& maxwell3d;
329 Tegra::Engines::KeplerCompute& kepler_compute; 336 Tegra::Engines::KeplerCompute& kepler_compute;
@@ -463,23 +470,28 @@ void BufferCache<P>::DownloadMemory(VAddr cpu_addr, u64 size) {
463} 470}
464 471
465template <class P> 472template <class P>
473void BufferCache<P>::ClearDownload(IntervalType subtract_interval) {
474 uncommitted_ranges.subtract(subtract_interval);
475 for (auto& interval_set : committed_ranges) {
476 interval_set.subtract(subtract_interval);
477 }
478}
479
480template <class P>
466bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount) { 481bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount) {
467 const std::optional<VAddr> cpu_src_address = gpu_memory.GpuToCpuAddress(src_address); 482 const std::optional<VAddr> cpu_src_address = gpu_memory.GpuToCpuAddress(src_address);
468 const std::optional<VAddr> cpu_dest_address = gpu_memory.GpuToCpuAddress(dest_address); 483 const std::optional<VAddr> cpu_dest_address = gpu_memory.GpuToCpuAddress(dest_address);
469 if (!cpu_src_address || !cpu_dest_address) { 484 if (!cpu_src_address || !cpu_dest_address) {
470 return false; 485 return false;
471 } 486 }
472 const bool source_dirty = IsRegionGpuModified(*cpu_src_address, amount); 487 const bool source_dirty = IsRegionRegistered(*cpu_src_address, amount);
473 const bool dest_dirty = IsRegionGpuModified(*cpu_dest_address, amount); 488 const bool dest_dirty = IsRegionRegistered(*cpu_dest_address, amount);
474 if (!source_dirty && !dest_dirty) { 489 if (!source_dirty && !dest_dirty) {
475 return false; 490 return false;
476 } 491 }
477 492
478 const IntervalType subtract_interval{*cpu_dest_address, *cpu_dest_address + amount}; 493 const IntervalType subtract_interval{*cpu_dest_address, *cpu_dest_address + amount};
479 uncommitted_ranges.subtract(subtract_interval); 494 ClearDownload(subtract_interval);
480 for (auto& interval_set : committed_ranges) {
481 interval_set.subtract(subtract_interval);
482 }
483 495
484 BufferId buffer_a; 496 BufferId buffer_a;
485 BufferId buffer_b; 497 BufferId buffer_b;
@@ -510,12 +522,13 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
510 ForEachWrittenRange(*cpu_src_address, amount, mirror); 522 ForEachWrittenRange(*cpu_src_address, amount, mirror);
511 // This subtraction in this order is important for overlapping copies. 523 // This subtraction in this order is important for overlapping copies.
512 common_ranges.subtract(subtract_interval); 524 common_ranges.subtract(subtract_interval);
525 bool atleast_1_download = tmp_intervals.size() != 0;
513 for (const IntervalType add_interval : tmp_intervals) { 526 for (const IntervalType add_interval : tmp_intervals) {
514 common_ranges.add(add_interval); 527 common_ranges.add(add_interval);
515 } 528 }
516 529
517 runtime.CopyBuffer(dest_buffer, src_buffer, copies); 530 runtime.CopyBuffer(dest_buffer, src_buffer, copies);
518 if (source_dirty) { 531 if (atleast_1_download) {
519 dest_buffer.MarkRegionAsGpuModified(*cpu_dest_address, amount); 532 dest_buffer.MarkRegionAsGpuModified(*cpu_dest_address, amount);
520 } 533 }
521 std::vector<u8> tmp_buffer(amount); 534 std::vector<u8> tmp_buffer(amount);
@@ -525,6 +538,33 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
525} 538}
526 539
527template <class P> 540template <class P>
541bool BufferCache<P>::DMAClear(GPUVAddr dst_address, u64 amount, u32 value) {
542 const std::optional<VAddr> cpu_dst_address = gpu_memory.GpuToCpuAddress(dst_address);
543 if (!cpu_dst_address) {
544 return false;
545 }
546 const bool dest_dirty = IsRegionRegistered(*cpu_dst_address, amount);
547 if (!dest_dirty) {
548 return false;
549 }
550
551 const size_t size = amount * sizeof(u32);
552 const IntervalType subtract_interval{*cpu_dst_address, *cpu_dst_address + size};
553 ClearDownload(subtract_interval);
554 common_ranges.subtract(subtract_interval);
555
556 BufferId buffer;
557 do {
558 has_deleted_buffers = false;
559 buffer = FindBuffer(*cpu_dst_address, static_cast<u32>(size));
560 } while (has_deleted_buffers);
561 auto& dest_buffer = slot_buffers[buffer];
562 const u32 offset = static_cast<u32>(*cpu_dst_address - dest_buffer.CpuAddr());
563 runtime.ClearBuffer(dest_buffer, offset, size, value);
564 return true;
565}
566
567template <class P>
528void BufferCache<P>::BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, 568void BufferCache<P>::BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr,
529 u32 size) { 569 u32 size) {
530 const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr); 570 const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr);
@@ -782,6 +822,27 @@ bool BufferCache<P>::IsRegionGpuModified(VAddr addr, size_t size) {
782} 822}
783 823
784template <class P> 824template <class P>
825bool BufferCache<P>::IsRegionRegistered(VAddr addr, size_t size) {
826 const VAddr end_addr = addr + size;
827 const u64 page_end = Common::DivCeil(end_addr, PAGE_SIZE);
828 for (u64 page = addr >> PAGE_BITS; page < page_end;) {
829 const BufferId buffer_id = page_table[page];
830 if (!buffer_id) {
831 ++page;
832 continue;
833 }
834 Buffer& buffer = slot_buffers[buffer_id];
835 const VAddr buf_start_addr = buffer.CpuAddr();
836 const VAddr buf_end_addr = buf_start_addr + buffer.SizeBytes();
837 if (buf_start_addr < end_addr && addr < buf_end_addr) {
838 return true;
839 }
840 page = Common::DivCeil(end_addr, PAGE_SIZE);
841 }
842 return false;
843}
844
845template <class P>
785bool BufferCache<P>::IsRegionCpuModified(VAddr addr, size_t size) { 846bool BufferCache<P>::IsRegionCpuModified(VAddr addr, size_t size) {
786 const u64 page_end = Common::DivCeil(addr + size, PAGE_SIZE); 847 const u64 page_end = Common::DivCeil(addr + size, PAGE_SIZE);
787 for (u64 page = addr >> PAGE_BITS; page < page_end;) { 848 for (u64 page = addr >> PAGE_BITS; page < page_end;) {
@@ -1425,6 +1486,7 @@ void BufferCache<P>::DownloadBufferMemory(Buffer& buffer, VAddr cpu_addr, u64 si
1425 const VAddr end_address = start_address + range_size; 1486 const VAddr end_address = start_address + range_size;
1426 ForEachWrittenRange(start_address, range_size, add_download); 1487 ForEachWrittenRange(start_address, range_size, add_download);
1427 const IntervalType subtract_interval{start_address, end_address}; 1488 const IntervalType subtract_interval{start_address, end_address};
1489 ClearDownload(subtract_interval);
1428 common_ranges.subtract(subtract_interval); 1490 common_ranges.subtract(subtract_interval);
1429 }); 1491 });
1430 if (total_size_bytes == 0) { 1492 if (total_size_bytes == 0) {
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index 24481952b..c51776466 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -4,6 +4,7 @@
4 4
5#include "common/assert.h" 5#include "common/assert.h"
6#include "common/logging/log.h" 6#include "common/logging/log.h"
7#include "common/microprofile.h"
7#include "common/settings.h" 8#include "common/settings.h"
8#include "core/core.h" 9#include "core/core.h"
9#include "video_core/engines/maxwell_3d.h" 10#include "video_core/engines/maxwell_3d.h"
@@ -12,6 +13,9 @@
12#include "video_core/renderer_base.h" 13#include "video_core/renderer_base.h"
13#include "video_core/textures/decoders.h" 14#include "video_core/textures/decoders.h"
14 15
16MICROPROFILE_DECLARE(GPU_DMAEngine);
17MICROPROFILE_DEFINE(GPU_DMAEngine, "GPU", "DMA Engine", MP_RGB(224, 224, 128));
18
15namespace Tegra::Engines { 19namespace Tegra::Engines {
16 20
17using namespace Texture; 21using namespace Texture;
@@ -43,6 +47,7 @@ void MaxwellDMA::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
43} 47}
44 48
45void MaxwellDMA::Launch() { 49void MaxwellDMA::Launch() {
50 MICROPROFILE_SCOPE(GPU_DMAEngine);
46 LOG_TRACE(Render_OpenGL, "DMA copy 0x{:x} -> 0x{:x}", static_cast<GPUVAddr>(regs.offset_in), 51 LOG_TRACE(Render_OpenGL, "DMA copy 0x{:x} -> 0x{:x}", static_cast<GPUVAddr>(regs.offset_in),
47 static_cast<GPUVAddr>(regs.offset_out)); 52 static_cast<GPUVAddr>(regs.offset_out));
48 53
@@ -87,9 +92,11 @@ void MaxwellDMA::CopyPitchToPitch() {
87 // TODO: allow multisized components. 92 // TODO: allow multisized components.
88 if (is_buffer_clear) { 93 if (is_buffer_clear) {
89 ASSERT(regs.remap_const.component_size_minus_one == 3); 94 ASSERT(regs.remap_const.component_size_minus_one == 3);
95 accelerate.BufferClear(regs.offset_out, regs.line_length_in, regs.remap_consta_value);
90 std::vector<u32> tmp_buffer(regs.line_length_in, regs.remap_consta_value); 96 std::vector<u32> tmp_buffer(regs.line_length_in, regs.remap_consta_value);
91 memory_manager.WriteBlock(regs.offset_out, reinterpret_cast<u8*>(tmp_buffer.data()), 97 memory_manager.WriteBlockUnsafe(regs.offset_out,
92 regs.line_length_in * sizeof(u32)); 98 reinterpret_cast<u8*>(tmp_buffer.data()),
99 regs.line_length_in * sizeof(u32));
93 return; 100 return;
94 } 101 }
95 UNIMPLEMENTED_IF(regs.launch_dma.remap_enable != 0); 102 UNIMPLEMENTED_IF(regs.launch_dma.remap_enable != 0);
@@ -179,8 +186,13 @@ void MaxwellDMA::CopyPitchToBlockLinear() {
179 write_buffer.resize(dst_size); 186 write_buffer.resize(dst_size);
180 } 187 }
181 188
182 memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size); 189 if (Settings::IsGPULevelExtreme()) {
183 memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size); 190 memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size);
191 memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size);
192 } else {
193 memory_manager.ReadBlockUnsafe(regs.offset_in, read_buffer.data(), src_size);
194 memory_manager.ReadBlockUnsafe(regs.offset_out, write_buffer.data(), dst_size);
195 }
184 196
185 // If the input is linear and the output is tiled, swizzle the input and copy it over. 197 // If the input is linear and the output is tiled, swizzle the input and copy it over.
186 if (regs.dst_params.block_size.depth > 0) { 198 if (regs.dst_params.block_size.depth > 0) {
diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h
index 4ed0d0996..d3329b0f8 100644
--- a/src/video_core/engines/maxwell_dma.h
+++ b/src/video_core/engines/maxwell_dma.h
@@ -31,6 +31,8 @@ class AccelerateDMAInterface {
31public: 31public:
32 /// Write the value to the register identified by method. 32 /// Write the value to the register identified by method.
33 virtual bool BufferCopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount) = 0; 33 virtual bool BufferCopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount) = 0;
34
35 virtual bool BufferClear(GPUVAddr src_address, u64 amount, u32 value) = 0;
34}; 36};
35 37
36/** 38/**
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
index c225d1fc9..c4189fb60 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
@@ -98,6 +98,12 @@ void BufferCacheRuntime::CopyBuffer(Buffer& dst_buffer, Buffer& src_buffer,
98 } 98 }
99} 99}
100 100
101void BufferCacheRuntime::ClearBuffer(Buffer& dest_buffer, u32 offset, size_t size, u32 value) {
102 glClearNamedBufferSubData(dest_buffer.Handle(), GL_R32UI, static_cast<GLintptr>(offset),
103 static_cast<GLsizeiptr>(size / sizeof(u32)), GL_RGBA, GL_UNSIGNED_INT,
104 &value);
105}
106
101void BufferCacheRuntime::BindIndexBuffer(Buffer& buffer, u32 offset, u32 size) { 107void BufferCacheRuntime::BindIndexBuffer(Buffer& buffer, u32 offset, u32 size) {
102 if (has_unified_vertex_buffers) { 108 if (has_unified_vertex_buffers) {
103 buffer.MakeResident(GL_READ_ONLY); 109 buffer.MakeResident(GL_READ_ONLY);
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h
index d8b20a9af..fe91aa452 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.h
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.h
@@ -57,6 +57,8 @@ public:
57 void CopyBuffer(Buffer& dst_buffer, Buffer& src_buffer, 57 void CopyBuffer(Buffer& dst_buffer, Buffer& src_buffer,
58 std::span<const VideoCommon::BufferCopy> copies); 58 std::span<const VideoCommon::BufferCopy> copies);
59 59
60 void ClearBuffer(Buffer& dest_buffer, u32 offset, size_t size, u32 value);
61
60 void BindIndexBuffer(Buffer& buffer, u32 offset, u32 size); 62 void BindIndexBuffer(Buffer& buffer, u32 offset, u32 size);
61 63
62 void BindVertexBuffer(u32 index, Buffer& buffer, u32 offset, u32 size, u32 stride); 64 void BindVertexBuffer(u32 index, Buffer& buffer, u32 offset, u32 size, u32 stride);
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 82c84127a..ceb3abcb2 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -1407,4 +1407,9 @@ bool AccelerateDMA::BufferCopy(GPUVAddr src_address, GPUVAddr dest_address, u64
1407 return buffer_cache.DMACopy(src_address, dest_address, amount); 1407 return buffer_cache.DMACopy(src_address, dest_address, amount);
1408} 1408}
1409 1409
1410bool AccelerateDMA::BufferClear(GPUVAddr src_address, u64 amount, u32 value) {
1411 std::scoped_lock lock{buffer_cache.mutex};
1412 return buffer_cache.DMAClear(src_address, amount, value);
1413}
1414
1410} // namespace OpenGL 1415} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index ccee9ba33..d30ad698f 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -65,6 +65,8 @@ public:
65 65
66 bool BufferCopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount) override; 66 bool BufferCopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount) override;
67 67
68 bool BufferClear(GPUVAddr src_address, u64 amount, u32 value) override;
69
68private: 70private:
69 BufferCache& buffer_cache; 71 BufferCache& buffer_cache;
70}; 72};
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h
index 25fe61566..cf3b789e3 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.h
+++ b/src/video_core/renderer_opengl/gl_texture_cache.h
@@ -122,7 +122,7 @@ private:
122 bool has_broken_texture_view_formats = false; 122 bool has_broken_texture_view_formats = false;
123 123
124 StagingBuffers upload_buffers{GL_MAP_WRITE_BIT, GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT}; 124 StagingBuffers upload_buffers{GL_MAP_WRITE_BIT, GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT};
125 StagingBuffers download_buffers{GL_MAP_READ_BIT, GL_MAP_READ_BIT}; 125 StagingBuffers download_buffers{GL_MAP_READ_BIT | GL_CLIENT_STORAGE_BIT, GL_MAP_READ_BIT};
126 126
127 OGLTexture null_image_1d_array; 127 OGLTexture null_image_1d_array;
128 OGLTexture null_image_cube_array; 128 OGLTexture null_image_cube_array;
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
index 0df4e1a1c..0def1e769 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
@@ -136,6 +136,30 @@ void BufferCacheRuntime::CopyBuffer(VkBuffer dst_buffer, VkBuffer src_buffer,
136 }); 136 });
137} 137}
138 138
139void BufferCacheRuntime::ClearBuffer(VkBuffer dest_buffer, u32 offset, size_t size, u32 value) {
140 static constexpr VkMemoryBarrier READ_BARRIER{
141 .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
142 .pNext = nullptr,
143 .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
144 .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
145 };
146 static constexpr VkMemoryBarrier WRITE_BARRIER{
147 .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
148 .pNext = nullptr,
149 .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
150 .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,
151 };
152
153 scheduler.RequestOutsideRenderPassOperationContext();
154 scheduler.Record([dest_buffer, offset, size, value](vk::CommandBuffer cmdbuf) {
155 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
156 0, READ_BARRIER);
157 cmdbuf.FillBuffer(dest_buffer, offset, size, value);
158 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
159 0, WRITE_BARRIER);
160 });
161}
162
139void BufferCacheRuntime::BindIndexBuffer(PrimitiveTopology topology, IndexFormat index_format, 163void BufferCacheRuntime::BindIndexBuffer(PrimitiveTopology topology, IndexFormat index_format,
140 u32 base_vertex, u32 num_indices, VkBuffer buffer, 164 u32 base_vertex, u32 num_indices, VkBuffer buffer,
141 u32 offset, [[maybe_unused]] u32 size) { 165 u32 offset, [[maybe_unused]] u32 size) {
@@ -152,8 +176,8 @@ void BufferCacheRuntime::BindIndexBuffer(PrimitiveTopology topology, IndexFormat
152 } 176 }
153 if (vk_buffer == VK_NULL_HANDLE) { 177 if (vk_buffer == VK_NULL_HANDLE) {
154 // Vulkan doesn't support null index buffers. Replace it with our own null buffer. 178 // Vulkan doesn't support null index buffers. Replace it with our own null buffer.
155 ReserveNullIndexBuffer(); 179 ReserveNullBuffer();
156 vk_buffer = *null_index_buffer; 180 vk_buffer = *null_buffer;
157 } 181 }
158 scheduler.Record([vk_buffer, vk_offset, vk_index_type](vk::CommandBuffer cmdbuf) { 182 scheduler.Record([vk_buffer, vk_offset, vk_index_type](vk::CommandBuffer cmdbuf) {
159 cmdbuf.BindIndexBuffer(vk_buffer, vk_offset, vk_index_type); 183 cmdbuf.BindIndexBuffer(vk_buffer, vk_offset, vk_index_type);
@@ -161,6 +185,13 @@ void BufferCacheRuntime::BindIndexBuffer(PrimitiveTopology topology, IndexFormat
161} 185}
162 186
163void BufferCacheRuntime::BindQuadArrayIndexBuffer(u32 first, u32 count) { 187void BufferCacheRuntime::BindQuadArrayIndexBuffer(u32 first, u32 count) {
188 if (count == 0) {
189 ReserveNullBuffer();
190 scheduler.Record([this](vk::CommandBuffer cmdbuf) {
191 cmdbuf.BindIndexBuffer(*null_buffer, 0, VK_INDEX_TYPE_UINT32);
192 });
193 return;
194 }
164 ReserveQuadArrayLUT(first + count, true); 195 ReserveQuadArrayLUT(first + count, true);
165 196
166 // The LUT has the indices 0, 1, 2, and 3 copied as an array 197 // The LUT has the indices 0, 1, 2, and 3 copied as an array
@@ -195,6 +226,14 @@ void BufferCacheRuntime::BindTransformFeedbackBuffer(u32 index, VkBuffer buffer,
195 // Already logged in the rasterizer 226 // Already logged in the rasterizer
196 return; 227 return;
197 } 228 }
229 if (buffer == VK_NULL_HANDLE) {
230 // Vulkan doesn't support null transform feedback buffers.
231 // Replace it with our own null buffer.
232 ReserveNullBuffer();
233 buffer = *null_buffer;
234 offset = 0;
235 size = 0;
236 }
198 scheduler.Record([index, buffer, offset, size](vk::CommandBuffer cmdbuf) { 237 scheduler.Record([index, buffer, offset, size](vk::CommandBuffer cmdbuf) {
199 const VkDeviceSize vk_offset = offset; 238 const VkDeviceSize vk_offset = offset;
200 const VkDeviceSize vk_size = size; 239 const VkDeviceSize vk_size = size;
@@ -279,11 +318,11 @@ void BufferCacheRuntime::ReserveQuadArrayLUT(u32 num_indices, bool wait_for_idle
279 }); 318 });
280} 319}
281 320
282void BufferCacheRuntime::ReserveNullIndexBuffer() { 321void BufferCacheRuntime::ReserveNullBuffer() {
283 if (null_index_buffer) { 322 if (null_buffer) {
284 return; 323 return;
285 } 324 }
286 null_index_buffer = device.GetLogical().CreateBuffer(VkBufferCreateInfo{ 325 null_buffer = device.GetLogical().CreateBuffer(VkBufferCreateInfo{
287 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, 326 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
288 .pNext = nullptr, 327 .pNext = nullptr,
289 .flags = 0, 328 .flags = 0,
@@ -294,12 +333,12 @@ void BufferCacheRuntime::ReserveNullIndexBuffer() {
294 .pQueueFamilyIndices = nullptr, 333 .pQueueFamilyIndices = nullptr,
295 }); 334 });
296 if (device.HasDebuggingToolAttached()) { 335 if (device.HasDebuggingToolAttached()) {
297 null_index_buffer.SetObjectNameEXT("Null index buffer"); 336 null_buffer.SetObjectNameEXT("Null index buffer");
298 } 337 }
299 null_index_buffer_commit = memory_allocator.Commit(null_index_buffer, MemoryUsage::DeviceLocal); 338 null_buffer_commit = memory_allocator.Commit(null_buffer, MemoryUsage::DeviceLocal);
300 339
301 scheduler.RequestOutsideRenderPassOperationContext(); 340 scheduler.RequestOutsideRenderPassOperationContext();
302 scheduler.Record([buffer = *null_index_buffer](vk::CommandBuffer cmdbuf) { 341 scheduler.Record([buffer = *null_buffer](vk::CommandBuffer cmdbuf) {
303 cmdbuf.FillBuffer(buffer, 0, VK_WHOLE_SIZE, 0); 342 cmdbuf.FillBuffer(buffer, 0, VK_WHOLE_SIZE, 0);
304 }); 343 });
305} 344}
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h
index 982e92191..3bb81d5b3 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.h
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h
@@ -60,6 +60,8 @@ public:
60 void CopyBuffer(VkBuffer src_buffer, VkBuffer dst_buffer, 60 void CopyBuffer(VkBuffer src_buffer, VkBuffer dst_buffer,
61 std::span<const VideoCommon::BufferCopy> copies); 61 std::span<const VideoCommon::BufferCopy> copies);
62 62
63 void ClearBuffer(VkBuffer dest_buffer, u32 offset, size_t size, u32 value);
64
63 void BindIndexBuffer(PrimitiveTopology topology, IndexFormat index_format, u32 num_indices, 65 void BindIndexBuffer(PrimitiveTopology topology, IndexFormat index_format, u32 num_indices,
64 u32 base_vertex, VkBuffer buffer, u32 offset, u32 size); 66 u32 base_vertex, VkBuffer buffer, u32 offset, u32 size);
65 67
@@ -92,7 +94,7 @@ private:
92 94
93 void ReserveQuadArrayLUT(u32 num_indices, bool wait_for_idle); 95 void ReserveQuadArrayLUT(u32 num_indices, bool wait_for_idle);
94 96
95 void ReserveNullIndexBuffer(); 97 void ReserveNullBuffer();
96 98
97 const Device& device; 99 const Device& device;
98 MemoryAllocator& memory_allocator; 100 MemoryAllocator& memory_allocator;
@@ -105,8 +107,8 @@ private:
105 VkIndexType quad_array_lut_index_type{}; 107 VkIndexType quad_array_lut_index_type{};
106 u32 current_num_indices = 0; 108 u32 current_num_indices = 0;
107 109
108 vk::Buffer null_index_buffer; 110 vk::Buffer null_buffer;
109 MemoryCommit null_index_buffer_commit; 111 MemoryCommit null_buffer_commit;
110 112
111 Uint8Pass uint8_pass; 113 Uint8Pass uint8_pass;
112 QuadIndexedPass quad_index_pass; 114 QuadIndexedPass quad_index_pass;
diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp
index 205cd3b05..4181d83ee 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp
+++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp
@@ -374,20 +374,20 @@ void ASTCDecoderPass::MakeDataBuffer() {
374 374
375 scheduler.Record([src = staging_ref.buffer, offset = staging_ref.offset, dst = *data_buffer, 375 scheduler.Record([src = staging_ref.buffer, offset = staging_ref.offset, dst = *data_buffer,
376 TOTAL_BUFFER_SIZE](vk::CommandBuffer cmdbuf) { 376 TOTAL_BUFFER_SIZE](vk::CommandBuffer cmdbuf) {
377 cmdbuf.CopyBuffer(src, dst, 377 static constexpr VkMemoryBarrier write_barrier{
378 VkBufferCopy{ 378 .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
379 .srcOffset = offset, 379 .pNext = nullptr,
380 .dstOffset = 0, 380 .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
381 .size = TOTAL_BUFFER_SIZE, 381 .dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
382 }); 382 };
383 cmdbuf.PipelineBarrier( 383 const VkBufferCopy copy{
384 VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 384 .srcOffset = offset,
385 VkMemoryBarrier{ 385 .dstOffset = 0,
386 .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, 386 .size = TOTAL_BUFFER_SIZE,
387 .pNext = nullptr, 387 };
388 .srcAccessMask = 0, 388 cmdbuf.CopyBuffer(src, dst, copy);
389 .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, 389 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
390 }); 390 0, write_barrier);
391 }); 391 });
392} 392}
393 393
@@ -411,7 +411,7 @@ void ASTCDecoderPass::Assemble(Image& image, const StagingBufferRef& map,
411 const VkImageMemoryBarrier image_barrier{ 411 const VkImageMemoryBarrier image_barrier{
412 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, 412 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
413 .pNext = nullptr, 413 .pNext = nullptr,
414 .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, 414 .srcAccessMask = is_initialized ? VK_ACCESS_SHADER_WRITE_BIT : VkAccessFlags{},
415 .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, 415 .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT,
416 .oldLayout = is_initialized ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED, 416 .oldLayout = is_initialized ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED,
417 .newLayout = VK_IMAGE_LAYOUT_GENERAL, 417 .newLayout = VK_IMAGE_LAYOUT_GENERAL,
@@ -426,7 +426,8 @@ void ASTCDecoderPass::Assemble(Image& image, const StagingBufferRef& map,
426 .layerCount = VK_REMAINING_ARRAY_LAYERS, 426 .layerCount = VK_REMAINING_ARRAY_LAYERS,
427 }, 427 },
428 }; 428 };
429 cmdbuf.PipelineBarrier(is_initialized ? VK_PIPELINE_STAGE_ALL_COMMANDS_BIT : 0, 429 cmdbuf.PipelineBarrier(is_initialized ? VK_PIPELINE_STAGE_ALL_COMMANDS_BIT
430 : VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
430 VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, image_barrier); 431 VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, image_barrier);
431 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, vk_pipeline); 432 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, vk_pipeline);
432 }); 433 });
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index a8ffbe6ba..f57c15b37 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -706,6 +706,11 @@ void RasterizerVulkan::FlushWork() {
706 706
707AccelerateDMA::AccelerateDMA(BufferCache& buffer_cache_) : buffer_cache{buffer_cache_} {} 707AccelerateDMA::AccelerateDMA(BufferCache& buffer_cache_) : buffer_cache{buffer_cache_} {}
708 708
709bool AccelerateDMA::BufferClear(GPUVAddr src_address, u64 amount, u32 value) {
710 std::scoped_lock lock{buffer_cache.mutex};
711 return buffer_cache.DMAClear(src_address, amount, value);
712}
713
709bool AccelerateDMA::BufferCopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount) { 714bool AccelerateDMA::BufferCopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount) {
710 std::scoped_lock lock{buffer_cache.mutex}; 715 std::scoped_lock lock{buffer_cache.mutex};
711 return buffer_cache.DMACopy(src_address, dest_address, amount); 716 return buffer_cache.DMACopy(src_address, dest_address, amount);
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h
index 3a78de258..2065209be 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.h
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.h
@@ -56,6 +56,8 @@ public:
56 56
57 bool BufferCopy(GPUVAddr start_address, GPUVAddr end_address, u64 amount) override; 57 bool BufferCopy(GPUVAddr start_address, GPUVAddr end_address, u64 amount) override;
58 58
59 bool BufferClear(GPUVAddr src_address, u64 amount, u32 value) override;
60
59private: 61private:
60 BufferCache& buffer_cache; 62 BufferCache& buffer_cache;
61}; 63};
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index a2ab4d1ee..fd01c902c 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -608,7 +608,10 @@ void TextureCacheRuntime::BlitImage(Framebuffer* dst_framebuffer, ImageView& dst
608 const VkImageAspectFlags aspect_mask = ImageAspectMask(src.format); 608 const VkImageAspectFlags aspect_mask = ImageAspectMask(src.format);
609 const bool is_dst_msaa = dst.Samples() != VK_SAMPLE_COUNT_1_BIT; 609 const bool is_dst_msaa = dst.Samples() != VK_SAMPLE_COUNT_1_BIT;
610 const bool is_src_msaa = src.Samples() != VK_SAMPLE_COUNT_1_BIT; 610 const bool is_src_msaa = src.Samples() != VK_SAMPLE_COUNT_1_BIT;
611 ASSERT(aspect_mask == ImageAspectMask(dst.format)); 611 if (aspect_mask != ImageAspectMask(dst.format)) {
612 UNIMPLEMENTED_MSG("Incompatible blit from format {} to {}", src.format, dst.format);
613 return;
614 }
612 if (aspect_mask == VK_IMAGE_ASPECT_COLOR_BIT && !is_src_msaa && !is_dst_msaa) { 615 if (aspect_mask == VK_IMAGE_ASPECT_COLOR_BIT && !is_src_msaa && !is_dst_msaa) {
613 blit_image_helper.BlitColor(dst_framebuffer, src, dst_region, src_region, filter, 616 blit_image_helper.BlitColor(dst_framebuffer, src, dst_region, src_region, filter,
614 operation); 617 operation);
@@ -911,6 +914,7 @@ void Image::UploadMemory(const StagingBufferRef& map,
911 914
912void Image::DownloadMemory(const StagingBufferRef& map, std::span<const BufferImageCopy> copies) { 915void Image::DownloadMemory(const StagingBufferRef& map, std::span<const BufferImageCopy> copies) {
913 std::vector vk_copies = TransformBufferImageCopies(copies, map.offset, aspect_mask); 916 std::vector vk_copies = TransformBufferImageCopies(copies, map.offset, aspect_mask);
917 scheduler->RequestOutsideRenderPassOperationContext();
914 scheduler->Record([buffer = map.buffer, image = *image, aspect_mask = aspect_mask, 918 scheduler->Record([buffer = map.buffer, image = *image, aspect_mask = aspect_mask,
915 vk_copies](vk::CommandBuffer cmdbuf) { 919 vk_copies](vk::CommandBuffer cmdbuf) {
916 const VkImageMemoryBarrier read_barrier{ 920 const VkImageMemoryBarrier read_barrier{
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index 01de2d498..85ce06d56 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -599,6 +599,12 @@ void TextureCache<P>::UpdateRenderTargets(bool is_clear) {
599 using namespace VideoCommon::Dirty; 599 using namespace VideoCommon::Dirty;
600 auto& flags = maxwell3d.dirty.flags; 600 auto& flags = maxwell3d.dirty.flags;
601 if (!flags[Dirty::RenderTargets]) { 601 if (!flags[Dirty::RenderTargets]) {
602 for (size_t index = 0; index < NUM_RT; ++index) {
603 ImageViewId& color_buffer_id = render_targets.color_buffer_ids[index];
604 PrepareImageView(color_buffer_id, true, is_clear && IsFullClear(color_buffer_id));
605 }
606 const ImageViewId depth_buffer_id = render_targets.depth_buffer_id;
607 PrepareImageView(depth_buffer_id, true, is_clear && IsFullClear(depth_buffer_id));
602 return; 608 return;
603 } 609 }
604 flags[Dirty::RenderTargets] = false; 610 flags[Dirty::RenderTargets] = false;
diff --git a/src/video_core/texture_cache/util.cpp b/src/video_core/texture_cache/util.cpp
index c872517b8..59cf2f561 100644
--- a/src/video_core/texture_cache/util.cpp
+++ b/src/video_core/texture_cache/util.cpp
@@ -169,23 +169,6 @@ template <u32 GOB_EXTENT>
169 return Common::DivCeil(AdjustMipSize(size, level), block_size); 169 return Common::DivCeil(AdjustMipSize(size, level), block_size);
170} 170}
171 171
172[[nodiscard]] constexpr std::pair<int, int> Samples(int num_samples) {
173 switch (num_samples) {
174 case 1:
175 return {1, 1};
176 case 2:
177 return {2, 1};
178 case 4:
179 return {2, 2};
180 case 8:
181 return {4, 2};
182 case 16:
183 return {4, 4};
184 }
185 UNREACHABLE_MSG("Invalid number of samples={}", num_samples);
186 return {1, 1};
187}
188
189[[nodiscard]] constexpr Extent2D DefaultBlockSize(PixelFormat format) { 172[[nodiscard]] constexpr Extent2D DefaultBlockSize(PixelFormat format) {
190 return {DefaultBlockWidth(format), DefaultBlockHeight(format)}; 173 return {DefaultBlockWidth(format), DefaultBlockHeight(format)};
191} 174}
@@ -283,14 +266,13 @@ template <u32 GOB_EXTENT>
283} 266}
284 267
285[[nodiscard]] constexpr LevelInfo MakeLevelInfo(PixelFormat format, Extent3D size, Extent3D block, 268[[nodiscard]] constexpr LevelInfo MakeLevelInfo(PixelFormat format, Extent3D size, Extent3D block,
286 u32 num_samples, u32 tile_width_spacing) { 269 u32 tile_width_spacing) {
287 const auto [samples_x, samples_y] = Samples(num_samples);
288 const u32 bytes_per_block = BytesPerBlock(format); 270 const u32 bytes_per_block = BytesPerBlock(format);
289 return { 271 return {
290 .size = 272 .size =
291 { 273 {
292 .width = size.width * samples_x, 274 .width = size.width,
293 .height = size.height * samples_y, 275 .height = size.height,
294 .depth = size.depth, 276 .depth = size.depth,
295 }, 277 },
296 .block = block, 278 .block = block,
@@ -301,14 +283,12 @@ template <u32 GOB_EXTENT>
301} 283}
302 284
303[[nodiscard]] constexpr LevelInfo MakeLevelInfo(const ImageInfo& info) { 285[[nodiscard]] constexpr LevelInfo MakeLevelInfo(const ImageInfo& info) {
304 return MakeLevelInfo(info.format, info.size, info.block, info.num_samples, 286 return MakeLevelInfo(info.format, info.size, info.block, info.tile_width_spacing);
305 info.tile_width_spacing);
306} 287}
307 288
308[[nodiscard]] constexpr u32 CalculateLevelOffset(PixelFormat format, Extent3D size, Extent3D block, 289[[nodiscard]] constexpr u32 CalculateLevelOffset(PixelFormat format, Extent3D size, Extent3D block,
309 u32 num_samples, u32 tile_width_spacing, 290 u32 tile_width_spacing, u32 level) {
310 u32 level) { 291 const LevelInfo info = MakeLevelInfo(format, size, block, tile_width_spacing);
311 const LevelInfo info = MakeLevelInfo(format, size, block, num_samples, tile_width_spacing);
312 u32 offset = 0; 292 u32 offset = 0;
313 for (u32 current_level = 0; current_level < level; ++current_level) { 293 for (u32 current_level = 0; current_level < level; ++current_level) {
314 offset += CalculateLevelSize(info, current_level); 294 offset += CalculateLevelSize(info, current_level);
@@ -645,8 +625,8 @@ u32 CalculateLayerStride(const ImageInfo& info) noexcept {
645 625
646u32 CalculateLayerSize(const ImageInfo& info) noexcept { 626u32 CalculateLayerSize(const ImageInfo& info) noexcept {
647 ASSERT(info.type != ImageType::Linear); 627 ASSERT(info.type != ImageType::Linear);
648 return CalculateLevelOffset(info.format, info.size, info.block, info.num_samples, 628 return CalculateLevelOffset(info.format, info.size, info.block, info.tile_width_spacing,
649 info.tile_width_spacing, info.resources.levels); 629 info.resources.levels);
650} 630}
651 631
652LevelArray CalculateMipLevelOffsets(const ImageInfo& info) noexcept { 632LevelArray CalculateMipLevelOffsets(const ImageInfo& info) noexcept {
@@ -1195,37 +1175,37 @@ static_assert(CalculateLevelSize(LevelInfo{{1920, 1080, 1}, {0, 2, 0}, {1, 1}, 2
1195 0x7f8000); 1175 0x7f8000);
1196static_assert(CalculateLevelSize(LevelInfo{{32, 32, 1}, {0, 0, 4}, {1, 1}, 4, 0}, 0) == 0x4000); 1176static_assert(CalculateLevelSize(LevelInfo{{32, 32, 1}, {0, 0, 4}, {1, 1}, 4, 0}, 0) == 0x4000);
1197 1177
1198static_assert(CalculateLevelOffset(PixelFormat::R8_SINT, {1920, 1080, 1}, {0, 2, 0}, 1, 0, 7) == 1178static_assert(CalculateLevelOffset(PixelFormat::R8_SINT, {1920, 1080, 1}, {0, 2, 0}, 0, 7) ==
1199 0x2afc00); 1179 0x2afc00);
1200static_assert(CalculateLevelOffset(PixelFormat::ASTC_2D_12X12_UNORM, {8192, 4096, 1}, {0, 2, 0}, 1, 1180static_assert(CalculateLevelOffset(PixelFormat::ASTC_2D_12X12_UNORM, {8192, 4096, 1}, {0, 2, 0}, 0,
1201 0, 12) == 0x50d200); 1181 12) == 0x50d200);
1202 1182
1203static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 1, 0, 1183static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 0, 0) ==
1204 0) == 0); 1184 0);
1205static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 1, 0, 1185static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 0, 1) ==
1206 1) == 0x400000); 1186 0x400000);
1207static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 1, 0, 1187static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 0, 2) ==
1208 2) == 0x500000); 1188 0x500000);
1209static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 1, 0, 1189static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 0, 3) ==
1210 3) == 0x540000); 1190 0x540000);
1211static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 1, 0, 1191static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 0, 4) ==
1212 4) == 0x550000); 1192 0x550000);
1213static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 1, 0, 1193static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 0, 5) ==
1214 5) == 0x554000); 1194 0x554000);
1215static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 1, 0, 1195static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 0, 6) ==
1216 6) == 0x555000); 1196 0x555000);
1217static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 1, 0, 1197static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 0, 7) ==
1218 7) == 0x555400); 1198 0x555400);
1219static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 1, 0, 1199static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 0, 8) ==
1220 8) == 0x555600); 1200 0x555600);
1221static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 1, 0, 1201static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 0, 9) ==
1222 9) == 0x555800); 1202 0x555800);
1223 1203
1224constexpr u32 ValidateLayerSize(PixelFormat format, u32 width, u32 height, u32 block_height, 1204constexpr u32 ValidateLayerSize(PixelFormat format, u32 width, u32 height, u32 block_height,
1225 u32 tile_width_spacing, u32 level) { 1205 u32 tile_width_spacing, u32 level) {
1226 const Extent3D size{width, height, 1}; 1206 const Extent3D size{width, height, 1};
1227 const Extent3D block{0, block_height, 0}; 1207 const Extent3D block{0, block_height, 0};
1228 const u32 offset = CalculateLevelOffset(format, size, block, 1, tile_width_spacing, level); 1208 const u32 offset = CalculateLevelOffset(format, size, block, tile_width_spacing, level);
1229 return AlignLayerSize(offset, size, block, DefaultBlockHeight(format), tile_width_spacing); 1209 return AlignLayerSize(offset, size, block, DefaultBlockHeight(format), tile_width_spacing);
1230} 1210}
1231 1211
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 7524e3c40..d72ca5acc 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -411,8 +411,9 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) {
411 if (event->source() == Qt::MouseEventSynthesizedBySystem) { 411 if (event->source() == Qt::MouseEventSynthesizedBySystem) {
412 return; 412 return;
413 } 413 }
414 414 // Qt sometimes returns the parent coordinates. To avoid this we read the global mouse
415 auto pos = event->pos(); 415 // coordinates and map them to the current render area
416 const auto pos = mapFromGlobal(QCursor::pos());
416 const auto [x, y] = ScaleTouch(pos); 417 const auto [x, y] = ScaleTouch(pos);
417 const auto button = QtButtonToMouseButton(event->button()); 418 const auto button = QtButtonToMouseButton(event->button());
418 input_subsystem->GetMouse()->PressButton(x, y, button); 419 input_subsystem->GetMouse()->PressButton(x, y, button);
@@ -429,7 +430,9 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
429 if (event->source() == Qt::MouseEventSynthesizedBySystem) { 430 if (event->source() == Qt::MouseEventSynthesizedBySystem) {
430 return; 431 return;
431 } 432 }
432 auto pos = event->pos(); 433 // Qt sometimes returns the parent coordinates. To avoid this we read the global mouse
434 // coordinates and map them to the current render area
435 const auto pos = mapFromGlobal(QCursor::pos());
433 const auto [x, y] = ScaleTouch(pos); 436 const auto [x, y] = ScaleTouch(pos);
434 const int center_x = width() / 2; 437 const int center_x = width() / 2;
435 const int center_y = height() / 2; 438 const int center_y = height() / 2;
@@ -564,6 +567,12 @@ std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedCont
564bool GRenderWindow::InitRenderTarget() { 567bool GRenderWindow::InitRenderTarget() {
565 ReleaseRenderTarget(); 568 ReleaseRenderTarget();
566 569
570 {
571 // Create a dummy render widget so that Qt
572 // places the render window at the correct position.
573 const RenderWidget dummy_widget{this};
574 }
575
567 first_frame = false; 576 first_frame = false;
568 577
569 switch (Settings::values.renderer_backend.GetValue()) { 578 switch (Settings::values.renderer_backend.GetValue()) {
diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp
index d8ba939d2..1d84bf4ed 100644
--- a/src/yuzu/configuration/configure_audio.cpp
+++ b/src/yuzu/configuration/configure_audio.cpp
@@ -47,8 +47,8 @@ void ConfigureAudio::SetConfiguration() {
47 47
48 SetAudioDeviceFromDeviceID(); 48 SetAudioDeviceFromDeviceID();
49 49
50 const auto volume_value = Settings::values.volume.GetValue() * ui->volume_slider->maximum(); 50 const auto volume_value = static_cast<int>(Settings::values.volume.GetValue());
51 ui->volume_slider->setValue(volume_value / 100); 51 ui->volume_slider->setValue(volume_value);
52 52
53 ui->toggle_audio_stretching->setChecked(Settings::values.enable_audio_stretching.GetValue()); 53 ui->toggle_audio_stretching->setChecked(Settings::values.enable_audio_stretching.GetValue());
54 54
@@ -113,16 +113,16 @@ void ConfigureAudio::ApplyConfiguration() {
113 113
114 // Guard if during game and set to game-specific value 114 // Guard if during game and set to game-specific value
115 if (Settings::values.volume.UsingGlobal()) { 115 if (Settings::values.volume.UsingGlobal()) {
116 const s32 volume = ui->volume_slider->sliderPosition() / ui->volume_slider->maximum(); 116 const auto volume = static_cast<u8>(ui->volume_slider->value());
117 Settings::values.volume.SetValue(static_cast<u8>(100 * volume)); 117 Settings::values.volume.SetValue(volume);
118 } 118 }
119 } else { 119 } else {
120 if (ui->volume_combo_box->currentIndex() == 0) { 120 if (ui->volume_combo_box->currentIndex() == 0) {
121 Settings::values.volume.SetGlobal(true); 121 Settings::values.volume.SetGlobal(true);
122 } else { 122 } else {
123 Settings::values.volume.SetGlobal(false); 123 Settings::values.volume.SetGlobal(false);
124 const s32 volume = ui->volume_slider->sliderPosition() / ui->volume_slider->maximum(); 124 const auto volume = static_cast<u8>(ui->volume_slider->value());
125 Settings::values.volume.SetValue(static_cast<u8>(100 * volume)); 125 Settings::values.volume.SetValue(volume);
126 } 126 }
127 } 127 }
128} 128}
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index d69324a69..6b9bd05f1 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -149,8 +149,9 @@ QString ButtonToText(const Common::ParamPackage& param) {
149 149
150 if (param.Has("button")) { 150 if (param.Has("button")) {
151 const QString button_str = QString::fromStdString(param.Get("button", "")); 151 const QString button_str = QString::fromStdString(param.Get("button", ""));
152 const QString toggle = QString::fromStdString(param.Get("toggle", false) ? "~" : "");
152 153
153 return QObject::tr("Button %1").arg(button_str); 154 return QObject::tr("%1Button %2").arg(toggle, button_str);
154 } 155 }
155 156
156 if (param.Has("motion")) { 157 if (param.Has("motion")) {
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index 218b4782b..76c063c97 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -404,9 +404,11 @@ void GameList::ValidateEntry(const QModelIndex& item) {
404 return; 404 return;
405 } 405 }
406 406
407 const auto title_id = selected.data(GameListItemPath::ProgramIdRole).toULongLong();
408
407 // Users usually want to run a different game after closing one 409 // Users usually want to run a different game after closing one
408 search_field->clear(); 410 search_field->clear();
409 emit GameChosen(file_path); 411 emit GameChosen(file_path, title_id);
410 break; 412 break;
411 } 413 }
412 case GameListItemType::AddDir: 414 case GameListItemType::AddDir:
@@ -548,10 +550,10 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
548 emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData, path); 550 emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData, path);
549 }); 551 });
550 connect(start_game, &QAction::triggered, [this, path]() { 552 connect(start_game, &QAction::triggered, [this, path]() {
551 emit BootGame(QString::fromStdString(path), 0, StartGameType::Normal); 553 emit BootGame(QString::fromStdString(path), 0, 0, StartGameType::Normal);
552 }); 554 });
553 connect(start_game_global, &QAction::triggered, [this, path]() { 555 connect(start_game_global, &QAction::triggered, [this, path]() {
554 emit BootGame(QString::fromStdString(path), 0, StartGameType::Global); 556 emit BootGame(QString::fromStdString(path), 0, 0, StartGameType::Global);
555 }); 557 });
556 connect(open_mod_location, &QAction::triggered, [this, program_id, path]() { 558 connect(open_mod_location, &QAction::triggered, [this, program_id, path]() {
557 emit OpenFolderRequested(program_id, GameListOpenTarget::ModData, path); 559 emit OpenFolderRequested(program_id, GameListOpenTarget::ModData, path);
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h
index 50402da51..c9a9f4654 100644
--- a/src/yuzu/game_list.h
+++ b/src/yuzu/game_list.h
@@ -88,8 +88,9 @@ public:
88 static const QStringList supported_file_extensions; 88 static const QStringList supported_file_extensions;
89 89
90signals: 90signals:
91 void BootGame(const QString& game_path, std::size_t program_index, StartGameType type); 91 void BootGame(const QString& game_path, u64 program_id, std::size_t program_index,
92 void GameChosen(const QString& game_path); 92 StartGameType type);
93 void GameChosen(const QString& game_path, const u64 title_id = 0);
93 void ShouldCancelWorker(); 94 void ShouldCancelWorker();
94 void OpenFolderRequested(u64 program_id, GameListOpenTarget target, 95 void OpenFolderRequested(u64 program_id, GameListOpenTarget target,
95 const std::string& game_path); 96 const std::string& game_path);
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp
index 33cc90d5a..2d5492157 100644
--- a/src/yuzu/game_list_worker.cpp
+++ b/src/yuzu/game_list_worker.cpp
@@ -336,18 +336,44 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa
336 } 336 }
337 } 337 }
338 } else { 338 } else {
339 std::vector<u8> icon; 339 std::vector<u64> program_ids;
340 [[maybe_unused]] const auto res1 = loader->ReadIcon(icon); 340 loader->ReadProgramIds(program_ids);
341
342 if (res2 == Loader::ResultStatus::Success && program_ids.size() > 1 &&
343 (file_type == Loader::FileType::XCI || file_type == Loader::FileType::NSP)) {
344 for (const auto id : program_ids) {
345 loader = Loader::GetLoader(system, file, id);
346 if (!loader) {
347 continue;
348 }
349
350 std::vector<u8> icon;
351 [[maybe_unused]] const auto res1 = loader->ReadIcon(icon);
341 352
342 std::string name = " "; 353 std::string name = " ";
343 [[maybe_unused]] const auto res3 = loader->ReadTitle(name); 354 [[maybe_unused]] const auto res3 = loader->ReadTitle(name);
344 355
345 const FileSys::PatchManager patch{program_id, system.GetFileSystemController(), 356 const FileSys::PatchManager patch{id, system.GetFileSystemController(),
346 system.GetContentProvider()}; 357 system.GetContentProvider()};
358
359 emit EntryReady(MakeGameListEntry(physical_name, name, icon, *loader, id,
360 compatibility_list, patch),
361 parent_dir);
362 }
363 } else {
364 std::vector<u8> icon;
365 [[maybe_unused]] const auto res1 = loader->ReadIcon(icon);
347 366
348 emit EntryReady(MakeGameListEntry(physical_name, name, icon, *loader, program_id, 367 std::string name = " ";
349 compatibility_list, patch), 368 [[maybe_unused]] const auto res3 = loader->ReadTitle(name);
350 parent_dir); 369
370 const FileSys::PatchManager patch{program_id, system.GetFileSystemController(),
371 system.GetContentProvider()};
372
373 emit EntryReady(MakeGameListEntry(physical_name, name, icon, *loader,
374 program_id, compatibility_list, patch),
375 parent_dir);
376 }
351 } 377 }
352 } else if (is_dir) { 378 } else if (is_dir) {
353 watch_list.append(QString::fromStdString(physical_name)); 379 watch_list.append(QString::fromStdString(physical_name));
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index b7fd33ae7..03a909d17 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -1221,7 +1221,7 @@ void GMainWindow::AllowOSSleep() {
1221#endif 1221#endif
1222} 1222}
1223 1223
1224bool GMainWindow::LoadROM(const QString& filename, std::size_t program_index) { 1224bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t program_index) {
1225 // Shutdown previous session if the emu thread is still active... 1225 // Shutdown previous session if the emu thread is still active...
1226 if (emu_thread != nullptr) 1226 if (emu_thread != nullptr)
1227 ShutdownGame(); 1227 ShutdownGame();
@@ -1244,7 +1244,7 @@ bool GMainWindow::LoadROM(const QString& filename, std::size_t program_index) {
1244 }); 1244 });
1245 1245
1246 const Core::System::ResultStatus result{ 1246 const Core::System::ResultStatus result{
1247 system.Load(*render_window, filename.toStdString(), program_index)}; 1247 system.Load(*render_window, filename.toStdString(), program_id, program_index)};
1248 1248
1249 const auto drd_callout = (UISettings::values.callout_flags.GetValue() & 1249 const auto drd_callout = (UISettings::values.callout_flags.GetValue() &
1250 static_cast<u32>(CalloutFlag::DRDDeprecation)) == 0; 1250 static_cast<u32>(CalloutFlag::DRDDeprecation)) == 0;
@@ -1331,7 +1331,8 @@ void GMainWindow::SelectAndSetCurrentUser() {
1331 Settings::values.current_user = dialog.GetIndex(); 1331 Settings::values.current_user = dialog.GetIndex();
1332} 1332}
1333 1333
1334void GMainWindow::BootGame(const QString& filename, std::size_t program_index, StartGameType type) { 1334void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t program_index,
1335 StartGameType type) {
1335 LOG_INFO(Frontend, "yuzu starting..."); 1336 LOG_INFO(Frontend, "yuzu starting...");
1336 StoreRecentFile(filename); // Put the filename on top of the list 1337 StoreRecentFile(filename); // Put the filename on top of the list
1337 1338
@@ -1341,7 +1342,7 @@ void GMainWindow::BootGame(const QString& filename, std::size_t program_index, S
1341 1342
1342 auto& system = Core::System::GetInstance(); 1343 auto& system = Core::System::GetInstance();
1343 const auto v_file = Core::GetGameFileFromPath(vfs, filename.toUtf8().constData()); 1344 const auto v_file = Core::GetGameFileFromPath(vfs, filename.toUtf8().constData());
1344 const auto loader = Loader::GetLoader(system, v_file, program_index); 1345 const auto loader = Loader::GetLoader(system, v_file, program_id, program_index);
1345 1346
1346 if (loader != nullptr && loader->ReadProgramId(title_id) == Loader::ResultStatus::Success && 1347 if (loader != nullptr && loader->ReadProgramId(title_id) == Loader::ResultStatus::Success &&
1347 type == StartGameType::Normal) { 1348 type == StartGameType::Normal) {
@@ -1369,7 +1370,7 @@ void GMainWindow::BootGame(const QString& filename, std::size_t program_index, S
1369 SelectAndSetCurrentUser(); 1370 SelectAndSetCurrentUser();
1370 } 1371 }
1371 1372
1372 if (!LoadROM(filename, program_index)) 1373 if (!LoadROM(filename, program_id, program_index))
1373 return; 1374 return;
1374 1375
1375 // Create and start the emulation thread 1376 // Create and start the emulation thread
@@ -1548,8 +1549,8 @@ void GMainWindow::UpdateRecentFiles() {
1548 ui.menu_recent_files->setEnabled(num_recent_files != 0); 1549 ui.menu_recent_files->setEnabled(num_recent_files != 0);
1549} 1550}
1550 1551
1551void GMainWindow::OnGameListLoadFile(QString game_path) { 1552void GMainWindow::OnGameListLoadFile(QString game_path, u64 program_id) {
1552 BootGame(game_path); 1553 BootGame(game_path, program_id);
1553} 1554}
1554 1555
1555void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target, 1556void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target,
@@ -2450,7 +2451,7 @@ void GMainWindow::OnLoadComplete() {
2450 2451
2451void GMainWindow::OnExecuteProgram(std::size_t program_index) { 2452void GMainWindow::OnExecuteProgram(std::size_t program_index) {
2452 ShutdownGame(); 2453 ShutdownGame();
2453 BootGame(last_filename_booted, program_index); 2454 BootGame(last_filename_booted, 0, program_index);
2454} 2455}
2455 2456
2456void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_text) { 2457void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_text) {
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 45c8310e1..a50e5b9fe 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -186,8 +186,8 @@ private:
186 void PreventOSSleep(); 186 void PreventOSSleep();
187 void AllowOSSleep(); 187 void AllowOSSleep();
188 188
189 bool LoadROM(const QString& filename, std::size_t program_index); 189 bool LoadROM(const QString& filename, u64 program_id, std::size_t program_index);
190 void BootGame(const QString& filename, std::size_t program_index = 0, 190 void BootGame(const QString& filename, u64 program_id = 0, std::size_t program_index = 0,
191 StartGameType with_config = StartGameType::Normal); 191 StartGameType with_config = StartGameType::Normal);
192 void ShutdownGame(); 192 void ShutdownGame();
193 193
@@ -238,7 +238,7 @@ private slots:
238 void OnOpenQuickstartGuide(); 238 void OnOpenQuickstartGuide();
239 void OnOpenFAQ(); 239 void OnOpenFAQ();
240 /// Called whenever a user selects a game in the game list widget. 240 /// Called whenever a user selects a game in the game list widget.
241 void OnGameListLoadFile(QString game_path); 241 void OnGameListLoadFile(QString game_path, u64 program_id);
242 void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target, 242 void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target,
243 const std::string& game_path); 243 const std::string& game_path);
244 void OnTransferableShaderCacheOpenFile(u64 program_id); 244 void OnTransferableShaderCacheOpenFile(u64 program_id);
diff --git a/src/yuzu_cmd/CMakeLists.txt b/src/yuzu_cmd/CMakeLists.txt
index 4bf25727b..e55a19649 100644
--- a/src/yuzu_cmd/CMakeLists.txt
+++ b/src/yuzu_cmd/CMakeLists.txt
@@ -38,6 +38,11 @@ target_include_directories(yuzu-cmd PRIVATE ${RESOURCES_DIR})
38 38
39target_include_directories(yuzu-cmd PRIVATE ../../externals/Vulkan-Headers/include) 39target_include_directories(yuzu-cmd PRIVATE ../../externals/Vulkan-Headers/include)
40 40
41if (YUZU_USE_EXTERNAL_SDL2)
42 target_compile_definitions(yuzu-cmd PRIVATE -DYUZU_USE_EXTERNAL_SDL2)
43 target_include_directories(yuzu-cmd PRIVATE ${PROJECT_BINARY_DIR}/externals/SDL/include)
44endif()
45
41if(UNIX AND NOT APPLE) 46if(UNIX AND NOT APPLE)
42 install(TARGETS yuzu-cmd RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") 47 install(TARGETS yuzu-cmd RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin")
43endif() 48endif()
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 23ada3f92..b18056baf 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -292,6 +292,8 @@ void Config::ReadValues() {
292 292
293 ReadSetting("ControlsGeneral", Settings::values.motion_device); 293 ReadSetting("ControlsGeneral", Settings::values.motion_device);
294 294
295 ReadSetting("ControlsGeneral", Settings::values.touch_device);
296
295 ReadSetting("ControlsGeneral", Settings::values.keyboard_enabled); 297 ReadSetting("ControlsGeneral", Settings::values.keyboard_enabled);
296 298
297 ReadSetting("ControlsGeneral", Settings::values.debug_pad_enabled); 299 ReadSetting("ControlsGeneral", Settings::values.debug_pad_enabled);
@@ -414,11 +416,31 @@ void Config::ReadValues() {
414 } 416 }
415 417
416 ReadSetting("System", Settings::values.language_index); 418 ReadSetting("System", Settings::values.language_index);
419 ReadSetting("System", Settings::values.region_index);
417 ReadSetting("System", Settings::values.time_zone_index); 420 ReadSetting("System", Settings::values.time_zone_index);
421 ReadSetting("System", Settings::values.sound_index);
418 422
419 // Core 423 // Core
420 ReadSetting("Core", Settings::values.use_multi_core); 424 ReadSetting("Core", Settings::values.use_multi_core);
421 425
426 // Cpu
427 ReadSetting("Cpu", Settings::values.cpu_accuracy);
428 ReadSetting("Cpu", Settings::values.cpu_debug_mode);
429 ReadSetting("Cpu", Settings::values.cpuopt_page_tables);
430 ReadSetting("Cpu", Settings::values.cpuopt_block_linking);
431 ReadSetting("Cpu", Settings::values.cpuopt_return_stack_buffer);
432 ReadSetting("Cpu", Settings::values.cpuopt_fast_dispatcher);
433 ReadSetting("Cpu", Settings::values.cpuopt_context_elimination);
434 ReadSetting("Cpu", Settings::values.cpuopt_const_prop);
435 ReadSetting("Cpu", Settings::values.cpuopt_misc_ir);
436 ReadSetting("Cpu", Settings::values.cpuopt_reduce_misalign_checks);
437 ReadSetting("Cpu", Settings::values.cpuopt_fastmem);
438 ReadSetting("Cpu", Settings::values.cpuopt_unsafe_unfuse_fma);
439 ReadSetting("Cpu", Settings::values.cpuopt_unsafe_reduce_fp_error);
440 ReadSetting("Cpu", Settings::values.cpuopt_unsafe_ignore_standard_fpcr);
441 ReadSetting("Cpu", Settings::values.cpuopt_unsafe_inaccurate_nan);
442 ReadSetting("Cpu", Settings::values.cpuopt_unsafe_fastmem_check);
443
422 // Renderer 444 // Renderer
423 ReadSetting("Renderer", Settings::values.renderer_backend); 445 ReadSetting("Renderer", Settings::values.renderer_backend);
424 ReadSetting("Renderer", Settings::values.renderer_debug); 446 ReadSetting("Renderer", Settings::values.renderer_debug);
@@ -438,6 +460,7 @@ void Config::ReadValues() {
438 ReadSetting("Renderer", Settings::values.use_nvdec_emulation); 460 ReadSetting("Renderer", Settings::values.use_nvdec_emulation);
439 ReadSetting("Renderer", Settings::values.accelerate_astc); 461 ReadSetting("Renderer", Settings::values.accelerate_astc);
440 ReadSetting("Renderer", Settings::values.use_fast_gpu_time); 462 ReadSetting("Renderer", Settings::values.use_fast_gpu_time);
463 ReadSetting("Renderer", Settings::values.use_caches_gc);
441 464
442 ReadSetting("Renderer", Settings::values.bg_red); 465 ReadSetting("Renderer", Settings::values.bg_red);
443 ReadSetting("Renderer", Settings::values.bg_green); 466 ReadSetting("Renderer", Settings::values.bg_green);
@@ -458,7 +481,6 @@ void Config::ReadValues() {
458 // Debugging 481 // Debugging
459 Settings::values.record_frame_times = 482 Settings::values.record_frame_times =
460 sdl2_config->GetBoolean("Debugging", "record_frame_times", false); 483 sdl2_config->GetBoolean("Debugging", "record_frame_times", false);
461 ReadSetting("Debugging", Settings::values.program_args);
462 ReadSetting("Debugging", Settings::values.dump_exefs); 484 ReadSetting("Debugging", Settings::values.dump_exefs);
463 ReadSetting("Debugging", Settings::values.dump_nso); 485 ReadSetting("Debugging", Settings::values.dump_nso);
464 ReadSetting("Debugging", Settings::values.enable_fs_access_log); 486 ReadSetting("Debugging", Settings::values.enable_fs_access_log);
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index 7d6bcccc7..b362f10b4 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -65,6 +65,13 @@ button_screenshot=
65lstick= 65lstick=
66rstick= 66rstick=
67 67
68# To use the debug_pad, prepend `debug_pad_` before each button setting above.
69# i.e. debug_pad_button_a=
70
71# Enable debug pad inputs to the guest
72# 0 (default): Disabled, 1: Enabled
73debug_pad_enabled =
74
68# Whether to enable or disable vibration 75# Whether to enable or disable vibration
69# 0: Disabled, 1 (default): Enabled 76# 0: Disabled, 1 (default): Enabled
70vibration_enabled= 77vibration_enabled=
@@ -73,6 +80,10 @@ vibration_enabled=
73# 0 (default): Disabled, 1: Enabled 80# 0 (default): Disabled, 1: Enabled
74enable_accurate_vibrations= 81enable_accurate_vibrations=
75 82
83# Enables controller motion inputs
84# 0: Disabled, 1 (default): Enabled
85motion_enabled =
86
76# for motion input, the following devices are available: 87# for motion input, the following devices are available:
77# - "motion_emu" (default) for emulating motion input from mouse input. Required parameters: 88# - "motion_emu" (default) for emulating motion input from mouse input. Required parameters:
78# - "update_period": update period in milliseconds (default to 100) 89# - "update_period": update period in milliseconds (default to 100)
@@ -98,19 +109,30 @@ use_touch_from_button=
98#touch_from_button_maps_0_bind_1=bar 109#touch_from_button_maps_0_bind_1=bar
99# etc. 110# etc.
100 111
101# Most desktop operating systems do not expose a way to poll the motion state of the controllers 112# List of Cemuhook UDP servers, delimited by ','.
102# so as a way around it, cemuhook created a udp client/server protocol to broadcast the data directly 113# Default: 127.0.0.1:26760
103# from a controller device to the client program. Citra has a client that can connect and read 114# Example: 127.0.0.1:26760,123.4.5.67:26761
104# from any cemuhook compatible motion program. 115udp_input_servers =
105 116
106# IPv4 address of the udp input server (Default "127.0.0.1") 117# Enable controlling an axis via a mouse input.
107udp_input_address=127.0.0.1 118# 0 (default): Off, 1: On
119mouse_panning =
120
121# Set mouse sensitivity.
122# Default: 1.0
123mouse_panning_sensitivity =
124
125# Emulate an analog control stick from keyboard inputs.
126# 0 (default): Disabled, 1: Enabled
127emulate_analog_keyboard =
108 128
109# Port of the udp input server. (Default 26760) 129# Enable mouse inputs to the guest
110udp_input_port= 130# 0 (default): Disabled, 1: Enabled
131mouse_enabled =
111 132
112# The pad to request data on. Should be between 0 (Pad 1) and 3 (Pad 4). (Default 0) 133# Enable keyboard inputs to the guest
113udp_pad_index= 134# 0 (default): Disabled, 1: Enabled
135keyboard_enabled =
114 136
115[Core] 137[Core]
116# Whether to use multi-core for CPU emulation 138# Whether to use multi-core for CPU emulation
@@ -118,6 +140,17 @@ udp_pad_index=
118use_multi_core= 140use_multi_core=
119 141
120[Cpu] 142[Cpu]
143# Adjusts various optimizations.
144# Auto-select mode enables choice unsafe optimizations.
145# Accurate enables only safe optimizations.
146# Unsafe allows any unsafe optimizations.
147# 0 (default): Auto-select, 1: Accurate, 2: Enable unsafe optimizations
148cpu_accuracy =
149
150# Allow disabling safe optimizations.
151# 0 (default): Disabled, 1: Enabled
152cpu_debug_mode =
153
121# Enable inline page tables optimization (faster guest memory access) 154# Enable inline page tables optimization (faster guest memory access)
122# 0: Disabled, 1 (default): Enabled 155# 0: Disabled, 1 (default): Enabled
123cpuopt_page_tables = 156cpuopt_page_tables =
@@ -154,6 +187,31 @@ cpuopt_reduce_misalign_checks =
154# 0: Disabled, 1 (default): Enabled 187# 0: Disabled, 1 (default): Enabled
155cpuopt_fastmem = 188cpuopt_fastmem =
156 189
190# Enable unfuse FMA (improve performance on CPUs without FMA)
191# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
192# 0: Disabled, 1 (default): Enabled
193cpuopt_unsafe_unfuse_fma =
194
195# Enable faster FRSQRTE and FRECPE
196# Only enabled if cpu_accuracy is set to Unsafe.
197# 0: Disabled, 1 (default): Enabled
198cpuopt_unsafe_reduce_fp_error =
199
200# Enable faster ASIMD instructions (32 bits only)
201# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
202# 0: Disabled, 1 (default): Enabled
203cpuopt_unsafe_ignore_standard_fpcr =
204
205# Enable inaccurate NaN handling
206# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
207# 0: Disabled, 1 (default): Enabled
208cpuopt_unsafe_inaccurate_nan =
209
210# Disable address space checks (64 bits only)
211# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
212# 0: Disabled, 1 (default): Enabled
213cpuopt_unsafe_fastmem_check =
214
157[Renderer] 215[Renderer]
158# Which backend API to use. 216# Which backend API to use.
159# 0 (default): OpenGL, 1: Vulkan 217# 0 (default): OpenGL, 1: Vulkan
@@ -166,14 +224,6 @@ debug =
166# Which Vulkan physical device to use (defaults to 0) 224# Which Vulkan physical device to use (defaults to 0)
167vulkan_device = 225vulkan_device =
168 226
169# Whether to use software or hardware rendering.
170# 0: Software, 1 (default): Hardware
171use_hw_renderer =
172
173# Whether to use the Just-In-Time (JIT) compiler for shader emulation
174# 0: Interpreter (slow), 1 (default): JIT (fast)
175use_shader_jit =
176
177# Aspect ratio 227# Aspect ratio
178# 0: Default (16:9), 1: Force 4:3, 2: Force 21:9, 3: Stretch to Window 228# 0: Default (16:9), 1: Force 4:3, 2: Force 21:9, 3: Stretch to Window
179aspect_ratio = 229aspect_ratio =
@@ -211,21 +261,20 @@ use_frame_limit =
211frame_limit = 261frame_limit =
212 262
213# Whether to use disk based shader cache 263# Whether to use disk based shader cache
214# 0 (default): Off, 1 : On 264# 0: Off, 1 (default): On
215use_disk_shader_cache = 265use_disk_shader_cache =
216 266
217# Which gpu accuracy level to use 267# Which gpu accuracy level to use
218# 0 (Normal), 1 (High), 2 (Extreme) 268# 0: Normal, 1 (default): High, 2: Extreme (Very slow)
219gpu_accuracy = 269gpu_accuracy =
220 270
221# Whether to use asynchronous GPU emulation 271# Whether to use asynchronous GPU emulation
222# 0 : Off (slow), 1 (default): On (fast) 272# 0 : Off (slow), 1 (default): On (fast)
223use_asynchronous_gpu_emulation = 273use_asynchronous_gpu_emulation =
224 274
225# Forces VSync on the display thread. Usually doesn't impact performance, but on some drivers it can 275# Inform the guest that GPU operations completed more quickly than they did.
226# so only turn this off if you notice a speed difference.
227# 0: Off, 1 (default): On 276# 0: Off, 1 (default): On
228use_vsync = 277use_fast_gpu_time =
229 278
230# Whether to use garbage collection or not for GPU caches. 279# Whether to use garbage collection or not for GPU caches.
231# 0 (default): Off, 1: On 280# 0 (default): Off, 1: On
@@ -237,31 +286,6 @@ bg_red =
237bg_blue = 286bg_blue =
238bg_green = 287bg_green =
239 288
240[Layout]
241# Layout for the screen inside the render window.
242# 0 (default): Default Top Bottom Screen, 1: Single Screen Only, 2: Large Screen Small Screen
243layout_option =
244
245# Toggle custom layout (using the settings below) on or off.
246# 0 (default): Off, 1: On
247custom_layout =
248
249# Screen placement when using Custom layout option
250# 0x, 0y is the top left corner of the render window.
251custom_top_left =
252custom_top_top =
253custom_top_right =
254custom_top_bottom =
255custom_bottom_left =
256custom_bottom_top =
257custom_bottom_right =
258custom_bottom_bottom =
259
260# Swaps the prominent screen with the other screen.
261# For example, if Single Screen is chosen, setting this to 1 will display the bottom screen instead of the top screen.
262# 0 (default): Top Screen is prominent, 1: Bottom Screen is prominent
263swap_screen =
264
265[Audio] 289[Audio]
266# Which audio output engine to use. 290# Which audio output engine to use.
267# auto (default): Auto-select 291# auto (default): Auto-select
@@ -308,10 +332,6 @@ gamecard_path =
308# 1 (default): Yes, 0: No 332# 1 (default): Yes, 0: No
309use_docked_mode = 333use_docked_mode =
310 334
311# Allow the use of NFC in games
312# 1 (default): Yes, 0 : No
313enable_nfc =
314
315# Sets the seed for the RNG generator built into the switch 335# Sets the seed for the RNG generator built into the switch
316# rng_seed will be ignored and randomly generated if rng_seed_enabled is false 336# rng_seed will be ignored and randomly generated if rng_seed_enabled is false
317rng_seed_enabled = 337rng_seed_enabled =
@@ -323,10 +343,6 @@ rng_seed =
323custom_rtc_enabled = 343custom_rtc_enabled =
324custom_rtc = 344custom_rtc =
325 345
326# Sets the account username, max length is 32 characters
327# yuzu (default)
328username = yuzu
329
330# Sets the systems language index 346# Sets the systems language index
331# 0: Japanese, 1: English (default), 2: French, 3: German, 4: Italian, 5: Spanish, 6: Chinese, 347# 0: Japanese, 1: English (default), 2: French, 3: German, 4: Italian, 5: Spanish, 6: Chinese,
332# 7: Korean, 8: Dutch, 9: Portuguese, 10: Russian, 11: Taiwanese, 12: British English, 13: Canadian French, 348# 7: Korean, 8: Dutch, 9: Portuguese, 10: Russian, 11: Taiwanese, 12: British English, 13: Canadian French,
@@ -335,17 +351,25 @@ language_index =
335 351
336# The system region that yuzu will use during emulation 352# The system region that yuzu will use during emulation
337# -1: Auto-select (default), 0: Japan, 1: USA, 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan 353# -1: Auto-select (default), 0: Japan, 1: USA, 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan
338region_value = 354region_index =
339 355
340# The system time zone that yuzu will use during emulation 356# The system time zone that yuzu will use during emulation
341# 0: Auto-select (default), 1: Default (system archive value), Others: Index for specified time zone 357# 0: Auto-select (default), 1: Default (system archive value), Others: Index for specified time zone
342time_zone_index = 358time_zone_index =
343 359
360# Sets the sound output mode.
361# 0: Mono, 1 (default): Stereo, 2: Surround
362sound_index =
363
344[Miscellaneous] 364[Miscellaneous]
345# A filter which removes logs below a certain logging level. 365# A filter which removes logs below a certain logging level.
346# Examples: *:Debug Kernel.SVC:Trace Service.*:Critical 366# Examples: *:Debug Kernel.SVC:Trace Service.*:Critical
347log_filter = *:Trace 367log_filter = *:Trace
348 368
369# Use developer keys
370# 0 (default): Disabled, 1: Enabled
371use_dev_keys =
372
349[Debugging] 373[Debugging]
350# Record frame time data, can be found in the log directory. Boolean value 374# Record frame time data, can be found in the log directory. Boolean value
351record_frame_times = 375record_frame_times =
@@ -355,6 +379,8 @@ dump_exefs=false
355dump_nso=false 379dump_nso=false
356# Determines whether or not yuzu will save the filesystem access log. 380# Determines whether or not yuzu will save the filesystem access log.
357enable_fs_access_log=false 381enable_fs_access_log=false
382# Enables verbose reporting services
383reporting_services =
358# Determines whether or not yuzu will report to the game that the emulated console is in Kiosk Mode 384# Determines whether or not yuzu will report to the game that the emulated console is in Kiosk Mode
359# false: Retail/Normal Mode (default), true: Kiosk Mode 385# false: Retail/Normal Mode (default), true: Kiosk Mode
360quest_flag = 386quest_flag =
@@ -393,4 +419,4 @@ title_ids =
393# For each title ID, have a key/value pair called `disabled_<title_id>` equal to the names of the add-ons to disable (sep. by '|') 419# For each title ID, have a key/value pair called `disabled_<title_id>` equal to the names of the add-ons to disable (sep. by '|')
394# e.x. disabled_0100000000010000 = Update|DLC <- disables Updates and DLC on Super Mario Odyssey 420# e.x. disabled_0100000000010000 = Update|DLC <- disables Updates and DLC on Super Mario Odyssey
395)"; 421)";
396} 422} // namespace DefaultINI
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index 06b20c975..8dfc09393 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -2,15 +2,7 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5// Ignore -Wimplicit-fallthrough due to https://github.com/libsdl-org/SDL/issues/4307
6#ifdef __clang__
7#pragma clang diagnostic push
8#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
9#endif
10#include <SDL.h> 5#include <SDL.h>
11#ifdef __clang__
12#pragma clang diagnostic pop
13#endif
14 6
15#include "common/logging/log.h" 7#include "common/logging/log.h"
16#include "common/scm_rev.h" 8#include "common/scm_rev.h"
@@ -130,12 +122,6 @@ void EmuWindow_SDL2::OnResize() {
130} 122}
131 123
132void EmuWindow_SDL2::Fullscreen() { 124void EmuWindow_SDL2::Fullscreen() {
133 if (SDL_SetWindowFullscreen(render_window, SDL_WINDOW_FULLSCREEN) == 0) {
134 return;
135 }
136
137 LOG_ERROR(Frontend, "Fullscreening failed: {}", SDL_GetError());
138
139 // Try a different fullscreening method 125 // Try a different fullscreening method
140 LOG_INFO(Frontend, "Attempting to use borderless fullscreen..."); 126 LOG_INFO(Frontend, "Attempting to use borderless fullscreen...");
141 if (SDL_SetWindowFullscreen(render_window, SDL_WINDOW_FULLSCREEN_DESKTOP) == 0) { 127 if (SDL_SetWindowFullscreen(render_window, SDL_WINDOW_FULLSCREEN_DESKTOP) == 0) {
@@ -144,6 +130,12 @@ void EmuWindow_SDL2::Fullscreen() {
144 130
145 LOG_ERROR(Frontend, "Borderless fullscreening failed: {}", SDL_GetError()); 131 LOG_ERROR(Frontend, "Borderless fullscreening failed: {}", SDL_GetError());
146 132
133 if (SDL_SetWindowFullscreen(render_window, SDL_WINDOW_FULLSCREEN) == 0) {
134 return;
135 }
136
137 LOG_ERROR(Frontend, "Fullscreening failed: {}", SDL_GetError());
138
147 // Fallback algorithm: Maximise window. 139 // Fallback algorithm: Maximise window.
148 // Works on all systems (unless something is seriously wrong), so no fallback for this one. 140 // Works on all systems (unless something is seriously wrong), so no fallback for this one.
149 LOG_INFO(Frontend, "Falling back on a maximised window..."); 141 LOG_INFO(Frontend, "Falling back on a maximised window...");
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
index 837a44be7..eadb41790 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
@@ -7,15 +7,7 @@
7#include <string> 7#include <string>
8 8
9#define SDL_MAIN_HANDLED 9#define SDL_MAIN_HANDLED
10// Ignore -Wimplicit-fallthrough due to https://github.com/libsdl-org/SDL/issues/4307
11#ifdef __clang__
12#pragma clang diagnostic push
13#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
14#endif
15#include <SDL.h> 10#include <SDL.h>
16#ifdef __clang__
17#pragma clang diagnostic pop
18#endif
19 11
20#include <fmt/format.h> 12#include <fmt/format.h>
21#include <glad/glad.h> 13#include <glad/glad.h>
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
index 3401ad4b4..d1473dbab 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
@@ -15,19 +15,16 @@
15#include "video_core/renderer_vulkan/renderer_vulkan.h" 15#include "video_core/renderer_vulkan/renderer_vulkan.h"
16#include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h" 16#include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h"
17 17
18// Include these late to avoid polluting everything with Xlib macros 18#ifdef YUZU_USE_EXTERNAL_SDL2
19// Ignore -Wimplicit-fallthrough due to https://github.com/libsdl-org/SDL/issues/4307 19// Include this before SDL.h to prevent the external from including a dummy
20#ifdef __clang__ 20#define USING_GENERATED_CONFIG_H
21#pragma clang diagnostic push 21#include <SDL_config.h>
22#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
23#endif 22#endif
23
24#include <SDL.h> 24#include <SDL.h>
25#ifdef __clang__
26#pragma clang diagnostic pop
27#endif
28#include <SDL_syswm.h> 25#include <SDL_syswm.h>
29 26
30EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem) 27EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem, bool fullscreen)
31 : EmuWindow_SDL2{input_subsystem} { 28 : EmuWindow_SDL2{input_subsystem} {
32 const std::string window_title = fmt::format("yuzu {} | {}-{} (Vulkan)", Common::g_build_name, 29 const std::string window_title = fmt::format("yuzu {} | {}-{} (Vulkan)", Common::g_build_name,
33 Common::g_scm_branch, Common::g_scm_desc); 30 Common::g_scm_branch, Common::g_scm_desc);
@@ -45,12 +42,21 @@ EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsyste
45 42
46 SetWindowIcon(); 43 SetWindowIcon();
47 44
45 if (fullscreen) {
46 Fullscreen();
47 }
48
48 switch (wm.subsystem) { 49 switch (wm.subsystem) {
49#ifdef SDL_VIDEO_DRIVER_WINDOWS 50#ifdef SDL_VIDEO_DRIVER_WINDOWS
50 case SDL_SYSWM_TYPE::SDL_SYSWM_WINDOWS: 51 case SDL_SYSWM_TYPE::SDL_SYSWM_WINDOWS:
51 window_info.type = Core::Frontend::WindowSystemType::Windows; 52 window_info.type = Core::Frontend::WindowSystemType::Windows;
52 window_info.render_surface = reinterpret_cast<void*>(wm.info.win.window); 53 window_info.render_surface = reinterpret_cast<void*>(wm.info.win.window);
53 break; 54 break;
55#else
56 case SDL_SYSWM_TYPE::SDL_SYSWM_WINDOWS:
57 LOG_CRITICAL(Frontend, "Window manager subsystem Windows not compiled");
58 std::exit(EXIT_FAILURE);
59 break;
54#endif 60#endif
55#ifdef SDL_VIDEO_DRIVER_X11 61#ifdef SDL_VIDEO_DRIVER_X11
56 case SDL_SYSWM_TYPE::SDL_SYSWM_X11: 62 case SDL_SYSWM_TYPE::SDL_SYSWM_X11:
@@ -58,6 +64,11 @@ EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsyste
58 window_info.display_connection = wm.info.x11.display; 64 window_info.display_connection = wm.info.x11.display;
59 window_info.render_surface = reinterpret_cast<void*>(wm.info.x11.window); 65 window_info.render_surface = reinterpret_cast<void*>(wm.info.x11.window);
60 break; 66 break;
67#else
68 case SDL_SYSWM_TYPE::SDL_SYSWM_X11:
69 LOG_CRITICAL(Frontend, "Window manager subsystem X11 not compiled");
70 std::exit(EXIT_FAILURE);
71 break;
61#endif 72#endif
62#ifdef SDL_VIDEO_DRIVER_WAYLAND 73#ifdef SDL_VIDEO_DRIVER_WAYLAND
63 case SDL_SYSWM_TYPE::SDL_SYSWM_WAYLAND: 74 case SDL_SYSWM_TYPE::SDL_SYSWM_WAYLAND:
@@ -65,6 +76,11 @@ EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsyste
65 window_info.display_connection = wm.info.wl.display; 76 window_info.display_connection = wm.info.wl.display;
66 window_info.render_surface = wm.info.wl.surface; 77 window_info.render_surface = wm.info.wl.surface;
67 break; 78 break;
79#else
80 case SDL_SYSWM_TYPE::SDL_SYSWM_WAYLAND:
81 LOG_CRITICAL(Frontend, "Window manager subsystem Wayland not compiled");
82 std::exit(EXIT_FAILURE);
83 break;
68#endif 84#endif
69 default: 85 default:
70 LOG_CRITICAL(Frontend, "Window manager subsystem not implemented"); 86 LOG_CRITICAL(Frontend, "Window manager subsystem not implemented");
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
index bdfdc3c6f..de53844f0 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
@@ -19,7 +19,7 @@ class InputSubsystem;
19 19
20class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 { 20class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 {
21public: 21public:
22 explicit EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem); 22 explicit EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem, bool fullscreen);
23 ~EmuWindow_SDL2_VK() override; 23 ~EmuWindow_SDL2_VK() override;
24 24
25 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; 25 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 9607cdcb1..ac4ea88d3 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -175,7 +175,7 @@ int main(int argc, char** argv) {
175 emu_window = std::make_unique<EmuWindow_SDL2_GL>(&input_subsystem, fullscreen); 175 emu_window = std::make_unique<EmuWindow_SDL2_GL>(&input_subsystem, fullscreen);
176 break; 176 break;
177 case Settings::RendererBackend::Vulkan: 177 case Settings::RendererBackend::Vulkan:
178 emu_window = std::make_unique<EmuWindow_SDL2_VK>(&input_subsystem); 178 emu_window = std::make_unique<EmuWindow_SDL2_VK>(&input_subsystem, fullscreen);
179 break; 179 break;
180 } 180 }
181 181