System: Add advanced 'Export Shared Memory' option
Memory map is exported as duckstation_<pid>. Previously, this only worked on Windows, now it is extended to Linux as well.
This commit is contained in:
123
src/core/bus.cpp
123
src/core/bus.cpp
@@ -118,6 +118,7 @@ union RAM_SIZE_REG
|
||||
} // namespace
|
||||
|
||||
static void* s_shmem_handle = nullptr;
|
||||
static std::string s_shmem_name;
|
||||
|
||||
std::bitset<RAM_8MB_CODE_PAGE_COUNT> g_ram_code_bits{};
|
||||
u8* g_ram = nullptr;
|
||||
@@ -153,6 +154,8 @@ static u8** s_fastmem_lut = nullptr;
|
||||
|
||||
static bool s_kernel_initialize_hook_run = false;
|
||||
|
||||
static bool AllocateMemoryMap(Error* error);
|
||||
static void ReleaseMemoryMap();
|
||||
static void SetRAMSize(bool enable_8mb_ram);
|
||||
|
||||
static std::tuple<TickCount, TickCount, TickCount> CalculateMemoryTiming(MEMDELAY mem_delay, COMDELAY common_delay);
|
||||
@@ -194,10 +197,12 @@ static constexpr size_t TOTAL_SIZE = LUT_OFFSET + LUT_SIZE;
|
||||
#define FIXUP_WORD_WRITE_VALUE(size, offset, value) \
|
||||
((size == MemoryAccessSize::Word) ? (value) : ((value) << (((offset) & 3u) * 8)))
|
||||
|
||||
bool Bus::AllocateMemory(Error* error)
|
||||
bool Bus::AllocateMemoryMap(Error* error)
|
||||
{
|
||||
s_shmem_handle =
|
||||
MemMap::CreateSharedMemory(MemMap::GetFileMappingName("duckstation").c_str(), MemoryMap::TOTAL_SIZE, error);
|
||||
// This executes super early in process startup, therefore export_shared_memory will always be false.
|
||||
if (g_settings.export_shared_memory)
|
||||
s_shmem_name = MemMap::GetFileMappingName("duckstation");
|
||||
s_shmem_handle = MemMap::CreateSharedMemory(s_shmem_name.c_str(), MemoryMap::TOTAL_SIZE, error);
|
||||
if (!s_shmem_handle)
|
||||
{
|
||||
#ifndef __linux__
|
||||
@@ -216,7 +221,7 @@ bool Bus::AllocateMemory(Error* error)
|
||||
if (!g_ram || !g_unprotected_ram)
|
||||
{
|
||||
Error::SetStringView(error, "Failed to map memory for RAM");
|
||||
ReleaseMemory();
|
||||
ReleaseMemoryMap();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -227,7 +232,7 @@ bool Bus::AllocateMemory(Error* error)
|
||||
if (!g_bios)
|
||||
{
|
||||
Error::SetStringView(error, "Failed to map memory for BIOS");
|
||||
ReleaseMemory();
|
||||
ReleaseMemoryMap();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -238,7 +243,7 @@ bool Bus::AllocateMemory(Error* error)
|
||||
if (!g_memory_handlers)
|
||||
{
|
||||
Error::SetStringView(error, "Failed to map memory for LUTs");
|
||||
ReleaseMemory();
|
||||
ReleaseMemoryMap();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -246,17 +251,6 @@ bool Bus::AllocateMemory(Error* error)
|
||||
g_memory_handlers_isc = g_memory_handlers + MEMORY_LUT_SLOTS;
|
||||
SetHandlers();
|
||||
|
||||
#ifdef ENABLE_MMAP_FASTMEM
|
||||
if (!s_fastmem_arena.Create(FASTMEM_ARENA_SIZE))
|
||||
{
|
||||
Error::SetStringView(error, "Failed to create fastmem arena");
|
||||
ReleaseMemory();
|
||||
return false;
|
||||
}
|
||||
|
||||
INFO_LOG("Fastmem base: {}", static_cast<void*>(s_fastmem_arena.BasePointer()));
|
||||
#endif
|
||||
|
||||
#ifndef __ANDROID__
|
||||
Exports::RAM = reinterpret_cast<uintptr_t>(g_unprotected_ram);
|
||||
#endif
|
||||
@@ -264,7 +258,7 @@ bool Bus::AllocateMemory(Error* error)
|
||||
return true;
|
||||
}
|
||||
|
||||
void Bus::ReleaseMemory()
|
||||
void Bus::ReleaseMemoryMap()
|
||||
{
|
||||
#ifndef __ANDROID__
|
||||
Exports::RAM = 0;
|
||||
@@ -272,14 +266,6 @@ void Bus::ReleaseMemory()
|
||||
Exports::RAM_MASK = 0;
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_MMAP_FASTMEM
|
||||
DebugAssert(s_fastmem_ram_views.empty());
|
||||
s_fastmem_arena.Destroy();
|
||||
#endif
|
||||
|
||||
std::free(s_fastmem_lut);
|
||||
s_fastmem_lut = nullptr;
|
||||
|
||||
g_memory_handlers_isc = nullptr;
|
||||
if (g_memory_handlers)
|
||||
{
|
||||
@@ -309,9 +295,88 @@ void Bus::ReleaseMemory()
|
||||
{
|
||||
MemMap::DestroySharedMemory(s_shmem_handle);
|
||||
s_shmem_handle = nullptr;
|
||||
|
||||
if (!s_shmem_name.empty())
|
||||
{
|
||||
MemMap::DeleteSharedMemory(s_shmem_name.c_str());
|
||||
s_shmem_name = {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Bus::AllocateMemory(Error* error)
|
||||
{
|
||||
if (!AllocateMemoryMap(error))
|
||||
return false;
|
||||
|
||||
#ifdef ENABLE_MMAP_FASTMEM
|
||||
if (!s_fastmem_arena.Create(FASTMEM_ARENA_SIZE))
|
||||
{
|
||||
Error::SetStringView(error, "Failed to create fastmem arena");
|
||||
ReleaseMemory();
|
||||
return false;
|
||||
}
|
||||
|
||||
INFO_LOG("Fastmem base: {}", static_cast<void*>(s_fastmem_arena.BasePointer()));
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Bus::ReleaseMemory()
|
||||
{
|
||||
#ifdef ENABLE_MMAP_FASTMEM
|
||||
DebugAssert(s_fastmem_ram_views.empty());
|
||||
s_fastmem_arena.Destroy();
|
||||
#endif
|
||||
|
||||
std::free(s_fastmem_lut);
|
||||
s_fastmem_lut = nullptr;
|
||||
|
||||
ReleaseMemoryMap();
|
||||
}
|
||||
|
||||
bool Bus::ReallocateMemoryMap(Error* error)
|
||||
{
|
||||
// Need to back up RAM+BIOS.
|
||||
DynamicHeapArray<u8> ram_backup;
|
||||
DynamicHeapArray<u8> bios_backup;
|
||||
|
||||
if (System::IsValid())
|
||||
{
|
||||
CPU::CodeCache::InvalidateAllRAMBlocks();
|
||||
UpdateFastmemViews(CPUFastmemMode::Disabled);
|
||||
|
||||
ram_backup.resize(RAM_8MB_SIZE);
|
||||
std::memcpy(ram_backup.data(), g_unprotected_ram, RAM_8MB_SIZE);
|
||||
bios_backup.resize(BIOS_SIZE);
|
||||
std::memcpy(bios_backup.data(), g_bios, BIOS_SIZE);
|
||||
}
|
||||
|
||||
ReleaseMemoryMap();
|
||||
if (!AllocateMemoryMap(error)) [[unlikely]]
|
||||
return false;
|
||||
|
||||
if (System::IsValid())
|
||||
{
|
||||
UpdateMappedRAMSize();
|
||||
std::memcpy(g_unprotected_ram, ram_backup.data(), RAM_8MB_SIZE);
|
||||
std::memcpy(g_bios, bios_backup.data(), BIOS_SIZE);
|
||||
UpdateFastmemViews(g_settings.cpu_fastmem_mode);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Bus::CleanupMemoryMap()
|
||||
{
|
||||
#if !defined(_WIN32) && !defined(__ANDROID__)
|
||||
// This is only needed on Linux.
|
||||
if (!s_shmem_name.empty())
|
||||
MemMap::DeleteSharedMemory(s_shmem_name.c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Bus::Initialize()
|
||||
{
|
||||
SetRAMSize(g_settings.enable_8mb_ram);
|
||||
@@ -337,12 +402,6 @@ void Bus::Shutdown()
|
||||
|
||||
g_ram_mask = 0;
|
||||
g_ram_size = 0;
|
||||
|
||||
#ifndef __ANDROID__
|
||||
Exports::RAM = 0;
|
||||
Exports::RAM_SIZE = 0;
|
||||
Exports::RAM_MASK = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void Bus::Reset()
|
||||
|
||||
@@ -116,6 +116,14 @@ static constexpr size_t FASTMEM_ARENA_SIZE = UINT64_C(0x100000000);
|
||||
bool AllocateMemory(Error* error);
|
||||
void ReleaseMemory();
|
||||
|
||||
/// Frees and re-allocates the memory map for the process.
|
||||
/// This should be called when shared memory exports are enabled.
|
||||
bool ReallocateMemoryMap(Error* error);
|
||||
|
||||
/// Cleans up/deletes the shared memory object for this process.
|
||||
/// Should be called when the process crashes, to avoid leaking.
|
||||
void CleanupMemoryMap();
|
||||
|
||||
bool Initialize();
|
||||
void Shutdown();
|
||||
void Reset();
|
||||
|
||||
@@ -337,6 +337,7 @@ void Settings::Load(SettingsInterface& si)
|
||||
audio_output_muted = si.GetBoolValue("Audio", "OutputMuted", false);
|
||||
|
||||
use_old_mdec_routines = si.GetBoolValue("Hacks", "UseOldMDECRoutines", false);
|
||||
export_shared_memory = si.GetBoolValue("Hacks", "ExportSharedMemory", false);
|
||||
pcdrv_enable = si.GetBoolValue("PCDrv", "Enabled", false);
|
||||
pcdrv_enable_writes = si.GetBoolValue("PCDrv", "EnableWrites", false);
|
||||
pcdrv_root = si.GetStringValue("PCDrv", "Root");
|
||||
@@ -597,6 +598,7 @@ void Settings::Save(SettingsInterface& si, bool ignore_base) const
|
||||
si.SetBoolValue("Audio", "OutputMuted", audio_output_muted);
|
||||
|
||||
si.SetBoolValue("Hacks", "UseOldMDECRoutines", use_old_mdec_routines);
|
||||
si.SetBoolValue("Hacks", "ExportSharedMemory", export_shared_memory);
|
||||
|
||||
if (!ignore_base)
|
||||
{
|
||||
|
||||
@@ -199,6 +199,7 @@ struct Settings
|
||||
|
||||
bool use_old_mdec_routines : 1 = false;
|
||||
bool pcdrv_enable : 1 = false;
|
||||
bool export_shared_memory : 1 = false;
|
||||
|
||||
// timing hacks section
|
||||
TickCount dma_max_slice_ticks = DEFAULT_DMA_MAX_SLICE_TICKS;
|
||||
|
||||
@@ -128,6 +128,9 @@ static bool SaveUndoLoadState();
|
||||
static void WarnAboutUnsafeSettings();
|
||||
static void LogUnsafeSettingsToConsole(const SmallStringBase& messages);
|
||||
|
||||
/// Checks for settings changes, std::move() the old settings away for comparing beforehand.
|
||||
static void CheckForSettingsChanges(const Settings& old_settings);
|
||||
|
||||
/// Throttles the system, i.e. sleeps until it's time to execute the next frame.
|
||||
static void Throttle(Common::Timer::Value current_time);
|
||||
static void UpdatePerformanceCounters();
|
||||
@@ -436,6 +439,13 @@ bool System::Internal::CPUThreadInitialize(Error* error)
|
||||
// This will call back to Host::LoadSettings() -> ReloadSources().
|
||||
LoadSettings(false);
|
||||
|
||||
// Yuckity yuck. We have to test for memory export explicitly, because the allocation happens before config load.
|
||||
if (g_settings.export_shared_memory && !Bus::ReallocateMemoryMap(error))
|
||||
{
|
||||
Error::AddPrefix(error, "Failed to reallocate memory map:\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_RAINTEGRATION
|
||||
if (Host::GetBaseBoolSettingValue("Cheevos", "UseRAIntegration", false))
|
||||
Achievements::SwitchToRAIntegration();
|
||||
@@ -996,19 +1006,13 @@ System::GameHash System::GetGameHashFromBuffer(std::string_view exe_name, std::s
|
||||
DiscRegion System::GetRegionForSerial(const std::string_view serial)
|
||||
{
|
||||
static constexpr const std::pair<const char*, DiscRegion> region_prefixes[] = {
|
||||
{"sces", DiscRegion::PAL},
|
||||
{"sced", DiscRegion::PAL},
|
||||
{"sles", DiscRegion::PAL},
|
||||
{"sces", DiscRegion::PAL}, {"sced", DiscRegion::PAL}, {"sles", DiscRegion::PAL},
|
||||
{"sled", DiscRegion::PAL},
|
||||
|
||||
{"scps", DiscRegion::NTSC_J},
|
||||
{"slps", DiscRegion::NTSC_J},
|
||||
{"slpm", DiscRegion::NTSC_J},
|
||||
{"sczs", DiscRegion::NTSC_J},
|
||||
{"papx", DiscRegion::NTSC_J},
|
||||
{"scps", DiscRegion::NTSC_J}, {"slps", DiscRegion::NTSC_J}, {"slpm", DiscRegion::NTSC_J},
|
||||
{"sczs", DiscRegion::NTSC_J}, {"papx", DiscRegion::NTSC_J},
|
||||
|
||||
{"scus", DiscRegion::NTSC_U},
|
||||
{"slus", DiscRegion::NTSC_U},
|
||||
{"scus", DiscRegion::NTSC_U}, {"slus", DiscRegion::NTSC_U},
|
||||
};
|
||||
|
||||
for (const auto& [prefix, region] : region_prefixes)
|
||||
@@ -4360,6 +4364,16 @@ void System::CheckForSettingsChanges(const Settings& old_settings)
|
||||
}
|
||||
#endif
|
||||
|
||||
if (g_settings.export_shared_memory != old_settings.export_shared_memory) [[unlikely]]
|
||||
{
|
||||
Error error;
|
||||
if (!Bus::ReallocateMemoryMap(&error)) [[unlikely]]
|
||||
{
|
||||
ERROR_LOG(error.GetDescription());
|
||||
Panic("Failed to reallocate memory map. The log may contain more information.");
|
||||
}
|
||||
}
|
||||
|
||||
if (g_settings.log_level != old_settings.log_level || g_settings.log_filter != old_settings.log_filter ||
|
||||
g_settings.log_timestamps != old_settings.log_timestamps ||
|
||||
g_settings.log_to_console != old_settings.log_to_console ||
|
||||
|
||||
@@ -341,9 +341,6 @@ void ApplyCheatCode(const CheatCode& code);
|
||||
/// Sets or clears the provided cheat list, applying every frame.
|
||||
void SetCheatList(std::unique_ptr<CheatList> cheats);
|
||||
|
||||
/// Checks for settings changes, std::move() the old settings away for comparing beforehand.
|
||||
void CheckForSettingsChanges(const Settings& old_settings);
|
||||
|
||||
/// Updates throttler.
|
||||
void UpdateSpeedLimiterState();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user