From 9e87a7a2b2659785bc05266fbb3292b60aecdf27 Mon Sep 17 00:00:00 2001 From: Nebuleon Fumika Date: Wed, 6 Feb 2013 00:34:01 -0500 Subject: Implement automatic CPU frequency switching, which improves battery life if playing games that don't use all of the MIPS CPU. If all of it is indeed needed, then the game will constantly play at 396 MHz. --- source/nds/entry.cpp | 244 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 145 insertions(+), 99 deletions(-) (limited to 'source/nds/entry.cpp') diff --git a/source/nds/entry.cpp b/source/nds/entry.cpp index 6cbf3ef..08e95cb 100644 --- a/source/nds/entry.cpp +++ b/source/nds/entry.cpp @@ -642,10 +642,19 @@ static unsigned int sync_next = 0; static unsigned int skip_rate= 0; +#define CPU_DOWNCLOCK_EARLY_TIME 293 /* 1 = 42.667 us. 391 = 16.67 ms */ +#define CPU_DOWNCLOCK_DETERMINATION_INTERVAL 46874 /* 23437 = 1 s */ + +static unsigned int LastAutoCPUTime = 0; +unsigned int AutoCPUFrequency = 5; +static bool8 ConsistentlyEarly = FALSE; +static bool8 FastForwardLastFrame = FALSE; + void S9xSyncSpeed () { uint32 syncnow; int32 syncdif; + unsigned int LastAutoCPUFrequency = AutoCPUFrequency; #if 0 if (Settings.SoundSync == 2) @@ -657,8 +666,19 @@ void S9xSyncSpeed () #endif syncnow = getSysTime(); - if (game_fast_forward || temporary_fast_forward /* hotkey is held */) + bool8 FastForward = game_fast_forward || temporary_fast_forward /* hotkey is held */; + + if (game_config.clock_speed_number == 0) + if (FastForward && !FastForwardLastFrame) + HighFrequencyCPU (); + else if (!FastForward && FastForwardLastFrame) + GameFrequencyCPU (); + + FastForwardLastFrame = FastForward; + + if (FastForward) { + ConsistentlyEarly = FALSE; // don't use fast-forward to lower CPU sync_last = syncnow; sync_next = syncnow; @@ -670,137 +690,149 @@ void S9xSyncSpeed () IPPU.RenderThisFrame = true; } } - else if (Settings.SkipFrames == AUTO_FRAMERATE /* && !game_fast_forward && !temporary_fast_forward */) + else { - // frame_time is in getSysTime units: 42.667 microseconds. - int32 frame_time = Settings.PAL ? 468 /* = 20.0 ms */ : 391 /* = 16.67 ms */; - if (sync_last > syncnow) // Overflow occurred! (every 50 hrs) + // Manual or automatic frame skipping, no fast-forward. + if (Settings.SkipFrames == AUTO_FRAMERATE) { - // Render this frame regardless, set the - // sync_next, and get the hell out. - IPPU.RenderThisFrame = TRUE; + // frame_time is in getSysTime units: 42.667 microseconds. + int32 frame_time = Settings.PAL ? 468 /* = 20.0 ms */ : 391 /* = 16.67 ms */; + if (sync_last > syncnow) // Overflow occurred! (every 50 hrs) + { + // Render this frame regardless, set the + // sync_next, and get the hell out. + ConsistentlyEarly = FALSE; + AutoCPUFrequency = 5; + LastAutoCPUTime = syncnow; + + IPPU.RenderThisFrame = TRUE; + sync_last = syncnow; + sync_next = syncnow + frame_time; + goto finalise; + } sync_last = syncnow; - sync_next = syncnow + frame_time; - return; - } - sync_last = syncnow; - // If this is positive, we have syncdif*42.66 microseconds to - // spare. - // If this is negative, we're late by syncdif*42.66 - // microseconds. - syncdif = sync_next - syncnow; - if(skip_rate < 2 /* did not skip 2 frames yet */) - { - // Skip a minimum of 2 frames between rendered frames. - // This prevents the DSTwo-DS link from being too busy - // to return button statuses. - ++skip_rate; - IPPU.RenderThisFrame = FALSE; - sync_next += frame_time; - } - else if (syncdif < 0 && syncdif >= -(frame_time / 2)) - { - // We're late, but by less than half a frame. Draw it - // anyway. If the next frame is too late, it'll be - // skipped. - skip_rate = 0; - IPPU.RenderThisFrame = true; - sync_next += frame_time; - } - else if(syncdif < 0) - { - /* - * If we're consistently late, delay up to 8 frames. - * - * That really helps with certain games, such as - * Super Mario RPG and Yoshi's Island. - */ - if(++skip_rate < 10) + // If this is positive, we have syncdif*42.66 microseconds to + // spare. + // If this is negative, we're late by syncdif*42.66 + // microseconds. + syncdif = sync_next - syncnow; + if(skip_rate < 2 /* did not skip 2 frames yet */) { - if(syncdif >= -11719 /* not more than 500.0 ms late */) + // Skip a minimum of 2 frames between rendered frames. + // This prevents the DSTwo-DS link from being too busy + // to return button statuses. + ++skip_rate; + IPPU.RenderThisFrame = FALSE; + sync_next += frame_time; + } + else if(syncdif < 0) + { + ConsistentlyEarly = FALSE; + AutoCPUFrequency = 5; + LastAutoCPUTime = syncnow; + + /* + * If we're consistently late, delay up to 8 frames. + * + * That really helps with certain games, such as + * Super Mario RPG and Yoshi's Island. + */ + if(++skip_rate < 10) { - IPPU.RenderThisFrame = FALSE; - sync_next += frame_time; + if(syncdif >= -11719 /* not more than 500.0 ms late */) + { + IPPU.RenderThisFrame = FALSE; + sync_next += frame_time; + } + else + { //lag more than 0.5s, maybe paused + IPPU.RenderThisFrame = TRUE; + sync_next = syncnow + frame_time; + } } else - { //lag more than 0.5s, maybe paused + { + skip_rate = 0; IPPU.RenderThisFrame = TRUE; sync_next = syncnow + frame_time; } } - else + else // Early { + ConsistentlyEarly = ConsistentlyEarly && syncdif >= CPU_DOWNCLOCK_EARLY_TIME; skip_rate = 0; - IPPU.RenderThisFrame = TRUE; - sync_next = syncnow + frame_time; - } - } - else // Early - { - skip_rate = 0; - if (syncdif > 0) - { - do { - S9xProcessSound (0); + if (syncdif > 0) + { + do { + S9xProcessSound (0); #ifdef ACCUMULATE_JOYPAD /* * This call allows NDSSFC to synchronise the DS controller more often. * If porting a later version of Snes9x into NDSSFC, it is essential to * preserve it. */ - NDSSFCAccumulateJoypad (); + NDSSFCAccumulateJoypad (); #endif - syncdif = sync_next - getSysTime(); - } while (syncdif > 0); - } + syncdif = sync_next - getSysTime(); + } while (syncdif > 0); + } - IPPU.RenderThisFrame = TRUE; - sync_next += frame_time; - } + IPPU.RenderThisFrame = TRUE; + sync_next += frame_time; + } #if 0 - if(++framenum >= 60) - { - syncdif = syncnow - sync_last; - sync_last = syncnow; - framenum = 0; - //printf("T %d %d\n", syncdif*42667/1000, realframe); - realframe = 0; - } + if(++framenum >= 60) + { + syncdif = syncnow - sync_last; + sync_last = syncnow; + framenum = 0; + //printf("T %d %d\n", syncdif*42667/1000, realframe); + realframe = 0; + } #endif - } - else /* if (Settings.SkipFrames != AUTO_FRAMERATE && !game_fast_forward && !temporary_fast_forward) */ - { - // frame_time is in getSysTime units: 42.667 microseconds. - uint32 frame_time = Settings.PAL ? 468 /* = 20.0 ms */ : 391 /* = 16.67 ms */; - sync_last = syncnow; - if (++skip_rate > Settings.SkipFrames) + } + else /* if (Settings.SkipFrames != AUTO_FRAMERATE) */ { - skip_rate = 0; - IPPU.RenderThisFrame = TRUE; - // Are we early? - syncdif = sync_next - syncnow; - if (syncdif > 0) + // frame_time is in getSysTime units: 42.667 microseconds. + uint32 frame_time = Settings.PAL ? 468 /* = 20.0 ms */ : 391 /* = 16.67 ms */; + sync_last = syncnow; + if (++skip_rate > Settings.SkipFrames) { - do { - S9xProcessSound (0); + skip_rate = 0; + IPPU.RenderThisFrame = TRUE; + // Are we early? + syncdif = sync_next - syncnow; + if (syncdif > 0) + { + ConsistentlyEarly = ConsistentlyEarly && syncdif >= CPU_DOWNCLOCK_EARLY_TIME; + do { + S9xProcessSound (0); #ifdef ACCUMULATE_JOYPAD /* * This call allows NDSSFC to synchronise the DS controller more often. * If porting a later version of Snes9x into NDSSFC, it is essential to * preserve it. */ - NDSSFCAccumulateJoypad (); + NDSSFCAccumulateJoypad (); #endif - syncdif = sync_next - getSysTime(); - } while (syncdif > 0); - // After that little delay, what time is it? - syncnow = getSysTime(); + syncdif = sync_next - getSysTime(); + } while (syncdif > 0); + // After that little delay, what time is it? + syncnow = getSysTime(); + } + else + { + // Nope, we're late. + ConsistentlyEarly = FALSE; + AutoCPUFrequency = 5; + LastAutoCPUTime = syncnow; + } + sync_next = syncnow + frame_time * (Settings.SkipFrames + 1); + } + else + { + IPPU.RenderThisFrame = FALSE; } - sync_next = syncnow + frame_time * (Settings.SkipFrames + 1); - } - else - { - IPPU.RenderThisFrame = FALSE; } } @@ -887,6 +919,20 @@ void S9xSyncSpeed () next1.tv_usec %= 1000000; } #endif + +finalise: ; + + if (syncnow - LastAutoCPUTime >= CPU_DOWNCLOCK_DETERMINATION_INTERVAL) { + if (ConsistentlyEarly && AutoCPUFrequency > 0) + AutoCPUFrequency--; + + LastAutoCPUTime = syncnow; + ConsistentlyEarly = TRUE; + // will get unset if the CPU should stay the same at next check + } + + if (game_config.clock_speed_number == 0 && LastAutoCPUFrequency != AutoCPUFrequency) + GameFrequencyCPU (); } bool8 S9xOpenSoundDevice (int mode, bool8 stereo, int buffer_size) -- cgit v1.2.3