FLTK: Add timer sync mode for cases where VSync is unreliable

This commit is contained in:
rdanbrook 2024-12-04 19:01:59 -06:00
parent 8e9b7755e0
commit 1259fe812c
3 changed files with 81 additions and 10 deletions

View file

@ -67,6 +67,7 @@ namespace {
int paused{0};
int speed{1};
int video_fullscreen{0};
int syncmode{0};
int refreshrate{60};
int screennum{0};
@ -150,6 +151,13 @@ Fl_Menu_Item *get_menuitem(std::string label) {
void update_refreshrate(void) {
// Get the screen refresh rate using an SDL window
if (syncmode) { // Don't use this in "Timer" sync mode
return;
}
#ifdef __APPLE__
refreshrate = 120; // Dirty hack for modern macOS
return;
#endif
SDL_Window *sdlwin;
sdlwin = SDL_CreateWindow(
"refreshrate",
@ -162,7 +170,7 @@ void update_refreshrate(void) {
SDL_DestroyWindow(sdlwin);
}
void exec_emu(void*) {
void exec_emu_vsync(void*) {
SDL_Event event;
while (SDL_PollEvent(&event)) {
inputmgr->event(event);
@ -186,6 +194,22 @@ void exec_emu(void*) {
glarea->redraw();
}
void exec_emu_timer(void*) {
SDL_Event event;
while (SDL_PollEvent(&event)) {
inputmgr->event(event);
}
if (!paused) {
for (int i = 0; i < speed; i++) {
jgm->exec_frame();
}
}
Fl::repeat_timeout(1.0 / jgm->get_frametime(), exec_emu_timer);
glarea->redraw();
}
}
void FltkUi::chtwin_open(Fl_Widget *w, void *data) {
@ -251,6 +275,8 @@ void FltkUi::rom_open(Fl_Widget *w, void *data) {
fc.type(Fl_Native_File_Chooser::BROWSE_FILE);
fc.filter("NES Games\t*.{nes,unf,fds,bin,zip,7z,gz,bz2,xz,xml,zst}");
run_emulation(false);
// Show file chooser
switch (fc.show()) {
case -1:
@ -274,6 +300,8 @@ void FltkUi::rom_open(Fl_Widget *w, void *data) {
}
break;
}
run_emulation();
}
void FltkUi::screenshot(std::string filename) {
@ -295,6 +323,8 @@ void FltkUi::screenshot_save(Fl_Widget *w, void *data) {
return;
}
run_emulation(false);
Fl_Native_File_Chooser fc;
fc.title("Save Screenshot");
fc.type(Fl_Native_File_Chooser::BROWSE_SAVE_FILE);
@ -307,6 +337,7 @@ void FltkUi::screenshot_save(Fl_Widget *w, void *data) {
}
screenshot(fc.filename());
run_emulation();
}
void FltkUi::state_load(Fl_Widget *w, void *userdata) {
@ -315,6 +346,8 @@ void FltkUi::state_load(Fl_Widget *w, void *userdata) {
return;
}
run_emulation(false);
Fl_Native_File_Chooser fc;
fc.title("Load State");
fc.type(Fl_Native_File_Chooser::BROWSE_FILE);
@ -334,6 +367,8 @@ void FltkUi::state_load(Fl_Widget *w, void *userdata) {
}
break;
}
run_emulation();
}
void FltkUi::state_save(Fl_Widget *w, void *data) {
@ -342,6 +377,8 @@ void FltkUi::state_save(Fl_Widget *w, void *data) {
return;
}
run_emulation(false);
Fl_Native_File_Chooser fc;
fc.title("Save State");
fc.type(Fl_Native_File_Chooser::BROWSE_SAVE_FILE);
@ -350,11 +387,13 @@ void FltkUi::state_save(Fl_Widget *w, void *data) {
// Show file chooser
if (fc.show()) {
run_emulation();
return;
}
std::string statefile{fc.filename()};
jgm->state_save(statefile);
run_emulation();
}
void FltkUi::palette_open(Fl_Widget *w, void *data) {
@ -481,6 +520,7 @@ void FltkUi::fds_insert(Fl_Widget *w, void *data) {
void FltkUi::about_close(Fl_Widget *w, void *data) {
Fl_Window *about = (Fl_Window*)data;
about->hide();
run_emulation();
}
void FltkUi::about(Fl_Widget *w, void *data) {
@ -524,6 +564,7 @@ void FltkUi::about(Fl_Widget *w, void *data) {
close.callback(FltkUi::about_close, (void*)&about);
about.set_modal();
run_emulation(false);
about.show();
while (about.shown()) {
Fl::wait();
@ -610,6 +651,9 @@ int NstGlArea::handle(int e) {
jgm->setup_video();
inputmgr->reassign();
audiomgr->unpause();
// Restart if in timer sync mode
FltkUi::run_emulation(false);
FltkUi::run_emulation();
}
return 1;
}
@ -707,6 +751,25 @@ void FltkUi::set_ffspeed(bool on) {
audiomgr->set_speed(speed);
}
void FltkUi::run_emulation(bool run) {
if (run) {
if (syncmode) {
Fl::add_timeout(0.001, exec_emu_timer);
}
else {
Fl::add_idle(exec_emu_vsync);
}
}
else {
if (syncmode) {
Fl::remove_timeout(exec_emu_timer);
}
else {
Fl::remove_idle(exec_emu_vsync);
}
}
}
int main(int argc, char *argv[]) {
// Parse command line arguments
std::string filename{};
@ -788,12 +851,14 @@ int main(int argc, char *argv[]) {
FltkUi::fullscreen(NULL, NULL);
}
syncmode = setmgr->get_setting("m_syncmode")->val;
LogDriver::log(LogLevel::Debug, syncmode ?
"Synchronization Mode: Timer" :
"Synchronization Mode: VSync");
update_refreshrate();
// Execute emulation using an FLTK idle callback. End when the main window
// is no longer shown. Using the while loop instead of Fl::run will cleanly
// exit the program even when other windows are still shown.
Fl::add_idle(exec_emu);
FltkUi::run_emulation();
while (nstwin->shown()) {
Fl::wait();
}

View file

@ -63,5 +63,6 @@ public:
static void setwin_open(Fl_Widget *w = nullptr, void *data = nullptr);
static void chtwin_open(Fl_Widget *w = nullptr, void *data = nullptr);
static void nstwin_open();
static void run_emulation(bool run = true);
static int handle(int e);
};

View file

@ -37,7 +37,7 @@
namespace {
jg_setting_t fe_settings[] = {
{ "v_renderer", "Video Renderer",
{ "v_renderer", "Video Renderer (Restart)",
"0 = Modern, 1 = Legacy",
"Use Modern (Core Profile, GLES) or Legacy (Compatibility Profile) OpenGL. "
"Use Modern unless you are on extremely weak or incapable hardware.",
@ -83,10 +83,10 @@ jg_setting_t fe_settings[] = {
"Hide the crosshair when a Zapper is present",
0, 0, 1, FLAG_FRONTEND
},
{ "l_loglevel", "Console Log Level",
"0 = Debug, 1 = Info, 2 = Warn, 3 = Error",
"Set the level of logs printed to the console. Debug shows all, Error shows only critical errors.",
1, 0, 3, FLAG_FRONTEND
{ "m_syncmode", "Synchronization Mode (Restart)",
"0 = VSync, 1 = Timer",
"Set the Synchronization Mode: VSync to sync to VBLANK, Timer in cases where VSync is unreliable",
0, 0, 1, FLAG_FRONTEND | JG_SETTING_RESTART
},
{ "s_crtmasktype", "CRT Mask Type",
"0 = No Mask, 1 = Aperture Grille Lite, 2 = Aperture Grille, "
@ -125,6 +125,11 @@ jg_setting_t fe_settings[] = {
"Set the level of Trinitron Curvature, which reduces the curve on the vertical axis",
10, 0, 10, FLAG_FRONTEND
},
{ "l_loglevel", "Console Log Level",
"0 = Debug, 1 = Info, 2 = Warn, 3 = Error",
"Set the level of logs printed to the console. Debug shows all, Error shows only critical errors.",
1, 0, 3, FLAG_FRONTEND
},
};
jg_setting_t nullsetting;