From 09753bf8ffc3c718b17f815f86c1a979520f0bb9 Mon Sep 17 00:00:00 2001 From: nmzik Date: Tue, 27 Oct 2026 09:12:53 +0100 Subject: [PATCH] memory: change defaults --- src/emulator.cpp | 4 -- .../presentation/performanceOverlay.cpp | 14 ++-- src/kernel/memory.cpp | 69 +++++++++++-------- src/kernel/memory.h | 8 +-- src/loader/systemContent.cpp | 32 ++++++--- src/loader/systemContent.h | 11 ++- tests/VirtualMemoryAllocationTests.cpp | 29 +++++--- 7 files changed, 103 insertions(+), 64 deletions(-) diff --git a/src/emulator.cpp b/src/emulator.cpp index 7834a390b..026b4b083 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -99,10 +99,6 @@ static void Init(const Config::ConfigOptions& cfg, const std::filesystem::path& if (Common::File::IsFileExisting(param_json)) { Loader::SystemContentLoadParamSfo(param_json); - if (const auto flexible_memory_size = Loader::SystemContentGetFlexibleMemorySize(); - flexible_memory_size != 0) { - Libs::LibKernel::Memory::SetFlexibleMemorySize(flexible_memory_size); - } } if (cfg.splash_enabled) { Libs::Graphics::InitializeSplashScreen(param_json.parent_path()); diff --git a/src/graphics/presentation/performanceOverlay.cpp b/src/graphics/presentation/performanceOverlay.cpp index 11d7943c4..b09d46e22 100644 --- a/src/graphics/presentation/performanceOverlay.cpp +++ b/src/graphics/presentation/performanceOverlay.cpp @@ -533,16 +533,16 @@ void DrawMemoryAndIo(const Snapshot& shown) { Bytes(guest.pooled_allocated) + " (" + Bytes(guest.pool_committed) + " committed)"); Row("Flexible", Bytes(guest.flexible_used) + " / " + Bytes(guest.flexible_total)); Row("Extended", Bytes(guest.automatic_allocated)); - // Past the pool size the guest sees no page-table entries available. - const auto entries = std::max(guest.cpu_page_entries, guest.gpu_page_entries); + // Past its total a side sees no page-table entries available. + const auto percent = std::max(guest.cpu_page_entries * 100 / guest.cpu_page_entries_total, + guest.gpu_page_entries * 100 / guest.gpu_page_entries_total); Row("Page table (2 MB)", - Fmt("CPU %llu GPU %llu / %llu", + Fmt("CPU %llu / %llu GPU %llu / %llu", static_cast(guest.cpu_page_entries), + static_cast(guest.cpu_page_entries_total), static_cast(guest.gpu_page_entries), - static_cast(guest.page_entries_total)), - entries > guest.page_entries_total ? BAD - : entries * 10 > guest.page_entries_total * 9 ? MEDIUM - : GOOD); + static_cast(guest.gpu_page_entries_total)), + LevelColor(static_cast(percent), 90, 100)); ImGui::EndTable(); } if (BeginSection("Emulator process")) { diff --git a/src/kernel/memory.cpp b/src/kernel/memory.cpp index 4f97c6344..178fa79ef 100644 --- a/src/kernel/memory.cpp +++ b/src/kernel/memory.cpp @@ -9,6 +9,7 @@ #include "graphics/host_gpu/renderer/renderContext.h" #include "libs/errno.h" #include "libs/libs.h" +#include "loader/systemContent.h" #include #include @@ -63,15 +64,21 @@ constexpr int PROT_GPU_WRITE = 0x20; enum class GpuAccessMode { NoAccess, Read, Write, ReadWrite }; -constexpr uint64_t PAGE_TABLE_POOL_SIZE = 4ull * 1024ull * 1024ull * 1024ull; -constexpr uint64_t PAGE_TABLE_GRANULARITY = 2ull * 1024ull * 1024ull; -constexpr int PAGE_TABLE_POOL_ENTRIES = - static_cast(PAGE_TABLE_POOL_SIZE / PAGE_TABLE_GRANULARITY); +constexpr uint64_t PAGE_TABLE_GRANULARITY = 2ull * 1024ull * 1024ull; constexpr uint64_t DEFAULT_FLEXIBLE_MEMORY_SIZE = 448ull * 1024ull * 1024ull; +// A game gets 12.5 GiB. A fixed 64 MiB cache and mapping table memory past the 64 MiB included +// for each of the CPU, GPU and AMM tables come off the top; flexible and direct memory share what +// is left. One 4 KiB table maps 2 MiB. +constexpr uint64_t GUEST_MEMORY_BUDGET = 12800ull * 1024ull * 1024ull; +constexpr uint64_t RESERVED_CACHE_SIZE = 64ull * 1024ull * 1024ull; +constexpr uint64_t INCLUDED_TABLE_MEMORY = 64ull * 1024ull * 1024ull; +constexpr uint64_t MAPPING_TABLE_SIZE = 4ull * 1024ull; -static uint64_t g_flexible_memory_size = DEFAULT_FLEXIBLE_MEMORY_SIZE; -static bool g_flexible_memory_size_frozen = false; -static Graphics::RenderContext* g_gpu_resources = nullptr; +static uint64_t g_guest_memory_size = GUEST_MEMORY_BUDGET - RESERVED_CACHE_SIZE; +static uint64_t g_flexible_memory_size = DEFAULT_FLEXIBLE_MEMORY_SIZE; +static uint64_t g_cpu_table_memory = INCLUDED_TABLE_MEMORY; +static uint64_t g_gpu_table_memory = INCLUDED_TABLE_MEMORY; +static Graphics::RenderContext* g_gpu_resources = nullptr; #if defined(KYTY_VIRTUAL_MEMORY_ALLOCATION_TESTS) static void (*g_test_gpu_range)(bool, uint64_t, size_t) = nullptr; #endif @@ -664,11 +671,8 @@ public: KYTY_CLASS_NO_COPY(PhysicalMemory); - static constexpr uint64_t TotalSize() { return static_cast(13824) * 1024 * 1024; } - static uint64_t Size() { - EXIT_IF(g_flexible_memory_size >= TotalSize()); - return TotalSize() - g_flexible_memory_size; - } + static uint64_t TotalSize() { return g_guest_memory_size; } + static uint64_t Size() { return TotalSize() - g_flexible_memory_size; } bool Alloc(uint64_t search_start, uint64_t search_end, size_t len, size_t alignment, uint64_t* phys_addr_out, int memory_type, @@ -998,7 +1002,22 @@ static bool SelfTestSub64SharedPlaceholderAlias() { static bool ReplaceFixedRangeWithReserved(uint64_t start, uint64_t size); void Initialize() { - g_flexible_memory_size_frozen = true; + const auto params = Loader::SystemContentGetMemoryParams(); + if (params.flexible_size != 0) { + g_flexible_memory_size = params.flexible_size; + } + g_cpu_table_memory = std::max(params.cpu_table_memory, INCLUDED_TABLE_MEMORY); + g_gpu_table_memory = std::max(params.gpu_table_memory, INCLUDED_TABLE_MEMORY); + + const auto table_overhead = g_cpu_table_memory + g_gpu_table_memory + + std::max(params.amm_table_memory, INCLUDED_TABLE_MEMORY) - + 3 * INCLUDED_TABLE_MEMORY; + EXIT_IF(g_flexible_memory_size + table_overhead >= GUEST_MEMORY_BUDGET - RESERVED_CACHE_SIZE); + g_guest_memory_size = GUEST_MEMORY_BUDGET - RESERVED_CACHE_SIZE - table_overhead; + LOGF("\t flexible memory size = %" PRIu64 " MiB, direct memory size = %" PRIu64 " MiB\n", + g_flexible_memory_size / (1024ull * 1024ull), + (g_guest_memory_size - g_flexible_memory_size) / (1024ull * 1024ull)); + VirtualMemory::Init(); g_guest_address_space = std::make_unique(PhysicalMemory::TotalSize()); g_physical_memory = std::make_unique(); @@ -1066,15 +1085,6 @@ void RegisterCallbacks(callback_func_t alloc_func, callback_func_t free_func) { } } -void SetFlexibleMemorySize(uint64_t size) { - constexpr uint64_t GuestPageSize = 0x4000; - EXIT_IF(g_flexible_memory_size_frozen || g_guest_address_space != nullptr); - EXIT_IF(size == 0 || (size & (GuestPageSize - 1u)) != 0 || size >= PhysicalMemory::TotalSize()); - g_flexible_memory_size = size; - LOGF("\t flexible memory size = 0x%016" PRIx64 " (%" PRIu64 " MiB)\n", size, - size / (1024ull * 1024ull)); -} - bool PhysicalMemory::Alloc(uint64_t search_start, uint64_t search_end, size_t len, size_t alignment, uint64_t* phys_addr_out, int memory_type, AllocationKind kind) { if (phys_addr_out == nullptr) { @@ -2655,12 +2665,12 @@ int KYTY_SYSV_ABI KernelGetPageTableStats(int* cpu_total, int* cpu_available, in const auto gpu_used = (g_virtual_ranges != nullptr ? g_virtual_ranges->CountPageTableEntries(true) : 0); - *cpu_total = PAGE_TABLE_POOL_ENTRIES; - *gpu_total = PAGE_TABLE_POOL_ENTRIES; - *cpu_available = PAGE_TABLE_POOL_ENTRIES - - static_cast(std::min(cpu_used, PAGE_TABLE_POOL_ENTRIES)); - *gpu_available = PAGE_TABLE_POOL_ENTRIES - - static_cast(std::min(gpu_used, PAGE_TABLE_POOL_ENTRIES)); + const auto cpu_tables = g_cpu_table_memory / MAPPING_TABLE_SIZE; + const auto gpu_tables = g_gpu_table_memory / MAPPING_TABLE_SIZE; + *cpu_total = static_cast(cpu_tables); + *gpu_total = static_cast(gpu_tables); + *cpu_available = static_cast(cpu_tables - std::min(cpu_used, cpu_tables)); + *gpu_available = static_cast(gpu_tables - std::min(gpu_used, gpu_tables)); LOGF_COLOR(Log::Color::Green, "\t cpu_total = %d\n" @@ -4381,7 +4391,8 @@ DebugStats GetDebugStats() { stats.direct_total = PhysicalMemory::Size(); stats.flexible_total = FlexibleMemory::Size(); stats.pool_committed = g_memory_pool_committed.load(std::memory_order_relaxed); - stats.page_entries_total = PAGE_TABLE_POOL_ENTRIES; + stats.cpu_page_entries_total = g_cpu_table_memory / MAPPING_TABLE_SIZE; + stats.gpu_page_entries_total = g_gpu_table_memory / MAPPING_TABLE_SIZE; if (g_physical_memory != nullptr) { Common::LockGuard lock(g_physical_memory->GetMutex()); for (const auto& [start, block]: g_physical_memory->GetPhysicalBlocks()) { diff --git a/src/kernel/memory.h b/src/kernel/memory.h index 244565839..fed2611ee 100644 --- a/src/kernel/memory.h +++ b/src/kernel/memory.h @@ -26,9 +26,10 @@ struct DebugStats { uint64_t flexible_total = 0; uint64_t flexible_used = 0; uint64_t pool_committed = 0; - uint64_t cpu_page_entries = 0; // 2 MiB page-table entries in use. - uint64_t gpu_page_entries = 0; - uint64_t page_entries_total = 0; // Per side. + uint64_t cpu_page_entries = 0; // 2 MiB page-table entries in use. + uint64_t gpu_page_entries = 0; + uint64_t cpu_page_entries_total = 0; + uint64_t gpu_page_entries_total = 0; }; [[nodiscard]] DebugStats GetDebugStats(); @@ -125,7 +126,6 @@ static_assert(sizeof(KernelMemoryPoolBlockStats) == 16, "KernelMemoryPoolBlockStats struct size is incorrect"); void RegisterCallbacks(callback_func_t alloc_func, callback_func_t free_func); -void SetFlexibleMemorySize(uint64_t size); int AllocateDirectMemory(int64_t search_start, int64_t search_end, size_t size, size_t alignment, int memory_type, int64_t* phys_addr_out, bool automatic = false); int MapAutomaticMemory(uint64_t vaddr, size_t size, int type, int prot); diff --git a/src/loader/systemContent.cpp b/src/loader/systemContent.cpp index 6db2bae54..db1812702 100644 --- a/src/loader/systemContent.cpp +++ b/src/loader/systemContent.cpp @@ -35,7 +35,7 @@ public: bool GetParamInt(const char* name, int32_t* value); bool GetParamString(const char* name, std::string* value); bool GetParamString(const char* name, char* value, size_t value_size); - uint64_t GetFlexibleMemorySize(); + MemoryParams GetMemoryParams(); void DbgPrint(); @@ -61,7 +61,7 @@ private: Common::Mutex m_mutex; bool m_opened = false; std::vector m_params; - uint64_t m_flexible_memory_size = 0; + MemoryParams m_memory_params; }; class PlayGo { @@ -172,11 +172,21 @@ void Psf::OpenJson(const std::filesystem::path& file_name) { add_int_param("userDefinedParam3", "USER_DEFINED_PARAM_3"); add_int_param("userDefinedParam4", "USER_DEFINED_PARAM_4"); + auto read_size = [](const nlohmann::ordered_json& object, const char* key, uint64_t* size, + uint64_t unit = 1) { + if (auto it = object.find(key); it != object.end()) { + *size = it->get() * unit; + } + }; if (auto kernel = param_json.find("kernel"); kernel != param_json.end() && kernel->is_object()) { - if (auto it = kernel->find("flexibleMemorySize"); it != kernel->end()) { - m_flexible_memory_size = it->get(); - } + read_size(*kernel, "flexibleMemorySize", &m_memory_params.flexible_size); + read_size(*kernel, "cpuPageTableSize", &m_memory_params.cpu_table_memory); + read_size(*kernel, "gpuPageTableSize", &m_memory_params.gpu_table_memory); + } + if (auto amm = param_json.find("amm"); amm != param_json.end() && amm->is_object()) { + read_size(*amm, "pagetableMemorySizeInMib", &m_memory_params.amm_table_memory, + 1024ull * 1024ull); } auto localized = param_json.find("localizedParameters"); @@ -227,8 +237,8 @@ void Psf::Close() { Common::LockGuard lock(m_mutex); m_params.clear(); - m_flexible_memory_size = 0; - m_opened = false; + m_memory_params = {}; + m_opened = false; } bool Psf::IsValid() { @@ -339,10 +349,10 @@ bool Psf::GetParamString(const char* name, char* value, size_t value_size) { return false; } -uint64_t Psf::GetFlexibleMemorySize() { +MemoryParams Psf::GetMemoryParams() { Common::LockGuard lock(m_mutex); - return m_flexible_memory_size; + return m_memory_params; } PlayGo::~PlayGo() { @@ -646,10 +656,10 @@ bool SystemContentParamSfoGetString(const char* name, char* value, size_t value_ return sc->psf.GetParamString(name, value, value_size); } -uint64_t SystemContentGetFlexibleMemorySize() { +MemoryParams SystemContentGetMemoryParams() { auto* sc = Common::Singleton::Instance(); - return sc->psf.GetFlexibleMemorySize(); + return sc->psf.GetMemoryParams(); } bool SystemContentGetIconPath(std::filesystem::path* path) { diff --git a/src/loader/systemContent.h b/src/loader/systemContent.h index 778847641..7407246e2 100644 --- a/src/loader/systemContent.h +++ b/src/loader/systemContent.h @@ -14,7 +14,16 @@ void SystemContentLoadParamSfo(const std::filesystem::path& file_name); bool SystemContentParamSfoGetInt(const char* name, int32_t* value); bool SystemContentParamSfoGetString(const char* name, std::string* value); bool SystemContentParamSfoGetString(const char* name, char* value, size_t value_size); -uint64_t SystemContentGetFlexibleMemorySize(); +// Memory sizes a game's param.json sets, in bytes; 0 where it sets none. +struct MemoryParams { + uint64_t flexible_size = 0; + uint64_t cpu_table_memory = 0; + uint64_t gpu_table_memory = 0; + uint64_t amm_table_memory = 0; +}; + +MemoryParams SystemContentGetMemoryParams(); + bool SystemContentGetIconPath(std::filesystem::path* path); bool SystemContentGetChunksNum(uint32_t* num); diff --git a/tests/VirtualMemoryAllocationTests.cpp b/tests/VirtualMemoryAllocationTests.cpp index 76efd1989..31674b3aa 100644 --- a/tests/VirtualMemoryAllocationTests.cpp +++ b/tests/VirtualMemoryAllocationTests.cpp @@ -88,9 +88,14 @@ using Libs::LibKernel::Memory::VirtualQueryInfo; // Prospero ABI? constexpr uint64_t SceKernelPageSize = 0x4000; -constexpr uint64_t SceKernelTotalPhysicalSize = 13824ull * 1024ull * 1024ull; constexpr uint64_t SceKernelFlexibleMemorySize = 448ull * 1024ull * 1024ull; constexpr uint64_t TestFlexibleMemorySize = 3072ull * 1024ull * 1024ull; +constexpr uint64_t GuestMemoryBudget = 12800ull * 1024ull * 1024ull; +constexpr uint64_t ReservedCacheSize = 64ull * 1024ull * 1024ull; +// The 128 MiB CPU and 96 MiB AMM mapping tables add 64 + 32 MiB; a zero GPU table size adds none. +constexpr uint64_t TestCpuTableMemory = 128ull * 1024ull * 1024ull; +constexpr uint64_t TestAmmTableMemory = 96ull * 1024ull * 1024ull; +constexpr uint64_t TestTableOverhead = 96ull * 1024ull * 1024ull; constexpr int SceKernelProtCpuRead = 0x01; constexpr int SceKernelProtCpuRw = 0x02; constexpr int SceKernelProtCpuExec = 0x04; @@ -175,7 +180,9 @@ void InitSubsystems() { const auto param_json = std::filesystem::temp_directory_path() / ("kyty_virtual_memory_" + std::to_string(reinterpret_cast(&initialized)) + ".json"); - constexpr char json[] = R"({"kernel":{"flexibleMemorySize":3221225472}})"; + constexpr char json[] = + R"({"kernel":{"flexibleMemorySize":3221225472,"cpuPageTableSize":134217728,)" + R"("gpuPageTableSize":0},"amm":{"pagetableMemorySizeInMib":96}})"; Common::File param_file; Check("InitSubsystems", param_file.Create(param_json), "failed to create temporary param.json"); uint32_t bytes_written = 0; @@ -185,16 +192,19 @@ void InitSubsystems() { "failed to write temporary param.json"); Loader::SystemContentLoadParamSfo(param_json); - const auto flexible_memory_size = Loader::SystemContentGetFlexibleMemorySize(); + const auto params = Loader::SystemContentGetMemoryParams(); Check("InitSubsystems", Common::File::DeleteFile(param_json), "failed to remove temporary param.json"); - Check("InitSubsystems", flexible_memory_size == TestFlexibleMemorySize, + Check("InitSubsystems", params.flexible_size == TestFlexibleMemorySize, "failed to read flexible memory size from param.json"); + Check("InitSubsystems", + params.cpu_table_memory == TestCpuTableMemory && params.gpu_table_memory == 0 && + params.amm_table_memory == TestAmmTableMemory, + "failed to read mapping table sizes from param.json"); size_t default_flexible_memory_size = 0; Libs::LibKernel::Memory::KernelConfiguredFlexibleMemorySize(&default_flexible_memory_size); Check("InitSubsystems", default_flexible_memory_size == SceKernelFlexibleMemorySize, "flexible memory does not default to 448 MiB without param.json"); - Libs::LibKernel::Memory::SetFlexibleMemorySize(flexible_memory_size); subsystems.Initialize(); @@ -822,8 +832,8 @@ void TestFlexibleMemoryCapacityIsBootFixed() { Check(test, configured == TestFlexibleMemorySize, "boot flexible pool did not use the param.json value"); Check(test, configured == baseline, "boot flexible pool did not start at configured capacity"); - Check(test, backing == SceKernelTotalPhysicalSize, - "boot backing is not the single 13.5 GiB physical file"); + Check(test, backing == GuestMemoryBudget - ReservedCacheSize - TestTableOverhead, + "boot backing is not the 12.5 GiB budget less the reserved cache and table overhead"); Check(test, backing == Libs::LibKernel::Memory::KernelGetDirectMemorySize() + configured, "direct and flexible regions do not partition the boot backing"); @@ -1409,7 +1419,10 @@ void TestPageTableStatsTrackRangeEdits() { "KernelGetPageTableStats"); return stats; }; - const auto baseline = read_stats(); + // A 4 KiB table maps 2 MiB: 128 MiB of CPU tables gives 32768, the default 64 MiB GPU 16384. + const auto baseline = read_stats(); + Check(test, baseline[0] == 32768 && baseline[2] == 16384, + "page-table totals do not follow the configured table memory"); const auto expect_used = [&](int cpu, int gpu, const char* action) { const auto stats = read_stats(); Check(test,