From b7db22d816e9a51937c83724f6450f38bf768fc6 Mon Sep 17 00:00:00 2001 From: ro8inmorgan Date: Tue, 1 Jul 2025 09:59:36 +0200 Subject: [PATCH 1/8] run ahead initial version --- workspace/all/minarch/minarch.c | 61 +++++++++++++++++++++++++++++---- 1 file changed, 55 insertions(+), 6 deletions(-) diff --git a/workspace/all/minarch/minarch.c b/workspace/all/minarch/minarch.c index 3ff6672fa..56943cc32 100644 --- a/workspace/all/minarch/minarch.c +++ b/workspace/all/minarch/minarch.c @@ -80,6 +80,11 @@ static int DEVICE_PITCH = 0; // FIXED_PITCH; GFX_Renderer renderer; +size_t max_state_size = 0; +uint8_t *base_state; +uint8_t *runahead_state; +int doRunAhead = 0; + /////////////////////////////////////// static struct Core { @@ -3108,15 +3113,21 @@ static void input_poll_callback(void) { case BTN_DPAD_LEFT: btn = BTN_LEFT; break; case BTN_DPAD_RIGHT: btn = BTN_RIGHT; break; } + } if (PAD_isPressed(btn) && (!mapping->mod || PAD_isPressed(BTN_MENU))) { buttons |= 1 << mapping->retro; if (mapping->mod) ignore_menu = 1; } + if(PAD_anyJustPressed()) { + doRunAhead = 1; + } // && !PWR_ignoreSettingInput(btn, show_setting) } // if (buttons) LOG_info("buttons: %i\n", buttons); + + } static int16_t input_state_callback(unsigned port, unsigned device, unsigned index, unsigned id) { if (port==0 && device==RETRO_DEVICE_JOYPAD && index==0) { @@ -4411,9 +4422,12 @@ void applyCircleReveal(uint32_t **data, size_t pitch, unsigned width, unsigned h *data = temp_buffer; } + + static void video_refresh_callback_main(const void *data, unsigned width, unsigned height, size_t pitch) { // return; + Special_render(); // static int tmp_frameskip = 0; @@ -4513,7 +4527,7 @@ static Uint32* rgbaData = NULL; static size_t rgbaDataSize = 0; static void video_refresh_callback(const void* data, unsigned width, unsigned height, size_t pitch) { - + // I need to check quit here because sometimes quit is true but callback is still called by the core after and it still runs one more frame and it looks ugly :D if(!quit) { if (!rgbaData || rgbaDataSize != width * height) { @@ -4602,7 +4616,8 @@ static size_t audio_sample_batch_callback(const int16_t *data, size_t frames) { } else return frames; // return frames; -}; +} + /////////////////////////////////////// @@ -4611,6 +4626,11 @@ void Core_getName(char* in_name, char* out_name) { char* tmp = strrchr(out_name, '_'); tmp[0] = '\0'; } + + void (*set_video_refresh_callback)(retro_video_refresh_t); + void (*set_audio_sample_callback)(retro_audio_sample_t); + void (*set_audio_sample_batch_callback)(retro_audio_sample_batch_t); + void Core_open(const char* core_path, const char* tag_name) { LOG_info("Core_open\n"); core.handle = dlopen(core_path, RTLD_LAZY); @@ -4637,9 +4657,7 @@ void Core_open(const char* core_path, const char* tag_name) { core.get_memory_size = dlsym(core.handle, "retro_get_memory_size"); void (*set_environment_callback)(retro_environment_t); - void (*set_video_refresh_callback)(retro_video_refresh_t); - void (*set_audio_sample_callback)(retro_audio_sample_t); - void (*set_audio_sample_batch_callback)(retro_audio_sample_batch_t); + void (*set_input_poll_callback)(retro_input_poll_t); void (*set_input_state_callback)(retro_input_state_t); @@ -6770,6 +6788,15 @@ static void limitFF(void) { } +static void fakecall(int16_t left, int16_t right) { + +} +static size_t fakecalls(const int16_t *data, size_t frames) { + +} +static void fakecallv(const void *data, unsigned width, unsigned height, size_t pitch) { +} + int main(int argc , char* argv[]) { LOG_info("MinArch\n"); pthread_t cpucheckthread; @@ -6876,9 +6903,31 @@ int main(int argc , char* argv[]) { // release config when all is loaded Config_free(); LOG_info("total startup time %ims\n\n",SDL_GetTicks()); + + max_state_size = core.serialize_size(); + base_state = malloc(max_state_size); + runahead_state = malloc(max_state_size); + while (!quit) { GFX_startFrame(); - + if(doRunAhead) { + core.serialize(base_state, max_state_size); + + // Run ahead one or more frames + set_video_refresh_callback(fakecallv); + set_audio_sample_callback(fakecall); + set_audio_sample_batch_callback(fakecalls); + for (int i = 0; i < 2; i++) { + core.run(); + } + set_video_refresh_callback(video_refresh_callback); + set_audio_sample_callback(audio_sample_callback); + set_audio_sample_batch_callback(audio_sample_batch_callback); + core.serialize(runahead_state, max_state_size); + + core.unserialize(runahead_state, max_state_size); + doRunAhead = 0; + } core.run(); limitFF(); trackFPS(); From 1542a94acaddea268e54485fb98bee171eb2ce3f Mon Sep 17 00:00:00 2001 From: ro8inmorgan Date: Tue, 1 Jul 2025 10:29:36 +0200 Subject: [PATCH 2/8] ff --- workspace/all/minarch/minarch.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/workspace/all/minarch/minarch.c b/workspace/all/minarch/minarch.c index 56943cc32..881b4f0be 100644 --- a/workspace/all/minarch/minarch.c +++ b/workspace/all/minarch/minarch.c @@ -6911,24 +6911,34 @@ int main(int argc , char* argv[]) { while (!quit) { GFX_startFrame(); if(doRunAhead) { + core.serialize(base_state, max_state_size); - // Run ahead one or more frames set_video_refresh_callback(fakecallv); set_audio_sample_callback(fakecall); set_audio_sample_batch_callback(fakecalls); - for (int i = 0; i < 2; i++) { + for (int i = 0; i < 1; i++) { core.run(); } set_video_refresh_callback(video_refresh_callback); set_audio_sample_callback(audio_sample_callback); set_audio_sample_batch_callback(audio_sample_batch_callback); - core.serialize(runahead_state, max_state_size); + core.run(); - core.unserialize(runahead_state, max_state_size); + // restore base state and make it run 1 frame to continue + set_video_refresh_callback(fakecallv); + set_audio_sample_callback(fakecall); + set_audio_sample_batch_callback(fakecalls); + core.unserialize(base_state, max_state_size); + core.run(); + set_video_refresh_callback(video_refresh_callback); + set_audio_sample_callback(audio_sample_callback); + set_audio_sample_batch_callback(audio_sample_batch_callback); doRunAhead = 0; + } else { + core.run(); } - core.run(); + limitFF(); trackFPS(); From 0d38b6fcaa5c804498eec7c82bda95bc30f97b5b Mon Sep 17 00:00:00 2001 From: ro8inmorgan Date: Tue, 1 Jul 2025 10:40:55 +0200 Subject: [PATCH 3/8] ff --- workspace/all/minarch/minarch.c | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/workspace/all/minarch/minarch.c b/workspace/all/minarch/minarch.c index 881b4f0be..56acb2b3f 100644 --- a/workspace/all/minarch/minarch.c +++ b/workspace/all/minarch/minarch.c @@ -6788,14 +6788,9 @@ static void limitFF(void) { } -static void fakecall(int16_t left, int16_t right) { - -} -static size_t fakecalls(const int16_t *data, size_t frames) { - -} -static void fakecallv(const void *data, unsigned width, unsigned height, size_t pitch) { -} +static void fakeAudioCall(int16_t left, int16_t right) {} +static size_t fakeBatchCall(const int16_t *data, size_t frames) {} +static void fakeVideoCall(const void *data, unsigned width, unsigned height, size_t pitch) {} int main(int argc , char* argv[]) { LOG_info("MinArch\n"); @@ -6914,9 +6909,9 @@ int main(int argc , char* argv[]) { core.serialize(base_state, max_state_size); // Run ahead one or more frames - set_video_refresh_callback(fakecallv); - set_audio_sample_callback(fakecall); - set_audio_sample_batch_callback(fakecalls); + set_video_refresh_callback(fakeVideoCall); + set_audio_sample_callback(fakeAudioCall); + set_audio_sample_batch_callback(fakeBatchCall); for (int i = 0; i < 1; i++) { core.run(); } @@ -6926,10 +6921,10 @@ int main(int argc , char* argv[]) { core.run(); // restore base state and make it run 1 frame to continue - set_video_refresh_callback(fakecallv); - set_audio_sample_callback(fakecall); - set_audio_sample_batch_callback(fakecalls); core.unserialize(base_state, max_state_size); + set_video_refresh_callback(fakeVideoCall); + set_audio_sample_callback(fakeAudioCall); + set_audio_sample_batch_callback(fakeBatchCall); core.run(); set_video_refresh_callback(video_refresh_callback); set_audio_sample_callback(audio_sample_callback); From 29a17de3c921599880ec51e058acb6f815c39d5c Mon Sep 17 00:00:00 2001 From: ro8inmorgan Date: Tue, 1 Jul 2025 10:44:12 +0200 Subject: [PATCH 4/8] comment a TODO --- workspace/all/minarch/minarch.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/workspace/all/minarch/minarch.c b/workspace/all/minarch/minarch.c index 56acb2b3f..95ed07155 100644 --- a/workspace/all/minarch/minarch.c +++ b/workspace/all/minarch/minarch.c @@ -6907,6 +6907,11 @@ int main(int argc , char* argv[]) { GFX_startFrame(); if(doRunAhead) { + // TODO + // this is runahead basically flipping an x avanced frames in future so it feels like it reacts more responsive + // currently it has performance issues + // serialize and unserialize are too cpu intensive it seems and cause slow downs, need to move it to seperate thread somehow + core.serialize(base_state, max_state_size); // Run ahead one or more frames set_video_refresh_callback(fakeVideoCall); From 5035f4f991e96d4d66fb49fe18747baab4f2fe28 Mon Sep 17 00:00:00 2001 From: ro8inmorgan Date: Tue, 1 Jul 2025 10:48:03 +0200 Subject: [PATCH 5/8] ff --- workspace/all/minarch/minarch.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/workspace/all/minarch/minarch.c b/workspace/all/minarch/minarch.c index 95ed07155..4b0becd15 100644 --- a/workspace/all/minarch/minarch.c +++ b/workspace/all/minarch/minarch.c @@ -6905,13 +6905,13 @@ int main(int argc , char* argv[]) { while (!quit) { GFX_startFrame(); - if(doRunAhead) { - - // TODO - // this is runahead basically flipping an x avanced frames in future so it feels like it reacts more responsive - // currently it has performance issues - // serialize and unserialize are too cpu intensive it seems and cause slow downs, need to move it to seperate thread somehow + // TODO + // this is runahead basically flipping an x avanced frames in future so it feels like it reacts more responsive + // currently it has performance issues + // serialize and unserialize are too cpu intensive it seems and cause slow downs, need to move it to seperate thread somehow + + if(doRunAhead == 1) { core.serialize(base_state, max_state_size); // Run ahead one or more frames set_video_refresh_callback(fakeVideoCall); @@ -6924,8 +6924,10 @@ int main(int argc , char* argv[]) { set_audio_sample_callback(audio_sample_callback); set_audio_sample_batch_callback(audio_sample_batch_callback); core.run(); - - // restore base state and make it run 1 frame to continue + doRunAhead = 2; + + } else if(doRunAhead == 2) { + // restore base state and make it run 1 frame like normally core.unserialize(base_state, max_state_size); set_video_refresh_callback(fakeVideoCall); set_audio_sample_callback(fakeAudioCall); @@ -6934,8 +6936,10 @@ int main(int argc , char* argv[]) { set_video_refresh_callback(video_refresh_callback); set_audio_sample_callback(audio_sample_callback); set_audio_sample_batch_callback(audio_sample_batch_callback); + core.run(); doRunAhead = 0; - } else { + } + else { core.run(); } From e690f758eb2c3508a74fef70ad9990b3b0c21312 Mon Sep 17 00:00:00 2001 From: ro8inmorgan Date: Tue, 1 Jul 2025 11:34:32 +0200 Subject: [PATCH 6/8] ff --- workspace/all/minarch/minarch.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/workspace/all/minarch/minarch.c b/workspace/all/minarch/minarch.c index 4b0becd15..df877c922 100644 --- a/workspace/all/minarch/minarch.c +++ b/workspace/all/minarch/minarch.c @@ -6910,7 +6910,7 @@ int main(int argc , char* argv[]) { // this is runahead basically flipping an x avanced frames in future so it feels like it reacts more responsive // currently it has performance issues // serialize and unserialize are too cpu intensive it seems and cause slow downs, need to move it to seperate thread somehow - + static int aheadcount = 0; if(doRunAhead == 1) { core.serialize(base_state, max_state_size); // Run ahead one or more frames @@ -6919,16 +6919,18 @@ int main(int argc , char* argv[]) { set_audio_sample_batch_callback(fakeBatchCall); for (int i = 0; i < 1; i++) { core.run(); + aheadcount++; } set_video_refresh_callback(video_refresh_callback); set_audio_sample_callback(audio_sample_callback); set_audio_sample_batch_callback(audio_sample_batch_callback); core.run(); + aheadcount++; doRunAhead = 2; - - } else if(doRunAhead == 2) { // restore base state and make it run 1 frame like normally core.unserialize(base_state, max_state_size); + } else if(doRunAhead == 2) { + // first catchup set_video_refresh_callback(fakeVideoCall); set_audio_sample_callback(fakeAudioCall); set_audio_sample_batch_callback(fakeBatchCall); @@ -6936,8 +6938,10 @@ int main(int argc , char* argv[]) { set_video_refresh_callback(video_refresh_callback); set_audio_sample_callback(audio_sample_callback); set_audio_sample_batch_callback(audio_sample_batch_callback); - core.run(); - doRunAhead = 0; + if(aheadcount<1) + doRunAhead = 0; + else + aheadcount--; } else { core.run(); From 9cf5065da5aba3c1b3191fac7be3299b12ef9e4c Mon Sep 17 00:00:00 2001 From: ro8inmorgan Date: Tue, 1 Jul 2025 23:15:16 +0200 Subject: [PATCH 7/8] Ok run ahead is correctly implemented, but def need to offload this to a seperate thread --- workspace/all/minarch/minarch.c | 103 ++++++++++++++++---------------- 1 file changed, 52 insertions(+), 51 deletions(-) diff --git a/workspace/all/minarch/minarch.c b/workspace/all/minarch/minarch.c index df877c922..434b3005a 100644 --- a/workspace/all/minarch/minarch.c +++ b/workspace/all/minarch/minarch.c @@ -2995,6 +2995,7 @@ static int setFastForward(int enable) { static uint32_t buttons = 0; // RETRO_DEVICE_ID_JOYPAD_* buttons static int ignore_menu = 0; +static int runahead_buttons = 0; static void input_poll_callback(void) { PAD_poll(); @@ -3119,9 +3120,7 @@ static void input_poll_callback(void) { buttons |= 1 << mapping->retro; if (mapping->mod) ignore_menu = 1; } - if(PAD_anyJustPressed()) { - doRunAhead = 1; - } + runahead_buttons = buttons; // && !PWR_ignoreSettingInput(btn, show_setting) } @@ -3212,6 +3211,7 @@ static bool set_rumble_state(unsigned port, enum retro_rumble_effect effect, uin VIB_setStrength(strength); return 1; } + static bool environment_callback(unsigned cmd, void *data) { // copied from picoarch initially // LOG_info("environment_callback: %i\n", cmd); @@ -4627,9 +4627,13 @@ void Core_getName(char* in_name, char* out_name) { tmp[0] = '\0'; } - void (*set_video_refresh_callback)(retro_video_refresh_t); - void (*set_audio_sample_callback)(retro_audio_sample_t); - void (*set_audio_sample_batch_callback)(retro_audio_sample_batch_t); +void (*set_video_refresh_callback)(retro_video_refresh_t); +void (*set_audio_sample_callback)(retro_audio_sample_t); +void (*set_audio_sample_batch_callback)(retro_audio_sample_batch_t); +void (*set_input_state_callback)(retro_input_state_t); + + + void Core_open(const char* core_path, const char* tag_name) { LOG_info("Core_open\n"); @@ -4659,7 +4663,7 @@ void Core_open(const char* core_path, const char* tag_name) { void (*set_environment_callback)(retro_environment_t); void (*set_input_poll_callback)(retro_input_poll_t); - void (*set_input_state_callback)(retro_input_state_t); + set_environment_callback = dlsym(core.handle, "retro_set_environment"); set_video_refresh_callback = dlsym(core.handle, "retro_set_video_refresh"); @@ -4667,7 +4671,9 @@ void Core_open(const char* core_path, const char* tag_name) { set_audio_sample_batch_callback = dlsym(core.handle, "retro_set_audio_sample_batch"); set_input_poll_callback = dlsym(core.handle, "retro_set_input_poll"); set_input_state_callback = dlsym(core.handle, "retro_set_input_state"); - + + + struct retro_system_info info = {}; core.get_system_info(&info); @@ -4700,6 +4706,7 @@ void Core_open(const char* core_path, const char* tag_name) { set_audio_sample_batch_callback(audio_sample_batch_callback); set_input_poll_callback(input_poll_callback); set_input_state_callback(input_state_callback); + } void Core_init(void) { LOG_info("Core_init\n"); @@ -6788,9 +6795,19 @@ static void limitFF(void) { } +int16_t dummy_buffer[4096]; + static void fakeAudioCall(int16_t left, int16_t right) {} -static size_t fakeBatchCall(const int16_t *data, size_t frames) {} +size_t fakeBatchCall(const int16_t *data, size_t frames) { + memcpy(dummy_buffer, data, frames * sizeof(int16_t) * 2); // Stereo + return frames; +} static void fakeVideoCall(const void *data, unsigned width, unsigned height, size_t pitch) {} +static int16_t fake_input_callback(unsigned port, unsigned device, unsigned index, unsigned id) { + int bit = 1 << id; + return (runahead_buttons & bit) ? 1 : 0; +} + int main(int argc , char* argv[]) { LOG_info("MinArch\n"); @@ -6901,55 +6918,39 @@ int main(int argc , char* argv[]) { max_state_size = core.serialize_size(); base_state = malloc(max_state_size); - runahead_state = malloc(max_state_size); - + int runAheadFrames = 1; // How many frames to run ahead while (!quit) { GFX_startFrame(); - // TODO - // this is runahead basically flipping an x avanced frames in future so it feels like it reacts more responsive - // currently it has performance issues - // serialize and unserialize are too cpu intensive it seems and cause slow downs, need to move it to seperate thread somehow - static int aheadcount = 0; - if(doRunAhead == 1) { - core.serialize(base_state, max_state_size); - // Run ahead one or more frames - set_video_refresh_callback(fakeVideoCall); - set_audio_sample_callback(fakeAudioCall); - set_audio_sample_batch_callback(fakeBatchCall); - for (int i = 0; i < 1; i++) { - core.run(); - aheadcount++; - } - set_video_refresh_callback(video_refresh_callback); - set_audio_sample_callback(audio_sample_callback); - set_audio_sample_batch_callback(audio_sample_batch_callback); - core.run(); - aheadcount++; - doRunAhead = 2; - // restore base state and make it run 1 frame like normally - core.unserialize(base_state, max_state_size); - } else if(doRunAhead == 2) { - // first catchup - set_video_refresh_callback(fakeVideoCall); - set_audio_sample_callback(fakeAudioCall); - set_audio_sample_batch_callback(fakeBatchCall); - core.run(); - set_video_refresh_callback(video_refresh_callback); - set_audio_sample_callback(audio_sample_callback); - set_audio_sample_batch_callback(audio_sample_batch_callback); - if(aheadcount<1) - doRunAhead = 0; - else - aheadcount--; - } - else { + retro_video_refresh_t saved_video = video_refresh_callback; + retro_audio_sample_t saved_audio = audio_sample_callback; + retro_audio_sample_batch_t saved_batch = audio_sample_batch_callback; + retro_input_state_t saved_input = input_state_callback; + + core.serialize(base_state, max_state_size); + + set_video_refresh_callback(fakeVideoCall); + set_audio_sample_callback(fakeAudioCall); + set_audio_sample_batch_callback(fakeBatchCall); + set_input_state_callback(fake_input_callback); + + core.unserialize(base_state, max_state_size); + + for (int i = 0; i < runAheadFrames; i++) { core.run(); } - + + set_video_refresh_callback(saved_video); + set_audio_sample_callback(saved_audio); + set_audio_sample_batch_callback(saved_batch); + set_input_state_callback(saved_input); + + core.unserialize(base_state, max_state_size); + core.run(); // Visible frame, synced to real time via your FPS limiter + limitFF(); trackFPS(); - + if (has_pending_opt_change) { has_pending_opt_change = 0; From 5fc2e2f1c1126fd45c909e7836f1e9ba036bd3c0 Mon Sep 17 00:00:00 2001 From: ro8inmorgan Date: Tue, 1 Jul 2025 23:33:11 +0200 Subject: [PATCH 8/8] works a bit better like this, but just need to move it threaded later --- workspace/all/minarch/minarch.c | 43 ++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/workspace/all/minarch/minarch.c b/workspace/all/minarch/minarch.c index 434b3005a..2314291f0 100644 --- a/workspace/all/minarch/minarch.c +++ b/workspace/all/minarch/minarch.c @@ -3120,6 +3120,9 @@ static void input_poll_callback(void) { buttons |= 1 << mapping->retro; if (mapping->mod) ignore_menu = 1; } + if(PAD_anyJustPressed()) { + doRunAhead = 1; + } runahead_buttons = buttons; // && !PWR_ignoreSettingInput(btn, show_setting) } @@ -6921,31 +6924,33 @@ int main(int argc , char* argv[]) { int runAheadFrames = 1; // How many frames to run ahead while (!quit) { GFX_startFrame(); + if(doRunAhead) { + retro_video_refresh_t saved_video = video_refresh_callback; + retro_audio_sample_t saved_audio = audio_sample_callback; + retro_audio_sample_batch_t saved_batch = audio_sample_batch_callback; + retro_input_state_t saved_input = input_state_callback; - retro_video_refresh_t saved_video = video_refresh_callback; - retro_audio_sample_t saved_audio = audio_sample_callback; - retro_audio_sample_batch_t saved_batch = audio_sample_batch_callback; - retro_input_state_t saved_input = input_state_callback; - - core.serialize(base_state, max_state_size); + core.serialize(base_state, max_state_size); - set_video_refresh_callback(fakeVideoCall); - set_audio_sample_callback(fakeAudioCall); - set_audio_sample_batch_callback(fakeBatchCall); - set_input_state_callback(fake_input_callback); + set_video_refresh_callback(fakeVideoCall); + set_audio_sample_callback(fakeAudioCall); + set_audio_sample_batch_callback(fakeBatchCall); + set_input_state_callback(fake_input_callback); - core.unserialize(base_state, max_state_size); + core.unserialize(base_state, max_state_size); - for (int i = 0; i < runAheadFrames; i++) { - core.run(); - } + for (int i = 0; i < runAheadFrames; i++) { + core.run(); + } - set_video_refresh_callback(saved_video); - set_audio_sample_callback(saved_audio); - set_audio_sample_batch_callback(saved_batch); - set_input_state_callback(saved_input); + set_video_refresh_callback(saved_video); + set_audio_sample_callback(saved_audio); + set_audio_sample_batch_callback(saved_batch); + set_input_state_callback(saved_input); - core.unserialize(base_state, max_state_size); + core.unserialize(base_state, max_state_size); + doRunAhead = 0; + } core.run(); // Visible frame, synced to real time via your FPS limiter limitFF();