Merge pull request #14665 from cristian64/simulated_memory_size

Core: Adjust emulated memory size automatically.
This commit is contained in:
Scott Mansell authored and GitHub committed 2026-07-12 11:34:44 +12:00
commit fc9fd6952a
16 files changed
+70 -17

No files matched your search

@@ -400,7 +400,7 @@
<string name="overclock_title_description">Adjusts the emulated CPU\'s clock rate if \"Override Emulated CPU Clock Speed\" is enabled.</string> <string name="overclock_title_description">Adjusts the emulated CPU\'s clock rate if \"Override Emulated CPU Clock Speed\" is enabled.</string>
<string name="memory_override">Memory Override</string> <string name="memory_override">Memory Override</string>
<string name="enable_memory_size_override">Enable Emulated Memory Size Override</string> <string name="enable_memory_size_override">Enable Emulated Memory Size Override</string>
<string name="enable_memory_size_override_description">Adjusts the amount of RAM in the emulated console.\n\nWARNING: Enabling this will completely break many games. Only a small number of games can benefit from this.</string> <string name="enable_memory_size_override_description">Sets the amount of RAM in the emulated console to the values provided.\n\nWARNING: Enabling this will completely break many games. By default, Dolphin determines what value is required based on the game information.</string>
<string name="main_mem1_size">MEM1 Size</string> <string name="main_mem1_size">MEM1 Size</string>
<string name="main_mem2_size">MEM2 Size</string> <string name="main_mem2_size">MEM2 Size</string>
<string name="gpu_options">GPU Options</string> <string name="gpu_options">GPU Options</string>
+18 -7
View File
@@ -154,27 +154,35 @@ u16 SConfig::GetRevision() const
return m_revision; return m_revision;
} }
u32 SConfig::GetSimulatedMemorySize() const
{
std::lock_guard<std::recursive_mutex> lock(m_metadata_lock);
return m_simulated_memory_size;
}
void SConfig::ResetRunningGameMetadata() void SConfig::ResetRunningGameMetadata()
{ {
std::lock_guard<std::recursive_mutex> lock(m_metadata_lock); std::lock_guard<std::recursive_mutex> lock(m_metadata_lock);
SetRunningGameMetadata("00000000", "", 0, 0, DiscIO::Region::Unknown); SetRunningGameMetadata("00000000", "", 0, 0, DiscIO::Region::Unknown, 0);
} }
void SConfig::SetRunningGameMetadata(const DiscIO::Volume& volume, void SConfig::SetRunningGameMetadata(const DiscIO::Volume& volume,
const DiscIO::Partition& partition) const DiscIO::Partition& partition)
{ {
std::lock_guard<std::recursive_mutex> lock(m_metadata_lock); std::lock_guard<std::recursive_mutex> lock(m_metadata_lock);
if (partition == volume.GetGamePartition()) if (partition == volume.GetGamePartition())
{ {
SetRunningGameMetadata(volume.GetGameID(), volume.GetGameTDBID(), SetRunningGameMetadata(volume.GetGameID(), volume.GetGameTDBID(),
volume.GetTitleID().value_or(0), volume.GetRevision().value_or(0), volume.GetTitleID().value_or(0), volume.GetRevision().value_or(0),
volume.GetRegion()); volume.GetRegion(), volume.GetSimulatedMemorySize());
} }
else else
{ {
SetRunningGameMetadata(volume.GetGameID(partition), volume.GetGameTDBID(partition), SetRunningGameMetadata(volume.GetGameID(partition), volume.GetGameTDBID(partition),
volume.GetTitleID(partition).value_or(0), volume.GetTitleID(partition).value_or(0),
volume.GetRevision(partition).value_or(0), volume.GetRegion()); volume.GetRevision(partition).value_or(0), volume.GetRegion(),
volume.GetSimulatedMemorySize());
} }
} }
@@ -192,26 +200,29 @@ void SConfig::SetRunningGameMetadata(const IOS::ES::TMDReader& tmd, DiscIO::Plat
{ {
// If not launching a disc game, just read everything from the TMD. // If not launching a disc game, just read everything from the TMD.
SetRunningGameMetadata(tmd.GetGameID(), tmd.GetGameTDBID(), tmd_title_id, tmd.GetTitleVersion(), SetRunningGameMetadata(tmd.GetGameID(), tmd.GetGameTDBID(), tmd_title_id, tmd.GetTitleVersion(),
tmd.GetRegion()); tmd.GetRegion(), 0);
} }
} }
void SConfig::SetRunningGameMetadata(const std::string& game_id) void SConfig::SetRunningGameMetadata(const std::string& game_id)
{ {
std::lock_guard<std::recursive_mutex> lock(m_metadata_lock); std::lock_guard<std::recursive_mutex> lock(m_metadata_lock);
SetRunningGameMetadata(game_id, "", 0, 0, DiscIO::Region::Unknown); SetRunningGameMetadata(game_id, "", 0, 0, DiscIO::Region::Unknown, 0);
} }
void SConfig::SetRunningGameMetadata(const std::string& game_id, const std::string& gametdb_id, void SConfig::SetRunningGameMetadata(const std::string& game_id, const std::string& gametdb_id,
u64 title_id, u16 revision, DiscIO::Region region) u64 title_id, u16 revision, DiscIO::Region region,
u32 simulated_memory_size)
{ {
std::lock_guard<std::recursive_mutex> lock(m_metadata_lock); std::lock_guard<std::recursive_mutex> lock(m_metadata_lock);
const bool was_changed = m_game_id != game_id || m_gametdb_id != gametdb_id || const bool was_changed = m_game_id != game_id || m_gametdb_id != gametdb_id ||
m_title_id != title_id || m_revision != revision; m_title_id != title_id || m_revision != revision ||
m_simulated_memory_size != simulated_memory_size;
m_game_id = game_id; m_game_id = game_id;
m_gametdb_id = gametdb_id; m_gametdb_id = gametdb_id;
m_title_id = title_id; m_title_id = title_id;
m_revision = revision; m_revision = revision;
m_simulated_memory_size = simulated_memory_size;
if (game_id.length() == 6) if (game_id.length() == 6)
{ {
+5 -1
View File
@@ -65,6 +65,7 @@ struct SConfig
const std::string GetTitleDescription() const; const std::string GetTitleDescription() const;
u64 GetTitleID() const; u64 GetTitleID() const;
u16 GetRevision() const; u16 GetRevision() const;
u32 GetSimulatedMemorySize() const;
void ResetRunningGameMetadata(); void ResetRunningGameMetadata();
void SetRunningGameMetadata(const DiscIO::Volume& volume, const DiscIO::Partition& partition); void SetRunningGameMetadata(const DiscIO::Volume& volume, const DiscIO::Partition& partition);
void SetRunningGameMetadata(const IOS::ES::TMDReader& tmd, DiscIO::Platform platform); void SetRunningGameMetadata(const IOS::ES::TMDReader& tmd, DiscIO::Platform platform);
@@ -119,7 +120,8 @@ private:
static void ReloadTextures(Core::System& system); static void ReloadTextures(Core::System& system);
void SetRunningGameMetadata(const std::string& game_id, const std::string& gametdb_id, void SetRunningGameMetadata(const std::string& game_id, const std::string& gametdb_id,
u64 title_id, u16 revision, DiscIO::Region region); u64 title_id, u16 revision, DiscIO::Region region,
u32 simulated_memory_size);
static SConfig* m_Instance; static SConfig* m_Instance;
mutable std::recursive_mutex m_metadata_lock; mutable std::recursive_mutex m_metadata_lock;
@@ -130,4 +132,6 @@ private:
std::string m_title_description; std::string m_title_description;
u64 m_title_id; u64 m_title_id;
u16 m_revision; u16 m_revision;
// Memory size requested by game's header.
u32 m_simulated_memory_size;
}; };
+3
View File
@@ -354,6 +354,9 @@ void DolphinAnalytics::MakePerGameBuilder()
// Unique id bound to the gameid. // Unique id bound to the gameid.
builder.AddData("id", MakeUniqueId(SConfig::GetInstance().GetGameID())); builder.AddData("id", MakeUniqueId(SConfig::GetInstance().GetGameID()));
// Other game-specific fields.
builder.AddData("simulated-memory-size", SConfig::GetInstance().GetSimulatedMemorySize());
// Configuration. // Configuration.
builder.AddData("cfg-dsp-hle", Config::Get(Config::MAIN_DSP_HLE)); builder.AddData("cfg-dsp-hle", Config::Get(Config::MAIN_DSP_HLE));
builder.AddData("cfg-dsp-jit", Config::Get(Config::MAIN_DSP_JIT)); builder.AddData("cfg-dsp-jit", Config::Get(Config::MAIN_DSP_JIT));
+2 -1
View File
@@ -140,7 +140,8 @@ bool FifoDataFile::Save(const std::string& filename)
header.fileId = FILE_ID; header.fileId = FILE_ID;
header.file_version = VERSION_NUMBER; header.file_version = VERSION_NUMBER;
// Maintain backwards compatibility so long as the RAM sizes aren't overridden. // Maintain backwards compatibility so long as the RAM sizes aren't overridden.
if (Config::Get(Config::MAIN_RAM_OVERRIDE_ENABLE)) if (Config::Get(Config::MAIN_RAM_OVERRIDE_ENABLE) ||
SConfig::GetInstance().GetSimulatedMemorySize() > Memory::MEM1_SIZE_RETAIL)
header.min_loader_version = MIN_LOADER_VERSION_FOR_RAM_OVERRIDE; header.min_loader_version = MIN_LOADER_VERSION_FOR_RAM_OVERRIDE;
else else
header.min_loader_version = MIN_LOADER_VERSION; header.min_loader_version = MIN_LOADER_VERSION;
+11 -2
View File
@@ -25,6 +25,7 @@
#include "Common/MsgHandler.h" #include "Common/MsgHandler.h"
#include "Common/Swap.h" #include "Common/Swap.h"
#include "Core/Config/MainSettings.h" #include "Core/Config/MainSettings.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h" #include "Core/Core.h"
#include "Core/HW/AudioInterface.h" #include "Core/HW/AudioInterface.h"
#include "Core/HW/DSP.h" #include "Core/HW/DSP.h"
@@ -87,10 +88,18 @@ void MemoryManager::InitMMIO(Core::System& system)
void MemoryManager::Init() void MemoryManager::Init()
{ {
const auto get_mem1_size = [] { const auto get_mem1_size = [this] {
if (Config::Get(Config::MAIN_RAM_OVERRIDE_ENABLE)) if (Config::Get(Config::MAIN_RAM_OVERRIDE_ENABLE))
return Config::Get(Config::MAIN_MEM1_SIZE); return Config::Get(Config::MAIN_MEM1_SIZE);
return Memory::MEM1_SIZE_RETAIL; // The simulated memory size in the game's header was originally used to ask the Apploader to
// simulate a smaller memory size than that in development systems. That is, it could make the
// 48 MiB devkit act like a 24 MiB retail console during testing, or any other config.
//
// Dolphin is reusing this as a signal to automatically adjust the emulated memory size. The
// upper bound is clamped to 64 MiB (maximum supported by Flipper's memory controller),
// reflecting Apploader behaviour of clamping the simulated size to physical memory size. It is
// never desired to emulate less than a retail console, so the lower bound is clapped to 24 MiB.
return std::clamp(m_system.GetSimulatedMemorySize(), MEM1_SIZE_RETAIL, MEM1_SIZE_MAX);
}; };
const auto get_mem2_size = [] { const auto get_mem2_size = [] {
if (Config::Get(Config::MAIN_RAM_OVERRIDE_ENABLE)) if (Config::Get(Config::MAIN_RAM_OVERRIDE_ENABLE))
+2
View File
@@ -34,8 +34,10 @@ constexpr u32 MEM1_BASE_ADDR = 0x80000000U;
constexpr u32 MEM2_BASE_ADDR = 0x90000000U; constexpr u32 MEM2_BASE_ADDR = 0x90000000U;
constexpr u32 MEM1_SIZE_RETAIL = 0x01800000U; constexpr u32 MEM1_SIZE_RETAIL = 0x01800000U;
constexpr u32 MEM1_SIZE_GDEV = 0x04000000U; constexpr u32 MEM1_SIZE_GDEV = 0x04000000U;
constexpr u32 MEM1_SIZE_MAX = 0x04000000U;
constexpr u32 MEM2_SIZE_RETAIL = 0x04000000U; constexpr u32 MEM2_SIZE_RETAIL = 0x04000000U;
constexpr u32 MEM2_SIZE_NDEV = 0x08000000U; constexpr u32 MEM2_SIZE_NDEV = 0x08000000U;
constexpr u32 MEM2_SIZE_MAX = 0x08000000U;
struct PhysicalMemoryRegion struct PhysicalMemoryRegion
{ {
+4 -1
View File
@@ -221,8 +221,11 @@ static void ReleasePPCAncast(Core::System& system)
void RAMOverrideForIOSMemoryValues(Memory::MemoryManager& memory, MemorySetupType setup_type) void RAMOverrideForIOSMemoryValues(Memory::MemoryManager& memory, MemorySetupType setup_type)
{ {
// Don't touch anything if the feature isn't enabled. // Don't touch anything if the feature isn't enabled.
if (!Config::Get(Config::MAIN_RAM_OVERRIDE_ENABLE)) if (!Config::Get(Config::MAIN_RAM_OVERRIDE_ENABLE) ||
SConfig::GetInstance().GetSimulatedMemorySize() <= Memory::MEM1_SIZE_RETAIL)
{
return; return;
}
// Some unstated constants that can be inferred. // Some unstated constants that can be inferred.
const u32 ipc_buffer_size = const u32 ipc_buffer_size =
+1
View File
@@ -119,6 +119,7 @@ void System::Initialize()
m_separate_cpu_and_gpu_threads = Config::Get(Config::MAIN_CPU_THREAD); m_separate_cpu_and_gpu_threads = Config::Get(Config::MAIN_CPU_THREAD);
m_mmu_enabled = Config::Get(Config::MAIN_MMU); m_mmu_enabled = Config::Get(Config::MAIN_MMU);
m_pause_on_panic_enabled = Config::Get(Config::MAIN_PAUSE_ON_PANIC); m_pause_on_panic_enabled = Config::Get(Config::MAIN_PAUSE_ON_PANIC);
m_simulated_memory_size = SConfig::GetInstance().GetSimulatedMemorySize();
} }
SoundStream* System::GetSoundStream() const SoundStream* System::GetSoundStream() const
+4
View File
@@ -5,6 +5,8 @@
#include <memory> #include <memory>
#include "Common/CommonTypes.h"
class GeometryShaderManager; class GeometryShaderManager;
class Interpreter; class Interpreter;
class JitInterface; class JitInterface;
@@ -147,6 +149,7 @@ public:
bool IsTriforce() const { return m_is_triforce; } bool IsTriforce() const { return m_is_triforce; }
bool IsWii() const { return m_is_wii; } bool IsWii() const { return m_is_wii; }
bool IsBranchWatchIgnoreApploader() { return m_branch_watch_ignore_apploader; } bool IsBranchWatchIgnoreApploader() { return m_branch_watch_ignore_apploader; }
u32 GetSimulatedMemorySize() const { return m_simulated_memory_size; }
void SetIsMIOS(bool is_mios) { m_is_mios = is_mios; } void SetIsMIOS(bool is_mios) { m_is_mios = is_mios; }
void SetIsTriforce(bool is_triforce) { m_is_triforce = is_triforce; } void SetIsTriforce(bool is_triforce) { m_is_triforce = is_triforce; }
@@ -216,5 +219,6 @@ private:
bool m_is_triforce = false; bool m_is_triforce = false;
bool m_is_wii = false; bool m_is_wii = false;
bool m_branch_watch_ignore_apploader = false; bool m_branch_watch_ignore_apploader = false;
u32 m_simulated_memory_size{0};
}; };
} // namespace Core } // namespace Core
+1
View File
@@ -126,6 +126,7 @@ public:
} }
virtual Region GetRegion() const = 0; virtual Region GetRegion() const = 0;
virtual Country GetCountry(const Partition& partition = PARTITION_NONE) const = 0; virtual Country GetCountry(const Partition& partition = PARTITION_NONE) const = 0;
virtual u32 GetSimulatedMemorySize() const = 0;
virtual BlobType GetBlobType() const = 0; virtual BlobType GetBlobType() const = 0;
// Size of virtual disc (may be inaccurate depending on the blob type) // Size of virtual disc (may be inaccurate depending on the blob type)
virtual u64 GetDataSize() const = 0; virtual u64 GetDataSize() const = 0;
+8
View File
@@ -179,6 +179,14 @@ std::optional<u8> VolumeDisc::GetDiscNumber(const Partition& partition) const
return ReadSwapped<u8>(6, partition); return ReadSwapped<u8>(6, partition);
} }
u32 VolumeDisc::GetSimulatedMemorySize() const
{
// Simulated memory size is a 32-bit unsigned integer in the `bi2.bin` file at offset `0x04`.
// Apploader reads this and "simulates" a smaller memory size. Originally useful for simulating
// 24 MiB of memory on devkits with 48 MiB.
return ReadSwapped<u32>(BI2_ADDRESS + 0x04, GetGamePartition()).value_or(0);
}
bool VolumeDisc::IsNKit() const bool VolumeDisc::IsNKit() const
{ {
constexpr u32 NKIT_MAGIC = 0x4E4B4954; // "NKIT" constexpr u32 NKIT_MAGIC = 0x4E4B4954; // "NKIT"
+1
View File
@@ -47,6 +47,7 @@ public:
std::string GetInternalName(const Partition& partition = PARTITION_NONE) const override; std::string GetInternalName(const Partition& partition = PARTITION_NONE) const override;
std::string GetApploaderDate(const Partition& partition) const override; std::string GetApploaderDate(const Partition& partition) const override;
std::optional<u8> GetDiscNumber(const Partition& partition = PARTITION_NONE) const override; std::optional<u8> GetDiscNumber(const Partition& partition = PARTITION_NONE) const override;
u32 GetSimulatedMemorySize() const override;
bool IsNKit() const override; bool IsNKit() const override;
protected: protected:
+1
View File
@@ -61,6 +61,7 @@ public:
bool IsNKit() const override; bool IsNKit() const override;
Region GetRegion() const override; Region GetRegion() const override;
Country GetCountry(const Partition& partition = PARTITION_NONE) const override; Country GetCountry(const Partition& partition = PARTITION_NONE) const override;
u32 GetSimulatedMemorySize() const override { return 0; }
BlobType GetBlobType() const override; BlobType GetBlobType() const override;
u64 GetDataSize() const override; u64 GetDataSize() const override;
@@ -276,10 +276,9 @@ void AdvancedPane::CreateLayout()
}); });
m_ram_override_checkbox->SetDescription( m_ram_override_checkbox->SetDescription(
tr("Adjusts the amount of RAM in the emulated console.<br><br>" tr("Sets the amount of RAM in the emulated console to the values provided.<br><br>"
"<b>WARNING</b>: Enabling this will completely break many games.<br>Only a small " "<b>WARNING</b>: Enabling this will completely break many games. By default, Dolphin "
"number " "determines what value is required based on the game information."
"of games can benefit from this."
"<br><br><dolphin_emphasis>If unsure, leave this unchecked.</dolphin_emphasis>")); "<br><br><dolphin_emphasis>If unsure, leave this unchecked.</dolphin_emphasis>"));
auto* rtc_options = new QGroupBox(tr("Custom RTC Options")); auto* rtc_options = new QGroupBox(tr("Custom RTC Options"));
@@ -10,6 +10,7 @@
#include "Common/Align.h" #include "Common/Align.h"
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/Swap.h" #include "Common/Swap.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h" #include "Core/Core.h"
#include "Core/MemTools.h" #include "Core/MemTools.h"
#include "Core/PowerPC/BreakPoints.h" #include "Core/PowerPC/BreakPoints.h"
@@ -131,6 +132,8 @@ public:
if (!EMM::IsExceptionHandlerSupported()) if (!EMM::IsExceptionHandlerSupported())
GTEST_SKIP() << "Skipping PageTableHostMappingTest because exception handler is unsupported."; GTEST_SKIP() << "Skipping PageTableHostMappingTest because exception handler is unsupported.";
SConfig::Init();
auto& system = Core::System::GetInstance(); auto& system = Core::System::GetInstance();
auto& memory = system.GetMemory(); auto& memory = system.GetMemory();
const u32 host_page_size = memory.GetHostPageSize(); const u32 host_page_size = memory.GetHostPageSize();
@@ -177,6 +180,8 @@ public:
EMM::UninstallExceptionHandler(); EMM::UninstallExceptionHandler();
Core::UndeclareAsCPUThread(); Core::UndeclareAsCPUThread();
system.GetMemory().Shutdown(); system.GetMemory().Shutdown();
SConfig::Shutdown();
} }
static void SetSR(size_t index, u32 vsid) static void SetSR(size_t index, u32 vsid)