mirror of
https://github.com/libretro/potator.git
synced 2024-11-23 08:29:41 +00:00
Merge pull request #7 from jdgleaver/frameskip
Add optional frameskipping
This commit is contained in:
commit
44a14b0212
@ -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
|
||||
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)
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user