Unverified Commit 44a14b02 authored by jdgleaver's avatar jdgleaver Committed by GitHub
Browse files

Merge pull request #7 from jdgleaver/frameskip

Add optional frameskipping
parents f40c6d99 1fc255a6
Pipeline #43199 passed with stages
in 5 minutes and 12 seconds
......@@ -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.
......
......@@ -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
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 (!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;
}
}
if (Rd6502(0x2026) & 0x01)
......
......@@ -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;
......
......@@ -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
......
......@@ -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));
}
\ No newline at end of file
}
......@@ -275,7 +275,7 @@ Increase Window Size: =\n\
while (!done) {
PollEvents();
HandleInput();
supervision_exec(screenBuffer);
supervision_exec(screenBuffer, FALSE);
Draw();
Wait();
}
......
......@@ -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();
......
......@@ -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);
......
......@@ -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();
......
......@@ -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 },
};
......
......@@ -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;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment