From 6df7bea0143b796cb45674def49e971905a34182 Mon Sep 17 00:00:00 2001 From: ParantezTech Date: Fri, 20 Feb 2026 17:58:25 +0300 Subject: [PATCH 01/11] rework flexible memory usage tracking --- src/core/memory.cpp | 121 +++++++++++++++++++++++++++++++++++++++++++- src/core/memory.h | 39 +++++++++++++- 2 files changed, 157 insertions(+), 3 deletions(-) diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 9d26142ce..297317d77 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2025-2026 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include + #include "common/alignment.h" #include "common/assert.h" #include "common/config.h" @@ -68,8 +70,25 @@ void MemoryManager::SetupMemoryRegions(u64 flexible_size, bool use_extended_mem1 fmem_map.emplace(total_direct_size, PhysicalMemoryArea{total_direct_size, remaining_physical_space}); + flexible_virtual_base = impl.SystemReservedVirtualBase(); + const u64 flexible_virtual_size = + std::min(total_flexible_size, impl.SystemReservedVirtualSize()); + flexible_virtual_end = flexible_virtual_base + flexible_virtual_size; + + { + std::scoped_lock lk{mutex}; + RecalculateFlexibleMappedUsageLocked(); + } + LOG_INFO(Kernel_Vmm, "Configured memory regions: flexible size = {:#x}, direct size = {:#x}", total_flexible_size, total_direct_size); + if (Config::debugDump()) { + LOG_DEBUG( + Kernel_Vmm, + "Flexible accounting region: [{:#x}, {:#x}), total = {:#x}, used = {:#x}, free = {:#x}", + flexible_virtual_base, flexible_virtual_end, total_flexible_size, flexible_mapped_usage, + GetAvailableFlexibleSize()); + } } u64 MemoryManager::ClampRangeSize(VAddr virtual_addr, u64 size) { @@ -557,6 +576,7 @@ s32 MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, u64 size, Memo // Acquire writer lock. std::unique_lock lk2{mutex}; + const u64 flexible_before = GetFlexibleMappedBytesInRangeLocked(virtual_addr, size); // Create VMA representing this mapping. auto new_vma_handle = CreateArea(virtual_addr, size, prot, flags, type, name, alignment); @@ -643,6 +663,9 @@ s32 MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, u64 size, Memo MergeAdjacent(vma_map, new_vma_handle); } + const u64 flexible_after = GetFlexibleMappedBytesInRangeLocked(mapped_addr, size); + AdjustFlexibleMappedUsageLocked(flexible_before, flexible_after); + *out_addr = std::bit_cast(mapped_addr); if (type != VMAType::Reserved && type != VMAType::PoolReserved) { // Flexible address space mappings were performed while finding direct memory areas. @@ -731,6 +754,7 @@ s32 MemoryManager::MapFile(void** out_addr, VAddr virtual_addr, u64 size, Memory // Aquire writer lock std::scoped_lock lk2{mutex}; + const u64 flexible_before = GetFlexibleMappedBytesInRangeLocked(virtual_addr, size); // Update VMA map and map to address space. auto new_vma_handle = CreateArea(virtual_addr, size, prot, flags, VMAType::File, "anon", 0); @@ -742,6 +766,9 @@ s32 MemoryManager::MapFile(void** out_addr, VAddr virtual_addr, u64 size, Memory impl.MapFile(mapped_addr, size, phys_addr, std::bit_cast(prot), handle); + const u64 flexible_after = GetFlexibleMappedBytesInRangeLocked(mapped_addr, size); + AdjustFlexibleMappedUsageLocked(flexible_before, flexible_after); + *out_addr = std::bit_cast(mapped_addr); return ORBIS_OK; } @@ -848,7 +875,13 @@ s32 MemoryManager::UnmapMemory(VAddr virtual_addr, u64 size) { // Acquire writer lock. std::scoped_lock lk2{mutex}; - return UnmapMemoryImpl(virtual_addr, size); + const u64 flexible_before = GetFlexibleMappedBytesInRangeLocked(virtual_addr, size); + const s32 result = UnmapMemoryImpl(virtual_addr, size); + if (result == ORBIS_OK) { + const u64 flexible_after = GetFlexibleMappedBytesInRangeLocked(virtual_addr, size); + AdjustFlexibleMappedUsageLocked(flexible_before, flexible_after); + } + return result; } u64 MemoryManager::UnmapBytesFromEntry(VAddr virtual_addr, VirtualMemoryArea vma_base, u64 size) { @@ -1344,6 +1377,92 @@ void MemoryManager::InvalidateMemory(const VAddr addr, const u64 size) const { } } +void MemoryManager::RecalculateFlexibleUsageForDebug() { + std::scoped_lock lk{mutex}; + RecalculateFlexibleMappedUsageLocked(); +} + +bool MemoryManager::IsFlexibleCountedVmaType(VMAType type) const { + return type == VMAType::Flexible || type == VMAType::Code; +} + +bool MemoryManager::IsFlexibleCommittedVma(const VirtualMemoryArea& vma) const { + if (!vma.IsMapped()) { + return false; + } + + const bool has_physical_tracking = + vma.type == VMAType::Direct || vma.type == VMAType::Flexible || vma.type == VMAType::Pooled; + if (has_physical_tracking) { + // Direct/flexible/pooled mappings should expose at least one physical sub-area when + // committed. + return !vma.phys_areas.empty(); + } + + // Non-phys-tracked mappings (code/stack/file) are committed through address-space map calls. + return vma.type == VMAType::Code || vma.type == VMAType::Stack || vma.type == VMAType::File; +} + +u64 MemoryManager::GetFlexibleMappedBytesInRangeLocked(VAddr virtual_addr, u64 size) const { + if (!IsFlexibleRegionConfigured() || size == 0) { + return 0; + } + + const VAddr aligned_start = Common::AlignDown(virtual_addr, 16_KB); + const u64 page_offset = virtual_addr - aligned_start; + if (size > std::numeric_limits::max() - page_offset) { + return 0; + } + const u64 aligned_size = Common::AlignUp(size + page_offset, 16_KB); + if (aligned_size == 0) { + return 0; + } + + const VAddr aligned_end = aligned_start + aligned_size; + const VAddr range_start = std::max(aligned_start, flexible_virtual_base); + const VAddr range_end = std::min(aligned_end, flexible_virtual_end); + if (range_start >= range_end) { + return 0; + } + + u64 mapped_bytes = 0; + auto it = std::prev(vma_map.upper_bound(range_start)); + while (it != vma_map.end() && it->second.base < range_end) { + const auto& vma = it->second; + const VAddr vma_end = vma.base + vma.size; + const VAddr overlap_start = std::max(range_start, vma.base); + const VAddr overlap_end = std::min(range_end, vma_end); + const bool counted_type = IsFlexibleCountedVmaType(vma.type); + const bool committed = IsFlexibleCommittedVma(vma); + + if (overlap_start < overlap_end && counted_type && committed) { + mapped_bytes += overlap_end - overlap_start; + } + + ++it; + } + return mapped_bytes; +} + +void MemoryManager::AdjustFlexibleMappedUsageLocked(u64 mapped_before, u64 mapped_after) { + if (mapped_after >= mapped_before) { + flexible_mapped_usage += mapped_after - mapped_before; + } else { + const u64 delta = mapped_before - mapped_after; + flexible_mapped_usage = delta > flexible_mapped_usage ? 0 : flexible_mapped_usage - delta; + } +} + +void MemoryManager::RecalculateFlexibleMappedUsageLocked() { + if (!IsFlexibleRegionConfigured()) { + flexible_mapped_usage = 0; + return; + } + + flexible_mapped_usage = GetFlexibleMappedBytesInRangeLocked( + flexible_virtual_base, flexible_virtual_end - flexible_virtual_base); +} + VAddr MemoryManager::SearchFree(VAddr virtual_addr, u64 size, u32 alignment) { // Calculate the minimum and maximum addresses present in our address space. auto min_search_address = impl.SystemManagedVirtualBase(); diff --git a/src/core/memory.h b/src/core/memory.h index f9ae64942..a41f357f3 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -157,9 +157,11 @@ struct VirtualMemoryArea { class MemoryManager { using PhysMap = std::map; using PhysHandle = PhysMap::iterator; + using PhysConstHandle = PhysMap::const_iterator; using VMAMap = std::map; using VMAHandle = VMAMap::iterator; + using VMAConstHandle = VMAMap::const_iterator; public: explicit MemoryManager(); @@ -181,8 +183,17 @@ public: return total_flexible_size; } + u64 GetUsedFlexibleSize() const { + return flexible_mapped_usage; + } + u64 GetAvailableFlexibleSize() const { - return total_flexible_size - flexible_usage; + const u64 used = GetUsedFlexibleSize(); + return used < total_flexible_size ? total_flexible_size - used : 0; + } + + bool IsFlexibleRegionConfigured() const { + return flexible_virtual_end > flexible_virtual_base; } VAddr SystemReservedVirtualBase() noexcept { @@ -288,20 +299,31 @@ public: void InvalidateMemory(VAddr addr, u64 size) const; + void RecalculateFlexibleUsageForDebug(); + private: VMAHandle FindVMA(VAddr target) { return std::prev(vma_map.upper_bound(target)); } + VMAConstHandle FindVMA(VAddr target) const { + return std::prev(vma_map.upper_bound(target)); + } PhysHandle FindDmemArea(PAddr target) { return std::prev(dmem_map.upper_bound(target)); } + PhysConstHandle FindDmemArea(PAddr target) const { + return std::prev(dmem_map.upper_bound(target)); + } PhysHandle FindFmemArea(PAddr target) { return std::prev(fmem_map.upper_bound(target)); } + PhysConstHandle FindFmemArea(PAddr target) const { + return std::prev(fmem_map.upper_bound(target)); + } - bool HasPhysicalBacking(VirtualMemoryArea vma) { + bool HasPhysicalBacking(const VirtualMemoryArea& vma) const { return vma.type == VMAType::Direct || vma.type == VMAType::Flexible || vma.type == VMAType::Pooled; } @@ -327,6 +349,16 @@ private: s32 UnmapMemoryImpl(VAddr virtual_addr, u64 size); + bool IsFlexibleCountedVmaType(VMAType type) const; + + bool IsFlexibleCommittedVma(const VirtualMemoryArea& vma) const; + + u64 GetFlexibleMappedBytesInRangeLocked(VAddr virtual_addr, u64 size) const; + + void AdjustFlexibleMappedUsageLocked(u64 mapped_before, u64 mapped_after); + + void RecalculateFlexibleMappedUsageLocked(); + private: AddressSpace impl; PhysMap dmem_map; @@ -337,6 +369,9 @@ private: u64 total_direct_size{}; u64 total_flexible_size{}; u64 flexible_usage{}; + VAddr flexible_virtual_base{}; + VAddr flexible_virtual_end{}; + u64 flexible_mapped_usage{}; u64 pool_budget{}; s32 sdk_version{}; Vulkan::Rasterizer* rasterizer{}; From e14ae0c3d9cc471000533624b47d43a500cc0979 Mon Sep 17 00:00:00 2001 From: ParantezTech Date: Fri, 20 Feb 2026 17:58:54 +0300 Subject: [PATCH 02/11] exclude trampoline from code mapping --- src/core/module.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/module.cpp b/src/core/module.cpp index d0fae3a9f..2522a82a0 100644 --- a/src/core/module.cpp +++ b/src/core/module.cpp @@ -113,7 +113,7 @@ void Module::LoadModuleToMemory(u32& max_tls_index) { // Map module segments (and possible TLS trampolines) void** out_addr = reinterpret_cast(&base_virtual_addr); - memory->MapMemory(out_addr, ModuleLoadBase, aligned_base_size + TrampolineSize, + memory->MapMemory(out_addr, ModuleLoadBase, aligned_base_size, MemoryProt::CpuReadWrite | MemoryProt::CpuExec, MemoryMapFlags::NoFlags, VMAType::Code, name); LOG_INFO(Core_Linker, "Loading module {} to {}", name, fmt::ptr(*out_addr)); From 2ca77d0895ab737287ac8583babdd91c3a31eb10 Mon Sep 17 00:00:00 2001 From: ParantezTech Date: Fri, 20 Feb 2026 18:10:39 +0300 Subject: [PATCH 03/11] need sleep --- src/core/memory.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 297317d77..e4429dffe 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -1394,8 +1394,7 @@ bool MemoryManager::IsFlexibleCommittedVma(const VirtualMemoryArea& vma) const { const bool has_physical_tracking = vma.type == VMAType::Direct || vma.type == VMAType::Flexible || vma.type == VMAType::Pooled; if (has_physical_tracking) { - // Direct/flexible/pooled mappings should expose at least one physical sub-area when - // committed. + // Direct/flexible/pooled mappings should expose at least one physical sub-area when committed. return !vma.phys_areas.empty(); } From 52e462b4ef343f69722fb0754ff9808e29f62569 Mon Sep 17 00:00:00 2001 From: ParantezTech Date: Fri, 20 Feb 2026 18:14:44 +0300 Subject: [PATCH 04/11] clang-format --- src/core/memory.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/memory.cpp b/src/core/memory.cpp index e4429dffe..297317d77 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -1394,7 +1394,8 @@ bool MemoryManager::IsFlexibleCommittedVma(const VirtualMemoryArea& vma) const { const bool has_physical_tracking = vma.type == VMAType::Direct || vma.type == VMAType::Flexible || vma.type == VMAType::Pooled; if (has_physical_tracking) { - // Direct/flexible/pooled mappings should expose at least one physical sub-area when committed. + // Direct/flexible/pooled mappings should expose at least one physical sub-area when + // committed. return !vma.phys_areas.empty(); } From 89171d89bf6694f8dbb4dd9bce5fec7479187c92 Mon Sep 17 00:00:00 2001 From: ParantezTech Date: Fri, 20 Feb 2026 18:22:15 +0300 Subject: [PATCH 05/11] map and register host trampoline for x86_64 patching --- src/core/module.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/core/module.cpp b/src/core/module.cpp index 2522a82a0..eb209da92 100644 --- a/src/core/module.cpp +++ b/src/core/module.cpp @@ -119,6 +119,17 @@ void Module::LoadModuleToMemory(u32& max_tls_index) { LOG_INFO(Core_Linker, "Loading module {} to {}", name, fmt::ptr(*out_addr)); #ifdef ARCH_X86_64 + void* trampoline_region = std::bit_cast(base_virtual_addr + aligned_base_size); + const int tramp_ret = memory->MapMemory( + &trampoline_region, + base_virtual_addr + aligned_base_size, + TrampolineSize, + MemoryProt::CpuReadWrite | MemoryProt::CpuExec, + MemoryMapFlags::Fixed | MemoryMapFlags::NoOverwrite, + VMAType::File, + "Trampoline"); + ASSERT_MSG(tramp_ret == 0, "Unable to map trampoline memory"); + // Initialize trampoline generator. void* trampoline_addr = std::bit_cast(base_virtual_addr + aligned_base_size); RegisterPatchModule(*out_addr, aligned_base_size, trampoline_addr, TrampolineSize); From c5e401d2155873ffd614244a32b56ed1c778ddc7 Mon Sep 17 00:00:00 2001 From: ParantezTech Date: Fri, 20 Feb 2026 18:32:28 +0300 Subject: [PATCH 06/11] clang-format (I always forget this) --- src/core/module.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/core/module.cpp b/src/core/module.cpp index eb209da92..b3c963679 100644 --- a/src/core/module.cpp +++ b/src/core/module.cpp @@ -121,13 +121,9 @@ void Module::LoadModuleToMemory(u32& max_tls_index) { #ifdef ARCH_X86_64 void* trampoline_region = std::bit_cast(base_virtual_addr + aligned_base_size); const int tramp_ret = memory->MapMemory( - &trampoline_region, - base_virtual_addr + aligned_base_size, - TrampolineSize, + &trampoline_region, base_virtual_addr + aligned_base_size, TrampolineSize, MemoryProt::CpuReadWrite | MemoryProt::CpuExec, - MemoryMapFlags::Fixed | MemoryMapFlags::NoOverwrite, - VMAType::File, - "Trampoline"); + MemoryMapFlags::Fixed | MemoryMapFlags::NoOverwrite, VMAType::File, "Trampoline"); ASSERT_MSG(tramp_ret == 0, "Unable to map trampoline memory"); // Initialize trampoline generator. From 7e2af62e566abea33ab0afe7984e8e5b03b0138c Mon Sep 17 00:00:00 2001 From: ParantezTech Date: Fri, 20 Feb 2026 18:33:00 +0300 Subject: [PATCH 07/11] avoid std::prev(begin()) --- src/core/memory.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 297317d77..f80ba841f 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -1426,7 +1426,10 @@ u64 MemoryManager::GetFlexibleMappedBytesInRangeLocked(VAddr virtual_addr, u64 s } u64 mapped_bytes = 0; - auto it = std::prev(vma_map.upper_bound(range_start)); + auto it = vma_map.upper_bound(range_start); + if (it != vma_map.begin()) { + it = std::prev(it); + } while (it != vma_map.end() && it->second.base < range_end) { const auto& vma = it->second; const VAddr vma_end = vma.base + vma.size; From da725d9312cec3e35e9c48de4e9022a571b1e728 Mon Sep 17 00:00:00 2001 From: ParantezTech Date: Sat, 21 Feb 2026 03:23:15 +0300 Subject: [PATCH 08/11] System modules should not consume the game's flexible memory --- src/core/memory.cpp | 22 +++++++++++++++++----- src/core/memory.h | 9 +++++++-- src/core/module.cpp | 3 ++- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/core/memory.cpp b/src/core/memory.cpp index f80ba841f..c5f2bae6a 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -413,6 +413,7 @@ s32 MemoryManager::PoolCommit(VAddr virtual_addr, u64 size, MemoryProt prot, s32 new_vma.prot = prot; new_vma.name = "anon"; new_vma.type = Core::VMAType::Pooled; + new_vma.is_system_module = false; new_vma.phys_areas.clear(); // Find suitable physical addresses @@ -464,7 +465,8 @@ s32 MemoryManager::PoolCommit(VAddr virtual_addr, u64 size, MemoryProt prot, s32 MemoryManager::VMAHandle MemoryManager::CreateArea(VAddr virtual_addr, u64 size, MemoryProt prot, MemoryMapFlags flags, VMAType type, - std::string_view name, u64 alignment) { + std::string_view name, u64 alignment, + bool is_system_module) { // Locate the VMA representing the requested region auto vma = FindVMA(virtual_addr)->second; if (True(flags & MemoryMapFlags::Fixed)) { @@ -499,13 +501,15 @@ MemoryManager::VMAHandle MemoryManager::CreateArea(VAddr virtual_addr, u64 size, new_vma.prot = prot; new_vma.name = name; new_vma.type = type; + new_vma.is_system_module = is_system_module; new_vma.phys_areas.clear(); return new_vma_handle; } s32 MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, u64 size, MemoryProt prot, MemoryMapFlags flags, VMAType type, std::string_view name, - bool validate_dmem, PAddr phys_addr, u64 alignment) { + bool validate_dmem, PAddr phys_addr, u64 alignment, + bool is_system_module) { // Certain games perform flexible mappings on loop to determine // the available flexible memory size. Questionable but we need to handle this. if (type == VMAType::Flexible && flexible_usage + size > total_flexible_size) { @@ -579,7 +583,8 @@ s32 MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, u64 size, Memo const u64 flexible_before = GetFlexibleMappedBytesInRangeLocked(virtual_addr, size); // Create VMA representing this mapping. - auto new_vma_handle = CreateArea(virtual_addr, size, prot, flags, type, name, alignment); + auto new_vma_handle = + CreateArea(virtual_addr, size, prot, flags, type, name, alignment, is_system_module); auto& new_vma = new_vma_handle->second; auto mapped_addr = new_vma.base; bool is_exec = True(prot & MemoryProt::CpuExec); @@ -757,7 +762,8 @@ s32 MemoryManager::MapFile(void** out_addr, VAddr virtual_addr, u64 size, Memory const u64 flexible_before = GetFlexibleMappedBytesInRangeLocked(virtual_addr, size); // Update VMA map and map to address space. - auto new_vma_handle = CreateArea(virtual_addr, size, prot, flags, VMAType::File, "anon", 0); + auto new_vma_handle = + CreateArea(virtual_addr, size, prot, flags, VMAType::File, "anon", 0, false); auto& new_vma = new_vma_handle->second; new_vma.fd = fd; @@ -841,6 +847,7 @@ s32 MemoryManager::PoolDecommit(VAddr virtual_addr, u64 size) { vma.prot = MemoryProt::NoAccess; vma.disallow_merge = false; vma.name = "anon"; + vma.is_system_module = false; vma.phys_areas.clear(); MergeAdjacent(vma_map, new_it); @@ -942,6 +949,7 @@ u64 MemoryManager::UnmapBytesFromEntry(VAddr virtual_addr, VirtualMemoryArea vma vma.phys_areas.clear(); vma.disallow_merge = false; vma.name = ""; + vma.is_system_module = false; MergeAdjacent(vma_map, new_it); if (vma_type != VMAType::Reserved && vma_type != VMAType::PoolReserved) { @@ -1400,7 +1408,11 @@ bool MemoryManager::IsFlexibleCommittedVma(const VirtualMemoryArea& vma) const { } // Non-phys-tracked mappings (code/stack/file) are committed through address-space map calls. - return vma.type == VMAType::Code || vma.type == VMAType::Stack || vma.type == VMAType::File; + if (vma.type == VMAType::Code) { + // System modules should not consume the game's flexible memory. + return !vma.is_system_module; + } + return vma.type == VMAType::Stack || vma.type == VMAType::File; } u64 MemoryManager::GetFlexibleMappedBytesInRangeLocked(VAddr virtual_addr, u64 size) const { diff --git a/src/core/memory.h b/src/core/memory.h index a41f357f3..3c465d22a 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -111,6 +111,7 @@ struct VirtualMemoryArea { std::string name = ""; s32 fd = 0; bool disallow_merge = false; + bool is_system_module = false; bool Contains(VAddr addr, u64 size) const { return addr >= base && (addr + size) <= (base + this->size); @@ -149,6 +150,9 @@ struct VirtualMemoryArea { if (name.compare(next.name) != 0) { return false; } + if (is_system_module != next.is_system_module) { + return false; + } return true; } @@ -263,7 +267,8 @@ public: s32 MapMemory(void** out_addr, VAddr virtual_addr, u64 size, MemoryProt prot, MemoryMapFlags flags, VMAType type, std::string_view name = "anon", - bool validate_dmem = false, PAddr phys_addr = -1, u64 alignment = 0); + bool validate_dmem = false, PAddr phys_addr = -1, u64 alignment = 0, + bool is_system_module = false); s32 MapFile(void** out_addr, VAddr virtual_addr, u64 size, MemoryProt prot, MemoryMapFlags flags, s32 fd, s64 phys_addr); @@ -329,7 +334,7 @@ private: } VMAHandle CreateArea(VAddr virtual_addr, u64 size, MemoryProt prot, MemoryMapFlags flags, - VMAType type, std::string_view name, u64 alignment); + VMAType type, std::string_view name, u64 alignment, bool is_system_module); VAddr SearchFree(VAddr virtual_addr, u64 size, u32 alignment); diff --git a/src/core/module.cpp b/src/core/module.cpp index b3c963679..d9c56e6ef 100644 --- a/src/core/module.cpp +++ b/src/core/module.cpp @@ -113,9 +113,10 @@ void Module::LoadModuleToMemory(u32& max_tls_index) { // Map module segments (and possible TLS trampolines) void** out_addr = reinterpret_cast(&base_virtual_addr); + const bool is_system_module = IsSystemLib(); memory->MapMemory(out_addr, ModuleLoadBase, aligned_base_size, MemoryProt::CpuReadWrite | MemoryProt::CpuExec, MemoryMapFlags::NoFlags, - VMAType::Code, name); + VMAType::Code, name, false, -1, 0, is_system_module); LOG_INFO(Core_Linker, "Loading module {} to {}", name, fmt::ptr(*out_addr)); #ifdef ARCH_X86_64 From cdec57acb8aa410821eb78084f3c59bed917b899 Mon Sep 17 00:00:00 2001 From: ParantezTech Date: Sat, 21 Feb 2026 15:45:56 +0300 Subject: [PATCH 09/11] optimize: unnecessary overhead --- src/core/memory.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/core/memory.cpp b/src/core/memory.cpp index c5f2bae6a..89f3230d7 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -1399,20 +1399,15 @@ bool MemoryManager::IsFlexibleCommittedVma(const VirtualMemoryArea& vma) const { return false; } - const bool has_physical_tracking = - vma.type == VMAType::Direct || vma.type == VMAType::Flexible || vma.type == VMAType::Pooled; - if (has_physical_tracking) { - // Direct/flexible/pooled mappings should expose at least one physical sub-area when - // committed. + if (vma.type == VMAType::Flexible) { return !vma.phys_areas.empty(); } - // Non-phys-tracked mappings (code/stack/file) are committed through address-space map calls. if (vma.type == VMAType::Code) { // System modules should not consume the game's flexible memory. return !vma.is_system_module; } - return vma.type == VMAType::Stack || vma.type == VMAType::File; + return false; } u64 MemoryManager::GetFlexibleMappedBytesInRangeLocked(VAddr virtual_addr, u64 size) const { @@ -1444,13 +1439,18 @@ u64 MemoryManager::GetFlexibleMappedBytesInRangeLocked(VAddr virtual_addr, u64 s } while (it != vma_map.end() && it->second.base < range_end) { const auto& vma = it->second; + const bool counted_type = IsFlexibleCountedVmaType(vma.type); + if (!counted_type) { + ++it; + continue; + } + const VAddr vma_end = vma.base + vma.size; const VAddr overlap_start = std::max(range_start, vma.base); const VAddr overlap_end = std::min(range_end, vma_end); - const bool counted_type = IsFlexibleCountedVmaType(vma.type); const bool committed = IsFlexibleCommittedVma(vma); - if (overlap_start < overlap_end && counted_type && committed) { + if (overlap_start < overlap_end && committed) { mapped_bytes += overlap_end - overlap_start; } From e739659c312ea469f9173ff2019f4350ecd508a3 Mon Sep 17 00:00:00 2001 From: ParantezTech Date: Sat, 21 Feb 2026 16:17:47 +0300 Subject: [PATCH 10/11] optimize cache for flexible mapped bytes in range queries --- src/core/memory.cpp | 54 ++++++++++++++++++++++++++++++++++++++++----- src/core/memory.h | 13 +++++++++++ 2 files changed, 62 insertions(+), 5 deletions(-) diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 89f3230d7..f86bc869c 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -77,6 +77,7 @@ void MemoryManager::SetupMemoryRegions(u64 flexible_size, bool use_extended_mem1 { std::scoped_lock lk{mutex}; + InvalidateFlexibleMappedRangeCacheLocked(); RecalculateFlexibleMappedUsageLocked(); } @@ -490,6 +491,7 @@ MemoryManager::VMAHandle MemoryManager::CreateArea(VAddr virtual_addr, u64 size, // Create a memory area representing this mapping. const auto new_vma_handle = CarveVMA(virtual_addr, size); auto& new_vma = new_vma_handle->second; + InvalidateFlexibleMappedRangeCacheLocked(); const bool is_exec = True(prot & MemoryProt::CpuExec); if (True(prot & MemoryProt::CpuWrite)) { // On PS4, read is appended to write mappings. @@ -668,7 +670,12 @@ s32 MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, u64 size, Memo MergeAdjacent(vma_map, new_vma_handle); } - const u64 flexible_after = GetFlexibleMappedBytesInRangeLocked(mapped_addr, size); + u64 flexible_after = 0; + if (type == VMAType::Flexible && !new_vma.phys_areas.empty()) { + flexible_after = GetFlexibleRangeOverlapBytesLocked(mapped_addr, size); + } else if (type == VMAType::Code && !is_system_module) { + flexible_after = GetFlexibleRangeOverlapBytesLocked(mapped_addr, size); + } AdjustFlexibleMappedUsageLocked(flexible_before, flexible_after); *out_addr = std::bit_cast(mapped_addr); @@ -772,8 +779,7 @@ s32 MemoryManager::MapFile(void** out_addr, VAddr virtual_addr, u64 size, Memory impl.MapFile(mapped_addr, size, phys_addr, std::bit_cast(prot), handle); - const u64 flexible_after = GetFlexibleMappedBytesInRangeLocked(mapped_addr, size); - AdjustFlexibleMappedUsageLocked(flexible_before, flexible_after); + AdjustFlexibleMappedUsageLocked(flexible_before, 0); *out_addr = std::bit_cast(mapped_addr); return ORBIS_OK; @@ -885,8 +891,7 @@ s32 MemoryManager::UnmapMemory(VAddr virtual_addr, u64 size) { const u64 flexible_before = GetFlexibleMappedBytesInRangeLocked(virtual_addr, size); const s32 result = UnmapMemoryImpl(virtual_addr, size); if (result == ORBIS_OK) { - const u64 flexible_after = GetFlexibleMappedBytesInRangeLocked(virtual_addr, size); - AdjustFlexibleMappedUsageLocked(flexible_before, flexible_after); + AdjustFlexibleMappedUsageLocked(flexible_before, 0); } return result; } @@ -944,6 +949,7 @@ u64 MemoryManager::UnmapBytesFromEntry(VAddr virtual_addr, VirtualMemoryArea vma // Mark region as free and attempt to coalesce it with neighbours. const auto new_it = CarveVMA(virtual_addr, size_in_vma); auto& vma = new_it->second; + InvalidateFlexibleMappedRangeCacheLocked(); vma.type = VMAType::Free; vma.prot = MemoryProt::NoAccess; vma.phys_areas.clear(); @@ -1410,6 +1416,27 @@ bool MemoryManager::IsFlexibleCommittedVma(const VirtualMemoryArea& vma) const { return false; } +u64 MemoryManager::GetFlexibleRangeOverlapBytesLocked(VAddr virtual_addr, u64 size) const { + if (!IsFlexibleRegionConfigured() || size == 0) { + return 0; + } + + const VAddr aligned_start = Common::AlignDown(virtual_addr, 16_KB); + const u64 page_offset = virtual_addr - aligned_start; + if (size > std::numeric_limits::max() - page_offset) { + return 0; + } + const u64 aligned_size = Common::AlignUp(size + page_offset, 16_KB); + if (aligned_size == 0) { + return 0; + } + + const VAddr aligned_end = aligned_start + aligned_size; + const VAddr range_start = std::max(aligned_start, flexible_virtual_base); + const VAddr range_end = std::min(aligned_end, flexible_virtual_end); + return range_start < range_end ? range_end - range_start : 0; +} + u64 MemoryManager::GetFlexibleMappedBytesInRangeLocked(VAddr virtual_addr, u64 size) const { if (!IsFlexibleRegionConfigured() || size == 0) { return 0; @@ -1432,6 +1459,12 @@ u64 MemoryManager::GetFlexibleMappedBytesInRangeLocked(VAddr virtual_addr, u64 s return 0; } + if (flexible_mapped_range_cache.valid && flexible_mapped_range_cache.revision == vma_revision && + flexible_mapped_range_cache.range_start == range_start && + flexible_mapped_range_cache.range_end == range_end) { + return flexible_mapped_range_cache.mapped_bytes; + } + u64 mapped_bytes = 0; auto it = vma_map.upper_bound(range_start); if (it != vma_map.begin()) { @@ -1456,9 +1489,20 @@ u64 MemoryManager::GetFlexibleMappedBytesInRangeLocked(VAddr virtual_addr, u64 s ++it; } + + flexible_mapped_range_cache.range_start = range_start; + flexible_mapped_range_cache.range_end = range_end; + flexible_mapped_range_cache.mapped_bytes = mapped_bytes; + flexible_mapped_range_cache.revision = vma_revision; + flexible_mapped_range_cache.valid = true; return mapped_bytes; } +void MemoryManager::InvalidateFlexibleMappedRangeCacheLocked() { + ++vma_revision; + flexible_mapped_range_cache.valid = false; +} + void MemoryManager::AdjustFlexibleMappedUsageLocked(u64 mapped_before, u64 mapped_after) { if (mapped_after >= mapped_before) { flexible_mapped_usage += mapped_after - mapped_before; diff --git a/src/core/memory.h b/src/core/memory.h index 3c465d22a..bce8e11c1 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -358,8 +358,12 @@ private: bool IsFlexibleCommittedVma(const VirtualMemoryArea& vma) const; + u64 GetFlexibleRangeOverlapBytesLocked(VAddr virtual_addr, u64 size) const; + u64 GetFlexibleMappedBytesInRangeLocked(VAddr virtual_addr, u64 size) const; + void InvalidateFlexibleMappedRangeCacheLocked(); + void AdjustFlexibleMappedUsageLocked(u64 mapped_before, u64 mapped_after); void RecalculateFlexibleMappedUsageLocked(); @@ -377,6 +381,15 @@ private: VAddr flexible_virtual_base{}; VAddr flexible_virtual_end{}; u64 flexible_mapped_usage{}; + struct FlexibleMappedRangeCache { + VAddr range_start{}; + VAddr range_end{}; + u64 mapped_bytes{}; + u64 revision{}; + bool valid{}; + }; + mutable FlexibleMappedRangeCache flexible_mapped_range_cache{}; + u64 vma_revision{}; u64 pool_budget{}; s32 sdk_version{}; Vulkan::Rasterizer* rasterizer{}; From 352ae6c7d588f88d6b524bc6714592105bc6855e Mon Sep 17 00:00:00 2001 From: ParantezTech Date: Thu, 26 Feb 2026 14:39:46 +0300 Subject: [PATCH 11/11] license --- src/core/memory.h | 2 +- src/core/module.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/memory.h b/src/core/memory.h index bce8e11c1..756710a4d 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project +// SPDX-FileCopyrightText: Copyright 2025-2026 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/module.cpp b/src/core/module.cpp index 166582864..a356be6f6 100644 --- a/src/core/module.cpp +++ b/src/core/module.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "common/alignment.h"