Removed VU-Skip [helps simplify the frameskipper logic] -- Use the VU Cycle Stealer hack instead for better, faster, more stable speedups of the VU1 unit.

git-svn-id: http://pcsx2.googlecode.com/svn/trunk@1114 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
Jake.Stine 2009-05-02 09:04:47 +00:00
parent f8f4c7f136
commit a69cfcc464
13 changed files with 105 additions and 189 deletions

View File

@ -54,7 +54,6 @@ extern SessionOverrideFlags g_Session;
#define PCSX2_FRAMELIMIT_NORMAL 0x000
#define PCSX2_FRAMELIMIT_LIMIT 0x400
#define PCSX2_FRAMELIMIT_SKIP 0x800
#define PCSX2_FRAMELIMIT_VUSKIP 0xc00
#define CHECK_FRAMELIMIT (Config.Options&PCSX2_FRAMELIMIT_MASK)

View File

@ -32,7 +32,6 @@
using namespace Threading;
extern u8 psxhblankgate;
u32 g_vu1SkipCount; // number of frames to disable/skip VU1
static const uint EECNT_FUTURE_TARGET = 0x10000000;
@ -50,8 +49,6 @@ SyncCounter vsyncCounter;
u32 nextsCounter; // records the cpuRegs.cycle value of the last call to rcntUpdate()
s32 nextCounter; // delta from nextsCounter, in cycles, until the next rcntUpdate()
// VUSkip Locals and Globals
void rcntReset(int index) {
counters[index].count = 0;
counters[index].sCycleT = cpuRegs.cycle;
@ -264,9 +261,6 @@ u32 UpdateVSyncRate()
m_iStart = GetCPUTicks();
cpuRcntSet();
// Initialize VU Skip Stuff...
g_vu1SkipCount = 0;
return (u32)m_iTicks;
}
@ -363,17 +357,7 @@ static __forceinline void VSyncEnd(u32 sCycle)
iFrame++;
if( g_vu1SkipCount > 0 )
{
gsPostVsyncEnd( false );
AtomicDecrement( g_vu1SkipCount );
vu1MicroEnableSkip();
}
else
{
gsPostVsyncEnd( true );
vu1MicroDisableSkip();
}
gsPostVsyncEnd( true );
hwIntcIrq(INTC_VBLANK_E); // HW Irq
psxVBlankEnd(); // psxCounters vBlank End

View File

@ -165,16 +165,10 @@ void gsSetVideoRegionType( u32 isPal )
// Make sure framelimiter options are in sync with the plugin's capabilities.
void gsInit()
{
switch(CHECK_FRAMELIMIT)
if( (CHECK_FRAMELIMIT == PCSX2_FRAMELIMIT_SKIP) && (GSsetFrameSkip == NULL) )
{
case PCSX2_FRAMELIMIT_SKIP:
case PCSX2_FRAMELIMIT_VUSKIP:
if( GSsetFrameSkip == NULL )
{
Config.Options &= ~PCSX2_FRAMELIMIT_MASK;
Console::WriteLn("Notice: Disabling frameskip -- GS plugin does not support it.");
}
break;
Config.Options &= ~PCSX2_FRAMELIMIT_MASK;
Console::WriteLn("Notice: Disabling frameskip -- GS plugin does not support it.");
}
}
@ -619,8 +613,7 @@ __forceinline void gsFrameSkip( bool forceskip )
static u8 FramesToRender = 0;
static u8 FramesToSkip = 0;
if( CHECK_FRAMELIMIT != PCSX2_FRAMELIMIT_SKIP &&
CHECK_FRAMELIMIT != PCSX2_FRAMELIMIT_VUSKIP ) return;
if( CHECK_FRAMELIMIT != PCSX2_FRAMELIMIT_SKIP ) return;
// FrameSkip and VU-Skip Magic!
// Skips a sequence of consecutive frames after a sequence of rendered frames
@ -652,14 +645,6 @@ __forceinline void gsFrameSkip( bool forceskip )
return;
}
// if we've already given the EE a skipcount assignment then don't do anything more.
// Otherwise we could start compounding the issue and skips would be too long.
if( g_vu1SkipCount > 0 )
{
//Console::Status("- Already Assigned a Skipcount.. %d", params g_vu1SkipCount );
return;
}
if( FramesToRender == 0 )
{
// -- Standard operation section --
@ -680,20 +665,9 @@ __forceinline void gsFrameSkip( bool forceskip )
if( (m_justSkipped && (sSlowDeltaTime > m_iSlowTicks)) ||
(sSlowDeltaTime > m_iSlowTicks*2) )
{
//Console::Status( "Frameskip Initiated! Lateness: %d", params (int)( (sSlowDeltaTime*100) / m_iSlowTicks ) );
if( CHECK_FRAMELIMIT == PCSX2_FRAMELIMIT_VUSKIP )
{
// For best results we have to wait for the EE to
// tell us when to skip, so that VU skips are synched with GS skips.
AtomicExchangeAdd( g_vu1SkipCount, yesSkipFrames+1 );
}
else
{
GSsetFrameSkip(1);
FramesToRender = noSkipFrames+1;
FramesToSkip = yesSkipFrames;
}
GSsetFrameSkip(1);
FramesToRender = noSkipFrames+1;
FramesToSkip = yesSkipFrames;
}
}
else
@ -771,7 +745,6 @@ void gsPostVsyncEnd( bool updategs )
void _gs_ResetFrameskip()
{
g_vu1SkipCount = 0; // set to 0 so that EE will re-enable the VU at the next vblank.
GSsetFrameSkip( 0 );
}

View File

@ -331,7 +331,6 @@ void mfifoGIFtransfer(int qwc);
int _GIFchain();
void gifMFIFOInterrupt();
extern u32 g_vu1SkipCount;
extern u32 CSRw;
extern u64 m_iSlowStart;

View File

@ -39,8 +39,6 @@ void OnCpu_Ok(GtkButton *button, gpointer user_data)
newopts |= PCSX2_FRAMELIMIT_LIMIT;
else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lookup_widget(CpuDlg, "GtkRadioButton_LimitFS"))))
newopts |= PCSX2_FRAMELIMIT_SKIP;
else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lookup_widget(CpuDlg, "GtkRadioButton_VUSkip"))))
newopts |= PCSX2_FRAMELIMIT_VUSKIP;
Config.CustomFps = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(lookup_widget(CpuDlg, "CustomFPSLimit")));
Config.CustomFrameSkip = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(lookup_widget(CpuDlg, "FrameThreshold")));
@ -88,7 +86,6 @@ void OnConf_Cpu(GtkMenuItem *menuitem, gpointer user_data)
gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(lookup_widget(CpuDlg, "GtkRadioButton_LimitNormal")), CHECK_FRAMELIMIT == PCSX2_FRAMELIMIT_NORMAL);
gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(lookup_widget(CpuDlg, "GtkRadioButton_LimitLimit")), CHECK_FRAMELIMIT == PCSX2_FRAMELIMIT_LIMIT);
gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(lookup_widget(CpuDlg, "GtkRadioButton_LimitFS")), CHECK_FRAMELIMIT == PCSX2_FRAMELIMIT_SKIP);
gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(lookup_widget(CpuDlg, "GtkRadioButton_VUSkip")), CHECK_FRAMELIMIT == PCSX2_FRAMELIMIT_VUSKIP);
sprintf(str, "Cpu Vendor: %s", cpuinfo.x86ID);
gtk_label_set_text(GTK_LABEL(lookup_widget(CpuDlg, "GtkLabel_CpuVendor")), str);

View File

@ -447,12 +447,19 @@ void CycleFrameLimit(int dir)
newFrameLimit = 0;
} else
newFrameLimit = oldFrameLimit;
} else if (dir > 0) {
// next
newFrameLimit = (curFrameLimit + PCSX2_FRAMELIMIT_LIMIT) & PCSX2_FRAMELIMIT_MASK;
} else {
// previous
newFrameLimit = (curFrameLimit + PCSX2_FRAMELIMIT_VUSKIP) & PCSX2_FRAMELIMIT_MASK;
}
else if (dir > 0) // next
{
newFrameLimit = curFrameLimit + PCSX2_FRAMELIMIT_LIMIT;
if( newFrameLimit > PCSX2_FRAMELIMIT_SKIP )
newFrameLimit = 0;
}
else // previous
{
if( newFrameLimit == 0 )
newFrameLimit = PCSX2_FRAMELIMIT_SKIP;
else
newFrameLimit = curFrameLimit - PCSX2_FRAMELIMIT_LIMIT;
}
newOptions = (Config.Options & ~PCSX2_FRAMELIMIT_MASK) | newFrameLimit;
@ -467,7 +474,6 @@ void CycleFrameLimit(int dir)
limitMsg = "Limit";
break;
case PCSX2_FRAMELIMIT_SKIP:
case PCSX2_FRAMELIMIT_VUSKIP:
if( GSsetFrameSkip == NULL )
{
newOptions &= ~PCSX2_FRAMELIMIT_MASK;
@ -479,7 +485,7 @@ void CycleFrameLimit(int dir)
// When enabling Skipping we have to make sure Skipper (GS) and Limiter (EE)
// are properly synchronized.
gsDynamicSkipEnable();
limitMsg = ((newOptions & PCSX2_FRAMELIMIT_MASK) == PCSX2_FRAMELIMIT_SKIP) ? "Skip" : "VUSkip";
limitMsg = "Skip";
}
break;

View File

@ -319,9 +319,6 @@ void SysClearExecutionCache()
psxCpu->Reset();
vuMicroCpuReset();
// make sure the VU1 doesn't have lingering "skip" enabled.
vu1MicroDisableSkip();
}
__forceinline void SysUpdate()

View File

@ -123,10 +123,6 @@ extern void vu1ResetRegs();
extern void vu1ExecMicro(u32 addr);
extern void vu1Exec(VURegs* VU);
extern void vu1MicroEnableSkip();
extern void vu1MicroDisableSkip();
extern bool vu1MicroIsSkipping();
void VU0_UPPER_FD_00();
void VU0_UPPER_FD_01();
void VU0_UPPER_FD_10();

View File

@ -38,21 +38,6 @@ static void DummyExecuteVU1Block(void)
VU1.vifRegs->stat &= ~4; // also reset the bit (grandia 3 works)
}
void vu1MicroEnableSkip()
{
CpuVU1.ExecuteBlock = DummyExecuteVU1Block;
}
void vu1MicroDisableSkip()
{
CpuVU1.ExecuteBlock = CHECK_VU1REC ? recVU1.ExecuteBlock : intVU1.ExecuteBlock;
}
bool vu1MicroIsSkipping()
{
return CpuVU1.ExecuteBlock == DummyExecuteVU1Block;
}
void vuMicroCpuReset()
{
CpuVU0 = CHECK_VU0REC ? recVU0 : intVU0;

View File

@ -580,13 +580,6 @@ static void VIFunpack(u32 *data, vifCode *v, unsigned int size, const unsigned i
vif = &vif1;
vifRow = g_vifRow1;
assert(v->addr < memsize);
if (vu1MicroIsSkipping())
{
// don't process since the frame is dummy
vif->tag.addr += (size / (VIFfuncTable[ vif->cmd & 0xf ].gsize * vifRegs->cycle.wl)) * ((vifRegs->cycle.cl - vifRegs->cycle.wl) * 16);
return;
}
}
dest = (u32*)(VU->Mem + v->addr);

View File

@ -134,7 +134,6 @@ BOOL CALLBACK CpuDlgProc(HWND hW, UINT uMsg, WPARAM wParam, LPARAM lParam)
if( SendDlgItemMessage(hW,IDC_CPU_FL_NORMAL,BM_GETCHECK,0,0) ) newopts |= PCSX2_FRAMELIMIT_NORMAL;
else if( SendDlgItemMessage(hW,IDC_CPU_FL_LIMIT,BM_GETCHECK,0,0) ) newopts |= PCSX2_FRAMELIMIT_LIMIT;
else if( SendDlgItemMessage(hW,IDC_CPU_FL_SKIP,BM_GETCHECK,0,0) ) newopts |= PCSX2_FRAMELIMIT_SKIP;
else if( SendDlgItemMessage(hW,IDC_CPU_FL_SKIPVU,BM_GETCHECK,0,0) ) newopts |= PCSX2_FRAMELIMIT_VUSKIP;
GetDlgItemText(hW, IDC_CUSTOMFPS, cfps, 20);
Config.CustomFps = atoi(cfps);

View File

@ -828,7 +828,7 @@ BEGIN
CONTROL "EE Counters log",IDC_EECNTLOG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,5,140,64,11
END
IDD_CPUDLG DIALOGEX 0, 0, 563, 321
IDD_CPUDLG DIALOGEX 0, 0, 563, 305
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
@ -843,13 +843,11 @@ BEGIN
CONTROL "Normal - All frames are rendered as fast as possible.",IDC_CPU_FL_NORMAL,
"Button",BS_AUTORADIOBUTTON | BS_MULTILINE | WS_GROUP,309,17,221,17
CONTROL "Limit - Force frames to normal speeds if too fast.\n (You can set a custom FPS limit below.)",IDC_CPU_FL_LIMIT,
"Button",BS_AUTORADIOBUTTON | BS_MULTILINE,309,33,222,15
"Button",BS_AUTORADIOBUTTON | BS_MULTILINE,309,37,222,15
CONTROL "Frame Skip - In order to achieve normal speeds,\n some frames are skipped (fast).\n Fps displayed counts skipped frames too.",IDC_CPU_FL_SKIP,
"Button",BS_AUTORADIOBUTTON | BS_MULTILINE,309,48,221,28
CONTROL "VU Skip - Same as 'Frame Skip', but tries to skip more.\n Artifacts might be present, but will be faster.",IDC_CPU_FL_SKIPVU,
"Button",BS_AUTORADIOBUTTON | BS_MULTILINE,309,75,220,25
DEFPUSHBUTTON "OK",IDOK,210,298,61,14
PUSHBUTTON "Cancel",IDCANCEL,282,298,61,14,NOT WS_TABSTOP
"Button",BS_AUTORADIOBUTTON | BS_MULTILINE,309,56,221,28
DEFPUSHBUTTON "OK",IDOK,210,287,61,14
PUSHBUTTON "Cancel",IDCANCEL,282,287,61,14,NOT WS_TABSTOP
LTEXT "CPU Vendor",IDC_VENDORNAME,12,23,88,8
LTEXT "Family",IDC_FAMILYNAME,12,41,88,8
LTEXT "Cpu Speed",IDC_CPUSPEEDNAME,12,60,88,8
@ -860,19 +858,19 @@ BEGIN
GROUPBOX "VU Recompilers - All options are set by default",IDC_CPU_VUGROUP,7,119,265,46
LTEXT "Features",IDC_FEATURESNAME,12,78,88,8
GROUPBOX "",IDC_STATIC,7,7,265,90
LTEXT "Custom FPS Limit (0=auto):",IDC_CUSTOM_FPS,327,113,124,12
EDITTEXT IDC_CUSTOMFPS,456,113,53,13,ES_AUTOHSCROLL | ES_NUMBER
EDITTEXT IDC_CUSTOM_FRAMESKIP,456,130,53,13,ES_AUTOHSCROLL | ES_NUMBER
EDITTEXT IDC_CUSTOM_CONSECUTIVE_FRAMES,456,148,53,13,ES_AUTOHSCROLL | ES_NUMBER
LTEXT "Skip Frames when slower than:\n(See Note 1)",IDC_FRAMESKIP_LABEL1,327,129,106,20
LTEXT "Consecutive Frames before skipping:\n(See Note 2)",IDC_FRAMESKIP_LABEL2,327,149,121,17
GROUPBOX "Frame Limiting (F4 key switches the mode in-game!)",IDC_FRAMELIMIT,301,7,250,280
GROUPBOX "Detailed Settings",IDC_FRAMELIMIT_OPTIONS,310,99,234,185
LTEXT "*Note 1: Will only skip when slower than this fps number.\n (0 = Auto) ; (9999 = Forced-Frameskip regardless of speed.)\n (e.g. If set to 45, will only skip when slower than 45fps.)",IDC_FRAMESKIP_LABEL3,318,189,217,26
LTEXT "*Note 2: Will render this number of consecutive frames before\n skipping the next frame. (0=default)\n (e.g. If set to 2, will render 2 frames before skipping 1.)",IDC_FRAMESKIP_LABEL4,318,216,217,25
EDITTEXT IDC_CUSTOM_CONSECUTIVE_SKIP,456,166,53,13,ES_AUTOHSCROLL | ES_NUMBER
LTEXT "Consecutive Frames to skip:\n(See Note 3)",IDC_FRAMESKIP_LABEL5,327,167,121,17
LTEXT "*Note 3: Will skip this number of frames before\n rendering the next sequence of frames. (0=default)\n (e.g. If set to 2, will skip 2 consecutive frames whenever its time\n to skip.)",IDC_FRAMESKIP_LABEL6,318,244,217,32
LTEXT "Custom FPS Limit (0=auto):",IDC_CUSTOM_FPS,327,103,124,12
EDITTEXT IDC_CUSTOMFPS,456,103,53,13,ES_AUTOHSCROLL | ES_NUMBER
EDITTEXT IDC_CUSTOM_FRAMESKIP,456,121,53,13,ES_AUTOHSCROLL | ES_NUMBER
EDITTEXT IDC_CUSTOM_CONSECUTIVE_FRAMES,456,139,53,13,ES_AUTOHSCROLL | ES_NUMBER
LTEXT "Skip Frames when slower than:\n(See Note 1)",IDC_FRAMESKIP_LABEL1,327,119,106,20
LTEXT "Consecutive Frames before skipping:\n(See Note 2)",IDC_FRAMESKIP_LABEL2,327,139,121,17
GROUPBOX "Frame Limiting (F4 key switches the mode in-game!)",IDC_FRAMELIMIT,301,7,250,274
GROUPBOX "Detailed Settings",IDC_FRAMELIMIT_OPTIONS,310,89,234,185
LTEXT "*Note 1: Will only skip when slower than this fps number.\n (0 = Auto) ; (9999 = Forced-Frameskip regardless of speed.)\n (e.g. If set to 45, will only skip when slower than 45fps.)",IDC_FRAMESKIP_LABEL3,318,179,217,26
LTEXT "*Note 2: Will render this number of consecutive frames before\n skipping the next frame. (0=default)\n (e.g. If set to 2, will render 2 frames before skipping 1.)",IDC_FRAMESKIP_LABEL4,318,206,217,25
EDITTEXT IDC_CUSTOM_CONSECUTIVE_SKIP,456,157,53,13,ES_AUTOHSCROLL | ES_NUMBER
LTEXT "Consecutive Frames to skip:\n(See Note 3)",IDC_FRAMESKIP_LABEL5,327,157,121,17
LTEXT "*Note 3: Will skip this number of frames before\n rendering the next sequence of frames. (0=default)\n (e.g. If set to 2, will skip 2 consecutive frames whenever its time\n to skip.)",IDC_FRAMESKIP_LABEL6,318,234,217,32
END

View File

@ -424,7 +424,7 @@ PCSX2_ALIGNED16( static u8 manual_counter[Ps2MemSize::Base >> 12] );
////////////////////////////////////////////////////
void recResetEE( void )
{
DbgCon::Status( "iR5900-32 > Resetting recompiler memory and structures." );
Console::Status( "Issuing EE/iR5900-32 Recompiler Reset [mem/structure cleanup]" );
maxrecmem = 0;
@ -1258,7 +1258,6 @@ void recRecompile( const u32 startpc )
// if recPtr reached the mem limit reset whole mem
if ( ( (uptr)recPtr - (uptr)recMem ) >= REC_CACHEMEM-0x40000 || dumplog == 0xffffffff) {
DevCon::WriteLn( "EE Recompiler data reset" );
recResetEE();
}
if ( ( (uptr)recStackPtr - (uptr)recStack ) >= RECSTACK_SIZE-0x100 ) {
@ -1334,10 +1333,7 @@ void recRecompile( const u32 startpc )
willbranch3 = 1;
s_nEndBlock = i;
// Log the pagesplits verbosely for now, until we see if any games are affected
// adversely by excessive splits.
DevCon::Notice( "Pagesplit @ %08X : size=%d insts", params startpc, (i-startpc) / 4 );
//DevCon::Notice( "Pagesplit @ %08X : size=%d insts", params startpc, (i-startpc) / 4 );
break;
}
@ -1508,86 +1504,80 @@ StartRecomp:
iDumpBlock(startpc, recPtr);
#endif
// fixme! The following manual/protected block code can be greatly simplified now.
// It originally had to account for cross-page blocks, but we have since guaranteed
// that no block will cross a page boundary.
u32 sz = (s_nEndBlock-startpc) >> 2;
u32 inpage_ptr = HWADDR(startpc);
u32 inpage_sz = sz*4;
while(inpage_sz)
{
int PageType = mmap_GetRamPageInfo((u32*)PSM(inpage_ptr));
u32 inpage_offs = inpage_ptr & 0xFFF;
u32 pgsz = std::min(0x1000 - inpage_offs, inpage_sz);
// note: blocks are guaranteed to reside within the confines of a single page.
if(PageType!=-1)
const int PageType = mmap_GetRamPageInfo((u32*)PSM(inpage_ptr));
const u32 inpage_offs = inpage_ptr & 0xFFF;
//const u32 pgsz = std::min(0x1000 - inpage_offs, inpage_sz);
const u32 pgsz = inpage_sz;
if(PageType!=-1)
{
if (PageType==0) {
mmap_MarkCountedRamPage(PSM(inpage_ptr),inpage_ptr&~0xFFF);
manual_page[inpage_ptr >> 12] = 0;
}
else
{
if (PageType==0) {
mmap_MarkCountedRamPage(PSM(inpage_ptr),inpage_ptr&~0xFFF);
manual_page[inpage_ptr >> 12] = 0;
xMOV( ecx, inpage_ptr );
xMOV( edx, pgsz / 4 );
//xMOV( eax, startpc ); // uncomment this to access startpc (as eax) in dyna_block_discard
u32 lpc = inpage_ptr;
u32 stg = pgsz;
while(stg>0)
{
xCMP( ptr32[PSM(lpc)], *(u32*)PSM(lpc) );
xJNE( dyna_block_discard );
stg -= 4;
lpc += 4;
}
// Tweakpoint! 3 is a 'magic' number representing the number of times a counted block
// is re-protected before the recompiler gives up and sets it up as an uncounted (permanent)
// manual block. Higher thresholds result in more recompilations for blocks that share code
// and data on the same page. Side effects of a lower threshold: over extended gameplay
// with several map changes, a game's overall performance could degrade.
// (ideally, perhaps, manual_counter should be reset to 0 every few minutes?)
if (startpc != 0x81fc0 && manual_counter[inpage_ptr >> 12] <= 3) {
// Counted blocks add a weighted (by block size) value into manual_page each time they're
// run. If the block gets run a lot, it resets and re-protects itself in the hope
// that whatever forced it to be manually-checked before was a 1-time deal.
// Counted blocks have a secondary threshold check in manual_counter, which forces a block
// to 'uncounted' mode if it's recompiled several time. This protects against excessive
// recompilation of blocks that reside on the same codepage as data.
// fixme? Currently this algo is kinda dumb and results in the forced recompilation of a
// lot of blocks before it decides to mark a 'busy' page as uncounted. There might be
// be a more clever approach that could streamline this process, by doing a first-pass
// test using the vtlb memory protection (without recompilation!) to reprotect a counted
// block. But unless a new also is relatively simple in implementation, it's probably
// not worth the effort (tests show that we have lots of recompiler memory to spare, and
// that the current amount of recompilation is fairly cheap).
xADD(ptr16[&manual_page[inpage_ptr >> 12]], sz);
xJC( dyna_page_reset );
// note: clearcnt is measured per-page, not per-block!
DbgCon::WriteLn( "Manual block @ %08X : size=%3d page/offs=%05X/%03X inpgsz=%d clearcnt=%d",
params startpc, sz, inpage_ptr>>12, inpage_offs, inpage_sz, manual_counter[inpage_ptr >> 12] );
}
else
{
xMOV( ecx, inpage_ptr );
xMOV( edx, pgsz / 4 );
//xMOV( eax, startpc ); // uncomment this to access startpc (as eax) in dyna_block_discard
u32 lpc = inpage_ptr;
u32 stg = pgsz;
while(stg>0)
{
xCMP( ptr32[PSM(lpc)], *(u32*)PSM(lpc) );
xJNE( dyna_block_discard );
stg -= 4;
lpc += 4;
}
// Tweakpoint! 3 is a 'magic' number representing the number of times a counted block
// is re-protected before the recompiler gives up and sets it up as an uncounted (permanent)
// manual block. 4 definitely seemed too high, but 2 might be better? Side effects of a
// lower threshold: over extended gameplay with several map changes, a game's overall
// performance could degrade.
// (ideally, perhaps, manual_counter should be reset to 0 every few minutes?)
if (startpc != 0x81fc0 && manual_counter[inpage_ptr >> 12] <= 3) {
// Counted blocks add a weighted (by block size) value into manual_page each time they're
// run. If the block gets run a lot, it resets and re-protects itself in the hope
// that whatever forced it to be manually-checked before was a 1-time deal.
// Counted blocks have a secondary threshold check in manual_counter, which forces a block
// to 'uncounted' mode if it's recompiled several time. This protects against excessive
// recompilation of blocks that reside on the same codepage as data.
// fixme? Currently this algo is kinda dumb and results in the forced recompilation of a
// lot of blocks before it decides to mark a 'busy' page as uncounted. There might be
// be a more clever approach that could streamline this process, by doing a first-pass
// test using the vtlb memory protection (without recompilation!) to reprotect a counted
// block. But unless a new also is relatively simple in implementation, it's probably
// not worth the effort (tests show that we have lots of recompiler memory to spare, and
// that the current amount of recompilation is fairly cheap).
xADD(ptr16[&manual_page[inpage_ptr >> 12]], sz);
xJC( dyna_page_reset );
// note: clearcnt is measured per-page, not per-block!
DbgCon::WriteLn( "Manual block @ %08X : size=%3d page/offs=%05X/%03X inpgsz=%d clearcnt=%d",
params startpc, sz, inpage_ptr>>12, inpage_offs, inpage_sz, manual_counter[inpage_ptr >> 12] );
}
else
{
DbgCon::Notice( "Uncounted Manual block @ %08X : size=%3d page/offs=%05X/%03X inpgsz=%d",
params startpc, sz, inpage_ptr>>12, inpage_offs, pgsz, inpage_sz );
}
DbgCon::Notice( "Uncounted Manual block @ %08X : size=%3d page/offs=%05X/%03X inpgsz=%d",
params startpc, sz, inpage_ptr>>12, inpage_offs, pgsz, inpage_sz );
}
}
inpage_ptr += pgsz;
inpage_sz -= pgsz;
}
// Finally: Generate x86 recompiled code!