diff options
28 files changed, 303 insertions, 189 deletions
diff --git a/externals/dynarmic b/externals/dynarmic -Subproject 82417da7803e2cf18efc28a1cd3f3d0a4b6045a +Subproject 0e1112b7df77ae55a62a51622940d5c8f9e8c84 diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 78c3bfb3b..5d54516eb 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -172,7 +172,6 @@ add_library(common STATIC virtual_buffer.h wall_clock.cpp wall_clock.h - web_result.h zstd_compression.cpp zstd_compression.h ) diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index 443ca72eb..b5f28a86e 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -143,7 +143,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable& config.wall_clock_cntpct = uses_wall_clock; // Safe optimizations - if (Settings::values.cpu_accuracy != Settings::CPUAccuracy::Accurate) { + if (Settings::values.cpu_accuracy == Settings::CPUAccuracy::DebugMode) { if (!Settings::values.cpuopt_page_tables) { config.page_table = nullptr; } @@ -170,6 +170,17 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable& } } + // Unsafe optimizations + if (Settings::values.cpu_accuracy == Settings::CPUAccuracy::Unsafe) { + config.unsafe_optimizations = true; + if (Settings::values.cpuopt_unsafe_unfuse_fma) { + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; + } + if (Settings::values.cpuopt_unsafe_reduce_fp_error) { + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP; + } + } + return std::make_unique<Dynarmic::A32::Jit>(config); } diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index a63a04a25..ce9968724 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<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable& config.wall_clock_cntpct = uses_wall_clock; // Safe optimizations - if (Settings::values.cpu_accuracy != Settings::CPUAccuracy::Accurate) { + if (Settings::values.cpu_accuracy == Settings::CPUAccuracy::DebugMode) { if (!Settings::values.cpuopt_page_tables) { config.page_table = nullptr; } @@ -222,6 +222,17 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable& } } + // Unsafe optimizations + if (Settings::values.cpu_accuracy == Settings::CPUAccuracy::Unsafe) { + config.unsafe_optimizations = true; + if (Settings::values.cpuopt_unsafe_unfuse_fma) { + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; + } + if (Settings::values.cpuopt_unsafe_reduce_fp_error) { + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP; + } + } + return std::make_shared<Dynarmic::A64::Jit>(config); } diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 71af26ec5..e6c8461a5 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -7,14 +7,14 @@ #include <string> #include <tuple> -#include "common/assert.h" #include "common/microprofile.h" #include "core/core_timing.h" #include "core/core_timing_util.h" +#include "core/hardware_properties.h" namespace Core::Timing { -constexpr u64 MAX_SLICE_LENGTH = 4000; +constexpr s64 MAX_SLICE_LENGTH = 4000; std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callback) { return std::make_shared<EventType>(std::move(callback), std::move(name)); @@ -37,10 +37,8 @@ struct CoreTiming::Event { } }; -CoreTiming::CoreTiming() { - clock = - Common::CreateBestMatchingClock(Core::Hardware::BASE_CLOCK_RATE, Core::Hardware::CNTFREQ); -} +CoreTiming::CoreTiming() + : clock{Common::CreateBestMatchingClock(Hardware::BASE_CLOCK_RATE, Hardware::CNTFREQ)} {} CoreTiming::~CoreTiming() = default; @@ -136,7 +134,7 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, void CoreTiming::AddTicks(u64 ticks) { this->ticks += ticks; - downcount -= ticks; + downcount -= static_cast<s64>(ticks); } void CoreTiming::Idle() { diff --git a/src/core/core_timing_util.cpp b/src/core/core_timing_util.cpp index aefc63663..8ce8e602e 100644 --- a/src/core/core_timing_util.cpp +++ b/src/core/core_timing_util.cpp @@ -8,6 +8,7 @@ #include <limits> #include "common/logging/log.h" #include "common/uint128.h" +#include "core/hardware_properties.h" namespace Core::Timing { diff --git a/src/core/core_timing_util.h b/src/core/core_timing_util.h index 2ed979e14..e4a046bf9 100644 --- a/src/core/core_timing_util.h +++ b/src/core/core_timing_util.h @@ -6,7 +6,6 @@ #include <chrono> #include "common/common_types.h" -#include "core/hardware_properties.h" namespace Core::Timing { diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp index 8783d1ac2..dc591c730 100644 --- a/src/core/crypto/key_manager.cpp +++ b/src/core/crypto/key_manager.cpp @@ -36,6 +36,7 @@ #include "core/settings.h" namespace Core::Crypto { +namespace { constexpr u64 CURRENT_CRYPTO_REVISION = 0x5; constexpr u64 FULL_TICKET_SIZE = 0x400; @@ -49,7 +50,72 @@ constexpr std::array eticket_source_hashes{ }; // clang-format on -const std::map<std::pair<S128KeyType, u64>, std::string> KEYS_VARIABLE_LENGTH{ +constexpr std::array<std::pair<std::string_view, KeyIndex<S128KeyType>>, 30> s128_file_id{{ + {"eticket_rsa_kek", {S128KeyType::ETicketRSAKek, 0, 0}}, + {"eticket_rsa_kek_source", + {S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKek), 0}}, + {"eticket_rsa_kekek_source", + {S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKekek), 0}}, + {"rsa_kek_mask_0", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Mask0), 0}}, + {"rsa_kek_seed_3", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Seed3), 0}}, + {"rsa_oaep_kek_generation_source", + {S128KeyType::Source, static_cast<u64>(SourceKeyType::RSAOaepKekGeneration), 0}}, + {"sd_card_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek), 0}}, + {"aes_kek_generation_source", + {S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration), 0}}, + {"aes_key_generation_source", + {S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration), 0}}, + {"package2_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Package2), 0}}, + {"master_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Master), 0}}, + {"header_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::HeaderKek), 0}}, + {"key_area_key_application_source", + {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey), + static_cast<u64>(KeyAreaKeyType::Application)}}, + {"key_area_key_ocean_source", + {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey), + static_cast<u64>(KeyAreaKeyType::Ocean)}}, + {"key_area_key_system_source", + {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey), + static_cast<u64>(KeyAreaKeyType::System)}}, + {"titlekek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Titlekek), 0}}, + {"keyblob_mac_key_source", + {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC), 0}}, + {"tsec_key", {S128KeyType::TSEC, 0, 0}}, + {"secure_boot_key", {S128KeyType::SecureBoot, 0, 0}}, + {"sd_seed", {S128KeyType::SDSeed, 0, 0}}, + {"bis_key_0_crypt", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Crypto)}}, + {"bis_key_0_tweak", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Tweak)}}, + {"bis_key_1_crypt", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Crypto)}}, + {"bis_key_1_tweak", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Tweak)}}, + {"bis_key_2_crypt", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Crypto)}}, + {"bis_key_2_tweak", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Tweak)}}, + {"bis_key_3_crypt", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Crypto)}}, + {"bis_key_3_tweak", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Tweak)}}, + {"header_kek", {S128KeyType::HeaderKek, 0, 0}}, + {"sd_card_kek", {S128KeyType::SDKek, 0, 0}}, +}}; + +auto Find128ByName(std::string_view name) { + return std::find_if(s128_file_id.begin(), s128_file_id.end(), + [&name](const auto& pair) { return pair.first == name; }); +} + +constexpr std::array<std::pair<std::string_view, KeyIndex<S256KeyType>>, 6> s256_file_id{{ + {"header_key", {S256KeyType::Header, 0, 0}}, + {"sd_card_save_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save), 0}}, + {"sd_card_nca_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::NCA), 0}}, + {"header_key_source", {S256KeyType::HeaderSource, 0, 0}}, + {"sd_card_save_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::Save), 0}}, + {"sd_card_nca_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::NCA), 0}}, +}}; + +auto Find256ByName(std::string_view name) { + return std::find_if(s256_file_id.begin(), s256_file_id.end(), + [&name](const auto& pair) { return pair.first == name; }); +} + +using KeyArray = std::array<std::pair<std::pair<S128KeyType, u64>, std::string_view>, 7>; +constexpr KeyArray KEYS_VARIABLE_LENGTH{{ {{S128KeyType::Master, 0}, "master_key_"}, {{S128KeyType::Package1, 0}, "package1_key_"}, {{S128KeyType::Package2, 0}, "package2_key_"}, @@ -57,14 +123,13 @@ const std::map<std::pair<S128KeyType, u64>, std::string> KEYS_VARIABLE_LENGTH{ {{S128KeyType::Source, static_cast<u64>(SourceKeyType::Keyblob)}, "keyblob_key_source_"}, {{S128KeyType::Keyblob, 0}, "keyblob_key_"}, {{S128KeyType::KeyblobMAC, 0}, "keyblob_mac_key_"}, -}; +}}; -namespace { template <std::size_t Size> bool IsAllZeroArray(const std::array<u8, Size>& array) { return std::all_of(array.begin(), array.end(), [](const auto& elem) { return elem == 0; }); } -} // namespace +} // Anonymous namespace u64 GetSignatureTypeDataSize(SignatureType type) { switch (type) { @@ -564,13 +629,13 @@ void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) { s128_keys[{S128KeyType::Titlekey, rights_id[1], rights_id[0]}] = key; } else { out[0] = Common::ToLower(out[0]); - if (s128_file_id.find(out[0]) != s128_file_id.end()) { - const auto index = s128_file_id.at(out[0]); - Key128 key = Common::HexStringToArray<16>(out[1]); + if (const auto iter128 = Find128ByName(out[0]); iter128 != s128_file_id.end()) { + const auto& index = iter128->second; + const Key128 key = Common::HexStringToArray<16>(out[1]); s128_keys[{index.type, index.field1, index.field2}] = key; - } else if (s256_file_id.find(out[0]) != s256_file_id.end()) { - const auto index = s256_file_id.at(out[0]); - Key256 key = Common::HexStringToArray<32>(out[1]); + } else if (const auto iter256 = Find256ByName(out[0]); iter256 != s256_file_id.end()) { + const auto& index = iter256->second; + const Key256 key = Common::HexStringToArray<32>(out[1]); s256_keys[{index.type, index.field1, index.field2}] = key; } else if (out[0].compare(0, 8, "keyblob_") == 0 && out[0].compare(0, 9, "keyblob_k") != 0) { @@ -742,8 +807,7 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) { } const auto iter2 = std::find_if( - s128_file_id.begin(), s128_file_id.end(), - [&id, &field1, &field2](const std::pair<std::string, KeyIndex<S128KeyType>> elem) { + s128_file_id.begin(), s128_file_id.end(), [&id, &field1, &field2](const auto& elem) { return std::tie(elem.second.type, elem.second.field1, elem.second.field2) == std::tie(id, field1, field2); }); @@ -753,9 +817,11 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) { // Variable cases if (id == S128KeyType::KeyArea) { - static constexpr std::array<const char*, 3> kak_names = {"key_area_key_application_{:02X}", - "key_area_key_ocean_{:02X}", - "key_area_key_system_{:02X}"}; + static constexpr std::array<const char*, 3> kak_names = { + "key_area_key_application_{:02X}", + "key_area_key_ocean_{:02X}", + "key_area_key_system_{:02X}", + }; WriteKeyToFile(category, fmt::format(kak_names.at(field2), field1), key); } else if (id == S128KeyType::Master) { WriteKeyToFile(category, fmt::format("master_key_{:02X}", field1), key); @@ -781,8 +847,7 @@ void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) { return; } const auto iter = std::find_if( - s256_file_id.begin(), s256_file_id.end(), - [&id, &field1, &field2](const std::pair<std::string, KeyIndex<S256KeyType>> elem) { + s256_file_id.begin(), s256_file_id.end(), [&id, &field1, &field2](const auto& elem) { return std::tie(elem.second.type, elem.second.field1, elem.second.field2) == std::tie(id, field1, field2); }); @@ -1245,58 +1310,4 @@ bool KeyManager::AddTicketPersonalized(Ticket raw) { SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]); return true; } - -const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> KeyManager::s128_file_id = { - {"eticket_rsa_kek", {S128KeyType::ETicketRSAKek, 0, 0}}, - {"eticket_rsa_kek_source", - {S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKek), 0}}, - {"eticket_rsa_kekek_source", - {S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKekek), 0}}, - {"rsa_kek_mask_0", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Mask0), 0}}, - {"rsa_kek_seed_3", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Seed3), 0}}, - {"rsa_oaep_kek_generation_source", - {S128KeyType::Source, static_cast<u64>(SourceKeyType::RSAOaepKekGeneration), 0}}, - {"sd_card_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek), 0}}, - {"aes_kek_generation_source", - {S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration), 0}}, - {"aes_key_generation_source", - {S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration), 0}}, - {"package2_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Package2), 0}}, - {"master_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Master), 0}}, - {"header_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::HeaderKek), 0}}, - {"key_area_key_application_source", - {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey), - static_cast<u64>(KeyAreaKeyType::Application)}}, - {"key_area_key_ocean_source", - {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey), - static_cast<u64>(KeyAreaKeyType::Ocean)}}, - {"key_area_key_system_source", - {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey), - static_cast<u64>(KeyAreaKeyType::System)}}, - {"titlekek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Titlekek), 0}}, - {"keyblob_mac_key_source", - {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC), 0}}, - {"tsec_key", {S128KeyType::TSEC, 0, 0}}, - {"secure_boot_key", {S128KeyType::SecureBoot, 0, 0}}, - {"sd_seed", {S128KeyType::SDSeed, 0, 0}}, - {"bis_key_0_crypt", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Crypto)}}, - {"bis_key_0_tweak", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Tweak)}}, - {"bis_key_1_crypt", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Crypto)}}, - {"bis_key_1_tweak", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Tweak)}}, - {"bis_key_2_crypt", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Crypto)}}, - {"bis_key_2_tweak", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Tweak)}}, - {"bis_key_3_crypt", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Crypto)}}, - {"bis_key_3_tweak", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Tweak)}}, - {"header_kek", {S128KeyType::HeaderKek, 0, 0}}, - {"sd_card_kek", {S128KeyType::SDKek, 0, 0}}, -}; - -const boost::container::flat_map<std::string, KeyIndex<S256KeyType>> KeyManager::s256_file_id = { - {"header_key", {S256KeyType::Header, 0, 0}}, - {"sd_card_save_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save), 0}}, - {"sd_card_nca_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::NCA), 0}}, - {"header_key_source", {S256KeyType::HeaderSource, 0, 0}}, - {"sd_card_save_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::Save), 0}}, - {"sd_card_nca_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::NCA), 0}}, -}; } // namespace Core::Crypto diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h index bdca3770a..321b75323 100644 --- a/src/core/crypto/key_manager.h +++ b/src/core/crypto/key_manager.h @@ -10,7 +10,6 @@ #include <string> #include <variant> -#include <boost/container/flat_map.hpp> #include <fmt/format.h> #include "common/common_funcs.h" #include "common/common_types.h" @@ -293,9 +292,6 @@ private: void SetKeyWrapped(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0); void SetKeyWrapped(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0); - - static const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> s128_file_id; - static const boost::container::flat_map<std::string, KeyIndex<S256KeyType>> s256_file_id; }; Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed); diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp index 1dbf632c1..488687ba9 100644 --- a/src/core/file_sys/vfs_real.cpp +++ b/src/core/file_sys/vfs_real.cpp @@ -72,8 +72,10 @@ VfsEntryType RealVfsFilesystem::GetEntryType(std::string_view path_) const { VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) { const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); - if (cache.find(path) != cache.end()) { - auto weak = cache[path]; + + if (const auto weak_iter = cache.find(path); weak_iter != cache.cend()) { + const auto& weak = weak_iter->second; + if (!weak.expired()) { return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, weak.lock(), path, perms)); } @@ -84,7 +86,7 @@ VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) { } auto backing = std::make_shared<FS::IOFile>(path, ModeFlagsToString(perms).c_str()); - cache[path] = backing; + cache.insert_or_assign(path, backing); // Cannot use make_shared as RealVfsFile constructor is private return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, backing, path, perms)); @@ -116,11 +118,12 @@ VirtualFile RealVfsFilesystem::CopyFile(std::string_view old_path_, std::string_ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) { const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault); const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault); + const auto cached_file_iter = cache.find(old_path); - if (cache.find(old_path) != cache.end()) { - auto file = cache[old_path].lock(); + if (cached_file_iter != cache.cend()) { + auto file = cached_file_iter->second.lock(); - if (!cache[old_path].expired()) { + if (!cached_file_iter->second.expired()) { file->Close(); } @@ -131,7 +134,7 @@ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_ cache.erase(old_path); file->Open(new_path, "r+b"); - cache[new_path] = file; + cache.insert_or_assign(new_path, std::move(file)); } else { UNREACHABLE(); return nullptr; @@ -142,12 +145,15 @@ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_ bool RealVfsFilesystem::DeleteFile(std::string_view path_) { const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); - if (cache.find(path) != cache.end()) { - if (!cache[path].expired()) { - cache[path].lock()->Close(); + const auto cached_iter = cache.find(path); + + if (cached_iter != cache.cend()) { + if (!cached_iter->second.expired()) { + cached_iter->second.lock()->Close(); } cache.erase(path); } + return FS::Delete(path); } @@ -192,21 +198,25 @@ VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_, } for (auto& kv : cache) { - // Path in cache starts with old_path - if (kv.first.rfind(old_path, 0) == 0) { - const auto file_old_path = - FS::SanitizePath(kv.first, FS::DirectorySeparator::PlatformDefault); - const auto file_new_path = - FS::SanitizePath(new_path + DIR_SEP + kv.first.substr(old_path.size()), - FS::DirectorySeparator::PlatformDefault); - auto cached = cache[file_old_path]; - if (!cached.expired()) { - auto file = cached.lock(); - file->Open(file_new_path, "r+b"); - cache.erase(file_old_path); - cache[file_new_path] = file; - } + // If the path in the cache doesn't start with old_path, then bail on this file. + if (kv.first.rfind(old_path, 0) != 0) { + continue; + } + + const auto file_old_path = + FS::SanitizePath(kv.first, FS::DirectorySeparator::PlatformDefault); + auto file_new_path = FS::SanitizePath(new_path + DIR_SEP + kv.first.substr(old_path.size()), + FS::DirectorySeparator::PlatformDefault); + const auto& cached = cache[file_old_path]; + + if (cached.expired()) { + continue; } + + auto file = cached.lock(); + file->Open(file_new_path, "r+b"); + cache.erase(file_old_path); + cache.insert_or_assign(std::move(file_new_path), std::move(file)); } return OpenDirectory(new_path, Mode::ReadWrite); @@ -214,15 +224,21 @@ VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_, bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) { const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); + for (auto& kv : cache) { - // Path in cache starts with old_path - if (kv.first.rfind(path, 0) == 0) { - if (!cache[kv.first].expired()) { - cache[kv.first].lock()->Close(); - } - cache.erase(kv.first); + // If the path in the cache doesn't start with path, then bail on this file. + if (kv.first.rfind(path, 0) != 0) { + continue; } + + const auto& entry = cache[kv.first]; + if (!entry.expired()) { + entry.lock()->Close(); + } + + cache.erase(kv.first); } + return FS::DeleteDirRecursively(path); } @@ -260,14 +276,14 @@ bool RealVfsFile::IsReadable() const { } std::size_t RealVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const { - if (!backing->Seek(offset, SEEK_SET)) { + if (!backing->Seek(static_cast<s64>(offset), SEEK_SET)) { return 0; } return backing->ReadBytes(data, length); } std::size_t RealVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) { - if (!backing->Seek(offset, SEEK_SET)) { + if (!backing->Seek(static_cast<s64>(offset), SEEK_SET)) { return 0; } return backing->WriteBytes(data, length); diff --git a/src/core/settings.h b/src/core/settings.h index bb145f193..3681b5e9d 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -359,7 +359,8 @@ enum class GPUAccuracy : u32 { enum class CPUAccuracy { Accurate = 0, - DebugMode = 1, + Unsafe = 1, + DebugMode = 2, }; extern bool configuring_global; @@ -419,6 +420,9 @@ struct Values { bool cpuopt_misc_ir; bool cpuopt_reduce_misalign_checks; + bool cpuopt_unsafe_unfuse_fma; + bool cpuopt_unsafe_reduce_fp_error; + // Renderer Setting<RendererBackend> renderer_backend; bool renderer_debug; diff --git a/src/video_core/fence_manager.h b/src/video_core/fence_manager.h index 8b2a6a42c..06cc12d5a 100644 --- a/src/video_core/fence_manager.h +++ b/src/video_core/fence_manager.h @@ -5,15 +5,10 @@ #pragma once #include <algorithm> -#include <array> -#include <memory> #include <queue> -#include "common/assert.h" #include "common/common_types.h" #include "core/core.h" -#include "core/memory.h" -#include "core/settings.h" #include "video_core/gpu.h" #include "video_core/memory_manager.h" #include "video_core/rasterizer_interface.h" diff --git a/src/video_core/macro/macro_interpreter.cpp b/src/video_core/macro/macro_interpreter.cpp index aa5256419..bd01fd1f2 100644 --- a/src/video_core/macro/macro_interpreter.cpp +++ b/src/video_core/macro/macro_interpreter.cpp @@ -34,7 +34,6 @@ void MacroInterpreterImpl::Execute(const std::vector<u32>& parameters, u32 metho this->parameters = std::make_unique<u32[]>(num_parameters); } std::memcpy(this->parameters.get(), parameters.data(), num_parameters * sizeof(u32)); - this->num_parameters = num_parameters; // Execute the code until we hit an exit condition. bool keep_executing = true; diff --git a/src/video_core/renderer_opengl/gl_fence_manager.cpp b/src/video_core/renderer_opengl/gl_fence_manager.cpp index ec5421afa..3d2588dd2 100644 --- a/src/video_core/renderer_opengl/gl_fence_manager.cpp +++ b/src/video_core/renderer_opengl/gl_fence_manager.cpp @@ -4,16 +4,17 @@ #include "common/assert.h" +#include <glad/glad.h> + #include "video_core/renderer_opengl/gl_buffer_cache.h" #include "video_core/renderer_opengl/gl_fence_manager.h" namespace OpenGL { -GLInnerFence::GLInnerFence(u32 payload, bool is_stubbed) - : VideoCommon::FenceBase(payload, is_stubbed), sync_object{} {} +GLInnerFence::GLInnerFence(u32 payload, bool is_stubbed) : FenceBase(payload, is_stubbed) {} GLInnerFence::GLInnerFence(GPUVAddr address, u32 payload, bool is_stubbed) - : VideoCommon::FenceBase(address, payload, is_stubbed), sync_object{} {} + : FenceBase(address, payload, is_stubbed) {} GLInnerFence::~GLInnerFence() = default; diff --git a/src/video_core/renderer_opengl/gl_fence_manager.h b/src/video_core/renderer_opengl/gl_fence_manager.h index c917b3343..1686cf5c8 100644 --- a/src/video_core/renderer_opengl/gl_fence_manager.h +++ b/src/video_core/renderer_opengl/gl_fence_manager.h @@ -5,7 +5,6 @@ #pragma once #include <memory> -#include <glad/glad.h> #include "common/common_types.h" #include "video_core/fence_manager.h" diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp index 52fbab3c1..40c0877c1 100644 --- a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp @@ -214,20 +214,20 @@ std::optional<std::vector<ShaderDiskCacheEntry>> ShaderDiskCacheOpenGL::LoadTran // Skip games without title id const bool has_title_id = system.CurrentProcess()->GetTitleID() != 0; if (!Settings::values.use_disk_shader_cache.GetValue() || !has_title_id) { - return {}; + return std::nullopt; } Common::FS::IOFile file(GetTransferablePath(), "rb"); if (!file.IsOpen()) { LOG_INFO(Render_OpenGL, "No transferable shader cache found"); is_usable = true; - return {}; + return std::nullopt; } u32 version{}; if (file.ReadBytes(&version, sizeof(version)) != sizeof(version)) { LOG_ERROR(Render_OpenGL, "Failed to get transferable cache version, skipping it"); - return {}; + return std::nullopt; } if (version < NativeVersion) { @@ -235,12 +235,12 @@ std::optional<std::vector<ShaderDiskCacheEntry>> ShaderDiskCacheOpenGL::LoadTran file.Close(); InvalidateTransferable(); is_usable = true; - return {}; + return std::nullopt; } if (version > NativeVersion) { LOG_WARNING(Render_OpenGL, "Transferable shader cache was generated with a newer version " "of the emulator, skipping"); - return {}; + return std::nullopt; } // Version is valid, load the shaders @@ -249,7 +249,7 @@ std::optional<std::vector<ShaderDiskCacheEntry>> ShaderDiskCacheOpenGL::LoadTran ShaderDiskCacheEntry& entry = entries.emplace_back(); if (!entry.Load(file)) { LOG_ERROR(Render_OpenGL, "Failed to load transferable raw entry, skipping"); - return {}; + return std::nullopt; } } @@ -290,12 +290,12 @@ std::optional<std::vector<ShaderDiskCachePrecompiled>> ShaderDiskCacheOpenGL::Lo ShaderCacheVersionHash file_hash{}; if (!LoadArrayFromPrecompiled(file_hash.data(), file_hash.size())) { precompiled_cache_virtual_file_offset = 0; - return {}; + return std::nullopt; } if (GetShaderCacheVersionHash() != file_hash) { LOG_INFO(Render_OpenGL, "Precompiled cache is from another version of the emulator"); precompiled_cache_virtual_file_offset = 0; - return {}; + return std::nullopt; } std::vector<ShaderDiskCachePrecompiled> entries; @@ -305,15 +305,16 @@ std::optional<std::vector<ShaderDiskCachePrecompiled>> ShaderDiskCacheOpenGL::Lo if (!LoadObjectFromPrecompiled(entry.unique_identifier) || !LoadObjectFromPrecompiled(entry.binary_format) || !LoadObjectFromPrecompiled(binary_size)) { - return {}; + return std::nullopt; } entry.binary.resize(binary_size); if (!LoadArrayFromPrecompiled(entry.binary.data(), entry.binary.size())) { - return {}; + return std::nullopt; } } - return entries; + + return std::move(entries); } void ShaderDiskCacheOpenGL::InvalidateTransferable() { diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp index 0a7bc9e2b..f403f388a 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.cpp +++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp @@ -403,7 +403,7 @@ void CachedSurface::DecorateSurfaceName() { LabelGLObject(GL_TEXTURE, texture.handle, GetGpuAddr(), params.TargetName()); } -void CachedSurfaceView::DecorateViewName(GPUVAddr gpu_addr, std::string prefix) { +void CachedSurfaceView::DecorateViewName(GPUVAddr gpu_addr, const std::string& prefix) { LabelGLObject(GL_TEXTURE, main_view.handle, gpu_addr, prefix); } diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h index bfc4ddf5d..de8f18489 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.h +++ b/src/video_core/renderer_opengl/gl_texture_cache.h @@ -90,7 +90,7 @@ public: Tegra::Texture::SwizzleSource z_source, Tegra::Texture::SwizzleSource w_source); - void DecorateViewName(GPUVAddr gpu_addr, std::string prefix); + void DecorateViewName(GPUVAddr gpu_addr, const std::string& prefix); void MarkAsModified(u64 tick) { surface.MarkAsModified(true, tick); diff --git a/src/web_service/CMakeLists.txt b/src/web_service/CMakeLists.txt index 06ab7c59d..7e484b906 100644 --- a/src/web_service/CMakeLists.txt +++ b/src/web_service/CMakeLists.txt @@ -5,6 +5,7 @@ add_library(web_service STATIC verify_login.h web_backend.cpp web_backend.h + web_result.h ) create_target_directory_groups(web_service) diff --git a/src/web_service/telemetry_json.cpp b/src/web_service/telemetry_json.cpp index c89a3a0db..6215c914f 100644 --- a/src/web_service/telemetry_json.cpp +++ b/src/web_service/telemetry_json.cpp @@ -4,9 +4,9 @@ #include <nlohmann/json.hpp> #include "common/detached_tasks.h" -#include "common/web_result.h" #include "web_service/telemetry_json.h" #include "web_service/web_backend.h" +#include "web_service/web_result.h" namespace WebService { @@ -125,7 +125,7 @@ bool TelemetryJson::SubmitTestcase() { Client client(impl->host, impl->username, impl->token); auto value = client.PostJson("/gamedb/testcase", content, false); - return value.result_code == Common::WebResult::Code::Success; + return value.result_code == WebResult::Code::Success; } } // namespace WebService diff --git a/src/web_service/verify_login.cpp b/src/web_service/verify_login.cpp index bfaa5b70a..ceb55ca6b 100644 --- a/src/web_service/verify_login.cpp +++ b/src/web_service/verify_login.cpp @@ -3,9 +3,9 @@ // Refer to the license.txt file included. #include <nlohmann/json.hpp> -#include "common/web_result.h" #include "web_service/verify_login.h" #include "web_service/web_backend.h" +#include "web_service/web_result.h" namespace WebService { diff --git a/src/web_service/web_backend.cpp b/src/web_service/web_backend.cpp index 09d1651ac..74e287045 100644 --- a/src/web_service/web_backend.cpp +++ b/src/web_service/web_backend.cpp @@ -6,13 +6,14 @@ #include <cstdlib> #include <mutex> #include <string> + #include <LUrlParser.h> #include <fmt/format.h> #include <httplib.h> -#include "common/common_types.h" + #include "common/logging/log.h" -#include "common/web_result.h" #include "web_service/web_backend.h" +#include "web_service/web_result.h" namespace WebService { @@ -33,17 +34,16 @@ struct Client::Impl { } /// A generic function handles POST, GET and DELETE request together - Common::WebResult GenericRequest(const std::string& method, const std::string& path, - const std::string& data, bool allow_anonymous, - const std::string& accept) { + WebResult GenericRequest(const std::string& method, const std::string& path, + const std::string& data, bool allow_anonymous, + const std::string& accept) { if (jwt.empty()) { UpdateJWT(); } if (jwt.empty() && !allow_anonymous) { LOG_ERROR(WebService, "Credentials must be provided for authenticated requests"); - return Common::WebResult{Common::WebResult::Code::CredentialsMissing, - "Credentials needed", ""}; + return WebResult{WebResult::Code::CredentialsMissing, "Credentials needed", ""}; } auto result = GenericRequest(method, path, data, accept, jwt); @@ -62,10 +62,10 @@ struct Client::Impl { * username + token is used if jwt is empty but username and token are * not empty anonymous if all of jwt, username and token are empty */ - Common::WebResult GenericRequest(const std::string& method, const std::string& path, - const std::string& data, const std::string& accept, - const std::string& jwt = "", const std::string& username = "", - const std::string& token = "") { + WebResult GenericRequest(const std::string& method, const std::string& path, + const std::string& data, const std::string& accept, + const std::string& jwt = "", const std::string& username = "", + const std::string& token = "") { if (cli == nullptr) { auto parsedUrl = LUrlParser::clParseURL::ParseURL(host); int port; @@ -81,12 +81,12 @@ struct Client::Impl { cli = std::make_unique<httplib::SSLClient>(parsedUrl.m_Host.c_str(), port); } else { LOG_ERROR(WebService, "Bad URL scheme {}", parsedUrl.m_Scheme); - return Common::WebResult{Common::WebResult::Code::InvalidURL, "Bad URL scheme", ""}; + return WebResult{WebResult::Code::InvalidURL, "Bad URL scheme", ""}; } } if (cli == nullptr) { LOG_ERROR(WebService, "Invalid URL {}", host + path); - return Common::WebResult{Common::WebResult::Code::InvalidURL, "Invalid URL", ""}; + return WebResult{WebResult::Code::InvalidURL, "Invalid URL", ""}; } cli->set_timeout_sec(TIMEOUT_SECONDS); @@ -106,7 +106,7 @@ struct Client::Impl { std::string(API_VERSION.begin(), API_VERSION.end())); if (method != "GET") { params.emplace(std::string("Content-Type"), std::string("application/json")); - }; + } httplib::Request request; request.method = method; @@ -118,29 +118,28 @@ struct Client::Impl { if (!cli->send(request, response)) { LOG_ERROR(WebService, "{} to {} returned null", method, host + path); - return Common::WebResult{Common::WebResult::Code::LibError, "Null response", ""}; + return WebResult{WebResult::Code::LibError, "Null response", ""}; } if (response.status >= 400) { LOG_ERROR(WebService, "{} to {} returned error status code: {}", method, host + path, response.status); - return Common::WebResult{Common::WebResult::Code::HttpError, - std::to_string(response.status), ""}; + return WebResult{WebResult::Code::HttpError, std::to_string(response.status), ""}; } auto content_type = response.headers.find("content-type"); if (content_type == response.headers.end()) { LOG_ERROR(WebService, "{} to {} returned no content", method, host + path); - return Common::WebResult{Common::WebResult::Code::WrongContent, "", ""}; + return WebResult{WebResult::Code::WrongContent, "", ""}; } if (content_type->second.find(accept) == std::string::npos) { LOG_ERROR(WebService, "{} to {} returned wrong content: {}", method, host + path, content_type->second); - return Common::WebResult{Common::WebResult::Code::WrongContent, "Wrong content", ""}; + return WebResult{WebResult::Code::WrongContent, "Wrong content", ""}; } - return Common::WebResult{Common::WebResult::Code::Success, "", response.body}; + return WebResult{WebResult::Code::Success, "", response.body}; } // Retrieve a new JWT from given username and token @@ -150,7 +149,7 @@ struct Client::Impl { } auto result = GenericRequest("POST", "/jwt/internal", "", "text/html", "", username, token); - if (result.result_code != Common::WebResult::Code::Success) { + if (result.result_code != WebResult::Code::Success) { LOG_ERROR(WebService, "UpdateJWT failed"); } else { std::lock_guard lock{jwt_cache.mutex}; @@ -180,29 +179,28 @@ Client::Client(std::string host, std::string username, std::string token) Client::~Client() = default; -Common::WebResult Client::PostJson(const std::string& path, const std::string& data, - bool allow_anonymous) { +WebResult Client::PostJson(const std::string& path, const std::string& data, bool allow_anonymous) { return impl->GenericRequest("POST", path, data, allow_anonymous, "application/json"); } -Common::WebResult Client::GetJson(const std::string& path, bool allow_anonymous) { +WebResult Client::GetJson(const std::string& path, bool allow_anonymous) { return impl->GenericRequest("GET", path, "", allow_anonymous, "application/json"); } -Common::WebResult Client::DeleteJson(const std::string& path, const std::string& data, - bool allow_anonymous) { +WebResult Client::DeleteJson(const std::string& path, const std::string& data, + bool allow_anonymous) { return impl->GenericRequest("DELETE", path, data, allow_anonymous, "application/json"); } -Common::WebResult Client::GetPlain(const std::string& path, bool allow_anonymous) { +WebResult Client::GetPlain(const std::string& path, bool allow_anonymous) { return impl->GenericRequest("GET", path, "", allow_anonymous, "text/plain"); } -Common::WebResult Client::GetImage(const std::string& path, bool allow_anonymous) { +WebResult Client::GetImage(const std::string& path, bool allow_anonymous) { return impl->GenericRequest("GET", path, "", allow_anonymous, "image/png"); } -Common::WebResult Client::GetExternalJWT(const std::string& audience) { +WebResult Client::GetExternalJWT(const std::string& audience) { return impl->GenericRequest("POST", fmt::format("/jwt/external/{}", audience), "", false, "text/html"); } diff --git a/src/web_service/web_backend.h b/src/web_service/web_backend.h index 04121f17e..81f58583c 100644 --- a/src/web_service/web_backend.h +++ b/src/web_service/web_backend.h @@ -7,12 +7,10 @@ #include <memory> #include <string> -namespace Common { -struct WebResult; -} - namespace WebService { +struct WebResult; + class Client { public: Client(std::string host, std::string username, std::string token); @@ -25,8 +23,7 @@ public: * @param allow_anonymous If true, allow anonymous unauthenticated requests. * @return the result of the request. */ - Common::WebResult PostJson(const std::string& path, const std::string& data, - bool allow_anonymous); + WebResult PostJson(const std::string& path, const std::string& data, bool allow_anonymous); /** * Gets JSON from the specified path. @@ -34,7 +31,7 @@ public: * @param allow_anonymous If true, allow anonymous unauthenticated requests. * @return the result of the request. */ - Common::WebResult GetJson(const std::string& path, bool allow_anonymous); + WebResult GetJson(const std::string& path, bool allow_anonymous); /** * Deletes JSON to the specified path. @@ -43,8 +40,7 @@ public: * @param allow_anonymous If true, allow anonymous unauthenticated requests. * @return the result of the request. */ - Common::WebResult DeleteJson(const std::string& path, const std::string& data, - bool allow_anonymous); + WebResult DeleteJson(const std::string& path, const std::string& data, bool allow_anonymous); /** * Gets a plain string from the specified path. @@ -52,7 +48,7 @@ public: * @param allow_anonymous If true, allow anonymous unauthenticated requests. * @return the result of the request. */ - Common::WebResult GetPlain(const std::string& path, bool allow_anonymous); + WebResult GetPlain(const std::string& path, bool allow_anonymous); /** * Gets an PNG image from the specified path. @@ -60,14 +56,14 @@ public: * @param allow_anonymous If true, allow anonymous unauthenticated requests. * @return the result of the request. */ - Common::WebResult GetImage(const std::string& path, bool allow_anonymous); + WebResult GetImage(const std::string& path, bool allow_anonymous); /** * Requests an external JWT for the specific audience provided. * @param audience the audience of the JWT requested. * @return the result of the request. */ - Common::WebResult GetExternalJWT(const std::string& audience); + WebResult GetExternalJWT(const std::string& audience); private: struct Impl; diff --git a/src/common/web_result.h b/src/web_service/web_result.h index 8bfa2141d..3aeeb5288 100644 --- a/src/common/web_result.h +++ b/src/web_service/web_result.h @@ -7,7 +7,7 @@ #include <string> #include "common/common_types.h" -namespace Common { +namespace WebService { struct WebResult { enum class Code : u32 { Success, @@ -22,4 +22,4 @@ struct WebResult { std::string result_string; std::string returned_data; }; -} // namespace Common +} // namespace WebService diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index a372190cc..7af974d8d 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -635,6 +635,11 @@ void Config::ReadCpuValues() { ReadSetting(QStringLiteral("cpuopt_misc_ir"), true).toBool(); Settings::values.cpuopt_reduce_misalign_checks = ReadSetting(QStringLiteral("cpuopt_reduce_misalign_checks"), true).toBool(); + + Settings::values.cpuopt_unsafe_unfuse_fma = + ReadSetting(QStringLiteral("cpuopt_unsafe_unfuse_fma"), true).toBool(); + Settings::values.cpuopt_unsafe_reduce_fp_error = + ReadSetting(QStringLiteral("cpuopt_unsafe_reduce_fp_error"), true).toBool(); } qt_config->endGroup(); @@ -1132,6 +1137,11 @@ void Config::SaveCpuValues() { WriteSetting(QStringLiteral("cpuopt_misc_ir"), Settings::values.cpuopt_misc_ir, true); WriteSetting(QStringLiteral("cpuopt_reduce_misalign_checks"), Settings::values.cpuopt_reduce_misalign_checks, true); + + WriteSetting(QStringLiteral("cpuopt_unsafe_unfuse_fma"), + Settings::values.cpuopt_unsafe_unfuse_fma, true); + WriteSetting(QStringLiteral("cpuopt_unsafe_reduce_fp_error"), + Settings::values.cpuopt_unsafe_reduce_fp_error, true); } qt_config->endGroup(); diff --git a/src/yuzu/configuration/configure_cpu.cpp b/src/yuzu/configuration/configure_cpu.cpp index 7493e5ffb..37fcd6adc 100644 --- a/src/yuzu/configuration/configure_cpu.cpp +++ b/src/yuzu/configuration/configure_cpu.cpp @@ -19,6 +19,8 @@ ConfigureCpu::ConfigureCpu(QWidget* parent) : QWidget(parent), ui(new Ui::Config connect(ui->accuracy, qOverload<int>(&QComboBox::activated), this, &ConfigureCpu::AccuracyUpdated); + connect(ui->accuracy, qOverload<int>(&QComboBox::currentIndexChanged), this, + &ConfigureCpu::UpdateGroup); } ConfigureCpu::~ConfigureCpu() = default; @@ -28,6 +30,12 @@ void ConfigureCpu::SetConfiguration() { ui->accuracy->setEnabled(runtime_lock); ui->accuracy->setCurrentIndex(static_cast<int>(Settings::values.cpu_accuracy)); + UpdateGroup(static_cast<int>(Settings::values.cpu_accuracy)); + + ui->cpuopt_unsafe_unfuse_fma->setEnabled(runtime_lock); + ui->cpuopt_unsafe_unfuse_fma->setChecked(Settings::values.cpuopt_unsafe_unfuse_fma); + ui->cpuopt_unsafe_reduce_fp_error->setEnabled(runtime_lock); + ui->cpuopt_unsafe_reduce_fp_error->setChecked(Settings::values.cpuopt_unsafe_reduce_fp_error); } void ConfigureCpu::AccuracyUpdated(int index) { @@ -38,14 +46,21 @@ void ConfigureCpu::AccuracyUpdated(int index) { QMessageBox::Yes | QMessageBox::No); if (result == QMessageBox::No) { ui->accuracy->setCurrentIndex(static_cast<int>(Settings::CPUAccuracy::Accurate)); - return; + UpdateGroup(static_cast<int>(Settings::CPUAccuracy::Accurate)); } } } +void ConfigureCpu::UpdateGroup(int index) { + ui->unsafe_group->setVisible(static_cast<Settings::CPUAccuracy>(index) == + Settings::CPUAccuracy::Unsafe); +} + void ConfigureCpu::ApplyConfiguration() { Settings::values.cpu_accuracy = static_cast<Settings::CPUAccuracy>(ui->accuracy->currentIndex()); + Settings::values.cpuopt_unsafe_unfuse_fma = ui->cpuopt_unsafe_unfuse_fma->isChecked(); + Settings::values.cpuopt_unsafe_reduce_fp_error = ui->cpuopt_unsafe_reduce_fp_error->isChecked(); } void ConfigureCpu::changeEvent(QEvent* event) { diff --git a/src/yuzu/configuration/configure_cpu.h b/src/yuzu/configuration/configure_cpu.h index e4741d3a4..3c5683d81 100644 --- a/src/yuzu/configuration/configure_cpu.h +++ b/src/yuzu/configuration/configure_cpu.h @@ -26,6 +26,7 @@ private: void RetranslateUI(); void AccuracyUpdated(int index); + void UpdateGroup(int index); void SetConfiguration(); diff --git a/src/yuzu/configuration/configure_cpu.ui b/src/yuzu/configuration/configure_cpu.ui index bf6ea79bb..ebdd2e6e9 100644 --- a/src/yuzu/configuration/configure_cpu.ui +++ b/src/yuzu/configuration/configure_cpu.ui @@ -40,6 +40,11 @@ </item> <item> <property name="text"> + <string>Unsafe</string> + </property> + </item> + <item> + <property name="text"> <string>Enable Debug Mode</string> </property> </item> @@ -63,6 +68,53 @@ </layout> </item> <item> + <layout class="QVBoxLayout"> + <item> + <widget class="QGroupBox" name="unsafe_group"> + <property name="title"> + <string>Unsafe CPU Optimization Settings</string> + </property> + <layout class="QVBoxLayout"> + <item> + <widget class="QLabel"> + <property name="wordWrap"> + <bool>1</bool> + </property> + <property name="text"> + <string>These settings reduce accuracy for speed.</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="cpuopt_unsafe_unfuse_fma"> + <property name="text"> + <string>Unfuse FMA (improve performance on CPUs without FMA)</string> + </property> + <property name="toolTip"> + <string> + <div>This option improves speed by reducing accuracy of fused-multiply-add instructions on CPUs without native FMA support.</div> + </string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="cpuopt_unsafe_reduce_fp_error"> + <property name="text"> + <string>Faster FRSQRTE and FRECPE</string> + </property> + <property name="toolTip"> + <string> + <div>This option improves the speed of some approximate floating-point functions by using less accurate native approximations.</div> + </string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </item> + <item> <spacer name="verticalSpacer"> <property name="orientation"> <enum>Qt::Vertical</enum> |