aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAutechre2020-10-20 18:48:50 +0200
committerGitHub2020-10-20 18:48:50 +0200
commit076dffc6e1a61d19565ee44e49518c7f5846306a (patch)
treef4b3502b4d191a68b23783370dc55feb823de1c4
parent8acd98afd3510ba258b93821a4d7796ba31a9b2f (diff)
parentc67092ae0078b6f27f4c0b40f173fb9a724f3e4a (diff)
downloadsnes9x2005-076dffc6e1a61d19565ee44e49518c7f5846306a.tar.gz
snes9x2005-076dffc6e1a61d19565ee44e49518c7f5846306a.tar.bz2
snes9x2005-076dffc6e1a61d19565ee44e49518c7f5846306a.zip
Merge pull request #80 from jdgleaver/frameskip-latency
Frameskip improvements
-rw-r--r--libretro.c91
-rw-r--r--libretro.h30
-rw-r--r--libretro_core_options.h34
3 files changed, 135 insertions, 20 deletions
diff --git a/libretro.c b/libretro.c
index 2932ac3..ca8b68d 100644
--- a/libretro.c
+++ b/libretro.c
@@ -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))
diff --git a/libretro.h b/libretro.h
index 8b579ed..59bd513 100644
--- a/libretro.h
+++ b/libretro.h
@@ -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:
diff --git a/libretro_core_options.h b/libretro_core_options.h
index 3f92b3b..032cefd 100644
--- a/libretro_core_options.h
+++ b/libretro_core_options.h
@@ -69,17 +69,41 @@ 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)",
"Many games for the SNES suffered from slowdown due to the weak main CPU. This option helps allievate that at the cost of possible bugs. COMPATIBLE: Reduce slowdown but keep as much game compatibility as much as possible. MAX: Reduce slowdown as much as possible but will break more games.",