From 0c486fb63ada58dedfad453b53158f7eb244b423 Mon Sep 17 00:00:00 2001 From: kd-11 Date: Sun, 30 Aug 2026 23:00:48 +0300 Subject: [PATCH] rsx/mm: Implement range merging mechanics --- rpcs3/Emu/RSX/Host/MM.cpp | 98 +++++++++++++++++++++++++++++++++++---- rpcs3/Emu/RSX/Host/MM.h | 14 ++++++ 2 files changed, 103 insertions(+), 9 deletions(-) diff --git a/rpcs3/Emu/RSX/Host/MM.cpp b/rpcs3/Emu/RSX/Host/MM.cpp index 1add47931e..40760c9dcc 100644 --- a/rpcs3/Emu/RSX/Host/MM.cpp +++ b/rpcs3/Emu/RSX/Host/MM.cpp @@ -14,14 +14,32 @@ namespace rsx rsx::simple_array g_deferred_mprotect_queue; shared_mutex g_mprotect_queue_lock; + void mm_sanitize_queue_internal() + { + u32 w = 0, r = 0; + for (; r < g_deferred_mprotect_queue.size(); ++r) + { + auto& block = g_deferred_mprotect_queue[r]; + if (!block.range.valid()) + { + continue; + } + g_deferred_mprotect_queue[w++] = block; + } + g_deferred_mprotect_queue.resize(w); + } + void mm_flush_mprotect_queue_internal(u32 count) { AUDIT(count <= g_deferred_mprotect_queue.size()); for (u32 i = 0; i < count; ++i) { - const auto& block = g_deferred_mprotect_queue[i]; + auto& block = g_deferred_mprotect_queue[i]; + ensure(block.range.valid()); + utils::memory_protect(reinterpret_cast(block.range.start), block.range.length(), block.prot); + block.range.invalidate(); } const u32 remaining = g_deferred_mprotect_queue.size() - count; @@ -31,9 +49,8 @@ namespace rsx return; } - // Pop count entries from the queue - std::memmove(g_deferred_mprotect_queue.data(), g_deferred_mprotect_queue.data() + count, remaining * sizeof(MM_block)); - g_deferred_mprotect_queue.resize(remaining); + // Pop processed entries from the queue + mm_sanitize_queue_internal(); } // Reverse scan to find the latest overlapping MM conflict. The result is a count of prefix blocks. @@ -53,9 +70,57 @@ namespace rsx void mm_defer_mprotect_internal(u64 start, u64 length, utils::protection prot) { - // We could stack and merge requests here, but that is more trouble than it is truly worth. - // A fresh call to memory_protect only takes a few nanoseconds of setup overhead, it is not worth the risk of hanging because of conflicts. - g_deferred_mprotect_queue.push_back({ utils::address_range64::start_length(start, length), prot }); + const auto range = utils::address_range64::start_length(start, length); + bool has_invalid = false; + bool is_merged = false; + + // Attempt a merge first. The queue length is short but the time taken to run mprotect is very high in comparison. + for (auto it = g_deferred_mprotect_queue.rbegin(); + it != g_deferred_mprotect_queue.rend(); + ++it) + { + auto& block = *it; + if (!block.touches(range)) + { + continue; + } + + if (block.prot != prot) + { + // Optimization. If our new range swallows the old one, replace it. + if (block.range.inside(range)) + { + block.range.invalidate(); + has_invalid = true; + continue; + } + + if (!block.overlaps(range)) + { + // Adjacent. Skip. + continue; + } + + // Preserve ordering. Do not proceed with merge. + break; + } + + block.merge(range); + is_merged = true; + break; + } + + if (has_invalid) + { + mm_sanitize_queue_internal(); + } + + if (is_merged) + { + return; + } + + g_deferred_mprotect_queue.push_back({ range, prot }); } void mm_protect(void* ptr, u64 length, utils::protection prot) @@ -75,9 +140,24 @@ namespace rsx if (prot == utils::protection::rw || prot == utils::protection::wx) { // Basically an unlock op. Flush the conflicting prefix block if any overlap is detected. - if (const u32 count = mm_find_conflict_internal(FN(x.overlaps(range)))) + if (u32 count = mm_find_conflict_internal(FN(x.overlaps(range)))) { - mm_flush_mprotect_queue_internal(count); + // Check for degenerate ranges that we'll be crushing + for (auto pblock = &g_deferred_mprotect_queue[count - 1]; + count > 0 && pblock->range.inside(range); + count--, pblock--) + { + pblock->range.invalidate(); + } + + if (count) + { + mm_flush_mprotect_queue_internal(count); + } + else + { + mm_sanitize_queue_internal(); + } } utils::memory_protect(ptr, length, prot); diff --git a/rpcs3/Emu/RSX/Host/MM.h b/rpcs3/Emu/RSX/Host/MM.h index 43053cdd17..7ba75e1a30 100644 --- a/rpcs3/Emu/RSX/Host/MM.h +++ b/rpcs3/Emu/RSX/Host/MM.h @@ -13,6 +13,15 @@ namespace rsx utils::address_range64 range; utils::protection prot; + inline void merge(const utils::address_range64& other) + { + AUDIT(other.valid()); + range = utils::address_range64::start_end( + std::min(range.start, other.start), + std::max(range.end, other.end) + ); + } + inline bool overlaps(const utils::address_range64& test) const { return range.overlaps(test); @@ -22,6 +31,11 @@ namespace rsx { return range.overlaps(addr); } + + inline bool touches(const utils::address_range64& test) const + { + return range.touches(test); + } }; enum mm_backend_ctrl : u32