Defer memory protect

This commit is contained in:
Lander Gallastegi 2025-05-16 15:16:34 +02:00
parent 7c6f3cc00c
commit 291d989b49

View File

@ -1,7 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <boost/icl/interval_set.hpp> #include <boost/container/small_vector.hpp>
#include "common/assert.h" #include "common/assert.h"
#include "common/signal_context.h" #include "common/signal_context.h"
#include "core/memory.h" #include "core/memory.h"
@ -54,6 +54,12 @@ struct PageManager::Impl {
} }
}; };
struct UpdateProtectRange {
VAddr addr;
u64 size;
Core::MemoryPermission perms;
};
static constexpr size_t ADDRESS_BITS = 40; static constexpr size_t ADDRESS_BITS = 40;
static constexpr size_t NUM_ADDRESS_PAGES = 1ULL << (40 - PAGE_BITS); static constexpr size_t NUM_ADDRESS_PAGES = 1ULL << (40 - PAGE_BITS);
inline static Vulkan::Rasterizer* rasterizer; inline static Vulkan::Rasterizer* rasterizer;
@ -184,47 +190,57 @@ struct PageManager::Impl {
#endif #endif
template <s32 delta> template <s32 delta>
void UpdatePageWatchers(VAddr addr, u64 size) { void UpdatePageWatchers(VAddr addr, u64 size) {
std::scoped_lock lk(lock); boost::container::small_vector<UpdateProtectRange, 16> update_ranges;
{
std::scoped_lock lk(lock);
size_t page = addr >> PAGE_BITS; size_t page = addr >> PAGE_BITS;
auto perms = cached_pages[page].Perm(); auto perms = cached_pages[page].Perm();
u64 range_begin = 0; u64 range_begin = 0;
u64 range_bytes = 0; u64 range_bytes = 0;
const auto release_pending = [&] { const auto release_pending = [&] {
if (range_bytes > 0) { RENDERER_TRACE;
Protect(range_begin << PAGE_BITS, range_bytes, perms); if (range_bytes > 0) {
range_bytes = 0; // Add pending (un)protect action
} update_ranges.push_back({range_begin << PAGE_BITS, range_bytes, perms});
}; range_bytes = 0;
}
// Iterate requested pages };
const u64 page_end = Common::DivCeil(addr + size, PAGE_SIZE);
for (; page != page_end; ++page) { // Iterate requested pages
PageState& state = cached_pages[page]; const u64 page_end = Common::DivCeil(addr + size, PAGE_SIZE);
for (; page != page_end; ++page) {
// Apply the change to the page state PageState& state = cached_pages[page];
const u8 new_count = state.AddDelta<delta>();
// Apply the change to the page state
// If the protection changed flush pending (un)protect action const u8 new_count = state.AddDelta<delta>();
if (auto new_perms = state.Perm(); new_perms != perms) [[unlikely]] {
release_pending(); // If the protection changed add pending (un)protect action
perms = new_perms; if (auto new_perms = state.Perm(); new_perms != perms) [[unlikely]] {
} release_pending();
perms = new_perms;
// If the page must be (un)protected, add it to the pending range }
if ((new_count == 0 && delta < 0) || (new_count == 1 && delta > 0)) {
if (range_bytes == 0) { // If the page must be (un)protected, add it to the pending range
range_begin = page; if ((new_count == 0 && delta < 0) || (new_count == 1 && delta > 0)) {
if (range_bytes == 0) {
range_begin = page;
}
range_bytes += PAGE_SIZE;
} else {
release_pending();
} }
range_bytes += PAGE_SIZE;
} else {
release_pending();
} }
// Add pending (un)protect action
release_pending();
} }
// Flush pending (un)protect action // Flush deferred protects
release_pending(); for (const auto& range : update_ranges) {
Protect(range.addr, range.size, range.perms);
}
} }
std::array<PageState, NUM_ADDRESS_PAGES> cached_pages{}; std::array<PageState, NUM_ADDRESS_PAGES> cached_pages{};