mirror of https://github.com/PCSX2/pcsx2.git
microVU:
- Kill programs if they haven't been used in ~7 seconds. - Only kill 1/4 (instead of all) of total programs if all slots are full. - Make sure programs are searched from oldest to newest. (this is needed to ensure that new programs don't 'evolve' into old programs and repetitively clone themselves.) git-svn-id: http://pcsx2.googlecode.com/svn/trunk@1470 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
parent
f24be5c352
commit
7f5827cee6
|
@ -40,15 +40,16 @@ microVUt(void) mVUinit(VURegs* vuRegsPtr, int vuIndex) {
|
|||
microVU* mVU = mVUx;
|
||||
memset(&mVU->prog, 0, sizeof(mVU->prog));
|
||||
|
||||
mVU->regs = vuRegsPtr;
|
||||
mVU->index = vuIndex;
|
||||
mVU->vuMemSize = (vuIndex ? 0x4000 : 0x1000);
|
||||
mVU->microMemSize = (vuIndex ? 0x4000 : 0x1000);
|
||||
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->regs = vuRegsPtr;
|
||||
mVU->index = vuIndex;
|
||||
mVU->vuMemSize = (vuIndex ? 0x4000 : 0x1000);
|
||||
mVU->microMemSize = (vuIndex ? 0x4000 : 0x1000);
|
||||
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->prog.progList = new int[mMaxProg];
|
||||
mVUprint((vuIndex) ? "microVU1: init" : "microVU0: init");
|
||||
|
||||
mVU->cache = SysMmapEx((vuIndex ? 0x5f240000 : 0x5e240000), mVU->cacheSize + 0x1000, 0, (vuIndex ? "Micro VU1" : "Micro VU0"));
|
||||
|
@ -69,18 +70,18 @@ microVUt(void) mVUinit(VURegs* vuRegsPtr, int vuIndex) {
|
|||
microVUt(void) mVUreset(mV) {
|
||||
|
||||
mVUprint((mVU->index) ? "microVU1: reset" : "microVU0: reset");
|
||||
mVUclose(mVU, 1);
|
||||
|
||||
// Clear All Program Data
|
||||
//memset(&mVU->prog, 0, sizeof(mVU->prog));
|
||||
memset(&mVU->prog.lpState, 0, sizeof(mVU->prog.lpState));
|
||||
|
||||
// Program Variables
|
||||
mVU->prog.cleared = 1;
|
||||
mVU->prog.isSame = -1;
|
||||
mVU->prog.cur = -1;
|
||||
mVU->prog.total = -1;
|
||||
mVU->prog.max = mMaxProg - 1;
|
||||
mVU->prog.cleared = 1;
|
||||
mVU->prog.isSame = -1;
|
||||
mVU->prog.cur = -1;
|
||||
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
|
||||
|
@ -89,20 +90,18 @@ microVUt(void) mVUreset(mV) {
|
|||
mVU->prog.x86end = (u8*)((uptr)z + (uptr)(mVU->cacheSize - (mVU->cacheSize*.05)));
|
||||
|
||||
for (int i = 0; i <= mVU->prog.max; i++) {
|
||||
for (int j = 0; j <= mVU->prog.prog[i].ranges.max; j++) {
|
||||
mVU->prog.prog[i].ranges.range[j][0] = -1; // Set range to
|
||||
mVU->prog.prog[i].ranges.range[j][1] = -1; // indeterminable status
|
||||
mVU->prog.prog[i].ranges.total = -1;
|
||||
}
|
||||
if (!mVU->index) mVUclearProg<0>(i);
|
||||
else mVUclearProg<1>(i);
|
||||
mVU->prog.progList[i] = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Free Allocated Resources
|
||||
microVUt(void) mVUclose(mV, bool isReset) {
|
||||
microVUt(void) mVUclose(mV) {
|
||||
|
||||
mVUprint((mVU->index) ? "microVU1: close" : "microVU0: close");
|
||||
|
||||
if (!isReset && mVU->cache) { HostSys::Munmap(mVU->cache, mVU->cacheSize); mVU->cache = NULL; }
|
||||
if (mVU->cache) { HostSys::Munmap(mVU->cache, mVU->cacheSize); mVU->cache = NULL; }
|
||||
|
||||
// Delete Programs and Block Managers
|
||||
if (mVU->prog.prog) {
|
||||
|
@ -111,8 +110,9 @@ microVUt(void) mVUclose(mV, bool isReset) {
|
|||
microBlockManager::Delete(mVU->prog.prog[i].block[j]);
|
||||
}
|
||||
}
|
||||
if (!isReset) safe_aligned_free(mVU->prog.prog);
|
||||
safe_aligned_free(mVU->prog.prog);
|
||||
}
|
||||
safe_delete(mVU->prog.progList);
|
||||
}
|
||||
|
||||
// Clears Block Data in specified range
|
||||
|
@ -127,18 +127,18 @@ microVUt(void) mVUclear(mV, u32 addr, u32 size) {
|
|||
// Micro VU - Private Functions
|
||||
//------------------------------------------------------------------
|
||||
|
||||
// Clears program data (Sets used to 1 because calling this function implies the program will be used at least once)
|
||||
// Clears program data
|
||||
microVUf(void) mVUclearProg(int progIndex) {
|
||||
microVU* mVU = mVUx;
|
||||
mVUprogI.used = 1;
|
||||
mVUprogI.last_used = 3;
|
||||
mVUprogI.used = 0;
|
||||
mVUprogI.isDead = 1;
|
||||
mVUprogI.frame = mVU->prog.curFrame;
|
||||
for (int j = 0; j <= mVUprogI.ranges.max; j++) {
|
||||
mVUprogI.ranges.range[j][0] = -1; // Set range to
|
||||
mVUprogI.ranges.range[j][1] = -1; // indeterminable status
|
||||
mVUprogI.ranges.total = -1;
|
||||
}
|
||||
for (u32 i = 0; i < (mVU->progSize / 2); i++) {
|
||||
//if (mVUprogI.block[i]) { mVUprogI.block[i]->reset(); }
|
||||
microBlockManager::Delete(mVUprogI.block[i]);
|
||||
}
|
||||
}
|
||||
|
@ -150,85 +150,66 @@ microVUf(void) mVUcacheProg(int progIndex) {
|
|||
mVUdumpProg(progIndex);
|
||||
}
|
||||
|
||||
#define aWrap(x, nMax) ((x > nMax) ? 0 : x)
|
||||
// Sorts the program list (Moves progIndex to Beginning of ProgList)
|
||||
microVUt(void) mVUsortProg(mV, int progIndex) {
|
||||
int* temp = new int[mVU->prog.max+1];
|
||||
int offset = 0;
|
||||
for (int i = 0; i <= (mVU->prog.max-1); i++) {
|
||||
if (progIndex == mVU->prog.progList[i]) offset = 1;
|
||||
temp[i+1] = mVU->prog.progList[i+offset];
|
||||
}
|
||||
temp[0] = progIndex;
|
||||
delete mVU->prog.progList;
|
||||
mVU->prog.progList = temp;
|
||||
}
|
||||
|
||||
// Finds the least used program, (if program list full clears and returns an old program; if not-full, returns free program)
|
||||
microVUf(int) mVUfindLeastUsedProg() {
|
||||
microVU* mVU = mVUx;
|
||||
if (mVU->prog.total < mVU->prog.max) {
|
||||
mVU->prog.total++;
|
||||
mVUcacheProg<vuIndex>(mVU->prog.total); // Cache Micro Program
|
||||
mVU->prog.prog[mVU->prog.total].used = 1;
|
||||
mVU->prog.prog[mVU->prog.total].last_used = 3;
|
||||
Console::Notice("microVU%d: Cached MicroPrograms = %d", params vuIndex, mVU->prog.total+1);
|
||||
return mVU->prog.total;
|
||||
}
|
||||
else {
|
||||
/*
|
||||
const int pMax = mVU->prog.max;
|
||||
int smallidx = aWrap((mVU->prog.cur+1), pMax);
|
||||
u64 smallval = mVU->prog.prog[smallidx].used;
|
||||
|
||||
for (int i = 1, j = aWrap((smallidx+1), pMax); i <= pMax; i++, aWrap((j+1), pMax)) {
|
||||
if (smallval > mVU->prog.prog[j].used) {
|
||||
smallval = mVU->prog.prog[j].used;
|
||||
smallidx = j;
|
||||
}
|
||||
}
|
||||
//smallidx = rand() % 200;
|
||||
mVUclearProg<vuIndex>(smallidx); // Clear old data if overwriting old program
|
||||
mVUcacheProg<vuIndex>(smallidx); // Cache Micro Program
|
||||
//Console::Notice("microVU%d: Overwriting existing program in slot %d [%d times used]", params vuIndex, smallidx, smallval);
|
||||
|
||||
return smallidx;
|
||||
*/
|
||||
|
||||
/*
|
||||
static int smallidx = 0;
|
||||
const int pMax = mVU->prog.max;
|
||||
smallidx = aWrap((smallidx+1), pMax);
|
||||
mVUclearProg<vuIndex>(smallidx); // Clear old data if overwriting old program
|
||||
mVUcacheProg<vuIndex>(smallidx); // Cache Micro Program
|
||||
//Console::Notice("microVU%d: Overwriting existing program in slot %d [%d times used]", params vuIndex, smallidx, smallval);
|
||||
return smallidx;
|
||||
*/
|
||||
|
||||
//mVUreset(mVU);
|
||||
mVU->prog.x86ptr = mVU->prog.x86start;
|
||||
for (int z = 0; z <= mVU->prog.max; z++) {
|
||||
mVUclearProg<vuIndex>(z);
|
||||
mVU->prog.prog[z].used = 0;
|
||||
mVU->prog.prog[z].last_used = 0;
|
||||
}
|
||||
mVU->prog.total = 0;
|
||||
mVUcacheProg<vuIndex>(mVU->prog.total); // Cache Micro Program
|
||||
mVU->prog.prog[mVU->prog.total].used = 1;
|
||||
mVU->prog.prog[mVU->prog.total].last_used = 3;
|
||||
Console::Notice("microVU%d: Cached MicroPrograms = %d", params vuIndex, mVU->prog.total+1);
|
||||
return mVU->prog.total;
|
||||
}
|
||||
}
|
||||
|
||||
// mVUvsyncUpdate -->
|
||||
// This should be run at 30fps intervals from Counters.cpp (or 60fps works too, but 30fps is
|
||||
// probably all we need for accurate results)
|
||||
//
|
||||
// To fix the program cache to more efficiently dispose of "obsolete" programs, we need to use a
|
||||
// frame-based decrementing system in combination with a program-execution-based incrementing
|
||||
// system. In English: if last_used >= 2 it means the program has been used for the current
|
||||
// or prev frame. if it's 0, the program hasn't been used for a while.
|
||||
microVUt(void) mVUvsyncUpdate(mV) {
|
||||
|
||||
if (mVU->prog.total < mVU->prog.max) return;
|
||||
|
||||
for (int i = 0; i <= mVU->prog.max; i++) {
|
||||
if (mVU->prog.prog[i].last_used != 0) {
|
||||
if (mVU->prog.prog[i].last_used >= 3) {
|
||||
mVU->prog.prog[i].used += 0x200; // give 'weighted' bonus
|
||||
}
|
||||
mVU->prog.prog[i].last_used--;
|
||||
if (mVU->prog.prog[i].isDead) {
|
||||
mVU->prog.total++;
|
||||
mVUcacheProg<vuIndex>(i); // Cache Micro Program
|
||||
mVU->prog.prog[i].isDead = 0;
|
||||
mVU->prog.prog[i].used = 1;
|
||||
mVUsortProg(mVU, i);
|
||||
Console::Notice("microVU%d: Cached MicroPrograms = [%03d] [%03d]", params vuIndex, i+1, mVU->prog.total+1);
|
||||
return i;
|
||||
}
|
||||
else mVU->prog.prog[i].used /= 2; // penalize unused programs.
|
||||
}
|
||||
|
||||
static int clearIdx = 0;
|
||||
int pIdx = clearIdx;
|
||||
for (int i = 0; i < ((mVU->prog.max+1)/4); i++) {
|
||||
mVUclearProg<vuIndex>(clearIdx);
|
||||
clearIdx = aWrap(clearIdx+1, mVU->prog.max);
|
||||
}
|
||||
mVU->prog.total -= ((mVU->prog.max+1)/4)-1;
|
||||
mVUcacheProg<vuIndex>(pIdx); // Cache Micro Program
|
||||
mVU->prog.prog[pIdx].isDead = 0;
|
||||
mVU->prog.prog[pIdx].used = 1;
|
||||
mVUsortProg(mVU, pIdx);
|
||||
Console::Notice("microVU%d: Cached MicroPrograms = [%03d] [%03d]", params vuIndex, pIdx+1, mVU->prog.total+1);
|
||||
return pIdx;
|
||||
}
|
||||
|
||||
// Finds and Kills Programs if they haven't been used in a while.
|
||||
microVUt(void) mVUvsyncUpdate(mV) {
|
||||
for (int i = 0; i <= mVU->prog.max; i++) {
|
||||
if (mVU->prog.prog[i].isDead) continue;
|
||||
if (mVU->prog.prog[i].used) {
|
||||
mVU->prog.prog[i].used = 0;
|
||||
mVU->prog.prog[i].frame = mVU->prog.curFrame;
|
||||
}
|
||||
if((mVU->prog.curFrame - mVU->prog.prog[i].frame) >= (60 * 7)) {
|
||||
mVU->prog.total--;
|
||||
if (!mVU->index) mVUclearProg<0>(i);
|
||||
else mVUclearProg<1>(i);
|
||||
DevCon::Status("microVU%d: Killing Dead Program [%03d]", params mVU->index, i+1);
|
||||
}
|
||||
}
|
||||
mVU->prog.curFrame++;
|
||||
}
|
||||
|
||||
microVUf(bool) mVUcmpPartial(int progIndex) {
|
||||
|
@ -244,16 +225,15 @@ microVUf(bool) mVUcmpPartial(int progIndex) {
|
|||
}
|
||||
|
||||
// Compare Cached microProgram to mVU->regs->Micro
|
||||
microVUf(bool) mVUcmpProg(int progIndex, bool progUsed, const bool cmpWholeProg) {
|
||||
microVUf(bool) mVUcmpProg(int progIndex, const bool cmpWholeProg) {
|
||||
microVU* mVU = mVUx;
|
||||
if (progUsed) {
|
||||
if (!mVUprogI.isDead) {
|
||||
if ((cmpWholeProg && !memcmp_mmx((u8*)mVUprogI.data, mVU->regs->Micro, mVU->microMemSize))
|
||||
|| (!cmpWholeProg && mVUcmpPartial<vuIndex>(progIndex))) {
|
||||
mVU->prog.cur = progIndex;
|
||||
mVU->prog.cleared = 0;
|
||||
mVU->prog.isSame = cmpWholeProg ? 1 : -1;
|
||||
mVU->prog.prog[progIndex].last_used = 3;
|
||||
mVU->prog.prog[progIndex].used++; // increment 'used'
|
||||
mVU->prog.prog[progIndex].used = 1;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
@ -263,23 +243,17 @@ microVUf(bool) mVUcmpProg(int progIndex, bool progUsed, const bool cmpWholeProg)
|
|||
// Searches for Cached Micro Program and sets prog.cur to it (returns 1 if program found, else returns 0)
|
||||
microVUf(int) mVUsearchProg() {
|
||||
microVU* mVU = mVUx;
|
||||
|
||||
if (mVU->prog.cleared) { // If cleared, we need to search for new program
|
||||
for (int i = 0; i <= mVU->prog.total; i++) {
|
||||
if (mVUcmpProg<vuIndex>(i, !!mVU->prog.prog[i].used, 0))
|
||||
return 1; // Check Recently Used Programs
|
||||
}
|
||||
for (int i = 0; i <= mVU->prog.total; i++) {
|
||||
if (mVUcmpProg<vuIndex>(i, !mVU->prog.prog[i].used, 0))
|
||||
return 1; // Check Older Programs
|
||||
for (int i = mVU->prog.max; i >= 0; i--) {
|
||||
if (mVUcmpProg<vuIndex>(mVU->prog.progList[i], 0))
|
||||
return 1;
|
||||
}
|
||||
mVU->prog.cur = mVUfindLeastUsedProg<vuIndex>(); // If cleared and program not found, make a new program instance
|
||||
mVU->prog.cleared = 0;
|
||||
mVU->prog.isSame = 1;
|
||||
return 0;
|
||||
}
|
||||
mVU->prog.prog[mVU->prog.cur].used++;
|
||||
mVU->prog.prog[mVU->prog.cur].last_used = 3;
|
||||
mVU->prog.prog[mVU->prog.cur].used = 1;
|
||||
return 1; // If !cleared, then we're still on the same program as last-time ;)
|
||||
}
|
||||
|
||||
|
@ -288,7 +262,7 @@ microVUf(int) mVUsearchProg() {
|
|||
//------------------------------------------------------------------
|
||||
|
||||
void initVUrec (VURegs* vuRegs, const int vuIndex) { mVUinit(vuRegs, vuIndex); }
|
||||
void closeVUrec(const int vuIndex) { mVUclose(mVUx, 0); }
|
||||
void closeVUrec(const int vuIndex) { mVUclose(mVUx); }
|
||||
void resetVUrec(const int vuIndex) { mVUreset(mVUx); }
|
||||
void vsyncVUrec(const int vuIndex) { mVUvsyncUpdate(mVUx); }
|
||||
|
||||
|
|
|
@ -114,23 +114,26 @@ struct microProgram {
|
|||
u32 data [mProgSize]; // Holds a copy of the VU microProgram
|
||||
microBlockManager* block[mProgSize/2]; // Array of Block Managers
|
||||
microRange ranges; // The ranges of the microProgram that have already been recompiled
|
||||
u64 used; // Number of times its been used
|
||||
u32 last_used; // Counters # of frames since last use (starts at 3 and counts backwards to 0 for each 30fps vSync)
|
||||
u32 frame; // Frame # the program was last used on
|
||||
u32 used; // Program was used this frame?
|
||||
bool isDead; // Program is dead?
|
||||
};
|
||||
|
||||
#define mMaxProg ((mVU->index)?400:8) // The amount of Micro Programs Recs will 'remember' (For n = 1, 2, 4, 8, 16, etc...)
|
||||
struct microProgManager {
|
||||
microIR<mProgSize> allocInfo; // IR information
|
||||
microProgram* prog; // Store MicroPrograms in memory
|
||||
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)
|
||||
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)
|
||||
u8* x86ptr; // Pointer to program's recompilation code
|
||||
u8* x86start; // Start of program's rec-cache
|
||||
u8* x86end; // Limit of program's rec-cache
|
||||
microRegInfo lpState; // Pipeline state from where program left off (useful for continuing execution)
|
||||
microIR<mProgSize> allocInfo; // IR information
|
||||
microProgram* prog; // Store MicroPrograms in memory
|
||||
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)
|
||||
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
|
||||
u8* x86ptr; // Pointer to program's recompilation code
|
||||
u8* x86start; // Start of program's rec-cache
|
||||
u8* x86end; // Limit of program's rec-cache
|
||||
microRegInfo lpState; // Pipeline state from where program left off (useful for continuing execution)
|
||||
};
|
||||
|
||||
#define mVUcacheSize (mMaxProg * (0x100000 * 0.5)) // 0.5mb per program
|
||||
|
@ -175,7 +178,7 @@ extern int mVUdebugNow;
|
|||
// Main Functions
|
||||
microVUt(void) mVUinit(VURegs*, int);
|
||||
microVUt(void) mVUreset(mV);
|
||||
microVUt(void) mVUclose(mV, bool isReset);
|
||||
microVUt(void) mVUclose(mV);
|
||||
microVUt(void) mVUclear(mV, u32, u32);
|
||||
microVUt(void*) mVUblockFetch(microVU* mVU, u32 startPC, uptr pState);
|
||||
microVUx(void*) __fastcall mVUcompileJIT(u32 startPC, uptr pState);
|
||||
|
@ -187,6 +190,8 @@ mVUop(mVUopU);
|
|||
mVUop(mVUopL);
|
||||
|
||||
// Private Functions
|
||||
microVUt(void) mVUsortProg(mV, int progIndex);
|
||||
microVUf(void) mVUclearProg(int progIndex);
|
||||
microVUf(int) mVUfindLeastUsedProg(microVU* mVU);
|
||||
microVUf(int) mVUsearchProg();
|
||||
microVUf(void) mVUcacheProg(int progIndex);
|
||||
|
|
|
@ -70,7 +70,7 @@
|
|||
#define doLowerOp() { incPC(-1); mVUopL(mVU, 1); incPC(1); }
|
||||
#define doSwapOp() { doBackupVF1(); mVUopL(mVU, 1); doBackupVF2(); incPC(1); doUpperOp(); doBackupVF3(); }
|
||||
#define doIbit() { if (mVUup.iBit) { incPC(-1); MOV32ItoM((uptr)&mVU->regs->VI[REG_I].UL, curI); incPC(1); } }
|
||||
#define blockCreate(addr) { if (!mVUblocks[addr]) mVUblocks[addr] = new microBlockManager();/*microBlockManager::AlignedNew();*/ }
|
||||
#define blockCreate(addr) { if (!mVUblocks[addr]) mVUblocks[addr] = new microBlockManager(); }
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// Helper Functions
|
||||
|
|
|
@ -219,6 +219,7 @@ typedef u32 (__fastcall *mVUCall)(void*, void*);
|
|||
#define cmpOffset(x) ((u8*)&(((u8*)x)[mVUprogI.ranges.range[i][0]]))
|
||||
#define Rmem (uptr)&mVU->regs->VI[REG_R].UL
|
||||
#define Roffset (uptr)&mVU->regs->VI[9].UL
|
||||
#define aWrap(x, m) ((x > m) ? 0 : x)
|
||||
|
||||
// Flag Info
|
||||
#define __Status (mVUflagInfo & (0xf<<0))
|
||||
|
|
Loading…
Reference in New Issue