Frameskip improvements

This commit is contained in:
jdgleaver 2020-10-20 15:45:38 +01:00
parent 8acd98afd3
commit c67092ae00
3 changed files with 135 additions and 20 deletions

View File

@ -55,7 +55,9 @@ static int32_t samples_per_frame = 0;
static int32_t samplerate = (((SNES_CLOCK_SPEED * 6) / (32 * ONE_APU_CYCLE)));
static unsigned frameskip_type = 0;
static unsigned frameskip_threshold = 0;
static uint16_t frameskip_counter = 0;
static bool retro_audio_buff_active = false;
static unsigned retro_audio_buff_occupancy = 0;
static bool retro_audio_buff_underrun = false;
@ -63,6 +65,9 @@ static bool retro_audio_buff_underrun = false;
* can be skipped */
#define FRAMESKIP_MAX 30
static unsigned retro_audio_latency = 0;
static bool update_audio_latency = false;
#ifdef PERF_TEST
#define RETRO_PERFORMANCE_INIT(name) \
retro_perf_tick_t current_ticks; \
@ -157,11 +162,34 @@ static void retro_set_audio_buff_status_cb(void)
retro_audio_buff_active = false;
retro_audio_buff_occupancy = 0;
retro_audio_buff_underrun = false;
retro_audio_latency = 0;
}
else
{
/* Frameskip is enabled - increase frontend
* audio latency to minimise potential
* buffer underruns */
uint32_t frame_time_usec = Settings.FrameTime;
if (Settings.ForceNTSC)
frame_time_usec = Settings.FrameTimeNTSC;
if (Settings.ForcePAL)
frame_time_usec = Settings.FrameTimePAL;
/* Set latency to 6x current frame time... */
retro_audio_latency = (unsigned)(6 * frame_time_usec / 1000);
/* ...then round up to nearest multiple of 32 */
retro_audio_latency = (retro_audio_latency + 0x1F) & ~0x1F;
}
}
else
environ_cb(RETRO_ENVIRONMENT_SET_AUDIO_BUFFER_STATUS_CALLBACK,
NULL);
{
environ_cb(RETRO_ENVIRONMENT_SET_AUDIO_BUFFER_STATUS_CALLBACK, NULL);
retro_audio_latency = 0;
}
update_audio_latency = true;
}
void S9xDeinitDisplay(void)
@ -383,7 +411,16 @@ void retro_deinit(void)
perf_cb.perf_log();
#endif
/* Reset globals (required for static builds) */
libretro_supports_bitmasks = false;
frameskip_type = 0;
frameskip_threshold = 0;
frameskip_counter = 0;
retro_audio_buff_active = false;
retro_audio_buff_occupancy = 0;
retro_audio_buff_underrun = false;
retro_audio_latency = 0;
update_audio_latency = false;
}
uint32_t S9xReadJoypad(int32_t port)
@ -423,14 +460,19 @@ uint32_t S9xReadJoypad(int32_t port)
return joypad;
}
static void check_variables(void)
static void check_variables(bool first_run)
{
struct retro_variable var;
bool prev_force_ntsc;
bool prev_force_pal;
bool prev_frameskip_type;
var.key = "catsfc_VideoMode";
var.value = NULL;
prev_force_ntsc = Settings.ForceNTSC;
prev_force_pal = Settings.ForcePAL;
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
{
Settings.ForceNTSC = !strcmp(var.value, "NTSC");
@ -447,14 +489,17 @@ static void check_variables(void)
{
if (strcmp(var.value, "auto") == 0)
frameskip_type = 1;
else if (strcmp(var.value, "aggressive") == 0)
else if (strcmp(var.value, "manual") == 0)
frameskip_type = 2;
else if (strcmp(var.value, "max") == 0)
frameskip_type = 3;
}
if (frameskip_type != prev_frameskip_type)
retro_set_audio_buff_status_cb();
var.key = "catsfc_frameskip_threshold";
var.value = NULL;
frameskip_threshold = 33;
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
frameskip_threshold = strtol(var.value, NULL, 10);
var.key = "catsfc_overclock_cycles";
var.value = NULL;
@ -489,6 +534,13 @@ static void check_variables(void)
else
reduce_sprite_flicker = false;
}
/* Reinitialise frameskipping, if required */
if (!first_run &&
((frameskip_type != prev_frameskip_type) ||
(Settings.ForceNTSC != prev_force_ntsc) ||
(Settings.ForcePAL != prev_force_pal)))
retro_set_audio_buff_status_cb();
}
static int32_t samples_to_play = 0;
@ -502,7 +554,7 @@ void retro_run(void)
#endif
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && updated)
check_variables();
check_variables(false);
#ifdef NO_VIDEO_OUTPUT
video_cb(NULL, IPPU.RenderedScreenWidth, IPPU.RenderedScreenHeight, GFX.Pitch);
@ -544,11 +596,8 @@ void retro_run(void)
case 1: /* auto */
skip_frame = retro_audio_buff_underrun;
break;
case 2: /* aggressive */
skip_frame = (retro_audio_buff_occupancy < 33);
break;
case 3: /* max */
skip_frame = (retro_audio_buff_occupancy < 50);
case 2: /* manual */
skip_frame = (retro_audio_buff_occupancy < frameskip_threshold);
break;
default:
skip_frame = false;
@ -567,6 +616,18 @@ void retro_run(void)
}
}
/* If frameskip/timing settings have changed,
* update frontend audio latency
* > Can do this before or after the frameskip
* check, but doing it after means we at least
* retain the current frame's audio output */
if (update_audio_latency)
{
environ_cb(RETRO_ENVIRONMENT_SET_MINIMUM_AUDIO_LATENCY,
&retro_audio_latency);
update_audio_latency = false;
}
poll_cb();
RETRO_PERFORMANCE_INIT(S9xMainLoop_func);
@ -960,7 +1021,7 @@ bool retro_load_game(const struct retro_game_info* game)
CPU.Flags = 0;
init_descriptors();
check_variables();
check_variables(true);
#ifdef LOAD_FROM_MEMORY_TEST
if (!LoadROM(game))

View File

@ -1344,6 +1344,36 @@ enum retro_mod
* in the frontend.
*/
#define RETRO_ENVIRONMENT_SET_MINIMUM_AUDIO_LATENCY 63
/* const unsigned * --
* Sets minimum frontend audio latency in milliseconds.
* Resultant audio latency may be larger than set value,
* or smaller if a hardware limit is encountered. A frontend
* is expected to honour requests up to 512 ms.
*
* - If value is less than current frontend
* audio latency, callback has no effect
* - If value is zero, default frontend audio
* latency is set
*
* May be used by a core to increase audio latency and
* therefore decrease the probability of buffer under-runs
* (crackling) when performing 'intensive' operations.
* A core utilising RETRO_ENVIRONMENT_SET_AUDIO_BUFFER_STATUS_CALLBACK
* to implement audio-buffer-based frame skipping may achieve
* optimal results by setting the audio latency to a 'high'
* (typically 6x or 8x) integer multiple of the expected
* frame time.
*
* WARNING: This can only be called from within retro_run().
* Calling this can require a full reinitialization of audio
* drivers in the frontend, so it is important to call it very
* sparingly, and usually only with the users explicit consent.
* An eventual driver reinitialize will happen so that audio
* callbacks happening after this call within the same retro_run()
* call will target the newly initialized driver.
*/
/* VFS functionality */
/* File paths:

View File

@ -69,16 +69,40 @@ struct retro_core_option_definition option_defs_us[] = {
{
"catsfc_frameskip",
"Frameskip",
"Automatically skip frames to avoid audio buffer under-run (crackling). 'Aggressive' and 'Max' increase the buffer threshold at which frames are skipped. Improves performance at the expense of visual smoothness. NOTE: For best results, frontend 'Audio Latency' should be set to at least 128 ms.",
"Skip frames to avoid audio buffer under-run (crackling). Improves performance at the expense of visual smoothness. 'Auto' skips frames when advised by the frontend. 'Manual' utilises the 'Frameskip Threshold (%)' setting.",
{
{ "disabled", NULL },
{ "auto", "Auto" },
{ "aggressive", "Aggressive" },
{ "max", "Max" },
{ "disabled", NULL },
{ "auto", "Auto" },
{ "manual", "Manual" },
{ NULL, NULL },
},
"disabled"
},
{
"catsfc_frameskip_threshold",
"Frameskip Threshold (%)",
"When 'Frameskip' is set to 'Manual', specifies the audio buffer occupancy threshold (percentage) below which frames will be skipped. Higher values reduce the risk of crackling by causing frames to be dropped more frequently.",
{
{ "15", NULL },
{ "18", NULL },
{ "21", NULL },
{ "24", NULL },
{ "27", NULL },
{ "30", NULL },
{ "33", NULL },
{ "36", NULL },
{ "39", NULL },
{ "42", NULL },
{ "45", NULL },
{ "48", NULL },
{ "51", NULL },
{ "54", NULL },
{ "57", NULL },
{ "60", NULL },
{ NULL, NULL },
},
"33"
},
{
"catsfc_overclock_cycles",
"Reduce Slowdown (Hack, Unsafe, Restart)",