diff options
Diffstat (limited to 'src')
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 | ||
| 461 | System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath, | 460 | System::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 | ||
| 466 | bool System::IsPoweredOn() const { | 465 | bool 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 | ||
| 32 | XCI::XCI(VirtualFile file_, std::size_t program_index) | 32 | XCI::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 | ||
| 177 | std::vector<u64> XCI::GetProgramTitleIDs() const { | ||
| 178 | return secure_partition->GetProgramTitleIDs(); | ||
| 179 | } | ||
| 180 | |||
| 177 | u32 XCI::GetSystemUpdateVersion() { | 181 | u32 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 | ||
| 231 | std::shared_ptr<NCA> XCI::GetNCAByType(NCAContentType type) const { | 235 | std::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 | ||
| 79 | class XCI : public ReadOnlyVfsDirectory { | 79 | class XCI : public ReadOnlyVfsDirectory { |
| 80 | public: | 80 | public: |
| 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 | ||
| 21 | namespace FileSys { | 21 | namespace FileSys { |
| 22 | 22 | ||
| 23 | NSP::NSP(VirtualFile file_, std::size_t program_index_) | 23 | NSP::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 | ||
| 49 | Loader::ResultStatus NSP::GetProgramStatus(u64 title_id) const { | 50 | Loader::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 | ||
| 60 | u64 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 | |||
| 70 | u64 NSP::GetProgramTitleID() const { | 61 | u64 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 | |||
| 84 | u64 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 | ||
| 94 | std::vector<u64> NSP::GetTitleIDs() const { | 97 | std::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 | |||
| 160 | VirtualFile NSP::GetNCAFile(u64 title_id, ContentRecordType type, TitleType title_type) const { | 160 | VirtualFile 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 | ||
| 28 | class NSP : public ReadOnlyVfsDirectory { | 29 | class NSP : public ReadOnlyVfsDirectory { |
| 29 | public: | 30 | public: |
| 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 | ||
| 31 | enum class ControllerSupportMode : u8 { | 32 | enum 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 { | |||
| 78 | static_assert(sizeof(ControllerSupportArgOld) == 0x21C, | 80 | static_assert(sizeof(ControllerSupportArgOld) == 0x21C, |
| 79 | "ControllerSupportArgOld has incorrect size."); | 81 | "ControllerSupportArgOld has incorrect size."); |
| 80 | 82 | ||
| 81 | // LibraryAppletVersion 0x7 | 83 | // LibraryAppletVersion 0x7, 0x8 |
| 82 | struct ControllerSupportArgNew { | 84 | struct 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 { | |||
| 95 | static_assert(sizeof(ControllerUpdateFirmwareArg) == 0x4, | 97 | static_assert(sizeof(ControllerUpdateFirmwareArg) == 0x4, |
| 96 | "ControllerUpdateFirmwareArg has incorrect size."); | 98 | "ControllerUpdateFirmwareArg has incorrect size."); |
| 97 | 99 | ||
| 100 | struct ControllerKeyRemappingArg { | ||
| 101 | u64 unknown{}; | ||
| 102 | u32 unknown_2{}; | ||
| 103 | INSERT_PADDING_WORDS(1); | ||
| 104 | }; | ||
| 105 | static_assert(sizeof(ControllerKeyRemappingArg) == 0x10, | ||
| 106 | "ControllerKeyRemappingArg has incorrect size."); | ||
| 107 | |||
| 98 | struct ControllerSupportResultInfo { | 108 | struct 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 | */ |
| 208 | static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::VirtualFile file, | 208 | static 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 | ||
| 254 | std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file, | 257 | std::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 | */ |
| 326 | std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file, | 337 | std::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 | ||
| 24 | AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file_, | 24 | AppLoader_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 | ||
| 153 | ResultStatus AppLoader_NSP::ReadProgramId(u64& out_program_id) { | 151 | ResultStatus 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; | 159 | ResultStatus 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 { | |||
| 28 | public: | 28 | public: |
| 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 | ||
| 23 | AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file_, | 23 | AppLoader_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 | ||
| 124 | ResultStatus AppLoader_XCI::ReadProgramIds(std::vector<u64>& out_program_ids) { | ||
| 125 | out_program_ids = xci->GetProgramTitleIDs(); | ||
| 126 | return ResultStatus::Success; | ||
| 127 | } | ||
| 128 | |||
| 124 | ResultStatus AppLoader_XCI::ReadIcon(std::vector<u8>& buffer) { | 129 | ResultStatus 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 | ||
| 151 | ResultStatus AppLoader_XCI::ReadManualRomFS(FileSys::VirtualFile& out_file) { | 156 | ResultStatus 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 { | |||
| 28 | public: | 28 | public: |
| 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: | |||
| 241 | private: | 276 | private: |
| 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 | ||
| 403 | class SDLButton final : public Input::ButtonDevice { | 440 | class SDLButton final : public Input::ButtonDevice { |
| 404 | public: | 441 | public: |
| 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 | ||
| 412 | private: | 457 | private: |
| 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 | ||
| 417 | class SDLDirectionButton final : public Input::ButtonDevice { | 463 | class 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 | ||
| 686 | private: | 733 | private: |
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 | ||
| 465 | template <class P> | 472 | template <class P> |
| 473 | void 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 | |||
| 480 | template <class P> | ||
| 466 | bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount) { | 481 | bool 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 | ||
| 527 | template <class P> | 540 | template <class P> |
| 541 | bool 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 | |||
| 567 | template <class P> | ||
| 528 | void BufferCache<P>::BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, | 568 | void 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 | ||
| 784 | template <class P> | 824 | template <class P> |
| 825 | bool 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 | |||
| 845 | template <class P> | ||
| 785 | bool BufferCache<P>::IsRegionCpuModified(VAddr addr, size_t size) { | 846 | bool 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 | ||
| 16 | MICROPROFILE_DECLARE(GPU_DMAEngine); | ||
| 17 | MICROPROFILE_DEFINE(GPU_DMAEngine, "GPU", "DMA Engine", MP_RGB(224, 224, 128)); | ||
| 18 | |||
| 15 | namespace Tegra::Engines { | 19 | namespace Tegra::Engines { |
| 16 | 20 | ||
| 17 | using namespace Texture; | 21 | using namespace Texture; |
| @@ -43,6 +47,7 @@ void MaxwellDMA::CallMultiMethod(u32 method, const u32* base_start, u32 amount, | |||
| 43 | } | 47 | } |
| 44 | 48 | ||
| 45 | void MaxwellDMA::Launch() { | 49 | void 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 { | |||
| 31 | public: | 31 | public: |
| 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 | ||
| 101 | void 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 | |||
| 101 | void BufferCacheRuntime::BindIndexBuffer(Buffer& buffer, u32 offset, u32 size) { | 107 | void 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 | ||
| 1410 | bool 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 | |||
| 68 | private: | 70 | private: |
| 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 | ||
| 139 | void 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 | |||
| 139 | void BufferCacheRuntime::BindIndexBuffer(PrimitiveTopology topology, IndexFormat index_format, | 163 | void 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 | ||
| 163 | void BufferCacheRuntime::BindQuadArrayIndexBuffer(u32 first, u32 count) { | 187 | void 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 | ||
| 282 | void BufferCacheRuntime::ReserveNullIndexBuffer() { | 321 | void 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 | ||
| 707 | AccelerateDMA::AccelerateDMA(BufferCache& buffer_cache_) : buffer_cache{buffer_cache_} {} | 707 | AccelerateDMA::AccelerateDMA(BufferCache& buffer_cache_) : buffer_cache{buffer_cache_} {} |
| 708 | 708 | ||
| 709 | bool 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 | |||
| 709 | bool AccelerateDMA::BufferCopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount) { | 714 | bool 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 | |||
| 59 | private: | 61 | private: |
| 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 | ||
| 912 | void Image::DownloadMemory(const StagingBufferRef& map, std::span<const BufferImageCopy> copies) { | 915 | void 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 | ||
| 646 | u32 CalculateLayerSize(const ImageInfo& info) noexcept { | 626 | u32 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 | ||
| 652 | LevelArray CalculateMipLevelOffsets(const ImageInfo& info) noexcept { | 632 | LevelArray 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); |
| 1196 | static_assert(CalculateLevelSize(LevelInfo{{32, 32, 1}, {0, 0, 4}, {1, 1}, 4, 0}, 0) == 0x4000); | 1176 | static_assert(CalculateLevelSize(LevelInfo{{32, 32, 1}, {0, 0, 4}, {1, 1}, 4, 0}, 0) == 0x4000); |
| 1197 | 1177 | ||
| 1198 | static_assert(CalculateLevelOffset(PixelFormat::R8_SINT, {1920, 1080, 1}, {0, 2, 0}, 1, 0, 7) == | 1178 | static_assert(CalculateLevelOffset(PixelFormat::R8_SINT, {1920, 1080, 1}, {0, 2, 0}, 0, 7) == |
| 1199 | 0x2afc00); | 1179 | 0x2afc00); |
| 1200 | static_assert(CalculateLevelOffset(PixelFormat::ASTC_2D_12X12_UNORM, {8192, 4096, 1}, {0, 2, 0}, 1, | 1180 | static_assert(CalculateLevelOffset(PixelFormat::ASTC_2D_12X12_UNORM, {8192, 4096, 1}, {0, 2, 0}, 0, |
| 1201 | 0, 12) == 0x50d200); | 1181 | 12) == 0x50d200); |
| 1202 | 1182 | ||
| 1203 | static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 1, 0, | 1183 | static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 0, 0) == |
| 1204 | 0) == 0); | 1184 | 0); |
| 1205 | static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 1, 0, | 1185 | static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 0, 1) == |
| 1206 | 1) == 0x400000); | 1186 | 0x400000); |
| 1207 | static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 1, 0, | 1187 | static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 0, 2) == |
| 1208 | 2) == 0x500000); | 1188 | 0x500000); |
| 1209 | static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 1, 0, | 1189 | static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 0, 3) == |
| 1210 | 3) == 0x540000); | 1190 | 0x540000); |
| 1211 | static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 1, 0, | 1191 | static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 0, 4) == |
| 1212 | 4) == 0x550000); | 1192 | 0x550000); |
| 1213 | static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 1, 0, | 1193 | static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 0, 5) == |
| 1214 | 5) == 0x554000); | 1194 | 0x554000); |
| 1215 | static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 1, 0, | 1195 | static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 0, 6) == |
| 1216 | 6) == 0x555000); | 1196 | 0x555000); |
| 1217 | static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 1, 0, | 1197 | static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 0, 7) == |
| 1218 | 7) == 0x555400); | 1198 | 0x555400); |
| 1219 | static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 1, 0, | 1199 | static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 0, 8) == |
| 1220 | 8) == 0x555600); | 1200 | 0x555600); |
| 1221 | static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 1, 0, | 1201 | static_assert(CalculateLevelOffset(PixelFormat::A8B8G8R8_UNORM, {1024, 1024, 1}, {0, 4, 0}, 0, 9) == |
| 1222 | 9) == 0x555800); | 1202 | 0x555800); |
| 1223 | 1203 | ||
| 1224 | constexpr u32 ValidateLayerSize(PixelFormat format, u32 width, u32 height, u32 block_height, | 1204 | constexpr 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 | |||
| 564 | bool GRenderWindow::InitRenderTarget() { | 567 | bool 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 | ||
| 90 | signals: | 90 | signals: |
| 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 | ||
| 1224 | bool GMainWindow::LoadROM(const QString& filename, std::size_t program_index) { | 1224 | bool 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 | ||
| 1334 | void GMainWindow::BootGame(const QString& filename, std::size_t program_index, StartGameType type) { | 1334 | void 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 | ||
| 1551 | void GMainWindow::OnGameListLoadFile(QString game_path) { | 1552 | void GMainWindow::OnGameListLoadFile(QString game_path, u64 program_id) { |
| 1552 | BootGame(game_path); | 1553 | BootGame(game_path, program_id); |
| 1553 | } | 1554 | } |
| 1554 | 1555 | ||
| 1555 | void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target, | 1556 | void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target, |
| @@ -2450,7 +2451,7 @@ void GMainWindow::OnLoadComplete() { | |||
| 2450 | 2451 | ||
| 2451 | void GMainWindow::OnExecuteProgram(std::size_t program_index) { | 2452 | void 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 | ||
| 2456 | void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_text) { | 2457 | void 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 | ||
| 39 | target_include_directories(yuzu-cmd PRIVATE ../../externals/Vulkan-Headers/include) | 39 | target_include_directories(yuzu-cmd PRIVATE ../../externals/Vulkan-Headers/include) |
| 40 | 40 | ||
| 41 | if (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) | ||
| 44 | endif() | ||
| 45 | |||
| 41 | if(UNIX AND NOT APPLE) | 46 | if(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") |
| 43 | endif() | 48 | endif() |
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= | |||
| 65 | lstick= | 65 | lstick= |
| 66 | rstick= | 66 | rstick= |
| 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 | ||
| 73 | debug_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 |
| 70 | vibration_enabled= | 77 | vibration_enabled= |
| @@ -73,6 +80,10 @@ vibration_enabled= | |||
| 73 | # 0 (default): Disabled, 1: Enabled | 80 | # 0 (default): Disabled, 1: Enabled |
| 74 | enable_accurate_vibrations= | 81 | enable_accurate_vibrations= |
| 75 | 82 | ||
| 83 | # Enables controller motion inputs | ||
| 84 | # 0: Disabled, 1 (default): Enabled | ||
| 85 | motion_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. | 115 | udp_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. |
| 107 | udp_input_address=127.0.0.1 | 118 | # 0 (default): Off, 1: On |
| 119 | mouse_panning = | ||
| 120 | |||
| 121 | # Set mouse sensitivity. | ||
| 122 | # Default: 1.0 | ||
| 123 | mouse_panning_sensitivity = | ||
| 124 | |||
| 125 | # Emulate an analog control stick from keyboard inputs. | ||
| 126 | # 0 (default): Disabled, 1: Enabled | ||
| 127 | emulate_analog_keyboard = | ||
| 108 | 128 | ||
| 109 | # Port of the udp input server. (Default 26760) | 129 | # Enable mouse inputs to the guest |
| 110 | udp_input_port= | 130 | # 0 (default): Disabled, 1: Enabled |
| 131 | mouse_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 |
| 113 | udp_pad_index= | 134 | # 0 (default): Disabled, 1: Enabled |
| 135 | keyboard_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= | |||
| 118 | use_multi_core= | 140 | use_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 | ||
| 148 | cpu_accuracy = | ||
| 149 | |||
| 150 | # Allow disabling safe optimizations. | ||
| 151 | # 0 (default): Disabled, 1: Enabled | ||
| 152 | cpu_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 |
| 123 | cpuopt_page_tables = | 156 | cpuopt_page_tables = |
| @@ -154,6 +187,31 @@ cpuopt_reduce_misalign_checks = | |||
| 154 | # 0: Disabled, 1 (default): Enabled | 187 | # 0: Disabled, 1 (default): Enabled |
| 155 | cpuopt_fastmem = | 188 | cpuopt_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 | ||
| 193 | cpuopt_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 | ||
| 198 | cpuopt_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 | ||
| 203 | cpuopt_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 | ||
| 208 | cpuopt_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 | ||
| 213 | cpuopt_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) |
| 167 | vulkan_device = | 225 | vulkan_device = |
| 168 | 226 | ||
| 169 | # Whether to use software or hardware rendering. | ||
| 170 | # 0: Software, 1 (default): Hardware | ||
| 171 | use_hw_renderer = | ||
| 172 | |||
| 173 | # Whether to use the Just-In-Time (JIT) compiler for shader emulation | ||
| 174 | # 0: Interpreter (slow), 1 (default): JIT (fast) | ||
| 175 | use_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 |
| 179 | aspect_ratio = | 229 | aspect_ratio = |
| @@ -211,21 +261,20 @@ use_frame_limit = | |||
| 211 | frame_limit = | 261 | frame_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 |
| 215 | use_disk_shader_cache = | 265 | use_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) |
| 219 | gpu_accuracy = | 269 | gpu_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) |
| 223 | use_asynchronous_gpu_emulation = | 273 | use_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 |
| 228 | use_vsync = | 277 | use_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 = | |||
| 237 | bg_blue = | 286 | bg_blue = |
| 238 | bg_green = | 287 | bg_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 | ||
| 243 | layout_option = | ||
| 244 | |||
| 245 | # Toggle custom layout (using the settings below) on or off. | ||
| 246 | # 0 (default): Off, 1: On | ||
| 247 | custom_layout = | ||
| 248 | |||
| 249 | # Screen placement when using Custom layout option | ||
| 250 | # 0x, 0y is the top left corner of the render window. | ||
| 251 | custom_top_left = | ||
| 252 | custom_top_top = | ||
| 253 | custom_top_right = | ||
| 254 | custom_top_bottom = | ||
| 255 | custom_bottom_left = | ||
| 256 | custom_bottom_top = | ||
| 257 | custom_bottom_right = | ||
| 258 | custom_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 | ||
| 263 | swap_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 |
| 309 | use_docked_mode = | 333 | use_docked_mode = |
| 310 | 334 | ||
| 311 | # Allow the use of NFC in games | ||
| 312 | # 1 (default): Yes, 0 : No | ||
| 313 | enable_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 |
| 317 | rng_seed_enabled = | 337 | rng_seed_enabled = |
| @@ -323,10 +343,6 @@ rng_seed = | |||
| 323 | custom_rtc_enabled = | 343 | custom_rtc_enabled = |
| 324 | custom_rtc = | 344 | custom_rtc = |
| 325 | 345 | ||
| 326 | # Sets the account username, max length is 32 characters | ||
| 327 | # yuzu (default) | ||
| 328 | username = 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 |
| 338 | region_value = | 354 | region_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 |
| 342 | time_zone_index = | 358 | time_zone_index = |
| 343 | 359 | ||
| 360 | # Sets the sound output mode. | ||
| 361 | # 0: Mono, 1 (default): Stereo, 2: Surround | ||
| 362 | sound_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 |
| 347 | log_filter = *:Trace | 367 | log_filter = *:Trace |
| 348 | 368 | ||
| 369 | # Use developer keys | ||
| 370 | # 0 (default): Disabled, 1: Enabled | ||
| 371 | use_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 |
| 351 | record_frame_times = | 375 | record_frame_times = |
| @@ -355,6 +379,8 @@ dump_exefs=false | |||
| 355 | dump_nso=false | 379 | dump_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. |
| 357 | enable_fs_access_log=false | 381 | enable_fs_access_log=false |
| 382 | # Enables verbose reporting services | ||
| 383 | reporting_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 |
| 360 | quest_flag = | 386 | quest_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 | ||
| 132 | void EmuWindow_SDL2::Fullscreen() { | 124 | void 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 | ||
| 30 | EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem) | 27 | EmuWindow_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 | ||
| 20 | class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 { | 20 | class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 { |
| 21 | public: | 21 | public: |
| 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 | ||