Page manager: batch protect, masked ranges

This commit is contained in:
Lander Gallastegi 2025-06-08 21:42:28 +02:00
parent 14a33d35a2
commit 2fc62b5b5f
3 changed files with 82 additions and 17 deletions

View File

@ -227,7 +227,7 @@ public:
return FirstRangeFrom(0); return FirstRangeFrom(0);
} }
Range LastRegionFrom(size_t end) const { Range LastRangeFrom(size_t end) const {
if (end == 0) { if (end == 0) {
return {0, 0}; return {0, 0};
} }
@ -289,8 +289,8 @@ public:
return {0, 0}; return {0, 0};
} }
inline constexpr Range LastRegion() const { inline constexpr Range LastRaange() const {
return LastRegionFrom(N); return LastRangeFrom(N);
} }
inline constexpr size_t Size() const { inline constexpr size_t Size() const {

View File

@ -48,9 +48,11 @@ struct PageManager::Impl {
u8 AddDelta() { u8 AddDelta() {
if constexpr (delta == 1) { if constexpr (delta == 1) {
return ++num_watchers; return ++num_watchers;
} else { } else if constexpr (delta == -1) {
ASSERT_MSG(num_watchers > 0, "Not enough watchers"); ASSERT_MSG(num_watchers > 0, "Not enough watchers");
return --num_watchers; return --num_watchers;
} else {
return num_watchers;
} }
} }
}; };
@ -223,22 +225,21 @@ struct PageManager::Impl {
PageState& state = cached_pages[page]; PageState& state = cached_pages[page];
// Apply the change to the page state // Apply the change to the page state
const u8 new_count = state.AddDelta<track ? 1 : -1>(); 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 (auto new_perms = state.Perm(); new_perms != perms) [[unlikely]] {
// If the protection changed add pending (un)protect action
release_pending(); release_pending();
perms = new_perms; 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 // Only start a new range if the page must be (un)protected
if ((new_count == 0 && !track) || (new_count == 1 && track)) { if (range_bytes == 0 && ((new_count == 0 && !track) || (new_count == 1 && track))) {
if (range_bytes == 0) { range_begin = page;
range_begin = page; range_bytes = PAGE_SIZE;
}
range_bytes += PAGE_SIZE;
} else {
release_pending();
} }
} }
@ -253,13 +254,70 @@ struct PageManager::Impl {
} }
template <bool track> template <bool track>
void UpdatePageWatchersMasked(VAddr addr, RegionBits& mask) { void UpdatePageWatchersMasked(VAddr base_addr, RegionBits& mask) {
RENDERER_TRACE; RENDERER_TRACE;
boost::container::small_vector<UpdateProtectRange, 16> update_ranges; boost::container::small_vector<UpdateProtectRange, 16> 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<track>(start_addr, size);
return;
}
{ {
std::scoped_lock lk(lock); 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 // Flush deferred protects
@ -294,7 +352,14 @@ void PageManager::UpdatePageWatchers(VAddr addr, u64 size) const {
impl->UpdatePageWatchers<track>(addr, size); impl->UpdatePageWatchers<track>(addr, size);
} }
template <bool track>
void PageManager::UpdatePageWatchersMasked(VAddr base_addr, RegionBits& mask) const {
impl->UpdatePageWatchersMasked<track>(base_addr, mask);
}
template void PageManager::UpdatePageWatchers<true>(VAddr addr, u64 size) const; template void PageManager::UpdatePageWatchers<true>(VAddr addr, u64 size) const;
template void PageManager::UpdatePageWatchers<false>(VAddr addr, u64 size) const; template void PageManager::UpdatePageWatchers<false>(VAddr addr, u64 size) const;
template void PageManager::UpdatePageWatchersMasked<true>(VAddr base_addr, RegionBits& mask) const;
template void PageManager::UpdatePageWatchersMasked<false>(VAddr base_addr, RegionBits& mask) const;
} // namespace VideoCore } // namespace VideoCore

View File

@ -35,7 +35,7 @@ public:
/// Updates watches in the pages touching the specified region /// Updates watches in the pages touching the specified region
/// using a mask. /// using a mask.
template <bool track> template <bool track>
void UpdatePageWatchersMasked(VAddr addr, RegionBits& mask) const; void UpdatePageWatchersMasked(VAddr base_addr, RegionBits& mask) const;
/// Returns page aligned address. /// Returns page aligned address.
static constexpr VAddr GetPageAddr(VAddr addr) { static constexpr VAddr GetPageAddr(VAddr addr) {