Merge pull request #7 from jdgleaver/frameskip

Add optional frameskipping
This commit is contained in:
jdgleaver 2021-07-23 17:54:11 +01:00 committed by GitHub
commit 44a14b0212
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 212 additions and 34 deletions

View File

@ -77,8 +77,8 @@ void supervision_done(void);
* \return TRUE - success, FALSE - error
*/
BOOL supervision_load(const uint8 *rom, uint32 romSize);
void supervision_exec(uint16 *backbuffer);
void supervision_exec_ex(uint16 *backbuffer, int16 backbufferWidth);
void supervision_exec(uint16 *backbuffer, BOOL skipFrame);
void supervision_exec_ex(uint16 *backbuffer, int16 backbufferWidth, BOOL skipFrame);
/*!
* \param data Bits 0-7: Right, Left, Down, Up, B, A, Select, Start.

View File

@ -70,12 +70,12 @@ BOOL supervision_load(const uint8 *rom, uint32 romSize)
return TRUE;
}
void supervision_exec(uint16 *backbuffer)
void supervision_exec(uint16 *backbuffer, BOOL skipFrame)
{
supervision_exec_ex(backbuffer, SV_W);
supervision_exec_ex(backbuffer, SV_W, skipFrame);
}
void supervision_exec_ex(uint16 *backbuffer, int16 backbufferWidth)
void supervision_exec_ex(uint16 *backbuffer, int16 backbufferWidth, BOOL skipFrame)
{
uint32 i, scan;
uint8 *regs = memorymap_getRegisters();
@ -87,19 +87,21 @@ void supervision_exec_ex(uint16 *backbuffer, int16 backbufferWidth)
timer_exec(m6502_registers.IPeriod);
}
//if (!(regs[BANK] & 0x8)) { printf("LCD off\n"); }
scan = regs[XPOS] / 4 + regs[YPOS] * 0x30;
innerx = regs[XPOS] & 3;
size = regs[XSIZE]; // regs[XSIZE] <= SV_W
if (size > SV_W)
size = SV_W; // 192: Chimera, Matta Blatta, Tennis Pro '92
if (!skipFrame) {
//if (!(regs[BANK] & 0x8)) { printf("LCD off\n"); }
scan = regs[XPOS] / 4 + regs[YPOS] * 0x30;
innerx = regs[XPOS] & 3;
size = regs[XSIZE]; // regs[XSIZE] <= SV_W
if (size > SV_W)
size = SV_W; // 192: Chimera, Matta Blatta, Tennis Pro '92
for (i = 0; i < SV_H; i++) {
if (scan >= 0x1fe0)
scan -= 0x1fe0; // SSSnake
gpu_render_scanline(scan, backbuffer, innerx, size);
backbuffer += backbufferWidth;
scan += 0x30;
for (i = 0; i < SV_H; i++) {
if (scan >= 0x1fe0)
scan -= 0x1fe0; // SSSnake
gpu_render_scanline(scan, backbuffer, innerx, size);
backbuffer += backbufferWidth;
scan += 0x30;
}
}
if (Rd6502(0x2026) & 0x01)

View File

@ -169,14 +169,14 @@ int main(int argc, char *argv[])
switch(currentConfig.videoMode){
case 0: {
int j;
supervision_exec(screenbuffer);
supervision_exec(screenbuffer, FALSE);
for (j = 0; j < 160; j++)
gp2x_memcpy(screen16+(80+(j+40)*320), screenbuffer+(j * 160), 160*2);
}
break;
case 1:
case 2:
supervision_exec(screen16);
supervision_exec(screen16, FALSE);
break;
default:
break;

View File

@ -135,7 +135,7 @@ int main()
while (true) {
CheckKeys();
supervision_exec(screenBuffer);
supervision_exec(screenBuffer, FALSE);
for (int j = 0; j < 160; j++) {
// Copy frame buffer to screen

View File

@ -284,7 +284,7 @@ int main(int argc, char* argv[])
memset(&oldPad, 0xFF, sizeof(SceCtrlData)); // Reset buttons
}
supervision_exec_ex((uint16_t*)pixels, TEX_WIDTH);
supervision_exec_ex((uint16_t*)pixels, TEX_WIDTH, FALSE);
sceKernelDcacheWritebackAll();
@ -1022,4 +1022,4 @@ void DrawButton(int button, int x, int y)
}
sceGuCopyImage(PIX_FORMAT, 0, 0, 16, 16, 16, pixs[button],
x, y, BUF_WIDTH, (void*)(((unsigned int)fbpc) + 0x4000000));
}
}

View File

@ -275,7 +275,7 @@ Increase Window Size: =\n\
while (!done) {
PollEvents();
HandleInput();
supervision_exec(screenBuffer);
supervision_exec(screenBuffer, FALSE);
Draw();
Wait();
}

View File

@ -368,7 +368,7 @@ void Loop(void)
switch (GetMenuState()) {
case MENUSTATE_EMULATION:
HandleInput();
supervision_exec(screenBuffer);
supervision_exec(screenBuffer, FALSE);
break;
case MENUSTATE_DROP_ROM:
DrawDropROM();

View File

@ -140,7 +140,7 @@ DWORD WINAPI run(LPVOID lpParameter)
supervision_set_input(controls_state);
while (NeedUpdate()) {
supervision_exec(screenBuffer);
supervision_exec(screenBuffer, FALSE);
#ifdef TERRIBLE_AUDIO_IMPLEMENTATION
supervision_update_sound(audioBuffer, BUFFER_SIZE / 2);

View File

@ -35,9 +35,10 @@ static retro_audio_sample_batch_t audio_batch_cb;
static bool libretro_supports_bitmasks = false;
#define SV_W 160
#define SV_H 160
#define AUDIO_BUFFER_SIZE ((SV_SAMPLE_RATE / 60) << 1)
#define SV_FPS 60
#define SV_W 160
#define SV_H 160
#define AUDIO_BUFFER_SIZE ((SV_SAMPLE_RATE / SV_FPS) << 1)
static enum SV_COLOR color_scheme = SV_COLOR_SCHEME_DEFAULT;
static int ghosting_frames = 0;
@ -87,6 +88,73 @@ struct sv_color_scheme sv_color_schemes[] = {
{ NULL, 0 },
};
/************************************
* Frameskipping Support
************************************/
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;
/* Maximum number of consecutive frames that
* can be skipped */
#define FRAMESKIP_MAX 60
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;
}
static 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 */
float frame_time_msec = 1000.0f / (float)SV_FPS;
/* 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;
}
/************************************
* Auxiliary functions
************************************/
@ -118,6 +186,7 @@ static void check_variables(bool startup)
struct retro_variable var = {0};
enum SV_COLOR color_scheme_last;
int ghosting_frames_last;
unsigned frameskip_type_last;
/* Internal Palette */
color_scheme_last = color_scheme;
@ -142,6 +211,31 @@ static void check_variables(bool startup)
if (startup || (ghosting_frames != ghosting_frames_last))
supervision_set_ghosting(ghosting_frames);
/* Frameskip */
frameskip_type_last = frameskip_type;
frameskip_type = 0;
var.key = "potator_frameskip";
var.value = NULL;
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
{
if (!strcmp(var.value, "auto"))
frameskip_type = 1;
else if (!strcmp(var.value, "manual"))
frameskip_type = 2;
}
if (startup || (frameskip_type != frameskip_type_last))
init_frameskip();
/* Frameskip Threshold (%) */
frameskip_threshold = 33;
var.key = "potator_frameskip_threshold";
var.value = NULL;
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
frameskip_threshold = strtol(var.value, NULL, 10);
}
static void update_input(void)
@ -253,8 +347,8 @@ void retro_get_system_info(struct retro_system_info *info)
void retro_get_system_av_info(struct retro_system_av_info *info)
{
memset(info, 0, sizeof(*info));
info->timing.fps = 60;
info->timing.sample_rate = SV_SAMPLE_RATE;
info->timing.fps = (double)SV_FPS;
info->timing.sample_rate = (double)SV_SAMPLE_RATE;
info->geometry.base_width = SV_W;
info->geometry.base_height = SV_H;
info->geometry.max_width = SV_W;
@ -440,6 +534,15 @@ void retro_init(void)
* of 128, to avoid potential overflows */
audio_samples_buffer = (uint8*)malloc(((AUDIO_BUFFER_SIZE + 0x7F) & ~0x7F) * sizeof(uint8));
audio_out_buffer = (int16_t*)malloc(AUDIO_BUFFER_SIZE * sizeof(int16_t));
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(void)
@ -479,7 +582,8 @@ void retro_reset(void)
void retro_run(void)
{
bool options_updated = false;
bool options_updated = false;
BOOL skip_frame = FALSE;
/* Core options */
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &options_updated) && options_updated)
@ -488,11 +592,46 @@ void retro_run(void)
/* Update input */
update_input();
/* Check whether current frame should be skipped */
if ((frameskip_type > 0) && retro_audio_buff_active)
{
switch (frameskip_type)
{
case 1: /* auto */
skip_frame = retro_audio_buff_underrun ? TRUE : FALSE;
break;
case 2: /* manual */
skip_frame = (retro_audio_buff_occupancy < frameskip_threshold) ? TRUE : FALSE;
break;
default:
skip_frame = 0;
break;
}
if (!skip_frame || (frameskip_counter >= FRAMESKIP_MAX))
{
skip_frame = 0;
frameskip_counter = 0;
}
else
frameskip_counter++;
}
/* 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;
}
/* Run emulator */
supervision_exec(video_buffer);
supervision_exec(video_buffer, skip_frame);
/* Output video */
video_cb(video_buffer, SV_W, SV_H, SV_W << 1);
video_cb((bool)skip_frame ? NULL : video_buffer,
SV_W, SV_H, SV_W << 1);
/* Output audio */
update_audio();

View File

@ -105,6 +105,43 @@ struct retro_core_option_definition option_defs_us[] = {
},
"0"
},
{
"potator_frameskip",
"Frameskip",
"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" },
{ "manual", "Manual" },
{ NULL, NULL },
},
"disabled"
},
{
"potator_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"
},
{ NULL, NULL, NULL, {{0}}, NULL },
};

View File

@ -269,7 +269,7 @@ int main(int argc, char *argv[]) {
supervision_set_input(controls_state);
// Update emulation
supervision_exec(XBuf);
supervision_exec(XBuf, FALSE);
graphics_paint();
nextTick += interval;