diff --git a/common/build/Utilities/utilities.vcproj b/common/build/Utilities/utilities.vcproj index f14e0f9ff..c7f540d76 100644 --- a/common/build/Utilities/utilities.vcproj +++ b/common/build/Utilities/utilities.vcproj @@ -425,6 +425,10 @@ RelativePath="..\..\include\Utilities\MemsetFast.inl" > + + diff --git a/common/include/Utilities/PageFaultSource.h b/common/include/Utilities/PageFaultSource.h index 5073744c5..2d4bf70bb 100644 --- a/common/include/Utilities/PageFaultSource.h +++ b/common/include/Utilities/PageFaultSource.h @@ -107,16 +107,11 @@ protected: uptr m_reserved; // Incremental size by which the buffer grows (in pages) - uptr m_block_size; + uptr m_blocksize; // Protection mode to be applied to committed blocks. PageProtectionMode m_prot_mode; - // Specifies the number of blocks that should be committed automatically when the - // reserve is created. Typically this chunk is larger than the block size, and - // should be based on whatever typical overhead is needed for basic block use. - uint m_def_commit; - // Records the number of pages committed to memory. // (metric for analysis of buffer usage) uptr m_commited; @@ -131,6 +126,9 @@ public: virtual void* Reserve( uint size, uptr base = 0, uptr upper_bounds = 0 ); virtual void Reset(); virtual void Free(); + virtual bool TryResize( uint newsize ); + + virtual void CommitBlocks( uptr page, uint blocks ); bool IsOk() const { return m_baseptr != NULL; } wxString GetName() const { return Name; } @@ -153,6 +151,17 @@ public: protected: void OnPageFaultEvent( const PageFaultInfo& info, bool& handled ); + // This function is called from OnPageFaultEvent after the address has been translated + // and confirmed to apply to this reserved area in question. OnPageFaultEvent contains + // a try/catch exception handler, which ensures "reasonable" error response behavior if + // this function throws exceptions. + // + // Important: This method is called from the context of an exception/signal handler. On + // Windows this isn't a big deal (most operations are ok). On Linux, however, logging + // and other facilities are probably not a good idea. + virtual void DoCommitAndProtect( uptr offset )=0; + + // This function is called for every committed block. virtual void OnCommittedBlock( void* block )=0; virtual void OnOutOfMemory( const Exception::OutOfMemory& ex, void* blockptr, bool& handled ) { @@ -190,27 +199,25 @@ class SpatialArrayReserve : public BaseVirtualMemoryReserve typedef BaseVirtualMemoryReserve __parent; protected: + uint m_numblocks; + + // Array of block bits, each bit indicating if the block has been committed to memory + // or not. The array length is typically determined via ((numblocks+7) / 8), though the + // actual array size may be larger in order to accommodate 32-bit or 128-bit accelerated + // operations. + ScopedAlignedAlloc m_blockbits; public: SpatialArrayReserve( const wxString& name, uint defCommit = 0 ); virtual void* Reserve( uint size, uptr base = 0, uptr upper_bounds = 0 ); + virtual void Reset(); void OnCommittedBlock( void* block ); void OnOutOfMemory( const Exception::OutOfMemory& ex, void* blockptr, bool& handled ); - // This method allows the programmer to specify the block size of the array as a function - // of its reserved size. This function *must* be called *after* the reserve has been made. - // Calls to this function prior to initializing the reserve will be ignored (and will - // generate an assertion in debug builds). SpatialArrayReserve& SetBlockCount( uint blocks ); - - // Sets the block size via pages (pages are defined by the __pagesize global, which is - // typically 4096). SpatialArrayReserve& SetBlockSizeInPages( uint bytes ); - - // This method assigns the block size of the spatial array, in bytes. The actual size of - // each block will be rounded up to the nearest page size. The resulting size is returned. uint SetBlockSize( uint bytes ); @@ -219,6 +226,9 @@ public: operator u8*() { return (u8*)m_baseptr; } operator const u8*() const { return (u8*)m_baseptr; } + +protected: + uint _calcBlockBitArrayLength() const; }; #ifdef __LINUX__ diff --git a/common/src/Utilities/VirtualMemory.cpp b/common/src/Utilities/VirtualMemory.cpp index 393d1d602..52a159247 100644 --- a/common/src/Utilities/VirtualMemory.cpp +++ b/common/src/Utilities/VirtualMemory.cpp @@ -58,7 +58,7 @@ BaseVirtualMemoryReserve::BaseVirtualMemoryReserve( const wxString& name ) m_commited = 0; m_reserved = 0; m_baseptr = NULL; - m_block_size = __pagesize; + m_blocksize = __pagesize; m_prot_mode = PageAccess_None(); } @@ -118,45 +118,84 @@ void BaseVirtualMemoryReserve::Free() HostSys::Munmap((uptr)m_baseptr, m_reserved*__pagesize); } -void BaseVirtualMemoryReserve::OnPageFaultEvent(const PageFaultInfo& info, bool& handled) +// If growing the array, or if shrinking the array to some point that's still *greater* than the +// committed memory range, then attempt a passive "on-the-fly" resize that maps/unmaps some portion +// of the reserve. +// +// If the above conditions are not met, or if the map/unmap fails, this method returns false. +// The caller will be responsible for manually resetting the reserve. +// +// Parameters: +// newsize - new size of the reserved buffer, in bytes. +bool BaseVirtualMemoryReserve::TryResize( uint newsize ) { - uptr offset = (info.addr - (uptr)m_baseptr) / __pagesize; - if (offset >= m_reserved) return; + uint newPages = (newsize + __pagesize - 1) / __pagesize; - try { + if (newPages > m_reserved) + { + uint toReservePages = newPages - m_reserved; + uint toReserveBytes = toReservePages * __pagesize; - if (!m_commited && m_def_commit) + DevCon.WriteLn( L"%-32s is being expanded by %u pages.", Name.c_str(), toReservePages); + + m_baseptr = (void*)HostSys::MmapReserve((uptr)GetPtrEnd(), toReserveBytes); + + if (!m_baseptr) { - const uint camt = m_def_commit * __pagesize; - // first block being committed! Commit the default requested - // amount if its different from the blocksize. - - HostSys::MmapCommitPtr(m_baseptr, camt, m_prot_mode); - - u8* init = (u8*)m_baseptr; - u8* endpos = init + camt; - for( ; init 0x%08X", m_baseptr, (uptr)m_baseptr+toReserveBytes); } - void* bleh = (u8*)m_baseptr + (offset * __pagesize); + DevCon.WriteLn( Color_Blue, L"%-32s @ 0x%08X -> 0x%08X [%umb]", Name.c_str(), + m_baseptr, (uptr)m_baseptr+toReserveBytes, toReserveBytes / _1mb); + } + else if (newPages < m_reserved) + { + if (m_commited > newsize) return false; - // Depending on the operating system, one or both of these could fail if the system - // is low on either physical ram or virtual memory. - HostSys::MmapCommitPtr(bleh, m_block_size*__pagesize, m_prot_mode); + uint toRemovePages = m_reserved - newPages; + uint toRemoveBytes = toRemovePages * __pagesize; - m_commited += m_block_size; - OnCommittedBlock(bleh); + DevCon.WriteLn( L"%-32s is being shrunk by %u pages.", Name.c_str(), toRemovePages); + HostSys::MmapResetPtr(GetPtrEnd(), toRemoveBytes); + + DevCon.WriteLn( Color_Blue, L"%-32s @ 0x%08X -> 0x%08X [%umb]", Name.c_str(), + m_baseptr, (uptr)m_baseptr+toRemoveBytes, toRemoveBytes / _1mb); + } + + return true; +} + +void BaseVirtualMemoryReserve::CommitBlocks( uptr page, uint blocks ) +{ + const uint blocksbytes = blocks * m_blocksize * __pagesize; + void* blockptr = (u8*)m_baseptr + (page * __pagesize); + + // Depending on the operating system, one or both of these could fail if the system + // is low on either physical ram or virtual memory. + HostSys::MmapCommitPtr(blockptr, blocksbytes, m_prot_mode); + + u8* init = (u8*)blockptr; + u8* endpos = init + blocksbytes; + for( ; init= m_reserved)) return; + + try { + DoCommitAndProtect( offset ); handled = true; } catch (Exception::OutOfMemory& ex) { + handled = false; OnOutOfMemory( ex, (u8*)m_baseptr + (offset * __pagesize), handled ); } #ifndef __WXMSW__ @@ -170,8 +209,8 @@ void BaseVirtualMemoryReserve::OnPageFaultEvent(const PageFaultInfo& info, bool& // *unless* its attached to a debugger; then we can, at a bare minimum, trap it. catch (Exception::BaseException& ex) { - wxTrap(); handled = false; + wxTrap(); } #endif } @@ -181,14 +220,79 @@ void BaseVirtualMemoryReserve::OnPageFaultEvent(const PageFaultInfo& info, bool& // SpatialArrayReserve (implementations) // -------------------------------------------------------------------------------------- +uint SpatialArrayReserve::_calcBlockBitArrayLength() const +{ + return (m_numblocks + 127) / 128; +} + void* SpatialArrayReserve::Reserve( uint size, uptr base, uptr upper_bounds ) { return __parent::Reserve( size, base, upper_bounds ); } +// Resets/clears the spatial array, reducing the memory commit pool overhead to zero (0). +void SpatialArrayReserve::Reset() +{ + __parent::Reset(); + memzero_sse_a(m_blockbits.GetPtr(), _calcBlockBitArrayLength()); +} + +// This method allows the programmer to specify the block size of the array as a function +// of its reserved size. This function *must* be called *after* the reserve has been made, +// and *before* the array contents have been accessed. +// +// Calls to this function prior to initializing the reserve or after the reserve has been +// accessed (resulting in committed blocks) will be ignored -- and will generate an assertion +// in debug builds. +SpatialArrayReserve& SpatialArrayReserve::SetBlockCount( uint blocks ) +{ + pxAssumeDev( !m_commited, "Invalid object state: SetBlockCount must be called prior to reserved memory accesses." ); + + // Calculate such that the last block extends past the end of the array, if necessary. + + m_numblocks = blocks; + m_blocksize = (m_reserved + m_numblocks-1) / m_numblocks; + + return *this; +} + +// Sets the block size via pages (pages are defined by the __pagesize global, which is +// typically 4096). +// +// This method must be called prior to accessing or modifying the array contents. Calls to +// a modified buffer will be ignored (and generate an assertion in dev/debug modes). +SpatialArrayReserve& SpatialArrayReserve::SetBlockSizeInPages( uint pages ) +{ + if (pxAssertDev(m_commited, "Invalid object state: Block size can only be changed prior to accessing or modifying the reserved buffer contents.")) + { + m_blocksize = pages; + m_numblocks = (m_reserved + m_blocksize - 1) / m_blocksize; + m_blockbits.Alloc( _calcBlockBitArrayLength() ); + } + return *this; +} + +// This method assigns the block size of the spatial array, in bytes. The actual size of +// each block will be rounded up to the nearest page size. The resulting size is returned. +// +// This method must be called prior to accessing or modifying the array contents. Calls to +// a modified buffer will be ignored (and generate an assertion in dev/debug modes). +uint SpatialArrayReserve::SetBlockSize( uint bytes ) +{ + SetBlockSizeInPages((bytes + __pagesize - 1) / __pagesize); + return m_blocksize * __pagesize; +} + void SpatialArrayReserve::OnCommittedBlock( void* block ) { + // Determine the block position in the blockbits array, flag it, and be done! + + uptr relative = (uptr)m_baseptr - (uptr)block; + pxAssume( (relative % (m_blocksize * __pagesize)) == 0); + relative /= m_blocksize * __pagesize; + m_blockbits[relative/32] |= 1 << (relative & 31); + m_commited += m_blocksize; } void SpatialArrayReserve::OnOutOfMemory( const Exception::OutOfMemory& ex, void* blockptr, bool& handled ) diff --git a/pcsx2/System.cpp b/pcsx2/System.cpp index b27563f54..0083ae623 100644 --- a/pcsx2/System.cpp +++ b/pcsx2/System.cpp @@ -43,7 +43,7 @@ extern void resetNewVif(int idx); RecompiledCodeReserve::RecompiledCodeReserve( const wxString& name, uint defCommit ) : BaseVirtualMemoryReserve( name ) { - m_block_size = (1024 * 128) / __pagesize; + m_blocksize = (1024 * 128) / __pagesize; m_prot_mode = PageAccess_Any(); m_def_commit = defCommit / __pagesize; @@ -68,6 +68,11 @@ void RecompiledCodeReserve::_termProfiler() ProfilerTerminateSource( m_profiler_name ); } +uint RecompiledCodeReserve::_calcDefaultCommitInBlocks() const +{ + return (m_def_commit + m_blocksize - 1) / m_blocksize; +} + void* RecompiledCodeReserve::Reserve( uint size, uptr base, uptr upper_bounds ) { if (!__parent::Reserve(size, base, upper_bounds)) return NULL; @@ -76,55 +81,6 @@ void* RecompiledCodeReserve::Reserve( uint size, uptr base, uptr upper_bounds ) } -// If growing the array, or if shrinking the array to some point that's still *greater* than the -// committed memory range, then attempt a passive "on-the-fly" resize that maps/unmaps some portion -// of the reserve. -// -// If the above conditions are not met, or if the map/unmap fails, this method returns false. -// The caller will be responsible for manually resetting the reserve. -// -// Parameters: -// newsize - new size of the reserved buffer, in bytes. -bool RecompiledCodeReserve::TryResize( uint newsize ) -{ - uint newPages = (newsize + __pagesize - 1) / __pagesize; - - if (newPages > m_reserved) - { - uint toReservePages = newPages - m_reserved; - uint toReserveBytes = toReservePages * __pagesize; - - DevCon.WriteLn( L"%-32s is being expanded by %u pages.", Name.c_str(), toReservePages); - - m_baseptr = (void*)HostSys::MmapReserve((uptr)GetPtrEnd(), toReserveBytes); - - if (!m_baseptr) - { - Console.Warning("%-32s could not be passively resized due to virtual memory conflict!"); - Console.Indent().Warning("(attempted to map memory @ 0x%08X -> 0x%08X", m_baseptr, (uptr)m_baseptr+toReserveBytes); - } - - DevCon.WriteLn( Color_Blue, L"%-32s @ 0x%08X -> 0x%08X [%umb]", Name.c_str(), - m_baseptr, (uptr)m_baseptr+toReserveBytes, toReserveBytes / _1mb); - } - else if (newPages < m_reserved) - { - if (m_commited > newsize) return false; - - uint toRemovePages = m_reserved - newPages; - uint toRemoveBytes = toRemovePages * __pagesize; - - DevCon.WriteLn( L"%-32s is being shrunk by %u pages.", Name.c_str(), toRemovePages); - - HostSys::MmapResetPtr(GetPtrEnd(), toRemoveBytes); - - DevCon.WriteLn( Color_Blue, L"%-32s @ 0x%08X -> 0x%08X [%umb]", Name.c_str(), - m_baseptr, (uptr)m_baseptr+toRemoveBytes, toRemoveBytes / _1mb); - } - - return true; -} - // Sets the abbreviated name used by the profiler. Name should be under 10 characters long. // After a name has been set, a profiler source will be automatically registered and cleared // in accordance with changes in the reserve area. @@ -135,6 +91,11 @@ RecompiledCodeReserve& RecompiledCodeReserve::SetProfilerName( const wxString& s return *this; } +void RecompiledCodeReserve::DoCommitAndProtect( uptr page ) +{ + CommitBlocks(page, (m_commited || !m_def_commit) ? 1 : _calcDefaultCommitInBlocks() ); +} + void RecompiledCodeReserve::OnCommittedBlock( void* block ) { if (IsDevBuild) @@ -143,7 +104,7 @@ void RecompiledCodeReserve::OnCommittedBlock( void* block ) // 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. - memset_sse_a<0xcc>( block, m_block_size * __pagesize ); + memset_sse_a<0xcc>( block, m_blocksize * __pagesize ); } } @@ -184,7 +145,7 @@ void RecompiledCodeReserve::OnOutOfMemory( const Exception::OutOfMemory& ex, voi ResetProcessReserves(); - uint cusion = std::min( m_block_size, 4 ); + uint cusion = std::min( m_blocksize, 4 ); HostSys::MmapCommitPtr((u8*)blockptr, cusion * __pagesize, m_prot_mode); handled = true; diff --git a/pcsx2/System/RecTypes.h b/pcsx2/System/RecTypes.h index b44c2f46e..c4367437f 100644 --- a/pcsx2/System/RecTypes.h +++ b/pcsx2/System/RecTypes.h @@ -30,6 +30,11 @@ class RecompiledCodeReserve : public BaseVirtualMemoryReserve typedef BaseVirtualMemoryReserve __parent; protected: + // Specifies the number of blocks that should be committed automatically when the + // reserve is created. Typically this chunk is larger than the block size, and + // should be based on whatever typical overhead is needed for basic block use. + uint m_def_commit; + wxString m_profiler_name; bool m_profiler_registered; @@ -47,8 +52,6 @@ public: return SetProfilerName( fromUTF8(shortname) ); } - virtual bool TryResize( uint newsize ); - operator void*() { return m_baseptr; } operator const void*() const { return m_baseptr; } @@ -57,7 +60,10 @@ public: protected: void ResetProcessReserves() const; - + void DoCommitAndProtect( uptr page ); + void _registerProfiler(); void _termProfiler(); + + uint _calcDefaultCommitInBlocks() const; }; diff --git a/pcsx2/x86/microVU.cpp b/pcsx2/x86/microVU.cpp index 479311a7d..b01d1cb90 100644 --- a/pcsx2/x86/microVU.cpp +++ b/pcsx2/x86/microVU.cpp @@ -81,8 +81,8 @@ const __aligned(32) mVU_Globals mVUglob = { static __fi void mVUthrowHardwareDeficiency(const wxChar* extFail, int vuIndex) { throw Exception::HardwareDeficiency() - .SetDiagMsg(wxsFormat(L"microVU%d recompiler init failed: %s is not available.", vuIndex, extFail)) - .SetUserMsg(wxsFormat(_("%s Extensions not found. microVU requires a host CPU with MMX, SSE, and SSE2 extensions."), extFail )); + .SetDiagMsg(pxsFmt(L"microVU%d recompiler init failed: %s is not available.", vuIndex, extFail)) + .SetUserMsg(pxsFmt(_("%s Extensions not found. microVU requires a host CPU with MMX, SSE, and SSE2 extensions."), extFail )); } void microVU::reserveCache() @@ -91,8 +91,8 @@ void microVU::reserveCache() cache_reserve->SetProfilerName( pxsFmt("mVU%urec", index) ); cache = index ? - (u8*)cache_reserve->Reserve( cacheSize, HostMemoryMap::mVU1rec ) : - (u8*)cache_reserve->Reserve( cacheSize, HostMemoryMap::mVU0rec ); + (u8*)cache_reserve->Reserve( cacheSize * _1mb, HostMemoryMap::mVU1rec ) : + (u8*)cache_reserve->Reserve( cacheSize * _1mb, HostMemoryMap::mVU0rec ); if(!cache_reserve->IsOk()) throw Exception::VirtualMemoryMapConflict().SetDiagMsg(pxsFmt( L"Micro VU%u Recompiler Cache", index )); @@ -114,6 +114,8 @@ void microVU::init(uint vuIndex) { progSize = (index ? 0x4000 : 0x1000) / 4; progMemMask = progSize-1; + reserveCache(); + dispCache = SysMmapEx(0, mVUdispCacheSize, 0, (index ? "Micro VU1 Dispatcher" : "Micro VU0 Dispatcher")); if (!dispCache) throw Exception::OutOfMemory( index ? L"Micro VU1 Dispatcher" : L"Micro VU0 Dispatcher" ); memset(dispCache, 0xcc, mVUdispCacheSize);