From 9f82a9a2444a232e746992fa89084b928255cb63 Mon Sep 17 00:00:00 2001 From: FearlessTobi Date: Wed, 20 May 2020 21:28:16 +0200 Subject: [PATCH 001/252] crypto: Make KeyManager a singleton class Previously, we were reading the keys everytime a KeyManager object was created, causing yuzu to reread the keys file multiple hundreds of times when loading the game list. With this change, it is only loaded once. On my system, this decreased game list loading times by a factor of 20. --- src/core/crypto/key_manager.h | 10 +++++++++- src/core/file_sys/bis_factory.cpp | 2 +- src/core/file_sys/card_image.cpp | 4 ++-- src/core/file_sys/card_image.h | 2 +- src/core/file_sys/content_archive.cpp | 5 ++--- src/core/file_sys/content_archive.h | 5 ++--- src/core/file_sys/registered_cache.cpp | 6 +++--- src/core/file_sys/registered_cache.h | 2 +- src/core/file_sys/submission_package.cpp | 4 ++-- src/core/file_sys/submission_package.h | 2 +- src/core/file_sys/xts_archive.h | 2 +- src/core/hle/service/es/es.cpp | 2 +- src/yuzu/main.cpp | 2 +- 13 files changed, 27 insertions(+), 21 deletions(-) diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h index 7265c4171..bf3434e1c 100644 --- a/src/core/crypto/key_manager.h +++ b/src/core/crypto/key_manager.h @@ -223,7 +223,13 @@ bool operator<(const KeyIndex& lhs, const KeyIndex& rhs) { class KeyManager { public: - KeyManager(); + static KeyManager& instance() { + static KeyManager instance; + return instance; + } + + KeyManager(KeyManager const&) = delete; + void operator=(KeyManager const&) = delete; bool HasKey(S128KeyType id, u64 field1 = 0, u64 field2 = 0) const; bool HasKey(S256KeyType id, u64 field1 = 0, u64 field2 = 0) const; @@ -257,6 +263,8 @@ public: bool AddTicketPersonalized(Ticket raw); private: + KeyManager(); + std::map, Key128> s128_keys; std::map, Key256> s256_keys; diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp index 0af44f340..464ca6503 100644 --- a/src/core/file_sys/bis_factory.cpp +++ b/src/core/file_sys/bis_factory.cpp @@ -79,7 +79,7 @@ VirtualDir BISFactory::OpenPartition(BisPartitionId id) const { } VirtualFile BISFactory::OpenPartitionStorage(BisPartitionId id) const { - Core::Crypto::KeyManager keys; + Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::instance(); Core::Crypto::PartitionDataManager pdm{ Core::System::GetInstance().GetFilesystem()->OpenDirectory( FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), Mode::Read)}; diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp index 07d0c8d5d..664a47e7f 100644 --- a/src/core/file_sys/card_image.cpp +++ b/src/core/file_sys/card_image.cpp @@ -178,7 +178,7 @@ u32 XCI::GetSystemUpdateVersion() { return 0; for (const auto& file : update->GetFiles()) { - NCA nca{file, nullptr, 0, keys}; + NCA nca{file, nullptr, 0}; if (nca.GetStatus() != Loader::ResultStatus::Success) continue; @@ -286,7 +286,7 @@ Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) { continue; } - auto nca = std::make_shared(file, nullptr, 0, keys); + auto nca = std::make_shared(file, nullptr, 0); if (nca->IsUpdate()) { continue; } diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h index c2ee0ea99..a09d504ae 100644 --- a/src/core/file_sys/card_image.h +++ b/src/core/file_sys/card_image.h @@ -140,6 +140,6 @@ private: u64 update_normal_partition_end; - Core::Crypto::KeyManager keys; + Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::instance(); }; } // namespace FileSys diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index b8bbdd1ef..473245d5a 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp @@ -118,9 +118,8 @@ static bool IsValidNCA(const NCAHeader& header) { return header.magic == Common::MakeMagic('N', 'C', 'A', '3'); } -NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset, - Core::Crypto::KeyManager keys_) - : file(std::move(file_)), bktr_base_romfs(std::move(bktr_base_romfs_)), keys(std::move(keys_)) { +NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset) + : file(std::move(file_)), bktr_base_romfs(std::move(bktr_base_romfs_)) { if (file == nullptr) { status = Loader::ResultStatus::ErrorNullFile; return; diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h index e249079b5..e6c887b32 100644 --- a/src/core/file_sys/content_archive.h +++ b/src/core/file_sys/content_archive.h @@ -99,8 +99,7 @@ inline bool IsDirectoryLogoPartition(const VirtualDir& pfs) { class NCA : public ReadOnlyVfsDirectory { public: explicit NCA(VirtualFile file, VirtualFile bktr_base_romfs = nullptr, - u64 bktr_base_ivfc_offset = 0, - Core::Crypto::KeyManager keys = Core::Crypto::KeyManager()); + u64 bktr_base_ivfc_offset = 0); ~NCA() override; Loader::ResultStatus GetStatus() const; @@ -159,7 +158,7 @@ private: bool encrypted = false; bool is_update = false; - Core::Crypto::KeyManager keys; + Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::instance(); }; } // namespace FileSys diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp index ba5f76288..27c1b0233 100644 --- a/src/core/file_sys/registered_cache.cpp +++ b/src/core/file_sys/registered_cache.cpp @@ -408,7 +408,7 @@ void RegisteredCache::ProcessFiles(const std::vector& ids) { if (file == nullptr) continue; - const auto nca = std::make_shared(parser(file, id), nullptr, 0, keys); + const auto nca = std::make_shared(parser(file, id), nullptr, 0); if (nca->GetStatus() != Loader::ResultStatus::Success || nca->GetType() != NCAContentType::Meta) { continue; @@ -486,7 +486,7 @@ std::unique_ptr RegisteredCache::GetEntry(u64 title_id, ContentRecordType t const auto raw = GetEntryRaw(title_id, type); if (raw == nullptr) return nullptr; - return std::make_unique(raw, nullptr, 0, keys); + return std::make_unique(raw, nullptr, 0); } template @@ -865,7 +865,7 @@ std::unique_ptr ManualContentProvider::GetEntry(u64 title_id, ContentRecord const auto res = GetEntryRaw(title_id, type); if (res == nullptr) return nullptr; - return std::make_unique(res, nullptr, 0, keys); + return std::make_unique(res, nullptr, 0); } std::vector ManualContentProvider::ListEntriesFilter( diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h index d1eec240e..4b2fb08cb 100644 --- a/src/core/file_sys/registered_cache.h +++ b/src/core/file_sys/registered_cache.h @@ -88,7 +88,7 @@ public: protected: // A single instance of KeyManager to be used by GetEntry() - Core::Crypto::KeyManager keys; + Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::instance(); }; class PlaceholderCache { diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp index ef3084681..c35a0d10b 100644 --- a/src/core/file_sys/submission_package.cpp +++ b/src/core/file_sys/submission_package.cpp @@ -21,7 +21,7 @@ namespace FileSys { namespace { void SetTicketKeys(const std::vector& files) { - Core::Crypto::KeyManager keys; + Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::instance(); for (const auto& ticket_file : files) { if (ticket_file == nullptr) { @@ -285,7 +285,7 @@ void NSP::ReadNCAs(const std::vector& files) { continue; } - auto next_nca = std::make_shared(std::move(next_file), nullptr, 0, keys); + auto next_nca = std::make_shared(std::move(next_file), nullptr, 0); if (next_nca->GetType() == NCAContentType::Program) { program_status[cnmt.GetTitleID()] = next_nca->GetStatus(); } diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h index ee9b6ce17..bd577f6e5 100644 --- a/src/core/file_sys/submission_package.h +++ b/src/core/file_sys/submission_package.h @@ -73,7 +73,7 @@ private: std::map, std::shared_ptr>> ncas; std::vector ticket_files; - Core::Crypto::KeyManager keys; + Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::instance(); VirtualFile romfs; VirtualDir exefs; diff --git a/src/core/file_sys/xts_archive.h b/src/core/file_sys/xts_archive.h index 7704dee90..95da907bc 100644 --- a/src/core/file_sys/xts_archive.h +++ b/src/core/file_sys/xts_archive.h @@ -62,6 +62,6 @@ private: VirtualFile dec_file; - Core::Crypto::KeyManager keys; + Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::instance(); }; } // namespace FileSys diff --git a/src/core/hle/service/es/es.cpp b/src/core/hle/service/es/es.cpp index f8e9df4b1..ad6841a64 100644 --- a/src/core/hle/service/es/es.cpp +++ b/src/core/hle/service/es/es.cpp @@ -263,7 +263,7 @@ private: rb.Push(write_size); } - Core::Crypto::KeyManager keys; + Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::instance(); }; void InstallInterfaces(SM::ServiceManager& service_manager) { diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 0a6839b2d..62f9d2ab2 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -2116,7 +2116,7 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { "title.keys_autogenerated"); } - Core::Crypto::KeyManager keys{}; + Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::instance(); if (keys.BaseDeriveNecessary()) { Core::Crypto::PartitionDataManager pdm{vfs->OpenDirectory( FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), FileSys::Mode::Read)}; From 2b1cc232bce45e12d466fc606f78b484bb13a3a8 Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Sun, 31 May 2020 18:49:51 +0200 Subject: [PATCH 002/252] ldr: Update NRR/NRO structs This was based on Switchbrew pages: https://switchbrew.org/wiki/NRR https://switchbrew.org/wiki/NRO --- src/core/hle/service/ldr/ldr.cpp | 112 ++++++++++++++++++++----------- 1 file changed, 72 insertions(+), 40 deletions(-) diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index 6ad3be1b3..62d4bfc08 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp @@ -39,47 +39,68 @@ constexpr ResultCode ERROR_NOT_INITIALIZED{ErrorModule::Loader, 87}; constexpr std::size_t MAXIMUM_LOADED_RO{0x40}; constexpr std::size_t MAXIMUM_MAP_RETRIES{0x200}; +struct Certification { + u64_le application_id_mask; + u64_le application_id_pattern; + std::array reserved; + std::array public_key; // Also known as modulus + std::array signature; +}; +static_assert(sizeof(Certification) == 0x220, "Certification has invalid size!"); + +using SHA256Hash = std::array; + struct NRRHeader { u32_le magic; - INSERT_PADDING_BYTES(12); - u64_le title_id_mask; - u64_le title_id_pattern; - INSERT_PADDING_BYTES(16); - std::array modulus; - std::array signature_1; - std::array signature_2; - u64_le title_id; + u32_le certification_signature_key_generation; // 9.0.0+ + u64_le reserved; + Certification certification; + std::array signature; + u64_le application_id; u32_le size; - INSERT_PADDING_BYTES(4); + u8 nrr_kind; + std::array reserved_2; u32_le hash_offset; u32_le hash_count; - INSERT_PADDING_BYTES(8); + u64_le reserved_3; + + // Must be dynamically allocated because, according to + // SwitchBrew, its size is (0x20 * hash_count) and + // it's impossible to determine the value of hash_count + // (SwitchBrew calls it "NumHash") before runtime, + // therefore it's not possible to calculate a SHA-256 + std::vector NroHashList; }; -static_assert(sizeof(NRRHeader) == 0x350, "NRRHeader has incorrect size."); + +struct SegmentHeader { + u32_le memory_offset; + u32_le memory_size; +}; +static_assert(sizeof(SegmentHeader) == 0x8, "SegmentHeader has invalid size!"); struct NROHeader { - INSERT_PADDING_WORDS(1); + // Switchbrew calls this "Start" (0x10) + u32_le unused; u32_le mod_offset; - INSERT_PADDING_WORDS(2); + u64_le padding; + + // Switchbrew calls this "Header" (0x70) u32_le magic; u32_le version; u32_le nro_size; u32_le flags; - u32_le text_offset; - u32_le text_size; - u32_le ro_offset; - u32_le ro_size; - u32_le rw_offset; - u32_le rw_size; + // .text, .ro, .data (yuzu previously called it "rw" instead of "data") + std::array segment_headers; u32_le bss_size; - INSERT_PADDING_WORDS(1); + u32_le reserved; std::array build_id; - INSERT_PADDING_BYTES(0x20); + u32_le dso_handle_offset; + u32_le unused_2; + // .apiInfo, .dynstr, .dynsym + std::array segment_headers_2; }; static_assert(sizeof(NROHeader) == 0x80, "NROHeader has invalid size."); -using SHA256Hash = std::array; - struct NROInfo { SHA256Hash hash{}; VAddr nro_address{}; @@ -226,11 +247,11 @@ public: return; } - if (system.CurrentProcess()->GetTitleID() != header.title_id) { + if (system.CurrentProcess()->GetTitleID() != header.application_id) { LOG_ERROR(Service_LDR, "Attempting to load NRR with title ID other than current process. (actual " "{:016X})!", - header.title_id); + header.application_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ERROR_INVALID_NRR); return; @@ -348,10 +369,10 @@ public: ResultCode LoadNro(Kernel::Process* process, const NROHeader& nro_header, VAddr nro_addr, VAddr start) const { - const VAddr text_start{start + nro_header.text_offset}; - const VAddr ro_start{start + nro_header.ro_offset}; - const VAddr data_start{start + nro_header.rw_offset}; - const VAddr bss_start{data_start + nro_header.rw_size}; + const VAddr text_start{start + nro_header.segment_headers[0].memory_offset}; + const VAddr ro_start{start + nro_header.segment_headers[1].memory_offset}; + const VAddr data_start{start + nro_header.segment_headers[2].memory_offset}; + const VAddr bss_start{data_start + nro_header.segment_headers[2].memory_size}; const VAddr bss_end_addr{ Common::AlignUp(bss_start + nro_header.bss_size, Kernel::Memory::PageSize)}; @@ -360,9 +381,12 @@ public: system.Memory().ReadBlock(src_addr, source_data.data(), source_data.size()); system.Memory().WriteBlock(dst_addr, source_data.data(), source_data.size()); }}; - CopyCode(nro_addr + nro_header.text_offset, text_start, nro_header.text_size); - CopyCode(nro_addr + nro_header.ro_offset, ro_start, nro_header.ro_size); - CopyCode(nro_addr + nro_header.rw_offset, data_start, nro_header.rw_size); + CopyCode(nro_addr + nro_header.segment_headers[0].memory_offset, text_start, + nro_header.segment_headers[0].memory_size); + CopyCode(nro_addr + nro_header.segment_headers[1].memory_offset, ro_start, + nro_header.segment_headers[1].memory_size); + CopyCode(nro_addr + nro_header.segment_headers[2].memory_offset, data_start, + nro_header.segment_headers[2].memory_size); CASCADE_CODE(process->PageTable().SetCodeMemoryPermission( text_start, ro_start - text_start, Kernel::Memory::MemoryPermission::ReadAndExecute)); @@ -484,9 +508,11 @@ public: } // Track the loaded NRO - nro.insert_or_assign(*map_result, NROInfo{hash, *map_result, nro_size, bss_address, - bss_size, header.text_size, header.ro_size, - header.rw_size, nro_address}); + nro.insert_or_assign(*map_result, + NROInfo{hash, *map_result, nro_size, bss_address, bss_size, + header.segment_headers[0].memory_size, + header.segment_headers[1].memory_size, + header.segment_headers[2].memory_size, nro_address}); // Invalidate JIT caches for the newly mapped process code system.InvalidateCpuInstructionCaches(); @@ -584,11 +610,17 @@ private: static bool IsValidNRO(const NROHeader& header, u64 nro_size, u64 bss_size) { return header.magic == Common::MakeMagic('N', 'R', 'O', '0') && header.nro_size == nro_size && header.bss_size == bss_size && - header.ro_offset == header.text_offset + header.text_size && - header.rw_offset == header.ro_offset + header.ro_size && - nro_size == header.rw_offset + header.rw_size && - Common::Is4KBAligned(header.text_size) && Common::Is4KBAligned(header.ro_size) && - Common::Is4KBAligned(header.rw_size); + header.segment_headers[1].memory_offset == + header.segment_headers[0].memory_offset + + header.segment_headers[0].memory_size && + header.segment_headers[2].memory_offset == + header.segment_headers[1].memory_offset + + header.segment_headers[1].memory_size && + nro_size == header.segment_headers[2].memory_offset + + header.segment_headers[2].memory_size && + Common::Is4KBAligned(header.segment_headers[0].memory_size) && + Common::Is4KBAligned(header.segment_headers[1].memory_size) && + Common::Is4KBAligned(header.segment_headers[2].memory_size); } Core::System& system; }; From a087b3365a26b14ea6733b213998df5ff318afb3 Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Sun, 31 May 2020 19:12:09 +0200 Subject: [PATCH 003/252] Add comment to nrr_kind MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to Atmosphére (https://github.com/Atmosphere-NX/Atmosphere/blob/c7026b90940a1d88f9c10a6d98263bf22e654fa5/libraries/libstratosphere/include/stratosphere/ro/ro_types.hpp), nrr_kind (Atmosphére calls it "type") is 7.0.0+ --- src/core/hle/service/ldr/ldr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index 62d4bfc08..252b79d2c 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp @@ -58,7 +58,7 @@ struct NRRHeader { std::array signature; u64_le application_id; u32_le size; - u8 nrr_kind; + u8 nrr_kind; // 7.0.0+ std::array reserved_2; u32_le hash_offset; u32_le hash_count; From dfd1badc125ca5a4a4fa4587b81f2e51bebb2a07 Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Tue, 2 Jun 2020 17:54:10 +0200 Subject: [PATCH 004/252] Address review comments --- src/core/hle/service/ldr/ldr.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index 252b79d2c..69caf3aae 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp @@ -59,7 +59,7 @@ struct NRRHeader { u64_le application_id; u32_le size; u8 nrr_kind; // 7.0.0+ - std::array reserved_2; + std::array reserved_2; u32_le hash_offset; u32_le hash_count; u64_le reserved_3; @@ -89,15 +89,15 @@ struct NROHeader { u32_le version; u32_le nro_size; u32_le flags; - // .text, .ro, .data (yuzu previously called it "rw" instead of "data") - std::array segment_headers; + // .text, .ro, .data + std::array segment_headers; u32_le bss_size; u32_le reserved; std::array build_id; u32_le dso_handle_offset; u32_le unused_2; // .apiInfo, .dynstr, .dynsym - std::array segment_headers_2; + std::array segment_headers_2; }; static_assert(sizeof(NROHeader) == 0x80, "NROHeader has invalid size."); From 20a779299a62808b4e4bbc7bfde6e9e4487a857d Mon Sep 17 00:00:00 2001 From: unknown <27208977+FreddyFunk@users.noreply.github.com> Date: Mon, 8 Jun 2020 23:58:04 +0200 Subject: [PATCH 005/252] Add game versio to title bar --- src/yuzu/main.cpp | 27 +++++++++++++++------------ src/yuzu/main.h | 3 ++- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 270cccc77..f552b9401 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -1037,17 +1037,19 @@ void GMainWindow::BootGame(const QString& filename) { const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID(); std::string title_name; + std::string title_version; const auto res = Core::System::GetInstance().GetGameName(title_name); - if (res != Loader::ResultStatus::Success) { - const auto metadata = FileSys::PatchManager(title_id).GetControlMetadata(); - if (metadata.first != nullptr) - title_name = metadata.first->GetApplicationName(); - if (title_name.empty()) - title_name = FileUtil::GetFilename(filename.toStdString()); + const auto metadata = FileSys::PatchManager(title_id).GetControlMetadata(); + if (metadata.first != nullptr) { + title_version = metadata.first->GetVersionString(); + title_name = metadata.first->GetApplicationName(); } - LOG_INFO(Frontend, "Booting game: {:016X} | {}", title_id, title_name); - UpdateWindowTitle(QString::fromStdString(title_name)); + if (res != Loader::ResultStatus::Success || title_name.empty()) { + title_name = FileUtil::GetFilename(filename.toStdString()); + } + LOG_INFO(Frontend, "Booting game: {:016X} | {} | {}", title_id, title_name, title_version); + UpdateWindowTitle(title_name, title_version); loading_screen->Prepare(Core::System::GetInstance().GetAppLoader()); loading_screen->show(); @@ -1995,7 +1997,8 @@ void GMainWindow::OnCaptureScreenshot() { OnStartGame(); } -void GMainWindow::UpdateWindowTitle(const QString& title_name) { +void GMainWindow::UpdateWindowTitle(const std::string& title_name, + const std::string& title_version) { const auto full_name = std::string(Common::g_build_fullname); const auto branch_name = std::string(Common::g_scm_branch); const auto description = std::string(Common::g_scm_desc); @@ -2004,7 +2007,7 @@ void GMainWindow::UpdateWindowTitle(const QString& title_name) { const auto date = QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd")).toStdString(); - if (title_name.isEmpty()) { + if (title_name.empty()) { const auto fmt = std::string(Common::g_title_bar_format_idle); setWindowTitle(QString::fromStdString(fmt::format(fmt.empty() ? "yuzu {0}| {1}-{2}" : fmt, full_name, branch_name, description, @@ -2012,8 +2015,8 @@ void GMainWindow::UpdateWindowTitle(const QString& title_name) { } else { const auto fmt = std::string(Common::g_title_bar_format_running); setWindowTitle(QString::fromStdString( - fmt::format(fmt.empty() ? "yuzu {0}| {3} | {1}-{2}" : fmt, full_name, branch_name, - description, title_name.toStdString(), date, build_id))); + fmt::format(fmt.empty() ? "yuzu {0}| {3} | {4} | {1}-{2}" : fmt, full_name, branch_name, + description, title_name, title_version, date, build_id))); } } diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 4f4c8ddbe..9ad61cab8 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -215,7 +215,8 @@ private slots: private: std::optional SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id); - void UpdateWindowTitle(const QString& title_name = {}); + void UpdateWindowTitle(const std::string& title_name = {}, + const std::string& title_version = {}); void UpdateStatusBar(); void HideMouseCursor(); void ShowMouseCursor(); From 151a3fe7b327c8adad2bc103029457c353b261c0 Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Sun, 14 Jun 2020 19:28:39 +0200 Subject: [PATCH 006/252] Attempt to fix crashes in SSBU and refactor IsValidNRO --- src/core/hle/service/ldr/ldr.cpp | 95 ++++++++++++++++++++------------ 1 file changed, 59 insertions(+), 36 deletions(-) diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index 69caf3aae..5b372b7db 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp @@ -39,22 +39,27 @@ constexpr ResultCode ERROR_NOT_INITIALIZED{ErrorModule::Loader, 87}; constexpr std::size_t MAXIMUM_LOADED_RO{0x40}; constexpr std::size_t MAXIMUM_MAP_RETRIES{0x200}; -struct Certification { +constexpr std::size_t TEXT_INDEX{0}; +constexpr std::size_t RO_INDEX{1}; +constexpr std::size_t DATA_INDEX{2}; + +struct NRRCertification { u64_le application_id_mask; u64_le application_id_pattern; std::array reserved; std::array public_key; // Also known as modulus std::array signature; }; -static_assert(sizeof(Certification) == 0x220, "Certification has invalid size!"); +static_assert(sizeof(NRRCertification) == 0x220, "Certification has invalid size!"); using SHA256Hash = std::array; +#pragma pack(1) struct NRRHeader { u32_le magic; u32_le certification_signature_key_generation; // 9.0.0+ u64_le reserved; - Certification certification; + NRRCertification certification; std::array signature; u64_le application_id; u32_le size; @@ -63,21 +68,19 @@ struct NRRHeader { u32_le hash_offset; u32_le hash_count; u64_le reserved_3; - - // Must be dynamically allocated because, according to - // SwitchBrew, its size is (0x20 * hash_count) and - // it's impossible to determine the value of hash_count - // (SwitchBrew calls it "NumHash") before runtime, - // therefore it's not possible to calculate a SHA-256 - std::vector NroHashList; }; +#pragma pack() +static_assert(sizeof(NRRHeader) == 0x350, "NRRHeader has invalid size!"); +#pragma pack(1) struct SegmentHeader { u32_le memory_offset; u32_le memory_size; }; +#pragma pack() static_assert(sizeof(SegmentHeader) == 0x8, "SegmentHeader has invalid size!"); +#pragma pack(1) struct NROHeader { // Switchbrew calls this "Start" (0x10) u32_le unused; @@ -99,8 +102,10 @@ struct NROHeader { // .apiInfo, .dynstr, .dynsym std::array segment_headers_2; }; +#pragma pack() static_assert(sizeof(NROHeader) == 0x80, "NROHeader has invalid size."); +#pragma pack(1) struct NROInfo { SHA256Hash hash{}; VAddr nro_address{}; @@ -112,6 +117,8 @@ struct NROInfo { std::size_t data_size{}; VAddr src_addr{}; }; +#pragma pack() +static_assert(sizeof(NROInfo) == 0x60, "NROInfo has invalid size."); class DebugMonitor final : public ServiceFramework { public: @@ -369,10 +376,10 @@ public: ResultCode LoadNro(Kernel::Process* process, const NROHeader& nro_header, VAddr nro_addr, VAddr start) const { - const VAddr text_start{start + nro_header.segment_headers[0].memory_offset}; - const VAddr ro_start{start + nro_header.segment_headers[1].memory_offset}; - const VAddr data_start{start + nro_header.segment_headers[2].memory_offset}; - const VAddr bss_start{data_start + nro_header.segment_headers[2].memory_size}; + const VAddr text_start{start + nro_header.segment_headers[TEXT_INDEX].memory_offset}; + const VAddr ro_start{start + nro_header.segment_headers[RO_INDEX].memory_offset}; + const VAddr data_start{start + nro_header.segment_headers[DATA_INDEX].memory_offset}; + const VAddr bss_start{data_start + nro_header.segment_headers[DATA_INDEX].memory_size}; const VAddr bss_end_addr{ Common::AlignUp(bss_start + nro_header.bss_size, Kernel::Memory::PageSize)}; @@ -381,12 +388,12 @@ public: system.Memory().ReadBlock(src_addr, source_data.data(), source_data.size()); system.Memory().WriteBlock(dst_addr, source_data.data(), source_data.size()); }}; - CopyCode(nro_addr + nro_header.segment_headers[0].memory_offset, text_start, - nro_header.segment_headers[0].memory_size); - CopyCode(nro_addr + nro_header.segment_headers[1].memory_offset, ro_start, - nro_header.segment_headers[1].memory_size); - CopyCode(nro_addr + nro_header.segment_headers[2].memory_offset, data_start, - nro_header.segment_headers[2].memory_size); + CopyCode(nro_addr + nro_header.segment_headers[TEXT_INDEX].memory_offset, text_start, + nro_header.segment_headers[TEXT_INDEX].memory_size); + CopyCode(nro_addr + nro_header.segment_headers[RO_INDEX].memory_offset, ro_start, + nro_header.segment_headers[RO_INDEX].memory_size); + CopyCode(nro_addr + nro_header.segment_headers[DATA_INDEX].memory_offset, data_start, + nro_header.segment_headers[DATA_INDEX].memory_size); CASCADE_CODE(process->PageTable().SetCodeMemoryPermission( text_start, ro_start - text_start, Kernel::Memory::MemoryPermission::ReadAndExecute)); @@ -510,9 +517,9 @@ public: // Track the loaded NRO nro.insert_or_assign(*map_result, NROInfo{hash, *map_result, nro_size, bss_address, bss_size, - header.segment_headers[0].memory_size, - header.segment_headers[1].memory_size, - header.segment_headers[2].memory_size, nro_address}); + header.segment_headers[TEXT_INDEX].memory_size, + header.segment_headers[RO_INDEX].memory_size, + header.segment_headers[DATA_INDEX].memory_size, nro_address}); // Invalidate JIT caches for the newly mapped process code system.InvalidateCpuInstructionCaches(); @@ -608,19 +615,35 @@ private: } static bool IsValidNRO(const NROHeader& header, u64 nro_size, u64 bss_size) { - return header.magic == Common::MakeMagic('N', 'R', 'O', '0') && - header.nro_size == nro_size && header.bss_size == bss_size && - header.segment_headers[1].memory_offset == - header.segment_headers[0].memory_offset + - header.segment_headers[0].memory_size && - header.segment_headers[2].memory_offset == - header.segment_headers[1].memory_offset + - header.segment_headers[1].memory_size && - nro_size == header.segment_headers[2].memory_offset + - header.segment_headers[2].memory_size && - Common::Is4KBAligned(header.segment_headers[0].memory_size) && - Common::Is4KBAligned(header.segment_headers[1].memory_size) && - Common::Is4KBAligned(header.segment_headers[2].memory_size); + + const bool valid_magic = header.magic == Common::MakeMagic('N', 'R', 'O', '0'); + + const bool valid_nro_size = header.nro_size == nro_size; + + const bool valid_bss_size = header.bss_size == bss_size; + + const bool valid_ro_offset = header.segment_headers[RO_INDEX].memory_offset == + header.segment_headers[TEXT_INDEX].memory_offset + + header.segment_headers[TEXT_INDEX].memory_size; + + const bool valid_rw_offset = header.segment_headers[DATA_INDEX].memory_offset == + header.segment_headers[RO_INDEX].memory_offset + + header.segment_headers[RO_INDEX].memory_size; + + const bool valid_nro_calculated_size = + nro_size == header.segment_headers[DATA_INDEX].memory_offset + + header.segment_headers[DATA_INDEX].memory_size; + + const bool text_aligned = + Common::Is4KBAligned(header.segment_headers[TEXT_INDEX].memory_size); + + const bool ro_aligned = Common::Is4KBAligned(header.segment_headers[RO_INDEX].memory_size); + + const bool rw_aligned = + Common::Is4KBAligned(header.segment_headers[DATA_INDEX].memory_size); + + return valid_magic && valid_nro_size && valid_bss_size && valid_ro_offset && + valid_rw_offset && valid_nro_calculated_size && text_aligned && ro_aligned && rw_aligned; } Core::System& system; }; From 761d2060493c5c4e1c920659d3b00d8a56164941 Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Sun, 14 Jun 2020 19:30:08 +0200 Subject: [PATCH 007/252] Make assert strings consistent --- src/core/hle/service/ldr/ldr.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index 5b372b7db..20f268ec0 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp @@ -50,7 +50,7 @@ struct NRRCertification { std::array public_key; // Also known as modulus std::array signature; }; -static_assert(sizeof(NRRCertification) == 0x220, "Certification has invalid size!"); +static_assert(sizeof(NRRCertification) == 0x220, "Certification has invalid size."); using SHA256Hash = std::array; @@ -70,7 +70,7 @@ struct NRRHeader { u64_le reserved_3; }; #pragma pack() -static_assert(sizeof(NRRHeader) == 0x350, "NRRHeader has invalid size!"); +static_assert(sizeof(NRRHeader) == 0x350, "NRRHeader has invalid size."); #pragma pack(1) struct SegmentHeader { @@ -78,7 +78,7 @@ struct SegmentHeader { u32_le memory_size; }; #pragma pack() -static_assert(sizeof(SegmentHeader) == 0x8, "SegmentHeader has invalid size!"); +static_assert(sizeof(SegmentHeader) == 0x8, "SegmentHeader has invalid size."); #pragma pack(1) struct NROHeader { From 1520d7865d0cd0afde690f99de8bf34400671556 Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Sun, 14 Jun 2020 19:34:58 +0200 Subject: [PATCH 008/252] Clang-format --- src/core/hle/service/ldr/ldr.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index 20f268ec0..dd0bdde38 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp @@ -643,7 +643,8 @@ private: Common::Is4KBAligned(header.segment_headers[DATA_INDEX].memory_size); return valid_magic && valid_nro_size && valid_bss_size && valid_ro_offset && - valid_rw_offset && valid_nro_calculated_size && text_aligned && ro_aligned && rw_aligned; + valid_rw_offset && valid_nro_calculated_size && text_aligned && ro_aligned && + rw_aligned; } Core::System& system; }; From 198b0fa7901da3a9ab71680351919c41b349e453 Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Sun, 14 Jun 2020 19:37:44 +0200 Subject: [PATCH 009/252] Use consistent variable names --- src/core/hle/service/ldr/ldr.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index dd0bdde38..d4c6879ac 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp @@ -626,7 +626,7 @@ private: header.segment_headers[TEXT_INDEX].memory_offset + header.segment_headers[TEXT_INDEX].memory_size; - const bool valid_rw_offset = header.segment_headers[DATA_INDEX].memory_offset == + const bool valid_data_offset = header.segment_headers[DATA_INDEX].memory_offset == header.segment_headers[RO_INDEX].memory_offset + header.segment_headers[RO_INDEX].memory_size; @@ -639,12 +639,12 @@ private: const bool ro_aligned = Common::Is4KBAligned(header.segment_headers[RO_INDEX].memory_size); - const bool rw_aligned = + const bool data_aligned = Common::Is4KBAligned(header.segment_headers[DATA_INDEX].memory_size); return valid_magic && valid_nro_size && valid_bss_size && valid_ro_offset && - valid_rw_offset && valid_nro_calculated_size && text_aligned && ro_aligned && - rw_aligned; + valid_data_offset && valid_nro_calculated_size && text_aligned && ro_aligned && + data_aligned; } Core::System& system; }; From 39213b1c593828147b4c2c0738e3fc72221c8e6e Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Sun, 14 Jun 2020 19:41:28 +0200 Subject: [PATCH 010/252] Clang-format again --- src/core/hle/service/ldr/ldr.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index d4c6879ac..09c17f41f 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp @@ -627,8 +627,8 @@ private: header.segment_headers[TEXT_INDEX].memory_size; const bool valid_data_offset = header.segment_headers[DATA_INDEX].memory_offset == - header.segment_headers[RO_INDEX].memory_offset + - header.segment_headers[RO_INDEX].memory_size; + header.segment_headers[RO_INDEX].memory_offset + + header.segment_headers[RO_INDEX].memory_size; const bool valid_nro_calculated_size = nro_size == header.segment_headers[DATA_INDEX].memory_offset + From 4b71bf654d54210c18e77abda1b3ca20c0e43a63 Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Tue, 16 Jun 2020 15:57:02 +0200 Subject: [PATCH 011/252] Update assert string --- src/core/hle/service/ldr/ldr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index 09c17f41f..6daea3408 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp @@ -50,7 +50,7 @@ struct NRRCertification { std::array public_key; // Also known as modulus std::array signature; }; -static_assert(sizeof(NRRCertification) == 0x220, "Certification has invalid size."); +static_assert(sizeof(NRRCertification) == 0x220, "NRRCertification has invalid size."); using SHA256Hash = std::array; From c0d61620506ef4d70b1aa60bc9961f08ce9a939f Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Tue, 16 Jun 2020 20:24:58 +0200 Subject: [PATCH 012/252] Revert IsValidNRO refactor but make it more readable --- src/core/hle/service/ldr/ldr.cpp | 39 +++++++++++--------------------- 1 file changed, 13 insertions(+), 26 deletions(-) diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index 6daea3408..ca53a9923 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp @@ -615,36 +615,23 @@ private: } static bool IsValidNRO(const NROHeader& header, u64 nro_size, u64 bss_size) { + return header.magic == Common::MakeMagic('N', 'R', 'O', '0') && + header.nro_size == nro_size && header.bss_size == bss_size && - const bool valid_magic = header.magic == Common::MakeMagic('N', 'R', 'O', '0'); + header.segment_headers[RO_INDEX].memory_offset == + header.segment_headers[TEXT_INDEX].memory_offset + + header.segment_headers[TEXT_INDEX].memory_size && - const bool valid_nro_size = header.nro_size == nro_size; + header.segment_headers[DATA_INDEX].memory_offset == + header.segment_headers[RO_INDEX].memory_offset + + header.segment_headers[RO_INDEX].memory_size && - const bool valid_bss_size = header.bss_size == bss_size; + nro_size == header.segment_headers[DATA_INDEX].memory_offset + + header.segment_headers[DATA_INDEX].memory_size && - const bool valid_ro_offset = header.segment_headers[RO_INDEX].memory_offset == - header.segment_headers[TEXT_INDEX].memory_offset + - header.segment_headers[TEXT_INDEX].memory_size; - - const bool valid_data_offset = header.segment_headers[DATA_INDEX].memory_offset == - header.segment_headers[RO_INDEX].memory_offset + - header.segment_headers[RO_INDEX].memory_size; - - const bool valid_nro_calculated_size = - nro_size == header.segment_headers[DATA_INDEX].memory_offset + - header.segment_headers[DATA_INDEX].memory_size; - - const bool text_aligned = - Common::Is4KBAligned(header.segment_headers[TEXT_INDEX].memory_size); - - const bool ro_aligned = Common::Is4KBAligned(header.segment_headers[RO_INDEX].memory_size); - - const bool data_aligned = - Common::Is4KBAligned(header.segment_headers[DATA_INDEX].memory_size); - - return valid_magic && valid_nro_size && valid_bss_size && valid_ro_offset && - valid_data_offset && valid_nro_calculated_size && text_aligned && ro_aligned && - data_aligned; + Common::Is4KBAligned(header.segment_headers[TEXT_INDEX].memory_size) && + Common::Is4KBAligned(header.segment_headers[RO_INDEX].memory_size) && + Common::Is4KBAligned(header.segment_headers[DATA_INDEX].memory_size); } Core::System& system; }; From bd9495c9ab5c1b452c700c54c2bbacf290306741 Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Tue, 16 Jun 2020 20:28:44 +0200 Subject: [PATCH 013/252] Remove unnecessary pragmas --- src/core/hle/service/ldr/ldr.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index ca53a9923..9ce2db191 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp @@ -54,7 +54,6 @@ static_assert(sizeof(NRRCertification) == 0x220, "NRRCertification has invalid s using SHA256Hash = std::array; -#pragma pack(1) struct NRRHeader { u32_le magic; u32_le certification_signature_key_generation; // 9.0.0+ @@ -69,18 +68,14 @@ struct NRRHeader { u32_le hash_count; u64_le reserved_3; }; -#pragma pack() static_assert(sizeof(NRRHeader) == 0x350, "NRRHeader has invalid size."); -#pragma pack(1) struct SegmentHeader { u32_le memory_offset; u32_le memory_size; }; -#pragma pack() static_assert(sizeof(SegmentHeader) == 0x8, "SegmentHeader has invalid size."); -#pragma pack(1) struct NROHeader { // Switchbrew calls this "Start" (0x10) u32_le unused; @@ -102,10 +97,8 @@ struct NROHeader { // .apiInfo, .dynstr, .dynsym std::array segment_headers_2; }; -#pragma pack() static_assert(sizeof(NROHeader) == 0x80, "NROHeader has invalid size."); -#pragma pack(1) struct NROInfo { SHA256Hash hash{}; VAddr nro_address{}; @@ -117,7 +110,6 @@ struct NROInfo { std::size_t data_size{}; VAddr src_addr{}; }; -#pragma pack() static_assert(sizeof(NROInfo) == 0x60, "NROInfo has invalid size."); class DebugMonitor final : public ServiceFramework { From 7a59eeb5befbb8bdeb93d27adb6848ef40511644 Mon Sep 17 00:00:00 2001 From: Kaiwen Xu Date: Tue, 16 Jun 2020 20:45:20 -0700 Subject: [PATCH 014/252] Fix framebuffer size on fractional scaling display. --- src/yuzu/bootmanager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 1f5e43043..7f617d6a7 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -355,7 +355,7 @@ QByteArray GRenderWindow::saveGeometry() { } qreal GRenderWindow::windowPixelRatio() const { - return devicePixelRatio(); + return devicePixelRatioF(); } std::pair GRenderWindow::ScaleTouch(const QPointF& pos) const { From 684dfbf20906c4476a7bc28273be9ebd53ab196d Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Thu, 18 Jun 2020 15:45:47 +0200 Subject: [PATCH 015/252] Move SHA256Hash to its original position It's not needed to have it in its previous position anymore --- src/core/hle/service/ldr/ldr.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index 9ce2db191..9f376657c 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp @@ -52,8 +52,6 @@ struct NRRCertification { }; static_assert(sizeof(NRRCertification) == 0x220, "NRRCertification has invalid size."); -using SHA256Hash = std::array; - struct NRRHeader { u32_le magic; u32_le certification_signature_key_generation; // 9.0.0+ @@ -99,6 +97,8 @@ struct NROHeader { }; static_assert(sizeof(NROHeader) == 0x80, "NROHeader has invalid size."); +using SHA256Hash = std::array; + struct NROInfo { SHA256Hash hash{}; VAddr nro_address{}; From 13ed9438fb47d62663fb1ef367baac1a567b25b3 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Tue, 4 Feb 2020 11:23:12 -0400 Subject: [PATCH 016/252] Common: Implement a basic SpinLock class --- src/common/CMakeLists.txt | 2 ++ src/common/spin_lock.cpp | 46 +++++++++++++++++++++++++++++++++++++++ src/common/spin_lock.h | 20 +++++++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 src/common/spin_lock.cpp create mode 100644 src/common/spin_lock.h diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 0a3e2f4d1..c8bf80372 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -143,6 +143,8 @@ add_library(common STATIC scm_rev.cpp scm_rev.h scope_exit.h + spin_lock.cpp + spin_lock.h string_util.cpp string_util.h swap.h diff --git a/src/common/spin_lock.cpp b/src/common/spin_lock.cpp new file mode 100644 index 000000000..8077b78d2 --- /dev/null +++ b/src/common/spin_lock.cpp @@ -0,0 +1,46 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/spin_lock.h" + +#if _MSC_VER +#include +#if _M_AMD64 +#define __x86_64__ 1 +#endif +#if _M_ARM64 +#define __aarch64__ 1 +#endif +#else +#if __x86_64__ +#include +#endif +#endif + +namespace { + +void thread_pause() { +#if __x86_64__ + _mm_pause(); +#elif __aarch64__ && _MSC_VER + __yield(); +#elif __aarch64__ + asm("yield"); +#endif +} + +} // namespace + +namespace Common { + +void SpinLock::lock() { + while (lck.test_and_set(std::memory_order_acquire)) + thread_pause(); +} + +void SpinLock::unlock() { + lck.clear(std::memory_order_release); +} + +} // namespace Common diff --git a/src/common/spin_lock.h b/src/common/spin_lock.h new file mode 100644 index 000000000..cbc67b6c8 --- /dev/null +++ b/src/common/spin_lock.h @@ -0,0 +1,20 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include + +namespace Common { + +class SpinLock { +public: + void lock(); + void unlock(); + +private: + std::atomic_flag lck = ATOMIC_FLAG_INIT; +}; + +} // namespace Common From bc266a9d98f38f6fd1006f1ca52bd57e6a7f37d3 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Tue, 4 Feb 2020 15:06:23 -0400 Subject: [PATCH 017/252] Common: Implement a basic Fiber class. --- src/common/CMakeLists.txt | 2 + src/common/fiber.cpp | 147 ++++++++++++++++++++++++++++++++++++++ src/common/fiber.h | 55 ++++++++++++++ 3 files changed, 204 insertions(+) create mode 100644 src/common/fiber.cpp create mode 100644 src/common/fiber.h diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index c8bf80372..554d6e253 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -110,6 +110,8 @@ add_library(common STATIC common_types.h dynamic_library.cpp dynamic_library.h + fiber.cpp + fiber.h file_util.cpp file_util.h hash.h diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp new file mode 100644 index 000000000..eb59f1aa9 --- /dev/null +++ b/src/common/fiber.cpp @@ -0,0 +1,147 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/fiber.h" + +namespace Common { + +#ifdef _MSC_VER +#include + +struct Fiber::FiberImpl { + LPVOID handle = nullptr; +}; + +void Fiber::_start([[maybe_unused]] void* parameter) { + guard.lock(); + if (previous_fiber) { + previous_fiber->guard.unlock(); + previous_fiber = nullptr; + } + entry_point(start_parameter); +} + +static void __stdcall FiberStartFunc(LPVOID lpFiberParameter) +{ + auto fiber = static_cast(lpFiberParameter); + fiber->_start(nullptr); +} + +Fiber::Fiber(std::function&& entry_point_func, void* start_parameter) + : guard{}, entry_point{std::move(entry_point_func)}, start_parameter{start_parameter}, previous_fiber{} { + impl = std::make_unique(); + impl->handle = CreateFiber(0, &FiberStartFunc, this); +} + +Fiber::Fiber() : guard{}, entry_point{}, start_parameter{}, previous_fiber{} { + impl = std::make_unique(); +} + +Fiber::~Fiber() { + // Make sure the Fiber is not being used + guard.lock(); + guard.unlock(); + DeleteFiber(impl->handle); +} + +void Fiber::Exit() { + if (!is_thread_fiber) { + return; + } + ConvertFiberToThread(); + guard.unlock(); +} + +void Fiber::YieldTo(std::shared_ptr from, std::shared_ptr to) { + to->guard.lock(); + to->previous_fiber = from; + SwitchToFiber(to->impl->handle); + auto previous_fiber = from->previous_fiber; + if (previous_fiber) { + previous_fiber->guard.unlock(); + previous_fiber.reset(); + } +} + +std::shared_ptr Fiber::ThreadToFiber() { + std::shared_ptr fiber = std::shared_ptr{new Fiber()}; + fiber->guard.lock(); + fiber->impl->handle = ConvertThreadToFiber(NULL); + fiber->is_thread_fiber = true; + return fiber; +} + +#else + +#include + +constexpr std::size_t default_stack_size = 1024 * 1024 * 4; // 4MB + +struct Fiber::FiberImpl { + boost::context::detail::fcontext_t context; + std::array stack; +}; + +void Fiber::_start(void* parameter) { + guard.lock(); + boost::context::detail::transfer_t* transfer = static_cast(parameter); + if (previous_fiber) { + previous_fiber->impl->context = transfer->fctx; + previous_fiber->guard.unlock(); + previous_fiber = nullptr; + } + entry_point(start_parameter); +} + +static void FiberStartFunc(boost::context::detail::transfer_t transfer) +{ + auto fiber = static_cast(transfer.data); + fiber->_start(&transfer); +} + +Fiber::Fiber(std::function&& entry_point_func, void* start_parameter) + : guard{}, entry_point{std::move(entry_point_func)}, start_parameter{start_parameter}, previous_fiber{} { + impl = std::make_unique(); + auto start_func = std::bind(&Fiber::start, this); + impl->context = + boost::context::detail::make_fcontext(impl->stack.data(), impl->stack.size(), &start_func); +} + +Fiber::Fiber() : guard{}, entry_point{}, start_parameter{}, previous_fiber{} { + impl = std::make_unique(); +} + +Fiber::~Fiber() { + // Make sure the Fiber is not being used + guard.lock(); + guard.unlock(); +} + +void Fiber::Exit() { + if (!is_thread_fiber) { + return; + } + guard.unlock(); +} + +void Fiber::YieldTo(std::shared_ptr from, std::shared_ptr to) { + to->guard.lock(); + to->previous_fiber = from; + auto transfer = boost::context::detail::jump_fcontext(to->impl.context, nullptr); + auto previous_fiber = from->previous_fiber; + if (previous_fiber) { + previous_fiber->impl->context = transfer.fctx; + previous_fiber->guard.unlock(); + previous_fiber.reset(); + } +} + +std::shared_ptr Fiber::ThreadToFiber() { + std::shared_ptr fiber = std::shared_ptr{new Fiber()}; + fiber->is_thread_fiber = true; + return fiber; +} + +#endif +} // namespace Common diff --git a/src/common/fiber.h b/src/common/fiber.h new file mode 100644 index 000000000..ab44905cf --- /dev/null +++ b/src/common/fiber.h @@ -0,0 +1,55 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +#include "common/common_types.h" +#include "common/spin_lock.h" + +namespace Common { + +class Fiber { +public: + Fiber(std::function&& entry_point_func, void* start_parameter); + ~Fiber(); + + Fiber(const Fiber&) = delete; + Fiber& operator=(const Fiber&) = delete; + + Fiber(Fiber&&) = default; + Fiber& operator=(Fiber&&) = default; + + /// Yields control from Fiber 'from' to Fiber 'to' + /// Fiber 'from' must be the currently running fiber. + static void YieldTo(std::shared_ptr from, std::shared_ptr to); + static std::shared_ptr ThreadToFiber(); + + /// Only call from main thread's fiber + void Exit(); + + /// Used internally but required to be public, Shall not be used + void _start(void* parameter); + + /// Changes the start parameter of the fiber. Has no effect if the fiber already started + void SetStartParameter(void* new_parameter) { + start_parameter = new_parameter; + } + +private: + Fiber(); + + struct FiberImpl; + + SpinLock guard; + std::function entry_point; + void* start_parameter; + std::shared_ptr previous_fiber; + std::unique_ptr impl; + bool is_thread_fiber{}; +}; + +} // namespace Common From 8d0e3c542258cc50081af93aa85e0e3cbf8900c3 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Wed, 5 Feb 2020 14:13:16 -0400 Subject: [PATCH 018/252] Tests: Add tests for fibers and refactor/fix Fiber class --- src/common/fiber.cpp | 32 +++--- src/common/fiber.h | 19 +++- src/tests/CMakeLists.txt | 1 + src/tests/common/fibers.cpp | 214 ++++++++++++++++++++++++++++++++++++ 4 files changed, 247 insertions(+), 19 deletions(-) create mode 100644 src/tests/common/fibers.cpp diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index eb59f1aa9..a2c0401c4 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp @@ -3,18 +3,21 @@ // Refer to the license.txt file included. #include "common/fiber.h" +#ifdef _MSC_VER +#include +#else +#include +#endif namespace Common { #ifdef _MSC_VER -#include struct Fiber::FiberImpl { LPVOID handle = nullptr; }; -void Fiber::_start([[maybe_unused]] void* parameter) { - guard.lock(); +void Fiber::start() { if (previous_fiber) { previous_fiber->guard.unlock(); previous_fiber = nullptr; @@ -22,10 +25,10 @@ void Fiber::_start([[maybe_unused]] void* parameter) { entry_point(start_parameter); } -static void __stdcall FiberStartFunc(LPVOID lpFiberParameter) +void __stdcall Fiber::FiberStartFunc(void* fiber_parameter) { - auto fiber = static_cast(lpFiberParameter); - fiber->_start(nullptr); + auto fiber = static_cast(fiber_parameter); + fiber->start(); } Fiber::Fiber(std::function&& entry_point_func, void* start_parameter) @@ -74,30 +77,26 @@ std::shared_ptr Fiber::ThreadToFiber() { #else -#include - constexpr std::size_t default_stack_size = 1024 * 1024 * 4; // 4MB -struct Fiber::FiberImpl { - boost::context::detail::fcontext_t context; +struct alignas(64) Fiber::FiberImpl { std::array stack; + boost::context::detail::fcontext_t context; }; -void Fiber::_start(void* parameter) { - guard.lock(); - boost::context::detail::transfer_t* transfer = static_cast(parameter); +void Fiber::start(boost::context::detail::transfer_t& transfer) { if (previous_fiber) { - previous_fiber->impl->context = transfer->fctx; + previous_fiber->impl->context = transfer.fctx; previous_fiber->guard.unlock(); previous_fiber = nullptr; } entry_point(start_parameter); } -static void FiberStartFunc(boost::context::detail::transfer_t transfer) +void Fiber::FiberStartFunc(boost::context::detail::transfer_t transfer) { auto fiber = static_cast(transfer.data); - fiber->_start(&transfer); + fiber->start(transfer); } Fiber::Fiber(std::function&& entry_point_func, void* start_parameter) @@ -139,6 +138,7 @@ void Fiber::YieldTo(std::shared_ptr from, std::shared_ptr to) { std::shared_ptr Fiber::ThreadToFiber() { std::shared_ptr fiber = std::shared_ptr{new Fiber()}; + fiber->guard.lock(); fiber->is_thread_fiber = true; return fiber; } diff --git a/src/common/fiber.h b/src/common/fiber.h index ab44905cf..812d6644a 100644 --- a/src/common/fiber.h +++ b/src/common/fiber.h @@ -10,6 +10,12 @@ #include "common/common_types.h" #include "common/spin_lock.h" +#ifndef _MSC_VER +namespace boost::context::detail { + struct transfer_t; +} +#endif + namespace Common { class Fiber { @@ -31,9 +37,6 @@ public: /// Only call from main thread's fiber void Exit(); - /// Used internally but required to be public, Shall not be used - void _start(void* parameter); - /// Changes the start parameter of the fiber. Has no effect if the fiber already started void SetStartParameter(void* new_parameter) { start_parameter = new_parameter; @@ -42,6 +45,16 @@ public: private: Fiber(); +#ifdef _MSC_VER + void start(); + static void FiberStartFunc(void* fiber_parameter); +#else + void start(boost::context::detail::transfer_t& transfer); + static void FiberStartFunc(boost::context::detail::transfer_t transfer); +#endif + + + struct FiberImpl; SpinLock guard; diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index c7038b217..47ef30aa9 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -1,6 +1,7 @@ add_executable(tests common/bit_field.cpp common/bit_utils.cpp + common/fibers.cpp common/multi_level_queue.cpp common/param_package.cpp common/ring_buffer.cpp diff --git a/src/tests/common/fibers.cpp b/src/tests/common/fibers.cpp new file mode 100644 index 000000000..ff840afa6 --- /dev/null +++ b/src/tests/common/fibers.cpp @@ -0,0 +1,214 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "common/common_types.h" +#include "common/fiber.h" +#include "common/spin_lock.h" + +namespace Common { + +class TestControl1 { +public: + TestControl1() = default; + + void DoWork(); + + void ExecuteThread(u32 id); + + std::unordered_map ids; + std::vector> thread_fibers; + std::vector> work_fibers; + std::vector items; + std::vector results; +}; + +static void WorkControl1(void* control) { + TestControl1* test_control = static_cast(control); + test_control->DoWork(); +} + +void TestControl1::DoWork() { + std::thread::id this_id = std::this_thread::get_id(); + u32 id = ids[this_id]; + u32 value = items[id]; + for (u32 i = 0; i < id; i++) { + value++; + } + results[id] = value; + Fiber::YieldTo(work_fibers[id], thread_fibers[id]); +} + +void TestControl1::ExecuteThread(u32 id) { + std::thread::id this_id = std::this_thread::get_id(); + ids[this_id] = id; + auto thread_fiber = Fiber::ThreadToFiber(); + thread_fibers[id] = thread_fiber; + work_fibers[id] = std::make_shared(std::function{WorkControl1}, this); + items[id] = rand() % 256; + Fiber::YieldTo(thread_fibers[id], work_fibers[id]); + thread_fibers[id]->Exit(); +} + +static void ThreadStart1(u32 id, TestControl1& test_control) { + test_control.ExecuteThread(id); +} + + +TEST_CASE("Fibers::Setup", "[common]") { + constexpr u32 num_threads = 7; + TestControl1 test_control{}; + test_control.thread_fibers.resize(num_threads, nullptr); + test_control.work_fibers.resize(num_threads, nullptr); + test_control.items.resize(num_threads, 0); + test_control.results.resize(num_threads, 0); + std::vector threads; + for (u32 i = 0; i < num_threads; i++) { + threads.emplace_back(ThreadStart1, i, std::ref(test_control)); + } + for (u32 i = 0; i < num_threads; i++) { + threads[i].join(); + } + for (u32 i = 0; i < num_threads; i++) { + REQUIRE(test_control.items[i] + i == test_control.results[i]); + } +} + +class TestControl2 { +public: + TestControl2() = default; + + void DoWork1() { + trap2 = false; + while (trap.load()); + for (u32 i = 0; i < 12000; i++) { + value1 += i; + } + Fiber::YieldTo(fiber1, fiber3); + std::thread::id this_id = std::this_thread::get_id(); + u32 id = ids[this_id]; + assert1 = id == 1; + value2 += 5000; + Fiber::YieldTo(fiber1, thread_fibers[id]); + } + + void DoWork2() { + while (trap2.load()); + value2 = 2000; + trap = false; + Fiber::YieldTo(fiber2, fiber1); + assert3 = false; + } + + void DoWork3() { + std::thread::id this_id = std::this_thread::get_id(); + u32 id = ids[this_id]; + assert2 = id == 0; + value1 += 1000; + Fiber::YieldTo(fiber3, thread_fibers[id]); + } + + void ExecuteThread(u32 id); + + void CallFiber1() { + std::thread::id this_id = std::this_thread::get_id(); + u32 id = ids[this_id]; + Fiber::YieldTo(thread_fibers[id], fiber1); + } + + void CallFiber2() { + std::thread::id this_id = std::this_thread::get_id(); + u32 id = ids[this_id]; + Fiber::YieldTo(thread_fibers[id], fiber2); + } + + void Exit(); + + bool assert1{}; + bool assert2{}; + bool assert3{true}; + u32 value1{}; + u32 value2{}; + std::atomic trap{true}; + std::atomic trap2{true}; + std::unordered_map ids; + std::vector> thread_fibers; + std::shared_ptr fiber1; + std::shared_ptr fiber2; + std::shared_ptr fiber3; +}; + +static void WorkControl2_1(void* control) { + TestControl2* test_control = static_cast(control); + test_control->DoWork1(); +} + +static void WorkControl2_2(void* control) { + TestControl2* test_control = static_cast(control); + test_control->DoWork2(); +} + +static void WorkControl2_3(void* control) { + TestControl2* test_control = static_cast(control); + test_control->DoWork3(); +} + +void TestControl2::ExecuteThread(u32 id) { + std::thread::id this_id = std::this_thread::get_id(); + ids[this_id] = id; + auto thread_fiber = Fiber::ThreadToFiber(); + thread_fibers[id] = thread_fiber; +} + +void TestControl2::Exit() { + std::thread::id this_id = std::this_thread::get_id(); + u32 id = ids[this_id]; + thread_fibers[id]->Exit(); +} + +static void ThreadStart2_1(u32 id, TestControl2& test_control) { + test_control.ExecuteThread(id); + test_control.CallFiber1(); + test_control.Exit(); +} + +static void ThreadStart2_2(u32 id, TestControl2& test_control) { + test_control.ExecuteThread(id); + test_control.CallFiber2(); + test_control.Exit(); +} + +TEST_CASE("Fibers::InterExchange", "[common]") { + TestControl2 test_control{}; + test_control.thread_fibers.resize(2, nullptr); + test_control.fiber1 = std::make_shared(std::function{WorkControl2_1}, &test_control); + test_control.fiber2 = std::make_shared(std::function{WorkControl2_2}, &test_control); + test_control.fiber3 = std::make_shared(std::function{WorkControl2_3}, &test_control); + std::thread thread1(ThreadStart2_1, 0, std::ref(test_control)); + std::thread thread2(ThreadStart2_2, 1, std::ref(test_control)); + thread1.join(); + thread2.join(); + REQUIRE(test_control.assert1); + REQUIRE(test_control.assert2); + REQUIRE(test_control.assert3); + REQUIRE(test_control.value2 == 7000); + u32 cal_value = 0; + for (u32 i = 0; i < 12000; i++) { + cal_value += i; + } + cal_value += 1000; + REQUIRE(test_control.value1 == cal_value); +} + + +} // namespace Common From be320a9e10fda32a984b12cdfe3aaf09cc67b39a Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Wed, 5 Feb 2020 15:48:20 -0400 Subject: [PATCH 019/252] Common: Polish Fiber class, add comments, asserts and more tests. --- src/common/fiber.cpp | 55 ++++++++++++--------- src/common/fiber.h | 14 +++++- src/common/spin_lock.cpp | 7 +++ src/common/spin_lock.h | 1 + src/tests/common/fibers.cpp | 95 ++++++++++++++++++++++++++++++++++++- 5 files changed, 147 insertions(+), 25 deletions(-) diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index a2c0401c4..a88a30ced 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/assert.h" #include "common/fiber.h" #ifdef _MSC_VER #include @@ -18,11 +19,11 @@ struct Fiber::FiberImpl { }; void Fiber::start() { - if (previous_fiber) { - previous_fiber->guard.unlock(); - previous_fiber = nullptr; - } + ASSERT(previous_fiber != nullptr); + previous_fiber->guard.unlock(); + previous_fiber.reset(); entry_point(start_parameter); + UNREACHABLE(); } void __stdcall Fiber::FiberStartFunc(void* fiber_parameter) @@ -43,12 +44,16 @@ Fiber::Fiber() : guard{}, entry_point{}, start_parameter{}, previous_fiber{} { Fiber::~Fiber() { // Make sure the Fiber is not being used - guard.lock(); - guard.unlock(); + bool locked = guard.try_lock(); + ASSERT_MSG(locked, "Destroying a fiber that's still running"); + if (locked) { + guard.unlock(); + } DeleteFiber(impl->handle); } void Fiber::Exit() { + ASSERT_MSG(is_thread_fiber, "Exitting non main thread fiber"); if (!is_thread_fiber) { return; } @@ -57,14 +62,15 @@ void Fiber::Exit() { } void Fiber::YieldTo(std::shared_ptr from, std::shared_ptr to) { + ASSERT_MSG(from != nullptr, "Yielding fiber is null!"); + ASSERT_MSG(to != nullptr, "Next fiber is null!"); to->guard.lock(); to->previous_fiber = from; SwitchToFiber(to->impl->handle); auto previous_fiber = from->previous_fiber; - if (previous_fiber) { - previous_fiber->guard.unlock(); - previous_fiber.reset(); - } + ASSERT(previous_fiber != nullptr); + previous_fiber->guard.unlock(); + previous_fiber.reset(); } std::shared_ptr Fiber::ThreadToFiber() { @@ -85,12 +91,12 @@ struct alignas(64) Fiber::FiberImpl { }; void Fiber::start(boost::context::detail::transfer_t& transfer) { - if (previous_fiber) { - previous_fiber->impl->context = transfer.fctx; - previous_fiber->guard.unlock(); - previous_fiber = nullptr; - } + ASSERT(previous_fiber != nullptr); + previous_fiber->impl->context = transfer.fctx; + previous_fiber->guard.unlock(); + previous_fiber.reset(); entry_point(start_parameter); + UNREACHABLE(); } void Fiber::FiberStartFunc(boost::context::detail::transfer_t transfer) @@ -113,11 +119,15 @@ Fiber::Fiber() : guard{}, entry_point{}, start_parameter{}, previous_fiber{} { Fiber::~Fiber() { // Make sure the Fiber is not being used - guard.lock(); - guard.unlock(); + bool locked = guard.try_lock(); + ASSERT_MSG(locked, "Destroying a fiber that's still running"); + if (locked) { + guard.unlock(); + } } void Fiber::Exit() { + ASSERT_MSG(is_thread_fiber, "Exitting non main thread fiber"); if (!is_thread_fiber) { return; } @@ -125,15 +135,16 @@ void Fiber::Exit() { } void Fiber::YieldTo(std::shared_ptr from, std::shared_ptr to) { + ASSERT_MSG(from != nullptr, "Yielding fiber is null!"); + ASSERT_MSG(to != nullptr, "Next fiber is null!"); to->guard.lock(); to->previous_fiber = from; auto transfer = boost::context::detail::jump_fcontext(to->impl.context, nullptr); auto previous_fiber = from->previous_fiber; - if (previous_fiber) { - previous_fiber->impl->context = transfer.fctx; - previous_fiber->guard.unlock(); - previous_fiber.reset(); - } + ASSERT(previous_fiber != nullptr); + previous_fiber->impl->context = transfer.fctx; + previous_fiber->guard.unlock(); + previous_fiber.reset(); } std::shared_ptr Fiber::ThreadToFiber() { diff --git a/src/common/fiber.h b/src/common/fiber.h index 812d6644a..89a01fdd8 100644 --- a/src/common/fiber.h +++ b/src/common/fiber.h @@ -18,6 +18,18 @@ namespace boost::context::detail { namespace Common { +/** + * Fiber class + * a fiber is a userspace thread with it's own context. They can be used to + * implement coroutines, emulated threading systems and certain asynchronous + * patterns. + * + * This class implements fibers at a low level, thus allowing greater freedom + * to implement such patterns. This fiber class is 'threadsafe' only one fiber + * can be running at a time and threads will be locked while trying to yield to + * a running fiber until it yields. WARNING exchanging two running fibers between + * threads will cause a deadlock. + */ class Fiber { public: Fiber(std::function&& entry_point_func, void* start_parameter); @@ -53,8 +65,6 @@ private: static void FiberStartFunc(boost::context::detail::transfer_t transfer); #endif - - struct FiberImpl; SpinLock guard; diff --git a/src/common/spin_lock.cpp b/src/common/spin_lock.cpp index 8077b78d2..82a1d39ff 100644 --- a/src/common/spin_lock.cpp +++ b/src/common/spin_lock.cpp @@ -43,4 +43,11 @@ void SpinLock::unlock() { lck.clear(std::memory_order_release); } +bool SpinLock::try_lock() { + if (lck.test_and_set(std::memory_order_acquire)) { + return false; + } + return true; +} + } // namespace Common diff --git a/src/common/spin_lock.h b/src/common/spin_lock.h index cbc67b6c8..70282a961 100644 --- a/src/common/spin_lock.h +++ b/src/common/spin_lock.h @@ -12,6 +12,7 @@ class SpinLock { public: void lock(); void unlock(); + bool try_lock(); private: std::atomic_flag lck = ATOMIC_FLAG_INIT; diff --git a/src/tests/common/fibers.cpp b/src/tests/common/fibers.cpp index ff840afa6..358393a19 100644 --- a/src/tests/common/fibers.cpp +++ b/src/tests/common/fibers.cpp @@ -64,7 +64,9 @@ static void ThreadStart1(u32 id, TestControl1& test_control) { test_control.ExecuteThread(id); } - +/** This test checks for fiber setup configuration and validates that fibers are + * doing all the work required. + */ TEST_CASE("Fibers::Setup", "[common]") { constexpr u32 num_threads = 7; TestControl1 test_control{}; @@ -188,6 +190,10 @@ static void ThreadStart2_2(u32 id, TestControl2& test_control) { test_control.Exit(); } +/** This test checks for fiber thread exchange configuration and validates that fibers are + * that a fiber has been succesfully transfered from one thread to another and that the TLS + * region of the thread is kept while changing fibers. + */ TEST_CASE("Fibers::InterExchange", "[common]") { TestControl2 test_control{}; test_control.thread_fibers.resize(2, nullptr); @@ -210,5 +216,92 @@ TEST_CASE("Fibers::InterExchange", "[common]") { REQUIRE(test_control.value1 == cal_value); } +class TestControl3 { +public: + TestControl3() = default; + + void DoWork1() { + value1 += 1; + Fiber::YieldTo(fiber1, fiber2); + std::thread::id this_id = std::this_thread::get_id(); + u32 id = ids[this_id]; + value3 += 1; + Fiber::YieldTo(fiber1, thread_fibers[id]); + } + + void DoWork2() { + value2 += 1; + std::thread::id this_id = std::this_thread::get_id(); + u32 id = ids[this_id]; + Fiber::YieldTo(fiber2, thread_fibers[id]); + } + + void ExecuteThread(u32 id); + + void CallFiber1() { + std::thread::id this_id = std::this_thread::get_id(); + u32 id = ids[this_id]; + Fiber::YieldTo(thread_fibers[id], fiber1); + } + + void Exit(); + + u32 value1{}; + u32 value2{}; + u32 value3{}; + std::unordered_map ids; + std::vector> thread_fibers; + std::shared_ptr fiber1; + std::shared_ptr fiber2; +}; + +static void WorkControl3_1(void* control) { + TestControl3* test_control = static_cast(control); + test_control->DoWork1(); +} + +static void WorkControl3_2(void* control) { + TestControl3* test_control = static_cast(control); + test_control->DoWork2(); +} + +void TestControl3::ExecuteThread(u32 id) { + std::thread::id this_id = std::this_thread::get_id(); + ids[this_id] = id; + auto thread_fiber = Fiber::ThreadToFiber(); + thread_fibers[id] = thread_fiber; +} + +void TestControl3::Exit() { + std::thread::id this_id = std::this_thread::get_id(); + u32 id = ids[this_id]; + thread_fibers[id]->Exit(); +} + +static void ThreadStart3(u32 id, TestControl3& test_control) { + test_control.ExecuteThread(id); + test_control.CallFiber1(); + test_control.Exit(); +} + +/** This test checks for one two threads racing for starting the same fiber. + * It checks execution occured in an ordered manner and by no time there were + * two contexts at the same time. + */ +TEST_CASE("Fibers::StartRace", "[common]") { + TestControl3 test_control{}; + test_control.thread_fibers.resize(2, nullptr); + test_control.fiber1 = std::make_shared(std::function{WorkControl3_1}, &test_control); + test_control.fiber2 = std::make_shared(std::function{WorkControl3_2}, &test_control); + std::thread thread1(ThreadStart3, 0, std::ref(test_control)); + std::thread thread2(ThreadStart3, 1, std::ref(test_control)); + thread1.join(); + thread2.join(); + REQUIRE(test_control.value1 == 1); + REQUIRE(test_control.value2 == 1); + REQUIRE(test_control.value3 == 1); +} + + } // namespace Common From 62e35ffc0effddfacb73ebc766735148436d7331 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Wed, 5 Feb 2020 19:12:27 -0400 Subject: [PATCH 020/252] Core: Implement a Host Timer. --- src/core/CMakeLists.txt | 2 + src/core/core_timing_util.cpp | 5 ++ src/core/core_timing_util.h | 1 + src/core/host_timing.cpp | 161 ++++++++++++++++++++++++++++++++++ src/core/host_timing.h | 126 ++++++++++++++++++++++++++ 5 files changed, 295 insertions(+) create mode 100644 src/core/host_timing.cpp create mode 100644 src/core/host_timing.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 47418006b..c0d068376 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -547,6 +547,8 @@ add_library(core STATIC hle/service/vi/vi_u.h hle/service/wlan/wlan.cpp hle/service/wlan/wlan.h + host_timing.cpp + host_timing.h loader/deconstructed_rom_directory.cpp loader/deconstructed_rom_directory.h loader/elf.cpp diff --git a/src/core/core_timing_util.cpp b/src/core/core_timing_util.cpp index de50d3b14..f42666b4d 100644 --- a/src/core/core_timing_util.cpp +++ b/src/core/core_timing_util.cpp @@ -49,6 +49,11 @@ s64 nsToCycles(std::chrono::nanoseconds ns) { return (Hardware::BASE_CLOCK_RATE * ns.count()) / 1000000000; } +u64 nsToClockCycles(std::chrono::nanoseconds ns) { + const u128 temporal = Common::Multiply64Into128(ns.count(), CNTFREQ); + return Common::Divide128On32(temporal, 1000000000).first; +} + u64 CpuCyclesToClockCycles(u64 ticks) { const u128 temporal = Common::Multiply64Into128(ticks, Hardware::CNTFREQ); return Common::Divide128On32(temporal, static_cast(Hardware::BASE_CLOCK_RATE)).first; diff --git a/src/core/core_timing_util.h b/src/core/core_timing_util.h index addc72b19..65fb7368b 100644 --- a/src/core/core_timing_util.h +++ b/src/core/core_timing_util.h @@ -13,6 +13,7 @@ namespace Core::Timing { s64 msToCycles(std::chrono::milliseconds ms); s64 usToCycles(std::chrono::microseconds us); s64 nsToCycles(std::chrono::nanoseconds ns); +u64 nsToClockCycles(std::chrono::nanoseconds ns); inline std::chrono::milliseconds CyclesToMs(s64 cycles) { return std::chrono::milliseconds(cycles * 1000 / Hardware::BASE_CLOCK_RATE); diff --git a/src/core/host_timing.cpp b/src/core/host_timing.cpp new file mode 100644 index 000000000..c02f571c6 --- /dev/null +++ b/src/core/host_timing.cpp @@ -0,0 +1,161 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/host_timing.h" + +#include +#include +#include +#include + +#include "common/assert.h" +#include "common/thread.h" +#include "core/core_timing_util.h" + +namespace Core::HostTiming { + +std::shared_ptr CreateEvent(std::string name, TimedCallback&& callback) { + return std::make_shared(std::move(callback), std::move(name)); +} + +struct CoreTiming::Event { + u64 time; + u64 fifo_order; + u64 userdata; + std::weak_ptr type; + + // Sort by time, unless the times are the same, in which case sort by + // the order added to the queue + friend bool operator>(const Event& left, const Event& right) { + return std::tie(left.time, left.fifo_order) > std::tie(right.time, right.fifo_order); + } + + friend bool operator<(const Event& left, const Event& right) { + return std::tie(left.time, left.fifo_order) < std::tie(right.time, right.fifo_order); + } +}; + +CoreTiming::CoreTiming() = default; +CoreTiming::~CoreTiming() = default; + +void CoreTiming::ThreadEntry(CoreTiming& instance) { + instance.Advance(); +} + +void CoreTiming::Initialize() { + event_fifo_id = 0; + const auto empty_timed_callback = [](u64, s64) {}; + ev_lost = CreateEvent("_lost_event", empty_timed_callback); + start_time = std::chrono::system_clock::now(); + timer_thread = std::make_unique(ThreadEntry, std::ref(*this)); +} + +void CoreTiming::Shutdown() { + std::unique_lock guard(inner_mutex); + shutting_down = true; + if (!is_set) { + is_set = true; + condvar.notify_one(); + } + inner_mutex.unlock(); + timer_thread->join(); + ClearPendingEvents(); +} + +void CoreTiming::ScheduleEvent(s64 ns_into_future, const std::shared_ptr& event_type, + u64 userdata) { + std::lock_guard guard{inner_mutex}; + const u64 timeout = static_cast(GetGlobalTimeNs().count() + ns_into_future); + + event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type}); + + std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); + if (!is_set) { + is_set = true; + condvar.notify_one(); + } +} + +void CoreTiming::UnscheduleEvent(const std::shared_ptr& event_type, u64 userdata) { + std::lock_guard guard{inner_mutex}; + + const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { + return e.type.lock().get() == event_type.get() && e.userdata == userdata; + }); + + // Removing random items breaks the invariant so we have to re-establish it. + if (itr != event_queue.end()) { + event_queue.erase(itr, event_queue.end()); + std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>()); + } +} + +u64 CoreTiming::GetCPUTicks() const { + std::chrono::nanoseconds time_now = GetGlobalTimeNs(); + return Core::Timing::nsToCycles(time_now); +} + +u64 CoreTiming::GetClockTicks() const { + std::chrono::nanoseconds time_now = GetGlobalTimeNs(); + return Core::Timing::nsToClockCycles(time_now); +} + +void CoreTiming::ClearPendingEvents() { + event_queue.clear(); +} + +void CoreTiming::RemoveEvent(const std::shared_ptr& event_type) { + std::lock_guard guard{inner_mutex}; + + const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { + return e.type.lock().get() == event_type.get(); + }); + + // Removing random items breaks the invariant so we have to re-establish it. + if (itr != event_queue.end()) { + event_queue.erase(itr, event_queue.end()); + std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>()); + } +} + +void CoreTiming::Advance() { + while (true) { + std::unique_lock guard(inner_mutex); + + global_timer = GetGlobalTimeNs().count(); + + while (!event_queue.empty() && event_queue.front().time <= global_timer) { + Event evt = std::move(event_queue.front()); + std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>()); + event_queue.pop_back(); + inner_mutex.unlock(); + + if (auto event_type{evt.type.lock()}) { + event_type->callback(evt.userdata, global_timer - evt.time); + } + + inner_mutex.lock(); + } + auto next_time = std::chrono::nanoseconds(event_queue.front().time - global_timer); + condvar.wait_for(guard, next_time, [this] { return is_set; }); + is_set = false; + if (shutting_down) { + break; + } + } +} + +std::chrono::nanoseconds CoreTiming::GetGlobalTimeNs() const { + sys_time_point current = std::chrono::system_clock::now(); + auto elapsed = current - start_time; + return std::chrono::duration_cast(elapsed); +} + +std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const { + sys_time_point current = std::chrono::system_clock::now(); + auto elapsed = current - start_time; + return std::chrono::duration_cast(elapsed); +} + +} // namespace Core::Timing diff --git a/src/core/host_timing.h b/src/core/host_timing.h new file mode 100644 index 000000000..a3a32e087 --- /dev/null +++ b/src/core/host_timing.h @@ -0,0 +1,126 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common/common_types.h" +#include "common/threadsafe_queue.h" + +namespace Core::HostTiming { + +/// A callback that may be scheduled for a particular core timing event. +using TimedCallback = std::function; +using sys_time_point = std::chrono::time_point; + +/// Contains the characteristics of a particular event. +struct EventType { + EventType(TimedCallback&& callback, std::string&& name) + : callback{std::move(callback)}, name{std::move(name)} {} + + /// The event's callback function. + TimedCallback callback; + /// A pointer to the name of the event. + const std::string name; +}; + +/** + * This is a system to schedule events into the emulated machine's future. Time is measured + * in main CPU clock cycles. + * + * To schedule an event, you first have to register its type. This is where you pass in the + * callback. You then schedule events using the type id you get back. + * + * The int cyclesLate that the callbacks get is how many cycles late it was. + * So to schedule a new event on a regular basis: + * inside callback: + * ScheduleEvent(periodInCycles - cyclesLate, callback, "whatever") + */ +class CoreTiming { +public: + CoreTiming(); + ~CoreTiming(); + + CoreTiming(const CoreTiming&) = delete; + CoreTiming(CoreTiming&&) = delete; + + CoreTiming& operator=(const CoreTiming&) = delete; + CoreTiming& operator=(CoreTiming&&) = delete; + + /// CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is + /// required to end slice - 1 and start slice 0 before the first cycle of code is executed. + void Initialize(); + + /// Tears down all timing related functionality. + void Shutdown(); + + /// Schedules an event in core timing + void ScheduleEvent(s64 ns_into_future, const std::shared_ptr& event_type, + u64 userdata = 0); + + void UnscheduleEvent(const std::shared_ptr& event_type, u64 userdata); + + /// We only permit one event of each type in the queue at a time. + void RemoveEvent(const std::shared_ptr& event_type); + + /// Returns current time in emulated CPU cycles + u64 GetCPUTicks() const; + + /// Returns current time in emulated in Clock cycles + u64 GetClockTicks() const; + + /// Returns current time in microseconds. + std::chrono::microseconds GetGlobalTimeUs() const; + + /// Returns current time in nanoseconds. + std::chrono::nanoseconds GetGlobalTimeNs() const; + +private: + struct Event; + + /// Clear all pending events. This should ONLY be done on exit. + void ClearPendingEvents(); + + static void ThreadEntry(CoreTiming& instance); + void Advance(); + + sys_time_point start_time; + + u64 global_timer = 0; + + std::chrono::nanoseconds start_point; + + // The queue is a min-heap using std::make_heap/push_heap/pop_heap. + // We don't use std::priority_queue because we need to be able to serialize, unserialize and + // erase arbitrary events (RemoveEvent()) regardless of the queue order. These aren't + // accomodated by the standard adaptor class. + std::vector event_queue; + u64 event_fifo_id = 0; + + std::shared_ptr ev_lost; + bool is_set = false; + std::condition_variable condvar; + std::mutex inner_mutex; + std::unique_ptr timer_thread; + std::atomic shutting_down{}; +}; + +/// Creates a core timing event with the given name and callback. +/// +/// @param name The name of the core timing event to create. +/// @param callback The callback to execute for the event. +/// +/// @returns An EventType instance representing the created event. +/// +std::shared_ptr CreateEvent(std::string name, TimedCallback&& callback); + +} // namespace Core::Timing From 0f8e5a146563d1f245f8f62cb931dc1e0b55de2f Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sat, 8 Feb 2020 12:48:57 -0400 Subject: [PATCH 021/252] Tests: Add base tests to host timing --- src/common/thread.h | 4 +- src/core/host_timing.cpp | 97 +++++++++++++-------- src/core/host_timing.h | 30 ++++++- src/tests/CMakeLists.txt | 1 + src/tests/core/host_timing.cpp | 150 +++++++++++++++++++++++++++++++++ 5 files changed, 241 insertions(+), 41 deletions(-) create mode 100644 src/tests/core/host_timing.cpp diff --git a/src/common/thread.h b/src/common/thread.h index 2fc071685..127cc7e23 100644 --- a/src/common/thread.h +++ b/src/common/thread.h @@ -9,6 +9,7 @@ #include #include #include +#include "common/common_types.h" namespace Common { @@ -28,8 +29,7 @@ public: is_set = false; } - template - bool WaitFor(const std::chrono::duration& time) { + bool WaitFor(const std::chrono::nanoseconds& time) { std::unique_lock lk{mutex}; if (!condvar.wait_for(lk, time, [this] { return is_set; })) return false; diff --git a/src/core/host_timing.cpp b/src/core/host_timing.cpp index c02f571c6..d9514b2c5 100644 --- a/src/core/host_timing.cpp +++ b/src/core/host_timing.cpp @@ -10,7 +10,6 @@ #include #include "common/assert.h" -#include "common/thread.h" #include "core/core_timing_util.h" namespace Core::HostTiming { @@ -47,39 +46,55 @@ void CoreTiming::Initialize() { event_fifo_id = 0; const auto empty_timed_callback = [](u64, s64) {}; ev_lost = CreateEvent("_lost_event", empty_timed_callback); - start_time = std::chrono::system_clock::now(); + start_time = std::chrono::steady_clock::now(); timer_thread = std::make_unique(ThreadEntry, std::ref(*this)); } void CoreTiming::Shutdown() { - std::unique_lock guard(inner_mutex); + paused = true; shutting_down = true; - if (!is_set) { - is_set = true; - condvar.notify_one(); - } - inner_mutex.unlock(); + event.Set(); timer_thread->join(); ClearPendingEvents(); + timer_thread.reset(); + has_started = false; +} + +void CoreTiming::Pause(bool is_paused) { + paused = is_paused; +} + +void CoreTiming::SyncPause(bool is_paused) { + if (is_paused == paused && paused_set == paused) { + return; + } + Pause(is_paused); + event.Set(); + while (paused_set != is_paused); +} + +bool CoreTiming::IsRunning() { + return !paused_set; +} + +bool CoreTiming::HasPendingEvents() { + return !(wait_set && event_queue.empty()); } void CoreTiming::ScheduleEvent(s64 ns_into_future, const std::shared_ptr& event_type, u64 userdata) { - std::lock_guard guard{inner_mutex}; + basic_lock.lock(); const u64 timeout = static_cast(GetGlobalTimeNs().count() + ns_into_future); event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type}); std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); - if (!is_set) { - is_set = true; - condvar.notify_one(); - } + basic_lock.unlock(); + event.Set(); } void CoreTiming::UnscheduleEvent(const std::shared_ptr& event_type, u64 userdata) { - std::lock_guard guard{inner_mutex}; - + basic_lock.lock(); const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { return e.type.lock().get() == event_type.get() && e.userdata == userdata; }); @@ -89,6 +104,7 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr& event_type, u event_queue.erase(itr, event_queue.end()); std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>()); } + basic_lock.unlock(); } u64 CoreTiming::GetCPUTicks() const { @@ -106,7 +122,7 @@ void CoreTiming::ClearPendingEvents() { } void CoreTiming::RemoveEvent(const std::shared_ptr& event_type) { - std::lock_guard guard{inner_mutex}; + basic_lock.lock(); const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { return e.type.lock().get() == event_type.get(); @@ -117,43 +133,54 @@ void CoreTiming::RemoveEvent(const std::shared_ptr& event_type) { event_queue.erase(itr, event_queue.end()); std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>()); } + basic_lock.unlock(); } void CoreTiming::Advance() { - while (true) { - std::unique_lock guard(inner_mutex); + has_started = true; + while (!shutting_down) { + while (!paused) { + paused_set = false; + basic_lock.lock(); + global_timer = GetGlobalTimeNs().count(); - global_timer = GetGlobalTimeNs().count(); + while (!event_queue.empty() && event_queue.front().time <= global_timer) { + Event evt = std::move(event_queue.front()); + std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>()); + event_queue.pop_back(); + basic_lock.unlock(); - while (!event_queue.empty() && event_queue.front().time <= global_timer) { - Event evt = std::move(event_queue.front()); - std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>()); - event_queue.pop_back(); - inner_mutex.unlock(); + if (auto event_type{evt.type.lock()}) { + event_type->callback(evt.userdata, global_timer - evt.time); + } - if (auto event_type{evt.type.lock()}) { - event_type->callback(evt.userdata, global_timer - evt.time); + basic_lock.lock(); } - inner_mutex.lock(); - } - auto next_time = std::chrono::nanoseconds(event_queue.front().time - global_timer); - condvar.wait_for(guard, next_time, [this] { return is_set; }); - is_set = false; - if (shutting_down) { - break; + if (!event_queue.empty()) { + std::chrono::nanoseconds next_time = std::chrono::nanoseconds(event_queue.front().time - global_timer); + basic_lock.unlock(); + event.WaitFor(next_time); + } else { + basic_lock.unlock(); + wait_set = true; + event.Wait(); + } + + wait_set = false; } + paused_set = true; } } std::chrono::nanoseconds CoreTiming::GetGlobalTimeNs() const { - sys_time_point current = std::chrono::system_clock::now(); + sys_time_point current = std::chrono::steady_clock::now(); auto elapsed = current - start_time; return std::chrono::duration_cast(elapsed); } std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const { - sys_time_point current = std::chrono::system_clock::now(); + sys_time_point current = std::chrono::steady_clock::now(); auto elapsed = current - start_time; return std::chrono::duration_cast(elapsed); } diff --git a/src/core/host_timing.h b/src/core/host_timing.h index a3a32e087..1d053a7fa 100644 --- a/src/core/host_timing.h +++ b/src/core/host_timing.h @@ -14,13 +14,15 @@ #include #include "common/common_types.h" +#include "common/spin_lock.h" +#include "common/thread.h" #include "common/threadsafe_queue.h" namespace Core::HostTiming { /// A callback that may be scheduled for a particular core timing event. using TimedCallback = std::function; -using sys_time_point = std::chrono::time_point; +using sys_time_point = std::chrono::time_point; /// Contains the characteristics of a particular event. struct EventType { @@ -63,6 +65,23 @@ public: /// Tears down all timing related functionality. void Shutdown(); + /// Pauses/Unpauses the execution of the timer thread. + void Pause(bool is_paused); + + /// Pauses/Unpauses the execution of the timer thread and waits until paused. + void SyncPause(bool is_paused); + + /// Checks if core timing is running. + bool IsRunning(); + + /// Checks if the timer thread has started. + bool HasStarted() { + return has_started; + } + + /// Checks if there are any pending time events. + bool HasPendingEvents(); + /// Schedules an event in core timing void ScheduleEvent(s64 ns_into_future, const std::shared_ptr& event_type, u64 userdata = 0); @@ -107,11 +126,14 @@ private: u64 event_fifo_id = 0; std::shared_ptr ev_lost; - bool is_set = false; - std::condition_variable condvar; - std::mutex inner_mutex; + Common::Event event{}; + Common::SpinLock basic_lock{}; std::unique_ptr timer_thread; + std::atomic paused{}; + std::atomic paused_set{}; + std::atomic wait_set{}; std::atomic shutting_down{}; + std::atomic has_started{}; }; /// Creates a core timing event with the given name and callback. diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 47ef30aa9..3f750b51c 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -8,6 +8,7 @@ add_executable(tests core/arm/arm_test_common.cpp core/arm/arm_test_common.h core/core_timing.cpp + core/host_timing.cpp tests.cpp ) diff --git a/src/tests/core/host_timing.cpp b/src/tests/core/host_timing.cpp new file mode 100644 index 000000000..ca9c8e50a --- /dev/null +++ b/src/tests/core/host_timing.cpp @@ -0,0 +1,150 @@ +// Copyright 2016 Dolphin Emulator Project / 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include + +#include +#include +#include +#include +#include + +#include "common/file_util.h" +#include "core/core.h" +#include "core/host_timing.h" + +// Numbers are chosen randomly to make sure the correct one is given. +static constexpr std::array CB_IDS{{42, 144, 93, 1026, UINT64_C(0xFFFF7FFFF7FFFF)}}; +static constexpr int MAX_SLICE_LENGTH = 10000; // Copied from CoreTiming internals +static constexpr std::array calls_order{{2,0,1,4,3}}; +static std::array delays{}; + +static std::bitset callbacks_ran_flags; +static u64 expected_callback = 0; +static s64 lateness = 0; + +template +void HostCallbackTemplate(u64 userdata, s64 nanoseconds_late) { + static_assert(IDX < CB_IDS.size(), "IDX out of range"); + callbacks_ran_flags.set(IDX); + REQUIRE(CB_IDS[IDX] == userdata); + REQUIRE(CB_IDS[IDX] == CB_IDS[calls_order[expected_callback]]); + delays[IDX] = nanoseconds_late; + ++expected_callback; +} + +static u64 callbacks_done = 0; + +struct ScopeInit final { + ScopeInit() { + core_timing.Initialize(); + } + ~ScopeInit() { + core_timing.Shutdown(); + } + + Core::HostTiming::CoreTiming core_timing; +}; + +TEST_CASE("HostTiming[BasicOrder]", "[core]") { + ScopeInit guard; + auto& core_timing = guard.core_timing; + std::vector> events; + events.resize(5); + events[0] = + Core::HostTiming::CreateEvent("callbackA", HostCallbackTemplate<0>); + events[1] = + Core::HostTiming::CreateEvent("callbackB", HostCallbackTemplate<1>); + events[2] = + Core::HostTiming::CreateEvent("callbackC", HostCallbackTemplate<2>); + events[3] = + Core::HostTiming::CreateEvent("callbackD", HostCallbackTemplate<3>); + events[4] = + Core::HostTiming::CreateEvent("callbackE", HostCallbackTemplate<4>); + + expected_callback = 0; + + core_timing.SyncPause(true); + + u64 one_micro = 1000U; + for (std::size_t i = 0; i < events.size(); i++) { + u64 order = calls_order[i]; + core_timing.ScheduleEvent(i*one_micro + 100U, events[order], CB_IDS[order]); + } + /// test pause + REQUIRE(callbacks_ran_flags.none()); + + core_timing.Pause(false); // No need to sync + + while (core_timing.HasPendingEvents()); + + REQUIRE(callbacks_ran_flags.all()); + + for (std::size_t i = 0; i < delays.size(); i++) { + const double delay = static_cast(delays[i]); + const double micro = delay / 1000.0f; + const double mili = micro / 1000.0f; + printf("HostTimer Pausing Delay[%zu]: %.3f %.6f\n", i, micro, mili); + } +} + +#pragma optimize("", off) +u64 TestTimerSpeed(Core::HostTiming::CoreTiming& core_timing) { + u64 start = core_timing.GetGlobalTimeNs().count(); + u64 placebo = 0; + for (std::size_t i = 0; i < 1000; i++) { + placebo += core_timing.GetGlobalTimeNs().count(); + } + u64 end = core_timing.GetGlobalTimeNs().count(); + return (end - start); +} +#pragma optimize("", on) + +TEST_CASE("HostTiming[BasicOrderNoPausing]", "[core]") { + ScopeInit guard; + auto& core_timing = guard.core_timing; + std::vector> events; + events.resize(5); + events[0] = + Core::HostTiming::CreateEvent("callbackA", HostCallbackTemplate<0>); + events[1] = + Core::HostTiming::CreateEvent("callbackB", HostCallbackTemplate<1>); + events[2] = + Core::HostTiming::CreateEvent("callbackC", HostCallbackTemplate<2>); + events[3] = + Core::HostTiming::CreateEvent("callbackD", HostCallbackTemplate<3>); + events[4] = + Core::HostTiming::CreateEvent("callbackE", HostCallbackTemplate<4>); + + core_timing.SyncPause(true); + core_timing.SyncPause(false); + + expected_callback = 0; + + u64 start = core_timing.GetGlobalTimeNs().count(); + u64 one_micro = 1000U; + for (std::size_t i = 0; i < events.size(); i++) { + u64 order = calls_order[i]; + core_timing.ScheduleEvent(i*one_micro + 100U, events[order], CB_IDS[order]); + } + u64 end = core_timing.GetGlobalTimeNs().count(); + const double scheduling_time = static_cast(end - start); + const double timer_time = static_cast(TestTimerSpeed(core_timing)); + + while (core_timing.HasPendingEvents()); + + REQUIRE(callbacks_ran_flags.all()); + + for (std::size_t i = 0; i < delays.size(); i++) { + const double delay = static_cast(delays[i]); + const double micro = delay / 1000.0f; + const double mili = micro / 1000.0f; + printf("HostTimer No Pausing Delay[%zu]: %.3f %.6f\n", i, micro, mili); + } + + const double micro = scheduling_time / 1000.0f; + const double mili = micro / 1000.0f; + printf("HostTimer No Pausing Scheduling Time: %.3f %.6f\n", micro, mili); + printf("HostTimer No Pausing Timer Time: %.3f %.6f\n", timer_time / 1000.f, timer_time / 1000000.f); +} From 234b5ff6a999d7d69cdcdf214e0c3984cdab11cf Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sun, 9 Feb 2020 16:53:22 -0400 Subject: [PATCH 022/252] Common: Implement WallClock Interface and implement a native clock for x64 --- src/common/CMakeLists.txt | 4 + src/common/wall_clock.cpp | 90 ++++++++++++++++++++++ src/common/wall_clock.h | 40 ++++++++++ src/common/x64/cpu_detect.cpp | 33 ++++++++ src/common/x64/cpu_detect.h | 12 +++ src/common/x64/native_clock.cpp | 128 ++++++++++++++++++++++++++++++++ src/common/x64/native_clock.h | 41 ++++++++++ src/core/host_timing.cpp | 21 +++--- src/core/host_timing.h | 4 +- src/tests/core/host_timing.cpp | 45 +++++------ 10 files changed, 378 insertions(+), 40 deletions(-) create mode 100644 src/common/wall_clock.cpp create mode 100644 src/common/wall_clock.h create mode 100644 src/common/x64/native_clock.cpp create mode 100644 src/common/x64/native_clock.h diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 554d6e253..aacea0ab7 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -167,6 +167,8 @@ add_library(common STATIC vector_math.h virtual_buffer.cpp virtual_buffer.h + wall_clock.cpp + wall_clock.h web_result.h zstd_compression.cpp zstd_compression.h @@ -177,6 +179,8 @@ if(ARCHITECTURE_x86_64) PRIVATE x64/cpu_detect.cpp x64/cpu_detect.h + x64/native_clock.cpp + x64/native_clock.h x64/xbyak_abi.h x64/xbyak_util.h ) diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp new file mode 100644 index 000000000..eabbba9da --- /dev/null +++ b/src/common/wall_clock.cpp @@ -0,0 +1,90 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/uint128.h" +#include "common/wall_clock.h" + +#ifdef ARCHITECTURE_x86_64 +#include "common/x64/cpu_detect.h" +#include "common/x64/native_clock.h" +#endif + +namespace Common { + +using base_timer = std::chrono::steady_clock; +using base_time_point = std::chrono::time_point; + +class StandardWallClock : public WallClock { +public: + StandardWallClock(u64 emulated_cpu_frequency, u64 emulated_clock_frequency) + : WallClock(emulated_cpu_frequency, emulated_clock_frequency, false) { + start_time = base_timer::now(); + } + + std::chrono::nanoseconds GetTimeNS() override { + base_time_point current = base_timer::now(); + auto elapsed = current - start_time; + return std::chrono::duration_cast(elapsed); + } + + std::chrono::microseconds GetTimeUS() override { + base_time_point current = base_timer::now(); + auto elapsed = current - start_time; + return std::chrono::duration_cast(elapsed); + } + + std::chrono::milliseconds GetTimeMS() override { + base_time_point current = base_timer::now(); + auto elapsed = current - start_time; + return std::chrono::duration_cast(elapsed); + } + + u64 GetClockCycles() override { + std::chrono::nanoseconds time_now = GetTimeNS(); + const u128 temporal = Common::Multiply64Into128(time_now.count(), emulated_clock_frequency); + return Common::Divide128On32(temporal, 1000000000).first; + } + + u64 GetCPUCycles() override { + std::chrono::nanoseconds time_now = GetTimeNS(); + const u128 temporal = Common::Multiply64Into128(time_now.count(), emulated_cpu_frequency); + return Common::Divide128On32(temporal, 1000000000).first; + } + +private: + base_time_point start_time; +}; + +#ifdef ARCHITECTURE_x86_64 + +WallClock* CreateBestMatchingClock(u32 emulated_cpu_frequency, u32 emulated_clock_frequency) { + const auto& caps = GetCPUCaps(); + u64 rtsc_frequency = 0; + if (caps.invariant_tsc) { + if (caps.base_frequency != 0) { + rtsc_frequency = static_cast(caps.base_frequency) * 1000000U; + } + if (rtsc_frequency == 0) { + rtsc_frequency = EstimateRDTSCFrequency(); + } + } + if (rtsc_frequency == 0) { + return static_cast( + new StandardWallClock(emulated_cpu_frequency, emulated_clock_frequency)); + } else { + return static_cast( + new X64::NativeClock(emulated_cpu_frequency, emulated_clock_frequency, rtsc_frequency)); + } +} + +#else + +WallClock* CreateBestMatchingClock(u32 emulated_cpu_frequency, u32 emulated_clock_frequency) { + return static_cast( + new StandardWallClock(emulated_cpu_frequency, emulated_clock_frequency)); +} + +#endif + +} // namespace Common diff --git a/src/common/wall_clock.h b/src/common/wall_clock.h new file mode 100644 index 000000000..6f763d74b --- /dev/null +++ b/src/common/wall_clock.h @@ -0,0 +1,40 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include + +#include "common/common_types.h" + +namespace Common { + +class WallClock { +public: + virtual std::chrono::nanoseconds GetTimeNS() = 0; + virtual std::chrono::microseconds GetTimeUS() = 0; + virtual std::chrono::milliseconds GetTimeMS() = 0; + virtual u64 GetClockCycles() = 0; + virtual u64 GetCPUCycles() = 0; + + /// Tells if the wall clock, uses the host CPU's hardware clock + bool IsNative() const { + return is_native; + } + +protected: + WallClock(u64 emulated_cpu_frequency, u64 emulated_clock_frequency, bool is_native) + : emulated_cpu_frequency{emulated_cpu_frequency}, + emulated_clock_frequency{emulated_clock_frequency}, is_native{is_native} {} + + u64 emulated_cpu_frequency; + u64 emulated_clock_frequency; + +private: + bool is_native; +}; + +WallClock* CreateBestMatchingClock(u32 emulated_cpu_frequency, u32 emulated_clock_frequency); + +} // namespace Common diff --git a/src/common/x64/cpu_detect.cpp b/src/common/x64/cpu_detect.cpp index c9349a6b4..d767c544c 100644 --- a/src/common/x64/cpu_detect.cpp +++ b/src/common/x64/cpu_detect.cpp @@ -62,6 +62,17 @@ static CPUCaps Detect() { std::memcpy(&caps.brand_string[0], &cpu_id[1], sizeof(int)); std::memcpy(&caps.brand_string[4], &cpu_id[3], sizeof(int)); std::memcpy(&caps.brand_string[8], &cpu_id[2], sizeof(int)); + if (cpu_id[1] == 0x756e6547 && cpu_id[2] == 0x6c65746e && cpu_id[3] == 0x49656e69) + caps.manufacturer = Manufacturer::Intel; + else if (cpu_id[1] == 0x68747541 && cpu_id[2] == 0x444d4163 && cpu_id[3] == 0x69746e65) + caps.manufacturer = Manufacturer::AMD; + else if (cpu_id[1] == 0x6f677948 && cpu_id[2] == 0x656e6975 && cpu_id[3] == 0x6e65476e) + caps.manufacturer = Manufacturer::Hygon; + else + caps.manufacturer = Manufacturer::Unknown; + + u32 family = {}; + u32 model = {}; __cpuid(cpu_id, 0x80000000); @@ -73,6 +84,14 @@ static CPUCaps Detect() { // Detect family and other miscellaneous features if (max_std_fn >= 1) { __cpuid(cpu_id, 0x00000001); + family = (cpu_id[0] >> 8) & 0xf; + model = (cpu_id[0] >> 4) & 0xf; + if (family == 0xf) { + family += (cpu_id[0] >> 20) & 0xff; + } + if (family >= 6) { + model += ((cpu_id[0] >> 16) & 0xf) << 4; + } if ((cpu_id[3] >> 25) & 1) caps.sse = true; @@ -130,6 +149,20 @@ static CPUCaps Detect() { caps.fma4 = true; } + if (max_ex_fn >= 0x80000007) { + __cpuid(cpu_id, 0x80000007); + if (cpu_id[3] & (1 << 8)) { + caps.invariant_tsc = true; + } + } + + if (max_std_fn >= 0x16) { + __cpuid(cpu_id, 0x16); + caps.base_frequency = cpu_id[0]; + caps.max_frequency = cpu_id[1]; + caps.bus_frequency = cpu_id[2]; + } + return caps; } diff --git a/src/common/x64/cpu_detect.h b/src/common/x64/cpu_detect.h index 20f2ba234..f0676fa5e 100644 --- a/src/common/x64/cpu_detect.h +++ b/src/common/x64/cpu_detect.h @@ -6,8 +6,16 @@ namespace Common { +enum class Manufacturer : u32 { + Intel = 0, + AMD = 1, + Hygon = 2, + Unknown = 3, +}; + /// x86/x64 CPU capabilities that may be detected by this module struct CPUCaps { + Manufacturer manufacturer; char cpu_string[0x21]; char brand_string[0x41]; bool sse; @@ -24,6 +32,10 @@ struct CPUCaps { bool fma; bool fma4; bool aes; + bool invariant_tsc; + u32 base_frequency; + u32 max_frequency; + u32 bus_frequency; }; /** diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp new file mode 100644 index 000000000..c799111fd --- /dev/null +++ b/src/common/x64/native_clock.cpp @@ -0,0 +1,128 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include + +#ifdef _MSC_VER +#include +#else +#include +#endif + +#include "common/x64/native_clock.h" + +namespace Common { + +#ifdef _MSC_VER + +namespace { + +struct uint128 { + u64 low; + u64 high; +}; + +u64 umuldiv64(u64 a, u64 b, u64 d) { + uint128 r{}; + r.low = _umul128(a, b, &r.high); + u64 remainder; + return _udiv128(r.high, r.low, d, &remainder); +} + +} // namespace + +#else + +namespace { + +u64 umuldiv64(u64 a, u64 b, u64 d) { + const u64 diva = a / d; + const u64 moda = a % d; + const u64 divb = b / d; + const u64 modb = b % d; + return diva * b + moda * divb + moda * modb / d; +} + +} // namespace + +#endif + +u64 EstimateRDTSCFrequency() { + const auto milli_10 = std::chrono::milliseconds{10}; + // get current time + _mm_mfence(); + const u64 tscStart = __rdtsc(); + const auto startTime = std::chrono::high_resolution_clock::now(); + // wait roughly 3 seconds + while (true) { + auto milli = std::chrono::duration_cast( + std::chrono::high_resolution_clock::now() - startTime); + if (milli.count() >= 3000) + break; + std::this_thread::sleep_for(milli_10); + } + const auto endTime = std::chrono::high_resolution_clock::now(); + _mm_mfence(); + const u64 tscEnd = __rdtsc(); + // calculate difference + const u64 timer_diff = + std::chrono::duration_cast(endTime - startTime).count(); + const u64 tsc_diff = tscEnd - tscStart; + const u64 tsc_freq = umuldiv64(tsc_diff, 1000000000ULL, timer_diff); + return tsc_freq; +} + +namespace X64 { +NativeClock::NativeClock(u64 emulated_cpu_frequency, u64 emulated_clock_frequency, + u64 rtsc_frequency) + : WallClock(emulated_cpu_frequency, emulated_clock_frequency, true), rtsc_frequency{ + rtsc_frequency} { + _mm_mfence(); + last_measure = __rdtsc(); + accumulated_ticks = 0U; +} + +u64 NativeClock::GetRTSC() { + rtsc_serialize.lock(); + _mm_mfence(); + const u64 current_measure = __rdtsc(); + u64 diff = current_measure - last_measure; + diff = diff & ~static_cast(static_cast(diff) >> 63); // max(diff, 0) + if (current_measure > last_measure) { + last_measure = current_measure; + } + accumulated_ticks += diff; + rtsc_serialize.unlock(); + return accumulated_ticks; +} + +std::chrono::nanoseconds NativeClock::GetTimeNS() { + const u64 rtsc_value = GetRTSC(); + return std::chrono::nanoseconds{umuldiv64(rtsc_value, 1000000000, rtsc_frequency)}; +} + +std::chrono::microseconds NativeClock::GetTimeUS() { + const u64 rtsc_value = GetRTSC(); + return std::chrono::microseconds{umuldiv64(rtsc_value, 1000000, rtsc_frequency)}; +} + +std::chrono::milliseconds NativeClock::GetTimeMS() { + const u64 rtsc_value = GetRTSC(); + return std::chrono::milliseconds{umuldiv64(rtsc_value, 1000, rtsc_frequency)}; +} + +u64 NativeClock::GetClockCycles() { + const u64 rtsc_value = GetRTSC(); + return umuldiv64(rtsc_value, emulated_clock_frequency, rtsc_frequency); +} + +u64 NativeClock::GetCPUCycles() { + const u64 rtsc_value = GetRTSC(); + return umuldiv64(rtsc_value, emulated_cpu_frequency, rtsc_frequency); +} + +} // namespace X64 + +} // namespace Common diff --git a/src/common/x64/native_clock.h b/src/common/x64/native_clock.h new file mode 100644 index 000000000..b58cf9f5a --- /dev/null +++ b/src/common/x64/native_clock.h @@ -0,0 +1,41 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include + +#include "common/spin_lock.h" +#include "common/wall_clock.h" + +namespace Common { + +namespace X64 { +class NativeClock : public WallClock { +public: + NativeClock(u64 emulated_cpu_frequency, u64 emulated_clock_frequency, u64 rtsc_frequency); + + std::chrono::nanoseconds GetTimeNS() override; + + std::chrono::microseconds GetTimeUS() override; + + std::chrono::milliseconds GetTimeMS() override; + + u64 GetClockCycles() override; + + u64 GetCPUCycles() override; + +private: + u64 GetRTSC(); + + SpinLock rtsc_serialize{}; + u64 last_measure{}; + u64 accumulated_ticks{}; + u64 rtsc_frequency; +}; +} // namespace X64 + +u64 EstimateRDTSCFrequency(); + +} // namespace Common diff --git a/src/core/host_timing.cpp b/src/core/host_timing.cpp index d9514b2c5..ef9977b76 100644 --- a/src/core/host_timing.cpp +++ b/src/core/host_timing.cpp @@ -35,7 +35,11 @@ struct CoreTiming::Event { } }; -CoreTiming::CoreTiming() = default; +CoreTiming::CoreTiming() { + Common::WallClock* wall = Common::CreateBestMatchingClock(Core::Timing::BASE_CLOCK_RATE, Core::Timing::CNTFREQ); + clock = std::unique_ptr(wall); +} + CoreTiming::~CoreTiming() = default; void CoreTiming::ThreadEntry(CoreTiming& instance) { @@ -46,7 +50,6 @@ void CoreTiming::Initialize() { event_fifo_id = 0; const auto empty_timed_callback = [](u64, s64) {}; ev_lost = CreateEvent("_lost_event", empty_timed_callback); - start_time = std::chrono::steady_clock::now(); timer_thread = std::make_unique(ThreadEntry, std::ref(*this)); } @@ -108,13 +111,11 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr& event_type, u } u64 CoreTiming::GetCPUTicks() const { - std::chrono::nanoseconds time_now = GetGlobalTimeNs(); - return Core::Timing::nsToCycles(time_now); + return clock->GetCPUCycles(); } u64 CoreTiming::GetClockTicks() const { - std::chrono::nanoseconds time_now = GetGlobalTimeNs(); - return Core::Timing::nsToClockCycles(time_now); + return clock->GetClockCycles(); } void CoreTiming::ClearPendingEvents() { @@ -174,15 +175,11 @@ void CoreTiming::Advance() { } std::chrono::nanoseconds CoreTiming::GetGlobalTimeNs() const { - sys_time_point current = std::chrono::steady_clock::now(); - auto elapsed = current - start_time; - return std::chrono::duration_cast(elapsed); + return clock->GetTimeNS(); } std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const { - sys_time_point current = std::chrono::steady_clock::now(); - auto elapsed = current - start_time; - return std::chrono::duration_cast(elapsed); + return clock->GetTimeUS(); } } // namespace Core::Timing diff --git a/src/core/host_timing.h b/src/core/host_timing.h index 1d053a7fa..f04a150ee 100644 --- a/src/core/host_timing.h +++ b/src/core/host_timing.h @@ -17,12 +17,12 @@ #include "common/spin_lock.h" #include "common/thread.h" #include "common/threadsafe_queue.h" +#include "common/wall_clock.h" namespace Core::HostTiming { /// A callback that may be scheduled for a particular core timing event. using TimedCallback = std::function; -using sys_time_point = std::chrono::time_point; /// Contains the characteristics of a particular event. struct EventType { @@ -112,7 +112,7 @@ private: static void ThreadEntry(CoreTiming& instance); void Advance(); - sys_time_point start_time; + std::unique_ptr clock; u64 global_timer = 0; diff --git a/src/tests/core/host_timing.cpp b/src/tests/core/host_timing.cpp index ca9c8e50a..3d0532d02 100644 --- a/src/tests/core/host_timing.cpp +++ b/src/tests/core/host_timing.cpp @@ -17,7 +17,7 @@ // Numbers are chosen randomly to make sure the correct one is given. static constexpr std::array CB_IDS{{42, 144, 93, 1026, UINT64_C(0xFFFF7FFFF7FFFF)}}; static constexpr int MAX_SLICE_LENGTH = 10000; // Copied from CoreTiming internals -static constexpr std::array calls_order{{2,0,1,4,3}}; +static constexpr std::array calls_order{{2, 0, 1, 4, 3}}; static std::array delays{}; static std::bitset callbacks_ran_flags; @@ -52,16 +52,11 @@ TEST_CASE("HostTiming[BasicOrder]", "[core]") { auto& core_timing = guard.core_timing; std::vector> events; events.resize(5); - events[0] = - Core::HostTiming::CreateEvent("callbackA", HostCallbackTemplate<0>); - events[1] = - Core::HostTiming::CreateEvent("callbackB", HostCallbackTemplate<1>); - events[2] = - Core::HostTiming::CreateEvent("callbackC", HostCallbackTemplate<2>); - events[3] = - Core::HostTiming::CreateEvent("callbackD", HostCallbackTemplate<3>); - events[4] = - Core::HostTiming::CreateEvent("callbackE", HostCallbackTemplate<4>); + events[0] = Core::HostTiming::CreateEvent("callbackA", HostCallbackTemplate<0>); + events[1] = Core::HostTiming::CreateEvent("callbackB", HostCallbackTemplate<1>); + events[2] = Core::HostTiming::CreateEvent("callbackC", HostCallbackTemplate<2>); + events[3] = Core::HostTiming::CreateEvent("callbackD", HostCallbackTemplate<3>); + events[4] = Core::HostTiming::CreateEvent("callbackE", HostCallbackTemplate<4>); expected_callback = 0; @@ -70,14 +65,15 @@ TEST_CASE("HostTiming[BasicOrder]", "[core]") { u64 one_micro = 1000U; for (std::size_t i = 0; i < events.size(); i++) { u64 order = calls_order[i]; - core_timing.ScheduleEvent(i*one_micro + 100U, events[order], CB_IDS[order]); + core_timing.ScheduleEvent(i * one_micro + 100U, events[order], CB_IDS[order]); } /// test pause REQUIRE(callbacks_ran_flags.none()); core_timing.Pause(false); // No need to sync - while (core_timing.HasPendingEvents()); + while (core_timing.HasPendingEvents()) + ; REQUIRE(callbacks_ran_flags.all()); @@ -106,16 +102,11 @@ TEST_CASE("HostTiming[BasicOrderNoPausing]", "[core]") { auto& core_timing = guard.core_timing; std::vector> events; events.resize(5); - events[0] = - Core::HostTiming::CreateEvent("callbackA", HostCallbackTemplate<0>); - events[1] = - Core::HostTiming::CreateEvent("callbackB", HostCallbackTemplate<1>); - events[2] = - Core::HostTiming::CreateEvent("callbackC", HostCallbackTemplate<2>); - events[3] = - Core::HostTiming::CreateEvent("callbackD", HostCallbackTemplate<3>); - events[4] = - Core::HostTiming::CreateEvent("callbackE", HostCallbackTemplate<4>); + events[0] = Core::HostTiming::CreateEvent("callbackA", HostCallbackTemplate<0>); + events[1] = Core::HostTiming::CreateEvent("callbackB", HostCallbackTemplate<1>); + events[2] = Core::HostTiming::CreateEvent("callbackC", HostCallbackTemplate<2>); + events[3] = Core::HostTiming::CreateEvent("callbackD", HostCallbackTemplate<3>); + events[4] = Core::HostTiming::CreateEvent("callbackE", HostCallbackTemplate<4>); core_timing.SyncPause(true); core_timing.SyncPause(false); @@ -126,13 +117,14 @@ TEST_CASE("HostTiming[BasicOrderNoPausing]", "[core]") { u64 one_micro = 1000U; for (std::size_t i = 0; i < events.size(); i++) { u64 order = calls_order[i]; - core_timing.ScheduleEvent(i*one_micro + 100U, events[order], CB_IDS[order]); + core_timing.ScheduleEvent(i * one_micro + 100U, events[order], CB_IDS[order]); } u64 end = core_timing.GetGlobalTimeNs().count(); const double scheduling_time = static_cast(end - start); const double timer_time = static_cast(TestTimerSpeed(core_timing)); - while (core_timing.HasPendingEvents()); + while (core_timing.HasPendingEvents()) + ; REQUIRE(callbacks_ran_flags.all()); @@ -146,5 +138,6 @@ TEST_CASE("HostTiming[BasicOrderNoPausing]", "[core]") { const double micro = scheduling_time / 1000.0f; const double mili = micro / 1000.0f; printf("HostTimer No Pausing Scheduling Time: %.3f %.6f\n", micro, mili); - printf("HostTimer No Pausing Timer Time: %.3f %.6f\n", timer_time / 1000.f, timer_time / 1000000.f); + printf("HostTimer No Pausing Timer Time: %.3f %.6f\n", timer_time / 1000.f, + timer_time / 1000000.f); } From e3524d114246a9221c766bdf1992777b208cbd67 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Mon, 10 Feb 2020 11:20:40 -0400 Subject: [PATCH 023/252] Common: Refactor & Document Wall clock. --- src/common/uint128.cpp | 22 +++++++++++++++ src/common/uint128.h | 3 +++ src/common/wall_clock.cpp | 13 ++++----- src/common/wall_clock.h | 13 ++++++++- src/common/x64/native_clock.cpp | 47 +++++---------------------------- src/core/host_timing.cpp | 3 +-- 6 files changed, 50 insertions(+), 51 deletions(-) diff --git a/src/common/uint128.cpp b/src/common/uint128.cpp index 32bf56730..7e77588db 100644 --- a/src/common/uint128.cpp +++ b/src/common/uint128.cpp @@ -6,12 +6,34 @@ #include #pragma intrinsic(_umul128) +#pragma intrinsic(_udiv128) #endif #include #include "common/uint128.h" namespace Common { +#ifdef _MSC_VER + +u64 MultiplyAndDivide64(u64 a, u64 b, u64 d) { + u128 r{}; + r[0] = _umul128(a, b, &r[1]); + u64 remainder; + return _udiv128(r[1], r[0], d, &remainder); +} + +#else + +u64 MultiplyAndDivide64(u64 a, u64 b, u64 d) { + const u64 diva = a / d; + const u64 moda = a % d; + const u64 divb = b / d; + const u64 modb = b % d; + return diva * b + moda * divb + moda * modb / d; +} + +#endif + u128 Multiply64Into128(u64 a, u64 b) { u128 result; #ifdef _MSC_VER diff --git a/src/common/uint128.h b/src/common/uint128.h index a3be2a2cb..503cd2d0c 100644 --- a/src/common/uint128.h +++ b/src/common/uint128.h @@ -9,6 +9,9 @@ namespace Common { +// This function multiplies 2 u64 values and divides it by a u64 value. +u64 MultiplyAndDivide64(u64 a, u64 b, u64 d); + // This function multiplies 2 u64 values and produces a u128 value; u128 Multiply64Into128(u64 a, u64 b); diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp index eabbba9da..8f5e17fa4 100644 --- a/src/common/wall_clock.cpp +++ b/src/common/wall_clock.cpp @@ -58,7 +58,7 @@ private: #ifdef ARCHITECTURE_x86_64 -WallClock* CreateBestMatchingClock(u32 emulated_cpu_frequency, u32 emulated_clock_frequency) { +std::unique_ptr CreateBestMatchingClock(u32 emulated_cpu_frequency, u32 emulated_clock_frequency) { const auto& caps = GetCPUCaps(); u64 rtsc_frequency = 0; if (caps.invariant_tsc) { @@ -70,19 +70,16 @@ WallClock* CreateBestMatchingClock(u32 emulated_cpu_frequency, u32 emulated_cloc } } if (rtsc_frequency == 0) { - return static_cast( - new StandardWallClock(emulated_cpu_frequency, emulated_clock_frequency)); + return std::make_unique(emulated_cpu_frequency, emulated_clock_frequency); } else { - return static_cast( - new X64::NativeClock(emulated_cpu_frequency, emulated_clock_frequency, rtsc_frequency)); + return std::make_unique(emulated_cpu_frequency, emulated_clock_frequency, rtsc_frequency); } } #else -WallClock* CreateBestMatchingClock(u32 emulated_cpu_frequency, u32 emulated_clock_frequency) { - return static_cast( - new StandardWallClock(emulated_cpu_frequency, emulated_clock_frequency)); +std::unique_ptr CreateBestMatchingClock(u32 emulated_cpu_frequency, u32 emulated_clock_frequency) { + return std::make_unique(emulated_cpu_frequency, emulated_clock_frequency); } #endif diff --git a/src/common/wall_clock.h b/src/common/wall_clock.h index 6f763d74b..fc34429bb 100644 --- a/src/common/wall_clock.h +++ b/src/common/wall_clock.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include "common/common_types.h" @@ -12,10 +13,20 @@ namespace Common { class WallClock { public: + + /// Returns current wall time in nanoseconds virtual std::chrono::nanoseconds GetTimeNS() = 0; + + /// Returns current wall time in microseconds virtual std::chrono::microseconds GetTimeUS() = 0; + + /// Returns current wall time in milliseconds virtual std::chrono::milliseconds GetTimeMS() = 0; + + /// Returns current wall time in emulated clock cycles virtual u64 GetClockCycles() = 0; + + /// Returns current wall time in emulated cpu cycles virtual u64 GetCPUCycles() = 0; /// Tells if the wall clock, uses the host CPU's hardware clock @@ -35,6 +46,6 @@ private: bool is_native; }; -WallClock* CreateBestMatchingClock(u32 emulated_cpu_frequency, u32 emulated_clock_frequency); +std::unique_ptr CreateBestMatchingClock(u32 emulated_cpu_frequency, u32 emulated_clock_frequency); } // namespace Common diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp index c799111fd..26d4d0ba6 100644 --- a/src/common/x64/native_clock.cpp +++ b/src/common/x64/native_clock.cpp @@ -11,44 +11,11 @@ #include #endif +#include "common/uint128.h" #include "common/x64/native_clock.h" namespace Common { -#ifdef _MSC_VER - -namespace { - -struct uint128 { - u64 low; - u64 high; -}; - -u64 umuldiv64(u64 a, u64 b, u64 d) { - uint128 r{}; - r.low = _umul128(a, b, &r.high); - u64 remainder; - return _udiv128(r.high, r.low, d, &remainder); -} - -} // namespace - -#else - -namespace { - -u64 umuldiv64(u64 a, u64 b, u64 d) { - const u64 diva = a / d; - const u64 moda = a % d; - const u64 divb = b / d; - const u64 modb = b % d; - return diva * b + moda * divb + moda * modb / d; -} - -} // namespace - -#endif - u64 EstimateRDTSCFrequency() { const auto milli_10 = std::chrono::milliseconds{10}; // get current time @@ -70,7 +37,7 @@ u64 EstimateRDTSCFrequency() { const u64 timer_diff = std::chrono::duration_cast(endTime - startTime).count(); const u64 tsc_diff = tscEnd - tscStart; - const u64 tsc_freq = umuldiv64(tsc_diff, 1000000000ULL, timer_diff); + const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, timer_diff); return tsc_freq; } @@ -100,27 +67,27 @@ u64 NativeClock::GetRTSC() { std::chrono::nanoseconds NativeClock::GetTimeNS() { const u64 rtsc_value = GetRTSC(); - return std::chrono::nanoseconds{umuldiv64(rtsc_value, 1000000000, rtsc_frequency)}; + return std::chrono::nanoseconds{MultiplyAndDivide64(rtsc_value, 1000000000, rtsc_frequency)}; } std::chrono::microseconds NativeClock::GetTimeUS() { const u64 rtsc_value = GetRTSC(); - return std::chrono::microseconds{umuldiv64(rtsc_value, 1000000, rtsc_frequency)}; + return std::chrono::microseconds{MultiplyAndDivide64(rtsc_value, 1000000, rtsc_frequency)}; } std::chrono::milliseconds NativeClock::GetTimeMS() { const u64 rtsc_value = GetRTSC(); - return std::chrono::milliseconds{umuldiv64(rtsc_value, 1000, rtsc_frequency)}; + return std::chrono::milliseconds{MultiplyAndDivide64(rtsc_value, 1000, rtsc_frequency)}; } u64 NativeClock::GetClockCycles() { const u64 rtsc_value = GetRTSC(); - return umuldiv64(rtsc_value, emulated_clock_frequency, rtsc_frequency); + return MultiplyAndDivide64(rtsc_value, emulated_clock_frequency, rtsc_frequency); } u64 NativeClock::GetCPUCycles() { const u64 rtsc_value = GetRTSC(); - return umuldiv64(rtsc_value, emulated_cpu_frequency, rtsc_frequency); + return MultiplyAndDivide64(rtsc_value, emulated_cpu_frequency, rtsc_frequency); } } // namespace X64 diff --git a/src/core/host_timing.cpp b/src/core/host_timing.cpp index ef9977b76..4ccf7c6c1 100644 --- a/src/core/host_timing.cpp +++ b/src/core/host_timing.cpp @@ -36,8 +36,7 @@ struct CoreTiming::Event { }; CoreTiming::CoreTiming() { - Common::WallClock* wall = Common::CreateBestMatchingClock(Core::Timing::BASE_CLOCK_RATE, Core::Timing::CNTFREQ); - clock = std::unique_ptr(wall); + clock = Common::CreateBestMatchingClock(Core::Timing::BASE_CLOCK_RATE, Core::Timing::CNTFREQ); } CoreTiming::~CoreTiming() = default; From 03e4f5dac436fe361834e6b9918983e9c4787acb Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Mon, 10 Feb 2020 13:18:23 -0400 Subject: [PATCH 024/252] Common: Correct fcontext fibers. --- src/common/fiber.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index a88a30ced..e91d86dbe 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp @@ -12,6 +12,7 @@ namespace Common { + #ifdef _MSC_VER struct Fiber::FiberImpl { @@ -82,7 +83,6 @@ std::shared_ptr Fiber::ThreadToFiber() { } #else - constexpr std::size_t default_stack_size = 1024 * 1024 * 4; // 4MB struct alignas(64) Fiber::FiberImpl { @@ -108,9 +108,8 @@ void Fiber::FiberStartFunc(boost::context::detail::transfer_t transfer) Fiber::Fiber(std::function&& entry_point_func, void* start_parameter) : guard{}, entry_point{std::move(entry_point_func)}, start_parameter{start_parameter}, previous_fiber{} { impl = std::make_unique(); - auto start_func = std::bind(&Fiber::start, this); - impl->context = - boost::context::detail::make_fcontext(impl->stack.data(), impl->stack.size(), &start_func); + impl->context = boost::context::detail::make_fcontext(impl->stack.data(), impl->stack.size(), + FiberStartFunc); } Fiber::Fiber() : guard{}, entry_point{}, start_parameter{}, previous_fiber{} { @@ -139,7 +138,7 @@ void Fiber::YieldTo(std::shared_ptr from, std::shared_ptr to) { ASSERT_MSG(to != nullptr, "Next fiber is null!"); to->guard.lock(); to->previous_fiber = from; - auto transfer = boost::context::detail::jump_fcontext(to->impl.context, nullptr); + auto transfer = boost::context::detail::jump_fcontext(to->impl->context, nullptr); auto previous_fiber = from->previous_fiber; ASSERT(previous_fiber != nullptr); previous_fiber->impl->context = transfer.fctx; From 1bd706344e2381e11245b2f0bdc291429e46c634 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Mon, 10 Feb 2020 13:33:13 -0400 Subject: [PATCH 025/252] Common/Tests: Clang Format. --- src/common/fiber.cpp | 21 ++++++++++----------- src/common/fiber.h | 2 +- src/common/wall_clock.cpp | 12 ++++++++---- src/common/wall_clock.h | 4 ++-- src/core/host_timing.cpp | 8 +++++--- src/core/host_timing.h | 2 +- src/tests/common/fibers.cpp | 23 ++++++++++++++--------- 7 files changed, 41 insertions(+), 31 deletions(-) diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index e91d86dbe..a46be73c1 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp @@ -12,7 +12,6 @@ namespace Common { - #ifdef _MSC_VER struct Fiber::FiberImpl { @@ -27,14 +26,14 @@ void Fiber::start() { UNREACHABLE(); } -void __stdcall Fiber::FiberStartFunc(void* fiber_parameter) -{ - auto fiber = static_cast(fiber_parameter); - fiber->start(); +void __stdcall Fiber::FiberStartFunc(void* fiber_parameter) { + auto fiber = static_cast(fiber_parameter); + fiber->start(); } Fiber::Fiber(std::function&& entry_point_func, void* start_parameter) - : guard{}, entry_point{std::move(entry_point_func)}, start_parameter{start_parameter}, previous_fiber{} { + : guard{}, entry_point{std::move(entry_point_func)}, start_parameter{start_parameter}, + previous_fiber{} { impl = std::make_unique(); impl->handle = CreateFiber(0, &FiberStartFunc, this); } @@ -99,14 +98,14 @@ void Fiber::start(boost::context::detail::transfer_t& transfer) { UNREACHABLE(); } -void Fiber::FiberStartFunc(boost::context::detail::transfer_t transfer) -{ - auto fiber = static_cast(transfer.data); - fiber->start(transfer); +void Fiber::FiberStartFunc(boost::context::detail::transfer_t transfer) { + auto fiber = static_cast(transfer.data); + fiber->start(transfer); } Fiber::Fiber(std::function&& entry_point_func, void* start_parameter) - : guard{}, entry_point{std::move(entry_point_func)}, start_parameter{start_parameter}, previous_fiber{} { + : guard{}, entry_point{std::move(entry_point_func)}, start_parameter{start_parameter}, + previous_fiber{} { impl = std::make_unique(); impl->context = boost::context::detail::make_fcontext(impl->stack.data(), impl->stack.size(), FiberStartFunc); diff --git a/src/common/fiber.h b/src/common/fiber.h index 89a01fdd8..b530bf4d2 100644 --- a/src/common/fiber.h +++ b/src/common/fiber.h @@ -12,7 +12,7 @@ #ifndef _MSC_VER namespace boost::context::detail { - struct transfer_t; +struct transfer_t; } #endif diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp index 8f5e17fa4..e6161c72c 100644 --- a/src/common/wall_clock.cpp +++ b/src/common/wall_clock.cpp @@ -58,7 +58,8 @@ private: #ifdef ARCHITECTURE_x86_64 -std::unique_ptr CreateBestMatchingClock(u32 emulated_cpu_frequency, u32 emulated_clock_frequency) { +std::unique_ptr CreateBestMatchingClock(u32 emulated_cpu_frequency, + u32 emulated_clock_frequency) { const auto& caps = GetCPUCaps(); u64 rtsc_frequency = 0; if (caps.invariant_tsc) { @@ -70,15 +71,18 @@ std::unique_ptr CreateBestMatchingClock(u32 emulated_cpu_frequency, u } } if (rtsc_frequency == 0) { - return std::make_unique(emulated_cpu_frequency, emulated_clock_frequency); + return std::make_unique(emulated_cpu_frequency, + emulated_clock_frequency); } else { - return std::make_unique(emulated_cpu_frequency, emulated_clock_frequency, rtsc_frequency); + return std::make_unique(emulated_cpu_frequency, emulated_clock_frequency, + rtsc_frequency); } } #else -std::unique_ptr CreateBestMatchingClock(u32 emulated_cpu_frequency, u32 emulated_clock_frequency) { +std::unique_ptr CreateBestMatchingClock(u32 emulated_cpu_frequency, + u32 emulated_clock_frequency) { return std::make_unique(emulated_cpu_frequency, emulated_clock_frequency); } diff --git a/src/common/wall_clock.h b/src/common/wall_clock.h index fc34429bb..ed284cf50 100644 --- a/src/common/wall_clock.h +++ b/src/common/wall_clock.h @@ -13,7 +13,6 @@ namespace Common { class WallClock { public: - /// Returns current wall time in nanoseconds virtual std::chrono::nanoseconds GetTimeNS() = 0; @@ -46,6 +45,7 @@ private: bool is_native; }; -std::unique_ptr CreateBestMatchingClock(u32 emulated_cpu_frequency, u32 emulated_clock_frequency); +std::unique_ptr CreateBestMatchingClock(u32 emulated_cpu_frequency, + u32 emulated_clock_frequency); } // namespace Common diff --git a/src/core/host_timing.cpp b/src/core/host_timing.cpp index 4ccf7c6c1..c734a118e 100644 --- a/src/core/host_timing.cpp +++ b/src/core/host_timing.cpp @@ -72,7 +72,8 @@ void CoreTiming::SyncPause(bool is_paused) { } Pause(is_paused); event.Set(); - while (paused_set != is_paused); + while (paused_set != is_paused) + ; } bool CoreTiming::IsRunning() { @@ -158,7 +159,8 @@ void CoreTiming::Advance() { } if (!event_queue.empty()) { - std::chrono::nanoseconds next_time = std::chrono::nanoseconds(event_queue.front().time - global_timer); + std::chrono::nanoseconds next_time = + std::chrono::nanoseconds(event_queue.front().time - global_timer); basic_lock.unlock(); event.WaitFor(next_time); } else { @@ -181,4 +183,4 @@ std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const { return clock->GetTimeUS(); } -} // namespace Core::Timing +} // namespace Core::HostTiming diff --git a/src/core/host_timing.h b/src/core/host_timing.h index f04a150ee..15a150904 100644 --- a/src/core/host_timing.h +++ b/src/core/host_timing.h @@ -145,4 +145,4 @@ private: /// std::shared_ptr CreateEvent(std::string name, TimedCallback&& callback); -} // namespace Core::Timing +} // namespace Core::HostTiming diff --git a/src/tests/common/fibers.cpp b/src/tests/common/fibers.cpp index 358393a19..d63194dd4 100644 --- a/src/tests/common/fibers.cpp +++ b/src/tests/common/fibers.cpp @@ -92,7 +92,8 @@ public: void DoWork1() { trap2 = false; - while (trap.load()); + while (trap.load()) + ; for (u32 i = 0; i < 12000; i++) { value1 += i; } @@ -105,7 +106,8 @@ public: } void DoWork2() { - while (trap2.load()); + while (trap2.load()) + ; value2 = 2000; trap = false; Fiber::YieldTo(fiber2, fiber1); @@ -197,9 +199,12 @@ static void ThreadStart2_2(u32 id, TestControl2& test_control) { TEST_CASE("Fibers::InterExchange", "[common]") { TestControl2 test_control{}; test_control.thread_fibers.resize(2, nullptr); - test_control.fiber1 = std::make_shared(std::function{WorkControl2_1}, &test_control); - test_control.fiber2 = std::make_shared(std::function{WorkControl2_2}, &test_control); - test_control.fiber3 = std::make_shared(std::function{WorkControl2_3}, &test_control); + test_control.fiber1 = + std::make_shared(std::function{WorkControl2_1}, &test_control); + test_control.fiber2 = + std::make_shared(std::function{WorkControl2_2}, &test_control); + test_control.fiber3 = + std::make_shared(std::function{WorkControl2_3}, &test_control); std::thread thread1(ThreadStart2_1, 0, std::ref(test_control)); std::thread thread2(ThreadStart2_2, 1, std::ref(test_control)); thread1.join(); @@ -291,8 +296,10 @@ static void ThreadStart3(u32 id, TestControl3& test_control) { TEST_CASE("Fibers::StartRace", "[common]") { TestControl3 test_control{}; test_control.thread_fibers.resize(2, nullptr); - test_control.fiber1 = std::make_shared(std::function{WorkControl3_1}, &test_control); - test_control.fiber2 = std::make_shared(std::function{WorkControl3_2}, &test_control); + test_control.fiber1 = + std::make_shared(std::function{WorkControl3_1}, &test_control); + test_control.fiber2 = + std::make_shared(std::function{WorkControl3_2}, &test_control); std::thread thread1(ThreadStart3, 0, std::ref(test_control)); std::thread thread2(ThreadStart3, 1, std::ref(test_control)); thread1.join(); @@ -302,6 +309,4 @@ TEST_CASE("Fibers::StartRace", "[common]") { REQUIRE(test_control.value3 == 1); } - - } // namespace Common From 3398f701eeac63f3cfcf193f3e9c1ee2f06edb08 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Mon, 10 Feb 2020 14:21:23 -0400 Subject: [PATCH 026/252] Common: Make MinGW build use Windows Fibers instead of fcontext_t --- src/common/fiber.cpp | 4 ++-- src/common/fiber.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index a46be73c1..050c93acb 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp @@ -4,7 +4,7 @@ #include "common/assert.h" #include "common/fiber.h" -#ifdef _MSC_VER +#if defined(_WIN32) || defined(WIN32) #include #else #include @@ -12,7 +12,7 @@ namespace Common { -#ifdef _MSC_VER +#if defined(_WIN32) || defined(WIN32) struct Fiber::FiberImpl { LPVOID handle = nullptr; diff --git a/src/common/fiber.h b/src/common/fiber.h index b530bf4d2..598fe7daa 100644 --- a/src/common/fiber.h +++ b/src/common/fiber.h @@ -10,7 +10,7 @@ #include "common/common_types.h" #include "common/spin_lock.h" -#ifndef _MSC_VER +#if !defined(_WIN32) && !defined(WIN32) namespace boost::context::detail { struct transfer_t; } @@ -57,7 +57,7 @@ public: private: Fiber(); -#ifdef _MSC_VER +#if defined(_WIN32) || defined(WIN32) void start(); static void FiberStartFunc(void* fiber_parameter); #else From 1f7dd36499786d373b143a4437d4c32e077a32aa Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Mon, 10 Feb 2020 14:45:08 -0400 Subject: [PATCH 027/252] Common/Tests: Address Feedback --- src/common/fiber.cpp | 5 ++--- src/common/fiber.h | 8 ++++---- src/common/spin_lock.cpp | 3 ++- src/core/core_timing_util.cpp | 14 ++++++++++++-- src/core/core_timing_util.h | 2 ++ src/core/host_timing.cpp | 4 ++-- src/core/host_timing.h | 6 +++--- src/tests/common/fibers.cpp | 20 ++++++++++---------- src/tests/core/host_timing.cpp | 28 ++++++++++++++-------------- 9 files changed, 51 insertions(+), 39 deletions(-) diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index 050c93acb..1220eddf0 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp @@ -32,13 +32,12 @@ void __stdcall Fiber::FiberStartFunc(void* fiber_parameter) { } Fiber::Fiber(std::function&& entry_point_func, void* start_parameter) - : guard{}, entry_point{std::move(entry_point_func)}, start_parameter{start_parameter}, - previous_fiber{} { + : entry_point{std::move(entry_point_func)}, start_parameter{start_parameter} { impl = std::make_unique(); impl->handle = CreateFiber(0, &FiberStartFunc, this); } -Fiber::Fiber() : guard{}, entry_point{}, start_parameter{}, previous_fiber{} { +Fiber::Fiber() { impl = std::make_unique(); } diff --git a/src/common/fiber.h b/src/common/fiber.h index 598fe7daa..7e3b130a4 100644 --- a/src/common/fiber.h +++ b/src/common/fiber.h @@ -67,10 +67,10 @@ private: struct FiberImpl; - SpinLock guard; - std::function entry_point; - void* start_parameter; - std::shared_ptr previous_fiber; + SpinLock guard{}; + std::function entry_point{}; + void* start_parameter{}; + std::shared_ptr previous_fiber{}; std::unique_ptr impl; bool is_thread_fiber{}; }; diff --git a/src/common/spin_lock.cpp b/src/common/spin_lock.cpp index 82a1d39ff..c7b46aac6 100644 --- a/src/common/spin_lock.cpp +++ b/src/common/spin_lock.cpp @@ -35,8 +35,9 @@ void thread_pause() { namespace Common { void SpinLock::lock() { - while (lck.test_and_set(std::memory_order_acquire)) + while (lck.test_and_set(std::memory_order_acquire)) { thread_pause(); + } } void SpinLock::unlock() { diff --git a/src/core/core_timing_util.cpp b/src/core/core_timing_util.cpp index f42666b4d..be34b26fe 100644 --- a/src/core/core_timing_util.cpp +++ b/src/core/core_timing_util.cpp @@ -49,9 +49,19 @@ s64 nsToCycles(std::chrono::nanoseconds ns) { return (Hardware::BASE_CLOCK_RATE * ns.count()) / 1000000000; } +u64 msToClockCycles(std::chrono::milliseconds ns) { + const u128 temp = Common::Multiply64Into128(ns.count(), Hardware::CNTFREQ); + return Common::Divide128On32(temp, 1000).first; +} + +u64 usToClockCycles(std::chrono::microseconds ns) { + const u128 temp = Common::Multiply64Into128(ns.count(), Hardware::CNTFREQ); + return Common::Divide128On32(temp, 1000000).first; +} + u64 nsToClockCycles(std::chrono::nanoseconds ns) { - const u128 temporal = Common::Multiply64Into128(ns.count(), CNTFREQ); - return Common::Divide128On32(temporal, 1000000000).first; + const u128 temp = Common::Multiply64Into128(ns.count(), Hardware::CNTFREQ); + return Common::Divide128On32(temp, 1000000000).first; } u64 CpuCyclesToClockCycles(u64 ticks) { diff --git a/src/core/core_timing_util.h b/src/core/core_timing_util.h index 65fb7368b..b3c58447d 100644 --- a/src/core/core_timing_util.h +++ b/src/core/core_timing_util.h @@ -13,6 +13,8 @@ namespace Core::Timing { s64 msToCycles(std::chrono::milliseconds ms); s64 usToCycles(std::chrono::microseconds us); s64 nsToCycles(std::chrono::nanoseconds ns); +u64 msToClockCycles(std::chrono::milliseconds ns); +u64 usToClockCycles(std::chrono::microseconds ns); u64 nsToClockCycles(std::chrono::nanoseconds ns); inline std::chrono::milliseconds CyclesToMs(s64 cycles) { diff --git a/src/core/host_timing.cpp b/src/core/host_timing.cpp index c734a118e..be80d9f8e 100644 --- a/src/core/host_timing.cpp +++ b/src/core/host_timing.cpp @@ -76,11 +76,11 @@ void CoreTiming::SyncPause(bool is_paused) { ; } -bool CoreTiming::IsRunning() { +bool CoreTiming::IsRunning() const { return !paused_set; } -bool CoreTiming::HasPendingEvents() { +bool CoreTiming::HasPendingEvents() const { return !(wait_set && event_queue.empty()); } diff --git a/src/core/host_timing.h b/src/core/host_timing.h index 15a150904..679fcf491 100644 --- a/src/core/host_timing.h +++ b/src/core/host_timing.h @@ -72,15 +72,15 @@ public: void SyncPause(bool is_paused); /// Checks if core timing is running. - bool IsRunning(); + bool IsRunning() const; /// Checks if the timer thread has started. - bool HasStarted() { + bool HasStarted() const { return has_started; } /// Checks if there are any pending time events. - bool HasPendingEvents(); + bool HasPendingEvents() const; /// Schedules an event in core timing void ScheduleEvent(s64 ns_into_future, const std::shared_ptr& event_type, diff --git a/src/tests/common/fibers.cpp b/src/tests/common/fibers.cpp index d63194dd4..0d3d5153d 100644 --- a/src/tests/common/fibers.cpp +++ b/src/tests/common/fibers.cpp @@ -34,7 +34,7 @@ public: }; static void WorkControl1(void* control) { - TestControl1* test_control = static_cast(control); + auto* test_control = static_cast(control); test_control->DoWork(); } @@ -70,8 +70,8 @@ static void ThreadStart1(u32 id, TestControl1& test_control) { TEST_CASE("Fibers::Setup", "[common]") { constexpr u32 num_threads = 7; TestControl1 test_control{}; - test_control.thread_fibers.resize(num_threads, nullptr); - test_control.work_fibers.resize(num_threads, nullptr); + test_control.thread_fibers.resize(num_threads); + test_control.work_fibers.resize(num_threads); test_control.items.resize(num_threads, 0); test_control.results.resize(num_threads, 0); std::vector threads; @@ -153,17 +153,17 @@ public: }; static void WorkControl2_1(void* control) { - TestControl2* test_control = static_cast(control); + auto* test_control = static_cast(control); test_control->DoWork1(); } static void WorkControl2_2(void* control) { - TestControl2* test_control = static_cast(control); + auto* test_control = static_cast(control); test_control->DoWork2(); } static void WorkControl2_3(void* control) { - TestControl2* test_control = static_cast(control); + auto* test_control = static_cast(control); test_control->DoWork3(); } @@ -198,7 +198,7 @@ static void ThreadStart2_2(u32 id, TestControl2& test_control) { */ TEST_CASE("Fibers::InterExchange", "[common]") { TestControl2 test_control{}; - test_control.thread_fibers.resize(2, nullptr); + test_control.thread_fibers.resize(2); test_control.fiber1 = std::make_shared(std::function{WorkControl2_1}, &test_control); test_control.fiber2 = @@ -261,12 +261,12 @@ public: }; static void WorkControl3_1(void* control) { - TestControl3* test_control = static_cast(control); + auto* test_control = static_cast(control); test_control->DoWork1(); } static void WorkControl3_2(void* control) { - TestControl3* test_control = static_cast(control); + auto* test_control = static_cast(control); test_control->DoWork2(); } @@ -295,7 +295,7 @@ static void ThreadStart3(u32 id, TestControl3& test_control) { */ TEST_CASE("Fibers::StartRace", "[common]") { TestControl3 test_control{}; - test_control.thread_fibers.resize(2, nullptr); + test_control.thread_fibers.resize(2); test_control.fiber1 = std::make_shared(std::function{WorkControl3_1}, &test_control); test_control.fiber2 = diff --git a/src/tests/core/host_timing.cpp b/src/tests/core/host_timing.cpp index 3d0532d02..ed060be55 100644 --- a/src/tests/core/host_timing.cpp +++ b/src/tests/core/host_timing.cpp @@ -50,13 +50,13 @@ struct ScopeInit final { TEST_CASE("HostTiming[BasicOrder]", "[core]") { ScopeInit guard; auto& core_timing = guard.core_timing; - std::vector> events; - events.resize(5); - events[0] = Core::HostTiming::CreateEvent("callbackA", HostCallbackTemplate<0>); - events[1] = Core::HostTiming::CreateEvent("callbackB", HostCallbackTemplate<1>); - events[2] = Core::HostTiming::CreateEvent("callbackC", HostCallbackTemplate<2>); - events[3] = Core::HostTiming::CreateEvent("callbackD", HostCallbackTemplate<3>); - events[4] = Core::HostTiming::CreateEvent("callbackE", HostCallbackTemplate<4>); + std::vector> events{ + Core::HostTiming::CreateEvent("callbackA", HostCallbackTemplate<0>), + Core::HostTiming::CreateEvent("callbackB", HostCallbackTemplate<1>), + Core::HostTiming::CreateEvent("callbackC", HostCallbackTemplate<2>), + Core::HostTiming::CreateEvent("callbackD", HostCallbackTemplate<3>), + Core::HostTiming::CreateEvent("callbackE", HostCallbackTemplate<4>), + }; expected_callback = 0; @@ -100,13 +100,13 @@ u64 TestTimerSpeed(Core::HostTiming::CoreTiming& core_timing) { TEST_CASE("HostTiming[BasicOrderNoPausing]", "[core]") { ScopeInit guard; auto& core_timing = guard.core_timing; - std::vector> events; - events.resize(5); - events[0] = Core::HostTiming::CreateEvent("callbackA", HostCallbackTemplate<0>); - events[1] = Core::HostTiming::CreateEvent("callbackB", HostCallbackTemplate<1>); - events[2] = Core::HostTiming::CreateEvent("callbackC", HostCallbackTemplate<2>); - events[3] = Core::HostTiming::CreateEvent("callbackD", HostCallbackTemplate<3>); - events[4] = Core::HostTiming::CreateEvent("callbackE", HostCallbackTemplate<4>); + std::vector> events{ + Core::HostTiming::CreateEvent("callbackA", HostCallbackTemplate<0>), + Core::HostTiming::CreateEvent("callbackB", HostCallbackTemplate<1>), + Core::HostTiming::CreateEvent("callbackC", HostCallbackTemplate<2>), + Core::HostTiming::CreateEvent("callbackD", HostCallbackTemplate<3>), + Core::HostTiming::CreateEvent("callbackE", HostCallbackTemplate<4>), + }; core_timing.SyncPause(true); core_timing.SyncPause(false); From 49a7e0984a1210832b8be24433a95711c7ce029b Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Mon, 10 Feb 2020 15:02:04 -0400 Subject: [PATCH 028/252] Core/HostTiming: Allow events to be advanced manually. --- src/common/fiber.cpp | 2 +- src/common/wall_clock.cpp | 9 +++--- src/core/host_timing.cpp | 61 +++++++++++++++++++++++---------------- src/core/host_timing.h | 6 +++- 4 files changed, 47 insertions(+), 31 deletions(-) diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index 1220eddf0..e9c0946b6 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp @@ -110,7 +110,7 @@ Fiber::Fiber(std::function&& entry_point_func, void* start_paramete FiberStartFunc); } -Fiber::Fiber() : guard{}, entry_point{}, start_parameter{}, previous_fiber{} { +Fiber::Fiber() { impl = std::make_unique(); } diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp index e6161c72c..d4d35f4e7 100644 --- a/src/common/wall_clock.cpp +++ b/src/common/wall_clock.cpp @@ -42,14 +42,15 @@ public: u64 GetClockCycles() override { std::chrono::nanoseconds time_now = GetTimeNS(); - const u128 temporal = Common::Multiply64Into128(time_now.count(), emulated_clock_frequency); - return Common::Divide128On32(temporal, 1000000000).first; + const u128 temporary = + Common::Multiply64Into128(time_now.count(), emulated_clock_frequency); + return Common::Divide128On32(temporary, 1000000000).first; } u64 GetCPUCycles() override { std::chrono::nanoseconds time_now = GetTimeNS(); - const u128 temporal = Common::Multiply64Into128(time_now.count(), emulated_cpu_frequency); - return Common::Divide128On32(temporal, 1000000000).first; + const u128 temporary = Common::Multiply64Into128(time_now.count(), emulated_cpu_frequency); + return Common::Divide128On32(temporary, 1000000000).first; } private: diff --git a/src/core/host_timing.cpp b/src/core/host_timing.cpp index be80d9f8e..5d35a96b1 100644 --- a/src/core/host_timing.cpp +++ b/src/core/host_timing.cpp @@ -42,7 +42,7 @@ CoreTiming::CoreTiming() { CoreTiming::~CoreTiming() = default; void CoreTiming::ThreadEntry(CoreTiming& instance) { - instance.Advance(); + instance.ThreadLoop(); } void CoreTiming::Initialize() { @@ -137,38 +137,49 @@ void CoreTiming::RemoveEvent(const std::shared_ptr& event_type) { basic_lock.unlock(); } -void CoreTiming::Advance() { +std::optional CoreTiming::Advance() { + advance_lock.lock(); + basic_lock.lock(); + global_timer = GetGlobalTimeNs().count(); + + while (!event_queue.empty() && event_queue.front().time <= global_timer) { + Event evt = std::move(event_queue.front()); + std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>()); + event_queue.pop_back(); + basic_lock.unlock(); + + if (auto event_type{evt.type.lock()}) { + event_type->callback(evt.userdata, global_timer - evt.time); + } + + basic_lock.lock(); + } + + if (!event_queue.empty()) { + const u64 next_time = event_queue.front().time - global_timer; + basic_lock.unlock(); + advance_lock.unlock(); + return next_time; + } else { + basic_lock.unlock(); + advance_lock.unlock(); + return std::nullopt; + } +} + +void CoreTiming::ThreadLoop() { has_started = true; while (!shutting_down) { while (!paused) { paused_set = false; - basic_lock.lock(); - global_timer = GetGlobalTimeNs().count(); - - while (!event_queue.empty() && event_queue.front().time <= global_timer) { - Event evt = std::move(event_queue.front()); - std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>()); - event_queue.pop_back(); - basic_lock.unlock(); - - if (auto event_type{evt.type.lock()}) { - event_type->callback(evt.userdata, global_timer - evt.time); - } - - basic_lock.lock(); - } - - if (!event_queue.empty()) { - std::chrono::nanoseconds next_time = - std::chrono::nanoseconds(event_queue.front().time - global_timer); - basic_lock.unlock(); - event.WaitFor(next_time); + const auto next_time = Advance(); + if (next_time) { + std::chrono::nanoseconds next_time_ns = std::chrono::nanoseconds(*next_time); + event.WaitFor(next_time_ns); } else { - basic_lock.unlock(); wait_set = true; event.Wait(); } - wait_set = false; } paused_set = true; diff --git a/src/core/host_timing.h b/src/core/host_timing.h index 679fcf491..cd44b308c 100644 --- a/src/core/host_timing.h +++ b/src/core/host_timing.h @@ -103,6 +103,9 @@ public: /// Returns current time in nanoseconds. std::chrono::nanoseconds GetGlobalTimeNs() const; + /// Checks for events manually and returns time in nanoseconds for next event, threadsafe. + std::optional Advance(); + private: struct Event; @@ -110,7 +113,7 @@ private: void ClearPendingEvents(); static void ThreadEntry(CoreTiming& instance); - void Advance(); + void ThreadLoop(); std::unique_ptr clock; @@ -128,6 +131,7 @@ private: std::shared_ptr ev_lost; Common::Event event{}; Common::SpinLock basic_lock{}; + Common::SpinLock advance_lock{}; std::unique_ptr timer_thread; std::atomic paused{}; std::atomic paused_set{}; From 96b2d8419c94f9bcb5f2f970bbb453aa7383b510 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sat, 15 Feb 2020 13:56:50 -0400 Subject: [PATCH 029/252] HostTiming: Correct rebase and implement AddTicks. --- src/core/host_timing.cpp | 11 ++++++++++- src/core/host_timing.h | 9 +++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/core/host_timing.cpp b/src/core/host_timing.cpp index 5d35a96b1..2f40de1a1 100644 --- a/src/core/host_timing.cpp +++ b/src/core/host_timing.cpp @@ -36,7 +36,8 @@ struct CoreTiming::Event { }; CoreTiming::CoreTiming() { - clock = Common::CreateBestMatchingClock(Core::Timing::BASE_CLOCK_RATE, Core::Timing::CNTFREQ); + clock = + Common::CreateBestMatchingClock(Core::Hardware::BASE_CLOCK_RATE, Core::Hardware::CNTFREQ); } CoreTiming::~CoreTiming() = default; @@ -110,6 +111,14 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr& event_type, u basic_lock.unlock(); } +void CoreTiming::AddTicks(std::size_t core_index, u64 ticks) { + ticks_count[core_index] += ticks; +} + +void CoreTiming::ResetTicks(std::size_t core_index) { + ticks_count[core_index] = 0; +} + u64 CoreTiming::GetCPUTicks() const { return clock->GetCPUCycles(); } diff --git a/src/core/host_timing.h b/src/core/host_timing.h index cd44b308c..5ad8c5f35 100644 --- a/src/core/host_timing.h +++ b/src/core/host_timing.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include #include @@ -18,6 +19,7 @@ #include "common/thread.h" #include "common/threadsafe_queue.h" #include "common/wall_clock.h" +#include "core/hardware_properties.h" namespace Core::HostTiming { @@ -91,6 +93,11 @@ public: /// We only permit one event of each type in the queue at a time. void RemoveEvent(const std::shared_ptr& event_type); + + void AddTicks(std::size_t core_index, u64 ticks); + + void ResetTicks(std::size_t core_index); + /// Returns current time in emulated CPU cycles u64 GetCPUTicks() const; @@ -138,6 +145,8 @@ private: std::atomic wait_set{}; std::atomic shutting_down{}; std::atomic has_started{}; + + std::array, Core::Hardware::NUM_CPU_CORES> ticks_count{}; }; /// Creates a core timing event with the given name and callback. From 8f6ffcd5c450b06f641580c410e5a4756a897fcc Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sat, 22 Feb 2020 12:56:33 -0400 Subject: [PATCH 030/252] Host Timing: Correct clang format. --- src/core/host_timing.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/core/host_timing.h b/src/core/host_timing.h index 5ad8c5f35..be6b68d7c 100644 --- a/src/core/host_timing.h +++ b/src/core/host_timing.h @@ -93,7 +93,6 @@ public: /// We only permit one event of each type in the queue at a time. void RemoveEvent(const std::shared_ptr& event_type); - void AddTicks(std::size_t core_index, u64 ticks); void ResetTicks(std::size_t core_index); From 7d2b1a6ec4a1c0daea0bac83a83c85f263609224 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Wed, 26 Feb 2020 14:39:27 -0400 Subject: [PATCH 031/252] Common/Fiber: Correct f_context based Fibers. --- src/common/fiber.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index e9c0946b6..3ef820c62 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp @@ -81,10 +81,10 @@ std::shared_ptr Fiber::ThreadToFiber() { } #else -constexpr std::size_t default_stack_size = 1024 * 1024 * 4; // 4MB +constexpr std::size_t default_stack_size = 1024 * 1024; // 4MB -struct alignas(64) Fiber::FiberImpl { - std::array stack; +struct Fiber::FiberImpl { + alignas(64) std::array stack; boost::context::detail::fcontext_t context; }; @@ -106,8 +106,10 @@ Fiber::Fiber(std::function&& entry_point_func, void* start_paramete : guard{}, entry_point{std::move(entry_point_func)}, start_parameter{start_parameter}, previous_fiber{} { impl = std::make_unique(); - impl->context = boost::context::detail::make_fcontext(impl->stack.data(), impl->stack.size(), - FiberStartFunc); + void* stack_start = + static_cast(static_cast(impl->stack.data()) + default_stack_size); + impl->context = + boost::context::detail::make_fcontext(stack_start, impl->stack.size(), FiberStartFunc); } Fiber::Fiber() { @@ -136,7 +138,7 @@ void Fiber::YieldTo(std::shared_ptr from, std::shared_ptr to) { ASSERT_MSG(to != nullptr, "Next fiber is null!"); to->guard.lock(); to->previous_fiber = from; - auto transfer = boost::context::detail::jump_fcontext(to->impl->context, nullptr); + auto transfer = boost::context::detail::jump_fcontext(to->impl->context, to.get()); auto previous_fiber = from->previous_fiber; ASSERT(previous_fiber != nullptr); previous_fiber->impl->context = transfer.fctx; From 41013381d69f952f78b85de3ce226c1499d889b6 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Wed, 26 Feb 2020 17:34:23 -0400 Subject: [PATCH 032/252] Common/Fiber: Additional corrections to f_context. --- src/common/fiber.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index 3ef820c62..e4ecc73df 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp @@ -81,7 +81,7 @@ std::shared_ptr Fiber::ThreadToFiber() { } #else -constexpr std::size_t default_stack_size = 1024 * 1024; // 4MB +constexpr std::size_t default_stack_size = 1024 * 1024; // 1MB struct Fiber::FiberImpl { alignas(64) std::array stack; @@ -106,10 +106,10 @@ Fiber::Fiber(std::function&& entry_point_func, void* start_paramete : guard{}, entry_point{std::move(entry_point_func)}, start_parameter{start_parameter}, previous_fiber{} { impl = std::make_unique(); - void* stack_start = - static_cast(static_cast(impl->stack.data()) + default_stack_size); + u8* stack_limit = impl->stack.data(); + u8* stack_base = stack_limit + default_stack_size; impl->context = - boost::context::detail::make_fcontext(stack_start, impl->stack.size(), FiberStartFunc); + boost::context::detail::make_fcontext(stack_base, impl->stack.size(), FiberStartFunc); } Fiber::Fiber() { From 137d862d9b275209b3d62a413396a15e9e14b4b4 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Thu, 27 Feb 2020 16:32:47 -0400 Subject: [PATCH 033/252] Common/Fiber: Implement Rewinding. --- src/common/fiber.cpp | 32 ++++++++++++++++++++++++-- src/common/fiber.h | 8 +++++++ src/tests/common/fibers.cpp | 46 +++++++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 2 deletions(-) diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index e4ecc73df..f61479e13 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp @@ -12,10 +12,13 @@ namespace Common { +constexpr std::size_t default_stack_size = 256 * 1024; // 256kb + #if defined(_WIN32) || defined(WIN32) struct Fiber::FiberImpl { LPVOID handle = nullptr; + LPVOID rewind_handle = nullptr; }; void Fiber::start() { @@ -26,15 +29,29 @@ void Fiber::start() { UNREACHABLE(); } +void Fiber::onRewind() { + ASSERT(impl->handle != nullptr); + DeleteFiber(impl->handle); + impl->handle = impl->rewind_handle; + impl->rewind_handle = nullptr; + rewind_point(rewind_parameter); + UNREACHABLE(); +} + void __stdcall Fiber::FiberStartFunc(void* fiber_parameter) { auto fiber = static_cast(fiber_parameter); fiber->start(); } +void __stdcall Fiber::RewindStartFunc(void* fiber_parameter) { + auto fiber = static_cast(fiber_parameter); + fiber->onRewind(); +} + Fiber::Fiber(std::function&& entry_point_func, void* start_parameter) : entry_point{std::move(entry_point_func)}, start_parameter{start_parameter} { impl = std::make_unique(); - impl->handle = CreateFiber(0, &FiberStartFunc, this); + impl->handle = CreateFiber(default_stack_size, &FiberStartFunc, this); } Fiber::Fiber() { @@ -60,6 +77,18 @@ void Fiber::Exit() { guard.unlock(); } +void Fiber::SetRewindPoint(std::function&& rewind_func, void* start_parameter) { + rewind_point = std::move(rewind_func); + rewind_parameter = start_parameter; +} + +void Fiber::Rewind() { + ASSERT(rewind_point); + ASSERT(impl->rewind_handle == nullptr); + impl->rewind_handle = CreateFiber(default_stack_size, &RewindStartFunc, this); + SwitchToFiber(impl->rewind_handle); +} + void Fiber::YieldTo(std::shared_ptr from, std::shared_ptr to) { ASSERT_MSG(from != nullptr, "Yielding fiber is null!"); ASSERT_MSG(to != nullptr, "Next fiber is null!"); @@ -81,7 +110,6 @@ std::shared_ptr Fiber::ThreadToFiber() { } #else -constexpr std::size_t default_stack_size = 1024 * 1024; // 1MB struct Fiber::FiberImpl { alignas(64) std::array stack; diff --git a/src/common/fiber.h b/src/common/fiber.h index 7e3b130a4..a710df257 100644 --- a/src/common/fiber.h +++ b/src/common/fiber.h @@ -46,6 +46,10 @@ public: static void YieldTo(std::shared_ptr from, std::shared_ptr to); static std::shared_ptr ThreadToFiber(); + void SetRewindPoint(std::function&& rewind_func, void* start_parameter); + + void Rewind(); + /// Only call from main thread's fiber void Exit(); @@ -58,8 +62,10 @@ private: Fiber(); #if defined(_WIN32) || defined(WIN32) + void onRewind(); void start(); static void FiberStartFunc(void* fiber_parameter); + static void RewindStartFunc(void* fiber_parameter); #else void start(boost::context::detail::transfer_t& transfer); static void FiberStartFunc(boost::context::detail::transfer_t transfer); @@ -69,6 +75,8 @@ private: SpinLock guard{}; std::function entry_point{}; + std::function rewind_point{}; + void* rewind_parameter{}; void* start_parameter{}; std::shared_ptr previous_fiber{}; std::unique_ptr impl; diff --git a/src/tests/common/fibers.cpp b/src/tests/common/fibers.cpp index 0d3d5153d..12536b6d8 100644 --- a/src/tests/common/fibers.cpp +++ b/src/tests/common/fibers.cpp @@ -309,4 +309,50 @@ TEST_CASE("Fibers::StartRace", "[common]") { REQUIRE(test_control.value3 == 1); } +class TestControl4; + +static void WorkControl4(void* control); + +class TestControl4 { +public: + TestControl4() { + fiber1 = std::make_shared(std::function{WorkControl4}, this); + goal_reached = false; + rewinded = false; + } + + void Execute() { + thread_fiber = Fiber::ThreadToFiber(); + Fiber::YieldTo(thread_fiber, fiber1); + thread_fiber->Exit(); + } + + void DoWork() { + fiber1->SetRewindPoint(std::function{WorkControl4}, this); + if (rewinded) { + goal_reached = true; + Fiber::YieldTo(fiber1, thread_fiber); + } + rewinded = true; + fiber1->Rewind(); + } + + std::shared_ptr fiber1; + std::shared_ptr thread_fiber; + bool goal_reached; + bool rewinded; +}; + +static void WorkControl4(void* control) { + auto* test_control = static_cast(control); + test_control->DoWork(); +} + +TEST_CASE("Fibers::Rewind", "[common]") { + TestControl4 test_control{}; + test_control.Execute(); + REQUIRE(test_control.goal_reached); + REQUIRE(test_control.rewinded); +} + } // namespace Common From 18f54f74862322d5a9360cbdc3541b6e3f15dce6 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Fri, 6 Mar 2020 11:24:08 -0400 Subject: [PATCH 034/252] Common/Fiber: Document fiber interexchange. --- src/common/fiber.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/common/fiber.h b/src/common/fiber.h index a710df257..3bbd506b5 100644 --- a/src/common/fiber.h +++ b/src/common/fiber.h @@ -28,7 +28,10 @@ namespace Common { * to implement such patterns. This fiber class is 'threadsafe' only one fiber * can be running at a time and threads will be locked while trying to yield to * a running fiber until it yields. WARNING exchanging two running fibers between - * threads will cause a deadlock. + * threads will cause a deadlock. In order to prevent a deadlock, each thread should + * have an intermediary fiber, you switch to the intermediary fiber of the current + * thread and then from it switch to the expected fiber. This way you can exchange + * 2 fibers within 2 different threads. */ class Fiber { public: From 59ce6e6d06e5ce8628f96bb247a342dec356fe43 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Thu, 12 Mar 2020 20:10:51 -0400 Subject: [PATCH 035/252] Common/uint128: Correct MSVC Compilation in old versions. --- src/common/uint128.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/common/uint128.cpp b/src/common/uint128.cpp index 7e77588db..16bf7c828 100644 --- a/src/common/uint128.cpp +++ b/src/common/uint128.cpp @@ -19,7 +19,11 @@ u64 MultiplyAndDivide64(u64 a, u64 b, u64 d) { u128 r{}; r[0] = _umul128(a, b, &r[1]); u64 remainder; +#if _MSC_VER < 1923 + return udiv128(r[1], r[0], d, &remainder); +#else return _udiv128(r[1], r[0], d, &remainder); +#endif } #else From b6655aa2e492e326e319b09e832c1612bf27acf4 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Wed, 1 Apr 2020 09:19:10 -0400 Subject: [PATCH 036/252] Common/Fiber: Implement Rewind on Boost Context. --- src/common/fiber.cpp | 39 +++++++++++++++++++++++++++++++++++++-- src/common/fiber.h | 2 ++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index f61479e13..6ea314d75 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp @@ -113,7 +113,11 @@ std::shared_ptr Fiber::ThreadToFiber() { struct Fiber::FiberImpl { alignas(64) std::array stack; + u8* stack_limit; + alignas(64) std::array rewind_stack; + u8* rewind_stack_limit; boost::context::detail::fcontext_t context; + boost::context::detail::fcontext_t rewind_context; }; void Fiber::start(boost::context::detail::transfer_t& transfer) { @@ -125,21 +129,43 @@ void Fiber::start(boost::context::detail::transfer_t& transfer) { UNREACHABLE(); } +void Fiber::onRewind(boost::context::detail::transfer_t& [[maybe_unused]] transfer) { + ASSERT(impl->context != nullptr); + impl->context = impl->rewind_context; + impl->rewind_context = nullptr; + u8* tmp = impl->stack_limit; + impl->stack_limit = impl->rewind_stack_limit; + impl->rewind_stack_limit = tmp; + rewind_point(rewind_parameter); + UNREACHABLE(); +} + void Fiber::FiberStartFunc(boost::context::detail::transfer_t transfer) { auto fiber = static_cast(transfer.data); fiber->start(transfer); } +void Fiber::RewindStartFunc(boost::context::detail::transfer_t transfer) { + auto fiber = static_cast(transfer.data); + fiber->onRewind(transfer); +} + Fiber::Fiber(std::function&& entry_point_func, void* start_parameter) : guard{}, entry_point{std::move(entry_point_func)}, start_parameter{start_parameter}, previous_fiber{} { impl = std::make_unique(); - u8* stack_limit = impl->stack.data(); - u8* stack_base = stack_limit + default_stack_size; + impl->stack_limit = impl->stack.data(); + impl->rewind_stack_limit = impl->rewind_stack.data(); + u8* stack_base = impl->stack_limit + default_stack_size; impl->context = boost::context::detail::make_fcontext(stack_base, impl->stack.size(), FiberStartFunc); } +void Fiber::SetRewindPoint(std::function&& rewind_func, void* start_parameter) { + rewind_point = std::move(rewind_func); + rewind_parameter = start_parameter; +} + Fiber::Fiber() { impl = std::make_unique(); } @@ -161,6 +187,15 @@ void Fiber::Exit() { guard.unlock(); } +void Fiber::Rewind() { + ASSERT(rewind_point); + ASSERT(impl->rewind_context == nullptr); + u8* stack_base = impl->rewind_stack_limit + default_stack_size; + impl->rewind_context = + boost::context::detail::make_fcontext(stack_base, impl->stack.size(), RewindStartFunc); + boost::context::detail::jump_fcontext(impl->rewind_context, this); +} + void Fiber::YieldTo(std::shared_ptr from, std::shared_ptr to) { ASSERT_MSG(from != nullptr, "Yielding fiber is null!"); ASSERT_MSG(to != nullptr, "Next fiber is null!"); diff --git a/src/common/fiber.h b/src/common/fiber.h index 3bbd506b5..cab7bc4b5 100644 --- a/src/common/fiber.h +++ b/src/common/fiber.h @@ -70,8 +70,10 @@ private: static void FiberStartFunc(void* fiber_parameter); static void RewindStartFunc(void* fiber_parameter); #else + void onRewind(boost::context::detail::transfer_t& transfer); void start(boost::context::detail::transfer_t& transfer); static void FiberStartFunc(boost::context::detail::transfer_t transfer); + static void RewindStartFunc(boost::context::detail::transfer_t transfer); #endif struct FiberImpl; From e77ee67bfacf9a0d3b9e7cd164531a2be158adc9 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Wed, 13 May 2020 13:49:36 -0400 Subject: [PATCH 037/252] Common/Fiber: Address Feedback and Correct Memory leaks. --- src/common/fiber.cpp | 58 ++++++++++++++++++++++++-------------------- src/common/fiber.h | 17 +++++++------ 2 files changed, 41 insertions(+), 34 deletions(-) diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index 6ea314d75..f97ad433b 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp @@ -21,7 +21,7 @@ struct Fiber::FiberImpl { LPVOID rewind_handle = nullptr; }; -void Fiber::start() { +void Fiber::Start() { ASSERT(previous_fiber != nullptr); previous_fiber->guard.unlock(); previous_fiber.reset(); @@ -29,7 +29,7 @@ void Fiber::start() { UNREACHABLE(); } -void Fiber::onRewind() { +void Fiber::OnRewind() { ASSERT(impl->handle != nullptr); DeleteFiber(impl->handle); impl->handle = impl->rewind_handle; @@ -38,14 +38,14 @@ void Fiber::onRewind() { UNREACHABLE(); } -void __stdcall Fiber::FiberStartFunc(void* fiber_parameter) { +void Fiber::FiberStartFunc(void* fiber_parameter) { auto fiber = static_cast(fiber_parameter); - fiber->start(); + fiber->Start(); } -void __stdcall Fiber::RewindStartFunc(void* fiber_parameter) { +void Fiber::RewindStartFunc(void* fiber_parameter) { auto fiber = static_cast(fiber_parameter); - fiber->onRewind(); + fiber->OnRewind(); } Fiber::Fiber(std::function&& entry_point_func, void* start_parameter) @@ -59,8 +59,11 @@ Fiber::Fiber() { } Fiber::~Fiber() { + if (released) { + return; + } // Make sure the Fiber is not being used - bool locked = guard.try_lock(); + const bool locked = guard.try_lock(); ASSERT_MSG(locked, "Destroying a fiber that's still running"); if (locked) { guard.unlock(); @@ -75,6 +78,7 @@ void Fiber::Exit() { } ConvertFiberToThread(); guard.unlock(); + released = true; } void Fiber::SetRewindPoint(std::function&& rewind_func, void* start_parameter) { @@ -89,22 +93,21 @@ void Fiber::Rewind() { SwitchToFiber(impl->rewind_handle); } -void Fiber::YieldTo(std::shared_ptr from, std::shared_ptr to) { +void Fiber::YieldTo(std::shared_ptr& from, std::shared_ptr& to) { ASSERT_MSG(from != nullptr, "Yielding fiber is null!"); ASSERT_MSG(to != nullptr, "Next fiber is null!"); to->guard.lock(); to->previous_fiber = from; SwitchToFiber(to->impl->handle); - auto previous_fiber = from->previous_fiber; - ASSERT(previous_fiber != nullptr); - previous_fiber->guard.unlock(); - previous_fiber.reset(); + ASSERT(from->previous_fiber != nullptr); + from->previous_fiber->guard.unlock(); + from->previous_fiber.reset(); } std::shared_ptr Fiber::ThreadToFiber() { std::shared_ptr fiber = std::shared_ptr{new Fiber()}; fiber->guard.lock(); - fiber->impl->handle = ConvertThreadToFiber(NULL); + fiber->impl->handle = ConvertThreadToFiber(nullptr); fiber->is_thread_fiber = true; return fiber; } @@ -120,7 +123,7 @@ struct Fiber::FiberImpl { boost::context::detail::fcontext_t rewind_context; }; -void Fiber::start(boost::context::detail::transfer_t& transfer) { +void Fiber::Start(boost::context::detail::transfer_t& transfer) { ASSERT(previous_fiber != nullptr); previous_fiber->impl->context = transfer.fctx; previous_fiber->guard.unlock(); @@ -129,7 +132,7 @@ void Fiber::start(boost::context::detail::transfer_t& transfer) { UNREACHABLE(); } -void Fiber::onRewind(boost::context::detail::transfer_t& [[maybe_unused]] transfer) { +void Fiber::OnRewind([[maybe_unused]] boost::context::detail::transfer_t& transfer) { ASSERT(impl->context != nullptr); impl->context = impl->rewind_context; impl->rewind_context = nullptr; @@ -142,17 +145,16 @@ void Fiber::onRewind(boost::context::detail::transfer_t& [[maybe_unused]] transf void Fiber::FiberStartFunc(boost::context::detail::transfer_t transfer) { auto fiber = static_cast(transfer.data); - fiber->start(transfer); + fiber->Start(transfer); } void Fiber::RewindStartFunc(boost::context::detail::transfer_t transfer) { auto fiber = static_cast(transfer.data); - fiber->onRewind(transfer); + fiber->OnRewind(transfer); } Fiber::Fiber(std::function&& entry_point_func, void* start_parameter) - : guard{}, entry_point{std::move(entry_point_func)}, start_parameter{start_parameter}, - previous_fiber{} { + : entry_point{std::move(entry_point_func)}, start_parameter{start_parameter} { impl = std::make_unique(); impl->stack_limit = impl->stack.data(); impl->rewind_stack_limit = impl->rewind_stack.data(); @@ -171,8 +173,11 @@ Fiber::Fiber() { } Fiber::~Fiber() { + if (released) { + return; + } // Make sure the Fiber is not being used - bool locked = guard.try_lock(); + const bool locked = guard.try_lock(); ASSERT_MSG(locked, "Destroying a fiber that's still running"); if (locked) { guard.unlock(); @@ -180,11 +185,13 @@ Fiber::~Fiber() { } void Fiber::Exit() { + ASSERT_MSG(is_thread_fiber, "Exitting non main thread fiber"); if (!is_thread_fiber) { return; } guard.unlock(); + released = true; } void Fiber::Rewind() { @@ -196,17 +203,16 @@ void Fiber::Rewind() { boost::context::detail::jump_fcontext(impl->rewind_context, this); } -void Fiber::YieldTo(std::shared_ptr from, std::shared_ptr to) { +void Fiber::YieldTo(std::shared_ptr& from, std::shared_ptr& to) { ASSERT_MSG(from != nullptr, "Yielding fiber is null!"); ASSERT_MSG(to != nullptr, "Next fiber is null!"); to->guard.lock(); to->previous_fiber = from; auto transfer = boost::context::detail::jump_fcontext(to->impl->context, to.get()); - auto previous_fiber = from->previous_fiber; - ASSERT(previous_fiber != nullptr); - previous_fiber->impl->context = transfer.fctx; - previous_fiber->guard.unlock(); - previous_fiber.reset(); + ASSERT(from->previous_fiber != nullptr); + from->previous_fiber->impl->context = transfer.fctx; + from->previous_fiber->guard.unlock(); + from->previous_fiber.reset(); } std::shared_ptr Fiber::ThreadToFiber() { diff --git a/src/common/fiber.h b/src/common/fiber.h index cab7bc4b5..dafc1100e 100644 --- a/src/common/fiber.h +++ b/src/common/fiber.h @@ -46,7 +46,7 @@ public: /// Yields control from Fiber 'from' to Fiber 'to' /// Fiber 'from' must be the currently running fiber. - static void YieldTo(std::shared_ptr from, std::shared_ptr to); + static void YieldTo(std::shared_ptr& from, std::shared_ptr& to); static std::shared_ptr ThreadToFiber(); void SetRewindPoint(std::function&& rewind_func, void* start_parameter); @@ -65,13 +65,13 @@ private: Fiber(); #if defined(_WIN32) || defined(WIN32) - void onRewind(); - void start(); + void OnRewind(); + void Start(); static void FiberStartFunc(void* fiber_parameter); static void RewindStartFunc(void* fiber_parameter); #else - void onRewind(boost::context::detail::transfer_t& transfer); - void start(boost::context::detail::transfer_t& transfer); + void OnRewind(boost::context::detail::transfer_t& transfer); + void Start(boost::context::detail::transfer_t& transfer); static void FiberStartFunc(boost::context::detail::transfer_t transfer); static void RewindStartFunc(boost::context::detail::transfer_t transfer); #endif @@ -79,13 +79,14 @@ private: struct FiberImpl; SpinLock guard{}; - std::function entry_point{}; - std::function rewind_point{}; + std::function entry_point; + std::function rewind_point; void* rewind_parameter{}; void* start_parameter{}; - std::shared_ptr previous_fiber{}; + std::shared_ptr previous_fiber; std::unique_ptr impl; bool is_thread_fiber{}; + bool released{}; }; } // namespace Common From 45d29436b32bbee1bdf1344e3dc3db365dc42937 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Thu, 14 May 2020 14:10:49 -0400 Subject: [PATCH 038/252] Tests/HostTiming: Correct GCC Compile error. --- src/tests/core/host_timing.cpp | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/src/tests/core/host_timing.cpp b/src/tests/core/host_timing.cpp index ed060be55..556254098 100644 --- a/src/tests/core/host_timing.cpp +++ b/src/tests/core/host_timing.cpp @@ -22,7 +22,6 @@ static std::array delays{}; static std::bitset callbacks_ran_flags; static u64 expected_callback = 0; -static s64 lateness = 0; template void HostCallbackTemplate(u64 userdata, s64 nanoseconds_late) { @@ -34,8 +33,6 @@ void HostCallbackTemplate(u64 userdata, s64 nanoseconds_late) { ++expected_callback; } -static u64 callbacks_done = 0; - struct ScopeInit final { ScopeInit() { core_timing.Initialize(); @@ -47,6 +44,20 @@ struct ScopeInit final { Core::HostTiming::CoreTiming core_timing; }; +#pragma optimize("", off) + +static u64 TestTimerSpeed(Core::HostTiming::CoreTiming& core_timing) { + u64 start = core_timing.GetGlobalTimeNs().count(); + u64 placebo = 0; + for (std::size_t i = 0; i < 1000; i++) { + placebo += core_timing.GetGlobalTimeNs().count(); + } + u64 end = core_timing.GetGlobalTimeNs().count(); + return (end - start); +} + +#pragma optimize("", on) + TEST_CASE("HostTiming[BasicOrder]", "[core]") { ScopeInit guard; auto& core_timing = guard.core_timing; @@ -85,18 +96,6 @@ TEST_CASE("HostTiming[BasicOrder]", "[core]") { } } -#pragma optimize("", off) -u64 TestTimerSpeed(Core::HostTiming::CoreTiming& core_timing) { - u64 start = core_timing.GetGlobalTimeNs().count(); - u64 placebo = 0; - for (std::size_t i = 0; i < 1000; i++) { - placebo += core_timing.GetGlobalTimeNs().count(); - } - u64 end = core_timing.GetGlobalTimeNs().count(); - return (end - start); -} -#pragma optimize("", on) - TEST_CASE("HostTiming[BasicOrderNoPausing]", "[core]") { ScopeInit guard; auto& core_timing = guard.core_timing; From 7d763f060eb0fe151a629aa36cce3d7ce076e12a Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Thu, 18 Jun 2020 17:47:19 -0300 Subject: [PATCH 039/252] vk_update_descriptor: Upload descriptor sets data directly Instead of copying to a temporary payload before sending the update task to the worker thread, insert elements to the payload directly. --- .../renderer_vulkan/vk_rasterizer.cpp | 4 +-- .../renderer_vulkan/vk_update_descriptor.cpp | 36 +++++++------------ .../renderer_vulkan/vk_update_descriptor.h | 32 ++++++++--------- 3 files changed, 30 insertions(+), 42 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 184b2238a..91da9ff80 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -1154,7 +1154,7 @@ void RasterizerVulkan::SetupTexture(const Tegra::Texture::FullTextureInfo& textu const auto sampler = sampler_cache.GetSampler(texture.tsc); update_descriptor_queue.AddSampledImage(sampler, image_view); - const auto image_layout = update_descriptor_queue.GetLastImageLayout(); + VkImageLayout* const image_layout = update_descriptor_queue.LastImageLayout(); *image_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; sampled_views.push_back(ImageView{std::move(view), image_layout}); } @@ -1180,7 +1180,7 @@ void RasterizerVulkan::SetupImage(const Tegra::Texture::TICEntry& tic, const Ima view->GetImageView(tic.x_source, tic.y_source, tic.z_source, tic.w_source); update_descriptor_queue.AddImage(image_view); - const auto image_layout = update_descriptor_queue.GetLastImageLayout(); + VkImageLayout* const image_layout = update_descriptor_queue.LastImageLayout(); *image_layout = VK_IMAGE_LAYOUT_GENERAL; image_views.push_back(ImageView{std::move(view), image_layout}); } diff --git a/src/video_core/renderer_vulkan/vk_update_descriptor.cpp b/src/video_core/renderer_vulkan/vk_update_descriptor.cpp index 681ecde98..351c048d2 100644 --- a/src/video_core/renderer_vulkan/vk_update_descriptor.cpp +++ b/src/video_core/renderer_vulkan/vk_update_descriptor.cpp @@ -24,35 +24,25 @@ void VKUpdateDescriptorQueue::TickFrame() { } void VKUpdateDescriptorQueue::Acquire() { - entries.clear(); -} + // Minimum number of entries required. + // This is the maximum number of entries a single draw call migth use. + static constexpr std::size_t MIN_ENTRIES = 0x400; -void VKUpdateDescriptorQueue::Send(VkDescriptorUpdateTemplateKHR update_template, - VkDescriptorSet set) { - if (payload.size() + entries.size() >= payload.max_size()) { + if (payload.size() + MIN_ENTRIES >= payload.max_size()) { LOG_WARNING(Render_Vulkan, "Payload overflow, waiting for worker thread"); scheduler.WaitWorker(); payload.clear(); } + upload_start = &*payload.end(); +} - // TODO(Rodrigo): Rework to write the payload directly - const auto payload_start = payload.data() + payload.size(); - for (const auto& entry : entries) { - if (const auto image = std::get_if(&entry)) { - payload.push_back(*image); - } else if (const auto buffer = std::get_if(&entry)) { - payload.push_back(*buffer); - } else if (const auto texel = std::get_if(&entry)) { - payload.push_back(*texel); - } else { - UNREACHABLE(); - } - } - - scheduler.Record( - [payload_start, set, update_template, logical = &device.GetLogical()](vk::CommandBuffer) { - logical->UpdateDescriptorSet(set, update_template, payload_start); - }); +void VKUpdateDescriptorQueue::Send(VkDescriptorUpdateTemplateKHR update_template, + VkDescriptorSet set) { + const void* const data = upload_start; + const vk::Device* const logical = &device.GetLogical(); + scheduler.Record([data, logical, set, update_template](vk::CommandBuffer) { + logical->UpdateDescriptorSet(set, update_template, data); + }); } } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_update_descriptor.h b/src/video_core/renderer_vulkan/vk_update_descriptor.h index cc7e3dff4..945320c72 100644 --- a/src/video_core/renderer_vulkan/vk_update_descriptor.h +++ b/src/video_core/renderer_vulkan/vk_update_descriptor.h @@ -15,17 +15,13 @@ namespace Vulkan { class VKDevice; class VKScheduler; -class DescriptorUpdateEntry { -public: - explicit DescriptorUpdateEntry() {} +struct DescriptorUpdateEntry { + DescriptorUpdateEntry(VkDescriptorImageInfo image_) : image{image_} {} - DescriptorUpdateEntry(VkDescriptorImageInfo image) : image{image} {} + DescriptorUpdateEntry(VkDescriptorBufferInfo buffer_) : buffer{buffer_} {} - DescriptorUpdateEntry(VkDescriptorBufferInfo buffer) : buffer{buffer} {} + DescriptorUpdateEntry(VkBufferView texel_buffer_) : texel_buffer{texel_buffer_} {} - DescriptorUpdateEntry(VkBufferView texel_buffer) : texel_buffer{texel_buffer} {} - -private: union { VkDescriptorImageInfo image; VkDescriptorBufferInfo buffer; @@ -45,32 +41,34 @@ public: void Send(VkDescriptorUpdateTemplateKHR update_template, VkDescriptorSet set); void AddSampledImage(VkSampler sampler, VkImageView image_view) { - entries.emplace_back(VkDescriptorImageInfo{sampler, image_view, {}}); + payload.emplace_back(VkDescriptorImageInfo{sampler, image_view, {}}); } void AddImage(VkImageView image_view) { - entries.emplace_back(VkDescriptorImageInfo{{}, image_view, {}}); + payload.emplace_back(VkDescriptorImageInfo{{}, image_view, {}}); } void AddBuffer(VkBuffer buffer, u64 offset, std::size_t size) { - entries.emplace_back(VkDescriptorBufferInfo{buffer, offset, size}); + payload.emplace_back(VkDescriptorBufferInfo{buffer, offset, size}); } void AddTexelBuffer(VkBufferView texel_buffer) { - entries.emplace_back(texel_buffer); + payload.emplace_back(texel_buffer); } - VkImageLayout* GetLastImageLayout() { - return &std::get(entries.back()).imageLayout; + VkImageLayout* LastImageLayout() { + return &payload.back().image.imageLayout; + } + + const VkImageLayout* LastImageLayout() const { + return &payload.back().image.imageLayout; } private: - using Variant = std::variant; - const VKDevice& device; VKScheduler& scheduler; - boost::container::static_vector entries; + const DescriptorUpdateEntry* upload_start = nullptr; boost::container::static_vector payload; }; From cf137ea40b8770310773cf9d51ae5e47bdbddf9d Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Thu, 18 Jun 2020 18:16:21 -0300 Subject: [PATCH 040/252] vk_rasterizer: Don't preserve contents on full screen clears There's no need to load contents from the CPU when a clear resets all the contents of the underlying memory. This is already implemented on OpenGL and the texture cache. --- .../renderer_vulkan/vk_rasterizer.cpp | 60 +++++++++++++++++-- .../renderer_vulkan/vk_rasterizer.h | 5 +- 2 files changed, 58 insertions(+), 7 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 184b2238a..a5fd68358 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -143,6 +143,49 @@ Tegra::Texture::FullTextureInfo GetTextureInfo(const Engine& engine, const Entry } } +/// @brief Determine if an attachment to be updated has to preserve contents +/// @param is_clear True when a clear is being executed +/// @param regs 3D registers +/// @return True when the contents have to be preserved +bool HasToPreserveColorContents(bool is_clear, const Maxwell& regs) { + if (!is_clear) { + return true; + } + // First we have to make sure all clear masks are enabled. + if (!regs.clear_buffers.R || !regs.clear_buffers.G || !regs.clear_buffers.B || + !regs.clear_buffers.A) { + return true; + } + // If scissors are disabled, the whole screen is cleared + if (!regs.clear_flags.scissor) { + return false; + } + // Then we have to confirm scissor testing clears the whole image + const std::size_t index = regs.clear_buffers.RT; + const auto& scissor = regs.scissor_test[0]; + return scissor.min_x > 0 || scissor.min_y > 0 || scissor.max_x < regs.rt[index].width || + scissor.max_y < regs.rt[index].height; +} + +/// @brief Determine if an attachment to be updated has to preserve contents +/// @param is_clear True when a clear is being executed +/// @param regs 3D registers +/// @return True when the contents have to be preserved +bool HasToPreserveDepthContents(bool is_clear, const Maxwell& regs) { + // If we are not clearing, the contents have to be preserved + if (!is_clear) { + return true; + } + // For depth stencil clears we only have to confirm scissor test covers the whole image + if (!regs.clear_flags.scissor) { + return false; + } + // Make sure the clear cover the whole image + const auto& scissor = regs.scissor_test[0]; + return scissor.min_x > 0 || scissor.min_y > 0 || scissor.max_x < regs.zeta_width || + scissor.max_y < regs.zeta_height; +} + } // Anonymous namespace class BufferBindings final { @@ -344,7 +387,7 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) { buffer_cache.Unmap(); - const Texceptions texceptions = UpdateAttachments(); + const Texceptions texceptions = UpdateAttachments(false); SetupImageTransitions(texceptions, color_attachments, zeta_attachment); key.renderpass_params = GetRenderPassParams(texceptions); @@ -400,7 +443,7 @@ void RasterizerVulkan::Clear() { return; } - [[maybe_unused]] const auto texceptions = UpdateAttachments(); + [[maybe_unused]] const auto texceptions = UpdateAttachments(true); DEBUG_ASSERT(texceptions.none()); SetupImageTransitions(0, color_attachments, zeta_attachment); @@ -677,9 +720,12 @@ void RasterizerVulkan::FlushWork() { draw_counter = 0; } -RasterizerVulkan::Texceptions RasterizerVulkan::UpdateAttachments() { +RasterizerVulkan::Texceptions RasterizerVulkan::UpdateAttachments(bool is_clear) { MICROPROFILE_SCOPE(Vulkan_RenderTargets); - auto& dirty = system.GPU().Maxwell3D().dirty.flags; + auto& maxwell3d = system.GPU().Maxwell3D(); + auto& dirty = maxwell3d.dirty.flags; + auto& regs = maxwell3d.regs; + const bool update_rendertargets = dirty[VideoCommon::Dirty::RenderTargets]; dirty[VideoCommon::Dirty::RenderTargets] = false; @@ -688,7 +734,8 @@ RasterizerVulkan::Texceptions RasterizerVulkan::UpdateAttachments() { Texceptions texceptions; for (std::size_t rt = 0; rt < Maxwell::NumRenderTargets; ++rt) { if (update_rendertargets) { - color_attachments[rt] = texture_cache.GetColorBufferSurface(rt, true); + const bool preserve_contents = HasToPreserveColorContents(is_clear, regs); + color_attachments[rt] = texture_cache.GetColorBufferSurface(rt, preserve_contents); } if (color_attachments[rt] && WalkAttachmentOverlaps(*color_attachments[rt])) { texceptions[rt] = true; @@ -696,7 +743,8 @@ RasterizerVulkan::Texceptions RasterizerVulkan::UpdateAttachments() { } if (update_rendertargets) { - zeta_attachment = texture_cache.GetDepthBufferSurface(true); + const bool preserve_contents = HasToPreserveDepthContents(is_clear, regs); + zeta_attachment = texture_cache.GetDepthBufferSurface(preserve_contents); } if (zeta_attachment && WalkAttachmentOverlaps(*zeta_attachment)) { texceptions[ZETA_TEXCEPTION_INDEX] = true; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index c8c187606..83e00e7e9 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -159,7 +159,10 @@ private: void FlushWork(); - Texceptions UpdateAttachments(); + /// @brief Updates the currently bound attachments + /// @param is_clear True when the framebuffer is updated as a clear + /// @return Bitfield of attachments being used as sampled textures + Texceptions UpdateAttachments(bool is_clear); std::tuple ConfigureFramebuffers(VkRenderPass renderpass); From 4514b80b3eedff01e994f225ea3d2da292c23e01 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Fri, 19 Jun 2020 21:55:00 -0400 Subject: [PATCH 041/252] buffer_cache: Eliminate local variable shadowing We can just make use of the instance in the scope above this one. --- src/video_core/buffer_cache/buffer_cache.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 308d8b55f..bae1d527c 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -47,7 +47,7 @@ public: bool is_written = false, bool use_fast_cbuf = false) { std::lock_guard lock{mutex}; - const auto& memory_manager = system.GPU().MemoryManager(); + auto& memory_manager = system.GPU().MemoryManager(); const std::optional cpu_addr_opt = memory_manager.GpuToCpuAddress(gpu_addr); if (!cpu_addr_opt) { return {GetEmptyBuffer(size), 0}; @@ -59,7 +59,6 @@ public: constexpr std::size_t max_stream_size = 0x800; if (use_fast_cbuf || size < max_stream_size) { if (!is_written && !IsRegionWritten(cpu_addr, cpu_addr + size - 1)) { - auto& memory_manager = system.GPU().MemoryManager(); const bool is_granular = memory_manager.IsGranularRange(gpu_addr, size); if (use_fast_cbuf) { u8* dest; From 811bff009eca0d0fa2ddb1455fc73fdaec4474da Mon Sep 17 00:00:00 2001 From: Lioncash Date: Fri, 19 Jun 2020 21:57:41 -0400 Subject: [PATCH 042/252] macro_jit_x64: Eliminate variable shadowing in Compile_ProcessResult() We can reduce the capture scope so that it's not possible for both "reg" variables to clash with one another. While we're at it, we can prevent unnecessary copies while we're at it. --- src/video_core/macro/macro_jit_x64.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/video_core/macro/macro_jit_x64.cpp b/src/video_core/macro/macro_jit_x64.cpp index bee34a7c0..9eface47e 100644 --- a/src/video_core/macro/macro_jit_x64.cpp +++ b/src/video_core/macro/macro_jit_x64.cpp @@ -546,7 +546,7 @@ Xbyak::Reg32 MacroJITx64Impl::Compile_GetRegister(u32 index, Xbyak::Reg32 dst) { } void MacroJITx64Impl::Compile_ProcessResult(Macro::ResultOperation operation, u32 reg) { - auto SetRegister = [=](u32 reg, Xbyak::Reg32 result) { + const auto SetRegister = [this](u32 reg, const Xbyak::Reg32& result) { // Register 0 is supposed to always return 0. NOP is implemented as a store to the zero // register. if (reg == 0) { @@ -554,7 +554,7 @@ void MacroJITx64Impl::Compile_ProcessResult(Macro::ResultOperation operation, u3 } mov(dword[STATE + offsetof(JITState, registers) + reg * sizeof(u32)], result); }; - auto SetMethodAddress = [=](Xbyak::Reg32 reg) { mov(METHOD_ADDRESS, reg); }; + const auto SetMethodAddress = [this](const Xbyak::Reg32& reg) { mov(METHOD_ADDRESS, reg); }; switch (operation) { case Macro::ResultOperation::IgnoreAndFetch: From 479605b3e5a3b88128455b8357da471c713d0f90 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Fri, 19 Jun 2020 22:02:56 -0400 Subject: [PATCH 043/252] memory_manager: Eliminate variable shadowing Renames some variables to prevent ones in inner scopes from shadowing outer-scoped variables. The Copy* functions have no shadowing, but we rename them anyways to remain consistent with the other functions. --- src/video_core/memory_manager.cpp | 40 +++++++++++++++++-------------- src/video_core/memory_manager.h | 12 +++++----- 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index dbee9f634..ff5505d12 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp @@ -210,10 +210,11 @@ bool MemoryManager::IsBlockContinuous(const GPUVAddr start, const std::size_t si return range == inner_size; } -void MemoryManager::ReadBlock(GPUVAddr src_addr, void* dest_buffer, const std::size_t size) const { +void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, + const std::size_t size) const { std::size_t remaining_size{size}; - std::size_t page_index{src_addr >> page_bits}; - std::size_t page_offset{src_addr & page_mask}; + std::size_t page_index{gpu_src_addr >> page_bits}; + std::size_t page_offset{gpu_src_addr & page_mask}; auto& memory = system.Memory(); @@ -234,11 +235,11 @@ void MemoryManager::ReadBlock(GPUVAddr src_addr, void* dest_buffer, const std::s } } -void MemoryManager::ReadBlockUnsafe(GPUVAddr src_addr, void* dest_buffer, +void MemoryManager::ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer, const std::size_t size) const { std::size_t remaining_size{size}; - std::size_t page_index{src_addr >> page_bits}; - std::size_t page_offset{src_addr & page_mask}; + std::size_t page_index{gpu_src_addr >> page_bits}; + std::size_t page_offset{gpu_src_addr & page_mask}; auto& memory = system.Memory(); @@ -259,10 +260,11 @@ void MemoryManager::ReadBlockUnsafe(GPUVAddr src_addr, void* dest_buffer, } } -void MemoryManager::WriteBlock(GPUVAddr dest_addr, const void* src_buffer, const std::size_t size) { +void MemoryManager::WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, + const std::size_t size) { std::size_t remaining_size{size}; - std::size_t page_index{dest_addr >> page_bits}; - std::size_t page_offset{dest_addr & page_mask}; + std::size_t page_index{gpu_dest_addr >> page_bits}; + std::size_t page_offset{gpu_dest_addr & page_mask}; auto& memory = system.Memory(); @@ -283,11 +285,11 @@ void MemoryManager::WriteBlock(GPUVAddr dest_addr, const void* src_buffer, const } } -void MemoryManager::WriteBlockUnsafe(GPUVAddr dest_addr, const void* src_buffer, +void MemoryManager::WriteBlockUnsafe(GPUVAddr gpu_dest_addr, const void* src_buffer, const std::size_t size) { std::size_t remaining_size{size}; - std::size_t page_index{dest_addr >> page_bits}; - std::size_t page_offset{dest_addr & page_mask}; + std::size_t page_index{gpu_dest_addr >> page_bits}; + std::size_t page_offset{gpu_dest_addr & page_mask}; auto& memory = system.Memory(); @@ -306,16 +308,18 @@ void MemoryManager::WriteBlockUnsafe(GPUVAddr dest_addr, const void* src_buffer, } } -void MemoryManager::CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, const std::size_t size) { +void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, + const std::size_t size) { std::vector tmp_buffer(size); - ReadBlock(src_addr, tmp_buffer.data(), size); - WriteBlock(dest_addr, tmp_buffer.data(), size); + ReadBlock(gpu_src_addr, tmp_buffer.data(), size); + WriteBlock(gpu_dest_addr, tmp_buffer.data(), size); } -void MemoryManager::CopyBlockUnsafe(GPUVAddr dest_addr, GPUVAddr src_addr, const std::size_t size) { +void MemoryManager::CopyBlockUnsafe(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, + const std::size_t size) { std::vector tmp_buffer(size); - ReadBlockUnsafe(src_addr, tmp_buffer.data(), size); - WriteBlockUnsafe(dest_addr, tmp_buffer.data(), size); + ReadBlockUnsafe(gpu_src_addr, tmp_buffer.data(), size); + WriteBlockUnsafe(gpu_dest_addr, tmp_buffer.data(), size); } bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) { diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index 0ddd52d5a..87658e87a 100644 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h @@ -79,9 +79,9 @@ public: * in the Host Memory counterpart. Note: This functions cause Host GPU Memory * Flushes and Invalidations, respectively to each operation. */ - void ReadBlock(GPUVAddr src_addr, void* dest_buffer, std::size_t size) const; - void WriteBlock(GPUVAddr dest_addr, const void* src_buffer, std::size_t size); - void CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, std::size_t size); + void ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const; + void WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size); + void CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size); /** * ReadBlockUnsafe and WriteBlockUnsafe are special versions of ReadBlock and @@ -93,9 +93,9 @@ public: * WriteBlockUnsafe instead of WriteBlock since it shouldn't invalidate the texture * being flushed. */ - void ReadBlockUnsafe(GPUVAddr src_addr, void* dest_buffer, std::size_t size) const; - void WriteBlockUnsafe(GPUVAddr dest_addr, const void* src_buffer, std::size_t size); - void CopyBlockUnsafe(GPUVAddr dest_addr, GPUVAddr src_addr, std::size_t size); + void ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const; + void WriteBlockUnsafe(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size); + void CopyBlockUnsafe(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size); /** * IsGranularRange checks if a gpu region can be simply read with a pointer From a6e5b84d1fbfa976819645d8b7234d847756fc88 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Fri, 19 Jun 2020 23:01:56 -0400 Subject: [PATCH 044/252] vulkan/wrapper: Remove noexcept from GetSurfaceCapabilitiesKHR() Check() can throw an exception if the Vulkan result isn't successful. We remove the check so that std::terminate isn't outright called and allows for better debugging (should it ever actually fail). --- src/video_core/renderer_vulkan/wrapper.cpp | 3 +-- src/video_core/renderer_vulkan/wrapper.h | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/video_core/renderer_vulkan/wrapper.cpp b/src/video_core/renderer_vulkan/wrapper.cpp index 2ce9b0626..42eff85d3 100644 --- a/src/video_core/renderer_vulkan/wrapper.cpp +++ b/src/video_core/renderer_vulkan/wrapper.cpp @@ -725,8 +725,7 @@ bool PhysicalDevice::GetSurfaceSupportKHR(u32 queue_family_index, VkSurfaceKHR s return supported == VK_TRUE; } -VkSurfaceCapabilitiesKHR PhysicalDevice::GetSurfaceCapabilitiesKHR(VkSurfaceKHR surface) const - noexcept { +VkSurfaceCapabilitiesKHR PhysicalDevice::GetSurfaceCapabilitiesKHR(VkSurfaceKHR surface) const { VkSurfaceCapabilitiesKHR capabilities; Check(dld->vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, surface, &capabilities)); return capabilities; diff --git a/src/video_core/renderer_vulkan/wrapper.h b/src/video_core/renderer_vulkan/wrapper.h index 98937a77a..da42ca88e 100644 --- a/src/video_core/renderer_vulkan/wrapper.h +++ b/src/video_core/renderer_vulkan/wrapper.h @@ -779,7 +779,7 @@ public: bool GetSurfaceSupportKHR(u32 queue_family_index, VkSurfaceKHR) const; - VkSurfaceCapabilitiesKHR GetSurfaceCapabilitiesKHR(VkSurfaceKHR) const noexcept; + VkSurfaceCapabilitiesKHR GetSurfaceCapabilitiesKHR(VkSurfaceKHR) const; std::vector GetSurfaceFormatsKHR(VkSurfaceKHR) const; From 480e1fa987ce427ce3208a49ae3f08494c417c5c Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Sun, 14 Jun 2020 00:02:42 -0400 Subject: [PATCH 045/252] decode/image: Implement B10G11R11F - Used by Kirby Star Allies --- src/video_core/shader/decode/image.cpp | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/video_core/shader/decode/image.cpp b/src/video_core/shader/decode/image.cpp index 60b6ad72a..07778dc3e 100644 --- a/src/video_core/shader/decode/image.cpp +++ b/src/video_core/shader/decode/image.cpp @@ -97,6 +97,7 @@ ComponentType GetComponentType(Tegra::Engines::SamplerDescriptor descriptor, break; case TextureFormat::B5G6R5: case TextureFormat::B6G5R5: + case TextureFormat::BF10GF11RF11: if (component == 0) { return descriptor.b_type; } @@ -119,7 +120,7 @@ ComponentType GetComponentType(Tegra::Engines::SamplerDescriptor descriptor, } break; } - UNIMPLEMENTED_MSG("texture format not implement={}", format); + UNIMPLEMENTED_MSG("Texture format not implemented={}", format); return ComponentType::FLOAT; } @@ -191,6 +192,14 @@ u32 GetComponentSize(TextureFormat format, std::size_t component) { return 6; } return 0; + case TextureFormat::BF10GF11RF11: + if (component == 1 || component == 2) { + return 11; + } + if (component == 0) { + return 10; + } + return 0; case TextureFormat::G8R24: if (component == 0) { return 8; @@ -211,10 +220,9 @@ u32 GetComponentSize(TextureFormat format, std::size_t component) { return (component == 0 || component == 1) ? 8 : 0; case TextureFormat::G4R4: return (component == 0 || component == 1) ? 4 : 0; - default: - UNIMPLEMENTED_MSG("texture format not implement={}", format); - return 0; } + UNIMPLEMENTED_MSG("Texture format not implemented={}", format); + return 0; } std::size_t GetImageComponentMask(TextureFormat format) { @@ -235,6 +243,7 @@ std::size_t GetImageComponentMask(TextureFormat format) { case TextureFormat::R32_B24G8: case TextureFormat::B5G6R5: case TextureFormat::B6G5R5: + case TextureFormat::BF10GF11RF11: return std::size_t{R | G | B}; case TextureFormat::R32_G32: case TextureFormat::R16_G16: @@ -248,10 +257,9 @@ std::size_t GetImageComponentMask(TextureFormat format) { case TextureFormat::R8: case TextureFormat::R1: return std::size_t{R}; - default: - UNIMPLEMENTED_MSG("texture format not implement={}", format); - return std::size_t{R | G | B | A}; } + UNIMPLEMENTED_MSG("Texture format not implemented={}", format); + return std::size_t{R | G | B | A}; } std::size_t GetImageTypeNumCoordinates(Tegra::Shader::ImageType image_type) { @@ -299,7 +307,7 @@ std::pair ShaderIR::GetComponentValue(ComponentType component_type, return {std::move(original_value), true}; } default: - UNIMPLEMENTED_MSG("Unimplement component type={}", component_type); + UNIMPLEMENTED_MSG("Unimplemented component type={}", component_type); return {std::move(original_value), true}; } } @@ -459,7 +467,7 @@ u32 ShaderIR::DecodeImage(NodeBlock& bb, u32 pc) { default: break; } - UNIMPLEMENTED_MSG("Unimplemented operation={} type={}", + UNIMPLEMENTED_MSG("Unimplemented operation={}, type={}", static_cast(instr.suatom_d.operation.Value()), static_cast(instr.suatom_d.operation_type.Value())); return OperationCode::AtomicImageAdd; From d6474b4aca7f054d00df350c716709475ef0f49b Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Sat, 16 May 2020 07:24:57 -0400 Subject: [PATCH 046/252] common/cpu_detect: Add AVX512 detection --- src/common/x64/cpu_detect.cpp | 5 +++++ src/common/x64/cpu_detect.h | 1 + 2 files changed, 6 insertions(+) diff --git a/src/common/x64/cpu_detect.cpp b/src/common/x64/cpu_detect.cpp index c9349a6b4..f35dcb498 100644 --- a/src/common/x64/cpu_detect.cpp +++ b/src/common/x64/cpu_detect.cpp @@ -110,6 +110,11 @@ static CPUCaps Detect() { caps.bmi1 = true; if ((cpu_id[1] >> 8) & 1) caps.bmi2 = true; + // Checks for AVX512F, AVX512CD, AVX512VL, AVX512DQ, AVX512BW (Intel Skylake-X/SP) + if ((cpu_id[1] >> 16) & 1 && (cpu_id[1] >> 28) & 1 && (cpu_id[1] >> 31) & 1 && + (cpu_id[1] >> 17) & 1 && (cpu_id[1] >> 30) & 1) { + caps.avx512 = caps.avx2; + } } } diff --git a/src/common/x64/cpu_detect.h b/src/common/x64/cpu_detect.h index 20f2ba234..7606c3f7b 100644 --- a/src/common/x64/cpu_detect.h +++ b/src/common/x64/cpu_detect.h @@ -19,6 +19,7 @@ struct CPUCaps { bool lzcnt; bool avx; bool avx2; + bool avx512; bool bmi1; bool bmi2; bool fma; From 97ba520434cceb42af3b17a59c731dd734e9108f Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Sat, 16 May 2020 07:25:13 -0400 Subject: [PATCH 047/252] common/telemetry: Add AVX512 to telemetry --- src/common/telemetry.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/common/telemetry.cpp b/src/common/telemetry.cpp index 200c6489a..16d42facd 100644 --- a/src/common/telemetry.cpp +++ b/src/common/telemetry.cpp @@ -60,6 +60,7 @@ void AppendCPUInfo(FieldCollection& fc) { fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AES", Common::GetCPUCaps().aes); fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX", Common::GetCPUCaps().avx); fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX2", Common::GetCPUCaps().avx2); + fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX512", Common::GetCPUCaps().avx512); fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_BMI1", Common::GetCPUCaps().bmi1); fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_BMI2", Common::GetCPUCaps().bmi2); fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_FMA", Common::GetCPUCaps().fma); From 9bb5bf0b2bf62207c117f05aa76d0540b6e106fd Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Sat, 16 May 2020 07:35:15 -0400 Subject: [PATCH 048/252] main: Append AVX and FMA instructions to cpu string Append AVX and FMA instructions to cpu string if the host cpu supports them --- src/yuzu/main.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 4119d7907..059b96e70 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -217,7 +217,20 @@ GMainWindow::GMainWindow() LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", yuzu_build_version, Common::g_scm_branch, Common::g_scm_desc); #ifdef ARCHITECTURE_x86_64 - LOG_INFO(Frontend, "Host CPU: {}", Common::GetCPUCaps().cpu_string); + const auto& caps = Common::GetCPUCaps(); + std::string cpu_string = caps.cpu_string; + if (caps.avx || caps.avx2 || caps.avx512) { + cpu_string += " | AVX"; + if (caps.avx512) { + cpu_string += "512"; + } else if (caps.avx2) { + cpu_string += '2'; + } + if (caps.fma || caps.fma4) { + cpu_string += " | FMA"; + } + } + LOG_INFO(Frontend, "Host CPU: {}", cpu_string); #endif LOG_INFO(Frontend, "Host OS: {}", QSysInfo::prettyProductName().toStdString()); LOG_INFO(Frontend, "Host RAM: {:.2f} GB", From a5ed0c3df7218f922f98d4bd1fed1214ec728869 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Sat, 20 Jun 2020 01:06:05 -0400 Subject: [PATCH 049/252] software_keyboard: Eliminate trivial redundant copies We can just make use of moves here to get rid of two redundant copies --- src/core/hle/service/am/applets/software_keyboard.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp index 54e63c138..d14076b02 100644 --- a/src/core/hle/service/am/applets/software_keyboard.cpp +++ b/src/core/hle/service/am/applets/software_keyboard.cpp @@ -30,7 +30,7 @@ static Core::Frontend::SoftwareKeyboardParameters ConvertToFrontendParameters( config.sub_text.size()); params.guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(config.guide_text.data(), config.guide_text.size()); - params.initial_text = initial_text; + params.initial_text = std::move(initial_text); params.max_length = config.length_limit == 0 ? DEFAULT_MAX_LENGTH : config.length_limit; params.password = static_cast(config.is_password); params.cursor_at_beginning = static_cast(config.initial_cursor_position); @@ -109,7 +109,7 @@ void SoftwareKeyboard::Execute() { const auto parameters = ConvertToFrontendParameters(config, initial_text); - frontend.RequestText([this](std::optional text) { WriteText(text); }, + frontend.RequestText([this](std::optional text) { WriteText(std::move(text)); }, parameters); } From ef53b2fd08f1122f22456500bfdc707f1c18906c Mon Sep 17 00:00:00 2001 From: Lioncash Date: Fri, 19 Jun 2020 23:13:48 -0400 Subject: [PATCH 050/252] texture_cache: Fix incorrect address used in a DeduceSurface() call Previously the source was being deduced twice in a row. --- src/video_core/texture_cache/texture_cache.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index b543fc8c0..85075e868 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -1053,7 +1053,7 @@ private: void DeduceBestBlit(SurfaceParams& src_params, SurfaceParams& dst_params, const GPUVAddr src_gpu_addr, const GPUVAddr dst_gpu_addr) { auto deduced_src = DeduceSurface(src_gpu_addr, src_params); - auto deduced_dst = DeduceSurface(src_gpu_addr, src_params); + auto deduced_dst = DeduceSurface(dst_gpu_addr, dst_params); if (deduced_src.Failed() || deduced_dst.Failed()) { return; } From a8674a7b8618b1f17b7abb8908ea7cd4367aa730 Mon Sep 17 00:00:00 2001 From: FearlessTobi Date: Sun, 21 Jun 2020 03:11:23 +0200 Subject: [PATCH 051/252] Fix: fatal error CVT1100 when compiling manifest file Occurs when doing a local compile in MSVC build. The compiler I'm using is as below: Microsoft Visual Studio Community 2019 Preview Version 16.6.0 Preview 5.0 Fixes this error: CVTRES : fatal error CVT1100: duplicate resource. type:MANIFEST, name:1, language:0x0409 LINK : fatal error LNK1123: failure during conversion to COFF: file invalid or corrupt I have put 0 since previous name was 1. If have other names in mind, please let me know. Co-Authored-By: dragios --- src/yuzu/yuzu.rc | 2 +- src/yuzu_cmd/yuzu.rc | 2 +- src/yuzu_tester/yuzu.rc | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/yuzu/yuzu.rc b/src/yuzu/yuzu.rc index 1b253653f..4a3645a71 100644 --- a/src/yuzu/yuzu.rc +++ b/src/yuzu/yuzu.rc @@ -16,4 +16,4 @@ IDI_ICON1 ICON "../../dist/yuzu.ico" // RT_MANIFEST // -1 RT_MANIFEST "../../dist/yuzu.manifest" +0 RT_MANIFEST "../../dist/yuzu.manifest" diff --git a/src/yuzu_cmd/yuzu.rc b/src/yuzu_cmd/yuzu.rc index 7de8ef3d9..0cde75e2f 100644 --- a/src/yuzu_cmd/yuzu.rc +++ b/src/yuzu_cmd/yuzu.rc @@ -14,4 +14,4 @@ YUZU_ICON ICON "../../dist/yuzu.ico" // RT_MANIFEST // -1 RT_MANIFEST "../../dist/yuzu.manifest" +0 RT_MANIFEST "../../dist/yuzu.manifest" diff --git a/src/yuzu_tester/yuzu.rc b/src/yuzu_tester/yuzu.rc index 7de8ef3d9..0cde75e2f 100644 --- a/src/yuzu_tester/yuzu.rc +++ b/src/yuzu_tester/yuzu.rc @@ -14,4 +14,4 @@ YUZU_ICON ICON "../../dist/yuzu.ico" // RT_MANIFEST // -1 RT_MANIFEST "../../dist/yuzu.manifest" +0 RT_MANIFEST "../../dist/yuzu.manifest" From 20ed33b53b29ef29c0c9578a6ac630bb81717b80 Mon Sep 17 00:00:00 2001 From: FearlessTobi Date: Sun, 21 Jun 2020 03:16:12 +0200 Subject: [PATCH 052/252] Update manifest file to include new elements that are introduced with Windows 10 later versions Co-Authored-By: dragios --- dist/yuzu.manifest | 80 +++++++++++++++++++++++++++++++++------------- 1 file changed, 57 insertions(+), 23 deletions(-) diff --git a/dist/yuzu.manifest b/dist/yuzu.manifest index fd30b656f..038edff23 100644 --- a/dist/yuzu.manifest +++ b/dist/yuzu.manifest @@ -1,24 +1,58 @@ - - - - - - - - - - - True/PM - true - - - - - - - - - - - \ No newline at end of file + + + + + + true/pm + + + + PerMonitorV2 + + + + true + + + true + + + + + + + + + + + + + + + + + + + + + + + + From 1e65da971bf6edd5611e6e409ba1cc4f99e58655 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Sat, 20 Jun 2020 07:41:55 -0400 Subject: [PATCH 053/252] gl_device: Check for GL_EXT_texture_shadow_lod --- src/video_core/renderer_opengl/gl_device.cpp | 2 ++ src/video_core/renderer_opengl/gl_device.h | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp index b31d604e4..1011c7738 100644 --- a/src/video_core/renderer_opengl/gl_device.cpp +++ b/src/video_core/renderer_opengl/gl_device.cpp @@ -216,6 +216,7 @@ Device::Device() has_shader_ballot = GLAD_GL_ARB_shader_ballot; has_vertex_viewport_layer = GLAD_GL_ARB_shader_viewport_layer_array; has_image_load_formatted = HasExtension(extensions, "GL_EXT_shader_image_load_formatted"); + has_texture_shadow_lod = HasExtension(extensions, "GL_EXT_texture_shadow_lod"); has_astc = IsASTCSupported(); has_variable_aoffi = TestVariableAoffi(); has_component_indexing_bug = is_amd; @@ -245,6 +246,7 @@ Device::Device(std::nullptr_t) { has_shader_ballot = true; has_vertex_viewport_layer = true; has_image_load_formatted = true; + has_texture_shadow_lod = true; has_variable_aoffi = true; } diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h index 145347943..c86e709b1 100644 --- a/src/video_core/renderer_opengl/gl_device.h +++ b/src/video_core/renderer_opengl/gl_device.h @@ -68,6 +68,10 @@ public: return has_image_load_formatted; } + bool HasTextureShadowLod() const { + return has_texture_shadow_lod; + } + bool HasASTC() const { return has_astc; } @@ -110,6 +114,7 @@ private: bool has_shader_ballot{}; bool has_vertex_viewport_layer{}; bool has_image_load_formatted{}; + bool has_texture_shadow_lod{}; bool has_astc{}; bool has_variable_aoffi{}; bool has_component_indexing_bug{}; From f77c897b8d5287adb64e08f65e494dac45033de3 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Sat, 20 Jun 2020 07:43:04 -0400 Subject: [PATCH 054/252] gl_shader_decompiler: Enable GL_EXT_texture_shadow_lod if available Enable GL_EXT_texture_shadow_lod if available. If this extension is not available, such as on Intel/AMD proprietary drivers, use textureGrad as a workaround. --- .../renderer_opengl/gl_shader_decompiler.cpp | 50 ++++++++++++++++--- 1 file changed, 43 insertions(+), 7 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index d6e30b321..2c49aeaac 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -37,6 +37,7 @@ using Tegra::Shader::IpaMode; using Tegra::Shader::IpaSampleMode; using Tegra::Shader::PixelImap; using Tegra::Shader::Register; +using Tegra::Shader::TextureType; using VideoCommon::Shader::BuildTransformFeedback; using VideoCommon::Shader::Registry; @@ -526,6 +527,9 @@ private: if (device.HasImageLoadFormatted()) { code.AddLine("#extension GL_EXT_shader_image_load_formatted : require"); } + if (device.HasTextureShadowLod()) { + code.AddLine("#extension GL_EXT_texture_shadow_lod : require"); + } if (device.HasWarpIntrinsics()) { code.AddLine("#extension GL_NV_gpu_shader5 : require"); code.AddLine("#extension GL_NV_shader_thread_group : require"); @@ -909,13 +913,13 @@ private: return "samplerBuffer"; } switch (sampler.type) { - case Tegra::Shader::TextureType::Texture1D: + case TextureType::Texture1D: return "sampler1D"; - case Tegra::Shader::TextureType::Texture2D: + case TextureType::Texture2D: return "sampler2D"; - case Tegra::Shader::TextureType::Texture3D: + case TextureType::Texture3D: return "sampler3D"; - case Tegra::Shader::TextureType::TextureCube: + case TextureType::TextureCube: return "samplerCube"; default: UNREACHABLE(); @@ -1380,8 +1384,19 @@ private: const std::size_t count = operation.GetOperandsCount(); const bool has_array = meta->sampler.is_array; const bool has_shadow = meta->sampler.is_shadow; + const bool workaround_lod_array_shadow_as_grad = + !device.HasTextureShadowLod() && function_suffix == "Lod" && meta->sampler.is_shadow && + ((meta->sampler.type == TextureType::Texture2D && meta->sampler.is_array) || + meta->sampler.type == TextureType::TextureCube); + + std::string expr = "texture"; + + if (workaround_lod_array_shadow_as_grad) { + expr += "Grad"; + } else { + expr += function_suffix; + } - std::string expr = "texture" + function_suffix; if (!meta->aoffi.empty()) { expr += "Offset"; } else if (!meta->ptp.empty()) { @@ -1415,6 +1430,16 @@ private: expr += ')'; } + if (workaround_lod_array_shadow_as_grad) { + switch (meta->sampler.type) { + case TextureType::Texture2D: + return expr + ", vec2(0.0), vec2(0.0))"; + case TextureType::TextureCube: + return expr + ", vec3(0.0), vec3(0.0))"; + } + UNREACHABLE(); + } + for (const auto& variant : extras) { if (const auto argument = std::get_if(&variant)) { expr += GenerateTextureArgument(*argument); @@ -2041,8 +2066,19 @@ private: const auto meta = std::get_if(&operation.GetMeta()); ASSERT(meta); - std::string expr = GenerateTexture( - operation, "Lod", {TextureArgument{Type::Float, meta->lod}, TextureOffset{}}); + std::string expr{}; + + if (!device.HasTextureShadowLod() && meta->sampler.is_shadow && + ((meta->sampler.type == TextureType::Texture2D && meta->sampler.is_array) || + meta->sampler.type == TextureType::TextureCube)) { + LOG_ERROR(Render_OpenGL, + "Device lacks GL_EXT_texture_shadow_lod, using textureGrad as a workaround"); + expr = GenerateTexture(operation, "Lod", {}); + } else { + expr = GenerateTexture(operation, "Lod", + {TextureArgument{Type::Float, meta->lod}, TextureOffset{}}); + } + if (meta->sampler.is_shadow) { expr = "vec4(" + expr + ')'; } From b81af6ae9bdbf7edcdc6c1ec4f68d5e29ad18c5f Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Sun, 21 Jun 2020 06:09:28 +0200 Subject: [PATCH 055/252] Add a "Open Mods Page" button to the GUI --- src/yuzu/main.cpp | 13 +++++++++++++ src/yuzu/main.h | 1 + src/yuzu/main.ui | 6 ++++++ 3 files changed, 20 insertions(+) diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 270cccc77..826f8903b 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -57,6 +57,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include #include #include +#include #include #include "common/common_paths.h" @@ -826,6 +827,7 @@ void GMainWindow::ConnectMenuEvents() { connect(ui.action_Stop, &QAction::triggered, this, &GMainWindow::OnStopGame); connect(ui.action_Report_Compatibility, &QAction::triggered, this, &GMainWindow::OnMenuReportCompatibility); + connect(ui.action_Open_Mods_Page, &QAction::triggered, this, &GMainWindow::OnSwitchModsPage); connect(ui.action_Restart, &QAction::triggered, this, [this] { BootGame(QString(game_path)); }); connect(ui.action_Configure, &QAction::triggered, this, &GMainWindow::OnConfigure); @@ -1797,6 +1799,17 @@ void GMainWindow::OnMenuReportCompatibility() { } } +void GMainWindow::OnSwitchModsPage() { + const std::string mods_page_url = "https://github.com/yuzu-emu/yuzu/wiki/Switch-Mods"; + const QString mods_page_url_qs = QString::fromStdString(mods_page_url); + const QUrl mods_page(mods_page_url_qs); + const bool open = QDesktopServices::openUrl(mods_page); + if (!open) { + QMessageBox::warning(this, tr("Error opening URL"), + tr("Unable to open the URL \"%1\".").arg(mods_page_url_qs)); + } +} + void GMainWindow::ToggleFullscreen() { if (!emulation_running) { return; diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 4f4c8ddbe..7d9910608 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -181,6 +181,7 @@ private slots: void OnPauseGame(); void OnStopGame(); void OnMenuReportCompatibility(); + void OnSwitchModsPage(); /// Called whenever a user selects a game in the game list widget. void OnGameListLoadFile(QString game_path); void OnGameListOpenFolder(GameListOpenTarget target, const std::string& game_path); diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index 97c90f50b..b5745dfd5 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui @@ -113,6 +113,7 @@ &Help + @@ -256,6 +257,11 @@ false + + + Open Mods Page + + Open yuzu Folder From 606e833d26a279dc1621b48b21994f1a6de3bf17 Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Sun, 21 Jun 2020 06:12:23 +0200 Subject: [PATCH 056/252] Address review comment by Lioncash Co-authored-by: LC --- src/yuzu/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 826f8903b..0ce7cd62a 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -1800,7 +1800,7 @@ void GMainWindow::OnMenuReportCompatibility() { } void GMainWindow::OnSwitchModsPage() { - const std::string mods_page_url = "https://github.com/yuzu-emu/yuzu/wiki/Switch-Mods"; + const auto mods_page_url = QStringLiteral("https://github.com/yuzu-emu/yuzu/wiki/Switch-Mods"); const QString mods_page_url_qs = QString::fromStdString(mods_page_url); const QUrl mods_page(mods_page_url_qs); const bool open = QDesktopServices::openUrl(mods_page); From d11b04ed46b2c6720e9bb222e98482097274a1f5 Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Sun, 21 Jun 2020 06:16:03 +0200 Subject: [PATCH 057/252] Remove unnecessary conversion --- src/yuzu/main.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 0ce7cd62a..6e6e5dffe 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -1801,12 +1801,11 @@ void GMainWindow::OnMenuReportCompatibility() { void GMainWindow::OnSwitchModsPage() { const auto mods_page_url = QStringLiteral("https://github.com/yuzu-emu/yuzu/wiki/Switch-Mods"); - const QString mods_page_url_qs = QString::fromStdString(mods_page_url); - const QUrl mods_page(mods_page_url_qs); + const QUrl mods_page(mods_page_url); const bool open = QDesktopServices::openUrl(mods_page); if (!open) { QMessageBox::warning(this, tr("Error opening URL"), - tr("Unable to open the URL \"%1\".").arg(mods_page_url_qs)); + tr("Unable to open the URL \"%1\".").arg(mods_page_url)); } } From 23d57ed4f733731206abcd4d9db7d8bdb34208e7 Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Sun, 21 Jun 2020 06:17:46 +0200 Subject: [PATCH 058/252] Clang-format --- src/yuzu/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 6e6e5dffe..e53c65f81 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -56,8 +56,8 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include #include #include -#include #include +#include #include #include "common/common_paths.h" From 182ac8a504233143fd2f78ef0dc4fb94331cff85 Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Sun, 21 Jun 2020 18:09:14 +0200 Subject: [PATCH 059/252] Correct function name (1/2) --- src/yuzu/main.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 7d9910608..d55e55cc6 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -181,7 +181,7 @@ private slots: void OnPauseGame(); void OnStopGame(); void OnMenuReportCompatibility(); - void OnSwitchModsPage(); + void OnOpenModsPage(); /// Called whenever a user selects a game in the game list widget. void OnGameListLoadFile(QString game_path); void OnGameListOpenFolder(GameListOpenTarget target, const std::string& game_path); From 409fedaf974f6c6add862a518c8fa5e7c8a341b1 Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Sun, 21 Jun 2020 18:10:23 +0200 Subject: [PATCH 060/252] Correct function name (2/2) --- src/yuzu/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index e53c65f81..cb36bb869 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -827,7 +827,7 @@ void GMainWindow::ConnectMenuEvents() { connect(ui.action_Stop, &QAction::triggered, this, &GMainWindow::OnStopGame); connect(ui.action_Report_Compatibility, &QAction::triggered, this, &GMainWindow::OnMenuReportCompatibility); - connect(ui.action_Open_Mods_Page, &QAction::triggered, this, &GMainWindow::OnSwitchModsPage); + connect(ui.action_Open_Mods_Page, &QAction::triggered, this, &GMainWindow::OnOpenModsPage); connect(ui.action_Restart, &QAction::triggered, this, [this] { BootGame(QString(game_path)); }); connect(ui.action_Configure, &QAction::triggered, this, &GMainWindow::OnConfigure); @@ -1799,7 +1799,7 @@ void GMainWindow::OnMenuReportCompatibility() { } } -void GMainWindow::OnSwitchModsPage() { +void GMainWindow::OnOpenModsPage() { const auto mods_page_url = QStringLiteral("https://github.com/yuzu-emu/yuzu/wiki/Switch-Mods"); const QUrl mods_page(mods_page_url); const bool open = QDesktopServices::openUrl(mods_page); From 0235915baaca8c0928a54fae1df60595e451e223 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Sun, 21 Jun 2020 15:48:02 -0400 Subject: [PATCH 061/252] hid: Implement Get/ResetGyroscopeZeroDriftMode - Used by Captain Toad Treasure Tracker --- src/core/hle/service/hid/controllers/npad.cpp | 8 ++++ src/core/hle/service/hid/controllers/npad.h | 10 ++++- src/core/hle/service/hid/hid.cpp | 42 ++++++++++++++++--- src/core/hle/service/hid/hid.h | 2 + 4 files changed, 56 insertions(+), 6 deletions(-) diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index c55d900e2..6fbee7efa 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -566,6 +566,14 @@ void Controller_NPad::DisconnectNPad(u32 npad_id) { connected_controllers[NPadIdToIndex(npad_id)].is_connected = false; } +void Controller_NPad::SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode) { + gyroscope_zero_drift_mode = drift_mode; +} + +Controller_NPad::GyroscopeZeroDriftMode Controller_NPad::GetGyroscopeZeroDriftMode() const { + return gyroscope_zero_drift_mode; +} + void Controller_NPad::StartLRAssignmentMode() { // Nothing internally is used for lr assignment mode. Since we have the ability to set the // controller types from boot, it doesn't really matter about showing a selection screen diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index 931f03430..5d4c58a43 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h @@ -58,6 +58,12 @@ public: }; static_assert(sizeof(Vibration) == 0x10, "Vibration is an invalid size"); + enum class GyroscopeZeroDriftMode : u32 { + Loose = 0, + Standard = 1, + Tight = 2, + }; + enum class NpadHoldType : u64 { Vertical = 0, Horizontal = 1, @@ -117,6 +123,8 @@ public: void ConnectNPad(u32 npad_id); void DisconnectNPad(u32 npad_id); + void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode); + GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const; LedPattern GetLedPattern(u32 npad_id); void SetVibrationEnabled(bool can_vibrate); bool IsVibrationEnabled() const; @@ -324,8 +332,8 @@ private: std::array styleset_changed_events; Vibration last_processed_vibration{}; std::array connected_controllers{}; + GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard}; bool can_controllers_vibrate{true}; - std::array npad_pad_states{}; bool is_in_lr_assignment_mode{false}; Core::System& system; diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 72a050de2..415c2829e 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -185,8 +185,8 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) { {77, nullptr, "GetAccelerometerPlayMode"}, {78, nullptr, "ResetAccelerometerPlayMode"}, {79, &Hid::SetGyroscopeZeroDriftMode, "SetGyroscopeZeroDriftMode"}, - {80, nullptr, "GetGyroscopeZeroDriftMode"}, - {81, nullptr, "ResetGyroscopeZeroDriftMode"}, + {80, &Hid::GetGyroscopeZeroDriftMode, "GetGyroscopeZeroDriftMode"}, + {81, &Hid::ResetGyroscopeZeroDriftMode, "ResetGyroscopeZeroDriftMode"}, {82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"}, {83, nullptr, "IsFirmwareUpdateAvailableForSixAxisSensor"}, {91, &Hid::ActivateGesture, "ActivateGesture"}, @@ -419,9 +419,41 @@ void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { const auto drift_mode{rp.Pop()}; const auto applet_resource_user_id{rp.Pop()}; - LOG_WARNING(Service_HID, - "(STUBBED) called, handle={}, drift_mode={}, applet_resource_user_id={}", handle, - drift_mode, applet_resource_user_id); + applet_resource->GetController(HidController::NPad) + .SetGyroscopeZeroDriftMode(Controller_NPad::GyroscopeZeroDriftMode{drift_mode}); + + LOG_DEBUG(Service_HID, "called, handle={}, drift_mode={}, applet_resource_user_id={}", handle, + drift_mode, applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto handle{rp.Pop()}; + const auto applet_resource_user_id{rp.Pop()}; + + LOG_DEBUG(Service_HID, "called, handle={}, applet_resource_user_id={}", handle, + applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push( + static_cast(applet_resource->GetController(HidController::NPad) + .GetGyroscopeZeroDriftMode())); +} + +void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto handle{rp.Pop()}; + const auto applet_resource_user_id{rp.Pop()}; + + applet_resource->GetController(HidController::NPad) + .SetGyroscopeZeroDriftMode(Controller_NPad::GyroscopeZeroDriftMode::Standard); + + LOG_DEBUG(Service_HID, "called, handle={}, applet_resource_user_id={}", handle, + applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index d481a75f8..41b330fa9 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -95,6 +95,8 @@ private: void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx); void StartSixAxisSensor(Kernel::HLERequestContext& ctx); void SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx); + void GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx); + void ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx); void IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx); void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx); void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx); From 2f09c7ddd314f03da0fbafacfcae6b0a47a209ae Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Mon, 22 Jun 2020 04:10:45 -0300 Subject: [PATCH 062/252] renderer_vulkan: Update validation layer name and test before enabling Update validation layer string to VK_LAYER_KHRONOS_validation. While we are at it, properly check for available validation layers before enabling them. --- .../renderer_vulkan/renderer_vulkan.cpp | 28 ++++++++++++++++--- src/video_core/renderer_vulkan/wrapper.cpp | 16 ++++++++++- src/video_core/renderer_vulkan/wrapper.h | 4 +++ 3 files changed, 43 insertions(+), 5 deletions(-) diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index cd9673d1f..2d9b18ed9 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -155,11 +155,31 @@ vk::Instance CreateInstance(Common::DynamicLibrary& library, vk::InstanceDispatc } } - static constexpr std::array layers_data{"VK_LAYER_LUNARG_standard_validation"}; - vk::Span layers = layers_data; - if (!enable_layers) { - layers = {}; + std::vector layers; + layers.reserve(1); + if (enable_layers) { + layers.push_back("VK_LAYER_KHRONOS_validation"); } + + const std::optional layer_properties = vk::EnumerateInstanceLayerProperties(dld); + if (!layer_properties) { + LOG_ERROR(Render_Vulkan, "Failed to query layer properties, disabling layers"); + layers.clear(); + } + + for (auto layer_it = layers.begin(); layer_it != layers.end();) { + const char* const layer = *layer_it; + const auto it = std::find_if( + layer_properties->begin(), layer_properties->end(), + [layer](const VkLayerProperties& prop) { return !std::strcmp(layer, prop.layerName); }); + if (it == layer_properties->end()) { + LOG_ERROR(Render_Vulkan, "Layer {} not available, removing it", layer); + layer_it = layers.erase(layer_it); + } else { + ++layer_it; + } + } + vk::Instance instance = vk::Instance::Create(layers, extensions, dld); if (!instance) { LOG_ERROR(Render_Vulkan, "Failed to create Vulkan instance"); diff --git a/src/video_core/renderer_vulkan/wrapper.cpp b/src/video_core/renderer_vulkan/wrapper.cpp index 42eff85d3..0d485a662 100644 --- a/src/video_core/renderer_vulkan/wrapper.cpp +++ b/src/video_core/renderer_vulkan/wrapper.cpp @@ -153,7 +153,8 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { bool Load(InstanceDispatch& dld) noexcept { #define X(name) Proc(dld.name, dld, #name) - return X(vkCreateInstance) && X(vkEnumerateInstanceExtensionProperties); + return X(vkCreateInstance) && X(vkEnumerateInstanceExtensionProperties) && + X(vkEnumerateInstanceLayerProperties); #undef X } @@ -770,4 +771,17 @@ std::optional> EnumerateInstanceExtensionProp return properties; } +std::optional> EnumerateInstanceLayerProperties( + const InstanceDispatch& dld) { + u32 num; + if (dld.vkEnumerateInstanceLayerProperties(&num, nullptr) != VK_SUCCESS) { + return std::nullopt; + } + std::vector properties(num); + if (dld.vkEnumerateInstanceLayerProperties(&num, properties.data()) != VK_SUCCESS) { + return std::nullopt; + } + return properties; +} + } // namespace Vulkan::vk diff --git a/src/video_core/renderer_vulkan/wrapper.h b/src/video_core/renderer_vulkan/wrapper.h index da42ca88e..d56fdb3f9 100644 --- a/src/video_core/renderer_vulkan/wrapper.h +++ b/src/video_core/renderer_vulkan/wrapper.h @@ -141,6 +141,7 @@ struct InstanceDispatch { PFN_vkCreateInstance vkCreateInstance; PFN_vkDestroyInstance vkDestroyInstance; PFN_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties; + PFN_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties; PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT; PFN_vkCreateDevice vkCreateDevice; @@ -996,4 +997,7 @@ private: std::optional> EnumerateInstanceExtensionProperties( const InstanceDispatch& dld); +std::optional> EnumerateInstanceLayerProperties( + const InstanceDispatch& dld); + } // namespace Vulkan::vk From e0af4cdf9871e42cb3aaef950b3505b5af6b679e Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Mon, 22 Jun 2020 06:59:41 -0400 Subject: [PATCH 063/252] arm_dynarmic_32: Log under Core_ARM instead of HW_GPU --- src/core/arm/dynarmic/arm_dynarmic_32.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index 19d798dc7..4c8663d03 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -62,7 +62,7 @@ public: case Dynarmic::A32::Exception::Breakpoint: break; } - LOG_CRITICAL(HW_GPU, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", + LOG_CRITICAL(Core_ARM, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", static_cast(exception), pc, MemoryReadCode(pc)); UNIMPLEMENTED(); } From f2df941e8dbecbb0c3240039f637ae749f633eb6 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Mon, 22 Jun 2020 07:00:24 -0400 Subject: [PATCH 064/252] arm_dynarmic_64: Log the instruction when an exception is raised --- src/core/arm/dynarmic/arm_dynarmic_64.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 337b97be9..5f5e36d94 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -98,8 +98,8 @@ public: } [[fallthrough]]; default: - ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:X})", - static_cast(exception), pc); + ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", + static_cast(exception), pc, MemoryReadCode(pc)); } } From 8cf6efe6776d8c931ede4e5e74b25b70e3619903 Mon Sep 17 00:00:00 2001 From: unknown <27208977+FreddyFunk@users.noreply.github.com> Date: Mon, 22 Jun 2020 15:56:41 +0200 Subject: [PATCH 065/252] Reorder variables to comply with the Auzure build pipeline --- src/yuzu/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index f552b9401..42bbf8b35 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -2015,8 +2015,8 @@ void GMainWindow::UpdateWindowTitle(const std::string& title_name, } else { const auto fmt = std::string(Common::g_title_bar_format_running); setWindowTitle(QString::fromStdString( - fmt::format(fmt.empty() ? "yuzu {0}| {3} | {4} | {1}-{2}" : fmt, full_name, branch_name, - description, title_name, title_version, date, build_id))); + fmt::format(fmt.empty() ? "yuzu {0}| {3} | {6} | {1}-{2}" : fmt, full_name, branch_name, + description, title_name, date, build_id, title_version))); } } From 544b15e8e415d56b415189717805a88b2e5dc06f Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Mon, 22 Jun 2020 11:29:55 -0400 Subject: [PATCH 066/252] TextureCache: Fix case where layer goes off bound. The returned layer is expected to be between 0 and the depth of the surface, anything larger is off bounds. --- src/video_core/texture_cache/surface_base.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/video_core/texture_cache/surface_base.cpp b/src/video_core/texture_cache/surface_base.cpp index 94d3a6ae5..0caf3b4f0 100644 --- a/src/video_core/texture_cache/surface_base.cpp +++ b/src/video_core/texture_cache/surface_base.cpp @@ -120,6 +120,9 @@ std::optional> SurfaceBaseImpl::GetLayerMipmap( } const auto relative_address{static_cast(candidate_gpu_addr - gpu_addr)}; const auto layer{static_cast(relative_address / layer_size)}; + if (layer >= params.depth) { + return {}; + } const GPUVAddr mipmap_address = relative_address - layer_size * layer; const auto mipmap_it = Common::BinaryFind(mipmap_offsets.begin(), mipmap_offsets.end(), mipmap_address); From e193aa3f53a09850606145466d6caa9abd3fb856 Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Mon, 22 Jun 2020 22:03:26 +0200 Subject: [PATCH 067/252] account: Update function tables and add missing classes (#4145) * account: Update function tables and add missing classes * clang-format * Add missing "public" * Add missing public again * Add missing final --- src/core/hle/service/acc/acc.cpp | 341 ++++++++++++++++++++++++++++ src/core/hle/service/acc/acc_aa.cpp | 4 +- src/core/hle/service/acc/acc_su.cpp | 34 +-- src/core/hle/service/acc/acc_u0.cpp | 18 +- src/core/hle/service/acc/acc_u1.cpp | 29 +-- 5 files changed, 384 insertions(+), 42 deletions(-) diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index 630a8b048..94d8c1fc6 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp @@ -44,6 +44,218 @@ static constexpr u32 SanitizeJPEGSize(std::size_t size) { return static_cast(std::min(size, max_jpeg_image_size)); } +class IManagerForSystemService final : public ServiceFramework { +public: + explicit IManagerForSystemService(Common::UUID user_id) + : ServiceFramework("IManagerForSystemService") { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "CheckAvailability"}, + {1, nullptr, "GetAccountId"}, + {2, nullptr, "EnsureIdTokenCacheAsync"}, + {3, nullptr, "LoadIdTokenCache"}, + {100, nullptr, "SetSystemProgramIdentification"}, + {101, nullptr, "RefreshNotificationTokenAsync"}, // 7.0.0+ + {110, nullptr, "GetServiceEntryRequirementCache"}, // 4.0.0+ + {111, nullptr, "InvalidateServiceEntryRequirementCache"}, // 4.0.0+ + {112, nullptr, "InvalidateTokenCache"}, // 4.0.0 - 6.2.0 + {113, nullptr, "GetServiceEntryRequirementCacheForOnlinePlay"}, // 6.1.0+ + {120, nullptr, "GetNintendoAccountId"}, + {121, nullptr, "CalculateNintendoAccountAuthenticationFingerprint"}, // 9.0.0+ + {130, nullptr, "GetNintendoAccountUserResourceCache"}, + {131, nullptr, "RefreshNintendoAccountUserResourceCacheAsync"}, + {132, nullptr, "RefreshNintendoAccountUserResourceCacheAsyncIfSecondsElapsed"}, + {133, nullptr, "GetNintendoAccountVerificationUrlCache"}, // 9.0.0+ + {134, nullptr, "RefreshNintendoAccountVerificationUrlCache"}, // 9.0.0+ + {135, nullptr, "RefreshNintendoAccountVerificationUrlCacheAsyncIfSecondsElapsed"}, // 9.0.0+ + {140, nullptr, "GetNetworkServiceLicenseCache"}, // 5.0.0+ + {141, nullptr, "RefreshNetworkServiceLicenseCacheAsync"}, // 5.0.0+ + {142, nullptr, "RefreshNetworkServiceLicenseCacheAsyncIfSecondsElapsed"}, // 5.0.0+ + {150, nullptr, "CreateAuthorizationRequest"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +// 3.0.0+ +class IFloatingRegistrationRequest final : public ServiceFramework { +public: + explicit IFloatingRegistrationRequest(Common::UUID user_id) + : ServiceFramework("IFloatingRegistrationRequest") { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "GetSessionId"}, + {12, nullptr, "GetAccountId"}, + {13, nullptr, "GetLinkedNintendoAccountId"}, + {14, nullptr, "GetNickname"}, + {15, nullptr, "GetProfileImage"}, + {21, nullptr, "LoadIdTokenCache"}, + {100, nullptr, "RegisterUser"}, // [1.0.0-3.0.2] RegisterAsync + {101, nullptr, "RegisterUserWithUid"}, // [1.0.0-3.0.2] RegisterWithUidAsync + {102, nullptr, "RegisterNetworkServiceAccountAsync"}, // 4.0.0+ + {103, nullptr, "RegisterNetworkServiceAccountWithUidAsync"}, // 4.0.0+ + {110, nullptr, "SetSystemProgramIdentification"}, + {111, nullptr, "EnsureIdTokenCacheAsync"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class IAdministrator final : public ServiceFramework { +public: + explicit IAdministrator(Common::UUID user_id) : ServiceFramework("IAdministrator") { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "CheckAvailability"}, + {1, nullptr, "GetAccountId"}, + {2, nullptr, "EnsureIdTokenCacheAsync"}, + {3, nullptr, "LoadIdTokenCache"}, + {100, nullptr, "SetSystemProgramIdentification"}, + {101, nullptr, "RefreshNotificationTokenAsync"}, // 7.0.0+ + {110, nullptr, "GetServiceEntryRequirementCache"}, // 4.0.0+ + {111, nullptr, "InvalidateServiceEntryRequirementCache"}, // 4.0.0+ + {112, nullptr, "InvalidateTokenCache"}, // 4.0.0 - 6.2.0 + {113, nullptr, "GetServiceEntryRequirementCacheForOnlinePlay"}, // 6.1.0+ + {120, nullptr, "GetNintendoAccountId"}, + {121, nullptr, "CalculateNintendoAccountAuthenticationFingerprint"}, // 9.0.0+ + {130, nullptr, "GetNintendoAccountUserResourceCache"}, + {131, nullptr, "RefreshNintendoAccountUserResourceCacheAsync"}, + {132, nullptr, "RefreshNintendoAccountUserResourceCacheAsyncIfSecondsElapsed"}, + {133, nullptr, "GetNintendoAccountVerificationUrlCache"}, // 9.0.0+ + {134, nullptr, "RefreshNintendoAccountVerificationUrlCacheAsync"}, // 9.0.0+ + {135, nullptr, "RefreshNintendoAccountVerificationUrlCacheAsyncIfSecondsElapsed"}, // 9.0.0+ + {140, nullptr, "GetNetworkServiceLicenseCache"}, // 5.0.0+ + {141, nullptr, "RefreshNetworkServiceLicenseCacheAsync"}, // 5.0.0+ + {142, nullptr, "RefreshNetworkServiceLicenseCacheAsyncIfSecondsElapsed"}, // 5.0.0+ + {150, nullptr, "CreateAuthorizationRequest"}, + {200, nullptr, "IsRegistered"}, + {201, nullptr, "RegisterAsync"}, + {202, nullptr, "UnregisterAsync"}, + {203, nullptr, "DeleteRegistrationInfoLocally"}, + {220, nullptr, "SynchronizeProfileAsync"}, + {221, nullptr, "UploadProfileAsync"}, + {222, nullptr, "SynchronizaProfileAsyncIfSecondsElapsed"}, + {250, nullptr, "IsLinkedWithNintendoAccount"}, + {251, nullptr, "CreateProcedureToLinkWithNintendoAccount"}, + {252, nullptr, "ResumeProcedureToLinkWithNintendoAccount"}, + {255, nullptr, "CreateProcedureToUpdateLinkageStateOfNintendoAccount"}, + {256, nullptr, "ResumeProcedureToUpdateLinkageStateOfNintendoAccount"}, + {260, nullptr, "CreateProcedureToLinkNnidWithNintendoAccount"}, // 3.0.0+ + {261, nullptr, "ResumeProcedureToLinkNnidWithNintendoAccount"}, // 3.0.0+ + {280, nullptr, "ProxyProcedureToAcquireApplicationAuthorizationForNintendoAccount"}, + {290, nullptr, "GetRequestForNintendoAccountUserResourceView"}, // 8.0.0+ + {300, nullptr, "TryRecoverNintendoAccountUserStateAsync"}, // 6.0.0+ + {400, nullptr, "IsServiceEntryRequirementCacheRefreshRequiredForOnlinePlay"}, // 6.1.0+ + {401, nullptr, "RefreshServiceEntryRequirementCacheForOnlinePlayAsync"}, // 6.1.0+ + {900, nullptr, "GetAuthenticationInfoForWin"}, // 9.0.0+ + {901, nullptr, "ImportAsyncForWin"}, // 9.0.0+ + {997, nullptr, "DebugUnlinkNintendoAccountAsync"}, + {998, nullptr, "DebugSetAvailabilityErrorDetail"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class IAuthorizationRequest final : public ServiceFramework { +public: + explicit IAuthorizationRequest(Common::UUID user_id) + : ServiceFramework("IAuthorizationRequest") { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "GetSessionId"}, + {10, nullptr, "InvokeWithoutInteractionAsync"}, + {19, nullptr, "IsAuthorized"}, + {20, nullptr, "GetAuthorizationCode"}, + {21, nullptr, "GetIdToken"}, + {22, nullptr, "GetState"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class IOAuthProcedure final : public ServiceFramework { +public: + explicit IOAuthProcedure(Common::UUID user_id) : ServiceFramework("IOAuthProcedure") { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "PrepareAsync"}, + {1, nullptr, "GetRequest"}, + {2, nullptr, "ApplyResponse"}, + {3, nullptr, "ApplyResponseAsync"}, + {10, nullptr, "Suspend"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +// 3.0.0+ +class IOAuthProcedureForExternalNsa final : public ServiceFramework { +public: + explicit IOAuthProcedureForExternalNsa(Common::UUID user_id) + : ServiceFramework("IOAuthProcedureForExternalNsa") { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "PrepareAsync"}, + {1, nullptr, "GetRequest"}, + {2, nullptr, "ApplyResponse"}, + {3, nullptr, "ApplyResponseAsync"}, + {10, nullptr, "Suspend"}, + {100, nullptr, "GetAccountId"}, + {101, nullptr, "GetLinkedNintendoAccountId"}, + {102, nullptr, "GetNickname"}, + {103, nullptr, "GetProfileImage"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class IOAuthProcedureForNintendoAccountLinkage final + : public ServiceFramework { +public: + explicit IOAuthProcedureForNintendoAccountLinkage(Common::UUID user_id) + : ServiceFramework("IOAuthProcedureForNintendoAccountLinkage") { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "PrepareAsync"}, + {1, nullptr, "GetRequest"}, + {2, nullptr, "ApplyResponse"}, + {3, nullptr, "ApplyResponseAsync"}, + {10, nullptr, "Suspend"}, + {100, nullptr, "GetRequestWithTheme"}, + {101, nullptr, "IsNetworkServiceAccountReplaced"}, + {199, nullptr, "GetUrlForIntroductionOfExtraMembership"}, // 2.0.0 - 5.1.0 + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class INotifier final : public ServiceFramework { +public: + explicit INotifier(Common::UUID user_id) : ServiceFramework("INotifier") { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "GetSystemEvent"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + class IProfileCommon : public ServiceFramework { public: explicit IProfileCommon(const char* name, bool editor_commands, Common::UUID user_id, @@ -226,6 +438,54 @@ public: : IProfileCommon("IProfileEditor", true, user_id, profile_manager) {} }; +class IAsyncContext final : public ServiceFramework { +public: + explicit IAsyncContext(Common::UUID user_id) : ServiceFramework("IAsyncContext") { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "GetSystemEvent"}, + {1, nullptr, "Cancel"}, + {2, nullptr, "HasDone"}, + {3, nullptr, "GetResult"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class ISessionObject final : public ServiceFramework { +public: + explicit ISessionObject(Common::UUID user_id) : ServiceFramework("ISessionObject") { + // clang-format off + static const FunctionInfo functions[] = { + {999, nullptr, "Dummy"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class IGuestLoginRequest final : public ServiceFramework { +public: + explicit IGuestLoginRequest(Common::UUID) : ServiceFramework("IGuestLoginRequest") { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "GetSessionId"}, + {11, nullptr, "Unknown"}, // 1.0.0 - 2.3.0 (the name is blank on Switchbrew) + {12, nullptr, "GetAccountId"}, + {13, nullptr, "GetLinkedNintendoAccountId"}, + {14, nullptr, "GetNickname"}, + {15, nullptr, "GetProfileImage"}, + {21, nullptr, "LoadIdTokenCache"}, // 3.0.0+ + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + class IManagerForApplication final : public ServiceFramework { public: explicit IManagerForApplication(Common::UUID user_id) @@ -265,6 +525,87 @@ private: Common::UUID user_id; }; +// 6.0.0+ +class IAsyncNetworkServiceLicenseKindContext final + : public ServiceFramework { +public: + explicit IAsyncNetworkServiceLicenseKindContext(Common::UUID user_id) + : ServiceFramework("IAsyncNetworkServiceLicenseKindContext") { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "GetSystemEvent"}, + {1, nullptr, "Cancel"}, + {2, nullptr, "HasDone"}, + {3, nullptr, "GetResult"}, + {4, nullptr, "GetNetworkServiceLicenseKind"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +// 8.0.0+ +class IOAuthProcedureForUserRegistration final + : public ServiceFramework { +public: + explicit IOAuthProcedureForUserRegistration(Common::UUID user_id) + : ServiceFramework("IOAuthProcedureForUserRegistration") { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "PrepareAsync"}, + {1, nullptr, "GetRequest"}, + {2, nullptr, "ApplyResponse"}, + {3, nullptr, "ApplyResponseAsync"}, + {10, nullptr, "Suspend"}, + {100, nullptr, "GetAccountId"}, + {101, nullptr, "GetLinkedNintendoAccountId"}, + {102, nullptr, "GetNickname"}, + {103, nullptr, "GetProfileImage"}, + {110, nullptr, "RegisterUserAsync"}, + {111, nullptr, "GetUid"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class DAUTH_O final : public ServiceFramework { +public: + explicit DAUTH_O(Common::UUID) : ServiceFramework("dauth:o") { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "EnsureAuthenticationTokenCacheAsync"}, // [5.0.0-5.1.0] GeneratePostData + {1, nullptr, "LoadAuthenticationTokenCache"}, // 6.0.0+ + {2, nullptr, "InvalidateAuthenticationTokenCache"}, // 6.0.0+ + {10, nullptr, "EnsureEdgeTokenCacheAsync"}, // 6.0.0+ + {11, nullptr, "LoadEdgeTokenCache"}, // 6.0.0+ + {12, nullptr, "InvalidateEdgeTokenCache"}, // 6.0.0+ + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +// 6.0.0+ +class IAsyncResult final : public ServiceFramework { +public: + explicit IAsyncResult(Common::UUID user_id) : ServiceFramework("IAsyncResult") { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "GetResult"}, + {1, nullptr, "Cancel"}, + {2, nullptr, "IsAvailable"}, + {3, nullptr, "GetSystemEvent"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_ACC, "called"); IPC::ResponseBuilder rb{ctx, 3}; diff --git a/src/core/hle/service/acc/acc_aa.cpp b/src/core/hle/service/acc/acc_aa.cpp index 3bac6bcd1..51f119b12 100644 --- a/src/core/hle/service/acc/acc_aa.cpp +++ b/src/core/hle/service/acc/acc_aa.cpp @@ -13,8 +13,8 @@ ACC_AA::ACC_AA(std::shared_ptr module, std::shared_ptr p {0, nullptr, "EnsureCacheAsync"}, {1, nullptr, "LoadCache"}, {2, nullptr, "GetDeviceAccountId"}, - {50, nullptr, "RegisterNotificationTokenAsync"}, - {51, nullptr, "UnregisterNotificationTokenAsync"}, + {50, nullptr, "RegisterNotificationTokenAsync"}, // 1.0.0 - 6.2.0 + {51, nullptr, "UnregisterNotificationTokenAsync"}, // 1.0.0 - 6.2.0 }; RegisterHandlers(functions); } diff --git a/src/core/hle/service/acc/acc_su.cpp b/src/core/hle/service/acc/acc_su.cpp index 2eefc6df5..85620bde3 100644 --- a/src/core/hle/service/acc/acc_su.cpp +++ b/src/core/hle/service/acc/acc_su.cpp @@ -17,28 +17,28 @@ ACC_SU::ACC_SU(std::shared_ptr module, std::shared_ptr p {3, &ACC_SU::ListOpenUsers, "ListOpenUsers"}, {4, &ACC_SU::GetLastOpenedUser, "GetLastOpenedUser"}, {5, &ACC_SU::GetProfile, "GetProfile"}, - {6, nullptr, "GetProfileDigest"}, + {6, nullptr, "GetProfileDigest"}, // 3.0.0+ {50, &ACC_SU::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, {51, &ACC_SU::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, - {60, nullptr, "ListOpenContextStoredUsers"}, - {99, nullptr, "DebugActivateOpenContextRetention"}, + {60, nullptr, "ListOpenContextStoredUsers"}, // 5.0.0 - 5.1.0 + {99, nullptr, "DebugActivateOpenContextRetention"}, // 6.0.0+ {100, nullptr, "GetUserRegistrationNotifier"}, {101, nullptr, "GetUserStateChangeNotifier"}, {102, nullptr, "GetBaasAccountManagerForSystemService"}, {103, nullptr, "GetBaasUserAvailabilityChangeNotifier"}, {104, nullptr, "GetProfileUpdateNotifier"}, - {105, nullptr, "CheckNetworkServiceAvailabilityAsync"}, - {106, nullptr, "GetProfileSyncNotifier"}, + {105, nullptr, "CheckNetworkServiceAvailabilityAsync"}, // 4.0.0+ + {106, nullptr, "GetProfileSyncNotifier"}, // 9.0.0+ {110, nullptr, "StoreSaveDataThumbnail"}, {111, nullptr, "ClearSaveDataThumbnail"}, {112, nullptr, "LoadSaveDataThumbnail"}, - {113, nullptr, "GetSaveDataThumbnailExistence"}, - {120, nullptr, "ListOpenUsersInApplication"}, - {130, nullptr, "ActivateOpenContextRetention"}, - {140, &ACC_SU::ListQualifiedUsers, "ListQualifiedUsers"}, - {150, nullptr, "AuthenticateApplicationAsync"}, - {190, nullptr, "GetUserLastOpenedApplication"}, - {191, nullptr, "ActivateOpenContextHolder"}, + {113, nullptr, "GetSaveDataThumbnailExistence"}, // 5.0.0+ + {120, nullptr, "ListOpenUsersInApplication"}, // 10.0.0+ + {130, nullptr, "ActivateOpenContextRetention"}, // 6.0.0+ + {140, &ACC_SU::ListQualifiedUsers, "ListQualifiedUsers"}, // 6.0.0+ + {150, nullptr, "AuthenticateApplicationAsync"}, // 10.0.0+ + {190, nullptr, "GetUserLastOpenedApplication"}, // 1.0.0 - 9.2.0 + {191, nullptr, "ActivateOpenContextHolder"}, // 7.0.0+ {200, nullptr, "BeginUserRegistration"}, {201, nullptr, "CompleteUserRegistration"}, {202, nullptr, "CancelUserRegistration"}, @@ -46,15 +46,15 @@ ACC_SU::ACC_SU(std::shared_ptr module, std::shared_ptr p {204, nullptr, "SetUserPosition"}, {205, &ACC_SU::GetProfileEditor, "GetProfileEditor"}, {206, nullptr, "CompleteUserRegistrationForcibly"}, - {210, nullptr, "CreateFloatingRegistrationRequest"}, - {211, nullptr, "CreateProcedureToRegisterUserWithNintendoAccount"}, - {212, nullptr, "ResumeProcedureToRegisterUserWithNintendoAccount"}, + {210, nullptr, "CreateFloatingRegistrationRequest"}, // 3.0.0+ + {211, nullptr, "CreateProcedureToRegisterUserWithNintendoAccount"}, // 8.0.0+ + {212, nullptr, "ResumeProcedureToRegisterUserWithNintendoAccount"}, // 8.0.0+ {230, nullptr, "AuthenticateServiceAsync"}, {250, nullptr, "GetBaasAccountAdministrator"}, {290, nullptr, "ProxyProcedureForGuestLoginWithNintendoAccount"}, - {291, nullptr, "ProxyProcedureForFloatingRegistrationWithNintendoAccount"}, + {291, nullptr, "ProxyProcedureForFloatingRegistrationWithNintendoAccount"}, // 3.0.0+ {299, nullptr, "SuspendBackgroundDaemon"}, - {997, nullptr, "DebugInvalidateTokenCacheForUser"}, + {997, nullptr, "DebugInvalidateTokenCacheForUser"}, // 3.0.0+ {998, nullptr, "DebugSetUserStateClose"}, {999, nullptr, "DebugSetUserStateOpen"}, }; diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp index fb4e7e772..49f6e20f1 100644 --- a/src/core/hle/service/acc/acc_u0.cpp +++ b/src/core/hle/service/acc/acc_u0.cpp @@ -17,23 +17,23 @@ ACC_U0::ACC_U0(std::shared_ptr module, std::shared_ptr p {3, &ACC_U0::ListOpenUsers, "ListOpenUsers"}, {4, &ACC_U0::GetLastOpenedUser, "GetLastOpenedUser"}, {5, &ACC_U0::GetProfile, "GetProfile"}, - {6, nullptr, "GetProfileDigest"}, + {6, nullptr, "GetProfileDigest"}, // 3.0.0+ {50, &ACC_U0::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, {51, &ACC_U0::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, - {60, nullptr, "ListOpenContextStoredUsers"}, - {99, nullptr, "DebugActivateOpenContextRetention"}, + {60, nullptr, "ListOpenContextStoredUsers"}, // 5.0.0 - 5.1.0 + {99, nullptr, "DebugActivateOpenContextRetention"}, // 6.0.0+ {100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"}, {101, &ACC_U0::GetBaasAccountManagerForApplication, "GetBaasAccountManagerForApplication"}, {102, nullptr, "AuthenticateApplicationAsync"}, - {103, nullptr, "CheckNetworkServiceAvailabilityAsync"}, + {103, nullptr, "CheckNetworkServiceAvailabilityAsync"}, // 4.0.0+ {110, nullptr, "StoreSaveDataThumbnail"}, {111, nullptr, "ClearSaveDataThumbnail"}, {120, nullptr, "CreateGuestLoginRequest"}, - {130, nullptr, "LoadOpenContext"}, - {131, nullptr, "ListOpenContextStoredUsers"}, - {140, &ACC_U0::InitializeApplicationInfoRestricted, "InitializeApplicationInfoRestricted"}, - {141, &ACC_U0::ListQualifiedUsers, "ListQualifiedUsers"}, - {150, &ACC_U0::IsUserAccountSwitchLocked, "IsUserAccountSwitchLocked"}, + {130, nullptr, "LoadOpenContext"}, // 5.0.0+ + {131, nullptr, "ListOpenContextStoredUsers"}, // 6.0.0+ + {140, &ACC_U0::InitializeApplicationInfoRestricted, "InitializeApplicationInfoRestricted"}, // 6.0.0+ + {141, &ACC_U0::ListQualifiedUsers, "ListQualifiedUsers"}, // 6.0.0+ + {150, &ACC_U0::IsUserAccountSwitchLocked, "IsUserAccountSwitchLocked"}, // 6.0.0+ }; // clang-format on diff --git a/src/core/hle/service/acc/acc_u1.cpp b/src/core/hle/service/acc/acc_u1.cpp index 9f29cdc82..f47004f84 100644 --- a/src/core/hle/service/acc/acc_u1.cpp +++ b/src/core/hle/service/acc/acc_u1.cpp @@ -17,28 +17,29 @@ ACC_U1::ACC_U1(std::shared_ptr module, std::shared_ptr p {3, &ACC_U1::ListOpenUsers, "ListOpenUsers"}, {4, &ACC_U1::GetLastOpenedUser, "GetLastOpenedUser"}, {5, &ACC_U1::GetProfile, "GetProfile"}, - {6, nullptr, "GetProfileDigest"}, + {6, nullptr, "GetProfileDigest"}, // 3.0.0+ {50, &ACC_U1::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, {51, &ACC_U1::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, - {60, nullptr, "ListOpenContextStoredUsers"}, - {99, nullptr, "DebugActivateOpenContextRetention"}, + {60, nullptr, "ListOpenContextStoredUsers"}, // 5.0.0 - 5.1.0 + {99, nullptr, "DebugActivateOpenContextRetention"}, // 6.0.0+ {100, nullptr, "GetUserRegistrationNotifier"}, {101, nullptr, "GetUserStateChangeNotifier"}, {102, nullptr, "GetBaasAccountManagerForSystemService"}, - {103, nullptr, "GetProfileUpdateNotifier"}, - {104, nullptr, "CheckNetworkServiceAvailabilityAsync"}, - {105, nullptr, "GetBaasUserAvailabilityChangeNotifier"}, - {106, nullptr, "GetProfileSyncNotifier"}, + {103, nullptr, "GetBaasUserAvailabilityChangeNotifier"}, + {104, nullptr, "GetProfileUpdateNotifier"}, + {105, nullptr, "CheckNetworkServiceAvailabilityAsync"}, // 4.0.0+ + {106, nullptr, "GetProfileSyncNotifier"}, // 9.0.0+ {110, nullptr, "StoreSaveDataThumbnail"}, {111, nullptr, "ClearSaveDataThumbnail"}, {112, nullptr, "LoadSaveDataThumbnail"}, - {113, nullptr, "GetSaveDataThumbnailExistence"}, - {130, nullptr, "ActivateOpenContextRetention"}, - {140, &ACC_U1::ListQualifiedUsers, "ListQualifiedUsers"}, - {150, nullptr, "AuthenticateApplicationAsync"}, - {190, nullptr, "GetUserLastOpenedApplication"}, - {191, nullptr, "ActivateOpenContextHolder"}, - {997, nullptr, "DebugInvalidateTokenCacheForUser"}, + {113, nullptr, "GetSaveDataThumbnailExistence"}, // 5.0.0+ + {120, nullptr, "ListOpenUsersInApplication"}, // 10.0.0+ + {130, nullptr, "ActivateOpenContextRetention"}, // 6.0.0+ + {140, &ACC_U1::ListQualifiedUsers, "ListQualifiedUsers"}, // 6.0.0+ + {150, nullptr, "AuthenticateApplicationAsync"}, // 10.0.0+ + {190, nullptr, "GetUserLastOpenedApplication"}, // 1.0.0 - 9.2.0 + {191, nullptr, "ActivateOpenContextHolder"}, // 7.0.0+ + {997, nullptr, "DebugInvalidateTokenCacheForUser"}, // 3.0.0+ {998, nullptr, "DebugSetUserStateClose"}, {999, nullptr, "DebugSetUserStateOpen"}, }; From 39ab33ee1c976d2653ceef724c0e60ece0c2ba06 Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Mon, 22 Jun 2020 20:46:25 -0300 Subject: [PATCH 068/252] shader/half_set: Implement HSET2_IMM Add HSET2_IMM. Due to the complexity of the encoding avoid using BitField unions and read the relevant bits from the code itself. This is less error prone. --- src/video_core/engines/shader_bytecode.h | 8 ++ src/video_core/shader/decode/half_set.cpp | 96 +++++++++++++++++------ 2 files changed, 79 insertions(+), 25 deletions(-) diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index e7cb87589..d374b73cf 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h @@ -661,6 +661,10 @@ union Instruction { constexpr Instruction(u64 value) : value{value} {} constexpr Instruction(const Instruction& instr) : value(instr.value) {} + constexpr bool Bit(u64 offset) const { + return ((value >> offset) & 1) != 0; + } + BitField<0, 8, Register> gpr0; BitField<8, 8, Register> gpr8; union { @@ -1874,7 +1878,9 @@ public: HSETP2_C, HSETP2_R, HSETP2_IMM, + HSET2_C, HSET2_R, + HSET2_IMM, POPC_C, POPC_R, POPC_IMM, @@ -2194,7 +2200,9 @@ private: INST("0111111-1-------", Id::HSETP2_C, Type::HalfSetPredicate, "HSETP2_C"), INST("0101110100100---", Id::HSETP2_R, Type::HalfSetPredicate, "HSETP2_R"), INST("0111111-0-------", Id::HSETP2_IMM, Type::HalfSetPredicate, "HSETP2_IMM"), + INST("0111110-1-------", Id::HSET2_C, Type::HalfSet, "HSET2_C"), INST("0101110100011---", Id::HSET2_R, Type::HalfSet, "HSET2_R"), + INST("0111110-0-------", Id::HSET2_IMM, Type::HalfSet, "HSET2_IMM"), INST("010110111010----", Id::FCMP_RR, Type::Arithmetic, "FCMP_RR"), INST("010010111010----", Id::FCMP_RC, Type::Arithmetic, "FCMP_RC"), INST("0101000010000---", Id::MUFU, Type::Arithmetic, "MUFU"), diff --git a/src/video_core/shader/decode/half_set.cpp b/src/video_core/shader/decode/half_set.cpp index 848e46874..b2e88fa20 100644 --- a/src/video_core/shader/decode/half_set.cpp +++ b/src/video_core/shader/decode/half_set.cpp @@ -13,55 +13,101 @@ namespace VideoCommon::Shader { +using std::move; using Tegra::Shader::Instruction; using Tegra::Shader::OpCode; +using Tegra::Shader::PredCondition; u32 ShaderIR::DecodeHalfSet(NodeBlock& bb, u32 pc) { const Instruction instr = {program_code[pc]}; const auto opcode = OpCode::Decode(instr); - if (instr.hset2.ftz == 0) { + PredCondition cond; + bool bf; + bool ftz; + bool neg_a; + bool abs_a; + bool neg_b; + bool abs_b; + switch (opcode->get().GetId()) { + case OpCode::Id::HSET2_C: + case OpCode::Id::HSET2_IMM: + cond = instr.hsetp2.cbuf_and_imm.cond; + bf = instr.Bit(53); + ftz = instr.Bit(54); + neg_a = instr.Bit(43); + abs_a = instr.Bit(44); + neg_b = instr.Bit(56); + abs_b = instr.Bit(54); + break; + case OpCode::Id::HSET2_R: + cond = instr.hsetp2.reg.cond; + bf = instr.Bit(49); + ftz = instr.Bit(50); + neg_a = instr.Bit(43); + abs_a = instr.Bit(44); + neg_b = instr.Bit(31); + abs_b = instr.Bit(30); + break; + default: + UNREACHABLE(); + } + + Node op_b = [this, instr, opcode] { + switch (opcode->get().GetId()) { + case OpCode::Id::HSET2_C: + // Inform as unimplemented as this is not tested. + UNIMPLEMENTED_MSG("HSET2_C is not implemented"); + return GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset()); + case OpCode::Id::HSET2_R: + return GetRegister(instr.gpr20); + case OpCode::Id::HSET2_IMM: + return UnpackHalfImmediate(instr, true); + default: + UNREACHABLE(); + return Node{}; + } + }(); + + if (!ftz) { LOG_DEBUG(HW_GPU, "{} without FTZ is not implemented", opcode->get().GetName()); } Node op_a = UnpackHalfFloat(GetRegister(instr.gpr8), instr.hset2.type_a); - op_a = GetOperandAbsNegHalf(op_a, instr.hset2.abs_a, instr.hset2.negate_a); + op_a = GetOperandAbsNegHalf(op_a, abs_a, neg_a); - Node op_b = [&]() { - switch (opcode->get().GetId()) { - case OpCode::Id::HSET2_R: - return GetRegister(instr.gpr20); - default: - UNREACHABLE(); - return Immediate(0); - } - }(); - op_b = UnpackHalfFloat(op_b, instr.hset2.type_b); - op_b = GetOperandAbsNegHalf(op_b, instr.hset2.abs_b, instr.hset2.negate_b); + switch (opcode->get().GetId()) { + case OpCode::Id::HSET2_R: + op_b = GetOperandAbsNegHalf(move(op_b), abs_b, neg_b); + [[fallthrough]]; + case OpCode::Id::HSET2_C: + op_b = UnpackHalfFloat(move(op_b), instr.hset2.type_b); + break; + default: + break; + } - const Node second_pred = GetPredicate(instr.hset2.pred39, instr.hset2.neg_pred); + Node second_pred = GetPredicate(instr.hset2.pred39, instr.hset2.neg_pred); - const Node comparison_pair = GetPredicateComparisonHalf(instr.hset2.cond, op_a, op_b); + Node comparison_pair = GetPredicateComparisonHalf(cond, op_a, op_b); const OperationCode combiner = GetPredicateCombiner(instr.hset2.op); // HSET2 operates on each half float in the pack. std::array values; for (u32 i = 0; i < 2; ++i) { - const u32 raw_value = instr.hset2.bf ? 0x3c00 : 0xffff; - const Node true_value = Immediate(raw_value << (i * 16)); - const Node false_value = Immediate(0); - - const Node comparison = - Operation(OperationCode::LogicalPick2, comparison_pair, Immediate(i)); - const Node predicate = Operation(combiner, comparison, second_pred); + const u32 raw_value = bf ? 0x3c00 : 0xffff; + Node true_value = Immediate(raw_value << (i * 16)); + Node false_value = Immediate(0); + Node comparison = Operation(OperationCode::LogicalPick2, comparison_pair, Immediate(i)); + Node predicate = Operation(combiner, comparison, second_pred); values[i] = - Operation(OperationCode::Select, NO_PRECISE, predicate, true_value, false_value); + Operation(OperationCode::Select, predicate, move(true_value), move(false_value)); } - const Node value = Operation(OperationCode::UBitwiseOr, NO_PRECISE, values[0], values[1]); - SetRegister(bb, instr.gpr0, value); + Node value = Operation(OperationCode::UBitwiseOr, values[0], values[1]); + SetRegister(bb, instr.gpr0, move(value)); return pc; } From 45dac6bc5cce693cf7affdfaffff433be8acd67f Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Mon, 22 Jun 2020 22:53:31 -0400 Subject: [PATCH 069/252] lm: Silence no return value warning --- src/core/hle/service/lm/manager.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/hle/service/lm/manager.cpp b/src/core/hle/service/lm/manager.cpp index b67081b86..3ee2374e7 100644 --- a/src/core/hle/service/lm/manager.cpp +++ b/src/core/hle/service/lm/manager.cpp @@ -86,7 +86,8 @@ std::string FormatField(Field type, const std::vector& data) { return Common::StringFromFixedZeroTerminatedBuffer( reinterpret_cast(data.data()), data.size()); default: - UNIMPLEMENTED(); + UNIMPLEMENTED_MSG("Unimplemented field type={}", type); + return ""; } } From b8798a995bd8aaefc0d21577c9d129e19b39fb15 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Mon, 22 Jun 2020 22:54:09 -0400 Subject: [PATCH 070/252] yuzu_tester: Silence type conversion warning --- src/yuzu_tester/service/yuzutest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yuzu_tester/service/yuzutest.cpp b/src/yuzu_tester/service/yuzutest.cpp index 85d3f436b..2d3f6e3a7 100644 --- a/src/yuzu_tester/service/yuzutest.cpp +++ b/src/yuzu_tester/service/yuzutest.cpp @@ -53,7 +53,7 @@ private: IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push(write_size); + rb.Push(static_cast(write_size)); } void StartIndividual(Kernel::HLERequestContext& ctx) { From 9f54cd4dad58c2c99874a9fe6bb4c34052a65555 Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Tue, 23 Jun 2020 22:51:03 -0300 Subject: [PATCH 071/252] gl_shader_cache: Avoid use after move for program size All programs had a size of zero due to this bug, skipping invalidations. While we are at it, remove some unused forward declarations. --- src/video_core/renderer_opengl/gl_shader_cache.cpp | 12 +++++++----- src/video_core/renderer_opengl/gl_shader_cache.h | 1 - 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 46e780a06..c6a3bf3a1 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -460,8 +460,9 @@ Shader* ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) { const u8* host_ptr_b = memory_manager.GetPointer(address_b); code_b = GetShaderCode(memory_manager, address_b, host_ptr_b, false); } + const std::size_t code_size = code.size() * sizeof(u64); - const auto unique_identifier = GetUniqueIdentifier( + const u64 unique_identifier = GetUniqueIdentifier( GetShaderType(program), program == Maxwell::ShaderProgram::VertexA, code, code_b); const ShaderParameters params{system, disk_cache, device, @@ -477,7 +478,7 @@ Shader* ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) { Shader* const result = shader.get(); if (cpu_addr) { - Register(std::move(shader), *cpu_addr, code.size() * sizeof(u64)); + Register(std::move(shader), *cpu_addr, code_size); } else { null_shader = std::move(shader); } @@ -495,8 +496,9 @@ Shader* ShaderCacheOpenGL::GetComputeKernel(GPUVAddr code_addr) { const auto host_ptr{memory_manager.GetPointer(code_addr)}; // No kernel found, create a new one - auto code{GetShaderCode(memory_manager, code_addr, host_ptr, true)}; - const auto unique_identifier{GetUniqueIdentifier(ShaderType::Compute, false, code)}; + ProgramCode code{GetShaderCode(memory_manager, code_addr, host_ptr, true)}; + const std::size_t code_size{code.size() * sizeof(u64)}; + const u64 unique_identifier{GetUniqueIdentifier(ShaderType::Compute, false, code)}; const ShaderParameters params{system, disk_cache, device, *cpu_addr, host_ptr, unique_identifier}; @@ -511,7 +513,7 @@ Shader* ShaderCacheOpenGL::GetComputeKernel(GPUVAddr code_addr) { Shader* const result = kernel.get(); if (cpu_addr) { - Register(std::move(kernel), *cpu_addr, code.size() * sizeof(u64)); + Register(std::move(kernel), *cpu_addr, code_size); } else { null_kernel = std::move(kernel); } diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h index 6848f1388..994aaeaf2 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.h +++ b/src/video_core/renderer_opengl/gl_shader_cache.h @@ -37,7 +37,6 @@ namespace OpenGL { class Device; class RasterizerOpenGL; -struct UnspecializedShader; using Maxwell = Tegra::Engines::Maxwell3D::Regs; From 5226610a15cc3bc90d2f3d8a755f3cff05717c56 Mon Sep 17 00:00:00 2001 From: David Marcec Date: Wed, 13 May 2020 14:50:43 +1000 Subject: [PATCH 072/252] Implement GetKeyCodeMap & GetKeyCodeMap2 Closes #3919 --- src/core/hle/service/set/set.cpp | 72 +++++++++++++++++++++++++++++++- src/core/hle/service/set/set.h | 2 + 2 files changed, 72 insertions(+), 2 deletions(-) diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp index f3b4b286c..bb0685eed 100644 --- a/src/core/hle/service/set/set.cpp +++ b/src/core/hle/service/set/set.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include +#include #include #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" @@ -31,6 +32,44 @@ constexpr std::array available_language_codes = {{ LanguageCode::ZH_HANT, }}; +enum class KeyboardLayout : u64 { + Japanese = 0, + EnglishUs = 1, + EnglishUsInternational = 2, + EnglishUk = 3, + French = 4, + FrenchCa = 5, + Spanish = 6, + SpanishLatin = 7, + German = 8, + Italian = 9, + Portuguese = 10, + Russian = 11, + Korean = 12, + ChineseSimplified = 13, + ChineseTraditional = 14, +}; + +constexpr std::array, 17> language_to_layout{{ + {LanguageCode::JA, KeyboardLayout::Japanese}, + {LanguageCode::EN_US, KeyboardLayout::EnglishUs}, + {LanguageCode::FR, KeyboardLayout::French}, + {LanguageCode::DE, KeyboardLayout::German}, + {LanguageCode::IT, KeyboardLayout::Italian}, + {LanguageCode::ES, KeyboardLayout::Spanish}, + {LanguageCode::ZH_CN, KeyboardLayout::ChineseSimplified}, + {LanguageCode::KO, KeyboardLayout::Korean}, + {LanguageCode::NL, KeyboardLayout::EnglishUsInternational}, + {LanguageCode::PT, KeyboardLayout::Portuguese}, + {LanguageCode::RU, KeyboardLayout::Russian}, + {LanguageCode::ZH_TW, KeyboardLayout::ChineseTraditional}, + {LanguageCode::EN_GB, KeyboardLayout::EnglishUk}, + {LanguageCode::FR_CA, KeyboardLayout::FrenchCa}, + {LanguageCode::ES_419, KeyboardLayout::SpanishLatin}, + {LanguageCode::ZH_HANS, KeyboardLayout::ChineseSimplified}, + {LanguageCode::ZH_HANT, KeyboardLayout::ChineseTraditional}, +}}; + constexpr std::size_t pre4_0_0_max_entries = 15; constexpr std::size_t post4_0_0_max_entries = 17; @@ -120,6 +159,35 @@ void SET::GetRegionCode(Kernel::HLERequestContext& ctx) { rb.Push(Settings::values.region_index); } +void GetKeyCodeMapImpl(Kernel::HLERequestContext& ctx) { + const auto language_code = available_language_codes[Settings::values.language_index]; + const auto key_code = + std::find_if(language_to_layout.cbegin(), language_to_layout.cend(), + [=](const auto& element) { return element.first == language_code; }); + KeyboardLayout layout = KeyboardLayout::EnglishUs; + if (key_code == language_to_layout.cend()) { + LOG_ERROR(Service_SET, + "Could not find keyboard layout for language index {} defaulting to english us", + Settings::values.language_index); + } else { + layout = key_code->second; + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + ctx.WriteBuffer(&layout, sizeof(KeyboardLayout)); +} + +void SET::GetKeyCodeMap(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "Called {}", ctx.Description()); + GetKeyCodeMapImpl(ctx); +} + +void SET::GetKeyCodeMap2(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "Called {}", ctx.Description()); + GetKeyCodeMapImpl(ctx); +} + SET::SET() : ServiceFramework("set") { // clang-format off static const FunctionInfo functions[] = { @@ -130,9 +198,9 @@ SET::SET() : ServiceFramework("set") { {4, &SET::GetRegionCode, "GetRegionCode"}, {5, &SET::GetAvailableLanguageCodes2, "GetAvailableLanguageCodes2"}, {6, &SET::GetAvailableLanguageCodeCount2, "GetAvailableLanguageCodeCount2"}, - {7, nullptr, "GetKeyCodeMap"}, + {7, &SET::GetKeyCodeMap, "GetKeyCodeMap"}, {8, &SET::GetQuestFlag, "GetQuestFlag"}, - {9, nullptr, "GetKeyCodeMap2"}, + {9, &SET::GetKeyCodeMap2, "GetKeyCodeMap2"}, {10, nullptr, "GetFirmwareVersionForDebug"}, }; // clang-format on diff --git a/src/core/hle/service/set/set.h b/src/core/hle/service/set/set.h index 6084b345d..8ac9c169d 100644 --- a/src/core/hle/service/set/set.h +++ b/src/core/hle/service/set/set.h @@ -44,6 +44,8 @@ private: void GetAvailableLanguageCodeCount2(Kernel::HLERequestContext& ctx); void GetQuestFlag(Kernel::HLERequestContext& ctx); void GetRegionCode(Kernel::HLERequestContext& ctx); + void GetKeyCodeMap(Kernel::HLERequestContext& ctx); + void GetKeyCodeMap2(Kernel::HLERequestContext& ctx); }; } // namespace Service::Set From e3d2b646e09cd144f7dfc624dceb441327fb1c95 Mon Sep 17 00:00:00 2001 From: David Marcec Date: Wed, 24 Jun 2020 12:07:41 +1000 Subject: [PATCH 073/252] Fixed logging output --- src/core/hle/service/set/set.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp index bb0685eed..7daf980ac 100644 --- a/src/core/hle/service/set/set.cpp +++ b/src/core/hle/service/set/set.cpp @@ -167,7 +167,7 @@ void GetKeyCodeMapImpl(Kernel::HLERequestContext& ctx) { KeyboardLayout layout = KeyboardLayout::EnglishUs; if (key_code == language_to_layout.cend()) { LOG_ERROR(Service_SET, - "Could not find keyboard layout for language index {} defaulting to english us", + "Could not find keyboard layout for language index {}, defaulting to English us", Settings::values.language_index); } else { layout = key_code->second; From 6ce5f3120be6a65a798d3abc6fda0fe6171d0296 Mon Sep 17 00:00:00 2001 From: David Marcec Date: Fri, 5 Jun 2020 01:42:19 +1000 Subject: [PATCH 074/252] Macro HLE support --- src/video_core/CMakeLists.txt | 2 + src/video_core/engines/maxwell_3d.cpp | 2 +- src/video_core/engines/maxwell_3d.h | 4 + src/video_core/macro/macro.cpp | 35 ++++++- src/video_core/macro/macro.h | 19 +++- src/video_core/macro/macro_hle.cpp | 108 +++++++++++++++++++++ src/video_core/macro/macro_hle.h | 43 ++++++++ src/video_core/macro/macro_interpreter.cpp | 3 +- src/video_core/macro/macro_jit_x64.cpp | 3 +- 9 files changed, 209 insertions(+), 10 deletions(-) create mode 100644 src/video_core/macro/macro_hle.cpp create mode 100644 src/video_core/macro/macro_hle.h diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 099bb446e..2dc752aa9 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -27,6 +27,8 @@ add_library(video_core STATIC engines/shader_type.h macro/macro.cpp macro/macro.h + macro/macro_hle.cpp + macro/macro_hle.h macro/macro_interpreter.cpp macro/macro_interpreter.h macro/macro_jit_x64.cpp diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index ea3c8a963..c01436295 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -128,7 +128,7 @@ void Maxwell3D::CallMacroMethod(u32 method, const std::vector& parameters) ((method - MacroRegistersStart) >> 1) % static_cast(macro_positions.size()); // Execute the current macro. - macro_engine->Execute(macro_positions[entry], parameters); + macro_engine->Execute(*this, macro_positions[entry], parameters); if (mme_draw.current_mode != MMEDrawMode::Undefined) { FlushMMEInlineDraw(); } diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index d5fe25065..5926c4d2d 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -1418,6 +1418,10 @@ public: return execute_on; } + VideoCore::RasterizerInterface& GetRasterizer() { + return rasterizer; + } + /// Notify a memory write has happened. void OnMemoryWrite() { dirty.flags |= dirty.on_write_stores; diff --git a/src/video_core/macro/macro.cpp b/src/video_core/macro/macro.cpp index 89077a2d8..c8aa2534a 100644 --- a/src/video_core/macro/macro.cpp +++ b/src/video_core/macro/macro.cpp @@ -2,23 +2,37 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include #include "common/assert.h" #include "common/logging/log.h" #include "core/settings.h" +#include "video_core/engines/maxwell_3d.h" #include "video_core/macro/macro.h" +#include "video_core/macro/macro_hle.h" #include "video_core/macro/macro_interpreter.h" #include "video_core/macro/macro_jit_x64.h" namespace Tegra { +MacroEngine::MacroEngine(Engines::Maxwell3D& maxwell3d) + : hle_macros{std::make_unique(maxwell3d)} {} + +MacroEngine::~MacroEngine() {} + void MacroEngine::AddCode(u32 method, u32 data) { uploaded_macro_code[method].push_back(data); } -void MacroEngine::Execute(u32 method, const std::vector& parameters) { +void MacroEngine::Execute(Engines::Maxwell3D& maxwell3d, u32 method, + const std::vector& parameters) { auto compiled_macro = macro_cache.find(method); if (compiled_macro != macro_cache.end()) { - compiled_macro->second->Execute(parameters, method); + const auto& cache_info = compiled_macro->second; + if (cache_info.has_hle_program) { + cache_info.hle_program->Execute(parameters, method); + } else { + cache_info.lle_program->Execute(parameters, method); + } } else { // Macro not compiled, check if it's uploaded and if so, compile it auto macro_code = uploaded_macro_code.find(method); @@ -26,8 +40,21 @@ void MacroEngine::Execute(u32 method, const std::vector& parameters) { UNREACHABLE_MSG("Macro 0x{0:x} was not uploaded", method); return; } - macro_cache[method] = Compile(macro_code->second); - macro_cache[method]->Execute(parameters, method); + auto& cache_info = macro_cache[method]; + cache_info.hash = boost::hash_value(macro_code->second); + cache_info.lle_program = Compile(macro_code->second); + + auto hle_program = hle_macros->GetHLEProgram(cache_info.hash); + if (hle_program.has_value()) { + cache_info.has_hle_program = true; + cache_info.hle_program = std::move(hle_program.value()); + } + + if (cache_info.has_hle_program) { + cache_info.hle_program->Execute(parameters, method); + } else { + cache_info.lle_program->Execute(parameters, method); + } } } diff --git a/src/video_core/macro/macro.h b/src/video_core/macro/macro.h index b76ed891f..5fa8023af 100644 --- a/src/video_core/macro/macro.h +++ b/src/video_core/macro/macro.h @@ -11,9 +11,11 @@ #include "common/common_types.h" namespace Tegra { + namespace Engines { class Maxwell3D; } + namespace Macro { constexpr std::size_t NUM_MACRO_REGISTERS = 8; enum class Operation : u32 { @@ -94,6 +96,8 @@ union MethodAddress { } // namespace Macro +class HLEMacro; + class CachedMacro { public: virtual ~CachedMacro() = default; @@ -107,20 +111,29 @@ public: class MacroEngine { public: - virtual ~MacroEngine() = default; + MacroEngine(Engines::Maxwell3D& maxwell3d); + virtual ~MacroEngine(); // Store the uploaded macro code to compile them when they're called. void AddCode(u32 method, u32 data); // Compiles the macro if its not in the cache, and executes the compiled macro - void Execute(u32 method, const std::vector& parameters); + void Execute(Engines::Maxwell3D& maxwell3d, u32 method, const std::vector& parameters); protected: virtual std::unique_ptr Compile(const std::vector& code) = 0; private: - std::unordered_map> macro_cache; + struct CacheInfo { + std::unique_ptr lle_program{}; + std::unique_ptr hle_program{}; + u64 hash{}; + bool has_hle_program{}; + }; + + std::unordered_map macro_cache; std::unordered_map> uploaded_macro_code; + std::unique_ptr hle_macros; }; std::unique_ptr GetMacroEngine(Engines::Maxwell3D& maxwell3d); diff --git a/src/video_core/macro/macro_hle.cpp b/src/video_core/macro/macro_hle.cpp new file mode 100644 index 000000000..51827c822 --- /dev/null +++ b/src/video_core/macro/macro_hle.cpp @@ -0,0 +1,108 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include "video_core/engines/maxwell_3d.h" +#include "video_core/macro/macro_hle.h" +#include "video_core/rasterizer_interface.h" + +namespace Tegra { + +// HLE'd functions +static void HLE_771BB18C62444DA0(Engines::Maxwell3D& maxwell3d, + const std::vector& parameters) { + const u32 instance_count = parameters[2] & maxwell3d.GetRegisterValue(0xD1B); + + maxwell3d.regs.draw.topology.Assign( + static_cast(parameters[0] & + ~(0x3ffffff << 26))); + maxwell3d.regs.vb_base_instance = parameters[5]; + maxwell3d.mme_draw.instance_count = instance_count; + maxwell3d.regs.vb_element_base = parameters[3]; + maxwell3d.regs.index_array.count = parameters[1]; + maxwell3d.regs.index_array.first = parameters[4]; + + if (maxwell3d.ShouldExecute()) { + maxwell3d.GetRasterizer().Draw(true, true); + } + maxwell3d.regs.index_array.count = 0; + maxwell3d.mme_draw.instance_count = 0; +} + +static void HLE_0D61FC9FAAC9FCAD(Engines::Maxwell3D& maxwell3d, + const std::vector& parameters) { + const u32 count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]); + + maxwell3d.regs.vertex_buffer.first = parameters[3]; + maxwell3d.regs.vertex_buffer.count = parameters[1]; + maxwell3d.regs.vb_base_instance = parameters[4]; + maxwell3d.regs.draw.topology.Assign( + static_cast(parameters[0])); + maxwell3d.mme_draw.instance_count = count; + + if (maxwell3d.ShouldExecute()) { + maxwell3d.GetRasterizer().Draw(false, true); + } + maxwell3d.regs.vertex_buffer.count = 0; + maxwell3d.mme_draw.instance_count = 0; +} + +static void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, + const std::vector& parameters) { + const u32 instance_count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]); + const u32 element_base = parameters[4]; + const u32 base_instance = parameters[5]; + maxwell3d.regs.index_array.first = parameters[3]; + maxwell3d.regs.reg_array[0x446] = element_base; // vertex id base? + maxwell3d.regs.index_array.count = parameters[1]; + maxwell3d.regs.vb_element_base = element_base; + maxwell3d.regs.vb_base_instance = base_instance; + maxwell3d.regs.const_buffer.cb_pos = 0x640; + maxwell3d.mme_draw.instance_count = instance_count; + maxwell3d.regs.const_buffer.cb_data[0] = element_base; + maxwell3d.regs.const_buffer.cb_data[1] = base_instance; + maxwell3d.regs.draw.topology.Assign( + static_cast(parameters[0])); + if (maxwell3d.ShouldExecute()) { + maxwell3d.GetRasterizer().Draw(true, true); + } + maxwell3d.regs.reg_array[0x446] = 0x0; // vertex id base? + maxwell3d.regs.index_array.count = 0; + maxwell3d.regs.vb_element_base = 0x0; + maxwell3d.regs.vb_base_instance = 0x0; + maxwell3d.regs.const_buffer.cb_pos = 0x640; + maxwell3d.regs.const_buffer.cb_data[0] = 0; + maxwell3d.regs.const_buffer.cb_data[1] = 0; + maxwell3d.mme_draw.instance_count = 0; +} + +static const std::unordered_map hle_funcs{ + {0x771BB18C62444DA0, &HLE_771BB18C62444DA0}, + {0x0D61FC9FAAC9FCAD, &HLE_0D61FC9FAAC9FCAD}, + {0x0217920100488FF7, &HLE_0217920100488FF7}, +}; + +HLEMacro::HLEMacro(Engines::Maxwell3D& maxwell3d) : maxwell3d(maxwell3d) {} +HLEMacro::~HLEMacro() = default; + +std::optional> HLEMacro::GetHLEProgram(u64 hash) const { + auto it = hle_funcs.find(hash); + if (it != hle_funcs.end()) { + return std::make_unique(maxwell3d, it->second); + } else { + return {}; + } +} + +HLEMacroImpl::~HLEMacroImpl() = default; + +HLEMacroImpl::HLEMacroImpl(Engines::Maxwell3D& maxwell3d, HLEFunction func) + : maxwell3d(maxwell3d), func(func) {} + +void HLEMacroImpl::Execute(const std::vector& parameters, u32 method) { + func(maxwell3d, parameters); +} + +} // namespace Tegra diff --git a/src/video_core/macro/macro_hle.h b/src/video_core/macro/macro_hle.h new file mode 100644 index 000000000..de7f43dc4 --- /dev/null +++ b/src/video_core/macro/macro_hle.h @@ -0,0 +1,43 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include "common/common_types.h" +#include "video_core/macro/macro.h" + +namespace Tegra { + +namespace Engines { +class Maxwell3D; +} + +using HLEFunction = void (*)(Engines::Maxwell3D& maxwell3d, const std::vector& parameters); + +class HLEMacro { +public: + HLEMacro(Engines::Maxwell3D& maxwell3d); + ~HLEMacro(); + std::optional> GetHLEProgram(u64 hash) const; + +private: + Engines::Maxwell3D& maxwell3d; +}; + +class HLEMacroImpl : public CachedMacro { +public: + explicit HLEMacroImpl(Engines::Maxwell3D& maxwell3d, HLEFunction func); + ~HLEMacroImpl(); + + void Execute(const std::vector& parameters, u32 method) override; + +private: + Engines::Maxwell3D& maxwell3d; + HLEFunction func; +}; + +} // namespace Tegra diff --git a/src/video_core/macro/macro_interpreter.cpp b/src/video_core/macro/macro_interpreter.cpp index 5edff27aa..aa5256419 100644 --- a/src/video_core/macro/macro_interpreter.cpp +++ b/src/video_core/macro/macro_interpreter.cpp @@ -11,7 +11,8 @@ MICROPROFILE_DEFINE(MacroInterp, "GPU", "Execute macro interpreter", MP_RGB(128, 128, 192)); namespace Tegra { -MacroInterpreter::MacroInterpreter(Engines::Maxwell3D& maxwell3d) : maxwell3d(maxwell3d) {} +MacroInterpreter::MacroInterpreter(Engines::Maxwell3D& maxwell3d) + : MacroEngine::MacroEngine(maxwell3d), maxwell3d(maxwell3d) {} std::unique_ptr MacroInterpreter::Compile(const std::vector& code) { return std::make_unique(maxwell3d, code); diff --git a/src/video_core/macro/macro_jit_x64.cpp b/src/video_core/macro/macro_jit_x64.cpp index 30abb66e5..07292702f 100644 --- a/src/video_core/macro/macro_jit_x64.cpp +++ b/src/video_core/macro/macro_jit_x64.cpp @@ -28,7 +28,8 @@ static const std::bitset<32> PERSISTENT_REGISTERS = Common::X64::BuildRegSet({ BRANCH_HOLDER, }); -MacroJITx64::MacroJITx64(Engines::Maxwell3D& maxwell3d) : maxwell3d(maxwell3d) {} +MacroJITx64::MacroJITx64(Engines::Maxwell3D& maxwell3d) + : MacroEngine::MacroEngine(maxwell3d), maxwell3d(maxwell3d) {} std::unique_ptr MacroJITx64::Compile(const std::vector& code) { return std::make_unique(maxwell3d, code); From 74b4334d510b58d96e8305bc3f5a7c8d05e842ba Mon Sep 17 00:00:00 2001 From: David Marcec Date: Fri, 5 Jun 2020 12:59:59 +1000 Subject: [PATCH 075/252] Fix constbuffer for 0217920100488FF7 --- src/video_core/macro/macro_hle.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/video_core/macro/macro_hle.cpp b/src/video_core/macro/macro_hle.cpp index 51827c822..887f40310 100644 --- a/src/video_core/macro/macro_hle.cpp +++ b/src/video_core/macro/macro_hle.cpp @@ -59,10 +59,10 @@ static void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, maxwell3d.regs.index_array.count = parameters[1]; maxwell3d.regs.vb_element_base = element_base; maxwell3d.regs.vb_base_instance = base_instance; - maxwell3d.regs.const_buffer.cb_pos = 0x640; maxwell3d.mme_draw.instance_count = instance_count; - maxwell3d.regs.const_buffer.cb_data[0] = element_base; - maxwell3d.regs.const_buffer.cb_data[1] = base_instance; + maxwell3d.CallMethodFromMME(0x8e3, 0x640); + maxwell3d.CallMethodFromMME(0x8e4, element_base); + maxwell3d.CallMethodFromMME(0x8e5, base_instance); maxwell3d.regs.draw.topology.Assign( static_cast(parameters[0])); if (maxwell3d.ShouldExecute()) { @@ -72,10 +72,10 @@ static void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, maxwell3d.regs.index_array.count = 0; maxwell3d.regs.vb_element_base = 0x0; maxwell3d.regs.vb_base_instance = 0x0; - maxwell3d.regs.const_buffer.cb_pos = 0x640; - maxwell3d.regs.const_buffer.cb_data[0] = 0; - maxwell3d.regs.const_buffer.cb_data[1] = 0; maxwell3d.mme_draw.instance_count = 0; + maxwell3d.CallMethodFromMME(0x8e3, 0x640); + maxwell3d.CallMethodFromMME(0x8e4, 0x0); + maxwell3d.CallMethodFromMME(0x8e5, 0x0); } static const std::unordered_map hle_funcs{ From fabdf5d3850c078d173653f259845c26a2ce6e7d Mon Sep 17 00:00:00 2001 From: David Marcec Date: Fri, 5 Jun 2020 13:09:52 +1000 Subject: [PATCH 076/252] Addressed issues --- src/video_core/engines/maxwell_3d.h | 4 ++++ src/video_core/macro/macro.cpp | 2 +- src/video_core/macro/macro.h | 2 +- src/video_core/macro/macro_hle.cpp | 20 ++++++++++---------- src/video_core/macro/macro_hle.h | 2 +- 5 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index 5926c4d2d..ef1618990 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -1422,6 +1422,10 @@ public: return rasterizer; } + const VideoCore::RasterizerInterface& GetRasterizer() const { + return rasterizer; + } + /// Notify a memory write has happened. void OnMemoryWrite() { dirty.flags |= dirty.on_write_stores; diff --git a/src/video_core/macro/macro.cpp b/src/video_core/macro/macro.cpp index c8aa2534a..ef7dad349 100644 --- a/src/video_core/macro/macro.cpp +++ b/src/video_core/macro/macro.cpp @@ -17,7 +17,7 @@ namespace Tegra { MacroEngine::MacroEngine(Engines::Maxwell3D& maxwell3d) : hle_macros{std::make_unique(maxwell3d)} {} -MacroEngine::~MacroEngine() {} +MacroEngine::~MacroEngine() = default; void MacroEngine::AddCode(u32 method, u32 data) { uploaded_macro_code[method].push_back(data); diff --git a/src/video_core/macro/macro.h b/src/video_core/macro/macro.h index 5fa8023af..4d00b84b0 100644 --- a/src/video_core/macro/macro.h +++ b/src/video_core/macro/macro.h @@ -111,7 +111,7 @@ public: class MacroEngine { public: - MacroEngine(Engines::Maxwell3D& maxwell3d); + explicit MacroEngine(Engines::Maxwell3D& maxwell3d); virtual ~MacroEngine(); // Store the uploaded macro code to compile them when they're called. diff --git a/src/video_core/macro/macro_hle.cpp b/src/video_core/macro/macro_hle.cpp index 887f40310..1f1348df3 100644 --- a/src/video_core/macro/macro_hle.cpp +++ b/src/video_core/macro/macro_hle.cpp @@ -2,7 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include +#include #include #include "video_core/engines/maxwell_3d.h" #include "video_core/macro/macro_hle.h" @@ -78,22 +78,22 @@ static void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, maxwell3d.CallMethodFromMME(0x8e5, 0x0); } -static const std::unordered_map hle_funcs{ - {0x771BB18C62444DA0, &HLE_771BB18C62444DA0}, - {0x0D61FC9FAAC9FCAD, &HLE_0D61FC9FAAC9FCAD}, - {0x0217920100488FF7, &HLE_0217920100488FF7}, +static const std::array, 3> hle_funcs{ + std::make_pair(0x771BB18C62444DA0, &HLE_771BB18C62444DA0), + std::make_pair(0x0D61FC9FAAC9FCAD, &HLE_0D61FC9FAAC9FCAD), + std::make_pair(0x0217920100488FF7, &HLE_0217920100488FF7), }; HLEMacro::HLEMacro(Engines::Maxwell3D& maxwell3d) : maxwell3d(maxwell3d) {} HLEMacro::~HLEMacro() = default; std::optional> HLEMacro::GetHLEProgram(u64 hash) const { - auto it = hle_funcs.find(hash); - if (it != hle_funcs.end()) { - return std::make_unique(maxwell3d, it->second); - } else { - return {}; + const auto it = std::find_if(hle_funcs.begin(), hle_funcs.end(), + [hash](auto& pair) { return pair.first == hash; }); + if (it == hle_funcs.end()) { + return std::nullopt; } + return std::make_unique(maxwell3d, it->second); } HLEMacroImpl::~HLEMacroImpl() = default; diff --git a/src/video_core/macro/macro_hle.h b/src/video_core/macro/macro_hle.h index de7f43dc4..7cd492a8f 100644 --- a/src/video_core/macro/macro_hle.h +++ b/src/video_core/macro/macro_hle.h @@ -20,7 +20,7 @@ using HLEFunction = void (*)(Engines::Maxwell3D& maxwell3d, const std::vector> GetHLEProgram(u64 hash) const; From 52340e94ac5a64572643f01a23316ad492a40f66 Mon Sep 17 00:00:00 2001 From: David Marcec Date: Fri, 5 Jun 2020 14:00:00 +1000 Subject: [PATCH 077/252] clear mme draw mode We already draw, so we can clear it --- src/video_core/macro/macro_hle.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/video_core/macro/macro_hle.cpp b/src/video_core/macro/macro_hle.cpp index 1f1348df3..689533f6a 100644 --- a/src/video_core/macro/macro_hle.cpp +++ b/src/video_core/macro/macro_hle.cpp @@ -29,6 +29,7 @@ static void HLE_771BB18C62444DA0(Engines::Maxwell3D& maxwell3d, } maxwell3d.regs.index_array.count = 0; maxwell3d.mme_draw.instance_count = 0; + maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined; } static void HLE_0D61FC9FAAC9FCAD(Engines::Maxwell3D& maxwell3d, @@ -47,6 +48,7 @@ static void HLE_0D61FC9FAAC9FCAD(Engines::Maxwell3D& maxwell3d, } maxwell3d.regs.vertex_buffer.count = 0; maxwell3d.mme_draw.instance_count = 0; + maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined; } static void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, @@ -76,6 +78,7 @@ static void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, maxwell3d.CallMethodFromMME(0x8e3, 0x640); maxwell3d.CallMethodFromMME(0x8e4, 0x0); maxwell3d.CallMethodFromMME(0x8e5, 0x0); + maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined; } static const std::array, 3> hle_funcs{ From f5e2aec4220ee2b72ec2986e0e60625897b2fd44 Mon Sep 17 00:00:00 2001 From: David Marcec Date: Wed, 24 Jun 2020 12:18:33 +1000 Subject: [PATCH 078/252] addressed issues --- src/video_core/macro/macro_hle.cpp | 10 ++++++---- src/video_core/macro/macro_hle.h | 1 + 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/video_core/macro/macro_hle.cpp b/src/video_core/macro/macro_hle.cpp index 689533f6a..410f99018 100644 --- a/src/video_core/macro/macro_hle.cpp +++ b/src/video_core/macro/macro_hle.cpp @@ -10,6 +10,7 @@ namespace Tegra { +namespace { // HLE'd functions static void HLE_771BB18C62444DA0(Engines::Maxwell3D& maxwell3d, const std::vector& parameters) { @@ -80,19 +81,20 @@ static void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, maxwell3d.CallMethodFromMME(0x8e5, 0x0); maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined; } +} // namespace -static const std::array, 3> hle_funcs{ +constexpr std::array, 3> hle_funcs{{ std::make_pair(0x771BB18C62444DA0, &HLE_771BB18C62444DA0), std::make_pair(0x0D61FC9FAAC9FCAD, &HLE_0D61FC9FAAC9FCAD), std::make_pair(0x0217920100488FF7, &HLE_0217920100488FF7), -}; +}}; HLEMacro::HLEMacro(Engines::Maxwell3D& maxwell3d) : maxwell3d(maxwell3d) {} HLEMacro::~HLEMacro() = default; std::optional> HLEMacro::GetHLEProgram(u64 hash) const { - const auto it = std::find_if(hle_funcs.begin(), hle_funcs.end(), - [hash](auto& pair) { return pair.first == hash; }); + const auto it = std::find_if(hle_funcs.cbegin(), hle_funcs.cend(), + [hash](const auto& pair) { return pair.first == hash; }); if (it == hle_funcs.end()) { return std::nullopt; } diff --git a/src/video_core/macro/macro_hle.h b/src/video_core/macro/macro_hle.h index 7cd492a8f..37af875a0 100644 --- a/src/video_core/macro/macro_hle.h +++ b/src/video_core/macro/macro_hle.h @@ -22,6 +22,7 @@ class HLEMacro { public: explicit HLEMacro(Engines::Maxwell3D& maxwell3d); ~HLEMacro(); + std::optional> GetHLEProgram(u64 hash) const; private: From 380fbd8cb785aed3594bdcb3f17f9f6d28baa624 Mon Sep 17 00:00:00 2001 From: David Marcec Date: Wed, 24 Jun 2020 13:05:08 +1000 Subject: [PATCH 079/252] Move GetKeyCodeMapImpl to an anonymous namespace --- src/core/hle/service/set/set.cpp | 38 ++++++++++++++++---------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp index 7daf980ac..e5cfd2101 100644 --- a/src/core/hle/service/set/set.cpp +++ b/src/core/hle/service/set/set.cpp @@ -89,6 +89,25 @@ void GetAvailableLanguageCodesImpl(Kernel::HLERequestContext& ctx, std::size_t m ctx.WriteBuffer(available_language_codes.data(), copy_size); PushResponseLanguageCode(ctx, copy_amount); } + +void GetKeyCodeMapImpl(Kernel::HLERequestContext& ctx) { + const auto language_code = available_language_codes[Settings::values.language_index]; + const auto key_code = + std::find_if(language_to_layout.cbegin(), language_to_layout.cend(), + [=](const auto& element) { return element.first == language_code; }); + KeyboardLayout layout = KeyboardLayout::EnglishUs; + if (key_code == language_to_layout.cend()) { + LOG_ERROR(Service_SET, + "Could not find keyboard layout for language index {}, defaulting to English us", + Settings::values.language_index); + } else { + layout = key_code->second; + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + ctx.WriteBuffer(&layout, sizeof(KeyboardLayout)); +} } // Anonymous namespace LanguageCode GetLanguageCodeFromIndex(std::size_t index) { @@ -159,25 +178,6 @@ void SET::GetRegionCode(Kernel::HLERequestContext& ctx) { rb.Push(Settings::values.region_index); } -void GetKeyCodeMapImpl(Kernel::HLERequestContext& ctx) { - const auto language_code = available_language_codes[Settings::values.language_index]; - const auto key_code = - std::find_if(language_to_layout.cbegin(), language_to_layout.cend(), - [=](const auto& element) { return element.first == language_code; }); - KeyboardLayout layout = KeyboardLayout::EnglishUs; - if (key_code == language_to_layout.cend()) { - LOG_ERROR(Service_SET, - "Could not find keyboard layout for language index {}, defaulting to English us", - Settings::values.language_index); - } else { - layout = key_code->second; - } - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); - ctx.WriteBuffer(&layout, sizeof(KeyboardLayout)); -} - void SET::GetKeyCodeMap(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_SET, "Called {}", ctx.Description()); GetKeyCodeMapImpl(ctx); From da79ec9565f670bcf1f09fdf7d9ae0241d97a241 Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Mon, 11 May 2020 16:18:53 -0300 Subject: [PATCH 080/252] gl_stream_buffer: Always use persistent memory maps yuzu no longer supports platforms without persistent maps. --- .../renderer_opengl/gl_stream_buffer.cpp | 40 ++++++------------- .../renderer_opengl/gl_stream_buffer.h | 4 +- 2 files changed, 14 insertions(+), 30 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_stream_buffer.cpp b/src/video_core/renderer_opengl/gl_stream_buffer.cpp index 932a2f69e..9cf0f6b46 100644 --- a/src/video_core/renderer_opengl/gl_stream_buffer.cpp +++ b/src/video_core/renderer_opengl/gl_stream_buffer.cpp @@ -14,8 +14,7 @@ MICROPROFILE_DEFINE(OpenGL_StreamBuffer, "OpenGL", "Stream Buffer Orphaning", namespace OpenGL { -OGLStreamBuffer::OGLStreamBuffer(GLsizeiptr size, bool vertex_data_usage, bool prefer_coherent, - bool use_persistent) +OGLStreamBuffer::OGLStreamBuffer(GLsizeiptr size, bool vertex_data_usage, bool prefer_coherent) : buffer_size(size) { gl_buffer.Create(); @@ -29,23 +28,16 @@ OGLStreamBuffer::OGLStreamBuffer(GLsizeiptr size, bool vertex_data_usage, bool p allocate_size *= 2; } - if (use_persistent) { - persistent = true; - coherent = prefer_coherent; - const GLbitfield flags = - GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | (coherent ? GL_MAP_COHERENT_BIT : 0); - glNamedBufferStorage(gl_buffer.handle, allocate_size, nullptr, flags); - mapped_ptr = static_cast(glMapNamedBufferRange( - gl_buffer.handle, 0, buffer_size, flags | (coherent ? 0 : GL_MAP_FLUSH_EXPLICIT_BIT))); - } else { - glNamedBufferData(gl_buffer.handle, allocate_size, nullptr, GL_STREAM_DRAW); - } + coherent = prefer_coherent; + const GLbitfield flags = + GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | (coherent ? GL_MAP_COHERENT_BIT : 0); + glNamedBufferStorage(gl_buffer.handle, allocate_size, nullptr, flags); + mapped_ptr = static_cast(glMapNamedBufferRange( + gl_buffer.handle, 0, buffer_size, flags | (coherent ? 0 : GL_MAP_FLUSH_EXPLICIT_BIT))); } OGLStreamBuffer::~OGLStreamBuffer() { - if (persistent) { - glUnmapNamedBuffer(gl_buffer.handle); - } + glUnmapNamedBuffer(gl_buffer.handle); gl_buffer.Release(); } @@ -63,16 +55,14 @@ std::tuple OGLStreamBuffer::Map(GLsizeiptr size, GLintptr a buffer_pos = 0; invalidate = true; - if (persistent) { - glUnmapNamedBuffer(gl_buffer.handle); - } + glUnmapNamedBuffer(gl_buffer.handle); } - if (invalidate || !persistent) { + if (invalidate) { MICROPROFILE_SCOPE(OpenGL_StreamBuffer); - GLbitfield flags = GL_MAP_WRITE_BIT | (persistent ? GL_MAP_PERSISTENT_BIT : 0) | - (coherent ? GL_MAP_COHERENT_BIT : GL_MAP_FLUSH_EXPLICIT_BIT) | - (invalidate ? GL_MAP_INVALIDATE_BUFFER_BIT : GL_MAP_UNSYNCHRONIZED_BIT); + const GLbitfield flags = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | + GL_MAP_INVALIDATE_BUFFER_BIT | + (coherent ? GL_MAP_COHERENT_BIT : GL_MAP_FLUSH_EXPLICIT_BIT); mapped_ptr = static_cast( glMapNamedBufferRange(gl_buffer.handle, buffer_pos, buffer_size - buffer_pos, flags)); mapped_offset = buffer_pos; @@ -88,10 +78,6 @@ void OGLStreamBuffer::Unmap(GLsizeiptr size) { glFlushMappedNamedBufferRange(gl_buffer.handle, buffer_pos - mapped_offset, size); } - if (!persistent) { - glUnmapNamedBuffer(gl_buffer.handle); - } - buffer_pos += size; } diff --git a/src/video_core/renderer_opengl/gl_stream_buffer.h b/src/video_core/renderer_opengl/gl_stream_buffer.h index 866da3594..65c3da93f 100644 --- a/src/video_core/renderer_opengl/gl_stream_buffer.h +++ b/src/video_core/renderer_opengl/gl_stream_buffer.h @@ -13,8 +13,7 @@ namespace OpenGL { class OGLStreamBuffer : private NonCopyable { public: - explicit OGLStreamBuffer(GLsizeiptr size, bool vertex_data_usage, bool prefer_coherent = false, - bool use_persistent = true); + explicit OGLStreamBuffer(GLsizeiptr size, bool vertex_data_usage, bool prefer_coherent = false); ~OGLStreamBuffer(); /* @@ -41,7 +40,6 @@ private: OGLBuffer gl_buffer; bool coherent = false; - bool persistent = false; GLintptr buffer_pos = 0; GLsizeiptr buffer_size = 0; From 00c66a728958c3b2804131ce5baf44880119e018 Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Mon, 11 May 2020 16:21:08 -0300 Subject: [PATCH 081/252] gl_stream_buffer: Always use a non-coherent buffer --- .../renderer_opengl/gl_stream_buffer.cpp | 20 +++++++++---------- .../renderer_opengl/gl_stream_buffer.h | 4 +--- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_stream_buffer.cpp b/src/video_core/renderer_opengl/gl_stream_buffer.cpp index 9cf0f6b46..aeafcfbfe 100644 --- a/src/video_core/renderer_opengl/gl_stream_buffer.cpp +++ b/src/video_core/renderer_opengl/gl_stream_buffer.cpp @@ -4,6 +4,7 @@ #include #include + #include "common/alignment.h" #include "common/assert.h" #include "common/microprofile.h" @@ -14,8 +15,7 @@ MICROPROFILE_DEFINE(OpenGL_StreamBuffer, "OpenGL", "Stream Buffer Orphaning", namespace OpenGL { -OGLStreamBuffer::OGLStreamBuffer(GLsizeiptr size, bool vertex_data_usage, bool prefer_coherent) - : buffer_size(size) { +OGLStreamBuffer::OGLStreamBuffer(GLsizeiptr size, bool vertex_data_usage) : buffer_size(size) { gl_buffer.Create(); GLsizeiptr allocate_size = size; @@ -28,12 +28,10 @@ OGLStreamBuffer::OGLStreamBuffer(GLsizeiptr size, bool vertex_data_usage, bool p allocate_size *= 2; } - coherent = prefer_coherent; - const GLbitfield flags = - GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | (coherent ? GL_MAP_COHERENT_BIT : 0); + static constexpr GLbitfield flags = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT; glNamedBufferStorage(gl_buffer.handle, allocate_size, nullptr, flags); - mapped_ptr = static_cast(glMapNamedBufferRange( - gl_buffer.handle, 0, buffer_size, flags | (coherent ? 0 : GL_MAP_FLUSH_EXPLICIT_BIT))); + mapped_ptr = static_cast( + glMapNamedBufferRange(gl_buffer.handle, 0, buffer_size, flags | GL_MAP_FLUSH_EXPLICIT_BIT)); } OGLStreamBuffer::~OGLStreamBuffer() { @@ -59,10 +57,10 @@ std::tuple OGLStreamBuffer::Map(GLsizeiptr size, GLintptr a } if (invalidate) { + static const GLbitfield flags = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | + GL_MAP_INVALIDATE_BUFFER_BIT | GL_MAP_FLUSH_EXPLICIT_BIT; + MICROPROFILE_SCOPE(OpenGL_StreamBuffer); - const GLbitfield flags = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | - GL_MAP_INVALIDATE_BUFFER_BIT | - (coherent ? GL_MAP_COHERENT_BIT : GL_MAP_FLUSH_EXPLICIT_BIT); mapped_ptr = static_cast( glMapNamedBufferRange(gl_buffer.handle, buffer_pos, buffer_size - buffer_pos, flags)); mapped_offset = buffer_pos; @@ -74,7 +72,7 @@ std::tuple OGLStreamBuffer::Map(GLsizeiptr size, GLintptr a void OGLStreamBuffer::Unmap(GLsizeiptr size) { ASSERT(size <= mapped_size); - if (!coherent && size > 0) { + if (size > 0) { glFlushMappedNamedBufferRange(gl_buffer.handle, buffer_pos - mapped_offset, size); } diff --git a/src/video_core/renderer_opengl/gl_stream_buffer.h b/src/video_core/renderer_opengl/gl_stream_buffer.h index 65c3da93f..826c2e361 100644 --- a/src/video_core/renderer_opengl/gl_stream_buffer.h +++ b/src/video_core/renderer_opengl/gl_stream_buffer.h @@ -13,7 +13,7 @@ namespace OpenGL { class OGLStreamBuffer : private NonCopyable { public: - explicit OGLStreamBuffer(GLsizeiptr size, bool vertex_data_usage, bool prefer_coherent = false); + explicit OGLStreamBuffer(GLsizeiptr size, bool vertex_data_usage); ~OGLStreamBuffer(); /* @@ -39,8 +39,6 @@ public: private: OGLBuffer gl_buffer; - bool coherent = false; - GLintptr buffer_pos = 0; GLsizeiptr buffer_size = 0; GLintptr mapped_offset = 0; From 73fb3a304b215abce3cfb1c0c5eb2b43740b65ed Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Thu, 18 Jun 2020 03:54:13 -0300 Subject: [PATCH 082/252] gl_device: Expose NV_vertex_buffer_unified_memory except on Turing Expose NV_vertex_buffer_unified_memory when the driver supports it. This commit adds a function the determine if a GL_RENDERER is a Turing GPU. This is required because on Turing GPUs Nvidia's driver crashes when the buffer is marked as resident or on DeleteBuffers. Without a synchronous debug output (single threaded driver), it's likely that the driver will crash in the first blocking call. --- src/video_core/renderer_opengl/gl_device.cpp | 26 +++++++++++++++++++- src/video_core/renderer_opengl/gl_device.h | 5 ++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp index 1011c7738..447a19595 100644 --- a/src/video_core/renderer_opengl/gl_device.cpp +++ b/src/video_core/renderer_opengl/gl_device.cpp @@ -188,16 +188,32 @@ bool IsASTCSupported() { return true; } +/// @brief Returns true when a GL_RENDERER is a Turing GPU +/// @param renderer GL_RENDERER string +bool IsTuring(std::string_view renderer) { + static constexpr std::array TURING_GPUS = { + "GTX 1650", "GTX 1660", "RTX 2060", "RTX 2070", + "RTX 2080", "TITAN RTX", "Quadro RTX 3000", "Quadro RTX 4000", + "Quadro RTX 5000", "Quadro RTX 6000", "Quadro RTX 8000", "Tesla T4", + }; + return std::any_of(TURING_GPUS.begin(), TURING_GPUS.end(), + [renderer](std::string_view candidate) { + return renderer.find(candidate) != std::string_view::npos; + }); +} + } // Anonymous namespace Device::Device() : max_uniform_buffers{BuildMaxUniformBuffers()}, base_bindings{BuildBaseBindings()} { const std::string_view vendor = reinterpret_cast(glGetString(GL_VENDOR)); + const std::string_view renderer = reinterpret_cast(glGetString(GL_RENDERER)); const std::string_view version = reinterpret_cast(glGetString(GL_VERSION)); const std::vector extensions = GetExtensions(); const bool is_nvidia = vendor == "NVIDIA Corporation"; const bool is_amd = vendor == "ATI Technologies Inc."; + const bool is_turing = is_nvidia && IsTuring(renderer); bool disable_fast_buffer_sub_data = false; if (is_nvidia && version == "4.6.0 NVIDIA 443.24") { @@ -221,8 +237,16 @@ Device::Device() has_variable_aoffi = TestVariableAoffi(); has_component_indexing_bug = is_amd; has_precise_bug = TestPreciseBug(); - has_fast_buffer_sub_data = is_nvidia && !disable_fast_buffer_sub_data; has_nv_viewport_array2 = GLAD_GL_NV_viewport_array2; + + // At the moment of writing this, only Nvidia's driver optimizes BufferSubData on exclusive + // uniform buffers as "push constants" + has_fast_buffer_sub_data = is_nvidia && !disable_fast_buffer_sub_data; + + // Nvidia's driver on Turing GPUs randomly crashes when the buffer is made resident, or on + // DeleteBuffers. Disable unified memory on these devices. + has_vertex_buffer_unified_memory = GLAD_GL_NV_vertex_buffer_unified_memory && !is_turing; + use_assembly_shaders = Settings::values.use_assembly_shaders && GLAD_GL_NV_gpu_program5 && GLAD_GL_NV_compute_program5 && GLAD_GL_NV_transform_feedback && GLAD_GL_NV_transform_feedback2; diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h index c86e709b1..e1d811966 100644 --- a/src/video_core/renderer_opengl/gl_device.h +++ b/src/video_core/renderer_opengl/gl_device.h @@ -72,6 +72,10 @@ public: return has_texture_shadow_lod; } + bool HasVertexBufferUnifiedMemory() const { + return has_vertex_buffer_unified_memory; + } + bool HasASTC() const { return has_astc; } @@ -115,6 +119,7 @@ private: bool has_vertex_viewport_layer{}; bool has_image_load_formatted{}; bool has_texture_shadow_lod{}; + bool has_vertex_buffer_unified_memory{}; bool has_astc{}; bool has_variable_aoffi{}; bool has_component_indexing_bug{}; From 32485917ba7cb7b2f0cad766c0897365294650a7 Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Mon, 11 May 2020 16:35:04 -0300 Subject: [PATCH 083/252] gl_buffer_cache: Mark buffers as resident Make stream buffer and cached buffers as resident and query their address. This allows us to use GPU addresses for several proprietary Nvidia extensions. --- src/video_core/buffer_cache/buffer_cache.h | 21 +++++---- .../renderer_opengl/gl_buffer_cache.cpp | 24 ++++++---- .../renderer_opengl/gl_buffer_cache.h | 20 ++++++--- .../renderer_opengl/gl_rasterizer.cpp | 44 +++++++++---------- .../renderer_opengl/gl_stream_buffer.cpp | 11 ++++- .../renderer_opengl/gl_stream_buffer.h | 11 ++++- .../renderer_vulkan/vk_buffer_cache.cpp | 4 +- .../renderer_vulkan/vk_buffer_cache.h | 6 ++- .../renderer_vulkan/vk_rasterizer.cpp | 31 +++++++------ .../renderer_vulkan/vk_stream_buffer.h | 6 ++- 10 files changed, 111 insertions(+), 67 deletions(-) diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index bae1d527c..6ea59253a 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -41,7 +41,11 @@ class BufferCache { static constexpr u64 BLOCK_PAGE_SIZE = 1ULL << BLOCK_PAGE_BITS; public: - using BufferInfo = std::pair; + struct BufferInfo { + BufferType handle; + u64 offset; + u64 address; + }; BufferInfo UploadMemory(GPUVAddr gpu_addr, std::size_t size, std::size_t alignment = 4, bool is_written = false, bool use_fast_cbuf = false) { @@ -50,7 +54,7 @@ public: auto& memory_manager = system.GPU().MemoryManager(); const std::optional cpu_addr_opt = memory_manager.GpuToCpuAddress(gpu_addr); if (!cpu_addr_opt) { - return {GetEmptyBuffer(size), 0}; + return GetEmptyBuffer(size); } const VAddr cpu_addr = *cpu_addr_opt; @@ -88,7 +92,7 @@ public: Buffer* const block = GetBlock(cpu_addr, size); MapInterval* const map = MapAddress(block, gpu_addr, cpu_addr, size); if (!map) { - return {GetEmptyBuffer(size), 0}; + return GetEmptyBuffer(size); } if (is_written) { map->MarkAsModified(true, GetModifiedTicks()); @@ -101,7 +105,7 @@ public: } } - return {block->Handle(), static_cast(block->Offset(cpu_addr))}; + return BufferInfo{block->Handle(), block->Offset(cpu_addr), block->Address()}; } /// Uploads from a host memory. Returns the OpenGL buffer where it's located and its offset. @@ -254,13 +258,12 @@ public: committed_flushes.pop_front(); } - virtual BufferType GetEmptyBuffer(std::size_t size) = 0; + virtual BufferInfo GetEmptyBuffer(std::size_t size) = 0; protected: explicit BufferCache(VideoCore::RasterizerInterface& rasterizer, Core::System& system, - std::unique_ptr stream_buffer_) - : rasterizer{rasterizer}, system{system}, stream_buffer{std::move(stream_buffer_)}, - stream_buffer_handle{stream_buffer->Handle()} {} + std::unique_ptr stream_buffer) + : rasterizer{rasterizer}, system{system}, stream_buffer{std::move(stream_buffer)} {} ~BufferCache() = default; @@ -449,7 +452,7 @@ private: buffer_ptr += size; buffer_offset += size; - return {stream_buffer_handle, uploaded_offset}; + return BufferInfo{stream_buffer->Handle(), uploaded_offset, stream_buffer->Address()}; } void AlignBuffer(std::size_t alignment) { diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp index ad0577a4f..e09b47f57 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp @@ -22,21 +22,28 @@ using Maxwell = Tegra::Engines::Maxwell3D::Regs; MICROPROFILE_DEFINE(OpenGL_Buffer_Download, "OpenGL", "Buffer Download", MP_RGB(192, 192, 128)); -Buffer::Buffer(VAddr cpu_addr, const std::size_t size) : VideoCommon::BufferBlock{cpu_addr, size} { +Buffer::Buffer(const Device& device, VAddr cpu_addr, std::size_t size) + : VideoCommon::BufferBlock{cpu_addr, size} { gl_buffer.Create(); glNamedBufferData(gl_buffer.handle, static_cast(size), nullptr, GL_DYNAMIC_DRAW); + if (device.HasVertexBufferUnifiedMemory()) { + glMakeNamedBufferResidentNV(gl_buffer.handle, GL_READ_WRITE); + glGetNamedBufferParameterui64vNV(gl_buffer.handle, GL_BUFFER_GPU_ADDRESS_NV, &gpu_address); + } } Buffer::~Buffer() = default; OGLBufferCache::OGLBufferCache(RasterizerOpenGL& rasterizer, Core::System& system, - const Device& device, std::size_t stream_size) - : GenericBufferCache{rasterizer, system, std::make_unique(stream_size, true)} { + const Device& device_, std::size_t stream_size) + : GenericBufferCache{rasterizer, system, + std::make_unique(device_, stream_size, true)}, + device{device_} { if (!device.HasFastBufferSubData()) { return; } - static constexpr auto size = static_cast(Maxwell::MaxConstBufferSize); + static constexpr GLsizeiptr size = static_cast(Maxwell::MaxConstBufferSize); glCreateBuffers(static_cast(std::size(cbufs)), std::data(cbufs)); for (const GLuint cbuf : cbufs) { glNamedBufferData(cbuf, size, nullptr, GL_STREAM_DRAW); @@ -48,11 +55,11 @@ OGLBufferCache::~OGLBufferCache() { } std::shared_ptr OGLBufferCache::CreateBlock(VAddr cpu_addr, std::size_t size) { - return std::make_shared(cpu_addr, size); + return std::make_shared(device, cpu_addr, size); } -GLuint OGLBufferCache::GetEmptyBuffer(std::size_t) { - return 0; +OGLBufferCache::BufferInfo OGLBufferCache::GetEmptyBuffer(std::size_t) { + return {0, 0, 0}; } void OGLBufferCache::UploadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size, @@ -79,8 +86,9 @@ OGLBufferCache::BufferInfo OGLBufferCache::ConstBufferUpload(const void* raw_poi std::size_t size) { DEBUG_ASSERT(cbuf_cursor < std::size(cbufs)); const GLuint cbuf = cbufs[cbuf_cursor++]; + glNamedBufferSubData(cbuf, 0, static_cast(size), raw_pointer); - return {cbuf, 0}; + return {cbuf, 0, 0}; } } // namespace OpenGL diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h index a49aaf9c4..6462cfae5 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.h +++ b/src/video_core/renderer_opengl/gl_buffer_cache.h @@ -25,15 +25,20 @@ class RasterizerOpenGL; class Buffer : public VideoCommon::BufferBlock { public: - explicit Buffer(VAddr cpu_addr, const std::size_t size); + explicit Buffer(const Device& device, VAddr cpu_addr, std::size_t size); ~Buffer(); - GLuint Handle() const { + GLuint Handle() const noexcept { return gl_buffer.handle; } + u64 Address() const noexcept { + return gpu_address; + } + private: OGLBuffer gl_buffer; + u64 gpu_address = 0; }; using GenericBufferCache = VideoCommon::BufferCache; @@ -43,7 +48,7 @@ public: const Device& device, std::size_t stream_size); ~OGLBufferCache(); - GLuint GetEmptyBuffer(std::size_t) override; + BufferInfo GetEmptyBuffer(std::size_t) override; void Acquire() noexcept { cbuf_cursor = 0; @@ -64,10 +69,13 @@ protected: BufferInfo ConstBufferUpload(const void* raw_pointer, std::size_t size) override; private: + static constexpr std::size_t NUM_CBUFS = Tegra::Engines::Maxwell3D::Regs::MaxConstBuffers * + Tegra::Engines::Maxwell3D::Regs::MaxShaderProgram; + + const Device& device; + std::size_t cbuf_cursor = 0; - std::array - cbufs; + std::array cbufs{}; }; } // namespace OpenGL diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 2d6c11320..7cb378a71 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -253,8 +253,8 @@ void RasterizerOpenGL::SetupVertexBuffer() { glBindVertexBuffer(static_cast(index), 0, 0, vertex_array.stride); continue; } - const auto [vertex_buffer, vertex_buffer_offset] = buffer_cache.UploadMemory(start, size); - glBindVertexBuffer(static_cast(index), vertex_buffer, vertex_buffer_offset, + const auto info = buffer_cache.UploadMemory(start, size); + glBindVertexBuffer(static_cast(index), info.handle, info.offset, vertex_array.stride); } } @@ -285,9 +285,9 @@ GLintptr RasterizerOpenGL::SetupIndexBuffer() { MICROPROFILE_SCOPE(OpenGL_Index); const auto& regs = system.GPU().Maxwell3D().regs; const std::size_t size = CalculateIndexBufferSize(); - const auto [buffer, offset] = buffer_cache.UploadMemory(regs.index_array.IndexStart(), size); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer); - return offset; + const auto info = buffer_cache.UploadMemory(regs.index_array.IndexStart(), size); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, info.handle); + return info.offset; } void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) { @@ -643,9 +643,9 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) { if (!device.UseAssemblyShaders()) { MaxwellUniformData ubo; ubo.SetFromRegs(gpu); - const auto [buffer, offset] = + const auto info = buffer_cache.UploadHostMemory(&ubo, sizeof(ubo), device.GetUniformBufferAlignment()); - glBindBufferRange(GL_UNIFORM_BUFFER, EmulationUniformBlockBinding, buffer, offset, + glBindBufferRange(GL_UNIFORM_BUFFER, EmulationUniformBlockBinding, info.handle, info.offset, static_cast(sizeof(ubo))); } @@ -956,8 +956,7 @@ void RasterizerOpenGL::SetupConstBuffer(GLenum stage, u32 binding, if (device.UseAssemblyShaders()) { glBindBufferRangeNV(stage, entry.GetIndex(), 0, 0, 0); } else { - glBindBufferRange(GL_UNIFORM_BUFFER, binding, - buffer_cache.GetEmptyBuffer(sizeof(float)), 0, sizeof(float)); + glBindBufferRange(GL_UNIFORM_BUFFER, binding, 0, 0, sizeof(float)); } return; } @@ -970,24 +969,25 @@ void RasterizerOpenGL::SetupConstBuffer(GLenum stage, u32 binding, const std::size_t alignment = use_unified ? 4 : device.GetUniformBufferAlignment(); const GPUVAddr gpu_addr = buffer.address; - auto [cbuf, offset] = buffer_cache.UploadMemory(gpu_addr, size, alignment, false, fast_upload); + auto info = buffer_cache.UploadMemory(gpu_addr, size, alignment, false, fast_upload); if (device.UseAssemblyShaders()) { UNIMPLEMENTED_IF(use_unified); - if (offset != 0) { + if (info.offset != 0) { const GLuint staging_cbuf = staging_cbufs[current_cbuf++]; - glCopyNamedBufferSubData(cbuf, staging_cbuf, offset, 0, size); - cbuf = staging_cbuf; - offset = 0; + glCopyNamedBufferSubData(info.handle, staging_cbuf, info.offset, 0, size); + info.handle = staging_cbuf; + info.offset = 0; } - glBindBufferRangeNV(stage, binding, cbuf, offset, size); + glBindBufferRangeNV(stage, binding, info.handle, info.offset, size); return; } if (use_unified) { - glCopyNamedBufferSubData(cbuf, unified_uniform_buffer.handle, offset, unified_offset, size); + glCopyNamedBufferSubData(info.handle, unified_uniform_buffer.handle, info.offset, + unified_offset, size); } else { - glBindBufferRange(GL_UNIFORM_BUFFER, binding, cbuf, offset, size); + glBindBufferRange(GL_UNIFORM_BUFFER, binding, info.handle, info.offset, size); } } @@ -1023,9 +1023,8 @@ void RasterizerOpenGL::SetupComputeGlobalMemory(Shader* kernel) { void RasterizerOpenGL::SetupGlobalMemory(u32 binding, const GlobalMemoryEntry& entry, GPUVAddr gpu_addr, std::size_t size) { const auto alignment{device.GetShaderStorageBufferAlignment()}; - const auto [ssbo, buffer_offset] = - buffer_cache.UploadMemory(gpu_addr, size, alignment, entry.is_written); - glBindBufferRange(GL_SHADER_STORAGE_BUFFER, binding, ssbo, buffer_offset, + const auto info = buffer_cache.UploadMemory(gpu_addr, size, alignment, entry.is_written); + glBindBufferRange(GL_SHADER_STORAGE_BUFFER, binding, info.handle, info.offset, static_cast(size)); } @@ -1712,8 +1711,9 @@ void RasterizerOpenGL::EndTransformFeedback() { const GLuint handle = transform_feedback_buffers[index].handle; const GPUVAddr gpu_addr = binding.Address(); const std::size_t size = binding.buffer_size; - const auto [dest_buffer, offset] = buffer_cache.UploadMemory(gpu_addr, size, 4, true); - glCopyNamedBufferSubData(handle, dest_buffer, 0, offset, static_cast(size)); + const auto info = buffer_cache.UploadMemory(gpu_addr, size, 4, true); + glCopyNamedBufferSubData(handle, info.handle, 0, info.offset, + static_cast(size)); } } diff --git a/src/video_core/renderer_opengl/gl_stream_buffer.cpp b/src/video_core/renderer_opengl/gl_stream_buffer.cpp index aeafcfbfe..164df4feb 100644 --- a/src/video_core/renderer_opengl/gl_stream_buffer.cpp +++ b/src/video_core/renderer_opengl/gl_stream_buffer.cpp @@ -2,12 +2,13 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include +#include #include #include "common/alignment.h" #include "common/assert.h" #include "common/microprofile.h" +#include "video_core/renderer_opengl/gl_device.h" #include "video_core/renderer_opengl/gl_stream_buffer.h" MICROPROFILE_DEFINE(OpenGL_StreamBuffer, "OpenGL", "Stream Buffer Orphaning", @@ -15,7 +16,8 @@ MICROPROFILE_DEFINE(OpenGL_StreamBuffer, "OpenGL", "Stream Buffer Orphaning", namespace OpenGL { -OGLStreamBuffer::OGLStreamBuffer(GLsizeiptr size, bool vertex_data_usage) : buffer_size(size) { +OGLStreamBuffer::OGLStreamBuffer(const Device& device, GLsizeiptr size, bool vertex_data_usage) + : buffer_size(size) { gl_buffer.Create(); GLsizeiptr allocate_size = size; @@ -32,6 +34,11 @@ OGLStreamBuffer::OGLStreamBuffer(GLsizeiptr size, bool vertex_data_usage) : buff glNamedBufferStorage(gl_buffer.handle, allocate_size, nullptr, flags); mapped_ptr = static_cast( glMapNamedBufferRange(gl_buffer.handle, 0, buffer_size, flags | GL_MAP_FLUSH_EXPLICIT_BIT)); + + if (device.HasVertexBufferUnifiedMemory()) { + glMakeNamedBufferResidentNV(gl_buffer.handle, GL_READ_ONLY); + glGetNamedBufferParameterui64vNV(gl_buffer.handle, GL_BUFFER_GPU_ADDRESS_NV, &gpu_address); + } } OGLStreamBuffer::~OGLStreamBuffer() { diff --git a/src/video_core/renderer_opengl/gl_stream_buffer.h b/src/video_core/renderer_opengl/gl_stream_buffer.h index 826c2e361..e67a82980 100644 --- a/src/video_core/renderer_opengl/gl_stream_buffer.h +++ b/src/video_core/renderer_opengl/gl_stream_buffer.h @@ -11,9 +11,11 @@ namespace OpenGL { +class Device; + class OGLStreamBuffer : private NonCopyable { public: - explicit OGLStreamBuffer(GLsizeiptr size, bool vertex_data_usage); + explicit OGLStreamBuffer(const Device& device, GLsizeiptr size, bool vertex_data_usage); ~OGLStreamBuffer(); /* @@ -32,13 +34,18 @@ public: return gl_buffer.handle; } - GLsizeiptr Size() const { + u64 Address() const { + return gpu_address; + } + + GLsizeiptr Size() const noexcept { return buffer_size; } private: OGLBuffer gl_buffer; + GLuint64EXT gpu_address = 0; GLintptr buffer_pos = 0; GLsizeiptr buffer_size = 0; GLintptr mapped_offset = 0; diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index 1fde38328..df258d7a4 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp @@ -71,14 +71,14 @@ std::shared_ptr VKBufferCache::CreateBlock(VAddr cpu_addr, std::size_t s return std::make_shared(device, memory_manager, cpu_addr, size); } -VkBuffer VKBufferCache::GetEmptyBuffer(std::size_t size) { +VKBufferCache::BufferInfo VKBufferCache::GetEmptyBuffer(std::size_t size) { size = std::max(size, std::size_t(4)); const auto& empty = staging_pool.GetUnusedBuffer(size, false); scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([size, buffer = *empty.handle](vk::CommandBuffer cmdbuf) { cmdbuf.FillBuffer(buffer, 0, size, 0); }); - return *empty.handle; + return {*empty.handle, 0, 0}; } void VKBufferCache::UploadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size, diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h index 9ebbef835..682383ff2 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.h +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h @@ -33,6 +33,10 @@ public: return *buffer.handle; } + u64 Address() const { + return 0; + } + private: VKBuffer buffer; }; @@ -44,7 +48,7 @@ public: VKScheduler& scheduler, VKStagingBufferPool& staging_pool); ~VKBufferCache(); - VkBuffer GetEmptyBuffer(std::size_t size) override; + BufferInfo GetEmptyBuffer(std::size_t size) override; protected: std::shared_ptr CreateBlock(VAddr cpu_addr, std::size_t size) override; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 29001953c..e3714ee6d 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -870,10 +870,10 @@ void RasterizerVulkan::BeginTransformFeedback() { UNIMPLEMENTED_IF(binding.buffer_offset != 0); const GPUVAddr gpu_addr = binding.Address(); - const auto size = static_cast(binding.buffer_size); - const auto [buffer, offset] = buffer_cache.UploadMemory(gpu_addr, size, 4, true); + const VkDeviceSize size = static_cast(binding.buffer_size); + const auto info = buffer_cache.UploadMemory(gpu_addr, size, 4, true); - scheduler.Record([buffer = buffer, offset = offset, size](vk::CommandBuffer cmdbuf) { + scheduler.Record([buffer = info.handle, offset = info.offset, size](vk::CommandBuffer cmdbuf) { cmdbuf.BindTransformFeedbackBuffersEXT(0, 1, &buffer, &offset, &size); cmdbuf.BeginTransformFeedbackEXT(0, 0, nullptr, nullptr); }); @@ -925,8 +925,8 @@ void RasterizerVulkan::SetupVertexArrays(FixedPipelineState::VertexInput& vertex buffer_bindings.AddVertexBinding(DefaultBuffer(), 0); continue; } - const auto [buffer, offset] = buffer_cache.UploadMemory(start, size); - buffer_bindings.AddVertexBinding(buffer, offset); + const auto info = buffer_cache.UploadMemory(start, size); + buffer_bindings.AddVertexBinding(info.handle, info.offset); } } @@ -948,7 +948,9 @@ void RasterizerVulkan::SetupIndexBuffer(BufferBindings& buffer_bindings, DrawPar break; } const GPUVAddr gpu_addr = regs.index_array.IndexStart(); - auto [buffer, offset] = buffer_cache.UploadMemory(gpu_addr, CalculateIndexBufferSize()); + const auto info = buffer_cache.UploadMemory(gpu_addr, CalculateIndexBufferSize()); + VkBuffer buffer = info.handle; + u64 offset = info.offset; std::tie(buffer, offset) = quad_indexed_pass.Assemble( regs.index_array.format, params.num_vertices, params.base_vertex, buffer, offset); @@ -962,7 +964,9 @@ void RasterizerVulkan::SetupIndexBuffer(BufferBindings& buffer_bindings, DrawPar break; } const GPUVAddr gpu_addr = regs.index_array.IndexStart(); - auto [buffer, offset] = buffer_cache.UploadMemory(gpu_addr, CalculateIndexBufferSize()); + const auto info = buffer_cache.UploadMemory(gpu_addr, CalculateIndexBufferSize()); + VkBuffer buffer = info.handle; + u64 offset = info.offset; auto format = regs.index_array.format; const bool is_uint8 = format == Maxwell::IndexFormat::UnsignedByte; @@ -1109,10 +1113,9 @@ void RasterizerVulkan::SetupConstBuffer(const ConstBufferEntry& entry, Common::AlignUp(CalculateConstBufferSize(entry, buffer), 4 * sizeof(float)); ASSERT(size <= MaxConstbufferSize); - const auto [buffer_handle, offset] = + const auto info = buffer_cache.UploadMemory(buffer.address, size, device.GetUniformBufferAlignment()); - - update_descriptor_queue.AddBuffer(buffer_handle, offset, size); + update_descriptor_queue.AddBuffer(info.handle, info.offset, size); } void RasterizerVulkan::SetupGlobalBuffer(const GlobalBufferEntry& entry, GPUVAddr address) { @@ -1126,14 +1129,14 @@ void RasterizerVulkan::SetupGlobalBuffer(const GlobalBufferEntry& entry, GPUVAdd // Note: Do *not* use DefaultBuffer() here, storage buffers can be written breaking the // default buffer. static constexpr std::size_t dummy_size = 4; - const auto buffer = buffer_cache.GetEmptyBuffer(dummy_size); - update_descriptor_queue.AddBuffer(buffer, 0, dummy_size); + const auto info = buffer_cache.GetEmptyBuffer(dummy_size); + update_descriptor_queue.AddBuffer(info.handle, info.offset, dummy_size); return; } - const auto [buffer, offset] = buffer_cache.UploadMemory( + const auto info = buffer_cache.UploadMemory( actual_addr, size, device.GetStorageBufferAlignment(), entry.IsWritten()); - update_descriptor_queue.AddBuffer(buffer, offset, size); + update_descriptor_queue.AddBuffer(info.handle, info.offset, size); } void RasterizerVulkan::SetupUniformTexels(const Tegra::Texture::TICEntry& tic, diff --git a/src/video_core/renderer_vulkan/vk_stream_buffer.h b/src/video_core/renderer_vulkan/vk_stream_buffer.h index c765c60a0..689f0d276 100644 --- a/src/video_core/renderer_vulkan/vk_stream_buffer.h +++ b/src/video_core/renderer_vulkan/vk_stream_buffer.h @@ -35,10 +35,14 @@ public: /// Ensures that "size" bytes of memory are available to the GPU, potentially recording a copy. void Unmap(u64 size); - VkBuffer Handle() const { + VkBuffer Handle() const noexcept { return *buffer; } + u64 Address() const noexcept { + return 0; + } + private: struct Watch final { VKFenceWatch fence; From 41a4090320ee52e914e8b4c789dfe14210794fed Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Thu, 18 Jun 2020 03:56:31 -0300 Subject: [PATCH 084/252] gl_rasterizer: Use NV_vertex_buffer_unified_memory for vertex buffer robustness Switch games are allowed to bind less data than what they use in a vertex buffer, the expected behavior here is that these values are read as zero. At the moment of writing this only D3D12, OpenGL and NVN through NV_vertex_buffer_unified_memory support vertex buffer with a size limit. In theory this could be emulated on Vulkan creating a new VkBuffer for each (handle, offset, length) tuple and binding the expected data to it. This is likely going to be slow and memory expensive when used on the vertex buffer and we have to do it on all draws because we can't know without analyzing indices when a game is going to read vertex data out of bounds. This is not a problem on OpenGL's BufferAddressRangeNV because it takes a length parameter, unlike Vulkan's CmdBindVertexBuffers that only takes buffers and offsets (the length is implicit in VkBuffer). It isn't a problem on D3D12 either, because D3D12_VERTEX_BUFFER_VIEW on IASetVertexBuffers takes SizeInBytes as a parameter (although I am not familiar with robustness on D3D12). Currently this only implements buffer ranges for vertex buffers, although indices can also be affected. A KHR_robustness profile is not created, but Nvidia's driver reads out of bound vertex data as zero anyway, this might have to be changed in the future. - Fixes SMO random triangles when capturing an enemy, getting hit, or looking at the environment on certain maps. --- .../renderer_opengl/gl_rasterizer.cpp | 28 +++++++++++++------ .../renderer_opengl/renderer_opengl.cpp | 17 ++++++++++- .../renderer_opengl/renderer_opengl.h | 3 ++ 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 7cb378a71..362457ffe 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -61,7 +61,8 @@ constexpr std::size_t NUM_CONST_BUFFERS_BYTES_PER_STAGE = constexpr std::size_t TOTAL_CONST_BUFFER_BYTES = NUM_CONST_BUFFERS_BYTES_PER_STAGE * Maxwell::MaxShaderStage; -constexpr std::size_t NumSupportedVertexAttributes = 16; +constexpr std::size_t NUM_SUPPORTED_VERTEX_ATTRIBUTES = 16; +constexpr std::size_t NUM_SUPPORTED_VERTEX_BINDINGS = 16; template Tegra::Texture::FullTextureInfo GetTextureInfo(const Engine& engine, const Entry& entry, @@ -193,7 +194,7 @@ void RasterizerOpenGL::SetupVertexFormat() { // avoid OpenGL errors. // TODO(Subv): Analyze the shader to identify which attributes are actually used and don't // assume every shader uses them all. - for (std::size_t index = 0; index < NumSupportedVertexAttributes; ++index) { + for (std::size_t index = 0; index < NUM_SUPPORTED_VERTEX_ATTRIBUTES; ++index) { if (!flags[Dirty::VertexFormat0 + index]) { continue; } @@ -231,9 +232,11 @@ void RasterizerOpenGL::SetupVertexBuffer() { MICROPROFILE_SCOPE(OpenGL_VB); + const bool use_unified_memory = device.HasVertexBufferUnifiedMemory(); + // Upload all guest vertex arrays sequentially to our buffer const auto& regs = gpu.regs; - for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) { + for (std::size_t index = 0; index < NUM_SUPPORTED_VERTEX_BINDINGS; ++index) { if (!flags[Dirty::VertexBuffer0 + index]) { continue; } @@ -246,16 +249,25 @@ void RasterizerOpenGL::SetupVertexBuffer() { const GPUVAddr start = vertex_array.StartAddress(); const GPUVAddr end = regs.vertex_array_limit[index].LimitAddress(); - ASSERT(end >= start); + + const GLuint gl_index = static_cast(index); const u64 size = end - start; if (size == 0) { - glBindVertexBuffer(static_cast(index), 0, 0, vertex_array.stride); + glBindVertexBuffer(gl_index, 0, 0, vertex_array.stride); + if (use_unified_memory) { + glBufferAddressRangeNV(GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV, gl_index, 0, 0); + } continue; } const auto info = buffer_cache.UploadMemory(start, size); - glBindVertexBuffer(static_cast(index), info.handle, info.offset, - vertex_array.stride); + if (use_unified_memory) { + glBindVertexBuffer(gl_index, 0, 0, vertex_array.stride); + glBufferAddressRangeNV(GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV, gl_index, + info.address + info.offset, size); + } else { + glBindVertexBuffer(gl_index, info.handle, info.offset, vertex_array.stride); + } } } @@ -268,7 +280,7 @@ void RasterizerOpenGL::SetupVertexInstances() { flags[Dirty::VertexInstances] = false; const auto& regs = gpu.regs; - for (std::size_t index = 0; index < NumSupportedVertexAttributes; ++index) { + for (std::size_t index = 0; index < NUM_SUPPORTED_VERTEX_ATTRIBUTES; ++index) { if (!flags[Dirty::VertexInstance0 + index]) { continue; } diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 6214fcbc3..c40adb6e7 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -488,6 +488,15 @@ void RendererOpenGL::InitOpenGLObjects() { // Clear screen to black LoadColorToActiveGLTexture(0, 0, 0, 0, screen_info.texture); + + // Enable unified vertex attributes and query vertex buffer address when the driver supports it + if (device.HasVertexBufferUnifiedMemory()) { + glEnableClientState(GL_VERTEX_ATTRIB_ARRAY_UNIFIED_NV); + + glMakeNamedBufferResidentNV(vertex_buffer.handle, GL_READ_ONLY); + glGetNamedBufferParameterui64vNV(vertex_buffer.handle, GL_BUFFER_GPU_ADDRESS_NV, + &vertex_buffer_address); + } } void RendererOpenGL::AddTelemetryFields() { @@ -656,7 +665,13 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) { offsetof(ScreenRectVertex, tex_coord)); glVertexAttribBinding(PositionLocation, 0); glVertexAttribBinding(TexCoordLocation, 0); - glBindVertexBuffer(0, vertex_buffer.handle, 0, sizeof(ScreenRectVertex)); + if (device.HasVertexBufferUnifiedMemory()) { + glBindVertexBuffer(0, 0, 0, sizeof(ScreenRectVertex)); + glBufferAddressRangeNV(GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV, 0, vertex_buffer_address, + sizeof(vertices)); + } else { + glBindVertexBuffer(0, vertex_buffer.handle, 0, sizeof(ScreenRectVertex)); + } glBindTextureUnit(0, screen_info.display_texture); glBindSampler(0, 0); diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index 61bf507f4..8b18d32e6 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h @@ -107,6 +107,9 @@ private: OGLPipeline pipeline; OGLFramebuffer screenshot_framebuffer; + // GPU address of the vertex buffer + GLuint64EXT vertex_buffer_address = 0; + /// Display information for Switch screen ScreenInfo screen_info; From 39c97f1b652898dbd0e5e6d028de2ba4b9fa94a0 Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Thu, 18 Jun 2020 21:53:47 -0300 Subject: [PATCH 085/252] gl_stream_buffer: Use InvalidateBufferData instead unmap and map Making the stream buffer resident increases GPU usage significantly on some games. This seems to be addressed invalidating the stream buffer with InvalidateBufferData instead of using a Unmap + Map (with invalidation flags). --- .../renderer_opengl/gl_stream_buffer.cpp | 19 +++++-------------- .../renderer_opengl/gl_stream_buffer.h | 1 - 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_stream_buffer.cpp b/src/video_core/renderer_opengl/gl_stream_buffer.cpp index 164df4feb..3655ff629 100644 --- a/src/video_core/renderer_opengl/gl_stream_buffer.cpp +++ b/src/video_core/renderer_opengl/gl_stream_buffer.cpp @@ -57,30 +57,21 @@ std::tuple OGLStreamBuffer::Map(GLsizeiptr size, GLintptr a bool invalidate = false; if (buffer_pos + size > buffer_size) { + MICROPROFILE_SCOPE(OpenGL_StreamBuffer); + glInvalidateBufferData(gl_buffer.handle); + buffer_pos = 0; invalidate = true; - - glUnmapNamedBuffer(gl_buffer.handle); } - if (invalidate) { - static const GLbitfield flags = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | - GL_MAP_INVALIDATE_BUFFER_BIT | GL_MAP_FLUSH_EXPLICIT_BIT; - - MICROPROFILE_SCOPE(OpenGL_StreamBuffer); - mapped_ptr = static_cast( - glMapNamedBufferRange(gl_buffer.handle, buffer_pos, buffer_size - buffer_pos, flags)); - mapped_offset = buffer_pos; - } - - return std::make_tuple(mapped_ptr + buffer_pos - mapped_offset, buffer_pos, invalidate); + return std::make_tuple(mapped_ptr + buffer_pos, buffer_pos, invalidate); } void OGLStreamBuffer::Unmap(GLsizeiptr size) { ASSERT(size <= mapped_size); if (size > 0) { - glFlushMappedNamedBufferRange(gl_buffer.handle, buffer_pos - mapped_offset, size); + glFlushMappedNamedBufferRange(gl_buffer.handle, buffer_pos, size); } buffer_pos += size; diff --git a/src/video_core/renderer_opengl/gl_stream_buffer.h b/src/video_core/renderer_opengl/gl_stream_buffer.h index e67a82980..307a67113 100644 --- a/src/video_core/renderer_opengl/gl_stream_buffer.h +++ b/src/video_core/renderer_opengl/gl_stream_buffer.h @@ -48,7 +48,6 @@ private: GLuint64EXT gpu_address = 0; GLintptr buffer_pos = 0; GLsizeiptr buffer_size = 0; - GLintptr mapped_offset = 0; GLsizeiptr mapped_size = 0; u8* mapped_ptr = nullptr; }; From 32a2dcd4153f4e2aea7b5f88c85d8a352f647f12 Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Fri, 19 Jun 2020 20:47:48 -0300 Subject: [PATCH 086/252] buffer_cache: Use buffer methods instead of cache virtual methods --- src/video_core/buffer_cache/buffer_cache.h | 23 ++--- .../renderer_opengl/gl_buffer_cache.cpp | 38 ++++---- .../renderer_opengl/gl_buffer_cache.h | 16 ++-- .../renderer_vulkan/vk_buffer_cache.cpp | 89 ++++++++++--------- .../renderer_vulkan/vk_buffer_cache.h | 23 ++--- 5 files changed, 90 insertions(+), 99 deletions(-) diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 6ea59253a..cf8bdd021 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -269,15 +269,6 @@ protected: virtual std::shared_ptr CreateBlock(VAddr cpu_addr, std::size_t size) = 0; - virtual void UploadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size, - const u8* data) = 0; - - virtual void DownloadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size, - u8* data) = 0; - - virtual void CopyBlock(const Buffer& src, const Buffer& dst, std::size_t src_offset, - std::size_t dst_offset, std::size_t size) = 0; - virtual BufferInfo ConstBufferUpload(const void* raw_pointer, std::size_t size) { return {}; } @@ -339,11 +330,11 @@ private: const VAddr cpu_addr_end = cpu_addr + size; if (memory_manager.IsGranularRange(gpu_addr, size)) { u8* host_ptr = memory_manager.GetPointer(gpu_addr); - UploadBlockData(*block, block->Offset(cpu_addr), size, host_ptr); + block->Upload(block->Offset(cpu_addr), size, host_ptr); } else { staging_buffer.resize(size); memory_manager.ReadBlockUnsafe(gpu_addr, staging_buffer.data(), size); - UploadBlockData(*block, block->Offset(cpu_addr), size, staging_buffer.data()); + block->Upload(block->Offset(cpu_addr), size, staging_buffer.data()); } return Register(MapInterval(cpu_addr, cpu_addr_end, gpu_addr)); } @@ -402,7 +393,7 @@ private: } staging_buffer.resize(size); system.Memory().ReadBlockUnsafe(interval.lower(), staging_buffer.data(), size); - UploadBlockData(*block, block->Offset(interval.lower()), size, staging_buffer.data()); + block->Upload(block->Offset(interval.lower()), size, staging_buffer.data()); } } @@ -439,7 +430,7 @@ private: const std::size_t size = map->end - map->start; staging_buffer.resize(size); - DownloadBlockData(*block, block->Offset(map->start), size, staging_buffer.data()); + block->Download(block->Offset(map->start), size, staging_buffer.data()); system.Memory().WriteBlockUnsafe(map->start, staging_buffer.data(), size); map->MarkAsModified(false, 0); } @@ -467,7 +458,7 @@ private: const std::size_t new_size = old_size + BLOCK_PAGE_SIZE; const VAddr cpu_addr = buffer->CpuAddr(); std::shared_ptr new_buffer = CreateBlock(cpu_addr, new_size); - CopyBlock(*buffer, *new_buffer, 0, 0, old_size); + new_buffer->CopyFrom(*buffer, 0, 0, old_size); QueueDestruction(std::move(buffer)); const VAddr cpu_addr_end = cpu_addr + new_size - 1; @@ -489,8 +480,8 @@ private: const std::size_t new_size = size_1 + size_2; std::shared_ptr new_buffer = CreateBlock(new_addr, new_size); - CopyBlock(*first, *new_buffer, 0, new_buffer->Offset(first_addr), size_1); - CopyBlock(*second, *new_buffer, 0, new_buffer->Offset(second_addr), size_2); + new_buffer->CopyFrom(*first, 0, new_buffer->Offset(first_addr), size_1); + new_buffer->CopyFrom(*second, 0, new_buffer->Offset(second_addr), size_2); QueueDestruction(std::move(first)); QueueDestruction(std::move(second)); diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp index e09b47f57..d9f7b4cc6 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp @@ -34,6 +34,24 @@ Buffer::Buffer(const Device& device, VAddr cpu_addr, std::size_t size) Buffer::~Buffer() = default; +void Buffer::Upload(std::size_t offset, std::size_t size, const u8* data) const { + glNamedBufferSubData(Handle(), static_cast(offset), static_cast(size), + data); +} + +void Buffer::Download(std::size_t offset, std::size_t size, u8* data) const { + MICROPROFILE_SCOPE(OpenGL_Buffer_Download); + glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); + glGetNamedBufferSubData(Handle(), static_cast(offset), static_cast(size), + data); +} + +void Buffer::CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset, + std::size_t size) const { + glCopyNamedBufferSubData(src.Handle(), Handle(), static_cast(src_offset), + static_cast(dst_offset), static_cast(size)); +} + OGLBufferCache::OGLBufferCache(RasterizerOpenGL& rasterizer, Core::System& system, const Device& device_, std::size_t stream_size) : GenericBufferCache{rasterizer, system, @@ -62,26 +80,6 @@ OGLBufferCache::BufferInfo OGLBufferCache::GetEmptyBuffer(std::size_t) { return {0, 0, 0}; } -void OGLBufferCache::UploadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size, - const u8* data) { - glNamedBufferSubData(buffer.Handle(), static_cast(offset), - static_cast(size), data); -} - -void OGLBufferCache::DownloadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size, - u8* data) { - MICROPROFILE_SCOPE(OpenGL_Buffer_Download); - glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); - glGetNamedBufferSubData(buffer.Handle(), static_cast(offset), - static_cast(size), data); -} - -void OGLBufferCache::CopyBlock(const Buffer& src, const Buffer& dst, std::size_t src_offset, - std::size_t dst_offset, std::size_t size) { - glCopyNamedBufferSubData(src.Handle(), dst.Handle(), static_cast(src_offset), - static_cast(dst_offset), static_cast(size)); -} - OGLBufferCache::BufferInfo OGLBufferCache::ConstBufferUpload(const void* raw_pointer, std::size_t size) { DEBUG_ASSERT(cbuf_cursor < std::size(cbufs)); diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h index 6462cfae5..59d95adbc 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.h +++ b/src/video_core/renderer_opengl/gl_buffer_cache.h @@ -28,6 +28,13 @@ public: explicit Buffer(const Device& device, VAddr cpu_addr, std::size_t size); ~Buffer(); + void Upload(std::size_t offset, std::size_t size, const u8* data) const; + + void Download(std::size_t offset, std::size_t size, u8* data) const; + + void CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset, + std::size_t size) const; + GLuint Handle() const noexcept { return gl_buffer.handle; } @@ -57,15 +64,6 @@ public: protected: std::shared_ptr CreateBlock(VAddr cpu_addr, std::size_t size) override; - void UploadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size, - const u8* data) override; - - void DownloadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size, - u8* data) override; - - void CopyBlock(const Buffer& src, const Buffer& dst, std::size_t src_offset, - std::size_t dst_offset, std::size_t size) override; - BufferInfo ConstBufferUpload(const void* raw_pointer, std::size_t size) override; private: diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index df258d7a4..f10f96cd8 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp @@ -37,9 +37,9 @@ std::unique_ptr CreateStreamBuffer(const VKDevice& device, VKSch } // Anonymous namespace -Buffer::Buffer(const VKDevice& device, VKMemoryManager& memory_manager, VAddr cpu_addr, - std::size_t size) - : VideoCommon::BufferBlock{cpu_addr, size} { +Buffer::Buffer(const VKDevice& device, VKMemoryManager& memory_manager, VKScheduler& scheduler_, + VKStagingBufferPool& staging_pool_, VAddr cpu_addr, std::size_t size) + : VideoCommon::BufferBlock{cpu_addr, size}, scheduler{scheduler_}, staging_pool{staging_pool_} { VkBufferCreateInfo ci; ci.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; ci.pNext = nullptr; @@ -56,40 +56,15 @@ Buffer::Buffer(const VKDevice& device, VKMemoryManager& memory_manager, VAddr cp Buffer::~Buffer() = default; -VKBufferCache::VKBufferCache(VideoCore::RasterizerInterface& rasterizer, Core::System& system, - const VKDevice& device, VKMemoryManager& memory_manager, - VKScheduler& scheduler, VKStagingBufferPool& staging_pool) - : VideoCommon::BufferCache{rasterizer, system, - CreateStreamBuffer(device, - scheduler)}, - device{device}, memory_manager{memory_manager}, scheduler{scheduler}, staging_pool{ - staging_pool} {} - -VKBufferCache::~VKBufferCache() = default; - -std::shared_ptr VKBufferCache::CreateBlock(VAddr cpu_addr, std::size_t size) { - return std::make_shared(device, memory_manager, cpu_addr, size); -} - -VKBufferCache::BufferInfo VKBufferCache::GetEmptyBuffer(std::size_t size) { - size = std::max(size, std::size_t(4)); - const auto& empty = staging_pool.GetUnusedBuffer(size, false); - scheduler.RequestOutsideRenderPassOperationContext(); - scheduler.Record([size, buffer = *empty.handle](vk::CommandBuffer cmdbuf) { - cmdbuf.FillBuffer(buffer, 0, size, 0); - }); - return {*empty.handle, 0, 0}; -} - -void VKBufferCache::UploadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size, - const u8* data) { +void Buffer::Upload(std::size_t offset, std::size_t size, const u8* data) const { const auto& staging = staging_pool.GetUnusedBuffer(size, true); std::memcpy(staging.commit->Map(size), data, size); scheduler.RequestOutsideRenderPassOperationContext(); - scheduler.Record([staging = *staging.handle, buffer = buffer.Handle(), offset, - size](vk::CommandBuffer cmdbuf) { - cmdbuf.CopyBuffer(staging, buffer, VkBufferCopy{0, offset, size}); + + const VkBuffer handle = Handle(); + scheduler.Record([staging = *staging.handle, handle, offset, size](vk::CommandBuffer cmdbuf) { + cmdbuf.CopyBuffer(staging, handle, VkBufferCopy{0, offset, size}); VkBufferMemoryBarrier barrier; barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; @@ -98,7 +73,7 @@ void VKBufferCache::UploadBlockData(const Buffer& buffer, std::size_t offset, st barrier.dstAccessMask = UPLOAD_ACCESS_BARRIERS; barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.buffer = buffer; + barrier.buffer = handle; barrier.offset = offset; barrier.size = size; cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, UPLOAD_PIPELINE_STAGE, 0, {}, @@ -106,12 +81,12 @@ void VKBufferCache::UploadBlockData(const Buffer& buffer, std::size_t offset, st }); } -void VKBufferCache::DownloadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size, - u8* data) { +void Buffer::Download(std::size_t offset, std::size_t size, u8* data) const { const auto& staging = staging_pool.GetUnusedBuffer(size, true); scheduler.RequestOutsideRenderPassOperationContext(); - scheduler.Record([staging = *staging.handle, buffer = buffer.Handle(), offset, - size](vk::CommandBuffer cmdbuf) { + + const VkBuffer handle = Handle(); + scheduler.Record([staging = *staging.handle, handle, offset, size](vk::CommandBuffer cmdbuf) { VkBufferMemoryBarrier barrier; barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; barrier.pNext = nullptr; @@ -119,7 +94,7 @@ void VKBufferCache::DownloadBlockData(const Buffer& buffer, std::size_t offset, barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.buffer = buffer; + barrier.buffer = handle; barrier.offset = offset; barrier.size = size; @@ -127,17 +102,19 @@ void VKBufferCache::DownloadBlockData(const Buffer& buffer, std::size_t offset, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, {}, barrier, {}); - cmdbuf.CopyBuffer(buffer, staging, VkBufferCopy{offset, 0, size}); + cmdbuf.CopyBuffer(handle, staging, VkBufferCopy{offset, 0, size}); }); scheduler.Finish(); std::memcpy(data, staging.commit->Map(size), size); } -void VKBufferCache::CopyBlock(const Buffer& src, const Buffer& dst, std::size_t src_offset, - std::size_t dst_offset, std::size_t size) { +void Buffer::CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset, + std::size_t size) const { scheduler.RequestOutsideRenderPassOperationContext(); - scheduler.Record([src_buffer = src.Handle(), dst_buffer = dst.Handle(), src_offset, dst_offset, + + const VkBuffer dst_buffer = Handle(); + scheduler.Record([src_buffer = src.Handle(), dst_buffer, src_offset, dst_offset, size](vk::CommandBuffer cmdbuf) { cmdbuf.CopyBuffer(src_buffer, dst_buffer, VkBufferCopy{src_offset, dst_offset, size}); @@ -165,4 +142,30 @@ void VKBufferCache::CopyBlock(const Buffer& src, const Buffer& dst, std::size_t }); } +VKBufferCache::VKBufferCache(VideoCore::RasterizerInterface& rasterizer, Core::System& system, + const VKDevice& device, VKMemoryManager& memory_manager, + VKScheduler& scheduler, VKStagingBufferPool& staging_pool) + : VideoCommon::BufferCache{rasterizer, system, + CreateStreamBuffer(device, + scheduler)}, + device{device}, memory_manager{memory_manager}, scheduler{scheduler}, staging_pool{ + staging_pool} {} + +VKBufferCache::~VKBufferCache() = default; + +std::shared_ptr VKBufferCache::CreateBlock(VAddr cpu_addr, std::size_t size) { + return std::make_shared(device, memory_manager, scheduler, staging_pool, cpu_addr, + size); +} + +VKBufferCache::BufferInfo VKBufferCache::GetEmptyBuffer(std::size_t size) { + size = std::max(size, std::size_t(4)); + const auto& empty = staging_pool.GetUnusedBuffer(size, false); + scheduler.RequestOutsideRenderPassOperationContext(); + scheduler.Record([size, buffer = *empty.handle](vk::CommandBuffer cmdbuf) { + cmdbuf.FillBuffer(buffer, 0, size, 0); + }); + return {*empty.handle, 0, 0}; +} + } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h index 682383ff2..3630aca77 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.h +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h @@ -25,10 +25,17 @@ class VKScheduler; class Buffer final : public VideoCommon::BufferBlock { public: - explicit Buffer(const VKDevice& device, VKMemoryManager& memory_manager, VAddr cpu_addr, - std::size_t size); + explicit Buffer(const VKDevice& device, VKMemoryManager& memory_manager, VKScheduler& scheduler, + VKStagingBufferPool& staging_pool, VAddr cpu_addr, std::size_t size); ~Buffer(); + void Upload(std::size_t offset, std::size_t size, const u8* data) const; + + void Download(std::size_t offset, std::size_t size, u8* data) const; + + void CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset, + std::size_t size) const; + VkBuffer Handle() const { return *buffer.handle; } @@ -38,6 +45,9 @@ public: } private: + VKScheduler& scheduler; + VKStagingBufferPool& staging_pool; + VKBuffer buffer; }; @@ -53,15 +63,6 @@ public: protected: std::shared_ptr CreateBlock(VAddr cpu_addr, std::size_t size) override; - void UploadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size, - const u8* data) override; - - void DownloadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size, - u8* data) override; - - void CopyBlock(const Buffer& src, const Buffer& dst, std::size_t src_offset, - std::size_t dst_offset, std::size_t size) override; - private: const VKDevice& device; VKMemoryManager& memory_manager; From 82ecdd0104802c917a1f321f48576aba6964438c Mon Sep 17 00:00:00 2001 From: David Marcec Date: Wed, 24 Jun 2020 22:50:27 +1000 Subject: [PATCH 087/252] Mark invalid IPC buffers as ASSERT_OR_EXECUTE_MSG Previously if applications would send faulty buffers(example homebrew) it would lead to us returning uninitalized data. Switching from ASSERT_MSG to ASSERT_OR_EXECUTE_MSG allows us to have a fail safe to prevent crashes but also continue execution without introducing undefined behavior --- src/core/hle/kernel/hle_ipc.cpp | 47 +++++++++++++++------------------ 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index ba0eac4c2..0d01a7047 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -282,18 +282,18 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) { } std::vector HLERequestContext::ReadBuffer(std::size_t buffer_index) const { - std::vector buffer; + std::vector buffer{}; const bool is_buffer_a{BufferDescriptorA().size() > buffer_index && BufferDescriptorA()[buffer_index].Size()}; if (is_buffer_a) { - ASSERT_MSG(BufferDescriptorA().size() > buffer_index, - "BufferDescriptorA invalid buffer_index {}", buffer_index); + ASSERT_OR_EXECUTE_MSG(BufferDescriptorA().size() > buffer_index, { return buffer; }, + "BufferDescriptorA invalid buffer_index {}", buffer_index); buffer.resize(BufferDescriptorA()[buffer_index].Size()); memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), buffer.size()); } else { - ASSERT_MSG(BufferDescriptorX().size() > buffer_index, - "BufferDescriptorX invalid buffer_index {}", buffer_index); + ASSERT_OR_EXECUTE_MSG(BufferDescriptorX().size() > buffer_index, { return buffer; }, + "BufferDescriptorX invalid buffer_index {}", buffer_index); buffer.resize(BufferDescriptorX()[buffer_index].Size()); memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), buffer.size()); } @@ -318,16 +318,16 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size, } if (is_buffer_b) { - ASSERT_MSG(BufferDescriptorB().size() > buffer_index, - "BufferDescriptorB invalid buffer_index {}", buffer_index); - ASSERT_MSG(BufferDescriptorB()[buffer_index].Size() >= size, - "BufferDescriptorB buffer_index {} is not large enough", buffer_index); + ASSERT_OR_EXECUTE_MSG(BufferDescriptorB().size() > buffer_index && + BufferDescriptorB()[buffer_index].Size() >= size, + { return 0; }, "BufferDescriptorB is invalid, index={}, size={}", + buffer_index, size); memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size); } else { - ASSERT_MSG(BufferDescriptorC().size() > buffer_index, - "BufferDescriptorC invalid buffer_index {}", buffer_index); - ASSERT_MSG(BufferDescriptorC()[buffer_index].Size() >= size, - "BufferDescriptorC buffer_index {} is not large enough", buffer_index); + ASSERT_OR_EXECUTE_MSG(BufferDescriptorC().size() > buffer_index && + BufferDescriptorC()[buffer_index].Size() >= size, + { return 0; }, "BufferDescriptorC is invalid, index={}, size={}", + buffer_index, size); memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size); } @@ -338,16 +338,12 @@ std::size_t HLERequestContext::GetReadBufferSize(std::size_t buffer_index) const const bool is_buffer_a{BufferDescriptorA().size() > buffer_index && BufferDescriptorA()[buffer_index].Size()}; if (is_buffer_a) { - ASSERT_MSG(BufferDescriptorA().size() > buffer_index, - "BufferDescriptorA invalid buffer_index {}", buffer_index); - ASSERT_MSG(BufferDescriptorA()[buffer_index].Size() > 0, - "BufferDescriptorA buffer_index {} is empty", buffer_index); + ASSERT_OR_EXECUTE_MSG(BufferDescriptorA().size() > buffer_index, { return 0; }, + "BufferDescriptorA invalid buffer_index {}", buffer_index); return BufferDescriptorA()[buffer_index].Size(); } else { - ASSERT_MSG(BufferDescriptorX().size() > buffer_index, - "BufferDescriptorX invalid buffer_index {}", buffer_index); - ASSERT_MSG(BufferDescriptorX()[buffer_index].Size() > 0, - "BufferDescriptorX buffer_index {} is empty", buffer_index); + ASSERT_OR_EXECUTE_MSG(BufferDescriptorX().size() > buffer_index, { return 0; }, + "BufferDescriptorX invalid buffer_index {}", buffer_index); return BufferDescriptorX()[buffer_index].Size(); } } @@ -356,14 +352,15 @@ std::size_t HLERequestContext::GetWriteBufferSize(std::size_t buffer_index) cons const bool is_buffer_b{BufferDescriptorB().size() > buffer_index && BufferDescriptorB()[buffer_index].Size()}; if (is_buffer_b) { - ASSERT_MSG(BufferDescriptorB().size() > buffer_index, - "BufferDescriptorB invalid buffer_index {}", buffer_index); + ASSERT_OR_EXECUTE_MSG(BufferDescriptorB().size() > buffer_index, { return 0; }, + "BufferDescriptorB invalid buffer_index {}", buffer_index); return BufferDescriptorB()[buffer_index].Size(); } else { - ASSERT_MSG(BufferDescriptorC().size() > buffer_index, - "BufferDescriptorC invalid buffer_index {}", buffer_index); + ASSERT_OR_EXECUTE_MSG(BufferDescriptorC().size() > buffer_index, { return 0; }, + "BufferDescriptorC invalid buffer_index {}", buffer_index); return BufferDescriptorC()[buffer_index].Size(); } + return 0; } std::string HLERequestContext::Description() const { From 2f0b322e721625def489f1dfaf9eb061e2eedb9a Mon Sep 17 00:00:00 2001 From: David Marcec Date: Wed, 24 Jun 2020 23:01:00 +1000 Subject: [PATCH 088/252] prepo: : Don't read extra buffer from report unless passed Prepo doesn't always pass a secondary buffer, we assume it always does which leads to a bad read. --- src/core/hle/service/prepo/prepo.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp index 14309c679..67833d9af 100644 --- a/src/core/hle/service/prepo/prepo.cpp +++ b/src/core/hle/service/prepo/prepo.cpp @@ -75,8 +75,13 @@ private: const auto user_id = rp.PopRaw(); const auto process_id = rp.PopRaw(); std::vector> data{ctx.ReadBuffer(0)}; + if constexpr (Type == Core::Reporter::PlayReportType::Old2) { - data.emplace_back(ctx.ReadBuffer(1)); + const auto read_buffer_count = + ctx.BufferDescriptorX().size() + ctx.BufferDescriptorA().size(); + if (read_buffer_count > 1) { + data.emplace_back(ctx.ReadBuffer(1)); + } } LOG_DEBUG( From 510838759f2315f21b0dcb8b27f840b489e1f63c Mon Sep 17 00:00:00 2001 From: David Marcec Date: Thu, 25 Jun 2020 00:25:15 +1000 Subject: [PATCH 089/252] Prevent nullptr dereference on swkbd error case --- src/core/hle/service/am/applets/software_keyboard.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp index d14076b02..fbe3686ae 100644 --- a/src/core/hle/service/am/applets/software_keyboard.cpp +++ b/src/core/hle/service/am/applets/software_keyboard.cpp @@ -60,7 +60,7 @@ void SoftwareKeyboard::Initialize() { std::memcpy(&config, keyboard_config.data(), sizeof(KeyboardConfig)); const auto work_buffer_storage = broker.PopNormalDataToApplet(); - ASSERT(work_buffer_storage != nullptr); + ASSERT_OR_EXECUTE(work_buffer_storage != nullptr, { return; }); const auto& work_buffer = work_buffer_storage->GetData(); if (config.initial_string_size == 0) From 2c9308954c0a7cc7e8f9b12508ff28f570ffe3e9 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Mon, 22 Jun 2020 05:37:46 -0400 Subject: [PATCH 090/252] hid: Stub a series of "SevenSixAxisSensor" service commands - Used by Captain Toad: Treasure Tracker Update 1.3.0 While we're at it, fix the input parameters for SetIsPalmaAllConnectable and SetPalmaBoostMode --- src/core/hle/service/hid/hid.cpp | 93 ++++++++++++++++++++++++++------ src/core/hle/service/hid/hid.h | 13 +++-- 2 files changed, 85 insertions(+), 21 deletions(-) diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 72a050de2..1ee99bfeb 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -230,15 +230,15 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) { {211, nullptr, "IsVibrationDeviceMounted"}, {300, &Hid::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"}, {301, &Hid::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"}, - {302, nullptr, "StopConsoleSixAxisSensor"}, - {303, nullptr, "ActivateSevenSixAxisSensor"}, - {304, nullptr, "StartSevenSixAxisSensor"}, + {302, &Hid::StopConsoleSixAxisSensor, "StopConsoleSixAxisSensor"}, + {303, &Hid::ActivateSevenSixAxisSensor, "ActivateSevenSixAxisSensor"}, + {304, &Hid::StartSevenSixAxisSensor, "StartSevenSixAxisSensor"}, {305, &Hid::StopSevenSixAxisSensor, "StopSevenSixAxisSensor"}, {306, &Hid::InitializeSevenSixAxisSensor, "InitializeSevenSixAxisSensor"}, - {307, nullptr, "FinalizeSevenSixAxisSensor"}, + {307, &Hid::FinalizeSevenSixAxisSensor, "FinalizeSevenSixAxisSensor"}, {308, nullptr, "SetSevenSixAxisSensorFusionStrength"}, {309, nullptr, "GetSevenSixAxisSensorFusionStrength"}, - {310, nullptr, "ResetSevenSixAxisSensorTimestamp"}, + {310, &Hid::ResetSevenSixAxisSensorTimestamp, "ResetSevenSixAxisSensorTimestamp"}, {400, nullptr, "IsUsbFullKeyControllerEnabled"}, {401, nullptr, "EnableUsbFullKeyController"}, {402, nullptr, "IsUsbFullKeyControllerConnected"}, @@ -374,6 +374,15 @@ void Hid::ActivateKeyboard(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); } +void Hid::SendKeyboardLockKeyEvent(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto flags{rp.Pop()}; + LOG_WARNING(Service_HID, "(STUBBED) called. flags={}", flags); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto unknown{rp.Pop()}; @@ -413,6 +422,18 @@ void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); } +void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto handle{rp.Pop()}; + const auto applet_resource_user_id{rp.Pop()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle, + applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto handle{rp.Pop()}; @@ -832,33 +853,35 @@ void Hid::StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); } -void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) { +void Hid::StopConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto handle{rp.Pop()}; + const auto applet_resource_user_id{rp.Pop()}; - LOG_WARNING(Service_HID, "(STUBBED) called, handle={}", handle); + LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle, + applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } -void Hid::SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx) { +void Hid::ActivateSevenSixAxisSensor(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto applet_resource_user_id{rp.Pop()}; - const auto unknown{rp.Pop()}; - LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}, unknown={}", - applet_resource_user_id, unknown); + LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", + applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } -void Hid::SetPalmaBoostMode(Kernel::HLERequestContext& ctx) { +void Hid::StartSevenSixAxisSensor(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto unknown{rp.Pop()}; + const auto applet_resource_user_id{rp.Pop()}; - LOG_WARNING(Service_HID, "(STUBBED) called, unknown={}", unknown); + LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", + applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -882,10 +905,46 @@ void Hid::InitializeSevenSixAxisSensor(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); } -void Hid::SendKeyboardLockKeyEvent(Kernel::HLERequestContext& ctx) { +void Hid::FinalizeSevenSixAxisSensor(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto flags{rp.Pop()}; - LOG_WARNING(Service_HID, "(STUBBED) called. flags={}", flags); + const auto applet_resource_user_id{rp.Pop()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", + applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void Hid::ResetSevenSixAxisSensorTimestamp(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", + applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void Hid::SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + const auto is_palma_all_connectable{rp.Pop()}; + + LOG_WARNING(Service_HID, + "(STUBBED) called, applet_resource_user_id={}, is_palma_all_connectable={}", + applet_resource_user_id, is_palma_all_connectable); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void Hid::SetPalmaBoostMode(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto palma_boost_mode{rp.Pop()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, palma_boost_mode={}", palma_boost_mode); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index d481a75f8..ffce5ba45 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -91,9 +91,11 @@ private: void ActivateTouchScreen(Kernel::HLERequestContext& ctx); void ActivateMouse(Kernel::HLERequestContext& ctx); void ActivateKeyboard(Kernel::HLERequestContext& ctx); + void SendKeyboardLockKeyEvent(Kernel::HLERequestContext& ctx); void ActivateGesture(Kernel::HLERequestContext& ctx); void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx); void StartSixAxisSensor(Kernel::HLERequestContext& ctx); + void StopSixAxisSensor(Kernel::HLERequestContext& ctx); void SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx); void IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx); void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx); @@ -126,12 +128,15 @@ private: void IsVibrationPermitted(Kernel::HLERequestContext& ctx); void ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx); void StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx); - void StopSixAxisSensor(Kernel::HLERequestContext& ctx); - void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx); - void SetPalmaBoostMode(Kernel::HLERequestContext& ctx); + void StopConsoleSixAxisSensor(Kernel::HLERequestContext& ctx); + void ActivateSevenSixAxisSensor(Kernel::HLERequestContext& ctx); + void StartSevenSixAxisSensor(Kernel::HLERequestContext& ctx); void StopSevenSixAxisSensor(Kernel::HLERequestContext& ctx); void InitializeSevenSixAxisSensor(Kernel::HLERequestContext& ctx); - void SendKeyboardLockKeyEvent(Kernel::HLERequestContext& ctx); + void FinalizeSevenSixAxisSensor(Kernel::HLERequestContext& ctx); + void ResetSevenSixAxisSensorTimestamp(Kernel::HLERequestContext& ctx); + void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx); + void SetPalmaBoostMode(Kernel::HLERequestContext& ctx); std::shared_ptr applet_resource; Core::System& system; From bc8d3b8f82c06e5d0b5a7c1640ef00b83e826dbf Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Thu, 25 Jun 2020 01:28:45 -0300 Subject: [PATCH 091/252] gl_device: Enable NV_vertex_buffer_unified_memory on Turing devices Once we make sure not to corrupt Nvidia's driver, we can safely use resident buffers on Turing devices. See GitHub pull request #4156 --- src/video_core/renderer_opengl/gl_device.cpp | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp index 447a19595..bb1375f82 100644 --- a/src/video_core/renderer_opengl/gl_device.cpp +++ b/src/video_core/renderer_opengl/gl_device.cpp @@ -188,20 +188,6 @@ bool IsASTCSupported() { return true; } -/// @brief Returns true when a GL_RENDERER is a Turing GPU -/// @param renderer GL_RENDERER string -bool IsTuring(std::string_view renderer) { - static constexpr std::array TURING_GPUS = { - "GTX 1650", "GTX 1660", "RTX 2060", "RTX 2070", - "RTX 2080", "TITAN RTX", "Quadro RTX 3000", "Quadro RTX 4000", - "Quadro RTX 5000", "Quadro RTX 6000", "Quadro RTX 8000", "Tesla T4", - }; - return std::any_of(TURING_GPUS.begin(), TURING_GPUS.end(), - [renderer](std::string_view candidate) { - return renderer.find(candidate) != std::string_view::npos; - }); -} - } // Anonymous namespace Device::Device() @@ -213,7 +199,6 @@ Device::Device() const bool is_nvidia = vendor == "NVIDIA Corporation"; const bool is_amd = vendor == "ATI Technologies Inc."; - const bool is_turing = is_nvidia && IsTuring(renderer); bool disable_fast_buffer_sub_data = false; if (is_nvidia && version == "4.6.0 NVIDIA 443.24") { @@ -238,15 +223,12 @@ Device::Device() has_component_indexing_bug = is_amd; has_precise_bug = TestPreciseBug(); has_nv_viewport_array2 = GLAD_GL_NV_viewport_array2; + has_vertex_buffer_unified_memory = GLAD_GL_NV_vertex_buffer_unified_memory; // At the moment of writing this, only Nvidia's driver optimizes BufferSubData on exclusive // uniform buffers as "push constants" has_fast_buffer_sub_data = is_nvidia && !disable_fast_buffer_sub_data; - // Nvidia's driver on Turing GPUs randomly crashes when the buffer is made resident, or on - // DeleteBuffers. Disable unified memory on these devices. - has_vertex_buffer_unified_memory = GLAD_GL_NV_vertex_buffer_unified_memory && !is_turing; - use_assembly_shaders = Settings::values.use_assembly_shaders && GLAD_GL_NV_gpu_program5 && GLAD_GL_NV_compute_program5 && GLAD_GL_NV_transform_feedback && GLAD_GL_NV_transform_feedback2; From 38868e575090887f3c652c86ad2d08e5fbc8f891 Mon Sep 17 00:00:00 2001 From: David Marcec Date: Thu, 25 Jun 2020 16:35:58 +1000 Subject: [PATCH 092/252] memory_manager: Remove useless assertion num_pages is an std::size_t. It will always be >= 0 --- src/core/hle/kernel/memory/memory_manager.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/core/hle/kernel/memory/memory_manager.cpp b/src/core/hle/kernel/memory/memory_manager.cpp index 616148190..acf13585c 100644 --- a/src/core/hle/kernel/memory/memory_manager.cpp +++ b/src/core/hle/kernel/memory/memory_manager.cpp @@ -139,7 +139,6 @@ ResultCode MemoryManager::Allocate(PageLinkedList& page_list, std::size_t num_pa } // Only succeed if we allocated as many pages as we wanted - ASSERT(num_pages >= 0); if (num_pages) { return ERR_OUT_OF_MEMORY; } From a927d8be52c343bc1025e5df822c56470eb27919 Mon Sep 17 00:00:00 2001 From: David Marcec Date: Thu, 25 Jun 2020 19:12:56 +1000 Subject: [PATCH 093/252] gl_device: Fix IsASTCSupported Other targets were never actually checked --- src/video_core/renderer_opengl/gl_device.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp index 447a19595..b6b6659c1 100644 --- a/src/video_core/renderer_opengl/gl_device.cpp +++ b/src/video_core/renderer_opengl/gl_device.cpp @@ -178,7 +178,7 @@ bool IsASTCSupported() { for (const GLenum format : formats) { for (const GLenum support : required_support) { GLint value; - glGetInternalformativ(GL_TEXTURE_2D, format, support, 1, &value); + glGetInternalformativ(target, format, support, 1, &value); if (value != GL_FULL_SUPPORT) { return false; } From 57b93395a87fae470ef489aa89f1f38633028c4b Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Thu, 25 Jun 2020 23:02:33 +0200 Subject: [PATCH 094/252] Add "Open Quickstart Guide" and "FAQ" buttons to the Help menu While we're at it, also refactor the function used by OnOpenModsPage to be compatible with other URLs --- src/yuzu/main.cpp | 26 ++++++++++++++++++++++++++ src/yuzu/main.h | 4 ++++ src/yuzu/main.ui | 18 ++++++++++++++++++ 3 files changed, 48 insertions(+) diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 270cccc77..ecafbfb00 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -56,6 +56,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include #include #include +#include #include #include @@ -826,6 +827,9 @@ void GMainWindow::ConnectMenuEvents() { connect(ui.action_Stop, &QAction::triggered, this, &GMainWindow::OnStopGame); connect(ui.action_Report_Compatibility, &QAction::triggered, this, &GMainWindow::OnMenuReportCompatibility); + connect(ui.action_Open_Mods_Page, &QAction::triggered, this, &GMainWindow::OnOpenModsPage); + connect(ui.action_Open_Quickstart_Guide, &QAction::triggered, this, &GMainWindow::OnQuickstartGuide); + connect(ui.action_Open_FAQ, &QAction::triggered, this, &GMainWindow::OnFAQ); connect(ui.action_Restart, &QAction::triggered, this, [this] { BootGame(QString(game_path)); }); connect(ui.action_Configure, &QAction::triggered, this, &GMainWindow::OnConfigure); @@ -1797,6 +1801,28 @@ void GMainWindow::OnMenuReportCompatibility() { } } +void GMainWindow::OpenURL(QString const& url_str) { + + const QUrl url{url_str}; + const bool open = QDesktopServices::openUrl(url); + if (!open) { + QMessageBox::warning(this, tr("Error opening URL"), + tr("Unable to open the URL \"%1\".").arg(url_str)); + } +} + +void GMainWindow::OnOpenModsPage() { + this->OpenURL(QStringLiteral("https://github.com/yuzu-emu/yuzu/wiki/Switch-Mods")); +} + +void GMainWindow::OnQuickstartGuide() { + this->OpenURL(QStringLiteral("https://yuzu-emu.org/help/quickstart/")); +} + +void GMainWindow::OnFAQ() { + this->OpenURL(QStringLiteral("https://yuzu-emu.org/wiki/faq/")); +} + void GMainWindow::ToggleFullscreen() { if (!emulation_running) { return; diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 4f4c8ddbe..9500bdbba 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -181,6 +181,9 @@ private slots: void OnPauseGame(); void OnStopGame(); void OnMenuReportCompatibility(); + void OnOpenModsPage(); + void OnQuickstartGuide(); + void OnFAQ(); /// Called whenever a user selects a game in the game list widget. void OnGameListLoadFile(QString game_path); void OnGameListOpenFolder(GameListOpenTarget target, const std::string& game_path); @@ -219,6 +222,7 @@ private: void UpdateStatusBar(); void HideMouseCursor(); void ShowMouseCursor(); + void OpenURL(const QString& url_str); Ui::MainWindow ui; diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index 97c90f50b..248a805d9 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui @@ -113,6 +113,9 @@ &Help + + + @@ -256,6 +259,21 @@ false + + + Open Mods Page + + + + + Open Quickstart Guide + + + + + FAQ + + Open yuzu Folder From 5f6e44552a65421f88dc79133aa8fa1d202ab7ff Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Thu, 25 Jun 2020 23:07:58 +0200 Subject: [PATCH 095/252] Fix typo --- src/yuzu/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index ecafbfb00..e454347d9 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -1801,7 +1801,7 @@ void GMainWindow::OnMenuReportCompatibility() { } } -void GMainWindow::OpenURL(QString const& url_str) { +void GMainWindow::OpenURL(const QString& url_str) { const QUrl url{url_str}; const bool open = QDesktopServices::openUrl(url); From 04497d9e4adcfbfdcfe912b90d1ffcf805443cea Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Thu, 25 Jun 2020 23:18:54 +0200 Subject: [PATCH 096/252] Fix formatting --- src/yuzu/main.ui | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index 248a805d9..bee6e107e 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui @@ -114,8 +114,8 @@ - - + + From 5c6adea222fca7aeab8b0e418b0afc8fa2cab042 Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Thu, 25 Jun 2020 23:28:38 +0200 Subject: [PATCH 097/252] Use QUrl (1/2) --- src/yuzu/main.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 9500bdbba..94d3dd7f6 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -222,7 +222,7 @@ private: void UpdateStatusBar(); void HideMouseCursor(); void ShowMouseCursor(); - void OpenURL(const QString& url_str); + void OpenURL(const QUrl& url); Ui::MainWindow ui; From 9e7ac6a009002aacba48f9c167efc58cfe34241d Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Thu, 25 Jun 2020 23:31:01 +0200 Subject: [PATCH 098/252] Use QUrl (2/2) --- src/yuzu/main.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index e454347d9..a2f637b96 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -1801,26 +1801,25 @@ void GMainWindow::OnMenuReportCompatibility() { } } -void GMainWindow::OpenURL(const QString& url_str) { +void GMainWindow::OpenURL(QUrl const& url) { - const QUrl url{url_str}; const bool open = QDesktopServices::openUrl(url); if (!open) { QMessageBox::warning(this, tr("Error opening URL"), - tr("Unable to open the URL \"%1\".").arg(url_str)); + tr("Unable to open the URL \"%1\".").arg(url.toString())); } } void GMainWindow::OnOpenModsPage() { - this->OpenURL(QStringLiteral("https://github.com/yuzu-emu/yuzu/wiki/Switch-Mods")); + OpenURL(QUrl(QStringLiteral("https://github.com/yuzu-emu/yuzu/wiki/Switch-Mods"))); } void GMainWindow::OnQuickstartGuide() { - this->OpenURL(QStringLiteral("https://yuzu-emu.org/help/quickstart/")); + OpenURL(QUrl(QStringLiteral("https://yuzu-emu.org/help/quickstart/"))); } void GMainWindow::OnFAQ() { - this->OpenURL(QStringLiteral("https://yuzu-emu.org/wiki/faq/")); + OpenURL(QUrl(QStringLiteral("https://yuzu-emu.org/wiki/faq/"))); } void GMainWindow::ToggleFullscreen() { From a46df409397855812812e83ae3ed6ce6261b72cb Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Thu, 25 Jun 2020 23:32:43 +0200 Subject: [PATCH 099/252] Fix typo 2: electric boogaloo --- src/yuzu/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index a2f637b96..b78f81095 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -1801,7 +1801,7 @@ void GMainWindow::OnMenuReportCompatibility() { } } -void GMainWindow::OpenURL(QUrl const& url) { +void GMainWindow::OpenURL(const QUrl& url) { const bool open = QDesktopServices::openUrl(url); if (!open) { From 6582857356445d515aa70fb93c72c03c7c3d7928 Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Thu, 25 Jun 2020 23:38:38 +0200 Subject: [PATCH 100/252] Remove unnecessary newline --- src/yuzu/main.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 4810b161a..9f552e3d2 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -1812,7 +1812,6 @@ void GMainWindow::OnMenuReportCompatibility() { } void GMainWindow::OpenURL(const QUrl& url) { - const bool open = QDesktopServices::openUrl(url); if (!open) { QMessageBox::warning(this, tr("Error opening URL"), From b9f0b9dd06f7bdbbc701261ffa8cb2688569fc40 Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Thu, 25 Jun 2020 23:40:53 +0200 Subject: [PATCH 101/252] Clang-format --- src/yuzu/main.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 9f552e3d2..3d8f8e534 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -838,7 +838,8 @@ void GMainWindow::ConnectMenuEvents() { connect(ui.action_Report_Compatibility, &QAction::triggered, this, &GMainWindow::OnMenuReportCompatibility); connect(ui.action_Open_Mods_Page, &QAction::triggered, this, &GMainWindow::OnOpenModsPage); - connect(ui.action_Open_Quickstart_Guide, &QAction::triggered, this, &GMainWindow::OnQuickstartGuide); + connect(ui.action_Open_Quickstart_Guide, &QAction::triggered, this, + &GMainWindow::OnQuickstartGuide); connect(ui.action_Open_FAQ, &QAction::triggered, this, &GMainWindow::OnFAQ); connect(ui.action_Restart, &QAction::triggered, this, [this] { BootGame(QString(game_path)); }); connect(ui.action_Configure, &QAction::triggered, this, &GMainWindow::OnConfigure); From 7d08d548a94377e4794acb770bc9b5bb2dac9130 Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Thu, 25 Jun 2020 23:44:41 +0200 Subject: [PATCH 102/252] Clang-format again --- src/yuzu/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 3d8f8e534..267b0e75d 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -838,7 +838,7 @@ void GMainWindow::ConnectMenuEvents() { connect(ui.action_Report_Compatibility, &QAction::triggered, this, &GMainWindow::OnMenuReportCompatibility); connect(ui.action_Open_Mods_Page, &QAction::triggered, this, &GMainWindow::OnOpenModsPage); - connect(ui.action_Open_Quickstart_Guide, &QAction::triggered, this, + connect(ui.action_Open_Quickstart_Guide, &QAction::triggered, this, &GMainWindow::OnQuickstartGuide); connect(ui.action_Open_FAQ, &QAction::triggered, this, &GMainWindow::OnFAQ); connect(ui.action_Restart, &QAction::triggered, this, [this] { BootGame(QString(game_path)); }); From 3eb8efc09574f7a49d11cccecc245674b7282fa2 Mon Sep 17 00:00:00 2001 From: Kewlan Date: Thu, 25 Jun 2020 21:05:03 +0200 Subject: [PATCH 103/252] Add a "Mute Audio" hotkey --- src/audio_core/stream.cpp | 2 +- src/core/settings.cpp | 7 +++++++ src/core/settings.h | 3 +++ src/yuzu/configuration/config.cpp | 3 ++- src/yuzu/configuration/config.h | 2 +- src/yuzu/main.cpp | 3 +++ 6 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp index 4ca98f8ea..ca7cfb030 100644 --- a/src/audio_core/stream.cpp +++ b/src/audio_core/stream.cpp @@ -67,7 +67,7 @@ s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const { } static void VolumeAdjustSamples(std::vector& samples, float game_volume) { - const float volume{std::clamp(Settings::values.volume - (1.0f - game_volume), 0.0f, 1.0f)}; + const float volume{std::clamp(Settings::Volume() - (1.0f - game_volume), 0.0f, 1.0f)}; if (volume == 1.0f) { return; diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 4edff9cd8..56df5e925 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -127,6 +127,13 @@ void LogSettings() { LogSetting("Services_BCATBoxcatLocal", Settings::values.bcat_boxcat_local); } +float Volume() { + if (values.audio_muted) { + return 0.0f; + } + return values.volume; +} + bool IsGPULevelExtreme() { return values.gpu_accuracy == GPUAccuracy::Extreme; } diff --git a/src/core/settings.h b/src/core/settings.h index 33e1e06cd..a598ccbc1 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -459,6 +459,7 @@ struct Values { bool use_dev_keys; // Audio + bool audio_muted; std::string sink_id; bool enable_audio_stretching; std::string audio_device_id; @@ -490,6 +491,8 @@ struct Values { std::map> disabled_addons; } extern values; +float Volume(); + bool IsGPULevelExtreme(); bool IsGPULevelHigh(); diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 32c81dc70..bbbd96113 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -211,7 +211,7 @@ const std::array Config::default // This must be in alphabetical order according to action name as it must have the same order as // UISetting::values.shortcuts, which is alphabetically ordered. // clang-format off -const std::array Config::default_hotkeys{{ +const std::array Config::default_hotkeys{{ {QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::ApplicationShortcut}}, {QStringLiteral("Change Docked Mode"), QStringLiteral("Main Window"), {QStringLiteral("F10"), Qt::ApplicationShortcut}}, {QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}}, @@ -222,6 +222,7 @@ const std::array Config::default_hotkeys{{ {QStringLiteral("Increase Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("+"), Qt::ApplicationShortcut}}, {QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), Qt::ApplicationShortcut}}, {QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), Qt::WindowShortcut}}, + {QStringLiteral("Mute Audio"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), Qt::WindowShortcut}}, {QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}}, {QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}}, {QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), Qt::WindowShortcut}}, diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h index 5cd2a5feb..09316382c 100644 --- a/src/yuzu/configuration/config.h +++ b/src/yuzu/configuration/config.h @@ -27,7 +27,7 @@ public: default_mouse_buttons; static const std::array default_keyboard_keys; static const std::array default_keyboard_mods; - static const std::array default_hotkeys; + static const std::array default_hotkeys; private: void ReadValues(); diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 059b96e70..6516f279a 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -736,6 +736,9 @@ void GMainWindow::InitializeHotkeys() { Settings::values.use_docked_mode); dock_status_button->setChecked(Settings::values.use_docked_mode); }); + connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Mute Audio"), this), + &QShortcut::activated, this, + [] { Settings::values.audio_muted = !Settings::values.audio_muted; }); } void GMainWindow::SetDefaultUIGeometry() { From 02a33feef44cac4b5f60d3fb5c33ad0deff189be Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Thu, 25 Jun 2020 01:58:13 -0400 Subject: [PATCH 104/252] caps: Update copyright headers Updated to "yuzu Emulator Project" --- src/core/hle/service/caps/caps.cpp | 2 +- src/core/hle/service/caps/caps.h | 2 +- src/core/hle/service/caps/caps_a.cpp | 2 +- src/core/hle/service/caps/caps_a.h | 2 +- src/core/hle/service/caps/caps_c.cpp | 2 +- src/core/hle/service/caps/caps_c.h | 2 +- src/core/hle/service/caps/caps_sc.cpp | 2 +- src/core/hle/service/caps/caps_sc.h | 2 +- src/core/hle/service/caps/caps_ss.cpp | 2 +- src/core/hle/service/caps/caps_ss.h | 2 +- src/core/hle/service/caps/caps_su.cpp | 2 +- src/core/hle/service/caps/caps_su.h | 2 +- src/core/hle/service/caps/caps_u.cpp | 2 +- src/core/hle/service/caps/caps_u.h | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/core/hle/service/caps/caps.cpp b/src/core/hle/service/caps/caps.cpp index 26c8a7081..ba5749b84 100644 --- a/src/core/hle/service/caps/caps.cpp +++ b/src/core/hle/service/caps/caps.cpp @@ -1,4 +1,4 @@ -// Copyright 2018 yuzu emulator team +// Copyright 2018 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. diff --git a/src/core/hle/service/caps/caps.h b/src/core/hle/service/caps/caps.h index fc70a4c27..e01b47349 100644 --- a/src/core/hle/service/caps/caps.h +++ b/src/core/hle/service/caps/caps.h @@ -1,4 +1,4 @@ -// Copyright 2018 yuzu emulator team +// Copyright 2018 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. diff --git a/src/core/hle/service/caps/caps_a.cpp b/src/core/hle/service/caps/caps_a.cpp index 88a3fdc05..a0a3b2ae3 100644 --- a/src/core/hle/service/caps/caps_a.cpp +++ b/src/core/hle/service/caps/caps_a.cpp @@ -1,4 +1,4 @@ -// Copyright 2020 yuzu emulator team +// Copyright 2020 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. diff --git a/src/core/hle/service/caps/caps_a.h b/src/core/hle/service/caps/caps_a.h index 8de832491..cb93aad5b 100644 --- a/src/core/hle/service/caps/caps_a.h +++ b/src/core/hle/service/caps/caps_a.h @@ -1,4 +1,4 @@ -// Copyright 2020 yuzu emulator team +// Copyright 2020 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. diff --git a/src/core/hle/service/caps/caps_c.cpp b/src/core/hle/service/caps/caps_c.cpp index ea6452ffa..ab17a187e 100644 --- a/src/core/hle/service/caps/caps_c.cpp +++ b/src/core/hle/service/caps/caps_c.cpp @@ -1,4 +1,4 @@ -// Copyright 2020 yuzu emulator team +// Copyright 2020 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. diff --git a/src/core/hle/service/caps/caps_c.h b/src/core/hle/service/caps/caps_c.h index d07cdb441..a9d028689 100644 --- a/src/core/hle/service/caps/caps_c.h +++ b/src/core/hle/service/caps/caps_c.h @@ -1,4 +1,4 @@ -// Copyright 2020 yuzu emulator team +// Copyright 2020 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. diff --git a/src/core/hle/service/caps/caps_sc.cpp b/src/core/hle/service/caps/caps_sc.cpp index d01a8a58e..822ee96c8 100644 --- a/src/core/hle/service/caps/caps_sc.cpp +++ b/src/core/hle/service/caps/caps_sc.cpp @@ -1,4 +1,4 @@ -// Copyright 2020 yuzu emulator team +// Copyright 2020 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. diff --git a/src/core/hle/service/caps/caps_sc.h b/src/core/hle/service/caps/caps_sc.h index 9ba372f7a..ac3e929ca 100644 --- a/src/core/hle/service/caps/caps_sc.h +++ b/src/core/hle/service/caps/caps_sc.h @@ -1,4 +1,4 @@ -// Copyright 2020 yuzu emulator team +// Copyright 2020 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. diff --git a/src/core/hle/service/caps/caps_ss.cpp b/src/core/hle/service/caps/caps_ss.cpp index eaa3a7494..24dc716e7 100644 --- a/src/core/hle/service/caps/caps_ss.cpp +++ b/src/core/hle/service/caps/caps_ss.cpp @@ -1,4 +1,4 @@ -// Copyright 2020 yuzu emulator team +// Copyright 2020 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. diff --git a/src/core/hle/service/caps/caps_ss.h b/src/core/hle/service/caps/caps_ss.h index e258a6925..450686e4f 100644 --- a/src/core/hle/service/caps/caps_ss.h +++ b/src/core/hle/service/caps/caps_ss.h @@ -1,4 +1,4 @@ -// Copyright 2020 yuzu emulator team +// Copyright 2020 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. diff --git a/src/core/hle/service/caps/caps_su.cpp b/src/core/hle/service/caps/caps_su.cpp index e8b0698e8..fffb2ecf9 100644 --- a/src/core/hle/service/caps/caps_su.cpp +++ b/src/core/hle/service/caps/caps_su.cpp @@ -1,4 +1,4 @@ -// Copyright 2020 yuzu emulator team +// Copyright 2020 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. diff --git a/src/core/hle/service/caps/caps_su.h b/src/core/hle/service/caps/caps_su.h index c494d7c84..62c9603a9 100644 --- a/src/core/hle/service/caps/caps_su.h +++ b/src/core/hle/service/caps/caps_su.h @@ -1,4 +1,4 @@ -// Copyright 2020 yuzu emulator team +// Copyright 2020 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. diff --git a/src/core/hle/service/caps/caps_u.cpp b/src/core/hle/service/caps/caps_u.cpp index 78bab6ed8..34f7f3881 100644 --- a/src/core/hle/service/caps/caps_u.cpp +++ b/src/core/hle/service/caps/caps_u.cpp @@ -1,4 +1,4 @@ -// Copyright 2020 yuzu emulator team +// Copyright 2020 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. diff --git a/src/core/hle/service/caps/caps_u.h b/src/core/hle/service/caps/caps_u.h index e6e0716ff..689364de4 100644 --- a/src/core/hle/service/caps/caps_u.h +++ b/src/core/hle/service/caps/caps_u.h @@ -1,4 +1,4 @@ -// Copyright 2020 yuzu emulator team +// Copyright 2020 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. From 3017be7855c3d4d9711b39fd5d0f41b652721348 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Thu, 25 Jun 2020 01:59:16 -0400 Subject: [PATCH 105/252] caps: Use enum classes and check struct sizes on compile time --- src/core/hle/service/caps/caps.h | 74 +++++++++++++++++--------------- 1 file changed, 40 insertions(+), 34 deletions(-) diff --git a/src/core/hle/service/caps/caps.h b/src/core/hle/service/caps/caps.h index e01b47349..b8c67b6e2 100644 --- a/src/core/hle/service/caps/caps.h +++ b/src/core/hle/service/caps/caps.h @@ -12,73 +12,79 @@ class ServiceManager; namespace Service::Capture { -enum AlbumImageOrientation { +enum class AlbumImageOrientation { Orientation0 = 0, Orientation1 = 1, Orientation2 = 2, Orientation3 = 3, }; -enum AlbumReportOption { +enum class AlbumReportOption { Disable = 0, Enable = 1, }; -enum ContentType : u8 { +enum class ContentType : u8 { Screenshot = 0, Movie = 1, ExtraMovie = 3, }; -enum AlbumStorage : u8 { +enum class AlbumStorage : u8 { NAND = 0, SD = 1, }; struct AlbumFileDateTime { - u16 year; - u8 month; - u8 day; - u8 hour; - u8 minute; - u8 second; - u8 uid; + s16 year{}; + s8 month{}; + s8 day{}; + s8 hour{}; + s8 minute{}; + s8 second{}; + s8 uid{}; }; +static_assert(sizeof(AlbumFileDateTime) == 0x8, "AlbumFileDateTime has incorrect size."); struct AlbumEntry { - u64 size; - u64 application_id; - AlbumFileDateTime datetime; - AlbumStorage storage; - ContentType content; - u8 padding[6]; + u64 size{}; + u64 application_id{}; + AlbumFileDateTime datetime{}; + AlbumStorage storage{}; + ContentType content{}; + INSERT_PADDING_BYTES(6); }; +static_assert(sizeof(AlbumEntry) == 0x20, "AlbumEntry has incorrect size."); struct AlbumFileEntry { - u64 size; - u64 hash; - AlbumFileDateTime datetime; - AlbumStorage storage; - ContentType content; - u8 padding[5]; - u8 unknown; + u64 size{}; // Size of the entry + u64 hash{}; // AES256 with hardcoded key over AlbumEntry + AlbumFileDateTime datetime{}; + AlbumStorage storage{}; + ContentType content{}; + INSERT_PADDING_BYTES(5); + u8 unknown{1}; // Set to 1 on official SW }; +static_assert(sizeof(AlbumFileEntry) == 0x20, "AlbumFileEntry has incorrect size."); struct ApplicationAlbumEntry { - u64 size; - u64 hash; - AlbumFileDateTime datetime; - AlbumStorage storage; - ContentType content; - u8 padding[5]; - u8 unknown; + u64 size{}; // Size of the entry + u64 hash{}; // AES256 with hardcoded key over AlbumEntry + AlbumFileDateTime datetime{}; + AlbumStorage storage{}; + ContentType content{}; + INSERT_PADDING_BYTES(5); + u8 unknown{1}; // Set to 1 on official SW }; +static_assert(sizeof(ApplicationAlbumEntry) == 0x20, "ApplicationAlbumEntry has incorrect size."); struct ApplicationAlbumFileEntry { - ApplicationAlbumEntry entry; - AlbumFileDateTime datetime; - u64 unknown; + ApplicationAlbumEntry entry{}; + AlbumFileDateTime datetime{}; + u64 unknown{}; }; +static_assert(sizeof(ApplicationAlbumFileEntry) == 0x30, + "ApplicationAlbumFileEntry has incorrect size."); /// Registers all Capture services with the specified service manager. void InstallInterfaces(SM::ServiceManager& sm); From 72f14ae21f93663c74aec7e12f23d6dc52a7350c Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Thu, 25 Jun 2020 02:00:25 -0400 Subject: [PATCH 106/252] caps_u: Fix GetAlbumContentsFileListForApplication stub --- src/core/hle/service/caps/caps_u.cpp | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/core/hle/service/caps/caps_u.cpp b/src/core/hle/service/caps/caps_u.cpp index 34f7f3881..f36d8de2d 100644 --- a/src/core/hle/service/caps/caps_u.cpp +++ b/src/core/hle/service/caps/caps_u.cpp @@ -58,19 +58,25 @@ void CAPS_U::GetAlbumContentsFileListForApplication(Kernel::HLERequestContext& c // u8 ContentType, two s64s, and an u64 AppletResourceUserId. Returns an output u64 for total // output entries (which is copied to a s32 by official SW). IPC::RequestParser rp{ctx}; - [[maybe_unused]] const auto application_album_file_entries = rp.PopRaw>(); - const auto pid = rp.Pop(); - const auto content_type = rp.PopRaw(); - [[maybe_unused]] const auto start_datetime = rp.PopRaw(); - [[maybe_unused]] const auto end_datetime = rp.PopRaw(); - const auto applet_resource_user_id = rp.Pop(); + const auto pid{rp.Pop()}; + const auto content_type{rp.PopEnum()}; + const auto start_posix_time{rp.Pop()}; + const auto end_posix_time{rp.Pop()}; + const auto applet_resource_user_id{rp.Pop()}; + + // TODO: Update this when we implement the album. + // Currently we do not have a method of accessing album entries, set this to 0 for now. + constexpr s32 total_entries{0}; + LOG_WARNING(Service_Capture, - "(STUBBED) called. pid={}, content_type={}, applet_resource_user_id={}", pid, - content_type, applet_resource_user_id); + "(STUBBED) called. pid={}, content_type={}, start_posix_time={}, " + "end_posix_time={}, applet_resource_user_id={}, total_entries={}", + pid, content_type, start_posix_time, end_posix_time, applet_resource_user_id, + total_entries); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push(0); + rb.Push(total_entries); } } // namespace Service::Capture From 7b893c7963a57bf41f5dad7dd1709985971ce291 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Fri, 19 Jun 2020 19:46:43 -0400 Subject: [PATCH 107/252] Common: Fix non-conan build --- src/common/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index aacea0ab7..3cc17d0e9 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -187,6 +187,7 @@ if(ARCHITECTURE_x86_64) endif() create_target_directory_groups(common) +find_package(Boost 1.71 COMPONENTS context headers REQUIRED) -target_link_libraries(common PUBLIC Boost::boost fmt::fmt microprofile) +target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile) target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd xbyak) From f8247826faf47b4ac5d917ddac3acd7ba89a88fd Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Fri, 26 Jun 2020 18:49:57 +0200 Subject: [PATCH 108/252] Update function name (1/2) --- src/yuzu/main.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 94d3dd7f6..8b0961932 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -182,7 +182,7 @@ private slots: void OnStopGame(); void OnMenuReportCompatibility(); void OnOpenModsPage(); - void OnQuickstartGuide(); + void OnOpenQuickstartGuide(); void OnFAQ(); /// Called whenever a user selects a game in the game list widget. void OnGameListLoadFile(QString game_path); From 0b86c7eb6a22528f41e94500bb7eba55cd3c3217 Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Fri, 26 Jun 2020 18:50:28 +0200 Subject: [PATCH 109/252] Update function name (2/2) --- src/yuzu/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 267b0e75d..d12f7c074 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -1824,7 +1824,7 @@ void GMainWindow::OnOpenModsPage() { OpenURL(QUrl(QStringLiteral("https://github.com/yuzu-emu/yuzu/wiki/Switch-Mods"))); } -void GMainWindow::OnQuickstartGuide() { +void GMainWindow::OnOpenQuickstartGuide() { OpenURL(QUrl(QStringLiteral("https://yuzu-emu.org/help/quickstart/"))); } From 9e1975a166c686980420672bbfe1da9aee3fa57f Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Fri, 26 Jun 2020 18:51:12 +0200 Subject: [PATCH 110/252] Update function name again --- src/yuzu/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index d12f7c074..e597f9733 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -839,7 +839,7 @@ void GMainWindow::ConnectMenuEvents() { &GMainWindow::OnMenuReportCompatibility); connect(ui.action_Open_Mods_Page, &QAction::triggered, this, &GMainWindow::OnOpenModsPage); connect(ui.action_Open_Quickstart_Guide, &QAction::triggered, this, - &GMainWindow::OnQuickstartGuide); + &GMainWindow::OnOpenQuickstartGuide); connect(ui.action_Open_FAQ, &QAction::triggered, this, &GMainWindow::OnFAQ); connect(ui.action_Restart, &QAction::triggered, this, [this] { BootGame(QString(game_path)); }); connect(ui.action_Configure, &QAction::triggered, this, &GMainWindow::OnConfigure); From 6481d91e4a5b5fbae899c3a7924af0b132c16bc8 Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Fri, 26 Jun 2020 16:58:40 -0300 Subject: [PATCH 111/252] gl_buffer_cache: Copy to buffers created as STREAM_READ before downloading After marking buffers as resident, Nvidia's driver seems to take a slow path. To workaround this issue, copy to a STREAM_READ buffer and then call GetNamedBufferSubData on it. This is a temporary solution until we have asynchronous flushing. --- src/video_core/buffer_cache/buffer_cache.h | 6 ++---- .../renderer_opengl/gl_buffer_cache.cpp | 17 ++++++++++++----- .../renderer_opengl/gl_buffer_cache.h | 7 ++++--- .../renderer_vulkan/vk_buffer_cache.cpp | 6 +++--- .../renderer_vulkan/vk_buffer_cache.h | 6 +++--- 5 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index cf8bdd021..c6479af9f 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -322,8 +322,7 @@ protected: } private: - MapInterval* MapAddress(const Buffer* block, GPUVAddr gpu_addr, VAddr cpu_addr, - std::size_t size) { + MapInterval* MapAddress(Buffer* block, GPUVAddr gpu_addr, VAddr cpu_addr, std::size_t size) { const VectorMapInterval overlaps = GetMapsInRange(cpu_addr, size); if (overlaps.empty()) { auto& memory_manager = system.GPU().MemoryManager(); @@ -377,8 +376,7 @@ private: return map; } - void UpdateBlock(const Buffer* block, VAddr start, VAddr end, - const VectorMapInterval& overlaps) { + void UpdateBlock(Buffer* block, VAddr start, VAddr end, const VectorMapInterval& overlaps) { const IntervalType base_interval{start, end}; IntervalSet interval_set{}; interval_set.add(base_interval); diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp index d9f7b4cc6..e461e4c70 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp @@ -34,20 +34,27 @@ Buffer::Buffer(const Device& device, VAddr cpu_addr, std::size_t size) Buffer::~Buffer() = default; -void Buffer::Upload(std::size_t offset, std::size_t size, const u8* data) const { +void Buffer::Upload(std::size_t offset, std::size_t size, const u8* data) { glNamedBufferSubData(Handle(), static_cast(offset), static_cast(size), data); } -void Buffer::Download(std::size_t offset, std::size_t size, u8* data) const { +void Buffer::Download(std::size_t offset, std::size_t size, u8* data) { MICROPROFILE_SCOPE(OpenGL_Buffer_Download); + const GLsizeiptr gl_size = static_cast(size); + const GLintptr gl_offset = static_cast(offset); + if (read_buffer.handle == 0) { + read_buffer.Create(); + glNamedBufferData(read_buffer.handle, static_cast(Size()), nullptr, + GL_STREAM_READ); + } glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); - glGetNamedBufferSubData(Handle(), static_cast(offset), static_cast(size), - data); + glCopyNamedBufferSubData(gl_buffer.handle, read_buffer.handle, gl_offset, gl_offset, gl_size); + glGetNamedBufferSubData(read_buffer.handle, gl_offset, gl_size, data); } void Buffer::CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset, - std::size_t size) const { + std::size_t size) { glCopyNamedBufferSubData(src.Handle(), Handle(), static_cast(src_offset), static_cast(dst_offset), static_cast(size)); } diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h index 59d95adbc..88fdc0536 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.h +++ b/src/video_core/renderer_opengl/gl_buffer_cache.h @@ -28,12 +28,12 @@ public: explicit Buffer(const Device& device, VAddr cpu_addr, std::size_t size); ~Buffer(); - void Upload(std::size_t offset, std::size_t size, const u8* data) const; + void Upload(std::size_t offset, std::size_t size, const u8* data); - void Download(std::size_t offset, std::size_t size, u8* data) const; + void Download(std::size_t offset, std::size_t size, u8* data); void CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset, - std::size_t size) const; + std::size_t size); GLuint Handle() const noexcept { return gl_buffer.handle; @@ -45,6 +45,7 @@ public: private: OGLBuffer gl_buffer; + OGLBuffer read_buffer; u64 gpu_address = 0; }; diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index f10f96cd8..2be38d419 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp @@ -56,7 +56,7 @@ Buffer::Buffer(const VKDevice& device, VKMemoryManager& memory_manager, VKSchedu Buffer::~Buffer() = default; -void Buffer::Upload(std::size_t offset, std::size_t size, const u8* data) const { +void Buffer::Upload(std::size_t offset, std::size_t size, const u8* data) { const auto& staging = staging_pool.GetUnusedBuffer(size, true); std::memcpy(staging.commit->Map(size), data, size); @@ -81,7 +81,7 @@ void Buffer::Upload(std::size_t offset, std::size_t size, const u8* data) const }); } -void Buffer::Download(std::size_t offset, std::size_t size, u8* data) const { +void Buffer::Download(std::size_t offset, std::size_t size, u8* data) { const auto& staging = staging_pool.GetUnusedBuffer(size, true); scheduler.RequestOutsideRenderPassOperationContext(); @@ -110,7 +110,7 @@ void Buffer::Download(std::size_t offset, std::size_t size, u8* data) const { } void Buffer::CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset, - std::size_t size) const { + std::size_t size) { scheduler.RequestOutsideRenderPassOperationContext(); const VkBuffer dst_buffer = Handle(); diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h index 3630aca77..991ee451c 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.h +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h @@ -29,12 +29,12 @@ public: VKStagingBufferPool& staging_pool, VAddr cpu_addr, std::size_t size); ~Buffer(); - void Upload(std::size_t offset, std::size_t size, const u8* data) const; + void Upload(std::size_t offset, std::size_t size, const u8* data); - void Download(std::size_t offset, std::size_t size, u8* data) const; + void Download(std::size_t offset, std::size_t size, u8* data); void CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset, - std::size_t size) const; + std::size_t size); VkBuffer Handle() const { return *buffer.handle; From 1d6be9febf7b9613014ec60fc0ec42e40cc073c9 Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Fri, 26 Jun 2020 19:22:29 -0300 Subject: [PATCH 112/252] video_core/compatible_formats: Table to test if two formats are legal to view or copy Add a flat table to test if it's legal to create a texture view between two formats or copy betweem them. This table is based on ARB_copy_image and ARB_texture_view. Copies are more permissive than views. --- src/video_core/CMakeLists.txt | 2 + src/video_core/compatible_formats.cpp | 162 ++++++++++++++++++++++++++ src/video_core/compatible_formats.h | 32 +++++ 3 files changed, 196 insertions(+) create mode 100644 src/video_core/compatible_formats.cpp create mode 100644 src/video_core/compatible_formats.h diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 2dc752aa9..21c46a567 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -3,6 +3,8 @@ add_library(video_core STATIC buffer_cache/buffer_cache.h buffer_cache/map_interval.cpp buffer_cache/map_interval.h + compatible_formats.cpp + compatible_formats.h dirty_flags.cpp dirty_flags.h dma_pusher.cpp diff --git a/src/video_core/compatible_formats.cpp b/src/video_core/compatible_formats.cpp new file mode 100644 index 000000000..01e5c26ae --- /dev/null +++ b/src/video_core/compatible_formats.cpp @@ -0,0 +1,162 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include + +#include "video_core/compatible_formats.h" +#include "video_core/surface.h" + +namespace VideoCore::Surface { + +namespace { + +// Compatibility table taken from Table 3.X.2 in: +// https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_texture_view.txt + +constexpr std::array VIEW_CLASS_128_BITS = { + PixelFormat::RGBA32F, + PixelFormat::RGBA32UI, +}; +// Missing formats: +// PixelFormat::RGBA32I + +constexpr std::array VIEW_CLASS_96_BITS = { + PixelFormat::RGB32F, +}; +// Missing formats: +// PixelFormat::RGB32UI, +// PixelFormat::RGB32I, + +constexpr std::array VIEW_CLASS_64_BITS = { + PixelFormat::RGBA16F, PixelFormat::RG32F, PixelFormat::RGBA16UI, PixelFormat::RG32UI, + PixelFormat::RGBA16U, PixelFormat::RGBA16F, PixelFormat::RGBA16S, +}; +// Missing formats: +// PixelFormat::RGBA16I +// PixelFormat::RG32I + +// TODO: How should we handle 48 bits? + +constexpr std::array VIEW_CLASS_32_BITS = { + PixelFormat::RG16F, PixelFormat::R11FG11FB10F, PixelFormat::R32F, + PixelFormat::A2B10G10R10U, PixelFormat::RG16UI, PixelFormat::R32UI, + PixelFormat::RG16I, PixelFormat::R32I, PixelFormat::ABGR8U, + PixelFormat::RG16, PixelFormat::ABGR8S, PixelFormat::RG16S, + PixelFormat::RGBA8_SRGB, PixelFormat::E5B9G9R9F, PixelFormat::BGRA8, + PixelFormat::BGRA8_SRGB, +}; +// Missing formats: +// PixelFormat::RGBA8UI +// PixelFormat::RGBA8I +// PixelFormat::RGB10_A2_UI + +// TODO: How should we handle 24 bits? + +constexpr std::array VIEW_CLASS_16_BITS = { + PixelFormat::R16F, PixelFormat::RG8UI, PixelFormat::R16UI, PixelFormat::R16I, + PixelFormat::RG8U, PixelFormat::R16U, PixelFormat::RG8S, PixelFormat::R16S, +}; +// Missing formats: +// PixelFormat::RG8I + +constexpr std::array VIEW_CLASS_8_BITS = { + PixelFormat::R8UI, + PixelFormat::R8U, +}; +// Missing formats: +// PixelFormat::R8I +// PixelFormat::R8S + +constexpr std::array VIEW_CLASS_RGTC1_RED = { + PixelFormat::DXN1, +}; +// Missing formats: +// COMPRESSED_SIGNED_RED_RGTC1 + +constexpr std::array VIEW_CLASS_RGTC2_RG = { + PixelFormat::DXN2UNORM, + PixelFormat::DXN2SNORM, +}; + +constexpr std::array VIEW_CLASS_BPTC_UNORM = { + PixelFormat::BC7U, + PixelFormat::BC7U_SRGB, +}; + +constexpr std::array VIEW_CLASS_BPTC_FLOAT = { + PixelFormat::BC6H_SF16, + PixelFormat::BC6H_UF16, +}; + +// Compatibility table taken from Table 4.X.1 in: +// https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_copy_image.txt + +constexpr std::array COPY_CLASS_128_BITS = { + PixelFormat::RGBA32UI, PixelFormat::RGBA32F, PixelFormat::DXT23, + PixelFormat::DXT23_SRGB, PixelFormat::DXT45, PixelFormat::DXT45_SRGB, + PixelFormat::DXN2SNORM, PixelFormat::BC7U, PixelFormat::BC7U_SRGB, + PixelFormat::BC6H_SF16, PixelFormat::BC6H_UF16, +}; +// Missing formats: +// PixelFormat::RGBA32I +// COMPRESSED_RG_RGTC2 + +constexpr std::array COPY_CLASS_64_BITS = { + PixelFormat::RGBA16F, PixelFormat::RG32F, PixelFormat::RGBA16UI, PixelFormat::RG32UI, + PixelFormat::RGBA16U, PixelFormat::RGBA16S, PixelFormat::DXT1_SRGB, PixelFormat::DXT1, + +}; +// Missing formats: +// PixelFormat::RGBA16I +// PixelFormat::RG32I, +// COMPRESSED_RGB_S3TC_DXT1_EXT +// COMPRESSED_SRGB_S3TC_DXT1_EXT +// COMPRESSED_RGBA_S3TC_DXT1_EXT +// COMPRESSED_SIGNED_RED_RGTC1 + +void Enable(FormatCompatibility::Table& compatiblity, size_t format_a, size_t format_b) { + compatiblity[format_a][format_b] = true; + compatiblity[format_b][format_a] = true; +} + +void Enable(FormatCompatibility::Table& compatibility, PixelFormat format_a, PixelFormat format_b) { + Enable(compatibility, static_cast(format_a), static_cast(format_b)); +} + +template +void EnableRange(FormatCompatibility::Table& compatibility, const Range& range) { + for (auto it_a = range.begin(); it_a != range.end(); ++it_a) { + for (auto it_b = it_a; it_b != range.end(); ++it_b) { + Enable(*it_a, *it_b); + } + } +} + +} // Anonymous namespace + +FormatCompatibility::FormatCompatibility() { + for (size_t i = 0; i < MaxPixelFormat; ++i) { + // Identity is allowed + Enable(view, i, i); + } + + EnableRange(view, VIEW_CLASS_128_BITS); + EnableRange(view, VIEW_CLASS_96_BITS); + EnableRange(view, VIEW_CLASS_64_BITS); + EnableRange(view, VIEW_CLASS_32_BITS); + EnableRange(view, VIEW_CLASS_16_BITS); + EnableRange(view, VIEW_CLASS_8_BITS); + EnableRange(view, VIEW_CLASS_RGTC1_RED); + EnableRange(view, VIEW_CLASS_RGTC2_RG); + EnableRange(view, VIEW_CLASS_BPTC_UNORM); + EnableRange(view, VIEW_CLASS_BPTC_FLOAT); + + copy = view; + EnableRange(copy, COPY_CLASS_128_BITS); + EnableRange(copy, COPY_CLASS_64_BITS); +} + +} // namespace VideoCore::Surface diff --git a/src/video_core/compatible_formats.h b/src/video_core/compatible_formats.h new file mode 100644 index 000000000..d1082566d --- /dev/null +++ b/src/video_core/compatible_formats.h @@ -0,0 +1,32 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include + +#include "video_core/surface.h" + +namespace VideoCore::Surface { + +class FormatCompatibility { +public: + using Table = std::array, MaxPixelFormat>; + + explicit FormatCompatibility(); + + bool TestView(PixelFormat format_a, PixelFormat format_b) const noexcept { + return view[static_cast(format_a)][static_cast(format_b)]; + } + + bool TestCopy(PixelFormat format_a, PixelFormat format_b) const noexcept { + return copy[static_cast(format_a)][static_cast(format_b)]; + } + +private: + Table view; + Table copy; +}; + +} // namespace VideoCore::Surface From bb2cbdf7047ed765c236e2da0c04420082d7fd8f Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Fri, 26 Jun 2020 19:25:49 -0300 Subject: [PATCH 113/252] texture_cache: Test format compatibility before copying Avoid illegal copies. This intercepts the last step of a copy to avoid generating validation errors or corrupting the driver on some instances. We can create views and emit copies accordingly in future commits and remove this last-step validation. --- src/video_core/compatible_formats.cpp | 2 +- src/video_core/texture_cache/texture_cache.h | 25 ++++++++++++++++---- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/video_core/compatible_formats.cpp b/src/video_core/compatible_formats.cpp index 01e5c26ae..6c426b035 100644 --- a/src/video_core/compatible_formats.cpp +++ b/src/video_core/compatible_formats.cpp @@ -130,7 +130,7 @@ template void EnableRange(FormatCompatibility::Table& compatibility, const Range& range) { for (auto it_a = range.begin(); it_a != range.end(); ++it_a) { for (auto it_b = it_a; it_b != range.end(); ++it_b) { - Enable(*it_a, *it_b); + Enable(compatibility, *it_a, *it_b); } } } diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 85075e868..6207d8dfe 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -24,6 +24,7 @@ #include "core/core.h" #include "core/memory.h" #include "core/settings.h" +#include "video_core/compatible_formats.h" #include "video_core/dirty_flags.h" #include "video_core/engines/fermi_2d.h" #include "video_core/engines/maxwell_3d.h" @@ -47,8 +48,8 @@ class RasterizerInterface; namespace VideoCommon { +using VideoCore::Surface::FormatCompatibility; using VideoCore::Surface::PixelFormat; - using VideoCore::Surface::SurfaceTarget; using RenderTargetConfig = Tegra::Engines::Maxwell3D::Regs::RenderTargetConfig; @@ -595,7 +596,7 @@ private: } else { new_surface = GetUncachedSurface(gpu_addr, params); } - const auto& final_params = new_surface->GetSurfaceParams(); + const SurfaceParams& final_params = new_surface->GetSurfaceParams(); if (cr_params.type != final_params.type) { if (Settings::IsGPULevelExtreme()) { BufferCopy(current_surface, new_surface); @@ -603,7 +604,7 @@ private: } else { std::vector bricks = current_surface->BreakDown(final_params); for (auto& brick : bricks) { - ImageCopy(current_surface, new_surface, brick); + TryCopyImage(current_surface, new_surface, brick); } } Unregister(current_surface); @@ -694,7 +695,7 @@ private: } const CopyParams copy_params(0, 0, 0, 0, 0, base_layer, 0, mipmap, width, height, src_params.depth); - ImageCopy(surface, new_surface, copy_params); + TryCopyImage(surface, new_surface, copy_params); } } if (passed_tests == 0) { @@ -791,7 +792,7 @@ private: const u32 width = params.width; const u32 height = params.height; const CopyParams copy_params(0, 0, 0, 0, 0, slice, 0, 0, width, height, 1); - ImageCopy(surface, new_surface, copy_params); + TryCopyImage(surface, new_surface, copy_params); } for (const auto& surface : overlaps) { Unregister(surface); @@ -1192,6 +1193,19 @@ private: return {}; } + /// Try to do an image copy logging when formats are incompatible. + void TryCopyImage(TSurface& src, TSurface& dst, const CopyParams& copy) { + const SurfaceParams& src_params = src->GetSurfaceParams(); + const SurfaceParams& dst_params = dst->GetSurfaceParams(); + if (!format_compatibility.TestCopy(src_params.pixel_format, dst_params.pixel_format)) { + LOG_ERROR(HW_GPU, "Illegal copy between formats={{{}, {}}}", + static_cast(dst_params.pixel_format), + static_cast(src_params.pixel_format)); + return; + } + ImageCopy(src, dst, copy); + } + constexpr PixelFormat GetSiblingFormat(PixelFormat format) const { return siblings_table[static_cast(format)]; } @@ -1241,6 +1255,7 @@ private: VideoCore::RasterizerInterface& rasterizer; FormatLookupTable format_lookup_table; + FormatCompatibility format_compatibility; u64 ticks{}; From d3e9b45ce0327d6497884f796410682f4f1ed708 Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Sat, 27 Jun 2020 01:57:48 +0200 Subject: [PATCH 114/252] btm: Update function tables This was based on Switchbrew page: https://switchbrew.org/wiki/BTM_services "No comment" edition --- src/core/hle/service/btm/btm.cpp | 147 ++++++++++++++++--------------- 1 file changed, 76 insertions(+), 71 deletions(-) diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp index 251b3c9df..f6539a7a4 100644 --- a/src/core/hle/service/btm/btm.cpp +++ b/src/core/hle/service/btm/btm.cpp @@ -132,66 +132,71 @@ public: explicit BTM() : ServiceFramework{"btm"} { // clang-format off static const FunctionInfo functions[] = { - {0, nullptr, "Unknown1"}, - {1, nullptr, "Unknown2"}, - {2, nullptr, "RegisterSystemEventForConnectedDeviceCondition"}, - {3, nullptr, "Unknown3"}, - {4, nullptr, "Unknown4"}, - {5, nullptr, "Unknown5"}, - {6, nullptr, "Unknown6"}, - {7, nullptr, "Unknown7"}, - {8, nullptr, "RegisterSystemEventForRegisteredDeviceInfo"}, - {9, nullptr, "Unknown8"}, - {10, nullptr, "Unknown9"}, - {11, nullptr, "Unknown10"}, - {12, nullptr, "Unknown11"}, - {13, nullptr, "Unknown12"}, + {0, nullptr, "GetState"}, + {1, nullptr, "GetHostDeviceProperty"}, + {2, nullptr, "AcquireDeviceConditionEvent"}, + {3, nullptr, "GetDeviceCondition"}, + {4, nullptr, "SetBurstMode"}, + {5, nullptr, "SetSlotMode"}, + {6, nullptr, "SetBluetoothMode"}, + {7, nullptr, "SetWlanMode"}, + {8, nullptr, "AcquireDeviceInfoEvent"}, + {9, nullptr, "GetDeviceInfo"}, + {10, nullptr, "AddDeviceInfo"}, + {11, nullptr, "RemoveDeviceInfo"}, + {12, nullptr, "IncreaseDeviceInfoOrder"}, + {13, nullptr, "LlrNotify"}, {14, nullptr, "EnableRadio"}, {15, nullptr, "DisableRadio"}, - {16, nullptr, "Unknown13"}, - {17, nullptr, "Unknown14"}, - {18, nullptr, "Unknown15"}, - {19, nullptr, "Unknown16"}, - {20, nullptr, "Unknown17"}, - {21, nullptr, "Unknown18"}, - {22, nullptr, "Unknown19"}, - {23, nullptr, "Unknown20"}, - {24, nullptr, "Unknown21"}, - {25, nullptr, "Unknown22"}, - {26, nullptr, "Unknown23"}, - {27, nullptr, "Unknown24"}, - {28, nullptr, "Unknown25"}, - {29, nullptr, "Unknown26"}, - {30, nullptr, "Unknown27"}, - {31, nullptr, "Unknown28"}, - {32, nullptr, "Unknown29"}, - {33, nullptr, "Unknown30"}, - {34, nullptr, "Unknown31"}, - {35, nullptr, "Unknown32"}, - {36, nullptr, "Unknown33"}, - {37, nullptr, "Unknown34"}, - {38, nullptr, "Unknown35"}, - {39, nullptr, "Unknown36"}, - {40, nullptr, "Unknown37"}, - {41, nullptr, "Unknown38"}, - {42, nullptr, "Unknown39"}, - {43, nullptr, "Unknown40"}, - {44, nullptr, "Unknown41"}, - {45, nullptr, "Unknown42"}, - {46, nullptr, "Unknown43"}, - {47, nullptr, "Unknown44"}, - {48, nullptr, "Unknown45"}, - {49, nullptr, "Unknown46"}, - {50, nullptr, "Unknown47"}, - {51, nullptr, "Unknown48"}, - {52, nullptr, "Unknown49"}, - {53, nullptr, "Unknown50"}, - {54, nullptr, "Unknown51"}, - {55, nullptr, "Unknown52"}, - {56, nullptr, "Unknown53"}, - {57, nullptr, "Unknown54"}, - {58, nullptr, "Unknown55"}, - {59, nullptr, "Unknown56"}, + {16, nullptr, "HidDisconnect"}, + {17, nullptr, "HidSetRetransmissionMode"}, + {18, nullptr, "AcquireAwakeReqEvent"}, + {19, nullptr, "AcquireLlrStateEvent"}, + {20, nullptr, "IsLlrStarted"}, + {21, nullptr, "EnableSlotSaving"}, + {22, nullptr, "ProtectDeviceInfo"}, + {23, nullptr, "AcquireBleScanEvent"}, + {24, nullptr, "GetBleScanParameterGeneral"}, + {25, nullptr, "GetBleScanParameterSmartDevice"}, + {26, nullptr, "StartBleScanForGeneral"}, + {27, nullptr, "StopBleScanForGeneral"}, + {28, nullptr, "GetBleScanResultsForGeneral"}, + {29, nullptr, "StartBleScanForPairedDevice"}, + {30, nullptr, "StopBleScanForPairedDevice"}, + {31, nullptr, "StartBleScanForSmartDevice"}, + {32, nullptr, "StopBleScanForSmartDevice"}, + {33, nullptr, "GetBleScanResultsForSmartDevice"}, + {34, nullptr, "AcquireBleConnectionEvent"}, + {35, nullptr, "BleConnect"}, + {36, nullptr, "BleOverrideConnection"}, + {37, nullptr, "BleDisconnect"}, + {38, nullptr, "BleGetConnectionState"}, + {39, nullptr, "BleGetGattClientConditionList"}, + {40, nullptr, "AcquireBlePairingEvent"}, + {41, nullptr, "BlePairDevice"}, + {42, nullptr, "BleUnpairDeviceOnBoth"}, + {43, nullptr, "BleUnpairDevice"}, + {44, nullptr, "BleGetPairedAddresses"}, + {45, nullptr, "AcquireBleServiceDiscoveryEvent"}, + {46, nullptr, "GetGattServices"}, + {47, nullptr, "GetGattService"}, + {48, nullptr, "GetGattIncludedServices"}, + {49, nullptr, "GetBelongingService"}, + {50, nullptr, "GetGattCharacteristics"}, + {51, nullptr, "GetGattDescriptors"}, + {52, nullptr, "AcquireBleMtuConfigEvent"}, + {53, nullptr, "ConfigureBleMtu"}, + {54, nullptr, "GetBleMtu"}, + {55, nullptr, "RegisterBleGattDataPath"}, + {56, nullptr, "UnregisterBleGattDataPath"}, + {57, nullptr, "RegisterAppletResourceUserId"}, + {58, nullptr, "UnregisterAppletResourceUserId"}, + {59, nullptr, "SetAppletResourceUserId"}, + {60, nullptr, "Unknown"}, + {61, nullptr, "Unknown2"}, + {62, nullptr, "Unknown3"}, + {63, nullptr, "Unknown4"}, + {64, nullptr, "Unknown5"}, }; // clang-format on @@ -204,19 +209,19 @@ public: explicit BTM_DBG() : ServiceFramework{"btm:dbg"} { // clang-format off static const FunctionInfo functions[] = { - {0, nullptr, "RegisterSystemEventForDiscovery"}, - {1, nullptr, "Unknown1"}, - {2, nullptr, "Unknown2"}, - {3, nullptr, "Unknown3"}, - {4, nullptr, "Unknown4"}, - {5, nullptr, "Unknown5"}, - {6, nullptr, "Unknown6"}, - {7, nullptr, "Unknown7"}, - {8, nullptr, "Unknown8"}, - {9, nullptr, "Unknown9"}, - {10, nullptr, "Unknown10"}, - {11, nullptr, "Unknown11"}, - {12, nullptr, "Unknown11"}, + {0, nullptr, "AcquireDiscoveryEvent"}, + {1, nullptr, "StartDiscovery"}, + {2, nullptr, "CancelDiscovery"}, + {3, nullptr, "GetDeviceProperty"}, + {4, nullptr, "CreateBond"}, + {5, nullptr, "CancelBond"}, + {6, nullptr, "SetTsiMode"}, + {7, nullptr, "GeneralTest"}, + {8, nullptr, "HidConnect"}, + {9, nullptr, "GeneralGet"}, + {10, nullptr, "GetGattClientDisconnectionReason"}, + {11, nullptr, "GetBleConnectionParameter"}, + {12, nullptr, "GetBleConnectionParameterRequest"}, }; // clang-format on From db96b5ee3bd8820fcf310ffc283447985755c678 Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Sat, 27 Jun 2020 02:13:34 +0200 Subject: [PATCH 115/252] Update FAQ function name (1/2) --- src/yuzu/main.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 8b0961932..79c1f8219 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -183,7 +183,7 @@ private slots: void OnMenuReportCompatibility(); void OnOpenModsPage(); void OnOpenQuickstartGuide(); - void OnFAQ(); + void OnOpenFAQ(); /// Called whenever a user selects a game in the game list widget. void OnGameListLoadFile(QString game_path); void OnGameListOpenFolder(GameListOpenTarget target, const std::string& game_path); From b1f4de78741108d0df3a03ae5ba0482e1d2d531e Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Sat, 27 Jun 2020 02:14:29 +0200 Subject: [PATCH 116/252] Update FAQ function name (2/2) --- src/yuzu/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index e597f9733..e83843a03 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -840,7 +840,7 @@ void GMainWindow::ConnectMenuEvents() { connect(ui.action_Open_Mods_Page, &QAction::triggered, this, &GMainWindow::OnOpenModsPage); connect(ui.action_Open_Quickstart_Guide, &QAction::triggered, this, &GMainWindow::OnOpenQuickstartGuide); - connect(ui.action_Open_FAQ, &QAction::triggered, this, &GMainWindow::OnFAQ); + connect(ui.action_Open_FAQ, &QAction::triggered, this, &GMainWindow::OnOpenFAQ); connect(ui.action_Restart, &QAction::triggered, this, [this] { BootGame(QString(game_path)); }); connect(ui.action_Configure, &QAction::triggered, this, &GMainWindow::OnConfigure); @@ -1828,7 +1828,7 @@ void GMainWindow::OnOpenQuickstartGuide() { OpenURL(QUrl(QStringLiteral("https://yuzu-emu.org/help/quickstart/"))); } -void GMainWindow::OnFAQ() { +void GMainWindow::OnOpenFAQ() { OpenURL(QUrl(QStringLiteral("https://yuzu-emu.org/wiki/faq/"))); } From 3828aa4927d604804f09bd2b8a307f3e50a8c986 Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Sat, 27 Jun 2020 02:17:51 +0200 Subject: [PATCH 117/252] es: Update function table This was based on Switchbrew page: https://switchbrew.org/wiki/ETicket_services --- src/core/hle/service/es/es.cpp | 43 ++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/src/core/hle/service/es/es.cpp b/src/core/hle/service/es/es.cpp index f8e9df4b1..593772ee2 100644 --- a/src/core/hle/service/es/es.cpp +++ b/src/core/hle/service/es/es.cpp @@ -27,8 +27,8 @@ public: {8, &ETicket::GetTitleKey, "GetTitleKey"}, {9, &ETicket::CountCommonTicket, "CountCommonTicket"}, {10, &ETicket::CountPersonalizedTicket, "CountPersonalizedTicket"}, - {11, &ETicket::ListCommonTicket, "ListCommonTicket"}, - {12, &ETicket::ListPersonalizedTicket, "ListPersonalizedTicket"}, + {11, &ETicket::ListCommonTicket, "ListCommonTicketRightsIds"}, + {12, &ETicket::ListPersonalizedTicket, "ListPersonalizedTicketRightsIds"}, {13, nullptr, "ListMissingPersonalizedTicket"}, {14, &ETicket::GetCommonTicketSize, "GetCommonTicketSize"}, {15, &ETicket::GetPersonalizedTicketSize, "GetPersonalizedTicketSize"}, @@ -55,7 +55,46 @@ public: {36, nullptr, "DeleteAllInactiveELicenseRequiredPersonalizedTicket"}, {37, nullptr, "OwnTicket2"}, {38, nullptr, "OwnTicket3"}, + {501, nullptr, "Unknown"}, + {502, nullptr, "Unknown2"}, {503, nullptr, "GetTitleKey"}, + {504, nullptr, "Unknown3"}, + {508, nullptr, "Unknown4"}, + {509, nullptr, "Unknown5"}, + {510, nullptr, "Unknown6"}, + {511, nullptr, "Unknown7"}, + {1001, nullptr, "Unknown8"}, + {1002, nullptr, "Unknown9"}, + {1003, nullptr, "Unknown10"}, + {1004, nullptr, "Unknown11"}, + {1005, nullptr, "Unknown12"}, + {1006, nullptr, "Unknown13"}, + {1007, nullptr, "Unknown14"}, + {1009, nullptr, "Unknown15"}, + {1010, nullptr, "Unknown16"}, + {1011, nullptr, "Unknown17"}, + {1012, nullptr, "Unknown18"}, + {1013, nullptr, "Unknown19"}, + {1014, nullptr, "Unknown20"}, + {1015, nullptr, "Unknown21"}, + {1016, nullptr, "Unknown22"}, + {1017, nullptr, "Unknown23"}, + {1018, nullptr, "Unknown24"}, + {1019, nullptr, "Unknown25"}, + {1020, nullptr, "Unknown26"}, + {1021, nullptr, "Unknown27"}, + {1501, nullptr, "Unknown28"}, + {1502, nullptr, "Unknown29"}, + {1503, nullptr, "Unknown30"}, + {1504, nullptr, "Unknown31"}, + {1505, nullptr, "Unknown32"}, + {2000, nullptr, "Unknown33"}, + {2001, nullptr, "Unknown34"}, + {2100, nullptr, "Unknown35"}, + {2501, nullptr, "Unknown36"}, + {2502, nullptr, "Unknown37"}, + {3001, nullptr, "Unknown38"}, + {3002, nullptr, "Unknown39"}, }; // clang-format on RegisterHandlers(functions); From 0f4a611129fd5b76ebdafd4f8c614b1257decae3 Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Sat, 27 Jun 2020 02:25:04 +0200 Subject: [PATCH 118/252] eupld: Update function table This was based on Switchbrew page: https://switchbrew.org/wiki/Error_Upload_services --- src/core/hle/service/eupld/eupld.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/hle/service/eupld/eupld.cpp b/src/core/hle/service/eupld/eupld.cpp index 2df30acee..0d6d244f4 100644 --- a/src/core/hle/service/eupld/eupld.cpp +++ b/src/core/hle/service/eupld/eupld.cpp @@ -19,6 +19,7 @@ public: {1, nullptr, "ImportCrt"}, {2, nullptr, "ImportPki"}, {3, nullptr, "SetAutoUpload"}, + {4, nullptr, "GetAutoUpload"}, }; // clang-format on From b5d54619cc8cc3a4860f783c1a82255b2d87d7ee Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Sat, 27 Jun 2020 02:31:44 +0200 Subject: [PATCH 119/252] aoc: Update function table (#4170) * aoc: Update function table * Remove comments --- src/core/hle/service/aoc/aoc_u.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp index 4227a4adf..8e79f707b 100644 --- a/src/core/hle/service/aoc/aoc_u.cpp +++ b/src/core/hle/service/aoc/aoc_u.cpp @@ -60,6 +60,7 @@ AOC_U::AOC_U(Core::System& system) {6, nullptr, "PrepareAddOnContentByApplicationId"}, {7, &AOC_U::PrepareAddOnContent, "PrepareAddOnContent"}, {8, &AOC_U::GetAddOnContentListChangedEvent, "GetAddOnContentListChangedEvent"}, + {9, nullptr, "GetAddOnContentLostErrorCode"}, {100, nullptr, "CreateEcPurchasedEventManager"}, {101, nullptr, "CreatePermanentEcPurchasedEventManager"}, }; From ca25a3845eed214ac3bd76d37c47739dfbd56865 Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Sat, 27 Jun 2020 02:32:26 +0200 Subject: [PATCH 120/252] am: Update function tables and add missing classes (#4169) * am: Update function tables and add missing classes * Remove comments (1/5) * Remove comments (2/5) * Remove comments (3/5) * Remove comments (4/5) * Remove comments (5/5) * Remove unused classes (1/2) * Remove unused classes (2/2) --- src/core/hle/service/am/am.cpp | 18 ++++++++++-------- src/core/hle/service/am/am.h | 2 +- src/core/hle/service/am/spsm.cpp | 16 ++++++++-------- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 4df74c4f9..20f366635 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -68,6 +68,7 @@ IWindowController::IWindowController(Core::System& system_) static const FunctionInfo functions[] = { {0, nullptr, "CreateWindow"}, {1, &IWindowController::GetAppletResourceUserId, "GetAppletResourceUserId"}, + {2, nullptr, "GetAppletResourceUserIdOfCallerApplet"}, {10, &IWindowController::AcquireForegroundRights, "AcquireForegroundRights"}, {11, nullptr, "ReleaseForegroundRights"}, {12, nullptr, "RejectToChangeIntoBackground"}, @@ -189,8 +190,8 @@ IDisplayController::IDisplayController() : ServiceFramework("IDisplayController" {5, nullptr, "GetLastForegroundCaptureImageEx"}, {6, nullptr, "GetLastApplicationCaptureImageEx"}, {7, nullptr, "GetCallerAppletCaptureImageEx"}, - {8, nullptr, "TakeScreenShotOfOwnLayer"}, // 2.0.0+ - {9, nullptr, "CopyBetweenCaptureBuffers"}, // 5.0.0+ + {8, nullptr, "TakeScreenShotOfOwnLayer"}, + {9, nullptr, "CopyBetweenCaptureBuffers"}, {10, nullptr, "AcquireLastApplicationCaptureBuffer"}, {11, nullptr, "ReleaseLastApplicationCaptureBuffer"}, {12, nullptr, "AcquireLastForegroundCaptureBuffer"}, @@ -200,17 +201,14 @@ IDisplayController::IDisplayController() : ServiceFramework("IDisplayController" {16, nullptr, "AcquireLastApplicationCaptureBufferEx"}, {17, nullptr, "AcquireLastForegroundCaptureBufferEx"}, {18, nullptr, "AcquireCallerAppletCaptureBufferEx"}, - // 2.0.0+ {20, nullptr, "ClearCaptureBuffer"}, {21, nullptr, "ClearAppletTransitionBuffer"}, - // 4.0.0+ {22, nullptr, "AcquireLastApplicationCaptureSharedBuffer"}, {23, nullptr, "ReleaseLastApplicationCaptureSharedBuffer"}, {24, nullptr, "AcquireLastForegroundCaptureSharedBuffer"}, {25, nullptr, "ReleaseLastForegroundCaptureSharedBuffer"}, {26, nullptr, "AcquireCallerAppletCaptureSharedBuffer"}, {27, nullptr, "ReleaseCallerAppletCaptureSharedBuffer"}, - // 6.0.0+ {28, nullptr, "TakeScreenShotOfOwnLayerEx"}, }; // clang-format on @@ -225,7 +223,7 @@ IDebugFunctions::IDebugFunctions() : ServiceFramework{"IDebugFunctions"} { static const FunctionInfo functions[] = { {0, nullptr, "NotifyMessageToHomeMenuForDebug"}, {1, nullptr, "OpenMainApplication"}, - {10, nullptr, "EmulateButtonEvent"}, + {10, nullptr, "PerformSystemButtonPressing"}, {20, nullptr, "InvalidateTransitionLayer"}, {30, nullptr, "RequestLaunchApplicationWithUserAndArgumentForDebug"}, {40, nullptr, "GetAppletResourceUsageInfo"}, @@ -267,7 +265,7 @@ ISelfController::ISelfController(Core::System& system, {16, &ISelfController::SetOutOfFocusSuspendingEnabled, "SetOutOfFocusSuspendingEnabled"}, {17, nullptr, "SetControllerFirmwareUpdateSection"}, {18, nullptr, "SetRequiresCaptureButtonShortPressedMessage"}, - {19, &ISelfController::SetScreenShotImageOrientation, "SetScreenShotImageOrientation"}, + {19, &ISelfController::SetAlbumImageOrientation, "SetAlbumImageOrientation"}, {20, nullptr, "SetDesirableKeyboardLayout"}, {40, &ISelfController::CreateManagedDisplayLayer, "CreateManagedDisplayLayer"}, {41, nullptr, "IsSystemBufferSharingEnabled"}, @@ -443,7 +441,7 @@ void ISelfController::SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& rb.Push(RESULT_SUCCESS); } -void ISelfController::SetScreenShotImageOrientation(Kernel::HLERequestContext& ctx) { +void ISelfController::SetAlbumImageOrientation(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_AM, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; @@ -607,6 +605,7 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system, {20, nullptr, "PushToGeneralChannel"}, {30, nullptr, "GetHomeButtonReaderLockAccessor"}, {31, nullptr, "GetReaderLockAccessorEx"}, + {32, nullptr, "GetWriterLockAccessorEx"}, {40, nullptr, "GetCradleFwVersion"}, {50, &ICommonStateGetter::IsVrModeEnabled, "IsVrModeEnabled"}, {51, &ICommonStateGetter::SetVrModeEnabled, "SetVrModeEnabled"}, @@ -1132,6 +1131,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_) {24, nullptr, "GetLaunchStorageInfoForDebug"}, {25, &IApplicationFunctions::ExtendSaveData, "ExtendSaveData"}, {26, &IApplicationFunctions::GetSaveDataSize, "GetSaveDataSize"}, + {27, nullptr, "CreateCacheStorage"}, {30, &IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed, "BeginBlockingHomeButtonShortAndLongPressed"}, {31, &IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed, "EndBlockingHomeButtonShortAndLongPressed"}, {32, &IApplicationFunctions::BeginBlockingHomeButton, "BeginBlockingHomeButton"}, @@ -1157,6 +1157,8 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_) {120, nullptr, "ExecuteProgram"}, {121, nullptr, "ClearUserChannel"}, {122, nullptr, "UnpopToUserChannel"}, + {123, nullptr, "GetPreviousProgramIndex"}, + {124, nullptr, "EnableApplicationAllThreadDumpOnCrash"}, {130, &IApplicationFunctions::GetGpuErrorDetectedSystemEvent, "GetGpuErrorDetectedSystemEvent"}, {140, &IApplicationFunctions::GetFriendInvitationStorageChannelEvent, "GetFriendInvitationStorageChannelEvent"}, {141, nullptr, "TryPopFromFriendInvitationStorageChannel"}, diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 469f7f814..2f69466ec 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -138,7 +138,7 @@ private: void SetFocusHandlingMode(Kernel::HLERequestContext& ctx); void SetRestartMessageEnabled(Kernel::HLERequestContext& ctx); void SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx); - void SetScreenShotImageOrientation(Kernel::HLERequestContext& ctx); + void SetAlbumImageOrientation(Kernel::HLERequestContext& ctx); void CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx); void SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx); void SetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx); diff --git a/src/core/hle/service/am/spsm.cpp b/src/core/hle/service/am/spsm.cpp index 003ee8667..f27729ce7 100644 --- a/src/core/hle/service/am/spsm.cpp +++ b/src/core/hle/service/am/spsm.cpp @@ -10,17 +10,17 @@ SPSM::SPSM() : ServiceFramework{"spsm"} { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "GetState"}, - {1, nullptr, "SleepSystemAndWaitAwake"}, - {2, nullptr, "Unknown1"}, - {3, nullptr, "Unknown2"}, + {1, nullptr, "EnterSleep"}, + {2, nullptr, "GetLastWakeReason"}, + {3, nullptr, "Shutdown"}, {4, nullptr, "GetNotificationMessageEventHandle"}, - {5, nullptr, "Unknown3"}, - {6, nullptr, "Unknown4"}, - {7, nullptr, "Unknown5"}, + {5, nullptr, "ReceiveNotificationMessage"}, + {6, nullptr, "AnalyzeLogForLastSleepWakeSequence"}, + {7, nullptr, "ResetEventLog"}, {8, nullptr, "AnalyzePerformanceLogForLastSleepWakeSequence"}, {9, nullptr, "ChangeHomeButtonLongPressingTime"}, - {10, nullptr, "Unknown6"}, - {11, nullptr, "Unknown7"}, + {10, nullptr, "PutErrorState"}, + {11, nullptr, "InvalidateCurrentHomeButtonPressing"}, }; // clang-format on From e6fee39ae74ccc0d21ee7fedc2d9e47196aea74e Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Sat, 27 Jun 2020 02:33:25 +0200 Subject: [PATCH 121/252] bcat: Update function tables and add missing classes (#4172) * bcat: Update function tables and add missing classes --- src/core/hle/service/bcat/bcat.cpp | 2 ++ src/core/hle/service/bcat/module.cpp | 3 +++ 2 files changed, 5 insertions(+) diff --git a/src/core/hle/service/bcat/bcat.cpp b/src/core/hle/service/bcat/bcat.cpp index 8bb2528c9..b31766212 100644 --- a/src/core/hle/service/bcat/bcat.cpp +++ b/src/core/hle/service/bcat/bcat.cpp @@ -14,6 +14,8 @@ BCAT::BCAT(Core::System& system, std::shared_ptr module, {0, &BCAT::CreateBcatService, "CreateBcatService"}, {1, &BCAT::CreateDeliveryCacheStorageService, "CreateDeliveryCacheStorageService"}, {2, &BCAT::CreateDeliveryCacheStorageServiceWithApplicationId, "CreateDeliveryCacheStorageServiceWithApplicationId"}, + {3, nullptr, "CreateDeliveryCacheProgressService"}, + {4, nullptr, "CreateDeliveryCacheProgressServiceWithApplicationId"}, }; // clang-format on RegisterHandlers(functions); diff --git a/src/core/hle/service/bcat/module.cpp b/src/core/hle/service/bcat/module.cpp index 34aba7a27..603b64d4f 100644 --- a/src/core/hle/service/bcat/module.cpp +++ b/src/core/hle/service/bcat/module.cpp @@ -143,10 +143,13 @@ public: {20401, nullptr, "UnregisterSystemApplicationDeliveryTask"}, {20410, nullptr, "SetSystemApplicationDeliveryTaskTimer"}, {30100, &IBcatService::SetPassphrase, "SetPassphrase"}, + {30101, nullptr, "Unknown"}, + {30102, nullptr, "Unknown2"}, {30200, nullptr, "RegisterBackgroundDeliveryTask"}, {30201, nullptr, "UnregisterBackgroundDeliveryTask"}, {30202, nullptr, "BlockDeliveryTask"}, {30203, nullptr, "UnblockDeliveryTask"}, + {30210, nullptr, "SetDeliveryTaskTimer"}, {30300, nullptr, "RegisterSystemApplicationDeliveryTasks"}, {90100, nullptr, "EnumerateBackgroundDeliveryTask"}, {90200, nullptr, "GetDeliveryList"}, From 6e14edbcc22c33bf32702011eb27412b1807cfe2 Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Sat, 27 Jun 2020 02:33:55 +0200 Subject: [PATCH 122/252] bpc: Update function tables (#4173) * bpc: Update function tables This was based on Switchbrew page: https://switchbrew.org/wiki/PCV_services --- src/core/hle/service/bpc/bpc.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/core/hle/service/bpc/bpc.cpp b/src/core/hle/service/bpc/bpc.cpp index 1c1ecdb60..fac6b2f9c 100644 --- a/src/core/hle/service/bpc/bpc.cpp +++ b/src/core/hle/service/bpc/bpc.cpp @@ -23,9 +23,14 @@ public: {5, nullptr, "GetBoardPowerControlEvent"}, {6, nullptr, "GetSleepButtonState"}, {7, nullptr, "GetPowerEvent"}, - {8, nullptr, "Unknown1"}, - {9, nullptr, "Unknown2"}, - {10, nullptr, "Unknown3"}, + {8, nullptr, "CreateWakeupTimer"}, + {9, nullptr, "CancelWakeupTimer"}, + {10, nullptr, "EnableWakeupTimerOnDevice"}, + {11, nullptr, "CreateWakeupTimerEx"}, + {12, nullptr, "GetLastEnabledWakeupTimerType"}, + {13, nullptr, "CleanAllWakeupTimers"}, + {14, nullptr, "Unknown"}, + {15, nullptr, "Unknown2"}, }; // clang-format on @@ -38,10 +43,11 @@ public: explicit BPC_R() : ServiceFramework{"bpc:r"} { // clang-format off static const FunctionInfo functions[] = { - {0, nullptr, "GetExternalRtcValue"}, - {1, nullptr, "SetExternalRtcValue"}, - {2, nullptr, "ReadExternalRtcResetFlag"}, - {3, nullptr, "ClearExternalRtcResetFlag"}, + {0, nullptr, "GetRtcTime"}, + {1, nullptr, "SetRtcTime"}, + {2, nullptr, "GetRtcResetDetected"}, + {3, nullptr, "ClearRtcResetDetected"}, + {4, nullptr, "SetUpRtcResetOnShutdown"}, }; // clang-format on From 032b7d490dfc456c271696997ca11b9572867600 Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Sat, 27 Jun 2020 02:34:29 +0200 Subject: [PATCH 123/252] btdrv: Update function table (#4174) * btdrv: Update function table --- src/core/hle/service/btdrv/btdrv.cpp | 167 ++++++++++++++------------- 1 file changed, 84 insertions(+), 83 deletions(-) diff --git a/src/core/hle/service/btdrv/btdrv.cpp b/src/core/hle/service/btdrv/btdrv.cpp index 40a06c9fd..f311afa2f 100644 --- a/src/core/hle/service/btdrv/btdrv.cpp +++ b/src/core/hle/service/btdrv/btdrv.cpp @@ -58,102 +58,103 @@ public: {1, nullptr, "InitializeBluetooth"}, {2, nullptr, "EnableBluetooth"}, {3, nullptr, "DisableBluetooth"}, - {4, nullptr, "CleanupBluetooth"}, + {4, nullptr, "FinalizeBluetooth"}, {5, nullptr, "GetAdapterProperties"}, {6, nullptr, "GetAdapterProperty"}, {7, nullptr, "SetAdapterProperty"}, - {8, nullptr, "StartDiscovery"}, - {9, nullptr, "CancelDiscovery"}, + {8, nullptr, "StartInquiry"}, + {9, nullptr, "StopInquiry"}, {10, nullptr, "CreateBond"}, {11, nullptr, "RemoveBond"}, {12, nullptr, "CancelBond"}, - {13, nullptr, "PinReply"}, - {14, nullptr, "SspReply"}, + {13, nullptr, "RespondToPinRequest"}, + {14, nullptr, "RespondToSspRequest"}, {15, nullptr, "GetEventInfo"}, {16, nullptr, "InitializeHid"}, - {17, nullptr, "HidConnect"}, - {18, nullptr, "HidDisconnect"}, - {19, nullptr, "HidSendData"}, - {20, nullptr, "HidSendData2"}, - {21, nullptr, "HidSetReport"}, - {22, nullptr, "HidGetReport"}, - {23, nullptr, "HidWakeController"}, - {24, nullptr, "HidAddPairedDevice"}, - {25, nullptr, "HidGetPairedDevice"}, - {26, nullptr, "CleanupHid"}, - {27, nullptr, "HidGetEventInfo"}, - {28, nullptr, "ExtSetTsi"}, - {29, nullptr, "ExtSetBurstMode"}, - {30, nullptr, "ExtSetZeroRetran"}, - {31, nullptr, "ExtSetMcMode"}, - {32, nullptr, "ExtStartLlrMode"}, - {33, nullptr, "ExtExitLlrMode"}, - {34, nullptr, "ExtSetRadio"}, - {35, nullptr, "ExtSetVisibility"}, - {36, nullptr, "ExtSetTbfcScan"}, + {17, nullptr, "OpenHidConnection"}, + {18, nullptr, "CloseHidConnection"}, + {19, nullptr, "WriteHidData"}, + {20, nullptr, "WriteHidData2"}, + {21, nullptr, "SetHidReport"}, + {22, nullptr, "GetHidReport"}, + {23, nullptr, "TriggerConnection"}, + {24, nullptr, "AddPairedDeviceInfo"}, + {25, nullptr, "GetPairedDeviceInfo"}, + {26, nullptr, "FinalizeHid"}, + {27, nullptr, "GetHidEventInfo"}, + {28, nullptr, "SetTsi"}, + {29, nullptr, "EnableBurstMode"}, + {30, nullptr, "SetZeroRetransmission"}, + {31, nullptr, "EnableMcMode"}, + {32, nullptr, "EnableLlrScan"}, + {33, nullptr, "DisableLlrScan"}, + {34, nullptr, "EnableRadio"}, + {35, nullptr, "SetVisibility"}, + {36, nullptr, "EnableTbfcScan"}, {37, nullptr, "RegisterHidReportEvent"}, - {38, nullptr, "HidGetReportEventInfo"}, + {38, nullptr, "GetHidReportEventInfo"}, {39, nullptr, "GetLatestPlr"}, - {40, nullptr, "ExtGetPendingConnections"}, + {40, nullptr, "GetPendingConnections"}, {41, nullptr, "GetChannelMap"}, - {42, nullptr, "EnableBluetoothBoostSetting"}, - {43, nullptr, "IsBluetoothBoostSettingEnabled"}, - {44, nullptr, "EnableBluetoothAfhSetting"}, - {45, nullptr, "IsBluetoothAfhSettingEnabled"}, - {46, nullptr, "InitializeBluetoothLe"}, - {47, nullptr, "EnableBluetoothLe"}, - {48, nullptr, "DisableBluetoothLe"}, - {49, nullptr, "CleanupBluetoothLe"}, - {50, nullptr, "SetLeVisibility"}, - {51, nullptr, "SetLeConnectionParameter"}, - {52, nullptr, "SetLeDefaultConnectionParameter"}, - {53, nullptr, "SetLeAdvertiseData"}, - {54, nullptr, "SetLeAdvertiseParameter"}, - {55, nullptr, "StartLeScan"}, - {56, nullptr, "StopLeScan"}, - {57, nullptr, "AddLeScanFilterCondition"}, - {58, nullptr, "DeleteLeScanFilterCondition"}, - {59, nullptr, "DeleteLeScanFilter"}, - {60, nullptr, "ClearLeScanFilters"}, - {61, nullptr, "EnableLeScanFilter"}, - {62, nullptr, "RegisterLeClient"}, - {63, nullptr, "UnregisterLeClient"}, - {64, nullptr, "UnregisterLeClientAll"}, - {65, nullptr, "LeClientConnect"}, - {66, nullptr, "LeClientCancelConnection"}, - {67, nullptr, "LeClientDisconnect"}, - {68, nullptr, "LeClientGetAttributes"}, - {69, nullptr, "LeClientDiscoverService"}, - {70, nullptr, "LeClientConfigureMtu"}, - {71, nullptr, "RegisterLeServer"}, - {72, nullptr, "UnregisterLeServer"}, - {73, nullptr, "LeServerConnect"}, - {74, nullptr, "LeServerDisconnect"}, - {75, nullptr, "CreateLeService"}, - {76, nullptr, "StartLeService"}, - {77, nullptr, "AddLeCharacteristic"}, - {78, nullptr, "AddLeDescriptor"}, - {79, nullptr, "GetLeCoreEventInfo"}, - {80, nullptr, "LeGetFirstCharacteristic"}, - {81, nullptr, "LeGetNextCharacteristic"}, - {82, nullptr, "LeGetFirstDescriptor"}, - {83, nullptr, "LeGetNextDescriptor"}, - {84, nullptr, "RegisterLeCoreDataPath"}, - {85, nullptr, "UnregisterLeCoreDataPath"}, - {86, nullptr, "RegisterLeHidDataPath"}, - {87, nullptr, "UnregisterLeHidDataPath"}, - {88, nullptr, "RegisterLeDataPath"}, - {89, nullptr, "UnregisterLeDataPath"}, - {90, nullptr, "LeClientReadCharacteristic"}, - {91, nullptr, "LeClientReadDescriptor"}, - {92, nullptr, "LeClientWriteCharacteristic"}, - {93, nullptr, "LeClientWriteDescriptor"}, - {94, nullptr, "LeClientRegisterNotification"}, - {95, nullptr, "LeClientDeregisterNotification"}, + {42, nullptr, "EnableTxPowerBoostSetting"}, + {43, nullptr, "IsTxPowerBoostSettingEnabled"}, + {44, nullptr, "EnableAfhSetting"}, + {45, nullptr, "IsAfhSettingEnabled"}, + {46, nullptr, "InitializeBle"}, + {47, nullptr, "EnableBle"}, + {48, nullptr, "DisableBle"}, + {49, nullptr, "FinalizeBle"}, + {50, nullptr, "SetBleVisibility"}, + {51, nullptr, "SetBleConnectionParameter"}, + {52, nullptr, "SetBleDefaultConnectionParameter"}, + {53, nullptr, "SetBleAdvertiseData"}, + {54, nullptr, "SetBleAdvertiseParameter"}, + {55, nullptr, "StartBleScan"}, + {56, nullptr, "StopBleScan"}, + {57, nullptr, "AddBleScanFilterCondition"}, + {58, nullptr, "DeleteBleScanFilterCondition"}, + {59, nullptr, "DeleteBleScanFilter"}, + {60, nullptr, "ClearBleScanFilters"}, + {61, nullptr, "EnableBleScanFilter"}, + {62, nullptr, "RegisterGattClient"}, + {63, nullptr, "UnregisterGattClient"}, + {64, nullptr, "UnregisterAllGattClients"}, + {65, nullptr, "ConnectGattServer"}, + {66, nullptr, "CancelConnectGattServer"}, + {67, nullptr, "DisconnectGattServer"}, + {68, nullptr, "GetGattAttribute"}, + {69, nullptr, "GetGattService"}, + {70, nullptr, "ConfigureAttMtu"}, + {71, nullptr, "RegisterGattServer"}, + {72, nullptr, "UnregisterGattServer"}, + {73, nullptr, "ConnectGattClient"}, + {74, nullptr, "DisconnectGattClient"}, + {75, nullptr, "AddGattService"}, + {76, nullptr, "EnableGattService"}, + {77, nullptr, "AddGattCharacteristic"}, + {78, nullptr, "AddGattDescriptor"}, + {79, nullptr, "GetBleManagedEventInfo"}, + {80, nullptr, "GetGattFirstCharacteristic"}, + {81, nullptr, "GetGattNextCharacteristic"}, + {82, nullptr, "GetGattFirstDescriptor"}, + {83, nullptr, "GetGattNextDescriptor"}, + {84, nullptr, "RegisterGattManagedDataPath"}, + {85, nullptr, "UnregisterGattManagedDataPath"}, + {86, nullptr, "RegisterGattHidDataPath"}, + {87, nullptr, "UnregisterGattHidDataPath"}, + {88, nullptr, "RegisterGattDataPath"}, + {89, nullptr, "UnregisterGattDataPath"}, + {90, nullptr, "ReadGattCharacteristic"}, + {91, nullptr, "ReadGattDescriptor"}, + {92, nullptr, "WriteGattCharacteristic"}, + {93, nullptr, "WriteGattDescriptor"}, + {94, nullptr, "RegisterGattNotification"}, + {95, nullptr, "UnregisterGattNotification"}, {96, nullptr, "GetLeHidEventInfo"}, {97, nullptr, "RegisterBleHidEvent"}, - {98, nullptr, "SetLeScanParameter"}, - {256, nullptr, "GetIsManufacturingMode"}, + {98, nullptr, "SetBleScanParameter"}, + {99, nullptr, "MoveToSecondaryPiconet"}, + {256, nullptr, "IsManufacturingMode"}, {257, nullptr, "EmulateBluetoothCrash"}, {258, nullptr, "GetBleChannelMap"}, }; From 0b23ce6ef2e97196c1388f90843b3970d51568fb Mon Sep 17 00:00:00 2001 From: David Marcec Date: Sat, 27 Jun 2020 10:42:46 +1000 Subject: [PATCH 124/252] btm: Give better names for unknown functions --- src/core/hle/service/btm/btm.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp index f6539a7a4..0d251c6d0 100644 --- a/src/core/hle/service/btm/btm.cpp +++ b/src/core/hle/service/btm/btm.cpp @@ -192,11 +192,11 @@ public: {57, nullptr, "RegisterAppletResourceUserId"}, {58, nullptr, "UnregisterAppletResourceUserId"}, {59, nullptr, "SetAppletResourceUserId"}, - {60, nullptr, "Unknown"}, - {61, nullptr, "Unknown2"}, - {62, nullptr, "Unknown3"}, - {63, nullptr, "Unknown4"}, - {64, nullptr, "Unknown5"}, + {60, nullptr, "Unknown60"}, + {61, nullptr, "Unknown61"}, + {62, nullptr, "Unknown62"}, + {63, nullptr, "Unknown63"}, + {64, nullptr, "Unknown64"}, }; // clang-format on From bc51a9365ba20303452792429a4ae611938f8d9c Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Sat, 27 Jun 2020 02:43:22 +0200 Subject: [PATCH 125/252] Update function names --- src/core/hle/service/es/es.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/hle/service/es/es.cpp b/src/core/hle/service/es/es.cpp index 593772ee2..884c818da 100644 --- a/src/core/hle/service/es/es.cpp +++ b/src/core/hle/service/es/es.cpp @@ -27,8 +27,8 @@ public: {8, &ETicket::GetTitleKey, "GetTitleKey"}, {9, &ETicket::CountCommonTicket, "CountCommonTicket"}, {10, &ETicket::CountPersonalizedTicket, "CountPersonalizedTicket"}, - {11, &ETicket::ListCommonTicket, "ListCommonTicketRightsIds"}, - {12, &ETicket::ListPersonalizedTicket, "ListPersonalizedTicketRightsIds"}, + {11, &ETicket::ListCommonTicketRightsIds, "ListCommonTicketRightsIds"}, + {12, &ETicket::ListPersonalizedTicketRightsIds, "ListPersonalizedTicketRightsIds"}, {13, nullptr, "ListMissingPersonalizedTicket"}, {14, &ETicket::GetCommonTicketSize, "GetCommonTicketSize"}, {15, &ETicket::GetPersonalizedTicketSize, "GetPersonalizedTicketSize"}, @@ -186,7 +186,7 @@ private: rb.Push(count); } - void ListCommonTicket(Kernel::HLERequestContext& ctx) { + void ListCommonTicketRightsIds(Kernel::HLERequestContext& ctx) { u32 out_entries; if (keys.GetCommonTickets().empty()) out_entries = 0; @@ -209,7 +209,7 @@ private: rb.Push(out_entries); } - void ListPersonalizedTicket(Kernel::HLERequestContext& ctx) { + void ListPersonalizedTicketRightsIds(Kernel::HLERequestContext& ctx) { u32 out_entries; if (keys.GetPersonalizedTickets().empty()) out_entries = 0; From 2d82b7f1a1eb43e3f0c9a1695a8ada3db2728462 Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Sat, 27 Jun 2020 02:48:29 +0200 Subject: [PATCH 126/252] Use better names for "Unknown"s --- src/core/hle/service/es/es.cpp | 78 +++++++++++++++++----------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/src/core/hle/service/es/es.cpp b/src/core/hle/service/es/es.cpp index 884c818da..9365f27e1 100644 --- a/src/core/hle/service/es/es.cpp +++ b/src/core/hle/service/es/es.cpp @@ -55,46 +55,46 @@ public: {36, nullptr, "DeleteAllInactiveELicenseRequiredPersonalizedTicket"}, {37, nullptr, "OwnTicket2"}, {38, nullptr, "OwnTicket3"}, - {501, nullptr, "Unknown"}, - {502, nullptr, "Unknown2"}, + {501, nullptr, "Unknown501"}, + {502, nullptr, "Unknown502"}, {503, nullptr, "GetTitleKey"}, - {504, nullptr, "Unknown3"}, - {508, nullptr, "Unknown4"}, - {509, nullptr, "Unknown5"}, - {510, nullptr, "Unknown6"}, - {511, nullptr, "Unknown7"}, - {1001, nullptr, "Unknown8"}, - {1002, nullptr, "Unknown9"}, - {1003, nullptr, "Unknown10"}, - {1004, nullptr, "Unknown11"}, - {1005, nullptr, "Unknown12"}, - {1006, nullptr, "Unknown13"}, - {1007, nullptr, "Unknown14"}, - {1009, nullptr, "Unknown15"}, - {1010, nullptr, "Unknown16"}, - {1011, nullptr, "Unknown17"}, - {1012, nullptr, "Unknown18"}, - {1013, nullptr, "Unknown19"}, - {1014, nullptr, "Unknown20"}, - {1015, nullptr, "Unknown21"}, - {1016, nullptr, "Unknown22"}, - {1017, nullptr, "Unknown23"}, - {1018, nullptr, "Unknown24"}, - {1019, nullptr, "Unknown25"}, - {1020, nullptr, "Unknown26"}, - {1021, nullptr, "Unknown27"}, - {1501, nullptr, "Unknown28"}, - {1502, nullptr, "Unknown29"}, - {1503, nullptr, "Unknown30"}, - {1504, nullptr, "Unknown31"}, - {1505, nullptr, "Unknown32"}, - {2000, nullptr, "Unknown33"}, - {2001, nullptr, "Unknown34"}, - {2100, nullptr, "Unknown35"}, - {2501, nullptr, "Unknown36"}, - {2502, nullptr, "Unknown37"}, - {3001, nullptr, "Unknown38"}, - {3002, nullptr, "Unknown39"}, + {504, nullptr, "Unknown504"}, + {508, nullptr, "Unknown508"}, + {509, nullptr, "Unknown509"}, + {510, nullptr, "Unknown510"}, + {511, nullptr, "Unknown511"}, + {1001, nullptr, "Unknown1001"}, + {1002, nullptr, "Unknown1001"}, + {1003, nullptr, "Unknown1003"}, + {1004, nullptr, "Unknown1004"}, + {1005, nullptr, "Unknown1005"}, + {1006, nullptr, "Unknown1006"}, + {1007, nullptr, "Unknown1007"}, + {1009, nullptr, "Unknown1009"}, + {1010, nullptr, "Unknown1010"}, + {1011, nullptr, "Unknown1011"}, + {1012, nullptr, "Unknown1012"}, + {1013, nullptr, "Unknown1013"}, + {1014, nullptr, "Unknown1014"}, + {1015, nullptr, "Unknown1015"}, + {1016, nullptr, "Unknown1016"}, + {1017, nullptr, "Unknown1017"}, + {1018, nullptr, "Unknown1018"}, + {1019, nullptr, "Unknown1019"}, + {1020, nullptr, "Unknown1020"}, + {1021, nullptr, "Unknown1021"}, + {1501, nullptr, "Unknown1501"}, + {1502, nullptr, "Unknown1502"}, + {1503, nullptr, "Unknown1503"}, + {1504, nullptr, "Unknown1504"}, + {1505, nullptr, "Unknown1505"}, + {2000, nullptr, "Unknown2000"}, + {2001, nullptr, "Unknown2001"}, + {2100, nullptr, "Unknown2100"}, + {2501, nullptr, "Unknown2501"}, + {2502, nullptr, "Unknown2502"}, + {3001, nullptr, "Unknown3001"}, + {3002, nullptr, "Unknown3002"}, }; // clang-format on RegisterHandlers(functions); From 323eb86c9fc5ad415b7a33c0f0699aa7a1095e4b Mon Sep 17 00:00:00 2001 From: Kewlan Date: Thu, 25 Jun 2020 22:54:05 +0200 Subject: [PATCH 127/252] Fix issues caused when changing the fullscreen hotkey --- src/yuzu/main.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 059b96e70..0820f8c7e 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -666,6 +666,11 @@ void GMainWindow::InitializeHotkeys() { ui.action_Capture_Screenshot->setShortcutContext( hotkey_registry.GetShortcutContext(main_window, capture_screenshot)); + ui.action_Fullscreen->setShortcut( + hotkey_registry.GetHotkey(main_window, fullscreen, this)->key()); + ui.action_Fullscreen->setShortcutContext( + hotkey_registry.GetShortcutContext(main_window, fullscreen)); + connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Load File"), this), &QShortcut::activated, this, &GMainWindow::OnMenuLoadFile); connect( @@ -849,10 +854,6 @@ void GMainWindow::ConnectMenuEvents() { connect(ui.action_Reset_Window_Size, &QAction::triggered, this, &GMainWindow::ResetWindowSize); // Fullscreen - ui.action_Fullscreen->setShortcut( - hotkey_registry - .GetHotkey(QStringLiteral("Main Window"), QStringLiteral("Fullscreen"), this) - ->key()); connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen); // Movie From af88767508fd9e5f522a63eda5f4e55ba67d67a2 Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Sat, 27 Jun 2020 12:39:10 +0200 Subject: [PATCH 128/252] friend: Update function table --- src/core/hle/service/friend/friend.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp index 68f259b70..b7adaffc7 100644 --- a/src/core/hle/service/friend/friend.cpp +++ b/src/core/hle/service/friend/friend.cpp @@ -25,9 +25,13 @@ public: {10101, &IFriendService::GetFriendList, "GetFriendList"}, {10102, nullptr, "UpdateFriendInfo"}, {10110, nullptr, "GetFriendProfileImage"}, + {10120, nullptr, "Unknown10120"}, + {10121, nullptr, "Unknown10121"}, {10200, nullptr, "SendFriendRequestForApplication"}, {10211, nullptr, "AddFacedFriendRequestForApplication"}, {10400, &IFriendService::GetBlockedUserListIds, "GetBlockedUserListIds"}, + {10420, nullptr, "Unknown10420"}, + {10421, nullptr, "Unknown10421"}, {10500, nullptr, "GetProfileList"}, {10600, nullptr, "DeclareOpenOnlinePlaySession"}, {10601, &IFriendService::DeclareCloseOnlinePlaySession, "DeclareCloseOnlinePlaySession"}, @@ -97,6 +101,8 @@ public: {30900, nullptr, "SendFriendInvitation"}, {30910, nullptr, "ReadFriendInvitation"}, {30911, nullptr, "ReadAllFriendInvitations"}, + {40100, nullptr, "Unknown40100"}, + {40400, nullptr, "Unknown40400"}, {49900, nullptr, "DeleteNetworkServiceAccountCache"}, }; // clang-format on From 64fa9b9f57a05914aa8ce4bd51f645ed772aecda Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Sat, 27 Jun 2020 12:41:21 +0200 Subject: [PATCH 129/252] grc: Update function table This was based on Switchbrew page: https://switchbrew.org/wiki/GRC_services --- src/core/hle/service/grc/grc.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/hle/service/grc/grc.cpp b/src/core/hle/service/grc/grc.cpp index 24910ac6c..fd9471e3c 100644 --- a/src/core/hle/service/grc/grc.cpp +++ b/src/core/hle/service/grc/grc.cpp @@ -17,6 +17,9 @@ public: static const FunctionInfo functions[] = { {1, nullptr, "OpenContinuousRecorder"}, {2, nullptr, "OpenGameMovieTrimmer"}, + {3, nullptr, "OpenOffscreenRecorder"}, + {101, nullptr, "CreateMovieMaker"}, + {9903, nullptr, "SetOffscreenRecordingMaker"} }; // clang-format on From 73b035d2e272efe12cfb53c9b2f18074d856cd00 Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Sat, 27 Jun 2020 12:43:33 +0200 Subject: [PATCH 130/252] lbl: Update function table This was based on Switchbrew page: https://switchbrew.org/wiki/Backlight_services --- src/core/hle/service/lbl/lbl.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/hle/service/lbl/lbl.cpp b/src/core/hle/service/lbl/lbl.cpp index e8f9f2d29..17350b403 100644 --- a/src/core/hle/service/lbl/lbl.cpp +++ b/src/core/hle/service/lbl/lbl.cpp @@ -47,6 +47,7 @@ public: {26, &LBL::EnableVrMode, "EnableVrMode"}, {27, &LBL::DisableVrMode, "DisableVrMode"}, {28, &LBL::IsVrModeEnabled, "IsVrModeEnabled"}, + {29, nullptr, "IsAutoBrightnessControlSupported"}, }; // clang-format on From a8d17adb7c4a6caed17b2204a6090ffb0f062f63 Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Sat, 27 Jun 2020 12:45:42 +0200 Subject: [PATCH 131/252] Oops (fix typo) --- src/core/hle/service/grc/grc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/hle/service/grc/grc.cpp b/src/core/hle/service/grc/grc.cpp index fd9471e3c..401e0b208 100644 --- a/src/core/hle/service/grc/grc.cpp +++ b/src/core/hle/service/grc/grc.cpp @@ -19,7 +19,7 @@ public: {2, nullptr, "OpenGameMovieTrimmer"}, {3, nullptr, "OpenOffscreenRecorder"}, {101, nullptr, "CreateMovieMaker"}, - {9903, nullptr, "SetOffscreenRecordingMaker"} + {9903, nullptr, "SetOffscreenRecordingMarker"} }; // clang-format on From b9be484a51df4e0c666501d516fb822bed699723 Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Sat, 27 Jun 2020 12:50:56 +0200 Subject: [PATCH 132/252] ldn: Update function table This was based on Switchbrew page: https://switchbrew.org/wiki/LDN_services --- src/core/hle/service/ldn/ldn.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/hle/service/ldn/ldn.cpp b/src/core/hle/service/ldn/ldn.cpp index 92adde6d4..49972cd69 100644 --- a/src/core/hle/service/ldn/ldn.cpp +++ b/src/core/hle/service/ldn/ldn.cpp @@ -69,6 +69,7 @@ public: {101, nullptr, "GetNetworkInfoLatestUpdate"}, {102, nullptr, "Scan"}, {103, nullptr, "ScanPrivate"}, + {104, nullptr, "SetWirelessControllerRestriction"}, {200, nullptr, "OpenAccessPoint"}, {201, nullptr, "CloseAccessPoint"}, {202, nullptr, "CreateNetwork"}, From 521942422609b76a575efd93feb06788ae957c21 Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Sat, 27 Jun 2020 12:53:59 +0200 Subject: [PATCH 133/252] mig: Update function table This was based on Switchbrew page: https://switchbrew.org/wiki/Migration_services --- src/core/hle/service/mig/mig.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/core/hle/service/mig/mig.cpp b/src/core/hle/service/mig/mig.cpp index d16367f2c..113a4665c 100644 --- a/src/core/hle/service/mig/mig.cpp +++ b/src/core/hle/service/mig/mig.cpp @@ -20,6 +20,12 @@ public: {101, nullptr, "ResumeServer"}, {200, nullptr, "CreateClient"}, {201, nullptr, "ResumeClient"}, + {1001, nullptr, "Unknown1001"}, + {1010, nullptr, "Unknown1010"}, + {1100, nullptr, "Unknown1100"}, + {1101, nullptr, "Unknown1101"}, + {1200, nullptr, "Unknown1200"}, + {1201, nullptr, "Unknown1201"} }; // clang-format on From b8296439469e91aa533ad29aac79cdb88cb2e306 Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Sat, 27 Jun 2020 12:59:01 +0200 Subject: [PATCH 134/252] mm: Update function table This was based on Switchbrew page: https://switchbrew.org/wiki/Display_services --- src/core/hle/service/mm/mm_u.cpp | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/core/hle/service/mm/mm_u.cpp b/src/core/hle/service/mm/mm_u.cpp index def63dc8a..25c24e537 100644 --- a/src/core/hle/service/mm/mm_u.cpp +++ b/src/core/hle/service/mm/mm_u.cpp @@ -14,14 +14,14 @@ public: explicit MM_U() : ServiceFramework{"mm:u"} { // clang-format off static const FunctionInfo functions[] = { - {0, &MM_U::Initialize, "Initialize"}, - {1, &MM_U::Finalize, "Finalize"}, - {2, &MM_U::SetAndWait, "SetAndWait"}, - {3, &MM_U::Get, "Get"}, - {4, &MM_U::InitializeWithId, "InitializeWithId"}, - {5, &MM_U::FinalizeWithId, "FinalizeWithId"}, - {6, &MM_U::SetAndWaitWithId, "SetAndWaitWithId"}, - {7, &MM_U::GetWithId, "GetWithId"}, + {0, &MM_U::InitializeOld, "InitializeOld"}, + {1, &MM_U::FinalizeOld, "FinalizeOld"}, + {2, &MM_U::SetAndWaitOld, "SetAndWaitOld"}, + {3, &MM_U::GetOld, "GetOld"}, + {4, &MM_U::Initialize, "Initialize"}, + {5, &MM_U::Finalize, "Finalize"}, + {6, &MM_U::SetAndWait, "SetAndWait"}, + {7, &MM_U::Get, "Get"}, }; // clang-format on @@ -29,21 +29,21 @@ public: } private: - void Initialize(Kernel::HLERequestContext& ctx) { + void InitializeOld(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_MM, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } - void Finalize(Kernel::HLERequestContext& ctx) { + void FinalizeOld(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_MM, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } - void SetAndWait(Kernel::HLERequestContext& ctx) { + void SetAndWaitOld(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; min = rp.Pop(); max = rp.Pop(); @@ -54,7 +54,7 @@ private: rb.Push(RESULT_SUCCESS); } - void Get(Kernel::HLERequestContext& ctx) { + void GetOld(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_MM, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 3}; @@ -62,7 +62,7 @@ private: rb.Push(current); } - void InitializeWithId(Kernel::HLERequestContext& ctx) { + void Initialize(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_MM, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 3}; @@ -70,14 +70,14 @@ private: rb.Push(id); // Any non zero value } - void FinalizeWithId(Kernel::HLERequestContext& ctx) { + void Finalize(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_MM, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } - void SetAndWaitWithId(Kernel::HLERequestContext& ctx) { + void SetAndWait(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; u32 input_id = rp.Pop(); min = rp.Pop(); @@ -90,7 +90,7 @@ private: rb.Push(RESULT_SUCCESS); } - void GetWithId(Kernel::HLERequestContext& ctx) { + void Get(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_MM, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 3}; From c56414b80d945b286b6e98bc689146211615cd5a Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Sat, 27 Jun 2020 13:05:22 +0200 Subject: [PATCH 135/252] ncm: Update function table This was based on Switchbrew page: https://switchbrew.org/wiki/NCM_services ILocationResolver's 16, 17, 18 and 19 have unofficial names --- src/core/hle/service/ncm/ncm.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/core/hle/service/ncm/ncm.cpp b/src/core/hle/service/ncm/ncm.cpp index ec9aae04a..e38dea1f4 100644 --- a/src/core/hle/service/ncm/ncm.cpp +++ b/src/core/hle/service/ncm/ncm.cpp @@ -28,16 +28,16 @@ public: {7, nullptr, "ResolveApplicationLegalInformationPath"}, {8, nullptr, "RedirectApplicationLegalInformationPath"}, {9, nullptr, "Refresh"}, - {10, nullptr, "RedirectProgramPath2"}, - {11, nullptr, "Refresh2"}, - {12, nullptr, "DeleteProgramPath"}, - {13, nullptr, "DeleteApplicationControlPath"}, - {14, nullptr, "DeleteApplicationHtmlDocumentPath"}, - {15, nullptr, "DeleteApplicationLegalInformationPath"}, - {16, nullptr, ""}, - {17, nullptr, ""}, - {18, nullptr, ""}, - {19, nullptr, ""}, + {10, nullptr, "RedirectApplicationProgramPath"}, + {11, nullptr, "ClearApplicationRedirection"}, + {12, nullptr, "EraseProgramRedirection"}, + {13, nullptr, "EraseApplicationControlRedirection"}, + {14, nullptr, "EraseApplicationHtmlDocumentRedirection"}, + {15, nullptr, "EraseApplicationLegalInformationRedirection"}, + {16, nullptr, "ResolveProgramPathForDebug"}, + {17, nullptr, "RedirectProgramPathForDebug"}, + {18, nullptr, "RedirectApplicationProgramPathForDebug"}, + {19, nullptr, "EraseProgramRedirectionForDebug"}, }; // clang-format on From 23515e0cccf4dd4ce71bbbfcb8620a5437b19fb3 Mon Sep 17 00:00:00 2001 From: VolcaEM <63682805+VolcaEM@users.noreply.github.com> Date: Sat, 27 Jun 2020 13:09:36 +0200 Subject: [PATCH 136/252] nfc: Update function table This was based on Switchbrew page: https://switchbrew.org/wiki/NFC_services --- src/core/hle/service/nfc/nfc.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp index b7b34ce7e..780ea30fe 100644 --- a/src/core/hle/service/nfc/nfc.cpp +++ b/src/core/hle/service/nfc/nfc.cpp @@ -198,9 +198,9 @@ public: static const FunctionInfo functions[] = { {0, nullptr, "Initialize"}, {1, nullptr, "Finalize"}, - {2, nullptr, "GetState"}, - {3, nullptr, "IsNfcEnabled"}, - {100, nullptr, "SetNfcEnabled"}, + {2, nullptr, "GetStateOld"}, + {3, nullptr, "IsNfcEnabledOld"}, + {100, nullptr, "SetNfcEnabledOld"}, {400, nullptr, "InitializeSystem"}, {401, nullptr, "FinalizeSystem"}, {402, nullptr, "GetState"}, From e31425df3877636c098ec7426ebd2067920715cb Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Mon, 24 Feb 2020 22:04:12 -0400 Subject: [PATCH 137/252] General: Recover Prometheus project from harddrive failure This commit: Implements CPU Interrupts, Replaces Cycle Timing for Host Timing, Reworks the Kernel's Scheduler, Introduce Idle State and Suspended State, Recreates the bootmanager, Initializes Multicore system. --- src/common/thread.cpp | 6 + src/core/CMakeLists.txt | 4 +- src/core/arm/arm_interface.h | 5 +- src/core/arm/cpu_interrupt_handler.cpp | 29 ++ src/core/arm/cpu_interrupt_handler.h | 39 ++ src/core/arm/dynarmic/arm_dynarmic_32.cpp | 6 +- src/core/arm/dynarmic/arm_dynarmic_32.h | 4 +- src/core/arm/dynarmic/arm_dynarmic_64.cpp | 28 +- src/core/arm/dynarmic/arm_dynarmic_64.h | 4 +- src/core/arm/unicorn/arm_unicorn.cpp | 14 +- src/core/arm/unicorn/arm_unicorn.h | 3 +- src/core/core.cpp | 57 +-- src/core/core.h | 34 +- src/core/core_manager.cpp | 4 +- src/core/core_timing.cpp | 212 ++++----- src/core/core_timing.h | 106 ++--- src/core/cpu_manager.cpp | 196 ++++++-- src/core/cpu_manager.h | 51 ++- src/core/hle/kernel/kernel.cpp | 84 +++- src/core/hle/kernel/kernel.h | 19 + src/core/hle/kernel/physical_core.cpp | 37 +- src/core/hle/kernel/physical_core.h | 21 + src/core/hle/kernel/process.cpp | 17 +- src/core/hle/kernel/scheduler.cpp | 419 +++++++++++++----- src/core/hle/kernel/scheduler.h | 98 ++-- src/core/hle/kernel/svc.cpp | 21 +- src/core/hle/kernel/thread.cpp | 232 ++++------ src/core/hle/kernel/thread.h | 81 +++- src/core/hle/kernel/time_manager.cpp | 2 +- .../hle/service/hid/controllers/debug_pad.cpp | 2 +- .../hle/service/hid/controllers/gesture.cpp | 2 +- .../hle/service/hid/controllers/keyboard.cpp | 2 +- .../hle/service/hid/controllers/mouse.cpp | 2 +- src/core/hle/service/hid/controllers/npad.cpp | 2 +- .../hle/service/hid/controllers/stubbed.cpp | 2 +- .../service/hid/controllers/touchscreen.cpp | 4 +- src/core/hle/service/hid/controllers/xpad.cpp | 2 +- src/core/hle/service/hid/hid.cpp | 16 +- src/core/hle/service/hid/irs.cpp | 2 +- .../service/nvdrv/devices/nvhost_ctrl_gpu.cpp | 3 +- src/core/hle/service/nvflinger/nvflinger.cpp | 13 +- .../time/standard_steady_clock_core.cpp | 5 +- .../time/tick_based_steady_clock_core.cpp | 5 +- src/core/hle/service/time/time.cpp | 5 +- .../hle/service/time/time_sharedmemory.cpp | 3 +- src/core/memory.cpp | 11 +- src/core/memory.h | 2 +- src/core/memory/cheat_engine.cpp | 8 +- src/core/tools/freezer.cpp | 8 +- src/tests/CMakeLists.txt | 1 - src/tests/core/core_timing.cpp | 186 ++++---- src/video_core/gpu.cpp | 5 +- src/yuzu/bootmanager.cpp | 32 +- src/yuzu/bootmanager.h | 7 + src/yuzu/debugger/wait_tree.cpp | 6 +- src/yuzu_cmd/yuzu.cpp | 2 +- src/yuzu_tester/yuzu.cpp | 2 +- 57 files changed, 1349 insertions(+), 824 deletions(-) create mode 100644 src/core/arm/cpu_interrupt_handler.cpp create mode 100644 src/core/arm/cpu_interrupt_handler.h diff --git a/src/common/thread.cpp b/src/common/thread.cpp index 0cd2d10bf..c9684aed9 100644 --- a/src/common/thread.cpp +++ b/src/common/thread.cpp @@ -70,6 +70,12 @@ void SetCurrentThreadName(const char* name) { } #endif +#if defined(_WIN32) +void SetCurrentThreadName(const char* name) { + // Do Nothing on MingW +} +#endif + #endif } // namespace Common diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index efbad628f..552094ddb 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -7,6 +7,8 @@ endif() add_library(core STATIC arm/arm_interface.h arm/arm_interface.cpp + arm/cpu_interrupt_handler.cpp + arm/cpu_interrupt_handler.h arm/exclusive_monitor.cpp arm/exclusive_monitor.h arm/unicorn/arm_unicorn.cpp @@ -547,8 +549,6 @@ add_library(core STATIC hle/service/vi/vi_u.h hle/service/wlan/wlan.cpp hle/service/wlan/wlan.h - host_timing.cpp - host_timing.h loader/deconstructed_rom_directory.cpp loader/deconstructed_rom_directory.h loader/elf.cpp diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index cb2e640e2..87a1c29cc 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h @@ -18,11 +18,13 @@ enum class VMAPermission : u8; namespace Core { class System; +class CPUInterruptHandler; /// Generic ARMv8 CPU interface class ARM_Interface : NonCopyable { public: - explicit ARM_Interface(System& system_) : system{system_} {} + explicit ARM_Interface(System& system_, CPUInterruptHandler& interrupt_handler) + : system{system_}, interrupt_handler{interrupt_handler} {} virtual ~ARM_Interface() = default; struct ThreadContext32 { @@ -175,6 +177,7 @@ public: protected: /// System context that this ARM interface is running under. System& system; + CPUInterruptHandler& interrupt_handler; }; } // namespace Core diff --git a/src/core/arm/cpu_interrupt_handler.cpp b/src/core/arm/cpu_interrupt_handler.cpp new file mode 100644 index 000000000..2f1a1a269 --- /dev/null +++ b/src/core/arm/cpu_interrupt_handler.cpp @@ -0,0 +1,29 @@ +// Copyright 2020 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/thread.h" +#include "core/arm/cpu_interrupt_handler.h" + +namespace Core { + +CPUInterruptHandler::CPUInterruptHandler() : is_interrupted{} { + interrupt_event = std::make_unique(); +} + +CPUInterruptHandler::~CPUInterruptHandler() = default; + +void CPUInterruptHandler::SetInterrupt(bool is_interrupted_) { + if (is_interrupted_) { + interrupt_event->Set(); + } + this->is_interrupted = is_interrupted_; +} + +void CPUInterruptHandler::AwaitInterrupt() { + interrupt_event->Wait(); +} + +} // namespace Core diff --git a/src/core/arm/cpu_interrupt_handler.h b/src/core/arm/cpu_interrupt_handler.h new file mode 100644 index 000000000..91c31a271 --- /dev/null +++ b/src/core/arm/cpu_interrupt_handler.h @@ -0,0 +1,39 @@ +// Copyright 2020 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include + +namespace Common { +class Event; +} + +namespace Core { + +class CPUInterruptHandler { +public: + CPUInterruptHandler(); + ~CPUInterruptHandler(); + + CPUInterruptHandler(const CPUInterruptHandler&) = delete; + CPUInterruptHandler& operator=(const CPUInterruptHandler&) = delete; + + CPUInterruptHandler(CPUInterruptHandler&&) = default; + CPUInterruptHandler& operator=(CPUInterruptHandler&&) = default; + + constexpr bool IsInterrupted() const { + return is_interrupted; + } + + void SetInterrupt(bool is_interrupted); + + void AwaitInterrupt(); + +private: + bool is_interrupted{}; + std::unique_ptr interrupt_event; +}; + +} // namespace Core diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index 4c8663d03..0b7aa6a69 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -114,9 +114,9 @@ void ARM_Dynarmic_32::Step() { jit->Step(); } -ARM_Dynarmic_32::ARM_Dynarmic_32(System& system, ExclusiveMonitor& exclusive_monitor, - std::size_t core_index) - : ARM_Interface{system}, cb(std::make_unique(*this)), +ARM_Dynarmic_32::ARM_Dynarmic_32(System& system, CPUInterruptHandler& interrupt_handler, + ExclusiveMonitor& exclusive_monitor, std::size_t core_index) + : ARM_Interface{system, interrupt_handler}, cb(std::make_unique(*this)), cp15(std::make_shared(*this)), core_index{core_index}, exclusive_monitor{dynamic_cast(exclusive_monitor)} {} diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h index e5b92d7bb..1e7e17e64 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.h +++ b/src/core/arm/dynarmic/arm_dynarmic_32.h @@ -21,6 +21,7 @@ class Memory; namespace Core { +class CPUInterruptHandler; class DynarmicCallbacks32; class DynarmicCP15; class DynarmicExclusiveMonitor; @@ -28,7 +29,8 @@ class System; class ARM_Dynarmic_32 final : public ARM_Interface { public: - ARM_Dynarmic_32(System& system, ExclusiveMonitor& exclusive_monitor, std::size_t core_index); + ARM_Dynarmic_32(System& system, CPUInterruptHandler& interrupt_handler, + ExclusiveMonitor& exclusive_monitor, std::size_t core_index); ~ARM_Dynarmic_32() override; void SetPC(u64 pc) override; diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 5f5e36d94..5e316ffd4 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -9,6 +9,7 @@ #include "common/logging/log.h" #include "common/microprofile.h" #include "common/page_table.h" +#include "core/arm/cpu_interrupt_handler.h" #include "core/arm/dynarmic/arm_dynarmic_64.h" #include "core/core.h" #include "core/core_manager.h" @@ -108,23 +109,16 @@ public: } void AddTicks(u64 ticks) override { - // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a - // rough approximation of the amount of executed ticks in the system, it may be thrown off - // if not all cores are doing a similar amount of work. Instead of doing this, we should - // device a way so that timing is consistent across all cores without increasing the ticks 4 - // times. - u64 amortized_ticks = (ticks - num_interpreted_instructions) / Core::NUM_CPU_CORES; - // Always execute at least one tick. - amortized_ticks = std::max(amortized_ticks, 1); - - parent.system.CoreTiming().AddTicks(amortized_ticks); - num_interpreted_instructions = 0; + /// We are using host timing, NOP } u64 GetTicksRemaining() override { - return std::max(parent.system.CoreTiming().GetDowncount(), s64{0}); + if (!parent.interrupt_handler.IsInterrupted()) { + return 1000ULL; + } + return 0ULL; } u64 GetCNTPCT() override { - return Timing::CpuCyclesToClockCycles(parent.system.CoreTiming().GetTicks()); + return parent.system.CoreTiming().GetClockTicks(); } ARM_Dynarmic_64& parent; @@ -183,10 +177,10 @@ void ARM_Dynarmic_64::Step() { cb->InterpreterFallback(jit->GetPC(), 1); } -ARM_Dynarmic_64::ARM_Dynarmic_64(System& system, ExclusiveMonitor& exclusive_monitor, - std::size_t core_index) - : ARM_Interface{system}, cb(std::make_unique(*this)), - inner_unicorn{system, ARM_Unicorn::Arch::AArch64}, core_index{core_index}, +ARM_Dynarmic_64::ARM_Dynarmic_64(System& system, CPUInterruptHandler& interrupt_handler, + ExclusiveMonitor& exclusive_monitor, std::size_t core_index) + : ARM_Interface{system, interrupt_handler}, cb(std::make_unique(*this)), + inner_unicorn{system, interrupt_handler, ARM_Unicorn::Arch::AArch64}, core_index{core_index}, exclusive_monitor{dynamic_cast(exclusive_monitor)} {} ARM_Dynarmic_64::~ARM_Dynarmic_64() = default; diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h index 647cecaf0..9e94b58c2 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.h +++ b/src/core/arm/dynarmic/arm_dynarmic_64.h @@ -22,12 +22,14 @@ class Memory; namespace Core { class DynarmicCallbacks64; +class CPUInterruptHandler; class DynarmicExclusiveMonitor; class System; class ARM_Dynarmic_64 final : public ARM_Interface { public: - ARM_Dynarmic_64(System& system, ExclusiveMonitor& exclusive_monitor, std::size_t core_index); + ARM_Dynarmic_64(System& system, CPUInterruptHandler& interrupt_handler, + ExclusiveMonitor& exclusive_monitor, std::size_t core_index); ~ARM_Dynarmic_64() override; void SetPC(u64 pc) override; diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp index e40e9626a..0393fe641 100644 --- a/src/core/arm/unicorn/arm_unicorn.cpp +++ b/src/core/arm/unicorn/arm_unicorn.cpp @@ -6,6 +6,7 @@ #include #include "common/assert.h" #include "common/microprofile.h" +#include "core/arm/cpu_interrupt_handler.h" #include "core/arm/unicorn/arm_unicorn.h" #include "core/core.h" #include "core/core_timing.h" @@ -62,7 +63,8 @@ static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int si return false; } -ARM_Unicorn::ARM_Unicorn(System& system, Arch architecture) : ARM_Interface{system} { +ARM_Unicorn::ARM_Unicorn(System& system, CPUInterruptHandler& interrupt_handler, Arch architecture) + : ARM_Interface{system, interrupt_handler} { const auto arch = architecture == Arch::AArch32 ? UC_ARCH_ARM : UC_ARCH_ARM64; CHECKED(uc_open(arch, UC_MODE_ARM, &uc)); @@ -160,8 +162,12 @@ void ARM_Unicorn::Run() { if (GDBStub::IsServerEnabled()) { ExecuteInstructions(std::max(4000000U, 0U)); } else { - ExecuteInstructions( - std::max(std::size_t(system.CoreTiming().GetDowncount()), std::size_t{0})); + while (true) { + if (interrupt_handler.IsInterrupted()) { + return; + } + ExecuteInstructions(10); + } } } @@ -183,8 +189,6 @@ void ARM_Unicorn::ExecuteInstructions(std::size_t num_instructions) { UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC, page_buffer.data())); CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions)); CHECKED(uc_mem_unmap(uc, map_addr, page_buffer.size())); - - system.CoreTiming().AddTicks(num_instructions); if (GDBStub::IsServerEnabled()) { if (last_bkpt_hit && last_bkpt.type == GDBStub::BreakpointType::Execute) { uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address); diff --git a/src/core/arm/unicorn/arm_unicorn.h b/src/core/arm/unicorn/arm_unicorn.h index 725c65085..0a4c087cd 100644 --- a/src/core/arm/unicorn/arm_unicorn.h +++ b/src/core/arm/unicorn/arm_unicorn.h @@ -11,6 +11,7 @@ namespace Core { +class CPUInterruptHandler; class System; class ARM_Unicorn final : public ARM_Interface { @@ -20,7 +21,7 @@ public: AArch64, // 64-bit ARM }; - explicit ARM_Unicorn(System& system, Arch architecture); + explicit ARM_Unicorn(System& system, CPUInterruptHandler& interrupt_handler, Arch architecture); ~ARM_Unicorn() override; void SetPC(u64 pc) override; diff --git a/src/core/core.cpp b/src/core/core.cpp index f9f8a3000..e8936b09d 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -11,7 +11,6 @@ #include "common/string_util.h" #include "core/arm/exclusive_monitor.h" #include "core/core.h" -#include "core/core_manager.h" #include "core/core_timing.h" #include "core/cpu_manager.h" #include "core/device_memory.h" @@ -117,23 +116,30 @@ struct System::Impl { : kernel{system}, fs_controller{system}, memory{system}, cpu_manager{system}, reporter{system}, applet_manager{system} {} - CoreManager& CurrentCoreManager() { - return cpu_manager.GetCurrentCoreManager(); - } - Kernel::PhysicalCore& CurrentPhysicalCore() { - const auto index = cpu_manager.GetActiveCoreIndex(); - return kernel.PhysicalCore(index); + return kernel.CurrentPhysicalCore(); } Kernel::PhysicalCore& GetPhysicalCore(std::size_t index) { return kernel.PhysicalCore(index); } - ResultStatus RunLoop(bool tight_loop) { + ResultStatus Run() { status = ResultStatus::Success; - cpu_manager.RunLoop(tight_loop); + kernel.Suspend(false); + core_timing.SyncPause(false); + cpu_manager.Pause(false); + + return status; + } + + ResultStatus Pause() { + status = ResultStatus::Success; + + kernel.Suspend(true); + core_timing.SyncPause(true); + cpu_manager.Pause(true); return status; } @@ -143,7 +149,7 @@ struct System::Impl { device_memory = std::make_unique(system); - core_timing.Initialize(); + core_timing.Initialize([&system]() { system.RegisterHostThread(); }); kernel.Initialize(); cpu_manager.Initialize(); @@ -387,20 +393,24 @@ struct System::Impl { System::System() : impl{std::make_unique(*this)} {} System::~System() = default; -CoreManager& System::CurrentCoreManager() { - return impl->CurrentCoreManager(); +CpuManager& System::GetCpuManager() { + return impl->cpu_manager; } -const CoreManager& System::CurrentCoreManager() const { - return impl->CurrentCoreManager(); +const CpuManager& System::GetCpuManager() const { + return impl->cpu_manager; } -System::ResultStatus System::RunLoop(bool tight_loop) { - return impl->RunLoop(tight_loop); +System::ResultStatus System::Run() { + return impl->Run(); +} + +System::ResultStatus System::Pause() { + return impl->Pause(); } System::ResultStatus System::SingleStep() { - return RunLoop(false); + return ResultStatus::Success; } void System::InvalidateCpuInstructionCaches() { @@ -444,7 +454,9 @@ const ARM_Interface& System::CurrentArmInterface() const { } std::size_t System::CurrentCoreIndex() const { - return impl->cpu_manager.GetActiveCoreIndex(); + std::size_t core = impl->kernel.GetCurrentHostThreadID(); + ASSERT(core < Core::Hardware::NUM_CPU_CORES); + return core; } Kernel::Scheduler& System::CurrentScheduler() { @@ -497,15 +509,6 @@ const ARM_Interface& System::ArmInterface(std::size_t core_index) const { return impl->GetPhysicalCore(core_index).ArmInterface(); } -CoreManager& System::GetCoreManager(std::size_t core_index) { - return impl->cpu_manager.GetCoreManager(core_index); -} - -const CoreManager& System::GetCoreManager(std::size_t core_index) const { - ASSERT(core_index < NUM_CPU_CORES); - return impl->cpu_manager.GetCoreManager(core_index); -} - ExclusiveMonitor& System::Monitor() { return impl->kernel.GetExclusiveMonitor(); } diff --git a/src/core/core.h b/src/core/core.h index acc53d6a1..7f170fc54 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -90,7 +90,7 @@ class InterruptManager; namespace Core { class ARM_Interface; -class CoreManager; +class CpuManager; class DeviceMemory; class ExclusiveMonitor; class FrameLimiter; @@ -136,16 +136,18 @@ public: }; /** - * Run the core CPU loop - * This function runs the core for the specified number of CPU instructions before trying to - * update hardware. This is much faster than SingleStep (and should be equivalent), as the CPU - * is not required to do a full dispatch with each instruction. NOTE: the number of instructions - * requested is not guaranteed to run, as this will be interrupted preemptively if a hardware - * update is requested (e.g. on a thread switch). - * @param tight_loop If false, the CPU single-steps. - * @return Result status, indicating whether or not the operation succeeded. + * Run the OS and Application + * This function will start emulation and run the competent devices */ - ResultStatus RunLoop(bool tight_loop = true); + ResultStatus Run(); + + /** + * Pause the OS and Application + * This function will pause emulation and stop the competent devices + */ + ResultStatus Pause(); + + /** * Step the CPU one instruction @@ -215,11 +217,9 @@ public: /// Gets a const reference to an ARM interface from the CPU core with the specified index const ARM_Interface& ArmInterface(std::size_t core_index) const; - /// Gets a CPU interface to the CPU core with the specified index - CoreManager& GetCoreManager(std::size_t core_index); + CpuManager& GetCpuManager(); - /// Gets a CPU interface to the CPU core with the specified index - const CoreManager& GetCoreManager(std::size_t core_index) const; + const CpuManager& GetCpuManager() const; /// Gets a reference to the exclusive monitor ExclusiveMonitor& Monitor(); @@ -373,12 +373,6 @@ public: private: System(); - /// Returns the currently running CPU core - CoreManager& CurrentCoreManager(); - - /// Returns the currently running CPU core - const CoreManager& CurrentCoreManager() const; - /** * Initialize the emulated system. * @param emu_window Reference to the host-system window used for video output and keyboard diff --git a/src/core/core_manager.cpp b/src/core/core_manager.cpp index b6b797c80..45f0bb547 100644 --- a/src/core/core_manager.cpp +++ b/src/core/core_manager.cpp @@ -34,7 +34,6 @@ void CoreManager::RunLoop(bool tight_loop) { // instead advance to the next event and try to yield to the next thread if (Kernel::GetCurrentThread() == nullptr) { LOG_TRACE(Core, "Core-{} idling", core_index); - core_timing.Idle(); } else { if (tight_loop) { physical_core.Run(); @@ -42,7 +41,6 @@ void CoreManager::RunLoop(bool tight_loop) { physical_core.Step(); } } - core_timing.Advance(); Reschedule(); } @@ -59,7 +57,7 @@ void CoreManager::Reschedule() { // Lock the global kernel mutex when we manipulate the HLE state std::lock_guard lock(HLE::g_hle_lock); - global_scheduler.SelectThread(core_index); + // global_scheduler.SelectThread(core_index); physical_core.Scheduler().TryDoContextSwitch(); } diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 46d4178c4..a3ce69790 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -1,5 +1,5 @@ -// Copyright 2008 Dolphin Emulator Project / 2017 Citra Emulator Project -// Licensed under GPLv2+ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version // Refer to the license.txt file included. #include "core/core_timing.h" @@ -10,20 +10,16 @@ #include #include "common/assert.h" -#include "common/thread.h" #include "core/core_timing_util.h" -#include "core/hardware_properties.h" namespace Core::Timing { -constexpr int MAX_SLICE_LENGTH = 10000; - std::shared_ptr CreateEvent(std::string name, TimedCallback&& callback) { return std::make_shared(std::move(callback), std::move(name)); } struct CoreTiming::Event { - s64 time; + u64 time; u64 fifo_order; u64 userdata; std::weak_ptr type; @@ -39,51 +35,74 @@ struct CoreTiming::Event { } }; -CoreTiming::CoreTiming() = default; +CoreTiming::CoreTiming() { + clock = + Common::CreateBestMatchingClock(Core::Hardware::BASE_CLOCK_RATE, Core::Hardware::CNTFREQ); +} + CoreTiming::~CoreTiming() = default; -void CoreTiming::Initialize() { - downcounts.fill(MAX_SLICE_LENGTH); - time_slice.fill(MAX_SLICE_LENGTH); - slice_length = MAX_SLICE_LENGTH; - global_timer = 0; - idled_cycles = 0; - current_context = 0; - - // The time between CoreTiming being initialized and the first call to Advance() is considered - // the slice boundary between slice -1 and slice 0. Dispatcher loops must call Advance() before - // executing the first cycle of each slice to prepare the slice length and downcount for - // that slice. - is_global_timer_sane = true; +void CoreTiming::ThreadEntry(CoreTiming& instance) { + std::string name = "yuzu:HostTiming"; + Common::SetCurrentThreadName(name.c_str()); + instance.on_thread_init(); + instance.ThreadLoop(); +} +void CoreTiming::Initialize(std::function&& on_thread_init_) { + on_thread_init = std::move(on_thread_init_); event_fifo_id = 0; - const auto empty_timed_callback = [](u64, s64) {}; ev_lost = CreateEvent("_lost_event", empty_timed_callback); + timer_thread = std::make_unique(ThreadEntry, std::ref(*this)); } void CoreTiming::Shutdown() { + paused = true; + shutting_down = true; + event.Set(); + timer_thread->join(); ClearPendingEvents(); + timer_thread.reset(); + has_started = false; } -void CoreTiming::ScheduleEvent(s64 cycles_into_future, const std::shared_ptr& event_type, - u64 userdata) { - std::lock_guard guard{inner_mutex}; - const s64 timeout = GetTicks() + cycles_into_future; +void CoreTiming::Pause(bool is_paused) { + paused = is_paused; +} - // If this event needs to be scheduled before the next advance(), force one early - if (!is_global_timer_sane) { - ForceExceptionCheck(cycles_into_future); +void CoreTiming::SyncPause(bool is_paused) { + if (is_paused == paused && paused_set == paused) { + return; } + Pause(is_paused); + event.Set(); + while (paused_set != is_paused) + ; +} + +bool CoreTiming::IsRunning() const { + return !paused_set; +} + +bool CoreTiming::HasPendingEvents() const { + return !(wait_set && event_queue.empty()); +} + +void CoreTiming::ScheduleEvent(s64 ns_into_future, const std::shared_ptr& event_type, + u64 userdata) { + basic_lock.lock(); + const u64 timeout = static_cast(GetGlobalTimeNs().count() + ns_into_future); event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type}); std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); + basic_lock.unlock(); + event.Set(); } void CoreTiming::UnscheduleEvent(const std::shared_ptr& event_type, u64 userdata) { - std::lock_guard guard{inner_mutex}; - + basic_lock.lock(); const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { return e.type.lock().get() == event_type.get() && e.userdata == userdata; }); @@ -93,23 +112,23 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr& event_type, u event_queue.erase(itr, event_queue.end()); std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>()); } + basic_lock.unlock(); } -u64 CoreTiming::GetTicks() const { - u64 ticks = static_cast(global_timer); - if (!is_global_timer_sane) { - ticks += accumulated_ticks; - } - return ticks; +void CoreTiming::AddTicks(std::size_t core_index, u64 ticks) { + ticks_count[core_index] += ticks; } -u64 CoreTiming::GetIdleTicks() const { - return static_cast(idled_cycles); +void CoreTiming::ResetTicks(std::size_t core_index) { + ticks_count[core_index] = 0; } -void CoreTiming::AddTicks(u64 ticks) { - accumulated_ticks += ticks; - downcounts[current_context] -= static_cast(ticks); +u64 CoreTiming::GetCPUTicks() const { + return clock->GetCPUCycles(); +} + +u64 CoreTiming::GetClockTicks() const { + return clock->GetClockCycles(); } void CoreTiming::ClearPendingEvents() { @@ -117,7 +136,7 @@ void CoreTiming::ClearPendingEvents() { } void CoreTiming::RemoveEvent(const std::shared_ptr& event_type) { - std::lock_guard guard{inner_mutex}; + basic_lock.lock(); const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { return e.type.lock().get() == event_type.get(); @@ -128,99 +147,64 @@ void CoreTiming::RemoveEvent(const std::shared_ptr& event_type) { event_queue.erase(itr, event_queue.end()); std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>()); } + basic_lock.unlock(); } -void CoreTiming::ForceExceptionCheck(s64 cycles) { - cycles = std::max(0, cycles); - if (downcounts[current_context] <= cycles) { - return; - } - - // downcount is always (much) smaller than MAX_INT so we can safely cast cycles to an int - // here. Account for cycles already executed by adjusting the g.slice_length - downcounts[current_context] = static_cast(cycles); -} - -std::optional CoreTiming::NextAvailableCore(const s64 needed_ticks) const { - const u64 original_context = current_context; - u64 next_context = (original_context + 1) % num_cpu_cores; - while (next_context != original_context) { - if (time_slice[next_context] >= needed_ticks) { - return {next_context}; - } else if (time_slice[next_context] >= 0) { - return std::nullopt; - } - next_context = (next_context + 1) % num_cpu_cores; - } - return std::nullopt; -} - -void CoreTiming::Advance() { - std::unique_lock guard(inner_mutex); - - const u64 cycles_executed = accumulated_ticks; - time_slice[current_context] = std::max(0, time_slice[current_context] - accumulated_ticks); - global_timer += cycles_executed; - - is_global_timer_sane = true; +std::optional CoreTiming::Advance() { + advance_lock.lock(); + basic_lock.lock(); + global_timer = GetGlobalTimeNs().count(); while (!event_queue.empty() && event_queue.front().time <= global_timer) { Event evt = std::move(event_queue.front()); std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>()); event_queue.pop_back(); - inner_mutex.unlock(); + basic_lock.unlock(); if (auto event_type{evt.type.lock()}) { event_type->callback(evt.userdata, global_timer - evt.time); } - inner_mutex.lock(); + basic_lock.lock(); } - is_global_timer_sane = false; - - // Still events left (scheduled in the future) if (!event_queue.empty()) { - const s64 needed_ticks = - std::min(event_queue.front().time - global_timer, MAX_SLICE_LENGTH); - const auto next_core = NextAvailableCore(needed_ticks); - if (next_core) { - downcounts[*next_core] = needed_ticks; + const u64 next_time = event_queue.front().time - global_timer; + basic_lock.unlock(); + advance_lock.unlock(); + return next_time; + } else { + basic_lock.unlock(); + advance_lock.unlock(); + return std::nullopt; + } +} + +void CoreTiming::ThreadLoop() { + has_started = true; + while (!shutting_down) { + while (!paused) { + paused_set = false; + const auto next_time = Advance(); + if (next_time) { + std::chrono::nanoseconds next_time_ns = std::chrono::nanoseconds(*next_time); + event.WaitFor(next_time_ns); + } else { + wait_set = true; + event.Wait(); + } + wait_set = false; } + paused_set = true; } - - accumulated_ticks = 0; - - downcounts[current_context] = time_slice[current_context]; } -void CoreTiming::ResetRun() { - downcounts.fill(MAX_SLICE_LENGTH); - time_slice.fill(MAX_SLICE_LENGTH); - current_context = 0; - // Still events left (scheduled in the future) - if (!event_queue.empty()) { - const s64 needed_ticks = - std::min(event_queue.front().time - global_timer, MAX_SLICE_LENGTH); - downcounts[current_context] = needed_ticks; - } - - is_global_timer_sane = false; - accumulated_ticks = 0; -} - -void CoreTiming::Idle() { - accumulated_ticks += downcounts[current_context]; - idled_cycles += downcounts[current_context]; - downcounts[current_context] = 0; +std::chrono::nanoseconds CoreTiming::GetGlobalTimeNs() const { + return clock->GetTimeNS(); } std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const { - return std::chrono::microseconds{GetTicks() * 1000000 / Hardware::BASE_CLOCK_RATE}; -} - -s64 CoreTiming::GetDowncount() const { - return downcounts[current_context]; + return clock->GetTimeUS(); } } // namespace Core::Timing diff --git a/src/core/core_timing.h b/src/core/core_timing.h index d50f4eb8a..707c8ef0c 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h @@ -1,19 +1,25 @@ -// Copyright 2008 Dolphin Emulator Project / 2017 Citra Emulator Project -// Licensed under GPLv2+ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version // Refer to the license.txt file included. #pragma once +#include #include #include #include #include #include #include +#include #include #include "common/common_types.h" +#include "common/spin_lock.h" +#include "common/thread.h" #include "common/threadsafe_queue.h" +#include "common/wall_clock.h" +#include "core/hardware_properties.h" namespace Core::Timing { @@ -56,16 +62,30 @@ public: /// CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is /// required to end slice - 1 and start slice 0 before the first cycle of code is executed. - void Initialize(); + void Initialize(std::function&& on_thread_init_); /// Tears down all timing related functionality. void Shutdown(); - /// After the first Advance, the slice lengths and the downcount will be reduced whenever an - /// event is scheduled earlier than the current values. - /// - /// Scheduling from a callback will not update the downcount until the Advance() completes. - void ScheduleEvent(s64 cycles_into_future, const std::shared_ptr& event_type, + /// Pauses/Unpauses the execution of the timer thread. + void Pause(bool is_paused); + + /// Pauses/Unpauses the execution of the timer thread and waits until paused. + void SyncPause(bool is_paused); + + /// Checks if core timing is running. + bool IsRunning() const; + + /// Checks if the timer thread has started. + bool HasStarted() const { + return has_started; + } + + /// Checks if there are any pending time events. + bool HasPendingEvents() const; + + /// Schedules an event in core timing + void ScheduleEvent(s64 ns_into_future, const std::shared_ptr& event_type, u64 userdata = 0); void UnscheduleEvent(const std::shared_ptr& event_type, u64 userdata); @@ -73,41 +93,24 @@ public: /// We only permit one event of each type in the queue at a time. void RemoveEvent(const std::shared_ptr& event_type); - void ForceExceptionCheck(s64 cycles); + void AddTicks(std::size_t core_index, u64 ticks); - /// This should only be called from the emu thread, if you are calling it any other thread, - /// you are doing something evil - u64 GetTicks() const; + void ResetTicks(std::size_t core_index); - u64 GetIdleTicks() const; + /// Returns current time in emulated CPU cycles + u64 GetCPUTicks() const; - void AddTicks(u64 ticks); - - /// Advance must be called at the beginning of dispatcher loops, not the end. Advance() ends - /// the previous timing slice and begins the next one, you must Advance from the previous - /// slice to the current one before executing any cycles. CoreTiming starts in slice -1 so an - /// Advance() is required to initialize the slice length before the first cycle of emulated - /// instructions is executed. - void Advance(); - - /// Pretend that the main CPU has executed enough cycles to reach the next event. - void Idle(); + /// Returns current time in emulated in Clock cycles + u64 GetClockTicks() const; + /// Returns current time in microseconds. std::chrono::microseconds GetGlobalTimeUs() const; - void ResetRun(); + /// Returns current time in nanoseconds. + std::chrono::nanoseconds GetGlobalTimeNs() const; - s64 GetDowncount() const; - - void SwitchContext(u64 new_context) { - current_context = new_context; - } - - bool CanCurrentContextRun() const { - return time_slice[current_context] > 0; - } - - std::optional NextAvailableCore(const s64 needed_ticks) const; + /// Checks for events manually and returns time in nanoseconds for next event, threadsafe. + std::optional Advance(); private: struct Event; @@ -115,21 +118,14 @@ private: /// Clear all pending events. This should ONLY be done on exit. void ClearPendingEvents(); - static constexpr u64 num_cpu_cores = 4; + static void ThreadEntry(CoreTiming& instance); + void ThreadLoop(); - s64 global_timer = 0; - s64 idled_cycles = 0; - s64 slice_length = 0; - u64 accumulated_ticks = 0; - std::array downcounts{}; - // Slice of time assigned to each core per run. - std::array time_slice{}; - u64 current_context = 0; + std::unique_ptr clock; - // Are we in a function that has been called from Advance() - // If events are scheduled from a function that gets called from Advance(), - // don't change slice_length and downcount. - bool is_global_timer_sane = false; + u64 global_timer = 0; + + std::chrono::nanoseconds start_point; // The queue is a min-heap using std::make_heap/push_heap/pop_heap. // We don't use std::priority_queue because we need to be able to serialize, unserialize and @@ -139,8 +135,18 @@ private: u64 event_fifo_id = 0; std::shared_ptr ev_lost; + Common::Event event{}; + Common::SpinLock basic_lock{}; + Common::SpinLock advance_lock{}; + std::unique_ptr timer_thread; + std::atomic paused{}; + std::atomic paused_set{}; + std::atomic wait_set{}; + std::atomic shutting_down{}; + std::atomic has_started{}; + std::function on_thread_init{}; - std::mutex inner_mutex; + std::array, Core::Hardware::NUM_CPU_CORES> ticks_count{}; }; /// Creates a core timing event with the given name and callback. diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index 70ddbdcca..494850992 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp @@ -2,80 +2,192 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/fiber.h" +#include "common/thread.h" #include "core/arm/exclusive_monitor.h" #include "core/core.h" -#include "core/core_manager.h" #include "core/core_timing.h" #include "core/cpu_manager.h" #include "core/gdbstub/gdbstub.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/physical_core.h" +#include "core/hle/kernel/scheduler.h" +#include "core/hle/kernel/thread.h" namespace Core { CpuManager::CpuManager(System& system) : system{system} {} CpuManager::~CpuManager() = default; +void CpuManager::ThreadStart(CpuManager& cpu_manager, std::size_t core) { + cpu_manager.RunThread(core); +} + void CpuManager::Initialize() { - for (std::size_t index = 0; index < core_managers.size(); ++index) { - core_managers[index] = std::make_unique(system, index); + running_mode = true; + for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { + core_data[core].host_thread = + std::make_unique(ThreadStart, std::ref(*this), core); } } void CpuManager::Shutdown() { - for (auto& cpu_core : core_managers) { - cpu_core.reset(); + running_mode = false; + Pause(false); + for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { + core_data[core].host_thread->join(); } } -CoreManager& CpuManager::GetCoreManager(std::size_t index) { - return *core_managers.at(index); +void CpuManager::GuestThreadFunction(void* cpu_manager_) { + CpuManager* cpu_manager = static_cast(cpu_manager_); + cpu_manager->RunGuestThread(); } -const CoreManager& CpuManager::GetCoreManager(std::size_t index) const { - return *core_managers.at(index); +void CpuManager::IdleThreadFunction(void* cpu_manager_) { + CpuManager* cpu_manager = static_cast(cpu_manager_); + cpu_manager->RunIdleThread(); } -CoreManager& CpuManager::GetCurrentCoreManager() { - // Otherwise, use single-threaded mode active_core variable - return *core_managers[active_core]; +void CpuManager::SuspendThreadFunction(void* cpu_manager_) { + CpuManager* cpu_manager = static_cast(cpu_manager_); + cpu_manager->RunSuspendThread(); } -const CoreManager& CpuManager::GetCurrentCoreManager() const { - // Otherwise, use single-threaded mode active_core variable - return *core_managers[active_core]; +std::function CpuManager::GetGuestThreadStartFunc() { + return std::function(GuestThreadFunction); } -void CpuManager::RunLoop(bool tight_loop) { - if (GDBStub::IsServerEnabled()) { - GDBStub::HandlePacket(); +std::function CpuManager::GetIdleThreadStartFunc() { + return std::function(IdleThreadFunction); +} - // If the loop is halted and we want to step, use a tiny (1) number of instructions to - // execute. Otherwise, get out of the loop function. - if (GDBStub::GetCpuHaltFlag()) { - if (GDBStub::GetCpuStepFlag()) { - tight_loop = false; - } else { - return; +std::function CpuManager::GetSuspendThreadStartFunc() { + return std::function(SuspendThreadFunction); +} + +void* CpuManager::GetStartFuncParamater() { + return static_cast(this); +} + +void CpuManager::RunGuestThread() { + auto& kernel = system.Kernel(); + { + auto& sched = kernel.CurrentScheduler(); + sched.OnThreadStart(); + } + while (true) { + auto& physical_core = kernel.CurrentPhysicalCore(); + LOG_CRITICAL(Core_ARM, "Running Guest Thread"); + physical_core.Idle(); + LOG_CRITICAL(Core_ARM, "Leaving Guest Thread"); + // physical_core.Run(); + auto& scheduler = physical_core.Scheduler(); + scheduler.TryDoContextSwitch(); + } +} + +void CpuManager::RunIdleThread() { + auto& kernel = system.Kernel(); + while (true) { + auto& physical_core = kernel.CurrentPhysicalCore(); + LOG_CRITICAL(Core_ARM, "Running Idle Thread"); + physical_core.Idle(); + auto& scheduler = physical_core.Scheduler(); + scheduler.TryDoContextSwitch(); + } +} + +void CpuManager::RunSuspendThread() { + LOG_CRITICAL(Core_ARM, "Suspending Thread Entered"); + auto& kernel = system.Kernel(); + { + auto& sched = kernel.CurrentScheduler(); + sched.OnThreadStart(); + } + while (true) { + auto core = kernel.GetCurrentHostThreadID(); + auto& scheduler = kernel.CurrentScheduler(); + Kernel::Thread* current_thread = scheduler.GetCurrentThread(); + LOG_CRITICAL(Core_ARM, "Suspending Core {}", core); + Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[core].host_context); + LOG_CRITICAL(Core_ARM, "Unsuspending Core {}", core); + ASSERT(scheduler.ContextSwitchPending()); + ASSERT(core == kernel.GetCurrentHostThreadID()); + scheduler.TryDoContextSwitch(); + } +} + +void CpuManager::Pause(bool paused) { + if (!paused) { + bool all_not_barrier = false; + while (!all_not_barrier) { + all_not_barrier = true; + for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { + all_not_barrier &= + !core_data[core].is_running.load() && core_data[core].initialized.load(); } } - } - - auto& core_timing = system.CoreTiming(); - core_timing.ResetRun(); - bool keep_running{}; - do { - keep_running = false; - for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) { - core_timing.SwitchContext(active_core); - if (core_timing.CanCurrentContextRun()) { - core_managers[active_core]->RunLoop(tight_loop); - } - keep_running |= core_timing.CanCurrentContextRun(); + for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { + core_data[core].enter_barrier->Set(); } - } while (keep_running); - - if (GDBStub::IsServerEnabled()) { - GDBStub::SetCpuStepFlag(false); + if (paused_state.load()) { + bool all_barrier = false; + while (!all_barrier) { + all_barrier = true; + for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { + all_barrier &= + core_data[core].is_paused.load() && core_data[core].initialized.load(); + } + } + for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { + core_data[core].exit_barrier->Set(); + } + } + } else { + /// Wait until all cores are paused. + bool all_barrier = false; + while (!all_barrier) { + all_barrier = true; + for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { + all_barrier &= + core_data[core].is_paused.load() && core_data[core].initialized.load(); + } + } + /// Don't release the barrier } + paused_state = paused; +} + +void CpuManager::RunThread(std::size_t core) { + /// Initialization + system.RegisterCoreThread(core); + std::string name = "yuzu:CoreHostThread_" + std::to_string(core); + Common::SetCurrentThreadName(name.c_str()); + auto& data = core_data[core]; + data.enter_barrier = std::make_unique(); + data.exit_barrier = std::make_unique(); + data.host_context = Common::Fiber::ThreadToFiber(); + data.is_running = false; + data.initialized = true; + /// Running + while (running_mode) { + data.is_running = false; + data.enter_barrier->Wait(); + auto& scheduler = system.Kernel().CurrentScheduler(); + Kernel::Thread* current_thread = scheduler.GetCurrentThread(); + data.is_running = true; + Common::Fiber::YieldTo(data.host_context, current_thread->GetHostContext()); + data.is_running = false; + data.is_paused = true; + data.exit_barrier->Wait(); + data.is_paused = false; + } + /// Time to cleanup + data.host_context->Exit(); + data.enter_barrier.reset(); + data.exit_barrier.reset(); + data.initialized = false; } } // namespace Core diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h index 97554d1bb..8103ae857 100644 --- a/src/core/cpu_manager.h +++ b/src/core/cpu_manager.h @@ -5,12 +5,18 @@ #pragma once #include +#include #include +#include #include "core/hardware_properties.h" +namespace Common { +class Event; +class Fiber; +} // namespace Common + namespace Core { -class CoreManager; class System; class CpuManager { @@ -27,21 +33,40 @@ public: void Initialize(); void Shutdown(); - CoreManager& GetCoreManager(std::size_t index); - const CoreManager& GetCoreManager(std::size_t index) const; + void Pause(bool paused); - CoreManager& GetCurrentCoreManager(); - const CoreManager& GetCurrentCoreManager() const; - - std::size_t GetActiveCoreIndex() const { - return active_core; - } - - void RunLoop(bool tight_loop); + std::function GetGuestThreadStartFunc(); + std::function GetIdleThreadStartFunc(); + std::function GetSuspendThreadStartFunc(); + void* GetStartFuncParamater(); private: - std::array, Hardware::NUM_CPU_CORES> core_managers; - std::size_t active_core{}; ///< Active core, only used in single thread mode + static void GuestThreadFunction(void* cpu_manager); + static void IdleThreadFunction(void* cpu_manager); + static void SuspendThreadFunction(void* cpu_manager); + + void RunGuestThread(); + void RunIdleThread(); + void RunSuspendThread(); + + static void ThreadStart(CpuManager& cpu_manager, std::size_t core); + + void RunThread(std::size_t core); + + struct CoreData { + std::shared_ptr host_context; + std::unique_ptr enter_barrier; + std::unique_ptr exit_barrier; + std::atomic is_running; + std::atomic is_paused; + std::atomic initialized; + std::unique_ptr host_thread; + }; + + std::atomic running_mode{}; + std::atomic paused_state{}; + + std::array core_data{}; System& system; }; diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 7655382fa..ba051a7d8 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -13,11 +13,13 @@ #include "common/assert.h" #include "common/logging/log.h" +#include "common/thread.h" #include "core/arm/arm_interface.h" #include "core/arm/exclusive_monitor.h" #include "core/core.h" #include "core/core_timing.h" #include "core/core_timing_util.h" +#include "core/cpu_manager.h" #include "core/device_memory.h" #include "core/hardware_properties.h" #include "core/hle/kernel/client_port.h" @@ -117,7 +119,9 @@ struct KernelCore::Impl { InitializeSystemResourceLimit(kernel); InitializeMemoryLayout(); InitializeThreads(); - InitializePreemption(); + InitializePreemption(kernel); + InitializeSchedulers(); + InitializeSuspendThreads(); } void Shutdown() { @@ -155,6 +159,12 @@ struct KernelCore::Impl { } } + void InitializeSchedulers() { + for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { + cores[i].Scheduler().Initialize(); + } + } + // Creates the default system resource limit void InitializeSystemResourceLimit(KernelCore& kernel) { system_resource_limit = ResourceLimit::Create(kernel); @@ -178,10 +188,13 @@ struct KernelCore::Impl { Core::Timing::CreateEvent("ThreadWakeupCallback", ThreadWakeupCallback); } - void InitializePreemption() { - preemption_event = - Core::Timing::CreateEvent("PreemptionCallback", [this](u64 userdata, s64 cycles_late) { - global_scheduler.PreemptThreads(); + void InitializePreemption(KernelCore& kernel) { + preemption_event = Core::Timing::CreateEvent( + "PreemptionCallback", [this, &kernel](u64 userdata, s64 cycles_late) { + { + SchedulerLock lock(kernel); + global_scheduler.PreemptThreads(); + } s64 time_interval = Core::Timing::msToCycles(std::chrono::milliseconds(10)); system.CoreTiming().ScheduleEvent(time_interval, preemption_event); }); @@ -190,6 +203,20 @@ struct KernelCore::Impl { system.CoreTiming().ScheduleEvent(time_interval, preemption_event); } + void InitializeSuspendThreads() { + for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { + std::string name = "Suspend Thread Id:" + std::to_string(i); + std::function init_func = + system.GetCpuManager().GetSuspendThreadStartFunc(); + void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater(); + ThreadType type = + static_cast(THREADTYPE_KERNEL | THREADTYPE_HLE | THREADTYPE_SUSPEND); + auto thread_res = Thread::Create(system, type, name, 0, 0, 0, static_cast(i), 0, + nullptr, std::move(init_func), init_func_parameter); + suspend_threads[i] = std::move(thread_res).Unwrap(); + } + } + void MakeCurrentProcess(Process* process) { current_process = process; @@ -201,7 +228,10 @@ struct KernelCore::Impl { core.SetIs64Bit(process->Is64BitProcess()); } - system.Memory().SetCurrentPageTable(*process); + u32 core_id = GetCurrentHostThreadID(); + if (core_id < Core::Hardware::NUM_CPU_CORES) { + system.Memory().SetCurrentPageTable(*process, core_id); + } } void RegisterCoreThread(std::size_t core_id) { @@ -219,7 +249,9 @@ struct KernelCore::Impl { std::unique_lock lock{register_thread_mutex}; const std::thread::id this_id = std::this_thread::get_id(); const auto it = host_thread_ids.find(this_id); - ASSERT(it == host_thread_ids.end()); + if (it != host_thread_ids.end()) { + return; + } host_thread_ids[this_id] = registered_thread_ids++; } @@ -343,6 +375,8 @@ struct KernelCore::Impl { std::shared_ptr irs_shared_mem; std::shared_ptr time_shared_mem; + std::array, Core::Hardware::NUM_CPU_CORES> suspend_threads{}; + // System context Core::System& system; }; @@ -412,6 +446,26 @@ const Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) const { return impl->cores[id]; } +Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() { + u32 core_id = impl->GetCurrentHostThreadID(); + ASSERT(core_id < Core::Hardware::NUM_CPU_CORES); + return impl->cores[core_id]; +} + +const Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() const { + u32 core_id = impl->GetCurrentHostThreadID(); + ASSERT(core_id < Core::Hardware::NUM_CPU_CORES); + return impl->cores[core_id]; +} + +Kernel::Scheduler& KernelCore::CurrentScheduler() { + return CurrentPhysicalCore().Scheduler(); +} + +const Kernel::Scheduler& KernelCore::CurrentScheduler() const { + return CurrentPhysicalCore().Scheduler(); +} + Kernel::Synchronization& KernelCore::Synchronization() { return impl->synchronization; } @@ -557,4 +611,20 @@ const Kernel::SharedMemory& KernelCore::GetTimeSharedMem() const { return *impl->time_shared_mem; } +void KernelCore::Suspend(bool in_suspention) { + const bool should_suspend = exception_exited || in_suspention; + { + SchedulerLock lock(*this); + ThreadStatus status = should_suspend ? ThreadStatus::Ready : ThreadStatus::WaitSleep; + for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { + impl->suspend_threads[i]->SetStatus(status); + } + } +} + +void KernelCore::ExceptionalExit() { + exception_exited = true; + Suspend(true); +} + } // namespace Kernel diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 83de1f542..5d32a8329 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -110,6 +110,18 @@ public: /// Gets the an instance of the respective physical CPU core. const Kernel::PhysicalCore& PhysicalCore(std::size_t id) const; + /// Gets the sole instance of the Scheduler at the current running core. + Kernel::Scheduler& CurrentScheduler(); + + /// Gets the sole instance of the Scheduler at the current running core. + const Kernel::Scheduler& CurrentScheduler() const; + + /// Gets the an instance of the current physical CPU core. + Kernel::PhysicalCore& CurrentPhysicalCore(); + + /// Gets the an instance of the current physical CPU core. + const Kernel::PhysicalCore& CurrentPhysicalCore() const; + /// Gets the an instance of the Synchronization Interface. Kernel::Synchronization& Synchronization(); @@ -191,6 +203,12 @@ public: /// Gets the shared memory object for Time services. const Kernel::SharedMemory& GetTimeSharedMem() const; + /// Suspend/unsuspend the OS. + void Suspend(bool in_suspention); + + /// Exceptional exit the OS. + void ExceptionalExit(); + private: friend class Object; friend class Process; @@ -219,6 +237,7 @@ private: struct Impl; std::unique_ptr impl; + bool exception_exited{}; }; } // namespace Kernel diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp index a15011076..69202540b 100644 --- a/src/core/hle/kernel/physical_core.cpp +++ b/src/core/hle/kernel/physical_core.cpp @@ -2,12 +2,15 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/assert.h" #include "common/logging/log.h" +#include "common/spin_lock.h" #include "core/arm/arm_interface.h" #ifdef ARCHITECTURE_x86_64 #include "core/arm/dynarmic/arm_dynarmic_32.h" #include "core/arm/dynarmic/arm_dynarmic_64.h" #endif +#include "core/arm/cpu_interrupt_handler.h" #include "core/arm/exclusive_monitor.h" #include "core/arm/unicorn/arm_unicorn.h" #include "core/core.h" @@ -19,21 +22,23 @@ namespace Kernel { PhysicalCore::PhysicalCore(Core::System& system, std::size_t id, Core::ExclusiveMonitor& exclusive_monitor) - : core_index{id} { + : interrupt_handler{}, core_index{id} { #ifdef ARCHITECTURE_x86_64 - arm_interface_32 = - std::make_unique(system, exclusive_monitor, core_index); - arm_interface_64 = - std::make_unique(system, exclusive_monitor, core_index); - + arm_interface_32 = std::make_unique(system, interrupt_handler, + exclusive_monitor, core_index); + arm_interface_64 = std::make_unique(system, interrupt_handler, + exclusive_monitor, core_index); #else using Core::ARM_Unicorn; - arm_interface_32 = std::make_unique(system, ARM_Unicorn::Arch::AArch32); - arm_interface_64 = std::make_unique(system, ARM_Unicorn::Arch::AArch64); + arm_interface_32 = + std::make_unique(system, interrupt_handler, ARM_Unicorn::Arch::AArch32); + arm_interface_64 = + std::make_unique(system, interrupt_handler, ARM_Unicorn::Arch::AArch64); LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); #endif scheduler = std::make_unique(system, core_index); + guard = std::make_unique(); } PhysicalCore::~PhysicalCore() = default; @@ -47,6 +52,10 @@ void PhysicalCore::Step() { arm_interface->Step(); } +void PhysicalCore::Idle() { + interrupt_handler.AwaitInterrupt(); +} + void PhysicalCore::Stop() { arm_interface->PrepareReschedule(); } @@ -63,4 +72,16 @@ void PhysicalCore::SetIs64Bit(bool is_64_bit) { } } +void PhysicalCore::Interrupt() { + guard->lock(); + interrupt_handler.SetInterrupt(true); + guard->unlock(); +} + +void PhysicalCore::ClearInterrupt() { + guard->lock(); + interrupt_handler.SetInterrupt(false); + guard->unlock(); +} + } // namespace Kernel diff --git a/src/core/hle/kernel/physical_core.h b/src/core/hle/kernel/physical_core.h index 3269166be..c3da30b72 100644 --- a/src/core/hle/kernel/physical_core.h +++ b/src/core/hle/kernel/physical_core.h @@ -7,6 +7,12 @@ #include #include +#include "core/arm/cpu_interrupt_handler.h" + +namespace Common { + class SpinLock; +} + namespace Kernel { class Scheduler; } // namespace Kernel @@ -32,11 +38,24 @@ public: /// Execute current jit state void Run(); + /// Set this core in IdleState. + void Idle(); /// Execute a single instruction in current jit. void Step(); /// Stop JIT execution/exit void Stop(); + /// Interrupt this physical core. + void Interrupt(); + + /// Clear this core's interrupt + void ClearInterrupt(); + + /// Check if this core is interrupted + bool IsInterrupted() const { + return interrupt_handler.IsInterrupted(); + } + // Shutdown this physical core. void Shutdown(); @@ -71,11 +90,13 @@ public: void SetIs64Bit(bool is_64_bit); private: + Core::CPUInterruptHandler interrupt_handler; std::size_t core_index; std::unique_ptr arm_interface_32; std::unique_ptr arm_interface_64; std::unique_ptr scheduler; Core::ARM_Interface* arm_interface{}; + std::unique_ptr guard; }; } // namespace Kernel diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index c4c5199b1..7e26a54f4 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -30,14 +30,15 @@ namespace { /** * Sets up the primary application thread * + * @param system The system instance to create the main thread under. * @param owner_process The parent process for the main thread - * @param kernel The kernel instance to create the main thread under. * @param priority The priority to give the main thread */ -void SetupMainThread(Process& owner_process, KernelCore& kernel, u32 priority, VAddr stack_top) { +void SetupMainThread(Core::System& system, Process& owner_process, u32 priority, VAddr stack_top) { const VAddr entry_point = owner_process.PageTable().GetCodeRegionStart(); - auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0, - owner_process.GetIdealCore(), stack_top, owner_process); + ThreadType type = THREADTYPE_USER; + auto thread_res = Thread::Create(system, type, "main", entry_point, priority, 0, + owner_process.GetIdealCore(), stack_top, &owner_process); std::shared_ptr thread = std::move(thread_res).Unwrap(); @@ -48,8 +49,12 @@ void SetupMainThread(Process& owner_process, KernelCore& kernel, u32 priority, V thread->GetContext32().cpu_registers[1] = thread_handle; thread->GetContext64().cpu_registers[1] = thread_handle; + auto& kernel = system.Kernel(); // Threads by default are dormant, wake up the main thread so it runs when the scheduler fires - thread->ResumeFromWait(); + { + SchedulerLock lock{kernel}; + thread->SetStatus(ThreadStatus::Ready); + } } } // Anonymous namespace @@ -294,7 +299,7 @@ void Process::Run(s32 main_thread_priority, u64 stack_size) { ChangeStatus(ProcessStatus::Running); - SetupMainThread(*this, kernel, main_thread_priority, main_thread_stack_top); + SetupMainThread(system, *this, main_thread_priority, main_thread_stack_top); resource_limit->Reserve(ResourceType::Threads, 1); resource_limit->Reserve(ResourceType::PhysicalMemory, main_thread_stack_size); } diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index 1140c72a3..5166020a0 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp @@ -11,11 +11,15 @@ #include #include "common/assert.h" +#include "common/bit_util.h" +#include "common/fiber.h" #include "common/logging/log.h" #include "core/arm/arm_interface.h" #include "core/core.h" #include "core/core_timing.h" +#include "core/cpu_manager.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/physical_core.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/scheduler.h" #include "core/hle/kernel/time_manager.h" @@ -27,78 +31,108 @@ GlobalScheduler::GlobalScheduler(KernelCore& kernel) : kernel{kernel} {} GlobalScheduler::~GlobalScheduler() = default; void GlobalScheduler::AddThread(std::shared_ptr thread) { + global_list_guard.lock(); thread_list.push_back(std::move(thread)); + global_list_guard.unlock(); } void GlobalScheduler::RemoveThread(std::shared_ptr thread) { + global_list_guard.lock(); thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread), thread_list.end()); + global_list_guard.unlock(); } -void GlobalScheduler::UnloadThread(std::size_t core) { - Scheduler& sched = kernel.Scheduler(core); - sched.UnloadThread(); -} - -void GlobalScheduler::SelectThread(std::size_t core) { +u32 GlobalScheduler::SelectThreads() { const auto update_thread = [](Thread* thread, Scheduler& sched) { + sched.guard.lock(); if (thread != sched.selected_thread.get()) { if (thread == nullptr) { ++sched.idle_selection_count; } sched.selected_thread = SharedFrom(thread); } - sched.is_context_switch_pending = sched.selected_thread != sched.current_thread; + const bool reschedule_pending = sched.selected_thread != sched.current_thread; + sched.is_context_switch_pending = reschedule_pending; std::atomic_thread_fence(std::memory_order_seq_cst); + sched.guard.unlock(); + return reschedule_pending; }; - Scheduler& sched = kernel.Scheduler(core); - Thread* current_thread = nullptr; + if (!is_reselection_pending.load()) { + return 0; + } + std::array top_threads{}; + + u32 idle_cores{}; + // Step 1: Get top thread in schedule queue. - current_thread = scheduled_queue[core].empty() ? nullptr : scheduled_queue[core].front(); - if (current_thread) { - update_thread(current_thread, sched); - return; - } - // Step 2: Try selecting a suggested thread. - Thread* winner = nullptr; - std::set sug_cores; - for (auto thread : suggested_queue[core]) { - s32 this_core = thread->GetProcessorID(); - Thread* thread_on_core = nullptr; - if (this_core >= 0) { - thread_on_core = scheduled_queue[this_core].front(); + for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { + Thread* top_thread = + scheduled_queue[core].empty() ? nullptr : scheduled_queue[core].front(); + if (top_thread != nullptr) { + // TODO(Blinkhawk): Implement Thread Pinning + } else { + idle_cores |= (1ul << core); } - if (this_core < 0 || thread != thread_on_core) { - winner = thread; - break; - } - sug_cores.insert(this_core); + top_threads[core] = top_thread; } - // if we got a suggested thread, select it, else do a second pass. - if (winner && winner->GetPriority() > 2) { - if (winner->IsRunning()) { - UnloadThread(static_cast(winner->GetProcessorID())); - } - TransferToCore(winner->GetPriority(), static_cast(core), winner); - update_thread(winner, sched); - return; - } - // Step 3: Select a suggested thread from another core - for (auto& src_core : sug_cores) { - auto it = scheduled_queue[src_core].begin(); - it++; - if (it != scheduled_queue[src_core].end()) { - Thread* thread_on_core = scheduled_queue[src_core].front(); - Thread* to_change = *it; - if (thread_on_core->IsRunning() || to_change->IsRunning()) { - UnloadThread(static_cast(src_core)); + + while (idle_cores != 0) { + u32 core_id = Common::CountTrailingZeroes32(idle_cores); + + if (!suggested_queue[core_id].empty()) { + std::array migration_candidates{}; + std::size_t num_candidates = 0; + auto iter = suggested_queue[core_id].begin(); + Thread* suggested = nullptr; + // Step 2: Try selecting a suggested thread. + while (iter != suggested_queue[core_id].end()) { + suggested = *iter; + iter++; + s32 suggested_core_id = suggested->GetProcessorID(); + Thread* top_thread = + suggested_core_id > 0 ? top_threads[suggested_core_id] : nullptr; + if (top_thread != suggested) { + if (top_thread != nullptr && + top_thread->GetPriority() < THREADPRIO_MAX_CORE_MIGRATION) { + suggested = nullptr; + break; + // There's a too high thread to do core migration, cancel + } + TransferToCore(suggested->GetPriority(), static_cast(core_id), suggested); + break; + } + migration_candidates[num_candidates++] = suggested_core_id; } - TransferToCore(thread_on_core->GetPriority(), static_cast(core), thread_on_core); - current_thread = thread_on_core; - break; + // Step 3: Select a suggested thread from another core + if (suggested == nullptr) { + for (std::size_t i = 0; i < num_candidates; i++) { + s32 candidate_core = migration_candidates[i]; + suggested = top_threads[candidate_core]; + auto it = scheduled_queue[candidate_core].begin(); + it++; + Thread* next = it != scheduled_queue[candidate_core].end() ? *it : nullptr; + if (next != nullptr) { + TransferToCore(suggested->GetPriority(), static_cast(core_id), + suggested); + top_threads[candidate_core] = next; + break; + } + } + } + top_threads[core_id] = suggested; + } + + idle_cores &= ~(1ul << core_id); + } + u32 cores_needing_context_switch{}; + for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { + Scheduler& sched = kernel.Scheduler(core); + if (update_thread(top_threads[core], sched)) { + cores_needing_context_switch |= (1ul << core); } } - update_thread(current_thread, sched); + return cores_needing_context_switch; } bool GlobalScheduler::YieldThread(Thread* yielding_thread) { @@ -153,9 +187,6 @@ bool GlobalScheduler::YieldThreadAndBalanceLoad(Thread* yielding_thread) { if (winner != nullptr) { if (winner != yielding_thread) { - if (winner->IsRunning()) { - UnloadThread(static_cast(winner->GetProcessorID())); - } TransferToCore(winner->GetPriority(), s32(core_id), winner); } } else { @@ -195,9 +226,6 @@ bool GlobalScheduler::YieldThreadAndWaitForLoadBalancing(Thread* yielding_thread } if (winner != nullptr) { if (winner != yielding_thread) { - if (winner->IsRunning()) { - UnloadThread(static_cast(winner->GetProcessorID())); - } TransferToCore(winner->GetPriority(), static_cast(core_id), winner); } } else { @@ -213,7 +241,9 @@ void GlobalScheduler::PreemptThreads() { const u32 priority = preemption_priorities[core_id]; if (scheduled_queue[core_id].size(priority) > 0) { - scheduled_queue[core_id].front(priority)->IncrementYieldCount(); + if (scheduled_queue[core_id].size(priority) > 1) { + scheduled_queue[core_id].front(priority)->IncrementYieldCount(); + } scheduled_queue[core_id].yield(priority); if (scheduled_queue[core_id].size(priority) > 1) { scheduled_queue[core_id].front(priority)->IncrementYieldCount(); @@ -247,9 +277,6 @@ void GlobalScheduler::PreemptThreads() { } if (winner != nullptr) { - if (winner->IsRunning()) { - UnloadThread(static_cast(winner->GetProcessorID())); - } TransferToCore(winner->GetPriority(), s32(core_id), winner); current_thread = winner->GetPriority() <= current_thread->GetPriority() ? winner : current_thread; @@ -280,9 +307,6 @@ void GlobalScheduler::PreemptThreads() { } if (winner != nullptr) { - if (winner->IsRunning()) { - UnloadThread(static_cast(winner->GetProcessorID())); - } TransferToCore(winner->GetPriority(), s32(core_id), winner); current_thread = winner; } @@ -292,6 +316,28 @@ void GlobalScheduler::PreemptThreads() { } } +void GlobalScheduler::EnableInterruptAndSchedule(u32 cores_pending_reschedule, + Core::EmuThreadHandle global_thread) { + u32 current_core = global_thread.host_handle; + bool must_context_switch = global_thread.guest_handle != InvalidHandle && + (current_core < Core::Hardware::NUM_CPU_CORES); + while (cores_pending_reschedule != 0) { + u32 core = Common::CountTrailingZeroes32(cores_pending_reschedule); + ASSERT(core < Core::Hardware::NUM_CPU_CORES); + if (!must_context_switch || core != current_core) { + auto& phys_core = kernel.PhysicalCore(core); + phys_core.Interrupt(); + } else { + must_context_switch = true; + } + cores_pending_reschedule &= ~(1ul << core); + } + if (must_context_switch) { + auto& core_scheduler = kernel.CurrentScheduler(); + core_scheduler.TryDoContextSwitch(); + } +} + void GlobalScheduler::Suggest(u32 priority, std::size_t core, Thread* thread) { suggested_queue[core].add(thread, priority); } @@ -349,6 +395,108 @@ bool GlobalScheduler::AskForReselectionOrMarkRedundant(Thread* current_thread, } } +void GlobalScheduler::AdjustSchedulingOnStatus(Thread* thread, u32 old_flags) { + if (old_flags == thread->scheduling_state) { + return; + } + + if (static_cast(old_flags & static_cast(ThreadSchedMasks::LowMask)) == + ThreadSchedStatus::Runnable) { + // In this case the thread was running, now it's pausing/exitting + if (thread->processor_id >= 0) { + Unschedule(thread->current_priority, static_cast(thread->processor_id), thread); + } + + for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { + if (core != static_cast(thread->processor_id) && + ((thread->affinity_mask >> core) & 1) != 0) { + Unsuggest(thread->current_priority, core, thread); + } + } + } else if (thread->GetSchedulingStatus() == ThreadSchedStatus::Runnable) { + // The thread is now set to running from being stopped + if (thread->processor_id >= 0) { + Schedule(thread->current_priority, static_cast(thread->processor_id), thread); + } + + for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { + if (core != static_cast(thread->processor_id) && + ((thread->affinity_mask >> core) & 1) != 0) { + Suggest(thread->current_priority, core, thread); + } + } + } + + SetReselectionPending(); +} + +void GlobalScheduler::AdjustSchedulingOnPriority(Thread* thread, u32 old_priority) { + if (thread->GetSchedulingStatus() != ThreadSchedStatus::Runnable) { + return; + } + if (thread->processor_id >= 0) { + Unschedule(old_priority, static_cast(thread->processor_id), thread); + } + + for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { + if (core != static_cast(thread->processor_id) && + ((thread->affinity_mask >> core) & 1) != 0) { + Unsuggest(old_priority, core, thread); + } + } + + if (thread->processor_id >= 0) { + // TODO(Blinkhawk): compare it with current thread running on current core, instead of + // checking running + if (thread->IsRunning()) { + SchedulePrepend(thread->current_priority, static_cast(thread->processor_id), + thread); + } else { + Schedule(thread->current_priority, static_cast(thread->processor_id), thread); + } + } + + for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { + if (core != static_cast(thread->processor_id) && + ((thread->affinity_mask >> core) & 1) != 0) { + Suggest(thread->current_priority, core, thread); + } + } + thread->IncrementYieldCount(); + SetReselectionPending(); +} + +void GlobalScheduler::AdjustSchedulingOnAffinity(Thread* thread, u64 old_affinity_mask, + s32 old_core) { + if (thread->GetSchedulingStatus() != ThreadSchedStatus::Runnable || + thread->current_priority >= THREADPRIO_COUNT) { + return; + } + + for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { + if (((old_affinity_mask >> core) & 1) != 0) { + if (core == static_cast(old_core)) { + Unschedule(thread->current_priority, core, thread); + } else { + Unsuggest(thread->current_priority, core, thread); + } + } + } + + for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { + if (((thread->affinity_mask >> core) & 1) != 0) { + if (core == static_cast(thread->processor_id)) { + Schedule(thread->current_priority, core, thread); + } else { + Suggest(thread->current_priority, core, thread); + } + } + } + + thread->IncrementYieldCount(); + SetReselectionPending(); +} + void GlobalScheduler::Shutdown() { for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { scheduled_queue[core].clear(); @@ -374,13 +522,12 @@ void GlobalScheduler::Unlock() { ASSERT(scope_lock > 0); return; } - for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { - SelectThread(i); - } + u32 cores_pending_reschedule = SelectThreads(); + Core::EmuThreadHandle leaving_thread = current_owner; current_owner = Core::EmuThreadHandle::InvalidHandle(); scope_lock = 1; inner_lock.unlock(); - // TODO(Blinkhawk): Setup the interrupts and change context on current core. + EnableInterruptAndSchedule(cores_pending_reschedule, leaving_thread); } Scheduler::Scheduler(Core::System& system, std::size_t core_id) @@ -393,56 +540,83 @@ bool Scheduler::HaveReadyThreads() const { } Thread* Scheduler::GetCurrentThread() const { - return current_thread.get(); + if (current_thread) { + return current_thread.get(); + } + return idle_thread.get(); } Thread* Scheduler::GetSelectedThread() const { return selected_thread.get(); } -void Scheduler::SelectThreads() { - system.GlobalScheduler().SelectThread(core_id); -} - u64 Scheduler::GetLastContextSwitchTicks() const { return last_context_switch_time; } void Scheduler::TryDoContextSwitch() { + auto& phys_core = system.Kernel().CurrentPhysicalCore(); + if (phys_core.IsInterrupted()) { + phys_core.ClearInterrupt(); + } + guard.lock(); if (is_context_switch_pending) { SwitchContext(); + } else { + guard.unlock(); } } -void Scheduler::UnloadThread() { - Thread* const previous_thread = GetCurrentThread(); - Process* const previous_process = system.Kernel().CurrentProcess(); +void Scheduler::OnThreadStart() { + SwitchContextStep2(); +} - UpdateLastContextSwitchTime(previous_thread, previous_process); +void Scheduler::SwitchContextStep2() { + Thread* previous_thread = current_thread.get(); + Thread* new_thread = selected_thread.get(); - // Save context for previous thread - if (previous_thread) { - system.ArmInterface(core_id).SaveContext(previous_thread->GetContext32()); - system.ArmInterface(core_id).SaveContext(previous_thread->GetContext64()); - // Save the TPIDR_EL0 system register in case it was modified. - previous_thread->SetTPIDR_EL0(system.ArmInterface(core_id).GetTPIDR_EL0()); + // Load context of new thread + Process* const previous_process = + previous_thread != nullptr ? previous_thread->GetOwnerProcess() : nullptr; - if (previous_thread->GetStatus() == ThreadStatus::Running) { - // This is only the case when a reschedule is triggered without the current thread - // yielding execution (i.e. an event triggered, system core time-sliced, etc) - previous_thread->SetStatus(ThreadStatus::Ready); + if (new_thread) { + new_thread->context_guard.lock(); + ASSERT_MSG(new_thread->GetProcessorID() == s32(this->core_id), + "Thread must be assigned to this core."); + ASSERT_MSG(new_thread->GetStatus() == ThreadStatus::Ready, + "Thread must be ready to become running."); + + // Cancel any outstanding wakeup events for this thread + current_thread = SharedFrom(new_thread); + new_thread->SetStatus(ThreadStatus::Running); + new_thread->SetIsRunning(true); + + auto* const thread_owner_process = current_thread->GetOwnerProcess(); + if (previous_process != thread_owner_process && thread_owner_process != nullptr) { + system.Kernel().MakeCurrentProcess(thread_owner_process); } - previous_thread->SetIsRunning(false); + if (!new_thread->IsHLEThread()) { + auto& cpu_core = system.ArmInterface(core_id); + cpu_core.LoadContext(new_thread->GetContext32()); + cpu_core.LoadContext(new_thread->GetContext64()); + cpu_core.SetTlsAddress(new_thread->GetTLSAddress()); + cpu_core.SetTPIDR_EL0(new_thread->GetTPIDR_EL0()); + } + } else { + current_thread = nullptr; + // Note: We do not reset the current process and current page table when idling because + // technically we haven't changed processes, our threads are just paused. } - current_thread = nullptr; + guard.unlock(); } void Scheduler::SwitchContext() { - Thread* const previous_thread = GetCurrentThread(); - Thread* const new_thread = GetSelectedThread(); + Thread* previous_thread = current_thread.get(); + Thread* new_thread = selected_thread.get(); is_context_switch_pending = false; if (new_thread == previous_thread) { + guard.unlock(); return; } @@ -452,51 +626,44 @@ void Scheduler::SwitchContext() { // Save context for previous thread if (previous_thread) { - system.ArmInterface(core_id).SaveContext(previous_thread->GetContext32()); - system.ArmInterface(core_id).SaveContext(previous_thread->GetContext64()); - // Save the TPIDR_EL0 system register in case it was modified. - previous_thread->SetTPIDR_EL0(system.ArmInterface(core_id).GetTPIDR_EL0()); + if (!previous_thread->IsHLEThread()) { + auto& cpu_core = system.ArmInterface(core_id); + cpu_core.SaveContext(previous_thread->GetContext32()); + cpu_core.SaveContext(previous_thread->GetContext64()); + // Save the TPIDR_EL0 system register in case it was modified. + previous_thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0()); + } if (previous_thread->GetStatus() == ThreadStatus::Running) { - // This is only the case when a reschedule is triggered without the current thread - // yielding execution (i.e. an event triggered, system core time-sliced, etc) previous_thread->SetStatus(ThreadStatus::Ready); } previous_thread->SetIsRunning(false); + previous_thread->context_guard.unlock(); } - // Load context of new thread - if (new_thread) { - ASSERT_MSG(new_thread->GetProcessorID() == s32(this->core_id), - "Thread must be assigned to this core."); - ASSERT_MSG(new_thread->GetStatus() == ThreadStatus::Ready, - "Thread must be ready to become running."); - - // Cancel any outstanding wakeup events for this thread - new_thread->CancelWakeupTimer(); - current_thread = SharedFrom(new_thread); - new_thread->SetStatus(ThreadStatus::Running); - new_thread->SetIsRunning(true); - - auto* const thread_owner_process = current_thread->GetOwnerProcess(); - if (previous_process != thread_owner_process) { - system.Kernel().MakeCurrentProcess(thread_owner_process); - } - - system.ArmInterface(core_id).LoadContext(new_thread->GetContext32()); - system.ArmInterface(core_id).LoadContext(new_thread->GetContext64()); - system.ArmInterface(core_id).SetTlsAddress(new_thread->GetTLSAddress()); - system.ArmInterface(core_id).SetTPIDR_EL0(new_thread->GetTPIDR_EL0()); + std::shared_ptr old_context; + if (previous_thread != nullptr) { + old_context = previous_thread->GetHostContext(); } else { - current_thread = nullptr; - // Note: We do not reset the current process and current page table when idling because - // technically we haven't changed processes, our threads are just paused. + old_context = idle_thread->GetHostContext(); } + + std::shared_ptr next_context; + if (new_thread != nullptr) { + next_context = new_thread->GetHostContext(); + } else { + next_context = idle_thread->GetHostContext(); + } + + Common::Fiber::YieldTo(old_context, next_context); + /// When a thread wakes up, the scheduler may have changed to other in another core. + auto& next_scheduler = system.Kernel().CurrentScheduler(); + next_scheduler.SwitchContextStep2(); } void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) { const u64 prev_switch_ticks = last_context_switch_time; - const u64 most_recent_switch_ticks = system.CoreTiming().GetTicks(); + const u64 most_recent_switch_ticks = system.CoreTiming().GetCPUTicks(); const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks; if (thread != nullptr) { @@ -510,6 +677,16 @@ void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) { last_context_switch_time = most_recent_switch_ticks; } +void Scheduler::Initialize() { + std::string name = "Idle Thread Id:" + std::to_string(core_id); + std::function init_func = system.GetCpuManager().GetIdleThreadStartFunc(); + void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater(); + ThreadType type = static_cast(THREADTYPE_KERNEL | THREADTYPE_HLE | THREADTYPE_IDLE); + auto thread_res = Thread::Create(system, type, name, 0, 64, 0, static_cast(core_id), 0, + nullptr, std::move(init_func), init_func_parameter); + idle_thread = std::move(thread_res).Unwrap(); +} + void Scheduler::Shutdown() { current_thread = nullptr; selected_thread = nullptr; diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h index 07df33f9c..16655b03f 100644 --- a/src/core/hle/kernel/scheduler.h +++ b/src/core/hle/kernel/scheduler.h @@ -11,6 +11,7 @@ #include "common/common_types.h" #include "common/multi_level_queue.h" +#include "common/spin_lock.h" #include "core/hardware_properties.h" #include "core/hle/kernel/thread.h" @@ -41,41 +42,17 @@ public: return thread_list; } - /** - * Add a thread to the suggested queue of a cpu core. Suggested threads may be - * picked if no thread is scheduled to run on the core. - */ - void Suggest(u32 priority, std::size_t core, Thread* thread); + /// Notify the scheduler a thread's status has changed. + void AdjustSchedulingOnStatus(Thread* thread, u32 old_flags); + + /// Notify the scheduler a thread's priority has changed. + void AdjustSchedulingOnPriority(Thread* thread, u32 old_priority); + + /// Notify the scheduler a thread's core and/or affinity mask has changed. + void AdjustSchedulingOnAffinity(Thread* thread, u64 old_affinity_mask, s32 old_core); /** - * Remove a thread to the suggested queue of a cpu core. Suggested threads may be - * picked if no thread is scheduled to run on the core. - */ - void Unsuggest(u32 priority, std::size_t core, Thread* thread); - - /** - * Add a thread to the scheduling queue of a cpu core. The thread is added at the - * back the queue in its priority level. - */ - void Schedule(u32 priority, std::size_t core, Thread* thread); - - /** - * Add a thread to the scheduling queue of a cpu core. The thread is added at the - * front the queue in its priority level. - */ - void SchedulePrepend(u32 priority, std::size_t core, Thread* thread); - - /// Reschedule an already scheduled thread based on a new priority - void Reschedule(u32 priority, std::size_t core, Thread* thread); - - /// Unschedules a thread. - void Unschedule(u32 priority, std::size_t core, Thread* thread); - - /// Selects a core and forces it to unload its current thread's context - void UnloadThread(std::size_t core); - - /** - * Takes care of selecting the new scheduled thread in three steps: + * Takes care of selecting the new scheduled threads in three steps: * * 1. First a thread is selected from the top of the priority queue. If no thread * is obtained then we move to step two, else we are done. @@ -85,8 +62,10 @@ public: * * 3. Third is no suggested thread is found, we do a second pass and pick a running * thread in another core and swap it with its current thread. + * + * returns the cores needing scheduling. */ - void SelectThread(std::size_t core); + u32 SelectThreads(); bool HaveReadyThreads(std::size_t core_id) const { return !scheduled_queue[core_id].empty(); @@ -149,6 +128,39 @@ private: /// Unlocks the scheduler, reselects threads, interrupts cores for rescheduling /// and reschedules current core if needed. void Unlock(); + + void EnableInterruptAndSchedule(u32 cores_pending_reschedule, Core::EmuThreadHandle global_thread); + + /** + * Add a thread to the suggested queue of a cpu core. Suggested threads may be + * picked if no thread is scheduled to run on the core. + */ + void Suggest(u32 priority, std::size_t core, Thread* thread); + + /** + * Remove a thread to the suggested queue of a cpu core. Suggested threads may be + * picked if no thread is scheduled to run on the core. + */ + void Unsuggest(u32 priority, std::size_t core, Thread* thread); + + /** + * Add a thread to the scheduling queue of a cpu core. The thread is added at the + * back the queue in its priority level. + */ + void Schedule(u32 priority, std::size_t core, Thread* thread); + + /** + * Add a thread to the scheduling queue of a cpu core. The thread is added at the + * front the queue in its priority level. + */ + void SchedulePrepend(u32 priority, std::size_t core, Thread* thread); + + /// Reschedule an already scheduled thread based on a new priority + void Reschedule(u32 priority, std::size_t core, Thread* thread); + + /// Unschedules a thread. + void Unschedule(u32 priority, std::size_t core, Thread* thread); + /** * Transfers a thread into an specific core. If the destination_core is -1 * it will be unscheduled from its source code and added into its suggested @@ -174,6 +186,8 @@ private: std::atomic scope_lock{}; Core::EmuThreadHandle current_owner{Core::EmuThreadHandle::InvalidHandle()}; + Common::SpinLock global_list_guard{}; + /// Lists all thread ids that aren't deleted/etc. std::vector> thread_list; KernelCore& kernel; @@ -190,12 +204,6 @@ public: /// Reschedules to the next available thread (call after current thread is suspended) void TryDoContextSwitch(); - /// Unloads currently running thread - void UnloadThread(); - - /// Select the threads in top of the scheduling multilist. - void SelectThreads(); - /// Gets the current running thread Thread* GetCurrentThread() const; @@ -209,15 +217,22 @@ public: return is_context_switch_pending; } + void Initialize(); + /// Shutdowns the scheduler. void Shutdown(); + void OnThreadStart(); + private: friend class GlobalScheduler; /// Switches the CPU's active thread context to that of the specified thread void SwitchContext(); + /// When a thread wakes up, it must run this through it's new scheduler + void SwitchContextStep2(); + /** * Called on every context switch to update the internal timestamp * This also updates the running time ticks for the given thread and @@ -233,12 +248,15 @@ private: std::shared_ptr current_thread = nullptr; std::shared_ptr selected_thread = nullptr; + std::shared_ptr idle_thread = nullptr; Core::System& system; u64 last_context_switch_time = 0; u64 idle_selection_count = 0; const std::size_t core_id; + Common::SpinLock guard{}; + bool is_context_switch_pending = false; }; diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 4ae4529f5..d7f0dcabd 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -863,9 +863,9 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) { const u64 thread_ticks = current_thread->GetTotalCPUTimeTicks(); - out_ticks = thread_ticks + (core_timing.GetTicks() - prev_ctx_ticks); + out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks); } else if (same_thread && info_sub_id == system.CurrentCoreIndex()) { - out_ticks = core_timing.GetTicks() - prev_ctx_ticks; + out_ticks = core_timing.GetCPUTicks() - prev_ctx_ticks; } *result = out_ticks; @@ -1428,9 +1428,10 @@ static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr e ASSERT(kernel.CurrentProcess()->GetResourceLimit()->Reserve(ResourceType::Threads, 1)); + ThreadType type = THREADTYPE_USER; CASCADE_RESULT(std::shared_ptr thread, - Thread::Create(kernel, "", entry_point, priority, arg, processor_id, stack_top, - *current_process)); + Thread::Create(system, type, "", entry_point, priority, arg, processor_id, stack_top, + current_process)); const auto new_thread_handle = current_process->GetHandleTable().Create(thread); if (new_thread_handle.Failed()) { @@ -1513,13 +1514,6 @@ static void SleepThread(Core::System& system, s64 nanoseconds) { } else { current_thread->Sleep(nanoseconds); } - - if (is_redundant) { - // If it's redundant, the core is pretty much idle. Some games keep idling - // a core while it's doing nothing, we advance timing to avoid costly continuous - // calls. - system.CoreTiming().AddTicks(2000); - } system.PrepareReschedule(current_thread->GetProcessorID()); } @@ -1725,10 +1719,7 @@ static u64 GetSystemTick(Core::System& system) { auto& core_timing = system.CoreTiming(); // Returns the value of cntpct_el0 (https://switchbrew.org/wiki/SVC#svcGetSystemTick) - const u64 result{Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks())}; - - // Advance time to defeat dumb games that busy-wait for the frame to end. - core_timing.AddTicks(400); + const u64 result{system.CoreTiming().GetClockTicks()}; return result; } diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index db7f379ac..8cb3593db 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -9,12 +9,14 @@ #include "common/assert.h" #include "common/common_types.h" +#include "common/fiber.h" #include "common/logging/log.h" #include "common/thread_queue_list.h" #include "core/arm/arm_interface.h" #include "core/core.h" #include "core/core_timing.h" #include "core/core_timing_util.h" +#include "core/cpu_manager.h" #include "core/hardware_properties.h" #include "core/hle/kernel/errors.h" #include "core/hle/kernel/handle_table.h" @@ -23,6 +25,7 @@ #include "core/hle/kernel/process.h" #include "core/hle/kernel/scheduler.h" #include "core/hle/kernel/thread.h" +#include "core/hle/kernel/time_manager.h" #include "core/hle/result.h" #include "core/memory.h" @@ -44,6 +47,7 @@ Thread::Thread(KernelCore& kernel) : SynchronizationObject{kernel} {} Thread::~Thread() = default; void Thread::Stop() { + SchedulerLock lock(kernel); // Cancel any outstanding wakeup events for this thread Core::System::GetInstance().CoreTiming().UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(), global_handle); @@ -71,9 +75,8 @@ void Thread::WakeAfterDelay(s64 nanoseconds) { // This function might be called from any thread so we have to be cautious and use the // thread-safe version of ScheduleEvent. - const s64 cycles = Core::Timing::nsToCycles(std::chrono::nanoseconds{nanoseconds}); Core::System::GetInstance().CoreTiming().ScheduleEvent( - cycles, kernel.ThreadWakeupCallbackEventType(), global_handle); + nanoseconds, kernel.ThreadWakeupCallbackEventType(), global_handle); } void Thread::CancelWakeupTimer() { @@ -125,6 +128,16 @@ void Thread::ResumeFromWait() { SetStatus(ThreadStatus::Ready); } +void Thread::OnWakeUp() { + SchedulerLock lock(kernel); + if (activity == ThreadActivity::Paused) { + SetStatus(ThreadStatus::Paused); + return; + } + + SetStatus(ThreadStatus::Ready); +} + void Thread::CancelWait() { if (GetSchedulingStatus() != ThreadSchedStatus::Paused) { is_sync_cancelled = true; @@ -153,12 +166,29 @@ static void ResetThreadContext64(Core::ARM_Interface::ThreadContext64& context, context.fpcr = 0; } -ResultVal> Thread::Create(KernelCore& kernel, std::string name, - VAddr entry_point, u32 priority, u64 arg, - s32 processor_id, VAddr stack_top, - Process& owner_process) { +std::shared_ptr Thread::GetHostContext() const { + return host_context; +} + +ResultVal> Thread::Create(Core::System& system, ThreadType type_flags, + std::string name, VAddr entry_point, u32 priority, + u64 arg, s32 processor_id, VAddr stack_top, + Process* owner_process) { + std::function init_func = system.GetCpuManager().GetGuestThreadStartFunc(); + void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater(); + return Create(system, type_flags, name, entry_point, priority, arg, processor_id, stack_top, + owner_process, std::move(init_func), init_func_parameter); +} + +ResultVal> Thread::Create(Core::System& system, ThreadType type_flags, + std::string name, VAddr entry_point, u32 priority, + u64 arg, s32 processor_id, VAddr stack_top, + Process* owner_process, + std::function&& thread_start_func, + void* thread_start_parameter) { + auto& kernel = system.Kernel(); // Check if priority is in ranged. Lowest priority -> highest priority id. - if (priority > THREADPRIO_LOWEST) { + if (priority > THREADPRIO_LOWEST && (type_flags & THREADTYPE_IDLE == 0)) { LOG_ERROR(Kernel_SVC, "Invalid thread priority: {}", priority); return ERR_INVALID_THREAD_PRIORITY; } @@ -168,11 +198,12 @@ ResultVal> Thread::Create(KernelCore& kernel, std::strin return ERR_INVALID_PROCESSOR_ID; } - auto& system = Core::System::GetInstance(); - if (!system.Memory().IsValidVirtualAddress(owner_process, entry_point)) { - LOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:016X}", name, entry_point); - // TODO (bunnei): Find the correct error code to use here - return RESULT_UNKNOWN; + if (owner_process) { + if (!system.Memory().IsValidVirtualAddress(*owner_process, entry_point)) { + LOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:016X}", name, entry_point); + // TODO (bunnei): Find the correct error code to use here + return RESULT_UNKNOWN; + } } std::shared_ptr thread = std::make_shared(kernel); @@ -183,7 +214,7 @@ ResultVal> Thread::Create(KernelCore& kernel, std::strin thread->stack_top = stack_top; thread->tpidr_el0 = 0; thread->nominal_priority = thread->current_priority = priority; - thread->last_running_ticks = system.CoreTiming().GetTicks(); + thread->last_running_ticks = 0; thread->processor_id = processor_id; thread->ideal_core = processor_id; thread->affinity_mask = 1ULL << processor_id; @@ -193,16 +224,27 @@ ResultVal> Thread::Create(KernelCore& kernel, std::strin thread->wait_handle = 0; thread->name = std::move(name); thread->global_handle = kernel.GlobalHandleTable().Create(thread).Unwrap(); - thread->owner_process = &owner_process; - auto& scheduler = kernel.GlobalScheduler(); - scheduler.AddThread(thread); - thread->tls_address = thread->owner_process->CreateTLSRegion(); - - thread->owner_process->RegisterThread(thread.get()); - - ResetThreadContext32(thread->context_32, static_cast(stack_top), - static_cast(entry_point), static_cast(arg)); - ResetThreadContext64(thread->context_64, stack_top, entry_point, arg); + thread->owner_process = owner_process; + thread->type = type_flags; + if ((type_flags & THREADTYPE_IDLE) == 0) { + auto& scheduler = kernel.GlobalScheduler(); + scheduler.AddThread(thread); + } + if (owner_process) { + thread->tls_address = thread->owner_process->CreateTLSRegion(); + thread->owner_process->RegisterThread(thread.get()); + } else { + thread->tls_address = 0; + } + // TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used + // to initialize the context + if ((type_flags & THREADTYPE_HLE) == 0) { + ResetThreadContext32(thread->context_32, static_cast(stack_top), + static_cast(entry_point), static_cast(arg)); + ResetThreadContext64(thread->context_64, stack_top, entry_point, arg); + } + thread->host_context = + std::make_shared(std::move(thread_start_func), thread_start_parameter); return MakeResult>(std::move(thread)); } @@ -258,7 +300,7 @@ void Thread::SetStatus(ThreadStatus new_status) { } if (status == ThreadStatus::Running) { - last_running_ticks = Core::System::GetInstance().CoreTiming().GetTicks(); + last_running_ticks = Core::System::GetInstance().CoreTiming().GetCPUTicks(); } status = new_status; @@ -375,38 +417,55 @@ void Thread::SetActivity(ThreadActivity value) { } void Thread::Sleep(s64 nanoseconds) { - // Sleep current thread and check for next thread to schedule - SetStatus(ThreadStatus::WaitSleep); + Handle event_handle{}; + { + SchedulerLockAndSleep lock(kernel, event_handle, this, nanoseconds); + SetStatus(ThreadStatus::WaitSleep); + } - // Create an event to wake the thread up after the specified nanosecond delay has passed - WakeAfterDelay(nanoseconds); + if (event_handle != InvalidHandle) { + auto& time_manager = kernel.TimeManager(); + time_manager.UnscheduleTimeEvent(event_handle); + } } bool Thread::YieldSimple() { - auto& scheduler = kernel.GlobalScheduler(); - return scheduler.YieldThread(this); + bool result{}; + { + SchedulerLock lock(kernel); + result = kernel.GlobalScheduler().YieldThread(this); + } + return result; } bool Thread::YieldAndBalanceLoad() { - auto& scheduler = kernel.GlobalScheduler(); - return scheduler.YieldThreadAndBalanceLoad(this); + bool result{}; + { + SchedulerLock lock(kernel); + result = kernel.GlobalScheduler().YieldThreadAndBalanceLoad(this); + } + return result; } bool Thread::YieldAndWaitForLoadBalancing() { - auto& scheduler = kernel.GlobalScheduler(); - return scheduler.YieldThreadAndWaitForLoadBalancing(this); + bool result{}; + { + SchedulerLock lock(kernel); + result = kernel.GlobalScheduler().YieldThreadAndWaitForLoadBalancing(this); + } + return result; } void Thread::SetSchedulingStatus(ThreadSchedStatus new_status) { const u32 old_flags = scheduling_state; scheduling_state = (scheduling_state & static_cast(ThreadSchedMasks::HighMask)) | static_cast(new_status); - AdjustSchedulingOnStatus(old_flags); + kernel.GlobalScheduler().AdjustSchedulingOnStatus(this, old_flags); } void Thread::SetCurrentPriority(u32 new_priority) { const u32 old_priority = std::exchange(current_priority, new_priority); - AdjustSchedulingOnPriority(old_priority); + kernel.GlobalScheduler().AdjustSchedulingOnPriority(this, old_priority); } ResultCode Thread::SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask) { @@ -443,111 +502,12 @@ ResultCode Thread::SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask) { processor_id = ideal_core; } } - AdjustSchedulingOnAffinity(old_affinity_mask, old_core); + kernel.GlobalScheduler().AdjustSchedulingOnAffinity(this, old_affinity_mask, old_core); } } return RESULT_SUCCESS; } -void Thread::AdjustSchedulingOnStatus(u32 old_flags) { - if (old_flags == scheduling_state) { - return; - } - - auto& scheduler = kernel.GlobalScheduler(); - if (static_cast(old_flags & static_cast(ThreadSchedMasks::LowMask)) == - ThreadSchedStatus::Runnable) { - // In this case the thread was running, now it's pausing/exitting - if (processor_id >= 0) { - scheduler.Unschedule(current_priority, static_cast(processor_id), this); - } - - for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { - if (core != static_cast(processor_id) && ((affinity_mask >> core) & 1) != 0) { - scheduler.Unsuggest(current_priority, core, this); - } - } - } else if (GetSchedulingStatus() == ThreadSchedStatus::Runnable) { - // The thread is now set to running from being stopped - if (processor_id >= 0) { - scheduler.Schedule(current_priority, static_cast(processor_id), this); - } - - for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { - if (core != static_cast(processor_id) && ((affinity_mask >> core) & 1) != 0) { - scheduler.Suggest(current_priority, core, this); - } - } - } - - scheduler.SetReselectionPending(); -} - -void Thread::AdjustSchedulingOnPriority(u32 old_priority) { - if (GetSchedulingStatus() != ThreadSchedStatus::Runnable) { - return; - } - auto& scheduler = kernel.GlobalScheduler(); - if (processor_id >= 0) { - scheduler.Unschedule(old_priority, static_cast(processor_id), this); - } - - for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { - if (core != static_cast(processor_id) && ((affinity_mask >> core) & 1) != 0) { - scheduler.Unsuggest(old_priority, core, this); - } - } - - // Add thread to the new priority queues. - Thread* current_thread = GetCurrentThread(); - - if (processor_id >= 0) { - if (current_thread == this) { - scheduler.SchedulePrepend(current_priority, static_cast(processor_id), this); - } else { - scheduler.Schedule(current_priority, static_cast(processor_id), this); - } - } - - for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { - if (core != static_cast(processor_id) && ((affinity_mask >> core) & 1) != 0) { - scheduler.Suggest(current_priority, core, this); - } - } - - scheduler.SetReselectionPending(); -} - -void Thread::AdjustSchedulingOnAffinity(u64 old_affinity_mask, s32 old_core) { - auto& scheduler = kernel.GlobalScheduler(); - if (GetSchedulingStatus() != ThreadSchedStatus::Runnable || - current_priority >= THREADPRIO_COUNT) { - return; - } - - for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { - if (((old_affinity_mask >> core) & 1) != 0) { - if (core == static_cast(old_core)) { - scheduler.Unschedule(current_priority, core, this); - } else { - scheduler.Unsuggest(current_priority, core, this); - } - } - } - - for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { - if (((affinity_mask >> core) & 1) != 0) { - if (core == static_cast(processor_id)) { - scheduler.Schedule(current_priority, core, this); - } else { - scheduler.Suggest(current_priority, core, this); - } - } - } - - scheduler.SetReselectionPending(); -} - //////////////////////////////////////////////////////////////////////////////////////////////////// /** diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 23fdef8a4..33d340b47 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -9,23 +9,42 @@ #include #include "common/common_types.h" +#include "common/spin_lock.h" #include "core/arm/arm_interface.h" #include "core/hle/kernel/object.h" #include "core/hle/kernel/synchronization_object.h" #include "core/hle/result.h" +namespace Common { +class Fiber; +} + +namespace Core { +class System; +} + namespace Kernel { +class GlobalScheduler; class KernelCore; class Process; class Scheduler; enum ThreadPriority : u32 { - THREADPRIO_HIGHEST = 0, ///< Highest thread priority - THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps - THREADPRIO_DEFAULT = 44, ///< Default thread priority for userland apps - THREADPRIO_LOWEST = 63, ///< Lowest thread priority - THREADPRIO_COUNT = 64, ///< Total number of possible thread priorities. + THREADPRIO_HIGHEST = 0, ///< Highest thread priority + THREADPRIO_MAX_CORE_MIGRATION = 2, ///< Highest priority for a core migration + THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps + THREADPRIO_DEFAULT = 44, ///< Default thread priority for userland apps + THREADPRIO_LOWEST = 63, ///< Lowest thread priority + THREADPRIO_COUNT = 64, ///< Total number of possible thread priorities. +}; + +enum ThreadType : u32 { + THREADTYPE_USER = 0x1, + THREADTYPE_KERNEL = 0x2, + THREADTYPE_HLE = 0x4, + THREADTYPE_IDLE = 0x8, + THREADTYPE_SUSPEND = 0x10, }; enum ThreadProcessorId : s32 { @@ -111,22 +130,43 @@ public: std::function thread, std::shared_ptr object, std::size_t index)>; + /** + * Creates and returns a new thread. The new thread is immediately scheduled + * @param system The instance of the whole system + * @param name The friendly name desired for the thread + * @param entry_point The address at which the thread should start execution + * @param priority The thread's priority + * @param arg User data to pass to the thread + * @param processor_id The ID(s) of the processors on which the thread is desired to be run + * @param stack_top The address of the thread's stack top + * @param owner_process The parent process for the thread, if null, it's a kernel thread + * @return A shared pointer to the newly created thread + */ + static ResultVal> Create(Core::System& system, ThreadType type_flags, std::string name, + VAddr entry_point, u32 priority, u64 arg, + s32 processor_id, VAddr stack_top, + Process* owner_process); + /** * Creates and returns a new thread. The new thread is immediately scheduled - * @param kernel The kernel instance this thread will be created under. + * @param system The instance of the whole system * @param name The friendly name desired for the thread * @param entry_point The address at which the thread should start execution * @param priority The thread's priority * @param arg User data to pass to the thread * @param processor_id The ID(s) of the processors on which the thread is desired to be run * @param stack_top The address of the thread's stack top - * @param owner_process The parent process for the thread + * @param owner_process The parent process for the thread, if null, it's a kernel thread + * @param thread_start_func The function where the host context will start. + * @param thread_start_parameter The parameter which will passed to host context on init * @return A shared pointer to the newly created thread */ - static ResultVal> Create(KernelCore& kernel, std::string name, + static ResultVal> Create(Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point, u32 priority, u64 arg, s32 processor_id, VAddr stack_top, - Process& owner_process); + Process* owner_process, + std::function&& thread_start_func, + void* thread_start_parameter); std::string GetName() const override { return name; @@ -192,7 +232,9 @@ public: } /// Resumes a thread from waiting - void ResumeFromWait(); + void /* deprecated */ ResumeFromWait(); + + void OnWakeUp(); /// Cancels a waiting operation that this thread may or may not be within. /// @@ -206,10 +248,10 @@ public: * Schedules an event to wake up the specified thread after the specified delay * @param nanoseconds The time this thread will be allowed to sleep for */ - void WakeAfterDelay(s64 nanoseconds); + void /* deprecated */ WakeAfterDelay(s64 nanoseconds); /// Cancel any outstanding wakeup events for this thread - void CancelWakeupTimer(); + void /* deprecated */ CancelWakeupTimer(); /** * Sets the result after the thread awakens (from svcWaitSynchronization) @@ -290,6 +332,12 @@ public: return context_64; } + bool IsHLEThread() const { + return (type & THREADTYPE_HLE) != 0; + } + + std::shared_ptr GetHostContext() const; + ThreadStatus GetStatus() const { return status; } @@ -467,16 +515,19 @@ public: } private: + friend class GlobalScheduler; + friend class Scheduler; + void SetSchedulingStatus(ThreadSchedStatus new_status); void SetCurrentPriority(u32 new_priority); ResultCode SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask); - void AdjustSchedulingOnStatus(u32 old_flags); - void AdjustSchedulingOnPriority(u32 old_priority); void AdjustSchedulingOnAffinity(u64 old_affinity_mask, s32 old_core); ThreadContext32 context_32{}; ThreadContext64 context_64{}; + Common::SpinLock context_guard{}; + std::shared_ptr host_context{}; u64 thread_id = 0; @@ -485,6 +536,8 @@ private: VAddr entry_point = 0; VAddr stack_top = 0; + ThreadType type; + /// Nominal thread priority, as set by the emulated application. /// The nominal priority is the thread priority without priority /// inheritance taken into account. diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp index 21b290468..0b8f0d993 100644 --- a/src/core/hle/kernel/time_manager.cpp +++ b/src/core/hle/kernel/time_manager.cpp @@ -19,7 +19,7 @@ TimeManager::TimeManager(Core::System& system) : system{system} { Handle proper_handle = static_cast(thread_handle); std::shared_ptr thread = this->system.Kernel().RetrieveThreadFromGlobalHandleTable(proper_handle); - thread->ResumeFromWait(); + thread->OnWakeUp(); }); } diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp index 1f2131ec8..cb35919e9 100644 --- a/src/core/hle/service/hid/controllers/debug_pad.cpp +++ b/src/core/hle/service/hid/controllers/debug_pad.cpp @@ -23,7 +23,7 @@ void Controller_DebugPad::OnRelease() {} void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) { - shared_memory.header.timestamp = core_timing.GetTicks(); + shared_memory.header.timestamp = core_timing.GetCPUTicks(); shared_memory.header.total_entry_count = 17; if (!IsControllerActivated()) { diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp index 6e990dd00..b7b7bfeae 100644 --- a/src/core/hle/service/hid/controllers/gesture.cpp +++ b/src/core/hle/service/hid/controllers/gesture.cpp @@ -19,7 +19,7 @@ void Controller_Gesture::OnRelease() {} void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) { - shared_memory.header.timestamp = core_timing.GetTicks(); + shared_memory.header.timestamp = core_timing.GetCPUTicks(); shared_memory.header.total_entry_count = 17; if (!IsControllerActivated()) { diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp index 9a8d354ba..feae89525 100644 --- a/src/core/hle/service/hid/controllers/keyboard.cpp +++ b/src/core/hle/service/hid/controllers/keyboard.cpp @@ -21,7 +21,7 @@ void Controller_Keyboard::OnRelease() {} void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) { - shared_memory.header.timestamp = core_timing.GetTicks(); + shared_memory.header.timestamp = core_timing.GetCPUTicks(); shared_memory.header.total_entry_count = 17; if (!IsControllerActivated()) { diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp index 93d88ea50..ac40989c5 100644 --- a/src/core/hle/service/hid/controllers/mouse.cpp +++ b/src/core/hle/service/hid/controllers/mouse.cpp @@ -19,7 +19,7 @@ void Controller_Mouse::OnRelease() {} void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) { - shared_memory.header.timestamp = core_timing.GetTicks(); + shared_memory.header.timestamp = core_timing.GetCPUTicks(); shared_memory.header.total_entry_count = 17; if (!IsControllerActivated()) { diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index 6fbee7efa..ef67ad690 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -328,7 +328,7 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* const auto& last_entry = main_controller->npad[main_controller->common.last_entry_index]; - main_controller->common.timestamp = core_timing.GetTicks(); + main_controller->common.timestamp = core_timing.GetCPUTicks(); main_controller->common.last_entry_index = (main_controller->common.last_entry_index + 1) % 17; diff --git a/src/core/hle/service/hid/controllers/stubbed.cpp b/src/core/hle/service/hid/controllers/stubbed.cpp index 9e527d176..e7483bfa2 100644 --- a/src/core/hle/service/hid/controllers/stubbed.cpp +++ b/src/core/hle/service/hid/controllers/stubbed.cpp @@ -23,7 +23,7 @@ void Controller_Stubbed::OnUpdate(const Core::Timing::CoreTiming& core_timing, u } CommonHeader header{}; - header.timestamp = core_timing.GetTicks(); + header.timestamp = core_timing.GetCPUTicks(); header.total_entry_count = 17; header.entry_count = 0; header.last_entry_index = 0; diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp index 1c6e55566..e326f8f5c 100644 --- a/src/core/hle/service/hid/controllers/touchscreen.cpp +++ b/src/core/hle/service/hid/controllers/touchscreen.cpp @@ -22,7 +22,7 @@ void Controller_Touchscreen::OnRelease() {} void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) { - shared_memory.header.timestamp = core_timing.GetTicks(); + shared_memory.header.timestamp = core_timing.GetCPUTicks(); shared_memory.header.total_entry_count = 17; if (!IsControllerActivated()) { @@ -49,7 +49,7 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin touch_entry.diameter_x = Settings::values.touchscreen.diameter_x; touch_entry.diameter_y = Settings::values.touchscreen.diameter_y; touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle; - const u64 tick = core_timing.GetTicks(); + const u64 tick = core_timing.GetCPUTicks(); touch_entry.delta_time = tick - last_touch; last_touch = tick; touch_entry.finger = Settings::values.touchscreen.finger; diff --git a/src/core/hle/service/hid/controllers/xpad.cpp b/src/core/hle/service/hid/controllers/xpad.cpp index 27511b27b..2503ef241 100644 --- a/src/core/hle/service/hid/controllers/xpad.cpp +++ b/src/core/hle/service/hid/controllers/xpad.cpp @@ -20,7 +20,7 @@ void Controller_XPad::OnRelease() {} void Controller_XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) { for (auto& xpad_entry : shared_memory.shared_memory_entries) { - xpad_entry.header.timestamp = core_timing.GetTicks(); + xpad_entry.header.timestamp = core_timing.GetCPUTicks(); xpad_entry.header.total_entry_count = 17; if (!IsControllerActivated()) { diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 57d5edea7..e9020e0dc 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -39,11 +39,9 @@ namespace Service::HID { // Updating period for each HID device. // TODO(ogniK): Find actual polling rate of hid -constexpr s64 pad_update_ticks = static_cast(Core::Hardware::BASE_CLOCK_RATE / 66); -[[maybe_unused]] constexpr s64 accelerometer_update_ticks = - static_cast(Core::Hardware::BASE_CLOCK_RATE / 100); -[[maybe_unused]] constexpr s64 gyroscope_update_ticks = - static_cast(Core::Hardware::BASE_CLOCK_RATE / 100); +constexpr s64 pad_update_ticks = static_cast(1000000000 / 66); +[[maybe_unused]] constexpr s64 accelerometer_update_ticks = static_cast(1000000000 / 100); +[[maybe_unused]] constexpr s64 gyroscope_update_ticks = static_cast(1000000000 / 100); constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; IAppletResource::IAppletResource(Core::System& system) @@ -78,8 +76,8 @@ IAppletResource::IAppletResource(Core::System& system) // Register update callbacks pad_update_event = - Core::Timing::CreateEvent("HID::UpdatePadCallback", [this](u64 userdata, s64 cycles_late) { - UpdateControllers(userdata, cycles_late); + Core::Timing::CreateEvent("HID::UpdatePadCallback", [this](u64 userdata, s64 ns_late) { + UpdateControllers(userdata, ns_late); }); // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?) @@ -109,7 +107,7 @@ void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) { rb.PushCopyObjects(shared_mem); } -void IAppletResource::UpdateControllers(u64 userdata, s64 cycles_late) { +void IAppletResource::UpdateControllers(u64 userdata, s64 ns_late) { auto& core_timing = system.CoreTiming(); const bool should_reload = Settings::values.is_device_reload_pending.exchange(false); @@ -120,7 +118,7 @@ void IAppletResource::UpdateControllers(u64 userdata, s64 cycles_late) { controller->OnUpdate(core_timing, shared_mem->GetPointer(), SHARED_MEMORY_SIZE); } - core_timing.ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event); + core_timing.ScheduleEvent(pad_update_ticks - ns_late, pad_update_event); } class IActiveVibrationDeviceList final : public ServiceFramework { diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp index 36ed6f7da..e82fd031b 100644 --- a/src/core/hle/service/hid/irs.cpp +++ b/src/core/hle/service/hid/irs.cpp @@ -98,7 +98,7 @@ void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 5}; rb.Push(RESULT_SUCCESS); - rb.PushRaw(system.CoreTiming().GetTicks()); + rb.PushRaw(system.CoreTiming().GetCPUTicks()); rb.PushRaw(0); } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp index 0d913334e..fba89e7a6 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp @@ -200,8 +200,7 @@ u32 nvhost_ctrl_gpu::GetGpuTime(const std::vector& input, std::vector& o IoctlGetGpuTime params{}; std::memcpy(¶ms, input.data(), input.size()); - const auto ns = Core::Timing::CyclesToNs(system.CoreTiming().GetTicks()); - params.gpu_time = static_cast(ns.count()); + params.gpu_time = static_cast(system.CoreTiming().GetGlobalTimeNs().count()); std::memcpy(output.data(), ¶ms, output.size()); return 0; } diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index 437bc5dee..aaf28995d 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp @@ -27,8 +27,8 @@ namespace Service::NVFlinger { -constexpr s64 frame_ticks = static_cast(Core::Hardware::BASE_CLOCK_RATE / 60); -constexpr s64 frame_ticks_30fps = static_cast(Core::Hardware::BASE_CLOCK_RATE / 30); +constexpr s64 frame_ticks = static_cast(1000000000 / 60); +constexpr s64 frame_ticks_30fps = static_cast(1000000000 / 30); NVFlinger::NVFlinger(Core::System& system) : system(system) { displays.emplace_back(0, "Default", system); @@ -39,11 +39,10 @@ NVFlinger::NVFlinger(Core::System& system) : system(system) { // Schedule the screen composition events composition_event = - Core::Timing::CreateEvent("ScreenComposition", [this](u64 userdata, s64 cycles_late) { + Core::Timing::CreateEvent("ScreenComposition", [this](u64 userdata, s64 ns_late) { Compose(); - const auto ticks = - Settings::values.force_30fps_mode ? frame_ticks_30fps : GetNextTicks(); - this->system.CoreTiming().ScheduleEvent(std::max(0LL, ticks - cycles_late), + const auto ticks = GetNextTicks(); + this->system.CoreTiming().ScheduleEvent(std::max(0LL, ticks - ns_late), composition_event); }); @@ -223,7 +222,7 @@ void NVFlinger::Compose() { s64 NVFlinger::GetNextTicks() const { constexpr s64 max_hertz = 120LL; - return (Core::Hardware::BASE_CLOCK_RATE * (1LL << swap_interval)) / max_hertz; + return (1000000000 * (1LL << swap_interval)) / max_hertz; } } // namespace Service::NVFlinger diff --git a/src/core/hle/service/time/standard_steady_clock_core.cpp b/src/core/hle/service/time/standard_steady_clock_core.cpp index 1575f0b49..59a272f4a 100644 --- a/src/core/hle/service/time/standard_steady_clock_core.cpp +++ b/src/core/hle/service/time/standard_steady_clock_core.cpp @@ -11,9 +11,8 @@ namespace Service::Time::Clock { TimeSpanType StandardSteadyClockCore::GetCurrentRawTimePoint(Core::System& system) { - const TimeSpanType ticks_time_span{TimeSpanType::FromTicks( - Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()), - Core::Hardware::CNTFREQ)}; + const TimeSpanType ticks_time_span{ + TimeSpanType::FromTicks(system.CoreTiming().GetClockTicks(), Core::Hardware::CNTFREQ)}; TimeSpanType raw_time_point{setup_value.nanoseconds + ticks_time_span.nanoseconds}; if (raw_time_point.nanoseconds < cached_raw_time_point.nanoseconds) { diff --git a/src/core/hle/service/time/tick_based_steady_clock_core.cpp b/src/core/hle/service/time/tick_based_steady_clock_core.cpp index 44d5bc651..8baaa2a6a 100644 --- a/src/core/hle/service/time/tick_based_steady_clock_core.cpp +++ b/src/core/hle/service/time/tick_based_steady_clock_core.cpp @@ -11,9 +11,8 @@ namespace Service::Time::Clock { SteadyClockTimePoint TickBasedSteadyClockCore::GetTimePoint(Core::System& system) { - const TimeSpanType ticks_time_span{TimeSpanType::FromTicks( - Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()), - Core::Hardware::CNTFREQ)}; + const TimeSpanType ticks_time_span{ + TimeSpanType::FromTicks(system.CoreTiming().GetClockTicks(), Core::Hardware::CNTFREQ)}; return {ticks_time_span.ToSeconds(), GetClockSourceId()}; } diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp index 67f1bbcf3..4cf58a61a 100644 --- a/src/core/hle/service/time/time.cpp +++ b/src/core/hle/service/time/time.cpp @@ -234,9 +234,8 @@ void Module::Interface::CalculateMonotonicSystemClockBaseTimePoint(Kernel::HLERe const auto current_time_point{steady_clock_core.GetCurrentTimePoint(system)}; if (current_time_point.clock_source_id == context.steady_time_point.clock_source_id) { - const auto ticks{Clock::TimeSpanType::FromTicks( - Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()), - Core::Hardware::CNTFREQ)}; + const auto ticks{Clock::TimeSpanType::FromTicks(system.CoreTiming().GetClockTicks(), + Core::Hardware::CNTFREQ)}; const s64 base_time_point{context.offset + current_time_point.time_point - ticks.ToSeconds()}; IPC::ResponseBuilder rb{ctx, (sizeof(s64) / 4) + 2}; diff --git a/src/core/hle/service/time/time_sharedmemory.cpp b/src/core/hle/service/time/time_sharedmemory.cpp index 999ec1e51..e0ae9f874 100644 --- a/src/core/hle/service/time/time_sharedmemory.cpp +++ b/src/core/hle/service/time/time_sharedmemory.cpp @@ -30,8 +30,7 @@ void SharedMemory::SetupStandardSteadyClock(Core::System& system, const Common::UUID& clock_source_id, Clock::TimeSpanType current_time_point) { const Clock::TimeSpanType ticks_time_span{Clock::TimeSpanType::FromTicks( - Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()), - Core::Hardware::CNTFREQ)}; + system.CoreTiming().GetClockTicks(), Core::Hardware::CNTFREQ)}; const Clock::SteadyClockContext context{ static_cast(current_time_point.nanoseconds - ticks_time_span.nanoseconds), clock_source_id}; diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 9d87045a0..66634596d 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -29,15 +29,12 @@ namespace Core::Memory { struct Memory::Impl { explicit Impl(Core::System& system_) : system{system_} {} - void SetCurrentPageTable(Kernel::Process& process) { + void SetCurrentPageTable(Kernel::Process& process, u32 core_id) { current_page_table = &process.PageTable().PageTableImpl(); const std::size_t address_space_width = process.PageTable().GetAddressSpaceWidth(); - system.ArmInterface(0).PageTableChanged(*current_page_table, address_space_width); - system.ArmInterface(1).PageTableChanged(*current_page_table, address_space_width); - system.ArmInterface(2).PageTableChanged(*current_page_table, address_space_width); - system.ArmInterface(3).PageTableChanged(*current_page_table, address_space_width); + system.ArmInterface(core_id).PageTableChanged(*current_page_table, address_space_width); } void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target) { @@ -689,8 +686,8 @@ struct Memory::Impl { Memory::Memory(Core::System& system) : impl{std::make_unique(system)} {} Memory::~Memory() = default; -void Memory::SetCurrentPageTable(Kernel::Process& process) { - impl->SetCurrentPageTable(process); +void Memory::SetCurrentPageTable(Kernel::Process& process, u32 core_id) { + impl->SetCurrentPageTable(process, core_id); } void Memory::MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target) { diff --git a/src/core/memory.h b/src/core/memory.h index 9292f3b0a..93f0c1d6c 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -64,7 +64,7 @@ public: * * @param process The process to use the page table of. */ - void SetCurrentPageTable(Kernel::Process& process); + void SetCurrentPageTable(Kernel::Process& process, u32 core_id); /** * Maps an allocated buffer onto a region of the emulated process address space. diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp index b139e8465..53d27859b 100644 --- a/src/core/memory/cheat_engine.cpp +++ b/src/core/memory/cheat_engine.cpp @@ -20,7 +20,7 @@ namespace Core::Memory { -constexpr s64 CHEAT_ENGINE_TICKS = static_cast(Core::Hardware::BASE_CLOCK_RATE / 12); +constexpr s64 CHEAT_ENGINE_TICKS = static_cast(1000000000 / 12); constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF; StandardVmCallbacks::StandardVmCallbacks(Core::System& system, const CheatProcessMetadata& metadata) @@ -190,7 +190,7 @@ CheatEngine::~CheatEngine() { void CheatEngine::Initialize() { event = Core::Timing::CreateEvent( "CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id), - [this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); }); + [this](u64 userdata, s64 ns_late) { FrameCallback(userdata, ns_late); }); core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS, event); metadata.process_id = system.CurrentProcess()->GetProcessID(); @@ -217,7 +217,7 @@ void CheatEngine::Reload(std::vector cheats) { MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70)); -void CheatEngine::FrameCallback(u64 userdata, s64 cycles_late) { +void CheatEngine::FrameCallback(u64 userdata, s64 ns_late) { if (is_pending_reload.exchange(false)) { vm.LoadProgram(cheats); } @@ -230,7 +230,7 @@ void CheatEngine::FrameCallback(u64 userdata, s64 cycles_late) { vm.Execute(metadata); - core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS - cycles_late, event); + core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS - ns_late, event); } } // namespace Core::Memory diff --git a/src/core/tools/freezer.cpp b/src/core/tools/freezer.cpp index b2c6c537e..8b0c50d11 100644 --- a/src/core/tools/freezer.cpp +++ b/src/core/tools/freezer.cpp @@ -14,7 +14,7 @@ namespace Tools { namespace { -constexpr s64 MEMORY_FREEZER_TICKS = static_cast(Core::Hardware::BASE_CLOCK_RATE / 60); +constexpr s64 MEMORY_FREEZER_TICKS = static_cast(1000000000 / 60); u64 MemoryReadWidth(Core::Memory::Memory& memory, u32 width, VAddr addr) { switch (width) { @@ -57,7 +57,7 @@ Freezer::Freezer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& m : core_timing{core_timing_}, memory{memory_} { event = Core::Timing::CreateEvent( "MemoryFreezer::FrameCallback", - [this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); }); + [this](u64 userdata, s64 ns_late) { FrameCallback(userdata, ns_late); }); core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS, event); } @@ -158,7 +158,7 @@ std::vector Freezer::GetEntries() const { return entries; } -void Freezer::FrameCallback(u64 userdata, s64 cycles_late) { +void Freezer::FrameCallback(u64 userdata, s64 ns_late) { if (!IsActive()) { LOG_DEBUG(Common_Memory, "Memory freezer has been deactivated, ending callback events."); return; @@ -173,7 +173,7 @@ void Freezer::FrameCallback(u64 userdata, s64 cycles_late) { MemoryWriteWidth(memory, entry.width, entry.address, entry.value); } - core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS - cycles_late, event); + core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS - ns_late, event); } void Freezer::FillEntryReads() { diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 3f750b51c..47ef30aa9 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -8,7 +8,6 @@ add_executable(tests core/arm/arm_test_common.cpp core/arm/arm_test_common.h core/core_timing.cpp - core/host_timing.cpp tests.cpp ) diff --git a/src/tests/core/core_timing.cpp b/src/tests/core/core_timing.cpp index ff2d11cc8..795f3da09 100644 --- a/src/tests/core/core_timing.cpp +++ b/src/tests/core/core_timing.cpp @@ -16,31 +16,30 @@ namespace { // Numbers are chosen randomly to make sure the correct one is given. -constexpr std::array CB_IDS{{42, 144, 93, 1026, UINT64_C(0xFFFF7FFFF7FFFF)}}; -constexpr int MAX_SLICE_LENGTH = 10000; // Copied from CoreTiming internals +static constexpr std::array CB_IDS{{42, 144, 93, 1026, UINT64_C(0xFFFF7FFFF7FFFF)}}; +static constexpr int MAX_SLICE_LENGTH = 10000; // Copied from CoreTiming internals +static constexpr std::array calls_order{{2, 0, 1, 4, 3}}; +static std::array delays{}; std::bitset callbacks_ran_flags; u64 expected_callback = 0; s64 lateness = 0; template -void CallbackTemplate(u64 userdata, s64 cycles_late) { +void HostCallbackTemplate(u64 userdata, s64 nanoseconds_late) { static_assert(IDX < CB_IDS.size(), "IDX out of range"); callbacks_ran_flags.set(IDX); REQUIRE(CB_IDS[IDX] == userdata); - REQUIRE(CB_IDS[IDX] == expected_callback); - REQUIRE(lateness == cycles_late); + REQUIRE(CB_IDS[IDX] == CB_IDS[calls_order[expected_callback]]); + delays[IDX] = nanoseconds_late; + ++expected_callback; } u64 callbacks_done = 0; -void EmptyCallback(u64 userdata, s64 cycles_late) { - ++callbacks_done; -} - struct ScopeInit final { ScopeInit() { - core_timing.Initialize(); + core_timing.Initialize([]() {}); } ~ScopeInit() { core_timing.Shutdown(); @@ -49,110 +48,97 @@ struct ScopeInit final { Core::Timing::CoreTiming core_timing; }; -void AdvanceAndCheck(Core::Timing::CoreTiming& core_timing, u32 idx, u32 context = 0, - int expected_lateness = 0, int cpu_downcount = 0) { - callbacks_ran_flags = 0; - expected_callback = CB_IDS[idx]; - lateness = expected_lateness; - - // Pretend we executed X cycles of instructions. - core_timing.SwitchContext(context); - core_timing.AddTicks(core_timing.GetDowncount() - cpu_downcount); - core_timing.Advance(); - core_timing.SwitchContext((context + 1) % 4); - - REQUIRE(decltype(callbacks_ran_flags)().set(idx) == callbacks_ran_flags); -} -} // Anonymous namespace - TEST_CASE("CoreTiming[BasicOrder]", "[core]") { ScopeInit guard; auto& core_timing = guard.core_timing; + std::vector> events{ + Core::Timing::CreateEvent("callbackA", HostCallbackTemplate<0>), + Core::Timing::CreateEvent("callbackB", HostCallbackTemplate<1>), + Core::Timing::CreateEvent("callbackC", HostCallbackTemplate<2>), + Core::Timing::CreateEvent("callbackD", HostCallbackTemplate<3>), + Core::Timing::CreateEvent("callbackE", HostCallbackTemplate<4>), + }; - std::shared_ptr cb_a = - Core::Timing::CreateEvent("callbackA", CallbackTemplate<0>); - std::shared_ptr cb_b = - Core::Timing::CreateEvent("callbackB", CallbackTemplate<1>); - std::shared_ptr cb_c = - Core::Timing::CreateEvent("callbackC", CallbackTemplate<2>); - std::shared_ptr cb_d = - Core::Timing::CreateEvent("callbackD", CallbackTemplate<3>); - std::shared_ptr cb_e = - Core::Timing::CreateEvent("callbackE", CallbackTemplate<4>); + expected_callback = 0; - // Enter slice 0 - core_timing.ResetRun(); + core_timing.SyncPause(true); - // D -> B -> C -> A -> E - core_timing.SwitchContext(0); - core_timing.ScheduleEvent(1000, cb_a, CB_IDS[0]); - REQUIRE(1000 == core_timing.GetDowncount()); - core_timing.ScheduleEvent(500, cb_b, CB_IDS[1]); - REQUIRE(500 == core_timing.GetDowncount()); - core_timing.ScheduleEvent(800, cb_c, CB_IDS[2]); - REQUIRE(500 == core_timing.GetDowncount()); - core_timing.ScheduleEvent(100, cb_d, CB_IDS[3]); - REQUIRE(100 == core_timing.GetDowncount()); - core_timing.ScheduleEvent(1200, cb_e, CB_IDS[4]); - REQUIRE(100 == core_timing.GetDowncount()); + u64 one_micro = 1000U; + for (std::size_t i = 0; i < events.size(); i++) { + u64 order = calls_order[i]; + core_timing.ScheduleEvent(i * one_micro + 100U, events[order], CB_IDS[order]); + } + /// test pause + REQUIRE(callbacks_ran_flags.none()); - AdvanceAndCheck(core_timing, 3, 0); - AdvanceAndCheck(core_timing, 1, 1); - AdvanceAndCheck(core_timing, 2, 2); - AdvanceAndCheck(core_timing, 0, 3); - AdvanceAndCheck(core_timing, 4, 0); + core_timing.Pause(false); // No need to sync + + while (core_timing.HasPendingEvents()) + ; + + REQUIRE(callbacks_ran_flags.all()); + + for (std::size_t i = 0; i < delays.size(); i++) { + const double delay = static_cast(delays[i]); + const double micro = delay / 1000.0f; + const double mili = micro / 1000.0f; + printf("HostTimer Pausing Delay[%zu]: %.3f %.6f\n", i, micro, mili); + } } -TEST_CASE("CoreTiming[FairSharing]", "[core]") { +#pragma optimize("", off) +u64 TestTimerSpeed(Core::Timing::CoreTiming& core_timing) { + u64 start = core_timing.GetGlobalTimeNs().count(); + u64 placebo = 0; + for (std::size_t i = 0; i < 1000; i++) { + placebo += core_timing.GetGlobalTimeNs().count(); + } + u64 end = core_timing.GetGlobalTimeNs().count(); + return (end - start); +} +#pragma optimize("", on) +TEST_CASE("CoreTiming[BasicOrderNoPausing]", "[core]") { ScopeInit guard; auto& core_timing = guard.core_timing; + std::vector> events{ + Core::Timing::CreateEvent("callbackA", HostCallbackTemplate<0>), + Core::Timing::CreateEvent("callbackB", HostCallbackTemplate<1>), + Core::Timing::CreateEvent("callbackC", HostCallbackTemplate<2>), + Core::Timing::CreateEvent("callbackD", HostCallbackTemplate<3>), + Core::Timing::CreateEvent("callbackE", HostCallbackTemplate<4>), + }; - std::shared_ptr empty_callback = - Core::Timing::CreateEvent("empty_callback", EmptyCallback); + core_timing.SyncPause(true); + core_timing.SyncPause(false); - callbacks_done = 0; - u64 MAX_CALLBACKS = 10; - for (std::size_t i = 0; i < 10; i++) { - core_timing.ScheduleEvent(i * 3333U, empty_callback, 0); + expected_callback = 0; + + u64 start = core_timing.GetGlobalTimeNs().count(); + u64 one_micro = 1000U; + for (std::size_t i = 0; i < events.size(); i++) { + u64 order = calls_order[i]; + core_timing.ScheduleEvent(i * one_micro + 100U, events[order], CB_IDS[order]); + } + u64 end = core_timing.GetGlobalTimeNs().count(); + const double scheduling_time = static_cast(end - start); + const double timer_time = static_cast(TestTimerSpeed(core_timing)); + + while (core_timing.HasPendingEvents()) + ; + + REQUIRE(callbacks_ran_flags.all()); + + for (std::size_t i = 0; i < delays.size(); i++) { + const double delay = static_cast(delays[i]); + const double micro = delay / 1000.0f; + const double mili = micro / 1000.0f; + printf("HostTimer No Pausing Delay[%zu]: %.3f %.6f\n", i, micro, mili); } - const s64 advances = MAX_SLICE_LENGTH / 10; - core_timing.ResetRun(); - u64 current_time = core_timing.GetTicks(); - bool keep_running{}; - do { - keep_running = false; - for (u32 active_core = 0; active_core < 4; ++active_core) { - core_timing.SwitchContext(active_core); - if (core_timing.CanCurrentContextRun()) { - core_timing.AddTicks(std::min(advances, core_timing.GetDowncount())); - core_timing.Advance(); - } - keep_running |= core_timing.CanCurrentContextRun(); - } - } while (keep_running); - u64 current_time_2 = core_timing.GetTicks(); - - REQUIRE(MAX_CALLBACKS == callbacks_done); - REQUIRE(current_time_2 == current_time + MAX_SLICE_LENGTH * 4); -} - -TEST_CASE("Core::Timing[PredictableLateness]", "[core]") { - ScopeInit guard; - auto& core_timing = guard.core_timing; - - std::shared_ptr cb_a = - Core::Timing::CreateEvent("callbackA", CallbackTemplate<0>); - std::shared_ptr cb_b = - Core::Timing::CreateEvent("callbackB", CallbackTemplate<1>); - - // Enter slice 0 - core_timing.ResetRun(); - - core_timing.ScheduleEvent(100, cb_a, CB_IDS[0]); - core_timing.ScheduleEvent(200, cb_b, CB_IDS[1]); - - AdvanceAndCheck(core_timing, 0, 0, 10, -10); // (100 - 10) - AdvanceAndCheck(core_timing, 1, 1, 50, -50); + const double micro = scheduling_time / 1000.0f; + const double mili = micro / 1000.0f; + printf("HostTimer No Pausing Scheduling Time: %.3f %.6f\n", micro, mili); + printf("HostTimer No Pausing Timer Time: %.3f %.6f\n", timer_time / 1000.f, + timer_time / 1000000.f); } diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 8eb017f65..482e49711 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include + #include "common/assert.h" #include "common/microprofile.h" #include "core/core.h" @@ -154,8 +156,7 @@ u64 GPU::GetTicks() const { constexpr u64 gpu_ticks_num = 384; constexpr u64 gpu_ticks_den = 625; - const u64 cpu_ticks = system.CoreTiming().GetTicks(); - u64 nanoseconds = Core::Timing::CyclesToNs(cpu_ticks).count(); + u64 nanoseconds = system.CoreTiming().GetGlobalTimeNs().count(); if (Settings::values.use_fast_gpu_time) { nanoseconds /= 256; } diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index bfeb16458..9ceb6c8d7 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -52,6 +52,8 @@ void EmuThread::run() { emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); + Core::System::GetInstance().RegisterHostThread(); + Core::System::GetInstance().Renderer().Rasterizer().LoadDiskResources( stop_run, [this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) { emit LoadProgress(stage, value, total); @@ -65,28 +67,30 @@ void EmuThread::run() { bool was_active = false; while (!stop_run) { if (running) { - if (!was_active) + if (was_active) { emit DebugModeLeft(); + } - Core::System::ResultStatus result = Core::System::GetInstance().RunLoop(); + running_guard = true; + Core::System::ResultStatus result = Core::System::GetInstance().Run(); if (result != Core::System::ResultStatus::Success) { + running_guard = false; this->SetRunning(false); emit ErrorThrown(result, Core::System::GetInstance().GetStatusDetails()); } + running_wait.Wait(); + result = Core::System::GetInstance().Pause(); + if (result != Core::System::ResultStatus::Success) { + running_guard = false; + this->SetRunning(false); + emit ErrorThrown(result, Core::System::GetInstance().GetStatusDetails()); + } + running_guard = false; - was_active = running || exec_step; - if (!was_active && !stop_run) - emit DebugModeEntered(); - } else if (exec_step) { - if (!was_active) - emit DebugModeLeft(); - - exec_step = false; - Core::System::GetInstance().SingleStep(); + was_active = true; emit DebugModeEntered(); - yieldCurrentThread(); - - was_active = false; + } else if (exec_step) { + UNIMPLEMENTED(); } else { std::unique_lock lock{running_mutex}; running_cv.wait(lock, [this] { return IsRunning() || exec_step || stop_run; }); diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index 3626604ca..768568b3e 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h @@ -59,6 +59,11 @@ public: this->running = running; lock.unlock(); running_cv.notify_all(); + if (!running) { + running_wait.Set(); + /// Wait until effectively paused + while (running_guard); + } } /** @@ -84,6 +89,8 @@ private: std::atomic_bool stop_run{false}; std::mutex running_mutex; std::condition_variable running_cv; + Common::Event running_wait{}; + std::atomic_bool running_guard{false}; signals: /** diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp index c1ea25fb8..765908c5a 100644 --- a/src/yuzu/debugger/wait_tree.cpp +++ b/src/yuzu/debugger/wait_tree.cpp @@ -59,8 +59,10 @@ std::vector> WaitTreeItem::MakeThreadItemList() std::size_t row = 0; auto add_threads = [&](const std::vector>& threads) { for (std::size_t i = 0; i < threads.size(); ++i) { - item_list.push_back(std::make_unique(*threads[i])); - item_list.back()->row = row; + if (!threads[i]->IsHLEThread()) { + item_list.push_back(std::make_unique(*threads[i])); + item_list.back()->row = row; + } ++row; } }; diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 4d2ea7e9e..1e5377840 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -237,7 +237,7 @@ int main(int argc, char** argv) { std::thread render_thread([&emu_window] { emu_window->Present(); }); while (emu_window->IsOpen()) { - system.RunLoop(); + //system.RunLoop(); } render_thread.join(); diff --git a/src/yuzu_tester/yuzu.cpp b/src/yuzu_tester/yuzu.cpp index 676e70ebd..1a45506d4 100644 --- a/src/yuzu_tester/yuzu.cpp +++ b/src/yuzu_tester/yuzu.cpp @@ -256,7 +256,7 @@ int main(int argc, char** argv) { system.Renderer().Rasterizer().LoadDiskResources(); while (!finished) { - system.RunLoop(); + //system.RunLoop(); } detached_tasks.WaitForAllTasks(); From 9e4b9f1afdb93bab7640e9d04cc2e28b9c48a288 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Tue, 25 Feb 2020 10:30:23 -0400 Subject: [PATCH 138/252] YuzuCMD/Tester: Correct execution --- src/yuzu_cmd/yuzu.cpp | 7 ++++--- src/yuzu_tester/yuzu.cpp | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 1e5377840..38ffdfbd3 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -236,9 +236,10 @@ int main(int argc, char** argv) { system.Renderer().Rasterizer().LoadDiskResources(); std::thread render_thread([&emu_window] { emu_window->Present(); }); - while (emu_window->IsOpen()) { - //system.RunLoop(); - } + system.Run(); + while (emu_window->IsOpen()) + ; + system.Pause(); render_thread.join(); system.Shutdown(); diff --git a/src/yuzu_tester/yuzu.cpp b/src/yuzu_tester/yuzu.cpp index 1a45506d4..d62686dd2 100644 --- a/src/yuzu_tester/yuzu.cpp +++ b/src/yuzu_tester/yuzu.cpp @@ -255,9 +255,10 @@ int main(int argc, char** argv) { system.GPU().Start(); system.Renderer().Rasterizer().LoadDiskResources(); - while (!finished) { - //system.RunLoop(); - } + system.Run(); + while (!finished) + ; + system.Pause(); detached_tasks.WaitForAllTasks(); return return_value; From a5c58a25ef3ed7975c8466933ca38e03c999b027 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Tue, 25 Feb 2020 10:43:34 -0400 Subject: [PATCH 139/252] CPU_Manager: remove debugging code. --- src/core/cpu_manager.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index 494850992..ff2fe8ead 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp @@ -78,10 +78,10 @@ void CpuManager::RunGuestThread() { } while (true) { auto& physical_core = kernel.CurrentPhysicalCore(); - LOG_CRITICAL(Core_ARM, "Running Guest Thread"); - physical_core.Idle(); - LOG_CRITICAL(Core_ARM, "Leaving Guest Thread"); - // physical_core.Run(); + if (!physical_core.IsInterrupted()) { + physical_core.Idle(); + //physical_core.Run(); + } auto& scheduler = physical_core.Scheduler(); scheduler.TryDoContextSwitch(); } @@ -91,7 +91,6 @@ void CpuManager::RunIdleThread() { auto& kernel = system.Kernel(); while (true) { auto& physical_core = kernel.CurrentPhysicalCore(); - LOG_CRITICAL(Core_ARM, "Running Idle Thread"); physical_core.Idle(); auto& scheduler = physical_core.Scheduler(); scheduler.TryDoContextSwitch(); @@ -99,7 +98,6 @@ void CpuManager::RunIdleThread() { } void CpuManager::RunSuspendThread() { - LOG_CRITICAL(Core_ARM, "Suspending Thread Entered"); auto& kernel = system.Kernel(); { auto& sched = kernel.CurrentScheduler(); @@ -109,9 +107,7 @@ void CpuManager::RunSuspendThread() { auto core = kernel.GetCurrentHostThreadID(); auto& scheduler = kernel.CurrentScheduler(); Kernel::Thread* current_thread = scheduler.GetCurrentThread(); - LOG_CRITICAL(Core_ARM, "Suspending Core {}", core); Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[core].host_context); - LOG_CRITICAL(Core_ARM, "Unsuspending Core {}", core); ASSERT(scheduler.ContextSwitchPending()); ASSERT(core == kernel.GetCurrentHostThreadID()); scheduler.TryDoContextSwitch(); From dc580582034fb5937aa53176fdaa4bd0fc4acce8 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Tue, 25 Feb 2020 11:12:46 -0400 Subject: [PATCH 140/252] General: Setup yuzu threads' microprofile, naming and registry. --- src/core/core_timing.cpp | 5 +++-- src/core/cpu_manager.cpp | 5 ++++- src/video_core/gpu_thread.cpp | 6 +++++- src/yuzu/bootmanager.cpp | 4 +++- src/yuzu/main.cpp | 2 ++ src/yuzu_cmd/yuzu.cpp | 6 ++++-- src/yuzu_tester/yuzu.cpp | 6 ++++-- 7 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index a3ce69790..cc32a853b 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -2,14 +2,14 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/core_timing.h" - #include #include #include #include #include "common/assert.h" +#include "common/microprofile.h" +#include "core/core_timing.h" #include "core/core_timing_util.h" namespace Core::Timing { @@ -44,6 +44,7 @@ CoreTiming::~CoreTiming() = default; void CoreTiming::ThreadEntry(CoreTiming& instance) { std::string name = "yuzu:HostTiming"; + MicroProfileOnThreadCreate(name.c_str()); Common::SetCurrentThreadName(name.c_str()); instance.on_thread_init(); instance.ThreadLoop(); diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index ff2fe8ead..9b9337131 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include "common/fiber.h" +#include "common/microprofile.h" #include "common/thread.h" #include "core/arm/exclusive_monitor.h" #include "core/core.h" @@ -36,6 +37,7 @@ void CpuManager::Shutdown() { Pause(false); for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { core_data[core].host_thread->join(); + core_data[core].host_thread.reset(); } } @@ -80,7 +82,7 @@ void CpuManager::RunGuestThread() { auto& physical_core = kernel.CurrentPhysicalCore(); if (!physical_core.IsInterrupted()) { physical_core.Idle(); - //physical_core.Run(); + // physical_core.Run(); } auto& scheduler = physical_core.Scheduler(); scheduler.TryDoContextSwitch(); @@ -159,6 +161,7 @@ void CpuManager::RunThread(std::size_t core) { /// Initialization system.RegisterCoreThread(core); std::string name = "yuzu:CoreHostThread_" + std::to_string(core); + MicroProfileOnThreadCreate(name.c_str()); Common::SetCurrentThreadName(name.c_str()); auto& data = core_data[core]; data.enter_barrier = std::make_unique(); diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp index c3bb4fe06..323185bfc 100644 --- a/src/video_core/gpu_thread.cpp +++ b/src/video_core/gpu_thread.cpp @@ -4,6 +4,7 @@ #include "common/assert.h" #include "common/microprofile.h" +#include "common/thread.h" #include "core/core.h" #include "core/frontend/emu_window.h" #include "core/settings.h" @@ -18,7 +19,10 @@ namespace VideoCommon::GPUThread { static void RunThread(Core::System& system, VideoCore::RendererBase& renderer, Core::Frontend::GraphicsContext& context, Tegra::DmaPusher& dma_pusher, SynchState& state) { - MicroProfileOnThreadCreate("GpuThread"); + std::string name = "yuzu:GPU"; + MicroProfileOnThreadCreate(name.c_str()); + Common::SetCurrentThreadName(name.c_str()); + system.RegisterHostThread(); // Wait for first GPU command before acquiring the window context while (state.queue.Empty()) diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 9ceb6c8d7..468dde782 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -44,7 +44,9 @@ EmuThread::EmuThread() = default; EmuThread::~EmuThread() = default; void EmuThread::run() { - MicroProfileOnThreadCreate("EmuThread"); + std::string name = "yuzu:EmuControlThread"; + MicroProfileOnThreadCreate(name.c_str()); + Common::SetCurrentThreadName(name.c_str()); // Main process has been loaded. Make the context current to this thread and begin GPU and CPU // execution. diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index ba69139e5..de0c7fe8c 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -925,6 +925,8 @@ bool GMainWindow::LoadROM(const QString& filename) { nullptr, // E-Commerce }); + system.RegisterHostThread(); + const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())}; const auto drd_callout = diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 38ffdfbd3..e6c6a839d 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include #include #include #include @@ -237,8 +238,9 @@ int main(int argc, char** argv) { std::thread render_thread([&emu_window] { emu_window->Present(); }); system.Run(); - while (emu_window->IsOpen()) - ; + while (emu_window->IsOpen()) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } system.Pause(); render_thread.join(); diff --git a/src/yuzu_tester/yuzu.cpp b/src/yuzu_tester/yuzu.cpp index d62686dd2..083667baf 100644 --- a/src/yuzu_tester/yuzu.cpp +++ b/src/yuzu_tester/yuzu.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include #include #include #include @@ -256,8 +257,9 @@ int main(int argc, char** argv) { system.Renderer().Rasterizer().LoadDiskResources(); system.Run(); - while (!finished) - ; + while (!finished) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } system.Pause(); detached_tasks.WaitForAllTasks(); From 6bf137a0e81fa3fae030687f10ac2c1e12d824ff Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Tue, 25 Feb 2020 11:51:33 -0400 Subject: [PATCH 141/252] AudioCore: Use nanoseconds instead of cycles for buffer time. --- src/audio_core/stream.cpp | 10 +++++----- src/audio_core/stream.h | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp index ca7cfb030..6d5539b6b 100644 --- a/src/audio_core/stream.cpp +++ b/src/audio_core/stream.cpp @@ -59,11 +59,11 @@ Stream::State Stream::GetState() const { return state; } -s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const { +s64 Stream::GetBufferReleaseNS(const Buffer& buffer) const { const std::size_t num_samples{buffer.GetSamples().size() / GetNumChannels()}; - const auto us = - std::chrono::microseconds((static_cast(num_samples) * 1000000) / sample_rate); - return Core::Timing::usToCycles(us); + const auto ns = + std::chrono::nanoseconds((static_cast(num_samples) * 1000000000ULL) / sample_rate); + return ns.count(); } static void VolumeAdjustSamples(std::vector& samples, float game_volume) { @@ -105,7 +105,7 @@ void Stream::PlayNextBuffer() { sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples()); - core_timing.ScheduleEvent(GetBufferReleaseCycles(*active_buffer), release_event, {}); + core_timing.ScheduleEvent(GetBufferReleaseNS(*active_buffer), release_event, {}); } void Stream::ReleaseActiveBuffer() { diff --git a/src/audio_core/stream.h b/src/audio_core/stream.h index 1708a4d98..0663ce435 100644 --- a/src/audio_core/stream.h +++ b/src/audio_core/stream.h @@ -96,7 +96,7 @@ private: void ReleaseActiveBuffer(); /// Gets the number of core cycles when the specified buffer will be released - s64 GetBufferReleaseCycles(const Buffer& buffer) const; + s64 GetBufferReleaseNS(const Buffer& buffer) const; u32 sample_rate; ///< Sample rate of the stream Format format; ///< Format of the stream From 18dcb0934217628711c5b1d22fd6d7635e683e3f Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Tue, 25 Feb 2020 12:28:55 -0400 Subject: [PATCH 142/252] HostTiming: Pause the hardware clock on pause. --- src/common/wall_clock.cpp | 4 ++++ src/common/wall_clock.h | 2 ++ src/common/x64/native_clock.cpp | 7 +++++++ src/common/x64/native_clock.h | 2 ++ src/core/core.cpp | 2 +- src/core/core_timing.cpp | 6 ++++++ src/core/core_timing.h | 1 + 7 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp index d4d35f4e7..a46db6bbf 100644 --- a/src/common/wall_clock.cpp +++ b/src/common/wall_clock.cpp @@ -53,6 +53,10 @@ public: return Common::Divide128On32(temporary, 1000000000).first; } + void Pause(bool is_paused) override { + // Do nothing in this clock type. + } + private: base_time_point start_time; }; diff --git a/src/common/wall_clock.h b/src/common/wall_clock.h index ed284cf50..367d72134 100644 --- a/src/common/wall_clock.h +++ b/src/common/wall_clock.h @@ -28,6 +28,8 @@ public: /// Returns current wall time in emulated cpu cycles virtual u64 GetCPUCycles() = 0; + virtual void Pause(bool is_paused) = 0; + /// Tells if the wall clock, uses the host CPU's hardware clock bool IsNative() const { return is_native; diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp index 26d4d0ba6..926f92ff8 100644 --- a/src/common/x64/native_clock.cpp +++ b/src/common/x64/native_clock.cpp @@ -65,6 +65,13 @@ u64 NativeClock::GetRTSC() { return accumulated_ticks; } +void NativeClock::Pause(bool is_paused) { + if (!is_paused) { + _mm_mfence(); + last_measure = __rdtsc(); + } +} + std::chrono::nanoseconds NativeClock::GetTimeNS() { const u64 rtsc_value = GetRTSC(); return std::chrono::nanoseconds{MultiplyAndDivide64(rtsc_value, 1000000000, rtsc_frequency)}; diff --git a/src/common/x64/native_clock.h b/src/common/x64/native_clock.h index b58cf9f5a..3851f8fc2 100644 --- a/src/common/x64/native_clock.h +++ b/src/common/x64/native_clock.h @@ -26,6 +26,8 @@ public: u64 GetCPUCycles() override; + void Pause(bool is_paused) override; + private: u64 GetRTSC(); diff --git a/src/core/core.cpp b/src/core/core.cpp index e8936b09d..1d6179a80 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -137,8 +137,8 @@ struct System::Impl { ResultStatus Pause() { status = ResultStatus::Success; - kernel.Suspend(true); core_timing.SyncPause(true); + kernel.Suspend(true); cpu_manager.Pause(true); return status; diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index cc32a853b..5a7abcfca 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -77,6 +77,9 @@ void CoreTiming::SyncPause(bool is_paused) { return; } Pause(is_paused); + if (!is_paused) { + pause_event.Set(); + } event.Set(); while (paused_set != is_paused) ; @@ -197,6 +200,9 @@ void CoreTiming::ThreadLoop() { wait_set = false; } paused_set = true; + clock->Pause(true); + pause_event.Wait(); + clock->Pause(false); } } diff --git a/src/core/core_timing.h b/src/core/core_timing.h index 707c8ef0c..c70b605c8 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h @@ -136,6 +136,7 @@ private: std::shared_ptr ev_lost; Common::Event event{}; + Common::Event pause_event{}; Common::SpinLock basic_lock{}; Common::SpinLock advance_lock{}; std::unique_ptr timer_thread; From 49ba56399563a87f29b4d89eb04b1178a571eb61 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Tue, 25 Feb 2020 12:40:33 -0400 Subject: [PATCH 143/252] SVC: Correct CreateThread, StartThread, ExitThread, SleepThread. --- src/core/hle/kernel/svc.cpp | 18 +++++----------- src/core/hle/kernel/thread.cpp | 38 ++++++++++++++++------------------ src/core/hle/kernel/thread.h | 12 +++++++---- 3 files changed, 31 insertions(+), 37 deletions(-) diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index d7f0dcabd..dfb032b4b 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -1464,13 +1464,7 @@ static ResultCode StartThread(Core::System& system, Handle thread_handle) { ASSERT(thread->GetStatus() == ThreadStatus::Dormant); - thread->ResumeFromWait(); - - if (thread->GetStatus() == ThreadStatus::Ready) { - system.PrepareReschedule(thread->GetProcessorID()); - } - - return RESULT_SUCCESS; + return thread->Start(); } /// Called when a thread exits @@ -1478,9 +1472,8 @@ static void ExitThread(Core::System& system) { LOG_DEBUG(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC()); auto* const current_thread = system.CurrentScheduler().GetCurrentThread(); - current_thread->Stop(); system.GlobalScheduler().RemoveThread(SharedFrom(current_thread)); - system.PrepareReschedule(); + current_thread->Stop(); } /// Sleep the current thread @@ -1500,13 +1493,13 @@ static void SleepThread(Core::System& system, s64 nanoseconds) { if (nanoseconds <= 0) { switch (static_cast(nanoseconds)) { case SleepType::YieldWithoutLoadBalancing: - is_redundant = current_thread->YieldSimple(); + current_thread->YieldSimple(); break; case SleepType::YieldWithLoadBalancing: - is_redundant = current_thread->YieldAndBalanceLoad(); + current_thread->YieldAndBalanceLoad(); break; case SleepType::YieldAndWaitForLoadBalancing: - is_redundant = current_thread->YieldAndWaitForLoadBalancing(); + current_thread->YieldAndWaitForLoadBalancing(); break; default: UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds); @@ -1514,7 +1507,6 @@ static void SleepThread(Core::System& system, s64 nanoseconds) { } else { current_thread->Sleep(nanoseconds); } - system.PrepareReschedule(current_thread->GetProcessorID()); } /// Wait process wide key atomic diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 8cb3593db..d9e610272 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -56,12 +56,6 @@ void Thread::Stop() { SetStatus(ThreadStatus::Dead); Signal(); - // Clean up any dangling references in objects that this thread was waiting for - for (auto& wait_object : wait_objects) { - wait_object->RemoveWaitingThread(SharedFrom(this)); - } - wait_objects.clear(); - owner_process->UnregisterThread(this); // Mark the TLS slot in the thread's page as free. @@ -138,6 +132,12 @@ void Thread::OnWakeUp() { SetStatus(ThreadStatus::Ready); } +ResultCode Thread::Start() { + SchedulerLock lock(kernel); + SetStatus(ThreadStatus::Ready); + return RESULT_SUCCESS; +} + void Thread::CancelWait() { if (GetSchedulingStatus() != ThreadSchedStatus::Paused) { is_sync_cancelled = true; @@ -188,7 +188,7 @@ ResultVal> Thread::Create(Core::System& system, ThreadTy void* thread_start_parameter) { auto& kernel = system.Kernel(); // Check if priority is in ranged. Lowest priority -> highest priority id. - if (priority > THREADPRIO_LOWEST && (type_flags & THREADTYPE_IDLE == 0)) { + if (priority > THREADPRIO_LOWEST && ((type_flags & THREADTYPE_IDLE) == 0)) { LOG_ERROR(Kernel_SVC, "Invalid thread priority: {}", priority); return ERR_INVALID_THREAD_PRIORITY; } @@ -416,7 +416,7 @@ void Thread::SetActivity(ThreadActivity value) { } } -void Thread::Sleep(s64 nanoseconds) { +ResultCode Thread::Sleep(s64 nanoseconds) { Handle event_handle{}; { SchedulerLockAndSleep lock(kernel, event_handle, this, nanoseconds); @@ -427,33 +427,31 @@ void Thread::Sleep(s64 nanoseconds) { auto& time_manager = kernel.TimeManager(); time_manager.UnscheduleTimeEvent(event_handle); } + return RESULT_SUCCESS; } -bool Thread::YieldSimple() { - bool result{}; +ResultCode Thread::YieldSimple() { { SchedulerLock lock(kernel); - result = kernel.GlobalScheduler().YieldThread(this); + kernel.GlobalScheduler().YieldThread(this); } - return result; + return RESULT_SUCCESS; } -bool Thread::YieldAndBalanceLoad() { - bool result{}; +ResultCode Thread::YieldAndBalanceLoad() { { SchedulerLock lock(kernel); - result = kernel.GlobalScheduler().YieldThreadAndBalanceLoad(this); + kernel.GlobalScheduler().YieldThreadAndBalanceLoad(this); } - return result; + return RESULT_SUCCESS; } -bool Thread::YieldAndWaitForLoadBalancing() { - bool result{}; +ResultCode Thread::YieldAndWaitForLoadBalancing() { { SchedulerLock lock(kernel); - result = kernel.GlobalScheduler().YieldThreadAndWaitForLoadBalancing(this); + kernel.GlobalScheduler().YieldThreadAndWaitForLoadBalancing(this); } - return result; + return RESULT_SUCCESS; } void Thread::SetSchedulingStatus(ThreadSchedStatus new_status) { diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 33d340b47..78a4357b0 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -236,6 +236,8 @@ public: void OnWakeUp(); + ResultCode Start(); + /// Cancels a waiting operation that this thread may or may not be within. /// /// When the thread is within a waiting state, this will set the thread's @@ -470,16 +472,16 @@ public: void SetActivity(ThreadActivity value); /// Sleeps this thread for the given amount of nanoseconds. - void Sleep(s64 nanoseconds); + ResultCode Sleep(s64 nanoseconds); /// Yields this thread without rebalancing loads. - bool YieldSimple(); + ResultCode YieldSimple(); /// Yields this thread and does a load rebalancing. - bool YieldAndBalanceLoad(); + ResultCode YieldAndBalanceLoad(); /// Yields this thread and if the core is left idle, loads are rebalanced - bool YieldAndWaitForLoadBalancing(); + ResultCode YieldAndWaitForLoadBalancing(); void IncrementYieldCount() { yield_count++; @@ -603,6 +605,8 @@ private: bool is_running = false; bool is_sync_cancelled = false; + bool will_be_terminated{}; + std::string name; }; From 589f9cf108d306e8265ff4856b522cd32fbc121f Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Tue, 25 Feb 2020 13:22:11 -0400 Subject: [PATCH 144/252] SVC: Correct GetThreadPriority, SetThreadPriority, GetThreadCoreMask, SetThreadCoreMask, GetCurrentProcessorNumber --- src/core/core.cpp | 8 ++++++++ src/core/core.h | 7 +++++++ src/core/hle/kernel/svc.cpp | 17 ++++++++--------- src/core/hle/kernel/thread.cpp | 6 ++---- src/core/hle/kernel/thread.h | 3 +-- 5 files changed, 26 insertions(+), 15 deletions(-) diff --git a/src/core/core.cpp b/src/core/core.cpp index 1d6179a80..5d4ecdce5 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -467,6 +467,14 @@ const Kernel::Scheduler& System::CurrentScheduler() const { return impl->CurrentPhysicalCore().Scheduler(); } +Kernel::PhysicalCore& System::CurrentPhysicalCore() { + return impl->CurrentPhysicalCore(); +} + +const Kernel::PhysicalCore& System::CurrentPhysicalCore() const { + return impl->CurrentPhysicalCore(); +} + Kernel::Scheduler& System::Scheduler(std::size_t core_index) { return impl->GetPhysicalCore(core_index).Scheduler(); } diff --git a/src/core/core.h b/src/core/core.h index 7f170fc54..9a0dd1075 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -27,6 +27,7 @@ class VfsFilesystem; namespace Kernel { class GlobalScheduler; class KernelCore; +class PhysicalCore; class Process; class Scheduler; } // namespace Kernel @@ -211,6 +212,12 @@ public: /// Gets the scheduler for the CPU core that is currently running const Kernel::Scheduler& CurrentScheduler() const; + /// Gets the physical core for the CPU core that is currently running + Kernel::PhysicalCore& CurrentPhysicalCore(); + + /// Gets the physical core for the CPU core that is currently running + const Kernel::PhysicalCore& CurrentPhysicalCore() const; + /// Gets a reference to an ARM interface for the CPU core with the specified index ARM_Interface& ArmInterface(std::size_t core_index); diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index dfb032b4b..2a218e294 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -27,6 +27,7 @@ #include "core/hle/kernel/memory/memory_block.h" #include "core/hle/kernel/memory/page_table.h" #include "core/hle/kernel/mutex.h" +#include "core/hle/kernel/physical_core.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/readable_event.h" #include "core/hle/kernel/resource_limit.h" @@ -1071,6 +1072,7 @@ static ResultCode GetThreadPriority(Core::System& system, u32* priority, Handle const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); const std::shared_ptr thread = handle_table.Get(handle); if (!thread) { + *priority = 0; LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle); return ERR_INVALID_HANDLE; } @@ -1105,14 +1107,13 @@ static ResultCode SetThreadPriority(Core::System& system, Handle handle, u32 pri thread->SetPriority(priority); - system.PrepareReschedule(thread->GetProcessorID()); return RESULT_SUCCESS; } /// Get which CPU core is executing the current thread static u32 GetCurrentProcessorNumber(Core::System& system) { LOG_TRACE(Kernel_SVC, "called"); - return system.CurrentScheduler().GetCurrentThread()->GetProcessorID(); + return static_cast(system.CurrentPhysicalCore().CoreIndex()); } static ResultCode MapSharedMemory(Core::System& system, Handle shared_memory_handle, VAddr addr, @@ -1430,8 +1431,8 @@ static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr e ThreadType type = THREADTYPE_USER; CASCADE_RESULT(std::shared_ptr thread, - Thread::Create(system, type, "", entry_point, priority, arg, processor_id, stack_top, - current_process)); + Thread::Create(system, type, "", entry_point, priority, arg, processor_id, + stack_top, current_process)); const auto new_thread_handle = current_process->GetHandleTable().Create(thread); if (new_thread_handle.Failed()) { @@ -1804,6 +1805,8 @@ static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle, if (!thread) { LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}", thread_handle); + *core = 0; + *mask = 0; return ERR_INVALID_HANDLE; } @@ -1866,11 +1869,7 @@ static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle, return ERR_INVALID_HANDLE; } - system.PrepareReschedule(thread->GetProcessorID()); - thread->ChangeCore(core, affinity_mask); - system.PrepareReschedule(thread->GetProcessorID()); - - return RESULT_SUCCESS; + return thread->SetCoreAndAffinityMask(core, affinity_mask); } static ResultCode CreateEvent(Core::System& system, Handle* write_handle, Handle* read_handle) { diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index d9e610272..e6bb7c666 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -250,6 +250,7 @@ ResultVal> Thread::Create(Core::System& system, ThreadTy } void Thread::SetPriority(u32 priority) { + SchedulerLock lock(kernel); ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST, "Invalid priority value."); nominal_priority = priority; @@ -383,10 +384,6 @@ void Thread::UpdatePriority() { lock_owner->UpdatePriority(); } -void Thread::ChangeCore(u32 core, u64 mask) { - SetCoreAndAffinityMask(core, mask); -} - bool Thread::AllSynchronizationObjectsReady() const { return std::none_of(wait_objects.begin(), wait_objects.end(), [this](const std::shared_ptr& object) { @@ -467,6 +464,7 @@ void Thread::SetCurrentPriority(u32 new_priority) { } ResultCode Thread::SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask) { + SchedulerLock lock(kernel); const auto HighestSetCore = [](u64 mask, u32 max_cores) { for (s32 core = static_cast(max_cores - 1); core >= 0; core--) { if (((mask >> core) & 1) != 0) { diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 78a4357b0..29fe5483b 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -221,7 +221,7 @@ public: void UpdatePriority(); /// Changes the core that the thread is running or scheduled to run on. - void ChangeCore(u32 core, u64 mask); + ResultCode SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask); /** * Gets the thread's thread ID @@ -522,7 +522,6 @@ private: void SetSchedulingStatus(ThreadSchedStatus new_status); void SetCurrentPriority(u32 new_priority); - ResultCode SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask); void AdjustSchedulingOnAffinity(u64 old_affinity_mask, s32 old_core); From ef4afa9760f07bc218256cdb044529ad3a7c08b5 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Tue, 25 Feb 2020 13:24:29 -0400 Subject: [PATCH 145/252] SVC: Remove global HLE Lock. --- src/core/hle/kernel/svc.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 2a218e294..a071b0c09 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -2410,9 +2410,6 @@ MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70)); void Call(Core::System& system, u32 immediate) { MICROPROFILE_SCOPE(Kernel_SVC); - // Lock the global kernel mutex when we enter the kernel HLE. - std::lock_guard lock{HLE::g_hle_lock}; - const FunctionDef* info = system.CurrentProcess()->Is64BitProcess() ? GetSVCInfo64(immediate) : GetSVCInfo32(immediate); if (info) { From 3b5b950c895a2db217a3e5c8105cec4498a2534e Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Tue, 25 Feb 2020 16:38:33 -0400 Subject: [PATCH 146/252] SVC: Correct SignalEvent, ClearEvent, ResetSignal, WaitSynchronization, CancelSynchronization, ArbitrateLock --- src/core/hle/kernel/mutex.cpp | 71 +++++++----- src/core/hle/kernel/process.cpp | 1 + src/core/hle/kernel/readable_event.cpp | 3 + src/core/hle/kernel/svc.cpp | 1 - src/core/hle/kernel/synchronization.cpp | 116 ++++++++++--------- src/core/hle/kernel/synchronization_object.h | 5 +- src/core/hle/kernel/thread.cpp | 14 ++- src/core/hle/kernel/thread.h | 17 ++- 8 files changed, 136 insertions(+), 92 deletions(-) diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 7869eb32b..3520c5e49 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -72,42 +72,55 @@ ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle, return ERR_INVALID_ADDRESS; } - const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); + auto& kernel = system.Kernel(); std::shared_ptr current_thread = - SharedFrom(system.CurrentScheduler().GetCurrentThread()); - std::shared_ptr holding_thread = handle_table.Get(holding_thread_handle); - std::shared_ptr requesting_thread = handle_table.Get(requesting_thread_handle); + SharedFrom(kernel.CurrentScheduler().GetCurrentThread()); + { + SchedulerLock lock(kernel); + // The mutex address must be 4-byte aligned + if ((address % sizeof(u32)) != 0) { + return ERR_INVALID_ADDRESS; + } - // TODO(Subv): It is currently unknown if it is possible to lock a mutex in behalf of another - // thread. - ASSERT(requesting_thread == current_thread); + const auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); + std::shared_ptr holding_thread = handle_table.Get(holding_thread_handle); + std::shared_ptr requesting_thread = handle_table.Get(requesting_thread_handle); - const u32 addr_value = system.Memory().Read32(address); + // TODO(Subv): It is currently unknown if it is possible to lock a mutex in behalf of another + // thread. + ASSERT(requesting_thread == current_thread); - // If the mutex isn't being held, just return success. - if (addr_value != (holding_thread_handle | Mutex::MutexHasWaitersFlag)) { - return RESULT_SUCCESS; + current_thread->SetSynchronizationResults(nullptr, RESULT_SUCCESS); + + const u32 addr_value = system.Memory().Read32(address); + + // If the mutex isn't being held, just return success. + if (addr_value != (holding_thread_handle | Mutex::MutexHasWaitersFlag)) { + return RESULT_SUCCESS; + } + + if (holding_thread == nullptr) { + return ERR_INVALID_HANDLE; + } + + // Wait until the mutex is released + current_thread->SetMutexWaitAddress(address); + current_thread->SetWaitHandle(requesting_thread_handle); + + current_thread->SetStatus(ThreadStatus::WaitMutex); + + // Update the lock holder thread's priority to prevent priority inversion. + holding_thread->AddMutexWaiter(current_thread); } - if (holding_thread == nullptr) { - LOG_ERROR(Kernel, "Holding thread does not exist! thread_handle={:08X}", - holding_thread_handle); - return ERR_INVALID_HANDLE; + { + SchedulerLock lock(kernel); + auto* owner = current_thread->GetLockOwner(); + if (owner != nullptr) { + owner->RemoveMutexWaiter(current_thread); + } } - - // Wait until the mutex is released - current_thread->SetMutexWaitAddress(address); - current_thread->SetWaitHandle(requesting_thread_handle); - - current_thread->SetStatus(ThreadStatus::WaitMutex); - current_thread->InvalidateWakeupCallback(); - - // Update the lock holder thread's priority to prevent priority inversion. - holding_thread->AddMutexWaiter(current_thread); - - system.PrepareReschedule(); - - return RESULT_SUCCESS; + return current_thread->GetSignalingResult(); } ResultCode Mutex::Release(VAddr address) { diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 7e26a54f4..cd4b0aa60 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -212,6 +212,7 @@ void Process::UnregisterThread(const Thread* thread) { } ResultCode Process::ClearSignalState() { + SchedulerLock lock(system.Kernel()); if (status == ProcessStatus::Exited) { LOG_ERROR(Kernel, "called on a terminated process instance."); return ERR_INVALID_STATE; diff --git a/src/core/hle/kernel/readable_event.cpp b/src/core/hle/kernel/readable_event.cpp index ef5e19e63..6e286419e 100644 --- a/src/core/hle/kernel/readable_event.cpp +++ b/src/core/hle/kernel/readable_event.cpp @@ -6,8 +6,10 @@ #include "common/assert.h" #include "common/logging/log.h" #include "core/hle/kernel/errors.h" +#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/object.h" #include "core/hle/kernel/readable_event.h" +#include "core/hle/kernel/scheduler.h" #include "core/hle/kernel/thread.h" namespace Kernel { @@ -37,6 +39,7 @@ void ReadableEvent::Clear() { } ResultCode ReadableEvent::Reset() { + SchedulerLock lock(kernel); if (!is_signaled) { LOG_TRACE(Kernel, "Handle is not signaled! object_id={}, object_type={}, object_name={}", GetObjectId(), GetTypeName(), GetName()); diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index a071b0c09..0d905c0ca 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -448,7 +448,6 @@ static ResultCode CancelSynchronization(Core::System& system, Handle thread_hand } thread->CancelWait(); - system.PrepareReschedule(thread->GetProcessorID()); return RESULT_SUCCESS; } diff --git a/src/core/hle/kernel/synchronization.cpp b/src/core/hle/kernel/synchronization.cpp index dc37fad1a..b36e550a0 100644 --- a/src/core/hle/kernel/synchronization.cpp +++ b/src/core/hle/kernel/synchronization.cpp @@ -10,78 +10,88 @@ #include "core/hle/kernel/synchronization.h" #include "core/hle/kernel/synchronization_object.h" #include "core/hle/kernel/thread.h" +#include "core/hle/kernel/time_manager.h" namespace Kernel { -/// Default thread wakeup callback for WaitSynchronization -static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, std::shared_ptr thread, - std::shared_ptr object, - std::size_t index) { - ASSERT(thread->GetStatus() == ThreadStatus::WaitSynch); - - if (reason == ThreadWakeupReason::Timeout) { - thread->SetWaitSynchronizationResult(RESULT_TIMEOUT); - return true; - } - - ASSERT(reason == ThreadWakeupReason::Signal); - thread->SetWaitSynchronizationResult(RESULT_SUCCESS); - thread->SetWaitSynchronizationOutput(static_cast(index)); - return true; -} - Synchronization::Synchronization(Core::System& system) : system{system} {} void Synchronization::SignalObject(SynchronizationObject& obj) const { + SchedulerLock lock(system.Kernel()); if (obj.IsSignaled()) { - obj.WakeupAllWaitingThreads(); + for (auto thread : obj.GetWaitingThreads()) { + if (thread->GetSchedulingStatus() == ThreadSchedStatus::Paused) { + thread->SetSynchronizationResults(&obj, RESULT_SUCCESS); + thread->ResumeFromWait(); + } + } } } std::pair Synchronization::WaitFor( std::vector>& sync_objects, s64 nano_seconds) { + auto& kernel = system.Kernel(); auto* const thread = system.CurrentScheduler().GetCurrentThread(); - // Find the first object that is acquirable in the provided list of objects - const auto itr = std::find_if(sync_objects.begin(), sync_objects.end(), - [thread](const std::shared_ptr& object) { - return object->IsSignaled(); - }); + Handle event_handle = InvalidHandle; + { + SchedulerLockAndSleep lock(kernel, event_handle, thread, nano_seconds); + const auto itr = + std::find_if(sync_objects.begin(), sync_objects.end(), + [thread](const std::shared_ptr& object) { + return object->IsSignaled(); + }); - if (itr != sync_objects.end()) { - // We found a ready object, acquire it and set the result value - SynchronizationObject* object = itr->get(); - object->Acquire(thread); - const u32 index = static_cast(std::distance(sync_objects.begin(), itr)); - return {RESULT_SUCCESS, index}; + if (itr != sync_objects.end()) { + // We found a ready object, acquire it and set the result value + SynchronizationObject* object = itr->get(); + object->Acquire(thread); + const u32 index = static_cast(std::distance(sync_objects.begin(), itr)); + lock.CancelSleep(); + return {RESULT_SUCCESS, index}; + } + + if (nano_seconds == 0) { + lock.CancelSleep(); + return {RESULT_TIMEOUT, InvalidHandle}; + } + + /// TODO(Blinkhawk): Check for termination pending + + if (thread->IsSyncCancelled()) { + thread->SetSyncCancelled(false); + lock.CancelSleep(); + return {ERR_SYNCHRONIZATION_CANCELED, InvalidHandle}; + } + + for (auto& object : sync_objects) { + object->AddWaitingThread(SharedFrom(thread)); + } + thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT); + thread->SetStatus(ThreadStatus::WaitSynch); } - // No objects were ready to be acquired, prepare to suspend the thread. - - // If a timeout value of 0 was provided, just return the Timeout error code instead of - // suspending the thread. - if (nano_seconds == 0) { - return {RESULT_TIMEOUT, InvalidHandle}; + if (event_handle != InvalidHandle) { + auto& time_manager = kernel.TimeManager(); + time_manager.UnscheduleTimeEvent(event_handle); } - if (thread->IsSyncCancelled()) { - thread->SetSyncCancelled(false); - return {ERR_SYNCHRONIZATION_CANCELED, InvalidHandle}; + { + SchedulerLock lock(kernel); + ResultCode signaling_result = thread->GetSignalingResult(); + SynchronizationObject* signaling_object = thread->GetSignalingObject(); + if (signaling_result == RESULT_SUCCESS) { + const auto itr = std::find_if( + sync_objects.begin(), sync_objects.end(), + [signaling_object](const std::shared_ptr& object) { + return object.get() == signaling_object; + }); + ASSERT(itr != sync_objects.end()); + signaling_object->Acquire(thread); + const u32 index = static_cast(std::distance(sync_objects.begin(), itr)); + return {RESULT_SUCCESS, index}; + } + return {signaling_result, -1}; } - - for (auto& object : sync_objects) { - object->AddWaitingThread(SharedFrom(thread)); - } - - thread->SetSynchronizationObjects(std::move(sync_objects)); - thread->SetStatus(ThreadStatus::WaitSynch); - - // Create an event to wake the thread up after the specified nanosecond delay has passed - thread->WakeAfterDelay(nano_seconds); - thread->SetWakeupCallback(DefaultThreadWakeupCallback); - - system.PrepareReschedule(thread->GetProcessorID()); - - return {RESULT_TIMEOUT, InvalidHandle}; } } // namespace Kernel diff --git a/src/core/hle/kernel/synchronization_object.h b/src/core/hle/kernel/synchronization_object.h index 741c31faf..0a0d069e0 100644 --- a/src/core/hle/kernel/synchronization_object.h +++ b/src/core/hle/kernel/synchronization_object.h @@ -12,6 +12,7 @@ namespace Kernel { class KernelCore; +class Synchronization; class Thread; /// Class that represents a Kernel object that a thread can be waiting on @@ -53,7 +54,7 @@ public: * Wake up all threads waiting on this object that can be awoken, in priority order, * and set the synchronization result and output of the thread. */ - void WakeupAllWaitingThreads(); + void /* deprecated */ WakeupAllWaitingThreads(); /** * Wakes up a single thread waiting on this object. @@ -62,7 +63,7 @@ public: void WakeupWaitingThread(std::shared_ptr thread); /// Obtains the highest priority thread that is ready to run from this object's waiting list. - std::shared_ptr GetHighestPriorityReadyThread() const; + std::shared_ptr /* deprecated */ GetHighestPriorityReadyThread() const; /// Get a const reference to the waiting threads list for debug use const std::vector>& GetWaitingThreads() const; diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index e6bb7c666..5fef3945b 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -139,12 +139,13 @@ ResultCode Thread::Start() { } void Thread::CancelWait() { + SchedulerLock lock(kernel); if (GetSchedulingStatus() != ThreadSchedStatus::Paused) { is_sync_cancelled = true; return; } is_sync_cancelled = false; - SetWaitSynchronizationResult(ERR_SYNCHRONIZATION_CANCELED); + SetSynchronizationResults(nullptr, ERR_SYNCHRONIZATION_CANCELED); ResumeFromWait(); } @@ -258,13 +259,16 @@ void Thread::SetPriority(u32 priority) { } void Thread::SetWaitSynchronizationResult(ResultCode result) { - context_32.cpu_registers[0] = result.raw; - context_64.cpu_registers[0] = result.raw; + UNREACHABLE(); } void Thread::SetWaitSynchronizationOutput(s32 output) { - context_32.cpu_registers[1] = output; - context_64.cpu_registers[1] = output; + UNREACHABLE(); +} + +void Thread::SetSynchronizationResults(SynchronizationObject* object, ResultCode result) { + signaling_object = object; + signaling_result = result; } s32 Thread::GetSynchronizationObjectIndex(std::shared_ptr object) const { diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 29fe5483b..a8ae1a66f 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -259,13 +259,23 @@ public: * Sets the result after the thread awakens (from svcWaitSynchronization) * @param result Value to set to the returned result */ - void SetWaitSynchronizationResult(ResultCode result); + void /*deprecated*/ SetWaitSynchronizationResult(ResultCode result); /** * Sets the output parameter value after the thread awakens (from svcWaitSynchronization) * @param output Value to set to the output parameter */ - void SetWaitSynchronizationOutput(s32 output); + void /*deprecated*/ SetWaitSynchronizationOutput(s32 output); + + void SetSynchronizationResults(SynchronizationObject* object, ResultCode result); + + SynchronizationObject* GetSignalingObject() const { + return signaling_object; + } + + ResultCode GetSignalingResult() const { + return signaling_result; + } /** * Retrieves the index that this particular object occupies in the list of objects @@ -565,6 +575,9 @@ private: /// passed to WaitSynchronization. ThreadSynchronizationObjects wait_objects; + SynchronizationObject* signaling_object; + ResultCode signaling_result{RESULT_SUCCESS}; + /// List of threads that are waiting for a mutex that is held by this thread. MutexWaitingThreads wait_mutex_threads; From 203e706302c24f278eec7d0bd2362ce73b0e2612 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Tue, 25 Feb 2020 17:37:12 -0400 Subject: [PATCH 147/252] SVC: Correct ArbitrateUnlock --- src/core/hle/kernel/mutex.cpp | 73 +++++++++++++++++----------------- src/core/hle/kernel/mutex.h | 3 ++ src/core/hle/kernel/thread.cpp | 2 +- 3 files changed, 41 insertions(+), 37 deletions(-) diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 3520c5e49..18325db57 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -84,10 +84,11 @@ ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle, const auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); std::shared_ptr holding_thread = handle_table.Get(holding_thread_handle); - std::shared_ptr requesting_thread = handle_table.Get(requesting_thread_handle); + std::shared_ptr requesting_thread = + handle_table.Get(requesting_thread_handle); - // TODO(Subv): It is currently unknown if it is possible to lock a mutex in behalf of another - // thread. + // TODO(Subv): It is currently unknown if it is possible to lock a mutex in behalf of + // another thread. ASSERT(requesting_thread == current_thread); current_thread->SetSynchronizationResults(nullptr, RESULT_SUCCESS); @@ -123,47 +124,47 @@ ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle, return current_thread->GetSignalingResult(); } -ResultCode Mutex::Release(VAddr address) { - // The mutex address must be 4-byte aligned - if ((address % sizeof(u32)) != 0) { - LOG_ERROR(Kernel, "Address is not 4-byte aligned! address={:016X}", address); - return ERR_INVALID_ADDRESS; - } +std::pair> Mutex::Unlock(std::shared_ptr owner, + VAddr address) { + // The mutex address must be 4-byte aligned + if ((address % sizeof(u32)) != 0) { + LOG_ERROR(Kernel, "Address is not 4-byte aligned! address={:016X}", address); + return {ERR_INVALID_ADDRESS, nullptr}; + } - std::shared_ptr current_thread = - SharedFrom(system.CurrentScheduler().GetCurrentThread()); - auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(current_thread, address); - - // There are no more threads waiting for the mutex, release it completely. - if (thread == nullptr) { + auto [new_owner, num_waiters] = GetHighestPriorityMutexWaitingThread(owner, address); + if (new_owner == nullptr) { system.Memory().Write32(address, 0); - return RESULT_SUCCESS; + return {RESULT_SUCCESS, nullptr}; } - // Transfer the ownership of the mutex from the previous owner to the new one. - TransferMutexOwnership(address, current_thread, thread); - - u32 mutex_value = thread->GetWaitHandle(); - + TransferMutexOwnership(address, owner, new_owner); + u32 mutex_value = new_owner->GetWaitHandle(); if (num_waiters >= 2) { // Notify the guest that there are still some threads waiting for the mutex mutex_value |= Mutex::MutexHasWaitersFlag; } - - // Grant the mutex to the next waiting thread and resume it. + new_owner->SetSynchronizationResults(nullptr, RESULT_SUCCESS); + new_owner->ResumeFromWait(); + new_owner->SetLockOwner(nullptr); system.Memory().Write32(address, mutex_value); - - ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex); - thread->ResumeFromWait(); - - thread->SetLockOwner(nullptr); - thread->SetCondVarWaitAddress(0); - thread->SetMutexWaitAddress(0); - thread->SetWaitHandle(0); - thread->SetWaitSynchronizationResult(RESULT_SUCCESS); - - system.PrepareReschedule(); - - return RESULT_SUCCESS; + return {RESULT_SUCCESS, new_owner}; } + +ResultCode Mutex::Release(VAddr address) { + auto& kernel = system.Kernel(); + SchedulerLock lock(kernel); + + std::shared_ptr current_thread = + SharedFrom(kernel.CurrentScheduler().GetCurrentThread()); + + auto [result, new_owner] = Unlock(current_thread, address); + + if (result != RESULT_SUCCESS && new_owner != nullptr) { + new_owner->SetSynchronizationResults(nullptr, result); + } + + return result; +} + } // namespace Kernel diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h index b904de2e8..bce06ecea 100644 --- a/src/core/hle/kernel/mutex.h +++ b/src/core/hle/kernel/mutex.h @@ -28,6 +28,9 @@ public: ResultCode TryAcquire(VAddr address, Handle holding_thread_handle, Handle requesting_thread_handle); + /// Unlocks a mutex for owner at address + std::pair> Unlock(std::shared_ptr owner, VAddr address); + /// Releases the mutex at the specified address. ResultCode Release(VAddr address); diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 5fef3945b..f100ffc70 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -80,7 +80,7 @@ void Thread::CancelWakeupTimer() { void Thread::ResumeFromWait() { ASSERT_MSG(wait_objects.empty(), "Thread is waking up while waiting for objects"); - + SchedulerLock lock(kernel); switch (status) { case ThreadStatus::Paused: case ThreadStatus::WaitSynch: From 15a79eb0d7abe752a9a55d0cfa7ea220e17318b7 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Tue, 25 Feb 2020 19:43:28 -0400 Subject: [PATCH 148/252] SVC: Correct SendSyncRequest. --- src/core/cpu_manager.cpp | 3 +- src/core/hle/kernel/hle_ipc.cpp | 24 ++++---- src/core/hle/kernel/scheduler.cpp | 9 +++ src/core/hle/kernel/scheduler.h | 2 + src/core/hle/kernel/server_session.cpp | 15 +++-- src/core/hle/kernel/svc.cpp | 21 +++++-- src/core/hle/kernel/thread.cpp | 14 +++-- src/core/hle/kernel/thread.h | 82 +++++++++++++++++--------- 8 files changed, 116 insertions(+), 54 deletions(-) diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index 9b9337131..6032cb0bf 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp @@ -81,8 +81,7 @@ void CpuManager::RunGuestThread() { while (true) { auto& physical_core = kernel.CurrentPhysicalCore(); if (!physical_core.IsInterrupted()) { - physical_core.Idle(); - // physical_core.Run(); + physical_core.Run(); } auto& scheduler = physical_core.Scheduler(); scheduler.TryDoContextSwitch(); diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index 0d01a7047..955d5fe1c 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -14,6 +14,7 @@ #include "common/common_types.h" #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/errors.h" #include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/kernel.h" @@ -21,7 +22,9 @@ #include "core/hle/kernel/process.h" #include "core/hle/kernel/readable_event.h" #include "core/hle/kernel/server_session.h" +#include "core/hle/kernel/scheduler.h" #include "core/hle/kernel/thread.h" +#include "core/hle/kernel/time_manager.h" #include "core/hle/kernel/writable_event.h" #include "core/memory.h" @@ -46,11 +49,10 @@ std::shared_ptr HLERequestContext::SleepClientThread( const std::string& reason, u64 timeout, WakeupCallback&& callback, std::shared_ptr writable_event) { // Put the client thread to sleep until the wait event is signaled or the timeout expires. - thread->SetWakeupCallback( + thread->SetHLECallback( [context = *this, callback](ThreadWakeupReason reason, std::shared_ptr thread, std::shared_ptr object, std::size_t index) mutable -> bool { - ASSERT(thread->GetStatus() == ThreadStatus::WaitHLEEvent); callback(thread, context, reason); context.WriteToOutgoingCommandBuffer(*thread); return true; @@ -62,14 +64,16 @@ std::shared_ptr HLERequestContext::SleepClientThread( writable_event = pair.writable; } - const auto readable_event{writable_event->GetReadableEvent()}; - writable_event->Clear(); - thread->SetStatus(ThreadStatus::WaitHLEEvent); - thread->SetSynchronizationObjects({readable_event}); - readable_event->AddWaitingThread(thread); - - if (timeout > 0) { - thread->WakeAfterDelay(timeout); + { + Handle event_handle = InvalidHandle; + SchedulerLockAndSleep lock(kernel, event_handle, thread.get(), timeout); + const auto readable_event{writable_event->GetReadableEvent()}; + writable_event->Clear(); + thread->SetStatus(ThreadStatus::WaitHLEEvent); + thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT); + readable_event->AddWaitingThread(thread); + lock.Release(); + thread->SetHLETimeEvent(event_handle); } is_thread_waiting = true; diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index 5166020a0..0e85ee69e 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp @@ -715,4 +715,13 @@ SchedulerLockAndSleep::~SchedulerLockAndSleep() { time_manager.ScheduleTimeEvent(event_handle, time_task, nanoseconds); } +void SchedulerLockAndSleep::Release() { + if (sleep_cancelled) { + return; + } + auto& time_manager = kernel.TimeManager(); + time_manager.ScheduleTimeEvent(event_handle, time_task, nanoseconds); + sleep_cancelled = true; +} + } // namespace Kernel diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h index 16655b03f..f5f64338f 100644 --- a/src/core/hle/kernel/scheduler.h +++ b/src/core/hle/kernel/scheduler.h @@ -279,6 +279,8 @@ public: sleep_cancelled = true; } + void Release(); + private: Handle& event_handle; Thread* time_task; diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index 25438b86b..05516a453 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp @@ -19,6 +19,7 @@ #include "core/hle/kernel/process.h" #include "core/hle/kernel/server_session.h" #include "core/hle/kernel/session.h" +#include "core/hle/kernel/scheduler.h" #include "core/hle/kernel/thread.h" #include "core/memory.h" @@ -168,9 +169,12 @@ ResultCode ServerSession::CompleteSyncRequest() { } // Some service requests require the thread to block - if (!context.IsThreadWaiting()) { - context.GetThread().ResumeFromWait(); - context.GetThread().SetWaitSynchronizationResult(result); + { + SchedulerLock lock(kernel); + if (!context.IsThreadWaiting()) { + context.GetThread().ResumeFromWait(); + context.GetThread().SetSynchronizationResults(nullptr, result); + } } request_queue.Pop(); @@ -180,8 +184,9 @@ ResultCode ServerSession::CompleteSyncRequest() { ResultCode ServerSession::HandleSyncRequest(std::shared_ptr thread, Core::Memory::Memory& memory) { - Core::System::GetInstance().CoreTiming().ScheduleEvent(20000, request_event, {}); - return QueueSyncRequest(std::move(thread), memory); + ResultCode result = QueueSyncRequest(std::move(thread), memory); + Core::System::GetInstance().CoreTiming().ScheduleEvent(0, request_event, {}); + return result; } } // namespace Kernel diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 0d905c0ca..768d72b92 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -38,6 +38,7 @@ #include "core/hle/kernel/svc_wrap.h" #include "core/hle/kernel/synchronization.h" #include "core/hle/kernel/thread.h" +#include "core/hle/kernel/time_manager.h" #include "core/hle/kernel/transfer_memory.h" #include "core/hle/kernel/writable_event.h" #include "core/hle/lock.h" @@ -318,11 +319,23 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) { LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName()); auto thread = system.CurrentScheduler().GetCurrentThread(); - thread->InvalidateWakeupCallback(); - thread->SetStatus(ThreadStatus::WaitIPC); - system.PrepareReschedule(thread->GetProcessorID()); + { + SchedulerLock lock(system.Kernel()); + thread->InvalidateHLECallback(); + thread->SetStatus(ThreadStatus::WaitIPC); + session->SendSyncRequest(SharedFrom(thread), system.Memory()); + } + ResultCode result = thread->GetSignalingResult(); + if (thread->HasHLECallback()) { + Handle event_handle = thread->GetHLETimeEvent(); + if (event_handle != InvalidHandle) { + auto& time_manager = system.Kernel().TimeManager(); + time_manager.UnscheduleTimeEvent(event_handle); + } + thread->InvokeHLECallback(ThreadWakeupReason::Timeout, SharedFrom(thread), nullptr, 0); + } - return session->SendSyncRequest(SharedFrom(thread), system.Memory()); + return result; } static ResultCode SendSyncRequest32(Core::System& system, Handle handle) { diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index f100ffc70..fb97535a3 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -96,7 +96,7 @@ void Thread::ResumeFromWait() { case ThreadStatus::Ready: // The thread's wakeup callback must have already been cleared when the thread was first // awoken. - ASSERT(wakeup_callback == nullptr); + ASSERT(hle_callback == nullptr); // If the thread is waiting on multiple wait objects, it might be awoken more than once // before actually resuming. We can ignore subsequent wakeups if the thread status has // already been set to ThreadStatus::Ready. @@ -112,7 +112,7 @@ void Thread::ResumeFromWait() { return; } - wakeup_callback = nullptr; + hle_callback = nullptr; if (activity == ThreadActivity::Paused) { SetStatus(ThreadStatus::Paused); @@ -398,8 +398,14 @@ bool Thread::AllSynchronizationObjectsReady() const { bool Thread::InvokeWakeupCallback(ThreadWakeupReason reason, std::shared_ptr thread, std::shared_ptr object, std::size_t index) { - ASSERT(wakeup_callback); - return wakeup_callback(reason, std::move(thread), std::move(object), index); + ASSERT(hle_callback); + return hle_callback(reason, std::move(thread), std::move(object), index); +} + +bool Thread::InvokeHLECallback(ThreadWakeupReason reason, std::shared_ptr thread, + std::shared_ptr object, std::size_t index) { + ASSERT(hle_callback); + return hle_callback(reason, std::move(thread), std::move(object), index); } void Thread::SetActivity(ThreadActivity value) { diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index a8ae1a66f..04496f96e 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -31,12 +31,12 @@ class Process; class Scheduler; enum ThreadPriority : u32 { - THREADPRIO_HIGHEST = 0, ///< Highest thread priority - THREADPRIO_MAX_CORE_MIGRATION = 2, ///< Highest priority for a core migration - THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps - THREADPRIO_DEFAULT = 44, ///< Default thread priority for userland apps - THREADPRIO_LOWEST = 63, ///< Lowest thread priority - THREADPRIO_COUNT = 64, ///< Total number of possible thread priorities. + THREADPRIO_HIGHEST = 0, ///< Highest thread priority + THREADPRIO_MAX_CORE_MIGRATION = 2, ///< Highest priority for a core migration + THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps + THREADPRIO_DEFAULT = 44, ///< Default thread priority for userland apps + THREADPRIO_LOWEST = 63, ///< Lowest thread priority + THREADPRIO_COUNT = 64, ///< Total number of possible thread priorities. }; enum ThreadType : u32 { @@ -129,23 +129,24 @@ public: using WakeupCallback = std::function thread, std::shared_ptr object, std::size_t index)>; + using HLECallback = std::function thread)>; - /** - * Creates and returns a new thread. The new thread is immediately scheduled - * @param system The instance of the whole system - * @param name The friendly name desired for the thread - * @param entry_point The address at which the thread should start execution - * @param priority The thread's priority - * @param arg User data to pass to the thread - * @param processor_id The ID(s) of the processors on which the thread is desired to be run - * @param stack_top The address of the thread's stack top - * @param owner_process The parent process for the thread, if null, it's a kernel thread - * @return A shared pointer to the newly created thread - */ - static ResultVal> Create(Core::System& system, ThreadType type_flags, std::string name, - VAddr entry_point, u32 priority, u64 arg, - s32 processor_id, VAddr stack_top, - Process* owner_process); + /** + * Creates and returns a new thread. The new thread is immediately scheduled + * @param system The instance of the whole system + * @param name The friendly name desired for the thread + * @param entry_point The address at which the thread should start execution + * @param priority The thread's priority + * @param arg User data to pass to the thread + * @param processor_id The ID(s) of the processors on which the thread is desired to be run + * @param stack_top The address of the thread's stack top + * @param owner_process The parent process for the thread, if null, it's a kernel thread + * @return A shared pointer to the newly created thread + */ + static ResultVal> Create(Core::System& system, ThreadType type_flags, + std::string name, VAddr entry_point, + u32 priority, u64 arg, s32 processor_id, + VAddr stack_top, Process* owner_process); /** * Creates and returns a new thread. The new thread is immediately scheduled @@ -161,10 +162,10 @@ public: * @param thread_start_parameter The parameter which will passed to host context on init * @return A shared pointer to the newly created thread */ - static ResultVal> Create(Core::System& system, ThreadType type_flags, std::string name, - VAddr entry_point, u32 priority, u64 arg, - s32 processor_id, VAddr stack_top, - Process* owner_process, + static ResultVal> Create(Core::System& system, ThreadType type_flags, + std::string name, VAddr entry_point, + u32 priority, u64 arg, s32 processor_id, + VAddr stack_top, Process* owner_process, std::function&& thread_start_func, void* thread_start_parameter); @@ -447,17 +448,37 @@ public: } bool HasWakeupCallback() const { - return wakeup_callback != nullptr; + return hle_callback != nullptr; + } + + bool HasHLECallback() const { + return hle_callback != nullptr; } void SetWakeupCallback(WakeupCallback callback) { - wakeup_callback = std::move(callback); + hle_callback = std::move(callback); + } + + void SetHLECallback(WakeupCallback callback) { + hle_callback = std::move(callback); + } + + void SetHLETimeEvent(Handle time_event) { + hle_time_event = time_event; + } + + Handle GetHLETimeEvent() const { + return hle_time_event; } void InvalidateWakeupCallback() { SetWakeupCallback(nullptr); } + void InvalidateHLECallback() { + SetHLECallback(nullptr); + } + /** * Invokes the thread's wakeup callback. * @@ -466,6 +487,8 @@ public: */ bool InvokeWakeupCallback(ThreadWakeupReason reason, std::shared_ptr thread, std::shared_ptr object, std::size_t index); + bool InvokeHLECallback(ThreadWakeupReason reason, std::shared_ptr thread, + std::shared_ptr object, std::size_t index); u32 GetIdealCore() const { return ideal_core; @@ -600,7 +623,8 @@ private: /// Callback that will be invoked when the thread is resumed from a waiting state. If the thread /// was waiting via WaitSynchronization then the object will be the last object that became /// available. In case of a timeout, the object will be nullptr. - WakeupCallback wakeup_callback; + WakeupCallback hle_callback; + Handle hle_time_event; Scheduler* scheduler = nullptr; From 3d9fbb8226e9bf7dce99568f6e616e7361d43c41 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Wed, 26 Feb 2020 10:44:21 -0400 Subject: [PATCH 149/252] CPU_Manager: Reconfigre guest threads for dynamrmic downsides --- src/core/cpu_manager.cpp | 3 ++- src/core/hle/kernel/physical_core.cpp | 3 +++ src/core/hle/kernel/physical_core.h | 2 ++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index 6032cb0bf..241971ff3 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp @@ -80,9 +80,10 @@ void CpuManager::RunGuestThread() { } while (true) { auto& physical_core = kernel.CurrentPhysicalCore(); - if (!physical_core.IsInterrupted()) { + while (!physical_core.IsInterrupted()) { physical_core.Run(); } + physical_core.ClearExclusive(); auto& scheduler = physical_core.Scheduler(); scheduler.TryDoContextSwitch(); } diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp index 69202540b..ff14fcb42 100644 --- a/src/core/hle/kernel/physical_core.cpp +++ b/src/core/hle/kernel/physical_core.cpp @@ -45,6 +45,9 @@ PhysicalCore::~PhysicalCore() = default; void PhysicalCore::Run() { arm_interface->Run(); +} + +void PhysicalCore::ClearExclusive() { arm_interface->ClearExclusiveState(); } diff --git a/src/core/hle/kernel/physical_core.h b/src/core/hle/kernel/physical_core.h index c3da30b72..cd2e42fc3 100644 --- a/src/core/hle/kernel/physical_core.h +++ b/src/core/hle/kernel/physical_core.h @@ -38,6 +38,8 @@ public: /// Execute current jit state void Run(); + /// Clear Exclusive state. + void ClearExclusive(); /// Set this core in IdleState. void Idle(); /// Execute a single instruction in current jit. From 5b6a67f849d0d78d6f71ccb7d93f48a97760a901 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Wed, 26 Feb 2020 18:55:11 -0400 Subject: [PATCH 150/252] SVC: Cleanup old methods. --- src/core/hle/kernel/svc.cpp | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 768d72b92..8634d3feb 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -622,7 +622,6 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) { // Kill the current thread current_thread->Stop(); - system.PrepareReschedule(); } } @@ -1004,6 +1003,7 @@ static ResultCode UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size /// Sets the thread activity static ResultCode SetThreadActivity(Core::System& system, Handle handle, u32 activity) { LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", handle, activity); + UNIMPLEMENTED(); if (activity > static_cast(ThreadActivity::Paused)) { return ERR_INVALID_ENUM_VALUE; } @@ -1032,7 +1032,6 @@ static ResultCode SetThreadActivity(Core::System& system, Handle handle, u32 act thread->SetActivity(static_cast(activity)); - system.PrepareReschedule(thread->GetProcessorID()); return RESULT_SUCCESS; } @@ -1385,6 +1384,7 @@ static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_ha /// Exits the current process static void ExitProcess(Core::System& system) { auto* current_process = system.Kernel().CurrentProcess(); + UNIMPLEMENTED(); LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID()); ASSERT_MSG(current_process->GetStatus() == ProcessStatus::Running, @@ -1394,8 +1394,6 @@ static void ExitProcess(Core::System& system) { // Kill the current thread system.CurrentScheduler().GetCurrentThread()->Stop(); - - system.PrepareReschedule(); } /// Creates a new thread @@ -1458,8 +1456,6 @@ static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr e thread->SetName( fmt::format("thread[entry_point={:X}, handle={:X}]", entry_point, *new_thread_handle)); - system.PrepareReschedule(thread->GetProcessorID()); - return RESULT_SUCCESS; } @@ -1545,6 +1541,8 @@ static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr mutex_add return ERR_INVALID_ADDRESS; } + UNIMPLEMENTED(); + ASSERT(condition_variable_addr == Common::AlignDown(condition_variable_addr, 4)); auto* const current_process = system.Kernel().CurrentProcess(); @@ -1569,7 +1567,6 @@ static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr mutex_add // Note: Deliberately don't attempt to inherit the lock owner's priority. - system.PrepareReschedule(current_thread->GetProcessorID()); return RESULT_SUCCESS; } @@ -1580,6 +1577,8 @@ static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_ ASSERT(condition_variable_addr == Common::AlignDown(condition_variable_addr, 4)); + UNIMPLEMENTED(); + // Retrieve a list of all threads that are waiting for this condition variable. auto* const current_process = system.Kernel().CurrentProcess(); std::vector> waiting_threads = @@ -1634,7 +1633,6 @@ static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_ thread->SetMutexWaitAddress(0); thread->SetWaitHandle(0); thread->SetWaitSynchronizationResult(RESULT_SUCCESS); - system.PrepareReschedule(thread->GetProcessorID()); } else { // The mutex is already owned by some other thread, make this thread wait on it. const Handle owner_handle = static_cast(mutex_val & Mutex::MutexOwnerMask); @@ -1646,7 +1644,6 @@ static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_ thread->SetStatus(ThreadStatus::WaitMutex); owner->AddMutexWaiter(thread); - system.PrepareReschedule(thread->GetProcessorID()); } } } @@ -1661,6 +1658,7 @@ static ResultCode WaitForAddress(Core::System& system, VAddr address, u32 type, LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, timeout={}", address, type, value, timeout); + UNIMPLEMENTED(); // If the passed address is a kernel virtual address, return invalid memory state. if (Core::Memory::IsKernelVirtualAddress(address)) { LOG_ERROR(Kernel_SVC, "Address is a kernel virtual address, address={:016X}", address); @@ -1677,9 +1675,6 @@ static ResultCode WaitForAddress(Core::System& system, VAddr address, u32 type, auto& address_arbiter = system.Kernel().CurrentProcess()->GetAddressArbiter(); const ResultCode result = address_arbiter.WaitForAddress(address, arbitration_type, value, timeout); - if (result == RESULT_SUCCESS) { - system.PrepareReschedule(); - } return result; } @@ -1689,6 +1684,8 @@ static ResultCode SignalToAddress(Core::System& system, VAddr address, u32 type, LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, num_to_wake=0x{:X}", address, type, value, num_to_wake); + UNIMPLEMENTED(); + // If the passed address is a kernel virtual address, return invalid memory state. if (Core::Memory::IsKernelVirtualAddress(address)) { LOG_ERROR(Kernel_SVC, "Address is a kernel virtual address, address={:016X}", address); @@ -1945,7 +1942,6 @@ static ResultCode SignalEvent(Core::System& system, Handle handle) { } writable_event->Signal(); - system.PrepareReschedule(); return RESULT_SUCCESS; } From d4ebb510a05d29befde6556e632413e5b35b9ab5 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Wed, 26 Feb 2020 22:26:53 -0400 Subject: [PATCH 151/252] SVC: Correct WaitSynchronization, WaitProcessWideKey, SignalProcessWideKey. --- src/core/hle/kernel/process.cpp | 1 - src/core/hle/kernel/scheduler.cpp | 2 +- src/core/hle/kernel/svc.cpp | 75 ++++++++++++------- src/core/hle/kernel/synchronization.cpp | 8 +- .../hle/kernel/synchronization_object.cpp | 4 + src/core/hle/kernel/synchronization_object.h | 2 + src/core/hle/kernel/thread.cpp | 2 +- src/core/hle/kernel/time_manager.cpp | 18 ++++- src/core/hle/kernel/time_manager.h | 5 ++ 9 files changed, 84 insertions(+), 33 deletions(-) diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index cd4b0aa60..ea5fe5b29 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -187,7 +187,6 @@ void Process::RemoveConditionVariableThread(std::shared_ptr thread) { } ++it; } - UNREACHABLE(); } std::vector> Process::GetConditionVariableThreads( diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index 0e85ee69e..758fa8188 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp @@ -632,7 +632,7 @@ void Scheduler::SwitchContext() { cpu_core.SaveContext(previous_thread->GetContext64()); // Save the TPIDR_EL0 system register in case it was modified. previous_thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0()); - + cpu_core.ClearExclusiveState(); } if (previous_thread->GetStatus() == ThreadStatus::Running) { previous_thread->SetStatus(ThreadStatus::Ready); diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 8634d3feb..a5193063b 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -1541,33 +1541,50 @@ static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr mutex_add return ERR_INVALID_ADDRESS; } - UNIMPLEMENTED(); - ASSERT(condition_variable_addr == Common::AlignDown(condition_variable_addr, 4)); - + auto& kernel = system.Kernel(); + Handle event_handle; + Thread* current_thread = system.CurrentScheduler().GetCurrentThread(); auto* const current_process = system.Kernel().CurrentProcess(); - const auto& handle_table = current_process->GetHandleTable(); - std::shared_ptr thread = handle_table.Get(thread_handle); - ASSERT(thread); + { + SchedulerLockAndSleep lock(kernel, event_handle, current_thread, nano_seconds); + const auto& handle_table = current_process->GetHandleTable(); + std::shared_ptr thread = handle_table.Get(thread_handle); + ASSERT(thread); - const auto release_result = current_process->GetMutex().Release(mutex_addr); - if (release_result.IsError()) { - return release_result; + current_thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT); + + const auto release_result = current_process->GetMutex().Release(mutex_addr); + if (release_result.IsError()) { + lock.CancelSleep(); + return release_result; + } + + if (nano_seconds == 0) { + lock.CancelSleep(); + return RESULT_TIMEOUT; + } + + current_thread->SetCondVarWaitAddress(condition_variable_addr); + current_thread->SetMutexWaitAddress(mutex_addr); + current_thread->SetWaitHandle(thread_handle); + current_thread->SetStatus(ThreadStatus::WaitCondVar); + current_process->InsertConditionVariableThread(SharedFrom(current_thread)); } - Thread* current_thread = system.CurrentScheduler().GetCurrentThread(); - current_thread->SetCondVarWaitAddress(condition_variable_addr); - current_thread->SetMutexWaitAddress(mutex_addr); - current_thread->SetWaitHandle(thread_handle); - current_thread->SetStatus(ThreadStatus::WaitCondVar); - current_thread->InvalidateWakeupCallback(); - current_process->InsertConditionVariableThread(SharedFrom(current_thread)); + if (event_handle != InvalidHandle) { + auto& time_manager = kernel.TimeManager(); + time_manager.UnscheduleTimeEvent(event_handle); + } - current_thread->WakeAfterDelay(nano_seconds); + { + SchedulerLock lock(kernel); + current_process->RemoveConditionVariableThread(SharedFrom(current_thread)); + } // Note: Deliberately don't attempt to inherit the lock owner's priority. - return RESULT_SUCCESS; + return current_thread->GetSignalingResult(); } /// Signal process wide key @@ -1577,10 +1594,10 @@ static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_ ASSERT(condition_variable_addr == Common::AlignDown(condition_variable_addr, 4)); - UNIMPLEMENTED(); - // Retrieve a list of all threads that are waiting for this condition variable. - auto* const current_process = system.Kernel().CurrentProcess(); + auto& kernel = system.Kernel(); + SchedulerLock lock(kernel); + auto* const current_process = kernel.CurrentProcess(); std::vector> waiting_threads = current_process->GetConditionVariableThreads(condition_variable_addr); @@ -1589,10 +1606,18 @@ static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_ std::size_t last = waiting_threads.size(); if (target > 0) last = std::min(waiting_threads.size(), static_cast(target)); - + auto& time_manager = kernel.TimeManager(); for (std::size_t index = 0; index < last; ++index) { auto& thread = waiting_threads[index]; + if (thread->GetStatus() != ThreadStatus::WaitCondVar) { + last++; + last = std::min(waiting_threads.size(), last); + continue; + } + + time_manager.CancelTimeEvent(thread.get()); + ASSERT(thread->GetCondVarWaitAddress() == condition_variable_addr); // liberate Cond Var Thread. @@ -1630,17 +1655,13 @@ static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_ } thread->SetLockOwner(nullptr); - thread->SetMutexWaitAddress(0); - thread->SetWaitHandle(0); - thread->SetWaitSynchronizationResult(RESULT_SUCCESS); + thread->SetSynchronizationResults(nullptr, RESULT_SUCCESS); } else { // The mutex is already owned by some other thread, make this thread wait on it. const Handle owner_handle = static_cast(mutex_val & Mutex::MutexOwnerMask); const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); auto owner = handle_table.Get(owner_handle); ASSERT(owner); - ASSERT(thread->GetStatus() == ThreadStatus::WaitCondVar); - thread->InvalidateWakeupCallback(); thread->SetStatus(ThreadStatus::WaitMutex); owner->AddMutexWaiter(thread); diff --git a/src/core/hle/kernel/synchronization.cpp b/src/core/hle/kernel/synchronization.cpp index b36e550a0..c60c5bb42 100644 --- a/src/core/hle/kernel/synchronization.cpp +++ b/src/core/hle/kernel/synchronization.cpp @@ -17,12 +17,15 @@ namespace Kernel { Synchronization::Synchronization(Core::System& system) : system{system} {} void Synchronization::SignalObject(SynchronizationObject& obj) const { - SchedulerLock lock(system.Kernel()); + auto& kernel = system.Kernel(); + SchedulerLock lock(kernel); + auto& time_manager = kernel.TimeManager(); if (obj.IsSignaled()) { for (auto thread : obj.GetWaitingThreads()) { if (thread->GetSchedulingStatus() == ThreadSchedStatus::Paused) { thread->SetSynchronizationResults(&obj, RESULT_SUCCESS); thread->ResumeFromWait(); + time_manager.CancelTimeEvent(thread.get()); } } } @@ -79,6 +82,9 @@ std::pair Synchronization::WaitFor( SchedulerLock lock(kernel); ResultCode signaling_result = thread->GetSignalingResult(); SynchronizationObject* signaling_object = thread->GetSignalingObject(); + for (auto& obj : sync_objects) { + obj->RemoveWaitingThread(SharedFrom(thread)); + } if (signaling_result == RESULT_SUCCESS) { const auto itr = std::find_if( sync_objects.begin(), sync_objects.end(), diff --git a/src/core/hle/kernel/synchronization_object.cpp b/src/core/hle/kernel/synchronization_object.cpp index 43f3eef18..be9e09106 100644 --- a/src/core/hle/kernel/synchronization_object.cpp +++ b/src/core/hle/kernel/synchronization_object.cpp @@ -102,6 +102,10 @@ void SynchronizationObject::WakeupAllWaitingThreads() { } } +void SynchronizationObject::ClearWaitingThreads() { + waiting_threads.clear(); +} + const std::vector>& SynchronizationObject::GetWaitingThreads() const { return waiting_threads; } diff --git a/src/core/hle/kernel/synchronization_object.h b/src/core/hle/kernel/synchronization_object.h index 0a0d069e0..a35544ac1 100644 --- a/src/core/hle/kernel/synchronization_object.h +++ b/src/core/hle/kernel/synchronization_object.h @@ -68,6 +68,8 @@ public: /// Get a const reference to the waiting threads list for debug use const std::vector>& GetWaitingThreads() const; + void ClearWaitingThreads(); + protected: bool is_signaled{}; // Tells if this sync object is signalled; diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index fb97535a3..a645ee3a2 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -49,12 +49,12 @@ Thread::~Thread() = default; void Thread::Stop() { SchedulerLock lock(kernel); // Cancel any outstanding wakeup events for this thread + Signal(); Core::System::GetInstance().CoreTiming().UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(), global_handle); kernel.GlobalHandleTable().Close(global_handle); global_handle = 0; SetStatus(ThreadStatus::Dead); - Signal(); owner_process->UnregisterThread(this); diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp index 0b8f0d993..dab5fc4c6 100644 --- a/src/core/hle/kernel/time_manager.cpp +++ b/src/core/hle/kernel/time_manager.cpp @@ -8,15 +8,21 @@ #include "core/core_timing_util.h" #include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/scheduler.h" #include "core/hle/kernel/thread.h" #include "core/hle/kernel/time_manager.h" namespace Kernel { -TimeManager::TimeManager(Core::System& system) : system{system} { +TimeManager::TimeManager(Core::System& system_) : system{system_} { time_manager_event_type = Core::Timing::CreateEvent( "Kernel::TimeManagerCallback", [this](u64 thread_handle, [[maybe_unused]] s64 cycles_late) { + SchedulerLock lock(system.Kernel()); Handle proper_handle = static_cast(thread_handle); + if (cancelled_events[proper_handle]) { + return; + } + event_fired[proper_handle] = true; std::shared_ptr thread = this->system.Kernel().RetrieveThreadFromGlobalHandleTable(proper_handle); thread->OnWakeUp(); @@ -24,14 +30,16 @@ TimeManager::TimeManager(Core::System& system) : system{system} { } void TimeManager::ScheduleTimeEvent(Handle& event_handle, Thread* timetask, s64 nanoseconds) { + event_handle = timetask->GetGlobalHandle(); if (nanoseconds > 0) { ASSERT(timetask); - event_handle = timetask->GetGlobalHandle(); const s64 cycles = Core::Timing::nsToCycles(std::chrono::nanoseconds{nanoseconds}); system.CoreTiming().ScheduleEvent(cycles, time_manager_event_type, event_handle); } else { event_handle = InvalidHandle; } + cancelled_events[event_handle] = false; + event_fired[event_handle] = false; } void TimeManager::UnscheduleTimeEvent(Handle event_handle) { @@ -39,6 +47,12 @@ void TimeManager::UnscheduleTimeEvent(Handle event_handle) { return; } system.CoreTiming().UnscheduleEvent(time_manager_event_type, event_handle); + cancelled_events[event_handle] = true; +} + +void TimeManager::CancelTimeEvent(Thread* time_task) { + Handle event_handle = time_task->GetGlobalHandle(); + UnscheduleTimeEvent(event_handle); } } // namespace Kernel diff --git a/src/core/hle/kernel/time_manager.h b/src/core/hle/kernel/time_manager.h index eaec486d1..3080ac838 100644 --- a/src/core/hle/kernel/time_manager.h +++ b/src/core/hle/kernel/time_manager.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include "core/hle/kernel/object.h" @@ -35,9 +36,13 @@ public: /// Unschedule an existing time event void UnscheduleTimeEvent(Handle event_handle); + void CancelTimeEvent(Thread* time_task); + private: Core::System& system; std::shared_ptr time_manager_event_type; + std::unordered_map cancelled_events; + std::unordered_map event_fired; }; } // namespace Kernel From 3902067008abed4ca748e2f6d4ad582748f9ed52 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Thu, 27 Feb 2020 10:28:44 -0400 Subject: [PATCH 152/252] SVC: Add locks to the memory management. --- src/core/hle/kernel/svc.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index a5193063b..279fe5888 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -135,6 +135,7 @@ enum class ResourceLimitValueType { ResultVal RetrieveResourceLimitValue(Core::System& system, Handle resource_limit, u32 resource_type, ResourceLimitValueType value_type) { + std::lock_guard lock{HLE::g_hle_lock}; const auto type = static_cast(resource_type); if (!IsValidResourceType(type)) { LOG_ERROR(Kernel_SVC, "Invalid resource limit type: '{}'", resource_type); @@ -162,6 +163,7 @@ ResultVal RetrieveResourceLimitValue(Core::System& system, Handle resource_ /// Set the process heap to a given Size. It can both extend and shrink the heap. static ResultCode SetHeapSize(Core::System& system, VAddr* heap_addr, u64 heap_size) { + std::lock_guard lock{HLE::g_hle_lock}; LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", heap_size); // Size must be a multiple of 0x200000 (2MB) and be equal to or less than 8GB. @@ -192,6 +194,7 @@ static ResultCode SetHeapSize32(Core::System& system, u32* heap_addr, u32 heap_s static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask, u32 attribute) { + std::lock_guard lock{HLE::g_hle_lock}; LOG_DEBUG(Kernel_SVC, "called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address, size, mask, attribute); @@ -230,6 +233,7 @@ static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 si /// Maps a memory range into a different range. static ResultCode MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) { + std::lock_guard lock{HLE::g_hle_lock}; LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, src_addr, size); @@ -245,6 +249,7 @@ static ResultCode MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr /// Unmaps a region that was previously mapped with svcMapMemory static ResultCode UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) { + std::lock_guard lock{HLE::g_hle_lock}; LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, src_addr, size); @@ -261,6 +266,7 @@ static ResultCode UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_ad /// Connect to an OS service given the port name, returns the handle to the port to out static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle, VAddr port_name_address) { + std::lock_guard lock{HLE::g_hle_lock}; auto& memory = system.Memory(); if (!memory.IsValidVirtualAddress(port_name_address)) { @@ -309,6 +315,7 @@ static ResultCode ConnectToNamedPort32(Core::System& system, Handle* out_handle, /// Makes a blocking IPC call to an OS service. static ResultCode SendSyncRequest(Core::System& system, Handle handle) { + std::lock_guard lock{HLE::g_hle_lock}; const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); std::shared_ptr session = handle_table.Get(handle); if (!session) { @@ -639,6 +646,7 @@ static void OutputDebugString([[maybe_unused]] Core::System& system, VAddr addre /// Gets system/memory information for the current process static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 handle, u64 info_sub_id) { + std::lock_guard lock{HLE::g_hle_lock}; LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id, info_sub_id, handle); @@ -904,6 +912,7 @@ static ResultCode GetInfo32(Core::System& system, u32* result_low, u32* result_h /// Maps memory at a desired address static ResultCode MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) { + std::lock_guard lock{HLE::g_hle_lock}; LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size); if (!Common::Is4KBAligned(addr)) { @@ -953,6 +962,7 @@ static ResultCode MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) /// Unmaps memory previously mapped via MapPhysicalMemory static ResultCode UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size) { + std::lock_guard lock{HLE::g_hle_lock}; LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size); if (!Common::Is4KBAligned(addr)) { @@ -1129,6 +1139,7 @@ static u32 GetCurrentProcessorNumber(Core::System& system) { static ResultCode MapSharedMemory(Core::System& system, Handle shared_memory_handle, VAddr addr, u64 size, u32 permissions) { + std::lock_guard lock{HLE::g_hle_lock}; LOG_TRACE(Kernel_SVC, "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}", shared_memory_handle, addr, size, permissions); @@ -1202,6 +1213,7 @@ static ResultCode MapSharedMemory(Core::System& system, Handle shared_memory_han static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address, VAddr page_info_address, Handle process_handle, VAddr address) { + std::lock_guard lock{HLE::g_hle_lock}; LOG_TRACE(Kernel_SVC, "called process=0x{:08X} address={:X}", process_handle, address); const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); std::shared_ptr process = handle_table.Get(process_handle); @@ -1782,6 +1794,7 @@ static ResultCode ResetSignal(Core::System& system, Handle handle) { /// Creates a TransferMemory object static ResultCode CreateTransferMemory(Core::System& system, Handle* handle, VAddr addr, u64 size, u32 permissions) { + std::lock_guard lock{HLE::g_hle_lock}; LOG_DEBUG(Kernel_SVC, "called addr=0x{:X}, size=0x{:X}, perms=0x{:08X}", addr, size, permissions); @@ -1993,6 +2006,7 @@ static ResultCode GetProcessInfo(Core::System& system, u64* out, Handle process_ } static ResultCode CreateResourceLimit(Core::System& system, Handle* out_handle) { + std::lock_guard lock{HLE::g_hle_lock}; LOG_DEBUG(Kernel_SVC, "called"); auto& kernel = system.Kernel(); @@ -2439,6 +2453,13 @@ MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70)); void Call(Core::System& system, u32 immediate) { MICROPROFILE_SCOPE(Kernel_SVC); + auto& physical_core = system.CurrentPhysicalCore(); + if (physical_core.IsInterrupted()) { + auto& sched = physical_core.Scheduler(); + sched.TryDoContextSwitch(); + } + physical_core.ClearExclusive(); + const FunctionDef* info = system.CurrentProcess()->Is64BitProcess() ? GetSVCInfo64(immediate) : GetSVCInfo32(immediate); if (info) { From cc3aa959265480e1990b10ee37ebf1c0ade3da64 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Thu, 27 Feb 2020 10:47:02 -0400 Subject: [PATCH 153/252] NVFlinger: Lock race condition between CPU, Host Timing, VSync. --- src/core/hle/service/nvflinger/nvflinger.cpp | 2 ++ src/core/hle/service/nvflinger/nvflinger.h | 7 +++++++ src/core/hle/service/vi/vi.cpp | 2 ++ 3 files changed, 11 insertions(+) diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index aaf28995d..b97f71350 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp @@ -36,10 +36,12 @@ NVFlinger::NVFlinger(Core::System& system) : system(system) { displays.emplace_back(2, "Edid", system); displays.emplace_back(3, "Internal", system); displays.emplace_back(4, "Null", system); + guard = std::make_shared(); // Schedule the screen composition events composition_event = Core::Timing::CreateEvent("ScreenComposition", [this](u64 userdata, s64 ns_late) { + Lock(); Compose(); const auto ticks = GetNextTicks(); this->system.CoreTiming().ScheduleEvent(std::max(0LL, ticks - ns_late), diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h index 57a21f33b..02c081494 100644 --- a/src/core/hle/service/nvflinger/nvflinger.h +++ b/src/core/hle/service/nvflinger/nvflinger.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include #include #include @@ -79,6 +80,10 @@ public: s64 GetNextTicks() const; + std::unique_lock Lock() { + return std::unique_lock{*guard}; + } + private: /// Finds the display identified by the specified ID. VI::Display* FindDisplay(u64 display_id); @@ -108,6 +113,8 @@ private: /// Event that handles screen composition. std::shared_ptr composition_event; + std::shared_ptr guard; + Core::System& system; }; diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 46e14c2a3..157092074 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -511,6 +511,7 @@ private: LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id, static_cast(transaction), flags); + nv_flinger->Lock(); auto& buffer_queue = nv_flinger->FindBufferQueue(id); switch (transaction) { @@ -550,6 +551,7 @@ private: [=](std::shared_ptr thread, Kernel::HLERequestContext& ctx, Kernel::ThreadWakeupReason reason) { // Repeat TransactParcel DequeueBuffer when a buffer is available + nv_flinger->Lock(); auto& buffer_queue = nv_flinger->FindBufferQueue(id); auto result = buffer_queue.DequeueBuffer(width, height); ASSERT_MSG(result != std::nullopt, "Could not dequeue buffer."); From bd36eaf15d88c3875aba7032fe5124fbb150bd5d Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Thu, 27 Feb 2020 11:25:42 -0400 Subject: [PATCH 154/252] SVC: Correct races on physical core switching. --- src/core/cpu_manager.cpp | 11 ++++++----- src/core/hle/kernel/svc.cpp | 9 ++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index 241971ff3..904aacd97 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp @@ -79,12 +79,13 @@ void CpuManager::RunGuestThread() { sched.OnThreadStart(); } while (true) { - auto& physical_core = kernel.CurrentPhysicalCore(); - while (!physical_core.IsInterrupted()) { - physical_core.Run(); + auto* physical_core = &kernel.CurrentPhysicalCore(); + while (!physical_core->IsInterrupted()) { + physical_core->Run(); + physical_core = &kernel.CurrentPhysicalCore(); } - physical_core.ClearExclusive(); - auto& scheduler = physical_core.Scheduler(); + physical_core->ClearExclusive(); + auto& scheduler = physical_core->Scheduler(); scheduler.TryDoContextSwitch(); } } diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 279fe5888..1e6c60d78 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -2454,11 +2454,6 @@ void Call(Core::System& system, u32 immediate) { MICROPROFILE_SCOPE(Kernel_SVC); auto& physical_core = system.CurrentPhysicalCore(); - if (physical_core.IsInterrupted()) { - auto& sched = physical_core.Scheduler(); - sched.TryDoContextSwitch(); - } - physical_core.ClearExclusive(); const FunctionDef* info = system.CurrentProcess()->Is64BitProcess() ? GetSVCInfo64(immediate) : GetSVCInfo32(immediate); @@ -2471,6 +2466,10 @@ void Call(Core::System& system, u32 immediate) { } else { LOG_CRITICAL(Kernel_SVC, "Unknown SVC function 0x{:X}", immediate); } + auto& physical_core_2 = system.CurrentPhysicalCore(); + if (physical_core.CoreIndex() != physical_core_2.CoreIndex()) { + physical_core.Stop(); + } } } // namespace Kernel::Svc From 04e0f8776c26930d7dc8015e53914b11bf1929c1 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Thu, 27 Feb 2020 19:12:41 -0400 Subject: [PATCH 155/252] General: Add better safety for JIT use. --- src/core/arm/arm_interface.h | 10 ++++++++++ src/core/cpu_manager.cpp | 25 +++++++++++++++++++------ src/core/cpu_manager.h | 2 ++ src/core/hle/kernel/scheduler.cpp | 3 +++ src/core/hle/kernel/svc.cpp | 6 +++++- 5 files changed, 39 insertions(+), 7 deletions(-) diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index 87a1c29cc..be9f3703a 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h @@ -6,6 +6,7 @@ #include #include +#include #include "common/common_types.h" namespace Common { @@ -164,6 +165,14 @@ public: std::string name; }; + void Lock() { + guard.lock(); + } + + void Unlock() { + guard.unlock(); + } + std::vector GetBacktrace() const; /// fp (= r29) points to the last frame record. @@ -178,6 +187,7 @@ protected: /// System context that this ARM interface is running under. System& system; CPUInterruptHandler& interrupt_handler; + std::mutex guard; }; } // namespace Core diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index 904aacd97..9a261968a 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp @@ -46,6 +46,11 @@ void CpuManager::GuestThreadFunction(void* cpu_manager_) { cpu_manager->RunGuestThread(); } +void CpuManager::GuestRewindFunction(void* cpu_manager_) { + CpuManager* cpu_manager = static_cast(cpu_manager_); + cpu_manager->RunGuestLoop(); +} + void CpuManager::IdleThreadFunction(void* cpu_manager_) { CpuManager* cpu_manager = static_cast(cpu_manager_); cpu_manager->RunIdleThread(); @@ -78,14 +83,22 @@ void CpuManager::RunGuestThread() { auto& sched = kernel.CurrentScheduler(); sched.OnThreadStart(); } + RunGuestLoop(); +} + +void CpuManager::RunGuestLoop() { + auto& kernel = system.Kernel(); + auto* thread = kernel.CurrentScheduler().GetCurrentThread(); + auto host_context = thread->GetHostContext(); + host_context->SetRewindPoint(std::function(GuestRewindFunction), this); + host_context.reset(); while (true) { - auto* physical_core = &kernel.CurrentPhysicalCore(); - while (!physical_core->IsInterrupted()) { - physical_core->Run(); - physical_core = &kernel.CurrentPhysicalCore(); + auto& physical_core = kernel.CurrentPhysicalCore(); + while (!physical_core.IsInterrupted()) { + physical_core.Run(); } - physical_core->ClearExclusive(); - auto& scheduler = physical_core->Scheduler(); + physical_core.ClearExclusive(); + auto& scheduler = physical_core.Scheduler(); scheduler.TryDoContextSwitch(); } } diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h index 8103ae857..e83ab20f9 100644 --- a/src/core/cpu_manager.h +++ b/src/core/cpu_manager.h @@ -42,10 +42,12 @@ public: private: static void GuestThreadFunction(void* cpu_manager); + static void GuestRewindFunction(void* cpu_manager); static void IdleThreadFunction(void* cpu_manager); static void SuspendThreadFunction(void* cpu_manager); void RunGuestThread(); + void RunGuestLoop(); void RunIdleThread(); void RunSuspendThread(); diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index 758fa8188..727d2e6cc 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp @@ -581,6 +581,7 @@ void Scheduler::SwitchContextStep2() { if (new_thread) { new_thread->context_guard.lock(); + cpu_core.Lock(); ASSERT_MSG(new_thread->GetProcessorID() == s32(this->core_id), "Thread must be assigned to this core."); ASSERT_MSG(new_thread->GetStatus() == ThreadStatus::Ready, @@ -601,6 +602,7 @@ void Scheduler::SwitchContextStep2() { cpu_core.LoadContext(new_thread->GetContext64()); cpu_core.SetTlsAddress(new_thread->GetTLSAddress()); cpu_core.SetTPIDR_EL0(new_thread->GetTPIDR_EL0()); + cpu_core.ClearExclusiveState(); } } else { current_thread = nullptr; @@ -639,6 +641,7 @@ void Scheduler::SwitchContext() { } previous_thread->SetIsRunning(false); previous_thread->context_guard.unlock(); + cpu_core.Unlock(); } std::shared_ptr old_context; diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 1e6c60d78..b535593c7 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -10,6 +10,7 @@ #include "common/alignment.h" #include "common/assert.h" +#include "common/fiber.h" #include "common/logging/log.h" #include "common/microprofile.h" #include "common/string_util.h" @@ -2468,7 +2469,10 @@ void Call(Core::System& system, u32 immediate) { } auto& physical_core_2 = system.CurrentPhysicalCore(); if (physical_core.CoreIndex() != physical_core_2.CoreIndex()) { - physical_core.Stop(); + LOG_CRITICAL(Kernel_SVC, "Rewinding"); + auto* thread = physical_core_2.Scheduler().GetCurrentThread(); + auto* host_context = thread->GetHostContext().get(); + host_context->Rewind(); } } From 2a8837ff51a9cf5a0123489dba5f7ab48373c2d3 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Fri, 28 Feb 2020 09:42:06 -0400 Subject: [PATCH 156/252] General: Add Asserts --- src/core/hardware_properties.h | 4 ++++ src/core/hle/kernel/scheduler.cpp | 18 ++++++++++++++++++ src/core/hle/kernel/scheduler.h | 1 + src/core/hle/kernel/svc.cpp | 1 + 4 files changed, 24 insertions(+) diff --git a/src/core/hardware_properties.h b/src/core/hardware_properties.h index b04e046ed..456b41e1b 100644 --- a/src/core/hardware_properties.h +++ b/src/core/hardware_properties.h @@ -42,6 +42,10 @@ struct EmuThreadHandle { constexpr u32 invalid_handle = 0xFFFFFFFF; return {invalid_handle, invalid_handle}; } + + bool IsInvalid() const { + return (*this) == InvalidHandle(); + } }; } // namespace Core diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index 727d2e6cc..d67d3c5cd 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp @@ -44,6 +44,7 @@ void GlobalScheduler::RemoveThread(std::shared_ptr thread) { } u32 GlobalScheduler::SelectThreads() { + ASSERT(is_locked); const auto update_thread = [](Thread* thread, Scheduler& sched) { sched.guard.lock(); if (thread != sched.selected_thread.get()) { @@ -136,6 +137,7 @@ u32 GlobalScheduler::SelectThreads() { } bool GlobalScheduler::YieldThread(Thread* yielding_thread) { + ASSERT(is_locked); // Note: caller should use critical section, etc. const u32 core_id = static_cast(yielding_thread->GetProcessorID()); const u32 priority = yielding_thread->GetPriority(); @@ -149,6 +151,7 @@ bool GlobalScheduler::YieldThread(Thread* yielding_thread) { } bool GlobalScheduler::YieldThreadAndBalanceLoad(Thread* yielding_thread) { + ASSERT(is_locked); // Note: caller should check if !thread.IsSchedulerOperationRedundant and use critical section, // etc. const u32 core_id = static_cast(yielding_thread->GetProcessorID()); @@ -197,6 +200,7 @@ bool GlobalScheduler::YieldThreadAndBalanceLoad(Thread* yielding_thread) { } bool GlobalScheduler::YieldThreadAndWaitForLoadBalancing(Thread* yielding_thread) { + ASSERT(is_locked); // Note: caller should check if !thread.IsSchedulerOperationRedundant and use critical section, // etc. Thread* winner = nullptr; @@ -237,6 +241,7 @@ bool GlobalScheduler::YieldThreadAndWaitForLoadBalancing(Thread* yielding_thread } void GlobalScheduler::PreemptThreads() { + ASSERT(is_locked); for (std::size_t core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { const u32 priority = preemption_priorities[core_id]; @@ -339,33 +344,40 @@ void GlobalScheduler::EnableInterruptAndSchedule(u32 cores_pending_reschedule, } void GlobalScheduler::Suggest(u32 priority, std::size_t core, Thread* thread) { + ASSERT(is_locked); suggested_queue[core].add(thread, priority); } void GlobalScheduler::Unsuggest(u32 priority, std::size_t core, Thread* thread) { + ASSERT(is_locked); suggested_queue[core].remove(thread, priority); } void GlobalScheduler::Schedule(u32 priority, std::size_t core, Thread* thread) { + ASSERT(is_locked); ASSERT_MSG(thread->GetProcessorID() == s32(core), "Thread must be assigned to this core."); scheduled_queue[core].add(thread, priority); } void GlobalScheduler::SchedulePrepend(u32 priority, std::size_t core, Thread* thread) { + ASSERT(is_locked); ASSERT_MSG(thread->GetProcessorID() == s32(core), "Thread must be assigned to this core."); scheduled_queue[core].add(thread, priority, false); } void GlobalScheduler::Reschedule(u32 priority, std::size_t core, Thread* thread) { + ASSERT(is_locked); scheduled_queue[core].remove(thread, priority); scheduled_queue[core].add(thread, priority); } void GlobalScheduler::Unschedule(u32 priority, std::size_t core, Thread* thread) { + ASSERT(is_locked); scheduled_queue[core].remove(thread, priority); } void GlobalScheduler::TransferToCore(u32 priority, s32 destination_core, Thread* thread) { + ASSERT(is_locked); const bool schedulable = thread->GetPriority() < THREADPRIO_COUNT; const s32 source_core = thread->GetProcessorID(); if (source_core == destination_core || !schedulable) { @@ -399,6 +411,7 @@ void GlobalScheduler::AdjustSchedulingOnStatus(Thread* thread, u32 old_flags) { if (old_flags == thread->scheduling_state) { return; } + ASSERT(is_locked); if (static_cast(old_flags & static_cast(ThreadSchedMasks::LowMask)) == ThreadSchedStatus::Runnable) { @@ -434,6 +447,7 @@ void GlobalScheduler::AdjustSchedulingOnPriority(Thread* thread, u32 old_priorit if (thread->GetSchedulingStatus() != ThreadSchedStatus::Runnable) { return; } + ASSERT(is_locked); if (thread->processor_id >= 0) { Unschedule(old_priority, static_cast(thread->processor_id), thread); } @@ -472,6 +486,7 @@ void GlobalScheduler::AdjustSchedulingOnAffinity(Thread* thread, u64 old_affinit thread->current_priority >= THREADPRIO_COUNT) { return; } + ASSERT(is_locked); for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { if (((old_affinity_mask >> core) & 1) != 0) { @@ -507,10 +522,12 @@ void GlobalScheduler::Shutdown() { void GlobalScheduler::Lock() { Core::EmuThreadHandle current_thread = kernel.GetCurrentEmuThreadID(); + ASSERT(!current_thread.IsInvalid()); if (current_thread == current_owner) { ++scope_lock; } else { inner_lock.lock(); + is_locked = true; current_owner = current_thread; ASSERT(current_owner != Core::EmuThreadHandle::InvalidHandle()); scope_lock = 1; @@ -526,6 +543,7 @@ void GlobalScheduler::Unlock() { Core::EmuThreadHandle leaving_thread = current_owner; current_owner = Core::EmuThreadHandle::InvalidHandle(); scope_lock = 1; + is_locked = false; inner_lock.unlock(); EnableInterruptAndSchedule(cores_pending_reschedule, leaving_thread); } diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h index f5f64338f..f26a554f5 100644 --- a/src/core/hle/kernel/scheduler.h +++ b/src/core/hle/kernel/scheduler.h @@ -182,6 +182,7 @@ private: std::array preemption_priorities = {59, 59, 59, 62}; /// Scheduler lock mechanisms. + bool is_locked{}; std::mutex inner_lock{}; // TODO(Blinkhawk): Replace for a SpinLock std::atomic scope_lock{}; Core::EmuThreadHandle current_owner{Core::EmuThreadHandle::InvalidHandle()}; diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index b535593c7..4c1040a3b 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -1657,6 +1657,7 @@ static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_ update_val = thread->GetWaitHandle(); } } while (!monitor.ExclusiveWrite32(current_core, mutex_address, update_val)); + monitor.ClearExclusive(); if (mutex_val == 0) { // We were able to acquire the mutex, resume this thread. ASSERT(thread->GetStatus() == ThreadStatus::WaitCondVar); From de5b521c0900bbd4d7012ffa3dfd63cc8f3d4371 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Tue, 3 Mar 2020 11:04:37 -0400 Subject: [PATCH 157/252] Process: Protect TLS region and Modules. --- src/core/hle/kernel/process.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index ea5fe5b29..f9d7c024d 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -22,6 +22,7 @@ #include "core/hle/kernel/resource_limit.h" #include "core/hle/kernel/scheduler.h" #include "core/hle/kernel/thread.h" +#include "core/hle/lock.h" #include "core/memory.h" #include "core/settings.h" @@ -345,6 +346,7 @@ static auto FindTLSPageWithAvailableSlots(std::vector& tls_pages) { } VAddr Process::CreateTLSRegion() { + SchedulerLock lock(system.Kernel()); if (auto tls_page_iter{FindTLSPageWithAvailableSlots(tls_pages)}; tls_page_iter != tls_pages.cend()) { return *tls_page_iter->ReserveSlot(); @@ -375,6 +377,7 @@ VAddr Process::CreateTLSRegion() { } void Process::FreeTLSRegion(VAddr tls_address) { + SchedulerLock lock(system.Kernel()); const VAddr aligned_address = Common::AlignDown(tls_address, Core::Memory::PAGE_SIZE); auto iter = std::find_if(tls_pages.begin(), tls_pages.end(), [aligned_address](const auto& page) { @@ -389,6 +392,7 @@ void Process::FreeTLSRegion(VAddr tls_address) { } void Process::LoadModule(CodeSet code_set, VAddr base_addr) { + std::lock_guard lock{HLE::g_hle_lock}; const auto ReprotectSegment = [&](const CodeSet::Segment& segment, Memory::MemoryPermission permission) { page_table->SetCodeMemoryPermission(segment.addr + base_addr, segment.size, permission); From 75e10578f12cf64bd734388ba80b5f5a46ca6133 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Tue, 3 Mar 2020 13:02:50 -0400 Subject: [PATCH 158/252] Core: Correct HLE Event Callbacks and other issues. --- src/core/hle/kernel/hle_ipc.cpp | 19 +++++++------ src/core/hle/kernel/svc.cpp | 5 ++-- src/core/hle/kernel/synchronization.cpp | 1 + src/core/hle/kernel/thread.cpp | 37 ++++++++++++------------- src/core/hle/kernel/thread.h | 16 ++++++----- 5 files changed, 40 insertions(+), 38 deletions(-) diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index 955d5fe1c..e74d91670 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -21,8 +21,8 @@ #include "core/hle/kernel/object.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/readable_event.h" -#include "core/hle/kernel/server_session.h" #include "core/hle/kernel/scheduler.h" +#include "core/hle/kernel/server_session.h" #include "core/hle/kernel/thread.h" #include "core/hle/kernel/time_manager.h" #include "core/hle/kernel/writable_event.h" @@ -49,14 +49,6 @@ std::shared_ptr HLERequestContext::SleepClientThread( const std::string& reason, u64 timeout, WakeupCallback&& callback, std::shared_ptr writable_event) { // Put the client thread to sleep until the wait event is signaled or the timeout expires. - thread->SetHLECallback( - [context = *this, callback](ThreadWakeupReason reason, std::shared_ptr thread, - std::shared_ptr object, - std::size_t index) mutable -> bool { - callback(thread, context, reason); - context.WriteToOutgoingCommandBuffer(*thread); - return true; - }); if (!writable_event) { // Create event if not provided @@ -67,6 +59,15 @@ std::shared_ptr HLERequestContext::SleepClientThread( { Handle event_handle = InvalidHandle; SchedulerLockAndSleep lock(kernel, event_handle, thread.get(), timeout); + thread->SetHLECallback( + [context = *this, callback](std::shared_ptr thread) mutable -> bool { + ThreadWakeupReason reason = thread->GetSignalingResult() == RESULT_TIMEOUT + ? ThreadWakeupReason::Timeout + : ThreadWakeupReason::Signal; + callback(thread, context, reason); + context.WriteToOutgoingCommandBuffer(*thread); + return true; + }); const auto readable_event{writable_event->GetReadableEvent()}; writable_event->Clear(); thread->SetStatus(ThreadStatus::WaitHLEEvent); diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 4c1040a3b..9f46a1758 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -333,17 +333,16 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) { thread->SetStatus(ThreadStatus::WaitIPC); session->SendSyncRequest(SharedFrom(thread), system.Memory()); } - ResultCode result = thread->GetSignalingResult(); if (thread->HasHLECallback()) { Handle event_handle = thread->GetHLETimeEvent(); if (event_handle != InvalidHandle) { auto& time_manager = system.Kernel().TimeManager(); time_manager.UnscheduleTimeEvent(event_handle); } - thread->InvokeHLECallback(ThreadWakeupReason::Timeout, SharedFrom(thread), nullptr, 0); + thread->InvokeHLECallback(SharedFrom(thread)); } - return result; + return RESULT_SUCCESS; } static ResultCode SendSyncRequest32(Core::System& system, Handle handle) { diff --git a/src/core/hle/kernel/synchronization.cpp b/src/core/hle/kernel/synchronization.cpp index c60c5bb42..4ee7ad93c 100644 --- a/src/core/hle/kernel/synchronization.cpp +++ b/src/core/hle/kernel/synchronization.cpp @@ -28,6 +28,7 @@ void Synchronization::SignalObject(SynchronizationObject& obj) const { time_manager.CancelTimeEvent(thread.get()); } } + obj.ClearWaitingThreads(); } } diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index a645ee3a2..16babe71a 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -47,19 +47,21 @@ Thread::Thread(KernelCore& kernel) : SynchronizationObject{kernel} {} Thread::~Thread() = default; void Thread::Stop() { - SchedulerLock lock(kernel); - // Cancel any outstanding wakeup events for this thread - Signal(); - Core::System::GetInstance().CoreTiming().UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(), - global_handle); - kernel.GlobalHandleTable().Close(global_handle); + { + SchedulerLock lock(kernel); + // Cancel any outstanding wakeup events for this thread + Signal(); + Core::System::GetInstance().CoreTiming().UnscheduleEvent( + kernel.ThreadWakeupCallbackEventType(), global_handle); + kernel.GlobalHandleTable().Close(global_handle); + SetStatus(ThreadStatus::Dead); + + owner_process->UnregisterThread(this); + + // Mark the TLS slot in the thread's page as free. + owner_process->FreeTLSRegion(tls_address); + } global_handle = 0; - SetStatus(ThreadStatus::Dead); - - owner_process->UnregisterThread(this); - - // Mark the TLS slot in the thread's page as free. - owner_process->FreeTLSRegion(tls_address); } void Thread::WakeAfterDelay(s64 nanoseconds) { @@ -112,8 +114,6 @@ void Thread::ResumeFromWait() { return; } - hle_callback = nullptr; - if (activity == ThreadActivity::Paused) { SetStatus(ThreadStatus::Paused); return; @@ -398,14 +398,13 @@ bool Thread::AllSynchronizationObjectsReady() const { bool Thread::InvokeWakeupCallback(ThreadWakeupReason reason, std::shared_ptr thread, std::shared_ptr object, std::size_t index) { - ASSERT(hle_callback); - return hle_callback(reason, std::move(thread), std::move(object), index); + ASSERT(wakeup_callback); + return wakeup_callback(reason, std::move(thread), std::move(object), index); } -bool Thread::InvokeHLECallback(ThreadWakeupReason reason, std::shared_ptr thread, - std::shared_ptr object, std::size_t index) { +bool Thread::InvokeHLECallback(std::shared_ptr thread) { ASSERT(hle_callback); - return hle_callback(reason, std::move(thread), std::move(object), index); + return hle_callback(std::move(thread)); } void Thread::SetActivity(ThreadActivity value) { diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 04496f96e..c4c9d69ec 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -448,7 +448,7 @@ public: } bool HasWakeupCallback() const { - return hle_callback != nullptr; + return wakeup_callback != nullptr; } bool HasHLECallback() const { @@ -456,10 +456,10 @@ public: } void SetWakeupCallback(WakeupCallback callback) { - hle_callback = std::move(callback); + wakeup_callback = std::move(callback); } - void SetHLECallback(WakeupCallback callback) { + void SetHLECallback(HLECallback callback) { hle_callback = std::move(callback); } @@ -487,8 +487,7 @@ public: */ bool InvokeWakeupCallback(ThreadWakeupReason reason, std::shared_ptr thread, std::shared_ptr object, std::size_t index); - bool InvokeHLECallback(ThreadWakeupReason reason, std::shared_ptr thread, - std::shared_ptr object, std::size_t index); + bool InvokeHLECallback(std::shared_ptr thread); u32 GetIdealCore() const { return ideal_core; @@ -622,8 +621,11 @@ private: /// Callback that will be invoked when the thread is resumed from a waiting state. If the thread /// was waiting via WaitSynchronization then the object will be the last object that became - /// available. In case of a timeout, the object will be nullptr. - WakeupCallback hle_callback; + /// available. In case of a timeout, the object will be nullptr. DEPRECATED + WakeupCallback wakeup_callback; + + /// Callback for HLE Events + HLECallback hle_callback; Handle hle_time_event; Scheduler* scheduler = nullptr; From b4dc01f16affe4baa9a7ab5ac4b240e03c03ae67 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Tue, 3 Mar 2020 13:37:11 -0400 Subject: [PATCH 159/252] Kernel: Correct Signal on Thread Death and Setup Sync Objects on Thread for Debugging --- src/core/hle/kernel/synchronization.cpp | 3 +++ src/core/hle/kernel/thread.cpp | 15 +++++++-------- src/core/hle/kernel/thread.h | 14 +++++++------- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/core/hle/kernel/synchronization.cpp b/src/core/hle/kernel/synchronization.cpp index 4ee7ad93c..ac43a7094 100644 --- a/src/core/hle/kernel/synchronization.cpp +++ b/src/core/hle/kernel/synchronization.cpp @@ -70,6 +70,8 @@ std::pair Synchronization::WaitFor( for (auto& object : sync_objects) { object->AddWaitingThread(SharedFrom(thread)); } + + thread->SetSynchronizationObjects(&sync_objects); thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT); thread->SetStatus(ThreadStatus::WaitSynch); } @@ -83,6 +85,7 @@ std::pair Synchronization::WaitFor( SchedulerLock lock(kernel); ResultCode signaling_result = thread->GetSignalingResult(); SynchronizationObject* signaling_object = thread->GetSignalingObject(); + thread->SetSynchronizationObjects(nullptr); for (auto& obj : sync_objects) { obj->RemoveWaitingThread(SharedFrom(thread)); } diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 16babe71a..fb1751860 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -50,11 +50,11 @@ void Thread::Stop() { { SchedulerLock lock(kernel); // Cancel any outstanding wakeup events for this thread - Signal(); Core::System::GetInstance().CoreTiming().UnscheduleEvent( kernel.ThreadWakeupCallbackEventType(), global_handle); - kernel.GlobalHandleTable().Close(global_handle); SetStatus(ThreadStatus::Dead); + Signal(); + kernel.GlobalHandleTable().Close(global_handle); owner_process->UnregisterThread(this); @@ -81,7 +81,6 @@ void Thread::CancelWakeupTimer() { } void Thread::ResumeFromWait() { - ASSERT_MSG(wait_objects.empty(), "Thread is waking up while waiting for objects"); SchedulerLock lock(kernel); switch (status) { case ThreadStatus::Paused: @@ -219,7 +218,7 @@ ResultVal> Thread::Create(Core::System& system, ThreadTy thread->processor_id = processor_id; thread->ideal_core = processor_id; thread->affinity_mask = 1ULL << processor_id; - thread->wait_objects.clear(); + thread->wait_objects = nullptr; thread->mutex_wait_address = 0; thread->condvar_wait_address = 0; thread->wait_handle = 0; @@ -272,9 +271,9 @@ void Thread::SetSynchronizationResults(SynchronizationObject* object, ResultCode } s32 Thread::GetSynchronizationObjectIndex(std::shared_ptr object) const { - ASSERT_MSG(!wait_objects.empty(), "Thread is not waiting for anything"); - const auto match = std::find(wait_objects.rbegin(), wait_objects.rend(), object); - return static_cast(std::distance(match, wait_objects.rend()) - 1); + ASSERT_MSG(!wait_objects->empty(), "Thread is not waiting for anything"); + const auto match = std::find(wait_objects->rbegin(), wait_objects->rend(), object); + return static_cast(std::distance(match, wait_objects->rend()) - 1); } VAddr Thread::GetCommandBufferAddress() const { @@ -389,7 +388,7 @@ void Thread::UpdatePriority() { } bool Thread::AllSynchronizationObjectsReady() const { - return std::none_of(wait_objects.begin(), wait_objects.end(), + return std::none_of(wait_objects->begin(), wait_objects->end(), [this](const std::shared_ptr& object) { return object->ShouldWait(this); }); diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index c4c9d69ec..7b6d1b4ec 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -21,7 +21,7 @@ class Fiber; namespace Core { class System; -} +} // namespace Core namespace Kernel { @@ -386,18 +386,18 @@ public: } const ThreadSynchronizationObjects& GetSynchronizationObjects() const { - return wait_objects; + return *wait_objects; } - void SetSynchronizationObjects(ThreadSynchronizationObjects objects) { - wait_objects = std::move(objects); + void SetSynchronizationObjects(ThreadSynchronizationObjects* objects) { + wait_objects = objects; } void ClearSynchronizationObjects() { - for (const auto& waiting_object : wait_objects) { + for (const auto& waiting_object : *wait_objects) { waiting_object->RemoveWaitingThread(SharedFrom(this)); } - wait_objects.clear(); + wait_objects->clear(); } /// Determines whether all the objects this thread is waiting on are ready. @@ -595,7 +595,7 @@ private: /// Objects that the thread is waiting on, in the same order as they were /// passed to WaitSynchronization. - ThreadSynchronizationObjects wait_objects; + ThreadSynchronizationObjects* wait_objects; SynchronizationObject* signaling_object; ResultCode signaling_result{RESULT_SUCCESS}; From 07993ac8c8c66bbf638dddc7750106f6dfb0e09b Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Tue, 3 Mar 2020 15:50:38 -0400 Subject: [PATCH 160/252] Kernel: Corrections to Scheduling. --- src/core/core_timing.cpp | 11 +++++++---- src/core/core_timing.h | 2 +- src/core/hle/kernel/scheduler.cpp | 26 +++++++++++++------------- src/core/hle/kernel/scheduler.h | 2 ++ src/core/hle/kernel/svc.cpp | 1 - 5 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 5a7abcfca..c91ae9975 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -154,7 +154,7 @@ void CoreTiming::RemoveEvent(const std::shared_ptr& event_type) { basic_lock.unlock(); } -std::optional CoreTiming::Advance() { +std::optional CoreTiming::Advance() { advance_lock.lock(); basic_lock.lock(); global_timer = GetGlobalTimeNs().count(); @@ -170,10 +170,11 @@ std::optional CoreTiming::Advance() { } basic_lock.lock(); + global_timer = GetGlobalTimeNs().count(); } if (!event_queue.empty()) { - const u64 next_time = event_queue.front().time - global_timer; + const s64 next_time = event_queue.front().time - global_timer; basic_lock.unlock(); advance_lock.unlock(); return next_time; @@ -191,8 +192,10 @@ void CoreTiming::ThreadLoop() { paused_set = false; const auto next_time = Advance(); if (next_time) { - std::chrono::nanoseconds next_time_ns = std::chrono::nanoseconds(*next_time); - event.WaitFor(next_time_ns); + if (*next_time > 0) { + std::chrono::nanoseconds next_time_ns = std::chrono::nanoseconds(*next_time); + event.WaitFor(next_time_ns); + } } else { wait_set = true; event.Wait(); diff --git a/src/core/core_timing.h b/src/core/core_timing.h index c70b605c8..032eb08aa 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h @@ -110,7 +110,7 @@ public: std::chrono::nanoseconds GetGlobalTimeNs() const; /// Checks for events manually and returns time in nanoseconds for next event, threadsafe. - std::optional Advance(); + std::optional Advance(); private: struct Event; diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index d67d3c5cd..da77967dd 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp @@ -47,13 +47,13 @@ u32 GlobalScheduler::SelectThreads() { ASSERT(is_locked); const auto update_thread = [](Thread* thread, Scheduler& sched) { sched.guard.lock(); - if (thread != sched.selected_thread.get()) { + if (thread != sched.selected_thread_set.get()) { if (thread == nullptr) { ++sched.idle_selection_count; } - sched.selected_thread = SharedFrom(thread); + sched.selected_thread_set = SharedFrom(thread); } - const bool reschedule_pending = sched.selected_thread != sched.current_thread; + const bool reschedule_pending = sched.selected_thread_set != sched.current_thread; sched.is_context_switch_pending = reschedule_pending; std::atomic_thread_fence(std::memory_order_seq_cst); sched.guard.unlock(); @@ -118,6 +118,8 @@ u32 GlobalScheduler::SelectThreads() { suggested); top_threads[candidate_core] = next; break; + } else { + suggested = nullptr; } } } @@ -590,7 +592,7 @@ void Scheduler::OnThreadStart() { } void Scheduler::SwitchContextStep2() { - Thread* previous_thread = current_thread.get(); + Thread* previous_thread = current_thread_prev.get(); Thread* new_thread = selected_thread.get(); // Load context of new thread @@ -606,8 +608,6 @@ void Scheduler::SwitchContextStep2() { "Thread must be ready to become running."); // Cancel any outstanding wakeup events for this thread - current_thread = SharedFrom(new_thread); - new_thread->SetStatus(ThreadStatus::Running); new_thread->SetIsRunning(true); auto* const thread_owner_process = current_thread->GetOwnerProcess(); @@ -622,21 +622,21 @@ void Scheduler::SwitchContextStep2() { cpu_core.SetTPIDR_EL0(new_thread->GetTPIDR_EL0()); cpu_core.ClearExclusiveState(); } - } else { - current_thread = nullptr; - // Note: We do not reset the current process and current page table when idling because - // technically we haven't changed processes, our threads are just paused. } - guard.unlock(); + + TryDoContextSwitch(); } void Scheduler::SwitchContext() { - Thread* previous_thread = current_thread.get(); + current_thread_prev = current_thread; + selected_thread = selected_thread_set; + Thread* previous_thread = current_thread_prev.get(); Thread* new_thread = selected_thread.get(); + current_thread = selected_thread; is_context_switch_pending = false; + guard.unlock(); if (new_thread == previous_thread) { - guard.unlock(); return; } diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h index f26a554f5..f73ca777e 100644 --- a/src/core/hle/kernel/scheduler.h +++ b/src/core/hle/kernel/scheduler.h @@ -249,6 +249,8 @@ private: std::shared_ptr current_thread = nullptr; std::shared_ptr selected_thread = nullptr; + std::shared_ptr current_thread_prev = nullptr; + std::shared_ptr selected_thread_set = nullptr; std::shared_ptr idle_thread = nullptr; Core::System& system; diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 9f46a1758..5e9dd43bf 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -316,7 +316,6 @@ static ResultCode ConnectToNamedPort32(Core::System& system, Handle* out_handle, /// Makes a blocking IPC call to an OS service. static ResultCode SendSyncRequest(Core::System& system, Handle handle) { - std::lock_guard lock{HLE::g_hle_lock}; const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); std::shared_ptr session = handle_table.Get(handle); if (!session) { From 1e987dbe8d9b5fbe778637c0b35cbc51b1a0956b Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Tue, 3 Mar 2020 15:59:09 -0400 Subject: [PATCH 161/252] Scheduler: Correct Select Threads Step 2. --- src/core/hle/kernel/scheduler.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index da77967dd..9329202c6 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp @@ -103,6 +103,7 @@ u32 GlobalScheduler::SelectThreads() { TransferToCore(suggested->GetPriority(), static_cast(core_id), suggested); break; } + suggested = nullptr; migration_candidates[num_candidates++] = suggested_core_id; } // Step 3: Select a suggested thread from another core From e4b175ade205095e7cc89e0f60c902c708d7d767 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Tue, 3 Mar 2020 17:19:44 -0400 Subject: [PATCH 162/252] SVC: Correct svcWaitForAddress and svcSignalToAddress. --- src/core/hle/kernel/address_arbiter.cpp | 230 ++++++++++++++++-------- src/core/hle/kernel/address_arbiter.h | 3 - src/core/hle/kernel/svc.cpp | 3 - src/core/hle/kernel/thread.h | 9 + 4 files changed, 169 insertions(+), 76 deletions(-) diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index 8475b698c..ebabde921 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp @@ -7,11 +7,15 @@ #include "common/assert.h" #include "common/common_types.h" +#include "core/arm/exclusive_monitor.h" #include "core/core.h" #include "core/hle/kernel/address_arbiter.h" #include "core/hle/kernel/errors.h" +#include "core/hle/kernel/handle_table.h" +#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/scheduler.h" #include "core/hle/kernel/thread.h" +#include "core/hle/kernel/time_manager.h" #include "core/hle/result.h" #include "core/memory.h" @@ -20,6 +24,7 @@ namespace Kernel { // Wake up num_to_wake (or all) threads in a vector. void AddressArbiter::WakeThreads(const std::vector>& waiting_threads, s32 num_to_wake) { + auto& time_manager = system.Kernel().TimeManager(); // Only process up to 'target' threads, unless 'target' is <= 0, in which case process // them all. std::size_t last = waiting_threads.size(); @@ -29,12 +34,20 @@ void AddressArbiter::WakeThreads(const std::vector>& wai // Signal the waiting threads. for (std::size_t i = 0; i < last; i++) { + if (waiting_threads[i]->GetStatus() != ThreadStatus::WaitArb) { + last++; + last = std::min(waiting_threads.size(), last); + continue; + } + + time_manager.CancelTimeEvent(waiting_threads[i].get()); + ASSERT(waiting_threads[i]->GetStatus() == ThreadStatus::WaitArb); - waiting_threads[i]->SetWaitSynchronizationResult(RESULT_SUCCESS); + waiting_threads[i]->SetSynchronizationResults(nullptr, RESULT_SUCCESS); RemoveThread(waiting_threads[i]); + waiting_threads[i]->WaitForArbitration(false); waiting_threads[i]->SetArbiterWaitAddress(0); waiting_threads[i]->ResumeFromWait(); - system.PrepareReschedule(waiting_threads[i]->GetProcessorID()); } } @@ -56,6 +69,7 @@ ResultCode AddressArbiter::SignalToAddress(VAddr address, SignalType type, s32 v } ResultCode AddressArbiter::SignalToAddressOnly(VAddr address, s32 num_to_wake) { + SchedulerLock lock(system.Kernel()); const std::vector> waiting_threads = GetThreadsWaitingOnAddress(address); WakeThreads(waiting_threads, num_to_wake); @@ -64,6 +78,7 @@ ResultCode AddressArbiter::SignalToAddressOnly(VAddr address, s32 num_to_wake) { ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake) { + SchedulerLock lock(system.Kernel()); auto& memory = system.Memory(); // Ensure that we can write to the address. @@ -71,16 +86,25 @@ ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32 return ERR_INVALID_ADDRESS_STATE; } - if (static_cast(memory.Read32(address)) != value) { - return ERR_INVALID_STATE; - } + const std::size_t current_core = system.CurrentCoreIndex(); + auto& monitor = system.Monitor(); + u32 current_value; + do { + monitor.SetExclusive(current_core, address); + current_value = memory.Read32(address); + + if (current_value != value) { + return ERR_INVALID_STATE; + } + current_value++; + } while (!monitor.ExclusiveWrite32(current_core, address, current_value)); - memory.Write32(address, static_cast(value + 1)); return SignalToAddressOnly(address, num_to_wake); } ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake) { + SchedulerLock lock(system.Kernel()); auto& memory = system.Memory(); // Ensure that we can write to the address. @@ -92,29 +116,34 @@ ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr a const std::vector> waiting_threads = GetThreadsWaitingOnAddress(address); - // Determine the modified value depending on the waiting count. + const std::size_t current_core = system.CurrentCoreIndex(); + auto& monitor = system.Monitor(); s32 updated_value; - if (num_to_wake <= 0) { - if (waiting_threads.empty()) { - updated_value = value + 1; - } else { - updated_value = value - 1; - } - } else { - if (waiting_threads.empty()) { - updated_value = value + 1; - } else if (waiting_threads.size() <= static_cast(num_to_wake)) { - updated_value = value - 1; - } else { - updated_value = value; - } - } + do { + monitor.SetExclusive(current_core, address); + updated_value = memory.Read32(address); - if (static_cast(memory.Read32(address)) != value) { - return ERR_INVALID_STATE; - } + if (updated_value != value) { + return ERR_INVALID_STATE; + } + // Determine the modified value depending on the waiting count. + if (num_to_wake <= 0) { + if (waiting_threads.empty()) { + updated_value = value + 1; + } else { + updated_value = value - 1; + } + } else { + if (waiting_threads.empty()) { + updated_value = value + 1; + } else if (waiting_threads.size() <= static_cast(num_to_wake)) { + updated_value = value - 1; + } else { + updated_value = value; + } + } + } while (!monitor.ExclusiveWrite32(current_core, address, updated_value)); - memory.Write32(address, static_cast(updated_value)); WakeThreads(waiting_threads, num_to_wake); return RESULT_SUCCESS; } @@ -136,60 +165,121 @@ ResultCode AddressArbiter::WaitForAddress(VAddr address, ArbitrationType type, s ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, bool should_decrement) { auto& memory = system.Memory(); + auto& kernel = system.Kernel(); + Thread* current_thread = system.CurrentScheduler().GetCurrentThread(); - // Ensure that we can read the address. - if (!memory.IsValidVirtualAddress(address)) { - return ERR_INVALID_ADDRESS_STATE; + Handle event_handle = InvalidHandle; + { + SchedulerLockAndSleep lock(kernel, event_handle, current_thread, timeout); + + // Ensure that we can read the address. + if (!memory.IsValidVirtualAddress(address)) { + lock.CancelSleep(); + return ERR_INVALID_ADDRESS_STATE; + } + + /// TODO(Blinkhawk): Check termination pending. + + s32 current_value = static_cast(memory.Read32(address)); + if (current_value >= value) { + lock.CancelSleep(); + return ERR_INVALID_STATE; + } + + s32 decrement_value; + + const std::size_t current_core = system.CurrentCoreIndex(); + auto& monitor = system.Monitor(); + do { + monitor.SetExclusive(current_core, address); + current_value = static_cast(memory.Read32(address)); + if (should_decrement) { + decrement_value = current_value - 1; + } else { + decrement_value = current_value; + } + } while ( + !monitor.ExclusiveWrite32(current_core, address, static_cast(decrement_value))); + + // Short-circuit without rescheduling, if timeout is zero. + if (timeout == 0) { + lock.CancelSleep(); + return RESULT_TIMEOUT; + } + + current_thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT); + current_thread->SetArbiterWaitAddress(address); + InsertThread(SharedFrom(current_thread)); + current_thread->SetStatus(ThreadStatus::WaitArb); + current_thread->WaitForArbitration(true); } - const s32 cur_value = static_cast(memory.Read32(address)); - if (cur_value >= value) { - return ERR_INVALID_STATE; + if (event_handle != InvalidHandle) { + auto& time_manager = kernel.TimeManager(); + time_manager.UnscheduleTimeEvent(event_handle); } - if (should_decrement) { - memory.Write32(address, static_cast(cur_value - 1)); + { + SchedulerLock lock(kernel); + if (current_thread->IsWaitingForArbitration()) { + RemoveThread(SharedFrom(current_thread)); + current_thread->WaitForArbitration(false); + } } - // Short-circuit without rescheduling, if timeout is zero. - if (timeout == 0) { - return RESULT_TIMEOUT; - } - - return WaitForAddressImpl(address, timeout); + return current_thread->GetSignalingResult(); } ResultCode AddressArbiter::WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) { auto& memory = system.Memory(); - - // Ensure that we can read the address. - if (!memory.IsValidVirtualAddress(address)) { - return ERR_INVALID_ADDRESS_STATE; - } - - // Only wait for the address if equal. - if (static_cast(memory.Read32(address)) != value) { - return ERR_INVALID_STATE; - } - - // Short-circuit without rescheduling if timeout is zero. - if (timeout == 0) { - return RESULT_TIMEOUT; - } - - return WaitForAddressImpl(address, timeout); -} - -ResultCode AddressArbiter::WaitForAddressImpl(VAddr address, s64 timeout) { + auto& kernel = system.Kernel(); Thread* current_thread = system.CurrentScheduler().GetCurrentThread(); - current_thread->SetArbiterWaitAddress(address); - InsertThread(SharedFrom(current_thread)); - current_thread->SetStatus(ThreadStatus::WaitArb); - current_thread->InvalidateWakeupCallback(); - current_thread->WakeAfterDelay(timeout); - system.PrepareReschedule(current_thread->GetProcessorID()); - return RESULT_TIMEOUT; + Handle event_handle = InvalidHandle; + { + SchedulerLockAndSleep lock(kernel, event_handle, current_thread, timeout); + + // Ensure that we can read the address. + if (!memory.IsValidVirtualAddress(address)) { + lock.CancelSleep(); + return ERR_INVALID_ADDRESS_STATE; + } + + /// TODO(Blinkhawk): Check termination pending. + + s32 current_value = static_cast(memory.Read32(address)); + if (current_value != value) { + lock.CancelSleep(); + return ERR_INVALID_STATE; + } + + // Short-circuit without rescheduling, if timeout is zero. + if (timeout == 0) { + lock.CancelSleep(); + return RESULT_TIMEOUT; + } + + current_thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT); + current_thread->SetArbiterWaitAddress(address); + InsertThread(SharedFrom(current_thread)); + current_thread->SetStatus(ThreadStatus::WaitArb); + current_thread->WaitForArbitration(true); + } + + if (event_handle != InvalidHandle) { + auto& time_manager = kernel.TimeManager(); + time_manager.UnscheduleTimeEvent(event_handle); + } + + { + SchedulerLock lock(kernel); + if (current_thread->IsWaitingForArbitration()) { + RemoveThread(SharedFrom(current_thread)); + current_thread->WaitForArbitration(false); + } + } + + return current_thread->GetSignalingResult(); } void AddressArbiter::HandleWakeupThread(std::shared_ptr thread) { @@ -221,9 +311,9 @@ void AddressArbiter::RemoveThread(std::shared_ptr thread) { const auto iter = std::find_if(thread_list.cbegin(), thread_list.cend(), [&thread](const auto& entry) { return thread == entry; }); - ASSERT(iter != thread_list.cend()); - - thread_list.erase(iter); + if (iter != thread_list.cend()) { + thread_list.erase(iter); + } } std::vector> AddressArbiter::GetThreadsWaitingOnAddress( diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h index f958eee5a..0b05d533c 100644 --- a/src/core/hle/kernel/address_arbiter.h +++ b/src/core/hle/kernel/address_arbiter.h @@ -73,9 +73,6 @@ private: /// Waits on an address if the value passed is equal to the argument value. ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout); - // Waits on the given address with a timeout in nanoseconds - ResultCode WaitForAddressImpl(VAddr address, s64 timeout); - /// Wake up num_to_wake (or all) threads in a vector. void WakeThreads(const std::vector>& waiting_threads, s32 num_to_wake); diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 5e9dd43bf..718462b2b 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -1691,7 +1691,6 @@ static ResultCode WaitForAddress(Core::System& system, VAddr address, u32 type, LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, timeout={}", address, type, value, timeout); - UNIMPLEMENTED(); // If the passed address is a kernel virtual address, return invalid memory state. if (Core::Memory::IsKernelVirtualAddress(address)) { LOG_ERROR(Kernel_SVC, "Address is a kernel virtual address, address={:016X}", address); @@ -1717,8 +1716,6 @@ static ResultCode SignalToAddress(Core::System& system, VAddr address, u32 type, LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, num_to_wake=0x{:X}", address, type, value, num_to_wake); - UNIMPLEMENTED(); - // If the passed address is a kernel virtual address, return invalid memory state. if (Core::Memory::IsKernelVirtualAddress(address)) { LOG_ERROR(Kernel_SVC, "Address is a kernel virtual address, address={:016X}", address); diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 7b6d1b4ec..e8355bbd1 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -548,6 +548,14 @@ public: return global_handle; } + bool IsWaitingForArbitration() const { + return waiting_for_arbitration; + } + + void WaitForArbitration(bool set) { + waiting_for_arbitration = set; + } + private: friend class GlobalScheduler; friend class Scheduler; @@ -615,6 +623,7 @@ private: /// If waiting for an AddressArbiter, this is the address being waited on. VAddr arb_wait_address{0}; + bool waiting_for_arbitration{}; /// Handle used as userdata to reference this object when inserting into the CoreTiming queue. Handle global_handle = 0; From a6bce296ad3fa8e7da66df3d32cd148448ac0abb Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Wed, 4 Mar 2020 22:46:22 -0400 Subject: [PATCH 163/252] Mutex: Correct Result writting to clear exclusivity. --- src/core/hle/kernel/mutex.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 18325db57..ebe3f6050 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -9,6 +9,7 @@ #include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" +#include "core/arm/exclusive_monitor.h" #include "core/hle/kernel/errors.h" #include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/kernel.h" @@ -133,8 +134,12 @@ std::pair> Mutex::Unlock(std::shared_ptr> Mutex::Unlock(std::shared_ptrSetSynchronizationResults(nullptr, RESULT_SUCCESS); - new_owner->ResumeFromWait(); new_owner->SetLockOwner(nullptr); - system.Memory().Write32(address, mutex_value); + new_owner->ResumeFromWait(); + + do { + monitor.SetExclusive(current_core, address); + } while (!monitor.ExclusiveWrite32(current_core, address, mutex_value)); return {RESULT_SUCCESS, new_owner}; } From c43e55973425be2dfca06804a85493fc6f9c07d1 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Fri, 6 Mar 2020 09:31:03 -0400 Subject: [PATCH 164/252] NVDRV: Remove frame limiting as Host Timing already takes care. --- src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp index 3f7b8e670..19df0dca7 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp @@ -41,7 +41,6 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u3 system.GetPerfStats().EndGameFrame(); system.GetPerfStats().EndSystemFrame(); system.GPU().SwapBuffers(&framebuffer); - system.FrameLimiter().DoFrameLimiting(system.CoreTiming().GetGlobalTimeUs()); system.GetPerfStats().BeginSystemFrame(); } From 1c672128c421ea3141a74f9c6695ecc83231ca30 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Fri, 6 Mar 2020 09:52:24 -0400 Subject: [PATCH 165/252] Scheduler: Release old thread fiber before trying to switch to the next thread fiber. --- src/core/hle/kernel/scheduler.cpp | 37 ++++++++++++++++++++++--------- src/core/hle/kernel/scheduler.h | 9 ++++++++ 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index 9329202c6..aa1f1a305 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp @@ -53,7 +53,8 @@ u32 GlobalScheduler::SelectThreads() { } sched.selected_thread_set = SharedFrom(thread); } - const bool reschedule_pending = sched.selected_thread_set != sched.current_thread; + const bool reschedule_pending = + sched.is_context_switch_pending || (sched.selected_thread_set != sched.current_thread); sched.is_context_switch_pending = reschedule_pending; std::atomic_thread_fence(std::memory_order_seq_cst); sched.guard.unlock(); @@ -552,7 +553,9 @@ void GlobalScheduler::Unlock() { } Scheduler::Scheduler(Core::System& system, std::size_t core_id) - : system{system}, core_id{core_id} {} + : system(system), core_id(core_id) { + switch_fiber = std::make_shared(std::function(OnSwitch), this); +} Scheduler::~Scheduler() = default; @@ -636,8 +639,9 @@ void Scheduler::SwitchContext() { current_thread = selected_thread; is_context_switch_pending = false; - guard.unlock(); + if (new_thread == previous_thread) { + guard.unlock(); return; } @@ -669,20 +673,31 @@ void Scheduler::SwitchContext() { } else { old_context = idle_thread->GetHostContext(); } + guard.unlock(); - std::shared_ptr next_context; - if (new_thread != nullptr) { - next_context = new_thread->GetHostContext(); - } else { - next_context = idle_thread->GetHostContext(); - } - - Common::Fiber::YieldTo(old_context, next_context); + Common::Fiber::YieldTo(old_context, switch_fiber); /// When a thread wakes up, the scheduler may have changed to other in another core. auto& next_scheduler = system.Kernel().CurrentScheduler(); next_scheduler.SwitchContextStep2(); } +void Scheduler::OnSwitch(void* this_scheduler) { + Scheduler* sched = static_cast(this_scheduler); + sched->SwitchToCurrent(); +} + +void Scheduler::SwitchToCurrent() { + while (true) { + std::shared_ptr next_context; + if (current_thread != nullptr) { + next_context = current_thread->GetHostContext(); + } else { + next_context = idle_thread->GetHostContext(); + } + Common::Fiber::YieldTo(switch_fiber, next_context); + } +} + void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) { const u64 prev_switch_ticks = last_context_switch_time; const u64 most_recent_switch_ticks = system.CoreTiming().GetCPUTicks(); diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h index f73ca777e..728cca802 100644 --- a/src/core/hle/kernel/scheduler.h +++ b/src/core/hle/kernel/scheduler.h @@ -15,6 +15,10 @@ #include "core/hardware_properties.h" #include "core/hle/kernel/thread.h" +namespace Common { + class Fiber; +} + namespace Core { class ARM_Interface; class System; @@ -247,12 +251,17 @@ private: */ void UpdateLastContextSwitchTime(Thread* thread, Process* process); + static void OnSwitch(void* this_scheduler); + void SwitchToCurrent(); + std::shared_ptr current_thread = nullptr; std::shared_ptr selected_thread = nullptr; std::shared_ptr current_thread_prev = nullptr; std::shared_ptr selected_thread_set = nullptr; std::shared_ptr idle_thread = nullptr; + std::shared_ptr switch_fiber = nullptr; + Core::System& system; u64 last_context_switch_time = 0; u64 idle_selection_count = 0; From a33fbaddec5d516328d7cd179114dcf0b93cfb69 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Fri, 6 Mar 2020 14:56:05 -0400 Subject: [PATCH 166/252] Core: Correct rebase. --- src/core/arm/dynarmic/arm_dynarmic_32.cpp | 18 ++++++------------ src/core/hle/kernel/scheduler.cpp | 11 +++++------ 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index 0b7aa6a69..30bf62ac1 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -8,6 +8,7 @@ #include #include #include "common/microprofile.h" +#include "core/arm/cpu_interrupt_handler.h" #include "core/arm/dynarmic/arm_dynarmic_32.h" #include "core/arm/dynarmic/arm_dynarmic_64.h" #include "core/arm/dynarmic/arm_dynarmic_cp15.h" @@ -72,20 +73,13 @@ public: } void AddTicks(u64 ticks) override { - // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a - // rough approximation of the amount of executed ticks in the system, it may be thrown off - // if not all cores are doing a similar amount of work. Instead of doing this, we should - // device a way so that timing is consistent across all cores without increasing the ticks 4 - // times. - u64 amortized_ticks = (ticks - num_interpreted_instructions) / Core::NUM_CPU_CORES; - // Always execute at least one tick. - amortized_ticks = std::max(amortized_ticks, 1); - - parent.system.CoreTiming().AddTicks(amortized_ticks); - num_interpreted_instructions = 0; + /// We are using host timing, NOP } u64 GetTicksRemaining() override { - return std::max(parent.system.CoreTiming().GetDowncount(), {}); + if (!parent.interrupt_handler.IsInterrupted()) { + return 1000ULL; + } + return 0ULL; } ARM_Dynarmic_32& parent; diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index aa1f1a305..ae89e908f 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp @@ -552,8 +552,7 @@ void GlobalScheduler::Unlock() { EnableInterruptAndSchedule(cores_pending_reschedule, leaving_thread); } -Scheduler::Scheduler(Core::System& system, std::size_t core_id) - : system(system), core_id(core_id) { +Scheduler::Scheduler(Core::System& system, std::size_t core_id) : system(system), core_id(core_id) { switch_fiber = std::make_shared(std::function(OnSwitch), this); } @@ -601,9 +600,10 @@ void Scheduler::SwitchContextStep2() { // Load context of new thread Process* const previous_process = - previous_thread != nullptr ? previous_thread->GetOwnerProcess() : nullptr; + previous_thread != nullptr ? previous_thread->GetOwnerProcess() : nullptr; if (new_thread) { + auto& cpu_core = system.ArmInterface(core_id); new_thread->context_guard.lock(); cpu_core.Lock(); ASSERT_MSG(new_thread->GetProcessorID() == s32(this->core_id), @@ -619,7 +619,6 @@ void Scheduler::SwitchContextStep2() { system.Kernel().MakeCurrentProcess(thread_owner_process); } if (!new_thread->IsHLEThread()) { - auto& cpu_core = system.ArmInterface(core_id); cpu_core.LoadContext(new_thread->GetContext32()); cpu_core.LoadContext(new_thread->GetContext64()); cpu_core.SetTlsAddress(new_thread->GetTLSAddress()); @@ -651,12 +650,12 @@ void Scheduler::SwitchContext() { // Save context for previous thread if (previous_thread) { + auto& cpu_core = system.ArmInterface(core_id); if (!previous_thread->IsHLEThread()) { - auto& cpu_core = system.ArmInterface(core_id); cpu_core.SaveContext(previous_thread->GetContext32()); cpu_core.SaveContext(previous_thread->GetContext64()); // Save the TPIDR_EL0 system register in case it was modified. - previous_thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0()); + previous_thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0()); cpu_core.ClearExclusiveState(); } if (previous_thread->GetStatus() == ThreadStatus::Running) { From 19847d4d42e061a24086fe90d62382a1ff198322 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Fri, 6 Mar 2020 19:30:37 -0400 Subject: [PATCH 167/252] Scheduler: Correct assert. --- src/core/hle/kernel/scheduler.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index ae89e908f..4e2a5adf3 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp @@ -606,10 +606,8 @@ void Scheduler::SwitchContextStep2() { auto& cpu_core = system.ArmInterface(core_id); new_thread->context_guard.lock(); cpu_core.Lock(); - ASSERT_MSG(new_thread->GetProcessorID() == s32(this->core_id), - "Thread must be assigned to this core."); - ASSERT_MSG(new_thread->GetStatus() == ThreadStatus::Ready, - "Thread must be ready to become running."); + ASSERT_MSG(new_thread->GetSchedulingStatus() == ThreadSchedStatus::Runnable, + "Thread must be runnable."); // Cancel any outstanding wakeup events for this thread new_thread->SetIsRunning(true); From 3de33348e41ab824eb13f202eccc29d5be2a6d42 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Fri, 6 Mar 2020 20:20:36 -0400 Subject: [PATCH 168/252] Scheduler: Protect on closed threads. --- src/core/hle/kernel/scheduler.cpp | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index 4e2a5adf3..74d3731fc 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp @@ -604,7 +604,6 @@ void Scheduler::SwitchContextStep2() { if (new_thread) { auto& cpu_core = system.ArmInterface(core_id); - new_thread->context_guard.lock(); cpu_core.Lock(); ASSERT_MSG(new_thread->GetSchedulingStatus() == ThreadSchedStatus::Runnable, "Thread must be runnable."); @@ -685,13 +684,24 @@ void Scheduler::OnSwitch(void* this_scheduler) { void Scheduler::SwitchToCurrent() { while (true) { - std::shared_ptr next_context; - if (current_thread != nullptr) { - next_context = current_thread->GetHostContext(); - } else { - next_context = idle_thread->GetHostContext(); + guard.lock(); + selected_thread = selected_thread_set; + current_thread = selected_thread; + guard.unlock(); + while (!is_context_switch_pending) { + current_thread->context_guard.lock(); + if (current_thread->GetSchedulingStatus() != ThreadSchedStatus::Runnable) { + current_thread->context_guard.unlock(); + break; + } + std::shared_ptr next_context; + if (current_thread != nullptr) { + next_context = current_thread->GetHostContext(); + } else { + next_context = idle_thread->GetHostContext(); + } + Common::Fiber::YieldTo(switch_fiber, next_context); } - Common::Fiber::YieldTo(switch_fiber, next_context); } } From 6ed28e15fa9f4c727cf24990c35b13a35547d943 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Fri, 6 Mar 2020 20:36:05 -0400 Subject: [PATCH 169/252] Scheduler: Fix HLE Threads on guard --- src/core/hle/kernel/scheduler.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index 74d3731fc..d7529360c 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp @@ -689,10 +689,12 @@ void Scheduler::SwitchToCurrent() { current_thread = selected_thread; guard.unlock(); while (!is_context_switch_pending) { - current_thread->context_guard.lock(); - if (current_thread->GetSchedulingStatus() != ThreadSchedStatus::Runnable) { - current_thread->context_guard.unlock(); - break; + if (current_thread != nullptr) { + current_thread->context_guard.lock(); + if (current_thread->GetSchedulingStatus() != ThreadSchedStatus::Runnable) { + current_thread->context_guard.unlock(); + break; + } } std::shared_ptr next_context; if (current_thread != nullptr) { From 44cb9997b3bf3b1e16b05c2b115c7c4ad5e37dd1 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Fri, 6 Mar 2020 22:58:56 -0400 Subject: [PATCH 170/252] Scheduler: Correct locking for hle threads. --- src/core/hle/kernel/scheduler.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index d7529360c..f020438fb 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp @@ -687,9 +687,10 @@ void Scheduler::SwitchToCurrent() { guard.lock(); selected_thread = selected_thread_set; current_thread = selected_thread; + is_context_switch_pending = false; guard.unlock(); while (!is_context_switch_pending) { - if (current_thread != nullptr) { + if (current_thread != nullptr && !current_thread->IsHLEThread()) { current_thread->context_guard.lock(); if (current_thread->GetSchedulingStatus() != ThreadSchedStatus::Runnable) { current_thread->context_guard.unlock(); From a66c61ca2de61e3a46fa857cf8afea359b2fb8eb Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sat, 7 Mar 2020 10:24:46 -0400 Subject: [PATCH 171/252] SCC: Small corrections to CancelSynchronization --- src/core/hle/kernel/synchronization.cpp | 2 ++ src/core/hle/kernel/thread.cpp | 5 +++-- src/core/hle/kernel/thread.h | 9 +++++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/core/hle/kernel/synchronization.cpp b/src/core/hle/kernel/synchronization.cpp index ac43a7094..a7e3fbe92 100644 --- a/src/core/hle/kernel/synchronization.cpp +++ b/src/core/hle/kernel/synchronization.cpp @@ -74,7 +74,9 @@ std::pair Synchronization::WaitFor( thread->SetSynchronizationObjects(&sync_objects); thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT); thread->SetStatus(ThreadStatus::WaitSynch); + thread->SetWaitingSync(true); } + thread->SetWaitingSync(false); if (event_handle != InvalidHandle) { auto& time_manager = kernel.TimeManager(); diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index fb1751860..e8962a0d8 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -139,13 +139,14 @@ ResultCode Thread::Start() { void Thread::CancelWait() { SchedulerLock lock(kernel); - if (GetSchedulingStatus() != ThreadSchedStatus::Paused) { + if (GetSchedulingStatus() != ThreadSchedStatus::Paused || !is_waiting_on_sync) { is_sync_cancelled = true; return; } + //TODO(Blinkhawk): Implement cancel of server session is_sync_cancelled = false; SetSynchronizationResults(nullptr, ERR_SYNCHRONIZATION_CANCELED); - ResumeFromWait(); + SetStatus(ThreadStatus::Ready); } static void ResetThreadContext32(Core::ARM_Interface::ThreadContext32& context, u32 stack_top, diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index e8355bbd1..d8a983200 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -556,6 +556,14 @@ public: waiting_for_arbitration = set; } + bool IsWaitingSync() const { + return is_waiting_on_sync; + } + + void SetWaitingSync(bool is_waiting) { + is_waiting_on_sync = is_waiting; + } + private: friend class GlobalScheduler; friend class Scheduler; @@ -650,6 +658,7 @@ private: u32 scheduling_state = 0; bool is_running = false; + bool is_waiting_on_sync = false; bool is_sync_cancelled = false; bool will_be_terminated{}; From 83c7ba1ef700eff17f30b6c2782db77710dc322e Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sat, 7 Mar 2020 12:44:35 -0400 Subject: [PATCH 172/252] SVC: Correct SetThreadActivity. --- src/core/hle/kernel/scheduler.cpp | 9 ++--- src/core/hle/kernel/svc.cpp | 5 +-- src/core/hle/kernel/thread.cpp | 61 ++++++++++++++++++++----------- src/core/hle/kernel/thread.h | 22 +++++++---- 4 files changed, 59 insertions(+), 38 deletions(-) diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index f020438fb..a37b992ec 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp @@ -417,8 +417,7 @@ void GlobalScheduler::AdjustSchedulingOnStatus(Thread* thread, u32 old_flags) { } ASSERT(is_locked); - if (static_cast(old_flags & static_cast(ThreadSchedMasks::LowMask)) == - ThreadSchedStatus::Runnable) { + if (old_flags == static_cast(ThreadSchedStatus::Runnable)) { // In this case the thread was running, now it's pausing/exitting if (thread->processor_id >= 0) { Unschedule(thread->current_priority, static_cast(thread->processor_id), thread); @@ -430,7 +429,7 @@ void GlobalScheduler::AdjustSchedulingOnStatus(Thread* thread, u32 old_flags) { Unsuggest(thread->current_priority, core, thread); } } - } else if (thread->GetSchedulingStatus() == ThreadSchedStatus::Runnable) { + } else if (thread->scheduling_state == static_cast(ThreadSchedStatus::Runnable)) { // The thread is now set to running from being stopped if (thread->processor_id >= 0) { Schedule(thread->current_priority, static_cast(thread->processor_id), thread); @@ -448,7 +447,7 @@ void GlobalScheduler::AdjustSchedulingOnStatus(Thread* thread, u32 old_flags) { } void GlobalScheduler::AdjustSchedulingOnPriority(Thread* thread, u32 old_priority) { - if (thread->GetSchedulingStatus() != ThreadSchedStatus::Runnable) { + if (thread->scheduling_state != static_cast(ThreadSchedStatus::Runnable)) { return; } ASSERT(is_locked); @@ -486,7 +485,7 @@ void GlobalScheduler::AdjustSchedulingOnPriority(Thread* thread, u32 old_priorit void GlobalScheduler::AdjustSchedulingOnAffinity(Thread* thread, u64 old_affinity_mask, s32 old_core) { - if (thread->GetSchedulingStatus() != ThreadSchedStatus::Runnable || + if (thread->scheduling_state != static_cast(ThreadSchedStatus::Runnable) || thread->current_priority >= THREADPRIO_COUNT) { return; } diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 718462b2b..da2f90a1d 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -1012,7 +1012,6 @@ static ResultCode UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size /// Sets the thread activity static ResultCode SetThreadActivity(Core::System& system, Handle handle, u32 activity) { LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", handle, activity); - UNIMPLEMENTED(); if (activity > static_cast(ThreadActivity::Paused)) { return ERR_INVALID_ENUM_VALUE; } @@ -1039,9 +1038,7 @@ static ResultCode SetThreadActivity(Core::System& system, Handle handle, u32 act return ERR_BUSY; } - thread->SetActivity(static_cast(activity)); - - return RESULT_SUCCESS; + return thread->SetActivity(static_cast(activity)); } /// Gets the thread context diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index e8962a0d8..b99e3b7a5 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -113,20 +113,11 @@ void Thread::ResumeFromWait() { return; } - if (activity == ThreadActivity::Paused) { - SetStatus(ThreadStatus::Paused); - return; - } - SetStatus(ThreadStatus::Ready); } void Thread::OnWakeUp() { SchedulerLock lock(kernel); - if (activity == ThreadActivity::Paused) { - SetStatus(ThreadStatus::Paused); - return; - } SetStatus(ThreadStatus::Ready); } @@ -143,7 +134,7 @@ void Thread::CancelWait() { is_sync_cancelled = true; return; } - //TODO(Blinkhawk): Implement cancel of server session + // TODO(Blinkhawk): Implement cancel of server session is_sync_cancelled = false; SetSynchronizationResults(nullptr, ERR_SYNCHRONIZATION_CANCELED); SetStatus(ThreadStatus::Ready); @@ -407,19 +398,31 @@ bool Thread::InvokeHLECallback(std::shared_ptr thread) { return hle_callback(std::move(thread)); } -void Thread::SetActivity(ThreadActivity value) { - activity = value; +ResultCode Thread::SetActivity(ThreadActivity value) { + SchedulerLock lock(kernel); + + auto sched_status = GetSchedulingStatus(); + + if (sched_status != ThreadSchedStatus::Runnable && sched_status != ThreadSchedStatus::Paused) { + return ERR_INVALID_STATE; + } + + if (IsPendingTermination()) { + return RESULT_SUCCESS; + } if (value == ThreadActivity::Paused) { - // Set status if not waiting - if (status == ThreadStatus::Ready || status == ThreadStatus::Running) { - SetStatus(ThreadStatus::Paused); - kernel.PrepareReschedule(processor_id); + if (pausing_state & static_cast(ThreadSchedFlags::ThreadPauseFlag) != 0) { + return ERR_INVALID_STATE; } - } else if (status == ThreadStatus::Paused) { - // Ready to reschedule - ResumeFromWait(); + AddSchedulingFlag(ThreadSchedFlags::ThreadPauseFlag); + } else { + if (pausing_state & static_cast(ThreadSchedFlags::ThreadPauseFlag) == 0) { + return ERR_INVALID_STATE; + } + RemoveSchedulingFlag(ThreadSchedFlags::ThreadPauseFlag); } + return RESULT_SUCCESS; } ResultCode Thread::Sleep(s64 nanoseconds) { @@ -460,11 +463,27 @@ ResultCode Thread::YieldAndWaitForLoadBalancing() { return RESULT_SUCCESS; } +void Thread::AddSchedulingFlag(ThreadSchedFlags flag) { + const u32 old_state = scheduling_state; + pausing_state |= static_cast(flag); + const u32 base_scheduling = static_cast(GetSchedulingStatus()); + scheduling_state = base_scheduling | pausing_state; + kernel.GlobalScheduler().AdjustSchedulingOnStatus(this, old_state); +} + +void Thread::RemoveSchedulingFlag(ThreadSchedFlags flag) { + const u32 old_state = scheduling_state; + pausing_state &= ~static_cast(flag); + const u32 base_scheduling = static_cast(GetSchedulingStatus()); + scheduling_state = base_scheduling | pausing_state; + kernel.GlobalScheduler().AdjustSchedulingOnStatus(this, old_state); +} + void Thread::SetSchedulingStatus(ThreadSchedStatus new_status) { - const u32 old_flags = scheduling_state; + const u32 old_state = scheduling_state; scheduling_state = (scheduling_state & static_cast(ThreadSchedMasks::HighMask)) | static_cast(new_status); - kernel.GlobalScheduler().AdjustSchedulingOnStatus(this, old_flags); + kernel.GlobalScheduler().AdjustSchedulingOnStatus(this, old_state); } void Thread::SetCurrentPriority(u32 new_priority) { diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index d8a983200..0a8f7bb65 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -497,11 +497,7 @@ public: return affinity_mask; } - ThreadActivity GetActivity() const { - return activity; - } - - void SetActivity(ThreadActivity value); + ResultCode SetActivity(ThreadActivity value); /// Sleeps this thread for the given amount of nanoseconds. ResultCode Sleep(s64 nanoseconds); @@ -564,11 +560,22 @@ public: is_waiting_on_sync = is_waiting; } + bool IsPendingTermination() const { + return will_be_terminated || GetSchedulingStatus() == ThreadSchedStatus::Exited; + } + + bool IsPaused() const { + return pausing_state != 0; + } + private: friend class GlobalScheduler; friend class Scheduler; void SetSchedulingStatus(ThreadSchedStatus new_status); + void AddSchedulingFlag(ThreadSchedFlags flag); + void RemoveSchedulingFlag(ThreadSchedFlags flag); + void SetCurrentPriority(u32 new_priority); void AdjustSchedulingOnAffinity(u64 old_affinity_mask, s32 old_core); @@ -650,18 +657,17 @@ private: u32 ideal_core{0xFFFFFFFF}; u64 affinity_mask{0x1}; - ThreadActivity activity = ThreadActivity::Normal; - s32 ideal_core_override = -1; u64 affinity_mask_override = 0x1; u32 affinity_override_count = 0; u32 scheduling_state = 0; + u32 pausing_state = 0; bool is_running = false; bool is_waiting_on_sync = false; bool is_sync_cancelled = false; - bool will_be_terminated{}; + bool will_be_terminated = false; std::string name; }; From 38c6c497f6895cff2f073802e77834aa3347f954 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sat, 7 Mar 2020 13:07:04 -0400 Subject: [PATCH 173/252] Yuzu/Debuggers: Correct Wait Tree for Paused threads. --- src/yuzu/debugger/wait_tree.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp index 765908c5a..ab7b18abe 100644 --- a/src/yuzu/debugger/wait_tree.cpp +++ b/src/yuzu/debugger/wait_tree.cpp @@ -208,7 +208,11 @@ QString WaitTreeThread::GetText() const { status = tr("running"); break; case Kernel::ThreadStatus::Ready: - status = tr("ready"); + if (!thread.IsPaused()) { + status = tr("ready"); + } else { + status = tr("paused"); + } break; case Kernel::ThreadStatus::Paused: status = tr("paused"); @@ -256,7 +260,11 @@ QColor WaitTreeThread::GetColor() const { case Kernel::ThreadStatus::Running: return QColor(Qt::GlobalColor::darkGreen); case Kernel::ThreadStatus::Ready: - return QColor(Qt::GlobalColor::darkBlue); + if (!thread.IsPaused()) { + return QColor(Qt::GlobalColor::darkBlue); + } else { + return QColor(Qt::GlobalColor::lightGray); + } case Kernel::ThreadStatus::Paused: return QColor(Qt::GlobalColor::lightGray); case Kernel::ThreadStatus::WaitHLEEvent: From 725bac14044b2645b9ce912d1b1e2c9c2a96818b Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sat, 7 Mar 2020 13:27:27 -0400 Subject: [PATCH 174/252] Scheduler: Remove arm_interface lock and a few corrections. --- src/core/arm/arm_interface.h | 10 ---------- src/core/hle/kernel/scheduler.cpp | 10 +++------- 2 files changed, 3 insertions(+), 17 deletions(-) diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index be9f3703a..87a1c29cc 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h @@ -6,7 +6,6 @@ #include #include -#include #include "common/common_types.h" namespace Common { @@ -165,14 +164,6 @@ public: std::string name; }; - void Lock() { - guard.lock(); - } - - void Unlock() { - guard.unlock(); - } - std::vector GetBacktrace() const; /// fp (= r29) points to the last frame record. @@ -187,7 +178,6 @@ protected: /// System context that this ARM interface is running under. System& system; CPUInterruptHandler& interrupt_handler; - std::mutex guard; }; } // namespace Core diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index a37b992ec..affc2fbed 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp @@ -463,9 +463,7 @@ void GlobalScheduler::AdjustSchedulingOnPriority(Thread* thread, u32 old_priorit } if (thread->processor_id >= 0) { - // TODO(Blinkhawk): compare it with current thread running on current core, instead of - // checking running - if (thread->IsRunning()) { + if (thread == kernel.CurrentScheduler().GetCurrentThread()) { SchedulePrepend(thread->current_priority, static_cast(thread->processor_id), thread); } else { @@ -602,8 +600,6 @@ void Scheduler::SwitchContextStep2() { previous_thread != nullptr ? previous_thread->GetOwnerProcess() : nullptr; if (new_thread) { - auto& cpu_core = system.ArmInterface(core_id); - cpu_core.Lock(); ASSERT_MSG(new_thread->GetSchedulingStatus() == ThreadSchedStatus::Runnable, "Thread must be runnable."); @@ -615,6 +611,7 @@ void Scheduler::SwitchContextStep2() { system.Kernel().MakeCurrentProcess(thread_owner_process); } if (!new_thread->IsHLEThread()) { + auto& cpu_core = system.ArmInterface(core_id); cpu_core.LoadContext(new_thread->GetContext32()); cpu_core.LoadContext(new_thread->GetContext64()); cpu_core.SetTlsAddress(new_thread->GetTLSAddress()); @@ -646,8 +643,8 @@ void Scheduler::SwitchContext() { // Save context for previous thread if (previous_thread) { - auto& cpu_core = system.ArmInterface(core_id); if (!previous_thread->IsHLEThread()) { + auto& cpu_core = system.ArmInterface(core_id); cpu_core.SaveContext(previous_thread->GetContext32()); cpu_core.SaveContext(previous_thread->GetContext64()); // Save the TPIDR_EL0 system register in case it was modified. @@ -659,7 +656,6 @@ void Scheduler::SwitchContext() { } previous_thread->SetIsRunning(false); previous_thread->context_guard.unlock(); - cpu_core.Unlock(); } std::shared_ptr old_context; From 535c542d84ea56b5710bf84af3fba6272913f48e Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sat, 7 Mar 2020 13:37:47 -0400 Subject: [PATCH 175/252] SVC: WaitSynchronization add Termination Pending Result. --- src/core/hle/kernel/errors.h | 1 + src/core/hle/kernel/synchronization.cpp | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h index 29bfa3621..d4e5d88cf 100644 --- a/src/core/hle/kernel/errors.h +++ b/src/core/hle/kernel/errors.h @@ -12,6 +12,7 @@ namespace Kernel { constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED{ErrorModule::Kernel, 7}; constexpr ResultCode ERR_INVALID_CAPABILITY_DESCRIPTOR{ErrorModule::Kernel, 14}; +constexpr ResultCode ERR_THREAD_TERMINATING{ErrorModule::Kernel, 59}; constexpr ResultCode ERR_INVALID_SIZE{ErrorModule::Kernel, 101}; constexpr ResultCode ERR_INVALID_ADDRESS{ErrorModule::Kernel, 102}; constexpr ResultCode ERR_OUT_OF_RESOURCES{ErrorModule::Kernel, 103}; diff --git a/src/core/hle/kernel/synchronization.cpp b/src/core/hle/kernel/synchronization.cpp index a7e3fbe92..4323fc120 100644 --- a/src/core/hle/kernel/synchronization.cpp +++ b/src/core/hle/kernel/synchronization.cpp @@ -59,7 +59,10 @@ std::pair Synchronization::WaitFor( return {RESULT_TIMEOUT, InvalidHandle}; } - /// TODO(Blinkhawk): Check for termination pending + if (thread->IsPendingTermination()) { + lock.CancelSleep(); + return {ERR_THREAD_TERMINATING, InvalidHandle}; + } if (thread->IsSyncCancelled()) { thread->SetSyncCancelled(false); From cd1c38be8d15d3caf52f566a9e8dc20504c61068 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sat, 7 Mar 2020 18:59:42 -0400 Subject: [PATCH 176/252] ARM/Memory: Correct Exclusive Monitor and Implement Exclusive Memory Writes. --- src/common/CMakeLists.txt | 2 + src/common/atomic_ops.cpp | 70 ++++++++++++++++ src/common/atomic_ops.h | 17 ++++ src/core/arm/dynarmic/arm_dynarmic_64.cpp | 66 ++++++++++++--- src/core/arm/dynarmic/arm_dynarmic_64.h | 6 +- src/core/arm/exclusive_monitor.h | 6 +- src/core/hle/kernel/address_arbiter.cpp | 6 +- src/core/hle/kernel/mutex.cpp | 5 +- src/core/hle/kernel/svc.cpp | 2 +- src/core/hle/kernel/thread.cpp | 6 +- src/core/memory.cpp | 98 +++++++++++++++++++++++ src/core/memory.h | 65 +++++++++++++++ 12 files changed, 325 insertions(+), 24 deletions(-) create mode 100644 src/common/atomic_ops.cpp create mode 100644 src/common/atomic_ops.h diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 3cc17d0e9..d120c8d3d 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -98,6 +98,8 @@ add_library(common STATIC algorithm.h alignment.h assert.h + atomic_ops.cpp + atomic_ops.h detached_tasks.cpp detached_tasks.h bit_field.h diff --git a/src/common/atomic_ops.cpp b/src/common/atomic_ops.cpp new file mode 100644 index 000000000..65cdfb4fd --- /dev/null +++ b/src/common/atomic_ops.cpp @@ -0,0 +1,70 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include + +#include "common/atomic_ops.h" + +#if _MSC_VER +#include +#endif + +namespace Common { + +#if _MSC_VER + +bool AtomicCompareAndSwap(u8 volatile* pointer, u8 value, u8 expected) { + u8 result = _InterlockedCompareExchange8((char*)pointer, value, expected); + return result == expected; +} + +bool AtomicCompareAndSwap(u16 volatile* pointer, u16 value, u16 expected) { + u16 result = _InterlockedCompareExchange16((short*)pointer, value, expected); + return result == expected; +} + +bool AtomicCompareAndSwap(u32 volatile* pointer, u32 value, u32 expected) { + u32 result = _InterlockedCompareExchange((long*)pointer, value, expected); + return result == expected; +} + +bool AtomicCompareAndSwap(u64 volatile* pointer, u64 value, u64 expected) { + u64 result = _InterlockedCompareExchange64((__int64*)pointer, value, expected); + return result == expected; +} + +bool AtomicCompareAndSwap(u64 volatile* pointer, u128 value, u128 expected) { + return _InterlockedCompareExchange128((__int64*)pointer, value[1], value[0], (__int64*)expected.data()) != 0; +} + + +#else + +bool AtomicCompareAndSwap(u8 volatile* pointer, u8 value, u8 expected) { + return __sync_bool_compare_and_swap (pointer, value, expected); +} + +bool AtomicCompareAndSwap(u16 volatile* pointer, u16 value, u16 expected) { + return __sync_bool_compare_and_swap (pointer, value, expected); +} + +bool AtomicCompareAndSwap(u32 volatile* pointer, u32 value, u32 expected) { + return __sync_bool_compare_and_swap (pointer, value, expected); +} + +bool AtomicCompareAndSwap(u64 volatile* pointer, u64 value, u64 expected) { + return __sync_bool_compare_and_swap (pointer, value, expected); +} + +bool AtomicCompareAndSwap(u64 volatile* pointer, u128 value, u128 expected) { + unsigned __int128 value_a; + unsigned __int128 expected_a; + std::memcpy(&value_a, value.data(), sizeof(u128)); + std::memcpy(&expected_a, expected.data(), sizeof(u128)); + return __sync_bool_compare_and_swap ((unsigned __int128*)pointer, value_a, expected_a); +} + +#endif + +} // namespace Common diff --git a/src/common/atomic_ops.h b/src/common/atomic_ops.h new file mode 100644 index 000000000..22cb3a402 --- /dev/null +++ b/src/common/atomic_ops.h @@ -0,0 +1,17 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_types.h" + +namespace Common { + +bool AtomicCompareAndSwap(u8 volatile * pointer, u8 value, u8 expected); +bool AtomicCompareAndSwap(u16 volatile* pointer, u16 value, u16 expected); +bool AtomicCompareAndSwap(u32 volatile* pointer, u32 value, u32 expected); +bool AtomicCompareAndSwap(u64 volatile* pointer, u64 value, u64 expected); +bool AtomicCompareAndSwap(u64 volatile* pointer, u128 value, u128 expected); + +} // namespace Common diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 5e316ffd4..a22c22bf0 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -66,6 +66,22 @@ public: memory.Write64(vaddr + 8, value[1]); } + bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, std::uint8_t expected) override { + return parent.system.Memory().WriteExclusive8(vaddr, value, expected); + } + bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, std::uint16_t expected) override { + return parent.system.Memory().WriteExclusive16(vaddr, value, expected); + } + bool MemoryWriteExclusive32(u64 vaddr, std::uint32_t value, std::uint32_t expected) override { + return parent.system.Memory().WriteExclusive32(vaddr, value, expected); + } + bool MemoryWriteExclusive64(u64 vaddr, std::uint64_t value, std::uint64_t expected) override { + return parent.system.Memory().WriteExclusive64(vaddr, value, expected); + } + bool MemoryWriteExclusive128(u64 vaddr, Vector value, Vector expected) override { + return parent.system.Memory().WriteExclusive128(vaddr, value, expected); + } + void InterpreterFallback(u64 pc, std::size_t num_instructions) override { LOG_INFO(Core_ARM, "Unicorn fallback @ 0x{:X} for {} instructions (instr = {:08X})", pc, num_instructions, MemoryReadCode(pc)); @@ -284,9 +300,29 @@ DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(Memory::Memory& memory, std:: DynarmicExclusiveMonitor::~DynarmicExclusiveMonitor() = default; -void DynarmicExclusiveMonitor::SetExclusive(std::size_t core_index, VAddr addr) { - // Size doesn't actually matter. - monitor.Mark(core_index, addr, 16); +void DynarmicExclusiveMonitor::SetExclusive8(std::size_t core_index, VAddr addr) { + monitor.Mark(core_index, addr, 1, [&]() -> u8 { return memory.Read8(addr); }); +} + +void DynarmicExclusiveMonitor::SetExclusive16(std::size_t core_index, VAddr addr) { + monitor.Mark(core_index, addr, 2, [&]() -> u16 { return memory.Read16(addr); }); +} + +void DynarmicExclusiveMonitor::SetExclusive32(std::size_t core_index, VAddr addr) { + monitor.Mark(core_index, addr, 4, [&]() -> u32 { return memory.Read32(addr); }); +} + +void DynarmicExclusiveMonitor::SetExclusive64(std::size_t core_index, VAddr addr) { + monitor.Mark(core_index, addr, 8, [&]() -> u64 { return memory.Read64(addr); }); +} + +void DynarmicExclusiveMonitor::SetExclusive128(std::size_t core_index, VAddr addr) { + monitor.Mark(core_index, addr, 16, [&]() -> u128 { + u128 result; + result[0] = memory.Read64(addr); + result[1] = memory.Read64(addr + 8); + return result; + }); } void DynarmicExclusiveMonitor::ClearExclusive() { @@ -294,28 +330,32 @@ void DynarmicExclusiveMonitor::ClearExclusive() { } bool DynarmicExclusiveMonitor::ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) { - return monitor.DoExclusiveOperation(core_index, vaddr, 1, [&] { memory.Write8(vaddr, value); }); + return monitor.DoExclusiveOperation(core_index, vaddr, 1, [&](u8 expected) -> bool { + return memory.WriteExclusive8(vaddr, value, expected); + }); } bool DynarmicExclusiveMonitor::ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) { - return monitor.DoExclusiveOperation(core_index, vaddr, 2, - [&] { memory.Write16(vaddr, value); }); + return monitor.DoExclusiveOperation(core_index, vaddr, 2, [&](u16 expected) -> bool { + return memory.WriteExclusive16(vaddr, value, expected); + }); } bool DynarmicExclusiveMonitor::ExclusiveWrite32(std::size_t core_index, VAddr vaddr, u32 value) { - return monitor.DoExclusiveOperation(core_index, vaddr, 4, - [&] { memory.Write32(vaddr, value); }); + return monitor.DoExclusiveOperation(core_index, vaddr, 4, [&](u32 expected) -> bool { + return memory.WriteExclusive32(vaddr, value, expected); + }); } bool DynarmicExclusiveMonitor::ExclusiveWrite64(std::size_t core_index, VAddr vaddr, u64 value) { - return monitor.DoExclusiveOperation(core_index, vaddr, 8, - [&] { memory.Write64(vaddr, value); }); + return monitor.DoExclusiveOperation(core_index, vaddr, 8, [&](u64 expected) -> bool { + return memory.WriteExclusive64(vaddr, value, expected); + }); } bool DynarmicExclusiveMonitor::ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) { - return monitor.DoExclusiveOperation(core_index, vaddr, 16, [&] { - memory.Write64(vaddr + 0, value[0]); - memory.Write64(vaddr + 8, value[1]); + return monitor.DoExclusiveOperation(core_index, vaddr, 16, [&](u128 expected) -> bool { + return memory.WriteExclusive128(vaddr, value, expected); }); } diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h index 9e94b58c2..3ead59f16 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.h +++ b/src/core/arm/dynarmic/arm_dynarmic_64.h @@ -82,7 +82,11 @@ public: explicit DynarmicExclusiveMonitor(Memory::Memory& memory, std::size_t core_count); ~DynarmicExclusiveMonitor() override; - void SetExclusive(std::size_t core_index, VAddr addr) override; + void SetExclusive8(std::size_t core_index, VAddr addr) override; + void SetExclusive16(std::size_t core_index, VAddr addr) override; + void SetExclusive32(std::size_t core_index, VAddr addr) override; + void SetExclusive64(std::size_t core_index, VAddr addr) override; + void SetExclusive128(std::size_t core_index, VAddr addr) override; void ClearExclusive() override; bool ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) override; diff --git a/src/core/arm/exclusive_monitor.h b/src/core/arm/exclusive_monitor.h index ccd73b80f..2ee312eee 100644 --- a/src/core/arm/exclusive_monitor.h +++ b/src/core/arm/exclusive_monitor.h @@ -18,7 +18,11 @@ class ExclusiveMonitor { public: virtual ~ExclusiveMonitor(); - virtual void SetExclusive(std::size_t core_index, VAddr addr) = 0; + virtual void SetExclusive8(std::size_t core_index, VAddr addr) = 0; + virtual void SetExclusive16(std::size_t core_index, VAddr addr) = 0; + virtual void SetExclusive32(std::size_t core_index, VAddr addr) = 0; + virtual void SetExclusive64(std::size_t core_index, VAddr addr) = 0; + virtual void SetExclusive128(std::size_t core_index, VAddr addr) = 0; virtual void ClearExclusive() = 0; virtual bool ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) = 0; diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index ebabde921..07acabc1d 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp @@ -90,7 +90,7 @@ ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32 auto& monitor = system.Monitor(); u32 current_value; do { - monitor.SetExclusive(current_core, address); + monitor.SetExclusive32(current_core, address); current_value = memory.Read32(address); if (current_value != value) { @@ -120,7 +120,7 @@ ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr a auto& monitor = system.Monitor(); s32 updated_value; do { - monitor.SetExclusive(current_core, address); + monitor.SetExclusive32(current_core, address); updated_value = memory.Read32(address); if (updated_value != value) { @@ -191,7 +191,7 @@ ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s6 const std::size_t current_core = system.CurrentCoreIndex(); auto& monitor = system.Monitor(); do { - monitor.SetExclusive(current_core, address); + monitor.SetExclusive32(current_core, address); current_value = static_cast(memory.Read32(address)); if (should_decrement) { decrement_value = current_value - 1; diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index ebe3f6050..16c95782a 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -10,6 +10,7 @@ #include "common/logging/log.h" #include "core/core.h" #include "core/arm/exclusive_monitor.h" +#include "core/core.h" #include "core/hle/kernel/errors.h" #include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/kernel.h" @@ -138,7 +139,7 @@ std::pair> Mutex::Unlock(std::shared_ptr> Mutex::Unlock(std::shared_ptrResumeFromWait(); do { - monitor.SetExclusive(current_core, address); + monitor.SetExclusive32(current_core, address); } while (!monitor.ExclusiveWrite32(current_core, address, mutex_value)); return {RESULT_SUCCESS, new_owner}; } diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index da2f90a1d..371beed0d 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -1641,7 +1641,7 @@ static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_ u32 update_val = 0; const VAddr mutex_address = thread->GetMutexWaitAddress(); do { - monitor.SetExclusive(current_core, mutex_address); + monitor.SetExclusive32(current_core, mutex_address); // If the mutex is not yet acquired, acquire it. mutex_val = memory.Read32(mutex_address); diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index b99e3b7a5..51cc5dcca 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -236,7 +236,7 @@ ResultVal> Thread::Create(Core::System& system, ThreadTy ResetThreadContext64(thread->context_64, stack_top, entry_point, arg); } thread->host_context = - std::make_shared(std::move(thread_start_func), thread_start_parameter); + std::make_shared(std::move(thread_start_func), thread_start_parameter); return MakeResult>(std::move(thread)); } @@ -412,12 +412,12 @@ ResultCode Thread::SetActivity(ThreadActivity value) { } if (value == ThreadActivity::Paused) { - if (pausing_state & static_cast(ThreadSchedFlags::ThreadPauseFlag) != 0) { + if ((pausing_state & static_cast(ThreadSchedFlags::ThreadPauseFlag)) != 0) { return ERR_INVALID_STATE; } AddSchedulingFlag(ThreadSchedFlags::ThreadPauseFlag); } else { - if (pausing_state & static_cast(ThreadSchedFlags::ThreadPauseFlag) == 0) { + if ((pausing_state & static_cast(ThreadSchedFlags::ThreadPauseFlag)) == 0) { return ERR_INVALID_STATE; } RemoveSchedulingFlag(ThreadSchedFlags::ThreadPauseFlag); diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 66634596d..4cb5d05e5 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -8,6 +8,7 @@ #include #include "common/assert.h" +#include "common/atomic_ops.h" #include "common/common_types.h" #include "common/logging/log.h" #include "common/page_table.h" @@ -176,6 +177,22 @@ struct Memory::Impl { } } + bool WriteExclusive8(const VAddr addr, const u8 data, const u8 expected) { + return WriteExclusive(addr, data, expected); + } + + bool WriteExclusive16(const VAddr addr, const u16 data, const u16 expected) { + return WriteExclusive(addr, data, expected); + } + + bool WriteExclusive32(const VAddr addr, const u32 data, const u32 expected) { + return WriteExclusive(addr, data, expected); + } + + bool WriteExclusive64(const VAddr addr, const u64 data, const u64 expected) { + return WriteExclusive(addr, data, expected); + } + std::string ReadCString(VAddr vaddr, std::size_t max_length) { std::string string; string.reserve(max_length); @@ -679,6 +696,67 @@ struct Memory::Impl { } } + template + bool WriteExclusive(const VAddr vaddr, const T data, const T expected) { + u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; + if (page_pointer != nullptr) { + // NOTE: Avoid adding any extra logic to this fast-path block + T volatile* pointer = reinterpret_cast(&page_pointer[vaddr]); + return Common::AtomicCompareAndSwap(pointer, data, expected); + } + + const Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS]; + switch (type) { + case Common::PageType::Unmapped: + LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8, + static_cast(data), vaddr); + return true; + case Common::PageType::Memory: + ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr); + break; + case Common::PageType::RasterizerCachedMemory: { + u8* host_ptr{GetPointerFromVMA(vaddr)}; + system.GPU().InvalidateRegion(ToCacheAddr(host_ptr), sizeof(T)); + T volatile* pointer = reinterpret_cast(&host_ptr); + return Common::AtomicCompareAndSwap(pointer, data, expected); + break; + } + default: + UNREACHABLE(); + } + return true; + } + + bool WriteExclusive128(const VAddr vaddr, const u128 data, const u128 expected) { + u8* const page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; + if (page_pointer != nullptr) { + // NOTE: Avoid adding any extra logic to this fast-path block + u64 volatile* pointer = reinterpret_cast(&page_pointer[vaddr]); + return Common::AtomicCompareAndSwap(pointer, data, expected); + } + + const Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS]; + switch (type) { + case Common::PageType::Unmapped: + LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}{:016X}", sizeof(data) * 8, + static_cast(data[1]), static_cast(data[0]), vaddr); + return true; + case Common::PageType::Memory: + ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr); + break; + case Common::PageType::RasterizerCachedMemory: { + u8* host_ptr{GetPointerFromVMA(vaddr)}; + system.GPU().InvalidateRegion(ToCacheAddr(host_ptr), sizeof(u128)); + u64 volatile* pointer = reinterpret_cast(&host_ptr); + return Common::AtomicCompareAndSwap(pointer, data, expected); + break; + } + default: + UNREACHABLE(); + } + return true; + } + Common::PageTable* current_page_table = nullptr; Core::System& system; }; @@ -761,6 +839,26 @@ void Memory::Write64(VAddr addr, u64 data) { impl->Write64(addr, data); } +bool Memory::WriteExclusive8(VAddr addr, u8 data, u8 expected) { + return impl->WriteExclusive8(addr, data, expected); +} + +bool Memory::WriteExclusive16(VAddr addr, u16 data, u16 expected) { + return impl->WriteExclusive16(addr, data, expected); +} + +bool Memory::WriteExclusive32(VAddr addr, u32 data, u32 expected) { + return impl->WriteExclusive32(addr, data, expected); +} + +bool Memory::WriteExclusive64(VAddr addr, u64 data, u64 expected) { + return impl->WriteExclusive64(addr, data, expected); +} + +bool Memory::WriteExclusive128(VAddr addr, u128 data, u128 expected) { + return impl->WriteExclusive128(addr, data, expected); +} + std::string Memory::ReadCString(VAddr vaddr, std::size_t max_length) { return impl->ReadCString(vaddr, max_length); } diff --git a/src/core/memory.h b/src/core/memory.h index 93f0c1d6c..4a1cc63f4 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -244,6 +244,71 @@ public: */ void Write64(VAddr addr, u64 data); + /** + * Writes a 8-bit unsigned integer to the given virtual address in + * the current process' address space if and only if the address contains + * the expected value. This operation is atomic. + * + * @param addr The virtual address to write the 8-bit unsigned integer to. + * @param data The 8-bit unsigned integer to write to the given virtual address. + * @param expected The 8-bit unsigned integer to check against the given virtual address. + * + * @post The memory range [addr, sizeof(data)) contains the given data value. + */ + bool WriteExclusive8(VAddr addr, u8 data, u8 expected); + + /** + * Writes a 16-bit unsigned integer to the given virtual address in + * the current process' address space if and only if the address contains + * the expected value. This operation is atomic. + * + * @param addr The virtual address to write the 16-bit unsigned integer to. + * @param data The 16-bit unsigned integer to write to the given virtual address. + * @param expected The 16-bit unsigned integer to check against the given virtual address. + * + * @post The memory range [addr, sizeof(data)) contains the given data value. + */ + bool WriteExclusive16(VAddr addr, u16 data, u16 expected); + + /** + * Writes a 32-bit unsigned integer to the given virtual address in + * the current process' address space if and only if the address contains + * the expected value. This operation is atomic. + * + * @param addr The virtual address to write the 32-bit unsigned integer to. + * @param data The 32-bit unsigned integer to write to the given virtual address. + * @param expected The 32-bit unsigned integer to check against the given virtual address. + * + * @post The memory range [addr, sizeof(data)) contains the given data value. + */ + bool WriteExclusive32(VAddr addr, u32 data, u32 expected); + + /** + * Writes a 64-bit unsigned integer to the given virtual address in + * the current process' address space if and only if the address contains + * the expected value. This operation is atomic. + * + * @param addr The virtual address to write the 64-bit unsigned integer to. + * @param data The 64-bit unsigned integer to write to the given virtual address. + * @param expected The 64-bit unsigned integer to check against the given virtual address. + * + * @post The memory range [addr, sizeof(data)) contains the given data value. + */ + bool WriteExclusive64(VAddr addr, u64 data, u64 expected); + + /** + * Writes a 128-bit unsigned integer to the given virtual address in + * the current process' address space if and only if the address contains + * the expected value. This operation is atomic. + * + * @param addr The virtual address to write the 128-bit unsigned integer to. + * @param data The 128-bit unsigned integer to write to the given virtual address. + * @param expected The 128-bit unsigned integer to check against the given virtual address. + * + * @post The memory range [addr, sizeof(data)) contains the given data value. + */ + bool WriteExclusive128(VAddr addr, u128 data, u128 expected); + /** * Reads a null-terminated string from the given virtual address. * This function will continually read characters until either: From 445b4342b3db8ab447aaa9e5422d2a7a3a45e5ac Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sat, 7 Mar 2020 19:04:02 -0400 Subject: [PATCH 177/252] Mutex: Revert workaround due to poor exclusive memory. --- src/core/hle/kernel/mutex.cpp | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 16c95782a..5a96d5e90 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -9,7 +9,6 @@ #include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" -#include "core/arm/exclusive_monitor.h" #include "core/core.h" #include "core/hle/kernel/errors.h" #include "core/hle/kernel/handle_table.h" @@ -135,12 +134,8 @@ std::pair> Mutex::Unlock(std::shared_ptr> Mutex::Unlock(std::shared_ptrSetLockOwner(nullptr); new_owner->ResumeFromWait(); - do { - monitor.SetExclusive32(current_core, address); - } while (!monitor.ExclusiveWrite32(current_core, address, mutex_value)); + system.Memory().Write32(address, mutex_value); return {RESULT_SUCCESS, new_owner}; } From 4217e58a103049675df27f404171f73fa0be8537 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sun, 8 Mar 2020 11:25:50 -0400 Subject: [PATCH 178/252] Scheduler: Correct yields. --- src/core/hle/kernel/scheduler.cpp | 28 +++++++++++++++++++++------- src/core/hle/kernel/thread.h | 4 ++++ 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index affc2fbed..ab17204bb 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp @@ -147,9 +147,11 @@ bool GlobalScheduler::YieldThread(Thread* yielding_thread) { const u32 priority = yielding_thread->GetPriority(); // Yield the thread - const Thread* const winner = scheduled_queue[core_id].front(priority); - ASSERT_MSG(yielding_thread == winner, "Thread yielding without being in front"); - scheduled_queue[core_id].yield(priority); + Reschedule(priority, core_id, yielding_thread); + const Thread* const winner = scheduled_queue[core_id].front(); + if (kernel.GetCurrentHostThreadID() != core_id) { + is_reselection_pending.store(true, std::memory_order_release); + } return AskForReselectionOrMarkRedundant(yielding_thread, winner); } @@ -162,9 +164,7 @@ bool GlobalScheduler::YieldThreadAndBalanceLoad(Thread* yielding_thread) { const u32 priority = yielding_thread->GetPriority(); // Yield the thread - ASSERT_MSG(yielding_thread == scheduled_queue[core_id].front(priority), - "Thread yielding without being in front"); - scheduled_queue[core_id].yield(priority); + Reschedule(priority, core_id, yielding_thread); std::array current_threads; for (std::size_t i = 0; i < current_threads.size(); i++) { @@ -200,6 +200,10 @@ bool GlobalScheduler::YieldThreadAndBalanceLoad(Thread* yielding_thread) { winner = next_thread; } + if (kernel.GetCurrentHostThreadID() != core_id) { + is_reselection_pending.store(true, std::memory_order_release); + } + return AskForReselectionOrMarkRedundant(yielding_thread, winner); } @@ -239,6 +243,12 @@ bool GlobalScheduler::YieldThreadAndWaitForLoadBalancing(Thread* yielding_thread } else { winner = yielding_thread; } + } else { + winner = scheduled_queue[i].front(); + } + + if (kernel.GetCurrentHostThreadID() != core_id) { + is_reselection_pending.store(true, std::memory_order_release); } return AskForReselectionOrMarkRedundant(yielding_thread, winner); @@ -687,7 +697,11 @@ void Scheduler::SwitchToCurrent() { while (!is_context_switch_pending) { if (current_thread != nullptr && !current_thread->IsHLEThread()) { current_thread->context_guard.lock(); - if (current_thread->GetSchedulingStatus() != ThreadSchedStatus::Runnable) { + if (!current_thread->IsRunnable()) { + current_thread->context_guard.unlock(); + break; + } + if (current_thread->GetProcessorID() != core_id) { current_thread->context_guard.unlock(); break; } diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 0a8f7bb65..953b023b5 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -524,6 +524,10 @@ public: static_cast(ThreadSchedMasks::LowMask)); } + bool IsRunnable() const { + return scheduling_state == static_cast(ThreadSchedStatus::Runnable); + } + bool IsRunning() const { return is_running; } From 6515c6e8c699584528486341579cf3a8dde3eea4 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sun, 8 Mar 2020 12:51:24 -0400 Subject: [PATCH 179/252] Kernel: Fixes, corrections and asserts to scheduler and different svcs. --- src/core/hle/kernel/address_arbiter.cpp | 27 +++++++++++-------------- src/core/hle/kernel/kernel.cpp | 1 + src/core/hle/kernel/scheduler.cpp | 3 ++- src/core/hle/kernel/scheduler.h | 5 +++-- src/core/hle/kernel/svc.cpp | 27 +++++++++++++------------ src/core/hle/kernel/synchronization.cpp | 10 +++++---- src/core/hle/kernel/time_manager.cpp | 2 -- src/core/hle/kernel/time_manager.h | 1 - 8 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index 07acabc1d..e8f22b598 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp @@ -34,19 +34,9 @@ void AddressArbiter::WakeThreads(const std::vector>& wai // Signal the waiting threads. for (std::size_t i = 0; i < last; i++) { - if (waiting_threads[i]->GetStatus() != ThreadStatus::WaitArb) { - last++; - last = std::min(waiting_threads.size(), last); - continue; - } - - time_manager.CancelTimeEvent(waiting_threads[i].get()); - - ASSERT(waiting_threads[i]->GetStatus() == ThreadStatus::WaitArb); waiting_threads[i]->SetSynchronizationResults(nullptr, RESULT_SUCCESS); RemoveThread(waiting_threads[i]); waiting_threads[i]->WaitForArbitration(false); - waiting_threads[i]->SetArbiterWaitAddress(0); waiting_threads[i]->ResumeFromWait(); } } @@ -172,20 +162,25 @@ ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s6 { SchedulerLockAndSleep lock(kernel, event_handle, current_thread, timeout); + if (current_thread->IsPendingTermination()) { + lock.CancelSleep(); + return ERR_THREAD_TERMINATING; + } + // Ensure that we can read the address. if (!memory.IsValidVirtualAddress(address)) { lock.CancelSleep(); return ERR_INVALID_ADDRESS_STATE; } - /// TODO(Blinkhawk): Check termination pending. - s32 current_value = static_cast(memory.Read32(address)); if (current_value >= value) { lock.CancelSleep(); return ERR_INVALID_STATE; } + current_thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT); + s32 decrement_value; const std::size_t current_core = system.CurrentCoreIndex(); @@ -207,7 +202,6 @@ ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s6 return RESULT_TIMEOUT; } - current_thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT); current_thread->SetArbiterWaitAddress(address); InsertThread(SharedFrom(current_thread)); current_thread->SetStatus(ThreadStatus::WaitArb); @@ -239,14 +233,17 @@ ResultCode AddressArbiter::WaitForAddressIfEqual(VAddr address, s32 value, s64 t { SchedulerLockAndSleep lock(kernel, event_handle, current_thread, timeout); + if (current_thread->IsPendingTermination()) { + lock.CancelSleep(); + return ERR_THREAD_TERMINATING; + } + // Ensure that we can read the address. if (!memory.IsValidVirtualAddress(address)) { lock.CancelSleep(); return ERR_INVALID_ADDRESS_STATE; } - /// TODO(Blinkhawk): Check termination pending. - s32 current_value = static_cast(memory.Read32(address)); if (current_value != value) { lock.CancelSleep(); diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index ba051a7d8..721ab1e70 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -49,6 +49,7 @@ namespace Kernel { * @param cycles_late The number of CPU cycles that have passed since the desired wakeup time */ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] s64 cycles_late) { + UNREACHABLE(); const auto proper_handle = static_cast(thread_handle); const auto& system = Core::System::GetInstance(); diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index ab17204bb..5322f0aae 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp @@ -133,6 +133,7 @@ u32 GlobalScheduler::SelectThreads() { u32 cores_needing_context_switch{}; for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { Scheduler& sched = kernel.Scheduler(core); + ASSERT(top_threads[core] == nullptr || top_threads[core]->GetProcessorID() == core); if (update_thread(top_threads[core], sched)) { cores_needing_context_switch |= (1ul << core); } @@ -244,7 +245,7 @@ bool GlobalScheduler::YieldThreadAndWaitForLoadBalancing(Thread* yielding_thread winner = yielding_thread; } } else { - winner = scheduled_queue[i].front(); + winner = scheduled_queue[core_id].front(); } if (kernel.GetCurrentHostThreadID() != core_id) { diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h index 728cca802..5e062bf59 100644 --- a/src/core/hle/kernel/scheduler.h +++ b/src/core/hle/kernel/scheduler.h @@ -16,7 +16,7 @@ #include "core/hle/kernel/thread.h" namespace Common { - class Fiber; +class Fiber; } namespace Core { @@ -133,7 +133,8 @@ private: /// and reschedules current core if needed. void Unlock(); - void EnableInterruptAndSchedule(u32 cores_pending_reschedule, Core::EmuThreadHandle global_thread); + void EnableInterruptAndSchedule(u32 cores_pending_reschedule, + Core::EmuThreadHandle global_thread); /** * Add a thread to the suggested queue of a cpu core. Suggested threads may be diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 371beed0d..aad2ac549 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -1562,6 +1562,11 @@ static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr mutex_add current_thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT); + if (thread->IsPendingTermination()) { + lock.CancelSleep(); + return ERR_THREAD_TERMINATING; + } + const auto release_result = current_process->GetMutex().Release(mutex_addr); if (release_result.IsError()) { lock.CancelSleep(); @@ -1588,6 +1593,11 @@ static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr mutex_add { SchedulerLock lock(kernel); + auto* owner = current_thread->GetLockOwner(); + if (owner != nullptr) { + owner->RemoveMutexWaiter(SharedFrom(current_thread)); + } + current_process->RemoveConditionVariableThread(SharedFrom(current_thread)); } // Note: Deliberately don't attempt to inherit the lock owner's priority. @@ -1618,19 +1628,10 @@ static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_ for (std::size_t index = 0; index < last; ++index) { auto& thread = waiting_threads[index]; - if (thread->GetStatus() != ThreadStatus::WaitCondVar) { - last++; - last = std::min(waiting_threads.size(), last); - continue; - } - - time_manager.CancelTimeEvent(thread.get()); - ASSERT(thread->GetCondVarWaitAddress() == condition_variable_addr); // liberate Cond Var Thread. current_process->RemoveConditionVariableThread(thread); - thread->SetCondVarWaitAddress(0); const std::size_t current_core = system.CurrentCoreIndex(); auto& monitor = system.Monitor(); @@ -1655,9 +1656,6 @@ static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_ monitor.ClearExclusive(); if (mutex_val == 0) { // We were able to acquire the mutex, resume this thread. - ASSERT(thread->GetStatus() == ThreadStatus::WaitCondVar); - thread->ResumeFromWait(); - auto* const lock_owner = thread->GetLockOwner(); if (lock_owner != nullptr) { lock_owner->RemoveMutexWaiter(thread); @@ -1665,13 +1663,16 @@ static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_ thread->SetLockOwner(nullptr); thread->SetSynchronizationResults(nullptr, RESULT_SUCCESS); + thread->ResumeFromWait(); } else { // The mutex is already owned by some other thread, make this thread wait on it. const Handle owner_handle = static_cast(mutex_val & Mutex::MutexOwnerMask); const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); auto owner = handle_table.Get(owner_handle); ASSERT(owner); - thread->SetStatus(ThreadStatus::WaitMutex); + if (thread->GetStatus() == ThreadStatus::WaitCondVar) { + thread->SetStatus(ThreadStatus::WaitMutex); + } owner->AddMutexWaiter(thread); } diff --git a/src/core/hle/kernel/synchronization.cpp b/src/core/hle/kernel/synchronization.cpp index 4323fc120..275bf11cc 100644 --- a/src/core/hle/kernel/synchronization.cpp +++ b/src/core/hle/kernel/synchronization.cpp @@ -23,9 +23,10 @@ void Synchronization::SignalObject(SynchronizationObject& obj) const { if (obj.IsSignaled()) { for (auto thread : obj.GetWaitingThreads()) { if (thread->GetSchedulingStatus() == ThreadSchedStatus::Paused) { + ASSERT(thread->GetStatus() == ThreadStatus::WaitSynch); + ASSERT(thread->IsWaitingSync()); thread->SetSynchronizationResults(&obj, RESULT_SUCCESS); thread->ResumeFromWait(); - time_manager.CancelTimeEvent(thread.get()); } } obj.ClearWaitingThreads(); @@ -91,10 +92,11 @@ std::pair Synchronization::WaitFor( ResultCode signaling_result = thread->GetSignalingResult(); SynchronizationObject* signaling_object = thread->GetSignalingObject(); thread->SetSynchronizationObjects(nullptr); + auto shared_thread = SharedFrom(thread); for (auto& obj : sync_objects) { - obj->RemoveWaitingThread(SharedFrom(thread)); + obj->RemoveWaitingThread(shared_thread); } - if (signaling_result == RESULT_SUCCESS) { + if (signaling_object != nullptr) { const auto itr = std::find_if( sync_objects.begin(), sync_objects.end(), [signaling_object](const std::shared_ptr& object) { @@ -103,7 +105,7 @@ std::pair Synchronization::WaitFor( ASSERT(itr != sync_objects.end()); signaling_object->Acquire(thread); const u32 index = static_cast(std::distance(sync_objects.begin(), itr)); - return {RESULT_SUCCESS, index}; + return {signaling_result, index}; } return {signaling_result, -1}; } diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp index dab5fc4c6..cc228f5f7 100644 --- a/src/core/hle/kernel/time_manager.cpp +++ b/src/core/hle/kernel/time_manager.cpp @@ -22,7 +22,6 @@ TimeManager::TimeManager(Core::System& system_) : system{system_} { if (cancelled_events[proper_handle]) { return; } - event_fired[proper_handle] = true; std::shared_ptr thread = this->system.Kernel().RetrieveThreadFromGlobalHandleTable(proper_handle); thread->OnWakeUp(); @@ -39,7 +38,6 @@ void TimeManager::ScheduleTimeEvent(Handle& event_handle, Thread* timetask, s64 event_handle = InvalidHandle; } cancelled_events[event_handle] = false; - event_fired[event_handle] = false; } void TimeManager::UnscheduleTimeEvent(Handle event_handle) { diff --git a/src/core/hle/kernel/time_manager.h b/src/core/hle/kernel/time_manager.h index 3080ac838..307a18765 100644 --- a/src/core/hle/kernel/time_manager.h +++ b/src/core/hle/kernel/time_manager.h @@ -42,7 +42,6 @@ private: Core::System& system; std::shared_ptr time_manager_event_type; std::unordered_map cancelled_events; - std::unordered_map event_fired; }; } // namespace Kernel From 9e9c287f8b24ce9a932490cc35b3d0b5f58bb7a3 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sun, 8 Mar 2020 16:20:05 -0400 Subject: [PATCH 180/252] Kernel: Corrections to TimeManager, Scheduler and Mutex. --- src/core/hle/kernel/mutex.cpp | 3 +-- src/core/hle/kernel/scheduler.cpp | 2 +- src/core/hle/kernel/time_manager.cpp | 5 +++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 5a96d5e90..32dc1ffae 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -35,8 +35,6 @@ static std::pair, u32> GetHighestPriorityMutexWaitingThr if (thread->GetMutexWaitAddress() != mutex_addr) continue; - ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex); - ++num_waiters; if (highest_priority_thread == nullptr || thread->GetPriority() < highest_priority_thread->GetPriority()) { @@ -50,6 +48,7 @@ static std::pair, u32> GetHighestPriorityMutexWaitingThr /// Update the mutex owner field of all threads waiting on the mutex to point to the new owner. static void TransferMutexOwnership(VAddr mutex_addr, std::shared_ptr current_thread, std::shared_ptr new_owner) { + current_thread->RemoveMutexWaiter(new_owner); const auto threads = current_thread->GetMutexWaitingThreads(); for (const auto& thread : threads) { if (thread->GetMutexWaitAddress() != mutex_addr) diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index 5322f0aae..98fbb8fe5 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp @@ -93,7 +93,7 @@ u32 GlobalScheduler::SelectThreads() { iter++; s32 suggested_core_id = suggested->GetProcessorID(); Thread* top_thread = - suggested_core_id > 0 ? top_threads[suggested_core_id] : nullptr; + suggested_core_id >= 0 ? top_threads[suggested_core_id] : nullptr; if (top_thread != suggested) { if (top_thread != nullptr && top_thread->GetPriority() < THREADPRIO_MAX_CORE_MIGRATION) { diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp index cc228f5f7..941305e8e 100644 --- a/src/core/hle/kernel/time_manager.cpp +++ b/src/core/hle/kernel/time_manager.cpp @@ -32,8 +32,9 @@ void TimeManager::ScheduleTimeEvent(Handle& event_handle, Thread* timetask, s64 event_handle = timetask->GetGlobalHandle(); if (nanoseconds > 0) { ASSERT(timetask); - const s64 cycles = Core::Timing::nsToCycles(std::chrono::nanoseconds{nanoseconds}); - system.CoreTiming().ScheduleEvent(cycles, time_manager_event_type, event_handle); + ASSERT(timetask->GetStatus() != ThreadStatus::Ready); + ASSERT(timetask->GetStatus() != ThreadStatus::WaitMutex); + system.CoreTiming().ScheduleEvent(nanoseconds, time_manager_event_type, event_handle); } else { event_handle = InvalidHandle; } From 391f5f360d4144c21d65b998dd4e467b56533f78 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sun, 8 Mar 2020 21:13:18 -0400 Subject: [PATCH 181/252] Scheduler: Set last running time on thread. --- src/core/hle/kernel/scheduler.cpp | 2 ++ src/core/hle/kernel/thread.cpp | 4 ---- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index 98fbb8fe5..d68d86cdf 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp @@ -616,6 +616,7 @@ void Scheduler::SwitchContextStep2() { // Cancel any outstanding wakeup events for this thread new_thread->SetIsRunning(true); + new_thread->last_running_ticks = system.CoreTiming().GetCPUTicks(); auto* const thread_owner_process = current_thread->GetOwnerProcess(); if (previous_process != thread_owner_process && thread_owner_process != nullptr) { @@ -654,6 +655,7 @@ void Scheduler::SwitchContext() { // Save context for previous thread if (previous_thread) { + previous_thread->last_running_ticks = system.CoreTiming().GetCPUTicks(); if (!previous_thread->IsHLEThread()) { auto& cpu_core = system.ArmInterface(core_id); cpu_core.SaveContext(previous_thread->GetContext32()); diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 51cc5dcca..fc6c0bc85 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -295,10 +295,6 @@ void Thread::SetStatus(ThreadStatus new_status) { break; } - if (status == ThreadStatus::Running) { - last_running_ticks = Core::System::GetInstance().CoreTiming().GetCPUTicks(); - } - status = new_status; } From ab9aae28bf5daa5fa7105bb4ef41b6d0b3c9cdc1 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sun, 8 Mar 2020 22:39:41 -0400 Subject: [PATCH 182/252] General: Initial Setup for Single Core. --- src/core/core.cpp | 3 + src/core/cpu_manager.cpp | 194 ++++++++++++++++--- src/core/cpu_manager.h | 30 ++- src/core/hle/kernel/kernel.cpp | 19 ++ src/core/hle/kernel/kernel.h | 3 + src/core/memory.cpp | 8 +- src/yuzu/configuration/configure_general.cpp | 6 + src/yuzu/configuration/configure_general.ui | 7 + 8 files changed, 232 insertions(+), 38 deletions(-) diff --git a/src/core/core.cpp b/src/core/core.cpp index 5d4ecdce5..fd1bdcaf0 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -149,6 +149,9 @@ struct System::Impl { device_memory = std::make_unique(system); + kernel.SetMulticore(Settings::values.use_multi_core); + cpu_manager.SetMulticore(Settings::values.use_multi_core); + core_timing.Initialize([&system]() { system.RegisterHostThread(); }); kernel.Initialize(); cpu_manager.Initialize(); diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index 9a261968a..e72f89808 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp @@ -26,9 +26,13 @@ void CpuManager::ThreadStart(CpuManager& cpu_manager, std::size_t core) { void CpuManager::Initialize() { running_mode = true; - for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { - core_data[core].host_thread = - std::make_unique(ThreadStart, std::ref(*this), core); + if (is_multicore) { + for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { + core_data[core].host_thread = + std::make_unique(ThreadStart, std::ref(*this), core); + } + } else { + core_data[0].host_thread = std::make_unique(ThreadStart, std::ref(*this), 0); } } @@ -41,26 +45,6 @@ void CpuManager::Shutdown() { } } -void CpuManager::GuestThreadFunction(void* cpu_manager_) { - CpuManager* cpu_manager = static_cast(cpu_manager_); - cpu_manager->RunGuestThread(); -} - -void CpuManager::GuestRewindFunction(void* cpu_manager_) { - CpuManager* cpu_manager = static_cast(cpu_manager_); - cpu_manager->RunGuestLoop(); -} - -void CpuManager::IdleThreadFunction(void* cpu_manager_) { - CpuManager* cpu_manager = static_cast(cpu_manager_); - cpu_manager->RunIdleThread(); -} - -void CpuManager::SuspendThreadFunction(void* cpu_manager_) { - CpuManager* cpu_manager = static_cast(cpu_manager_); - cpu_manager->RunSuspendThread(); -} - std::function CpuManager::GetGuestThreadStartFunc() { return std::function(GuestThreadFunction); } @@ -73,20 +57,60 @@ std::function CpuManager::GetSuspendThreadStartFunc() { return std::function(SuspendThreadFunction); } +void CpuManager::GuestThreadFunction(void* cpu_manager_) { + CpuManager* cpu_manager = static_cast(cpu_manager_); + if (cpu_manager->is_multicore) { + cpu_manager->MultiCoreRunGuestThread(); + } else { + cpu_manager->SingleCoreRunGuestThread(); + } +} + +void CpuManager::GuestRewindFunction(void* cpu_manager_) { + CpuManager* cpu_manager = static_cast(cpu_manager_); + if (cpu_manager->is_multicore) { + cpu_manager->MultiCoreRunGuestLoop(); + } else { + cpu_manager->SingleCoreRunGuestLoop(); + } +} + +void CpuManager::IdleThreadFunction(void* cpu_manager_) { + CpuManager* cpu_manager = static_cast(cpu_manager_); + if (cpu_manager->is_multicore) { + cpu_manager->MultiCoreRunIdleThread(); + } else { + cpu_manager->SingleCoreRunIdleThread(); + } +} + +void CpuManager::SuspendThreadFunction(void* cpu_manager_) { + CpuManager* cpu_manager = static_cast(cpu_manager_); + if (cpu_manager->is_multicore) { + cpu_manager->MultiCoreRunSuspendThread(); + } else { + cpu_manager->SingleCoreRunSuspendThread(); + } +} + void* CpuManager::GetStartFuncParamater() { return static_cast(this); } -void CpuManager::RunGuestThread() { +/////////////////////////////////////////////////////////////////////////////// +/// MultiCore /// +/////////////////////////////////////////////////////////////////////////////// + +void CpuManager::MultiCoreRunGuestThread() { auto& kernel = system.Kernel(); { auto& sched = kernel.CurrentScheduler(); sched.OnThreadStart(); } - RunGuestLoop(); + MultiCoreRunGuestLoop(); } -void CpuManager::RunGuestLoop() { +void CpuManager::MultiCoreRunGuestLoop() { auto& kernel = system.Kernel(); auto* thread = kernel.CurrentScheduler().GetCurrentThread(); auto host_context = thread->GetHostContext(); @@ -103,7 +127,7 @@ void CpuManager::RunGuestLoop() { } } -void CpuManager::RunIdleThread() { +void CpuManager::MultiCoreRunIdleThread() { auto& kernel = system.Kernel(); while (true) { auto& physical_core = kernel.CurrentPhysicalCore(); @@ -113,7 +137,7 @@ void CpuManager::RunIdleThread() { } } -void CpuManager::RunSuspendThread() { +void CpuManager::MultiCoreRunSuspendThread() { auto& kernel = system.Kernel(); { auto& sched = kernel.CurrentScheduler(); @@ -130,7 +154,7 @@ void CpuManager::RunSuspendThread() { } } -void CpuManager::Pause(bool paused) { +void CpuManager::MultiCorePause(bool paused) { if (!paused) { bool all_not_barrier = false; while (!all_not_barrier) { @@ -171,10 +195,120 @@ void CpuManager::Pause(bool paused) { paused_state = paused; } +/////////////////////////////////////////////////////////////////////////////// +/// SingleCore /// +/////////////////////////////////////////////////////////////////////////////// + +void CpuManager::SingleCoreRunGuestThread() { + auto& kernel = system.Kernel(); + { + auto& sched = kernel.CurrentScheduler(); + sched.OnThreadStart(); + } + SingleCoreRunGuestLoop(); +} + +void CpuManager::SingleCoreRunGuestLoop() { + auto& kernel = system.Kernel(); + auto* thread = kernel.CurrentScheduler().GetCurrentThread(); + auto host_context = thread->GetHostContext(); + host_context->SetRewindPoint(std::function(GuestRewindFunction), this); + host_context.reset(); + while (true) { + auto& physical_core = kernel.CurrentPhysicalCore(); + while (!physical_core.IsInterrupted()) { + physical_core.Run(); + preemption_count++; + if (preemption_count % max_cycle_runs == 0) { + break; + } + } + physical_core.ClearExclusive(); + PreemptSingleCore(); + auto& scheduler = physical_core.Scheduler(); + scheduler.TryDoContextSwitch(); + } +} + +void CpuManager::SingleCoreRunIdleThread() { + auto& kernel = system.Kernel(); + while (true) { + auto& physical_core = kernel.CurrentPhysicalCore(); + PreemptSingleCore(); + auto& scheduler = physical_core.Scheduler(); + scheduler.TryDoContextSwitch(); + } +} + +void CpuManager::SingleCoreRunSuspendThread() { + auto& kernel = system.Kernel(); + { + auto& sched = kernel.CurrentScheduler(); + sched.OnThreadStart(); + } + while (true) { + auto core = kernel.GetCurrentHostThreadID(); + auto& scheduler = kernel.CurrentScheduler(); + Kernel::Thread* current_thread = scheduler.GetCurrentThread(); + Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[0].host_context); + ASSERT(scheduler.ContextSwitchPending()); + ASSERT(core == kernel.GetCurrentHostThreadID()); + scheduler.TryDoContextSwitch(); + } +} + +void CpuManager::PreemptSingleCore() { + preemption_count = 0; + std::size_t old_core = current_core; + current_core = (current_core + 1) % Core::Hardware::NUM_CPU_CORES; + auto& scheduler = system.Kernel().Scheduler(old_core); + Kernel::Thread* current_thread = system.Kernel().Scheduler(old_core).GetCurrentThread(); + Kernel::Thread* next_thread = system.Kernel().Scheduler(current_core).GetCurrentThread(); + Common::Fiber::YieldTo(current_thread->GetHostContext(), next_thread->GetHostContext()); +} + +void CpuManager::SingleCorePause(bool paused) { + if (!paused) { + bool all_not_barrier = false; + while (!all_not_barrier) { + all_not_barrier = !core_data[0].is_running.load() && core_data[0].initialized.load(); + } + core_data[0].enter_barrier->Set(); + if (paused_state.load()) { + bool all_barrier = false; + while (!all_barrier) { + all_barrier = core_data[0].is_paused.load() && core_data[0].initialized.load(); + } + core_data[0].exit_barrier->Set(); + } + } else { + /// Wait until all cores are paused. + bool all_barrier = false; + while (!all_barrier) { + all_barrier = core_data[0].is_paused.load() && core_data[0].initialized.load(); + } + /// Don't release the barrier + } + paused_state = paused; +} + +void CpuManager::Pause(bool paused) { + if (is_multicore) { + MultiCorePause(paused); + } else { + SingleCorePause(paused); + } +} + void CpuManager::RunThread(std::size_t core) { /// Initialization system.RegisterCoreThread(core); - std::string name = "yuzu:CoreHostThread_" + std::to_string(core); + std::string name; + if (is_multicore) { + name = "yuzu:CoreCPUThread_" + std::to_string(core); + } else { + name = "yuzu:CPUThread"; + } MicroProfileOnThreadCreate(name.c_str()); Common::SetCurrentThreadName(name.c_str()); auto& data = core_data[core]; diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h index e83ab20f9..1e81481ec 100644 --- a/src/core/cpu_manager.h +++ b/src/core/cpu_manager.h @@ -30,6 +30,10 @@ public: CpuManager& operator=(const CpuManager&) = delete; CpuManager& operator=(CpuManager&&) = delete; + /// Sets if emulation is multicore or single core, must be set before Initialize + void SetMulticore(bool is_multicore) { + this->is_multicore = is_multicore; + } void Initialize(); void Shutdown(); @@ -40,21 +44,34 @@ public: std::function GetSuspendThreadStartFunc(); void* GetStartFuncParamater(); + std::size_t CurrentCore() const { + return current_core; + } + private: static void GuestThreadFunction(void* cpu_manager); static void GuestRewindFunction(void* cpu_manager); static void IdleThreadFunction(void* cpu_manager); static void SuspendThreadFunction(void* cpu_manager); - void RunGuestThread(); - void RunGuestLoop(); - void RunIdleThread(); - void RunSuspendThread(); + void MultiCoreRunGuestThread(); + void MultiCoreRunGuestLoop(); + void MultiCoreRunIdleThread(); + void MultiCoreRunSuspendThread(); + void MultiCorePause(bool paused); + + void SingleCoreRunGuestThread(); + void SingleCoreRunGuestLoop(); + void SingleCoreRunIdleThread(); + void SingleCoreRunSuspendThread(); + void SingleCorePause(bool paused); static void ThreadStart(CpuManager& cpu_manager, std::size_t core); void RunThread(std::size_t core); + void PreemptSingleCore(); + struct CoreData { std::shared_ptr host_context; std::unique_ptr enter_barrier; @@ -70,6 +87,11 @@ private: std::array core_data{}; + bool is_multicore{}; + std::size_t current_core{}; + std::size_t preemption_count{}; + static constexpr std::size_t max_cycle_runs = 5; + System& system; }; diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 721ab1e70..4a091ea38 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -113,6 +113,10 @@ struct KernelCore::Impl { explicit Impl(Core::System& system, KernelCore& kernel) : global_scheduler{kernel}, synchronization{system}, time_manager{system}, system{system} {} + void SetMulticore(bool is_multicore) { + this->is_multicore = is_multicore; + } + void Initialize(KernelCore& kernel) { Shutdown(); @@ -237,6 +241,9 @@ struct KernelCore::Impl { void RegisterCoreThread(std::size_t core_id) { std::unique_lock lock{register_thread_mutex}; + if (!is_multicore) { + single_core_thread_id = std::this_thread::get_id(); + } const std::thread::id this_id = std::this_thread::get_id(); const auto it = host_thread_ids.find(this_id); ASSERT(core_id < Core::Hardware::NUM_CPU_CORES); @@ -258,6 +265,11 @@ struct KernelCore::Impl { u32 GetCurrentHostThreadID() const { const std::thread::id this_id = std::this_thread::get_id(); + if (!is_multicore) { + if (single_core_thread_id == this_id) { + return static_cast(system.GetCpuManager().CurrentCore()); + } + } const auto it = host_thread_ids.find(this_id); if (it == host_thread_ids.end()) { return Core::INVALID_HOST_THREAD_ID; @@ -378,6 +390,9 @@ struct KernelCore::Impl { std::array, Core::Hardware::NUM_CPU_CORES> suspend_threads{}; + bool is_multicore{}; + std::thread::id single_core_thread_id{}; + // System context Core::System& system; }; @@ -387,6 +402,10 @@ KernelCore::~KernelCore() { Shutdown(); } +void KernelCore::SetMulticore(bool is_multicore) { + impl->SetMulticore(is_multicore); +} + void KernelCore::Initialize() { impl->Initialize(*this); } diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 5d32a8329..162bbd2f8 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -65,6 +65,9 @@ public: KernelCore(KernelCore&&) = delete; KernelCore& operator=(KernelCore&&) = delete; + /// Sets if emulation is multicore or single core, must be set before Initialize + void SetMulticore(bool is_multicore); + /// Resets the kernel to a clean slate for use. void Initialize(); diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 4cb5d05e5..7def00768 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -715,8 +715,8 @@ struct Memory::Impl { ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr); break; case Common::PageType::RasterizerCachedMemory: { - u8* host_ptr{GetPointerFromVMA(vaddr)}; - system.GPU().InvalidateRegion(ToCacheAddr(host_ptr), sizeof(T)); + u8* host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)}; + system.GPU().InvalidateRegion(vaddr, sizeof(T)); T volatile* pointer = reinterpret_cast(&host_ptr); return Common::AtomicCompareAndSwap(pointer, data, expected); break; @@ -745,8 +745,8 @@ struct Memory::Impl { ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr); break; case Common::PageType::RasterizerCachedMemory: { - u8* host_ptr{GetPointerFromVMA(vaddr)}; - system.GPU().InvalidateRegion(ToCacheAddr(host_ptr), sizeof(u128)); + u8* host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)}; + system.GPU().InvalidateRegion(vaddr, sizeof(u128)); u64 volatile* pointer = reinterpret_cast(&host_ptr); return Common::AtomicCompareAndSwap(pointer, data, expected); break; diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp index cb95423e0..74b2ad537 100644 --- a/src/yuzu/configuration/configure_general.cpp +++ b/src/yuzu/configuration/configure_general.cpp @@ -23,6 +23,11 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent) ConfigureGeneral::~ConfigureGeneral() = default; void ConfigureGeneral::SetConfiguration() { + const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn(); + + ui->use_multi_core->setEnabled(runtime_lock); + ui->use_multi_core->setChecked(Settings::values.use_multi_core); + ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing); ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot); ui->toggle_background_pause->setChecked(UISettings::values.pause_when_in_background); @@ -41,6 +46,7 @@ void ConfigureGeneral::ApplyConfiguration() { Settings::values.use_frame_limit = ui->toggle_frame_limit->isChecked(); Settings::values.frame_limit = ui->frame_limit->value(); + Settings::values.use_multi_core = ui->use_multi_core->isChecked(); } void ConfigureGeneral::changeEvent(QEvent* event) { diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui index fc3b7e65a..f872bddec 100644 --- a/src/yuzu/configuration/configure_general.ui +++ b/src/yuzu/configuration/configure_general.ui @@ -51,6 +51,13 @@ + + + + Emulate CPU in Multiple Cores + + + From 8a78fc25802b8f69d073b703728a3501c98bf5e8 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Mon, 9 Mar 2020 10:51:05 -0400 Subject: [PATCH 183/252] Synchronization: Correct wide Assertion. --- src/core/hle/kernel/synchronization.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core/hle/kernel/synchronization.cpp b/src/core/hle/kernel/synchronization.cpp index 275bf11cc..851b702a5 100644 --- a/src/core/hle/kernel/synchronization.cpp +++ b/src/core/hle/kernel/synchronization.cpp @@ -23,8 +23,10 @@ void Synchronization::SignalObject(SynchronizationObject& obj) const { if (obj.IsSignaled()) { for (auto thread : obj.GetWaitingThreads()) { if (thread->GetSchedulingStatus() == ThreadSchedStatus::Paused) { - ASSERT(thread->GetStatus() == ThreadStatus::WaitSynch); - ASSERT(thread->IsWaitingSync()); + if (thread->GetStatus() != ThreadStatus::WaitHLEEvent) { + ASSERT(thread->GetStatus() == ThreadStatus::WaitSynch); + ASSERT(thread->IsWaitingSync()); + } thread->SetSynchronizationResults(&obj, RESULT_SUCCESS); thread->ResumeFromWait(); } From a439cdf22ea50f0e39cb51f6dff15fee3b495d16 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Tue, 10 Mar 2020 11:50:33 -0400 Subject: [PATCH 184/252] CPU_Manager: Unload/Reload threads on preemption on SingleCore --- src/core/cpu_manager.cpp | 14 +++++++---- src/core/cpu_manager.h | 5 ++-- src/core/hle/kernel/scheduler.cpp | 42 +++++++++++++++++++++++++++++++ src/core/hle/kernel/scheduler.h | 10 ++++++++ 4 files changed, 64 insertions(+), 7 deletions(-) diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index e72f89808..95842aad1 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp @@ -225,7 +225,7 @@ void CpuManager::SingleCoreRunGuestLoop() { } physical_core.ClearExclusive(); PreemptSingleCore(); - auto& scheduler = physical_core.Scheduler(); + auto& scheduler = kernel.Scheduler(current_core); scheduler.TryDoContextSwitch(); } } @@ -260,11 +260,15 @@ void CpuManager::SingleCoreRunSuspendThread() { void CpuManager::PreemptSingleCore() { preemption_count = 0; std::size_t old_core = current_core; - current_core = (current_core + 1) % Core::Hardware::NUM_CPU_CORES; + current_core.store((current_core + 1) % Core::Hardware::NUM_CPU_CORES); auto& scheduler = system.Kernel().Scheduler(old_core); - Kernel::Thread* current_thread = system.Kernel().Scheduler(old_core).GetCurrentThread(); - Kernel::Thread* next_thread = system.Kernel().Scheduler(current_core).GetCurrentThread(); - Common::Fiber::YieldTo(current_thread->GetHostContext(), next_thread->GetHostContext()); + Kernel::Thread* current_thread = scheduler.GetCurrentThread(); + scheduler.Unload(); + auto& next_scheduler = system.Kernel().Scheduler(current_core); + Common::Fiber::YieldTo(current_thread->GetHostContext(), next_scheduler.ControlContext()); + /// May have changed scheduler + auto& current_scheduler = system.Kernel().Scheduler(current_core); + current_scheduler.Reload(); } void CpuManager::SingleCorePause(bool paused) { diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h index 1e81481ec..ff1935d5c 100644 --- a/src/core/cpu_manager.h +++ b/src/core/cpu_manager.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include #include #include @@ -45,7 +46,7 @@ public: void* GetStartFuncParamater(); std::size_t CurrentCore() const { - return current_core; + return current_core.load(); } private: @@ -88,7 +89,7 @@ private: std::array core_data{}; bool is_multicore{}; - std::size_t current_core{}; + std::atomic current_core{}; std::size_t preemption_count{}; static constexpr std::size_t max_cycle_runs = 5; diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index d68d86cdf..00322d997 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp @@ -602,6 +602,48 @@ void Scheduler::OnThreadStart() { SwitchContextStep2(); } +void Scheduler::Unload() { + Thread* thread = current_thread.get(); + if (thread) { + thread->last_running_ticks = system.CoreTiming().GetCPUTicks(); + thread->SetIsRunning(false); + if (!thread->IsHLEThread()) { + auto& cpu_core = system.ArmInterface(core_id); + cpu_core.SaveContext(thread->GetContext32()); + cpu_core.SaveContext(thread->GetContext64()); + // Save the TPIDR_EL0 system register in case it was modified. + thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0()); + cpu_core.ClearExclusiveState(); + } + thread->context_guard.unlock(); + } +} + +void Scheduler::Reload() { + Thread* thread = current_thread.get(); + if (thread) { + ASSERT_MSG(thread->GetSchedulingStatus() == ThreadSchedStatus::Runnable, + "Thread must be runnable."); + + // Cancel any outstanding wakeup events for this thread + thread->SetIsRunning(true); + thread->last_running_ticks = system.CoreTiming().GetCPUTicks(); + + auto* const thread_owner_process = thread->GetOwnerProcess(); + if (thread_owner_process != nullptr) { + system.Kernel().MakeCurrentProcess(thread_owner_process); + } + if (!thread->IsHLEThread()) { + auto& cpu_core = system.ArmInterface(core_id); + cpu_core.LoadContext(thread->GetContext32()); + cpu_core.LoadContext(thread->GetContext64()); + cpu_core.SetTlsAddress(thread->GetTLSAddress()); + cpu_core.SetTPIDR_EL0(thread->GetTPIDR_EL0()); + cpu_core.ClearExclusiveState(); + } + } +} + void Scheduler::SwitchContextStep2() { Thread* previous_thread = current_thread_prev.get(); Thread* new_thread = selected_thread.get(); diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h index 5e062bf59..f63cc5085 100644 --- a/src/core/hle/kernel/scheduler.h +++ b/src/core/hle/kernel/scheduler.h @@ -210,6 +210,12 @@ public: /// Reschedules to the next available thread (call after current thread is suspended) void TryDoContextSwitch(); + /// The next two are for SingleCore Only. + /// Unload current thread before preempting core. + void Unload(); + /// Reload current thread after core preemption. + void Reload(); + /// Gets the current running thread Thread* GetCurrentThread() const; @@ -230,6 +236,10 @@ public: void OnThreadStart(); + std::shared_ptr ControlContext() { + return switch_fiber; + } + private: friend class GlobalScheduler; From d494b074e8afd3aff7b65afc7b977496be06ccc9 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Tue, 10 Mar 2020 13:13:39 -0400 Subject: [PATCH 185/252] Kernel: Preempt Single core on redudant yields. --- src/core/cpu_manager.h | 4 ++-- src/core/hle/kernel/kernel.cpp | 4 ++++ src/core/hle/kernel/kernel.h | 2 ++ src/core/hle/kernel/svc.cpp | 23 +++++++++++++++++------ src/core/hle/kernel/thread.cpp | 21 ++++++++++++--------- src/core/hle/kernel/thread.h | 9 +++++---- 6 files changed, 42 insertions(+), 21 deletions(-) diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h index ff1935d5c..c0e454a7d 100644 --- a/src/core/cpu_manager.h +++ b/src/core/cpu_manager.h @@ -45,6 +45,8 @@ public: std::function GetSuspendThreadStartFunc(); void* GetStartFuncParamater(); + void PreemptSingleCore(); + std::size_t CurrentCore() const { return current_core.load(); } @@ -71,8 +73,6 @@ private: void RunThread(std::size_t core); - void PreemptSingleCore(); - struct CoreData { std::shared_ptr host_context; std::unique_ptr enter_barrier; diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 4a091ea38..2a1b91752 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -642,6 +642,10 @@ void KernelCore::Suspend(bool in_suspention) { } } +bool KernelCore::IsMulticore() const { + return impl->is_multicore; +} + void KernelCore::ExceptionalExit() { exception_exited = true; Suspend(true); diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 162bbd2f8..50eeb50ec 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -212,6 +212,8 @@ public: /// Exceptional exit the OS. void ExceptionalExit(); + bool IsMulticore() const; + private: friend class Object; friend class Process; diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index aad2ac549..eca92b356 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -19,6 +19,7 @@ #include "core/core_manager.h" #include "core/core_timing.h" #include "core/core_timing_util.h" +#include "core/cpu_manager.h" #include "core/hle/kernel/address_arbiter.h" #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/client_session.h" @@ -1509,21 +1510,31 @@ static void SleepThread(Core::System& system, s64 nanoseconds) { if (nanoseconds <= 0) { switch (static_cast(nanoseconds)) { - case SleepType::YieldWithoutLoadBalancing: - current_thread->YieldSimple(); + case SleepType::YieldWithoutLoadBalancing: { + auto pair = current_thread->YieldSimple(); + is_redundant = pair.second; break; - case SleepType::YieldWithLoadBalancing: - current_thread->YieldAndBalanceLoad(); + } + case SleepType::YieldWithLoadBalancing: { + auto pair = current_thread->YieldAndBalanceLoad(); + is_redundant = pair.second; break; - case SleepType::YieldAndWaitForLoadBalancing: - current_thread->YieldAndWaitForLoadBalancing(); + } + case SleepType::YieldAndWaitForLoadBalancing: { + auto pair = current_thread->YieldAndWaitForLoadBalancing(); + is_redundant = pair.second; break; + } default: UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds); } } else { current_thread->Sleep(nanoseconds); } + + if (is_redundant && !system.Kernel().IsMulticore()) { + system.GetCpuManager().PreemptSingleCore(); + } } /// Wait process wide key atomic diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index fc6c0bc85..1c32552b1 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -435,28 +435,31 @@ ResultCode Thread::Sleep(s64 nanoseconds) { return RESULT_SUCCESS; } -ResultCode Thread::YieldSimple() { +std::pair Thread::YieldSimple() { + bool is_redundant = false; { SchedulerLock lock(kernel); - kernel.GlobalScheduler().YieldThread(this); + is_redundant = kernel.GlobalScheduler().YieldThread(this); } - return RESULT_SUCCESS; + return {RESULT_SUCCESS, is_redundant}; } -ResultCode Thread::YieldAndBalanceLoad() { +std::pair Thread::YieldAndBalanceLoad() { + bool is_redundant = false; { SchedulerLock lock(kernel); - kernel.GlobalScheduler().YieldThreadAndBalanceLoad(this); + is_redundant = kernel.GlobalScheduler().YieldThreadAndBalanceLoad(this); } - return RESULT_SUCCESS; + return {RESULT_SUCCESS, is_redundant}; } -ResultCode Thread::YieldAndWaitForLoadBalancing() { +std::pair Thread::YieldAndWaitForLoadBalancing() { + bool is_redundant = false; { SchedulerLock lock(kernel); - kernel.GlobalScheduler().YieldThreadAndWaitForLoadBalancing(this); + is_redundant = kernel.GlobalScheduler().YieldThreadAndWaitForLoadBalancing(this); } - return RESULT_SUCCESS; + return {RESULT_SUCCESS, is_redundant}; } void Thread::AddSchedulingFlag(ThreadSchedFlags flag) { diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 953b023b5..9a29875ac 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -6,6 +6,7 @@ #include #include +#include #include #include "common/common_types.h" @@ -503,13 +504,13 @@ public: ResultCode Sleep(s64 nanoseconds); /// Yields this thread without rebalancing loads. - ResultCode YieldSimple(); + std::pair YieldSimple(); /// Yields this thread and does a load rebalancing. - ResultCode YieldAndBalanceLoad(); + std::pair YieldAndBalanceLoad(); /// Yields this thread and if the core is left idle, loads are rebalanced - ResultCode YieldAndWaitForLoadBalancing(); + std::pair YieldAndWaitForLoadBalancing(); void IncrementYieldCount() { yield_count++; @@ -587,7 +588,7 @@ private: ThreadContext32 context_32{}; ThreadContext64 context_64{}; Common::SpinLock context_guard{}; - std::shared_ptr host_context{}; + std::shared_ptr host_context{}; u64 thread_id = 0; From f370de84b16f9d668b7d5dcc0fd851264e2e6144 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Tue, 10 Mar 2020 18:41:11 -0400 Subject: [PATCH 186/252] Kernel: Rewind on SVC change. --- src/core/hle/kernel/scheduler.cpp | 2 ++ src/core/hle/kernel/svc.cpp | 9 ++++----- src/core/hle/kernel/thread.h | 10 ++++++++++ 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index 00322d997..25fc8a3e8 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp @@ -605,6 +605,7 @@ void Scheduler::OnThreadStart() { void Scheduler::Unload() { Thread* thread = current_thread.get(); if (thread) { + thread->SetContinuousOnSVC(false); thread->last_running_ticks = system.CoreTiming().GetCPUTicks(); thread->SetIsRunning(false); if (!thread->IsHLEThread()) { @@ -697,6 +698,7 @@ void Scheduler::SwitchContext() { // Save context for previous thread if (previous_thread) { + previous_thread->SetContinuousOnSVC(false); previous_thread->last_running_ticks = system.CoreTiming().GetCPUTicks(); if (!previous_thread->IsHLEThread()) { auto& cpu_core = system.ArmInterface(core_id); diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index eca92b356..d3d4e7bf9 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -2459,7 +2459,8 @@ MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70)); void Call(Core::System& system, u32 immediate) { MICROPROFILE_SCOPE(Kernel_SVC); - auto& physical_core = system.CurrentPhysicalCore(); + auto* thread = system.CurrentScheduler().GetCurrentThread(); + thread->SetContinuousOnSVC(true); const FunctionDef* info = system.CurrentProcess()->Is64BitProcess() ? GetSVCInfo64(immediate) : GetSVCInfo32(immediate); @@ -2472,10 +2473,8 @@ void Call(Core::System& system, u32 immediate) { } else { LOG_CRITICAL(Kernel_SVC, "Unknown SVC function 0x{:X}", immediate); } - auto& physical_core_2 = system.CurrentPhysicalCore(); - if (physical_core.CoreIndex() != physical_core_2.CoreIndex()) { - LOG_CRITICAL(Kernel_SVC, "Rewinding"); - auto* thread = physical_core_2.Scheduler().GetCurrentThread(); + + if (!thread->IsContinuousOnSVC()) { auto* host_context = thread->GetHostContext().get(); host_context->Rewind(); } diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 9a29875ac..168828ab0 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -573,6 +573,14 @@ public: return pausing_state != 0; } + bool IsContinuousOnSVC() const { + return is_continuous_on_svc; + } + + void SetContinuousOnSVC(bool is_continuous) { + is_continuous_on_svc = is_continuous; + } + private: friend class GlobalScheduler; friend class Scheduler; @@ -672,6 +680,8 @@ private: bool is_waiting_on_sync = false; bool is_sync_cancelled = false; + bool is_continuous_on_svc = false; + bool will_be_terminated = false; std::string name; From e6f8bde74b9476dced103c6c54ab81616d34b97e Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Wed, 11 Mar 2020 20:44:53 -0400 Subject: [PATCH 187/252] General: Fix Stop function --- src/core/core_timing.cpp | 1 + src/core/hle/kernel/kernel.cpp | 15 +++++++++++++++ src/core/hle/kernel/thread.cpp | 8 +++++--- src/yuzu/bootmanager.cpp | 6 ++++-- 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index c91ae9975..3438f79ce 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -61,6 +61,7 @@ void CoreTiming::Initialize(std::function&& on_thread_init_) { void CoreTiming::Shutdown() { paused = true; shutting_down = true; + pause_event.Set(); event.Set(); timer_thread->join(); ClearPendingEvents(); diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 2a1b91752..24da4367e 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -119,6 +119,7 @@ struct KernelCore::Impl { void Initialize(KernelCore& kernel) { Shutdown(); + RegisterHostThread(); InitializePhysicalCores(); InitializeSystemResourceLimit(kernel); @@ -135,6 +136,19 @@ struct KernelCore::Impl { next_user_process_id = Process::ProcessIDMin; next_thread_id = 1; + for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { + if (suspend_threads[i]) { + suspend_threads[i].reset(); + } + } + + for (std::size_t i = 0; i < cores.size(); i++) { + cores[i].Shutdown(); + } + cores.clear(); + + registered_core_threads.reset(); + process_list.clear(); current_process = nullptr; @@ -154,6 +168,7 @@ struct KernelCore::Impl { cores.clear(); exclusive_monitor.reset(); + host_thread_ids.clear(); } void InitializePhysicalCores() { diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 1c32552b1..6f8e7a070 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -56,10 +56,12 @@ void Thread::Stop() { Signal(); kernel.GlobalHandleTable().Close(global_handle); - owner_process->UnregisterThread(this); + if (owner_process) { + owner_process->UnregisterThread(this); - // Mark the TLS slot in the thread's page as free. - owner_process->FreeTLSRegion(tls_address); + // Mark the TLS slot in the thread's page as free. + owner_process->FreeTLSRegion(tls_address); + } } global_handle = 0; } diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 468dde782..6fad01d50 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -89,8 +89,10 @@ void EmuThread::run() { } running_guard = false; - was_active = true; - emit DebugModeEntered(); + if (!stop_run) { + was_active = true; + emit DebugModeEntered(); + } } else if (exec_step) { UNIMPLEMENTED(); } else { From 7020d498c5aef7c1180bfc57031cdd7fbfecdf0f Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Thu, 12 Mar 2020 16:48:43 -0400 Subject: [PATCH 188/252] General: Fix microprofile on dynarmic/svc, fix wait tree showing which threads were running. --- src/core/arm/dynarmic/arm_dynarmic_32.cpp | 4 ---- src/core/arm/dynarmic/arm_dynarmic_64.cpp | 5 +---- src/core/core.cpp | 15 +++++++++++++++ src/core/core.h | 6 ++++++ src/core/cpu_manager.cpp | 4 ++++ src/core/hle/kernel/kernel.cpp | 15 +++++++++++++++ src/core/hle/kernel/kernel.h | 4 ++++ src/core/hle/kernel/scheduler.cpp | 7 +++++++ src/core/hle/kernel/svc.cpp | 10 +++++++--- src/core/hle/kernel/thread.h | 18 ++++++++++++++++++ src/yuzu/debugger/wait_tree.cpp | 12 ++++++++++-- 11 files changed, 87 insertions(+), 13 deletions(-) diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index 30bf62ac1..618f02f30 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -7,7 +7,6 @@ #include #include #include -#include "common/microprofile.h" #include "core/arm/cpu_interrupt_handler.h" #include "core/arm/dynarmic/arm_dynarmic_32.h" #include "core/arm/dynarmic/arm_dynarmic_64.h" @@ -97,10 +96,7 @@ std::shared_ptr ARM_Dynarmic_32::MakeJit(Common::PageTable& return std::make_unique(config); } -MICROPROFILE_DEFINE(ARM_Jit_Dynarmic_32, "ARM JIT", "Dynarmic", MP_RGB(255, 64, 64)); - void ARM_Dynarmic_32::Run() { - MICROPROFILE_SCOPE(ARM_Jit_Dynarmic_32); jit->Run(); } diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index a22c22bf0..84b872f0b 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -7,7 +7,6 @@ #include #include #include "common/logging/log.h" -#include "common/microprofile.h" #include "common/page_table.h" #include "core/arm/cpu_interrupt_handler.h" #include "core/arm/dynarmic/arm_dynarmic_64.h" @@ -181,11 +180,9 @@ std::shared_ptr ARM_Dynarmic_64::MakeJit(Common::PageTable& return std::make_shared(config); } -MICROPROFILE_DEFINE(ARM_Jit_Dynarmic_64, "ARM JIT", "Dynarmic", MP_RGB(255, 64, 64)); + void ARM_Dynarmic_64::Run() { - MICROPROFILE_SCOPE(ARM_Jit_Dynarmic_64); - jit->Run(); } diff --git a/src/core/core.cpp b/src/core/core.cpp index fd1bdcaf0..032da7aa5 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -8,6 +8,7 @@ #include "common/file_util.h" #include "common/logging/log.h" +#include "common/microprofile.h" #include "common/string_util.h" #include "core/arm/exclusive_monitor.h" #include "core/core.h" @@ -50,6 +51,8 @@ #include "video_core/renderer_base.h" #include "video_core/video_core.h" +MICROPROFILE_DEFINE(ARM_Jit_Dynarmic, "ARM JIT", "Dynarmic", MP_RGB(255, 64, 64)); + namespace Core { namespace { @@ -391,6 +394,8 @@ struct System::Impl { std::unique_ptr perf_stats; Core::FrameLimiter frame_limiter; + + std::array dynarmic_ticks{}; }; System::System() : impl{std::make_unique(*this)} {} @@ -736,4 +741,14 @@ void System::RegisterHostThread() { impl->kernel.RegisterHostThread(); } +void System::EnterDynarmicProfile() { + std::size_t core = impl->kernel.GetCurrentHostThreadID(); + impl->dynarmic_ticks[core] = MicroProfileEnter(MICROPROFILE_TOKEN(ARM_Jit_Dynarmic)); +} + +void System::ExitDynarmicProfile() { + std::size_t core = impl->kernel.GetCurrentHostThreadID(); + MicroProfileLeave(MICROPROFILE_TOKEN(ARM_Jit_Dynarmic), impl->dynarmic_ticks[core]); +} + } // namespace Core diff --git a/src/core/core.h b/src/core/core.h index 9a0dd1075..87df79d57 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -377,6 +377,12 @@ public: /// Register a host thread as an auxiliary thread. void RegisterHostThread(); + /// Enter Dynarmic Microprofile + void EnterDynarmicProfile(); + + /// Exit Dynarmic Microprofile + void ExitDynarmicProfile(); + private: System(); diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index 95842aad1..9e2e6d49f 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp @@ -118,9 +118,11 @@ void CpuManager::MultiCoreRunGuestLoop() { host_context.reset(); while (true) { auto& physical_core = kernel.CurrentPhysicalCore(); + system.EnterDynarmicProfile(); while (!physical_core.IsInterrupted()) { physical_core.Run(); } + system.ExitDynarmicProfile(); physical_core.ClearExclusive(); auto& scheduler = physical_core.Scheduler(); scheduler.TryDoContextSwitch(); @@ -216,6 +218,7 @@ void CpuManager::SingleCoreRunGuestLoop() { host_context.reset(); while (true) { auto& physical_core = kernel.CurrentPhysicalCore(); + system.EnterDynarmicProfile(); while (!physical_core.IsInterrupted()) { physical_core.Run(); preemption_count++; @@ -224,6 +227,7 @@ void CpuManager::SingleCoreRunGuestLoop() { } } physical_core.ClearExclusive(); + system.ExitDynarmicProfile(); PreemptSingleCore(); auto& scheduler = kernel.Scheduler(current_core); scheduler.TryDoContextSwitch(); diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 24da4367e..d2f5f9bf2 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -13,6 +13,7 @@ #include "common/assert.h" #include "common/logging/log.h" +#include "common/microprofile.h" #include "common/thread.h" #include "core/arm/arm_interface.h" #include "core/arm/exclusive_monitor.h" @@ -41,6 +42,8 @@ #include "core/hle/result.h" #include "core/memory.h" +MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70)); + namespace Kernel { /** @@ -408,6 +411,8 @@ struct KernelCore::Impl { bool is_multicore{}; std::thread::id single_core_thread_id{}; + std::array svc_ticks{}; + // System context Core::System& system; }; @@ -666,4 +671,14 @@ void KernelCore::ExceptionalExit() { Suspend(true); } +void KernelCore::EnterSVCProfile() { + std::size_t core = impl->GetCurrentHostThreadID(); + impl->svc_ticks[core] = MicroProfileEnter(MICROPROFILE_TOKEN(Kernel_SVC)); +} + +void KernelCore::ExitSVCProfile() { + std::size_t core = impl->GetCurrentHostThreadID(); + MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[core]); +} + } // namespace Kernel diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 50eeb50ec..1eb6ede73 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -214,6 +214,10 @@ public: bool IsMulticore() const; + void EnterSVCProfile(); + + void ExitSVCProfile(); + private: friend class Object; friend class Process; diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index 25fc8a3e8..2ad380b17 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp @@ -354,7 +354,9 @@ void GlobalScheduler::EnableInterruptAndSchedule(u32 cores_pending_reschedule, } if (must_context_switch) { auto& core_scheduler = kernel.CurrentScheduler(); + kernel.ExitSVCProfile(); core_scheduler.TryDoContextSwitch(); + kernel.EnterSVCProfile(); } } @@ -628,6 +630,7 @@ void Scheduler::Reload() { // Cancel any outstanding wakeup events for this thread thread->SetIsRunning(true); + thread->SetWasRunning(false); thread->last_running_ticks = system.CoreTiming().GetCPUTicks(); auto* const thread_owner_process = thread->GetOwnerProcess(); @@ -660,6 +663,7 @@ void Scheduler::SwitchContextStep2() { // Cancel any outstanding wakeup events for this thread new_thread->SetIsRunning(true); new_thread->last_running_ticks = system.CoreTiming().GetCPUTicks(); + new_thread->SetWasRunning(false); auto* const thread_owner_process = current_thread->GetOwnerProcess(); if (previous_process != thread_owner_process && thread_owner_process != nullptr) { @@ -698,6 +702,9 @@ void Scheduler::SwitchContext() { // Save context for previous thread if (previous_thread) { + if (new_thread != nullptr && new_thread->IsSuspendThread()) { + previous_thread->SetWasRunning(true); + } previous_thread->SetContinuousOnSVC(false); previous_thread->last_running_ticks = system.CoreTiming().GetCPUTicks(); if (!previous_thread->IsHLEThread()) { diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index d3d4e7bf9..9b9f9402e 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -2454,10 +2454,10 @@ static const FunctionDef* GetSVCInfo64(u32 func_num) { return &SVC_Table_64[func_num]; } -MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70)); - void Call(Core::System& system, u32 immediate) { - MICROPROFILE_SCOPE(Kernel_SVC); + system.ExitDynarmicProfile(); + auto& kernel = system.Kernel(); + kernel.EnterSVCProfile(); auto* thread = system.CurrentScheduler().GetCurrentThread(); thread->SetContinuousOnSVC(true); @@ -2474,10 +2474,14 @@ void Call(Core::System& system, u32 immediate) { LOG_CRITICAL(Kernel_SVC, "Unknown SVC function 0x{:X}", immediate); } + kernel.ExitSVCProfile(); + if (!thread->IsContinuousOnSVC()) { auto* host_context = thread->GetHostContext().get(); host_context->Rewind(); } + + system.EnterDynarmicProfile(); } } // namespace Kernel::Svc diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 168828ab0..f42d7bd13 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -350,6 +350,22 @@ public: return (type & THREADTYPE_HLE) != 0; } + bool IsSuspendThread() const { + return (type & THREADTYPE_SUSPEND) != 0; + } + + bool IsIdleThread() const { + return (type & THREADTYPE_IDLE) != 0; + } + + bool WasRunning() const { + return was_running; + } + + void SetWasRunning(bool value) { + was_running = value; + } + std::shared_ptr GetHostContext() const; ThreadStatus GetStatus() const { @@ -684,6 +700,8 @@ private: bool will_be_terminated = false; + bool was_running = false; + std::string name; }; diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp index ab7b18abe..fa091f457 100644 --- a/src/yuzu/debugger/wait_tree.cpp +++ b/src/yuzu/debugger/wait_tree.cpp @@ -209,7 +209,11 @@ QString WaitTreeThread::GetText() const { break; case Kernel::ThreadStatus::Ready: if (!thread.IsPaused()) { - status = tr("ready"); + if (thread.WasRunning()) { + status = tr("running"); + } else { + status = tr("ready"); + } } else { status = tr("paused"); } @@ -261,7 +265,11 @@ QColor WaitTreeThread::GetColor() const { return QColor(Qt::GlobalColor::darkGreen); case Kernel::ThreadStatus::Ready: if (!thread.IsPaused()) { - return QColor(Qt::GlobalColor::darkBlue); + if (thread.WasRunning()) { + return QColor(Qt::GlobalColor::darkGreen); + } else { + return QColor(Qt::GlobalColor::darkBlue); + } } else { return QColor(Qt::GlobalColor::lightGray); } From db68fba4a634ab6127ed485f55c41b5e1a05bc10 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Thu, 12 Mar 2020 19:53:54 -0400 Subject: [PATCH 189/252] Scheduler: Correct yielding interaction with SetThreadActivity. --- src/core/hle/kernel/scheduler.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index 2ad380b17..8d56b49ce 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp @@ -144,6 +144,11 @@ u32 GlobalScheduler::SelectThreads() { bool GlobalScheduler::YieldThread(Thread* yielding_thread) { ASSERT(is_locked); // Note: caller should use critical section, etc. + if (!yielding_thread->IsRunnable()) { + // Normally this case shouldn't happen except for SetThreadActivity. + is_reselection_pending.store(true, std::memory_order_release); + return false; + } const u32 core_id = static_cast(yielding_thread->GetProcessorID()); const u32 priority = yielding_thread->GetPriority(); @@ -161,6 +166,11 @@ bool GlobalScheduler::YieldThreadAndBalanceLoad(Thread* yielding_thread) { ASSERT(is_locked); // Note: caller should check if !thread.IsSchedulerOperationRedundant and use critical section, // etc. + if (!yielding_thread->IsRunnable()) { + // Normally this case shouldn't happen except for SetThreadActivity. + is_reselection_pending.store(true, std::memory_order_release); + return false; + } const u32 core_id = static_cast(yielding_thread->GetProcessorID()); const u32 priority = yielding_thread->GetPriority(); @@ -212,6 +222,11 @@ bool GlobalScheduler::YieldThreadAndWaitForLoadBalancing(Thread* yielding_thread ASSERT(is_locked); // Note: caller should check if !thread.IsSchedulerOperationRedundant and use critical section, // etc. + if (!yielding_thread->IsRunnable()) { + // Normally this case shouldn't happen except for SetThreadActivity. + is_reselection_pending.store(true, std::memory_order_release); + return false; + } Thread* winner = nullptr; const u32 core_id = static_cast(yielding_thread->GetProcessorID()); From 1a5f2e290ba831ad6d18514bcdfcd92673643e4b Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Thu, 12 Mar 2020 19:55:53 -0400 Subject: [PATCH 190/252] CPU_Manager: Correct stopping on SingleCore. --- src/core/cpu_manager.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index 9e2e6d49f..e92b0fb37 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp @@ -39,9 +39,14 @@ void CpuManager::Initialize() { void CpuManager::Shutdown() { running_mode = false; Pause(false); - for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { - core_data[core].host_thread->join(); - core_data[core].host_thread.reset(); + if (is_multicore) { + for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { + core_data[core].host_thread->join(); + core_data[core].host_thread.reset(); + } + } else { + core_data[0].host_thread->join(); + core_data[0].host_thread.reset(); } } From 25565dffd588006aace7530486e71ff318dc5550 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sun, 15 Mar 2020 15:54:40 -0400 Subject: [PATCH 191/252] ARM: Addapt to new Exclusive Monitor Interface. --- src/core/arm/dynarmic/arm_dynarmic_64.cpp | 22 ++++++++++------------ src/core/arm/dynarmic/arm_dynarmic_64.h | 10 +++++----- src/core/arm/exclusive_monitor.h | 10 +++++----- src/core/hle/kernel/address_arbiter.cpp | 9 +++------ src/core/hle/kernel/svc.cpp | 4 +--- 5 files changed, 24 insertions(+), 31 deletions(-) diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 84b872f0b..2e664cfa8 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -180,8 +180,6 @@ std::shared_ptr ARM_Dynarmic_64::MakeJit(Common::PageTable& return std::make_shared(config); } - - void ARM_Dynarmic_64::Run() { jit->Run(); } @@ -297,24 +295,24 @@ DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(Memory::Memory& memory, std:: DynarmicExclusiveMonitor::~DynarmicExclusiveMonitor() = default; -void DynarmicExclusiveMonitor::SetExclusive8(std::size_t core_index, VAddr addr) { - monitor.Mark(core_index, addr, 1, [&]() -> u8 { return memory.Read8(addr); }); +u8 DynarmicExclusiveMonitor::ExclusiveRead8(std::size_t core_index, VAddr addr) { + return monitor.ReadAndMark(core_index, addr, [&]() -> u8 { return memory.Read8(addr); }); } -void DynarmicExclusiveMonitor::SetExclusive16(std::size_t core_index, VAddr addr) { - monitor.Mark(core_index, addr, 2, [&]() -> u16 { return memory.Read16(addr); }); +u16 DynarmicExclusiveMonitor::ExclusiveRead16(std::size_t core_index, VAddr addr) { + return monitor.ReadAndMark(core_index, addr, [&]() -> u16 { return memory.Read16(addr); }); } -void DynarmicExclusiveMonitor::SetExclusive32(std::size_t core_index, VAddr addr) { - monitor.Mark(core_index, addr, 4, [&]() -> u32 { return memory.Read32(addr); }); +u32 DynarmicExclusiveMonitor::ExclusiveRead32(std::size_t core_index, VAddr addr) { + return monitor.ReadAndMark(core_index, addr, [&]() -> u32 { return memory.Read32(addr); }); } -void DynarmicExclusiveMonitor::SetExclusive64(std::size_t core_index, VAddr addr) { - monitor.Mark(core_index, addr, 8, [&]() -> u64 { return memory.Read64(addr); }); +u64 DynarmicExclusiveMonitor::ExclusiveRead64(std::size_t core_index, VAddr addr) { + return monitor.ReadAndMark(core_index, addr, [&]() -> u64 { return memory.Read64(addr); }); } -void DynarmicExclusiveMonitor::SetExclusive128(std::size_t core_index, VAddr addr) { - monitor.Mark(core_index, addr, 16, [&]() -> u128 { +u128 DynarmicExclusiveMonitor::ExclusiveRead128(std::size_t core_index, VAddr addr) { + return monitor.ReadAndMark(core_index, addr, [&]() -> u128 { u128 result; result[0] = memory.Read64(addr); result[1] = memory.Read64(addr + 8); diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h index 3ead59f16..5560578ad 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.h +++ b/src/core/arm/dynarmic/arm_dynarmic_64.h @@ -82,11 +82,11 @@ public: explicit DynarmicExclusiveMonitor(Memory::Memory& memory, std::size_t core_count); ~DynarmicExclusiveMonitor() override; - void SetExclusive8(std::size_t core_index, VAddr addr) override; - void SetExclusive16(std::size_t core_index, VAddr addr) override; - void SetExclusive32(std::size_t core_index, VAddr addr) override; - void SetExclusive64(std::size_t core_index, VAddr addr) override; - void SetExclusive128(std::size_t core_index, VAddr addr) override; + u8 ExclusiveRead8(std::size_t core_index, VAddr addr) override; + u16 ExclusiveRead16(std::size_t core_index, VAddr addr) override; + u32 ExclusiveRead32(std::size_t core_index, VAddr addr) override; + u64 ExclusiveRead64(std::size_t core_index, VAddr addr) override; + u128 ExclusiveRead128(std::size_t core_index, VAddr addr) override; void ClearExclusive() override; bool ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) override; diff --git a/src/core/arm/exclusive_monitor.h b/src/core/arm/exclusive_monitor.h index 2ee312eee..62f6e6023 100644 --- a/src/core/arm/exclusive_monitor.h +++ b/src/core/arm/exclusive_monitor.h @@ -18,11 +18,11 @@ class ExclusiveMonitor { public: virtual ~ExclusiveMonitor(); - virtual void SetExclusive8(std::size_t core_index, VAddr addr) = 0; - virtual void SetExclusive16(std::size_t core_index, VAddr addr) = 0; - virtual void SetExclusive32(std::size_t core_index, VAddr addr) = 0; - virtual void SetExclusive64(std::size_t core_index, VAddr addr) = 0; - virtual void SetExclusive128(std::size_t core_index, VAddr addr) = 0; + virtual u8 ExclusiveRead8(std::size_t core_index, VAddr addr) = 0; + virtual u16 ExclusiveRead16(std::size_t core_index, VAddr addr) = 0; + virtual u32 ExclusiveRead32(std::size_t core_index, VAddr addr) = 0; + virtual u64 ExclusiveRead64(std::size_t core_index, VAddr addr) = 0; + virtual u128 ExclusiveRead128(std::size_t core_index, VAddr addr) = 0; virtual void ClearExclusive() = 0; virtual bool ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) = 0; diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index e8f22b598..4d2a9b35d 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp @@ -80,8 +80,7 @@ ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32 auto& monitor = system.Monitor(); u32 current_value; do { - monitor.SetExclusive32(current_core, address); - current_value = memory.Read32(address); + current_value = monitor.ExclusiveRead32(current_core, address); if (current_value != value) { return ERR_INVALID_STATE; @@ -110,8 +109,7 @@ ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr a auto& monitor = system.Monitor(); s32 updated_value; do { - monitor.SetExclusive32(current_core, address); - updated_value = memory.Read32(address); + updated_value = monitor.ExclusiveRead32(current_core, address); if (updated_value != value) { return ERR_INVALID_STATE; @@ -186,8 +184,7 @@ ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s6 const std::size_t current_core = system.CurrentCoreIndex(); auto& monitor = system.Monitor(); do { - monitor.SetExclusive32(current_core, address); - current_value = static_cast(memory.Read32(address)); + current_value = static_cast(monitor.ExclusiveRead32(current_core, address)); if (should_decrement) { decrement_value = current_value - 1; } else { diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 9b9f9402e..36e9c48f9 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -1653,10 +1653,8 @@ static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_ u32 update_val = 0; const VAddr mutex_address = thread->GetMutexWaitAddress(); do { - monitor.SetExclusive32(current_core, mutex_address); - // If the mutex is not yet acquired, acquire it. - mutex_val = memory.Read32(mutex_address); + mutex_val = monitor.ExclusiveRead32(current_core, mutex_address); if (mutex_val != 0) { update_val = mutex_val | Mutex::MutexHasWaitersFlag; From 5d3a2be04f265c2d6a8687431593029f7329060f Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sun, 15 Mar 2020 21:34:22 -0400 Subject: [PATCH 192/252] GUI: Make multicore only work with Async and add GUI for multicore. --- src/core/core.cpp | 11 ++++++-- src/core/cpu_manager.cpp | 11 ++++++++ src/core/cpu_manager.h | 14 ++++++++++ src/yuzu/configuration/configure_general.ui | 2 +- src/yuzu/main.cpp | 29 +++++++++++++++++++-- src/yuzu/main.h | 1 + 6 files changed, 63 insertions(+), 5 deletions(-) diff --git a/src/core/core.cpp b/src/core/core.cpp index 032da7aa5..0f0eb885a 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -152,8 +152,12 @@ struct System::Impl { device_memory = std::make_unique(system); - kernel.SetMulticore(Settings::values.use_multi_core); - cpu_manager.SetMulticore(Settings::values.use_multi_core); + is_multicore = Settings::values.use_multi_core; + is_async_gpu = is_multicore || Settings::values.use_asynchronous_gpu_emulation; + + kernel.SetMulticore(is_multicore); + cpu_manager.SetMulticore(is_multicore); + cpu_manager.SetAsyncGpu(is_async_gpu); core_timing.Initialize([&system]() { system.RegisterHostThread(); }); kernel.Initialize(); @@ -395,6 +399,9 @@ struct System::Impl { std::unique_ptr perf_stats; Core::FrameLimiter frame_limiter; + bool is_multicore{}; + bool is_async_gpu{}; + std::array dynarmic_ticks{}; }; diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index e92b0fb37..d7bd162bc 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp @@ -9,6 +9,7 @@ #include "core/core.h" #include "core/core_timing.h" #include "core/cpu_manager.h" +#include "core/frontend/emu_window.h" #include "core/gdbstub/gdbstub.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/physical_core.h" @@ -21,7 +22,17 @@ CpuManager::CpuManager(System& system) : system{system} {} CpuManager::~CpuManager() = default; void CpuManager::ThreadStart(CpuManager& cpu_manager, std::size_t core) { + if (!cpu_manager.is_async_gpu && !cpu_manager.is_multicore) { + cpu_manager.render_window->MakeCurrent(); + } cpu_manager.RunThread(core); + if (!cpu_manager.is_async_gpu && !cpu_manager.is_multicore) { + cpu_manager.render_window->DoneCurrent(); + } +} + +void CpuManager::SetRenderWindow(Core::Frontend::EmuWindow& render_window) { + this->render_window = &render_window; } void CpuManager::Initialize() { diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h index c0e454a7d..37cef2b12 100644 --- a/src/core/cpu_manager.h +++ b/src/core/cpu_manager.h @@ -16,6 +16,10 @@ class Event; class Fiber; } // namespace Common +namespace Core::Frontend { +class EmuWindow; +} // namespace Core::Frontend + namespace Core { class System; @@ -35,6 +39,12 @@ public: void SetMulticore(bool is_multicore) { this->is_multicore = is_multicore; } + + /// Sets if emulation is using an asynchronous GPU. + void SetAsyncGpu(bool is_async_gpu) { + this->is_async_gpu = is_async_gpu; + } + void Initialize(); void Shutdown(); @@ -51,6 +61,8 @@ public: return current_core.load(); } + void SetRenderWindow(Core::Frontend::EmuWindow& render_window); + private: static void GuestThreadFunction(void* cpu_manager); static void GuestRewindFunction(void* cpu_manager); @@ -88,10 +100,12 @@ private: std::array core_data{}; + bool is_async_gpu{}; bool is_multicore{}; std::atomic current_core{}; std::size_t preemption_count{}; static constexpr std::size_t max_cycle_runs = 5; + Core::Frontend::EmuWindow* render_window; System& system; }; diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui index f872bddec..2711116a2 100644 --- a/src/yuzu/configuration/configure_general.ui +++ b/src/yuzu/configuration/configure_general.ui @@ -54,7 +54,7 @@ - Emulate CPU in Multiple Cores + Multicore CPU Emulation diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index de0c7fe8c..f1d9ec326 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -534,14 +534,34 @@ void GMainWindow::InitializeWidgets() { if (emulation_running) { return; } - Settings::values.use_asynchronous_gpu_emulation = - !Settings::values.use_asynchronous_gpu_emulation; + bool is_async = !Settings::values.use_asynchronous_gpu_emulation || Settings::values.use_multi_core; + Settings::values.use_asynchronous_gpu_emulation = is_async; async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation); Settings::Apply(); }); async_status_button->setText(tr("ASYNC")); async_status_button->setCheckable(true); async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation); + + // Setup Multicore button + multicore_status_button = new QPushButton(); + multicore_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton")); + multicore_status_button->setFocusPolicy(Qt::NoFocus); + connect(multicore_status_button, &QPushButton::clicked, [&] { + if (emulation_running) { + return; + } + Settings::values.use_multi_core = !Settings::values.use_multi_core; + bool is_async = Settings::values.use_asynchronous_gpu_emulation || Settings::values.use_multi_core; + Settings::values.use_asynchronous_gpu_emulation = is_async; + async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation); + multicore_status_button->setChecked(Settings::values.use_multi_core); + Settings::Apply(); + }); + multicore_status_button->setText(tr("MULTICORE")); + multicore_status_button->setCheckable(true); + multicore_status_button->setChecked(Settings::values.use_multi_core); + statusBar()->insertPermanentWidget(0, multicore_status_button); statusBar()->insertPermanentWidget(0, async_status_button); // Setup Renderer API button @@ -1043,6 +1063,7 @@ void GMainWindow::BootGame(const QString& filename) { } status_bar_update_timer.start(2000); async_status_button->setDisabled(true); + multicore_status_button->setDisabled(true); renderer_status_button->setDisabled(true); if (UISettings::values.hide_mouse) { @@ -1130,6 +1151,7 @@ void GMainWindow::ShutdownGame() { game_fps_label->setVisible(false); emu_frametime_label->setVisible(false); async_status_button->setEnabled(true); + multicore_status_button->setEnabled(true); #ifdef HAS_VULKAN renderer_status_button->setEnabled(true); #endif @@ -1935,7 +1957,10 @@ void GMainWindow::OnConfigure() { } dock_status_button->setChecked(Settings::values.use_docked_mode); + multicore_status_button->setChecked(Settings::values.use_multi_core); + Settings::values.use_asynchronous_gpu_emulation = Settings::values.use_asynchronous_gpu_emulation || Settings::values.use_multi_core; async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation); + #ifdef HAS_VULKAN renderer_status_button->setChecked(Settings::values.renderer_backend == Settings::RendererBackend::Vulkan); diff --git a/src/yuzu/main.h b/src/yuzu/main.h index d55e55cc6..5581874ed 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -235,6 +235,7 @@ private: QLabel* game_fps_label = nullptr; QLabel* emu_frametime_label = nullptr; QPushButton* async_status_button = nullptr; + QPushButton* multicore_status_button = nullptr; QPushButton* renderer_status_button = nullptr; QPushButton* dock_status_button = nullptr; QTimer status_bar_update_timer; From f2ade343e2492c213ac93680a55e9bed712dac9a Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Thu, 19 Mar 2020 13:09:32 -0400 Subject: [PATCH 193/252] SingleCore: Move Host Timing from a sepparate thread to main cpu thread. --- src/core/core.cpp | 2 ++ src/core/core_timing.cpp | 20 +++++++++++++------- src/core/core_timing.h | 7 +++++++ src/core/cpu_manager.cpp | 17 +++++++++++++++-- src/core/cpu_manager.h | 1 + src/core/hle/kernel/kernel.cpp | 2 +- src/core/hle/kernel/thread.h | 9 +++++++++ 7 files changed, 48 insertions(+), 10 deletions(-) diff --git a/src/core/core.cpp b/src/core/core.cpp index 0f0eb885a..2ca9c0be5 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -158,6 +158,8 @@ struct System::Impl { kernel.SetMulticore(is_multicore); cpu_manager.SetMulticore(is_multicore); cpu_manager.SetAsyncGpu(is_async_gpu); + core_timing.SetMulticore(is_multicore); + cpu_manager.SetRenderWindow(emu_window); core_timing.Initialize([&system]() { system.RegisterHostThread(); }); kernel.Initialize(); diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 3438f79ce..189d4aa34 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -55,7 +55,9 @@ void CoreTiming::Initialize(std::function&& on_thread_init_) { event_fifo_id = 0; const auto empty_timed_callback = [](u64, s64) {}; ev_lost = CreateEvent("_lost_event", empty_timed_callback); - timer_thread = std::make_unique(ThreadEntry, std::ref(*this)); + if (is_multicore) { + timer_thread = std::make_unique(ThreadEntry, std::ref(*this)); + } } void CoreTiming::Shutdown() { @@ -63,7 +65,9 @@ void CoreTiming::Shutdown() { shutting_down = true; pause_event.Set(); event.Set(); - timer_thread->join(); + if (timer_thread) { + timer_thread->join(); + } ClearPendingEvents(); timer_thread.reset(); has_started = false; @@ -78,12 +82,14 @@ void CoreTiming::SyncPause(bool is_paused) { return; } Pause(is_paused); - if (!is_paused) { - pause_event.Set(); + if (timer_thread) { + if (!is_paused) { + pause_event.Set(); + } + event.Set(); + while (paused_set != is_paused) + ; } - event.Set(); - while (paused_set != is_paused) - ; } bool CoreTiming::IsRunning() const { diff --git a/src/core/core_timing.h b/src/core/core_timing.h index 032eb08aa..03f9a5c76 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h @@ -67,6 +67,11 @@ public: /// Tears down all timing related functionality. void Shutdown(); + /// Sets if emulation is multicore or single core, must be set before Initialize + void SetMulticore(bool is_multicore) { + this->is_multicore = is_multicore; + } + /// Pauses/Unpauses the execution of the timer thread. void Pause(bool is_paused); @@ -147,6 +152,8 @@ private: std::atomic has_started{}; std::function on_thread_init{}; + bool is_multicore{}; + std::array, Core::Hardware::NUM_CPU_CORES> ticks_count{}; }; diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index d7bd162bc..2aea95a25 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp @@ -242,8 +242,11 @@ void CpuManager::SingleCoreRunGuestLoop() { break; } } - physical_core.ClearExclusive(); system.ExitDynarmicProfile(); + thread->SetPhantomMode(true); + system.CoreTiming().Advance(); + thread->SetPhantomMode(false); + physical_core.ClearExclusive(); PreemptSingleCore(); auto& scheduler = kernel.Scheduler(current_core); scheduler.TryDoContextSwitch(); @@ -255,6 +258,7 @@ void CpuManager::SingleCoreRunIdleThread() { while (true) { auto& physical_core = kernel.CurrentPhysicalCore(); PreemptSingleCore(); + idle_count++; auto& scheduler = physical_core.Scheduler(); scheduler.TryDoContextSwitch(); } @@ -280,15 +284,24 @@ void CpuManager::SingleCoreRunSuspendThread() { void CpuManager::PreemptSingleCore() { preemption_count = 0; std::size_t old_core = current_core; - current_core.store((current_core + 1) % Core::Hardware::NUM_CPU_CORES); auto& scheduler = system.Kernel().Scheduler(old_core); Kernel::Thread* current_thread = scheduler.GetCurrentThread(); + if (idle_count >= 4) { + current_thread->SetPhantomMode(true); + system.CoreTiming().Advance(); + current_thread->SetPhantomMode(false); + } + current_core.store((current_core + 1) % Core::Hardware::NUM_CPU_CORES); scheduler.Unload(); auto& next_scheduler = system.Kernel().Scheduler(current_core); Common::Fiber::YieldTo(current_thread->GetHostContext(), next_scheduler.ControlContext()); /// May have changed scheduler auto& current_scheduler = system.Kernel().Scheduler(current_core); current_scheduler.Reload(); + auto* currrent_thread2 = current_scheduler.GetCurrentThread(); + if (!currrent_thread2->IsIdleThread()) { + idle_count = 0; + } } void CpuManager::SingleCorePause(bool paused) { diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h index 37cef2b12..e6b8612f0 100644 --- a/src/core/cpu_manager.h +++ b/src/core/cpu_manager.h @@ -104,6 +104,7 @@ private: bool is_multicore{}; std::atomic current_core{}; std::size_t preemption_count{}; + std::size_t idle_count{}; static constexpr std::size_t max_cycle_runs = 5; Core::Frontend::EmuWindow* render_window; diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index d2f5f9bf2..a19cd7a1f 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -303,7 +303,7 @@ struct KernelCore::Impl { } const Kernel::Scheduler& sched = cores[result.host_handle].Scheduler(); const Kernel::Thread* current = sched.GetCurrentThread(); - if (current != nullptr) { + if (current != nullptr && !current->IsPhantomMode()) { result.guest_handle = current->GetGlobalHandle(); } else { result.guest_handle = InvalidHandle; diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index f42d7bd13..f998890c4 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -597,6 +597,14 @@ public: is_continuous_on_svc = is_continuous; } + bool IsPhantomMode() const { + return is_phantom_mode; + } + + void SetPhantomMode(bool phantom) { + is_phantom_mode = phantom; + } + private: friend class GlobalScheduler; friend class Scheduler; @@ -699,6 +707,7 @@ private: bool is_continuous_on_svc = false; bool will_be_terminated = false; + bool is_phantom_mode = false; bool was_running = false; From 87c49aa7be4b7277f8ae929058633827d339a052 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Fri, 20 Mar 2020 12:36:01 -0400 Subject: [PATCH 194/252] SVC/ARM: Correct svcSendSyncRequest and cache ticks on arm interface. --- src/core/arm/dynarmic/arm_dynarmic_32.cpp | 11 +++++++++-- src/core/arm/dynarmic/arm_dynarmic_64.cpp | 12 ++++++++++-- src/core/hle/kernel/svc.cpp | 2 +- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index 618f02f30..c8a1ce6e7 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -72,17 +72,23 @@ public: } void AddTicks(u64 ticks) override { - /// We are using host timing, NOP + this->ticks -= ticks; } + u64 GetTicksRemaining() override { if (!parent.interrupt_handler.IsInterrupted()) { - return 1000ULL; + return std::max(ticks, 0); } return 0ULL; } + void ResetTicks() { + ticks = 1000LL; + } + ARM_Dynarmic_32& parent; std::size_t num_interpreted_instructions{}; + s64 ticks{}; }; std::shared_ptr ARM_Dynarmic_32::MakeJit(Common::PageTable& page_table, @@ -97,6 +103,7 @@ std::shared_ptr ARM_Dynarmic_32::MakeJit(Common::PageTable& } void ARM_Dynarmic_32::Run() { + cb->ResetTicks(); jit->Run(); } diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 2e664cfa8..547a6e07e 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -124,22 +124,29 @@ public: } void AddTicks(u64 ticks) override { - /// We are using host timing, NOP + this->ticks -= ticks; } + u64 GetTicksRemaining() override { if (!parent.interrupt_handler.IsInterrupted()) { - return 1000ULL; + return std::max(ticks, 0); } return 0ULL; } + u64 GetCNTPCT() override { return parent.system.CoreTiming().GetClockTicks(); } + void ResetTicks() { + ticks = 1000LL; + } + ARM_Dynarmic_64& parent; std::size_t num_interpreted_instructions = 0; u64 tpidrro_el0 = 0; u64 tpidr_el0 = 0; + s64 ticks{}; }; std::shared_ptr ARM_Dynarmic_64::MakeJit(Common::PageTable& page_table, @@ -181,6 +188,7 @@ std::shared_ptr ARM_Dynarmic_64::MakeJit(Common::PageTable& } void ARM_Dynarmic_64::Run() { + cb->ResetTicks(); jit->Run(); } diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 36e9c48f9..f08745226 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -342,7 +342,7 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) { thread->InvokeHLECallback(SharedFrom(thread)); } - return RESULT_SUCCESS; + return thread->GetSignalingResult(); } static ResultCode SendSyncRequest32(Core::System& system, Handle handle) { From 7b18174eef70feb434f9319a57a6cfbe362730e3 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Fri, 20 Mar 2020 14:05:47 -0400 Subject: [PATCH 195/252] ARM/WaitTree: Better track the CallStack for each thread. --- src/core/arm/arm_interface.cpp | 57 +++++++++++++++++++++++++++++++++ src/core/arm/arm_interface.h | 3 ++ src/yuzu/debugger/wait_tree.cpp | 25 ++++++++------- 3 files changed, 74 insertions(+), 11 deletions(-) diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp index d079a1bc8..d2295ed90 100644 --- a/src/core/arm/arm_interface.cpp +++ b/src/core/arm/arm_interface.cpp @@ -139,6 +139,63 @@ std::optional GetSymbolName(const Symbols& symbols, VAddr func_addr constexpr u64 SEGMENT_BASE = 0x7100000000ull; +std::vector ARM_Interface::GetBacktraceFromContext( + System& system, const ThreadContext64& ctx) { + std::vector out; + auto& memory = system.Memory(); + + auto fp = ctx.cpu_registers[29]; + auto lr = ctx.cpu_registers[30]; + while (true) { + out.push_back({"", 0, lr, 0}); + if (!fp) { + break; + } + lr = memory.Read64(fp + 8) - 4; + fp = memory.Read64(fp); + } + + std::map modules; + auto& loader{system.GetAppLoader()}; + if (loader.ReadNSOModules(modules) != Loader::ResultStatus::Success) { + return {}; + } + + std::map symbols; + for (const auto& module : modules) { + symbols.insert_or_assign(module.second, GetSymbols(module.first, memory)); + } + + for (auto& entry : out) { + VAddr base = 0; + for (auto iter = modules.rbegin(); iter != modules.rend(); ++iter) { + const auto& module{*iter}; + if (entry.original_address >= module.first) { + entry.module = module.second; + base = module.first; + break; + } + } + + entry.offset = entry.original_address - base; + entry.address = SEGMENT_BASE + entry.offset; + + if (entry.module.empty()) + entry.module = "unknown"; + + const auto symbol_set = symbols.find(entry.module); + if (symbol_set != symbols.end()) { + const auto symbol = GetSymbolName(symbol_set->second, entry.offset); + if (symbol.has_value()) { + // TODO(DarkLordZach): Add demangling of symbol names. + entry.name = *symbol; + } + } + } + + return out; +} + std::vector ARM_Interface::GetBacktrace() const { std::vector out; auto& memory = system.Memory(); diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index 87a1c29cc..e701ddf21 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h @@ -164,6 +164,9 @@ public: std::string name; }; + static std::vector GetBacktraceFromContext(System& system, + const ThreadContext64& ctx); + std::vector GetBacktrace() const; /// fp (= r29) points to the last frame record. diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp index fa091f457..d2dbb259c 100644 --- a/src/yuzu/debugger/wait_tree.cpp +++ b/src/yuzu/debugger/wait_tree.cpp @@ -2,10 +2,13 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include + #include "yuzu/debugger/wait_tree.h" #include "yuzu/util/util.h" #include "common/assert.h" +#include "core/arm/arm_interface.h" #include "core/core.h" #include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/mutex.h" @@ -116,20 +119,20 @@ QString WaitTreeCallstack::GetText() const { std::vector> WaitTreeCallstack::GetChildren() const { std::vector> list; - constexpr std::size_t BaseRegister = 29; - auto& memory = Core::System::GetInstance().Memory(); - u64 base_pointer = thread.GetContext64().cpu_registers[BaseRegister]; + if (thread.IsHLEThread()) { + return list; + } - while (base_pointer != 0) { - const u64 lr = memory.Read64(base_pointer + sizeof(u64)); - if (lr == 0) { - break; - } + if (thread.GetOwnerProcess() == nullptr || !thread.GetOwnerProcess()->Is64BitProcess()) { + return list; + } - list.push_back(std::make_unique( - tr("0x%1").arg(lr - sizeof(u32), 16, 16, QLatin1Char{'0'}))); + auto backtrace = Core::ARM_Interface::GetBacktraceFromContext(Core::System::GetInstance(), thread.GetContext64()); - base_pointer = memory.Read64(base_pointer); + for (auto& entry : backtrace) { + std::string s = fmt::format("{:20}{:016X} {:016X} {:016X} {}", entry.module, entry.address, + entry.original_address, entry.offset, entry.name); + list.push_back(std::make_unique(QString::fromStdString(s))); } return list; From 534466754f381e90f5f6475a0c02031242a5c256 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sat, 21 Mar 2020 12:23:13 -0400 Subject: [PATCH 196/252] X64 Clock: Reduce accuracy to be less or equal to guest accuracy. --- src/common/x64/native_clock.cpp | 3 ++- src/common/x64/native_clock.h | 5 +++++ src/core/arm/dynarmic/arm_dynarmic_64.cpp | 3 +++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp index 926f92ff8..f1bc60fd2 100644 --- a/src/common/x64/native_clock.cpp +++ b/src/common/x64/native_clock.cpp @@ -62,7 +62,8 @@ u64 NativeClock::GetRTSC() { } accumulated_ticks += diff; rtsc_serialize.unlock(); - return accumulated_ticks; + /// The clock cannot be more precise than the guest timer, remove the lower bits + return accumulated_ticks & inaccuracy_mask; } void NativeClock::Pause(bool is_paused) { diff --git a/src/common/x64/native_clock.h b/src/common/x64/native_clock.h index 3851f8fc2..e853094d2 100644 --- a/src/common/x64/native_clock.h +++ b/src/common/x64/native_clock.h @@ -31,6 +31,11 @@ public: private: u64 GetRTSC(); + /// value used to reduce the native clocks accuracy as some apss rely on + /// undefined behavior where the level of accuracy in the clock shouldn't + /// be higher. + static constexpr u64 inaccuracy_mask = ~(0x100 - 1); + SpinLock rtsc_serialize{}; u64 last_measure{}; u64 accumulated_ticks{}; diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 547a6e07e..3f18dede2 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -184,6 +184,9 @@ std::shared_ptr ARM_Dynarmic_64::MakeJit(Common::PageTable& config.enable_fast_dispatch = false; } + // CNTPCT uses wall clock. + config.wall_clock_cntpct = true; + return std::make_shared(config); } From 1b82ccec2220a69711ba75cf51ee98cbcfe6a510 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sat, 29 Feb 2020 13:58:50 -0400 Subject: [PATCH 197/252] Core: Refactor ARM Interface. --- src/core/arm/arm_interface.h | 9 +++++--- src/core/arm/dynarmic/arm_dynarmic_32.cpp | 6 ++--- src/core/arm/dynarmic/arm_dynarmic_32.h | 2 +- src/core/arm/dynarmic/arm_dynarmic_64.cpp | 13 +++++++---- src/core/arm/dynarmic/arm_dynarmic_64.h | 2 +- src/core/arm/unicorn/arm_unicorn.cpp | 7 +++--- src/core/arm/unicorn/arm_unicorn.h | 5 ++-- src/core/hle/kernel/kernel.cpp | 28 ++++++++++++++++++++++- src/core/hle/kernel/physical_core.cpp | 25 +++++++------------- src/core/hle/kernel/physical_core.h | 14 +++++++----- 10 files changed, 69 insertions(+), 42 deletions(-) diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index e701ddf21..dc9b2ff7b 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h @@ -7,6 +7,7 @@ #include #include #include "common/common_types.h" +#include "core/hardware_properties.h" namespace Common { struct PageTable; @@ -20,11 +21,13 @@ namespace Core { class System; class CPUInterruptHandler; +using CPUInterrupts = std::array; + /// Generic ARMv8 CPU interface class ARM_Interface : NonCopyable { public: - explicit ARM_Interface(System& system_, CPUInterruptHandler& interrupt_handler) - : system{system_}, interrupt_handler{interrupt_handler} {} + explicit ARM_Interface(System& system_, CPUInterrupts& interrupt_handlers) + : system{system_}, interrupt_handlers{interrupt_handlers} {} virtual ~ARM_Interface() = default; struct ThreadContext32 { @@ -180,7 +183,7 @@ public: protected: /// System context that this ARM interface is running under. System& system; - CPUInterruptHandler& interrupt_handler; + CPUInterrupts& interrupt_handlers; }; } // namespace Core diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index c8a1ce6e7..f36c5f401 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -76,7 +76,7 @@ public: } u64 GetTicksRemaining() override { - if (!parent.interrupt_handler.IsInterrupted()) { + if (!parent.interrupt_handlers[parent.core_index].IsInterrupted()) { return std::max(ticks, 0); } return 0ULL; @@ -111,9 +111,9 @@ void ARM_Dynarmic_32::Step() { jit->Step(); } -ARM_Dynarmic_32::ARM_Dynarmic_32(System& system, CPUInterruptHandler& interrupt_handler, +ARM_Dynarmic_32::ARM_Dynarmic_32(System& system, CPUInterrupts& interrupt_handlers, ExclusiveMonitor& exclusive_monitor, std::size_t core_index) - : ARM_Interface{system, interrupt_handler}, cb(std::make_unique(*this)), + : ARM_Interface{system, interrupt_handlers}, cb(std::make_unique(*this)), cp15(std::make_shared(*this)), core_index{core_index}, exclusive_monitor{dynamic_cast(exclusive_monitor)} {} diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h index 1e7e17e64..2dd9a4df0 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.h +++ b/src/core/arm/dynarmic/arm_dynarmic_32.h @@ -29,7 +29,7 @@ class System; class ARM_Dynarmic_32 final : public ARM_Interface { public: - ARM_Dynarmic_32(System& system, CPUInterruptHandler& interrupt_handler, + ARM_Dynarmic_32(System& system, CPUInterrupts& interrupt_handlers, ExclusiveMonitor& exclusive_monitor, std::size_t core_index); ~ARM_Dynarmic_32() override; diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 3f18dede2..8401ba631 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -128,7 +128,7 @@ public: } u64 GetTicksRemaining() override { - if (!parent.interrupt_handler.IsInterrupted()) { + if (!parent.interrupt_handlers[parent.core_index].IsInterrupted()) { return std::max(ticks, 0); } return 0ULL; @@ -199,11 +199,14 @@ void ARM_Dynarmic_64::Step() { cb->InterpreterFallback(jit->GetPC(), 1); } -ARM_Dynarmic_64::ARM_Dynarmic_64(System& system, CPUInterruptHandler& interrupt_handler, +ARM_Dynarmic_64::ARM_Dynarmic_64(System& system, CPUInterrupts& interrupt_handlers, ExclusiveMonitor& exclusive_monitor, std::size_t core_index) - : ARM_Interface{system, interrupt_handler}, cb(std::make_unique(*this)), - inner_unicorn{system, interrupt_handler, ARM_Unicorn::Arch::AArch64}, core_index{core_index}, - exclusive_monitor{dynamic_cast(exclusive_monitor)} {} + : ARM_Interface{system, interrupt_handler}, + cb(std::make_unique(*this)), inner_unicorn{system, interrupt_handler, + ARM_Unicorn::Arch::AArch64, + core_index}, + core_index{core_index}, exclusive_monitor{ + dynamic_cast(exclusive_monitor)} {} ARM_Dynarmic_64::~ARM_Dynarmic_64() = default; diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h index 5560578ad..1c6791d4e 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.h +++ b/src/core/arm/dynarmic/arm_dynarmic_64.h @@ -28,7 +28,7 @@ class System; class ARM_Dynarmic_64 final : public ARM_Interface { public: - ARM_Dynarmic_64(System& system, CPUInterruptHandler& interrupt_handler, + ARM_Dynarmic_64(System& system, CPUInterrupts& interrupt_handlers, ExclusiveMonitor& exclusive_monitor, std::size_t core_index); ~ARM_Dynarmic_64() override; diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp index 0393fe641..d81d1b5b0 100644 --- a/src/core/arm/unicorn/arm_unicorn.cpp +++ b/src/core/arm/unicorn/arm_unicorn.cpp @@ -63,8 +63,9 @@ static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int si return false; } -ARM_Unicorn::ARM_Unicorn(System& system, CPUInterruptHandler& interrupt_handler, Arch architecture) - : ARM_Interface{system, interrupt_handler} { +ARM_Unicorn::ARM_Unicorn(System& system, CPUInterruptHandler& interrupt_handler, Arch architecture, + std::size_t core_index) + : ARM_Interface{system, interrupt_handler}, core_index{core_index} { const auto arch = architecture == Arch::AArch32 ? UC_ARCH_ARM : UC_ARCH_ARM64; CHECKED(uc_open(arch, UC_MODE_ARM, &uc)); @@ -163,7 +164,7 @@ void ARM_Unicorn::Run() { ExecuteInstructions(std::max(4000000U, 0U)); } else { while (true) { - if (interrupt_handler.IsInterrupted()) { + if (interrupt_handlers[core_index].IsInterrupted()) { return; } ExecuteInstructions(10); diff --git a/src/core/arm/unicorn/arm_unicorn.h b/src/core/arm/unicorn/arm_unicorn.h index 0a4c087cd..e3da368de 100644 --- a/src/core/arm/unicorn/arm_unicorn.h +++ b/src/core/arm/unicorn/arm_unicorn.h @@ -11,7 +11,6 @@ namespace Core { -class CPUInterruptHandler; class System; class ARM_Unicorn final : public ARM_Interface { @@ -21,7 +20,8 @@ public: AArch64, // 64-bit ARM }; - explicit ARM_Unicorn(System& system, CPUInterruptHandler& interrupt_handler, Arch architecture); + explicit ARM_Unicorn(System& system, CPUInterruptHandler& interrupt_handler, Arch architecture, + std::size_t core_index); ~ARM_Unicorn() override; void SetPC(u64 pc) override; @@ -56,6 +56,7 @@ private: uc_engine* uc{}; GDBStub::BreakpointAddress last_bkpt{}; bool last_bkpt_hit = false; + std::size_t core_index; }; } // namespace Core diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index a19cd7a1f..e33ef5323 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include #include #include #include @@ -16,7 +17,9 @@ #include "common/microprofile.h" #include "common/thread.h" #include "core/arm/arm_interface.h" +#include "core/arm/cpu_interrupt_handler.h" #include "core/arm/exclusive_monitor.h" +#include "core/arm/unicorn/arm_unicorn.h" #include "core/core.h" #include "core/core_timing.h" #include "core/core_timing_util.h" @@ -42,6 +45,11 @@ #include "core/hle/result.h" #include "core/memory.h" +#ifdef ARCHITECTURE_x86_64 +#include "core/arm/dynarmic/arm_dynarmic_32.h" +#include "core/arm/dynarmic/arm_dynarmic_64.h" +#endif + MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70)); namespace Kernel { @@ -178,7 +186,20 @@ struct KernelCore::Impl { exclusive_monitor = Core::MakeExclusiveMonitor(system.Memory(), Core::Hardware::NUM_CPU_CORES); for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { - cores.emplace_back(system, i, *exclusive_monitor); +#ifdef ARCHITECTURE_x86_64 + arm_interfaces_32[i] = + std::make_unique(system, interrupts, *exclusive_monitor, i); + arm_interfaces_64[i] = + std::make_unique(system, interrupts, *exclusive_monitor, i); +#else + arm_interfaces_32[i] = std::make_shared( + system, interrupts, ARM_Unicorn::Arch::AArch32, i); + arm_interfaces_64[i] = std::make_shared( + system, interrupts, ARM_Unicorn::Arch::AArch64, i); + LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); +#endif + cores.emplace_back(system, i, *exclusive_monitor, interrupts[i], *arm_interfaces_32[i], + *arm_interfaces_64[i]); } } @@ -407,6 +428,11 @@ struct KernelCore::Impl { std::shared_ptr time_shared_mem; std::array, Core::Hardware::NUM_CPU_CORES> suspend_threads{}; + std::array interrupts{}; + std::array, Core::Hardware::NUM_CPU_CORES> + arm_interfaces_32{}; + std::array, Core::Hardware::NUM_CPU_CORES> + arm_interfaces_64{}; bool is_multicore{}; std::thread::id single_core_thread_id{}; diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp index ff14fcb42..9146b331d 100644 --- a/src/core/hle/kernel/physical_core.cpp +++ b/src/core/hle/kernel/physical_core.cpp @@ -21,21 +21,12 @@ namespace Kernel { PhysicalCore::PhysicalCore(Core::System& system, std::size_t id, - Core::ExclusiveMonitor& exclusive_monitor) - : interrupt_handler{}, core_index{id} { -#ifdef ARCHITECTURE_x86_64 - arm_interface_32 = std::make_unique(system, interrupt_handler, - exclusive_monitor, core_index); - arm_interface_64 = std::make_unique(system, interrupt_handler, - exclusive_monitor, core_index); -#else - using Core::ARM_Unicorn; - arm_interface_32 = - std::make_unique(system, interrupt_handler, ARM_Unicorn::Arch::AArch32); - arm_interface_64 = - std::make_unique(system, interrupt_handler, ARM_Unicorn::Arch::AArch64); - LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); -#endif + Core::ExclusiveMonitor& exclusive_monitor, + Core::CPUInterruptHandler& interrupt_handler, + Core::ARM_Interface& arm_interface32, + Core::ARM_Interface& arm_interface64) + : interrupt_handler{interrupt_handler}, core_index{id}, arm_interface_32{arm_interface32}, + arm_interface_64{arm_interface64} { scheduler = std::make_unique(system, core_index); guard = std::make_unique(); @@ -69,9 +60,9 @@ void PhysicalCore::Shutdown() { void PhysicalCore::SetIs64Bit(bool is_64_bit) { if (is_64_bit) { - arm_interface = arm_interface_64.get(); + arm_interface = &arm_interface_64; } else { - arm_interface = arm_interface_32.get(); + arm_interface = &arm_interface_32; } } diff --git a/src/core/hle/kernel/physical_core.h b/src/core/hle/kernel/physical_core.h index cd2e42fc3..2673d90f2 100644 --- a/src/core/hle/kernel/physical_core.h +++ b/src/core/hle/kernel/physical_core.h @@ -10,7 +10,7 @@ #include "core/arm/cpu_interrupt_handler.h" namespace Common { - class SpinLock; +class SpinLock; } namespace Kernel { @@ -27,7 +27,9 @@ namespace Kernel { class PhysicalCore { public: - PhysicalCore(Core::System& system, std::size_t id, Core::ExclusiveMonitor& exclusive_monitor); + PhysicalCore(Core::System& system, std::size_t id, Core::ExclusiveMonitor& exclusive_monitor, + Core::CPUInterruptHandler& interrupt_handler, Core::ARM_Interface& arm_interface32, + Core::ARM_Interface& arm_interface64); ~PhysicalCore(); PhysicalCore(const PhysicalCore&) = delete; @@ -92,13 +94,13 @@ public: void SetIs64Bit(bool is_64_bit); private: - Core::CPUInterruptHandler interrupt_handler; + Core::CPUInterruptHandler& interrupt_handler; std::size_t core_index; - std::unique_ptr arm_interface_32; - std::unique_ptr arm_interface_64; + Core::ARM_Interface& arm_interface_32; + Core::ARM_Interface& arm_interface_64; std::unique_ptr scheduler; Core::ARM_Interface* arm_interface{}; - std::unique_ptr guard; + std::unique_ptr guard; }; } // namespace Kernel From 1567824d2da8e9b49b433f3d1d753d8ad84e65f9 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sun, 1 Mar 2020 12:14:17 -0400 Subject: [PATCH 198/252] General: Move ARM_Interface into Threads. --- src/core/arm/arm_interface.h | 2 + src/core/arm/dynarmic/arm_dynarmic_32.cpp | 4 ++ src/core/arm/dynarmic/arm_dynarmic_32.h | 1 + src/core/arm/dynarmic/arm_dynarmic_64.cpp | 4 ++ src/core/arm/dynarmic/arm_dynarmic_64.h | 1 + src/core/arm/unicorn/arm_unicorn.cpp | 4 ++ src/core/arm/unicorn/arm_unicorn.h | 1 + src/core/core.cpp | 34 ++++++------- src/core/core_manager.cpp | 18 +------ src/core/cpu_manager.cpp | 30 ++++++------ src/core/hle/kernel/kernel.cpp | 59 ++++++++--------------- src/core/hle/kernel/kernel.h | 8 ++- src/core/hle/kernel/physical_core.cpp | 37 ++------------ src/core/hle/kernel/physical_core.h | 37 +++----------- src/core/hle/kernel/scheduler.cpp | 12 ++--- src/core/hle/kernel/svc.cpp | 11 +---- src/core/hle/kernel/thread.cpp | 35 ++++++++++++++ src/core/hle/kernel/thread.h | 8 ++- 18 files changed, 136 insertions(+), 170 deletions(-) diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index dc9b2ff7b..e5c484336 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h @@ -148,6 +148,8 @@ public: */ virtual void SetTPIDR_EL0(u64 value) = 0; + virtual void ChangeProcessorId(std::size_t new_core_id) = 0; + virtual void SaveContext(ThreadContext32& ctx) = 0; virtual void SaveContext(ThreadContext64& ctx) = 0; virtual void LoadContext(const ThreadContext32& ctx) = 0; diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index f36c5f401..9c47c133c 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -165,6 +165,10 @@ void ARM_Dynarmic_32::SetTPIDR_EL0(u64 value) { cp15->uprw = static_cast(value); } +void ARM_Dynarmic_32::ChangeProcessorId(std::size_t new_core_id) { + // jit->ChangeProcessorId(new_core_id); +} + void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) { Dynarmic::A32::Context context; jit->SaveContext(context); diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h index 2dd9a4df0..bea4933c8 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.h +++ b/src/core/arm/dynarmic/arm_dynarmic_32.h @@ -47,6 +47,7 @@ public: void SetTlsAddress(VAddr address) override; void SetTPIDR_EL0(u64 value) override; u64 GetTPIDR_EL0() const override; + void ChangeProcessorId(std::size_t new_core_id) override; void SaveContext(ThreadContext32& ctx) override; void SaveContext(ThreadContext64& ctx) override {} diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 8401ba631..03b3313cf 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -258,6 +258,10 @@ void ARM_Dynarmic_64::SetTPIDR_EL0(u64 value) { cb->tpidr_el0 = value; } +void ARM_Dynarmic_64::ChangeProcessorId(std::size_t new_core_id) { + jit->ChangeProcessorId(new_core_id); +} + void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) { ctx.cpu_registers = jit->GetRegisters(); ctx.sp = jit->GetSP(); diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h index 1c6791d4e..c26b47249 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.h +++ b/src/core/arm/dynarmic/arm_dynarmic_64.h @@ -46,6 +46,7 @@ public: void SetTlsAddress(VAddr address) override; void SetTPIDR_EL0(u64 value) override; u64 GetTPIDR_EL0() const override; + void ChangeProcessorId(std::size_t new_core_id) override; void SaveContext(ThreadContext32& ctx) override {} void SaveContext(ThreadContext64& ctx) override; diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp index d81d1b5b0..099229c8d 100644 --- a/src/core/arm/unicorn/arm_unicorn.cpp +++ b/src/core/arm/unicorn/arm_unicorn.cpp @@ -159,6 +159,10 @@ void ARM_Unicorn::SetTPIDR_EL0(u64 value) { CHECKED(uc_reg_write(uc, UC_ARM64_REG_TPIDR_EL0, &value)); } +void ARM_Unicorn::ChangeProcessorId(std::size_t new_core_id) { + core_index = new_core_id; +} + void ARM_Unicorn::Run() { if (GDBStub::IsServerEnabled()) { ExecuteInstructions(std::max(4000000U, 0U)); diff --git a/src/core/arm/unicorn/arm_unicorn.h b/src/core/arm/unicorn/arm_unicorn.h index e3da368de..f09b24a85 100644 --- a/src/core/arm/unicorn/arm_unicorn.h +++ b/src/core/arm/unicorn/arm_unicorn.h @@ -36,6 +36,7 @@ public: void SetTlsAddress(VAddr address) override; void SetTPIDR_EL0(u64 value) override; u64 GetTPIDR_EL0() const override; + void ChangeProcessorId(std::size_t new_core_id) override; void PrepareReschedule() override; void ClearExclusiveState() override; void ExecuteInstructions(std::size_t num_instructions); diff --git a/src/core/core.cpp b/src/core/core.cpp index 2ca9c0be5..40eea297e 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -119,14 +119,6 @@ struct System::Impl { : kernel{system}, fs_controller{system}, memory{system}, cpu_manager{system}, reporter{system}, applet_manager{system} {} - Kernel::PhysicalCore& CurrentPhysicalCore() { - return kernel.CurrentPhysicalCore(); - } - - Kernel::PhysicalCore& GetPhysicalCore(std::size_t index) { - return kernel.PhysicalCore(index); - } - ResultStatus Run() { status = ResultStatus::Success; @@ -443,7 +435,7 @@ bool System::IsPoweredOn() const { } void System::PrepareReschedule() { - impl->CurrentPhysicalCore().Stop(); + //impl->CurrentPhysicalCore().Stop(); } void System::PrepareReschedule(const u32 core_index) { @@ -463,11 +455,11 @@ const TelemetrySession& System::TelemetrySession() const { } ARM_Interface& System::CurrentArmInterface() { - return impl->CurrentPhysicalCore().ArmInterface(); + return impl->kernel.CurrentScheduler().GetCurrentThread()->ArmInterface(); } const ARM_Interface& System::CurrentArmInterface() const { - return impl->CurrentPhysicalCore().ArmInterface(); + return impl->kernel.CurrentScheduler().GetCurrentThread()->ArmInterface(); } std::size_t System::CurrentCoreIndex() const { @@ -477,27 +469,27 @@ std::size_t System::CurrentCoreIndex() const { } Kernel::Scheduler& System::CurrentScheduler() { - return impl->CurrentPhysicalCore().Scheduler(); + return impl->kernel.CurrentScheduler(); } const Kernel::Scheduler& System::CurrentScheduler() const { - return impl->CurrentPhysicalCore().Scheduler(); + return impl->kernel.CurrentScheduler(); } Kernel::PhysicalCore& System::CurrentPhysicalCore() { - return impl->CurrentPhysicalCore(); + return impl->kernel.CurrentPhysicalCore(); } const Kernel::PhysicalCore& System::CurrentPhysicalCore() const { - return impl->CurrentPhysicalCore(); + return impl->kernel.CurrentPhysicalCore(); } Kernel::Scheduler& System::Scheduler(std::size_t core_index) { - return impl->GetPhysicalCore(core_index).Scheduler(); + return impl->kernel.Scheduler(core_index); } const Kernel::Scheduler& System::Scheduler(std::size_t core_index) const { - return impl->GetPhysicalCore(core_index).Scheduler(); + return impl->kernel.Scheduler(core_index); } /// Gets the global scheduler @@ -527,11 +519,15 @@ const Kernel::Process* System::CurrentProcess() const { } ARM_Interface& System::ArmInterface(std::size_t core_index) { - return impl->GetPhysicalCore(core_index).ArmInterface(); + auto* thread = impl->kernel.Scheduler(core_index).GetCurrentThread(); + ASSERT(thread && !thread->IsHLEThread()); + return thread->ArmInterface(); } const ARM_Interface& System::ArmInterface(std::size_t core_index) const { - return impl->GetPhysicalCore(core_index).ArmInterface(); + auto* thread = impl->kernel.Scheduler(core_index).GetCurrentThread(); + ASSERT(thread && !thread->IsHLEThread()); + return thread->ArmInterface(); } ExclusiveMonitor& System::Monitor() { diff --git a/src/core/core_manager.cpp b/src/core/core_manager.cpp index 45f0bb547..82d7acb40 100644 --- a/src/core/core_manager.cpp +++ b/src/core/core_manager.cpp @@ -28,21 +28,7 @@ CoreManager::CoreManager(System& system, std::size_t core_index) CoreManager::~CoreManager() = default; void CoreManager::RunLoop(bool tight_loop) { - Reschedule(); - - // If we don't have a currently active thread then don't execute instructions, - // instead advance to the next event and try to yield to the next thread - if (Kernel::GetCurrentThread() == nullptr) { - LOG_TRACE(Core, "Core-{} idling", core_index); - } else { - if (tight_loop) { - physical_core.Run(); - } else { - physical_core.Step(); - } - } - - Reschedule(); + /// Deprecated } void CoreManager::SingleStep() { @@ -50,7 +36,7 @@ void CoreManager::SingleStep() { } void CoreManager::PrepareReschedule() { - physical_core.Stop(); + //physical_core.Stop(); } void CoreManager::Reschedule() { diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index 2aea95a25..2e9dc9dc3 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp @@ -129,18 +129,17 @@ void CpuManager::MultiCoreRunGuestThread() { void CpuManager::MultiCoreRunGuestLoop() { auto& kernel = system.Kernel(); auto* thread = kernel.CurrentScheduler().GetCurrentThread(); - auto host_context = thread->GetHostContext(); - host_context->SetRewindPoint(std::function(GuestRewindFunction), this); - host_context.reset(); while (true) { - auto& physical_core = kernel.CurrentPhysicalCore(); + auto* physical_core = &kernel.CurrentPhysicalCore(); + auto& arm_interface = thread->ArmInterface(); system.EnterDynarmicProfile(); - while (!physical_core.IsInterrupted()) { - physical_core.Run(); + while (!physical_core->IsInterrupted()) { + arm_interface.Run(); + physical_core = &kernel.CurrentPhysicalCore(); } system.ExitDynarmicProfile(); - physical_core.ClearExclusive(); - auto& scheduler = physical_core.Scheduler(); + arm_interface.ClearExclusiveState(); + auto& scheduler = kernel.CurrentScheduler(); scheduler.TryDoContextSwitch(); } } @@ -150,7 +149,7 @@ void CpuManager::MultiCoreRunIdleThread() { while (true) { auto& physical_core = kernel.CurrentPhysicalCore(); physical_core.Idle(); - auto& scheduler = physical_core.Scheduler(); + auto& scheduler = kernel.CurrentScheduler(); scheduler.TryDoContextSwitch(); } } @@ -229,14 +228,13 @@ void CpuManager::SingleCoreRunGuestThread() { void CpuManager::SingleCoreRunGuestLoop() { auto& kernel = system.Kernel(); auto* thread = kernel.CurrentScheduler().GetCurrentThread(); - auto host_context = thread->GetHostContext(); - host_context->SetRewindPoint(std::function(GuestRewindFunction), this); - host_context.reset(); while (true) { - auto& physical_core = kernel.CurrentPhysicalCore(); + auto* physical_core = &kernel.CurrentPhysicalCore(); + auto& arm_interface = thread->ArmInterface(); system.EnterDynarmicProfile(); - while (!physical_core.IsInterrupted()) { - physical_core.Run(); + while (!physical_core->IsInterrupted()) { + arm_interface.Run(); + physical_core = &kernel.CurrentPhysicalCore(); preemption_count++; if (preemption_count % max_cycle_runs == 0) { break; @@ -246,7 +244,7 @@ void CpuManager::SingleCoreRunGuestLoop() { thread->SetPhantomMode(true); system.CoreTiming().Advance(); thread->SetPhantomMode(false); - physical_core.ClearExclusive(); + arm_interface.ClearExclusiveState(); PreemptSingleCore(); auto& scheduler = kernel.Scheduler(current_core); scheduler.TryDoContextSwitch(); diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index e33ef5323..3feddd9ad 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -19,7 +19,6 @@ #include "core/arm/arm_interface.h" #include "core/arm/cpu_interrupt_handler.h" #include "core/arm/exclusive_monitor.h" -#include "core/arm/unicorn/arm_unicorn.h" #include "core/core.h" #include "core/core_timing.h" #include "core/core_timing_util.h" @@ -45,11 +44,6 @@ #include "core/hle/result.h" #include "core/memory.h" -#ifdef ARCHITECTURE_x86_64 -#include "core/arm/dynarmic/arm_dynarmic_32.h" -#include "core/arm/dynarmic/arm_dynarmic_64.h" -#endif - MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70)); namespace Kernel { @@ -186,20 +180,8 @@ struct KernelCore::Impl { exclusive_monitor = Core::MakeExclusiveMonitor(system.Memory(), Core::Hardware::NUM_CPU_CORES); for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { -#ifdef ARCHITECTURE_x86_64 - arm_interfaces_32[i] = - std::make_unique(system, interrupts, *exclusive_monitor, i); - arm_interfaces_64[i] = - std::make_unique(system, interrupts, *exclusive_monitor, i); -#else - arm_interfaces_32[i] = std::make_shared( - system, interrupts, ARM_Unicorn::Arch::AArch32, i); - arm_interfaces_64[i] = std::make_shared( - system, interrupts, ARM_Unicorn::Arch::AArch64, i); - LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); -#endif - cores.emplace_back(system, i, *exclusive_monitor, interrupts[i], *arm_interfaces_32[i], - *arm_interfaces_64[i]); + schedulers[i] = std::make_unique(system, i); + cores.emplace_back(system, i, *schedulers[i], interrupts[i]); } } @@ -268,10 +250,6 @@ struct KernelCore::Impl { return; } - for (auto& core : cores) { - core.SetIs64Bit(process->Is64BitProcess()); - } - u32 core_id = GetCurrentHostThreadID(); if (core_id < Core::Hardware::NUM_CPU_CORES) { system.Memory().SetCurrentPageTable(*process, core_id); @@ -429,10 +407,7 @@ struct KernelCore::Impl { std::array, Core::Hardware::NUM_CPU_CORES> suspend_threads{}; std::array interrupts{}; - std::array, Core::Hardware::NUM_CPU_CORES> - arm_interfaces_32{}; - std::array, Core::Hardware::NUM_CPU_CORES> - arm_interfaces_64{}; + std::array, Core::Hardware::NUM_CPU_CORES> schedulers{}; bool is_multicore{}; std::thread::id single_core_thread_id{}; @@ -497,11 +472,11 @@ const Kernel::GlobalScheduler& KernelCore::GlobalScheduler() const { } Kernel::Scheduler& KernelCore::Scheduler(std::size_t id) { - return impl->cores[id].Scheduler(); + return *impl->schedulers[id]; } const Kernel::Scheduler& KernelCore::Scheduler(std::size_t id) const { - return impl->cores[id].Scheduler(); + return *impl->schedulers[id]; } Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) { @@ -525,11 +500,23 @@ const Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() const { } Kernel::Scheduler& KernelCore::CurrentScheduler() { - return CurrentPhysicalCore().Scheduler(); + u32 core_id = impl->GetCurrentHostThreadID(); + ASSERT(core_id < Core::Hardware::NUM_CPU_CORES); + return *impl->schedulers[core_id]; } const Kernel::Scheduler& KernelCore::CurrentScheduler() const { - return CurrentPhysicalCore().Scheduler(); + u32 core_id = impl->GetCurrentHostThreadID(); + ASSERT(core_id < Core::Hardware::NUM_CPU_CORES); + return *impl->schedulers[core_id]; +} + +std::array& KernelCore::Interrupts() { + return impl->interrupts; +} + +const std::array& KernelCore::Interrupts() const { + return impl->interrupts; } Kernel::Synchronization& KernelCore::Synchronization() { @@ -557,15 +544,11 @@ const Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() const { } void KernelCore::InvalidateAllInstructionCaches() { - for (std::size_t i = 0; i < impl->global_scheduler.CpuCoresCount(); i++) { - PhysicalCore(i).ArmInterface().ClearInstructionCache(); - } + //TODO: Reimplement, this } void KernelCore::PrepareReschedule(std::size_t id) { - if (id < impl->global_scheduler.CpuCoresCount()) { - impl->cores[id].Stop(); - } + // TODO: Reimplement, this } void KernelCore::AddNamedPort(std::string name, std::shared_ptr port) { diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 1eb6ede73..846056b85 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -4,15 +4,17 @@ #pragma once +#include #include #include #include #include +#include "core/hardware_properties.h" #include "core/hle/kernel/memory/memory_types.h" #include "core/hle/kernel/object.h" namespace Core { -struct EmuThreadHandle; +class CPUInterruptHandler; class ExclusiveMonitor; class System; } // namespace Core @@ -144,6 +146,10 @@ public: const Core::ExclusiveMonitor& GetExclusiveMonitor() const; + std::array& Interrupts(); + + const std::array& Interrupts() const; + void InvalidateAllInstructionCaches(); /// Adds a port to the named port table diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp index 9146b331d..c82c60a16 100644 --- a/src/core/hle/kernel/physical_core.cpp +++ b/src/core/hle/kernel/physical_core.cpp @@ -20,50 +20,21 @@ namespace Kernel { -PhysicalCore::PhysicalCore(Core::System& system, std::size_t id, - Core::ExclusiveMonitor& exclusive_monitor, - Core::CPUInterruptHandler& interrupt_handler, - Core::ARM_Interface& arm_interface32, - Core::ARM_Interface& arm_interface64) - : interrupt_handler{interrupt_handler}, core_index{id}, arm_interface_32{arm_interface32}, - arm_interface_64{arm_interface64} { +PhysicalCore::PhysicalCore(Core::System& system, std::size_t id, Kernel::Scheduler& scheduler, + Core::CPUInterruptHandler& interrupt_handler) + : interrupt_handler{interrupt_handler}, core_index{id}, scheduler{scheduler} { - scheduler = std::make_unique(system, core_index); guard = std::make_unique(); } PhysicalCore::~PhysicalCore() = default; -void PhysicalCore::Run() { - arm_interface->Run(); -} - -void PhysicalCore::ClearExclusive() { - arm_interface->ClearExclusiveState(); -} - -void PhysicalCore::Step() { - arm_interface->Step(); -} - void PhysicalCore::Idle() { interrupt_handler.AwaitInterrupt(); } -void PhysicalCore::Stop() { - arm_interface->PrepareReschedule(); -} - void PhysicalCore::Shutdown() { - scheduler->Shutdown(); -} - -void PhysicalCore::SetIs64Bit(bool is_64_bit) { - if (is_64_bit) { - arm_interface = &arm_interface_64; - } else { - arm_interface = &arm_interface_32; - } + scheduler.Shutdown(); } void PhysicalCore::Interrupt() { diff --git a/src/core/hle/kernel/physical_core.h b/src/core/hle/kernel/physical_core.h index 2673d90f2..751b994a7 100644 --- a/src/core/hle/kernel/physical_core.h +++ b/src/core/hle/kernel/physical_core.h @@ -10,7 +10,7 @@ #include "core/arm/cpu_interrupt_handler.h" namespace Common { -class SpinLock; + class SpinLock; } namespace Kernel { @@ -27,9 +27,9 @@ namespace Kernel { class PhysicalCore { public: - PhysicalCore(Core::System& system, std::size_t id, Core::ExclusiveMonitor& exclusive_monitor, - Core::CPUInterruptHandler& interrupt_handler, Core::ARM_Interface& arm_interface32, - Core::ARM_Interface& arm_interface64); + PhysicalCore(Core::System& system, std::size_t id, + Kernel::Scheduler& scheduler, + Core::CPUInterruptHandler& interrupt_handler); ~PhysicalCore(); PhysicalCore(const PhysicalCore&) = delete; @@ -38,17 +38,7 @@ public: PhysicalCore(PhysicalCore&&) = default; PhysicalCore& operator=(PhysicalCore&&) = default; - /// Execute current jit state - void Run(); - /// Clear Exclusive state. - void ClearExclusive(); - /// Set this core in IdleState. void Idle(); - /// Execute a single instruction in current jit. - void Step(); - /// Stop JIT execution/exit - void Stop(); - /// Interrupt this physical core. void Interrupt(); @@ -63,14 +53,6 @@ public: // Shutdown this physical core. void Shutdown(); - Core::ARM_Interface& ArmInterface() { - return *arm_interface; - } - - const Core::ARM_Interface& ArmInterface() const { - return *arm_interface; - } - bool IsMainCore() const { return core_index == 0; } @@ -84,22 +66,17 @@ public: } Kernel::Scheduler& Scheduler() { - return *scheduler; + return scheduler; } const Kernel::Scheduler& Scheduler() const { - return *scheduler; + return scheduler; } - void SetIs64Bit(bool is_64_bit); - private: Core::CPUInterruptHandler& interrupt_handler; std::size_t core_index; - Core::ARM_Interface& arm_interface_32; - Core::ARM_Interface& arm_interface_64; - std::unique_ptr scheduler; - Core::ARM_Interface* arm_interface{}; + Kernel::Scheduler& scheduler; std::unique_ptr guard; }; diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index 8d56b49ce..a5083ae7c 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp @@ -681,15 +681,16 @@ void Scheduler::SwitchContextStep2() { new_thread->SetWasRunning(false); auto* const thread_owner_process = current_thread->GetOwnerProcess(); - if (previous_process != thread_owner_process && thread_owner_process != nullptr) { + if (thread_owner_process != nullptr) { system.Kernel().MakeCurrentProcess(thread_owner_process); } if (!new_thread->IsHLEThread()) { - auto& cpu_core = system.ArmInterface(core_id); + Core::ARM_Interface& cpu_core = new_thread->ArmInterface(); cpu_core.LoadContext(new_thread->GetContext32()); cpu_core.LoadContext(new_thread->GetContext64()); cpu_core.SetTlsAddress(new_thread->GetTLSAddress()); cpu_core.SetTPIDR_EL0(new_thread->GetTPIDR_EL0()); + cpu_core.ChangeProcessorId(this->core_id); cpu_core.ClearExclusiveState(); } } @@ -722,18 +723,15 @@ void Scheduler::SwitchContext() { } previous_thread->SetContinuousOnSVC(false); previous_thread->last_running_ticks = system.CoreTiming().GetCPUTicks(); + previous_thread->SetIsRunning(false); if (!previous_thread->IsHLEThread()) { - auto& cpu_core = system.ArmInterface(core_id); + Core::ARM_Interface& cpu_core = previous_thread->ArmInterface(); cpu_core.SaveContext(previous_thread->GetContext32()); cpu_core.SaveContext(previous_thread->GetContext64()); // Save the TPIDR_EL0 system register in case it was modified. previous_thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0()); cpu_core.ClearExclusiveState(); } - if (previous_thread->GetStatus() == ThreadStatus::Running) { - previous_thread->SetStatus(ThreadStatus::Ready); - } - previous_thread->SetIsRunning(false); previous_thread->context_guard.unlock(); } diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index f08745226..599972211 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -1533,7 +1533,9 @@ static void SleepThread(Core::System& system, s64 nanoseconds) { } if (is_redundant && !system.Kernel().IsMulticore()) { + system.Kernel().ExitSVCProfile(); system.GetCpuManager().PreemptSingleCore(); + system.Kernel().EnterSVCProfile(); } } @@ -2457,9 +2459,6 @@ void Call(Core::System& system, u32 immediate) { auto& kernel = system.Kernel(); kernel.EnterSVCProfile(); - auto* thread = system.CurrentScheduler().GetCurrentThread(); - thread->SetContinuousOnSVC(true); - const FunctionDef* info = system.CurrentProcess()->Is64BitProcess() ? GetSVCInfo64(immediate) : GetSVCInfo32(immediate); if (info) { @@ -2473,12 +2472,6 @@ void Call(Core::System& system, u32 immediate) { } kernel.ExitSVCProfile(); - - if (!thread->IsContinuousOnSVC()) { - auto* host_context = thread->GetHostContext().get(); - host_context->Rewind(); - } - system.EnterDynarmicProfile(); } diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 6f8e7a070..58b06aa9e 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -13,6 +13,13 @@ #include "common/logging/log.h" #include "common/thread_queue_list.h" #include "core/arm/arm_interface.h" +#ifdef ARCHITECTURE_x86_64 +#include "core/arm/dynarmic/arm_dynarmic_32.h" +#include "core/arm/dynarmic/arm_dynarmic_64.h" +#endif +#include "core/arm/cpu_interrupt_handler.h" +#include "core/arm/exclusive_monitor.h" +#include "core/arm/unicorn/arm_unicorn.h" #include "core/core.h" #include "core/core_timing.h" #include "core/core_timing_util.h" @@ -232,7 +239,27 @@ ResultVal> Thread::Create(Core::System& system, ThreadTy } // TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used // to initialize the context + thread->arm_interface.reset(); if ((type_flags & THREADTYPE_HLE) == 0) { +#ifdef ARCHITECTURE_x86_64 + if (owner_process && !owner_process->Is64BitProcess()) { + thread->arm_interface = std::make_unique( + system, kernel.Interrupts(), kernel.GetExclusiveMonitor(), processor_id); + } else { + thread->arm_interface = std::make_unique( + system, kernel.Interrupts(), kernel.GetExclusiveMonitor(), processor_id); + } + +#else + if (owner_process && !owner_process->Is64BitProcess()) { + thread->arm_interface = std::make_shared( + system, kernel.Interrupts(), ARM_Unicorn::Arch::AArch32, processor_id); + } else { + thread->arm_interface = std::make_shared( + system, kernel.Interrupts(), ARM_Unicorn::Arch::AArch64, processor_id); + } + LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); +#endif ResetThreadContext32(thread->context_32, static_cast(stack_top), static_cast(entry_point), static_cast(arg)); ResetThreadContext64(thread->context_64, stack_top, entry_point, arg); @@ -276,6 +303,14 @@ VAddr Thread::GetCommandBufferAddress() const { return GetTLSAddress() + command_header_offset; } +Core::ARM_Interface& Thread::ArmInterface() { + return *arm_interface; +} + +const Core::ARM_Interface& Thread::ArmInterface() const { + return *arm_interface; +} + void Thread::SetStatus(ThreadStatus new_status) { if (new_status == status) { return; diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index f998890c4..c08fc3a89 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -21,6 +21,7 @@ class Fiber; } namespace Core { +class ARM_Interface; class System; } // namespace Core @@ -271,6 +272,10 @@ public: void SetSynchronizationResults(SynchronizationObject* object, ResultCode result); + Core::ARM_Interface& ArmInterface(); + + const Core::ARM_Interface& ArmInterface() const; + SynchronizationObject* GetSignalingObject() const { return signaling_object; } @@ -617,9 +622,10 @@ private: void AdjustSchedulingOnAffinity(u64 old_affinity_mask, s32 old_core); + Common::SpinLock context_guard{}; ThreadContext32 context_32{}; ThreadContext64 context_64{}; - Common::SpinLock context_guard{}; + std::unique_ptr arm_interface{}; std::shared_ptr host_context{}; u64 thread_id = 0; From 5974e3ea33e12e7abd813704e5b895003ba83555 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sat, 7 Mar 2020 14:16:25 -0400 Subject: [PATCH 199/252] Thread: Release the ARM Interface on exitting. --- src/core/hle/kernel/scheduler.cpp | 2 +- src/core/hle/kernel/thread.cpp | 2 ++ src/core/hle/kernel/thread.h | 5 +++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index a5083ae7c..ce7e1986d 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp @@ -724,7 +724,7 @@ void Scheduler::SwitchContext() { previous_thread->SetContinuousOnSVC(false); previous_thread->last_running_ticks = system.CoreTiming().GetCPUTicks(); previous_thread->SetIsRunning(false); - if (!previous_thread->IsHLEThread()) { + if (!previous_thread->IsHLEThread() && !previous_thread->HasExited()) { Core::ARM_Interface& cpu_core = previous_thread->ArmInterface(); cpu_core.SaveContext(previous_thread->GetContext32()); cpu_core.SaveContext(previous_thread->GetContext64()); diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 58b06aa9e..65fedfc9b 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -69,6 +69,8 @@ void Thread::Stop() { // Mark the TLS slot in the thread's page as free. owner_process->FreeTLSRegion(tls_address); } + arm_interface.reset(); + has_exited = true; } global_handle = 0; } diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index c08fc3a89..f651d7822 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -610,6 +610,10 @@ public: is_phantom_mode = phantom; } + bool HasExited() const { + return has_exited; + } + private: friend class GlobalScheduler; friend class Scheduler; @@ -714,6 +718,7 @@ private: bool will_be_terminated = false; bool is_phantom_mode = false; + bool has_exited = false; bool was_running = false; From 9bde28d7b100fdcd1cc50c8f2a4b8c74704f5e34 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Tue, 10 Mar 2020 13:24:52 -0400 Subject: [PATCH 200/252] Scheduler: Correct Reload/Unload --- src/core/hle/kernel/kernel.cpp | 1 + src/core/hle/kernel/scheduler.cpp | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 3feddd9ad..739205eca 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -149,6 +149,7 @@ struct KernelCore::Impl { for (std::size_t i = 0; i < cores.size(); i++) { cores[i].Shutdown(); + schedulers[i].reset(); } cores.clear(); diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index ce7e1986d..43c924fa0 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp @@ -625,8 +625,8 @@ void Scheduler::Unload() { thread->SetContinuousOnSVC(false); thread->last_running_ticks = system.CoreTiming().GetCPUTicks(); thread->SetIsRunning(false); - if (!thread->IsHLEThread()) { - auto& cpu_core = system.ArmInterface(core_id); + if (!thread->IsHLEThread() && !thread->HasExited()) { + Core::ARM_Interface& cpu_core = thread->ArmInterface(); cpu_core.SaveContext(thread->GetContext32()); cpu_core.SaveContext(thread->GetContext64()); // Save the TPIDR_EL0 system register in case it was modified. @@ -653,11 +653,12 @@ void Scheduler::Reload() { system.Kernel().MakeCurrentProcess(thread_owner_process); } if (!thread->IsHLEThread()) { - auto& cpu_core = system.ArmInterface(core_id); + Core::ARM_Interface& cpu_core = thread->ArmInterface(); cpu_core.LoadContext(thread->GetContext32()); cpu_core.LoadContext(thread->GetContext64()); cpu_core.SetTlsAddress(thread->GetTLSAddress()); cpu_core.SetTPIDR_EL0(thread->GetTPIDR_EL0()); + cpu_core.ChangeProcessorId(this->core_id); cpu_core.ClearExclusiveState(); } } From f5e32935ca9d1727624c86ca78aff91027caf819 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sat, 28 Mar 2020 15:23:28 -0400 Subject: [PATCH 201/252] SingleCore: Use Cycle Timing instead of Host Timing. --- src/core/arm/arm_interface.h | 6 ++-- src/core/arm/dynarmic/arm_dynarmic_32.cpp | 37 +++++++++++++------- src/core/arm/dynarmic/arm_dynarmic_32.h | 2 +- src/core/arm/dynarmic/arm_dynarmic_64.cpp | 39 ++++++++++++++------- src/core/arm/dynarmic/arm_dynarmic_64.h | 2 +- src/core/arm/unicorn/arm_unicorn.cpp | 6 ++-- src/core/arm/unicorn/arm_unicorn.h | 4 +-- src/core/core_timing.cpp | 41 ++++++++++++++++++----- src/core/core_timing.h | 14 ++++++-- src/core/core_timing_util.cpp | 29 +++++++++++----- src/core/core_timing_util.h | 15 ++------- src/core/cpu_manager.cpp | 18 +++++----- src/core/cpu_manager.h | 2 +- src/core/hle/kernel/svc.cpp | 5 +++ src/core/hle/kernel/thread.cpp | 12 ++++--- 15 files changed, 152 insertions(+), 80 deletions(-) diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index e5c484336..fbdce4134 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h @@ -26,8 +26,9 @@ using CPUInterrupts = std::arrayticks -= ticks; + if (parent.uses_wall_clock) { + return; + } + // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a + // rough approximation of the amount of executed ticks in the system, it may be thrown off + // if not all cores are doing a similar amount of work. Instead of doing this, we should + // device a way so that timing is consistent across all cores without increasing the ticks 4 + // times. + u64 amortized_ticks = + (ticks - num_interpreted_instructions) / Core::Hardware::NUM_CPU_CORES; + // Always execute at least one tick. + amortized_ticks = std::max(amortized_ticks, 1); + + parent.system.CoreTiming().AddTicks(amortized_ticks); + num_interpreted_instructions = 0; } u64 GetTicksRemaining() override { - if (!parent.interrupt_handlers[parent.core_index].IsInterrupted()) { - return std::max(ticks, 0); + if (parent.uses_wall_clock) { + if (!parent.interrupt_handlers[parent.core_index].IsInterrupted()) { + return std::max(1000U, 0); + } + return 0ULL; } - return 0ULL; - } - - void ResetTicks() { - ticks = 1000LL; + return std::max(parent.system.CoreTiming().GetDowncount(), 0LL); } ARM_Dynarmic_32& parent; std::size_t num_interpreted_instructions{}; - s64 ticks{}; }; std::shared_ptr ARM_Dynarmic_32::MakeJit(Common::PageTable& page_table, @@ -103,7 +115,6 @@ std::shared_ptr ARM_Dynarmic_32::MakeJit(Common::PageTable& } void ARM_Dynarmic_32::Run() { - cb->ResetTicks(); jit->Run(); } @@ -112,8 +123,10 @@ void ARM_Dynarmic_32::Step() { } ARM_Dynarmic_32::ARM_Dynarmic_32(System& system, CPUInterrupts& interrupt_handlers, - ExclusiveMonitor& exclusive_monitor, std::size_t core_index) - : ARM_Interface{system, interrupt_handlers}, cb(std::make_unique(*this)), + bool uses_wall_clock, ExclusiveMonitor& exclusive_monitor, + std::size_t core_index) + : ARM_Interface{system, interrupt_handlers, uses_wall_clock}, + cb(std::make_unique(*this)), cp15(std::make_shared(*this)), core_index{core_index}, exclusive_monitor{dynamic_cast(exclusive_monitor)} {} diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h index bea4933c8..8afd15c8b 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.h +++ b/src/core/arm/dynarmic/arm_dynarmic_32.h @@ -29,7 +29,7 @@ class System; class ARM_Dynarmic_32 final : public ARM_Interface { public: - ARM_Dynarmic_32(System& system, CPUInterrupts& interrupt_handlers, + ARM_Dynarmic_32(System& system, CPUInterrupts& interrupt_handlers, bool uses_wall_clock, ExclusiveMonitor& exclusive_monitor, std::size_t core_index); ~ARM_Dynarmic_32() override; diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 03b3313cf..a518733b6 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -124,29 +124,41 @@ public: } void AddTicks(u64 ticks) override { - this->ticks -= ticks; + if (parent.uses_wall_clock) { + return; + } + // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a + // rough approximation of the amount of executed ticks in the system, it may be thrown off + // if not all cores are doing a similar amount of work. Instead of doing this, we should + // device a way so that timing is consistent across all cores without increasing the ticks 4 + // times. + u64 amortized_ticks = + (ticks - num_interpreted_instructions) / Core::Hardware::NUM_CPU_CORES; + // Always execute at least one tick. + amortized_ticks = std::max(amortized_ticks, 1); + + parent.system.CoreTiming().AddTicks(amortized_ticks); + num_interpreted_instructions = 0; } u64 GetTicksRemaining() override { - if (!parent.interrupt_handlers[parent.core_index].IsInterrupted()) { - return std::max(ticks, 0); + if (parent.uses_wall_clock) { + if (!parent.interrupt_handlers[parent.core_index].IsInterrupted()) { + return std::max(1000U, 0); + } + return 0ULL; } - return 0ULL; + return std::max(parent.system.CoreTiming().GetDowncount(), 0LL); } u64 GetCNTPCT() override { return parent.system.CoreTiming().GetClockTicks(); } - void ResetTicks() { - ticks = 1000LL; - } - ARM_Dynarmic_64& parent; std::size_t num_interpreted_instructions = 0; u64 tpidrro_el0 = 0; u64 tpidr_el0 = 0; - s64 ticks{}; }; std::shared_ptr ARM_Dynarmic_64::MakeJit(Common::PageTable& page_table, @@ -185,13 +197,12 @@ std::shared_ptr ARM_Dynarmic_64::MakeJit(Common::PageTable& } // CNTPCT uses wall clock. - config.wall_clock_cntpct = true; + config.wall_clock_cntpct = uses_wall_clock; return std::make_shared(config); } void ARM_Dynarmic_64::Run() { - cb->ResetTicks(); jit->Run(); } @@ -200,9 +211,11 @@ void ARM_Dynarmic_64::Step() { } ARM_Dynarmic_64::ARM_Dynarmic_64(System& system, CPUInterrupts& interrupt_handlers, - ExclusiveMonitor& exclusive_monitor, std::size_t core_index) - : ARM_Interface{system, interrupt_handler}, + bool uses_wall_clock, ExclusiveMonitor& exclusive_monitor, + std::size_t core_index) + : ARM_Interface{system, interrupt_handler, uses_wall_clock}, cb(std::make_unique(*this)), inner_unicorn{system, interrupt_handler, + uses_wall_clock, ARM_Unicorn::Arch::AArch64, core_index}, core_index{core_index}, exclusive_monitor{ diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h index c26b47249..31ec16521 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.h +++ b/src/core/arm/dynarmic/arm_dynarmic_64.h @@ -28,7 +28,7 @@ class System; class ARM_Dynarmic_64 final : public ARM_Interface { public: - ARM_Dynarmic_64(System& system, CPUInterrupts& interrupt_handlers, + ARM_Dynarmic_64(System& system, CPUInterrupts& interrupt_handlers, bool uses_wall_clock, ExclusiveMonitor& exclusive_monitor, std::size_t core_index); ~ARM_Dynarmic_64() override; diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp index 099229c8d..1cb71942b 100644 --- a/src/core/arm/unicorn/arm_unicorn.cpp +++ b/src/core/arm/unicorn/arm_unicorn.cpp @@ -63,9 +63,9 @@ static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int si return false; } -ARM_Unicorn::ARM_Unicorn(System& system, CPUInterruptHandler& interrupt_handler, Arch architecture, - std::size_t core_index) - : ARM_Interface{system, interrupt_handler}, core_index{core_index} { +ARM_Unicorn::ARM_Unicorn(System& system, CPUInterruptHandler& interrupt_handler, + bool uses_wall_clock, Arch architecture, std::size_t core_index) + : ARM_Interface{system, interrupt_handler, uses_wall_clock}, core_index{core_index} { const auto arch = architecture == Arch::AArch32 ? UC_ARCH_ARM : UC_ARCH_ARM64; CHECKED(uc_open(arch, UC_MODE_ARM, &uc)); diff --git a/src/core/arm/unicorn/arm_unicorn.h b/src/core/arm/unicorn/arm_unicorn.h index f09b24a85..a01751e65 100644 --- a/src/core/arm/unicorn/arm_unicorn.h +++ b/src/core/arm/unicorn/arm_unicorn.h @@ -20,8 +20,8 @@ public: AArch64, // 64-bit ARM }; - explicit ARM_Unicorn(System& system, CPUInterruptHandler& interrupt_handler, Arch architecture, - std::size_t core_index); + explicit ARM_Unicorn(System& system, CPUInterruptHandler& interrupt_handler, + bool uses_wall_clock, Arch architecture, std::size_t core_index); ~ARM_Unicorn() override; void SetPC(u64 pc) override; diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 189d4aa34..12e9e60a4 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -14,6 +14,8 @@ namespace Core::Timing { +constexpr u64 MAX_SLICE_LENGTH = 4000; + std::shared_ptr CreateEvent(std::string name, TimedCallback&& callback) { return std::make_shared(std::move(callback), std::move(name)); } @@ -53,6 +55,7 @@ void CoreTiming::ThreadEntry(CoreTiming& instance) { void CoreTiming::Initialize(std::function&& on_thread_init_) { on_thread_init = std::move(on_thread_init_); event_fifo_id = 0; + ticks = 0; const auto empty_timed_callback = [](u64, s64) {}; ev_lost = CreateEvent("_lost_event", empty_timed_callback); if (is_multicore) { @@ -126,20 +129,36 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr& event_type, u basic_lock.unlock(); } -void CoreTiming::AddTicks(std::size_t core_index, u64 ticks) { - ticks_count[core_index] += ticks; +void CoreTiming::AddTicks(u64 ticks) { + this->ticks += ticks; + downcount -= ticks; } -void CoreTiming::ResetTicks(std::size_t core_index) { - ticks_count[core_index] = 0; +void CoreTiming::Idle() { + if (!event_queue.empty()) { + u64 next_event_time = event_queue.front().time; + ticks = nsToCycles(std::chrono::nanoseconds(next_event_time)) + 10U; + return; + } + ticks += 1000U; +} + +void CoreTiming::ResetTicks() { + downcount = MAX_SLICE_LENGTH; } u64 CoreTiming::GetCPUTicks() const { - return clock->GetCPUCycles(); + if (is_multicore) { + return clock->GetCPUCycles(); + } + return ticks; } u64 CoreTiming::GetClockTicks() const { - return clock->GetClockCycles(); + if (is_multicore) { + return clock->GetClockCycles(); + } + return CpuCyclesToClockCycles(ticks); } void CoreTiming::ClearPendingEvents() { @@ -217,11 +236,17 @@ void CoreTiming::ThreadLoop() { } std::chrono::nanoseconds CoreTiming::GetGlobalTimeNs() const { - return clock->GetTimeNS(); + if (is_multicore) { + return clock->GetTimeNS(); + } + return CyclesToNs(ticks); } std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const { - return clock->GetTimeUS(); + if (is_multicore) { + return clock->GetTimeUS(); + } + return CyclesToUs(ticks); } } // namespace Core::Timing diff --git a/src/core/core_timing.h b/src/core/core_timing.h index 03f9a5c76..ed5de9b97 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h @@ -98,9 +98,15 @@ public: /// We only permit one event of each type in the queue at a time. void RemoveEvent(const std::shared_ptr& event_type); - void AddTicks(std::size_t core_index, u64 ticks); + void AddTicks(u64 ticks); - void ResetTicks(std::size_t core_index); + void ResetTicks(); + + void Idle(); + + s64 GetDowncount() const { + return downcount; + } /// Returns current time in emulated CPU cycles u64 GetCPUTicks() const; @@ -154,7 +160,9 @@ private: bool is_multicore{}; - std::array, Core::Hardware::NUM_CPU_CORES> ticks_count{}; + /// Cycle timing + u64 ticks{}; + s64 downcount{}; }; /// Creates a core timing event with the given name and callback. diff --git a/src/core/core_timing_util.cpp b/src/core/core_timing_util.cpp index be34b26fe..aefc63663 100644 --- a/src/core/core_timing_util.cpp +++ b/src/core/core_timing_util.cpp @@ -38,15 +38,8 @@ s64 usToCycles(std::chrono::microseconds us) { } s64 nsToCycles(std::chrono::nanoseconds ns) { - if (static_cast(ns.count() / 1000000000) > MAX_VALUE_TO_MULTIPLY) { - LOG_ERROR(Core_Timing, "Integer overflow, use max value"); - return std::numeric_limits::max(); - } - if (static_cast(ns.count()) > MAX_VALUE_TO_MULTIPLY) { - LOG_DEBUG(Core_Timing, "Time very big, do rounding"); - return Hardware::BASE_CLOCK_RATE * (ns.count() / 1000000000); - } - return (Hardware::BASE_CLOCK_RATE * ns.count()) / 1000000000; + const u128 temporal = Common::Multiply64Into128(ns.count(), Hardware::BASE_CLOCK_RATE); + return Common::Divide128On32(temporal, static_cast(1000000000)).first; } u64 msToClockCycles(std::chrono::milliseconds ns) { @@ -69,4 +62,22 @@ u64 CpuCyclesToClockCycles(u64 ticks) { return Common::Divide128On32(temporal, static_cast(Hardware::BASE_CLOCK_RATE)).first; } +std::chrono::milliseconds CyclesToMs(s64 cycles) { + const u128 temporal = Common::Multiply64Into128(cycles, 1000); + u64 ms = Common::Divide128On32(temporal, static_cast(Hardware::BASE_CLOCK_RATE)).first; + return std::chrono::milliseconds(ms); +} + +std::chrono::nanoseconds CyclesToNs(s64 cycles) { + const u128 temporal = Common::Multiply64Into128(cycles, 1000000000); + u64 ns = Common::Divide128On32(temporal, static_cast(Hardware::BASE_CLOCK_RATE)).first; + return std::chrono::nanoseconds(ns); +} + +std::chrono::microseconds CyclesToUs(s64 cycles) { + const u128 temporal = Common::Multiply64Into128(cycles, 1000000); + u64 us = Common::Divide128On32(temporal, static_cast(Hardware::BASE_CLOCK_RATE)).first; + return std::chrono::microseconds(us); +} + } // namespace Core::Timing diff --git a/src/core/core_timing_util.h b/src/core/core_timing_util.h index b3c58447d..2ed979e14 100644 --- a/src/core/core_timing_util.h +++ b/src/core/core_timing_util.h @@ -16,18 +16,9 @@ s64 nsToCycles(std::chrono::nanoseconds ns); u64 msToClockCycles(std::chrono::milliseconds ns); u64 usToClockCycles(std::chrono::microseconds ns); u64 nsToClockCycles(std::chrono::nanoseconds ns); - -inline std::chrono::milliseconds CyclesToMs(s64 cycles) { - return std::chrono::milliseconds(cycles * 1000 / Hardware::BASE_CLOCK_RATE); -} - -inline std::chrono::nanoseconds CyclesToNs(s64 cycles) { - return std::chrono::nanoseconds(cycles * 1000000000 / Hardware::BASE_CLOCK_RATE); -} - -inline std::chrono::microseconds CyclesToUs(s64 cycles) { - return std::chrono::microseconds(cycles * 1000000 / Hardware::BASE_CLOCK_RATE); -} +std::chrono::milliseconds CyclesToMs(s64 cycles); +std::chrono::nanoseconds CyclesToNs(s64 cycles); +std::chrono::microseconds CyclesToUs(s64 cycles); u64 CpuCyclesToClockCycles(u64 ticks); diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index 2e9dc9dc3..604405060 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp @@ -232,13 +232,10 @@ void CpuManager::SingleCoreRunGuestLoop() { auto* physical_core = &kernel.CurrentPhysicalCore(); auto& arm_interface = thread->ArmInterface(); system.EnterDynarmicProfile(); - while (!physical_core->IsInterrupted()) { + if (!physical_core->IsInterrupted()) { + system.CoreTiming().ResetTicks(); arm_interface.Run(); physical_core = &kernel.CurrentPhysicalCore(); - preemption_count++; - if (preemption_count % max_cycle_runs == 0) { - break; - } } system.ExitDynarmicProfile(); thread->SetPhantomMode(true); @@ -255,7 +252,7 @@ void CpuManager::SingleCoreRunIdleThread() { auto& kernel = system.Kernel(); while (true) { auto& physical_core = kernel.CurrentPhysicalCore(); - PreemptSingleCore(); + PreemptSingleCore(false); idle_count++; auto& scheduler = physical_core.Scheduler(); scheduler.TryDoContextSwitch(); @@ -279,12 +276,15 @@ void CpuManager::SingleCoreRunSuspendThread() { } } -void CpuManager::PreemptSingleCore() { - preemption_count = 0; +void CpuManager::PreemptSingleCore(bool from_running_enviroment) { std::size_t old_core = current_core; auto& scheduler = system.Kernel().Scheduler(old_core); Kernel::Thread* current_thread = scheduler.GetCurrentThread(); - if (idle_count >= 4) { + if (idle_count >= 4 || from_running_enviroment) { + if (!from_running_enviroment) { + system.CoreTiming().Idle(); + idle_count = 0; + } current_thread->SetPhantomMode(true); system.CoreTiming().Advance(); current_thread->SetPhantomMode(false); diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h index e6b8612f0..ae55d6427 100644 --- a/src/core/cpu_manager.h +++ b/src/core/cpu_manager.h @@ -55,7 +55,7 @@ public: std::function GetSuspendThreadStartFunc(); void* GetStartFuncParamater(); - void PreemptSingleCore(); + void PreemptSingleCore(bool from_running_enviroment = true); std::size_t CurrentCore() const { return current_core.load(); diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 599972211..c47fa9167 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -1534,6 +1534,7 @@ static void SleepThread(Core::System& system, s64 nanoseconds) { if (is_redundant && !system.Kernel().IsMulticore()) { system.Kernel().ExitSVCProfile(); + system.CoreTiming().AddTicks(1000U); system.GetCpuManager().PreemptSingleCore(); system.Kernel().EnterSVCProfile(); } @@ -1762,6 +1763,10 @@ static u64 GetSystemTick(Core::System& system) { // Returns the value of cntpct_el0 (https://switchbrew.org/wiki/SVC#svcGetSystemTick) const u64 result{system.CoreTiming().GetClockTicks()}; + if (!system.Kernel().IsMulticore()) { + core_timing.AddTicks(400U); + } + return result; } diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 65fedfc9b..d88039a16 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -246,19 +246,23 @@ ResultVal> Thread::Create(Core::System& system, ThreadTy #ifdef ARCHITECTURE_x86_64 if (owner_process && !owner_process->Is64BitProcess()) { thread->arm_interface = std::make_unique( - system, kernel.Interrupts(), kernel.GetExclusiveMonitor(), processor_id); + system, kernel.Interrupts(), kernel.IsMulticore(), kernel.GetExclusiveMonitor(), + processor_id); } else { thread->arm_interface = std::make_unique( - system, kernel.Interrupts(), kernel.GetExclusiveMonitor(), processor_id); + system, kernel.Interrupts(), kernel.IsMulticore(), kernel.GetExclusiveMonitor(), + processor_id); } #else if (owner_process && !owner_process->Is64BitProcess()) { thread->arm_interface = std::make_shared( - system, kernel.Interrupts(), ARM_Unicorn::Arch::AArch32, processor_id); + system, kernel.Interrupts(), kernel.IsMulticore(), ARM_Unicorn::Arch::AArch32, + processor_id); } else { thread->arm_interface = std::make_shared( - system, kernel.Interrupts(), ARM_Unicorn::Arch::AArch64, processor_id); + system, kernel.Interrupts(), kernel.IsMulticore(), ARM_Unicorn::Arch::AArch64, + processor_id); } LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); #endif From a7ecd9e19cd2d46768c7c55e1aa7f5e4870d7c9e Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sat, 28 Mar 2020 15:40:57 -0400 Subject: [PATCH 202/252] FrameLimiting: Enable frame limiting for single core. --- src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp | 1 + src/core/perf_stats.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp index 19df0dca7..3f7b8e670 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp @@ -41,6 +41,7 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u3 system.GetPerfStats().EndGameFrame(); system.GetPerfStats().EndSystemFrame(); system.GPU().SwapBuffers(&framebuffer); + system.FrameLimiter().DoFrameLimiting(system.CoreTiming().GetGlobalTimeUs()); system.GetPerfStats().BeginSystemFrame(); } diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp index f1ae9d4df..9f3a6b811 100644 --- a/src/core/perf_stats.cpp +++ b/src/core/perf_stats.cpp @@ -119,7 +119,7 @@ double PerfStats::GetLastFrameTimeScale() { } void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) { - if (!Settings::values.use_frame_limit) { + if (!Settings::values.use_frame_limit || Settings::values.use_multi_core) { return; } From 7e2ce2f7f47e77b46d66dcefbd0c391e58421103 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sun, 29 Mar 2020 17:06:46 -0400 Subject: [PATCH 203/252] SingleCore: Improve Cycle timing Behavior and replace mutex in global scheduler for spinlock. --- src/core/cpu_manager.cpp | 1 + src/core/hle/kernel/scheduler.h | 2 +- src/core/hle/kernel/server_session.cpp | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index 604405060..d604aa446 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp @@ -253,6 +253,7 @@ void CpuManager::SingleCoreRunIdleThread() { while (true) { auto& physical_core = kernel.CurrentPhysicalCore(); PreemptSingleCore(false); + system.CoreTiming().AddTicks(1000U); idle_count++; auto& scheduler = physical_core.Scheduler(); scheduler.TryDoContextSwitch(); diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h index f63cc5085..10dc4b832 100644 --- a/src/core/hle/kernel/scheduler.h +++ b/src/core/hle/kernel/scheduler.h @@ -188,7 +188,7 @@ private: /// Scheduler lock mechanisms. bool is_locked{}; - std::mutex inner_lock{}; // TODO(Blinkhawk): Replace for a SpinLock + Common::SpinLock inner_lock{}; std::atomic scope_lock{}; Core::EmuThreadHandle current_owner{Core::EmuThreadHandle::InvalidHandle()}; diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index 05516a453..e988a3f22 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp @@ -185,7 +185,8 @@ ResultCode ServerSession::CompleteSyncRequest() { ResultCode ServerSession::HandleSyncRequest(std::shared_ptr thread, Core::Memory::Memory& memory) { ResultCode result = QueueSyncRequest(std::move(thread), memory); - Core::System::GetInstance().CoreTiming().ScheduleEvent(0, request_event, {}); + const u64 delay = kernel.IsMulticore() ? 0U : 20000U; + Core::System::GetInstance().CoreTiming().ScheduleEvent(delay, request_event, {}); return result; } From 19165cd859dcbb1f7d5e2c74c831e5196c2d1c41 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Mon, 30 Mar 2020 21:50:05 -0400 Subject: [PATCH 204/252] HLE_IPC: Correct HLE Event behavior on timeout. --- src/core/hle/kernel/hle_ipc.cpp | 1 + src/core/hle/kernel/svc.cpp | 10 +++++++++- src/core/hle/kernel/thread.h | 9 +++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index e74d91670..9277b5d08 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -70,6 +70,7 @@ std::shared_ptr HLERequestContext::SleepClientThread( }); const auto readable_event{writable_event->GetReadableEvent()}; writable_event->Clear(); + thread->SetHLESyncObject(readable_event.get()); thread->SetStatus(ThreadStatus::WaitHLEEvent); thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT); readable_event->AddWaitingThread(thread); diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index c47fa9167..37e893c84 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -333,13 +333,21 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) { thread->SetStatus(ThreadStatus::WaitIPC); session->SendSyncRequest(SharedFrom(thread), system.Memory()); } + if (thread->HasHLECallback()) { Handle event_handle = thread->GetHLETimeEvent(); if (event_handle != InvalidHandle) { auto& time_manager = system.Kernel().TimeManager(); time_manager.UnscheduleTimeEvent(event_handle); } - thread->InvokeHLECallback(SharedFrom(thread)); + + { + SchedulerLock lock(system.Kernel()); + auto* sync_object = thread->GetHLESyncObject(); + sync_object->RemoveWaitingThread(SharedFrom(thread)); + + thread->InvokeHLECallback(SharedFrom(thread)); + } } return thread->GetSignalingResult(); diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index f651d7822..61963148d 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -489,10 +489,18 @@ public: hle_time_event = time_event; } + void SetHLESyncObject(SynchronizationObject* object) { + hle_object = object; + } + Handle GetHLETimeEvent() const { return hle_time_event; } + SynchronizationObject* GetHLESyncObject() const { + return hle_object; + } + void InvalidateWakeupCallback() { SetWakeupCallback(nullptr); } @@ -698,6 +706,7 @@ private: /// Callback for HLE Events HLECallback hle_callback; Handle hle_time_event; + SynchronizationObject* hle_object; Scheduler* scheduler = nullptr; From 54e304fe2a38984ea27a7f2240c41a85931d7f6b Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Tue, 31 Mar 2020 13:52:07 -0400 Subject: [PATCH 205/252] Bootmanager/CPU_Manager: Correct shader caches and sync GPU on OpenGL. --- src/core/cpu_manager.cpp | 15 +++++++++------ src/yuzu/bootmanager.cpp | 4 ++++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index d604aa446..c0974ee38 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp @@ -22,13 +22,7 @@ CpuManager::CpuManager(System& system) : system{system} {} CpuManager::~CpuManager() = default; void CpuManager::ThreadStart(CpuManager& cpu_manager, std::size_t core) { - if (!cpu_manager.is_async_gpu && !cpu_manager.is_multicore) { - cpu_manager.render_window->MakeCurrent(); - } cpu_manager.RunThread(core); - if (!cpu_manager.is_async_gpu && !cpu_manager.is_multicore) { - cpu_manager.render_window->DoneCurrent(); - } } void CpuManager::SetRenderWindow(Core::Frontend::EmuWindow& render_window) { @@ -353,10 +347,16 @@ void CpuManager::RunThread(std::size_t core) { data.host_context = Common::Fiber::ThreadToFiber(); data.is_running = false; data.initialized = true; + const bool sc_sync = !is_async_gpu && !is_multicore; + bool sc_sync_first_use = sc_sync; /// Running while (running_mode) { data.is_running = false; data.enter_barrier->Wait(); + if (sc_sync_first_use) { + render_window->MakeCurrent(); + sc_sync_first_use = false; + } auto& scheduler = system.Kernel().CurrentScheduler(); Kernel::Thread* current_thread = scheduler.GetCurrentThread(); data.is_running = true; @@ -366,6 +366,9 @@ void CpuManager::RunThread(std::size_t core) { data.exit_barrier->Wait(); data.is_paused = false; } + if (sc_sync) { + render_window->DoneCurrent(); + } /// Time to cleanup data.host_context->Exit(); data.enter_barrier.reset(); diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 6fad01d50..6aa161e99 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -56,6 +56,8 @@ void EmuThread::run() { Core::System::GetInstance().RegisterHostThread(); + context.MakeCurrent(); + Core::System::GetInstance().Renderer().Rasterizer().LoadDiskResources( stop_run, [this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) { emit LoadProgress(stage, value, total); @@ -63,6 +65,8 @@ void EmuThread::run() { emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); + context.DoneCurrent(); + // Holds whether the cpu was running during the last iteration, // so that the DebugModeLeft signal can be emitted before the // next execution step From c8bf47dcfbd43f3e7835d2e45b4704e056d8e9ee Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Tue, 31 Mar 2020 15:12:41 -0400 Subject: [PATCH 206/252] Kernel/svcBreak: Implement CacheInvalidation for Singlecore and correct svcBreak. --- src/core/hle/kernel/kernel.cpp | 12 +++++++++++- src/core/hle/kernel/svc.cpp | 4 ++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 739205eca..1f230fc4a 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -545,7 +545,17 @@ const Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() const { } void KernelCore::InvalidateAllInstructionCaches() { - //TODO: Reimplement, this + if (!IsMulticore()) { + auto& threads = GlobalScheduler().GetThreadList(); + for (auto& thread : threads) { + if (!thread->IsHLEThread()) { + auto& arm_interface = thread->ArmInterface(); + arm_interface.ClearInstructionCache(); + } + } + } else { + UNIMPLEMENTED_MSG("Cache Invalidation unimplemented for multicore"); + } } void KernelCore::PrepareReschedule(std::size_t id) { diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 37e893c84..dbd35580e 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -622,6 +622,7 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) { info2, has_dumped_buffer ? std::make_optional(debug_buffer) : std::nullopt); if (!break_reason.signal_debugger) { + SchedulerLock lock(system.Kernel()); LOG_CRITICAL( Debug_Emulated, "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", @@ -633,9 +634,8 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) { const auto thread_processor_id = current_thread->GetProcessorID(); system.ArmInterface(static_cast(thread_processor_id)).LogBacktrace(); - system.Kernel().CurrentProcess()->PrepareForTermination(); - // Kill the current thread + system.Kernel().ExceptionalExit(); current_thread->Stop(); } } From 48fa3b7a0f2054a836b0a8061e6b082c246b5ae0 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Wed, 1 Apr 2020 17:28:49 -0400 Subject: [PATCH 207/252] General: Cleanup legacy code. --- src/core/CMakeLists.txt | 2 - src/core/arm/dynarmic/arm_dynarmic_32.cpp | 1 - src/core/arm/dynarmic/arm_dynarmic_64.cpp | 1 - src/core/core_manager.cpp | 51 ----- src/core/core_manager.h | 63 ------ src/core/gdbstub/gdbstub.cpp | 1 - src/core/hle/kernel/client_port.cpp | 2 +- src/core/hle/kernel/kernel.cpp | 81 +------ src/core/hle/kernel/kernel.h | 3 - src/core/hle/kernel/svc.cpp | 3 +- .../hle/kernel/synchronization_object.cpp | 64 ------ src/core/hle/kernel/synchronization_object.h | 15 -- src/core/hle/kernel/thread.cpp | 34 --- src/core/hle/kernel/thread.h | 56 +---- src/core/hle/service/sm/sm.cpp | 2 +- src/core/host_timing.cpp | 206 ------------------ src/core/host_timing.h | 160 -------------- src/tests/core/core_timing.cpp | 1 + src/yuzu/debugger/wait_tree.cpp | 2 +- 19 files changed, 8 insertions(+), 740 deletions(-) delete mode 100644 src/core/core_manager.cpp delete mode 100644 src/core/core_manager.h delete mode 100644 src/core/host_timing.cpp delete mode 100644 src/core/host_timing.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 552094ddb..653b7620b 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -17,8 +17,6 @@ add_library(core STATIC constants.h core.cpp core.h - core_manager.cpp - core_manager.h core_timing.cpp core_timing.h core_timing_util.cpp diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index c4aeedef9..c8adf2866 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -12,7 +12,6 @@ #include "core/arm/dynarmic/arm_dynarmic_64.h" #include "core/arm/dynarmic/arm_dynarmic_cp15.h" #include "core/core.h" -#include "core/core_manager.h" #include "core/core_timing.h" #include "core/hle/kernel/svc.h" #include "core/memory.h" diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index a518733b6..14b394368 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -11,7 +11,6 @@ #include "core/arm/cpu_interrupt_handler.h" #include "core/arm/dynarmic/arm_dynarmic_64.h" #include "core/core.h" -#include "core/core_manager.h" #include "core/core_timing.h" #include "core/core_timing_util.h" #include "core/gdbstub/gdbstub.h" diff --git a/src/core/core_manager.cpp b/src/core/core_manager.cpp deleted file mode 100644 index 82d7acb40..000000000 --- a/src/core/core_manager.cpp +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include -#include - -#include "common/logging/log.h" -#include "core/arm/exclusive_monitor.h" -#include "core/arm/unicorn/arm_unicorn.h" -#include "core/core.h" -#include "core/core_manager.h" -#include "core/core_timing.h" -#include "core/hle/kernel/kernel.h" -#include "core/hle/kernel/physical_core.h" -#include "core/hle/kernel/scheduler.h" -#include "core/hle/kernel/thread.h" -#include "core/hle/lock.h" -#include "core/settings.h" - -namespace Core { - -CoreManager::CoreManager(System& system, std::size_t core_index) - : global_scheduler{system.GlobalScheduler()}, physical_core{system.Kernel().PhysicalCore( - core_index)}, - core_timing{system.CoreTiming()}, core_index{core_index} {} - -CoreManager::~CoreManager() = default; - -void CoreManager::RunLoop(bool tight_loop) { - /// Deprecated -} - -void CoreManager::SingleStep() { - return RunLoop(false); -} - -void CoreManager::PrepareReschedule() { - //physical_core.Stop(); -} - -void CoreManager::Reschedule() { - // Lock the global kernel mutex when we manipulate the HLE state - std::lock_guard lock(HLE::g_hle_lock); - - // global_scheduler.SelectThread(core_index); - - physical_core.Scheduler().TryDoContextSwitch(); -} - -} // namespace Core diff --git a/src/core/core_manager.h b/src/core/core_manager.h deleted file mode 100644 index d525de00a..000000000 --- a/src/core/core_manager.h +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include -#include -#include -#include "common/common_types.h" - -namespace Kernel { -class GlobalScheduler; -class PhysicalCore; -} // namespace Kernel - -namespace Core { -class System; -} - -namespace Core::Timing { -class CoreTiming; -} - -namespace Core::Memory { -class Memory; -} - -namespace Core { - -constexpr unsigned NUM_CPU_CORES{4}; - -class CoreManager { -public: - CoreManager(System& system, std::size_t core_index); - ~CoreManager(); - - void RunLoop(bool tight_loop = true); - - void SingleStep(); - - void PrepareReschedule(); - - bool IsMainCore() const { - return core_index == 0; - } - - std::size_t CoreIndex() const { - return core_index; - } - -private: - void Reschedule(); - - Kernel::GlobalScheduler& global_scheduler; - Kernel::PhysicalCore& physical_core; - Timing::CoreTiming& core_timing; - - std::atomic reschedule_pending = false; - std::size_t core_index; -}; - -} // namespace Core diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index 70c0f8b80..79f22a403 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp @@ -35,7 +35,6 @@ #include "common/swap.h" #include "core/arm/arm_interface.h" #include "core/core.h" -#include "core/core_manager.h" #include "core/gdbstub/gdbstub.h" #include "core/hle/kernel/memory/page_table.h" #include "core/hle/kernel/process.h" diff --git a/src/core/hle/kernel/client_port.cpp b/src/core/hle/kernel/client_port.cpp index 5498fd313..8aff2227a 100644 --- a/src/core/hle/kernel/client_port.cpp +++ b/src/core/hle/kernel/client_port.cpp @@ -34,7 +34,7 @@ ResultVal> ClientPort::Connect() { } // Wake the threads waiting on the ServerPort - server_port->WakeupAllWaitingThreads(); + server_port->Signal(); return MakeResult(std::move(client)); } diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 1f230fc4a..dbb75416d 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -48,72 +48,6 @@ MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70)); namespace Kernel { -/** - * Callback that will wake up the thread it was scheduled for - * @param thread_handle The handle of the thread that's been awoken - * @param cycles_late The number of CPU cycles that have passed since the desired wakeup time - */ -static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] s64 cycles_late) { - UNREACHABLE(); - const auto proper_handle = static_cast(thread_handle); - const auto& system = Core::System::GetInstance(); - - // Lock the global kernel mutex when we enter the kernel HLE. - std::lock_guard lock{HLE::g_hle_lock}; - - std::shared_ptr thread = - system.Kernel().RetrieveThreadFromGlobalHandleTable(proper_handle); - if (thread == nullptr) { - LOG_CRITICAL(Kernel, "Callback fired for invalid thread {:08X}", proper_handle); - return; - } - - bool resume = true; - - if (thread->GetStatus() == ThreadStatus::WaitSynch || - thread->GetStatus() == ThreadStatus::WaitHLEEvent) { - // Remove the thread from each of its waiting objects' waitlists - for (const auto& object : thread->GetSynchronizationObjects()) { - object->RemoveWaitingThread(thread); - } - thread->ClearSynchronizationObjects(); - - // Invoke the wakeup callback before clearing the wait objects - if (thread->HasWakeupCallback()) { - resume = thread->InvokeWakeupCallback(ThreadWakeupReason::Timeout, thread, nullptr, 0); - } - } else if (thread->GetStatus() == ThreadStatus::WaitMutex || - thread->GetStatus() == ThreadStatus::WaitCondVar) { - thread->SetMutexWaitAddress(0); - thread->SetWaitHandle(0); - if (thread->GetStatus() == ThreadStatus::WaitCondVar) { - thread->GetOwnerProcess()->RemoveConditionVariableThread(thread); - thread->SetCondVarWaitAddress(0); - } - - auto* const lock_owner = thread->GetLockOwner(); - // Threads waking up by timeout from WaitProcessWideKey do not perform priority inheritance - // and don't have a lock owner unless SignalProcessWideKey was called first and the thread - // wasn't awakened due to the mutex already being acquired. - if (lock_owner != nullptr) { - lock_owner->RemoveMutexWaiter(thread); - } - } - - if (thread->GetStatus() == ThreadStatus::WaitArb) { - auto& address_arbiter = thread->GetOwnerProcess()->GetAddressArbiter(); - address_arbiter.HandleWakeupThread(thread); - } - - if (resume) { - if (thread->GetStatus() == ThreadStatus::WaitCondVar || - thread->GetStatus() == ThreadStatus::WaitArb) { - thread->SetWaitSynchronizationResult(RESULT_TIMEOUT); - } - thread->ResumeFromWait(); - } -} - struct KernelCore::Impl { explicit Impl(Core::System& system, KernelCore& kernel) : global_scheduler{kernel}, synchronization{system}, time_manager{system}, system{system} {} @@ -129,7 +63,6 @@ struct KernelCore::Impl { InitializePhysicalCores(); InitializeSystemResourceLimit(kernel); InitializeMemoryLayout(); - InitializeThreads(); InitializePreemption(kernel); InitializeSchedulers(); InitializeSuspendThreads(); @@ -161,7 +94,6 @@ struct KernelCore::Impl { system_resource_limit = nullptr; global_handle_table.Clear(); - thread_wakeup_event_type = nullptr; preemption_event = nullptr; global_scheduler.Shutdown(); @@ -210,11 +142,6 @@ struct KernelCore::Impl { } } - void InitializeThreads() { - thread_wakeup_event_type = - Core::Timing::CreateEvent("ThreadWakeupCallback", ThreadWakeupCallback); - } - void InitializePreemption(KernelCore& kernel) { preemption_event = Core::Timing::CreateEvent( "PreemptionCallback", [this, &kernel](u64 userdata, s64 cycles_late) { @@ -376,7 +303,6 @@ struct KernelCore::Impl { std::shared_ptr system_resource_limit; - std::shared_ptr thread_wakeup_event_type; std::shared_ptr preemption_event; // This is the kernel's handle table or supervisor handle table which @@ -516,7 +442,8 @@ std::array& KernelCore return impl->interrupts; } -const std::array& KernelCore::Interrupts() const { +const std::array& KernelCore::Interrupts() + const { return impl->interrupts; } @@ -595,10 +522,6 @@ u64 KernelCore::CreateNewUserProcessID() { return impl->next_user_process_id++; } -const std::shared_ptr& KernelCore::ThreadWakeupCallbackEventType() const { - return impl->thread_wakeup_event_type; -} - Kernel::HandleTable& KernelCore::GlobalHandleTable() { return impl->global_handle_table; } diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 846056b85..49bd47e89 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -241,9 +241,6 @@ private: /// Creates a new thread ID, incrementing the internal thread ID counter. u64 CreateNewThreadID(); - /// Retrieves the event type used for thread wakeup callbacks. - const std::shared_ptr& ThreadWakeupCallbackEventType() const; - /// Provides a reference to the global handle table. Kernel::HandleTable& GlobalHandleTable(); diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index dbd35580e..781032cd1 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -16,7 +16,6 @@ #include "common/string_util.h" #include "core/arm/exclusive_monitor.h" #include "core/core.h" -#include "core/core_manager.h" #include "core/core_timing.h" #include "core/core_timing_util.h" #include "core/cpu_manager.h" @@ -1909,7 +1908,7 @@ static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle, return ERR_INVALID_COMBINATION; } - if (core < Core::NUM_CPU_CORES) { + if (core < Core::Hardware::NUM_CPU_CORES) { if ((affinity_mask & (1ULL << core)) == 0) { LOG_ERROR(Kernel_SVC, "Core is not enabled for the current mask, core={}, mask={:016X}", core, diff --git a/src/core/hle/kernel/synchronization_object.cpp b/src/core/hle/kernel/synchronization_object.cpp index be9e09106..ba4d39157 100644 --- a/src/core/hle/kernel/synchronization_object.cpp +++ b/src/core/hle/kernel/synchronization_object.cpp @@ -38,70 +38,6 @@ void SynchronizationObject::RemoveWaitingThread(std::shared_ptr thread) waiting_threads.erase(itr); } -std::shared_ptr SynchronizationObject::GetHighestPriorityReadyThread() const { - Thread* candidate = nullptr; - u32 candidate_priority = THREADPRIO_LOWEST + 1; - - for (const auto& thread : waiting_threads) { - const ThreadStatus thread_status = thread->GetStatus(); - - // The list of waiting threads must not contain threads that are not waiting to be awakened. - ASSERT_MSG(thread_status == ThreadStatus::WaitSynch || - thread_status == ThreadStatus::WaitHLEEvent, - "Inconsistent thread statuses in waiting_threads"); - - if (thread->GetPriority() >= candidate_priority) - continue; - - if (ShouldWait(thread.get())) - continue; - - candidate = thread.get(); - candidate_priority = thread->GetPriority(); - } - - return SharedFrom(candidate); -} - -void SynchronizationObject::WakeupWaitingThread(std::shared_ptr thread) { - ASSERT(!ShouldWait(thread.get())); - - if (!thread) { - return; - } - - if (thread->IsSleepingOnWait()) { - for (const auto& object : thread->GetSynchronizationObjects()) { - ASSERT(!object->ShouldWait(thread.get())); - object->Acquire(thread.get()); - } - } else { - Acquire(thread.get()); - } - - const std::size_t index = thread->GetSynchronizationObjectIndex(SharedFrom(this)); - - thread->ClearSynchronizationObjects(); - - thread->CancelWakeupTimer(); - - bool resume = true; - if (thread->HasWakeupCallback()) { - resume = thread->InvokeWakeupCallback(ThreadWakeupReason::Signal, thread, SharedFrom(this), - index); - } - if (resume) { - thread->ResumeFromWait(); - kernel.PrepareReschedule(thread->GetProcessorID()); - } -} - -void SynchronizationObject::WakeupAllWaitingThreads() { - while (auto thread = GetHighestPriorityReadyThread()) { - WakeupWaitingThread(thread); - } -} - void SynchronizationObject::ClearWaitingThreads() { waiting_threads.clear(); } diff --git a/src/core/hle/kernel/synchronization_object.h b/src/core/hle/kernel/synchronization_object.h index a35544ac1..f89b24204 100644 --- a/src/core/hle/kernel/synchronization_object.h +++ b/src/core/hle/kernel/synchronization_object.h @@ -50,21 +50,6 @@ public: */ void RemoveWaitingThread(std::shared_ptr thread); - /** - * Wake up all threads waiting on this object that can be awoken, in priority order, - * and set the synchronization result and output of the thread. - */ - void /* deprecated */ WakeupAllWaitingThreads(); - - /** - * Wakes up a single thread waiting on this object. - * @param thread Thread that is waiting on this object to wakeup. - */ - void WakeupWaitingThread(std::shared_ptr thread); - - /// Obtains the highest priority thread that is ready to run from this object's waiting list. - std::shared_ptr /* deprecated */ GetHighestPriorityReadyThread() const; - /// Get a const reference to the waiting threads list for debug use const std::vector>& GetWaitingThreads() const; diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index d88039a16..fba2a9c85 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -56,9 +56,6 @@ Thread::~Thread() = default; void Thread::Stop() { { SchedulerLock lock(kernel); - // Cancel any outstanding wakeup events for this thread - Core::System::GetInstance().CoreTiming().UnscheduleEvent( - kernel.ThreadWakeupCallbackEventType(), global_handle); SetStatus(ThreadStatus::Dead); Signal(); kernel.GlobalHandleTable().Close(global_handle); @@ -75,22 +72,6 @@ void Thread::Stop() { global_handle = 0; } -void Thread::WakeAfterDelay(s64 nanoseconds) { - // Don't schedule a wakeup if the thread wants to wait forever - if (nanoseconds == -1) - return; - - // This function might be called from any thread so we have to be cautious and use the - // thread-safe version of ScheduleEvent. - Core::System::GetInstance().CoreTiming().ScheduleEvent( - nanoseconds, kernel.ThreadWakeupCallbackEventType(), global_handle); -} - -void Thread::CancelWakeupTimer() { - Core::System::GetInstance().CoreTiming().UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(), - global_handle); -} - void Thread::ResumeFromWait() { SchedulerLock lock(kernel); switch (status) { @@ -284,14 +265,6 @@ void Thread::SetPriority(u32 priority) { UpdatePriority(); } -void Thread::SetWaitSynchronizationResult(ResultCode result) { - UNREACHABLE(); -} - -void Thread::SetWaitSynchronizationOutput(s32 output) { - UNREACHABLE(); -} - void Thread::SetSynchronizationResults(SynchronizationObject* object, ResultCode result) { signaling_object = object; signaling_result = result; @@ -425,13 +398,6 @@ bool Thread::AllSynchronizationObjectsReady() const { }); } -bool Thread::InvokeWakeupCallback(ThreadWakeupReason reason, std::shared_ptr thread, - std::shared_ptr object, - std::size_t index) { - ASSERT(wakeup_callback); - return wakeup_callback(reason, std::move(thread), std::move(object), index); -} - bool Thread::InvokeHLECallback(std::shared_ptr thread) { ASSERT(hle_callback); return hle_callback(std::move(thread)); diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 61963148d..3ae0df6ef 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -128,9 +128,6 @@ public: using ThreadSynchronizationObjects = std::vector>; - using WakeupCallback = - std::function thread, - std::shared_ptr object, std::size_t index)>; using HLECallback = std::function thread)>; /** @@ -235,7 +232,7 @@ public: } /// Resumes a thread from waiting - void /* deprecated */ ResumeFromWait(); + void ResumeFromWait(); void OnWakeUp(); @@ -249,27 +246,6 @@ public: /// void CancelWait(); - /** - * Schedules an event to wake up the specified thread after the specified delay - * @param nanoseconds The time this thread will be allowed to sleep for - */ - void /* deprecated */ WakeAfterDelay(s64 nanoseconds); - - /// Cancel any outstanding wakeup events for this thread - void /* deprecated */ CancelWakeupTimer(); - - /** - * Sets the result after the thread awakens (from svcWaitSynchronization) - * @param result Value to set to the returned result - */ - void /*deprecated*/ SetWaitSynchronizationResult(ResultCode result); - - /** - * Sets the output parameter value after the thread awakens (from svcWaitSynchronization) - * @param output Value to set to the output parameter - */ - void /*deprecated*/ SetWaitSynchronizationOutput(s32 output); - void SetSynchronizationResults(SynchronizationObject* object, ResultCode result); Core::ARM_Interface& ArmInterface(); @@ -330,11 +306,6 @@ public: */ VAddr GetCommandBufferAddress() const; - /// Returns whether this thread is waiting on objects from a WaitSynchronization call. - bool IsSleepingOnWait() const { - return status == ThreadStatus::WaitSynch; - } - ThreadContext32& GetContext32() { return context_32; } @@ -469,18 +440,10 @@ public: arb_wait_address = address; } - bool HasWakeupCallback() const { - return wakeup_callback != nullptr; - } - bool HasHLECallback() const { return hle_callback != nullptr; } - void SetWakeupCallback(WakeupCallback callback) { - wakeup_callback = std::move(callback); - } - void SetHLECallback(HLECallback callback) { hle_callback = std::move(callback); } @@ -501,22 +464,10 @@ public: return hle_object; } - void InvalidateWakeupCallback() { - SetWakeupCallback(nullptr); - } - void InvalidateHLECallback() { SetHLECallback(nullptr); } - /** - * Invokes the thread's wakeup callback. - * - * @pre A valid wakeup callback has been set. Violating this precondition - * will cause an assertion to trigger. - */ - bool InvokeWakeupCallback(ThreadWakeupReason reason, std::shared_ptr thread, - std::shared_ptr object, std::size_t index); bool InvokeHLECallback(std::shared_ptr thread); u32 GetIdealCore() const { @@ -698,11 +649,6 @@ private: /// Handle used as userdata to reference this object when inserting into the CoreTiming queue. Handle global_handle = 0; - /// Callback that will be invoked when the thread is resumed from a waiting state. If the thread - /// was waiting via WaitSynchronization then the object will be the last object that became - /// available. In case of a timeout, the object will be nullptr. DEPRECATED - WakeupCallback wakeup_callback; - /// Callback for HLE Events HLECallback hle_callback; Handle hle_time_event; diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index 6ada13be4..d872de16c 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp @@ -142,7 +142,7 @@ void SM::GetService(Kernel::HLERequestContext& ctx) { } // Wake the threads waiting on the ServerPort - server_port->WakeupAllWaitingThreads(); + server_port->Signal(); LOG_DEBUG(Service_SM, "called service={} -> session={}", name, client->GetObjectId()); IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; diff --git a/src/core/host_timing.cpp b/src/core/host_timing.cpp deleted file mode 100644 index 2f40de1a1..000000000 --- a/src/core/host_timing.cpp +++ /dev/null @@ -1,206 +0,0 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "core/host_timing.h" - -#include -#include -#include -#include - -#include "common/assert.h" -#include "core/core_timing_util.h" - -namespace Core::HostTiming { - -std::shared_ptr CreateEvent(std::string name, TimedCallback&& callback) { - return std::make_shared(std::move(callback), std::move(name)); -} - -struct CoreTiming::Event { - u64 time; - u64 fifo_order; - u64 userdata; - std::weak_ptr type; - - // Sort by time, unless the times are the same, in which case sort by - // the order added to the queue - friend bool operator>(const Event& left, const Event& right) { - return std::tie(left.time, left.fifo_order) > std::tie(right.time, right.fifo_order); - } - - friend bool operator<(const Event& left, const Event& right) { - return std::tie(left.time, left.fifo_order) < std::tie(right.time, right.fifo_order); - } -}; - -CoreTiming::CoreTiming() { - clock = - Common::CreateBestMatchingClock(Core::Hardware::BASE_CLOCK_RATE, Core::Hardware::CNTFREQ); -} - -CoreTiming::~CoreTiming() = default; - -void CoreTiming::ThreadEntry(CoreTiming& instance) { - instance.ThreadLoop(); -} - -void CoreTiming::Initialize() { - event_fifo_id = 0; - const auto empty_timed_callback = [](u64, s64) {}; - ev_lost = CreateEvent("_lost_event", empty_timed_callback); - timer_thread = std::make_unique(ThreadEntry, std::ref(*this)); -} - -void CoreTiming::Shutdown() { - paused = true; - shutting_down = true; - event.Set(); - timer_thread->join(); - ClearPendingEvents(); - timer_thread.reset(); - has_started = false; -} - -void CoreTiming::Pause(bool is_paused) { - paused = is_paused; -} - -void CoreTiming::SyncPause(bool is_paused) { - if (is_paused == paused && paused_set == paused) { - return; - } - Pause(is_paused); - event.Set(); - while (paused_set != is_paused) - ; -} - -bool CoreTiming::IsRunning() const { - return !paused_set; -} - -bool CoreTiming::HasPendingEvents() const { - return !(wait_set && event_queue.empty()); -} - -void CoreTiming::ScheduleEvent(s64 ns_into_future, const std::shared_ptr& event_type, - u64 userdata) { - basic_lock.lock(); - const u64 timeout = static_cast(GetGlobalTimeNs().count() + ns_into_future); - - event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type}); - - std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); - basic_lock.unlock(); - event.Set(); -} - -void CoreTiming::UnscheduleEvent(const std::shared_ptr& event_type, u64 userdata) { - basic_lock.lock(); - const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { - return e.type.lock().get() == event_type.get() && e.userdata == userdata; - }); - - // Removing random items breaks the invariant so we have to re-establish it. - if (itr != event_queue.end()) { - event_queue.erase(itr, event_queue.end()); - std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>()); - } - basic_lock.unlock(); -} - -void CoreTiming::AddTicks(std::size_t core_index, u64 ticks) { - ticks_count[core_index] += ticks; -} - -void CoreTiming::ResetTicks(std::size_t core_index) { - ticks_count[core_index] = 0; -} - -u64 CoreTiming::GetCPUTicks() const { - return clock->GetCPUCycles(); -} - -u64 CoreTiming::GetClockTicks() const { - return clock->GetClockCycles(); -} - -void CoreTiming::ClearPendingEvents() { - event_queue.clear(); -} - -void CoreTiming::RemoveEvent(const std::shared_ptr& event_type) { - basic_lock.lock(); - - const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { - return e.type.lock().get() == event_type.get(); - }); - - // Removing random items breaks the invariant so we have to re-establish it. - if (itr != event_queue.end()) { - event_queue.erase(itr, event_queue.end()); - std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>()); - } - basic_lock.unlock(); -} - -std::optional CoreTiming::Advance() { - advance_lock.lock(); - basic_lock.lock(); - global_timer = GetGlobalTimeNs().count(); - - while (!event_queue.empty() && event_queue.front().time <= global_timer) { - Event evt = std::move(event_queue.front()); - std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>()); - event_queue.pop_back(); - basic_lock.unlock(); - - if (auto event_type{evt.type.lock()}) { - event_type->callback(evt.userdata, global_timer - evt.time); - } - - basic_lock.lock(); - } - - if (!event_queue.empty()) { - const u64 next_time = event_queue.front().time - global_timer; - basic_lock.unlock(); - advance_lock.unlock(); - return next_time; - } else { - basic_lock.unlock(); - advance_lock.unlock(); - return std::nullopt; - } -} - -void CoreTiming::ThreadLoop() { - has_started = true; - while (!shutting_down) { - while (!paused) { - paused_set = false; - const auto next_time = Advance(); - if (next_time) { - std::chrono::nanoseconds next_time_ns = std::chrono::nanoseconds(*next_time); - event.WaitFor(next_time_ns); - } else { - wait_set = true; - event.Wait(); - } - wait_set = false; - } - paused_set = true; - } -} - -std::chrono::nanoseconds CoreTiming::GetGlobalTimeNs() const { - return clock->GetTimeNS(); -} - -std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const { - return clock->GetTimeUS(); -} - -} // namespace Core::HostTiming diff --git a/src/core/host_timing.h b/src/core/host_timing.h deleted file mode 100644 index be6b68d7c..000000000 --- a/src/core/host_timing.h +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "common/common_types.h" -#include "common/spin_lock.h" -#include "common/thread.h" -#include "common/threadsafe_queue.h" -#include "common/wall_clock.h" -#include "core/hardware_properties.h" - -namespace Core::HostTiming { - -/// A callback that may be scheduled for a particular core timing event. -using TimedCallback = std::function; - -/// Contains the characteristics of a particular event. -struct EventType { - EventType(TimedCallback&& callback, std::string&& name) - : callback{std::move(callback)}, name{std::move(name)} {} - - /// The event's callback function. - TimedCallback callback; - /// A pointer to the name of the event. - const std::string name; -}; - -/** - * This is a system to schedule events into the emulated machine's future. Time is measured - * in main CPU clock cycles. - * - * To schedule an event, you first have to register its type. This is where you pass in the - * callback. You then schedule events using the type id you get back. - * - * The int cyclesLate that the callbacks get is how many cycles late it was. - * So to schedule a new event on a regular basis: - * inside callback: - * ScheduleEvent(periodInCycles - cyclesLate, callback, "whatever") - */ -class CoreTiming { -public: - CoreTiming(); - ~CoreTiming(); - - CoreTiming(const CoreTiming&) = delete; - CoreTiming(CoreTiming&&) = delete; - - CoreTiming& operator=(const CoreTiming&) = delete; - CoreTiming& operator=(CoreTiming&&) = delete; - - /// CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is - /// required to end slice - 1 and start slice 0 before the first cycle of code is executed. - void Initialize(); - - /// Tears down all timing related functionality. - void Shutdown(); - - /// Pauses/Unpauses the execution of the timer thread. - void Pause(bool is_paused); - - /// Pauses/Unpauses the execution of the timer thread and waits until paused. - void SyncPause(bool is_paused); - - /// Checks if core timing is running. - bool IsRunning() const; - - /// Checks if the timer thread has started. - bool HasStarted() const { - return has_started; - } - - /// Checks if there are any pending time events. - bool HasPendingEvents() const; - - /// Schedules an event in core timing - void ScheduleEvent(s64 ns_into_future, const std::shared_ptr& event_type, - u64 userdata = 0); - - void UnscheduleEvent(const std::shared_ptr& event_type, u64 userdata); - - /// We only permit one event of each type in the queue at a time. - void RemoveEvent(const std::shared_ptr& event_type); - - void AddTicks(std::size_t core_index, u64 ticks); - - void ResetTicks(std::size_t core_index); - - /// Returns current time in emulated CPU cycles - u64 GetCPUTicks() const; - - /// Returns current time in emulated in Clock cycles - u64 GetClockTicks() const; - - /// Returns current time in microseconds. - std::chrono::microseconds GetGlobalTimeUs() const; - - /// Returns current time in nanoseconds. - std::chrono::nanoseconds GetGlobalTimeNs() const; - - /// Checks for events manually and returns time in nanoseconds for next event, threadsafe. - std::optional Advance(); - -private: - struct Event; - - /// Clear all pending events. This should ONLY be done on exit. - void ClearPendingEvents(); - - static void ThreadEntry(CoreTiming& instance); - void ThreadLoop(); - - std::unique_ptr clock; - - u64 global_timer = 0; - - std::chrono::nanoseconds start_point; - - // The queue is a min-heap using std::make_heap/push_heap/pop_heap. - // We don't use std::priority_queue because we need to be able to serialize, unserialize and - // erase arbitrary events (RemoveEvent()) regardless of the queue order. These aren't - // accomodated by the standard adaptor class. - std::vector event_queue; - u64 event_fifo_id = 0; - - std::shared_ptr ev_lost; - Common::Event event{}; - Common::SpinLock basic_lock{}; - Common::SpinLock advance_lock{}; - std::unique_ptr timer_thread; - std::atomic paused{}; - std::atomic paused_set{}; - std::atomic wait_set{}; - std::atomic shutting_down{}; - std::atomic has_started{}; - - std::array, Core::Hardware::NUM_CPU_CORES> ticks_count{}; -}; - -/// Creates a core timing event with the given name and callback. -/// -/// @param name The name of the core timing event to create. -/// @param callback The callback to execute for the event. -/// -/// @returns An EventType instance representing the created event. -/// -std::shared_ptr CreateEvent(std::string name, TimedCallback&& callback); - -} // namespace Core::HostTiming diff --git a/src/tests/core/core_timing.cpp b/src/tests/core/core_timing.cpp index 795f3da09..21a5840fb 100644 --- a/src/tests/core/core_timing.cpp +++ b/src/tests/core/core_timing.cpp @@ -39,6 +39,7 @@ u64 callbacks_done = 0; struct ScopeInit final { ScopeInit() { + core_timing.SetMulticore(true); core_timing.Initialize([]() {}); } ~ScopeInit() { diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp index d2dbb259c..0226ae2e2 100644 --- a/src/yuzu/debugger/wait_tree.cpp +++ b/src/yuzu/debugger/wait_tree.cpp @@ -340,7 +340,7 @@ std::vector> WaitTreeThread::GetChildren() const { if (thread.GetStatus() == Kernel::ThreadStatus::WaitSynch) { list.push_back(std::make_unique(thread.GetSynchronizationObjects(), - thread.IsSleepingOnWait())); + thread.IsWaitingSync())); } list.push_back(std::make_unique(thread)); From bece52cd810197e905416fa46a4ed484caa41db5 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Thu, 2 Apr 2020 13:24:39 -0400 Subject: [PATCH 208/252] SingleCore: Correct ticks reset to be on preemption. --- src/core/cpu_manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index c0974ee38..b7c2a7832 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp @@ -227,7 +227,6 @@ void CpuManager::SingleCoreRunGuestLoop() { auto& arm_interface = thread->ArmInterface(); system.EnterDynarmicProfile(); if (!physical_core->IsInterrupted()) { - system.CoreTiming().ResetTicks(); arm_interface.Run(); physical_core = &kernel.CurrentPhysicalCore(); } @@ -285,6 +284,7 @@ void CpuManager::PreemptSingleCore(bool from_running_enviroment) { current_thread->SetPhantomMode(false); } current_core.store((current_core + 1) % Core::Hardware::NUM_CPU_CORES); + system.CoreTiming().ResetTicks(); scheduler.Unload(); auto& next_scheduler = system.Kernel().Scheduler(current_core); Common::Fiber::YieldTo(current_thread->GetHostContext(), next_scheduler.ControlContext()); From bfb5244cf8c600e6444d8590c8850ec31779bc6c Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Thu, 2 Apr 2020 13:27:08 -0400 Subject: [PATCH 209/252] CoreTiming/CycleTimer: Correct Idling. --- src/core/core_timing.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 12e9e60a4..b02119494 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -136,8 +136,11 @@ void CoreTiming::AddTicks(u64 ticks) { void CoreTiming::Idle() { if (!event_queue.empty()) { - u64 next_event_time = event_queue.front().time; - ticks = nsToCycles(std::chrono::nanoseconds(next_event_time)) + 10U; + const u64 next_event_time = event_queue.front().time; + const u64 next_ticks = nsToCycles(std::chrono::nanoseconds(next_event_time)) + 10U; + if (next_ticks > ticks) { + ticks = next_ticks; + } return; } ticks += 1000U; From ad92865497f83fe4c19cd9ab78cce9da1a8c3a6c Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Fri, 3 Apr 2020 11:58:43 -0400 Subject: [PATCH 210/252] General: Correct rebase, sync gpu and context management. --- src/core/core.cpp | 3 +-- src/core/cpu_manager.cpp | 11 ++--------- src/core/cpu_manager.h | 7 ------- src/video_core/gpu.h | 6 ++++++ src/video_core/gpu_asynch.cpp | 9 ++++++++- src/video_core/gpu_asynch.h | 2 ++ src/video_core/gpu_synch.cpp | 8 +++++++- src/video_core/gpu_synch.h | 2 ++ src/yuzu/bootmanager.cpp | 29 +++++++++++++++++------------ 9 files changed, 45 insertions(+), 32 deletions(-) diff --git a/src/core/core.cpp b/src/core/core.cpp index 40eea297e..3393c33eb 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -151,7 +151,6 @@ struct System::Impl { cpu_manager.SetMulticore(is_multicore); cpu_manager.SetAsyncGpu(is_async_gpu); core_timing.SetMulticore(is_multicore); - cpu_manager.SetRenderWindow(emu_window); core_timing.Initialize([&system]() { system.RegisterHostThread(); }); kernel.Initialize(); @@ -435,7 +434,7 @@ bool System::IsPoweredOn() const { } void System::PrepareReschedule() { - //impl->CurrentPhysicalCore().Stop(); + // impl->CurrentPhysicalCore().Stop(); } void System::PrepareReschedule(const u32 core_index) { diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index b7c2a7832..63c578852 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp @@ -9,12 +9,12 @@ #include "core/core.h" #include "core/core_timing.h" #include "core/cpu_manager.h" -#include "core/frontend/emu_window.h" #include "core/gdbstub/gdbstub.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/physical_core.h" #include "core/hle/kernel/scheduler.h" #include "core/hle/kernel/thread.h" +#include "video_core/gpu.h" namespace Core { @@ -25,10 +25,6 @@ void CpuManager::ThreadStart(CpuManager& cpu_manager, std::size_t core) { cpu_manager.RunThread(core); } -void CpuManager::SetRenderWindow(Core::Frontend::EmuWindow& render_window) { - this->render_window = &render_window; -} - void CpuManager::Initialize() { running_mode = true; if (is_multicore) { @@ -354,7 +350,7 @@ void CpuManager::RunThread(std::size_t core) { data.is_running = false; data.enter_barrier->Wait(); if (sc_sync_first_use) { - render_window->MakeCurrent(); + system.GPU().ObtainContext(); sc_sync_first_use = false; } auto& scheduler = system.Kernel().CurrentScheduler(); @@ -366,9 +362,6 @@ void CpuManager::RunThread(std::size_t core) { data.exit_barrier->Wait(); data.is_paused = false; } - if (sc_sync) { - render_window->DoneCurrent(); - } /// Time to cleanup data.host_context->Exit(); data.enter_barrier.reset(); diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h index ae55d6427..35929ed94 100644 --- a/src/core/cpu_manager.h +++ b/src/core/cpu_manager.h @@ -16,10 +16,6 @@ class Event; class Fiber; } // namespace Common -namespace Core::Frontend { -class EmuWindow; -} // namespace Core::Frontend - namespace Core { class System; @@ -61,8 +57,6 @@ public: return current_core.load(); } - void SetRenderWindow(Core::Frontend::EmuWindow& render_window); - private: static void GuestThreadFunction(void* cpu_manager); static void GuestRewindFunction(void* cpu_manager); @@ -106,7 +100,6 @@ private: std::size_t preemption_count{}; std::size_t idle_count{}; static constexpr std::size_t max_cycle_runs = 5; - Core::Frontend::EmuWindow* render_window; System& system; }; diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index a1b4c305c..2c42483bd 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h @@ -284,6 +284,12 @@ public: /// core timing events. virtual void Start() = 0; + /// Obtain the CPU Context + virtual void ObtainContext() = 0; + + /// Release the CPU Context + virtual void ReleaseContext() = 0; + /// Push GPU command entries to be processed virtual void PushGPUEntries(Tegra::CommandList&& entries) = 0; diff --git a/src/video_core/gpu_asynch.cpp b/src/video_core/gpu_asynch.cpp index 53305ab43..7b855f63e 100644 --- a/src/video_core/gpu_asynch.cpp +++ b/src/video_core/gpu_asynch.cpp @@ -19,10 +19,17 @@ GPUAsynch::GPUAsynch(Core::System& system, std::unique_ptrMakeCurrent(); gpu_thread.StartThread(*renderer, *gpu_context, *dma_pusher); } +void GPUAsynch::ObtainContext() { + cpu_context->MakeCurrent(); +} + +void GPUAsynch::ReleaseContext() { + cpu_context->DoneCurrent(); +} + void GPUAsynch::PushGPUEntries(Tegra::CommandList&& entries) { gpu_thread.SubmitList(std::move(entries)); } diff --git a/src/video_core/gpu_asynch.h b/src/video_core/gpu_asynch.h index 517658612..15e9f1d38 100644 --- a/src/video_core/gpu_asynch.h +++ b/src/video_core/gpu_asynch.h @@ -25,6 +25,8 @@ public: ~GPUAsynch() override; void Start() override; + void ObtainContext() override; + void ReleaseContext() override; void PushGPUEntries(Tegra::CommandList&& entries) override; void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; void FlushRegion(VAddr addr, u64 size) override; diff --git a/src/video_core/gpu_synch.cpp b/src/video_core/gpu_synch.cpp index 6f38a672a..aaeb9811d 100644 --- a/src/video_core/gpu_synch.cpp +++ b/src/video_core/gpu_synch.cpp @@ -13,10 +13,16 @@ GPUSynch::GPUSynch(Core::System& system, std::unique_ptrMakeCurrent(); } +void GPUSynch::ReleaseContext() { + context->DoneCurrent(); +} + void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) { dma_pusher->Push(std::move(entries)); dma_pusher->DispatchCalls(); diff --git a/src/video_core/gpu_synch.h b/src/video_core/gpu_synch.h index 4a6e9a01d..762c20aa5 100644 --- a/src/video_core/gpu_synch.h +++ b/src/video_core/gpu_synch.h @@ -24,6 +24,8 @@ public: ~GPUSynch() override; void Start() override; + void ObtainContext() override; + void ReleaseContext() override; void PushGPUEntries(Tegra::CommandList&& entries) override; void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; void FlushRegion(VAddr addr, u64 size) override; diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 6aa161e99..5f93bd432 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -48,24 +48,29 @@ void EmuThread::run() { MicroProfileOnThreadCreate(name.c_str()); Common::SetCurrentThreadName(name.c_str()); + auto& system = Core::System::GetInstance(); + + system.RegisterHostThread(); + + auto& gpu = system.GPU(); + // Main process has been loaded. Make the context current to this thread and begin GPU and CPU // execution. - Core::System::GetInstance().GPU().Start(); + gpu.Start(); + + gpu.ObtainContext(); emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); - Core::System::GetInstance().RegisterHostThread(); - - context.MakeCurrent(); - - Core::System::GetInstance().Renderer().Rasterizer().LoadDiskResources( + system.Renderer().Rasterizer().LoadDiskResources( stop_run, [this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) { emit LoadProgress(stage, value, total); }); emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); - context.DoneCurrent(); + gpu.ReleaseContext(); + // Holds whether the cpu was running during the last iteration, // so that the DebugModeLeft signal can be emitted before the @@ -78,18 +83,18 @@ void EmuThread::run() { } running_guard = true; - Core::System::ResultStatus result = Core::System::GetInstance().Run(); + Core::System::ResultStatus result = system.Run(); if (result != Core::System::ResultStatus::Success) { running_guard = false; this->SetRunning(false); - emit ErrorThrown(result, Core::System::GetInstance().GetStatusDetails()); + emit ErrorThrown(result, system.GetStatusDetails()); } running_wait.Wait(); - result = Core::System::GetInstance().Pause(); + result = system.Pause(); if (result != Core::System::ResultStatus::Success) { running_guard = false; this->SetRunning(false); - emit ErrorThrown(result, Core::System::GetInstance().GetStatusDetails()); + emit ErrorThrown(result, system.GetStatusDetails()); } running_guard = false; @@ -106,7 +111,7 @@ void EmuThread::run() { } // Shutdown the core emulation - Core::System::GetInstance().Shutdown(); + system.Shutdown(); #if MICROPROFILE_ENABLED MicroProfileOnThreadExit(); From 7b44187fd277a87cf33891c1fdc023dae87abfbc Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Fri, 3 Apr 2020 14:11:04 -0400 Subject: [PATCH 211/252] Dynarmic Interface: don't clear cache if JIT has not been created. --- src/core/arm/dynarmic/arm_dynarmic_32.cpp | 3 +++ src/core/arm/dynarmic/arm_dynarmic_64.cpp | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index c8adf2866..bd9c072d0 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -200,6 +200,9 @@ void ARM_Dynarmic_32::PrepareReschedule() { } void ARM_Dynarmic_32::ClearInstructionCache() { + if (!jit) { + return; + } jit->ClearCache(); } diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 14b394368..99eb2ea4a 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -301,6 +301,9 @@ void ARM_Dynarmic_64::PrepareReschedule() { } void ARM_Dynarmic_64::ClearInstructionCache() { + if (!jit) { + return; + } jit->ClearCache(); } From 528b19a84287167d7699465e495b196d216b99db Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sun, 5 Apr 2020 09:48:53 -0400 Subject: [PATCH 212/252] General: Tune the priority of main emulation threads so they have higher priority than less important helper threads. --- src/common/thread.cpp | 46 +++++++++++++++++++ src/common/thread.h | 9 ++++ src/core/core_timing.cpp | 1 + src/core/cpu_manager.cpp | 1 + src/video_core/gpu_thread.cpp | 1 + .../renderer_vulkan/vk_scheduler.cpp | 2 + 6 files changed, 60 insertions(+) diff --git a/src/common/thread.cpp b/src/common/thread.cpp index c9684aed9..33c8437f5 100644 --- a/src/common/thread.cpp +++ b/src/common/thread.cpp @@ -25,6 +25,52 @@ namespace Common { +#ifdef _WIN32 + +void SetCurrentThreadPriority(ThreadPriority new_priority) { + auto handle = GetCurrentThread(); + int windows_priority = 0; + switch (new_priority) { + case ThreadPriority::Low: + windows_priority = THREAD_PRIORITY_BELOW_NORMAL; + break; + case ThreadPriority::Normal: + windows_priority = THREAD_PRIORITY_NORMAL; + break; + case ThreadPriority::High: + windows_priority = THREAD_PRIORITY_ABOVE_NORMAL; + break; + case ThreadPriority::VeryHigh: + windows_priority = THREAD_PRIORITY_HIGHEST; + break; + default: + windows_priority = THREAD_PRIORITY_NORMAL; + break; + } + SetThreadPriority(handle, windows_priority); +} + +#else + +void SetCurrentThreadPriority(ThreadPriority new_priority) { + pthread_t this_thread = pthread_self(); + + s32 max_prio = sched_get_priority_max(SCHED_OTHER); + s32 min_prio = sched_get_priority_min(SCHED_OTHER); + u32 level = static_cast(new_priority) + 1; + + struct sched_param params; + if (max_prio > min_prio) { + params.sched_priority = min_prio + ((max_prio - min_prio) * level) / 4; + } else { + params.sched_priority = min_prio - ((min_prio - max_prio) * level) / 4; + } + + pthread_setschedparam(this_thread, SCHED_OTHER, ¶ms); +} + +#endif + #ifdef _MSC_VER // Sets the debugger-visible name of the current thread. diff --git a/src/common/thread.h b/src/common/thread.h index 127cc7e23..52b359413 100644 --- a/src/common/thread.h +++ b/src/common/thread.h @@ -86,6 +86,15 @@ private: std::size_t generation = 0; // Incremented once each time the barrier is used }; +enum class ThreadPriority : u32 { + Low = 0, + Normal = 1, + High = 2, + VeryHigh = 3, +}; + +void SetCurrentThreadPriority(ThreadPriority new_priority); + void SetCurrentThreadName(const char* name); } // namespace Common diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index b02119494..032b29e33 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -48,6 +48,7 @@ void CoreTiming::ThreadEntry(CoreTiming& instance) { std::string name = "yuzu:HostTiming"; MicroProfileOnThreadCreate(name.c_str()); Common::SetCurrentThreadName(name.c_str()); + Common::SetCurrentThreadPriority(Common::ThreadPriority::VeryHigh); instance.on_thread_init(); instance.ThreadLoop(); } diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index 63c578852..32afcf3ae 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp @@ -337,6 +337,7 @@ void CpuManager::RunThread(std::size_t core) { } MicroProfileOnThreadCreate(name.c_str()); Common::SetCurrentThreadName(name.c_str()); + Common::SetCurrentThreadPriority(Common::ThreadPriority::High); auto& data = core_data[core]; data.enter_barrier = std::make_unique(); data.exit_barrier = std::make_unique(); diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp index 323185bfc..738c6f0c1 100644 --- a/src/video_core/gpu_thread.cpp +++ b/src/video_core/gpu_thread.cpp @@ -22,6 +22,7 @@ static void RunThread(Core::System& system, VideoCore::RendererBase& renderer, std::string name = "yuzu:GPU"; MicroProfileOnThreadCreate(name.c_str()); Common::SetCurrentThreadName(name.c_str()); + Common::SetCurrentThreadPriority(Common::ThreadPriority::High); system.RegisterHostThread(); // Wait for first GPU command before acquiring the window context diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 82ec9180e..56524e6f3 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -9,6 +9,7 @@ #include #include "common/microprofile.h" +#include "common/thread.h" #include "video_core/renderer_vulkan/vk_device.h" #include "video_core/renderer_vulkan/vk_query_cache.h" #include "video_core/renderer_vulkan/vk_resource_manager.h" @@ -133,6 +134,7 @@ void VKScheduler::BindGraphicsPipeline(VkPipeline pipeline) { } void VKScheduler::WorkerThread() { + Common::SetCurrentThreadPriority(Common::ThreadPriority::High); std::unique_lock lock{mutex}; do { cv.wait(lock, [this] { return !chunk_queue.Empty() || quit; }); From cdf900f1e3e8d51365d27b1139cc531a179eeb6e Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sun, 12 Apr 2020 19:25:53 -0400 Subject: [PATCH 213/252] Core: Split Microprofile Dynarmic timing per Core --- src/core/core.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/core/core.cpp b/src/core/core.cpp index 3393c33eb..50656697f 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -51,7 +51,10 @@ #include "video_core/renderer_base.h" #include "video_core/video_core.h" -MICROPROFILE_DEFINE(ARM_Jit_Dynarmic, "ARM JIT", "Dynarmic", MP_RGB(255, 64, 64)); +MICROPROFILE_DEFINE(ARM_Jit_Dynarmic_CPU0, "ARM JIT", "Dynarmic CPU 0", MP_RGB(255, 64, 64)); +MICROPROFILE_DEFINE(ARM_Jit_Dynarmic_CPU1, "ARM JIT", "Dynarmic CPU 1", MP_RGB(255, 64, 64)); +MICROPROFILE_DEFINE(ARM_Jit_Dynarmic_CPU2, "ARM JIT", "Dynarmic CPU 2", MP_RGB(255, 64, 64)); +MICROPROFILE_DEFINE(ARM_Jit_Dynarmic_CPU3, "ARM JIT", "Dynarmic CPU 3", MP_RGB(255, 64, 64)); namespace Core { @@ -189,6 +192,11 @@ struct System::Impl { is_powered_on = true; exit_lock = false; + microprofile_dynarmic[0] = MICROPROFILE_TOKEN(ARM_Jit_Dynarmic_CPU0); + microprofile_dynarmic[1] = MICROPROFILE_TOKEN(ARM_Jit_Dynarmic_CPU1); + microprofile_dynarmic[2] = MICROPROFILE_TOKEN(ARM_Jit_Dynarmic_CPU2); + microprofile_dynarmic[3] = MICROPROFILE_TOKEN(ARM_Jit_Dynarmic_CPU3); + LOG_DEBUG(Core, "Initialized OK"); return ResultStatus::Success; @@ -396,6 +404,7 @@ struct System::Impl { bool is_async_gpu{}; std::array dynarmic_ticks{}; + std::array microprofile_dynarmic{}; }; System::System() : impl{std::make_unique(*this)} {} @@ -747,12 +756,12 @@ void System::RegisterHostThread() { void System::EnterDynarmicProfile() { std::size_t core = impl->kernel.GetCurrentHostThreadID(); - impl->dynarmic_ticks[core] = MicroProfileEnter(MICROPROFILE_TOKEN(ARM_Jit_Dynarmic)); + impl->dynarmic_ticks[core] = MicroProfileEnter(impl->microprofile_dynarmic[core]); } void System::ExitDynarmicProfile() { std::size_t core = impl->kernel.GetCurrentHostThreadID(); - MicroProfileLeave(MICROPROFILE_TOKEN(ARM_Jit_Dynarmic), impl->dynarmic_ticks[core]); + MicroProfileLeave(impl->microprofile_dynarmic[core], impl->dynarmic_ticks[core]); } } // namespace Core From 71f1c0f9f9aa92200fbc6c173a3305fcb5a0c85d Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Thu, 23 Apr 2020 12:58:41 -0400 Subject: [PATCH 214/252] CoreTiming: Correct rebase bugs and other miscellaneous things. --- src/core/core_timing.cpp | 2 ++ src/tests/core/core_timing.cpp | 10 ++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 032b29e33..1aa89a1cc 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -56,6 +56,7 @@ void CoreTiming::ThreadEntry(CoreTiming& instance) { void CoreTiming::Initialize(std::function&& on_thread_init_) { on_thread_init = std::move(on_thread_init_); event_fifo_id = 0; + shutting_down = false; ticks = 0; const auto empty_timed_callback = [](u64, s64) {}; ev_lost = CreateEvent("_lost_event", empty_timed_callback); @@ -79,6 +80,7 @@ void CoreTiming::Shutdown() { void CoreTiming::Pause(bool is_paused) { paused = is_paused; + pause_event.Set(); } void CoreTiming::SyncPause(bool is_paused) { diff --git a/src/tests/core/core_timing.cpp b/src/tests/core/core_timing.cpp index 21a5840fb..5aa339731 100644 --- a/src/tests/core/core_timing.cpp +++ b/src/tests/core/core_timing.cpp @@ -16,10 +16,10 @@ namespace { // Numbers are chosen randomly to make sure the correct one is given. -static constexpr std::array CB_IDS{{42, 144, 93, 1026, UINT64_C(0xFFFF7FFFF7FFFF)}}; -static constexpr int MAX_SLICE_LENGTH = 10000; // Copied from CoreTiming internals -static constexpr std::array calls_order{{2, 0, 1, 4, 3}}; -static std::array delays{}; +constexpr std::array CB_IDS{{42, 144, 93, 1026, UINT64_C(0xFFFF7FFFF7FFFF)}}; +constexpr int MAX_SLICE_LENGTH = 10000; // Copied from CoreTiming internals +constexpr std::array calls_order{{2, 0, 1, 4, 3}}; +std::array delays{}; std::bitset callbacks_ran_flags; u64 expected_callback = 0; @@ -49,6 +49,8 @@ struct ScopeInit final { Core::Timing::CoreTiming core_timing; }; +} // Anonymous namespace + TEST_CASE("CoreTiming[BasicOrder]", "[core]") { ScopeInit guard; auto& core_timing = guard.core_timing; From dda6147b0dc882c053fc02c987aa87140471649a Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Tue, 28 Apr 2020 23:15:04 -0400 Subject: [PATCH 215/252] ARMInterface: Correct rebase errors. --- src/core/arm/dynarmic/arm_dynarmic_64.cpp | 4 ++-- src/core/arm/unicorn/arm_unicorn.cpp | 4 ++-- src/core/arm/unicorn/arm_unicorn.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 99eb2ea4a..226fd4915 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -212,8 +212,8 @@ void ARM_Dynarmic_64::Step() { ARM_Dynarmic_64::ARM_Dynarmic_64(System& system, CPUInterrupts& interrupt_handlers, bool uses_wall_clock, ExclusiveMonitor& exclusive_monitor, std::size_t core_index) - : ARM_Interface{system, interrupt_handler, uses_wall_clock}, - cb(std::make_unique(*this)), inner_unicorn{system, interrupt_handler, + : ARM_Interface{system, interrupt_handlers, uses_wall_clock}, + cb(std::make_unique(*this)), inner_unicorn{system, interrupt_handlers, uses_wall_clock, ARM_Unicorn::Arch::AArch64, core_index}, diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp index 1cb71942b..9f9690454 100644 --- a/src/core/arm/unicorn/arm_unicorn.cpp +++ b/src/core/arm/unicorn/arm_unicorn.cpp @@ -63,9 +63,9 @@ static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int si return false; } -ARM_Unicorn::ARM_Unicorn(System& system, CPUInterruptHandler& interrupt_handler, +ARM_Unicorn::ARM_Unicorn(System& system, CPUInterrupts& interrupt_handlers, bool uses_wall_clock, Arch architecture, std::size_t core_index) - : ARM_Interface{system, interrupt_handler, uses_wall_clock}, core_index{core_index} { + : ARM_Interface{system, interrupt_handlers, uses_wall_clock}, core_index{core_index} { const auto arch = architecture == Arch::AArch32 ? UC_ARCH_ARM : UC_ARCH_ARM64; CHECKED(uc_open(arch, UC_MODE_ARM, &uc)); diff --git a/src/core/arm/unicorn/arm_unicorn.h b/src/core/arm/unicorn/arm_unicorn.h index a01751e65..9b7d7f6c2 100644 --- a/src/core/arm/unicorn/arm_unicorn.h +++ b/src/core/arm/unicorn/arm_unicorn.h @@ -20,7 +20,7 @@ public: AArch64, // 64-bit ARM }; - explicit ARM_Unicorn(System& system, CPUInterruptHandler& interrupt_handler, + explicit ARM_Unicorn(System& system, CPUInterrupts& interrupt_handlers, bool uses_wall_clock, Arch architecture, std::size_t core_index); ~ARM_Unicorn() override; From 3714f2e471e06856499432761981760c557d2d90 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sat, 2 May 2020 22:03:09 -0400 Subject: [PATCH 216/252] ARMInterface/Externals: Update dynarmic and fit to latest version. --- externals/dynarmic | 2 +- src/core/arm/dynarmic/arm_dynarmic_64.cpp | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/externals/dynarmic b/externals/dynarmic index e7166e8ba..f4922a97f 160000 --- a/externals/dynarmic +++ b/externals/dynarmic @@ -1 +1 @@ -Subproject commit e7166e8ba74d7b9c85e87afc0aaf667e7e84cfe0 +Subproject commit f4922a97f6eb4b93decfbd80a881a7eac89d6890 diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 226fd4915..8095901d1 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -195,7 +195,7 @@ std::shared_ptr ARM_Dynarmic_64::MakeJit(Common::PageTable& config.enable_fast_dispatch = false; } - // CNTPCT uses wall clock. + // Timing config.wall_clock_cntpct = uses_wall_clock; return std::make_shared(config); @@ -271,7 +271,7 @@ void ARM_Dynarmic_64::SetTPIDR_EL0(u64 value) { } void ARM_Dynarmic_64::ChangeProcessorId(std::size_t new_core_id) { - jit->ChangeProcessorId(new_core_id); + jit->ChangeProcessorID(new_core_id); } void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) { @@ -358,31 +358,31 @@ void DynarmicExclusiveMonitor::ClearExclusive() { } bool DynarmicExclusiveMonitor::ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) { - return monitor.DoExclusiveOperation(core_index, vaddr, 1, [&](u8 expected) -> bool { + return monitor.DoExclusiveOperation(core_index, vaddr, [&](u8 expected) -> bool { return memory.WriteExclusive8(vaddr, value, expected); }); } bool DynarmicExclusiveMonitor::ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) { - return monitor.DoExclusiveOperation(core_index, vaddr, 2, [&](u16 expected) -> bool { + return monitor.DoExclusiveOperation(core_index, vaddr, [&](u16 expected) -> bool { return memory.WriteExclusive16(vaddr, value, expected); }); } bool DynarmicExclusiveMonitor::ExclusiveWrite32(std::size_t core_index, VAddr vaddr, u32 value) { - return monitor.DoExclusiveOperation(core_index, vaddr, 4, [&](u32 expected) -> bool { + return monitor.DoExclusiveOperation(core_index, vaddr, [&](u32 expected) -> bool { return memory.WriteExclusive32(vaddr, value, expected); }); } bool DynarmicExclusiveMonitor::ExclusiveWrite64(std::size_t core_index, VAddr vaddr, u64 value) { - return monitor.DoExclusiveOperation(core_index, vaddr, 8, [&](u64 expected) -> bool { + return monitor.DoExclusiveOperation(core_index, vaddr, [&](u64 expected) -> bool { return memory.WriteExclusive64(vaddr, value, expected); }); } bool DynarmicExclusiveMonitor::ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) { - return monitor.DoExclusiveOperation(core_index, vaddr, 16, [&](u128 expected) -> bool { + return monitor.DoExclusiveOperation(core_index, vaddr, [&](u128 expected) -> bool { return memory.WriteExclusive128(vaddr, value, expected); }); } From 467d43570e10b98fa33067352d35fe62ceb3cb9e Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Fri, 8 May 2020 18:53:13 -0400 Subject: [PATCH 217/252] Clang Format. --- src/common/atomic_ops.cpp | 14 ++++++------ src/common/atomic_ops.h | 2 +- src/common/thread.cpp | 30 +++++++++++++------------- src/core/arm/unicorn/arm_unicorn.cpp | 4 ++-- src/core/arm/unicorn/arm_unicorn.h | 4 ++-- src/core/core.h | 2 -- src/core/hle/kernel/mutex.cpp | 11 +++++----- src/core/hle/kernel/mutex.h | 3 ++- src/core/hle/kernel/physical_core.h | 7 +++--- src/core/hle/kernel/server_session.cpp | 2 +- src/yuzu/bootmanager.cpp | 1 - src/yuzu/bootmanager.h | 3 ++- src/yuzu/debugger/wait_tree.cpp | 5 +++-- src/yuzu/main.cpp | 9 +++++--- 14 files changed, 49 insertions(+), 48 deletions(-) diff --git a/src/common/atomic_ops.cpp b/src/common/atomic_ops.cpp index 65cdfb4fd..6b2236114 100644 --- a/src/common/atomic_ops.cpp +++ b/src/common/atomic_ops.cpp @@ -35,26 +35,26 @@ bool AtomicCompareAndSwap(u64 volatile* pointer, u64 value, u64 expected) { } bool AtomicCompareAndSwap(u64 volatile* pointer, u128 value, u128 expected) { - return _InterlockedCompareExchange128((__int64*)pointer, value[1], value[0], (__int64*)expected.data()) != 0; + return _InterlockedCompareExchange128((__int64*)pointer, value[1], value[0], + (__int64*)expected.data()) != 0; } - #else bool AtomicCompareAndSwap(u8 volatile* pointer, u8 value, u8 expected) { - return __sync_bool_compare_and_swap (pointer, value, expected); + return __sync_bool_compare_and_swap(pointer, value, expected); } bool AtomicCompareAndSwap(u16 volatile* pointer, u16 value, u16 expected) { - return __sync_bool_compare_and_swap (pointer, value, expected); + return __sync_bool_compare_and_swap(pointer, value, expected); } bool AtomicCompareAndSwap(u32 volatile* pointer, u32 value, u32 expected) { - return __sync_bool_compare_and_swap (pointer, value, expected); + return __sync_bool_compare_and_swap(pointer, value, expected); } bool AtomicCompareAndSwap(u64 volatile* pointer, u64 value, u64 expected) { - return __sync_bool_compare_and_swap (pointer, value, expected); + return __sync_bool_compare_and_swap(pointer, value, expected); } bool AtomicCompareAndSwap(u64 volatile* pointer, u128 value, u128 expected) { @@ -62,7 +62,7 @@ bool AtomicCompareAndSwap(u64 volatile* pointer, u128 value, u128 expected) { unsigned __int128 expected_a; std::memcpy(&value_a, value.data(), sizeof(u128)); std::memcpy(&expected_a, expected.data(), sizeof(u128)); - return __sync_bool_compare_and_swap ((unsigned __int128*)pointer, value_a, expected_a); + return __sync_bool_compare_and_swap((unsigned __int128*)pointer, value_a, expected_a); } #endif diff --git a/src/common/atomic_ops.h b/src/common/atomic_ops.h index 22cb3a402..e6181d521 100644 --- a/src/common/atomic_ops.h +++ b/src/common/atomic_ops.h @@ -8,7 +8,7 @@ namespace Common { -bool AtomicCompareAndSwap(u8 volatile * pointer, u8 value, u8 expected); +bool AtomicCompareAndSwap(u8 volatile* pointer, u8 value, u8 expected); bool AtomicCompareAndSwap(u16 volatile* pointer, u16 value, u16 expected); bool AtomicCompareAndSwap(u32 volatile* pointer, u32 value, u32 expected); bool AtomicCompareAndSwap(u64 volatile* pointer, u64 value, u64 expected); diff --git a/src/common/thread.cpp b/src/common/thread.cpp index 33c8437f5..8e5935e6a 100644 --- a/src/common/thread.cpp +++ b/src/common/thread.cpp @@ -31,21 +31,21 @@ void SetCurrentThreadPriority(ThreadPriority new_priority) { auto handle = GetCurrentThread(); int windows_priority = 0; switch (new_priority) { - case ThreadPriority::Low: - windows_priority = THREAD_PRIORITY_BELOW_NORMAL; - break; - case ThreadPriority::Normal: - windows_priority = THREAD_PRIORITY_NORMAL; - break; - case ThreadPriority::High: - windows_priority = THREAD_PRIORITY_ABOVE_NORMAL; - break; - case ThreadPriority::VeryHigh: - windows_priority = THREAD_PRIORITY_HIGHEST; - break; - default: - windows_priority = THREAD_PRIORITY_NORMAL; - break; + case ThreadPriority::Low: + windows_priority = THREAD_PRIORITY_BELOW_NORMAL; + break; + case ThreadPriority::Normal: + windows_priority = THREAD_PRIORITY_NORMAL; + break; + case ThreadPriority::High: + windows_priority = THREAD_PRIORITY_ABOVE_NORMAL; + break; + case ThreadPriority::VeryHigh: + windows_priority = THREAD_PRIORITY_HIGHEST; + break; + default: + windows_priority = THREAD_PRIORITY_NORMAL; + break; } SetThreadPriority(handle, windows_priority); } diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp index 9f9690454..35e8f42e8 100644 --- a/src/core/arm/unicorn/arm_unicorn.cpp +++ b/src/core/arm/unicorn/arm_unicorn.cpp @@ -63,8 +63,8 @@ static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int si return false; } -ARM_Unicorn::ARM_Unicorn(System& system, CPUInterrupts& interrupt_handlers, - bool uses_wall_clock, Arch architecture, std::size_t core_index) +ARM_Unicorn::ARM_Unicorn(System& system, CPUInterrupts& interrupt_handlers, bool uses_wall_clock, + Arch architecture, std::size_t core_index) : ARM_Interface{system, interrupt_handlers, uses_wall_clock}, core_index{core_index} { const auto arch = architecture == Arch::AArch32 ? UC_ARCH_ARM : UC_ARCH_ARM64; CHECKED(uc_open(arch, UC_MODE_ARM, &uc)); diff --git a/src/core/arm/unicorn/arm_unicorn.h b/src/core/arm/unicorn/arm_unicorn.h index 9b7d7f6c2..8ace8b86f 100644 --- a/src/core/arm/unicorn/arm_unicorn.h +++ b/src/core/arm/unicorn/arm_unicorn.h @@ -20,8 +20,8 @@ public: AArch64, // 64-bit ARM }; - explicit ARM_Unicorn(System& system, CPUInterrupts& interrupt_handlers, - bool uses_wall_clock, Arch architecture, std::size_t core_index); + explicit ARM_Unicorn(System& system, CPUInterrupts& interrupt_handlers, bool uses_wall_clock, + Arch architecture, std::size_t core_index); ~ARM_Unicorn() override; void SetPC(u64 pc) override; diff --git a/src/core/core.h b/src/core/core.h index 87df79d57..d2d1fcc5b 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -148,8 +148,6 @@ public: */ ResultStatus Pause(); - - /** * Step the CPU one instruction * @return Result status, indicating whether or not the operation succeeded. diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 32dc1ffae..8f6c944d1 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -9,7 +9,6 @@ #include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" -#include "core/core.h" #include "core/hle/kernel/errors.h" #include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/kernel.h" @@ -126,11 +125,11 @@ ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle, std::pair> Mutex::Unlock(std::shared_ptr owner, VAddr address) { - // The mutex address must be 4-byte aligned - if ((address % sizeof(u32)) != 0) { - LOG_ERROR(Kernel, "Address is not 4-byte aligned! address={:016X}", address); - return {ERR_INVALID_ADDRESS, nullptr}; - } + // The mutex address must be 4-byte aligned + if ((address % sizeof(u32)) != 0) { + LOG_ERROR(Kernel, "Address is not 4-byte aligned! address={:016X}", address); + return {ERR_INVALID_ADDRESS, nullptr}; + } auto [new_owner, num_waiters] = GetHighestPriorityMutexWaitingThread(owner, address); if (new_owner == nullptr) { diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h index bce06ecea..3b81dc3df 100644 --- a/src/core/hle/kernel/mutex.h +++ b/src/core/hle/kernel/mutex.h @@ -29,7 +29,8 @@ public: Handle requesting_thread_handle); /// Unlocks a mutex for owner at address - std::pair> Unlock(std::shared_ptr owner, VAddr address); + std::pair> Unlock(std::shared_ptr owner, + VAddr address); /// Releases the mutex at the specified address. ResultCode Release(VAddr address); diff --git a/src/core/hle/kernel/physical_core.h b/src/core/hle/kernel/physical_core.h index 751b994a7..85f6dec05 100644 --- a/src/core/hle/kernel/physical_core.h +++ b/src/core/hle/kernel/physical_core.h @@ -10,7 +10,7 @@ #include "core/arm/cpu_interrupt_handler.h" namespace Common { - class SpinLock; +class SpinLock; } namespace Kernel { @@ -27,9 +27,8 @@ namespace Kernel { class PhysicalCore { public: - PhysicalCore(Core::System& system, std::size_t id, - Kernel::Scheduler& scheduler, - Core::CPUInterruptHandler& interrupt_handler); + PhysicalCore(Core::System& system, std::size_t id, Kernel::Scheduler& scheduler, + Core::CPUInterruptHandler& interrupt_handler); ~PhysicalCore(); PhysicalCore(const PhysicalCore&) = delete; diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index e988a3f22..7b23a6889 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp @@ -17,9 +17,9 @@ #include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/process.h" +#include "core/hle/kernel/scheduler.h" #include "core/hle/kernel/server_session.h" #include "core/hle/kernel/session.h" -#include "core/hle/kernel/scheduler.h" #include "core/hle/kernel/thread.h" #include "core/memory.h" diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 5f93bd432..4bfce48a4 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -71,7 +71,6 @@ void EmuThread::run() { gpu.ReleaseContext(); - // Holds whether the cpu was running during the last iteration, // so that the DebugModeLeft signal can be emitted before the // next execution step diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index 768568b3e..6c59b4d5c 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h @@ -62,7 +62,8 @@ public: if (!running) { running_wait.Set(); /// Wait until effectively paused - while (running_guard); + while (running_guard) + ; } } diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp index 0226ae2e2..9bb0a0109 100644 --- a/src/yuzu/debugger/wait_tree.cpp +++ b/src/yuzu/debugger/wait_tree.cpp @@ -127,11 +127,12 @@ std::vector> WaitTreeCallstack::GetChildren() cons return list; } - auto backtrace = Core::ARM_Interface::GetBacktraceFromContext(Core::System::GetInstance(), thread.GetContext64()); + auto backtrace = Core::ARM_Interface::GetBacktraceFromContext(Core::System::GetInstance(), + thread.GetContext64()); for (auto& entry : backtrace) { std::string s = fmt::format("{:20}{:016X} {:016X} {:016X} {}", entry.module, entry.address, - entry.original_address, entry.offset, entry.name); + entry.original_address, entry.offset, entry.name); list.push_back(std::make_unique(QString::fromStdString(s))); } diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index f1d9ec326..53790c89c 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -534,7 +534,8 @@ void GMainWindow::InitializeWidgets() { if (emulation_running) { return; } - bool is_async = !Settings::values.use_asynchronous_gpu_emulation || Settings::values.use_multi_core; + bool is_async = + !Settings::values.use_asynchronous_gpu_emulation || Settings::values.use_multi_core; Settings::values.use_asynchronous_gpu_emulation = is_async; async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation); Settings::Apply(); @@ -552,7 +553,8 @@ void GMainWindow::InitializeWidgets() { return; } Settings::values.use_multi_core = !Settings::values.use_multi_core; - bool is_async = Settings::values.use_asynchronous_gpu_emulation || Settings::values.use_multi_core; + bool is_async = + Settings::values.use_asynchronous_gpu_emulation || Settings::values.use_multi_core; Settings::values.use_asynchronous_gpu_emulation = is_async; async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation); multicore_status_button->setChecked(Settings::values.use_multi_core); @@ -1958,7 +1960,8 @@ void GMainWindow::OnConfigure() { dock_status_button->setChecked(Settings::values.use_docked_mode); multicore_status_button->setChecked(Settings::values.use_multi_core); - Settings::values.use_asynchronous_gpu_emulation = Settings::values.use_asynchronous_gpu_emulation || Settings::values.use_multi_core; + Settings::values.use_asynchronous_gpu_emulation = + Settings::values.use_asynchronous_gpu_emulation || Settings::values.use_multi_core; async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation); #ifdef HAS_VULKAN From 0e4c35c591bec72525b128336254feb76c4adc73 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sat, 9 May 2020 15:02:23 -0400 Subject: [PATCH 218/252] YuzuQT: Hide Speed UI on Multicore. --- src/yuzu/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 53790c89c..82625e67f 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -2090,7 +2090,7 @@ void GMainWindow::UpdateStatusBar() { game_fps_label->setText(tr("Game: %1 FPS").arg(results.game_fps, 0, 'f', 0)); emu_frametime_label->setText(tr("Frame: %1 ms").arg(results.frametime * 1000.0, 0, 'f', 2)); - emu_speed_label->setVisible(true); + emu_speed_label->setVisible(!Settings::values.use_multi_core); game_fps_label->setVisible(true); emu_frametime_label->setVisible(true); } From d24014358883987d7ebdafc4863a7bc36addfa1b Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Wed, 13 May 2020 14:17:34 -0400 Subject: [PATCH 219/252] Kernel: Correct Host Context on Threads and Scheduler. --- src/core/hle/kernel/scheduler.cpp | 16 ++++++++-------- src/core/hle/kernel/scheduler.h | 2 +- src/core/hle/kernel/thread.cpp | 2 +- src/core/hle/kernel/thread.h | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index 43c924fa0..61b8a396a 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp @@ -736,15 +736,15 @@ void Scheduler::SwitchContext() { previous_thread->context_guard.unlock(); } - std::shared_ptr old_context; + std::shared_ptr* old_context; if (previous_thread != nullptr) { - old_context = previous_thread->GetHostContext(); + old_context = &previous_thread->GetHostContext(); } else { - old_context = idle_thread->GetHostContext(); + old_context = &idle_thread->GetHostContext(); } guard.unlock(); - Common::Fiber::YieldTo(old_context, switch_fiber); + Common::Fiber::YieldTo(*old_context, switch_fiber); /// When a thread wakes up, the scheduler may have changed to other in another core. auto& next_scheduler = system.Kernel().CurrentScheduler(); next_scheduler.SwitchContextStep2(); @@ -774,13 +774,13 @@ void Scheduler::SwitchToCurrent() { break; } } - std::shared_ptr next_context; + std::shared_ptr* next_context; if (current_thread != nullptr) { - next_context = current_thread->GetHostContext(); + next_context = ¤t_thread->GetHostContext(); } else { - next_context = idle_thread->GetHostContext(); + next_context = &idle_thread->GetHostContext(); } - Common::Fiber::YieldTo(switch_fiber, next_context); + Common::Fiber::YieldTo(switch_fiber, *next_context); } } } diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h index 10dc4b832..348107160 100644 --- a/src/core/hle/kernel/scheduler.h +++ b/src/core/hle/kernel/scheduler.h @@ -236,7 +236,7 @@ public: void OnThreadStart(); - std::shared_ptr ControlContext() { + std::shared_ptr& ControlContext() { return switch_fiber; } diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index fba2a9c85..2b1092697 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -150,7 +150,7 @@ static void ResetThreadContext64(Core::ARM_Interface::ThreadContext64& context, context.fpcr = 0; } -std::shared_ptr Thread::GetHostContext() const { +std::shared_ptr& Thread::GetHostContext() { return host_context; } diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 3ae0df6ef..c0342c462 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -342,7 +342,7 @@ public: was_running = value; } - std::shared_ptr GetHostContext() const; + std::shared_ptr& GetHostContext(); ThreadStatus GetStatus() const { return status; From 7b1804dab40fabfbf73157cd5ce76793de1375ee Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Thu, 14 May 2020 14:44:03 -0400 Subject: [PATCH 220/252] Common/AtomicOps: Correct GCC Intrinsic argument ordering. --- src/common/atomic_ops.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/common/atomic_ops.cpp b/src/common/atomic_ops.cpp index 6b2236114..1098e21ff 100644 --- a/src/common/atomic_ops.cpp +++ b/src/common/atomic_ops.cpp @@ -42,19 +42,19 @@ bool AtomicCompareAndSwap(u64 volatile* pointer, u128 value, u128 expected) { #else bool AtomicCompareAndSwap(u8 volatile* pointer, u8 value, u8 expected) { - return __sync_bool_compare_and_swap(pointer, value, expected); + return __sync_bool_compare_and_swap(pointer, expected, value); } bool AtomicCompareAndSwap(u16 volatile* pointer, u16 value, u16 expected) { - return __sync_bool_compare_and_swap(pointer, value, expected); + return __sync_bool_compare_and_swap(pointer, expected, value); } bool AtomicCompareAndSwap(u32 volatile* pointer, u32 value, u32 expected) { - return __sync_bool_compare_and_swap(pointer, value, expected); + return __sync_bool_compare_and_swap(pointer, expected, value); } bool AtomicCompareAndSwap(u64 volatile* pointer, u64 value, u64 expected) { - return __sync_bool_compare_and_swap(pointer, value, expected); + return __sync_bool_compare_and_swap(pointer, expected, value); } bool AtomicCompareAndSwap(u64 volatile* pointer, u128 value, u128 expected) { @@ -62,7 +62,7 @@ bool AtomicCompareAndSwap(u64 volatile* pointer, u128 value, u128 expected) { unsigned __int128 expected_a; std::memcpy(&value_a, value.data(), sizeof(u128)); std::memcpy(&expected_a, expected.data(), sizeof(u128)); - return __sync_bool_compare_and_swap((unsigned __int128*)pointer, value_a, expected_a); + return __sync_bool_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a); } #endif From 0a8013d71eebf13864e73c94075ef969e76cb485 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Thu, 14 May 2020 14:48:50 -0400 Subject: [PATCH 221/252] ARMDynarmicInterface: Correct GCC Build Errors. --- src/core/arm/dynarmic/arm_dynarmic_32.cpp | 6 +++--- src/core/arm/dynarmic/arm_dynarmic_64.cpp | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index bd9c072d0..71d7e169a 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -91,11 +91,11 @@ public: u64 GetTicksRemaining() override { if (parent.uses_wall_clock) { if (!parent.interrupt_handlers[parent.core_index].IsInterrupted()) { - return std::max(1000U, 0); + return 1000U; } - return 0ULL; + return 0U; } - return std::max(parent.system.CoreTiming().GetDowncount(), 0LL); + return std::max(parent.system.CoreTiming().GetDowncount(), 0); } ARM_Dynarmic_32& parent; diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 8095901d1..8e8f7ce0a 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -143,11 +143,11 @@ public: u64 GetTicksRemaining() override { if (parent.uses_wall_clock) { if (!parent.interrupt_handlers[parent.core_index].IsInterrupted()) { - return std::max(1000U, 0); + return 1000U; } - return 0ULL; + return 0U; } - return std::max(parent.system.CoreTiming().GetDowncount(), 0LL); + return std::max(parent.system.CoreTiming().GetDowncount(), 0); } u64 GetCNTPCT() override { From 71c4779211dc081a3b2dd4af52edad5748e7a7f5 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Thu, 14 May 2020 16:17:44 -0400 Subject: [PATCH 222/252] Tests/CoreTiming: Correct host timing tests. --- src/tests/core/core_timing.cpp | 29 ++++--- src/tests/core/host_timing.cpp | 142 --------------------------------- 2 files changed, 14 insertions(+), 157 deletions(-) delete mode 100644 src/tests/core/host_timing.cpp diff --git a/src/tests/core/core_timing.cpp b/src/tests/core/core_timing.cpp index 5aa339731..e66db1940 100644 --- a/src/tests/core/core_timing.cpp +++ b/src/tests/core/core_timing.cpp @@ -23,7 +23,6 @@ std::array delays{}; std::bitset callbacks_ran_flags; u64 expected_callback = 0; -s64 lateness = 0; template void HostCallbackTemplate(u64 userdata, s64 nanoseconds_late) { @@ -35,8 +34,6 @@ void HostCallbackTemplate(u64 userdata, s64 nanoseconds_late) { ++expected_callback; } -u64 callbacks_done = 0; - struct ScopeInit final { ScopeInit() { core_timing.SetMulticore(true); @@ -49,6 +46,20 @@ struct ScopeInit final { Core::Timing::CoreTiming core_timing; }; +#pragma optimize("", off) + +u64 TestTimerSpeed(Core::Timing::CoreTiming& core_timing) { + u64 start = core_timing.GetGlobalTimeNs().count(); + u64 placebo = 0; + for (std::size_t i = 0; i < 1000; i++) { + placebo += core_timing.GetGlobalTimeNs().count(); + } + u64 end = core_timing.GetGlobalTimeNs().count(); + return (end - start); +} + +#pragma optimize("", on) + } // Anonymous namespace TEST_CASE("CoreTiming[BasicOrder]", "[core]") { @@ -89,18 +100,6 @@ TEST_CASE("CoreTiming[BasicOrder]", "[core]") { } } -#pragma optimize("", off) -u64 TestTimerSpeed(Core::Timing::CoreTiming& core_timing) { - u64 start = core_timing.GetGlobalTimeNs().count(); - u64 placebo = 0; - for (std::size_t i = 0; i < 1000; i++) { - placebo += core_timing.GetGlobalTimeNs().count(); - } - u64 end = core_timing.GetGlobalTimeNs().count(); - return (end - start); -} -#pragma optimize("", on) - TEST_CASE("CoreTiming[BasicOrderNoPausing]", "[core]") { ScopeInit guard; auto& core_timing = guard.core_timing; diff --git a/src/tests/core/host_timing.cpp b/src/tests/core/host_timing.cpp deleted file mode 100644 index 556254098..000000000 --- a/src/tests/core/host_timing.cpp +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright 2016 Dolphin Emulator Project / 2017 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#include - -#include -#include -#include -#include -#include - -#include "common/file_util.h" -#include "core/core.h" -#include "core/host_timing.h" - -// Numbers are chosen randomly to make sure the correct one is given. -static constexpr std::array CB_IDS{{42, 144, 93, 1026, UINT64_C(0xFFFF7FFFF7FFFF)}}; -static constexpr int MAX_SLICE_LENGTH = 10000; // Copied from CoreTiming internals -static constexpr std::array calls_order{{2, 0, 1, 4, 3}}; -static std::array delays{}; - -static std::bitset callbacks_ran_flags; -static u64 expected_callback = 0; - -template -void HostCallbackTemplate(u64 userdata, s64 nanoseconds_late) { - static_assert(IDX < CB_IDS.size(), "IDX out of range"); - callbacks_ran_flags.set(IDX); - REQUIRE(CB_IDS[IDX] == userdata); - REQUIRE(CB_IDS[IDX] == CB_IDS[calls_order[expected_callback]]); - delays[IDX] = nanoseconds_late; - ++expected_callback; -} - -struct ScopeInit final { - ScopeInit() { - core_timing.Initialize(); - } - ~ScopeInit() { - core_timing.Shutdown(); - } - - Core::HostTiming::CoreTiming core_timing; -}; - -#pragma optimize("", off) - -static u64 TestTimerSpeed(Core::HostTiming::CoreTiming& core_timing) { - u64 start = core_timing.GetGlobalTimeNs().count(); - u64 placebo = 0; - for (std::size_t i = 0; i < 1000; i++) { - placebo += core_timing.GetGlobalTimeNs().count(); - } - u64 end = core_timing.GetGlobalTimeNs().count(); - return (end - start); -} - -#pragma optimize("", on) - -TEST_CASE("HostTiming[BasicOrder]", "[core]") { - ScopeInit guard; - auto& core_timing = guard.core_timing; - std::vector> events{ - Core::HostTiming::CreateEvent("callbackA", HostCallbackTemplate<0>), - Core::HostTiming::CreateEvent("callbackB", HostCallbackTemplate<1>), - Core::HostTiming::CreateEvent("callbackC", HostCallbackTemplate<2>), - Core::HostTiming::CreateEvent("callbackD", HostCallbackTemplate<3>), - Core::HostTiming::CreateEvent("callbackE", HostCallbackTemplate<4>), - }; - - expected_callback = 0; - - core_timing.SyncPause(true); - - u64 one_micro = 1000U; - for (std::size_t i = 0; i < events.size(); i++) { - u64 order = calls_order[i]; - core_timing.ScheduleEvent(i * one_micro + 100U, events[order], CB_IDS[order]); - } - /// test pause - REQUIRE(callbacks_ran_flags.none()); - - core_timing.Pause(false); // No need to sync - - while (core_timing.HasPendingEvents()) - ; - - REQUIRE(callbacks_ran_flags.all()); - - for (std::size_t i = 0; i < delays.size(); i++) { - const double delay = static_cast(delays[i]); - const double micro = delay / 1000.0f; - const double mili = micro / 1000.0f; - printf("HostTimer Pausing Delay[%zu]: %.3f %.6f\n", i, micro, mili); - } -} - -TEST_CASE("HostTiming[BasicOrderNoPausing]", "[core]") { - ScopeInit guard; - auto& core_timing = guard.core_timing; - std::vector> events{ - Core::HostTiming::CreateEvent("callbackA", HostCallbackTemplate<0>), - Core::HostTiming::CreateEvent("callbackB", HostCallbackTemplate<1>), - Core::HostTiming::CreateEvent("callbackC", HostCallbackTemplate<2>), - Core::HostTiming::CreateEvent("callbackD", HostCallbackTemplate<3>), - Core::HostTiming::CreateEvent("callbackE", HostCallbackTemplate<4>), - }; - - core_timing.SyncPause(true); - core_timing.SyncPause(false); - - expected_callback = 0; - - u64 start = core_timing.GetGlobalTimeNs().count(); - u64 one_micro = 1000U; - for (std::size_t i = 0; i < events.size(); i++) { - u64 order = calls_order[i]; - core_timing.ScheduleEvent(i * one_micro + 100U, events[order], CB_IDS[order]); - } - u64 end = core_timing.GetGlobalTimeNs().count(); - const double scheduling_time = static_cast(end - start); - const double timer_time = static_cast(TestTimerSpeed(core_timing)); - - while (core_timing.HasPendingEvents()) - ; - - REQUIRE(callbacks_ran_flags.all()); - - for (std::size_t i = 0; i < delays.size(); i++) { - const double delay = static_cast(delays[i]); - const double micro = delay / 1000.0f; - const double mili = micro / 1000.0f; - printf("HostTimer No Pausing Delay[%zu]: %.3f %.6f\n", i, micro, mili); - } - - const double micro = scheduling_time / 1000.0f; - const double mili = micro / 1000.0f; - printf("HostTimer No Pausing Scheduling Time: %.3f %.6f\n", micro, mili); - printf("HostTimer No Pausing Timer Time: %.3f %.6f\n", timer_time / 1000.f, - timer_time / 1000000.f); -} From 31651523968312433ebd267a74e86689107a7023 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Mon, 18 May 2020 13:08:53 -0400 Subject: [PATCH 223/252] Common/NativeClockx86: Reduce native clock accuracy further. --- src/common/x64/native_clock.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/x64/native_clock.h b/src/common/x64/native_clock.h index e853094d2..891a3bbfd 100644 --- a/src/common/x64/native_clock.h +++ b/src/common/x64/native_clock.h @@ -34,7 +34,7 @@ private: /// value used to reduce the native clocks accuracy as some apss rely on /// undefined behavior where the level of accuracy in the clock shouldn't /// be higher. - static constexpr u64 inaccuracy_mask = ~(0x100 - 1); + static constexpr u64 inaccuracy_mask = ~(0x400 - 1); SpinLock rtsc_serialize{}; u64 last_measure{}; From 39ddce1ab5bc40b94e0bc4c04acdd886d5ccc0a3 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Fri, 29 May 2020 13:31:47 -0400 Subject: [PATCH 224/252] Externals: Update Dynarmic. --- externals/dynarmic | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/externals/dynarmic b/externals/dynarmic index f4922a97f..3a50d444d 160000 --- a/externals/dynarmic +++ b/externals/dynarmic @@ -1 +1 @@ -Subproject commit f4922a97f6eb4b93decfbd80a881a7eac89d6890 +Subproject commit 3a50d444dcb66c868528dd12057f63dc623d09a5 From 272a87127a68bbb9d7c7984e619ee12702d7b8e0 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Fri, 29 May 2020 15:00:17 -0400 Subject: [PATCH 225/252] Services/NvFlinger: Do vSync in a sepparate thread on Multicore. --- src/core/core.cpp | 8 +++- src/core/core.h | 3 ++ src/core/hle/service/nvflinger/nvflinger.cpp | 49 ++++++++++++++++++-- src/core/hle/service/nvflinger/nvflinger.h | 14 ++++++ 4 files changed, 69 insertions(+), 5 deletions(-) diff --git a/src/core/core.cpp b/src/core/core.cpp index 50656697f..8256ec0fc 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -294,8 +294,6 @@ struct System::Impl { service_manager.reset(); cheat_engine.reset(); telemetry_session.reset(); - perf_stats.reset(); - gpu_core.reset(); device_memory.reset(); // Close all CPU/threading state @@ -307,6 +305,8 @@ struct System::Impl { // Close app loader app_loader.reset(); + gpu_core.reset(); + perf_stats.reset(); // Clear all applets applet_manager.ClearAll(); @@ -764,4 +764,8 @@ void System::ExitDynarmicProfile() { MicroProfileLeave(impl->microprofile_dynarmic[core], impl->dynarmic_ticks[core]); } +bool System::IsMulticore() const { + return impl->is_multicore; +} + } // namespace Core diff --git a/src/core/core.h b/src/core/core.h index d2d1fcc5b..133ecb8e1 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -381,6 +381,9 @@ public: /// Exit Dynarmic Microprofile void ExitDynarmicProfile(); + /// Tells if system is running on multicore. + bool IsMulticore() const; + private: System(); diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index b97f71350..2f44d3779 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp @@ -9,6 +9,7 @@ #include "common/logging/log.h" #include "common/microprofile.h" #include "common/scope_exit.h" +#include "common/thread.h" #include "core/core.h" #include "core/core_timing.h" #include "core/core_timing_util.h" @@ -30,6 +31,33 @@ namespace Service::NVFlinger { constexpr s64 frame_ticks = static_cast(1000000000 / 60); constexpr s64 frame_ticks_30fps = static_cast(1000000000 / 30); +void NVFlinger::VSyncThread(NVFlinger& nv_flinger) { + nv_flinger.SplitVSync(); +} + +void NVFlinger::SplitVSync() { + system.RegisterHostThread(); + std::string name = "yuzu:VSyncThread"; + MicroProfileOnThreadCreate(name.c_str()); + Common::SetCurrentThreadName(name.c_str()); + Common::SetCurrentThreadPriority(Common::ThreadPriority::High); + s64 delay = 0; + while (is_running) { + guard->lock(); + const s64 time_start = system.CoreTiming().GetGlobalTimeNs().count(); + Compose(); + const auto ticks = GetNextTicks(); + const s64 time_end = system.CoreTiming().GetGlobalTimeNs().count(); + const s64 time_passed = time_end - time_start; + const s64 next_time = std::max(0, ticks - time_passed - delay); + guard->unlock(); + if (next_time > 0) { + wait_event->WaitFor(std::chrono::nanoseconds{next_time}); + } + delay = (system.CoreTiming().GetGlobalTimeNs().count() - time_end) - next_time; + } +} + NVFlinger::NVFlinger(Core::System& system) : system(system) { displays.emplace_back(0, "Default", system); displays.emplace_back(1, "External", system); @@ -47,12 +75,25 @@ NVFlinger::NVFlinger(Core::System& system) : system(system) { this->system.CoreTiming().ScheduleEvent(std::max(0LL, ticks - ns_late), composition_event); }); - - system.CoreTiming().ScheduleEvent(frame_ticks, composition_event); + if (system.IsMulticore()) { + is_running = true; + wait_event = std::make_unique(); + vsync_thread = std::make_unique(VSyncThread, std::ref(*this)); + } else { + system.CoreTiming().ScheduleEvent(frame_ticks, composition_event); + } } NVFlinger::~NVFlinger() { - system.CoreTiming().UnscheduleEvent(composition_event, 0); + if (system.IsMulticore()) { + is_running = false; + wait_event->Set(); + vsync_thread->join(); + vsync_thread.reset(); + wait_event.reset(); + } else { + system.CoreTiming().UnscheduleEvent(composition_event, 0); + } } void NVFlinger::SetNVDrvInstance(std::shared_ptr instance) { @@ -200,10 +241,12 @@ void NVFlinger::Compose() { auto& gpu = system.GPU(); const auto& multi_fence = buffer->get().multi_fence; + guard->unlock(); for (u32 fence_id = 0; fence_id < multi_fence.num_fences; fence_id++) { const auto& fence = multi_fence.fences[fence_id]; gpu.WaitFence(fence.id, fence.value); } + guard->lock(); MicroProfileFlip(); diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h index 02c081494..4bc3d7ab2 100644 --- a/src/core/hle/service/nvflinger/nvflinger.h +++ b/src/core/hle/service/nvflinger/nvflinger.h @@ -4,16 +4,22 @@ #pragma once +#include #include #include #include #include #include #include +#include #include "common/common_types.h" #include "core/hle/kernel/object.h" +namespace Common { +class Event; +} // namespace Common + namespace Core::Timing { class CoreTiming; struct EventType; @@ -97,6 +103,10 @@ private: /// Finds the layer identified by the specified ID in the desired display. const VI::Layer* FindLayer(u64 display_id, u64 layer_id) const; + static void VSyncThread(NVFlinger& nv_flinger); + + void SplitVSync(); + std::shared_ptr nvdrv; std::vector displays; @@ -116,6 +126,10 @@ private: std::shared_ptr guard; Core::System& system; + + std::unique_ptr vsync_thread; + std::unique_ptr wait_event; + std::atomic is_running{}; }; } // namespace Service::NVFlinger From 7fd7d05838b88e9dd63a7329e29ea355669a5f18 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Fri, 29 May 2020 17:37:37 -0400 Subject: [PATCH 226/252] Common/Kernel: Corrections and small bug fixing. --- src/common/wall_clock.cpp | 7 +------ src/core/hle/kernel/svc.cpp | 4 ++-- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp index a46db6bbf..3afbdb898 100644 --- a/src/common/wall_clock.cpp +++ b/src/common/wall_clock.cpp @@ -68,12 +68,7 @@ std::unique_ptr CreateBestMatchingClock(u32 emulated_cpu_frequency, const auto& caps = GetCPUCaps(); u64 rtsc_frequency = 0; if (caps.invariant_tsc) { - if (caps.base_frequency != 0) { - rtsc_frequency = static_cast(caps.base_frequency) * 1000000U; - } - if (rtsc_frequency == 0) { - rtsc_frequency = EstimateRDTSCFrequency(); - } + rtsc_frequency = EstimateRDTSCFrequency(); } if (rtsc_frequency == 0) { return std::make_unique(emulated_cpu_frequency, diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 781032cd1..013ae9e34 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -344,9 +344,9 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) { SchedulerLock lock(system.Kernel()); auto* sync_object = thread->GetHLESyncObject(); sync_object->RemoveWaitingThread(SharedFrom(thread)); - - thread->InvokeHLECallback(SharedFrom(thread)); } + + thread->InvokeHLECallback(SharedFrom(thread)); } return thread->GetSignalingResult(); From e3d561fb842fa9ea986064a9a73001a5889f15d8 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Fri, 29 May 2020 17:37:57 -0400 Subject: [PATCH 227/252] Audio: Correct buffer release for host timing. --- src/audio_core/stream.cpp | 15 ++++++++++++++- src/audio_core/stream.h | 3 +++ src/core/core_timing.h | 5 +++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp index 6d5539b6b..dfc4805d9 100644 --- a/src/audio_core/stream.cpp +++ b/src/audio_core/stream.cpp @@ -66,6 +66,15 @@ s64 Stream::GetBufferReleaseNS(const Buffer& buffer) const { return ns.count(); } +s64 Stream::GetBufferReleaseNSHostTiming(const Buffer& buffer) const { + const std::size_t num_samples{buffer.GetSamples().size() / GetNumChannels()}; + /// DSP signals before playing the last sample, in HLE we emulate this in this way + s64 base_samples = std::max(static_cast(num_samples) - 1, 0); + const auto ns = + std::chrono::nanoseconds((static_cast(base_samples) * 1000000000ULL) / sample_rate); + return ns.count(); +} + static void VolumeAdjustSamples(std::vector& samples, float game_volume) { const float volume{std::clamp(Settings::Volume() - (1.0f - game_volume), 0.0f, 1.0f)}; @@ -105,7 +114,11 @@ void Stream::PlayNextBuffer() { sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples()); - core_timing.ScheduleEvent(GetBufferReleaseNS(*active_buffer), release_event, {}); + if (core_timing.IsHostTiming()) { + core_timing.ScheduleEvent(GetBufferReleaseNSHostTiming(*active_buffer), release_event, {}); + } else { + core_timing.ScheduleEvent(GetBufferReleaseNS(*active_buffer), release_event, {}); + } } void Stream::ReleaseActiveBuffer() { diff --git a/src/audio_core/stream.h b/src/audio_core/stream.h index 0663ce435..e309d60fe 100644 --- a/src/audio_core/stream.h +++ b/src/audio_core/stream.h @@ -98,6 +98,9 @@ private: /// Gets the number of core cycles when the specified buffer will be released s64 GetBufferReleaseNS(const Buffer& buffer) const; + /// Gets the number of core cycles when the specified buffer will be released + s64 GetBufferReleaseNSHostTiming(const Buffer& buffer) const; + u32 sample_rate; ///< Sample rate of the stream Format format; ///< Format of the stream float game_volume = 1.0f; ///< The volume the game currently has set diff --git a/src/core/core_timing.h b/src/core/core_timing.h index ed5de9b97..72faaab64 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h @@ -72,6 +72,11 @@ public: this->is_multicore = is_multicore; } + /// Check if it's using host timing. + bool IsHostTiming() const { + return is_multicore; + } + /// Pauses/Unpauses the execution of the timer thread. void Pause(bool is_paused); From ec11918323bec287c8b71cea6baee193c417fb38 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Thu, 18 Jun 2020 17:47:44 -0400 Subject: [PATCH 228/252] ArmDynarmic32: Setup CNTPCT correctly --- src/core/arm/dynarmic/arm_dynarmic_cp15.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp index d43e4dd70..54556e0f9 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp @@ -97,7 +97,7 @@ CallbackOrAccessTwoWords DynarmicCP15::CompileGetTwoWords(bool two, unsigned opc const auto callback = static_cast( [](Dynarmic::A32::Jit*, void* arg, u32, u32) -> u64 { ARM_Dynarmic_32& parent = *(ARM_Dynarmic_32*)arg; - return Timing::CpuCyclesToClockCycles(parent.system.CoreTiming().GetTicks()); + return parent.system.CoreTiming().GetClockTicks(); }); return Dynarmic::A32::Coprocessor::Callback{callback, (void*)&parent}; } From 22ceaca2f415b962416637db9fff9782575746ce Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Thu, 18 Jun 2020 18:15:19 -0400 Subject: [PATCH 229/252] SVC: Add GetThreadPriority32 & SetThreadPriority32 --- src/core/hle/kernel/svc.cpp | 14 ++++++++++++-- src/core/hle/kernel/svc_wrap.h | 18 ++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 013ae9e34..5674d9558 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -1135,6 +1135,10 @@ static ResultCode SetThreadPriority(Core::System& system, Handle handle, u32 pri return RESULT_SUCCESS; } +static ResultCode SetThreadPriority32(Core::System& system, Handle handle, u32 priority) { + return SetThreadPriority(system, handle, priority); +} + /// Get which CPU core is executing the current thread static u32 GetCurrentProcessorNumber(Core::System& system) { LOG_TRACE(Kernel_SVC, "called"); @@ -1933,6 +1937,12 @@ static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle, return thread->SetCoreAndAffinityMask(core, affinity_mask); } +static ResultCode SetThreadCoreMask32(Core::System& system, Handle thread_handle, u32 core, + u32 affinity_mask_low, u32 affinity_mask_high) { + const u64 affinity_mask = static_cast(affinity_mask_low) | (static_cast(affinity_mask_high) << 32); + return SetThreadCoreMask(system, thread_handle, core, affinity_mask); +} + static ResultCode CreateEvent(Core::System& system, Handle* write_handle, Handle* read_handle) { LOG_DEBUG(Kernel_SVC, "called"); @@ -2206,9 +2216,9 @@ static const FunctionDef SVC_Table_32[] = { {0x0a, nullptr, "ExitThread32"}, {0x0b, nullptr, "SleepThread32"}, {0x0c, SvcWrap32, "GetThreadPriority32"}, - {0x0d, nullptr, "SetThreadPriority32"}, + {0x0d, SvcWrap32, "SetThreadPriority32"}, {0x0e, nullptr, "GetThreadCoreMask32"}, - {0x0f, nullptr, "SetThreadCoreMask32"}, + {0x0f, SvcWrap32, "SetThreadCoreMask32"}, {0x10, nullptr, "GetCurrentProcessorNumber32"}, {0x11, nullptr, "SignalEvent32"}, {0x12, nullptr, "ClearEvent32"}, diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index 7d735e3fa..fd4edba2a 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h @@ -399,6 +399,24 @@ void SvcWrap32(Core::System& system) { func(system, static_cast(Param(system, 0)), static_cast(Param(system, 1))); } +// Used by SetThreadPriority32 +template +void SvcWrap32(Core::System& system) { + const u32 retval = + func(system, static_cast(Param(system, 0)), static_cast(Param(system, 1))).raw; + FuncReturn(system, retval); +} + +// Used by SetThreadCoreMask32 +template +void SvcWrap32(Core::System& system) { + const u32 retval = + func(system, static_cast(Param(system, 0)), static_cast(Param(system, 1)), + static_cast(Param(system, 2)), static_cast(Param(system, 3))) + .raw; + FuncReturn(system, retval); +} + // Used by SendSyncRequest32 template void SvcWrap32(Core::System& system) { From b8df61c642310aeab3118f2637d4447740b8fcd3 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Thu, 18 Jun 2020 19:56:59 -0400 Subject: [PATCH 230/252] ARM: Update Dynarmic and Setup A32 according to latest interface. --- externals/dynarmic | 2 +- src/core/CMakeLists.txt | 8 ++ src/core/arm/dynarmic/arm_dynarmic_32.cpp | 41 +++++++++- src/core/arm/dynarmic/arm_dynarmic_32.h | 2 +- src/core/arm/dynarmic/arm_dynarmic_64.cpp | 65 +--------------- src/core/arm/dynarmic/arm_dynarmic_64.h | 25 ------ .../arm/dynarmic/arm_exclusive_monitor.cpp | 76 +++++++++++++++++++ src/core/arm/dynarmic/arm_exclusive_monitor.h | 48 ++++++++++++ src/core/arm/exclusive_monitor.cpp | 2 +- 9 files changed, 175 insertions(+), 94 deletions(-) create mode 100644 src/core/arm/dynarmic/arm_exclusive_monitor.cpp create mode 100644 src/core/arm/dynarmic/arm_exclusive_monitor.h diff --git a/externals/dynarmic b/externals/dynarmic index 3a50d444d..b759773b3 160000 --- a/externals/dynarmic +++ b/externals/dynarmic @@ -1 +1 @@ -Subproject commit 3a50d444dcb66c868528dd12057f63dc623d09a5 +Subproject commit b759773b3b76c62200ecd4e097ec6ecfd825aacb diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 653b7620b..f87d67db5 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -9,6 +9,14 @@ add_library(core STATIC arm/arm_interface.cpp arm/cpu_interrupt_handler.cpp arm/cpu_interrupt_handler.h + arm/dynarmic/arm_dynarmic_32.cpp + arm/dynarmic/arm_dynarmic_32.h + arm/dynarmic/arm_dynarmic_64.cpp + arm/dynarmic/arm_dynarmic_64.h + arm/dynarmic/arm_dynarmic_cp15.cpp + arm/dynarmic/arm_dynarmic_cp15.h + arm/dynarmic/arm_exclusive_monitor.cpp + arm/dynarmic/arm_exclusive_monitor.h arm/exclusive_monitor.cpp arm/exclusive_monitor.h arm/unicorn/arm_unicorn.cpp diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index 71d7e169a..5df4fc079 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -7,14 +7,17 @@ #include #include #include +#include "common/logging/log.h" +#include "common/page_table.h" #include "core/arm/cpu_interrupt_handler.h" #include "core/arm/dynarmic/arm_dynarmic_32.h" -#include "core/arm/dynarmic/arm_dynarmic_64.h" #include "core/arm/dynarmic/arm_dynarmic_cp15.h" +#include "core/arm/dynarmic/arm_exclusive_monitor.h" #include "core/core.h" #include "core/core_timing.h" #include "core/hle/kernel/svc.h" #include "core/memory.h" +#include "core/settings.h" namespace Core { @@ -48,6 +51,19 @@ public: parent.system.Memory().Write64(vaddr, value); } + bool MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) override { + return parent.system.Memory().WriteExclusive8(vaddr, value, expected); + } + bool MemoryWriteExclusive16(u32 vaddr, u16 value, u16 expected) override { + return parent.system.Memory().WriteExclusive16(vaddr, value, expected); + } + bool MemoryWriteExclusive32(u32 vaddr, u32 value, u32 expected) override { + return parent.system.Memory().WriteExclusive32(vaddr, value, expected); + } + bool MemoryWriteExclusive64(u32 vaddr, u64 value, u64 expected) override { + return parent.system.Memory().WriteExclusive64(vaddr, value, expected); + } + void InterpreterFallback(u32 pc, std::size_t num_instructions) override { UNIMPLEMENTED_MSG("This should never happen, pc = {:08X}, code = {:08X}", pc, MemoryReadCode(pc)); @@ -110,6 +126,27 @@ std::shared_ptr ARM_Dynarmic_32::MakeJit(Common::PageTable& // config.page_table = &page_table.pointers; config.coprocessors[15] = cp15; config.define_unpredictable_behaviour = true; + static constexpr std::size_t PAGE_BITS = 12; + static constexpr std::size_t NUM_PAGE_TABLE_ENTRIES = 1 << (32 - PAGE_BITS); + config.page_table = reinterpret_cast*>( + page_table.pointers.data()); + config.absolute_offset_page_table = true; + config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128; + config.only_detect_misalignment_via_page_table_on_page_boundary = true; + + // Multi-process state + config.processor_id = core_index; + config.global_monitor = &exclusive_monitor.monitor; + + // Timing + config.wall_clock_cntpct = uses_wall_clock; + + // Optimizations + if (Settings::values.disable_cpu_opt) { + config.enable_optimizations = false; + config.enable_fast_dispatch = false; + } + return std::make_unique(config); } @@ -178,7 +215,7 @@ void ARM_Dynarmic_32::SetTPIDR_EL0(u64 value) { } void ARM_Dynarmic_32::ChangeProcessorId(std::size_t new_core_id) { - // jit->ChangeProcessorId(new_core_id); + jit->ChangeProcessorID(new_core_id); } void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) { diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h index 8afd15c8b..d9c0bfede 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.h +++ b/src/core/arm/dynarmic/arm_dynarmic_32.h @@ -9,7 +9,7 @@ #include #include -#include +#include #include "common/common_types.h" #include "common/hash.h" #include "core/arm/arm_interface.h" diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 8e8f7ce0a..35a99e28a 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -10,6 +10,7 @@ #include "common/page_table.h" #include "core/arm/cpu_interrupt_handler.h" #include "core/arm/dynarmic/arm_dynarmic_64.h" +#include "core/arm/dynarmic/arm_exclusive_monitor.h" #include "core/core.h" #include "core/core_timing.h" #include "core/core_timing_util.h" @@ -323,68 +324,4 @@ void ARM_Dynarmic_64::PageTableChanged(Common::PageTable& page_table, jit_cache.emplace(key, jit); } -DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(Memory::Memory& memory, std::size_t core_count) - : monitor(core_count), memory{memory} {} - -DynarmicExclusiveMonitor::~DynarmicExclusiveMonitor() = default; - -u8 DynarmicExclusiveMonitor::ExclusiveRead8(std::size_t core_index, VAddr addr) { - return monitor.ReadAndMark(core_index, addr, [&]() -> u8 { return memory.Read8(addr); }); -} - -u16 DynarmicExclusiveMonitor::ExclusiveRead16(std::size_t core_index, VAddr addr) { - return monitor.ReadAndMark(core_index, addr, [&]() -> u16 { return memory.Read16(addr); }); -} - -u32 DynarmicExclusiveMonitor::ExclusiveRead32(std::size_t core_index, VAddr addr) { - return monitor.ReadAndMark(core_index, addr, [&]() -> u32 { return memory.Read32(addr); }); -} - -u64 DynarmicExclusiveMonitor::ExclusiveRead64(std::size_t core_index, VAddr addr) { - return monitor.ReadAndMark(core_index, addr, [&]() -> u64 { return memory.Read64(addr); }); -} - -u128 DynarmicExclusiveMonitor::ExclusiveRead128(std::size_t core_index, VAddr addr) { - return monitor.ReadAndMark(core_index, addr, [&]() -> u128 { - u128 result; - result[0] = memory.Read64(addr); - result[1] = memory.Read64(addr + 8); - return result; - }); -} - -void DynarmicExclusiveMonitor::ClearExclusive() { - monitor.Clear(); -} - -bool DynarmicExclusiveMonitor::ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) { - return monitor.DoExclusiveOperation(core_index, vaddr, [&](u8 expected) -> bool { - return memory.WriteExclusive8(vaddr, value, expected); - }); -} - -bool DynarmicExclusiveMonitor::ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) { - return monitor.DoExclusiveOperation(core_index, vaddr, [&](u16 expected) -> bool { - return memory.WriteExclusive16(vaddr, value, expected); - }); -} - -bool DynarmicExclusiveMonitor::ExclusiveWrite32(std::size_t core_index, VAddr vaddr, u32 value) { - return monitor.DoExclusiveOperation(core_index, vaddr, [&](u32 expected) -> bool { - return memory.WriteExclusive32(vaddr, value, expected); - }); -} - -bool DynarmicExclusiveMonitor::ExclusiveWrite64(std::size_t core_index, VAddr vaddr, u64 value) { - return monitor.DoExclusiveOperation(core_index, vaddr, [&](u64 expected) -> bool { - return memory.WriteExclusive64(vaddr, value, expected); - }); -} - -bool DynarmicExclusiveMonitor::ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) { - return monitor.DoExclusiveOperation(core_index, vaddr, [&](u128 expected) -> bool { - return memory.WriteExclusive128(vaddr, value, expected); - }); -} - } // namespace Core diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h index 31ec16521..c74fcbcea 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.h +++ b/src/core/arm/dynarmic/arm_dynarmic_64.h @@ -8,7 +8,6 @@ #include #include -#include #include "common/common_types.h" #include "common/hash.h" #include "core/arm/arm_interface.h" @@ -78,28 +77,4 @@ private: DynarmicExclusiveMonitor& exclusive_monitor; }; -class DynarmicExclusiveMonitor final : public ExclusiveMonitor { -public: - explicit DynarmicExclusiveMonitor(Memory::Memory& memory, std::size_t core_count); - ~DynarmicExclusiveMonitor() override; - - u8 ExclusiveRead8(std::size_t core_index, VAddr addr) override; - u16 ExclusiveRead16(std::size_t core_index, VAddr addr) override; - u32 ExclusiveRead32(std::size_t core_index, VAddr addr) override; - u64 ExclusiveRead64(std::size_t core_index, VAddr addr) override; - u128 ExclusiveRead128(std::size_t core_index, VAddr addr) override; - void ClearExclusive() override; - - bool ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) override; - bool ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) override; - bool ExclusiveWrite32(std::size_t core_index, VAddr vaddr, u32 value) override; - bool ExclusiveWrite64(std::size_t core_index, VAddr vaddr, u64 value) override; - bool ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) override; - -private: - friend class ARM_Dynarmic_64; - Dynarmic::A64::ExclusiveMonitor monitor; - Core::Memory::Memory& memory; -}; - } // namespace Core diff --git a/src/core/arm/dynarmic/arm_exclusive_monitor.cpp b/src/core/arm/dynarmic/arm_exclusive_monitor.cpp new file mode 100644 index 000000000..4e209f6a5 --- /dev/null +++ b/src/core/arm/dynarmic/arm_exclusive_monitor.cpp @@ -0,0 +1,76 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include "core/arm/dynarmic/arm_exclusive_monitor.h" +#include "core/memory.h" + +namespace Core { + +DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(Memory::Memory& memory, std::size_t core_count) + : monitor(core_count), memory{memory} {} + +DynarmicExclusiveMonitor::~DynarmicExclusiveMonitor() = default; + +u8 DynarmicExclusiveMonitor::ExclusiveRead8(std::size_t core_index, VAddr addr) { + return monitor.ReadAndMark(core_index, addr, [&]() -> u8 { return memory.Read8(addr); }); +} + +u16 DynarmicExclusiveMonitor::ExclusiveRead16(std::size_t core_index, VAddr addr) { + return monitor.ReadAndMark(core_index, addr, [&]() -> u16 { return memory.Read16(addr); }); +} + +u32 DynarmicExclusiveMonitor::ExclusiveRead32(std::size_t core_index, VAddr addr) { + return monitor.ReadAndMark(core_index, addr, [&]() -> u32 { return memory.Read32(addr); }); +} + +u64 DynarmicExclusiveMonitor::ExclusiveRead64(std::size_t core_index, VAddr addr) { + return monitor.ReadAndMark(core_index, addr, [&]() -> u64 { return memory.Read64(addr); }); +} + +u128 DynarmicExclusiveMonitor::ExclusiveRead128(std::size_t core_index, VAddr addr) { + return monitor.ReadAndMark(core_index, addr, [&]() -> u128 { + u128 result; + result[0] = memory.Read64(addr); + result[1] = memory.Read64(addr + 8); + return result; + }); +} + +void DynarmicExclusiveMonitor::ClearExclusive() { + monitor.Clear(); +} + +bool DynarmicExclusiveMonitor::ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) { + return monitor.DoExclusiveOperation(core_index, vaddr, [&](u8 expected) -> bool { + return memory.WriteExclusive8(vaddr, value, expected); + }); +} + +bool DynarmicExclusiveMonitor::ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) { + return monitor.DoExclusiveOperation(core_index, vaddr, [&](u16 expected) -> bool { + return memory.WriteExclusive16(vaddr, value, expected); + }); +} + +bool DynarmicExclusiveMonitor::ExclusiveWrite32(std::size_t core_index, VAddr vaddr, u32 value) { + return monitor.DoExclusiveOperation(core_index, vaddr, [&](u32 expected) -> bool { + return memory.WriteExclusive32(vaddr, value, expected); + }); +} + +bool DynarmicExclusiveMonitor::ExclusiveWrite64(std::size_t core_index, VAddr vaddr, u64 value) { + return monitor.DoExclusiveOperation(core_index, vaddr, [&](u64 expected) -> bool { + return memory.WriteExclusive64(vaddr, value, expected); + }); +} + +bool DynarmicExclusiveMonitor::ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) { + return monitor.DoExclusiveOperation(core_index, vaddr, [&](u128 expected) -> bool { + return memory.WriteExclusive128(vaddr, value, expected); + }); +} + +} // namespace Core diff --git a/src/core/arm/dynarmic/arm_exclusive_monitor.h b/src/core/arm/dynarmic/arm_exclusive_monitor.h new file mode 100644 index 000000000..964f4a55d --- /dev/null +++ b/src/core/arm/dynarmic/arm_exclusive_monitor.h @@ -0,0 +1,48 @@ +// Copyright 2020 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +#include + +#include "common/common_types.h" +#include "core/arm/dynarmic/arm_dynarmic_32.h" +#include "core/arm/dynarmic/arm_dynarmic_64.h" +#include "core/arm/exclusive_monitor.h" + +namespace Core::Memory { +class Memory; +} + +namespace Core { + +class DynarmicExclusiveMonitor final : public ExclusiveMonitor { +public: + explicit DynarmicExclusiveMonitor(Memory::Memory& memory, std::size_t core_count); + ~DynarmicExclusiveMonitor() override; + + u8 ExclusiveRead8(std::size_t core_index, VAddr addr) override; + u16 ExclusiveRead16(std::size_t core_index, VAddr addr) override; + u32 ExclusiveRead32(std::size_t core_index, VAddr addr) override; + u64 ExclusiveRead64(std::size_t core_index, VAddr addr) override; + u128 ExclusiveRead128(std::size_t core_index, VAddr addr) override; + void ClearExclusive() override; + + bool ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) override; + bool ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) override; + bool ExclusiveWrite32(std::size_t core_index, VAddr vaddr, u32 value) override; + bool ExclusiveWrite64(std::size_t core_index, VAddr vaddr, u64 value) override; + bool ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) override; + +private: + friend class ARM_Dynarmic_32; + friend class ARM_Dynarmic_64; + Dynarmic::ExclusiveMonitor monitor; + Core::Memory::Memory& memory; +}; + +} // namespace Core diff --git a/src/core/arm/exclusive_monitor.cpp b/src/core/arm/exclusive_monitor.cpp index b32401e0b..d8cba369d 100644 --- a/src/core/arm/exclusive_monitor.cpp +++ b/src/core/arm/exclusive_monitor.cpp @@ -3,7 +3,7 @@ // Refer to the license.txt file included. #ifdef ARCHITECTURE_x86_64 -#include "core/arm/dynarmic/arm_dynarmic_64.h" +#include "core/arm/dynarmic/arm_exclusive_monitor.h" #endif #include "core/arm/exclusive_monitor.h" #include "core/memory.h" From ce350e7ce03b8d9e3e2c8ff8774f9b929a70047f Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Thu, 18 Jun 2020 20:33:04 -0400 Subject: [PATCH 231/252] SVC: Add GetCurrentProcessorNumber32, CreateTransferMemory32, SetMemoryAttribute32 --- src/core/hle/kernel/svc.cpp | 29 +++++++++++++++++++++++------ src/core/hle/kernel/svc_wrap.h | 16 ++++++++++++++++ 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 5674d9558..1d47a2779 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -232,6 +232,12 @@ static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 si static_cast(attribute)); } +static ResultCode SetMemoryAttribute32(Core::System& system, u32 address, u32 size, u32 mask, + u32 attribute) { + return SetMemoryAttribute(system, static_cast(address), static_cast(size), + mask, attribute); +} + /// Maps a memory range into a different range. static ResultCode MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) { std::lock_guard lock{HLE::g_hle_lock}; @@ -1136,7 +1142,7 @@ static ResultCode SetThreadPriority(Core::System& system, Handle handle, u32 pri } static ResultCode SetThreadPriority32(Core::System& system, Handle handle, u32 priority) { - return SetThreadPriority(system, handle, priority); + return SetThreadPriority(system, handle, priority); } /// Get which CPU core is executing the current thread @@ -1145,6 +1151,10 @@ static u32 GetCurrentProcessorNumber(Core::System& system) { return static_cast(system.CurrentPhysicalCore().CoreIndex()); } +static u32 GetCurrentProcessorNumber32(Core::System& system) { + return GetCurrentProcessorNumber(system); +} + static ResultCode MapSharedMemory(Core::System& system, Handle shared_memory_handle, VAddr addr, u64 size, u32 permissions) { std::lock_guard lock{HLE::g_hle_lock}; @@ -1861,6 +1871,12 @@ static ResultCode CreateTransferMemory(Core::System& system, Handle* handle, VAd return RESULT_SUCCESS; } +static ResultCode CreateTransferMemory32(Core::System& system, Handle* handle, u32 addr, u32 size, + u32 permissions) { + return CreateTransferMemory(system, handle, static_cast(addr), + static_cast(size), permissions); +} + static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle, u32* core, u64* mask) { LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle); @@ -1938,8 +1954,9 @@ static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle, } static ResultCode SetThreadCoreMask32(Core::System& system, Handle thread_handle, u32 core, - u32 affinity_mask_low, u32 affinity_mask_high) { - const u64 affinity_mask = static_cast(affinity_mask_low) | (static_cast(affinity_mask_high) << 32); + u32 affinity_mask_low, u32 affinity_mask_high) { + const u64 affinity_mask = + static_cast(affinity_mask_low) | (static_cast(affinity_mask_high) << 32); return SetThreadCoreMask(system, thread_handle, core, affinity_mask); } @@ -2206,7 +2223,7 @@ static const FunctionDef SVC_Table_32[] = { {0x00, nullptr, "Unknown"}, {0x01, SvcWrap32, "SetHeapSize32"}, {0x02, nullptr, "Unknown"}, - {0x03, nullptr, "SetMemoryAttribute32"}, + {0x03, SvcWrap32, "SetMemoryAttribute32"}, {0x04, nullptr, "MapMemory32"}, {0x05, nullptr, "UnmapMemory32"}, {0x06, SvcWrap32, "QueryMemory32"}, @@ -2219,12 +2236,12 @@ static const FunctionDef SVC_Table_32[] = { {0x0d, SvcWrap32, "SetThreadPriority32"}, {0x0e, nullptr, "GetThreadCoreMask32"}, {0x0f, SvcWrap32, "SetThreadCoreMask32"}, - {0x10, nullptr, "GetCurrentProcessorNumber32"}, + {0x10, SvcWrap32, "GetCurrentProcessorNumber32"}, {0x11, nullptr, "SignalEvent32"}, {0x12, nullptr, "ClearEvent32"}, {0x13, nullptr, "MapSharedMemory32"}, {0x14, nullptr, "UnmapSharedMemory32"}, - {0x15, nullptr, "CreateTransferMemory32"}, + {0x15, SvcWrap32, "CreateTransferMemory32"}, {0x16, SvcWrap32, "CloseHandle32"}, {0x17, nullptr, "ResetSignal32"}, {0x18, SvcWrap32, "WaitSynchronization32"}, diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index fd4edba2a..ba90c354f 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h @@ -357,6 +357,12 @@ void SvcWrap32(Core::System& system) { func(system, Param32(system, 0), Param32(system, 1), Param32(system, 2)).raw); } +// Used by GetCurrentProcessorNumber32 +template +void SvcWrap32(Core::System& system) { + FuncReturn(system, func(system)); +} + // Used by GetInfo32 template void SvcWrap32(Core::System& system) { @@ -423,6 +429,16 @@ void SvcWrap32(Core::System& system) { FuncReturn(system, func(system, static_cast(Param(system, 0))).raw); } +// Used by CreateTransferMemory32 +template +void SvcWrap32(Core::System& system) { + Handle handle = 0; + const u32 retval = + func(system, &handle, Param32(system, 1), Param32(system, 2), Param32(system, 3)).raw; + system.CurrentArmInterface().SetReg(1, handle); + FuncReturn(system, retval); +} + // Used by WaitSynchronization32 template void SvcWrap32(Core::System& system) { From 4105f38022a525aab2e7d4288f121b4f0a0dd7b2 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Fri, 19 Jun 2020 19:40:07 -0400 Subject: [PATCH 232/252] SVC: Implement 32-bits wrappers and update Dynarmic. --- externals/dynarmic | 2 +- src/core/arm/arm_interface.h | 5 +- src/core/arm/dynarmic/arm_dynarmic_32.cpp | 8 +- src/core/hle/kernel/svc.cpp | 198 +++++++++++++++++++--- src/core/hle/kernel/svc_wrap.h | 105 +++++++++++- 5 files changed, 283 insertions(+), 35 deletions(-) diff --git a/externals/dynarmic b/externals/dynarmic index b759773b3..4f967387c 160000 --- a/externals/dynarmic +++ b/externals/dynarmic @@ -1 +1 @@ -Subproject commit b759773b3b76c62200ecd4e097ec6ecfd825aacb +Subproject commit 4f967387c07365b7ea35d2fa3e19b7df8872a09b diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index fbdce4134..0c1d6ac39 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h @@ -33,16 +33,15 @@ public: struct ThreadContext32 { std::array cpu_registers{}; + std::array extension_registers{}; u32 cpsr{}; - std::array padding{}; - std::array fprs{}; u32 fpscr{}; u32 fpexc{}; u32 tpidr{}; }; // Internally within the kernel, it expects the AArch32 version of the // thread context to be 344 bytes in size. - static_assert(sizeof(ThreadContext32) == 0x158); + static_assert(sizeof(ThreadContext32) == 0x150); struct ThreadContext64 { std::array cpu_registers{}; diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index 5df4fc079..cfda12098 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -222,13 +222,17 @@ void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) { Dynarmic::A32::Context context; jit->SaveContext(context); ctx.cpu_registers = context.Regs(); + ctx.extension_registers = context.ExtRegs(); ctx.cpsr = context.Cpsr(); + ctx.fpscr = context.Fpscr(); } void ARM_Dynarmic_32::LoadContext(const ThreadContext32& ctx) { Dynarmic::A32::Context context; context.Regs() = ctx.cpu_registers; + context.ExtRegs() = ctx.extension_registers; context.SetCpsr(ctx.cpsr); + context.SetFpscr(ctx.fpscr); jit->LoadContext(context); } @@ -243,7 +247,9 @@ void ARM_Dynarmic_32::ClearInstructionCache() { jit->ClearCache(); } -void ARM_Dynarmic_32::ClearExclusiveState() {} +void ARM_Dynarmic_32::ClearExclusiveState() { + jit->ClearExclusiveState(); +} void ARM_Dynarmic_32::PageTableChanged(Common::PageTable& page_table, std::size_t new_address_space_size_in_bits) { diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 1d47a2779..5db19dcf3 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -254,6 +254,11 @@ static ResultCode MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr return page_table.Map(dst_addr, src_addr, size); } +static ResultCode MapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) { + return MapMemory(system, static_cast(dst_addr), static_cast(src_addr), + static_cast(size)); +} + /// Unmaps a region that was previously mapped with svcMapMemory static ResultCode UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) { std::lock_guard lock{HLE::g_hle_lock}; @@ -270,6 +275,11 @@ static ResultCode UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_ad return page_table.Unmap(dst_addr, src_addr, size); } +static ResultCode UnmapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) { + return UnmapMemory(system, static_cast(dst_addr), static_cast(src_addr), + static_cast(size)); +} + /// Connect to an OS service given the port name, returns the handle to the port to out static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle, VAddr port_name_address) { @@ -417,6 +427,15 @@ static ResultCode GetProcessId(Core::System& system, u64* process_id, Handle han return ERR_INVALID_HANDLE; } +static ResultCode GetProcessId32(Core::System& system, u32* process_id_low, u32* process_id_high, + Handle handle) { + u64 process_id{}; + const auto result = GetProcessId(system, &process_id, handle); + *process_id_low = static_cast(process_id); + *process_id_high = static_cast(process_id >> 32); + return result; +} + /// Wait for the given handles to synchronize, timeout after the specified nanoseconds static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr handles_address, u64 handle_count, s64 nano_seconds) { @@ -484,6 +503,10 @@ static ResultCode CancelSynchronization(Core::System& system, Handle thread_hand return RESULT_SUCCESS; } +static ResultCode CancelSynchronization32(Core::System& system, Handle thread_handle) { + return CancelSynchronization(system, thread_handle); +} + /// Attempts to locks a mutex, creating it if it does not already exist static ResultCode ArbitrateLock(Core::System& system, Handle holding_thread_handle, VAddr mutex_addr, Handle requesting_thread_handle) { @@ -508,6 +531,12 @@ static ResultCode ArbitrateLock(Core::System& system, Handle holding_thread_hand requesting_thread_handle); } +static ResultCode ArbitrateLock32(Core::System& system, Handle holding_thread_handle, + u32 mutex_addr, Handle requesting_thread_handle) { + return ArbitrateLock(system, holding_thread_handle, static_cast(mutex_addr), + requesting_thread_handle); +} + /// Unlock a mutex static ResultCode ArbitrateUnlock(Core::System& system, VAddr mutex_addr) { LOG_TRACE(Kernel_SVC, "called mutex_addr=0x{:X}", mutex_addr); @@ -527,6 +556,10 @@ static ResultCode ArbitrateUnlock(Core::System& system, VAddr mutex_addr) { return current_process->GetMutex().Release(mutex_addr); } +static ResultCode ArbitrateUnlock32(Core::System& system, u32 mutex_addr) { + return ArbitrateUnlock(system, static_cast(mutex_addr)); +} + enum class BreakType : u32 { Panic = 0, AssertionFailed = 1, @@ -645,6 +678,10 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) { } } +static void Break32(Core::System& system, u32 reason, u32 info1, u32 info2) { + Break(system, reason, static_cast(info1), static_cast(info2)); +} + /// Used to output a message on a debug hardware unit - does nothing on a retail unit static void OutputDebugString([[maybe_unused]] Core::System& system, VAddr address, u64 len) { if (len == 0) { @@ -973,6 +1010,10 @@ static ResultCode MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) return page_table.MapPhysicalMemory(addr, size); } +static ResultCode MapPhysicalMemory32(Core::System& system, u32 addr, u32 size) { + return MapPhysicalMemory(system, static_cast(addr), static_cast(size)); +} + /// Unmaps memory previously mapped via MapPhysicalMemory static ResultCode UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size) { std::lock_guard lock{HLE::g_hle_lock}; @@ -1023,6 +1064,10 @@ static ResultCode UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size return page_table.UnmapPhysicalMemory(addr, size); } +static ResultCode UnmapPhysicalMemory32(Core::System& system, u32 addr, u32 size) { + return UnmapPhysicalMemory(system, static_cast(addr), static_cast(size)); +} + /// Sets the thread activity static ResultCode SetThreadActivity(Core::System& system, Handle handle, u32 activity) { LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", handle, activity); @@ -1055,6 +1100,10 @@ static ResultCode SetThreadActivity(Core::System& system, Handle handle, u32 act return thread->SetActivity(static_cast(activity)); } +static ResultCode SetThreadActivity32(Core::System& system, Handle handle, u32 activity) { + return SetThreadActivity(system, handle, activity); +} + /// Gets the thread context static ResultCode GetThreadContext(Core::System& system, VAddr thread_context, Handle handle) { LOG_DEBUG(Kernel_SVC, "called, context=0x{:08X}, thread=0x{:X}", thread_context, handle); @@ -1096,6 +1145,10 @@ static ResultCode GetThreadContext(Core::System& system, VAddr thread_context, H return RESULT_SUCCESS; } +static ResultCode GetThreadContext32(Core::System& system, u32 thread_context, Handle handle) { + return GetThreadContext(system, static_cast(thread_context), handle); +} + /// Gets the priority for the specified thread static ResultCode GetThreadPriority(Core::System& system, u32* priority, Handle handle) { LOG_TRACE(Kernel_SVC, "called"); @@ -1228,6 +1281,12 @@ static ResultCode MapSharedMemory(Core::System& system, Handle shared_memory_han return shared_memory->Map(*current_process, addr, size, permission_type); } +static ResultCode MapSharedMemory32(Core::System& system, Handle shared_memory_handle, u32 addr, + u32 size, u32 permissions) { + return MapSharedMemory(system, shared_memory_handle, static_cast(addr), + static_cast(size), permissions); +} + static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address, VAddr page_info_address, Handle process_handle, VAddr address) { @@ -1426,6 +1485,10 @@ static void ExitProcess(Core::System& system) { system.CurrentScheduler().GetCurrentThread()->Stop(); } +static void ExitProcess32(Core::System& system) { + ExitProcess(system); +} + /// Creates a new thread static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg, VAddr stack_top, u32 priority, s32 processor_id) { @@ -1489,6 +1552,12 @@ static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr e return RESULT_SUCCESS; } +static ResultCode CreateThread32(Core::System& system, Handle* out_handle, u32 priority, + u32 entry_point, u32 arg, u32 stack_top, s32 processor_id) { + return CreateThread(system, out_handle, static_cast(entry_point), static_cast(arg), + static_cast(stack_top), priority, processor_id); +} + /// Starts the thread for the provided handle static ResultCode StartThread(Core::System& system, Handle thread_handle) { LOG_DEBUG(Kernel_SVC, "called thread=0x{:08X}", thread_handle); @@ -1506,6 +1575,10 @@ static ResultCode StartThread(Core::System& system, Handle thread_handle) { return thread->Start(); } +static ResultCode StartThread32(Core::System& system, Handle thread_handle) { + return StartThread(system, thread_handle); +} + /// Called when a thread exits static void ExitThread(Core::System& system) { LOG_DEBUG(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC()); @@ -1515,6 +1588,10 @@ static void ExitThread(Core::System& system) { current_thread->Stop(); } +static void ExitThread32(Core::System& system) { + ExitThread(system); +} + /// Sleep the current thread static void SleepThread(Core::System& system, s64 nanoseconds) { LOG_DEBUG(Kernel_SVC, "called nanoseconds={}", nanoseconds); @@ -1561,6 +1638,12 @@ static void SleepThread(Core::System& system, s64 nanoseconds) { } } +static void SleepThread32(Core::System& system, u32 nanoseconds_low, u32 nanoseconds_high) { + const s64 nanoseconds = static_cast(static_cast(nanoseconds_low) | + (static_cast(nanoseconds_high) << 32)); + SleepThread(system, nanoseconds); +} + /// Wait process wide key atomic static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr mutex_addr, VAddr condition_variable_addr, Handle thread_handle, @@ -1640,6 +1723,16 @@ static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr mutex_add return current_thread->GetSignalingResult(); } +static ResultCode WaitProcessWideKeyAtomic32(Core::System& system, u32 mutex_addr, + u32 condition_variable_addr, Handle thread_handle, + u32 nanoseconds_low, u32 nanoseconds_high) { + const s64 nanoseconds = + static_cast(nanoseconds_low | (static_cast(nanoseconds_high) << 32)); + return WaitProcessWideKeyAtomic(system, static_cast(mutex_addr), + static_cast(condition_variable_addr), thread_handle, + nanoseconds); +} + /// Signal process wide key static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_addr, s32 target) { LOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x{:X}, target=0x{:08X}", @@ -1741,6 +1834,12 @@ static ResultCode WaitForAddress(Core::System& system, VAddr address, u32 type, return result; } +static ResultCode WaitForAddress32(Core::System& system, u32 address, u32 type, s32 value, + u32 timeout_low, u32 timeout_high) { + s64 timeout = static_cast(timeout_low | (static_cast(timeout_high) << 32)); + return WaitForAddress(system, static_cast(address), type, value, timeout); +} + // Signals to an address (via Address Arbiter) static ResultCode SignalToAddress(Core::System& system, VAddr address, u32 type, s32 value, s32 num_to_wake) { @@ -1764,6 +1863,11 @@ static ResultCode SignalToAddress(Core::System& system, VAddr address, u32 type, return address_arbiter.SignalToAddress(address, signal_type, value, num_to_wake); } +static ResultCode SignalToAddress32(Core::System& system, u32 address, u32 type, s32 value, + s32 num_to_wake) { + return SignalToAddress(system, static_cast(address), type, value, num_to_wake); +} + static void KernelDebug([[maybe_unused]] Core::System& system, [[maybe_unused]] u32 kernel_debug_type, [[maybe_unused]] u64 param1, [[maybe_unused]] u64 param2, [[maybe_unused]] u64 param3) { @@ -1791,6 +1895,12 @@ static u64 GetSystemTick(Core::System& system) { return result; } +static void GetSystemTick32(Core::System& system, u32* time_low, u32* time_high) { + u64 time = GetSystemTick(system); + *time_low = static_cast(time); + *time_high = static_cast(time >> 32); +} + /// Close a handle static ResultCode CloseHandle(Core::System& system, Handle handle) { LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle); @@ -1823,6 +1933,10 @@ static ResultCode ResetSignal(Core::System& system, Handle handle) { return ERR_INVALID_HANDLE; } +static ResultCode ResetSignal32(Core::System& system, Handle handle) { + return ResetSignal(system, handle); +} + /// Creates a TransferMemory object static ResultCode CreateTransferMemory(Core::System& system, Handle* handle, VAddr addr, u64 size, u32 permissions) { @@ -1897,6 +2011,15 @@ static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle, return RESULT_SUCCESS; } +static ResultCode GetThreadCoreMask32(Core::System& system, Handle thread_handle, u32* core, + u32* mask_low, u32* mask_high) { + u64 mask{}; + const auto result = GetThreadCoreMask(system, thread_handle, core, &mask); + *mask_high = static_cast(mask >> 32); + *mask_low = static_cast(mask); + return result; +} + static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle, u32 core, u64 affinity_mask) { LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, core=0x{:X}, affinity_mask=0x{:016X}", @@ -1988,6 +2111,10 @@ static ResultCode CreateEvent(Core::System& system, Handle* write_handle, Handle return RESULT_SUCCESS; } +static ResultCode CreateEvent32(Core::System& system, Handle* write_handle, Handle* read_handle) { + return CreateEvent(system, write_handle, read_handle); +} + static ResultCode ClearEvent(Core::System& system, Handle handle) { LOG_TRACE(Kernel_SVC, "called, event=0x{:08X}", handle); @@ -2009,6 +2136,10 @@ static ResultCode ClearEvent(Core::System& system, Handle handle) { return ERR_INVALID_HANDLE; } +static ResultCode ClearEvent32(Core::System& system, Handle handle) { + return ClearEvent(system, handle); +} + static ResultCode SignalEvent(Core::System& system, Handle handle) { LOG_DEBUG(Kernel_SVC, "called. Handle=0x{:08X}", handle); @@ -2024,6 +2155,10 @@ static ResultCode SignalEvent(Core::System& system, Handle handle) { return RESULT_SUCCESS; } +static ResultCode SignalEvent32(Core::System& system, Handle handle) { + return SignalEvent(system, handle); +} + static ResultCode GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type) { LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type); @@ -2209,6 +2344,15 @@ static ResultCode GetThreadList(Core::System& system, u32* out_num_threads, VAdd return RESULT_SUCCESS; } +static ResultCode FlushProcessDataCache32(Core::System& system, Handle handle, u32 address, + u32 size) { + // Note(Blinkhawk): For emulation purposes of the data cache this is mostly a nope + // as all emulation is done in the same cache level in host architecture, thus data cache + // does not need flushing. + LOG_DEBUG(Kernel_SVC, "called"); + return RESULT_SUCCESS; +} + namespace { struct FunctionDef { using Func = void(Core::System&); @@ -2224,56 +2368,56 @@ static const FunctionDef SVC_Table_32[] = { {0x01, SvcWrap32, "SetHeapSize32"}, {0x02, nullptr, "Unknown"}, {0x03, SvcWrap32, "SetMemoryAttribute32"}, - {0x04, nullptr, "MapMemory32"}, - {0x05, nullptr, "UnmapMemory32"}, + {0x04, SvcWrap32, "MapMemory32"}, + {0x05, SvcWrap32, "UnmapMemory32"}, {0x06, SvcWrap32, "QueryMemory32"}, - {0x07, nullptr, "ExitProcess32"}, - {0x08, nullptr, "CreateThread32"}, - {0x09, nullptr, "StartThread32"}, - {0x0a, nullptr, "ExitThread32"}, - {0x0b, nullptr, "SleepThread32"}, + {0x07, SvcWrap32, "ExitProcess32"}, + {0x08, SvcWrap32, "CreateThread32"}, + {0x09, SvcWrap32, "StartThread32"}, + {0x0a, SvcWrap32, "ExitThread32"}, + {0x0b, SvcWrap32, "SleepThread32"}, {0x0c, SvcWrap32, "GetThreadPriority32"}, {0x0d, SvcWrap32, "SetThreadPriority32"}, - {0x0e, nullptr, "GetThreadCoreMask32"}, + {0x0e, SvcWrap32, "GetThreadCoreMask32"}, {0x0f, SvcWrap32, "SetThreadCoreMask32"}, {0x10, SvcWrap32, "GetCurrentProcessorNumber32"}, - {0x11, nullptr, "SignalEvent32"}, - {0x12, nullptr, "ClearEvent32"}, - {0x13, nullptr, "MapSharedMemory32"}, + {0x11, SvcWrap32, "SignalEvent32"}, + {0x12, SvcWrap32, "ClearEvent32"}, + {0x13, SvcWrap32, "MapSharedMemory32"}, {0x14, nullptr, "UnmapSharedMemory32"}, {0x15, SvcWrap32, "CreateTransferMemory32"}, {0x16, SvcWrap32, "CloseHandle32"}, - {0x17, nullptr, "ResetSignal32"}, + {0x17, SvcWrap32, "ResetSignal32"}, {0x18, SvcWrap32, "WaitSynchronization32"}, - {0x19, nullptr, "CancelSynchronization32"}, - {0x1a, nullptr, "ArbitrateLock32"}, - {0x1b, nullptr, "ArbitrateUnlock32"}, - {0x1c, nullptr, "WaitProcessWideKeyAtomic32"}, + {0x19, SvcWrap32, "CancelSynchronization32"}, + {0x1a, SvcWrap32, "ArbitrateLock32"}, + {0x1b, SvcWrap32, "ArbitrateUnlock32"}, + {0x1c, SvcWrap32, "WaitProcessWideKeyAtomic32"}, {0x1d, SvcWrap32, "SignalProcessWideKey32"}, - {0x1e, nullptr, "GetSystemTick32"}, + {0x1e, SvcWrap32, "GetSystemTick32"}, {0x1f, SvcWrap32, "ConnectToNamedPort32"}, {0x20, nullptr, "Unknown"}, {0x21, SvcWrap32, "SendSyncRequest32"}, {0x22, nullptr, "SendSyncRequestWithUserBuffer32"}, {0x23, nullptr, "Unknown"}, - {0x24, nullptr, "GetProcessId32"}, + {0x24, SvcWrap32, "GetProcessId32"}, {0x25, SvcWrap32, "GetThreadId32"}, - {0x26, nullptr, "Break32"}, + {0x26, SvcWrap32, "Break32"}, {0x27, nullptr, "OutputDebugString32"}, {0x28, nullptr, "Unknown"}, {0x29, SvcWrap32, "GetInfo32"}, {0x2a, nullptr, "Unknown"}, {0x2b, nullptr, "Unknown"}, - {0x2c, nullptr, "MapPhysicalMemory32"}, - {0x2d, nullptr, "UnmapPhysicalMemory32"}, + {0x2c, SvcWrap32, "MapPhysicalMemory32"}, + {0x2d, SvcWrap32, "UnmapPhysicalMemory32"}, {0x2e, nullptr, "Unknown"}, {0x2f, nullptr, "Unknown"}, {0x30, nullptr, "Unknown"}, {0x31, nullptr, "Unknown"}, - {0x32, nullptr, "SetThreadActivity32"}, - {0x33, nullptr, "GetThreadContext32"}, - {0x34, nullptr, "WaitForAddress32"}, - {0x35, nullptr, "SignalToAddress32"}, + {0x32, SvcWrap32, "SetThreadActivity32"}, + {0x33, SvcWrap32, "GetThreadContext32"}, + {0x34, SvcWrap32, "WaitForAddress32"}, + {0x35, SvcWrap32, "SignalToAddress32"}, {0x36, nullptr, "Unknown"}, {0x37, nullptr, "Unknown"}, {0x38, nullptr, "Unknown"}, @@ -2289,7 +2433,7 @@ static const FunctionDef SVC_Table_32[] = { {0x42, nullptr, "Unknown"}, {0x43, nullptr, "ReplyAndReceive32"}, {0x44, nullptr, "Unknown"}, - {0x45, nullptr, "CreateEvent32"}, + {0x45, SvcWrap32, "CreateEvent32"}, {0x46, nullptr, "Unknown"}, {0x47, nullptr, "Unknown"}, {0x48, nullptr, "Unknown"}, @@ -2315,7 +2459,7 @@ static const FunctionDef SVC_Table_32[] = { {0x5c, nullptr, "Unknown"}, {0x5d, nullptr, "Unknown"}, {0x5e, nullptr, "Unknown"}, - {0x5F, nullptr, "FlushProcessDataCache32"}, + {0x5F, SvcWrap32, "FlushProcessDataCache32"}, {0x60, nullptr, "Unknown"}, {0x61, nullptr, "Unknown"}, {0x62, nullptr, "Unknown"}, diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index ba90c354f..0b6dd9df0 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h @@ -350,17 +350,48 @@ void SvcWrap64(Core::System& system) { func(system, static_cast(Param(system, 0)), Param(system, 1), Param(system, 2)); } -// Used by QueryMemory32 +// Used by QueryMemory32, ArbitrateLock32 template void SvcWrap32(Core::System& system) { FuncReturn32(system, func(system, Param32(system, 0), Param32(system, 1), Param32(system, 2)).raw); } +// Used by Break32 +template +void SvcWrap32(Core::System& system) { + func(system, Param32(system, 0), Param32(system, 1), Param32(system, 2)); +} + +// Used by ExitProcess32, ExitThread32 +template +void SvcWrap32(Core::System& system) { + func(system); +} + // Used by GetCurrentProcessorNumber32 template void SvcWrap32(Core::System& system) { - FuncReturn(system, func(system)); + FuncReturn32(system, func(system)); +} + +// Used by SleepThread32 +template +void SvcWrap32(Core::System& system) { + func(system, Param32(system, 0), Param32(system, 1)); +} + +// Used by CreateThread32 +template +void SvcWrap32(Core::System& system) { + Handle param_1 = 0; + + const u32 retval = func(system, ¶m_1, Param32(system, 0), Param32(system, 1), + Param32(system, 2), Param32(system, 3), Param32(system, 4)) + .raw; + + system.CurrentArmInterface().SetReg(1, param_1); + FuncReturn(system, retval); } // Used by GetInfo32 @@ -399,6 +430,43 @@ void SvcWrap32(Core::System& system) { FuncReturn(system, retval); } +// Used by GetSystemTick32 +template +void SvcWrap32(Core::System& system) { + u32 param_1 = 0; + u32 param_2 = 0; + + func(system, ¶m_1, ¶m_2); + system.CurrentArmInterface().SetReg(0, param_1); + system.CurrentArmInterface().SetReg(1, param_2); +} + +// Used by CreateEvent32 +template +void SvcWrap32(Core::System& system) { + Handle param_1 = 0; + Handle param_2 = 0; + + const u32 retval = func(system, ¶m_1, ¶m_2).raw; + system.CurrentArmInterface().SetReg(1, param_1); + system.CurrentArmInterface().SetReg(2, param_2); + FuncReturn(system, retval); +} + +// Used by GetThreadId32 +template +void SvcWrap32(Core::System& system) { + u32 param_1 = 0; + u32 param_2 = 0; + u32 param_3 = 0; + + const u32 retval = func(system, Param32(system, 2), ¶m_1, ¶m_2, ¶m_3).raw; + system.CurrentArmInterface().SetReg(1, param_1); + system.CurrentArmInterface().SetReg(2, param_2); + system.CurrentArmInterface().SetReg(3, param_3); + FuncReturn(system, retval); +} + // Used by SignalProcessWideKey32 template void SvcWrap32(Core::System& system) { @@ -423,7 +491,38 @@ void SvcWrap32(Core::System& system) { FuncReturn(system, retval); } -// Used by SendSyncRequest32 +// Used by WaitProcessWideKeyAtomic32 +template +void SvcWrap32(Core::System& system) { + const u32 retval = + func(system, static_cast(Param(system, 0)), static_cast(Param(system, 1)), + static_cast(Param(system, 2)), static_cast(Param(system, 3)), + static_cast(Param(system, 4))) + .raw; + FuncReturn(system, retval); +} + +// Used by WaitForAddress32 +template +void SvcWrap32(Core::System& system) { + const u32 retval = func(system, static_cast(Param(system, 0)), + static_cast(Param(system, 1)), static_cast(Param(system, 2)), + static_cast(Param(system, 3)), static_cast(Param(system, 4))) + .raw; + FuncReturn(system, retval); +} + +// Used by SignalToAddress32 +template +void SvcWrap32(Core::System& system) { + const u32 retval = + func(system, static_cast(Param(system, 0)), static_cast(Param(system, 1)), + static_cast(Param(system, 2)), static_cast(Param(system, 3))) + .raw; + FuncReturn(system, retval); +} + +// Used by SendSyncRequest32, ArbitrateUnlock32 template void SvcWrap32(Core::System& system) { FuncReturn(system, func(system, static_cast(Param(system, 0))).raw); From 626cc44d7ababf037cbf1f0a6fd1372efc296b64 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sat, 27 Jun 2020 10:52:23 -0400 Subject: [PATCH 233/252] Build System: Fix GCC & MINGW Build. --- src/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3a57356ab..1e977e8a8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -62,6 +62,10 @@ else() -Wno-unused-parameter ) + if (ARCHITECTURE_x86_64) + add_compile_options("-mcx16") + endif() + if (APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL Clang) add_compile_options("-stdlib=libc++") endif() From e486c66850637a05ba2912e3f1e25a97cfebc41e Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sat, 27 Jun 2020 10:59:51 -0400 Subject: [PATCH 234/252] NvFlinger: Clang Format. --- src/core/hle/service/nvflinger/nvflinger.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h index 4bc3d7ab2..e4959a9af 100644 --- a/src/core/hle/service/nvflinger/nvflinger.h +++ b/src/core/hle/service/nvflinger/nvflinger.h @@ -10,8 +10,8 @@ #include #include #include -#include #include +#include #include "common/common_types.h" #include "core/hle/kernel/object.h" From 2f8947583f2f0af4058600243d6c1d244e3c4890 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sat, 27 Jun 2020 18:20:06 -0400 Subject: [PATCH 235/252] Core/Common: Address Feedback. --- src/common/fiber.cpp | 10 +++----- src/common/spin_lock.cpp | 6 ++--- src/common/spin_lock.h | 5 ++++ src/common/x64/native_clock.cpp | 4 ++-- src/core/arm/arm_interface.h | 2 +- src/core/arm/cpu_interrupt_handler.h | 2 +- src/core/arm/dynarmic/arm_dynarmic_32.cpp | 5 ++-- src/core/arm/dynarmic/arm_dynarmic_32.h | 2 +- src/core/arm/dynarmic/arm_dynarmic_64.cpp | 5 ++-- src/core/arm/dynarmic/arm_dynarmic_64.h | 2 +- src/core/arm/unicorn/arm_unicorn.cpp | 2 +- src/core/arm/unicorn/arm_unicorn.h | 2 +- src/core/core.cpp | 2 +- src/core/core.h | 4 ++-- src/core/core_timing.cpp | 28 ++++++++++------------- src/core/hle/kernel/kernel.cpp | 14 ++++-------- src/core/hle/kernel/physical_core.cpp | 4 ++++ src/core/hle/kernel/physical_core.h | 7 ++---- src/core/hle/kernel/scheduler.cpp | 4 ++-- src/core/hle/kernel/scheduler.h | 4 ++++ src/tests/common/fibers.cpp | 2 +- 21 files changed, 58 insertions(+), 58 deletions(-) diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index f97ad433b..1c1d09ccb 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp @@ -54,9 +54,7 @@ Fiber::Fiber(std::function&& entry_point_func, void* start_paramete impl->handle = CreateFiber(default_stack_size, &FiberStartFunc, this); } -Fiber::Fiber() { - impl = std::make_unique(); -} +Fiber::Fiber() : impl{std::make_unique()} {} Fiber::~Fiber() { if (released) { @@ -116,8 +114,8 @@ std::shared_ptr Fiber::ThreadToFiber() { struct Fiber::FiberImpl { alignas(64) std::array stack; - u8* stack_limit; alignas(64) std::array rewind_stack; + u8* stack_limit; u8* rewind_stack_limit; boost::context::detail::fcontext_t context; boost::context::detail::fcontext_t rewind_context; @@ -168,9 +166,7 @@ void Fiber::SetRewindPoint(std::function&& rewind_func, void* start rewind_parameter = start_parameter; } -Fiber::Fiber() { - impl = std::make_unique(); -} +Fiber::Fiber() : impl{std::make_unique()} {} Fiber::~Fiber() { if (released) { diff --git a/src/common/spin_lock.cpp b/src/common/spin_lock.cpp index c7b46aac6..c1524220f 100644 --- a/src/common/spin_lock.cpp +++ b/src/common/spin_lock.cpp @@ -20,7 +20,7 @@ namespace { -void thread_pause() { +void ThreadPause() { #if __x86_64__ _mm_pause(); #elif __aarch64__ && _MSC_VER @@ -30,13 +30,13 @@ void thread_pause() { #endif } -} // namespace +} // Anonymous namespace namespace Common { void SpinLock::lock() { while (lck.test_and_set(std::memory_order_acquire)) { - thread_pause(); + ThreadPause(); } } diff --git a/src/common/spin_lock.h b/src/common/spin_lock.h index 70282a961..1df5528c4 100644 --- a/src/common/spin_lock.h +++ b/src/common/spin_lock.h @@ -8,6 +8,11 @@ namespace Common { +/** + * SpinLock class + * a lock similar to mutex that forces a thread to spin wait instead calling the + * supervisor. Should be used on short sequences of code. + */ class SpinLock { public: void lock(); diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp index f1bc60fd2..424b39b1f 100644 --- a/src/common/x64/native_clock.cpp +++ b/src/common/x64/native_clock.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include +#include #include #ifdef _MSC_VER @@ -52,7 +53,7 @@ NativeClock::NativeClock(u64 emulated_cpu_frequency, u64 emulated_clock_frequenc } u64 NativeClock::GetRTSC() { - rtsc_serialize.lock(); + std::scoped_lock scope{rtsc_serialize}; _mm_mfence(); const u64 current_measure = __rdtsc(); u64 diff = current_measure - last_measure; @@ -61,7 +62,6 @@ u64 NativeClock::GetRTSC() { last_measure = current_measure; } accumulated_ticks += diff; - rtsc_serialize.unlock(); /// The clock cannot be more precise than the guest timer, remove the lower bits return accumulated_ticks & inaccuracy_mask; } diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index 0c1d6ac39..1f24051e4 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h @@ -148,7 +148,7 @@ public: */ virtual void SetTPIDR_EL0(u64 value) = 0; - virtual void ChangeProcessorId(std::size_t new_core_id) = 0; + virtual void ChangeProcessorID(std::size_t new_core_id) = 0; virtual void SaveContext(ThreadContext32& ctx) = 0; virtual void SaveContext(ThreadContext64& ctx) = 0; diff --git a/src/core/arm/cpu_interrupt_handler.h b/src/core/arm/cpu_interrupt_handler.h index 91c31a271..3d062d326 100644 --- a/src/core/arm/cpu_interrupt_handler.h +++ b/src/core/arm/cpu_interrupt_handler.h @@ -23,7 +23,7 @@ public: CPUInterruptHandler(CPUInterruptHandler&&) = default; CPUInterruptHandler& operator=(CPUInterruptHandler&&) = default; - constexpr bool IsInterrupted() const { + bool IsInterrupted() const { return is_interrupted; } diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index cfda12098..0d4ab95b7 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -107,7 +107,7 @@ public: u64 GetTicksRemaining() override { if (parent.uses_wall_clock) { if (!parent.interrupt_handlers[parent.core_index].IsInterrupted()) { - return 1000U; + return minimum_run_cycles; } return 0U; } @@ -116,6 +116,7 @@ public: ARM_Dynarmic_32& parent; std::size_t num_interpreted_instructions{}; + static constexpr u64 minimum_run_cycles = 1000U; }; std::shared_ptr ARM_Dynarmic_32::MakeJit(Common::PageTable& page_table, @@ -214,7 +215,7 @@ void ARM_Dynarmic_32::SetTPIDR_EL0(u64 value) { cp15->uprw = static_cast(value); } -void ARM_Dynarmic_32::ChangeProcessorId(std::size_t new_core_id) { +void ARM_Dynarmic_32::ChangeProcessorID(std::size_t new_core_id) { jit->ChangeProcessorID(new_core_id); } diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h index d9c0bfede..2bab31b92 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.h +++ b/src/core/arm/dynarmic/arm_dynarmic_32.h @@ -47,7 +47,7 @@ public: void SetTlsAddress(VAddr address) override; void SetTPIDR_EL0(u64 value) override; u64 GetTPIDR_EL0() const override; - void ChangeProcessorId(std::size_t new_core_id) override; + void ChangeProcessorID(std::size_t new_core_id) override; void SaveContext(ThreadContext32& ctx) override; void SaveContext(ThreadContext64& ctx) override {} diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 35a99e28a..790981034 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -144,7 +144,7 @@ public: u64 GetTicksRemaining() override { if (parent.uses_wall_clock) { if (!parent.interrupt_handlers[parent.core_index].IsInterrupted()) { - return 1000U; + return minimum_run_cycles; } return 0U; } @@ -159,6 +159,7 @@ public: std::size_t num_interpreted_instructions = 0; u64 tpidrro_el0 = 0; u64 tpidr_el0 = 0; + static constexpr u64 minimum_run_cycles = 1000U; }; std::shared_ptr ARM_Dynarmic_64::MakeJit(Common::PageTable& page_table, @@ -271,7 +272,7 @@ void ARM_Dynarmic_64::SetTPIDR_EL0(u64 value) { cb->tpidr_el0 = value; } -void ARM_Dynarmic_64::ChangeProcessorId(std::size_t new_core_id) { +void ARM_Dynarmic_64::ChangeProcessorID(std::size_t new_core_id) { jit->ChangeProcessorID(new_core_id); } diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h index c74fcbcea..403c55961 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.h +++ b/src/core/arm/dynarmic/arm_dynarmic_64.h @@ -45,7 +45,7 @@ public: void SetTlsAddress(VAddr address) override; void SetTPIDR_EL0(u64 value) override; u64 GetTPIDR_EL0() const override; - void ChangeProcessorId(std::size_t new_core_id) override; + void ChangeProcessorID(std::size_t new_core_id) override; void SaveContext(ThreadContext32& ctx) override {} void SaveContext(ThreadContext64& ctx) override; diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp index 35e8f42e8..1df3f3ed1 100644 --- a/src/core/arm/unicorn/arm_unicorn.cpp +++ b/src/core/arm/unicorn/arm_unicorn.cpp @@ -159,7 +159,7 @@ void ARM_Unicorn::SetTPIDR_EL0(u64 value) { CHECKED(uc_reg_write(uc, UC_ARM64_REG_TPIDR_EL0, &value)); } -void ARM_Unicorn::ChangeProcessorId(std::size_t new_core_id) { +void ARM_Unicorn::ChangeProcessorID(std::size_t new_core_id) { core_index = new_core_id; } diff --git a/src/core/arm/unicorn/arm_unicorn.h b/src/core/arm/unicorn/arm_unicorn.h index 8ace8b86f..810aff311 100644 --- a/src/core/arm/unicorn/arm_unicorn.h +++ b/src/core/arm/unicorn/arm_unicorn.h @@ -36,7 +36,7 @@ public: void SetTlsAddress(VAddr address) override; void SetTPIDR_EL0(u64 value) override; u64 GetTPIDR_EL0() const override; - void ChangeProcessorId(std::size_t new_core_id) override; + void ChangeProcessorID(std::size_t new_core_id) override; void PrepareReschedule() override; void ClearExclusiveState() override; void ExecuteInstructions(std::size_t num_instructions); diff --git a/src/core/core.cpp b/src/core/core.cpp index 8256ec0fc..1a243c515 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -443,7 +443,7 @@ bool System::IsPoweredOn() const { } void System::PrepareReschedule() { - // impl->CurrentPhysicalCore().Stop(); + // Deprecated, does nothing, kept for backward compatibility. } void System::PrepareReschedule(const u32 core_index) { diff --git a/src/core/core.h b/src/core/core.h index 133ecb8e1..5c6cfbffe 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -138,13 +138,13 @@ public: /** * Run the OS and Application - * This function will start emulation and run the competent devices + * This function will start emulation and run the relevant devices */ ResultStatus Run(); /** * Pause the OS and Application - * This function will pause emulation and stop the competent devices + * This function will pause emulation and stop the relevant devices */ ResultStatus Pause(); diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 1aa89a1cc..5c83c41a4 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -45,9 +45,9 @@ CoreTiming::CoreTiming() { CoreTiming::~CoreTiming() = default; void CoreTiming::ThreadEntry(CoreTiming& instance) { - std::string name = "yuzu:HostTiming"; - MicroProfileOnThreadCreate(name.c_str()); - Common::SetCurrentThreadName(name.c_str()); + constexpr char name[] = "yuzu:HostTiming"; + MicroProfileOnThreadCreate(name); + Common::SetCurrentThreadName(name); Common::SetCurrentThreadPriority(Common::ThreadPriority::VeryHigh); instance.on_thread_init(); instance.ThreadLoop(); @@ -108,18 +108,19 @@ bool CoreTiming::HasPendingEvents() const { void CoreTiming::ScheduleEvent(s64 ns_into_future, const std::shared_ptr& event_type, u64 userdata) { - basic_lock.lock(); - const u64 timeout = static_cast(GetGlobalTimeNs().count() + ns_into_future); + { + std::scoped_lock scope{basic_lock}; + const u64 timeout = static_cast(GetGlobalTimeNs().count() + ns_into_future); - event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type}); + event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type}); - std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); - basic_lock.unlock(); + std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); + } event.Set(); } void CoreTiming::UnscheduleEvent(const std::shared_ptr& event_type, u64 userdata) { - basic_lock.lock(); + std::scoped_lock scope{basic_lock}; const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { return e.type.lock().get() == event_type.get() && e.userdata == userdata; }); @@ -129,7 +130,6 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr& event_type, u event_queue.erase(itr, event_queue.end()); std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>()); } - basic_lock.unlock(); } void CoreTiming::AddTicks(u64 ticks) { @@ -187,8 +187,8 @@ void CoreTiming::RemoveEvent(const std::shared_ptr& event_type) { } std::optional CoreTiming::Advance() { - advance_lock.lock(); - basic_lock.lock(); + std::scoped_lock advance_scope{advance_lock}; + std::scoped_lock basic_scope{basic_lock}; global_timer = GetGlobalTimeNs().count(); while (!event_queue.empty() && event_queue.front().time <= global_timer) { @@ -207,12 +207,8 @@ std::optional CoreTiming::Advance() { if (!event_queue.empty()) { const s64 next_time = event_queue.front().time - global_timer; - basic_lock.unlock(); - advance_lock.unlock(); return next_time; } else { - basic_lock.unlock(); - advance_lock.unlock(); return std::nullopt; } } diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index dbb75416d..1f2af7a1b 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -472,16 +472,12 @@ const Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() const { } void KernelCore::InvalidateAllInstructionCaches() { - if (!IsMulticore()) { - auto& threads = GlobalScheduler().GetThreadList(); - for (auto& thread : threads) { - if (!thread->IsHLEThread()) { - auto& arm_interface = thread->ArmInterface(); - arm_interface.ClearInstructionCache(); - } + auto& threads = GlobalScheduler().GetThreadList(); + for (auto& thread : threads) { + if (!thread->IsHLEThread()) { + auto& arm_interface = thread->ArmInterface(); + arm_interface.ClearInstructionCache(); } - } else { - UNIMPLEMENTED_MSG("Cache Invalidation unimplemented for multicore"); } } diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp index c82c60a16..c6bbdb080 100644 --- a/src/core/hle/kernel/physical_core.cpp +++ b/src/core/hle/kernel/physical_core.cpp @@ -37,6 +37,10 @@ void PhysicalCore::Shutdown() { scheduler.Shutdown(); } +bool PhysicalCore::IsInterrupted() const { + return interrupt_handler.IsInterrupted(); +} + void PhysicalCore::Interrupt() { guard->lock(); interrupt_handler.SetInterrupt(true); diff --git a/src/core/hle/kernel/physical_core.h b/src/core/hle/kernel/physical_core.h index 85f6dec05..d7a7a951c 100644 --- a/src/core/hle/kernel/physical_core.h +++ b/src/core/hle/kernel/physical_core.h @@ -7,8 +7,6 @@ #include #include -#include "core/arm/cpu_interrupt_handler.h" - namespace Common { class SpinLock; } @@ -19,6 +17,7 @@ class Scheduler; namespace Core { class ARM_Interface; +class CPUInterruptHandler; class ExclusiveMonitor; class System; } // namespace Core @@ -45,9 +44,7 @@ public: void ClearInterrupt(); /// Check if this core is interrupted - bool IsInterrupted() const { - return interrupt_handler.IsInterrupted(); - } + bool IsInterrupted() const; // Shutdown this physical core. void Shutdown(); diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index 61b8a396a..2b12c0dbf 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp @@ -658,7 +658,7 @@ void Scheduler::Reload() { cpu_core.LoadContext(thread->GetContext64()); cpu_core.SetTlsAddress(thread->GetTLSAddress()); cpu_core.SetTPIDR_EL0(thread->GetTPIDR_EL0()); - cpu_core.ChangeProcessorId(this->core_id); + cpu_core.ChangeProcessorID(this->core_id); cpu_core.ClearExclusiveState(); } } @@ -691,7 +691,7 @@ void Scheduler::SwitchContextStep2() { cpu_core.LoadContext(new_thread->GetContext64()); cpu_core.SetTlsAddress(new_thread->GetTLSAddress()); cpu_core.SetTPIDR_EL0(new_thread->GetTPIDR_EL0()); - cpu_core.ChangeProcessorId(this->core_id); + cpu_core.ChangeProcessorID(this->core_id); cpu_core.ClearExclusiveState(); } } diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h index 348107160..b3b4b5169 100644 --- a/src/core/hle/kernel/scheduler.h +++ b/src/core/hle/kernel/scheduler.h @@ -240,6 +240,10 @@ public: return switch_fiber; } + const std::shared_ptr& ControlContext() const { + return switch_fiber; + } + private: friend class GlobalScheduler; diff --git a/src/tests/common/fibers.cpp b/src/tests/common/fibers.cpp index 12536b6d8..4fd92428f 100644 --- a/src/tests/common/fibers.cpp +++ b/src/tests/common/fibers.cpp @@ -68,7 +68,7 @@ static void ThreadStart1(u32 id, TestControl1& test_control) { * doing all the work required. */ TEST_CASE("Fibers::Setup", "[common]") { - constexpr u32 num_threads = 7; + constexpr std::size_t num_threads = 7; TestControl1 test_control{}; test_control.thread_fibers.resize(num_threads); test_control.work_fibers.resize(num_threads); From f0baf2abf2f6b5e5638117998c21daec10820990 Mon Sep 17 00:00:00 2001 From: David Marcec Date: Sun, 28 Jun 2020 16:44:36 +1000 Subject: [PATCH 236/252] acc: ListOpenContextStoredUsers partial stub Needed by Baldur's Gate 1/2 --- src/core/hle/service/acc/acc.cpp | 9 +++++++++ src/core/hle/service/acc/acc.h | 1 + src/core/hle/service/acc/acc_su.cpp | 2 +- src/core/hle/service/acc/acc_u0.cpp | 4 ++-- src/core/hle/service/acc/acc_u1.cpp | 2 +- 5 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index 94d8c1fc6..8ac856ec3 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp @@ -776,6 +776,15 @@ void Module::Interface::ListQualifiedUsers(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); } +void Module::Interface::ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_ACC, "(STUBBED) called"); + + // TODO(ogniK): Handle open contexts + ctx.WriteBuffer(profile_manager->GetOpenUsers()); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_ACC, "called"); // A u8 is passed into this function which we can safely ignore. It's to determine if we have diff --git a/src/core/hle/service/acc/acc.h b/src/core/hle/service/acc/acc.h index 74ca39d6e..d4c6395c6 100644 --- a/src/core/hle/service/acc/acc.h +++ b/src/core/hle/service/acc/acc.h @@ -34,6 +34,7 @@ public: void IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx); void GetProfileEditor(Kernel::HLERequestContext& ctx); void ListQualifiedUsers(Kernel::HLERequestContext& ctx); + void ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx); private: ResultCode InitializeApplicationInfoBase(); diff --git a/src/core/hle/service/acc/acc_su.cpp b/src/core/hle/service/acc/acc_su.cpp index 85620bde3..d2bb8c2c8 100644 --- a/src/core/hle/service/acc/acc_su.cpp +++ b/src/core/hle/service/acc/acc_su.cpp @@ -20,7 +20,7 @@ ACC_SU::ACC_SU(std::shared_ptr module, std::shared_ptr p {6, nullptr, "GetProfileDigest"}, // 3.0.0+ {50, &ACC_SU::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, {51, &ACC_SU::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, - {60, nullptr, "ListOpenContextStoredUsers"}, // 5.0.0 - 5.1.0 + {60, &ACC_SU::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 5.0.0 - 5.1.0 {99, nullptr, "DebugActivateOpenContextRetention"}, // 6.0.0+ {100, nullptr, "GetUserRegistrationNotifier"}, {101, nullptr, "GetUserStateChangeNotifier"}, diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp index 49f6e20f1..cb44e06b7 100644 --- a/src/core/hle/service/acc/acc_u0.cpp +++ b/src/core/hle/service/acc/acc_u0.cpp @@ -20,7 +20,7 @@ ACC_U0::ACC_U0(std::shared_ptr module, std::shared_ptr p {6, nullptr, "GetProfileDigest"}, // 3.0.0+ {50, &ACC_U0::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, {51, &ACC_U0::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, - {60, nullptr, "ListOpenContextStoredUsers"}, // 5.0.0 - 5.1.0 + {60, &ACC_U0::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 5.0.0 - 5.1.0 {99, nullptr, "DebugActivateOpenContextRetention"}, // 6.0.0+ {100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"}, {101, &ACC_U0::GetBaasAccountManagerForApplication, "GetBaasAccountManagerForApplication"}, @@ -30,7 +30,7 @@ ACC_U0::ACC_U0(std::shared_ptr module, std::shared_ptr p {111, nullptr, "ClearSaveDataThumbnail"}, {120, nullptr, "CreateGuestLoginRequest"}, {130, nullptr, "LoadOpenContext"}, // 5.0.0+ - {131, nullptr, "ListOpenContextStoredUsers"}, // 6.0.0+ + {131, &ACC_U0::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 6.0.0+ {140, &ACC_U0::InitializeApplicationInfoRestricted, "InitializeApplicationInfoRestricted"}, // 6.0.0+ {141, &ACC_U0::ListQualifiedUsers, "ListQualifiedUsers"}, // 6.0.0+ {150, &ACC_U0::IsUserAccountSwitchLocked, "IsUserAccountSwitchLocked"}, // 6.0.0+ diff --git a/src/core/hle/service/acc/acc_u1.cpp b/src/core/hle/service/acc/acc_u1.cpp index f47004f84..a4aa5316a 100644 --- a/src/core/hle/service/acc/acc_u1.cpp +++ b/src/core/hle/service/acc/acc_u1.cpp @@ -20,7 +20,7 @@ ACC_U1::ACC_U1(std::shared_ptr module, std::shared_ptr p {6, nullptr, "GetProfileDigest"}, // 3.0.0+ {50, &ACC_U1::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, {51, &ACC_U1::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, - {60, nullptr, "ListOpenContextStoredUsers"}, // 5.0.0 - 5.1.0 + {60, &ACC_U1::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 5.0.0 - 5.1.0 {99, nullptr, "DebugActivateOpenContextRetention"}, // 6.0.0+ {100, nullptr, "GetUserRegistrationNotifier"}, {101, nullptr, "GetUserStateChangeNotifier"}, From 78d80d99a07893b79cecefbb613bf326c8e783eb Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Sun, 28 Jun 2020 02:48:14 -0400 Subject: [PATCH 237/252] maxwell_to_gl: Add 32 bit component sizes to (un)signed scaled formats Add 32 bit component sizes to (un)signed scaled formats and group (un)signed normalized, scaled, and integer formats together. --- .../renderer_opengl/maxwell_to_gl.h | 34 +++---------------- 1 file changed, 4 insertions(+), 30 deletions(-) diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h index 35e329240..8f3871e90 100644 --- a/src/video_core/renderer_opengl/maxwell_to_gl.h +++ b/src/video_core/renderer_opengl/maxwell_to_gl.h @@ -26,8 +26,9 @@ using Maxwell = Tegra::Engines::Maxwell3D::Regs; inline GLenum VertexType(Maxwell::VertexAttribute attrib) { switch (attrib.type) { - case Maxwell::VertexAttribute::Type::UnsignedInt: case Maxwell::VertexAttribute::Type::UnsignedNorm: + case Maxwell::VertexAttribute::Type::UnsignedScaled: + case Maxwell::VertexAttribute::Type::UnsignedInt: switch (attrib.size) { case Maxwell::VertexAttribute::Size::Size_8: case Maxwell::VertexAttribute::Size::Size_8_8: @@ -48,8 +49,9 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) { return GL_UNSIGNED_INT_2_10_10_10_REV; } break; - case Maxwell::VertexAttribute::Type::SignedInt: case Maxwell::VertexAttribute::Type::SignedNorm: + case Maxwell::VertexAttribute::Type::SignedScaled: + case Maxwell::VertexAttribute::Type::SignedInt: switch (attrib.size) { case Maxwell::VertexAttribute::Size::Size_8: case Maxwell::VertexAttribute::Size::Size_8_8: @@ -84,34 +86,6 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) { return GL_FLOAT; } break; - case Maxwell::VertexAttribute::Type::UnsignedScaled: - switch (attrib.size) { - case Maxwell::VertexAttribute::Size::Size_8: - case Maxwell::VertexAttribute::Size::Size_8_8: - case Maxwell::VertexAttribute::Size::Size_8_8_8: - case Maxwell::VertexAttribute::Size::Size_8_8_8_8: - return GL_UNSIGNED_BYTE; - case Maxwell::VertexAttribute::Size::Size_16: - case Maxwell::VertexAttribute::Size::Size_16_16: - case Maxwell::VertexAttribute::Size::Size_16_16_16: - case Maxwell::VertexAttribute::Size::Size_16_16_16_16: - return GL_UNSIGNED_SHORT; - } - break; - case Maxwell::VertexAttribute::Type::SignedScaled: - switch (attrib.size) { - case Maxwell::VertexAttribute::Size::Size_8: - case Maxwell::VertexAttribute::Size::Size_8_8: - case Maxwell::VertexAttribute::Size::Size_8_8_8: - case Maxwell::VertexAttribute::Size::Size_8_8_8_8: - return GL_BYTE; - case Maxwell::VertexAttribute::Size::Size_16: - case Maxwell::VertexAttribute::Size::Size_16_16: - case Maxwell::VertexAttribute::Size::Size_16_16_16: - case Maxwell::VertexAttribute::Size::Size_16_16_16_16: - return GL_SHORT; - } - break; } UNIMPLEMENTED_MSG("Unimplemented vertex type={} and size={}", attrib.TypeString(), attrib.SizeString()); From dcf345febe605d2921487926f5ead337a798048f Mon Sep 17 00:00:00 2001 From: David Marcec Date: Sun, 28 Jun 2020 16:51:28 +1000 Subject: [PATCH 238/252] am: Stub GetIndirectLayerConsumerHandle Needed by Monster Hunter Generations Ultimate --- src/core/hle/service/am/am.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 20f366635..1bb544dd8 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -841,7 +841,7 @@ public: {110, nullptr, "NeedsToExitProcess"}, {120, nullptr, "GetLibraryAppletInfo"}, {150, nullptr, "RequestForAppletToGetForeground"}, - {160, nullptr, "GetIndirectLayerConsumerHandle"}, + {160, &ILibraryAppletAccessor::GetIndirectLayerConsumerHandle, "GetIndirectLayerConsumerHandle"}, }; // clang-format on @@ -960,6 +960,18 @@ private: rb.PushCopyObjects(applet->GetBroker().GetInteractiveDataEvent()); } + void GetIndirectLayerConsumerHandle(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + // We require a non-zero handle to be valid. Using 0xdeadbeef allows us to trace if this is + // actually used anywhere + constexpr u64 handle = 0xdeadbeef; + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push(handle); + } + std::shared_ptr applet; }; From 4a35df337b7aaa3d4056a5b10da471bff11b4b2f Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Sun, 28 Jun 2020 02:49:17 -0400 Subject: [PATCH 239/252] maxwell_to_vk: Reorder vertex formats and add A2B10G10R10 for all types except float --- .../renderer_vulkan/maxwell_to_vk.cpp | 174 +++++++++--------- 1 file changed, 84 insertions(+), 90 deletions(-) diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp index 1f2b6734b..d7f1ae89f 100644 --- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp +++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp @@ -294,30 +294,6 @@ VkPrimitiveTopology PrimitiveTopology([[maybe_unused]] const VKDevice& device, VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttribute::Size size) { switch (type) { - case Maxwell::VertexAttribute::Type::SignedNorm: - switch (size) { - case Maxwell::VertexAttribute::Size::Size_8: - return VK_FORMAT_R8_SNORM; - case Maxwell::VertexAttribute::Size::Size_8_8: - return VK_FORMAT_R8G8_SNORM; - case Maxwell::VertexAttribute::Size::Size_8_8_8: - return VK_FORMAT_R8G8B8_SNORM; - case Maxwell::VertexAttribute::Size::Size_8_8_8_8: - return VK_FORMAT_R8G8B8A8_SNORM; - case Maxwell::VertexAttribute::Size::Size_16: - return VK_FORMAT_R16_SNORM; - case Maxwell::VertexAttribute::Size::Size_16_16: - return VK_FORMAT_R16G16_SNORM; - case Maxwell::VertexAttribute::Size::Size_16_16_16: - return VK_FORMAT_R16G16B16_SNORM; - case Maxwell::VertexAttribute::Size::Size_16_16_16_16: - return VK_FORMAT_R16G16B16A16_SNORM; - case Maxwell::VertexAttribute::Size::Size_10_10_10_2: - return VK_FORMAT_A2B10G10R10_SNORM_PACK32; - default: - break; - } - break; case Maxwell::VertexAttribute::Type::UnsignedNorm: switch (size) { case Maxwell::VertexAttribute::Size::Size_8: @@ -338,38 +314,72 @@ VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttrib return VK_FORMAT_R16G16B16A16_UNORM; case Maxwell::VertexAttribute::Size::Size_10_10_10_2: return VK_FORMAT_A2B10G10R10_UNORM_PACK32; - default: - break; } break; - case Maxwell::VertexAttribute::Type::SignedInt: + case Maxwell::VertexAttribute::Type::SignedNorm: switch (size) { case Maxwell::VertexAttribute::Size::Size_8: - return VK_FORMAT_R8_SINT; + return VK_FORMAT_R8_SNORM; case Maxwell::VertexAttribute::Size::Size_8_8: - return VK_FORMAT_R8G8_SINT; + return VK_FORMAT_R8G8_SNORM; case Maxwell::VertexAttribute::Size::Size_8_8_8: - return VK_FORMAT_R8G8B8_SINT; + return VK_FORMAT_R8G8B8_SNORM; case Maxwell::VertexAttribute::Size::Size_8_8_8_8: - return VK_FORMAT_R8G8B8A8_SINT; + return VK_FORMAT_R8G8B8A8_SNORM; case Maxwell::VertexAttribute::Size::Size_16: - return VK_FORMAT_R16_SINT; + return VK_FORMAT_R16_SNORM; case Maxwell::VertexAttribute::Size::Size_16_16: - return VK_FORMAT_R16G16_SINT; + return VK_FORMAT_R16G16_SNORM; case Maxwell::VertexAttribute::Size::Size_16_16_16: - return VK_FORMAT_R16G16B16_SINT; + return VK_FORMAT_R16G16B16_SNORM; case Maxwell::VertexAttribute::Size::Size_16_16_16_16: - return VK_FORMAT_R16G16B16A16_SINT; - case Maxwell::VertexAttribute::Size::Size_32: - return VK_FORMAT_R32_SINT; - case Maxwell::VertexAttribute::Size::Size_32_32: - return VK_FORMAT_R32G32_SINT; - case Maxwell::VertexAttribute::Size::Size_32_32_32: - return VK_FORMAT_R32G32B32_SINT; - case Maxwell::VertexAttribute::Size::Size_32_32_32_32: - return VK_FORMAT_R32G32B32A32_SINT; - default: - break; + return VK_FORMAT_R16G16B16A16_SNORM; + case Maxwell::VertexAttribute::Size::Size_10_10_10_2: + return VK_FORMAT_A2B10G10R10_SNORM_PACK32; + } + break; + case Maxwell::VertexAttribute::Type::UnsignedScaled: + switch (size) { + case Maxwell::VertexAttribute::Size::Size_8: + return VK_FORMAT_R8_USCALED; + case Maxwell::VertexAttribute::Size::Size_8_8: + return VK_FORMAT_R8G8_USCALED; + case Maxwell::VertexAttribute::Size::Size_8_8_8: + return VK_FORMAT_R8G8B8_USCALED; + case Maxwell::VertexAttribute::Size::Size_8_8_8_8: + return VK_FORMAT_R8G8B8A8_USCALED; + case Maxwell::VertexAttribute::Size::Size_16: + return VK_FORMAT_R16_USCALED; + case Maxwell::VertexAttribute::Size::Size_16_16: + return VK_FORMAT_R16G16_USCALED; + case Maxwell::VertexAttribute::Size::Size_16_16_16: + return VK_FORMAT_R16G16B16_USCALED; + case Maxwell::VertexAttribute::Size::Size_16_16_16_16: + return VK_FORMAT_R16G16B16A16_USCALED; + case Maxwell::VertexAttribute::Size::Size_10_10_10_2: + return VK_FORMAT_A2B10G10R10_USCALED_PACK32; + } + break; + case Maxwell::VertexAttribute::Type::SignedScaled: + switch (size) { + case Maxwell::VertexAttribute::Size::Size_8: + return VK_FORMAT_R8_SSCALED; + case Maxwell::VertexAttribute::Size::Size_8_8: + return VK_FORMAT_R8G8_SSCALED; + case Maxwell::VertexAttribute::Size::Size_8_8_8: + return VK_FORMAT_R8G8B8_SSCALED; + case Maxwell::VertexAttribute::Size::Size_8_8_8_8: + return VK_FORMAT_R8G8B8A8_SSCALED; + case Maxwell::VertexAttribute::Size::Size_16: + return VK_FORMAT_R16_SSCALED; + case Maxwell::VertexAttribute::Size::Size_16_16: + return VK_FORMAT_R16G16_SSCALED; + case Maxwell::VertexAttribute::Size::Size_16_16_16: + return VK_FORMAT_R16G16B16_SSCALED; + case Maxwell::VertexAttribute::Size::Size_16_16_16_16: + return VK_FORMAT_R16G16B16A16_SSCALED; + case Maxwell::VertexAttribute::Size::Size_10_10_10_2: + return VK_FORMAT_A2B10G10R10_SSCALED_PACK32; } break; case Maxwell::VertexAttribute::Type::UnsignedInt: @@ -398,64 +408,42 @@ VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttrib return VK_FORMAT_R32G32B32_UINT; case Maxwell::VertexAttribute::Size::Size_32_32_32_32: return VK_FORMAT_R32G32B32A32_UINT; - default: - break; + case Maxwell::VertexAttribute::Size::Size_10_10_10_2: + return VK_FORMAT_A2B10G10R10_UINT_PACK32; } break; - case Maxwell::VertexAttribute::Type::UnsignedScaled: + case Maxwell::VertexAttribute::Type::SignedInt: switch (size) { case Maxwell::VertexAttribute::Size::Size_8: - return VK_FORMAT_R8_USCALED; + return VK_FORMAT_R8_SINT; case Maxwell::VertexAttribute::Size::Size_8_8: - return VK_FORMAT_R8G8_USCALED; + return VK_FORMAT_R8G8_SINT; case Maxwell::VertexAttribute::Size::Size_8_8_8: - return VK_FORMAT_R8G8B8_USCALED; + return VK_FORMAT_R8G8B8_SINT; case Maxwell::VertexAttribute::Size::Size_8_8_8_8: - return VK_FORMAT_R8G8B8A8_USCALED; + return VK_FORMAT_R8G8B8A8_SINT; case Maxwell::VertexAttribute::Size::Size_16: - return VK_FORMAT_R16_USCALED; + return VK_FORMAT_R16_SINT; case Maxwell::VertexAttribute::Size::Size_16_16: - return VK_FORMAT_R16G16_USCALED; + return VK_FORMAT_R16G16_SINT; case Maxwell::VertexAttribute::Size::Size_16_16_16: - return VK_FORMAT_R16G16B16_USCALED; + return VK_FORMAT_R16G16B16_SINT; case Maxwell::VertexAttribute::Size::Size_16_16_16_16: - return VK_FORMAT_R16G16B16A16_USCALED; - default: - break; - } - break; - case Maxwell::VertexAttribute::Type::SignedScaled: - switch (size) { - case Maxwell::VertexAttribute::Size::Size_8: - return VK_FORMAT_R8_SSCALED; - case Maxwell::VertexAttribute::Size::Size_8_8: - return VK_FORMAT_R8G8_SSCALED; - case Maxwell::VertexAttribute::Size::Size_8_8_8: - return VK_FORMAT_R8G8B8_SSCALED; - case Maxwell::VertexAttribute::Size::Size_8_8_8_8: - return VK_FORMAT_R8G8B8A8_SSCALED; - case Maxwell::VertexAttribute::Size::Size_16: - return VK_FORMAT_R16_SSCALED; - case Maxwell::VertexAttribute::Size::Size_16_16: - return VK_FORMAT_R16G16_SSCALED; - case Maxwell::VertexAttribute::Size::Size_16_16_16: - return VK_FORMAT_R16G16B16_SSCALED; - case Maxwell::VertexAttribute::Size::Size_16_16_16_16: - return VK_FORMAT_R16G16B16A16_SSCALED; - default: - break; + return VK_FORMAT_R16G16B16A16_SINT; + case Maxwell::VertexAttribute::Size::Size_32: + return VK_FORMAT_R32_SINT; + case Maxwell::VertexAttribute::Size::Size_32_32: + return VK_FORMAT_R32G32_SINT; + case Maxwell::VertexAttribute::Size::Size_32_32_32: + return VK_FORMAT_R32G32B32_SINT; + case Maxwell::VertexAttribute::Size::Size_32_32_32_32: + return VK_FORMAT_R32G32B32A32_SINT; + case Maxwell::VertexAttribute::Size::Size_10_10_10_2: + return VK_FORMAT_A2B10G10R10_SINT_PACK32; } break; case Maxwell::VertexAttribute::Type::Float: switch (size) { - case Maxwell::VertexAttribute::Size::Size_32: - return VK_FORMAT_R32_SFLOAT; - case Maxwell::VertexAttribute::Size::Size_32_32: - return VK_FORMAT_R32G32_SFLOAT; - case Maxwell::VertexAttribute::Size::Size_32_32_32: - return VK_FORMAT_R32G32B32_SFLOAT; - case Maxwell::VertexAttribute::Size::Size_32_32_32_32: - return VK_FORMAT_R32G32B32A32_SFLOAT; case Maxwell::VertexAttribute::Size::Size_16: return VK_FORMAT_R16_SFLOAT; case Maxwell::VertexAttribute::Size::Size_16_16: @@ -464,8 +452,14 @@ VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttrib return VK_FORMAT_R16G16B16_SFLOAT; case Maxwell::VertexAttribute::Size::Size_16_16_16_16: return VK_FORMAT_R16G16B16A16_SFLOAT; - default: - break; + case Maxwell::VertexAttribute::Size::Size_32: + return VK_FORMAT_R32_SFLOAT; + case Maxwell::VertexAttribute::Size::Size_32_32: + return VK_FORMAT_R32G32_SFLOAT; + case Maxwell::VertexAttribute::Size::Size_32_32_32: + return VK_FORMAT_R32G32B32_SFLOAT; + case Maxwell::VertexAttribute::Size::Size_32_32_32_32: + return VK_FORMAT_R32G32B32A32_SFLOAT; } break; } From db824b59c84ddf67df69c493a4def49ca32eacfc Mon Sep 17 00:00:00 2001 From: David Marcec Date: Sun, 28 Jun 2020 20:54:37 +1000 Subject: [PATCH 240/252] ldr: Cleanup NRO & NRR structs --- src/core/hle/service/ldr/ldr.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index 9f376657c..64a526b9e 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp @@ -46,7 +46,7 @@ constexpr std::size_t DATA_INDEX{2}; struct NRRCertification { u64_le application_id_mask; u64_le application_id_pattern; - std::array reserved; + INSERT_PADDING_BYTES(0x10); std::array public_key; // Also known as modulus std::array signature; }; @@ -55,16 +55,16 @@ static_assert(sizeof(NRRCertification) == 0x220, "NRRCertification has invalid s struct NRRHeader { u32_le magic; u32_le certification_signature_key_generation; // 9.0.0+ - u64_le reserved; + INSERT_PADDING_WORDS(2); NRRCertification certification; std::array signature; u64_le application_id; u32_le size; u8 nrr_kind; // 7.0.0+ - std::array reserved_2; + INSERT_PADDING_BYTES(3); u32_le hash_offset; u32_le hash_count; - u64_le reserved_3; + INSERT_PADDING_WORDS(2); }; static_assert(sizeof(NRRHeader) == 0x350, "NRRHeader has invalid size."); @@ -76,9 +76,9 @@ static_assert(sizeof(SegmentHeader) == 0x8, "SegmentHeader has invalid size."); struct NROHeader { // Switchbrew calls this "Start" (0x10) - u32_le unused; + INSERT_PADDING_WORDS(1); u32_le mod_offset; - u64_le padding; + INSERT_PADDING_WORDS(2); // Switchbrew calls this "Header" (0x70) u32_le magic; @@ -88,10 +88,10 @@ struct NROHeader { // .text, .ro, .data std::array segment_headers; u32_le bss_size; - u32_le reserved; + INSERT_PADDING_WORDS(1); std::array build_id; u32_le dso_handle_offset; - u32_le unused_2; + INSERT_PADDING_WORDS(1); // .apiInfo, .dynstr, .dynsym std::array segment_headers_2; }; From 765e37aa356d9398ee8d4b4eaac2fd35b065aa38 Mon Sep 17 00:00:00 2001 From: John Galt Date: Mon, 29 Jun 2020 06:49:22 -0400 Subject: [PATCH 241/252] cmake: fix fmt linking On gcc/ld, and clang/lld, fmt::v6 symbols are excluded, so linking fails. This fixes the issue. Note: This was included in the FindBoost changes I shared with BlinkHawk, however only they were merged. I'm not sure if it was missed, or if there was an issue with this part of the change. --- src/common/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index d120c8d3d..1808f7e3f 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -191,5 +191,5 @@ endif() create_target_directory_groups(common) find_package(Boost 1.71 COMPONENTS context headers REQUIRED) -target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile) +target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt microprofile) target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd xbyak) From 10eca7f651d0dc407c7c4076d11e0b960d9dedd4 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Mon, 29 Jun 2020 11:48:38 -0400 Subject: [PATCH 242/252] maxwell_to_gl: Rename VertexType() to VertexFormat() --- src/video_core/renderer_opengl/gl_rasterizer.cpp | 5 +++-- src/video_core/renderer_opengl/maxwell_to_gl.h | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 362457ffe..e960a0ef1 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -213,9 +213,10 @@ void RasterizerOpenGL::SetupVertexFormat() { if (attrib.type == Maxwell::VertexAttribute::Type::SignedInt || attrib.type == Maxwell::VertexAttribute::Type::UnsignedInt) { glVertexAttribIFormat(gl_index, attrib.ComponentCount(), - MaxwellToGL::VertexType(attrib), attrib.offset); + MaxwellToGL::VertexFormat(attrib), attrib.offset); } else { - glVertexAttribFormat(gl_index, attrib.ComponentCount(), MaxwellToGL::VertexType(attrib), + glVertexAttribFormat(gl_index, attrib.ComponentCount(), + MaxwellToGL::VertexFormat(attrib), attrib.IsNormalized() ? GL_TRUE : GL_FALSE, attrib.offset); } glVertexAttribBinding(gl_index, attrib.buffer); diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h index 8f3871e90..774e70a5b 100644 --- a/src/video_core/renderer_opengl/maxwell_to_gl.h +++ b/src/video_core/renderer_opengl/maxwell_to_gl.h @@ -24,7 +24,7 @@ namespace MaxwellToGL { using Maxwell = Tegra::Engines::Maxwell3D::Regs; -inline GLenum VertexType(Maxwell::VertexAttribute attrib) { +inline GLenum VertexFormat(Maxwell::VertexAttribute attrib) { switch (attrib.type) { case Maxwell::VertexAttribute::Type::UnsignedNorm: case Maxwell::VertexAttribute::Type::UnsignedScaled: @@ -87,7 +87,7 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) { } break; } - UNIMPLEMENTED_MSG("Unimplemented vertex type={} and size={}", attrib.TypeString(), + UNIMPLEMENTED_MSG("Unimplemented vertex format of type={} and size={}", attrib.TypeString(), attrib.SizeString()); return {}; } From e6085ea35f0b2579a1df40ee4e24d02e47ee85bc Mon Sep 17 00:00:00 2001 From: Jan Beich Date: Mon, 29 Jun 2020 22:39:31 +0000 Subject: [PATCH 243/252] common: add sysconf() fallback src/common/memory_detect.cpp:15:10: fatal error: 'sys/sysinfo.h' file not found #include ^~~~~~~~~~~~~~~ --- src/common/memory_detect.cpp | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/common/memory_detect.cpp b/src/common/memory_detect.cpp index 3fdc309a2..e981b9a2a 100644 --- a/src/common/memory_detect.cpp +++ b/src/common/memory_detect.cpp @@ -9,10 +9,12 @@ // clang-format on #else #include -#ifdef __APPLE__ +#if defined(__APPLE__) || defined(__FreeBSD__) #include -#else +#elif defined(__linux__) #include +#else +#include #endif #endif @@ -42,11 +44,22 @@ static MemoryInfo Detect() { sysctlbyname("vm.swapusage", &vmusage, &sizeof_vmusage, NULL, 0); mem_info.TotalPhysicalMemory = ramsize; mem_info.TotalSwapMemory = vmusage.xsu_total; -#else +#elif defined(__FreeBSD__) + u_long physmem, swap_total; + std::size_t sizeof_u_long = sizeof(u_long); + // sysctlbyname(const char *, void *, size_t *, const void *, size_t); + sysctlbyname("hw.physmem", &physmem, &sizeof_u_long, NULL, 0); + sysctlbyname("vm.swap_total", &swap_total, &sizeof_u_long, NULL, 0); + mem_info.TotalPhysicalMemory = physmem; + mem_info.TotalSwapMemory = swap_total; +#elif defined(__linux__) struct sysinfo meminfo; sysinfo(&meminfo); mem_info.TotalPhysicalMemory = meminfo.totalram; mem_info.TotalSwapMemory = meminfo.totalswap; +#else + mem_info.TotalPhysicalMemory = sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGE_SIZE); + mem_info.TotalSwapMemory = 0; #endif return mem_info; From dda90ce1c24935ebc085dfcee4eecba200a49d17 Mon Sep 17 00:00:00 2001 From: Jan Beich Date: Mon, 29 Jun 2020 23:52:40 +0000 Subject: [PATCH 244/252] cmake: depend on WebEngine with system Qt CMake Error at src/yuzu/CMakeLists.txt:7 (add_executable): Target "yuzu" links to target "Qt5::WebEngineCore" but the target was not found. Perhaps a find_package() call is missing for an IMPORTED target, or an ALIAS target is missing? --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index b71071271..73405ce4b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -214,6 +214,9 @@ if(ENABLE_QT) set(QT_PREFIX_HINT HINTS "${QT_PREFIX}") endif() find_package(Qt5 5.9 COMPONENTS Widgets OpenGL ${QT_PREFIX_HINT}) + if (YUZU_USE_QT_WEB_ENGINE) + find_package(Qt5 COMPONENTS WebEngineCore WebEngineWidgets) + endif() if (NOT Qt5_FOUND) list(APPEND CONAN_REQUIRED_LIBS "qt/5.14.1@bincrafters/stable") endif() From 7c970132b5dd6eaa40f114355e0125091ceb8142 Mon Sep 17 00:00:00 2001 From: David <25727384+ogniK5377@users.noreply.github.com> Date: Tue, 30 Jun 2020 15:32:24 +1000 Subject: [PATCH 245/252] macro: Add support for "middle methods" on the code cache (#4112) Macro code is just uploaded sequentially from a starting address, however that does not mean the entry point for the macro is at that address. This PR adds preliminary support for executing macros in the middle of our cached code. --- src/video_core/macro/macro.cpp | 35 ++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/src/video_core/macro/macro.cpp b/src/video_core/macro/macro.cpp index ef7dad349..a50e7b4e0 100644 --- a/src/video_core/macro/macro.cpp +++ b/src/video_core/macro/macro.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include #include #include "common/assert.h" #include "common/logging/log.h" @@ -35,22 +36,40 @@ void MacroEngine::Execute(Engines::Maxwell3D& maxwell3d, u32 method, } } else { // Macro not compiled, check if it's uploaded and if so, compile it - auto macro_code = uploaded_macro_code.find(method); + std::optional mid_method = std::nullopt; + const auto macro_code = uploaded_macro_code.find(method); if (macro_code == uploaded_macro_code.end()) { - UNREACHABLE_MSG("Macro 0x{0:x} was not uploaded", method); - return; + for (const auto& [method_base, code] : uploaded_macro_code) { + if (method >= method_base && (method - method_base) < code.size()) { + mid_method = method_base; + break; + } + } + if (!mid_method.has_value()) { + UNREACHABLE_MSG("Macro 0x{0:x} was not uploaded", method); + return; + } } auto& cache_info = macro_cache[method]; - cache_info.hash = boost::hash_value(macro_code->second); - cache_info.lle_program = Compile(macro_code->second); + + if (!mid_method.has_value()) { + cache_info.lle_program = Compile(macro_code->second); + cache_info.hash = boost::hash_value(macro_code->second); + } else { + const auto& macro_cached = uploaded_macro_code[mid_method.value()]; + const auto rebased_method = method - mid_method.value(); + auto& code = uploaded_macro_code[method]; + code.resize(macro_cached.size() - rebased_method); + std::memcpy(code.data(), macro_cached.data() + rebased_method, + code.size() * sizeof(u32)); + cache_info.hash = boost::hash_value(code); + cache_info.lle_program = Compile(code); + } auto hle_program = hle_macros->GetHLEProgram(cache_info.hash); if (hle_program.has_value()) { cache_info.has_hle_program = true; cache_info.hle_program = std::move(hle_program.value()); - } - - if (cache_info.has_hle_program) { cache_info.hle_program->Execute(parameters, method); } else { cache_info.lle_program->Execute(parameters, method); From 1b31755ba6eb3940d2ec0661337ef21913f9a756 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Sat, 13 Jun 2020 11:21:27 -0400 Subject: [PATCH 246/252] maxwell_to_gl: Implement MirrorOnceClampOGL using GL_MIRROR_CLAMP_EXT Like MirrorOnceBorder, this requires the GL_EXT_texture_mirror_clamp extension. This extension is unfortunately not available on Intel's drivers (both Windows proprietary and Linux Mesa). Use GL_MIRROR_CLAMP_TO_EDGE as a fallback if the extension is unavailable. --- src/video_core/renderer_opengl/maxwell_to_gl.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h index 774e70a5b..fe9bd4b5a 100644 --- a/src/video_core/renderer_opengl/maxwell_to_gl.h +++ b/src/video_core/renderer_opengl/maxwell_to_gl.h @@ -191,6 +191,12 @@ inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) { } else { return GL_MIRROR_CLAMP_TO_EDGE; } + case Tegra::Texture::WrapMode::MirrorOnceClampOGL: + if (GL_EXT_texture_mirror_clamp) { + return GL_MIRROR_CLAMP_EXT; + } else { + return GL_MIRROR_CLAMP_TO_EDGE; + } } UNIMPLEMENTED_MSG("Unimplemented texture wrap mode={}", static_cast(wrap_mode)); return GL_REPEAT; From 3b1683a152610b2da161ba3084e2dd5942127303 Mon Sep 17 00:00:00 2001 From: Jan Beich Date: Tue, 30 Jun 2020 22:55:47 +0000 Subject: [PATCH 247/252] common: switch to nullptr for sysctl's empty new value --- src/common/memory_detect.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/common/memory_detect.cpp b/src/common/memory_detect.cpp index e981b9a2a..8cff6ec37 100644 --- a/src/common/memory_detect.cpp +++ b/src/common/memory_detect.cpp @@ -40,16 +40,16 @@ static MemoryInfo Detect() { // hw and vm are defined in sysctl.h // https://github.com/apple/darwin-xnu/blob/master/bsd/sys/sysctl.h#L471 // sysctlbyname(const char *, void *, size_t *, void *, size_t); - sysctlbyname("hw.memsize", &ramsize, &sizeof_ramsize, NULL, 0); - sysctlbyname("vm.swapusage", &vmusage, &sizeof_vmusage, NULL, 0); + sysctlbyname("hw.memsize", &ramsize, &sizeof_ramsize, nullptr, 0); + sysctlbyname("vm.swapusage", &vmusage, &sizeof_vmusage, nullptr, 0); mem_info.TotalPhysicalMemory = ramsize; mem_info.TotalSwapMemory = vmusage.xsu_total; #elif defined(__FreeBSD__) u_long physmem, swap_total; std::size_t sizeof_u_long = sizeof(u_long); // sysctlbyname(const char *, void *, size_t *, const void *, size_t); - sysctlbyname("hw.physmem", &physmem, &sizeof_u_long, NULL, 0); - sysctlbyname("vm.swap_total", &swap_total, &sizeof_u_long, NULL, 0); + sysctlbyname("hw.physmem", &physmem, &sizeof_u_long, nullptr, 0); + sysctlbyname("vm.swap_total", &swap_total, &sizeof_u_long, nullptr, 0); mem_info.TotalPhysicalMemory = physmem; mem_info.TotalSwapMemory = swap_total; #elif defined(__linux__) From 00a1d106bd11e4290ac9b9c460435c8cae3f5d6b Mon Sep 17 00:00:00 2001 From: Lioncash Date: Wed, 1 Jul 2020 00:21:29 -0400 Subject: [PATCH 248/252] key_manager: Make use of canonical deleted operator= operator= typically returns a reference, it's not void. While we're at it, we can correct the parameter formatting to adhere to the codebase. --- src/core/crypto/key_manager.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h index bf3434e1c..f1125e083 100644 --- a/src/core/crypto/key_manager.h +++ b/src/core/crypto/key_manager.h @@ -228,8 +228,8 @@ public: return instance; } - KeyManager(KeyManager const&) = delete; - void operator=(KeyManager const&) = delete; + KeyManager(const KeyManager&) = delete; + KeyManager& operator=(const KeyManager&) = delete; bool HasKey(S128KeyType id, u64 field1 = 0, u64 field2 = 0) const; bool HasKey(S256KeyType id, u64 field1 = 0, u64 field2 = 0) const; From c91710a82fc99b420dfab103fe89bdd44ba004cb Mon Sep 17 00:00:00 2001 From: Lioncash Date: Wed, 1 Jul 2020 00:24:35 -0400 Subject: [PATCH 249/252] key_manager: Delete move operations Prevents the singleton from being moved from. --- src/core/crypto/key_manager.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h index f1125e083..44e7c5bb5 100644 --- a/src/core/crypto/key_manager.h +++ b/src/core/crypto/key_manager.h @@ -231,6 +231,9 @@ public: KeyManager(const KeyManager&) = delete; KeyManager& operator=(const KeyManager&) = delete; + KeyManager(KeyManager&&) = delete; + KeyManager& operator=(KeyManager&&) = delete; + bool HasKey(S128KeyType id, u64 field1 = 0, u64 field2 = 0) const; bool HasKey(S256KeyType id, u64 field1 = 0, u64 field2 = 0) const; From fb13f053bb6ef304bc7656b521727c87eff697f1 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Wed, 1 Jul 2020 00:28:49 -0400 Subject: [PATCH 250/252] key_manager: Correct casing of instance() Our codebase uppercases member function names. --- src/core/crypto/key_manager.h | 2 +- src/core/file_sys/bis_factory.cpp | 2 +- src/core/file_sys/card_image.h | 2 +- src/core/file_sys/content_archive.h | 2 +- src/core/file_sys/registered_cache.h | 2 +- src/core/file_sys/submission_package.cpp | 2 +- src/core/file_sys/submission_package.h | 2 +- src/core/file_sys/xts_archive.h | 2 +- src/core/hle/service/es/es.cpp | 2 +- src/yuzu/main.cpp | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h index 44e7c5bb5..9269a73f2 100644 --- a/src/core/crypto/key_manager.h +++ b/src/core/crypto/key_manager.h @@ -223,7 +223,7 @@ bool operator<(const KeyIndex& lhs, const KeyIndex& rhs) { class KeyManager { public: - static KeyManager& instance() { + static KeyManager& Instance() { static KeyManager instance; return instance; } diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp index 464ca6503..8935a62c3 100644 --- a/src/core/file_sys/bis_factory.cpp +++ b/src/core/file_sys/bis_factory.cpp @@ -79,7 +79,7 @@ VirtualDir BISFactory::OpenPartition(BisPartitionId id) const { } VirtualFile BISFactory::OpenPartitionStorage(BisPartitionId id) const { - Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::instance(); + auto& keys = Core::Crypto::KeyManager::Instance(); Core::Crypto::PartitionDataManager pdm{ Core::System::GetInstance().GetFilesystem()->OpenDirectory( FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), Mode::Read)}; diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h index a09d504ae..e1b136426 100644 --- a/src/core/file_sys/card_image.h +++ b/src/core/file_sys/card_image.h @@ -140,6 +140,6 @@ private: u64 update_normal_partition_end; - Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::instance(); + Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance(); }; } // namespace FileSys diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h index e6c887b32..d25cbcf91 100644 --- a/src/core/file_sys/content_archive.h +++ b/src/core/file_sys/content_archive.h @@ -158,7 +158,7 @@ private: bool encrypted = false; bool is_update = false; - Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::instance(); + Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance(); }; } // namespace FileSys diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h index 4b2fb08cb..f339cd17b 100644 --- a/src/core/file_sys/registered_cache.h +++ b/src/core/file_sys/registered_cache.h @@ -88,7 +88,7 @@ public: protected: // A single instance of KeyManager to be used by GetEntry() - Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::instance(); + Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance(); }; class PlaceholderCache { diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp index c35a0d10b..175a8266a 100644 --- a/src/core/file_sys/submission_package.cpp +++ b/src/core/file_sys/submission_package.cpp @@ -21,7 +21,7 @@ namespace FileSys { namespace { void SetTicketKeys(const std::vector& files) { - Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::instance(); + auto& keys = Core::Crypto::KeyManager::Instance(); for (const auto& ticket_file : files) { if (ticket_file == nullptr) { diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h index bd577f6e5..cf89de6a9 100644 --- a/src/core/file_sys/submission_package.h +++ b/src/core/file_sys/submission_package.h @@ -73,7 +73,7 @@ private: std::map, std::shared_ptr>> ncas; std::vector ticket_files; - Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::instance(); + Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance(); VirtualFile romfs; VirtualDir exefs; diff --git a/src/core/file_sys/xts_archive.h b/src/core/file_sys/xts_archive.h index 95da907bc..563531bb6 100644 --- a/src/core/file_sys/xts_archive.h +++ b/src/core/file_sys/xts_archive.h @@ -62,6 +62,6 @@ private: VirtualFile dec_file; - Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::instance(); + Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance(); }; } // namespace FileSys diff --git a/src/core/hle/service/es/es.cpp b/src/core/hle/service/es/es.cpp index da6b74a22..a41c73c48 100644 --- a/src/core/hle/service/es/es.cpp +++ b/src/core/hle/service/es/es.cpp @@ -302,7 +302,7 @@ private: rb.Push(write_size); } - Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::instance(); + Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance(); }; void InstallInterfaces(SM::ServiceManager& service_manager) { diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index f586950e7..fb299a39b 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -2226,7 +2226,7 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { "title.keys_autogenerated"); } - Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::instance(); + Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance(); if (keys.BaseDeriveNecessary()) { Core::Crypto::PartitionDataManager pdm{vfs->OpenDirectory( FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), FileSys::Mode::Read)}; From d5dfe34c4935d1494f4b742fa3bb4066d9096236 Mon Sep 17 00:00:00 2001 From: David Marcec Date: Wed, 1 Jul 2020 15:09:33 +1000 Subject: [PATCH 251/252] externals: Track opus as submodule instead of using conan Supersedes #4068 see for details. --- .gitmodules | 3 + CMakeLists.txt | 2 - externals/CMakeLists.txt | 3 + externals/opus/CMakeLists.txt | 254 ++++++++++++++++++++++++++++++++++ externals/opus/opus | 1 + src/core/CMakeLists.txt | 2 +- 6 files changed, 262 insertions(+), 3 deletions(-) create mode 100644 externals/opus/CMakeLists.txt create mode 160000 externals/opus/opus diff --git a/.gitmodules b/.gitmodules index 9ba8fe207..6fa823c1c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -34,3 +34,6 @@ [submodule "xbyak"] path = externals/xbyak url = https://github.com/herumi/xbyak.git +[submodule "opus"] + path = externals/opus/opus + url = https://github.com/xiph/opus.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 73405ce4b..d0af994da 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -156,8 +156,6 @@ macro(yuzu_find_packages) #"libzip 1.5 libzip/1.5.2@bincrafters/stable" "lz4 1.8 lz4/1.9.2" "nlohmann_json 3.7 nlohmann_json/3.7.3" - # we need to be careful as the version check might be broken https://github.com/xiph/opus/issues/110 - "opus 1.3 opus/1.3.1" "ZLIB 1.2 zlib/1.2.11" "zstd 1.4 zstd/1.4.4" ) diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index b80b27605..d1dcc403b 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -91,3 +91,6 @@ if (ENABLE_WEB_SERVICE) target_compile_definitions(httplib INTERFACE -DCPPHTTPLIB_OPENSSL_SUPPORT) target_link_libraries(httplib INTERFACE ${OPENSSL_LIBRARIES}) endif() + +# Opus +add_subdirectory(opus) diff --git a/externals/opus/CMakeLists.txt b/externals/opus/CMakeLists.txt new file mode 100644 index 000000000..94a86551f --- /dev/null +++ b/externals/opus/CMakeLists.txt @@ -0,0 +1,254 @@ +cmake_minimum_required(VERSION 3.8) + +project(opus) + +option(OPUS_STACK_PROTECTOR "Use stack protection" OFF) +option(OPUS_USE_ALLOCA "Use alloca for stack arrays (on non-C99 compilers)" OFF) +option(OPUS_CUSTOM_MODES "Enable non-Opus modes, e.g. 44.1 kHz & 2^n frames" OFF) +option(OPUS_FIXED_POINT "Compile as fixed-point (for machines without a fast enough FPU)" OFF) +option(OPUS_ENABLE_FLOAT_API "Compile with the floating point API (for machines with float library" ON) + +include(opus/opus_functions.cmake) + +if(OPUS_STACK_PROTECTOR) + if(NOT MSVC) # GC on by default on MSVC + check_and_set_flag(STACK_PROTECTION_STRONG -fstack-protector-strong) + endif() +else() + if(MSVC) + check_and_set_flag(BUFFER_SECURITY_CHECK /GS-) + endif() +endif() + +add_library(opus STATIC + # CELT sources + opus/celt/bands.c + opus/celt/celt.c + opus/celt/celt_decoder.c + opus/celt/celt_encoder.c + opus/celt/celt_lpc.c + opus/celt/cwrs.c + opus/celt/entcode.c + opus/celt/entdec.c + opus/celt/entenc.c + opus/celt/kiss_fft.c + opus/celt/laplace.c + opus/celt/mathops.c + opus/celt/mdct.c + opus/celt/modes.c + opus/celt/pitch.c + opus/celt/quant_bands.c + opus/celt/rate.c + opus/celt/vq.c + + # SILK sources + opus/silk/A2NLSF.c + opus/silk/CNG.c + opus/silk/HP_variable_cutoff.c + opus/silk/LPC_analysis_filter.c + opus/silk/LPC_fit.c + opus/silk/LPC_inv_pred_gain.c + opus/silk/LP_variable_cutoff.c + opus/silk/NLSF2A.c + opus/silk/NLSF_VQ.c + opus/silk/NLSF_VQ_weights_laroia.c + opus/silk/NLSF_decode.c + opus/silk/NLSF_del_dec_quant.c + opus/silk/NLSF_encode.c + opus/silk/NLSF_stabilize.c + opus/silk/NLSF_unpack.c + opus/silk/NSQ.c + opus/silk/NSQ_del_dec.c + opus/silk/PLC.c + opus/silk/VAD.c + opus/silk/VQ_WMat_EC.c + opus/silk/ana_filt_bank_1.c + opus/silk/biquad_alt.c + opus/silk/bwexpander.c + opus/silk/bwexpander_32.c + opus/silk/check_control_input.c + opus/silk/code_signs.c + opus/silk/control_SNR.c + opus/silk/control_audio_bandwidth.c + opus/silk/control_codec.c + opus/silk/dec_API.c + opus/silk/decode_core.c + opus/silk/decode_frame.c + opus/silk/decode_indices.c + opus/silk/decode_parameters.c + opus/silk/decode_pitch.c + opus/silk/decode_pulses.c + opus/silk/decoder_set_fs.c + opus/silk/enc_API.c + opus/silk/encode_indices.c + opus/silk/encode_pulses.c + opus/silk/gain_quant.c + opus/silk/init_decoder.c + opus/silk/init_encoder.c + opus/silk/inner_prod_aligned.c + opus/silk/interpolate.c + opus/silk/lin2log.c + opus/silk/log2lin.c + opus/silk/pitch_est_tables.c + opus/silk/process_NLSFs.c + opus/silk/quant_LTP_gains.c + opus/silk/resampler.c + opus/silk/resampler_down2.c + opus/silk/resampler_down2_3.c + opus/silk/resampler_private_AR2.c + opus/silk/resampler_private_IIR_FIR.c + opus/silk/resampler_private_down_FIR.c + opus/silk/resampler_private_up2_HQ.c + opus/silk/resampler_rom.c + opus/silk/shell_coder.c + opus/silk/sigm_Q15.c + opus/silk/sort.c + opus/silk/stereo_LR_to_MS.c + opus/silk/stereo_MS_to_LR.c + opus/silk/stereo_decode_pred.c + opus/silk/stereo_encode_pred.c + opus/silk/stereo_find_predictor.c + opus/silk/stereo_quant_pred.c + opus/silk/sum_sqr_shift.c + opus/silk/table_LSF_cos.c + opus/silk/tables_LTP.c + opus/silk/tables_NLSF_CB_NB_MB.c + opus/silk/tables_NLSF_CB_WB.c + opus/silk/tables_gain.c + opus/silk/tables_other.c + opus/silk/tables_pitch_lag.c + opus/silk/tables_pulses_per_block.c + + # Opus sources + opus/src/analysis.c + opus/src/mapping_matrix.c + opus/src/mlp.c + opus/src/mlp_data.c + opus/src/opus.c + opus/src/opus_decoder.c + opus/src/opus_encoder.c + opus/src/opus_multistream.c + opus/src/opus_multistream_decoder.c + opus/src/opus_multistream_encoder.c + opus/src/opus_projection_decoder.c + opus/src/opus_projection_encoder.c + opus/src/repacketizer.c +) + +if (DEBUG) + target_sources(opus PRIVATE opus/silk/debug.c) +endif() + +if (OPUS_FIXED_POINT) + target_sources(opus PRIVATE + opus/silk/fixed/LTP_analysis_filter_FIX.c + opus/silk/fixed/LTP_scale_ctrl_FIX.c + opus/silk/fixed/apply_sine_window_FIX.c + opus/silk/fixed/autocorr_FIX.c + opus/silk/fixed/burg_modified_FIX.c + opus/silk/fixed/corrMatrix_FIX.c + opus/silk/fixed/encode_frame_FIX.c + opus/silk/fixed/find_LPC_FIX.c + opus/silk/fixed/find_LTP_FIX.c + opus/silk/fixed/find_pitch_lags_FIX.c + opus/silk/fixed/find_pred_coefs_FIX.c + opus/silk/fixed/k2a_FIX.c + opus/silk/fixed/k2a_Q16_FIX.c + opus/silk/fixed/noise_shape_analysis_FIX.c + opus/silk/fixed/pitch_analysis_core_FIX.c + opus/silk/fixed/prefilter_FIX.c + opus/silk/fixed/process_gains_FIX.c + opus/silk/fixed/regularize_correlations_FIX.c + opus/silk/fixed/residual_energy16_FIX.c + opus/silk/fixed/residual_energy_FIX.c + opus/silk/fixed/schur64_FIX.c + opus/silk/fixed/schur_FIX.c + opus/silk/fixed/solve_LS_FIX.c + opus/silk/fixed/vector_ops_FIX.c + opus/silk/fixed/warped_autocorrelation_FIX.c + ) +else() + target_sources(opus PRIVATE + opus/silk/float/LPC_analysis_filter_FLP.c + opus/silk/float/LPC_inv_pred_gain_FLP.c + opus/silk/float/LTP_analysis_filter_FLP.c + opus/silk/float/LTP_scale_ctrl_FLP.c + opus/silk/float/apply_sine_window_FLP.c + opus/silk/float/autocorrelation_FLP.c + opus/silk/float/burg_modified_FLP.c + opus/silk/float/bwexpander_FLP.c + opus/silk/float/corrMatrix_FLP.c + opus/silk/float/encode_frame_FLP.c + opus/silk/float/energy_FLP.c + opus/silk/float/find_LPC_FLP.c + opus/silk/float/find_LTP_FLP.c + opus/silk/float/find_pitch_lags_FLP.c + opus/silk/float/find_pred_coefs_FLP.c + opus/silk/float/inner_product_FLP.c + opus/silk/float/k2a_FLP.c + opus/silk/float/noise_shape_analysis_FLP.c + opus/silk/float/pitch_analysis_core_FLP.c + opus/silk/float/process_gains_FLP.c + opus/silk/float/regularize_correlations_FLP.c + opus/silk/float/residual_energy_FLP.c + opus/silk/float/scale_copy_vector_FLP.c + opus/silk/float/scale_vector_FLP.c + opus/silk/float/schur_FLP.c + opus/silk/float/sort_FLP.c + opus/silk/float/warped_autocorrelation_FLP.c + opus/silk/float/wrappers_FLP.c + ) +endif() + +target_compile_definitions(opus PRIVATE OPUS_BUILD ENABLE_HARDENING) + +if(NOT MSVC) + if(MINGW) + target_compile_definitions(opus PRIVATE _FORTIFY_SOURCE=0) + else() + target_compile_definitions(opus PRIVATE _FORTIFY_SOURCE=2) + endif() +endif() + +# It is strongly recommended to uncomment one of these VAR_ARRAYS: Use C99 +# variable-length arrays for stack allocation USE_ALLOCA: Use alloca() for stack +# allocation If none is defined, then the fallback is a non-threadsafe global +# array +if(OPUS_USE_ALLOCA OR MSVC) + target_compile_definitions(opus PRIVATE USE_ALLOCA) +else() + target_compile_definitions(opus PRIVATE VAR_ARRAYS) +endif() + +if(OPUS_CUSTOM_MODES) + target_compile_definitions(opus PRIVATE CUSTOM_MODES) +endif() + +if(NOT OPUS_ENABLE_FLOAT_API) + target_compile_definitions(opus PRIVATE DISABLE_FLOAT_API) +endif() + +target_compile_definitions(opus +PUBLIC + -DOPUS_VERSION="\\"1.3.1\\"" + +PRIVATE + # Use C99 intrinsics to speed up float-to-int conversion + HAVE_LRINTF +) + +if (FIXED_POINT) + target_compile_definitions(opus PRIVATE -DFIXED_POINT=1 -DDISABLE_FLOAT_API) +endif() + +target_include_directories(opus +PUBLIC + opus/include + +PRIVATE + opus/celt + opus/silk + opus/silk/fixed + opus/silk/float + opus/src +) diff --git a/externals/opus/opus b/externals/opus/opus new file mode 160000 index 000000000..ad8fe90db --- /dev/null +++ b/externals/opus/opus @@ -0,0 +1 @@ +Subproject commit ad8fe90db79b7d2a135e3dfd2ed6631b0c5662ab diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index f87d67db5..d1f173f42 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -614,7 +614,7 @@ endif() create_target_directory_groups(core) target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) -target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::Opus unicorn zip) +target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls opus unicorn zip) if (YUZU_ENABLE_BOXCAT) target_compile_definitions(core PRIVATE -DYUZU_ENABLE_BOXCAT) From 98fcd3ba5d24d65e91f89f7c9c4f3f60e29e080f Mon Sep 17 00:00:00 2001 From: bunnei Date: Thu, 2 Jul 2020 22:53:12 -0400 Subject: [PATCH 252/252] Revert "cmake: fix fmt linking" --- src/common/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 1808f7e3f..d120c8d3d 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -191,5 +191,5 @@ endif() create_target_directory_groups(common) find_package(Boost 1.71 COMPONENTS context headers REQUIRED) -target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt microprofile) +target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile) target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd xbyak)