mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2026-02-11 00:47:48 -07:00
Core: Combine guest pages into host pages larger than 4K
Most systems that Dolphin runs on have a page size of 4 KiB, which conveniently matches the page size of the GameCube and Wii. But there are also systems that use larger page sizes, notably Apple CPUs with 16 KiB page sizes. To let us create host mappings on such systems, this commit implements combining guest mappings into host page sized mappings wherever possible. For this to work for a given mapping, not only do four (in the case of 16 KiB) guest mappings have to exist adjacent to each other, but the corresponding translated addresses also have to be adjacent, and the lowest bits of the addresses have to match. When I tested a few games, the following percentages of guest mappings met these criteria: Spider-Man 2: 0%-12% Rogue Squadron 2: 39%-42% Rogue Squadron 3: 28%-41% So while 16 KiB systems don't get as much of a performance improvement as 4 KiB systems, they do still get some improvement.
This commit is contained in:
parent
0ce95299f6
commit
b0e2a28e14
@ -10,6 +10,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <bit>
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
@ -44,12 +45,22 @@
|
||||
namespace Memory
|
||||
{
|
||||
MemoryManager::MemoryManager(Core::System& system)
|
||||
: m_page_size(m_arena.GetPageSize()), m_system(system)
|
||||
: m_page_size(static_cast<u32>(m_arena.GetPageSize())),
|
||||
m_guest_pages_per_host_page(m_page_size / PowerPC::HW_PAGE_SIZE),
|
||||
m_host_page_type(GetHostPageTypeForPageSize(m_page_size)), m_system(system)
|
||||
{
|
||||
}
|
||||
|
||||
MemoryManager::~MemoryManager() = default;
|
||||
|
||||
MemoryManager::HostPageType MemoryManager::GetHostPageTypeForPageSize(u32 page_size)
|
||||
{
|
||||
if (!std::has_single_bit(page_size))
|
||||
return HostPageType::Unsupported;
|
||||
|
||||
return page_size > PowerPC::HW_PAGE_SIZE ? HostPageType::LargePages : HostPageType::SmallPages;
|
||||
}
|
||||
|
||||
void MemoryManager::InitMMIO(bool is_wii)
|
||||
{
|
||||
m_mmio_mapping = std::make_unique<MMIO::Mapping>();
|
||||
@ -312,10 +323,69 @@ void MemoryManager::UpdateDBATMappings(const PowerPC::BatTable& dbat_table)
|
||||
|
||||
void MemoryManager::AddPageTableMapping(u32 logical_address, u32 translated_address, bool writeable)
|
||||
{
|
||||
if (!m_is_fastmem_arena_initialized || m_page_size > PowerPC::HW_PAGE_SIZE)
|
||||
if (!m_is_fastmem_arena_initialized)
|
||||
return;
|
||||
|
||||
constexpr u32 logical_size = PowerPC::HW_PAGE_SIZE;
|
||||
switch (m_host_page_type)
|
||||
{
|
||||
case HostPageType::SmallPages:
|
||||
return AddHostPageTableMapping(logical_address, translated_address, writeable,
|
||||
PowerPC::HW_PAGE_SIZE);
|
||||
case HostPageType::LargePages:
|
||||
return TryAddLargePageTableMapping(logical_address, translated_address, writeable);
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryManager::TryAddLargePageTableMapping(u32 logical_address, u32 translated_address,
|
||||
bool writeable)
|
||||
{
|
||||
const bool add_readable =
|
||||
TryAddLargePageTableMapping(logical_address, translated_address, m_large_readable_pages);
|
||||
|
||||
const bool add_writeable =
|
||||
writeable &&
|
||||
TryAddLargePageTableMapping(logical_address, translated_address, m_large_writeable_pages);
|
||||
|
||||
if (add_readable || add_writeable)
|
||||
{
|
||||
AddHostPageTableMapping(logical_address & ~(m_page_size - 1),
|
||||
translated_address & ~(m_page_size - 1), add_writeable, m_page_size);
|
||||
}
|
||||
}
|
||||
|
||||
bool MemoryManager::TryAddLargePageTableMapping(u32 logical_address, u32 translated_address,
|
||||
std::map<u32, std::vector<u32>>& map)
|
||||
{
|
||||
std::vector<u32>& entries = map[logical_address & ~(m_page_size - 1)];
|
||||
|
||||
if (entries.empty())
|
||||
entries = std::vector<u32>(m_guest_pages_per_host_page, INVALID_MAPPING);
|
||||
|
||||
entries[(logical_address & (m_page_size - 1)) / PowerPC::HW_PAGE_SIZE] = translated_address;
|
||||
|
||||
return CanCreateHostMappingForGuestPages(entries);
|
||||
}
|
||||
|
||||
bool MemoryManager::CanCreateHostMappingForGuestPages(const std::vector<u32>& entries) const
|
||||
{
|
||||
const u32 translated_address = entries[0];
|
||||
if ((translated_address & (m_page_size - 1)) != 0)
|
||||
return false;
|
||||
|
||||
for (size_t i = 1; i < m_guest_pages_per_host_page; ++i)
|
||||
{
|
||||
if (entries[i] != translated_address + i * PowerPC::HW_PAGE_SIZE)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MemoryManager::AddHostPageTableMapping(u32 logical_address, u32 translated_address,
|
||||
bool writeable, u32 logical_size)
|
||||
{
|
||||
for (const auto& physical_region : m_physical_regions)
|
||||
{
|
||||
if (!physical_region.active)
|
||||
@ -367,9 +437,45 @@ void MemoryManager::AddPageTableMapping(u32 logical_address, u32 translated_addr
|
||||
|
||||
void MemoryManager::RemovePageTableMappings(const std::set<u32>& mappings)
|
||||
{
|
||||
if (m_page_size > PowerPC::HW_PAGE_SIZE)
|
||||
switch (m_host_page_type)
|
||||
{
|
||||
case HostPageType::SmallPages:
|
||||
return RemoveHostPageTableMappings(mappings);
|
||||
case HostPageType::LargePages:
|
||||
for (u32 logical_address : mappings)
|
||||
RemoveLargePageTableMapping(logical_address);
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryManager::RemoveLargePageTableMapping(u32 logical_address)
|
||||
{
|
||||
RemoveLargePageTableMapping(logical_address, m_large_readable_pages);
|
||||
RemoveLargePageTableMapping(logical_address, m_large_writeable_pages);
|
||||
|
||||
const u32 aligned_logical_address = logical_address & ~(m_page_size - 1);
|
||||
const auto it = m_page_table_mapped_entries.find(aligned_logical_address);
|
||||
if (it != m_page_table_mapped_entries.end())
|
||||
{
|
||||
const LogicalMemoryView& entry = it->second;
|
||||
m_arena.UnmapFromMemoryRegion(entry.mapped_pointer, entry.mapped_size);
|
||||
|
||||
m_page_table_mapped_entries.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryManager::RemoveLargePageTableMapping(u32 logical_address,
|
||||
std::map<u32, std::vector<u32>>& map)
|
||||
{
|
||||
const auto it = map.find(logical_address & ~(m_page_size - 1));
|
||||
if (it != map.end())
|
||||
it->second[(logical_address & (m_page_size - 1)) / PowerPC::HW_PAGE_SIZE] = INVALID_MAPPING;
|
||||
}
|
||||
|
||||
void MemoryManager::RemoveHostPageTableMappings(const std::set<u32>& mappings)
|
||||
{
|
||||
if (mappings.empty())
|
||||
return;
|
||||
|
||||
@ -389,6 +495,8 @@ void MemoryManager::RemoveAllPageTableMappings()
|
||||
m_arena.UnmapFromMemoryRegion(entry.mapped_pointer, entry.mapped_size);
|
||||
}
|
||||
m_page_table_mapped_entries.clear();
|
||||
m_large_readable_pages.clear();
|
||||
m_large_writeable_pages.clear();
|
||||
}
|
||||
|
||||
void MemoryManager::DoState(PointerWrap& p)
|
||||
@ -486,6 +594,9 @@ void MemoryManager::ShutdownFastmemArena()
|
||||
|
||||
m_arena.ReleaseMemoryRegion();
|
||||
|
||||
m_large_readable_pages.clear();
|
||||
m_large_writeable_pages.clear();
|
||||
|
||||
m_fastmem_arena = nullptr;
|
||||
m_fastmem_arena_size = 0;
|
||||
m_physical_base = nullptr;
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
#include <set>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/MathUtil.h"
|
||||
@ -161,6 +162,16 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
enum class HostPageType
|
||||
{
|
||||
// 4K or smaller
|
||||
SmallPages,
|
||||
// 8K or larger
|
||||
LargePages,
|
||||
// Required APIs aren't available, or the page size isn't a power of 2
|
||||
Unsupported,
|
||||
};
|
||||
|
||||
// Base is a pointer to the base of the memory map. Yes, some MMU tricks
|
||||
// are used to set up a full GC or Wii memory map in process memory.
|
||||
// In 64-bit, this might point to "high memory" (above the 32-bit limit),
|
||||
@ -212,7 +223,9 @@ private:
|
||||
// The MemArena class
|
||||
Common::MemArena m_arena;
|
||||
|
||||
const size_t m_page_size;
|
||||
const u32 m_page_size;
|
||||
const u32 m_guest_pages_per_host_page;
|
||||
const HostPageType m_host_page_type;
|
||||
|
||||
// Dolphin allocates memory to represent four regions:
|
||||
// - 32MB RAM (actually 24MB on hardware), available on GameCube and Wii
|
||||
@ -261,8 +274,26 @@ private:
|
||||
std::array<void*, PowerPC::BAT_PAGE_COUNT> m_physical_page_mappings{};
|
||||
std::array<void*, PowerPC::BAT_PAGE_COUNT> m_logical_page_mappings{};
|
||||
|
||||
// If the host page size is larger than the guest page size, these two maps are used
|
||||
// to keep track of which guest pages can be combined and mapped as one host page.
|
||||
static constexpr u32 INVALID_MAPPING = 0xFFFFFFFF;
|
||||
std::map<u32, std::vector<u32>> m_large_readable_pages;
|
||||
std::map<u32, std::vector<u32>> m_large_writeable_pages;
|
||||
|
||||
Core::System& m_system;
|
||||
|
||||
static HostPageType GetHostPageTypeForPageSize(u32 page_size);
|
||||
|
||||
void InitMMIO(bool is_wii);
|
||||
|
||||
void TryAddLargePageTableMapping(u32 logical_address, u32 translated_address, bool writeable);
|
||||
bool TryAddLargePageTableMapping(u32 logical_address, u32 translated_address,
|
||||
std::map<u32, std::vector<u32>>& map);
|
||||
bool CanCreateHostMappingForGuestPages(const std::vector<u32>& entries) const;
|
||||
void AddHostPageTableMapping(u32 logical_address, u32 translated_address, bool writeable,
|
||||
u32 logical_size);
|
||||
void RemoveLargePageTableMapping(u32 logical_address);
|
||||
void RemoveLargePageTableMapping(u32 logical_address, std::map<u32, std::vector<u32>>& map);
|
||||
void RemoveHostPageTableMappings(const std::set<u32>& mappings);
|
||||
};
|
||||
} // namespace Memory
|
||||
|
||||
Loading…
Reference in New Issue
Block a user