mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-07 08:36:02 -10:00
runloop: tick RUN_FRAMES only after advancing the core
This commit is contained in:
1 parent
92d69e6025
commit
2c6b61e9fb
10 files changed
+322
-25
No files matched your search
@@ -22,6 +22,7 @@ on:
|
||||
- 'playlist.h'
|
||||
- 'tasks/task_content*'
|
||||
- 'samples/menu/playlist_nav/**'
|
||||
- 'samples/network/command_frames/**'
|
||||
- 'samples/menu/nav_repeat_stall/**'
|
||||
- 'samples/menu/mouse_click/**'
|
||||
- 'samples/menu/menu_icon_ownership/**'
|
||||
@@ -44,6 +45,7 @@ on:
|
||||
- 'playlist.h'
|
||||
- 'tasks/task_content*'
|
||||
- 'samples/menu/playlist_nav/**'
|
||||
- 'samples/network/command_frames/**'
|
||||
- 'samples/menu/nav_repeat_stall/**'
|
||||
- 'samples/menu/mouse_click/**'
|
||||
- 'samples/menu/menu_icon_ownership/**'
|
||||
@@ -438,6 +440,13 @@ jobs:
|
||||
./samples/menu/explore_build/explore_build_test
|
||||
make -C samples/menu/explore_build clean
|
||||
|
||||
- name: Frame command progress through the real runloop
|
||||
shell: bash
|
||||
run: |
|
||||
set -eu
|
||||
make -C samples/network/command_frames check
|
||||
make -C samples/network/command_frames clean
|
||||
|
||||
- name: Save ccache
|
||||
# Only master's pushes keep a cache: pull requests restore
|
||||
# master's, so one push does not evict the next one's.
|
||||
|
||||
@@ -546,8 +546,7 @@ bool command_set_option(command_t *cmd, const char *arg);
|
||||
bool command_get_log(command_t *cmd, const char *arg);
|
||||
#endif
|
||||
|
||||
/* RUN_FRAMES <count>; the run loop calls tick after each frame it runs
|
||||
* and cancel when the content goes */
|
||||
/* RUN_FRAMES <count>; tick after a frontend advance, cancel on close. */
|
||||
#define COMMAND_RUN_FRAMES_MAX 36000
|
||||
bool command_run_frames(command_t *cmd, const char *arg);
|
||||
void command_run_frames_tick(void);
|
||||
@@ -745,7 +744,7 @@ static const struct cmd_action_map action_map[] = {
|
||||
#ifdef HAVE_MCP
|
||||
{ "GET_LOG", command_get_log, "[since]", "Report the lines RetroArch has logged since the given line, then NEXT and the line to ask from next time.", CMD_INFO_READ_ONLY, cmd_params_log },
|
||||
#endif
|
||||
{ "RUN_FRAMES", command_run_frames, "<count>", "Pause and run the content the given number of frames, answered once the last has run; it stays paused.", 0, cmd_params_run_frames },
|
||||
{ "RUN_FRAMES", command_run_frames, "<count>", "Pause and advance the content by the given number of frontend frames, excluding speculative replay calls; finish paused.", 0, cmd_params_run_frames },
|
||||
{ "SET_INPUT", command_set_input, "<player> <control> <value>", "Hold a RetroPad button or stick axis for the core, over the player's own input, until set again or released. Answers with what the player holds: buttons and axes, tab separated.", 0, cmd_params_input },
|
||||
{ "RELEASE_INPUT", command_release_input, "[player]", "Let go of everything SET_INPUT holds, for one player or all.", 0, cmd_params_input_release },
|
||||
{ "GET_CORE_OPTION", command_get_core_option, "[key]", "List the running core's options: key, value and description, tab separated; with a key, that option with the values it takes.", CMD_INFO_READ_ONLY, cmd_params_core_option },
|
||||
|
||||
@@ -35,8 +35,8 @@ bool core_unset_netplay_callbacks(void);
|
||||
|
||||
bool core_set_poll_type(unsigned type);
|
||||
|
||||
/* Runs the core for one frame. */
|
||||
void core_run(void);
|
||||
/* Runs the core for one frame; false when it did not run. */
|
||||
bool core_run(void);
|
||||
|
||||
void core_reset(void);
|
||||
|
||||
|
||||
+20
-11
@@ -1749,7 +1749,7 @@ static void runahead_core_run_use_last_input(runloop_state_t *runloop_st)
|
||||
runloop_st->current_core.retro_set_input_state(cbs->state_cb);
|
||||
}
|
||||
|
||||
void runahead_run(void *data,
|
||||
bool runahead_run(void *data,
|
||||
int runahead_count,
|
||||
bool runahead_hide_warnings,
|
||||
bool use_secondary)
|
||||
@@ -1758,6 +1758,7 @@ void runahead_run(void *data,
|
||||
int frame_number = 0;
|
||||
bool last_frame = false;
|
||||
bool suspended_frame = false;
|
||||
bool advanced = false;
|
||||
#if defined(HAVE_DYNAMIC)
|
||||
const bool have_dynamic = true;
|
||||
settings_t *settings = config_get_ptr();
|
||||
@@ -1865,7 +1866,7 @@ void runahead_run(void *data,
|
||||
}
|
||||
|
||||
if (frame_number == 0)
|
||||
core_run();
|
||||
advanced = core_run();
|
||||
else
|
||||
runahead_core_run_use_last_input(runloop_st);
|
||||
|
||||
@@ -1879,6 +1880,9 @@ void runahead_run(void *data,
|
||||
AUDIO_FLAGS_CLEAR(audio_st, AUDIO_FLAG_SUSPENDED);
|
||||
}
|
||||
|
||||
if (!advanced)
|
||||
return false;
|
||||
|
||||
if (frame_number == 0)
|
||||
{
|
||||
if (!runahead_save_state(runloop_st))
|
||||
@@ -1888,7 +1892,7 @@ void runahead_run(void *data,
|
||||
runloop_msg_queue_push(_msg, strlen(_msg), 0, 3 * 60, true, NULL,
|
||||
MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
|
||||
RARCH_WARN("[Run-Ahead] %s\n", _msg);
|
||||
return;
|
||||
return advanced;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1900,7 +1904,7 @@ void runahead_run(void *data,
|
||||
runloop_msg_queue_push(_msg, strlen(_msg), 0, 3 * 60, true, NULL,
|
||||
MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
|
||||
RARCH_WARN("[Run-Ahead] %s\n", _msg);
|
||||
return;
|
||||
return advanced;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1912,12 +1916,15 @@ void runahead_run(void *data,
|
||||
|
||||
/* run main core with video suspended */
|
||||
video_st->main_flags &= ~VIDEO_FLAG_ACTIVE;
|
||||
core_run();
|
||||
advanced = core_run();
|
||||
if ((uint32_t)retro_atomic_load_relaxed_int(&video_st->flags) & VIDEO_FLAG_RUNAHEAD_IS_ACTIVE)
|
||||
video_st->main_flags |= VIDEO_FLAG_ACTIVE;
|
||||
else
|
||||
video_st->main_flags &= ~VIDEO_FLAG_ACTIVE;
|
||||
|
||||
if (!advanced)
|
||||
return false;
|
||||
|
||||
if ( (runloop_st->flags & RUNLOOP_FLAG_INPUT_IS_DIRTY)
|
||||
|| (runloop_st->flags & RUNLOOP_FLAG_RUNAHEAD_FORCE_INPUT_DIRTY))
|
||||
{
|
||||
@@ -1929,7 +1936,7 @@ void runahead_run(void *data,
|
||||
runloop_msg_queue_push(_msg, strlen(_msg), 0, 3 * 60, true, NULL,
|
||||
MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
|
||||
RARCH_WARN("[Run-Ahead] %s\n", _msg);
|
||||
return;
|
||||
return advanced;
|
||||
}
|
||||
|
||||
if (!runahead_load_state_secondary(runloop_st, settings))
|
||||
@@ -1938,7 +1945,7 @@ void runahead_run(void *data,
|
||||
runloop_msg_queue_push(_msg, strlen(_msg), 0, 3 * 60, true, NULL,
|
||||
MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
|
||||
RARCH_WARN("[Run-Ahead] %s\n", _msg);
|
||||
return;
|
||||
return advanced;
|
||||
}
|
||||
|
||||
for (frame_number = 0; frame_number < runahead_count - 1; frame_number++)
|
||||
@@ -1965,11 +1972,12 @@ void runahead_run(void *data,
|
||||
#endif
|
||||
}
|
||||
runloop_st->flags &= ~RUNLOOP_FLAG_RUNAHEAD_FORCE_INPUT_DIRTY;
|
||||
return;
|
||||
return advanced;
|
||||
|
||||
force_input_dirty:
|
||||
core_run();
|
||||
advanced = core_run();
|
||||
runloop_st->flags |= RUNLOOP_FLAG_RUNAHEAD_FORCE_INPUT_DIRTY;
|
||||
return advanced;
|
||||
}
|
||||
|
||||
/* Preemptive Frames */
|
||||
@@ -2303,7 +2311,7 @@ static INLINE void preempt_input_poll(preempt_t *preempt,
|
||||
*
|
||||
* Call in place of core_run() for preemptive frames.
|
||||
**/
|
||||
void preempt_run(preempt_t *preempt, void *data)
|
||||
bool preempt_run(preempt_t *preempt, void *data)
|
||||
{
|
||||
runloop_state_t *runloop_st = (runloop_state_t*)data;
|
||||
struct retro_core_t *current_core = &runloop_st->current_core;
|
||||
@@ -2374,7 +2382,7 @@ void preempt_run(preempt_t *preempt, void *data)
|
||||
runloop_st->auto_state_load_core_ran = true;
|
||||
audio_driver_frame_end();
|
||||
preempt->frame_count++;
|
||||
return;
|
||||
return true;
|
||||
|
||||
error:
|
||||
runloop_st->flags &= ~(RUNLOOP_FLAG_REQUEST_SPECIAL_SAVESTATE
|
||||
@@ -2387,6 +2395,7 @@ error:
|
||||
runloop_msg_queue_push(_msg, strlen(_msg), 0, 2 * 60, true, NULL,
|
||||
MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
|
||||
RARCH_ERR("[Run-Ahead Preemptive] %s\n", _msg);
|
||||
return false;
|
||||
}
|
||||
|
||||
void runahead_clear_variables(void *data)
|
||||
|
||||
+4
-2
@@ -70,7 +70,8 @@ RETRO_BEGIN_DECLS
|
||||
|
||||
typedef bool(*runahead_load_state_function)(const void*, size_t);
|
||||
|
||||
void runahead_run(
|
||||
/* Whether the primary core advanced. */
|
||||
bool runahead_run(
|
||||
void *data,
|
||||
int runahead_count,
|
||||
bool runahead_hide_warnings,
|
||||
@@ -91,7 +92,8 @@ void runahead_secondary_core_destroy(void *data);
|
||||
bool preempt_init(void *data);
|
||||
void preempt_deinit(void *data);
|
||||
|
||||
void preempt_run(preempt_t *preempt, void *data);
|
||||
/* Whether the normal frame ran, excluding replay. */
|
||||
bool preempt_run(preempt_t *preempt, void *data);
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
|
||||
@@ -9439,6 +9439,7 @@ int runloop_iterate(void)
|
||||
#endif
|
||||
float slowmotion_ratio = settings->floats.slowmotion_ratio;
|
||||
bool audio_sync = settings->bools.audio_sync;
|
||||
bool frame_advanced;
|
||||
bool savestate_automatic_enable = settings->uints.savestate_automatic_interval > 0;
|
||||
|
||||
runloop_msg_queue_drain_deferred();
|
||||
@@ -9803,23 +9804,25 @@ int runloop_iterate(void)
|
||||
#endif
|
||||
|
||||
if (want_runahead)
|
||||
runahead_run(
|
||||
frame_advanced = runahead_run(
|
||||
runloop_st,
|
||||
run_ahead_num_frames,
|
||||
run_ahead_hide_warnings,
|
||||
run_ahead_secondary_instance);
|
||||
else if (runloop_st->preempt_data)
|
||||
preempt_run(runloop_st->preempt_data, runloop_st);
|
||||
frame_advanced = preempt_run(runloop_st->preempt_data, runloop_st);
|
||||
else
|
||||
#endif
|
||||
core_run();
|
||||
frame_advanced = core_run();
|
||||
}
|
||||
|
||||
runloop_load_deferred_auto_state();
|
||||
|
||||
#ifdef HAVE_COMMAND
|
||||
if (runloop_st->frame_work & RUNLOOP_WORK_RUN_FRAMES)
|
||||
if (frame_advanced && (runloop_st->frame_work & RUNLOOP_WORK_RUN_FRAMES))
|
||||
command_run_frames_tick();
|
||||
#else
|
||||
(void)frame_advanced;
|
||||
#endif
|
||||
|
||||
/* Increment runtime tick counter after each call to
|
||||
@@ -10706,7 +10709,7 @@ static void runloop_load_deferred_auto_state(void)
|
||||
runloop_st->auto_state_load_core_ran = false;
|
||||
}
|
||||
|
||||
void core_run(void)
|
||||
bool core_run(void)
|
||||
{
|
||||
runloop_state_t *runloop_st = &runloop_state;
|
||||
struct retro_core_t *
|
||||
@@ -10718,6 +10721,7 @@ void core_run(void)
|
||||
: (enum poll_type)(current_core->poll_type);
|
||||
bool early_polling = (new_poll_type == POLL_TYPE_EARLY);
|
||||
bool late_polling = (new_poll_type == POLL_TYPE_LATE);
|
||||
bool ran = false;
|
||||
#ifdef HAVE_NETWORKING
|
||||
/* Declared with the other locals and assigned below, so the
|
||||
* closing guard can return before netplay's pre-frame call
|
||||
@@ -10742,7 +10746,7 @@ void core_run(void)
|
||||
{
|
||||
input_driver_poll_between_frames(input_driver_poll);
|
||||
video_driver_cached_frame();
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef HAVE_NETWORKING
|
||||
@@ -10755,7 +10759,7 @@ void core_run(void)
|
||||
* netplay peer pausing doesn't just hang. */
|
||||
input_driver_poll_between_frames(input_driver_poll);
|
||||
video_driver_cached_frame();
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -10779,6 +10783,7 @@ void core_run(void)
|
||||
* all, reach the frame's own audio rather than the next frame's. */
|
||||
audio_driver_publish_runloop();
|
||||
current_core->retro_run();
|
||||
ran = true;
|
||||
if (runloop_st->auto_state_load_pending)
|
||||
runloop_st->auto_state_load_core_ran = true;
|
||||
audio_driver_frame_end();
|
||||
@@ -10818,6 +10823,7 @@ void core_run(void)
|
||||
#ifdef HAVE_NETWORKING
|
||||
netplay_driver_ctl(RARCH_NETPLAY_CTL_POST_FRAME, NULL);
|
||||
#endif
|
||||
return ran;
|
||||
}
|
||||
|
||||
bool core_has_set_input_descriptor(void)
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
MAKE_ARGS ?=
|
||||
EXE ?=
|
||||
|
||||
all:
|
||||
@MAKE_ARGS="$(MAKE_ARGS)" ./build.sh
|
||||
|
||||
check: all
|
||||
$(RUNNER) ./command_frames_test$(EXE) ./harness_core.so
|
||||
|
||||
clean:
|
||||
rm -f command_frames_test command_frames_test.exe harness_core.so
|
||||
rm -f retroarch_nomain.o retroarch_nomain.d harness_main.o harness_main.d
|
||||
|
||||
.PHONY: all check clean
|
||||
Executable
+57
@@ -0,0 +1,57 @@
|
||||
#!/bin/sh
|
||||
# Links the shipping frontend objects with a test main and netplay gate.
|
||||
# Requires a built non-Qt tree; MAKE_ARGS selects its build flags.
|
||||
set -eu
|
||||
|
||||
here=$(cd "$(dirname "$0")" && pwd)
|
||||
root=$(cd "$here/../../.." && pwd)
|
||||
out=samples/network/command_frames
|
||||
MAKE_ARGS=${MAKE_ARGS:-}
|
||||
|
||||
cd "$root"
|
||||
|
||||
cc_line=$(mktemp)
|
||||
ld_line=$(mktemp)
|
||||
trap 'rm -f "$cc_line" "$ld_line"' EXIT
|
||||
|
||||
# The project's own compile line for retroarch.o, which also tells us
|
||||
# which object directory this configuration builds into.
|
||||
touch retroarch.c
|
||||
make -n $MAKE_ARGS 2>/dev/null | grep -E '\-o obj-unix/[^/ ]+/retroarch\.o' | head -1 > "$cc_line"
|
||||
if [ ! -s "$cc_line" ]; then
|
||||
echo "could not determine the compile command for retroarch.o" >&2
|
||||
exit 1
|
||||
fi
|
||||
objdir=$(grep -oE 'obj-unix/[^/ ]+/retroarch\.o' "$cc_line" | head -1 | sed 's#/retroarch\.o##')
|
||||
if [ ! -f "$objdir/video_driver.o" ] && [ ! -f "$objdir/gfx/video_driver.o" ]; then
|
||||
echo "build RetroArch first: ./configure --disable-qt && make $MAKE_ARGS" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
sed "s#-o $objdir/retroarch\.o#-Dmain=rarch_harness_unused_main -o $out/retroarch_nomain.o#" \
|
||||
"$cc_line" | sh
|
||||
|
||||
sed -e "s#-o $objdir/retroarch\.o#-o $out/harness_main.o#" \
|
||||
-e "s# retroarch\.c# $out/command_frames_test.c#" \
|
||||
"$cc_line" | sh
|
||||
|
||||
rm -f retroarch retroarch_debug retroarch.exe retroarch_debug.exe
|
||||
make -n $MAKE_ARGS 2>/dev/null | grep -E ' -o retroarch(_debug)?(\.exe)? ' | tail -1 > "$ld_line"
|
||||
if [ ! -s "$ld_line" ]; then
|
||||
echo "could not determine the link command" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
sed -e "s#$objdir/retroarch\.o#$out/retroarch_nomain.o $out/harness_main.o#" \
|
||||
-e "s#-o retroarch\(_debug\)\?\(\.exe\)\? #-Wl,--wrap=netplay_driver_ctl -o $out/command_frames_test\2 #" \
|
||||
"$ld_line" | sh
|
||||
|
||||
# The harness core: a plain shared library, no sanitizer, so that what
|
||||
# the sanitizer reports is the frontend. Built with the compiler the
|
||||
# tree's own compile line names, so a cross build (mingw) gets a core
|
||||
# the frontend can load; the .so name is only a name, every loader
|
||||
# takes it.
|
||||
core_cc=$(awk '{print $1}' "$cc_line")
|
||||
$core_cc -O1 -g -shared -fPIC -Ilibretro-common/include -o $out/harness_core.so $out/harness_core.c
|
||||
|
||||
echo "built $out/command_frames_test and $out/harness_core.so"
|
||||
@@ -0,0 +1,148 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <file/file_path.h>
|
||||
#include <file/config_file.h>
|
||||
#include <time/rtime.h>
|
||||
#include "../../../runloop.h"
|
||||
#include "../../../retroarch.h"
|
||||
#include "../../../configuration.h"
|
||||
#include "../../../command.h"
|
||||
#include "../../../frontend/frontend_driver.h"
|
||||
#include "../../../frontend/frontend.h"
|
||||
#include "../../../runahead.h"
|
||||
#include "../../../network/netplay/netplay.h"
|
||||
#include "../../../menu/menu_driver.h"
|
||||
static unsigned failures;
|
||||
#define CHECK(c) do { if (!(c)) { fprintf(stderr, "FAIL %u: %s\n", (unsigned)__LINE__, #c); failures++; } } while (0)
|
||||
static bool blocked;
|
||||
static unsigned calls, gates;
|
||||
static void (*loaded_run)(void);
|
||||
static char reply[256];
|
||||
static void counted_run(void) { calls++; loaded_run(); }
|
||||
static bool fail_save(void *data, size_t size) { (void)data; (void)size; return false; }
|
||||
bool __real_netplay_driver_ctl(enum rarch_netplay_ctl_state, void*);
|
||||
bool __wrap_netplay_driver_ctl(enum rarch_netplay_ctl_state state, void *data)
|
||||
{
|
||||
if (state == RARCH_NETPLAY_CTL_PRE_FRAME && blocked)
|
||||
{ gates++; return false; }
|
||||
return __real_netplay_driver_ctl(state, data);
|
||||
}
|
||||
static void receive(command_t *cmd, const char *data, size_t n)
|
||||
{
|
||||
(void)cmd;
|
||||
if (n >= sizeof(reply)) n = sizeof(reply)-1;
|
||||
memcpy(reply,data,n); reply[n]=0;
|
||||
}
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
char cfg_path[512];
|
||||
char *args[7];
|
||||
FILE *cfg;
|
||||
unsigned i, before;
|
||||
command_t cmd;
|
||||
runloop_state_t *loop;
|
||||
if (argc < 2) return 2;
|
||||
snprintf(cfg_path,sizeof(cfg_path),"%s.cfg",argv[0]);
|
||||
cfg=fopen(cfg_path,"wb"); if (!cfg) return 2;
|
||||
fputs("video_driver = \"null\"\naudio_driver = \"null\"\ninput_driver = \"null\"\ninput_joypad_driver = \"null\"\nmenu_driver = \"rgui\"\nvideo_threaded = \"false\"\nvideo_vsync = \"false\"\npause_nonactive = \"false\"\nconfig_save_on_exit = \"false\"\nthreaded_data_runloop_enable = \"false\"\n",cfg);
|
||||
fclose(cfg);
|
||||
config_file_set_io_default(config_file_io_filestream());
|
||||
rtime_init(); retroarch_config_init();
|
||||
retroarch_ctl(RARCH_CTL_STATE_FREE,NULL);
|
||||
frontend_driver_init_first(NULL);
|
||||
args[0]=(char*)"retroarch"; args[1]=(char*)"--config"; args[2]=cfg_path;
|
||||
args[3]=(char*)"-L"; args[4]=argv[1]; args[5]=NULL;
|
||||
if (!retroarch_main_init(5,args)) return 2;
|
||||
for (i=0;i<5;i++) runloop_iterate();
|
||||
loop=runloop_state_get_ptr();
|
||||
if (loop->current_core_type == CORE_TYPE_DUMMY) return 2;
|
||||
loaded_run=loop->current_core.retro_run;
|
||||
loop->current_core.retro_run=counted_run;
|
||||
memset(&cmd,0,sizeof(cmd)); cmd.replier=receive;
|
||||
before=calls; blocked=true;
|
||||
if (!command_run_frames(&cmd,"3")) return 2;
|
||||
for (i=0;i<3;i++) runloop_iterate();
|
||||
printf("blocked: core_calls=%u gate_denials=%u pending=%u reply=%s\n",calls-before,gates,
|
||||
!!(loop->frame_work & RUNLOOP_WORK_RUN_FRAMES),reply);
|
||||
CHECK(calls == before && gates == 3 && !reply[0]);
|
||||
CHECK(loop->frame_work & RUNLOOP_WORK_RUN_FRAMES);
|
||||
blocked=false;
|
||||
before=calls; reply[0]=0;
|
||||
for (i=0;i<3;i++) runloop_iterate();
|
||||
printf("unblocked: core_calls=%u pending=%u reply=%s\n",calls-before,
|
||||
!!(loop->frame_work & RUNLOOP_WORK_RUN_FRAMES),reply);
|
||||
|
||||
CHECK(calls - before == 3 && !strcmp(reply, "RUN_FRAMES 3\n"));
|
||||
CHECK(!(loop->frame_work & RUNLOOP_WORK_RUN_FRAMES));
|
||||
before=calls;
|
||||
for (i=0;i<3;i++) runloop_iterate();
|
||||
CHECK(calls == before && (loop->flags & RUNLOOP_FLAG_PAUSED));
|
||||
{
|
||||
settings_t *settings=config_get_ptr();
|
||||
uint32_t *state=(uint32_t*)loop->current_core.retro_get_memory_data(RETRO_MEMORY_SYSTEM_RAM);
|
||||
unsigned base=*state;
|
||||
before=calls; reply[0]=0;
|
||||
runahead_clear_variables(loop);
|
||||
settings->bools.run_ahead_enabled=true;
|
||||
settings->bools.run_ahead_secondary_instance=false;
|
||||
settings->uints.run_ahead_frames=2;
|
||||
if (!command_run_frames(&cmd,"3")) return 2;
|
||||
for (i=0;i<3;i++) runloop_iterate();
|
||||
printf("runahead: core_calls=%u logical_frames=%u pending=%u reply=%s\n",calls-before,*state-base,
|
||||
!!(loop->frame_work & RUNLOOP_WORK_RUN_FRAMES),reply);
|
||||
CHECK(calls-before == 9 && *state-base == 3 && !strcmp(reply,"RUN_FRAMES 3\n"));
|
||||
settings->bools.run_ahead_enabled=false;
|
||||
settings->bools.preemptive_frames_enable=true;
|
||||
preempt_deinit(loop);
|
||||
if (!preempt_init(loop)) return 2;
|
||||
base=*state; before=calls; reply[0]=0;
|
||||
if (!command_run_frames(&cmd,"3")) return 2;
|
||||
for (i=0;i<3;i++) runloop_iterate();
|
||||
printf("preempt: core_calls=%u logical_frames=%u pending=%u reply=%s\n",calls-before,*state-base,
|
||||
!!(loop->frame_work & RUNLOOP_WORK_RUN_FRAMES),reply);
|
||||
|
||||
CHECK(calls-before == 3 && *state-base == 3 && !strcmp(reply,"RUN_FRAMES 3\n"));
|
||||
CHECK(command_set_input(&cmd,"1 b 1"));
|
||||
base=*state; before=calls; reply[0]=0;
|
||||
CHECK(command_run_frames(&cmd,"3"));
|
||||
for (i=0;i<3;i++) runloop_iterate();
|
||||
printf("preempt replay: core_calls=%u logical_frames=%u reply=%s\n",calls-before,*state-base,reply);
|
||||
CHECK(calls-before > 3 && *state-base == 3 && !strcmp(reply,"RUN_FRAMES 3\n"));
|
||||
{
|
||||
bool (*save)(void*,size_t)=loop->current_core.retro_serialize;
|
||||
loop->current_core.retro_serialize=fail_save;
|
||||
before=calls; reply[0]=0;
|
||||
CHECK(command_run_frames(&cmd,"1"));
|
||||
runloop_iterate();
|
||||
CHECK(calls == before && !reply[0] && (loop->frame_work & RUNLOOP_WORK_RUN_FRAMES));
|
||||
loop->current_core.retro_serialize=save;
|
||||
runloop_iterate();
|
||||
CHECK(calls-before == 1 && !strcmp(reply,"RUN_FRAMES 1\n"));
|
||||
}
|
||||
CHECK(command_release_input(&cmd,""));
|
||||
preempt_deinit(loop);
|
||||
}
|
||||
|
||||
before=calls; reply[0]=0;
|
||||
CHECK(command_run_frames(&cmd,"3"));
|
||||
runloop_iterate();
|
||||
blocked=true;
|
||||
for(i=0;i<3;i++) runloop_iterate();
|
||||
CHECK(calls-before == 1 && !reply[0] && (loop->frame_work & RUNLOOP_WORK_RUN_FRAMES));
|
||||
blocked=false;
|
||||
runloop_iterate(); runloop_iterate();
|
||||
CHECK(calls-before == 3 && !strcmp(reply,"RUN_FRAMES 3\n"));
|
||||
before=calls; reply[0]=0;
|
||||
CHECK(command_run_frames(&cmd,"1"));
|
||||
loop->current_core.retro_run=NULL;
|
||||
runloop_iterate();
|
||||
CHECK(calls==before && !reply[0] && (loop->frame_work & RUNLOOP_WORK_RUN_FRAMES));
|
||||
loop->current_core.retro_run=counted_run;
|
||||
runloop_iterate();
|
||||
CHECK(calls-before==1 && !strcmp(reply,"RUN_FRAMES 1\n"));
|
||||
main_exit(NULL); remove(cfg_path);
|
||||
printf("command_frames: %s (%u failures)\n",failures?"FAIL":"PASS",failures);
|
||||
return failures ? 1 : 0;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <libretro.h>
|
||||
static retro_video_refresh_t video;
|
||||
static retro_input_poll_t poll;
|
||||
static retro_input_state_t input;
|
||||
static uint32_t frame;
|
||||
static uint32_t pixels[64];
|
||||
void retro_set_environment(retro_environment_t cb)
|
||||
{
|
||||
bool no_game=true;
|
||||
enum retro_pixel_format fmt=RETRO_PIXEL_FORMAT_XRGB8888;
|
||||
cb(RETRO_ENVIRONMENT_SET_SUPPORT_NO_GAME,&no_game);
|
||||
cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT,&fmt);
|
||||
}
|
||||
void retro_set_video_refresh(retro_video_refresh_t cb) { video=cb; }
|
||||
void retro_set_audio_sample(retro_audio_sample_t cb) { (void)cb; }
|
||||
void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb) { (void)cb; }
|
||||
void retro_set_input_poll(retro_input_poll_t cb) { poll=cb; }
|
||||
void retro_set_input_state(retro_input_state_t cb) { input=cb; }
|
||||
void retro_init(void) { frame=0; }
|
||||
void retro_deinit(void) { }
|
||||
unsigned retro_api_version(void) { return RETRO_API_VERSION; }
|
||||
void retro_get_system_info(struct retro_system_info *i)
|
||||
{
|
||||
memset(i,0,sizeof(*i)); i->library_name="command_frames_harness";
|
||||
i->library_version="1"; i->valid_extensions="";
|
||||
}
|
||||
void retro_get_system_av_info(struct retro_system_av_info *i)
|
||||
{
|
||||
memset(i,0,sizeof(*i)); i->geometry.base_width=i->geometry.max_width=8;
|
||||
i->geometry.base_height=i->geometry.max_height=8;
|
||||
i->timing.fps=60.0; i->timing.sample_rate=48000.0;
|
||||
}
|
||||
void retro_set_controller_port_device(unsigned p,unsigned d) { (void)p; (void)d; }
|
||||
void retro_reset(void) { frame=0; }
|
||||
void retro_run(void)
|
||||
{
|
||||
poll(); input(0,RETRO_DEVICE_JOYPAD,0,RETRO_DEVICE_ID_JOYPAD_B);
|
||||
frame++; pixels[0]=frame; video(pixels,8,8,8*sizeof(*pixels));
|
||||
}
|
||||
size_t retro_serialize_size(void) { return sizeof(frame); }
|
||||
bool retro_serialize(void *p,size_t n) { if(n<sizeof(frame))return false; memcpy(p,&frame,sizeof(frame));return true; }
|
||||
bool retro_unserialize(const void *p,size_t n) { if(n<sizeof(frame))return false; memcpy(&frame,p,sizeof(frame));return true; }
|
||||
void retro_cheat_reset(void) { }
|
||||
void retro_cheat_set(unsigned i,bool b,const char *c) { (void)i;(void)b;(void)c; }
|
||||
bool retro_load_game(const struct retro_game_info *g) { (void)g;return true; }
|
||||
bool retro_load_game_special(unsigned t,const struct retro_game_info *g,size_t n) { (void)t;(void)g;(void)n;return false; }
|
||||
void retro_unload_game(void) { }
|
||||
unsigned retro_get_region(void) { return RETRO_REGION_NTSC; }
|
||||
void *retro_get_memory_data(unsigned id) { return id==RETRO_MEMORY_SYSTEM_RAM?&frame:NULL; }
|
||||
size_t retro_get_memory_size(unsigned id) { return id==RETRO_MEMORY_SYSTEM_RAM?sizeof(frame):0; }
|
||||
Reference in new issue
Block a user