Files
archived-pcsx2/pcsx2/VUmicro.h
cottonvibes ac9bf45f98 pcsx2: Implemented Threaded VU1 :D
Threading VU1 took a lot of rewrites and new code to make possible (MTGS, microVU, gifUnit...), but we finally got to the point where it was feasible, and now we've done it! (so now everyone can stop complaining that pcsx2 only takes advantages of 2 cores :p).

The speedups in the games that benefit from it are great if you have a cpu with 3+ cores (generally a 10~45% speedup), however games that are GS limited can be a slowdown (especially on dual core cpu's).

The option can be found in the speedhacks section as "MTVU (Multi-Threaded microVU1)". And when enabled it should should show the VU thread-time percentage on the title bar window (Like we currently do for EE/GS/UI threads).

It is listed as a speedhack because in order for threading VU1 to have been a speedup, we need to assume that games will not send gif packets containing Signal/Finish/Label commands from path 1 (vu1's xgkick). The good news is very-few games ever do this, so the compatibility of MTVU is very high (a game that does do this will likely hang).

Note: vs2010 builds and Linux builds need to be updated to include "MTVU.h" and "MTVU.cpp".


git-svn-id: http://pcsx2.googlecode.com/svn/trunk@4865 96395faa-99c1-11dd-bbfe-3dabce05a288
2011-08-12 02:31:49 +00:00

328 lines
9.9 KiB
C++

/* 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 "VU.h"
#include "VUops.h"
#include "R5900.h"
static const uint VU0_MEMSIZE = 0x1000; // 4kb
static const uint VU0_PROGSIZE = 0x1000; // 4kb
static const uint VU1_MEMSIZE = 0x4000; // 16kb
static const uint VU1_PROGSIZE = 0x4000; // 16kb
static const uint VU0_MEMMASK = VU0_MEMSIZE-1;
static const uint VU0_PROGMASK = VU0_PROGSIZE-1;
static const uint VU1_MEMMASK = VU1_MEMSIZE-1;
static const uint VU1_PROGMASK = VU1_PROGSIZE-1;
#define vuRunCycles (512*12) // Cycles to run ExecuteBlockJIT() for (called from within recs)
#define vu0RunCycles (512*12) // Cycles to run vu0 for whenever ExecuteBlock() is called
#define vu1RunCycles (3000000) // mVU1 uses this for inf loop detection on dev builds
// --------------------------------------------------------------------------------------
// BaseCpuProvider
// --------------------------------------------------------------------------------------
//
// Design Note: This class is only partial C++ style. It still relies on Alloc and Shutdown
// calls for memory and resource management. This is because the underlying implementations
// of our CPU emulators don't have properly encapsulated objects yet -- if we allocate ram
// in a constructor, it won't get free'd if an exception occurs during object construction.
// Once we've resolved all the 'dangling pointers' and stuff in the recompilers, Alloc
// and Shutdown can be removed in favor of constructor/destructor syntax.
//
class BaseCpuProvider
{
protected:
// 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
// are valid for logging or not. (see DisVU1Micro.cpp, etc) [kinda hacky, might
// be removed in the future]
bool IsInterpreter;
public:
BaseCpuProvider()
{
m_Reserved = 0;
}
virtual ~BaseCpuProvider() throw()
{
if( m_Reserved != 0 )
Console.Warning( "Cleanup miscount detected on CPU provider. Count=%d", m_Reserved );
}
virtual const char* GetShortName() const=0;
virtual wxString GetLongName() const=0;
// returns the number of bytes committed to the working caches for this CPU
// provider (typically this refers to recompiled code caches, but could also refer
// to other optional growable allocations).
virtual size_t GetCommittedCache() const
{
return 0;
}
virtual void Reserve()=0;
virtual void Shutdown()=0;
virtual void Reset()=0;
virtual void Execute(u32 cycles)=0;
virtual void ExecuteBlock(bool startUp)=0;
virtual void Step()=0;
virtual void Clear(u32 Addr, u32 Size)=0;
// C++ Calling Conventions are unstable, and some compilers don't even allow us to take the
// address of C++ methods. We need to use a wrapper function to invoke the ExecuteBlock from
// recompiled code.
static void __fastcall ExecuteBlockJIT( BaseCpuProvider* cpu )
{
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;
};
// --------------------------------------------------------------------------------------
// BaseVUmicroCPU
// --------------------------------------------------------------------------------------
// Layer class for possible future implementation (currently is nothing more than a type-safe
// type define).
//
class BaseVUmicroCPU : public BaseCpuProvider {
public:
int m_Idx;
u32 m_lastEEcycles;
BaseVUmicroCPU() {
m_Idx = 0;
m_lastEEcycles = 0;
}
virtual ~BaseVUmicroCPU() throw() {}
// Called by the PS2 VM's event manager for every internal vertical sync (occurs at either
// 50hz (pal) or 59.94hz (NTSC).
//
// Exceptions:
// This method is not allowed to throw exceptions, since exceptions may not propagate
// safely from the context of recompiled code stackframes.
//
// Thread Affinity:
// Called from the EEcore thread. No locking is performed, so any necessary locks must
// be implemented by the CPU provider manually.
//
virtual void Vsync() throw() { }
virtual void Step() {
// Ideally this would fall back on interpretation for executing single instructions
// for all CPU types, but due to VU complexities and large discrepancies between
// clamping in recs and ints, it's not really worth bothering with yet.
}
// Execute VU for the number of VU cycles (recs might go over 0~30 cycles)
// virtual void Execute(u32 cycles)=0;
// Executes a Block based on static preset cycles OR
// Executes a Block based on EE delta time (see VUmicro.cpp)
virtual void ExecuteBlock(bool startUp=0);
static void __fastcall ExecuteBlockJIT(BaseVUmicroCPU* cpu);
// VU1 sometimes needs to break execution on XGkick Path1 transfers if
// there is another gif path 2/3 transfer already taking place.
// Use this method to resume execution of VU1.
virtual void ResumeXGkick() {}
};
// --------------------------------------------------------------------------------------
// InterpVU0 / InterpVU1
// --------------------------------------------------------------------------------------
class InterpVU0 : public BaseVUmicroCPU
{
public:
InterpVU0();
virtual ~InterpVU0() throw() { Shutdown(); }
const char* GetShortName() const { return "intVU0"; }
wxString GetLongName() const { return L"VU0 Interpreter"; }
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
{
public:
InterpVU1();
virtual ~InterpVU1() throw() { Shutdown(); }
const char* GetShortName() const { return "intVU1"; }
wxString GetLongName() const { return L"VU1 Interpreter"; }
void Reserve() { }
void Shutdown() throw();
void Reset();
void Step();
void Execute(u32 cycles);
void Clear(u32 addr, u32 size) {}
void ResumeXGkick() {}
uint GetCacheReserve() const { return 0; }
void SetCacheReserve( uint reserveInMegs ) const {}
};
// --------------------------------------------------------------------------------------
// recMicroVU0 / recMicroVU1
// --------------------------------------------------------------------------------------
class recMicroVU0 : public BaseVUmicroCPU
{
public:
recMicroVU0();
virtual ~recMicroVU0() throw() { Shutdown(); }
const char* GetShortName() const { return "mVU0"; }
wxString GetLongName() const { return L"microVU0 Recompiler"; }
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
{
public:
recMicroVU1();
virtual ~recMicroVU1() throw() { Shutdown(); }
const char* GetShortName() const { return "mVU1"; }
wxString GetLongName() const { return L"microVU1 Recompiler"; }
void Reserve();
void Shutdown() throw();
void Reset();
void Execute(u32 cycles);
void Clear(u32 addr, u32 size);
void Vsync() throw();
void ResumeXGkick();
uint GetCacheReserve() const;
void SetCacheReserve( uint reserveInMegs ) const;
};
// --------------------------------------------------------------------------------------
// recSuperVU0 / recSuperVU1
// --------------------------------------------------------------------------------------
class recSuperVU0 : public BaseVUmicroCPU
{
public:
recSuperVU0();
const char* GetShortName() const { return "sVU0"; }
wxString GetLongName() const { return L"SuperVU0 Recompiler"; }
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
{
public:
recSuperVU1();
const char* GetShortName() const { return "sVU1"; }
wxString GetLongName() const { return L"SuperVU1 Recompiler"; }
void Reserve();
void Shutdown() throw();
void Reset();
void Execute(u32 cycles);
void Clear(u32 Addr, u32 Size);
void ResumeXGkick() { Console.Warning("ResumeXGkick() Not implemented!"); }
uint GetCacheReserve() const;
void SetCacheReserve( uint reserveInMegs ) const;
};
extern BaseVUmicroCPU* CpuVU0;
extern BaseVUmicroCPU* CpuVU1;
// VU0
extern void vu0ResetRegs();
extern void __fastcall vu0ExecMicro(u32 addr);
extern void vu0Exec(VURegs* VU);
extern void vu0Finish();
extern void iDumpVU0Registers();
// VU1
extern void vu1Finish();
extern void vu1ResetRegs();
extern void __fastcall vu1ExecMicro(u32 addr);
extern void vu1Exec(VURegs* VU);
extern void iDumpVU1Registers();
#ifdef VUM_LOG
#define IdebugUPPER(VU) \
VUM_LOG("%s", dis##VU##MicroUF(VU.code, VU.VI[REG_TPC].UL));
#define IdebugLOWER(VU) \
VUM_LOG("%s", dis##VU##MicroLF(VU.code, VU.VI[REG_TPC].UL));
#define _vuExecMicroDebug(VU) \
VUM_LOG("_vuExecMicro: %8.8x", VU.VI[REG_TPC].UL);
#else
#define IdebugUPPER(VU)
#define IdebugLOWER(VU)
#define _vuExecMicroDebug(VU)
#endif