diff --git a/svn-current/trunk/src/burner/libretro/libretro.cpp b/svn-current/trunk/src/burner/libretro/libretro.cpp index 639aab9..1b23498 100644 --- a/svn-current/trunk/src/burner/libretro/libretro.cpp +++ b/svn-current/trunk/src/burner/libretro/libretro.cpp @@ -153,6 +153,72 @@ void retro_set_environment(retro_environment_t cb) environ_cb = cb; } +/* Frameskipping Support */ + +unsigned frameskip_type = 0; +unsigned frameskip_threshold = 0; +uint16_t frameskip_counter = 0; +unsigned frameskip_interval = 0; + +static bool retro_audio_buff_active = false; +static unsigned retro_audio_buff_occupancy = 0; +static bool retro_audio_buff_underrun = false; + +static unsigned audio_latency = 0; +static bool update_audio_latency = false; + +static void retro_audio_buff_status_cb( + bool active, unsigned occupancy, bool underrun_likely) +{ + retro_audio_buff_active = active; + retro_audio_buff_occupancy = occupancy; + retro_audio_buff_underrun = underrun_likely; +} + +void init_frameskip(void) +{ + if (frameskip_type > 0) + { + struct retro_audio_buffer_status_callback buf_status_cb; + + buf_status_cb.callback = retro_audio_buff_status_cb; + if (!environ_cb(RETRO_ENVIRONMENT_SET_AUDIO_BUFFER_STATUS_CALLBACK, + &buf_status_cb)) + { + if (log_cb) + log_cb(RETRO_LOG_WARN, "Frameskip disabled - frontend does not support audio buffer status monitoring.\n"); + + retro_audio_buff_active = false; + retro_audio_buff_occupancy = 0; + retro_audio_buff_underrun = false; + audio_latency = 0; + } + else + { + /* Frameskip is enabled - increase frontend + * audio latency to minimise potential + * buffer underruns */ +#ifdef FBACORES_CPS + float frame_time_msec = 1000.0f / 59.629403f; +#else + float frame_time_msec = 1000.0f / ((float)nBurnFPS / 100.0f); +#endif + /* Set latency to 6x current frame time... */ + audio_latency = (unsigned)((6.0f * frame_time_msec) + 0.5f); + + /* ...then round up to nearest multiple of 32 */ + audio_latency = (audio_latency + 0x1F) & ~0x1F; + } + } + else + { + environ_cb(RETRO_ENVIRONMENT_SET_AUDIO_BUFFER_STATUS_CALLBACK, NULL); + audio_latency = 0; + } + + update_audio_latency = true; +} + char g_rom_dir[MAX_PATH]; char g_save_dir[1024]; char g_system_dir[1024]; @@ -789,6 +855,14 @@ void retro_init() log_cb = log_dummy; BurnLibInit(); + frameskip_type = 0; + frameskip_threshold = 0; + frameskip_counter = 0; + retro_audio_buff_active = false; + retro_audio_buff_occupancy = 0; + retro_audio_buff_underrun = false; + audio_latency = 0; + update_audio_latency = false; } void retro_deinit() @@ -828,10 +902,50 @@ void retro_run() { int width, height; BurnDrvGetVisibleSize(&width, &height); + int nSkipFrame = 0; + pBurnDraw = (uint8_t*)g_fba_frame; InputMake(); + /* Check whether current frame should + * be skipped */ + if ((frameskip_type > 0) && retro_audio_buff_active) + { + switch (frameskip_type) + { + case 1: /* auto */ + nSkipFrame = retro_audio_buff_underrun ? 1 : 0; + break; + case 2: /* manual */ + nSkipFrame = (retro_audio_buff_occupancy < frameskip_threshold) ? 1 : 0; + break; + default: + nSkipFrame = 0; + break; + } + + if (!nSkipFrame || (frameskip_counter >= frameskip_interval)) + { + nSkipFrame = 0; + frameskip_counter = 0; + } + else + frameskip_counter++; + } + + if (nSkipFrame) + pBurnDraw = NULL; + + /* If frameskip settings have changed, update + * frontend audio latency */ + if (update_audio_latency) + { + environ_cb(RETRO_ENVIRONMENT_SET_MINIMUM_AUDIO_LATENCY, + &audio_latency); + update_audio_latency = false; + } + ForceFrameStep(); unsigned drv_flags = BurnDrvGetFlags(); @@ -851,7 +965,11 @@ void retro_run() nBurnPitch = width * pitch_size; } - video_cb(g_fba_frame, width, height, nBurnPitch); + if (!nSkipFrame) + video_cb(g_fba_frame, width, height, nBurnPitch); + else + video_cb(NULL, width, height, nBurnPitch); + audio_batch_cb(g_audio_buf, nBurnSoundLen); bool updated = false; @@ -860,7 +978,7 @@ void retro_run() neo_geo_modes old_g_opt_neo_geo_mode = g_opt_neo_geo_mode; bool old_bVerticalMode = bVerticalMode; - check_variables(); + check_variables(false); apply_dipswitch_from_variables(); @@ -1166,7 +1284,7 @@ bool retro_load_game(const struct retro_game_info *info) SetControllerInfo(); set_environment(); - check_variables(); + check_variables(true); pBurnSoundOut = g_audio_buf; nBurnSoundRate = AUDIO_SAMPLERATE; diff --git a/svn-current/trunk/src/burner/libretro/retro_common.cpp b/svn-current/trunk/src/burner/libretro/retro_common.cpp index 6b8bf31..95c994a 100644 --- a/svn-current/trunk/src/burner/libretro/retro_common.cpp +++ b/svn-current/trunk/src/burner/libretro/retro_common.cpp @@ -62,6 +62,11 @@ INT32 g_audio_samplerate = 48000; UINT8 *diag_input; neo_geo_modes g_opt_neo_geo_mode = NEO_GEO_MODE_MVS; +extern unsigned frameskip_type; +extern unsigned frameskip_threshold; +extern uint16_t frameskip_counter; +extern unsigned frameskip_interval; + #ifdef USE_CYCLONE // 0 - c68k, 1 - m68k // we don't use cyclone by default because it breaks savestates cross-platform compatibility (including netplay) @@ -78,9 +83,11 @@ static UINT8 diag_input_select_l_r[] = {RETRO_DEVICE_ID_JOYPAD_SELECT, RETRO_DE // Global core options static const struct retro_variable var_empty = { NULL, NULL }; +static const struct retro_variable var_fbneo_frameskip = { "fbneo-frameskip-type", "Frameskip ; disabled|auto|threshold" }; +static const struct retro_variable var_fbneo_frameskip_threshold = { "fbneo-threshold", "FS Threshold (%) ; 30|40|50|60" }; +static const struct retro_variable var_fbneo_frameskip_interval = { "fbneo-interval", "FS Interval ; 0|1|2|3|4|5|6|7|8|9" }; static const struct retro_variable var_fbneo_allow_depth_32 = { "fbneo-allow-depth-32", "Use 32-bits color depth when available; disabled|enabled" }; static const struct retro_variable var_fbneo_vertical_mode = { "fbneo-vertical-mode", "Vertical mode; disabled|enabled" }; -static const struct retro_variable var_fbneo_frameskip = { "fbneo-frameskip", "Frameskip; 0|1|2|3|4|5" }; static const struct retro_variable var_fbneo_cpu_speed_adjust = { "fbneo-cpu-speed-adjust", "CPU overclock; 100|110|120|130|140|150|160|170|180|190|200" }; static const struct retro_variable var_fbneo_diagnostic_input = { "fbneo-diagnostic-input", "Diagnostic Input; None|Hold Start|Start + A + B|Hold Start + A + B|Start + L + R|Hold Start + L + R|Hold Select|Select + A + B|Hold Select + A + B|Select + L + R|Hold Select + L + R" }; static const struct retro_variable var_fbneo_hiscores = { "fbneo-hiscores", "Hiscores; enabled|disabled" }; @@ -227,9 +234,11 @@ void set_environment() #endif // Add the Global core options + vars_systems.push_back(&var_fbneo_frameskip); + vars_systems.push_back(&var_fbneo_frameskip_threshold); + vars_systems.push_back(&var_fbneo_frameskip_interval); vars_systems.push_back(&var_fbneo_allow_depth_32); vars_systems.push_back(&var_fbneo_vertical_mode); - vars_systems.push_back(&var_fbneo_frameskip); vars_systems.push_back(&var_fbneo_cpu_speed_adjust); vars_systems.push_back(&var_fbneo_hiscores); if (nGameType != RETRO_GAME_TYPE_NEOCD) @@ -304,9 +313,40 @@ void set_environment() #endif } -void check_variables(void) +void check_variables(bool first_run) { struct retro_variable var = {0}; + bool prev_frameskip_type; + + var.key = var_fbneo_frameskip.key; + var.value = NULL; + + prev_frameskip_type = frameskip_type; + frameskip_type = 0; + + if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) + { + if (strcmp(var.value, "auto") == 0) + frameskip_type = 1; + if (strcmp(var.value, "threshold") == 0) + frameskip_type = 2; + } + + var.key = var_fbneo_frameskip_threshold.key; + var.value = NULL; + + frameskip_threshold = 30; + + if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) + frameskip_threshold = strtol(var.value, NULL, 10); + + var.key = var_fbneo_frameskip_interval.key; + var.value = NULL; + + frameskip_interval = 1; + + if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) + frameskip_interval = strtol(var.value, NULL, 10); var.key = var_fbneo_cpu_speed_adjust.key; if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var)) @@ -542,6 +582,11 @@ void check_variables(void) bCycloneEnabled = false; } #endif + + /* Reinitialise frameskipping, if required */ + if (!first_run && + ((frameskip_type != prev_frameskip_type))) + init_frameskip(); } #ifdef USE_CYCLONE diff --git a/svn-current/trunk/src/burner/libretro/retro_common.h b/svn-current/trunk/src/burner/libretro/retro_common.h index 3efc786..f4feae6 100644 --- a/svn-current/trunk/src/burner/libretro/retro_common.h +++ b/svn-current/trunk/src/burner/libretro/retro_common.h @@ -78,7 +78,8 @@ char* str_char_replace(char* destination, char c_find, char c_replace); void set_neo_system_bios(); void evaluate_neogeo_bios_mode(const char* drvname); void set_environment(); -void check_variables(void); +void check_variables(bool first_run); +void init_frameskip(); #ifdef USE_CYCLONE void SetSekCpuCore(); #endif diff --git a/svn-current/trunk/src/burner/libretro/retro_input.cpp b/svn-current/trunk/src/burner/libretro/retro_input.cpp index d6b1980..2bf2e8d 100644 --- a/svn-current/trunk/src/burner/libretro/retro_input.cpp +++ b/svn-current/trunk/src/burner/libretro/retro_input.cpp @@ -2293,7 +2293,7 @@ void InputInit() // Update core option for diagnostic inputs set_environment(); // Read the user core option values - check_variables(); + check_variables(false); // The list of input descriptors is filled, we can assign them to retroarch //SetInputDescriptors();