microVU: Finished most of the caching/memory model rewrite (whats left is mostly just tweaking/cleanup...), mVU should be back to stable status now afaik.

With this rewrite, God of War 1/2 games don't constantly recompile and play a lot more smoother on mVU than they did before; this was one of my goals with the rewrite as i always hated how crappy GoW ran with mVU.

Also a nice big noticeable change, pcsx2's memory footprint was reduced by around ~170 mb! microVU was taking up a lot of memory because it made GoW games run better to have a huge rec-cache with the old model, but now thats not needed anymore.

git-svn-id: http://pcsx2.googlecode.com/svn/trunk@2668 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
cottonvibes 2010-03-05 06:24:15 +00:00
parent c19dfa41d4
commit 69fd9fb395
4 changed files with 70 additions and 118 deletions

View File

@ -92,8 +92,6 @@ _f void mVUinit(VURegs* vuRegsPtr, int vuIndex) {
mVU->progSize = (vuIndex ? 0x4000 : 0x1000) / 4;
mVU->cache = NULL;
mVU->cacheSize = mVUcacheSize;
mVU->prog.max = mMaxProg - 1;
mVU->prog.prog = (microProgram*)_aligned_malloc(sizeof(microProgram)*(mVU->prog.max+1), 64);
mVU->regAlloc = new microRegAlloc(mVU->regs);
mVUprint((vuIndex) ? "microVU1: init" : "microVU0: init");
@ -105,10 +103,9 @@ _f void mVUinit(VURegs* vuRegsPtr, int vuIndex) {
if (!mVU->cache) throw Exception::OutOfMemory( "microVU Error: Failed to allocate recompiler memory!" );
memset(mVU->cache, 0xcc, mVU->cacheSize + 0x1000);
memset(mVU->prog.prog, 0, sizeof(microProgram)*(mVU->prog.max+1));
for (u32 i = 0; i < (mVU->progSize / 2); i++) {
mVU->prog.list[i].list = new deque<microProgram*>();
mVU->prog.prog[i].list = new deque<microProgram*>();
}
// Setup Entrance/Exit Points
@ -131,10 +128,9 @@ _f void mVUreset(mV) {
// Program Variables
mVU->prog.cleared = 1;
mVU->prog.isSame = -1;
mVU->prog.cur = -1;
mVU->prog.cur = NULL;
mVU->prog.total = -1;
mVU->prog.curFrame = 0;
mVU->prog.max = mMaxProg - 1;
// Setup Dynarec Cache Limits for Each Program
u8* z = (mVU->cache + 0x1000); // Dispatcher Code is in first page of cache
@ -143,16 +139,9 @@ _f void mVUreset(mV) {
mVU->prog.x86end = (u8*)((uptr)z + (uptr)(mVU->cacheSize - (_1mb * 3))); // 3mb "Safe Zone"
for (u32 i = 0; i < (mVU->progSize / 2); i++) {
mVU->prog.list[i].list->clear();
mVU->prog.list[i].size = 0;
mVU->prog.list[i].quick = NULL;
mVU->prog.list[i].quickIdx = -1;
}
for (int i = 0; i <= mVU->prog.max; i++) {
if (!mVU->index) mVUclearProg<0>(mVU->prog.prog[i]);
else mVUclearProg<1>(mVU->prog.prog[i]);
mVU->prog.prog[i].idx = i;
mVU->prog.prog[i].list->clear();
mVU->prog.quick[i].block = NULL;
mVU->prog.quick[i].prog = NULL;
}
}
@ -164,17 +153,14 @@ _f void mVUclose(mV) {
if (mVU->cache) { HostSys::Munmap(mVU->cache, mVU->cacheSize); mVU->cache = NULL; }
// Delete Programs and Block Managers
if (mVU->prog.prog) {
for (int i = 0; i <= mVU->prog.max; i++) {
for (u32 j = 0; j < (mVU->progSize / 2); j++) {
safe_delete(mVU->prog.prog[i].block[j]);
}
}
safe_aligned_free(mVU->prog.prog);
}
for (u32 i = 0; i < (mVU->progSize / 2); i++) {
safe_delete(mVU->prog.list[i].list);
deque<microProgram*>::iterator it = mVU->prog.prog[i].list->begin();
for ( ; it != mVU->prog.prog[i].list->end(); it++) {
safe_aligned_free(it[0]);
}
safe_delete(mVU->prog.prog[i].list);
}
safe_delete(mVU->regAlloc);
}
@ -184,8 +170,8 @@ _f void mVUclear(mV, u32 addr, u32 size) {
memzero(mVU->prog.lpState); // Clear pipeline state
mVU->prog.cleared = 1; // Next execution searches/creates a new microprogram
for (u32 i = 0; i < (mVU->progSize / 2); i++) {
mVU->prog.list[i].quick = NULL; // Clear current quick-reference prog list
mVU->prog.list[i].quickIdx = -1; // Set to 'invalid' index
mVU->prog.quick[i].block = NULL; // Clear current quick-reference block
mVU->prog.quick[i].prog = NULL; // Clear current quick-reference prog
}
}
}
@ -195,7 +181,7 @@ _f void mVUclear(mV, u32 addr, u32 size) {
//------------------------------------------------------------------
// Clears program data
_mVUt _f void mVUclearProg(microProgram& program) {
_mVUt _f void mVUclearProg(microProgram& program, bool deleteBlocks) {
microVU* mVU = mVUx;
program.used = 0;
program.age = isDead;
@ -207,15 +193,27 @@ _mVUt _f void mVUclearProg(microProgram& program) {
program.ranges.total = -1;
}
for (u32 i = 0; i < (mVU->progSize / 2); i++) {
safe_delete(program.block[i]);
if (deleteBlocks) safe_delete(program.block[i]);
}
}
/*
microProgram* mVUcreateProg(int progIndex, int startPC) {
return (microProgram*)_aligned_malloc(sizeof(microProgram), 64);
// Creates a new Micro Program
_mVUt _f microProgram* mVUcreateProg(int startPC) {
microVU* mVU = mVUx;
microProgram* prog = (microProgram*)_aligned_malloc(sizeof(microProgram), 64);
memzero_ptr<sizeof(microProgram)>(prog);
mVUclearProg<vuIndex>(*prog);
prog->age = isYoung;
prog->used = 1;
prog->idx = mVU->prog.total++;
prog->startPC = startPC;
mVUcacheProg<vuIndex>(*prog); // Cache Micro Program
float cacheSize = (float)(u32)((u32)mVU->prog.x86end - (u32)mVU->prog.x86start);
float cacheStat =((float)(u32)((u32)mVU->prog.x86ptr - (u32)mVU->prog.x86start)) / cacheSize * 100;
Console.WriteLn(Color_Orange, "microVU%d: Cached MicroPrograms = [%03d] [PC=%04x] [List=%02d] (Cache = %f%%)",
vuIndex, mVU->prog.total, startPC, mVU->prog.prog[startPC].list->size()+1, cacheStat);
return prog;
}
*/
// Caches Micro Program
_mVUt _f void mVUcacheProg(microProgram& prog) {
@ -225,57 +223,15 @@ _mVUt _f void mVUcacheProg(microProgram& prog) {
mVUdumpProg(prog.idx);
}
// Finds the least used program, (if program list full clears and returns an old program; if not-full, returns free program)
_mVUt _f int mVUfindLeastUsedProg() {
microVU* mVU = mVUx;
for (int i = 0; i <= mVU->prog.max; i++) {
if (mVU->prog.prog[i].age == isDead) {
mVU->prog.total++;
mVUcacheProg<vuIndex>(mVU->prog.prog[i]); // Cache Micro Program
mVU->prog.prog[i].age = isYoung;
mVU->prog.prog[i].used = 1;
Console.WriteLn(Color_Orange, "microVU%d: Cached MicroPrograms = [%03d] [%03d]", vuIndex, i+1, mVU->prog.total+1);
return i;
}
}
// If we reach this, it means all program slots are used, so delete old ones...
static int clearIdx = 0;
int pIdx = clearIdx;
for (int i = 0; i < ((mVU->prog.max+1)/4); i++) {
if (mVU->prog.prog[i].used) continue; // Don't delete currently executing program(s)
assert(mVU->prog.prog[i].startPC < (mVU->progSize / 2));
microProgramList& list = mVU->prog.list[mVU->prog.prog[i].startPC];
deque<microProgram*>::iterator it = list.list->begin();
for ( ; it != list.list->end(); it++) {
if (it[0] == &mVU->prog.prog[i]) {
list.list->erase(it);
list.quick = NULL;
DevCon.WriteLn("Deleting List Reference!");
break;
}
}
mVUclearProg<vuIndex>(mVU->prog.prog[clearIdx]);
clearIdx = aWrap(clearIdx+1, mVU->prog.max);
}
mVU->prog.total -= ((mVU->prog.max+1)/4)-1;
mVUcacheProg<vuIndex>(mVU->prog.prog[pIdx]); // Cache Micro Program
mVU->prog.prog[pIdx].age = isYoung;
mVU->prog.prog[pIdx].used = 1;
Console.WriteLn(Color_Orange, "microVU%d: Cached MicroPrograms = [%03d] [%03d]", vuIndex, pIdx+1, mVU->prog.total+1);
return pIdx;
}
// Finds and Ages/Kills Programs if they haven't been used in a while.
_f void mVUvsyncUpdate(mV) {
for (int i = 0; i <= mVU->prog.max; i++) {
/*for (int i = 0; i <= mVU->prog.max; i++) {
if (mVU->prog.prog[i].age == isDead) continue;
if (mVU->prog.prog[i].used) {
mVU->prog.prog[i].used = 0;
mVU->prog.prog[i].frame = mVU->prog.curFrame;
}
/*else { // Age Micro Program that wasn't used
else { // Age Micro Program that wasn't used
s32 diff = mVU->prog.curFrame - mVU->prog.prog[i].frame;
if (diff >= (60 * 1)) {
if (i == mVU->prog.cur) continue; // Don't Age/Kill last used program
@ -286,8 +242,8 @@ _f void mVUvsyncUpdate(mV) {
}
//elif(diff >= (60 * 1)) { mVU->prog.prog[i].age = isOld; }
//elif(diff >= (20 * 1)) { mVU->prog.prog[i].age = isAged; }
}*/
}
}*/
mVU->prog.curFrame++;
}
@ -310,7 +266,7 @@ _mVUt _f bool mVUcmpProg(microProgram& prog, const bool cmpWholeProg) {
if ((cmpWholeProg && !memcmp_mmx((u8*)prog.data, mVU->regs->Micro, mVU->microMemSize))
|| (!cmpWholeProg && mVUcmpPartial<vuIndex>(prog))) {
mVU->prog.cleared = 0;
mVU->prog.cur = prog.idx;
mVU->prog.cur =&prog;
mVU->prog.isSame = cmpWholeProg ? 1 : -1;
prog.used = 1;
prog.age = isYoung;
@ -322,35 +278,34 @@ _mVUt _f bool mVUcmpProg(microProgram& prog, const bool cmpWholeProg) {
// Searches for Cached Micro Program and sets prog.cur to it (returns entry-point to program)
_mVUt _f void* mVUsearchProg(u32 startPC, uptr pState) {
microVU* mVU = mVUx;
microProgramList& list = mVU->prog.list[startPC/8];
if(!list.quick) { // If null, we need to search for new program
microProgramQuick& quick = mVU->prog.quick[startPC/8];
microProgramList& list = mVU->prog.prog [startPC/8];
if(!quick.prog) { // If null, we need to search for new program
deque<microProgram*>::iterator it = list.list->begin();
for ( ; it != list.list->end(); it++) {
if (mVUcmpProg<vuIndex>(*it[0], 0)) {
microProgram* t = it[0];
quick.block = it[0]->block[startPC/8];
quick.prog = it[0];
list.list->erase(it);
list.list->push_front(t);
list.quick = t->block[startPC/8];
list.quickIdx = t->idx;
return mVUentryGet(mVU, list.quick, startPC, pState);
list.list->push_front(quick.prog);
return mVUentryGet(mVU, quick.block, startPC, pState);
}
}
mVU->prog.cur = mVUfindLeastUsedProg<vuIndex>(); // If cleared and program not found, make a new program instance
mVU->prog.cur = mVUcreateProg<vuIndex>(startPC/8); // If cleared and program not found, make a new program instance
mVU->prog.cleared = 0;
mVU->prog.isSame = 1;
mVUcurProg.startPC = startPC / 8;
void* entryPoint = mVUblockFetch(mVU, startPC, pState);
list.quick = mVUcurProg.block[startPC/8];
list.quickIdx = mVUcurProg.idx;
quick.block = mVUcurProg.block[startPC/8];
quick.prog = &mVUcurProg;
list.list->push_front(&mVUcurProg);
DevCon.WriteLn("List[%d].Size = %d", startPC/8, list.list->size());
return entryPoint;
}
mVU->prog.cur = list.quickIdx;
mVU->prog.isSame = -1;
mVUcurProg.used = 1;
mVUcurProg.age = isYoung;
return mVUentryGet(mVU, list.quick, startPC, pState); // If list.quick, then we've already found and recompiled the program ;)
mVU->prog.cur = quick.prog;
mVU->prog.cur->used = 1;
mVU->prog.cur->age = isYoung;
return mVUentryGet(mVU, quick.block, startPC, pState); // If list.quick, then we've already found and recompiled the program ;)
}
//------------------------------------------------------------------

View File

@ -121,7 +121,6 @@ enum microProgramAge {
};
#define mProgSize (0x4000/4)
#define mMaxProg ((mVU->index)?400:8) // The amount of Micro Programs Recs will 'remember'
struct microProgram {
u32 data [mProgSize]; // Holds a copy of the VU microProgram
microBlockManager* block[mProgSize/2]; // Array of Block Managers
@ -135,19 +134,19 @@ struct microProgram {
struct microProgramList {
deque<microProgram*>* list; // List of microPrograms who start with the same startPC value
microBlockManager* quick; // Quick reference to valid microBlockManager for current startPC
int quickIdx; // Index of the microProgram who is the owner of 'quick'
int size; // Current size of the list...
};
struct microProgramQuick {
microBlockManager* block; // Quick reference to valid microBlockManager for current startPC
microProgram* prog; // The microProgram who is the owner of 'block'
};
struct microProgManager {
microIR<mProgSize> IRinfo; // IR information
microProgram* prog; // Cache MicroPrograms in memory (indirect jumps are treated as new programs)
microProgramList list[mProgSize/2]; // List of microProgram references indexed by startPC values
//int* progList; // List of program indexes ordered by age (ordered from newest to oldest)
int max; // Max Number of MicroPrograms minus 1
int total; // Total Number of valid MicroPrograms minus 1
int cur; // Index to Current MicroProgram thats running (-1 = uncached)
microProgramList prog [mProgSize/2]; // List of microPrograms indexed by startPC values
microProgramQuick quick[mProgSize/2]; // Quick reference to valid microPrograms for current execution
microProgram* cur; // Pointer to currently running MicroProgram
int total; // Total Number of valid MicroPrograms
int isSame; // Current cached microProgram is Exact Same program as mVU->regs->Micro (-1 = unknown, 0 = No, 1 = Yes)
int cleared; // Micro Program is Indeterminate so must be searched for (and if no matches are found then recompile a new one)
u32 curFrame; // Frame Counter
@ -157,7 +156,7 @@ struct microProgManager {
microRegInfo lpState; // Pipeline state from where program left off (useful for continuing execution)
};
#define mVUcacheSize ((mMaxProg < 20) ? (_1mb * 10) : (mMaxProg * (_1mb * 0.5))) // 0.5mb per program
#define mVUcacheSize ((mVU->index) ? (_1mb * 20) : (_1mb * 5))
struct microVU {
__aligned16 u32 macFlag[4]; // 4 instances of mac flag (used in execution)
@ -215,10 +214,10 @@ mVUop(mVUopU);
mVUop(mVUopL);
// Private Functions
_mVUt _f void mVUclearProg(microProgram& prog);
_mVUt _f void mVUclearProg(microProgram& prog, bool deleteBlocks = 1);
_mVUt _f void mVUcacheProg(microProgram& prog);
_mVUt _f int mVUfindLeastUsedProg();
_mVUt _f void* mVUsearchProg(u32 startPC, uptr pState);
_mVUt _f microProgram* mVUfindLeastUsedProg();
void* __fastcall mVUexecuteVU0(u32 startPC, u32 cycles);
void* __fastcall mVUexecuteVU1(u32 startPC, u32 cycles);

View File

@ -37,8 +37,8 @@ _f void mVUcheckIsSame(mV) {
mVU->prog.isSame = !memcmp_mmx((u8*)mVUcurProg.data, mVU->regs->Micro, mVU->microMemSize);
}
if (mVU->prog.isSame == 0) {
if (!isVU1) mVUcacheProg<0>(mVU->prog.prog[mVU->prog.cur]);
else mVUcacheProg<1>(mVU->prog.prog[mVU->prog.cur]);
if (!isVU1) mVUcacheProg<0>(*mVU->prog.cur);
else mVUcacheProg<1>(*mVU->prog.cur);
mVU->prog.isSame = 1;
}
}
@ -447,7 +447,6 @@ _f void* mVUblockFetch(microVU* mVU, u32 startPC, uptr pState) {
// mVUcompileJIT() - Called By JR/JALR during execution
_mVUt void* __fastcall mVUcompileJIT(u32 startPC, uptr pState) {
//DevCon.WriteLn("JR/JALR!");
//return mVUblockFetch(mVUx, startPC, pState);
return mVUsearchProg<vuIndex>(startPC, pState); // Find and set correct program
}

View File

@ -156,9 +156,8 @@ typedef u32 (__fastcall *mVUCall)(void*, void*);
// Misc Macros...
#define __four(val) { val, val, val, val }
#define mVUprogI mVU->prog.prog[progIndex]
#define mVUcurProg mVU->prog.prog[mVU->prog.cur]
#define mVUblocks mVU->prog.prog[mVU->prog.cur].block
#define mVUcurProg mVU->prog.cur[0]
#define mVUblocks mVU->prog.cur->block
#define mVUir mVU->prog.IRinfo
#define mVUbranch mVU->prog.IRinfo.branch
#define mVUcycles mVU->prog.IRinfo.cycles