System: Simplify memory allocation

This commit is contained in:
Stenzek 2023-10-10 00:16:15 +10:00 committed by Connor McLaughlin
parent 377746f155
commit 606cbb3883
38 changed files with 446 additions and 1161 deletions

View File

@ -187,11 +187,6 @@ private:
#endif
};
// Safe version of Munmap -- NULLs the pointer variable immediately after free'ing it.
#define SafeSysMunmap(ptr, size) \
((void)(HostSys::Munmap(ptr, size), (ptr) = 0))
extern u64 GetTickFrequency();
extern u64 GetCPUTicks();
extern u64 GetPhysicalMemory();

View File

@ -24,6 +24,8 @@
#include "pcsx2/GameList.h"
#include "pcsx2/Patch.h"
#include "common/Assertions.h"
#include <algorithm>
GamePatchDetailsWidget::GamePatchDetailsWidget(std::string name, const std::string& author,

View File

@ -140,7 +140,6 @@ set(pcsx2Sources
Vif_Codes.cpp
Vif_Transfer.cpp
Vif_Unpack.cpp
VirtualMemory.cpp
VMManager.cpp
vtlb.cpp
VU0.cpp
@ -217,7 +216,6 @@ set(pcsx2Headers
Vif_Dma.h
Vif.h
Vif_Unpack.h
VirtualMemory.h
VMManager.h
vtlb.h
VUflags.h

View File

@ -15,6 +15,7 @@
#pragma once
#include "common/Assertions.h"
#include "common/StringUtil.h"
// Useful enums for some of the fields.

View File

@ -17,39 +17,33 @@
#include "GS/Renderers/Common/GSFunctionMap.h"
#include "System.h"
static GSCodeReserve s_instance;
GSCodeReserve::GSCodeReserve()
: RecompiledCodeReserve("GS Software Renderer")
namespace GSCodeReserve
{
static u8* s_memory_base;
static u8* s_memory_end;
static u8* s_memory_ptr;
}
GSCodeReserve::~GSCodeReserve() = default;
GSCodeReserve& GSCodeReserve::GetInstance()
void GSCodeReserve::ResetMemory()
{
return s_instance;
s_memory_base = SysMemory::GetSWRec();
s_memory_end = SysMemory::GetSWRecEnd();
s_memory_ptr = s_memory_base;
}
void GSCodeReserve::Assign(VirtualMemoryManagerPtr allocator)
size_t GSCodeReserve::GetMemoryUsed()
{
RecompiledCodeReserve::Assign(std::move(allocator), HostMemoryMap::SWrecOffset, HostMemoryMap::SWrecSize);
return s_memory_ptr - s_memory_base;
}
void GSCodeReserve::Reset()
u8* GSCodeReserve::ReserveMemory(size_t size)
{
RecompiledCodeReserve::Reset();
m_memory_used = 0;
pxAssert((s_memory_ptr + size) <= s_memory_end);
return s_memory_ptr;
}
u8* GSCodeReserve::Reserve(size_t size)
void GSCodeReserve::CommitMemory(size_t size)
{
pxAssert((m_memory_used + size) <= m_size);
return m_baseptr + m_memory_used;
}
void GSCodeReserve::Commit(size_t size)
{
pxAssert((m_memory_used + size) <= m_size);
m_memory_used += size;
pxAssert((s_memory_ptr + size) <= s_memory_end);
s_memory_ptr += size;
}

View File

@ -17,7 +17,7 @@
#include "GS/GSExtra.h"
#include "GS/Renderers/SW/GSScanlineEnvironment.h"
#include "VirtualMemory.h"
#include "System.h"
#include "common/emitter/tools.h"
template <class KEY, class VALUE>
@ -147,25 +147,15 @@ public:
// --------------------------------------------------------------------------------------
// Stores code buffers for the GS software JIT.
//
class GSCodeReserve : public RecompiledCodeReserve
namespace GSCodeReserve
{
public:
GSCodeReserve();
~GSCodeReserve();
void ResetMemory();
static GSCodeReserve& GetInstance();
size_t GetMemoryUsed();
size_t GetMemoryUsed() const { return m_memory_used; }
void Assign(VirtualMemoryManagerPtr allocator);
void Reset();
u8* Reserve(size_t size);
void Commit(size_t size);
private:
size_t m_memory_used = 0;
};
u8* ReserveMemory(size_t size);
void CommitMemory(size_t size);
}
template <class CG, class KEY, class VALUE>
class GSCodeGeneratorFunctionMap : public GSFunctionMap<KEY, VALUE>
@ -200,7 +190,7 @@ public:
}
else
{
u8* code_ptr = GSCodeReserve::GetInstance().Reserve(MAX_SIZE);
u8* code_ptr = GSCodeReserve::ReserveMemory(MAX_SIZE);
CG cg(key, code_ptr, MAX_SIZE);
ASSERT(cg.getSize() < MAX_SIZE);
@ -210,7 +200,7 @@ public:
sel.Print();
#endif
GSCodeReserve::GetInstance().Commit(cg.getSize());
GSCodeReserve::CommitMemory(cg.getSize());
ret = (VALUE)cg.getCode();

View File

@ -38,12 +38,12 @@ GSDrawScanline::GSDrawScanline()
: m_sp_map("GSSetupPrim")
, m_ds_map("GSDrawScanline")
{
GSCodeReserve::GetInstance().Reset();
GSCodeReserve::ResetMemory();
}
GSDrawScanline::~GSDrawScanline()
{
if (const size_t used = GSCodeReserve::GetInstance().GetMemoryUsed(); used > 0)
if (const size_t used = GSCodeReserve::GetMemoryUsed(); used > 0)
DevCon.WriteLn("SW JIT generated %zu bytes of code", used);
}
@ -82,7 +82,7 @@ void GSDrawScanline::ResetCodeCache()
Console.Warning("GS Software JIT cache overflow, resetting.");
m_sp_map.Clear();
m_ds_map.Clear();
GSCodeReserve::GetInstance().Reset();
GSCodeReserve::ResetMemory();
}
bool GSDrawScanline::SetupDraw(GSRasterizerData& data)

View File

@ -20,7 +20,6 @@
#include "Hardware.h"
#include "SPU2/spu2.h"
#include "USB/USB.h"
#include "x86/newVif.h"
#include "common/WrappedMemCopy.h"
@ -31,35 +30,8 @@ using namespace R5900;
const int rdram_devices = 2; // put 8 for TOOL and 2 for PS2 and PSX
int rdram_sdevid = 0;
static bool hwInitialized = false;
void hwInit()
{
// [TODO] / FIXME: PCSX2 no longer works on an Init system. It assumes that the
// static global vars for the process will be initialized when the process is created, and
// then issues *resets only* from then on. (reset code for various S2 components should do
// NULL checks and allocate memory and such if the pointers are NULL only).
if( hwInitialized ) return;
VifUnpackSSE_Init();
hwInitialized = true;
}
void hwShutdown()
{
if (!hwInitialized) return;
VifUnpackSSE_Destroy();
hwInitialized = false;
}
void hwReset()
{
hwInit();
std::memset(eeHw, 0, sizeof(eeHw));
psHu32(SBUS_F260) = 0x1D000060;

View File

@ -364,7 +364,6 @@ enum GSRegisterAddresses
};
extern void hwReset();
extern void hwShutdown();
extern const int rdram_devices;
extern int rdram_sdevid;

View File

@ -30,35 +30,20 @@ IopVM_MemoryAllocMess* iopMem = NULL;
alignas(__pagesize) u8 iopHw[Ps2MemSize::IopHardware];
// --------------------------------------------------------------------------------------
// iopMemoryReserve
// --------------------------------------------------------------------------------------
iopMemoryReserve::iopMemoryReserve()
: _parent("IOP Main Memory (2mb)")
{
}
iopMemoryReserve::~iopMemoryReserve()
{
Release();
}
void iopMemoryReserve::Assign(VirtualMemoryManagerPtr allocator)
void iopMemAlloc()
{
// TODO: Move to memmap
psxMemWLUT = (uptr*)_aligned_malloc(0x2000 * sizeof(uptr) * 2, 16);
if (!psxMemWLUT)
pxFailRel("Failed to allocate IOP memory lookup table");
psxMemRLUT = psxMemWLUT + 0x2000; //(uptr*)_aligned_malloc(0x10000 * sizeof(uptr),16);
VtlbMemoryReserve::Assign(std::move(allocator), HostMemoryMap::IOPmemOffset, sizeof(*iopMem));
iopMem = reinterpret_cast<IopVM_MemoryAllocMess*>(GetPtr());
iopMem = reinterpret_cast<IopVM_MemoryAllocMess*>(SysMemory::GetCodePtr(HostMemoryMap::IOPmemOffset));
}
void iopMemoryReserve::Release()
void iopMemRelease()
{
_parent::Release();
safe_aligned_free(psxMemWLUT);
psxMemRLUT = nullptr;
iopMem = nullptr;
@ -66,10 +51,8 @@ void iopMemoryReserve::Release()
// Note! Resetting the IOP's memory state is dependent on having *all* psx memory allocated,
// which is performed by MemInit and PsxMemInit()
void iopMemoryReserve::Reset()
void iopMemReset()
{
_parent::Reset();
pxAssert( iopMem );
DbgCon.WriteLn("IOP resetting main memory...");

View File

@ -71,10 +71,9 @@ static __fi u8* iopPhysMem( u32 addr )
#define psxHu16(mem) (*(u16*)&iopHw[(mem) & 0xffff])
#define psxHu32(mem) (*(u32*)&iopHw[(mem) & 0xffff])
extern void psxMemReserve();
extern void psxMemAlloc();
extern void psxMemReset();
extern void psxMemShutdown();
extern void iopMemAlloc();
extern void iopMemReset();
extern void iopMemRelease();
extern u8 iopMemRead8 (u32 mem);
extern u16 iopMemRead16(u32 mem);

View File

@ -701,28 +701,13 @@ void memBindConditionalHandlers()
// --------------------------------------------------------------------------------------
// eeMemoryReserve (implementations)
// --------------------------------------------------------------------------------------
eeMemoryReserve::eeMemoryReserve()
: _parent("EE Main Memory")
void memAllocate()
{
eeMem = reinterpret_cast<EEVM_MemoryAllocMess*>(SysMemory::GetEEMem());
}
eeMemoryReserve::~eeMemoryReserve()
void memReset()
{
Release();
}
void eeMemoryReserve::Assign(VirtualMemoryManagerPtr allocator)
{
_parent::Assign(std::move(allocator), HostMemoryMap::EEmemOffset, sizeof(*eeMem));
eeMem = reinterpret_cast<EEVM_MemoryAllocMess*>(GetPtr());
}
// Resets memory mappings, unmaps TLBs, reloads bios roms, etc.
void eeMemoryReserve::Reset()
{
_parent::Reset();
// Note!! Ideally the vtlb should only be initialized once, and then subsequent
// resets of the system hardware would only clear vtlb mappings, but since the
// rest of the emu is not really set up to support a "soft" reset of that sort
@ -842,8 +827,7 @@ void eeMemoryReserve::Reset()
CopyBIOSToMemory();
}
void eeMemoryReserve::Release()
void memRelease()
{
eeMem = nullptr;
_parent::Release();
}

View File

@ -97,6 +97,9 @@ static __fi void ZeroQWC( u128& dest )
#define psSu64(mem) (*(u64 *)&eeMem->Scratch[(mem) & 0x3fff])
#define psSu128(mem) (*(u128*)&eeMem->Scratch[(mem) & 0x3fff])
extern void memAllocate();
extern void memReset();
extern void memRelease();
extern void memSetKernelMode();
//extern void memSetSupervisorMode();

View File

@ -1,5 +1,5 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2010 PCSX2 Dev Team
* Copyright (C) 2002-2023 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
@ -18,18 +18,19 @@
namespace Ps2MemSize
{
static const uint MainRam = _32mb; // 32 MB main memory!
static const uint Rom = _1mb * 4; // 4 MB main rom
static const uint Rom1 = _1mb * 4; // DVD player
static const uint Rom2 = 0x00080000; // Chinese rom extension
static const uint Hardware = _64kb;
static const uint Scratch = _16kb;
static constexpr u32 MainRam = _32mb; // 32 MB main memory.
static constexpr u32 ExtraRam = _1mb * 96; // 32+96 MB devkit memory.
static constexpr u32 Rom = _1mb * 4; // 4 MB main rom
static constexpr u32 Rom1 = _1mb * 4; // DVD player
static constexpr u32 Rom2 = 0x00080000; // Chinese rom extension
static constexpr u32 Hardware = _64kb;
static constexpr u32 Scratch = _16kb;
static const uint IopRam = _1mb * 2; // 2MB main ram on the IOP.
static const uint IopHardware = _64kb;
static constexpr u32 IopRam = _1mb * 2; // 2MB main ram on the IOP.
static constexpr u32 IopHardware = _64kb;
static const uint GSregs = 0x00002000; // 8k for the GS registers and stuff.
}
static constexpr u32 GSregs = 0x00002000; // 8k for the GS registers and stuff.
} // namespace Ps2MemSize
typedef u8 mem8_t;
typedef u16 mem16_t;
@ -39,11 +40,12 @@ typedef u128 mem128_t;
struct EEVM_MemoryAllocMess
{
u8 Main[Ps2MemSize::MainRam]; // Main memory (hard-wired to 32MB)
u8 Scratch[Ps2MemSize::Scratch]; // Scratchpad!
u8 ROM[Ps2MemSize::Rom]; // Boot rom (4MB)
u8 ROM1[Ps2MemSize::Rom1]; // DVD player (4MB)
u8 ROM2[Ps2MemSize::Rom2]; // Chinese extensions
u8 Main[Ps2MemSize::MainRam]; // Main memory (hard-wired to 32MB)
u8 ExtraMemory[Ps2MemSize::ExtraRam]; // Extra memory (32MB up to 128MB => 96MB).
u8 Scratch[Ps2MemSize::Scratch]; // Scratchpad!
u8 ROM[Ps2MemSize::Rom]; // Boot rom (4MB)
u8 ROM1[Ps2MemSize::Rom1]; // DVD player (4MB)
u8 ROM2[Ps2MemSize::Rom2]; // Chinese extensions
// Two 1 megabyte (max DMA) buffers for reading and writing to high memory (>32MB).
// Such accesses are not documented as causing bus errors but as the memory does
@ -56,9 +58,9 @@ struct EEVM_MemoryAllocMess
struct IopVM_MemoryAllocMess
{
u8 Main[Ps2MemSize::IopRam]; // Main memory (hard-wired to 2MB)
u8 P[_64kb]; // I really have no idea what this is... --air
u8 Sif[0x100]; // a few special SIF/SBUS registers (likely not needed)
u8 Main[Ps2MemSize::IopRam]; // Main memory (hard-wired to 2MB)
u8 P[_64kb]; // I really have no idea what this is... --air
u8 Sif[0x100]; // a few special SIF/SBUS registers (likely not needed)
};

View File

@ -17,6 +17,7 @@
#define _PC_ // disables MIPS opcode macros.
#include "common/Assertions.h"
#include "common/ByteSwap.h"
#include "common/FileSystem.h"
#include "common/Path.h"

View File

@ -207,7 +207,6 @@ extern R3000Acpu psxRec;
extern void psxReset();
extern void psxException(u32 code, u32 step);
extern void iopEventTest();
extern void psxMemReset();
int psxIsBreakpointNeeded(u32 addr);
int psxIsMemcheckNeeded(u32 pc);

View File

@ -67,12 +67,8 @@ const int kMaxArgs = 16;
uptr g_argPtrs[kMaxArgs];
#define DEBUG_LAUNCHARG 0 // show lots of helpful console messages as the launch arguments are passed to the game
extern SysMainMemory& GetVmMemory();
void cpuReset()
{
GetVmMemory().Reset();
std::memset(&cpuRegs, 0, sizeof(cpuRegs));
std::memset(&fpuRegs, 0, sizeof(fpuRegs));
std::memset(&tlb, 0, sizeof(tlb));
@ -91,8 +87,6 @@ void cpuReset()
psxReset();
pgifInit();
hwReset();
extern void Deci2Reset(); // lazy, no good header for it yet.
Deci2Reset();

View File

@ -248,48 +248,19 @@ struct R5900cpu
// the virtual cpu provider. Allocating additional heap memory from this method is
// NOT recommended. Heap allocations should be performed by Reset only. This
// maximizes the likeliness of reservations claiming addresses they prefer.
//
// Thread Affinity:
// Called from the main/UI thread only. Cpu execution status is guaranteed to
// be inactive. No locking is necessary.
//
// Exception Throws:
// HardwareDeficiency - The host machine's hardware does not support this CPU provider.
// OutOfMemory - Not enough memory, or the memory areas required were already
// reserved.
void (*Reserve)();
// Deallocates ram allocated by Allocate, Reserve, and/or by runtime code execution.
//
// Thread Affinity:
// Called from the main/UI thread only. Cpu execution status is guaranteed to
// be inactive. No locking is necessary.
//
// Exception Throws: None. This function is a destructor, and should not throw.
//
void (*Shutdown)();
// Initializes / Resets code execution states. Typically implementation is only
// needed for recompilers, as interpreters have no internal execution states and
// rely on the CPU/VM states almost entirely.
//
// Thread Affinity:
// Can be called from any thread. CPU execution status is indeterminate and may
// already be in progress. Implementations should be sure to queue and execute
// resets at the earliest safe convenience (typically right before recompiling a
// new block of code, or after a vsync event).
//
// Exception Throws: Emulator-defined. Common exception types to expect are
// OutOfMemory, Stream Exceptions
//
void (*Reset)();
// Steps a single instruction. Meant to be used by debuggers. Is currently unused
// and unimplemented. Future note: recompiler "step" should *always* fall back
// on interpreters.
//
// Exception Throws: [TODO] (possible execution-related throws to be added)
//
void (*Step)();
// Executes code until a break is signaled. Execution can be paused or suspended
@ -297,11 +268,6 @@ struct R5900cpu
// Execution Breakages are handled the same way, where-by a signal causes the Execute
// call to return at the nearest state check (typically handled internally using
// either C++ exceptions or setjmp/longjmp).
//
// Exception Throws:
// Throws BaseR5900Exception and all derivatives.
// Throws FileNotFound or other Streaming errors (typically related to BIOS MEC/NVM)
//
void (*Execute)();
// Immediately exits execution of recompiled code if we are in a state to do so, or
@ -319,13 +285,6 @@ struct R5900cpu
// Also: the calls from COP0's TLB remap code should be replaced with full recompiler
// resets, since TLB remaps affect more than just the code they contain (code that
// may reference the remapped blocks via memory loads/stores, for example).
//
// Thread Affinity Rule:
// Can be called from any thread (namely for being called from debugging threads)
//
// Exception Throws: [TODO] Emulator defined? (probably shouldn't throw, probably
// doesn't matter if we're stripping it out soon. ;)
//
void (*Clear)(u32 Addr, u32 Size);
};

View File

@ -20,6 +20,7 @@
#include "SIO/Memcard/MemoryCardFolder.h"
#include "SIO/Sio.h"
#include "common/Assertions.h"
#include "common/FileSystem.h"
#include "common/Path.h"
#include "common/StringUtil.h"

View File

@ -18,6 +18,7 @@
#include "SIO/Memcard/MemoryCardFile.h"
#include "SIO/Memcard/MemoryCardFolder.h"
#include "common/Assertions.h"
#include "common/Path.h"
#include "System.h"

View File

@ -20,6 +20,8 @@
#include "CDVD/CDVD.h"
#include "Elfheader.h"
#include "GSDumpReplayer.h"
#include "Host.h"
#include "IopMem.h"
#include "MTVU.h"
#include "R3000A.h"
#include "VUmicro.h"
@ -44,6 +46,20 @@ SSE_MXCSR g_sseMXCSR = {DEFAULT_sseMXCSR};
SSE_MXCSR g_sseVU0MXCSR = {DEFAULT_sseVUMXCSR};
SSE_MXCSR g_sseVU1MXCSR = {DEFAULT_sseVUMXCSR};
namespace SysMemory
{
static u8* TryAllocateVirtualMemory(const char* name, void* file_handle, uptr base, size_t size);
static u8* AllocateVirtualMemory(const char* name, void* file_handle, size_t size, size_t offset_from_base);
static bool AllocateMemoryMap();
static void DumpMemoryMap();
static void ReleaseMemoryMap();
static u8* s_data_memory;
static void* s_data_memory_file_handle;
static u8* s_code_memory;
} // namespace SysMemory
// SetCPUState -- for assignment of SSE roundmodes and clampmodes.
//
void SetCPUState(SSE_MXCSR sseMXCSR, SSE_MXCSR sseVU0MXCSR, SSE_MXCSR sseVU1MXCSR)
@ -140,20 +156,55 @@ namespace HostMemoryMap
// For debuggers
extern "C" {
#ifdef _WIN32
_declspec(dllexport) uptr EEmem, IOPmem, VUmem, EErec, IOPrec, VIF0rec, VIF1rec, mVU0rec, mVU1rec, SWjit, bumpAllocator;
_declspec(dllexport) uptr EEmem, IOPmem, VUmem;
#else
__attribute__((visibility("default"), used)) uptr EEmem, IOPmem, VUmem, EErec, IOPrec, VIF0rec, VIF1rec, mVU0rec, mVU1rec, SWjit, bumpAllocator;
__attribute__((visibility("default"), used)) uptr EEmem, IOPmem, VUmem;
#endif
}
} // namespace HostMemoryMap
/// Attempts to find a spot near static variables for the main memory
static VirtualMemoryManagerPtr makeMemoryManager(const char* name, const char* file_mapping_name, size_t size, size_t offset_from_base)
u8* SysMemory::TryAllocateVirtualMemory(const char* name, void* file_handle, uptr base, size_t size)
{
u8* baseptr;
if (file_handle)
baseptr = static_cast<u8*>(HostSys::MapSharedMemory(file_handle, 0, (void*)base, size, PageAccess_ReadWrite()));
else
baseptr = static_cast<u8*>(HostSys::Mmap((void*)base, size, PageAccess_Any()));
if (!baseptr)
return nullptr;
if ((uptr)baseptr != base)
{
if (file_handle)
{
if (baseptr)
HostSys::UnmapSharedMemory(baseptr, size);
}
else
{
if (baseptr)
HostSys::Munmap(baseptr, size);
}
return nullptr;
}
DevCon.WriteLn(Color_Gray, "%-32s @ 0x%016" PRIXPTR " -> 0x%016" PRIXPTR " %s", name,
baseptr, (uptr)baseptr + size, fmt::format("[{}mb]", size / _1mb).c_str());
return baseptr;
}
u8* SysMemory::AllocateVirtualMemory(const char* name, void* file_handle, size_t size, size_t offset_from_base)
{
pxAssertRel(Common::IsAlignedPow2(size, __pagesize), "Virtual memory size is page aligned");
// Everything looks nicer when the start of all the sections is a nice round looking number.
// Also reduces the variation in the address due to small changes in code.
// Breaks ASLR but so does anything else that tries to make addresses constant for our debugging pleasure
uptr codeBase = (uptr)(void*)makeMemoryManager / (1 << 28) * (1 << 28);
uptr codeBase = (uptr)(void*)AllocateVirtualMemory / (1 << 28) * (1 << 28);
// The allocation is ~640mb in size, slighly under 3*2^28.
// We'll hope that the code generated for the PCSX2 executable stays under 512mb (which is likely)
@ -167,122 +218,177 @@ static VirtualMemoryManagerPtr makeMemoryManager(const char* name, const char* f
// VTLB will throw a fit if we try to put EE main memory here
continue;
}
auto mgr = std::make_shared<VirtualMemoryManager>(name, file_mapping_name, base, size, /*upper_bounds=*/0, /*strict=*/true);
if (mgr->IsOk())
{
return mgr;
}
if (u8* ret = TryAllocateVirtualMemory(name, file_handle, base, size))
return ret;
DevCon.Warning("%s: host memory @ 0x%016" PRIXPTR " -> 0x%016" PRIXPTR " is unavailable; attempting to map elsewhere...", name,
base, base + size);
}
// If the above failed and it's x86-64, recompiled code is going to break!
// If it's i386 anything can reach anything so it doesn't matter
if (sizeof(void*) == 8)
return nullptr;
}
bool SysMemory::AllocateMemoryMap()
{
s_data_memory_file_handle = HostSys::CreateSharedMemory(HostSys::GetFileMappingName("pcsx2").c_str(), HostMemoryMap::MainSize);
if (!s_data_memory_file_handle)
{
pxAssertRel(0, "Failed to find a good place for the memory allocation, recompilers may fail");
Host::ReportErrorAsync("Error", "Failed to create shared memory file.");
ReleaseMemoryMap();
return false;
}
return std::make_shared<VirtualMemoryManager>(name, file_mapping_name, 0, size);
if ((s_data_memory = AllocateVirtualMemory("Data Memory", s_data_memory_file_handle, HostMemoryMap::MainSize, 0)) == nullptr)
{
Host::ReportErrorAsync("Error", "Failed to map data memory at an acceptable location.");
ReleaseMemoryMap();
return false;
}
if ((s_code_memory = AllocateVirtualMemory("Code Memory", nullptr, HostMemoryMap::CodeSize, HostMemoryMap::MainSize)) == nullptr)
{
Host::ReportErrorAsync("Error", "Failed to allocate code memory at an acceptable location.");
ReleaseMemoryMap();
return false;
}
HostMemoryMap::EEmem = (uptr)(s_data_memory + HostMemoryMap::EEmemOffset);
HostMemoryMap::IOPmem = (uptr)(s_data_memory + HostMemoryMap::IOPmemOffset);
HostMemoryMap::VUmem = (uptr)(s_data_memory + HostMemoryMap::VUmemSize);
DumpMemoryMap();
return true;
}
// --------------------------------------------------------------------------------------
// SysReserveVM (implementations)
// --------------------------------------------------------------------------------------
SysMainMemory::SysMainMemory()
: m_mainMemory(makeMemoryManager("Main Memory Manager", "pcsx2", HostMemoryMap::MainSize, 0))
, m_codeMemory(makeMemoryManager("Code Memory Manager", nullptr, HostMemoryMap::CodeSize, HostMemoryMap::MainSize))
, m_bumpAllocator(m_mainMemory, HostMemoryMap::bumpAllocatorOffset, HostMemoryMap::MainSize - HostMemoryMap::bumpAllocatorOffset)
void SysMemory::DumpMemoryMap()
{
uptr main_base = (uptr)MainMemory()->GetBase();
uptr code_base = (uptr)MainMemory()->GetBase();
HostMemoryMap::EEmem = main_base + HostMemoryMap::EEmemOffset;
HostMemoryMap::IOPmem = main_base + HostMemoryMap::IOPmemOffset;
HostMemoryMap::VUmem = main_base + HostMemoryMap::VUmemOffset;
HostMemoryMap::EErec = code_base + HostMemoryMap::EErecOffset;
HostMemoryMap::IOPrec = code_base + HostMemoryMap::IOPrecOffset;
HostMemoryMap::VIF0rec = code_base + HostMemoryMap::VIF0recOffset;
HostMemoryMap::VIF1rec = code_base + HostMemoryMap::VIF1recOffset;
HostMemoryMap::mVU0rec = code_base + HostMemoryMap::mVU0recOffset;
HostMemoryMap::mVU1rec = code_base + HostMemoryMap::mVU1recOffset;
HostMemoryMap::bumpAllocator = main_base + HostMemoryMap::bumpAllocatorOffset;
#define DUMP_REGION(name, base, offset, size) \
DevCon.WriteLn(Color_Gray, "%-32s @ 0x%016" PRIXPTR " -> 0x%016" PRIXPTR " %s", name, \
(uptr)(base + offset), (uptr)(base + offset + size), fmt::format("[{}mb]", size / _1mb).c_str());
DUMP_REGION("EE Main Memory", s_data_memory, HostMemoryMap::EEmemOffset, HostMemoryMap::EEmemSize);
DUMP_REGION("IOP Main Memory", s_data_memory, HostMemoryMap::IOPmemOffset, HostMemoryMap::IOPmemSize);
DUMP_REGION("VU0/1 On-Chip Memory", s_data_memory, HostMemoryMap::VUmemOffset, HostMemoryMap::VUmemSize);
DUMP_REGION("VTLB Virtual Map", s_data_memory, HostMemoryMap::VTLBAddressMapOffset, HostMemoryMap::VTLBVirtualMapSize);
DUMP_REGION("VTLB Address Map", s_data_memory, HostMemoryMap::VTLBAddressMapSize, HostMemoryMap::VTLBAddressMapSize);
DUMP_REGION("R5900 Recompiler Cache", s_code_memory, HostMemoryMap::EErecOffset, HostMemoryMap::EErecSize);
DUMP_REGION("R3000A Recompiler Cache", s_code_memory, HostMemoryMap::IOPrecOffset, HostMemoryMap::IOPrecSize);
DUMP_REGION("Micro VU0 Recompiler Cache", s_code_memory, HostMemoryMap::mVU0recOffset, HostMemoryMap::mVU0recSize);
DUMP_REGION("Micro VU0 Recompiler Cache", s_code_memory, HostMemoryMap::mVU1recOffset, HostMemoryMap::mVU1recSize);
DUMP_REGION("VIF0 Unpack Recompiler Cache", s_code_memory, HostMemoryMap::VIF0recOffset, HostMemoryMap::VIF0recSize);
DUMP_REGION("VIF1 Unpack Recompiler Cache", s_code_memory, HostMemoryMap::VIF1recOffset, HostMemoryMap::VIF1recSize);
DUMP_REGION("VIF Unpack Recompiler Cache", s_code_memory, HostMemoryMap::VIFUnpackRecOffset, HostMemoryMap::VIFUnpackRecSize);
DUMP_REGION("GS Software Renderer", s_code_memory, HostMemoryMap::SWrecOffset, HostMemoryMap::SWrecSize);
#undef DUMP_REGION
}
SysMainMemory::~SysMainMemory()
void SysMemory::ReleaseMemoryMap()
{
Release();
if (s_code_memory)
{
HostSys::Munmap(s_code_memory, HostMemoryMap::CodeSize);
s_code_memory = nullptr;
}
if (s_data_memory)
{
HostSys::UnmapSharedMemory(s_data_memory, HostMemoryMap::MainSize);
s_data_memory = nullptr;
}
if (s_data_memory_file_handle)
{
HostSys::DestroySharedMemory(s_data_memory_file_handle);
s_data_memory_file_handle = nullptr;
}
}
bool SysMainMemory::Allocate()
bool SysMemory::Allocate()
{
DevCon.WriteLn(Color_StrongBlue, "Allocating host memory for virtual systems...");
ConsoleIndentScope indent(1);
m_ee.Assign(MainMemory());
m_iop.Assign(MainMemory());
m_vu.Assign(MainMemory());
if (!AllocateMemoryMap())
return false;
vtlb_Core_Alloc();
memAllocate();
iopMemAlloc();
vuMemAllocate();
if (!vtlb_Core_Alloc())
return false;
return true;
}
void SysMainMemory::Reset()
void SysMemory::Reset()
{
DevCon.WriteLn(Color_StrongBlue, "Resetting host memory for virtual systems...");
ConsoleIndentScope indent(1);
m_ee.Reset();
m_iop.Reset();
m_vu.Reset();
memReset();
iopMemReset();
vuMemReset();
// Note: newVif is reset as part of other VIF structures.
// Software is reset on the GS thread.
}
void SysMainMemory::Release()
void SysMemory::Release()
{
Console.WriteLn(Color_Blue, "Releasing host memory for virtual systems...");
ConsoleIndentScope indent(1);
hwShutdown();
vtlb_Core_Free(); // Just to be sure... (calling order could result in it getting missed during Decommit).
releaseNewVif(0);
releaseNewVif(1);
m_ee.Release();
m_iop.Release();
m_vu.Release();
vuMemRelease();
iopMemRelease();
memRelease();
ReleaseMemoryMap();
}
u8* SysMemory::GetDataPtr(size_t offset)
{
pxAssert(offset <= HostMemoryMap::MainSize);
return s_data_memory + offset;
}
u8* SysMemory::GetCodePtr(size_t offset)
{
pxAssert(offset <= HostMemoryMap::CodeSize);
return s_code_memory + offset;
}
void* SysMemory::GetDataFileHandle()
{
return s_data_memory_file_handle;
}
// --------------------------------------------------------------------------------------
// SysCpuProviderPack (implementations)
// --------------------------------------------------------------------------------------
SysCpuProviderPack::SysCpuProviderPack()
{
Console.WriteLn(Color_StrongBlue, "Reserving memory for recompilers...");
ConsoleIndentScope indent(1);
recCpu.Reserve();
psxRec.Reserve();
CpuMicroVU0.Reserve();
CpuMicroVU1.Reserve();
if constexpr (newVifDynaRec)
{
dVifReserve(0);
dVifReserve(1);
}
GSCodeReserve::GetInstance().Assign(GetVmMemory().CodeMemory());
VifUnpackSSE_Init();
}
SysCpuProviderPack::~SysCpuProviderPack()
{
GSCodeReserve::GetInstance().Release();
if (newVifDynaRec)
{
dVifRelease(1);

View File

@ -16,7 +16,6 @@
#pragma once
#include "Config.h"
#include "VirtualMemory.h"
#include "vtlb.h"
// This is a table of default virtual map addresses for ps2vm components. These locations
@ -35,85 +34,125 @@ namespace HostMemoryMap
//////////////////////////////////////////////////////////////////////////
// Main
//////////////////////////////////////////////////////////////////////////
static const u32 MainSize = 0x14000000;
// PS2 main memory, SPR, and ROMs (approximately 40.5MB, but we round up to 64MB for simplicity).
static const u32 EEmemOffset = 0x00000000;
// PS2 main memory, SPR, and ROMs (approximately 138.5MB, but we round up to 139MB for simplicity).
static constexpr u32 EEmemOffset = 0x00000000;
static constexpr u32 EEmemSize = 0x8B00000;
// IOP main memory and ROMs
static const u32 IOPmemOffset = 0x04000000;
// IOP main memory (2MB + 64K + 256b, rounded up to 3MB for simplicity).
static constexpr u32 IOPmemOffset = EEmemOffset + EEmemSize;
static constexpr u32 IOPmemSize = 0x300000;
// VU0 and VU1 memory.
static const u32 VUmemOffset = 0x08000000;
// VU0 and VU1 memory (40KB, rounded up to 1MB for simplicity).
static constexpr u32 VUmemOffset = IOPmemOffset + IOPmemSize;
static constexpr u32 VUmemSize = 0x100000;
// Bump allocator for any other small allocations
// size: Difference between it and HostMemoryMap::Size, so nothing should allocate higher than it!
static const u32 bumpAllocatorOffset = 0x10000000;
// VTLB virtual map ((4GB / 4096) * sizeof(ptr))
static constexpr u32 VTLBVirtualMapOffset = VUmemOffset + VUmemSize;
static constexpr u32 VTLBVirtualMapSize = (0x100000000ULL / 4096) * sizeof(void*);
// VTLB address map ((4GB / 4096) * sizeof(u32))
static constexpr u32 VTLBAddressMapOffset = VTLBVirtualMapOffset + VTLBVirtualMapSize;
static constexpr u32 VTLBAddressMapSize = (0x100000000ULL / 4096) * sizeof(u32);
// Overall size.
static constexpr u32 MainSize = VTLBAddressMapOffset + VTLBAddressMapSize;
//////////////////////////////////////////////////////////////////////////
// Code
//////////////////////////////////////////////////////////////////////////
static const u32 CodeSize = 0x13100000; // 305 mb
// EE recompiler code cache area (64mb)
static const u32 EErecOffset = 0x00000000;
static constexpr u32 EErecOffset = 0x00000000;
static constexpr u32 EErecSize = 0x4000000;
// IOP recompiler code cache area (32mb)
static const u32 IOPrecOffset = 0x04000000;
static constexpr u32 IOPrecOffset = EErecOffset + EErecSize;
static constexpr u32 IOPrecSize = 0x2000000;
// newVif0 recompiler code cache area (8mb)
static const u32 VIF0recOffset = 0x06000000;
static constexpr u32 VIF0recOffset = IOPrecOffset + IOPrecSize;
static constexpr u32 VIF0recSize = 0x800000;
// newVif1 recompiler code cache area (8mb)
static const u32 VIF1recOffset = 0x06800000;
static constexpr u32 VIF1recOffset = VIF0recOffset + VIF0recSize;
static constexpr u32 VIF1recSize = 0x800000;
// microVU1 recompiler code cache area (64mb)
static const u32 mVU0recOffset = 0x07000000;
static constexpr u32 mVU0recOffset = VIF1recOffset + VIF1recSize;
static constexpr u32 mVU0recSize = 0x4000000;
// microVU0 recompiler code cache area (64mb)
static const u32 mVU1recOffset = 0x0B000000;
static constexpr u32 mVU1recOffset = mVU0recOffset + mVU0recSize;
static constexpr u32 mVU1recSize = 0x4000000;
// SSE-optimized VIF unpack functions (1mb)
static const u32 VIFUnpackRecOffset = 0x0F000000;
static constexpr u32 VIFUnpackRecOffset = mVU1recOffset + mVU1recSize;
static constexpr u32 VIFUnpackRecSize = 0x100000;
// Software Renderer JIT buffer (64mb)
static const u32 SWrecOffset = 0x0F100000;
static const u32 SWrecSize = 0x04000000;
}
static constexpr u32 SWrecOffset = VIFUnpackRecOffset + VIFUnpackRecSize;
static constexpr u32 SWrecSize = 0x04000000;
// Overall size.
static constexpr u32 CodeSize = SWrecOffset + SWrecSize; // 305 mb
} // namespace HostMemoryMap
// --------------------------------------------------------------------------------------
// SysMainMemory
// HostMemory
// --------------------------------------------------------------------------------------
// This class provides the main memory for the virtual machines.
class SysMainMemory final
namespace SysMemory
{
protected:
const VirtualMemoryManagerPtr m_mainMemory;
const VirtualMemoryManagerPtr m_codeMemory;
VirtualMemoryBumpAllocator m_bumpAllocator;
eeMemoryReserve m_ee;
iopMemoryReserve m_iop;
vuMemoryReserve m_vu;
public:
SysMainMemory();
~SysMainMemory();
const VirtualMemoryManagerPtr& MainMemory() { return m_mainMemory; }
const VirtualMemoryManagerPtr& CodeMemory() { return m_codeMemory; }
VirtualMemoryBumpAllocator& BumpAllocator() { return m_bumpAllocator; }
const eeMemoryReserve& EEMemory() const { return m_ee; }
const iopMemoryReserve& IOPMemory() const { return m_iop; }
const vuMemoryReserve& VUMemory() const { return m_vu; }
bool Allocate();
void Reset();
void Release();
};
/// Returns data memory (Main in Memory Map).
u8* GetDataPtr(size_t offset);
/// Returns memory used for the recompilers.
u8* GetCodePtr(size_t offset);
/// Returns the file mapping which backs the data memory.
void* GetDataFileHandle();
// clang-format off
//////////////////////////////////////////////////////////////////////////
// Data Memory Accessors
//////////////////////////////////////////////////////////////////////////
__fi static u8* GetEEMem() { return GetDataPtr(HostMemoryMap::EEmemOffset); }
__fi static u8* GetEEMemEnd() { return GetDataPtr(HostMemoryMap::EEmemOffset + HostMemoryMap::EEmemSize); }
__fi static u8* GetIOPMem() { return GetDataPtr(HostMemoryMap::IOPmemOffset); }
__fi static u8* GetIOPMemEnd() { return GetDataPtr(HostMemoryMap::IOPmemOffset + HostMemoryMap::IOPmemSize); }
__fi static u8* GetVUMem() { return GetDataPtr(HostMemoryMap::VUmemOffset); }
__fi static u8* GetVUMemEnd() { return GetDataPtr(HostMemoryMap::VUmemOffset + HostMemoryMap::VUmemSize); }
__fi static u8* GetVTLBVirtualMap() { return GetDataPtr(HostMemoryMap::VTLBVirtualMapOffset); }
__fi static u8* GetVTLBVirtualMapEnd() { return GetDataPtr(HostMemoryMap::VTLBVirtualMapOffset + HostMemoryMap::VTLBVirtualMapSize); }
__fi static u8* GetVTLBAddressMap() { return GetDataPtr(HostMemoryMap::VTLBAddressMapOffset); }
__fi static u8* GetVTLBAddressMapEnd() { return GetDataPtr(HostMemoryMap::VTLBAddressMapOffset + HostMemoryMap::VTLBAddressMapSize); }
//////////////////////////////////////////////////////////////////////////
// Code Memory Accessors
//////////////////////////////////////////////////////////////////////////
__fi static u8* GetEERec() { return GetCodePtr(HostMemoryMap::EErecOffset); }
__fi static u8* GetEERecEnd() { return GetCodePtr(HostMemoryMap::EErecOffset + HostMemoryMap::EErecSize); }
__fi static u8* GetIOPRec() { return GetCodePtr(HostMemoryMap::IOPrecOffset); }
__fi static u8* GetIOPRecEnd() { return GetCodePtr(HostMemoryMap::IOPrecOffset + HostMemoryMap::IOPrecSize); }
__fi static u8* GetVU0Rec() { return GetCodePtr(HostMemoryMap::mVU0recOffset); }
__fi static u8* GetVU0RecEnd() { return GetCodePtr(HostMemoryMap::mVU0recOffset + HostMemoryMap::mVU0recSize); }
__fi static u8* GetVU1Rec() { return GetCodePtr(HostMemoryMap::mVU1recOffset); }
__fi static u8* GetVU1RecEnd() { return GetCodePtr(HostMemoryMap::mVU1recOffset + HostMemoryMap::mVU1recSize); }
__fi static u8* GetVIFUnpackRec() { return GetCodePtr(HostMemoryMap::VIFUnpackRecOffset); }
__fi static u8* GetVIFUnpackRecEnd() { return GetCodePtr(HostMemoryMap::VIFUnpackRecOffset + HostMemoryMap::VIFUnpackRecSize); }
__fi static u8* GetSWRec() { return GetCodePtr(HostMemoryMap::SWrecOffset); }
__fi static u8* GetSWRecEnd() { return GetCodePtr(HostMemoryMap::SWrecOffset + HostMemoryMap::SWrecSize); }
// clang-format on
} // namespace SysMemory
// --------------------------------------------------------------------------------------
// SysCpuProviderPack
@ -131,10 +170,8 @@ public:
// implemented by the provisioning interface.
extern SysCpuProviderPack& GetCpuProviders();
extern void SysLogMachineCaps(); // Detects cpu type and fills cpuInfo structs.
extern void SysClearExecutionCache(); // clears recompiled execution caches!
extern SysMainMemory& GetVmMemory();
extern void SysLogMachineCaps(); // Detects cpu type and fills cpuInfo structs.
extern void SysClearExecutionCache(); // clears recompiled execution caches!
extern void SetCPUState(SSE_MXCSR sseMXCSR, SSE_MXCSR sseVU0MXCSR, SSE_MXCSR sseVU1MXCSR);
extern SSE_MXCSR g_sseVU0MXCSR, g_sseVU1MXCSR, g_sseMXCSR;

View File

@ -147,7 +147,6 @@ namespace VMManager
static constexpr u32 SETTINGS_VERSION = 1;
static std::unique_ptr<SysMainMemory> s_vm_memory;
static std::unique_ptr<SysCpuProviderPack> s_cpu_provider_pack;
static std::unique_ptr<INISettingsInterface> s_game_settings_interface;
static std::unique_ptr<INISettingsInterface> s_input_settings_interface;
@ -349,15 +348,15 @@ bool VMManager::Internal::CPUThreadInitialize()
x86caps.SIMD_EstablishMXCSRmask();
SysLogMachineCaps();
pxAssert(!s_vm_memory && !s_cpu_provider_pack);
s_vm_memory = std::make_unique<SysMainMemory>();
s_cpu_provider_pack = std::make_unique<SysCpuProviderPack>();
if (!s_vm_memory->Allocate())
if (!SysMemory::Allocate())
{
Host::ReportErrorAsync("Error", "Failed to allocate VM memory.");
return false;
}
pxAssert(!s_cpu_provider_pack);
s_cpu_provider_pack = std::make_unique<SysCpuProviderPack>();
GSinit();
USBinit();
@ -388,7 +387,6 @@ void VMManager::Internal::CPUThreadShutdown()
WaitForSaveStateFlush();
s_cpu_provider_pack.reset();
s_vm_memory.reset();
PerformanceMetrics::SetCPUThread(Threading::ThreadHandle());
@ -397,16 +395,13 @@ void VMManager::Internal::CPUThreadShutdown()
MTGS::ShutdownThread();
SysMemory::Release();
#ifdef _WIN32
CoUninitialize();
#endif
}
SysMainMemory& GetVmMemory()
{
return *s_vm_memory;
}
SysCpuProviderPack& GetCpuProviders()
{
return *s_cpu_provider_pack;
@ -1296,8 +1291,9 @@ bool VMManager::Initialize(VMBootParameters boot_params)
SetCPUState(EmuConfig.Cpu.sseMXCSR, EmuConfig.Cpu.sseVU0MXCSR, EmuConfig.Cpu.sseVU1MXCSR);
SysClearExecutionCache();
memBindConditionalHandlers();
SysMemory::Reset();
cpuReset();
hwReset();
Console.WriteLn("VM subsystems initialized in %.2f ms", init_timer.GetTimeMilliseconds());
s_state.store(VMState::Paused, std::memory_order_release);
@ -1438,7 +1434,9 @@ void VMManager::Reset()
SysClearExecutionCache();
memBindConditionalHandlers();
SysMemory::Reset();
cpuReset();
hwReset();
if (g_InputRecording.isActive())
{

View File

@ -233,4 +233,7 @@ static VURegs& VU1 = vuRegs[1];
inline bool VURegs::IsVU1() const { return this == &vuRegs[1]; }
inline bool VURegs::IsVU0() const { return this == &vuRegs[0]; }
extern void vuMemAllocate();
extern void vuMemReset();
extern void vuMemRelease();
extern u32* GET_VU_MEM(VURegs* VU, u32 addr);

View File

@ -21,42 +21,23 @@
alignas(16) VURegs vuRegs[2];
vuMemoryReserve::vuMemoryReserve()
: _parent("VU0/1 on-chip memory")
void vuMemAllocate()
{
}
vuMemoryReserve::~vuMemoryReserve()
{
Release();
}
void vuMemoryReserve::Assign(VirtualMemoryManagerPtr allocator)
{
static constexpr u32 VU_MEMORY_RESERVE_SIZE = VU1_PROGSIZE + VU1_MEMSIZE + VU0_PROGSIZE + VU0_MEMSIZE;
_parent::Assign(std::move(allocator), HostMemoryMap::VUmemOffset, VU_MEMORY_RESERVE_SIZE);
u8* curpos = GetPtr();
u8* curpos = SysMemory::GetDataPtr(HostMemoryMap::VUmemOffset);
VU0.Micro = curpos; curpos += VU0_PROGSIZE;
VU0.Mem = curpos; curpos += VU0_MEMSIZE;
VU1.Micro = curpos; curpos += VU1_PROGSIZE;
VU1.Mem = curpos; curpos += VU1_MEMSIZE;
}
void vuMemoryReserve::Release()
void vuMemRelease()
{
_parent::Release();
VU0.Micro = VU0.Mem = nullptr;
VU1.Micro = VU1.Mem = nullptr;
}
void vuMemoryReserve::Reset()
void vuMemReset()
{
_parent::Reset();
pxAssert( VU0.Mem );
pxAssert( VU1.Mem );

View File

@ -1,336 +0,0 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2010 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include "VirtualMemory.h"
#include "common/BitUtils.h"
#include "common/Console.h"
#include "common/Perf.h"
#include "fmt/core.h"
#include <cinttypes>
// --------------------------------------------------------------------------------------
// VirtualMemoryManager (implementations)
// --------------------------------------------------------------------------------------
VirtualMemoryManager::VirtualMemoryManager(std::string name, const char* file_mapping_name, uptr base, size_t size, uptr upper_bounds, bool strict)
: m_name(std::move(name))
, m_file_handle(nullptr)
, m_baseptr(0)
, m_pageuse(nullptr)
, m_pages_reserved(0)
{
if (!size)
return;
size_t reserved_bytes = Common::PageAlign(size);
m_pages_reserved = reserved_bytes / __pagesize;
if (file_mapping_name && file_mapping_name[0])
{
std::string real_file_mapping_name(HostSys::GetFileMappingName(file_mapping_name));
m_file_handle = HostSys::CreateSharedMemory(real_file_mapping_name.c_str(), reserved_bytes);
if (!m_file_handle)
return;
m_baseptr = static_cast<u8*>(HostSys::MapSharedMemory(m_file_handle, 0, (void*)base, reserved_bytes, PageAccess_ReadWrite()));
if (!m_baseptr || (upper_bounds != 0 && (((uptr)m_baseptr + reserved_bytes) > upper_bounds)))
{
DevCon.Warning("%s: host memory @ 0x%016" PRIXPTR " -> 0x%016" PRIXPTR " is unavailable; attempting to map elsewhere...",
m_name.c_str(), base, base + size);
SafeSysMunmap(m_baseptr, reserved_bytes);
if (base)
{
// Let's try again at an OS-picked memory area, and then hope it meets needed
// boundschecking criteria below.
m_baseptr = static_cast<u8*>(HostSys::MapSharedMemory(m_file_handle, 0, nullptr, reserved_bytes, PageAccess_ReadWrite()));
}
}
}
else
{
m_baseptr = static_cast<u8*>(HostSys::Mmap((void*)base, reserved_bytes, PageAccess_Any()));
if (!m_baseptr || (upper_bounds != 0 && (((uptr)m_baseptr + reserved_bytes) > upper_bounds)))
{
DevCon.Warning("%s: host memory @ 0x%016" PRIXPTR " -> 0x%016" PRIXPTR " is unavailable; attempting to map elsewhere...",
m_name.c_str(), base, base + size);
SafeSysMunmap(m_baseptr, reserved_bytes);
if (base)
{
// Let's try again at an OS-picked memory area, and then hope it meets needed
// boundschecking criteria below.
m_baseptr = static_cast<u8*>(HostSys::Mmap(0, reserved_bytes, PageAccess_Any()));
}
}
}
bool fulfillsRequirements = true;
if (strict && (uptr)m_baseptr != base)
fulfillsRequirements = false;
if ((upper_bounds != 0) && ((uptr)(m_baseptr + reserved_bytes) > upper_bounds))
fulfillsRequirements = false;
if (!fulfillsRequirements)
{
if (m_file_handle)
{
if (m_baseptr)
HostSys::UnmapSharedMemory(m_baseptr, reserved_bytes);
m_baseptr = 0;
HostSys::DestroySharedMemory(m_file_handle);
m_file_handle = nullptr;
}
else
{
SafeSysMunmap(m_baseptr, reserved_bytes);
}
}
if (!m_baseptr)
return;
m_pageuse = new std::atomic<bool>[m_pages_reserved]();
std::string mbkb;
uint mbytes = reserved_bytes / _1mb;
if (mbytes)
mbkb = fmt::format("[{}mb]", mbytes);
else
mbkb = fmt::format("[{}kb]", reserved_bytes / 1024);
DevCon.WriteLn(Color_Gray, "%-32s @ 0x%016" PRIXPTR " -> 0x%016" PRIXPTR " %s", m_name.c_str(),
m_baseptr, (uptr)m_baseptr + reserved_bytes, mbkb.c_str());
}
VirtualMemoryManager::~VirtualMemoryManager()
{
if (m_pageuse)
delete[] m_pageuse;
if (m_baseptr)
{
if (m_file_handle)
HostSys::UnmapSharedMemory((void*)m_baseptr, m_pages_reserved * __pagesize);
else
HostSys::Munmap(m_baseptr, m_pages_reserved * __pagesize);
}
if (m_file_handle)
HostSys::DestroySharedMemory(m_file_handle);
}
static bool VMMMarkPagesAsInUse(std::atomic<bool>* begin, std::atomic<bool>* end)
{
for (auto current = begin; current < end; current++)
{
bool expected = false;
if (!current->compare_exchange_strong(expected, true, std::memory_order_relaxed))
{
// This was already allocated! Undo the things we've set until this point
while (--current >= begin)
{
if (!current->compare_exchange_strong(expected, false, std::memory_order_relaxed))
{
// In the time we were doing this, someone set one of the things we just set to true back to false
// This should never happen, but if it does we'll just stop and hope nothing bad happens
pxAssert(0);
return false;
}
}
return false;
}
}
return true;
}
u8* VirtualMemoryManager::Alloc(uptr offsetLocation, size_t size) const
{
size = Common::PageAlign(size);
if (!pxAssertDev(offsetLocation % __pagesize == 0, "(VirtualMemoryManager) alloc at unaligned offsetLocation"))
return nullptr;
if (!pxAssertDev(size + offsetLocation <= m_pages_reserved * __pagesize, "(VirtualMemoryManager) alloc outside reserved area"))
return nullptr;
if (m_baseptr == 0)
return nullptr;
auto puStart = &m_pageuse[offsetLocation / __pagesize];
auto puEnd = &m_pageuse[(offsetLocation + size) / __pagesize];
if (!pxAssertDev(VMMMarkPagesAsInUse(puStart, puEnd), "(VirtualMemoryManager) allocation requests overlapped"))
return nullptr;
return m_baseptr + offsetLocation;
}
void VirtualMemoryManager::Free(void* address, size_t size) const
{
uptr offsetLocation = (uptr)address - (uptr)m_baseptr;
if (!pxAssertDev(offsetLocation % __pagesize == 0, "(VirtualMemoryManager) free at unaligned address"))
{
uptr newLoc = Common::PageAlign(offsetLocation);
size -= (offsetLocation - newLoc);
offsetLocation = newLoc;
}
if (!pxAssertDev(size % __pagesize == 0, "(VirtualMemoryManager) free with unaligned size"))
size -= size % __pagesize;
if (!pxAssertDev(size + offsetLocation <= m_pages_reserved * __pagesize, "(VirtualMemoryManager) free outside reserved area"))
return;
auto puStart = &m_pageuse[offsetLocation / __pagesize];
auto puEnd = &m_pageuse[(offsetLocation + size) / __pagesize];
for (; puStart < puEnd; puStart++)
{
bool expected = true;
if (!puStart->compare_exchange_strong(expected, false, std::memory_order_relaxed))
{
pxAssertDev(0, "(VirtaulMemoryManager) double-free");
}
}
}
// --------------------------------------------------------------------------------------
// VirtualMemoryBumpAllocator (implementations)
// --------------------------------------------------------------------------------------
VirtualMemoryBumpAllocator::VirtualMemoryBumpAllocator(VirtualMemoryManagerPtr allocator, uptr offsetLocation, size_t size)
: m_allocator(std::move(allocator))
, m_baseptr(m_allocator->Alloc(offsetLocation, size))
, m_endptr(m_baseptr + size)
{
if (m_baseptr.load() == 0)
pxAssertDev(0, "(VirtualMemoryBumpAllocator) tried to construct from bad VirtualMemoryManager");
}
u8* VirtualMemoryBumpAllocator::Alloc(size_t size)
{
if (m_baseptr.load() == 0) // True if constructed from bad VirtualMemoryManager (assertion was on initialization)
return nullptr;
size_t reservedSize = Common::PageAlign(size);
u8* out = m_baseptr.fetch_add(reservedSize, std::memory_order_relaxed);
if (!pxAssertDev(out - reservedSize + size <= m_endptr, "(VirtualMemoryBumpAllocator) ran out of memory"))
return nullptr;
return out;
}
// --------------------------------------------------------------------------------------
// VirtualMemoryReserve (implementations)
// --------------------------------------------------------------------------------------
VirtualMemoryReserve::VirtualMemoryReserve(std::string name)
: m_name(std::move(name))
{
}
VirtualMemoryReserve::~VirtualMemoryReserve()
{
pxAssertRel(!m_baseptr, "VirtualMemoryReserve has not been released.");
}
// Notes:
// * This method should be called if the object is already in an released (unreserved) state.
// Subsequent calls will be ignored, and the existing reserve will be returned.
//
// Parameters:
// baseptr - the new base pointer that's about to be assigned
// size - size of the region pointed to by baseptr
//
void VirtualMemoryReserve::Assign(VirtualMemoryManagerPtr allocator, u8* baseptr, size_t size)
{
pxAssertRel(size > 0 && Common::IsAlignedPow2(size, __pagesize), "VM allocation is not page aligned");
pxAssertRel(!m_baseptr, "Virtual memory reserve has already been assigned");
m_allocator = std::move(allocator);
m_baseptr = baseptr;
m_size = size;
std::string mbkb;
uint mbytes = size / _1mb;
if (mbytes)
mbkb = fmt::format("[{}mb]", mbytes);
else
mbkb = fmt::format("[{}kb]", size / 1024);
DevCon.WriteLn(Color_Gray, "%-32s @ 0x%016" PRIXPTR " -> 0x%016" PRIXPTR " %s", m_name.c_str(),
m_baseptr, (uptr)m_baseptr + size, mbkb.c_str());
}
u8* VirtualMemoryReserve::BumpAllocate(VirtualMemoryBumpAllocator& allocator, size_t size)
{
u8* base = allocator.Alloc(size);
if (base)
Assign(allocator.GetAllocator(), base, size);
return base;
}
void VirtualMemoryReserve::Release()
{
if (!m_baseptr)
return;
m_allocator->Free(m_baseptr, m_size);
m_baseptr = nullptr;
m_size = 0;
}
// --------------------------------------------------------------------------------------
// RecompiledCodeReserve (implementations)
// --------------------------------------------------------------------------------------
// Constructor!
// Parameters:
// name - a nice long name that accurately describes the contents of this reserve.
RecompiledCodeReserve::RecompiledCodeReserve(std::string name)
: VirtualMemoryReserve(std::move(name))
{
}
RecompiledCodeReserve::~RecompiledCodeReserve()
{
Release();
}
void RecompiledCodeReserve::Assign(VirtualMemoryManagerPtr allocator, size_t offset, size_t size)
{
// Anything passed to the memory allocator must be page aligned.
size = Common::PageAlign(size);
// Since the memory has already been allocated as part of the main memory map, this should never fail.
u8* base = allocator->Alloc(offset, size);
if (!base)
{
Console.WriteLn("(RecompiledCodeReserve) Failed to allocate %zu bytes for %s at offset %zu", size, m_name.c_str(), offset);
pxFailRel("RecompiledCodeReserve allocation failed.");
}
VirtualMemoryReserve::Assign(std::move(allocator), base, size);
}
void RecompiledCodeReserve::Reset()
{
if (IsDevBuild && m_baseptr)
{
// Clear the recompiled code block to 0xcc (INT3) -- this helps disasm tools show
// the assembly dump more cleanly. We don't clear the block on Release builds since
// it can add a noticeable amount of overhead to large block recompilations.
std::memset(m_baseptr, 0xCC, m_size);
}
}

View File

@ -1,166 +0,0 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2010 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "common/General.h"
#include "common/Assertions.h"
#include <atomic>
#include <memory>
#include <mutex>
#include <string>
// --------------------------------------------------------------------------------------
// VirtualMemoryManager: Manages the allocation of PCSX2 VM
// Ensures that all memory is close enough together for rip-relative addressing
// --------------------------------------------------------------------------------------
class VirtualMemoryManager
{
DeclareNoncopyableObject(VirtualMemoryManager);
std::string m_name;
void* m_file_handle;
u8* m_baseptr;
// An array to track page usage (to trigger asserts if things try to overlap)
std::atomic<bool>* m_pageuse;
// reserved memory (in pages)
u32 m_pages_reserved;
public:
// If upper_bounds is nonzero and the OS fails to allocate memory that is below it,
// calls to IsOk() will return false and Alloc() will always return null pointers
// strict indicates that the allocation should quietly fail if the memory can't be mapped at `base`
VirtualMemoryManager(std::string name, const char* file_mapping_name, uptr base, size_t size, uptr upper_bounds = 0, bool strict = false);
~VirtualMemoryManager();
bool IsSharedMemory() const { return (m_file_handle != nullptr); }
void* GetFileHandle() const { return m_file_handle; }
u8* GetBase() const { return m_baseptr; }
u8* GetEnd() const { return (m_baseptr + m_pages_reserved * __pagesize); }
// Request the use of the memory at offsetLocation bytes from the start of the reserved memory area
// offsetLocation must be page-aligned
u8* Alloc(uptr offsetLocation, size_t size) const;
u8* AllocAtAddress(void* address, size_t size) const
{
return Alloc(size, static_cast<const u8*>(address) - m_baseptr);
}
void Free(void* address, size_t size) const;
// Was this VirtualMemoryManager successfully able to get its memory mapping?
// (If not, calls to Alloc will return null pointers)
bool IsOk() const { return m_baseptr != 0; }
};
typedef std::shared_ptr<const VirtualMemoryManager> VirtualMemoryManagerPtr;
// --------------------------------------------------------------------------------------
// VirtualMemoryBumpAllocator: Allocates memory for things that don't have explicitly-reserved spots
// --------------------------------------------------------------------------------------
class VirtualMemoryBumpAllocator
{
const VirtualMemoryManagerPtr m_allocator;
std::atomic<u8*> m_baseptr{0};
const u8* m_endptr = 0;
public:
VirtualMemoryBumpAllocator(VirtualMemoryManagerPtr allocator, size_t size, uptr offsetLocation);
u8* Alloc(size_t size);
const VirtualMemoryManagerPtr& GetAllocator() { return m_allocator; }
};
// --------------------------------------------------------------------------------------
// VirtualMemoryReserve
// --------------------------------------------------------------------------------------
class VirtualMemoryReserve
{
DeclareNoncopyableObject(VirtualMemoryReserve);
protected:
std::string m_name;
// Where the memory came from (so we can return it)
VirtualMemoryManagerPtr m_allocator;
u8* m_baseptr = nullptr;
size_t m_size = 0;
public:
VirtualMemoryReserve(std::string name);
virtual ~VirtualMemoryReserve();
// Initialize with the given piece of memory
// Note: The memory is already allocated, the allocator is for future use to free the region
// It may be null in which case there is no way to free the memory in a way it will be usable again
void Assign(VirtualMemoryManagerPtr allocator, u8* baseptr, size_t size);
u8* BumpAllocate(VirtualMemoryBumpAllocator& allocator, size_t size);
void Release();
bool IsOk() const { return m_baseptr != NULL; }
const std::string& GetName() const { return m_name; }
u8* GetPtr() { return m_baseptr; }
const u8* GetPtr() const { return m_baseptr; }
u8* GetPtrEnd() { return m_baseptr + m_size; }
const u8* GetPtrEnd() const { return m_baseptr + m_size; }
size_t GetSize() const { return m_size; }
operator void*() { return m_baseptr; }
operator const void*() const { return m_baseptr; }
operator u8*() { return (u8*)m_baseptr; }
operator const u8*() const { return (u8*)m_baseptr; }
u8& operator[](uint idx)
{
pxAssert(idx < m_size);
return *((u8*)m_baseptr + idx);
}
const u8& operator[](uint idx) const
{
pxAssert(idx < m_size);
return *((u8*)m_baseptr + idx);
}
};
// --------------------------------------------------------------------------------------
// RecompiledCodeReserve
// --------------------------------------------------------------------------------------
// A recompiled code reserve is a simple sequential-growth block of memory which is auto-
// cleared to INT 3 (0xcc) as needed.
//
class RecompiledCodeReserve : public VirtualMemoryReserve
{
typedef VirtualMemoryReserve _parent;
public:
RecompiledCodeReserve(std::string name);
~RecompiledCodeReserve();
void Assign(VirtualMemoryManagerPtr allocator, size_t offset, size_t size);
void Reset();
operator u8*() { return m_baseptr; }
operator const u8*() const { return m_baseptr; }
};

View File

@ -352,7 +352,6 @@
<ClCompile Include="USB\usb-pad\usb-turntable.cpp" />
<ClCompile Include="USB\usb-printer\usb-printer.cpp" />
<ClCompile Include="USB\USB.cpp" />
<ClCompile Include="VirtualMemory.cpp" />
<ClCompile Include="VMManager.cpp" />
<ClCompile Include="windows\Optimus.cpp" />
<ClCompile Include="Pcsx2Config.cpp" />
@ -723,7 +722,6 @@
<ClInclude Include="ps2\HwInternal.h" />
<ClInclude Include="Cache.h" />
<ClInclude Include="Memory.h" />
<ClInclude Include="VirtualMemory.h" />
<ClInclude Include="VMManager.h" />
<ClInclude Include="vtlb.h" />
<ClInclude Include="MTVU.h" />

View File

@ -1265,9 +1265,6 @@
<ClCompile Include="GSDumpReplayer.cpp">
<Filter>Tools</Filter>
</ClCompile>
<ClCompile Include="VirtualMemory.cpp">
<Filter>System</Filter>
</ClCompile>
<ClCompile Include="GS\Renderers\DX11\D3D11ShaderCache.cpp">
<Filter>System\Ps2\GS\Renderers\Direct3D11</Filter>
</ClCompile>
@ -2210,9 +2207,6 @@
<ClInclude Include="GS.h">
<Filter>System\Ps2\GS\GIF</Filter>
</ClInclude>
<ClInclude Include="VirtualMemory.h">
<Filter>System</Filter>
</ClInclude>
<ClInclude Include="GS\Renderers\DX11\D3D11ShaderCache.h">
<Filter>System\Ps2\GS\Renderers\Direct3D11</Filter>
</ClInclude>

View File

@ -842,7 +842,6 @@ static bool vtlb_IsHostCoalesced(u32 page)
static bool vtlb_GetMainMemoryOffsetFromPtr(uptr ptr, u32* mainmem_offset, u32* mainmem_size, PageProtectionMode* prot)
{
const uptr page_end = ptr + VTLB_PAGE_SIZE;
SysMainMemory& vmmem = GetVmMemory();
// EE memory and ROMs.
if (ptr >= (uptr)eeMem->Main && page_end <= (uptr)eeMem->ZeroRead)
@ -867,11 +866,11 @@ static bool vtlb_GetMainMemoryOffsetFromPtr(uptr ptr, u32* mainmem_offset, u32*
// VU memory - this includes both data and code for VU0/VU1.
// Practically speaking, this is only data, because the code goes through a handler.
if (ptr >= (uptr)vmmem.VUMemory().GetPtr() && page_end <= (uptr)vmmem.VUMemory().GetPtrEnd())
if (ptr >= (uptr)SysMemory::GetVUMem() && page_end <= (uptr)SysMemory::GetVUMemEnd())
{
const u32 vumem_offset = static_cast<u32>(ptr - (uptr)vmmem.VUMemory().GetPtr());
const u32 vumem_offset = static_cast<u32>(ptr - (uptr)SysMemory::GetVUMem());
*mainmem_offset = vumem_offset + HostMemoryMap::VUmemOffset;
*mainmem_size = vmmem.VUMemory().GetSize() - vumem_offset;
*mainmem_size = HostMemoryMap::VUmemSize - vumem_offset;
*prot = PageProtectionMode().Read().Write();
return true;
}
@ -932,7 +931,7 @@ static void vtlb_CreateFastmemMapping(u32 vaddr, u32 mainmem_offset, const PageP
const u32 host_page = vtlb_HostPage(page);
const u32 host_offset = vtlb_HostAlignOffset(mainmem_offset);
if (!s_fastmem_area->Map(GetVmMemory().MainMemory()->GetFileHandle(), host_offset,
if (!s_fastmem_area->Map(SysMemory::GetDataFileHandle(), host_offset,
s_fastmem_area->PagePointer(host_page), __pagesize, mode))
{
Console.Error("Failed to map vaddr %08X to mainmem offset %08X", vtlb_HostAlignOffset(vaddr), host_offset);
@ -1023,7 +1022,7 @@ bool vtlb_ResolveFastmemMapping(uptr* addr)
const u32 mainmem_offset = s_fastmem_virtual_mapping[vpage] + (vaddr & VTLB_PAGE_MASK);
FASTMEM_LOG("Resolved %p (vaddr %08X) to mainmem offset %08X", uaddr, vaddr, mainmem_offset);
*addr = ((uptr)GetVmMemory().MainMemory()->GetBase()) + mainmem_offset;
*addr = ((uptr)SysMemory::GetDataPtr(0)) + mainmem_offset;
return true;
}
@ -1293,47 +1292,31 @@ void vtlb_ResetFastmem()
}
}
static constexpr size_t VMAP_SIZE = sizeof(VTLBVirtual) * VTLB_VMAP_ITEMS;
// Reserves the vtlb core allocation used by various emulation components!
// [TODO] basemem - request allocating memory at the specified virtual location, which can allow
// for easier debugging and/or 3rd party cheat programs. If 0, the operating system
// default is used.
bool vtlb_Core_Alloc()
{
// Can't return regions to the bump allocator
static VTLBVirtual* vmap = nullptr;
if (!vmap)
static constexpr size_t VMAP_SIZE = sizeof(VTLBVirtual) * VTLB_VMAP_ITEMS;
static_assert(HostMemoryMap::VTLBVirtualMapSize == VMAP_SIZE);
pxAssert(!vtlbdata.vmap && !vtlbdata.fastmem_base && !s_fastmem_area);
vtlbdata.vmap = reinterpret_cast<VTLBVirtual*>(SysMemory::GetVTLBVirtualMap());
pxAssert(!s_fastmem_area);
s_fastmem_area = SharedMemoryMappingArea::Create(FASTMEM_AREA_SIZE);
if (!s_fastmem_area)
{
vmap = (VTLBVirtual*)GetVmMemory().BumpAllocator().Alloc(VMAP_SIZE);
if (!vmap)
{
Host::ReportErrorAsync("Error", "Failed to allocate vtlb vmap");
return false;
}
Host::ReportErrorAsync("Error", "Failed to allocate fastmem area");
return false;
}
if (!vtlbdata.vmap)
{
HostSys::MemProtect(vmap, VMAP_SIZE, PageProtectionMode().Read().Write());
vtlbdata.vmap = vmap;
}
if (!vtlbdata.fastmem_base)
{
pxAssert(!s_fastmem_area);
s_fastmem_area = SharedMemoryMappingArea::Create(FASTMEM_AREA_SIZE);
if (!s_fastmem_area)
{
Host::ReportErrorAsync("Error", "Failed to allocate fastmem area");
return false;
}
s_fastmem_virtual_mapping.resize(FASTMEM_PAGE_COUNT, NO_FASTMEM_MAPPING);
vtlbdata.fastmem_base = (uptr)s_fastmem_area->BasePointer();
Console.WriteLn(Color_StrongGreen, "Fastmem area: %p - %p",
vtlbdata.fastmem_base, vtlbdata.fastmem_base + (FASTMEM_AREA_SIZE - 1));
}
s_fastmem_virtual_mapping.resize(FASTMEM_PAGE_COUNT, NO_FASTMEM_MAPPING);
vtlbdata.fastmem_base = (uptr)s_fastmem_area->BasePointer();
DevCon.WriteLn(Color_StrongGreen, "Fastmem area: %p - %p",
vtlbdata.fastmem_base, vtlbdata.fastmem_base + (FASTMEM_AREA_SIZE - 1));
if (!HostSys::InstallPageFaultHandler(&vtlb_private::PageFaultHandler))
{
@ -1344,22 +1327,17 @@ bool vtlb_Core_Alloc()
return true;
}
static constexpr size_t PPMAP_SIZE = sizeof(*vtlbdata.ppmap) * VTLB_VMAP_ITEMS;
// The LUT is only used for 1 game so we allocate it only when the gamefix is enabled (save 4MB)
// However automatic gamefix is done after the standard init so a new init function was done.
void vtlb_Alloc_Ppmap()
{
static constexpr size_t PPMAP_SIZE = sizeof(*vtlbdata.ppmap) * VTLB_VMAP_ITEMS;
static_assert(HostMemoryMap::VTLBAddressMapSize == PPMAP_SIZE);
if (vtlbdata.ppmap)
return;
static u32* ppmap = nullptr;
if (!ppmap)
ppmap = (u32*)GetVmMemory().BumpAllocator().Alloc(PPMAP_SIZE);
HostSys::MemProtect(ppmap, PPMAP_SIZE, PageProtectionMode().Read().Write());
vtlbdata.ppmap = ppmap;
vtlbdata.ppmap = reinterpret_cast<u32*>(SysMemory::GetVTLBAddressMap());
// By default a 1:1 virtual to physical mapping
for (u32 i = 0; i < VTLB_VMAP_ITEMS; i++)
@ -1370,16 +1348,8 @@ void vtlb_Core_Free()
{
HostSys::RemovePageFaultHandler(&vtlb_private::PageFaultHandler);
if (vtlbdata.vmap)
{
HostSys::MemProtect(vtlbdata.vmap, VMAP_SIZE, PageProtectionMode());
vtlbdata.vmap = nullptr;
}
if (vtlbdata.ppmap)
{
HostSys::MemProtect(vtlbdata.ppmap, PPMAP_SIZE, PageProtectionMode());
vtlbdata.ppmap = nullptr;
}
vtlbdata.vmap = nullptr;
vtlbdata.ppmap = nullptr;
vtlb_RemoveFastmemMappings();
vtlb_ClearLoadStoreInfo();
@ -1390,36 +1360,6 @@ void vtlb_Core_Free()
s_fastmem_area.reset();
}
// --------------------------------------------------------------------------------------
// VtlbMemoryReserve (implementations)
// --------------------------------------------------------------------------------------
VtlbMemoryReserve::VtlbMemoryReserve(std::string name)
: VirtualMemoryReserve(std::move(name))
{
}
void VtlbMemoryReserve::Assign(VirtualMemoryManagerPtr allocator, size_t offset, size_t size)
{
// Anything passed to the memory allocator must be page aligned.
size = Common::PageAlign(size);
// Since the memory has already been allocated as part of the main memory map, this should never fail.
u8* base = allocator->Alloc(offset, size);
if (!base)
{
Console.WriteLn("(VtlbMemoryReserve) Failed to allocate %zu bytes for %s at offset %zu", size, m_name.c_str(), offset);
pxFailRel("VtlbMemoryReserve allocation failed.");
}
VirtualMemoryReserve::Assign(std::move(allocator), base, size);
}
void VtlbMemoryReserve::Reset()
{
std::memset(GetPtr(), 0, GetSize());
}
// ===========================================================================================
// Memory Protection and Block Checking, vtlb Style!
// ===========================================================================================

View File

@ -17,7 +17,7 @@
#include "MemoryTypes.h"
#include "SingleRegisterTypes.h"
#include "VirtualMemory.h"
#include "System.h"
static const uptr VTLB_AllocUpperBounds = _1gb * 2;
@ -126,70 +126,6 @@ extern void vtlb_DynGenWrite_Const(u32 bits, bool xmm, u32 addr_const, int value
extern void vtlb_DynGenDispatchers();
// --------------------------------------------------------------------------------------
// VtlbMemoryReserve
// --------------------------------------------------------------------------------------
class VtlbMemoryReserve : public VirtualMemoryReserve
{
public:
VtlbMemoryReserve(std::string name);
void Assign(VirtualMemoryManagerPtr allocator, size_t offset, size_t size);
virtual void Reset();
};
// --------------------------------------------------------------------------------------
// eeMemoryReserve
// --------------------------------------------------------------------------------------
class eeMemoryReserve : public VtlbMemoryReserve
{
typedef VtlbMemoryReserve _parent;
public:
eeMemoryReserve();
~eeMemoryReserve();
void Assign(VirtualMemoryManagerPtr allocator);
void Release();
void Reset() override;
};
// --------------------------------------------------------------------------------------
// iopMemoryReserve
// --------------------------------------------------------------------------------------
class iopMemoryReserve : public VtlbMemoryReserve
{
typedef VtlbMemoryReserve _parent;
public:
iopMemoryReserve();
~iopMemoryReserve();
void Assign(VirtualMemoryManagerPtr allocator);
void Release();
void Reset() override;
};
// --------------------------------------------------------------------------------------
// vuMemoryReserve
// --------------------------------------------------------------------------------------
class vuMemoryReserve : public VtlbMemoryReserve
{
typedef VtlbMemoryReserve _parent;
public:
vuMemoryReserve();
~vuMemoryReserve();
void Assign(VirtualMemoryManagerPtr allocator);
void Release();
void Reset() override;
};
namespace vtlb_private
{
static const uint VTLB_PAGE_BITS = 12;

View File

@ -27,7 +27,7 @@
#include "IopBios.h"
#include "IopHw.h"
#include "Common.h"
#include "VirtualMemory.h"
#include "System.h"
#include "VMManager.h"
#include <time.h>
@ -72,23 +72,22 @@ u32 psxhwLUT[0x10000];
static __fi u32 HWADDR(u32 mem) { return psxhwLUT[mem >> 16] + mem; }
static RecompiledCodeReserve* recMem = NULL;
static BASEBLOCK* recRAM = NULL; // and the ptr to the blocks here
static BASEBLOCK* recROM = NULL; // and here
static BASEBLOCK* recROM1 = NULL; // also here
static BASEBLOCK* recROM2 = NULL; // also here
static BASEBLOCK* recRAM = nullptr; // and the ptr to the blocks here
static BASEBLOCK* recROM = nullptr; // and here
static BASEBLOCK* recROM1 = nullptr; // also here
static BASEBLOCK* recROM2 = nullptr; // also here
static BaseBlocks recBlocks;
static u8* recPtr = NULL;
static u8* recPtr = nullptr;
static u8* recPtrEnd = nullptr;
u32 psxpc; // recompiler psxpc
int psxbranch; // set for branch
u32 g_iopCyclePenalty;
static EEINST* s_pInstCache = NULL;
static EEINST* s_pInstCache = nullptr;
static u32 s_nInstCacheSize = 0;
static BASEBLOCK* s_pCurBlock = NULL;
static BASEBLOCKEX* s_pCurBlockEx = NULL;
static BASEBLOCK* s_pCurBlock = nullptr;
static BASEBLOCKEX* s_pCurBlockEx = nullptr;
static u32 s_nEndBlock = 0; // what psxpc the current block ends
static u32 s_branchTo;
@ -96,7 +95,7 @@ static bool s_nBlockFF;
static u32 s_saveConstRegs[32];
static u32 s_saveHasConstReg = 0, s_saveFlushedConstReg = 0;
static EEINST* s_psaveInstInfo = NULL;
static EEINST* s_psaveInstInfo = nullptr;
u32 s_psxBlockCycles = 0; // cycles of current block recompiling
static u32 s_savenBlockCycles = 0;
@ -879,15 +878,9 @@ static const uint m_recBlockAllocSize =
static void recReserve()
{
if (recMem)
return;
recPtr = SysMemory::GetIOPRec();
recPtrEnd = SysMemory::GetIOPRecEnd() - _64kb;
recMem = new RecompiledCodeReserve("R3000A Recompiler Cache");
recMem->Assign(GetVmMemory().CodeMemory(), HostMemoryMap::IOPrecOffset, 32 * _1mb);
}
static void recAlloc()
{
// Goal: Allocate BASEBLOCKs for every possible branch target in IOP memory.
// Any 4-byte aligned address makes a valid branch target as per MIPS design (all instructions are
// always 4 bytes long).
@ -910,23 +903,18 @@ static void recAlloc()
recROM2 = (BASEBLOCK*)curpos;
curpos += (Ps2MemSize::Rom2 / 4) * sizeof(BASEBLOCK);
pxAssertRel(!s_pInstCache, "InstCache not allocated");
s_nInstCacheSize = 128;
s_pInstCache = (EEINST*)malloc(sizeof(EEINST) * s_nInstCacheSize);
if (!s_pInstCache)
{
s_nInstCacheSize = 128;
s_pInstCache = (EEINST*)malloc(sizeof(EEINST) * s_nInstCacheSize);
if (!s_pInstCache)
pxFailRel("Failed to allocate R3000 InstCache array.");
}
pxFailRel("Failed to allocate R3000 InstCache array.");
}
void recResetIOP()
{
DevCon.WriteLn("iR3000A Recompiler reset.");
recAlloc();
recMem->Reset();
xSetPtr(*recMem);
xSetPtr(SysMemory::GetIOPRec());
_DynGen_Dispatchers();
recPtr = xGetPtr();
@ -983,12 +971,13 @@ void recResetIOP()
static void recShutdown()
{
safe_delete(recMem);
safe_aligned_free(m_recBlockAlloc);
safe_free(s_pInstCache);
s_nInstCacheSize = 0;
recPtr = nullptr;
recPtrEnd = nullptr;
}
static void iopClearRecLUT(BASEBLOCK* base, int count)
@ -1561,7 +1550,7 @@ static void iopRecRecompile(const u32 startpc)
pxAssert(startpc);
// if recPtr reached the mem limit reset whole mem
if (recPtr >= (recMem->GetPtrEnd() - _64kb))
if (recPtr >= recPtrEnd)
{
recResetIOP();
}
@ -1754,7 +1743,7 @@ StartRecomp:
}
}
pxAssert(xGetPtr() < recMem->GetPtrEnd());
pxAssert(xGetPtr() < recPtrEnd);
pxAssert(xGetPtr() - recPtr < _64kb);
s_pCurBlockEx->x86size = xGetPtr() - recPtr;

View File

@ -22,10 +22,10 @@
#include "GS.h"
#include "Memory.h"
#include "Patch.h"
#include "System.h"
#include "R3000A.h"
#include "R5900OpcodeTables.h"
#include "VMManager.h"
#include "VirtualMemory.h"
#include "vtlb.h"
#include "x86/BaseblockEx.h"
#include "x86/iR5900.h"
@ -83,23 +83,23 @@ eeProfiler EE::Profiler;
#define X86
static RecompiledCodeReserve* recMem = NULL;
static u8* recRAMCopy = NULL;
static u8* recLutReserve_RAM = NULL;
static u8* recRAMCopy = nullptr;
static u8* recLutReserve_RAM = nullptr;
static const size_t recLutSize = (Ps2MemSize::MainRam + Ps2MemSize::Rom + Ps2MemSize::Rom1 + Ps2MemSize::Rom2) * wordsize / 4;
static BASEBLOCK* recRAM = NULL; // and the ptr to the blocks here
static BASEBLOCK* recROM = NULL; // and here
static BASEBLOCK* recROM1 = NULL; // also here
static BASEBLOCK* recROM2 = NULL; // also here
static BASEBLOCK* recRAM = nullptr; // and the ptr to the blocks here
static BASEBLOCK* recROM = nullptr; // and here
static BASEBLOCK* recROM1 = nullptr; // also here
static BASEBLOCK* recROM2 = nullptr; // also here
static BaseBlocks recBlocks;
static u8* recPtr = NULL;
EEINST* s_pInstCache = NULL;
static u8* recPtr = nullptr;
static u8* recPtrEnd = nullptr;
EEINST* s_pInstCache = nullptr;
static u32 s_nInstCacheSize = 0;
static BASEBLOCK* s_pCurBlock = NULL;
static BASEBLOCKEX* s_pCurBlockEx = NULL;
static BASEBLOCK* s_pCurBlock = nullptr;
static BASEBLOCKEX* s_pCurBlockEx = nullptr;
u32 s_nEndBlock = 0; // what pc the current block ends
u32 s_branchTo;
static bool s_nBlockFF;
@ -107,7 +107,7 @@ static bool s_nBlockFF;
// save states for branches
GPR_reg64 s_saveConstRegs[32];
static u32 s_saveHasConstReg = 0, s_saveFlushedConstReg = 0;
static EEINST* s_psaveInstInfo = NULL;
static EEINST* s_psaveInstInfo = nullptr;
static u32 s_savenBlockCycles = 0;
@ -512,15 +512,9 @@ static __ri void ClearRecLUT(BASEBLOCK* base, int memsize)
static void recReserve()
{
if (recMem)
return;
recPtr = SysMemory::GetEERec();
recPtrEnd = SysMemory::GetEERecEnd() - _64kb;
recMem = new RecompiledCodeReserve("R5900 Recompiler Cache");
recMem->Assign(GetVmMemory().CodeMemory(), HostMemoryMap::EErecOffset, 64 * _1mb);
}
static void recAlloc()
{
if (!recRAMCopy)
{
recRAMCopy = (u8*)_aligned_malloc(Ps2MemSize::MainRam, 4096);
@ -577,13 +571,11 @@ static void recAlloc()
recLUT_SetPage(recLUT, hwLUT, recROM2, 0xa000, i, i - 0x1e40);
}
if (s_pInstCache == NULL)
{
s_nInstCacheSize = 128;
s_pInstCache = (EEINST*)malloc(sizeof(EEINST) * s_nInstCacheSize);
if (!s_pInstCache)
pxFailRel("Failed to allocate R5900 InstCache array");
}
pxAssertRel(!s_pInstCache, "InstCache not allocated");
s_nInstCacheSize = 128;
s_pInstCache = (EEINST*)malloc(sizeof(EEINST) * s_nInstCacheSize);
if (!s_pInstCache)
pxFailRel("Failed to allocate R5900 InstCache array");
}
alignas(16) static u16 manual_page[Ps2MemSize::MainRam >> 12];
@ -596,10 +588,7 @@ static void recResetRaw()
EE::Profiler.Reset();
recAlloc();
recMem->Reset();
xSetPtr(*recMem);
xSetPtr(SysMemory::GetEERec());
_DynGen_Dispatchers();
vtlb_DynGenDispatchers();
recPtr = xGetPtr();
@ -620,9 +609,8 @@ static void recResetRaw()
g_resetEeScalingStats = true;
}
static void recShutdown()
void recShutdown()
{
safe_delete(recMem);
safe_aligned_free(recRAMCopy);
safe_aligned_free(recLutReserve_RAM);
@ -632,6 +620,9 @@ static void recShutdown()
safe_free(s_pInstCache);
s_nInstCacheSize = 0;
recPtr = nullptr;
recPtrEnd = nullptr;
}
void recStep()
@ -909,7 +900,7 @@ void SetBranchImm(u32 imm)
u8* recBeginThunk()
{
// if recPtr reached the mem limit reset whole mem
if (recPtr >= (recMem->GetPtrEnd() - _64kb))
if (recPtr >= recPtrEnd)
eeRecNeedsReset = true;
xSetPtr(recPtr);
@ -923,7 +914,7 @@ u8* recEndThunk()
{
u8* block_end = x86Ptr;
pxAssert(block_end < recMem->GetPtrEnd());
pxAssert(block_end < recPtrEnd);
recPtr = block_end;
return block_end;
}
@ -2208,7 +2199,7 @@ static void recRecompile(const u32 startpc)
pxAssert(startpc);
// if recPtr reached the mem limit reset whole mem
if (recPtr >= (recMem->GetPtrEnd() - _64kb))
if (recPtr >= recPtrEnd)
eeRecNeedsReset = true;
if (HWADDR(startpc) == VMManager::Internal::GetCurrentELFEntryPoint())
@ -2741,7 +2732,7 @@ StartRecomp:
}
}
pxAssert(xGetPtr() < recMem->GetPtrEnd());
pxAssert(xGetPtr() < recPtrEnd);
s_pCurBlockEx->x86size = static_cast<u32>(xGetPtr() - recPtr);
@ -2757,8 +2748,8 @@ StartRecomp:
pxAssert((g_cpuHasConstReg & g_cpuFlushedConstReg) == g_cpuHasConstReg);
s_pCurBlock = NULL;
s_pCurBlockEx = NULL;
s_pCurBlock = nullptr;
s_pCurBlockEx = nullptr;
}
R5900cpu recCpu = {

View File

@ -26,15 +26,6 @@
// Micro VU - Main Functions
//------------------------------------------------------------------
void mVUreserveCache(microVU& mVU)
{
mVU.cache_reserve = new RecompiledCodeReserve(StringUtil::StdStringFromFormat("Micro VU%u Recompiler Cache", mVU.index));
const size_t alloc_offset = mVU.index ? HostMemoryMap::mVU0recOffset : HostMemoryMap::mVU1recOffset;
mVU.cache_reserve->Assign(GetVmMemory().CodeMemory(), alloc_offset, mVU.cacheSize * _1mb);
mVU.cache = mVU.cache_reserve->GetPtr();
}
// Only run this once per VU! ;)
void mVUinit(microVU& mVU, uint vuIndex)
{
@ -46,12 +37,8 @@ void mVUinit(microVU& mVU, uint vuIndex)
mVU.microMemSize = (mVU.index ? 0x4000 : 0x1000);
mVU.progSize = (mVU.index ? 0x4000 : 0x1000) / 4;
mVU.progMemMask = mVU.progSize-1;
mVU.cacheSize = mVUcacheReserve;
mVU.cache = nullptr;
mVU.startFunct = nullptr;
mVU.exitFunct = nullptr;
mVUreserveCache(mVU);
mVU.cache = vuIndex ? SysMemory::GetVU1Rec() : SysMemory::GetVU0Rec();
mVU.prog.x86end = (vuIndex ? SysMemory::GetVU1RecEnd() : SysMemory::GetVU0RecEnd()) - (mVUcacheSafeZone * _1mb);
mVU.regAlloc.reset(new microRegAlloc(mVU.index));
}
@ -59,7 +46,6 @@ void mVUinit(microVU& mVU, uint vuIndex)
// Resets Rec Data
void mVUreset(microVU& mVU, bool resetReserve)
{
if (THREAD_VU1)
{
DevCon.Warning("mVU Reset");
@ -70,9 +56,6 @@ void mVUreset(microVU& mVU, bool resetReserve)
}
VU0.VI[REG_VPU_STAT].UL &= ~0x100;
}
// Restore reserve to uncommitted state
if (resetReserve)
mVU.cache_reserve->Reset();
xSetPtr(mVU.cache);
mVUdispatcherAB(mVU);
@ -95,7 +78,6 @@ void mVUreset(microVU& mVU, bool resetReserve)
// Setup Dynarec Cache Limits for Each Program
mVU.prog.x86start = xGetAlignedCallTarget();
mVU.prog.x86ptr = mVU.prog.x86start;
mVU.prog.x86end = mVU.cache + ((mVU.cacheSize - mVUcacheSafeZone) * _1mb);
for (u32 i = 0; i < (mVU.progSize / 2); i++)
{
@ -118,9 +100,6 @@ void mVUreset(microVU& mVU, bool resetReserve)
// Free Allocated Resources
void mVUclose(microVU& mVU)
{
safe_delete(mVU.cache_reserve);
// Delete Programs and Block Managers
for (u32 i = 0; i < (mVU.progSize / 2); i++)
{

View File

@ -30,7 +30,7 @@ using namespace x86Emitter;
#include "Gif_Unit.h"
#include "iR5900.h"
#include "R5900OpcodeTables.h"
#include "VirtualMemory.h"
#include "System.h"
#include "common/emitter/x86emitter.h"
#include "microVU_Misc.h"
#include "microVU_IR.h"
@ -91,9 +91,7 @@ struct microProgManager
microRegInfo lpState; // Pipeline state from where program left off (useful for continuing execution)
};
static const uint mVUdispCacheSize = __pagesize; // Dispatcher Cache Size (in bytes)
static const uint mVUcacheSafeZone = 3; // Safe-Zone for program recompilation (in megabytes)
static const uint mVUcacheReserve = 64; // mVU0, mVU1 Reserve Cache Size (in megabytes)
struct microVU
{
@ -117,7 +115,6 @@ struct microVU
std::unique_ptr<microRegAlloc> regAlloc; // Reg Alloc Class
std::FILE* logFile; // Log File Pointer
RecompiledCodeReserve* cache_reserve;
u8* cache; // Dynarec Cache Start (where we will start writing the recompiled code to)
u8* startFunct; // Function Ptr to the recompiler dispatcher (start)
u8* exitFunct; // Function Ptr to the recompiler dispatcher (exit)

View File

@ -17,7 +17,6 @@
#include "Vif.h"
#include "VU.h"
#include "VirtualMemory.h"
#include "common/emitter/x86emitter.h"
@ -32,12 +31,10 @@ typedef void (*nVifrecCall)(uptr dest, uptr src);
extern void mVUmergeRegs(const xRegisterSSE& dest, const xRegisterSSE& src, int xyzw, bool modXYZW = 0);
extern void mVUsaveReg(const xRegisterSSE& reg, xAddressVoid ptr, int xyzw, bool modXYZW);
extern void _nVifUnpack (int idx, const u8* data, uint mode, bool isFill);
extern void dVifReserve (int idx);
extern void dVifReset (int idx);
extern void dVifClose (int idx);
extern void dVifRelease (int idx);
extern void VifUnpackSSE_Init();
extern void VifUnpackSSE_Destroy();
_vifT extern void dVifUnpack(const u8* data, bool isFill);
@ -64,8 +61,8 @@ struct nVifStruct
// (templates are used for most or all VIF indexing)
u32 idx;
RecompiledCodeReserve* recReserve;
u8* recWritePtr; // current write pos into the reserve
u8* recEndPtr;
HashBucket vifBlocks; // Vif Blocks

View File

@ -24,42 +24,19 @@
#include "common/StringUtil.h"
#include "fmt/core.h"
static void recReset(int idx)
void dVifReset(int idx)
{
nVif[idx].vifBlocks.reset();
nVif[idx].recReserve->Reset();
nVif[idx].recWritePtr = nVif[idx].recReserve->GetPtr();
}
void dVifReserve(int idx)
{
if (nVif[idx].recReserve)
return;
const size_t offset = idx ? HostMemoryMap::VIF1recOffset : HostMemoryMap::VIF0recOffset;
nVif[idx].recReserve = new RecompiledCodeReserve(StringUtil::StdStringFromFormat("VIF%u Unpack Recompiler Cache", idx));
nVif[idx].recReserve->Assign(GetVmMemory().CodeMemory(), offset, 8 * _1mb);
}
void dVifReset(int idx)
{
pxAssertDev(nVif[idx].recReserve, "Dynamic VIF recompiler reserve must be created prior to VIF use or reset!");
recReset(idx);
}
void dVifClose(int idx)
{
if (nVif[idx].recReserve)
nVif[idx].recReserve->Reset();
const size_t size = idx ? HostMemoryMap::VIF1recSize : HostMemoryMap::VIF0recSize;
nVif[idx].recWritePtr = SysMemory::GetCodePtr(offset);
nVif[idx].recEndPtr = nVif[idx].recWritePtr + (size - _256kb);
}
void dVifRelease(int idx)
{
dVifClose(idx);
safe_delete(nVif[idx].recReserve);
nVif[idx].vifBlocks.clear();
}
VifUnpackSSE_Dynarec::VifUnpackSSE_Dynarec(const nVifStruct& vif_, const nVifBlock& vifBlock_)
@ -368,11 +345,11 @@ _vifT __fi nVifBlock* dVifCompile(nVifBlock& block, bool isFill)
nVifStruct& v = nVif[idx];
// Check size before the compilation
if (v.recWritePtr > (v.recReserve->GetPtrEnd() - _256kb))
if (v.recWritePtr >= v.recEndPtr)
{
DevCon.WriteLn("nVif Recompiler Cache Reset! [0x%016" PRIXPTR " > 0x%016" PRIXPTR "]",
v.recWritePtr, v.recReserve->GetPtrEnd());
recReset(idx);
v.recWritePtr, v.recEndPtr);
dVifReset(idx);
}
// Compile the block now

View File

@ -24,9 +24,6 @@
#define xMOV64(regX, loc) xMOVUPS (regX, loc)
#define xMOV128(regX, loc) xMOVUPS (regX, loc)
//alignas(__pagesize) static u8 nVifUpkExec[__pagesize*4];
static RecompiledCodeReserve* nVifUpkExec = NULL;
// =====================================================================================================
// VifUnpackSSE_Base Section
// =====================================================================================================
@ -376,14 +373,9 @@ static void nVifGen(int usn, int mask, int curCycle)
void VifUnpackSSE_Init()
{
if (nVifUpkExec)
return;
DevCon.WriteLn("Generating SSE-optimized unpacking functions for VIF interpreters...");
nVifUpkExec = new RecompiledCodeReserve("VIF SSE-optimized Unpacking Functions");
nVifUpkExec->Assign(GetVmMemory().CodeMemory(), HostMemoryMap::VIFUnpackRecOffset, _1mb);
xSetPtr(*nVifUpkExec);
xSetPtr(SysMemory::GetVIFUnpackRec());
for (int a = 0; a < 2; a++)
for (int b = 0; b < 2; b++)
@ -392,17 +384,12 @@ void VifUnpackSSE_Init()
DevCon.WriteLn("Unpack function generation complete. Generated function statistics:");
DevCon.Indent().WriteLn(
"Reserved buffer : %u bytes @ 0x%016" PRIXPTR "\n"
"x86 code generated : %u bytes\n",
(uint)nVifUpkExec->GetSize(),
nVifUpkExec->GetPtr(),
(uint)(xGetPtr() - nVifUpkExec->GetPtr())
"Reserved buffer : %zu bytes @ 0x%016" PRIXPTR "\n"
"x86 code generated : %zu bytes\n",
SysMemory::GetVIFUnpackRecEnd() - SysMemory::GetVIFUnpackRec(),
SysMemory::GetVIFUnpackRec(),
xGetPtr() - SysMemory::GetVIFUnpackRec()
);
Perf::any.Register(nVifUpkExec->GetPtr(), xGetPtr() - nVifUpkExec->GetPtr(), "VIF Unpack");
}
void VifUnpackSSE_Destroy()
{
safe_delete(nVifUpkExec);
Perf::any.Register(SysMemory::GetVIFUnpackRec(), xGetPtr() - SysMemory::GetVIFUnpackRec(), "VIF Unpack");
}