From 2fc62b5b5fa42f39c1a01c040eb48b026b891434 Mon Sep 17 00:00:00 2001 From: Lander Gallastegi Date: Sun, 8 Jun 2025 21:42:28 +0200 Subject: [PATCH] Page manager: batch protect, masked ranges --- src/common/bit_array.h | 6 +-- src/video_core/page_manager.cpp | 91 ++++++++++++++++++++++++++++----- src/video_core/page_manager.h | 2 +- 3 files changed, 82 insertions(+), 17 deletions(-) diff --git a/src/common/bit_array.h b/src/common/bit_array.h index 492bcdf3e..827368d06 100644 --- a/src/common/bit_array.h +++ b/src/common/bit_array.h @@ -227,7 +227,7 @@ public: return FirstRangeFrom(0); } - Range LastRegionFrom(size_t end) const { + Range LastRangeFrom(size_t end) const { if (end == 0) { return {0, 0}; } @@ -289,8 +289,8 @@ public: return {0, 0}; } - inline constexpr Range LastRegion() const { - return LastRegionFrom(N); + inline constexpr Range LastRaange() const { + return LastRangeFrom(N); } inline constexpr size_t Size() const { diff --git a/src/video_core/page_manager.cpp b/src/video_core/page_manager.cpp index 0cee1ee9b..7686b58bf 100644 --- a/src/video_core/page_manager.cpp +++ b/src/video_core/page_manager.cpp @@ -48,9 +48,11 @@ struct PageManager::Impl { u8 AddDelta() { if constexpr (delta == 1) { return ++num_watchers; - } else { + } else if constexpr (delta == -1) { ASSERT_MSG(num_watchers > 0, "Not enough watchers"); return --num_watchers; + } else { + return num_watchers; } } }; @@ -223,22 +225,21 @@ struct PageManager::Impl { PageState& state = cached_pages[page]; // Apply the change to the page state - const u8 new_count = state.AddDelta(); + const u8 new_count = state.AddDelta < track ? 1 : -1 > (); - // If the protection changed add pending (un)protect action if (auto new_perms = state.Perm(); new_perms != perms) [[unlikely]] { + // If the protection changed add pending (un)protect action release_pending(); perms = new_perms; + } else if (range_bytes != 0) { + // If the protection did not change, extend the current range + range_bytes += PAGE_SIZE; } - // If the page must be (un)protected, add it to the pending range - if ((new_count == 0 && !track) || (new_count == 1 && track)) { - if (range_bytes == 0) { - range_begin = page; - } - range_bytes += PAGE_SIZE; - } else { - release_pending(); + // Only start a new range if the page must be (un)protected + if (range_bytes == 0 && ((new_count == 0 && !track) || (new_count == 1 && track))) { + range_begin = page; + range_bytes = PAGE_SIZE; } } @@ -253,13 +254,70 @@ struct PageManager::Impl { } template - void UpdatePageWatchersMasked(VAddr addr, RegionBits& mask) { + void UpdatePageWatchersMasked(VAddr base_addr, RegionBits& mask) { RENDERER_TRACE; boost::container::small_vector update_ranges; + + auto start_range = mask.FirstRange(); + auto end_range = mask.LastRaange(); + + if (start_range.second == end_range.second) { + // Optimization: if all pages are contiguous, use the regular UpdatePageWatchers + const VAddr start_addr = base_addr + (start_range.first << PAGE_BITS); + const u64 size = (start_range.second - start_range.first) << PAGE_BITS; + + UpdatePageWatchers(start_addr, size); + return; + } + { std::scoped_lock lk(lock); - + size_t base_page = (base_addr >> PAGE_BITS); + auto perms = cached_pages[base_page + start_range.first].Perm(); + u64 range_begin = 0; + u64 range_bytes = 0; + + const auto release_pending = [&] { + if (range_bytes > 0) { + RENDERER_TRACE; + // Add pending (un)protect action + update_ranges.push_back({range_begin << PAGE_BITS, range_bytes, perms}); + range_bytes = 0; + } + }; + + for (size_t page = start_range.first; page <= end_range.second; ++page) { + PageState& state = cached_pages[base_page + page]; + const bool update = mask.Get(page); + + // Apply the change to the page state + const u8 new_count = + update ? state.AddDelta < track ? 1 : -1 > () : state.AddDelta<0>(); + + if (auto new_perms = state.Perm(); new_perms != perms) [[unlikely]] { + // If the protection changed add pending (un)protect action + release_pending(); + perms = new_perms; + } else if (range_bytes != 0) { + // If the protection did not change, extend the current range + range_bytes += PAGE_SIZE; + } + + // If the page is not being updated, skip it + if (!update) { + continue; + } + + // Only start a new range if the page must be (un)protected + if (range_bytes == 0 && ((new_count == 0 && !track) || (new_count == 1 && track))) { + range_begin = base_page + page; + range_bytes = PAGE_SIZE; + } + } + + // Add pending (un)protect action + release_pending(); } // Flush deferred protects @@ -294,7 +352,14 @@ void PageManager::UpdatePageWatchers(VAddr addr, u64 size) const { impl->UpdatePageWatchers(addr, size); } +template +void PageManager::UpdatePageWatchersMasked(VAddr base_addr, RegionBits& mask) const { + impl->UpdatePageWatchersMasked(base_addr, mask); +} + template void PageManager::UpdatePageWatchers(VAddr addr, u64 size) const; template void PageManager::UpdatePageWatchers(VAddr addr, u64 size) const; +template void PageManager::UpdatePageWatchersMasked(VAddr base_addr, RegionBits& mask) const; +template void PageManager::UpdatePageWatchersMasked(VAddr base_addr, RegionBits& mask) const; } // namespace VideoCore diff --git a/src/video_core/page_manager.h b/src/video_core/page_manager.h index 59071dabc..9b21ef6b0 100644 --- a/src/video_core/page_manager.h +++ b/src/video_core/page_manager.h @@ -35,7 +35,7 @@ public: /// Updates watches in the pages touching the specified region /// using a mask. template - void UpdatePageWatchersMasked(VAddr addr, RegionBits& mask) const; + void UpdatePageWatchersMasked(VAddr base_addr, RegionBits& mask) const; /// Returns page aligned address. static constexpr VAddr GetPageAddr(VAddr addr) {