diff --git a/common/build/Utilities/Utilities.cbp b/common/build/Utilities/Utilities.cbp index 585bc4c499..cfb00c78c8 100644 --- a/common/build/Utilities/Utilities.cbp +++ b/common/build/Utilities/Utilities.cbp @@ -155,6 +155,8 @@ + + @@ -200,6 +202,7 @@ + diff --git a/common/build/Utilities/utilities.vcproj b/common/build/Utilities/utilities.vcproj index 6d7bf5ee1f..f14e0f9ff9 100644 --- a/common/build/Utilities/utilities.vcproj +++ b/common/build/Utilities/utilities.vcproj @@ -291,6 +291,10 @@ RelativePath="..\..\src\Utilities\StringHelpers.cpp" > + + @@ -417,6 +421,10 @@ RelativePath="..\..\include\Utilities\MemcpyFast.h" > + + diff --git a/common/include/Utilities/Dependencies.h b/common/include/Utilities/Dependencies.h index 98b3e7e8a9..69018be958 100644 --- a/common/include/Utilities/Dependencies.h +++ b/common/include/Utilities/Dependencies.h @@ -160,7 +160,7 @@ public: ~ScopedBool() throw() { - m_boolme = false; + *m_boolme = false; } }; @@ -177,6 +177,19 @@ public: #include "Pcsx2Defs.h" +static const sptr _64kb = 0x10000; +static const sptr _16kb = _64kb / 4; +static const sptr _128kb = _64kb * 2; +static const sptr _256kb = _128kb * 2; + +static const s64 _1mb = 0x100000; +static const s64 _8mb = _1mb * 8; +static const s64 _16mb = _1mb * 16; +static const s64 _64mb = _1mb * 64; +static const s64 _256mb = _1mb * 256; +static const s64 _1gb = _256mb * 4; + + // =========================================================================================== // i18n/Translation Feature Set! // =========================================================================================== diff --git a/common/include/Utilities/MemsetFast.inl b/common/include/Utilities/MemsetFast.inl new file mode 100644 index 0000000000..64b4a91266 --- /dev/null +++ b/common/include/Utilities/MemsetFast.inl @@ -0,0 +1,85 @@ +/* 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 . + */ + +#pragma once + +#include + +#define StoreDestIdx(idx) case idx: _mm_store_ps(&destxmm[idx-1][0], srcreg) + + template< u8 data > +__noinline void memset_sse_a( void* dest, const size_t size ) +{ + const uint MZFqwc = size / 16; + + pxAssert( (size & 0xf) == 0 ); + + __m128 srcreg; + + if (data != 0) + { + static __aligned16 const u8 loadval[8] = { data,data,data,data,data,data,data,data }; + srcreg = _mm_loadh_pi( _mm_load_ps( (float*)loadval ), (__m64*)loadval ); + } + else + srcreg = _mm_setzero_ps(); + + float (*destxmm)[4] = (float(*)[4])dest; + + switch( MZFqwc & 0x07 ) + { + StoreDestIdx(0x07); + StoreDestIdx(0x06); + StoreDestIdx(0x05); + StoreDestIdx(0x04); + StoreDestIdx(0x03); + StoreDestIdx(0x02); + StoreDestIdx(0x01); + } + + destxmm += (MZFqwc & 0x07); + for( uint i=0; i( dest, size ); +} + +#undef StoreDestIdx + +template< u8 data, typename T > +__noinline void memset_sse_a( T& dest ) +{ + C_ASSERT( (sizeof(dest) & 0xf) == 0 ); + memset_sse_a( &dest, sizeof(dest) ); +} + +template< typename T > +void memzero_sse_a( T& dest ) +{ + C_ASSERT( (sizeof(dest) & 0xf) == 0 ); + memset_sse_a<0>( &dest, sizeof(dest) ); +} diff --git a/pcsx2/System/PageFaultSource.h b/common/include/Utilities/PageFaultSource.h similarity index 66% rename from pcsx2/System/PageFaultSource.h rename to common/include/Utilities/PageFaultSource.h index 94cc9a0bd8..bf64331fe2 100644 --- a/pcsx2/System/PageFaultSource.h +++ b/common/include/Utilities/PageFaultSource.h @@ -13,6 +13,8 @@ * If not, see . */ +// [TODO] Rename this file to VirtualMemory.h !! + #pragma once // ===================================================================================================== @@ -20,8 +22,9 @@ // ===================================================================================================== // Win32 platforms use the SEH model: __try {} __except {} // Linux platforms use the POSIX Signals model: sigaction() +// [TODO] OS-X (Darwin) platforms should use the Mach exception model (not implemented) -#include "Utilities/EventSource.h" +#include "EventSource.h" struct PageFaultInfo { @@ -125,9 +128,11 @@ public: Free(); } - void* Reserve( uint size, uptr base = 0, uptr upper_bounds = 0 ); - void Reset(); - void Free(); + virtual void* Reserve( uint size, uptr base = 0, uptr upper_bounds = 0 ); + virtual void Reset(); + virtual void Free(); + + bool IsOk() const { return m_baseptr != NULL; } uptr GetReserveSizeInBytes() const { return m_reserved * __pagesize; } uptr GetReserveSizeInPages() const { return m_reserved; } @@ -155,16 +160,58 @@ protected: }; // -------------------------------------------------------------------------------------- -// RecompiledCodeReserve +// SpatialArrayReserve // -------------------------------------------------------------------------------------- -class RecompiledCodeReserve : public BaseVirtualMemoryReserve +// A spatial array is one where large areas of the memory reserve will remain unused during +// process execution. Only areas put to use will be committed to virtual memory. +// +// Spatial array efficiency depends heavily on selecting the right parameters for the array's +// primary intended use. Memory in a spatial array is arranged by blocks, with each block +// containing some number of pages (pages are 4096 bytes each on most platforms). When the +// array is accessed, the entire block containing the addressed memory will be committed at +// once. Blocks can be a single page in size (4096 bytes), though this is highly discouraged +// due to overhead and fragmentation penalties. +// +// Balancing block sizes: +// Larger blocks are good for reducing memory fragmentation and block-tracking overhead, but +// can also result in a lot of otherwise unused memory being committed to memory. Smaller +// blocks are good for arrays that will tend toward more sequential behavior, as they reduce +// the amount of unused memory being committed. However, since every block requires a +// tracking entry, assigning small blocks to a very large array can result in quite a bit of +// unwanted overhead. Furthermore, if the array is accessed randomly, system physical memory +// will become very fragmented, which will also hurt performance. +// +// By default, the base block size is based on a heuristic that balances the size of the spatial +// array reserve against a best-guess performance profile for the target platform. +// +class SpatialArrayReserve : public BaseVirtualMemoryReserve { + typedef BaseVirtualMemoryReserve __parent; + protected: public: - RecompiledCodeReserve( const wxString& name, uint defCommit = 0 ); + SpatialArrayReserve( const wxString& name, uint defCommit = 0 ); + + virtual void* Reserve( uint size, uptr base = 0, uptr upper_bounds = 0 ); + 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 ); + operator void*() { return m_baseptr; } operator const void*() const { return m_baseptr; } @@ -190,7 +237,6 @@ extern int SysPageFaultExceptionFilter(struct _EXCEPTION_POINTERS* eps); # error PCSX2 - Unsupported operating system platform. #endif - extern void InstallSignalHandler(); extern SrcType_PageFault* Source_PageFault; diff --git a/common/include/Utilities/Path.h b/common/include/Utilities/Path.h index 351e99d851..334dc2bca2 100644 --- a/common/include/Utilities/Path.h +++ b/common/include/Utilities/Path.h @@ -101,6 +101,7 @@ public: wxFileName operator+( const wxFileName& right ) const { return Combine( right ); } wxDirName operator+( const wxDirName& right ) const { return Combine( right ); } + wxFileName operator+( const wxString& right ) const { return Combine( wxFileName(right) ); } bool operator==(const wxDirName& filename) const { return SameAs(filename); } bool operator!=(const wxDirName& filename) const { return !SameAs(filename); } @@ -121,7 +122,7 @@ public: // -------------------------------------------------------------------------------------- // Cross-platform utilities for manipulation of paths and filenames. Mostly these fall // back on wxWidgets APIs internally, but are still helpful because some of wx's file stuff -// has minor glitches, or requies sloppy wxFileName typecasting. +// has minor glitches, or requires sloppy wxFileName typecasting. // namespace Path { diff --git a/common/src/Utilities/CMakeLists.txt b/common/src/Utilities/CMakeLists.txt index 72c51a5a53..a659c5b4c0 100644 --- a/common/src/Utilities/CMakeLists.txt +++ b/common/src/Utilities/CMakeLists.txt @@ -121,6 +121,7 @@ set(UtilitiesSources ThreadingDialogs.cpp ThreadTools.cpp vssprintf.cpp + VirtualMemory.cpp wxAppWithHelpers.cpp wxGuiTools.cpp wxHelpers.cpp @@ -145,7 +146,9 @@ set(UtilitiesHeaders ../../include/Utilities/HashMap.h ../../include/Utilities/lnx_memzero.h ../../include/Utilities/MemcpyFast.h + ../../include/Utilities/MemsetFast.h ../../include/Utilities/Path.h + ../../include/Utilities/PageFaultSource.h ../../include/Utilities/pxCheckBox.h ../../include/Utilities/pxRadioPanel.h ../../include/Utilities/pxStaticText.h diff --git a/common/src/Utilities/PathUtils.cpp b/common/src/Utilities/PathUtils.cpp index 800576b76e..3b6daaa2ff 100644 --- a/common/src/Utilities/PathUtils.cpp +++ b/common/src/Utilities/PathUtils.cpp @@ -136,7 +136,7 @@ wxString Path::Combine( const wxDirName& srcPath, const wxFileName& srcFile ) wxString Path::Combine( const wxString& srcPath, const wxDirName& srcFile ) { - return ((wxDirName)srcPath + srcFile).ToString(); + return (wxDirName( srcPath ) + srcFile).ToString(); } // Replaces the extension of the file with the one given. diff --git a/common/src/Utilities/VirtualMemory.cpp b/common/src/Utilities/VirtualMemory.cpp new file mode 100644 index 0000000000..8eab5a1edc --- /dev/null +++ b/common/src/Utilities/VirtualMemory.cpp @@ -0,0 +1,203 @@ +/* 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 . + */ + +#include "PrecompiledHeader.h" + +#include "PageFaultSource.h" +#include "EventSource.inl" +#include "MemsetFast.inl" + +template class EventSource< IEventListener_PageFault >; + +SrcType_PageFault* Source_PageFault = NULL; + +EventListener_PageFault::EventListener_PageFault() +{ + pxAssume(Source_PageFault); + Source_PageFault->Add( *this ); +} + +EventListener_PageFault::~EventListener_PageFault() throw() +{ + if (Source_PageFault) + Source_PageFault->Remove( *this ); +} + +void SrcType_PageFault::Dispatch( const PageFaultInfo& params ) +{ + m_handled = false; + _parent::Dispatch( params ); +} + +void SrcType_PageFault::_DispatchRaw( ListenerIterator iter, const ListenerIterator& iend, const PageFaultInfo& evt ) +{ + do { + (*iter)->DispatchEvent( evt, m_handled ); + } while( (++iter != iend) && !m_handled ); +} + +// -------------------------------------------------------------------------------------- +// BaseVirtualMemoryReserve (implementations) +// -------------------------------------------------------------------------------------- + +BaseVirtualMemoryReserve::BaseVirtualMemoryReserve( const wxString& name ) + : Name( name ) +{ + m_commited = 0; + m_reserved = 0; + m_baseptr = NULL; + m_block_size = __pagesize; + m_prot_mode = PageAccess_None(); +} + +// Parameters: +// upper_bounds - criteria that must be met for the allocation to be valid. +// If the OS refuses to allocate the memory below the specified address, the +// object will fail to initialize and an exception will be thrown. +void* BaseVirtualMemoryReserve::Reserve( uint size, uptr base, uptr upper_bounds ) +{ + if (!pxAssertDev( m_baseptr == NULL, "(VirtualMemoryReserve) Invalid object state; object has already been reserved." )) + return m_baseptr; + + m_reserved = (size + __pagesize-4) / __pagesize; + uptr reserved_bytes = m_reserved * __pagesize; + + m_baseptr = (void*)HostSys::MmapReserve(base, reserved_bytes); + + if (!m_baseptr && (upper_bounds != 0 && (((uptr)m_baseptr + reserved_bytes) > upper_bounds))) + { + if (base) + { + DevCon.Warning( L"%s: address 0x%08x is unavailable; trying OS-selected address instead.", Name.c_str(), base ); + + // Let's try again at an OS-picked memory area, and then hope it meets needed + // boundschecking criteria below. + SafeSysMunmap( m_baseptr, reserved_bytes ); + m_baseptr = (void*)HostSys::MmapReserve( NULL, reserved_bytes ); + } + + if ((upper_bounds != 0) && (((uptr)m_baseptr + reserved_bytes) > upper_bounds)) + { + SafeSysMunmap( m_baseptr, reserved_bytes ); + // returns null, caller should throw an exception or handle appropriately. + } + } + + if (!m_baseptr) return NULL; + + DevCon.WriteLn( Color_Blue, L"%-32s @ 0x%08X -> 0x%08X [%umb]", Name.c_str(), + m_baseptr, (uptr)m_baseptr+reserved_bytes, reserved_bytes / _1mb); + + /*if (m_def_commit) + { + const uint camt = m_def_commit * __pagesize; + HostSys::MmapCommit(m_baseptr, camt); + HostSys::MemProtect(m_baseptr, camt, m_prot_mode); + + u8* init = (u8*)m_baseptr; + u8* endpos = init + camt; + for( ; init= m_reserved) return; + + try { + + if (!m_commited && m_def_commit) + { + const uint camt = m_def_commit * __pagesize; + // first block being committed! Commit the default requested + // amount if its different from the blocksize. + + HostSys::MmapCommit(m_baseptr, camt); + HostSys::MemProtect(m_baseptr, camt, m_prot_mode); + + u8* init = (u8*)m_baseptr; + u8* endpos = init + camt; + for( ; init +#include "Utilities/MemsetFast.inl" + // the BP doesn't advance and returns -1 if there is no data to be read __aligned16 tIPU_cmd ipu_cmd; __aligned16 tIPU_BP g_BP; diff --git a/pcsx2/IPU/mpeg2lib/Mpeg.cpp b/pcsx2/IPU/mpeg2lib/Mpeg.cpp index 4a2c3f00d2..f2fd7e3ac8 100644 --- a/pcsx2/IPU/mpeg2lib/Mpeg.cpp +++ b/pcsx2/IPU/mpeg2lib/Mpeg.cpp @@ -33,6 +33,8 @@ #include "Mpeg.h" #include "Vlc.h" +#include "Utilities/MemsetFast.inl" + const int non_linear_quantizer_scale [] = { 0, 1, 2, 3, 4, 5, 6, 7, diff --git a/pcsx2/IPU/mpeg2lib/Mpeg.h b/pcsx2/IPU/mpeg2lib/Mpeg.h index b4741d9574..982007ed6c 100644 --- a/pcsx2/IPU/mpeg2lib/Mpeg.h +++ b/pcsx2/IPU/mpeg2lib/Mpeg.h @@ -24,48 +24,6 @@ #pragma once -#include - -template< typename T > -__noinline void memzero_sse_a( T& dest ) -{ -#define MZFqwc (sizeof(dest)/16) - - C_ASSERT( (sizeof(dest) & 0xf) == 0 ); - - __m128 zeroreg = _mm_setzero_ps(); - - float (*destxmm)[4] = (float(*)[4])&dest; - -#define StoreDestIdx(idx) case idx: _mm_store_ps(&destxmm[idx-1][0], zeroreg) - - switch( MZFqwc & 0x07 ) - { - StoreDestIdx(0x07); - StoreDestIdx(0x06); - StoreDestIdx(0x05); - StoreDestIdx(0x04); - StoreDestIdx(0x03); - StoreDestIdx(0x02); - StoreDestIdx(0x01); - } - - destxmm += (MZFqwc & 0x07); - for( uint i=0; i - + diff --git a/pcsx2/Memory.cpp b/pcsx2/Memory.cpp index 626d7d299c..5efe544ea4 100644 --- a/pcsx2/Memory.cpp +++ b/pcsx2/Memory.cpp @@ -40,11 +40,12 @@ BIOS #include "IopCommon.h" #include "VUmicro.h" #include "GS.h" -#include "System/PageFaultSource.h" #include "ps2/HwInternal.h" #include "ps2/BiosTools.h" +#include "Utilities/PageFaultSource.h" + #ifdef ENABLECACHE #include "Cache.h" #endif diff --git a/pcsx2/PrecompiledHeader.h b/pcsx2/PrecompiledHeader.h index e24bcfc556..5788d481cd 100644 --- a/pcsx2/PrecompiledHeader.h +++ b/pcsx2/PrecompiledHeader.h @@ -102,20 +102,9 @@ typedef int BOOL; typedef void FnType_Void(); typedef FnType_Void* Fnptr_Void; -static const sptr _64kb = 0x10000; -static const sptr _16kb = _64kb / 4; -static const sptr _128kb = _64kb * 2; -static const sptr _256kb = _128kb * 2; - -static const s64 _1mb = 0x100000; -static const s64 _8mb = _1mb * 8; -static const s64 _16mb = _1mb * 16; -static const s64 _64mb = _1mb * 64; -static const s64 _256mb = _1mb * 256; -static const s64 _1gb = _256mb * 4; - -////////////////////////////////////////////////////////////////////////////////////////// -// Compiler/OS specific macros and defines -- Begin Section +// -------------------------------------------------------------------------------------- +// Compiler/OS specific macros and defines +// -------------------------------------------------------------------------------------- // Linux isn't set up for svn version numbers yet. #ifdef __LINUX__ diff --git a/pcsx2/SaveState.cpp b/pcsx2/SaveState.cpp index 411cafe84b..c9a547942e 100644 --- a/pcsx2/SaveState.cpp +++ b/pcsx2/SaveState.cpp @@ -48,7 +48,7 @@ static void PostLoadPrep() wxString SaveStateBase::GetFilename( int slot ) { return (g_Conf->Folders.Savestates + - wxsFormat( L"%8.8X.%3.3d", ElfCRC, slot )).GetFullPath(); + pxsFmt( L"%08X.%03d", ElfCRC, slot )).GetFullPath(); } SaveStateBase::SaveStateBase( SafeArray& memblock ) diff --git a/pcsx2/System.cpp b/pcsx2/System.cpp index c9433e7914..ecb33f9d90 100644 --- a/pcsx2/System.cpp +++ b/pcsx2/System.cpp @@ -16,192 +16,24 @@ #include "PrecompiledHeader.h" #include "Common.h" #include "IopCommon.h" - -#include "System/PageFaultSource.h" -#include "Utilities/EventSource.inl" +#include "VUmicro.h" // Includes needed for cleanup, since we don't have a good system (yet) for // cleaning up these things. -#include "sVU_zerorec.h" #include "GameDatabase.h" #include "Elfheader.h" +#include "System/RecTypes.h" + +#include "Utilities/MemsetFast.inl" + + extern void closeNewVif(int idx); extern void resetNewVif(int idx); -template class EventSource< IEventListener_PageFault >; - -SrcType_PageFault* Source_PageFault = NULL; - -EventListener_PageFault::EventListener_PageFault() -{ - pxAssume(Source_PageFault); - Source_PageFault->Add( *this ); -} - -EventListener_PageFault::~EventListener_PageFault() throw() -{ - if (Source_PageFault) - Source_PageFault->Remove( *this ); -} - -void SrcType_PageFault::Dispatch( const PageFaultInfo& params ) -{ - m_handled = false; - _parent::Dispatch( params ); -} - -void SrcType_PageFault::_DispatchRaw( ListenerIterator iter, const ListenerIterator& iend, const PageFaultInfo& evt ) -{ - do { - (*iter)->DispatchEvent( evt, m_handled ); - } while( (++iter != iend) && !m_handled ); -} - // -------------------------------------------------------------------------------------- -// BaseVirtualMemoryReserve (implementations) +// RecompiledCodeReserve (implementations) // -------------------------------------------------------------------------------------- - -BaseVirtualMemoryReserve::BaseVirtualMemoryReserve( const wxString& name ) - : Name( name ) -{ - m_commited = 0; - m_reserved = 0; - m_baseptr = NULL; - m_block_size = __pagesize; - m_prot_mode = PageAccess_None(); -} - -// Parameters: -// upper_bounds - criteria that must be met for the allocation to be valid. -// If the OS refuses to allocate the memory below the specified address, the -// object will fail to initialize and an exception will be thrown. -void* BaseVirtualMemoryReserve::Reserve( uint size, uptr base, uptr upper_bounds ) -{ - if (!pxAssertDev( m_baseptr == NULL, "(VirtualMemoryReserve) Invalid object state; object has already been reserved." )) - return m_baseptr; - - m_reserved = (size + __pagesize-4) / __pagesize; - uptr reserved_bytes = m_reserved * __pagesize; - - m_baseptr = (void*)HostSys::MmapReserve(base, reserved_bytes); - - if (!m_baseptr && (upper_bounds != 0 && (((uptr)m_baseptr + reserved_bytes) > upper_bounds))) - { - if (base) - { - DevCon.Warning( L"%s default address 0x%08x is unavailable; falling back on OS-default address.", Name.c_str(), base ); - - // Let's try again at an OS-picked memory area, and then hope it meets needed - // boundschecking criteria below. - SafeSysMunmap( m_baseptr, reserved_bytes ); - m_baseptr = (void*)HostSys::MmapReserve( NULL, reserved_bytes ); - } - - if ((upper_bounds != 0) && (((uptr)m_baseptr + reserved_bytes) > upper_bounds)) - { - SafeSysMunmap( m_baseptr, reserved_bytes ); - // returns null, caller should throw an exception or handle appropriately. - } - } - - if (!m_baseptr) return NULL; - - DevCon.WriteLn( Color_Blue, L"%s mapped @ 0x%08X -> 0x%08X [%umb]", Name.c_str(), - m_baseptr, (uptr)m_baseptr+reserved_bytes, reserved_bytes / _1mb); - - /*if (m_def_commit) - { - const uint camt = m_def_commit * __pagesize; - HostSys::MmapCommit(m_baseptr, camt); - HostSys::MemProtect(m_baseptr, camt, m_prot_mode); - - u8* init = (u8*)m_baseptr; - u8* endpos = init + camt; - for( ; init= m_reserved) return; - - try { - - if (!m_commited && m_def_commit) - { - const uint camt = m_def_commit * __pagesize; - // first block being committed! Commit the default requested - // amount if its different from the blocksize. - - HostSys::MmapCommit(m_baseptr, camt); - HostSys::MemProtect(m_baseptr, camt, m_prot_mode); - - u8* init = (u8*)m_baseptr; - u8* endpos = init + camt; - for( ; init -__noinline void memset_sse_a( void* dest, const size_t size ) -{ - const uint MZFqwc = size / 16; - - pxAssert( (size & 0xf) == 0 ); - - static __aligned16 const u8 loadval[8] = { data,data,data,data,data,data,data,data }; - __m128 srcreg = _mm_load_ps( (float*)loadval ); - srcreg = _mm_loadh_pi( srcreg, (__m64*)loadval ); - - float (*destxmm)[4] = (float(*)[4])dest; - -#define StoreDestIdx(idx) case idx: _mm_store_ps(&destxmm[idx-1][0], srcreg) - - switch( MZFqwc & 0x07 ) - { - StoreDestIdx(0x07); - StoreDestIdx(0x06); - StoreDestIdx(0x05); - StoreDestIdx(0x04); - StoreDestIdx(0x03); - StoreDestIdx(0x02); - StoreDestIdx(0x01); - } - - destxmm += (MZFqwc & 0x07); - for( uint i=0; iSetCacheReserve( (Cpu->GetCacheReserve() * 3) / 2 ); Cpu->Reset(); - //CpuVU0->SetCacheReserve( (CpuVU0->GetCacheReserve() * 3) / 2 ); + CpuVU0->SetCacheReserve( (CpuVU0->GetCacheReserve() * 3) / 2 ); CpuVU0->Reset(); - //CpuVU1->SetCacheReserve( (CpuVU1->GetCacheReserve() * 3) / 2 ); + CpuVU1->SetCacheReserve( (CpuVU1->GetCacheReserve() * 3) / 2 ); CpuVU1->Reset(); //psxCpu->SetCacheReserve( (psxCpu->GetCacheReserve() * 3) / 2 ); psxCpu->Reset(); +} + +// Default behavior for out of memory: the +void RecompiledCodeReserve::OnOutOfMemory( const Exception::OutOfMemory& ex, void* blockptr, bool& handled ) +{ // Since the recompiler is happy writing away to memory, we have to truncate the reserve // to include the page currently being accessed, and cannot go any smaller. This will // allow the rec to finish emitting the current block of instructions, detect that it has @@ -293,6 +87,11 @@ void RecompiledCodeReserve::OnOutOfMemory( const Exception::OutOfMemory& ex, voi try { + // Truncate and reset reserves of all other in-use recompiler caches, as this should + // help free up quite a bit of emergency memory. + + ResetProcessReserves(); + uint cusion = std::min( m_block_size, 4 ); HostSys::MmapCommit((u8*)blockptr, cusion * __pagesize); HostSys::MemProtect((u8*)blockptr, cusion * __pagesize, m_prot_mode); @@ -442,7 +241,7 @@ CpuInitializer< CpuType >::CpuInitializer() { try { MyCpu = new CpuType(); - MyCpu->Allocate(); + MyCpu->Reserve(); } catch( Exception::RuntimeError& ex ) { @@ -461,7 +260,7 @@ CpuInitializer< CpuType >::CpuInitializer() template< typename CpuType > CpuInitializer< CpuType >::~CpuInitializer() throw() { - if( MyCpu ) + if (MyCpu) MyCpu->Shutdown(); } @@ -608,11 +407,6 @@ SysCpuProviderPack::SysCpuProviderPack() } // hmm! : VU0 and VU1 pre-allocations should do sVU and mVU separately? Sounds complicated. :( - - // If both VUrecs failed, then make sure the SuperVU is totally closed out, because it - // actually initializes everything once and then shares it between both VU recs. - if( !IsRecAvailable_SuperVU0() && !IsRecAvailable_SuperVU1() ) - SuperVUDestroy( -1 ); } bool SysCpuProviderPack::IsRecAvailable_MicroVU0() const { return CpuProviders->microVU0.IsAvailable(); } @@ -633,9 +427,6 @@ void SysCpuProviderPack::CleanupMess() throw() closeNewVif(0); closeNewVif(1); - // Special SuperVU "complete" terminator (stupid hacky recompiler) - SuperVUDestroy( -1 ); - psxRec.Shutdown(); recCpu.Shutdown(); } @@ -695,10 +486,6 @@ void SysClearExecutionCache() { GetCpuProviders().ApplyConfig(); - // SuperVUreset will do nothing is none of the recs are initialized. - // But it's needed if one or the other is initialized. - SuperVUReset(-1); - Cpu->Reset(); psxCpu->Reset(); diff --git a/pcsx2/System.h b/pcsx2/System.h index cec9cc0c66..f0e1fc1db5 100644 --- a/pcsx2/System.h +++ b/pcsx2/System.h @@ -22,8 +22,9 @@ #include "CDVD/CDVDaccess.h" typedef SafeArray VmStateBuffer; -class BaseVUmicroCPU; +class BaseVUmicroCPU; +class RecompiledCodeReserve; // This is a table of default virtual map addresses for ps2vm components. These locations // are provided and used to assist in debugging and possibly hacking; as it makes it possible @@ -38,8 +39,9 @@ class BaseVUmicroCPU; namespace HostMemoryMap { // superVU is OLD SCHOOL, and it requires its allocation to be in the lower 256mb - // of the virtual memory space. (8mb) - static const uptr sVUrec = 0x0f1e0000; + // of the virtual memory space. (8mb each) + static const uptr sVU0rec = _256mb - (_16mb*2); + static const uptr sVU1rec = _256mb - (_16mb*1); // PS2 main memory, SPR, and ROMs static const uptr EEmem = 0x20000000; diff --git a/pcsx2/System/RecTypes.h b/pcsx2/System/RecTypes.h new file mode 100644 index 0000000000..995444669d --- /dev/null +++ b/pcsx2/System/RecTypes.h @@ -0,0 +1,46 @@ +/* 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 . + */ + +#pragma once + +#include "Utilities/PageFaultSource.h" + +// -------------------------------------------------------------------------------------- +// RecompiledCodeReserve +// -------------------------------------------------------------------------------------- +// A recompiled code reserve is a simple sequential-growth block of memory which is auto- +// cleared to INT 3 (0xcc) as needed. When using this class, care should be take to re- +// implement the provided OnOutOfMemory handler so that it clears other recompiled memory +// reserves that are known to be attached to the process. +// +class RecompiledCodeReserve : public BaseVirtualMemoryReserve +{ +protected: + +public: + RecompiledCodeReserve( const wxString& name, uint defCommit = 0 ); + + virtual void OnCommittedBlock( void* block ); + virtual void OnOutOfMemory( const Exception::OutOfMemory& ex, void* blockptr, bool& handled ); + + 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; } + +protected: + void ResetProcessReserves() const; +}; diff --git a/pcsx2/System/SysCoreThread.cpp b/pcsx2/System/SysCoreThread.cpp index 1238275900..a1f59ded4e 100644 --- a/pcsx2/System/SysCoreThread.cpp +++ b/pcsx2/System/SysCoreThread.cpp @@ -22,9 +22,9 @@ #include "GS.h" #include "Elfheader.h" #include "Patch.h" -#include "PageFaultSource.h" #include "SysThreads.h" +#include "Utilities/PageFaultSource.h" #include "Utilities/TlsVariable.inl" #ifdef __WXMSW__ diff --git a/pcsx2/VUmicro.h b/pcsx2/VUmicro.h index 81436cdf48..8270c17714 100644 --- a/pcsx2/VUmicro.h +++ b/pcsx2/VUmicro.h @@ -47,9 +47,9 @@ static const uint VU1_PROGMASK = VU1_PROGSIZE-1; class BaseCpuProvider { protected: - // allocation counter for multiple init/shutdown calls - // (most or all implementations will need this!) - int m_AllocCount; + // allocation counter for multiple calls to Reserve. Most implementations should utilize + // this variable for sake of robustness. + u32 m_Reserved; public: // this boolean indicates to some generic logging facilities if the VU's registers @@ -60,13 +60,13 @@ public: public: BaseCpuProvider() { - m_AllocCount = 0; + m_Reserved = 0; } virtual ~BaseCpuProvider() throw() { - if( m_AllocCount != 0 ) - Console.Warning( "Cleanup miscount detected on CPU provider. Count=%d", m_AllocCount ); + if( m_Reserved != 0 ) + Console.Warning( "Cleanup miscount detected on CPU provider. Count=%d", m_Reserved ); } virtual const char* GetShortName() const=0; @@ -80,7 +80,7 @@ public: return 0; } - virtual void Allocate()=0; + virtual void Reserve()=0; virtual void Shutdown()=0; virtual void Reset()=0; virtual void Execute(u32 cycles)=0; @@ -96,6 +96,15 @@ public: { cpu->Execute(1024); } + + // Gets the current cache reserve allocated to this CPU (value returned in megabytes) + virtual uint GetCacheReserve() const=0; + + // Specifies the maximum cache reserve amount for this CPU (value in megabytes). + // CPU providers are allowed to reset their reserves (recompiler resets, etc) if such is + // needed to conform to the new amount requested. + virtual void SetCacheReserve( uint reserveInMegs ) const=0; + }; // -------------------------------------------------------------------------------------- @@ -157,13 +166,16 @@ public: const char* GetShortName() const { return "intVU0"; } wxString GetLongName() const { return L"VU0 Interpreter"; } - void Allocate() { } + void Reserve() { } void Shutdown() throw() { } void Reset() { } void Step(); void Execute(u32 cycles); void Clear(u32 addr, u32 size) {} + + uint GetCacheReserve() const { return 0; } + void SetCacheReserve( uint reserveInMegs ) const {} }; class InterpVU1 : public BaseVUmicroCPU @@ -175,13 +187,16 @@ public: const char* GetShortName() const { return "intVU1"; } wxString GetLongName() const { return L"VU1 Interpreter"; } - void Allocate() { } + void Reserve() { } void Shutdown() throw() { } void Reset() { } void Step(); void Execute(u32 cycles); void Clear(u32 addr, u32 size) {} + + uint GetCacheReserve() const { return 0; } + void SetCacheReserve( uint reserveInMegs ) const {} }; // -------------------------------------------------------------------------------------- @@ -196,13 +211,16 @@ public: const char* GetShortName() const { return "mVU0"; } wxString GetLongName() const { return L"microVU0 Recompiler"; } - void Allocate(); + void Reserve(); void Shutdown() throw(); void Reset(); void Execute(u32 cycles); void Clear(u32 addr, u32 size); void Vsync() throw(); + + uint GetCacheReserve() const; + void SetCacheReserve( uint reserveInMegs ) const; }; class recMicroVU1 : public BaseVUmicroCPU @@ -214,12 +232,15 @@ public: const char* GetShortName() const { return "mVU1"; } wxString GetLongName() const { return L"microVU1 Recompiler"; } - void Allocate(); + void Reserve(); void Shutdown() throw(); void Reset(); void Execute(u32 cycles); void Clear(u32 addr, u32 size); void Vsync() throw(); + + uint GetCacheReserve() const; + void SetCacheReserve( uint reserveInMegs ) const; }; // -------------------------------------------------------------------------------------- @@ -234,11 +255,14 @@ public: const char* GetShortName() const { return "sVU0"; } wxString GetLongName() const { return L"SuperVU0 Recompiler"; } - void Allocate(); + void Reserve(); void Shutdown() throw(); void Reset(); void Execute(u32 cycles); void Clear(u32 Addr, u32 Size); + + uint GetCacheReserve() const; + void SetCacheReserve( uint reserveInMegs ) const; }; class recSuperVU1 : public BaseVUmicroCPU @@ -249,11 +273,14 @@ public: const char* GetShortName() const { return "sVU1"; } wxString GetLongName() const { return L"SuperVU1 Recompiler"; } - void Allocate(); + void Reserve(); void Shutdown() throw(); void Reset(); void Execute(u32 cycles); void Clear(u32 Addr, u32 Size); + + uint GetCacheReserve() const; + void SetCacheReserve( uint reserveInMegs ) const; }; extern BaseVUmicroCPU* CpuVU0; diff --git a/pcsx2/windows/VCprojects/pcsx2_2008.vcproj b/pcsx2/windows/VCprojects/pcsx2_2008.vcproj index 1affaf6adf..c72c18c5cb 100644 --- a/pcsx2/windows/VCprojects/pcsx2_2008.vcproj +++ b/pcsx2/windows/VCprojects/pcsx2_2008.vcproj @@ -459,6 +459,10 @@ RelativePath="..\..\Plugins.h" > + + diff --git a/pcsx2/windows/WinSysExec.cpp b/pcsx2/windows/WinSysExec.cpp index b454bea0d8..5d1f482fba 100644 --- a/pcsx2/windows/WinSysExec.cpp +++ b/pcsx2/windows/WinSysExec.cpp @@ -18,7 +18,7 @@ #include #include "Common.h" -#include "System/PageFaultSource.h" +#include "Utilities/PageFaultSource.h" int SysPageFaultExceptionFilter( EXCEPTION_POINTERS* eps ) { diff --git a/pcsx2/x86/iR3000A.cpp b/pcsx2/x86/iR3000A.cpp index 13724b55ba..2886d53080 100644 --- a/pcsx2/x86/iR3000A.cpp +++ b/pcsx2/x86/iR3000A.cpp @@ -22,7 +22,7 @@ #include "iR3000A.h" #include "BaseblockEx.h" -#include "PageFaultSource.h" +#include "System/RecTypes.h" #include @@ -759,8 +759,9 @@ static void recReserve() { if (!recMem) { - recMem = new RecompiledCodeReserve(L"R3000A recompiled code cache", _1mb * 2); + recMem = new RecompiledCodeReserve(L"R3000A Recompiler Cache", _1mb * 2); recMem->Reserve( _16mb, HostMemoryMap::IOPrec ); + ProfilerRegisterSource( "IOP Rec", *recMem, recMem->GetReserveSizeInBytes() ); } } @@ -790,17 +791,16 @@ static void recAlloc() if( s_pInstCache == NULL ) throw Exception::OutOfMemory( L"R3000 InstCache." ); - ProfilerRegisterSource( "IOP Rec", *recMem, recMem->GetReserveSizeInBytes() ); _DynGen_Dispatchers(); } void recResetIOP() { + DevCon.WriteLn( "iR3000A Recompiler reset." ); + recAlloc(); recMem->Reset(); - DevCon.WriteLn( "iR3000A Recompiler reset." ); - iopClearRecLUT((BASEBLOCK*)m_recBlockAlloc, (((Ps2MemSize::IopRam + Ps2MemSize::Rom + Ps2MemSize::Rom1) / 4))); diff --git a/pcsx2/x86/ix86-32/iR5900-32.cpp b/pcsx2/x86/ix86-32/iR5900-32.cpp index b3eb7783a5..3c90e9a28b 100644 --- a/pcsx2/x86/ix86-32/iR5900-32.cpp +++ b/pcsx2/x86/ix86-32/iR5900-32.cpp @@ -21,16 +21,15 @@ #include "R5900Exceptions.h" #include "R5900OpcodeTables.h" #include "iR5900.h" - #include "BaseblockEx.h" +#include "System/RecTypes.h" + #include "vtlb.h" #include "SamplProf.h" #include "Dump.h" #include "System/SysThreads.h" -#include "System/PageFaultSource.h" #include "GS.h" - #include "CDVD/CDVD.h" #include "Elfheader.h" @@ -562,7 +561,7 @@ static void recReserve() if ( !x86caps.hasStreamingSIMD2Extensions ) recThrowHardwareDeficiency( L"SSE2" ); - recMem = new RecompiledCodeReserve(L"R5900-32 recompiled code cache", _1mb * 4); + recMem = new RecompiledCodeReserve(L"R5900-32 Recompiler Cache", _1mb * 4); recMem->Reserve( _64mb, HostMemoryMap::EErec ); } @@ -614,6 +613,7 @@ static __aligned16 u8 manual_counter[Ps2MemSize::MainRam >> 12]; static u32 eeRecIsReset = false; static u32 eeRecNeedsReset = false; static bool eeRecIsActive = false; +static bool eeCpuExecuting = false; //////////////////////////////////////////////////// static void recResetRaw() @@ -701,7 +701,7 @@ static void recShutdown() static void recResetEE() { - if (eeRecIsActive) + if (eeRecIsActive || eeCpuExecuting) { AtomicExchange( eeRecNeedsReset, true ); return; @@ -746,8 +746,6 @@ static void recCheckExecutionState() } } -static bool m_recExecutingCode = false; - static void recExecute() { // Implementation Notes: @@ -755,12 +753,14 @@ static void recExecute() #if PCSX2_SEH eeRecIsReset = false; - ScopedBool executing(m_recExecutingCode); + ScopedBool executing(eeCpuExecuting); try { EnterRecompiledCode(); } - catch( Exception::ExitCpuExecute& ) { } + catch( Exception::ExitCpuExecute& ) + { + } #else @@ -1369,7 +1369,7 @@ static void __fastcall recRecompile( const u32 startpc ) // From here on we need to have EE recompile resets disabled, since to reset // the rec while we're writing to it typically leads to GPF. - ScopedBool active_scope(eeRecIsActive); + //ScopedBool active_scope(eeRecIsActive); xSetPtr( recPtr ); recPtr = xGetAlignedCallTarget(); @@ -1868,7 +1868,7 @@ static void recThrowException( const BaseR5900Exception& ex ) #if PCSX2_SEH ex.Rethrow(); #else - if (!m_recExecutingCode) ex.Rethrow(); + if (!eeCpuExecuting) ex.Rethrow(); m_cpuException = ex.Clone(); recExitExecution(); #endif @@ -1879,7 +1879,7 @@ static void recThrowException( const BaseException& ex ) #if PCSX2_SEH ex.Rethrow(); #else - if (!m_recExecutingCode) ex.Rethrow(); + if (!eeCpuExecuting) ex.Rethrow(); m_Exception = ex.Clone(); recExitExecution(); #endif diff --git a/pcsx2/x86/microVU.cpp b/pcsx2/x86/microVU.cpp index 114e9ed3d1..f6c409cb99 100644 --- a/pcsx2/x86/microVU.cpp +++ b/pcsx2/x86/microVU.cpp @@ -18,6 +18,7 @@ #include "PrecompiledHeader.h" #include "Common.h" #include "microVU.h" +#include "RecTypes.h" // Include all the *.inl files (Needed because C++ sucks with templates and *.cpp files) #include "microVU_Clamp.inl" @@ -99,22 +100,22 @@ void microVU::init(uint vuIndex) { microMemSize = (index ? 0x4000 : 0x1000); progSize = (index ? 0x4000 : 0x1000) / 4; progMemMask = progSize-1; - dispCache = NULL; - cache = NULL; - cacheSize = mVUcacheSize; - regAlloc = new microRegAlloc(index); - - for (u32 i = 0; i < (progSize / 2); i++) { - prog.prog[i] = new deque(); - } 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); - // Allocates rec-cache and calls mVUreset() - mVUresizeCache(this, cacheSize + mVUcacheSafeZone); - //if (vuIndex) gen_memcpy_vibes(); + cache_reserve = new RecompiledCodeReserve( pxsFmt("Micro VU%u Recompiler Cache", index) ); + cache = index ? + (u8*)cache_reserve->Reserve( cacheSize, HostMemoryMap::mVU1rec ) : + (u8*)cache_reserve->Reserve( cacheSize, HostMemoryMap::mVU0rec ); + + if(!cache_reserve->IsOk()) + throw Exception::VirtualMemoryMapConflict().SetDiagMsg(pxsFmt( L"Micro VU%u Recompiler Cache", index )); + + ProfilerRegisterSource (index ? "mVU1 Rec" : "mVU0 Rec", cache, cacheSize); + + regAlloc = new microRegAlloc(index); } // Resets Rec Data @@ -144,9 +145,15 @@ void microVU::reset() { u8* z = cache; prog.x86start = z; prog.x86ptr = z; - prog.x86end = (u8*)((uptr)z + (uptr)(cacheSize - mVUcacheSafeZone)); // "Safe Zone" + prog.x86end = z + (cacheSize - mVUcacheSafeZone); for (u32 i = 0; i < (progSize / 2); i++) { + if (!prog.prog[i]) + { + prog.prog[i] = new deque(); + continue; + } + deque::iterator it(prog.prog[i]->begin()); for ( ; it != prog.prog[i]->end(); ++it) { if (index) mVUdeleteProg<1>(it[0]); @@ -161,11 +168,17 @@ void microVU::reset() { // Free Allocated Resources void microVU::close() { - if (dispCache) { HostSys::Munmap(dispCache, mVUdispCacheSize); dispCache = NULL; } - if (cache) { HostSys::Munmap(cache, cacheSize); cache = NULL; } + if (cache_reserve && cache_reserve->IsOk()) + { + ProfilerTerminateSource (index ? "mVU1 Rec" : "mVU0 Rec"); + safe_delete(cache_reserve); + } + + SafeSysMunmap(dispCache, mVUdispCacheSize); // Delete Programs and Block Managers for (u32 i = 0; i < (progSize / 2); i++) { + if (!prog.prog[i]) continue; deque::iterator it(prog.prog[i]->begin()); for ( ; it != prog.prog[i]->end(); ++it) { if (index) mVUdeleteProg<1>(it[0]); @@ -175,35 +188,6 @@ void microVU::close() { } } -static void mVUresizeCache(mV, u32 size) { - - if (size >= (u32)mVUcacheMaxSize) { - if (mVU->cacheSize==mVUcacheMaxSize) { - // We can't grow the rec any larger, so just reset it and start over. - //(if we don't reset, the rec will eventually crash) - Console.WriteLn(Color_Magenta, "microVU%d: Cannot grow cache, size limit reached! [%dmb]. Resetting rec.", mVU->index, mVU->cacheSize/_1mb); - mVU->reset(); - return; - } - size = mVUcacheMaxSize; - } - - if (mVU->cache) Console.WriteLn(Color_Green, "microVU%d: Attempting to resize Cache [%dmb]", mVU->index, size/_1mb); - - u8* cache = SysMmapEx(mVU->index ? HostMemoryMap::mVU1rec : HostMemoryMap::mVU0rec, size, 0, (mVU->index ? "Micro VU1 RecCache" : "Micro VU0 RecCache")); - if(!cache && !mVU->cache) throw Exception::OutOfMemory( wxsFormat( L"Micro VU%d recompiled code cache", mVU->index) ); - if(!cache) { Console.Error("microVU%d Error - Cache Resize Failed...", mVU->index); mVU->reset(); return; } - if (mVU->cache) { - HostSys::Munmap(mVU->cache, mVU->cacheSize); - ProfilerTerminateSource(isVU1?"mVU1 Rec":"mVU0 Rec"); - } - - mVU->cache = cache; - mVU->cacheSize = size; - ProfilerRegisterSource(isVU1?"mVU1 Rec":"mVU0 Rec", mVU->cache, mVU->cacheSize); - mVU->reset(); -} - // Clears Block Data in specified range static __fi void mVUclear(mV, u32 addr, u32 size) { if (!mVU->prog.cleared) { @@ -323,55 +307,40 @@ _mVUt __fi void* mVUsearchProg(u32 startPC, uptr pState) { // recMicroVU0 / recMicroVU1 //------------------------------------------------------------------ -static u32 mvu0_allocated = 0; -static u32 mvu1_allocated = 0; - recMicroVU0::recMicroVU0() { m_Idx = 0; IsInterpreter = false; } recMicroVU1::recMicroVU1() { m_Idx = 1; IsInterpreter = false; } void recMicroVU0::Vsync() throw() { mVUvsyncUpdate(µVU0); } void recMicroVU1::Vsync() throw() { mVUvsyncUpdate(µVU1); } -void recMicroVU0::Allocate() { - if(!m_AllocCount) { - m_AllocCount++; - if (AtomicExchange(mvu0_allocated, 1) == 0) - microVU0.init(0); - } +void recMicroVU0::Reserve() { + if (AtomicExchange(m_Reserved, 1) == 0) + microVU0.init(0); } -void recMicroVU1::Allocate() { - if(!m_AllocCount) { - m_AllocCount++; - if (AtomicExchange(mvu1_allocated, 1) == 0) - microVU1.init(1); - } +void recMicroVU1::Reserve() { + if (AtomicExchange(m_Reserved, 1) == 0) + microVU1.init(1); } void recMicroVU0::Shutdown() throw() { - if (m_AllocCount > 0) { - m_AllocCount--; - if (AtomicExchange(mvu0_allocated, 0) == 1) - microVU0.close(); - } + if (AtomicExchange(m_Reserved, 0) == 1) + microVU0.close(); } void recMicroVU1::Shutdown() throw() { - if (m_AllocCount > 0) { - m_AllocCount--; - if (AtomicExchange(mvu1_allocated, 0) == 1) - microVU1.close(); - } + if (AtomicExchange(m_Reserved, 0) == 1) + microVU1.close(); } void recMicroVU0::Reset() { - if(!pxAssertDev(m_AllocCount, "MicroVU0 CPU Provider has not been allocated prior to reset!")) return; + if(!pxAssertDev(m_Reserved, "MicroVU0 CPU Provider has not been reserved prior to reset!")) return; microVU0.reset(); } void recMicroVU1::Reset() { - if(!pxAssertDev(m_AllocCount, "MicroVU1 CPU Provider has not been allocated prior to reset!")) return; + if(!pxAssertDev(m_Reserved, "MicroVU1 CPU Provider has not been reserved prior to reset!")) return; microVU1.reset(); } void recMicroVU0::Execute(u32 cycles) { - pxAssert(mvu0_allocated); // please allocate me first! :| + pxAssert(m_Reserved); // please allocate me first! :| if(!(VU0.VI[REG_VPU_STAT].UL & 1)) return; @@ -381,17 +350,35 @@ void recMicroVU0::Execute(u32 cycles) { ((mVUrecCall)microVU0.startFunct)(VU0.VI[REG_TPC].UL, cycles); } void recMicroVU1::Execute(u32 cycles) { - pxAssert(mvu1_allocated); // please allocate me first! :| + pxAssert(m_Reserved); // please allocate me first! :| if(!(VU0.VI[REG_VPU_STAT].UL & 0x100)) return; ((mVUrecCall)microVU1.startFunct)(VU1.VI[REG_TPC].UL, vu1RunCycles); } void recMicroVU0::Clear(u32 addr, u32 size) { - pxAssert(mvu0_allocated); // please allocate me first! :| + pxAssert(m_Reserved); // please allocate me first! :| mVUclear(µVU0, addr, size); } void recMicroVU1::Clear(u32 addr, u32 size) { - pxAssert(mvu1_allocated); // please allocate me first! :| + pxAssert(m_Reserved); // please allocate me first! :| mVUclear(µVU1, addr, size); } + +uint recMicroVU0::GetCacheReserve() const +{ + return microVU0.cacheSize / _1mb; +} +uint recMicroVU1::GetCacheReserve() const +{ + return microVU1.cacheSize / _1mb; +} + +void recMicroVU0::SetCacheReserve( uint reserveInMegs ) const +{ + microVU0.cacheSize = reserveInMegs * _1mb; +} +void recMicroVU1::SetCacheReserve( uint reserveInMegs ) const +{ + microVU1.cacheSize = reserveInMegs * _1mb; +} diff --git a/pcsx2/x86/microVU.h b/pcsx2/x86/microVU.h index 5cc8eb8f06..57cd99a092 100644 --- a/pcsx2/x86/microVU.h +++ b/pcsx2/x86/microVU.h @@ -150,7 +150,7 @@ struct microProgManager { microRegInfo lpState; // Pipeline state from where program left off (useful for continuing execution) }; -#define mVUdispCacheSize (0x1000) // Dispatcher Cache Size +#define mVUdispCacheSize (__pagesize) // Dispatcher Cache Size #define mVUcacheSize ((index) ? (_1mb * 17) : (_1mb * 7)) // Initial Size (Excluding Safe-Zone) #define mVUcacheMaxSize ((mVU->index) ? (_1mb * 100) : (_1mb * 50)) // Max Size allowed to grow to #define mVUcacheGrowBy ((mVU->index) ? (_1mb * 15) : (_1mb * 10)) // Grows by this amount @@ -175,7 +175,9 @@ struct microVU { ScopedPtr regAlloc; // Reg Alloc Class ScopedPtr logFile; // Log File Pointer - u8* cache; // Dynarec Cache Start (where we will start writing the recompiled code to) + + RecompiledCodeReserve* cache_reserve; + u8* cache; // Dynarec Cache Start (where we will start writing the recompiled code to) u8* dispCache; // Dispatchers Cache (where startFunct and exitFunct are written to) u8* startFunct; // Ptr Function to the Start code for recompiled programs u8* exitFunct; // Ptr Function to the Exit code for recompiled programs @@ -224,6 +226,15 @@ struct microVU { pxAssumeDev((prog.IRinfo.curPC & 1) == 0, "microVU recompiler: Upper instructions cannot have valid branch addresses."); return (((prog.IRinfo.curPC + 4) + (Imm11() * 2)) & progMemMask) * 4; } + + microVU() + { + cacheSize = _1mb * 64; + cache = NULL; + dispCache = NULL; + startFunct = NULL; + exitFunct = NULL; + } void init(uint vuIndex); void reset(); @@ -239,7 +250,6 @@ int mVUdebugNow = 0; // Main Functions static void mVUclear(mV, u32, u32); -static void mVUresizeCache(mV, u32); static void* mVUblockFetch(microVU* mVU, u32 startPC, uptr pState); _mVUt extern void* __fastcall mVUcompileJIT(u32 startPC, uptr pState); diff --git a/pcsx2/x86/microVU_Execute.inl b/pcsx2/x86/microVU_Execute.inl index 198ab530da..cca83ed6ba 100644 --- a/pcsx2/x86/microVU_Execute.inl +++ b/pcsx2/x86/microVU_Execute.inl @@ -92,7 +92,7 @@ void mVUdispatcherB(mV) { xRET(); - mVUcacheCheck(x86Ptr, mVU->dispCache, mVUdispCacheSize); + pxAssertDev(xGetPtr() < (mVU->dispCache + mVUdispCacheSize), "microVU: Dispatcher generation exceeded reserved cache area!"); } //------------------------------------------------------------------ @@ -121,8 +121,14 @@ _mVUt void mVUcleanUp() { //mVUprint("microVU: Program exited successfully!"); //mVUprint("microVU: VF0 = {%x,%x,%x,%x}", mVU->regs().VF[0].UL[0], mVU->regs().VF[0].UL[1], mVU->regs().VF[0].UL[2], mVU->regs().VF[0].UL[3]); //mVUprint("microVU: VI0 = %x", mVU->regs().VI[0].UL); + mVU->prog.x86ptr = x86Ptr; - mVUcacheCheck(x86Ptr, mVU->prog.x86start, (uptr)(mVU->prog.x86end - mVU->prog.x86start)); + + if ((xGetPtr() < mVU->prog.x86start) || (xGetPtr() >= mVU->prog.x86end)) { + Console.WriteLn(vuIndex ? Color_Orange : Color_Magenta, "microVU%d: Program cache limit reached.", mVU->index); + mVU->reset(); + } + mVU->cycles = mVU->totalCycles - mVU->cycles; mVU->regs().cycle += mVU->cycles; cpuRegs.cycle += ((mVU->cycles < 3000) ? mVU->cycles : 3000) * EmuConfig.Speedhacks.VUCycleSteal; diff --git a/pcsx2/x86/microVU_Misc.h b/pcsx2/x86/microVU_Misc.h index ef58dbe8ef..ff6b1b40e0 100644 --- a/pcsx2/x86/microVU_Misc.h +++ b/pcsx2/x86/microVU_Misc.h @@ -298,15 +298,6 @@ static const bool doConstProp = 0; // Set to 1 to turn on vi15 const propagation //------------------------------------------------------------------ -// Cache Limit Check -#define mVUcacheCheck(ptr, start, limit) { \ - uptr diff = ptr - start; \ - if (diff >= limit) { \ - DevCon.WriteLn("microVU%d: Program cache limit reached. Size = 0x%x", mVU->index, diff); \ - mVUresizeCache(mVU, mVU->cacheSize + mVUcacheGrowBy); \ - } \ -} - extern void mVUmergeRegs(const xmm& dest, const xmm& src, int xyzw, bool modXYZW=false); extern void mVUsaveReg(const xmm& reg, xAddressVoid ptr, int xyzw, bool modXYZW); extern void mVUloadReg(const xmm& reg, xAddressVoid ptr, int xyzw); diff --git a/pcsx2/x86/sVU_zerorec.cpp b/pcsx2/x86/sVU_zerorec.cpp index 299e6764a1..43e1bf55e6 100644 --- a/pcsx2/x86/sVU_zerorec.cpp +++ b/pcsx2/x86/sVU_zerorec.cpp @@ -35,6 +35,7 @@ #include "R5900.h" #include "iR5900.h" +#include "System/RecTypes.h" #include "sVU_zerorec.h" #include "SamplProf.h" @@ -72,7 +73,7 @@ extern void iDumpVU1Registers(); #define SUPERVU_CHECKCONDITION 0 // has to be 0!! -static const uint VU_EXESIZE = _1mb * 8; +static const uint sVU_EXESIZE = _8mb; #define _Imm11_ (s32)( (vucode & 0x400) ? (0xfffffc00 | (vucode & 0x3ff)) : (vucode & 0x3ff) ) #define _UImm11_ (s32)(vucode & 0x7ff) @@ -90,7 +91,9 @@ static const u32 PWaitTimes[] = { 53, 43, 28, 23, 17, 11, 10 }; static u32 s_vuInfo; // info passed into rec insts static const u32 s_MemSize[2] = {VU0_MEMSIZE, VU1_MEMSIZE}; -static u8* s_recVUMem = NULL, *s_recVUPtr = NULL; +//static u8* s_recVUMem = NULL, *s_recVUPtr = NULL; +static RecompiledCodeReserve* s_recVUMem[2] = { NULL, NULL }; +static u8* s_recVUPtr[2] = { NULL, NULL }; // tables which are defined at the bottom of this massive file. extern void (*recVU_UPPER_OPCODE[64])(VURegs* VU, s32 info); @@ -315,9 +318,11 @@ VuBaseBlock::VuBaseBlock() static list s_listVUHeaders[2]; static list* s_plistCachedHeaders[2] = {NULL, NULL}; -static VuFunctionHeader** recVUHeaders[2] = {NULL, NULL}; -static VuBlockHeader* recVUBlocks[2] = {NULL, NULL}; -static u8* recVUStack = NULL, *recVUStackPtr = NULL; +static VuFunctionHeader** recVUHeaders[2] = { NULL, NULL }; +static VuBlockHeader* recVUBlocks[2] = { NULL, NULL }; +static u8* recVUStack[2] = { NULL, NULL }; +static u8* recVUStackPtr[2] = { NULL, NULL }; + static vector<_x86regs> s_vecRegArray(128); static VURegs* VU = NULL; @@ -343,47 +348,24 @@ static void SuperVURecompile(); // allocate VU resources static void SuperVUAlloc(int vuindex) { - // The old -1 crap has been depreciated on this function. Please - // specify either 0 or 1, thanks. - pxAssert(vuindex >= 0); + if (s_recVUMem[vuindex]) return; + s_recVUMem[vuindex] = new RecompiledCodeReserve( L"SuperVU Recompiler Cache", 0 ); + s_recVUMem[vuindex]->Reserve( sVU_EXESIZE, vuindex ? HostMemoryMap::sVU1rec : HostMemoryMap::sVU0rec, _256mb ); + // upper 4 bits must be zero! - if (s_recVUMem == NULL) + if (!s_recVUMem[vuindex]->IsOk()) { - // upper 4 bits must be zero! - // Changed "first try base" to 0xf1e0000, since 0x0c000000 liked to fail a lot. (cottonvibes) - s_recVUMem = SysMmapEx(HostMemoryMap::sVUrec, VU_EXESIZE, 0x10000000, "SuperVUAlloc"); + safe_delete(s_recVUMem[vuindex]); + throw Exception::VirtualMemoryMapConflict() + .SetDiagMsg(pxsFmt( L"SuperVU failed to allocate virtual memory below 256MB." )) + .SetUserMsg(pxE( ".Error:superVU:VirtualMemoryAlloc", + L"Out of Memory (sorta): The SuperVU recompiler was unable to reserve the specific memory " + L"ranges required, and will not be available for use. This is not a critical error, since " + L"the sVU rec is obsolete, and you should use microVU instead anyway. :)" + )); - // Try again at some other random memory location... whatever. >_< - if( s_recVUMem == NULL ) - s_recVUMem = SysMmapEx(0xc2b0000, VU_EXESIZE, 0x10000000, "SuperVUAlloc"); - - if (s_recVUMem == NULL) - { - throw Exception::VirtualMemoryMapConflict() - .SetDiagMsg(pxsFmt( L"SuperVU failed to allocate virtual memory below 256MB." )) - .SetUserMsg(pxE( ".Error:superVU:VirtualMemoryAlloc", - L"Out of Memory (sorta): The SuperVU recompiler was unable to reserve the specific memory " - L"ranges required, and will not be available for use. This is not a critical error, since " - L"the sVU rec is obsolete, and you should use microVU instead anyway. :)" - )); - } - - ProfilerRegisterSource("sVU Rec", s_recVUMem, VU_EXESIZE); - - if (recVUStack == NULL) recVUStack = new u8[SUPERVU_STACKSIZE * 4]; - } - - if (vuindex >= 0) - { - pxAssert(s_recVUMem != NULL); - - if (recVUHeaders[vuindex] == NULL) - recVUHeaders[vuindex] = new VuFunctionHeader* [s_MemSize[vuindex] / 8]; - if (recVUBlocks[vuindex] == NULL) - recVUBlocks[vuindex] = new VuBlockHeader[s_MemSize[vuindex] / 8]; - if (s_plistCachedHeaders[vuindex] == NULL) - s_plistCachedHeaders[vuindex] = new list[s_MemSize[vuindex] / 8]; + ProfilerRegisterSource("sVU Rec", *s_recVUMem[vuindex], sVU_EXESIZE); } } @@ -416,72 +398,66 @@ void DestroyVUHeaders(int vuindex) // destroy VU resources void SuperVUDestroy(int vuindex) { - list::iterator it; + pxAssumeDev(vuindex >= 0 && vuindex <= 2, "Invalid VU index parameter!"); - if (vuindex < 0) - { - SuperVUDestroy(0); - SuperVUDestroy(1); - ProfilerTerminateSource("VURec"); - SafeSysMunmap(s_recVUMem, VU_EXESIZE); - safe_delete_array(recVUStack); - } - else - { - safe_delete_array(recVUHeaders[vuindex]); - safe_delete_array(recVUBlocks[vuindex]); + safe_delete_array(recVUHeaders[vuindex]); + safe_delete_array(recVUBlocks[vuindex]); - if (s_plistCachedHeaders[vuindex] != NULL) + if (s_plistCachedHeaders[vuindex] != NULL) + { + for (u32 j = 0; j < s_MemSize[vuindex] / 8; ++j) { - for (u32 j = 0; j < s_MemSize[vuindex] / 8; ++j) - { - DestroyCachedHeaders(vuindex, j); - } - safe_delete_array(s_plistCachedHeaders[vuindex]); + DestroyCachedHeaders(vuindex, j); } - DestroyVUHeaders(vuindex); + safe_delete_array(s_plistCachedHeaders[vuindex]); } + DestroyVUHeaders(vuindex); + + ProfilerTerminateSource(vuindex ? "sVU1Rec" : "sVU0Rec"); + + safe_delete(s_recVUMem[vuindex]); + safe_delete_array(recVUStack[vuindex]); } // reset VU void SuperVUReset(int vuindex) { + pxAssumeDev(vuindex >= 0 && vuindex <= 2, "Invalid VU index parameter!"); + #ifdef PCSX2_DEBUG s_vucount = 0; #endif - if (s_recVUMem == NULL) - return; + DevCon.WriteLn("SuperVU%d: Resetting function and block lists.", vuindex); - //pxAssume( s_recVUMem != NULL ); + if (recVUHeaders[vuindex] == NULL) + recVUHeaders[vuindex] = new VuFunctionHeader* [s_MemSize[vuindex] / 8]; + if (recVUBlocks[vuindex] == NULL) + recVUBlocks[vuindex] = new VuBlockHeader[s_MemSize[vuindex] / 8]; + if (s_plistCachedHeaders[vuindex] == NULL) + s_plistCachedHeaders[vuindex] = new std::list[s_MemSize[vuindex] / 8]; - if (vuindex < 0) + if (recVUHeaders[vuindex]) memset(recVUHeaders[vuindex], 0, sizeof(VuFunctionHeader*) * (s_MemSize[vuindex] / 8)); + if (recVUBlocks[vuindex]) memset(recVUBlocks[vuindex], 0, sizeof(VuBlockHeader) * (s_MemSize[vuindex] / 8)); + + if (s_plistCachedHeaders[vuindex] != NULL) { - DbgCon.WriteLn("SuperVU: Resetting recompiler memory and structures."); - - // Does this cause problems on VU recompiler resets? It could, if the VU works like - // the EE used to, and actually tries to re-enter the recBlock after issuing a clear. (air) - - //memset_8<0xcd, VU_EXESIZE>(s_recVUMem); - memzero_ptr(recVUStack); - - s_recVUPtr = s_recVUMem; - } - else - { - DbgCon.WriteLn("SuperVU [VU%d]: Resetting the recs and junk", vuindex); - if (recVUHeaders[vuindex]) memset(recVUHeaders[vuindex], 0, sizeof(VuFunctionHeader*) * (s_MemSize[vuindex] / 8)); - if (recVUBlocks[vuindex]) memset(recVUBlocks[vuindex], 0, sizeof(VuBlockHeader) * (s_MemSize[vuindex] / 8)); - - if (s_plistCachedHeaders[vuindex] != NULL) + for (u32 j = 0; j < s_MemSize[vuindex] / 8; ++j) { - for (u32 j = 0; j < s_MemSize[vuindex] / 8; ++j) - { - DestroyCachedHeaders(vuindex, j); - } + DestroyCachedHeaders(vuindex, j); } - DestroyVUHeaders(vuindex); } + DestroyVUHeaders(vuindex); + + if (!s_recVUMem[vuindex] || !s_recVUMem[vuindex]->IsOk()) return; + + DevCon.WriteLn("SuperVU%u: Resetting recompiler cache.", vuindex); + + if (!recVUStack[vuindex]) recVUStack[vuindex] = new u8[SUPERVU_STACKSIZE * 4]; + memzero_ptr(recVUStack[vuindex]); + + s_recVUMem[vuindex]->Reset(); + s_recVUPtr[vuindex] = *s_recVUMem[vuindex]; } // clear the block and any joining blocks @@ -847,16 +823,15 @@ void VuBaseBlock::GetInstsAtPc(int instpc, list& listinsts) static VuFunctionHeader* SuperVURecompileProgram(u32 startpc, int vuindex) { pxAssert(vuindex < 2); - pxAssert(s_recVUPtr != NULL); + pxAssert(s_recVUPtr[vuindex] != NULL); //Console.WriteLn("svu%c rec: %x", '0'+vuindex, startpc); // if recPtr reached the mem limit reset whole mem - if (((uptr)s_recVUPtr - (uptr)s_recVUMem) >= VU_EXESIZE - 0x40000) + if ((s_recVUPtr[vuindex] < s_recVUMem[vuindex]->GetPtr()) || (s_recVUPtr[vuindex] >= s_recVUMem[vuindex]->GetPtrEnd() - _256kb)) { - //Console.WriteLn("SuperVU reset mem"); + Console.WriteLn("SuperVU%u: Recompiler cache reset...", vuindex); SuperVUReset(0); SuperVUReset(1); - SuperVUReset(-1); if (s_TotalVUCycles > 0) { // already executing, so return NULL @@ -912,12 +887,12 @@ static VuFunctionHeader* SuperVURecompileProgram(u32 startpc, int vuindex) #endif // code generation - x86SetPtr(s_recVUPtr); + xSetPtr(s_recVUPtr[vuindex]); branch = 0; SuperVURecompile(); - s_recVUPtr = x86Ptr; + s_recVUPtr[vuindex] = xGetPtr(); // set the function's range VuFunctionHeader::RANGE r; @@ -947,7 +922,7 @@ static VuFunctionHeader* SuperVURecompileProgram(u32 startpc, int vuindex) } s_listBlocks.clear(); - pxAssert(s_recVUPtr < s_recVUMem + VU_EXESIZE); + pxAssertDev(s_recVUPtr[vuindex] < s_recVUMem[vuindex]->GetPtrEnd(), "SuperVU recompiler cache exceeded! (possible memory corruption)"); return s_pFnHeader; } @@ -2697,18 +2672,18 @@ void SuperVUFlush(int p, int wait) // executed only once per program static u32* SuperVUStaticAlloc(u32 size) { - pxAssert(recVUStackPtr + size <= recVUStack + SUPERVU_STACKSIZE); + pxAssert(recVUStackPtr[s_vu] + size <= recVUStack[s_vu] + SUPERVU_STACKSIZE); // always zero - if (size == 4) *(u32*)recVUStackPtr = 0; - else memset(recVUStackPtr, 0, size); - recVUStackPtr += size; - return (u32*)(recVUStackPtr - size); + if (size == 4) *(u32*)recVUStackPtr[s_vu] = 0; + else memset(recVUStackPtr[s_vu], 0, size); + recVUStackPtr[s_vu] += size; + return (u32*)(recVUStackPtr[s_vu] - size); } static void SuperVURecompile() { // save cpu state - recVUStackPtr = recVUStack; + recVUStackPtr[s_vu] = recVUStack[s_vu]; _initXMMregs(); @@ -4617,9 +4592,9 @@ recSuperVU0::recSuperVU0() IsInterpreter = false; } -void recSuperVU0::Allocate() +void recSuperVU0::Reserve() { - SuperVUAlloc( 0 ); + SuperVUAlloc(0); } void recSuperVU0::Shutdown() throw() @@ -4645,6 +4620,15 @@ void recSuperVU0::Clear(u32 Addr, u32 Size) SuperVUClear(Addr, Size, 0); } +uint recSuperVU0::GetCacheReserve() const +{ + return sVU_EXESIZE / _1mb; +} + +void recSuperVU0::SetCacheReserve( uint reserveInMegs ) const +{ + //microVU0.cacheSize = reserveInMegs * _1mb; +} // -------------------------------------------------------------------------------------- // recSuperVU1 Interface @@ -4655,9 +4639,9 @@ recSuperVU1::recSuperVU1() IsInterpreter = false; } -void recSuperVU1::Allocate() +void recSuperVU1::Reserve() { - SuperVUAlloc( 1 ); + SuperVUAlloc(1); } void recSuperVU1::Shutdown() throw() @@ -4670,6 +4654,16 @@ void recSuperVU1::Reset() SuperVUReset( 1 ); } +uint recSuperVU1::GetCacheReserve() const +{ + return sVU_EXESIZE / _1mb; +} + +void recSuperVU1::SetCacheReserve( uint reserveInMegs ) const +{ + //microVU0.cacheSize = reserveInMegs * _1mb; +} + #if 0 #include "sVU_Compare.h" #else