diff --git a/pcsx2/x86/microVU.cpp b/pcsx2/x86/microVU.cpp index 7f66c7fa37..817f6c8c5b 100644 --- a/pcsx2/x86/microVU.cpp +++ b/pcsx2/x86/microVU.cpp @@ -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(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(smallidx); // Clear old data if overwriting old program - mVUcacheProg(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(smallidx); // Clear old data if overwriting old program - mVUcacheProg(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(z); - mVU->prog.prog[z].used = 0; - mVU->prog.prog[z].last_used = 0; - } - mVU->prog.total = 0; - mVUcacheProg(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(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(clearIdx); + clearIdx = aWrap(clearIdx+1, mVU->prog.max); + } + mVU->prog.total -= ((mVU->prog.max+1)/4)-1; + mVUcacheProg(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(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(i, !!mVU->prog.prog[i].used, 0)) - return 1; // Check Recently Used Programs - } - for (int i = 0; i <= mVU->prog.total; i++) { - if (mVUcmpProg(i, !mVU->prog.prog[i].used, 0)) - return 1; // Check Older Programs + for (int i = mVU->prog.max; i >= 0; i--) { + if (mVUcmpProg(mVU->prog.progList[i], 0)) + return 1; } mVU->prog.cur = mVUfindLeastUsedProg(); // 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); } diff --git a/pcsx2/x86/microVU.h b/pcsx2/x86/microVU.h index 8d6e165983..13abfd572a 100644 --- a/pcsx2/x86/microVU.h +++ b/pcsx2/x86/microVU.h @@ -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 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 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); diff --git a/pcsx2/x86/microVU_Compile.inl b/pcsx2/x86/microVU_Compile.inl index cac735f8de..74980bf408 100644 --- a/pcsx2/x86/microVU_Compile.inl +++ b/pcsx2/x86/microVU_Compile.inl @@ -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 diff --git a/pcsx2/x86/microVU_Misc.h b/pcsx2/x86/microVU_Misc.h index 7b13a0dc3c..9778f07b68 100644 --- a/pcsx2/x86/microVU_Misc.h +++ b/pcsx2/x86/microVU_Misc.h @@ -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))