aboutsummaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
authorJonas <104419015+jojo-chaechae@users.noreply.github.com>2026-11-03 04:36:01 +0600
committerGitHub <noreply@github.com>2026-11-03 04:36:01 +0600
commit8362c44ff835d7be73d2adad9ce3e2d4467a6aeb (patch)
treec1eb12a54510bbeaed67a8a524b5ce9e8576cfcd /src/core
parent155bf34fc297b6b8e4c281ecf7ea5d9ce3140762 (diff)
parentfe3c344573140b7864c9a86f204eb9c0dce0d548 (diff)
Merge pull request #334 from snowstormxd/codex/upstream-mkw-test
MKW performance improvements
Diffstat (limited to 'src/core')
-rw-r--r--src/core/CMakeLists.txt5
-rw-r--r--src/core/hle/kernel/k_priority_queue.h20
-rw-r--r--src/core/hle/kernel/kernel.cpp4
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp22
-rw-r--r--src/core/memory.cpp15
5 files changed, 56 insertions, 10 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index eeb3202d12..1ff9f271d2 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -1290,4 +1290,9 @@ else()
target_sources(core PRIVATE hle/service/ssl/ssl_backend_none.cpp)
endif()
+if (ENABLE_LTO AND CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang" AND NOT MSVC)
+ # Fibers can migrate between host threads; keep kernel TLS lookups outside LTO callers.
+ set_source_files_properties(hle/kernel/kernel.cpp PROPERTIES COMPILE_OPTIONS -fno-lto)
+endif()
+
create_target_directory_groups(core)
diff --git a/src/core/hle/kernel/k_priority_queue.h b/src/core/hle/kernel/k_priority_queue.h
index 89a0c5bbea..5d93455d33 100644
--- a/src/core/hle/kernel/k_priority_queue.h
+++ b/src/core/hle/kernel/k_priority_queue.h
@@ -11,6 +11,7 @@
#include <bitset>
#include <concepts>
#include <cstddef>
+#include <type_traits>
#include "common/alignment.h"
#include "common/assert.h"
@@ -196,12 +197,7 @@ public:
constexpr Member* GetFront(s32 core) const {
ASSERT(IsValidCore(core));
- const s32 priority = s32([](auto const& e) {
- for (size_t i = 0; i < e.size(); ++i)
- if (e[i])
- return i;
- return e.size();
- }(m_available_priorities[core]));
+ const s32 priority = s32(GetNextSet(m_available_priorities[core], size_t(-1)));
if (priority <= LowestPriority) {
return m_queues[priority].GetFront(core);
} else {
@@ -222,7 +218,17 @@ public:
template<size_t N>
constexpr size_t GetNextSet(std::bitset<N> const& bit, size_t n) const {
- for (size_t i = n + 1; i < bit.size(); i++)
+ const size_t first = n + 1;
+ if (first >= bit.size()) {
+ return bit.size();
+ }
+ if constexpr (N <= 64) {
+ if (!std::is_constant_evaluated()) {
+ const u64 remaining = bit.to_ullong() >> first;
+ return remaining != 0 ? first + size_t(std::countr_zero(remaining)) : bit.size();
+ }
+ }
+ for (size_t i = first; i < bit.size(); i++)
if (bit[i])
return i;
return bit.size();
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index f006d129a7..9e6ccc2d89 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -79,7 +79,7 @@ struct KernelCore::Impl {
// And we have the guarantee that the data won't move out of the way so we can safely
// take a reference to it. This isn't always universally true but this is "global" data
// so it will be statically given a TLS slot anyways.
- static inline thread_local ThreadLocalData tls_data = {};
+ static thread_local ThreadLocalData tls_data;
explicit Impl(Core::System& system_, KernelCore& kernel_) : system{system_} {
tls_data.lock = true;
@@ -861,6 +861,8 @@ struct KernelCore::Impl {
Core::System& system;
};
+thread_local ThreadLocalData KernelCore::Impl::tls_data{};
+
KernelCore::KernelCore(Core::System& system) : impl{std::make_unique<Impl>(system, *this)} {}
KernelCore::~KernelCore() = default;
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index 4b1168a59b..49352a5b49 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -81,6 +81,24 @@ struct PatchCollection {
namespace {
+FileSys::VirtualDir FindLooseRomFSDirectory(const FileSys::VirtualDir& parent) {
+ if (parent == nullptr) {
+ return nullptr;
+ }
+ if (const auto dir = parent->GetSubdirectory("romfs")) {
+ return dir;
+ }
+ for (const auto& dir : parent->GetSubdirectories()) {
+ const auto name = dir->GetName();
+ if (name.size() == 5 && std::equal(name.begin(), name.end(), "romfs", [](char a, char b) {
+ return std::tolower(static_cast<unsigned char>(a)) == b;
+ })) {
+ return dir;
+ }
+ }
+ return nullptr;
+}
+
// RomFS for a loose ExeFS: the first .romfs file next to main, else a romfs/ directory next to
// the exefs/ directory (the usual layout of an extracted title).
FileSys::VirtualFile FindLooseRomFS(const FileSys::VirtualDir& exefs_dir) {
@@ -93,7 +111,7 @@ FileSys::VirtualFile FindLooseRomFS(const FileSys::VirtualDir& exefs_dir) {
}
}
if (const auto parent = exefs_dir->GetParentDirectory()) {
- if (const auto romfs_dir = parent->GetSubdirectory("romfs")) {
+ if (const auto romfs_dir = FindLooseRomFSDirectory(parent)) {
// Building it walks every file and keeps an object for each (title 1: 206k files, ~150
// MiB), so loaders of the same directory share one build while any of them holds it.
static std::mutex cache_mutex;
@@ -268,7 +286,7 @@ AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys
romfs_exefs_dir = file_dir;
if (const auto title_dir = file_dir->GetParentDirectory()) {
- romfs_version = RomFSProductVersion(title_dir->GetSubdirectory("romfs"));
+ romfs_version = RomFSProductVersion(FindLooseRomFSDirectory(title_dir));
}
}
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index cfc39916db..1f7315f3c1 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -549,6 +549,8 @@ struct Memory::Impl {
}
void RasterizerArmReadTraps(u64 vaddr, u64 size) {
+ // Clean cached read areas also need invalidation when reactive flushing is enabled.
+ read_trap_generation.fetch_add(1, std::memory_order_release);
if (vaddr == 0 || size == 0 || !AddressSpaceContains(*current_page_table, vaddr, size) ||
!ReadTrapsEnabled()) {
return;
@@ -781,6 +783,19 @@ struct Memory::Impl {
gpu_device_memory = &system.Host1x().MemoryManager();
}
const size_t core = system.GetCurrentHostThreadID();
+ if (core >= Core::Hardware::NUM_CPU_CORES) {
+ // A service thread has no slot in the arrays below, and it does not use a slot of a
+ // core. It keeps no area, so it asks on each read.
+ if (static std::atomic_flag logged; !logged.test_and_set(std::memory_order_relaxed)) {
+ LOG_INFO(HW_Memory, "Read of cached guest memory from a thread that is not a core");
+ }
+ Common::ScratchBuffer<u32> scratch;
+ gpu_device_memory->ApplyOpOnPointer(p, scratch, [&](DAddr address) {
+ DisarmReadTraps(v_address, size);
+ (void)system.GPU().OnCPURead(address, size);
+ });
+ return;
+ }
auto& current_area = rasterizer_read_areas[core];
if (const u64 generation = read_trap_generation.load(std::memory_order_acquire);
read_area_generations[core] != generation) {