mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-08 08:10:30 -10:00
menu: rgui's frame path reads the snapshot and writes no settings
rgui_frame() runs inside the video driver's frame(), which under threaded video is the video thread. Its frame closure held the driver's last two live-settings functions, and one of them wrote: rgui_set_video_config() stored five settings fields (the aspect index and the custom viewport) plus the global custom-aspect LUT entry, and the frame path's aspect-lock and resize handling called it from the video thread. The old delay_update flag already deferred CMD_EVENT_VIDEO_SET_ASPECT_RATIO to rgui_render() because firing it from the frame deadlocks; the settings writes now take the same road. - rgui_set_video_config() splits into rgui_stage_video_config() (copy the configuration onto the rgui handle and raise RGUI_FLAG_ASPECT_UPDATE_PENDING; latest staged wins) and rgui_set_video_config_now() (main thread: apply and fire). rgui_render()'s pending consumer applies the staged copy - on the thread that owns settings writes - before firing the command that consumes it. - rgui_set_aspect_ratio() is dual-context (render, init, populate and toggle on the main thread; the frame path's aspect and resize checks on the video thread), so its two settings arrive as arguments from each caller's own context, and it always stages: the immediate-mode callers that need the configuration in place before proceeding (video init, menu toggle, render's framebuffer resize) call the new rgui_flush_video_config() on return, so the apply side stays structurally outside the frame closure rather than dead-in-a-branch inside it. - rgui_frame()'s config_get_ptr() root is gone; the frame closure (16 functions, walked over comment-stripped call edges) contains no settings-> read and no config_get_ptr. samples/menu/rgui_aspect_defer links the shipping objects with only main() replaced, boots rgui headless on the null video driver, flips the aspect-ratio lock on the main thread, and drives menu_driver_frame() with the real video_driver_build_info() snapshot followed by the render slot: the detecting frame must leave the settings untouched, the render must land the staged configuration (custom aspect index, populated custom viewport), and further rounds must leave it alone. On the pre-fix shape - the frame path writing the settings itself - the first lane fails; here all pass. This closes the menu class sweep: materialui, xmb, ozone and rgui all read the per-frame snapshot on the frame path, and none of them touch the live settings from the thread that draws. Verified: thread-read audit 0 findings / 0 allowlisted pairs, the defer harness green (and red on the pre-fix shape), the playlist_nav harness (which boots this driver) fully green, hot-function and stack budgets within limits, the 74-lane compile matrix, five forced atomic backends and the C89 gate on menu/drivers/rgui.c.
This commit is contained in:
1 parent
f01ad54b65
commit
96ee694ae9
4 files changed
+388
-36
No files matched your search
+105
-36
@@ -327,7 +327,12 @@ typedef struct
|
||||
thumbnail_t mini_thumbnail;
|
||||
thumbnail_t mini_left_thumbnail;
|
||||
|
||||
rgui_video_settings_t menu_video_settings; /* int alignment */
|
||||
rgui_video_settings_t menu_video_settings;
|
||||
/* Copy staged by a delay_update publish (the frame path under
|
||||
* threaded video); rgui_render() on the main thread - which owns
|
||||
* settings writes - applies it under RGUI_FLAG_ASPECT_UPDATE_PENDING
|
||||
* before firing CMD_EVENT_VIDEO_SET_ASPECT_RATIO. */
|
||||
rgui_video_settings_t pending_video_config; /* int alignment */
|
||||
rgui_video_settings_t content_video_settings; /* int alignment */
|
||||
|
||||
unsigned font_width;
|
||||
@@ -5709,9 +5714,16 @@ static enum rgui_entry_value_type rgui_get_entry_value_type(
|
||||
static bool rgui_set_aspect_ratio(
|
||||
rgui_t *rgui,
|
||||
gfx_display_t *p_disp,
|
||||
bool delay_update);
|
||||
bool delay_update,
|
||||
unsigned aspect_ratio,
|
||||
unsigned aspect_ratio_lock);
|
||||
#endif
|
||||
|
||||
/* Forward: rgui_render()'s pending-aspect pump runs before the
|
||||
* definition. */
|
||||
static void rgui_apply_video_config(
|
||||
const rgui_video_settings_t *video_settings);
|
||||
|
||||
/* Fetches current thumbnail label.
|
||||
* Returns true if label is valid. */
|
||||
static bool gfx_thumbnail_get_label(
|
||||
@@ -5813,9 +5825,12 @@ static void rgui_render(void *data, unsigned width, unsigned height,
|
||||
rgui->mini_thumbnail_delay--;
|
||||
}
|
||||
|
||||
/* Apply pending aspect ratio update */
|
||||
/* Apply pending aspect ratio update: the staged settings writes
|
||||
* land here, on the thread that owns them, before the command
|
||||
* that consumes them fires. */
|
||||
if (rgui->flags & RGUI_FLAG_ASPECT_UPDATE_PENDING)
|
||||
{
|
||||
rgui_apply_video_config(&rgui->pending_video_config);
|
||||
command_event(CMD_EVENT_VIDEO_SET_ASPECT_RATIO, NULL);
|
||||
rgui->flags &= ~RGUI_FLAG_ASPECT_UPDATE_PENDING;
|
||||
}
|
||||
@@ -5865,7 +5880,12 @@ static void rgui_render(void *data, unsigned width, unsigned height,
|
||||
* must be regenerated - easiest way is to just call
|
||||
* rgui_set_aspect_ratio() */
|
||||
if (fb_size_changed)
|
||||
rgui_set_aspect_ratio(rgui, p_disp, false);
|
||||
{
|
||||
rgui_set_aspect_ratio(rgui, p_disp, false,
|
||||
settings->uints.menu_rgui_aspect_ratio,
|
||||
settings->uints.menu_rgui_aspect_ratio_lock);
|
||||
rgui_flush_video_config(rgui);
|
||||
}
|
||||
#endif
|
||||
|
||||
if ( (rgui->flags & RGUI_FLAG_BG_MODIFIED)
|
||||
@@ -6721,12 +6741,12 @@ static void rgui_get_video_config(
|
||||
video_settings->vp.y = custom_vp->y;
|
||||
}
|
||||
|
||||
static void rgui_set_video_config(
|
||||
rgui_t *rgui,
|
||||
settings_t *settings,
|
||||
rgui_video_settings_t *video_settings,
|
||||
bool delay_update)
|
||||
/* Main thread only: writes the aspect index and custom viewport into
|
||||
* the configuration and refreshes the custom-aspect LUT entry. */
|
||||
static void rgui_apply_video_config(
|
||||
const rgui_video_settings_t *video_settings)
|
||||
{
|
||||
settings_t *settings = config_get_ptr();
|
||||
/* Could use settings->video_vp_custom directly,
|
||||
* but this seems to be the standard way of doing it... */
|
||||
video_viewport_t *custom_vp = &settings->video_vp_custom;
|
||||
@@ -6738,14 +6758,40 @@ static void rgui_set_video_config(
|
||||
|
||||
aspectratio_lut[ASPECT_RATIO_CUSTOM].value =
|
||||
(float)custom_vp->width / custom_vp->height;
|
||||
}
|
||||
|
||||
if (delay_update)
|
||||
rgui->flags |= RGUI_FLAG_ASPECT_UPDATE_PENDING;
|
||||
else
|
||||
{
|
||||
command_event(CMD_EVENT_VIDEO_SET_ASPECT_RATIO, NULL);
|
||||
rgui->flags &= ~RGUI_FLAG_ASPECT_UPDATE_PENDING;
|
||||
}
|
||||
/* Stage a video configuration for rgui_render() to apply. The frame
|
||||
* path reaches here under threaded video, and both the settings
|
||||
* writes and CMD_EVENT_VIDEO_SET_ASPECT_RATIO belong to the main
|
||||
* thread (the command deadlocks from the frame - see the note in
|
||||
* rgui_frame); main-side callers that want the deferral (populate)
|
||||
* use it too. Latest staged wins. */
|
||||
static void rgui_stage_video_config(
|
||||
rgui_t *rgui,
|
||||
const rgui_video_settings_t *video_settings)
|
||||
{
|
||||
rgui->pending_video_config = *video_settings;
|
||||
rgui->flags |= RGUI_FLAG_ASPECT_UPDATE_PENDING;
|
||||
}
|
||||
|
||||
/* Main thread only: apply and signal immediately. */
|
||||
static void rgui_set_video_config_now(
|
||||
rgui_t *rgui,
|
||||
const rgui_video_settings_t *video_settings)
|
||||
{
|
||||
rgui_apply_video_config(video_settings);
|
||||
command_event(CMD_EVENT_VIDEO_SET_ASPECT_RATIO, NULL);
|
||||
rgui->flags &= ~RGUI_FLAG_ASPECT_UPDATE_PENDING;
|
||||
}
|
||||
|
||||
/* Main thread only: apply whatever rgui_set_aspect_ratio() staged,
|
||||
* for callers that need the configuration in place before they
|
||||
* proceed (video init, menu toggle) rather than at the next
|
||||
* rgui_render(). */
|
||||
static void rgui_flush_video_config(rgui_t *rgui)
|
||||
{
|
||||
if (rgui->flags & RGUI_FLAG_ASPECT_UPDATE_PENDING)
|
||||
rgui_set_video_config_now(rgui, &rgui->pending_video_config);
|
||||
}
|
||||
|
||||
/* Note: This function is only called when aspect ratio
|
||||
@@ -6871,14 +6917,20 @@ static void rgui_update_menu_viewport(
|
||||
rgui->menu_video_settings.vp.y = 0;
|
||||
}
|
||||
|
||||
/* Dual-context: rgui_render(), init, populate and toggle call this
|
||||
* on the main thread; rgui_frame() calls it on the video thread
|
||||
* under threaded video. The two settings it needs arrive as
|
||||
* arguments, supplied from each caller's own context - the live
|
||||
* settings on the main paths, the snapshot on the frame path. */
|
||||
static bool rgui_set_aspect_ratio(
|
||||
rgui_t *rgui,
|
||||
gfx_display_t *p_disp,
|
||||
bool delay_update)
|
||||
bool delay_update,
|
||||
unsigned aspect_ratio,
|
||||
unsigned aspect_ratio_lock)
|
||||
{
|
||||
unsigned base_term_width;
|
||||
unsigned mini_thumbnail_term_width;
|
||||
settings_t *settings = config_get_ptr();
|
||||
#if defined(GEKKO)
|
||||
/* Note: Maximum Wii frame buffer width is 424, not
|
||||
* the usual 426, since the last two bits of the
|
||||
@@ -6897,8 +6949,6 @@ static bool rgui_set_aspect_ratio(
|
||||
unsigned aspect_ratio = RGUI_DINGUX_ASPECT_RATIO;
|
||||
unsigned aspect_ratio_lock = RGUI_ASPECT_RATIO_LOCK_NONE;
|
||||
#else
|
||||
unsigned aspect_ratio = settings->uints.menu_rgui_aspect_ratio;
|
||||
unsigned aspect_ratio_lock = settings->uints.menu_rgui_aspect_ratio_lock;
|
||||
#endif
|
||||
|
||||
rgui_buffers_free(rgui);
|
||||
@@ -7264,8 +7314,14 @@ static bool rgui_set_aspect_ratio(
|
||||
if ( (aspect_ratio_lock != RGUI_ASPECT_RATIO_LOCK_NONE)
|
||||
&& (!(rgui->flags & RGUI_FLAG_IGNORE_RESIZE_EVENTS)))
|
||||
{
|
||||
rgui_update_menu_viewport(rgui, p_disp, settings->uints.menu_rgui_aspect_ratio_lock);
|
||||
rgui_set_video_config(rgui, settings, &rgui->menu_video_settings, delay_update);
|
||||
rgui_update_menu_viewport(rgui, p_disp, aspect_ratio_lock);
|
||||
/* Always staged: this function is frame-reachable, and the
|
||||
* settings writes plus the aspect command belong to the main
|
||||
* thread. Main-side callers that need the update applied
|
||||
* before they proceed call rgui_flush_video_config() on
|
||||
* return; everyone else picks it up at the next
|
||||
* rgui_render(). */
|
||||
rgui_stage_video_config(rgui, &rgui->menu_video_settings);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -7357,8 +7413,13 @@ static void *rgui_init(void **userdata, bool video_is_threaded)
|
||||
* - Configures variable 'menu display' settings */
|
||||
rgui->menu_aspect_ratio_lock = aspect_ratio_lock;
|
||||
rgui->flags &= ~RGUI_FLAG_ASPECT_UPDATE_PENDING;
|
||||
if (!rgui_set_aspect_ratio(rgui, p_disp, false))
|
||||
if (!rgui_set_aspect_ratio(rgui, p_disp, false,
|
||||
settings->uints.menu_rgui_aspect_ratio,
|
||||
settings->uints.menu_rgui_aspect_ratio_lock))
|
||||
goto error;
|
||||
/* Video init reads the aspect settings next: apply what was
|
||||
* staged before proceeding. */
|
||||
rgui_flush_video_config(rgui);
|
||||
|
||||
/* Fixed 'menu display' settings */
|
||||
new_font_height = rgui->font_height_stride * 2;
|
||||
@@ -8244,7 +8305,9 @@ static void rgui_populate_entries(
|
||||
/* Need to recalculate terminal dimensions
|
||||
* > easiest method is to call
|
||||
* rgui_set_aspect_ratio() */
|
||||
rgui_set_aspect_ratio(rgui, p_disp, true);
|
||||
rgui_set_aspect_ratio(rgui, p_disp, true,
|
||||
settings->uints.menu_rgui_aspect_ratio,
|
||||
settings->uints.menu_rgui_aspect_ratio_lock);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -8411,7 +8474,7 @@ static void rgui_populate_entries(
|
||||
rgui_get_video_config(¤t_video_settings, settings, settings->uints.video_aspect_ratio_idx);
|
||||
if (rgui_is_video_config_equal(¤t_video_settings, &rgui->menu_video_settings))
|
||||
{
|
||||
rgui_set_video_config(rgui, settings, &rgui->content_video_settings, false);
|
||||
rgui_set_video_config_now(rgui, &rgui->content_video_settings);
|
||||
/* Menu viewport has been overridden - must ignore
|
||||
* resize events until the menu is next toggled off */
|
||||
rgui->flags |= RGUI_FLAG_IGNORE_RESIZE_EVENTS;
|
||||
@@ -8540,7 +8603,6 @@ static int rgui_pointer_up(
|
||||
static void rgui_frame(void *data, video_frame_info_t *video_info)
|
||||
{
|
||||
rgui_t *rgui = (rgui_t*)data;
|
||||
settings_t *settings = config_get_ptr();
|
||||
struct menu_state *menu_st = menu_state_get_ptr();
|
||||
bool bg_filler_thickness_enable = video_info->menu.rgui_background_filler_thickness_enable;
|
||||
bool border_filler_thickness_enable = video_info->menu.rgui_border_filler_thickness_enable;
|
||||
@@ -8679,7 +8741,7 @@ static void rgui_frame(void *data, video_frame_info_t *video_info)
|
||||
rgui->flags |= RGUI_FLAG_FORCE_REDRAW;
|
||||
}
|
||||
|
||||
/* Note: both rgui_set_aspect_ratio() and rgui_set_video_config()
|
||||
/* Note: both rgui_set_aspect_ratio() and the video-config path
|
||||
* normally call command_event(CMD_EVENT_VIDEO_SET_ASPECT_RATIO, NULL)
|
||||
* ## THIS CANNOT BE DONE INSIDE rgui_frame() IF THREADED VIDEO IS ENABLED ##
|
||||
* Attempting to do so creates a deadlock, and causes RetroArch to hang.
|
||||
@@ -8697,7 +8759,9 @@ static void rgui_frame(void *data, video_frame_info_t *video_info)
|
||||
* no longer makes sense to ignore resize events */
|
||||
rgui->flags &= ~RGUI_FLAG_IGNORE_RESIZE_EVENTS;
|
||||
|
||||
rgui_set_aspect_ratio(rgui, p_disp, true);
|
||||
rgui_set_aspect_ratio(rgui, p_disp, true,
|
||||
video_info->menu.rgui_aspect_ratio,
|
||||
video_info->menu.rgui_aspect_ratio_lock);
|
||||
}
|
||||
|
||||
/* > Check for changes in aspect ratio lock setting */
|
||||
@@ -8707,7 +8771,7 @@ static void rgui_frame(void *data, video_frame_info_t *video_info)
|
||||
rgui->menu_aspect_ratio_lock = aspect_ratio_lock;
|
||||
|
||||
if (aspect_ratio_lock == RGUI_ASPECT_RATIO_LOCK_NONE)
|
||||
rgui_set_video_config(rgui, settings, &rgui->content_video_settings, true);
|
||||
rgui_stage_video_config(rgui, &rgui->content_video_settings);
|
||||
else
|
||||
{
|
||||
/* As with changes in aspect ratio, if we reach this point
|
||||
@@ -8715,7 +8779,7 @@ static void rgui_frame(void *data, video_frame_info_t *video_info)
|
||||
* events should be monitored again */
|
||||
rgui->flags &= ~RGUI_FLAG_IGNORE_RESIZE_EVENTS;
|
||||
rgui_update_menu_viewport(rgui, p_disp, video_info->menu.rgui_aspect_ratio_lock);
|
||||
rgui_set_video_config(rgui, settings, &rgui->menu_video_settings, true);
|
||||
rgui_stage_video_config(rgui, &rgui->menu_video_settings);
|
||||
}
|
||||
|
||||
/* Clear any pending 'restore aspect lock' flags */
|
||||
@@ -8773,7 +8837,9 @@ static void rgui_frame(void *data, video_frame_info_t *video_info)
|
||||
|| (rgui->window_width < default_fb_width)
|
||||
|| (video_height < 240)
|
||||
|| (rgui->window_height < 240))
|
||||
rgui_set_aspect_ratio(rgui, p_disp, true);
|
||||
rgui_set_aspect_ratio(rgui, p_disp, true,
|
||||
video_info->menu.rgui_aspect_ratio,
|
||||
video_info->menu.rgui_aspect_ratio_lock);
|
||||
#endif
|
||||
|
||||
/* If aspect ratio is locked, have to update viewport */
|
||||
@@ -8781,7 +8847,7 @@ static void rgui_frame(void *data, video_frame_info_t *video_info)
|
||||
&& (!(rgui->flags & RGUI_FLAG_IGNORE_RESIZE_EVENTS)))
|
||||
{
|
||||
rgui_update_menu_viewport(rgui, p_disp, video_info->menu.rgui_aspect_ratio_lock);
|
||||
rgui_set_video_config(rgui, settings, &rgui->menu_video_settings, true);
|
||||
rgui_stage_video_config(rgui, &rgui->menu_video_settings);
|
||||
}
|
||||
|
||||
rgui->window_width = video_width;
|
||||
@@ -8864,11 +8930,14 @@ static void rgui_toggle(void *userdata, bool menu_on)
|
||||
/* Update menu viewport */
|
||||
rgui_update_menu_viewport(rgui, p_disp, settings->uints.menu_rgui_aspect_ratio_lock);
|
||||
/* Apply menu video settings */
|
||||
rgui_set_video_config(rgui, settings, &rgui->menu_video_settings, false);
|
||||
rgui_set_video_config_now(rgui, &rgui->menu_video_settings);
|
||||
}
|
||||
else if (rgui->menu_aspect_ratio == RGUI_ASPECT_RATIO_AUTO)
|
||||
{
|
||||
rgui_set_aspect_ratio(rgui, p_disp, false);
|
||||
rgui_set_aspect_ratio(rgui, p_disp, false,
|
||||
settings->uints.menu_rgui_aspect_ratio,
|
||||
settings->uints.menu_rgui_aspect_ratio_lock);
|
||||
rgui_flush_video_config(rgui);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -8882,7 +8951,7 @@ static void rgui_toggle(void *userdata, bool menu_on)
|
||||
rgui_get_video_config(¤t_video_settings, settings, settings->uints.video_aspect_ratio_idx);
|
||||
|
||||
if (rgui_is_video_config_equal(¤t_video_settings, &rgui->menu_video_settings))
|
||||
rgui_set_video_config(rgui, settings, &rgui->content_video_settings, false);
|
||||
rgui_set_video_config_now(rgui, &rgui->content_video_settings);
|
||||
|
||||
/* Any modified video scaling settings have now been
|
||||
* registered, so it is again 'safe' to respond to window
|
||||
@@ -8986,7 +9055,7 @@ static enum menu_action rgui_parse_menu_entry_action(
|
||||
* until the menu is next toggled off; this is a
|
||||
* one-shot 'fix' that should only be active
|
||||
* during the config save operation */
|
||||
rgui_set_video_config(rgui, settings, &rgui->content_video_settings, false);
|
||||
rgui_set_video_config_now(rgui, &rgui->content_video_settings);
|
||||
/* Schedule a restoration of the aspect ratio
|
||||
* lock on the next frame */
|
||||
rgui->flags |= RGUI_FLAG_RESTORE_ASPECT_LOCK;
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
TARGET := rgui_aspect_defer_test
|
||||
|
||||
all:
|
||||
@sh ./build.sh
|
||||
|
||||
clean:
|
||||
rm -f $(TARGET) retroarch_nomain.o retroarch_nomain.d \
|
||||
harness_main.o harness_main.d
|
||||
|
||||
.PHONY: all clean
|
||||
Executable
+71
@@ -0,0 +1,71 @@
|
||||
#!/bin/sh
|
||||
# Builds the rgui deferred aspect-config write harness.
|
||||
#
|
||||
# Like playlist_nav, this links the SHIPPING objects of a built
|
||||
# RetroArch with only main() replaced - nothing is stubbed. The seam
|
||||
# under test runs through rgui_frame's aspect-change detection, the staged config on the
|
||||
# rgui handle, and the application rgui_render performs on the main
|
||||
# thread before CMD_EVENT_VIDEO_SET_ASPECT_RATIO fires.
|
||||
# real frame and render code can see whether the write lands in the
|
||||
# configuration or dies with the frame's snapshot copy.
|
||||
#
|
||||
# Requires a completed NON-Qt build: with Qt enabled main() lives in
|
||||
# ui_qt.o, which drags the whole Qt UI in with it. From the repo
|
||||
# root:
|
||||
#
|
||||
# ./configure --disable-qt && make
|
||||
# samples/menu/rgui_aspect_defer/build.sh
|
||||
#
|
||||
# retroarch.c is recompiled here with main renamed, so the harness can
|
||||
# supply its own without having to exclude the object that holds most
|
||||
# of the frontend. Both the compile and the link reuse the project's
|
||||
# own command lines, taken from make -n, so the harness cannot drift
|
||||
# away from how the program is really built.
|
||||
set -eu
|
||||
|
||||
here=$(cd "$(dirname "$0")" && pwd)
|
||||
root=$(cd "$here/../../.." && pwd)
|
||||
|
||||
cd "$root"
|
||||
|
||||
if [ ! -f obj-unix/release/retroarch.o ]; then
|
||||
echo "build RetroArch first: ./configure --disable-qt && make" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cc_line=$(mktemp)
|
||||
ld_line=$(mktemp)
|
||||
trap 'rm -f "$cc_line" "$ld_line"' EXIT
|
||||
|
||||
# The project's own compile line for retroarch.o ...
|
||||
touch retroarch.c
|
||||
make -n 2>/dev/null | grep -E '\-o obj-unix/release/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
|
||||
|
||||
# ... reused twice: once for retroarch.c with main renamed out of the
|
||||
# way, once for the harness itself, so both are built exactly the way
|
||||
# the frontend is.
|
||||
sed 's#-o obj-unix/release/retroarch\.o#-Dmain=rarch_harness_unused_main -o samples/menu/rgui_aspect_defer/retroarch_nomain.o#' \
|
||||
"$cc_line" | sh
|
||||
|
||||
sed -e 's#-o obj-unix/release/retroarch\.o#-o samples/menu/rgui_aspect_defer/harness_main.o#' \
|
||||
-e 's# retroarch\.c# samples/menu/rgui_aspect_defer/rgui_aspect_defer_test.c#' \
|
||||
"$cc_line" | sh
|
||||
|
||||
# The project's own link line, with retroarch.o swapped for the pair
|
||||
# above. Removing the binary first is what makes make -n emit it.
|
||||
rm -f retroarch
|
||||
make -n 2>/dev/null | grep -E ' -o retroarch ' | tail -1 > "$ld_line"
|
||||
if [ ! -s "$ld_line" ]; then
|
||||
echo "could not determine the link command" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
sed -e 's#obj-unix/release/retroarch\.o#samples/menu/rgui_aspect_defer/retroarch_nomain.o samples/menu/rgui_aspect_defer/harness_main.o#' \
|
||||
-e 's#-o retroarch #-o samples/menu/rgui_aspect_defer/rgui_aspect_defer_test #' \
|
||||
"$ld_line" | sh
|
||||
|
||||
echo "built samples/menu/rgui_aspect_defer/rgui_aspect_defer_test"
|
||||
@@ -0,0 +1,202 @@
|
||||
/* Rgui deferred aspect-config write.
|
||||
*
|
||||
* Links the shipping RetroArch objects with only main() replaced
|
||||
* (the playlist_nav pattern) and drives the seam between rgui's
|
||||
* frame path and its main-thread render:
|
||||
*
|
||||
* frame (menu_driver_frame -> rgui_frame): under threaded video
|
||||
* this is the video thread. When the aspect-ratio lock setting
|
||||
* changes, the frame path re-lays the menu and STAGES the video
|
||||
* configuration on the rgui handle - it must not write settings
|
||||
* (five fields plus the custom-aspect LUT entry) or fire
|
||||
* CMD_EVENT_VIDEO_SET_ASPECT_RATIO from there;
|
||||
*
|
||||
* render (main thread): applies the staged configuration into the
|
||||
* settings and fires the command, under
|
||||
* RGUI_FLAG_ASPECT_UPDATE_PENDING.
|
||||
*
|
||||
* The claims: after a frame that detects the change, the settings
|
||||
* are still untouched (the frame staged, nothing more); after the
|
||||
* render that follows, video_aspect_ratio_idx and the custom
|
||||
* viewport carry the menu configuration; and once the pipeline
|
||||
* settles, further frame+render rounds leave the settings alone.
|
||||
*
|
||||
* Requires a completed non-Qt build:
|
||||
*
|
||||
* ./configure --disable-qt && make
|
||||
* samples/menu/rgui_aspect_defer/build.sh
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <boolean.h>
|
||||
#include <time/rtime.h>
|
||||
#include <file/config_file.h>
|
||||
#include <streams/file_stream.h>
|
||||
|
||||
#include "../../../config.def.h"
|
||||
#include "../../../configuration.h"
|
||||
#include "../../../retroarch.h"
|
||||
#include "../../../menu/menu_driver.h"
|
||||
#include "../../../gfx/video_driver.h"
|
||||
#include "../../../frontend/frontend_driver.h"
|
||||
|
||||
static unsigned failures = 0;
|
||||
|
||||
#define CHECK(cond, ...) \
|
||||
do { \
|
||||
if (!(cond)) \
|
||||
{ \
|
||||
fprintf(stderr, "FAIL %s:%d: ", __FILE__, __LINE__); \
|
||||
fprintf(stderr, __VA_ARGS__); \
|
||||
fprintf(stderr, "\n"); \
|
||||
failures++; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static void one_frame(void)
|
||||
{
|
||||
video_frame_info_t vinfo;
|
||||
video_driver_build_info(&vinfo);
|
||||
menu_driver_frame(true, &vinfo);
|
||||
}
|
||||
|
||||
static void one_render(void)
|
||||
{
|
||||
struct menu_state *menu_st = menu_state_get_ptr();
|
||||
if (menu_st->driver_ctx && menu_st->driver_ctx->render)
|
||||
menu_st->driver_ctx->render(menu_st->userdata, 320, 240, false);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char fixture_dir[512];
|
||||
char cmd[700];
|
||||
static char cfg_path[640];
|
||||
char *rarch_argv[8];
|
||||
int rarch_argc = 0;
|
||||
settings_t *settings = NULL;
|
||||
unsigned idx_before;
|
||||
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
snprintf(fixture_dir, sizeof(fixture_dir),
|
||||
"/tmp/rgui_aspect_%ld", (long)getpid());
|
||||
snprintf(cmd, sizeof(cmd), "mkdir -p %s", fixture_dir);
|
||||
if (system(cmd) != 0)
|
||||
return 1;
|
||||
|
||||
rarch_argv[rarch_argc++] = (char*)"retroarch";
|
||||
rarch_argv[rarch_argc++] = (char*)"--menu";
|
||||
rarch_argv[rarch_argc++] = (char*)"--config";
|
||||
rarch_argv[rarch_argc++] = cfg_path;
|
||||
|
||||
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);
|
||||
{
|
||||
FILE *cfg;
|
||||
snprintf(cfg_path, sizeof(cfg_path), "%s/harness.cfg",
|
||||
fixture_dir);
|
||||
if ((cfg = fopen(cfg_path, "wb")))
|
||||
{
|
||||
fprintf(cfg, "video_driver = \"null\"\n");
|
||||
fprintf(cfg, "audio_driver = \"null\"\n");
|
||||
fprintf(cfg, "input_driver = \"null\"\n");
|
||||
fprintf(cfg, "input_joypad_driver = \"null\"\n");
|
||||
fprintf(cfg, "menu_driver = \"rgui\"\n");
|
||||
fprintf(cfg, "video_threaded = \"false\"\n");
|
||||
/* Lock off at init; the harness flips it and lets the
|
||||
* frame path discover the change. */
|
||||
fprintf(cfg, "rgui_aspect_ratio_lock = \"0\"\n");
|
||||
fclose(cfg);
|
||||
}
|
||||
}
|
||||
|
||||
if (!retroarch_main_init(rarch_argc, rarch_argv))
|
||||
{
|
||||
fprintf(stderr, "FAIL: retroarch_main_init failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
settings = config_get_ptr();
|
||||
retroarch_menu_running();
|
||||
|
||||
{
|
||||
struct menu_state *menu_st = menu_state_get_ptr();
|
||||
CHECK( menu_st->driver_ctx && menu_st->driver_ctx->ident
|
||||
&& strcmp(menu_st->driver_ctx->ident, "rgui") == 0,
|
||||
"fixture: menu driver is %s, want rgui",
|
||||
(menu_st->driver_ctx && menu_st->driver_ctx->ident)
|
||||
? menu_st->driver_ctx->ident : "(none)");
|
||||
CHECK((menu_st->flags & MENU_ST_FLAG_ALIVE) != 0,
|
||||
"fixture: menu not ALIVE");
|
||||
}
|
||||
|
||||
/* Settle: one frame + render under the baseline. */
|
||||
one_frame();
|
||||
one_render();
|
||||
|
||||
idx_before = settings->uints.video_aspect_ratio_idx;
|
||||
|
||||
/* The change the frame path detects: the person turns the aspect
|
||||
* lock on. The harness is the main thread, where settings writes
|
||||
* belong. */
|
||||
configuration_set_uint(settings,
|
||||
settings->uints.menu_rgui_aspect_ratio_lock, 1);
|
||||
|
||||
/* Lane 1: the detecting frame stages but must not write - the
|
||||
* settings are untouched until the render applies. */
|
||||
one_frame();
|
||||
CHECK(settings->uints.video_aspect_ratio_idx == idx_before,
|
||||
"the frame path wrote video_aspect_ratio_idx itself"
|
||||
" (%u -> %u)",
|
||||
idx_before, settings->uints.video_aspect_ratio_idx);
|
||||
|
||||
/* Lane 2: the render applies the staged configuration - the
|
||||
* aspect index now carries the menu configuration (custom, since
|
||||
* the locked menu viewport is a custom viewport). */
|
||||
one_render();
|
||||
CHECK(settings->uints.video_aspect_ratio_idx == ASPECT_RATIO_CUSTOM,
|
||||
"staged aspect config not applied by render: idx %u, want"
|
||||
" %u (custom)",
|
||||
settings->uints.video_aspect_ratio_idx,
|
||||
(unsigned)ASPECT_RATIO_CUSTOM);
|
||||
CHECK(settings->video_vp_custom.width > 0
|
||||
&& settings->video_vp_custom.height > 0,
|
||||
"custom viewport not populated (%ux%u)",
|
||||
settings->video_vp_custom.width,
|
||||
settings->video_vp_custom.height);
|
||||
|
||||
/* Lane 3: settled - further rounds leave the settings alone. */
|
||||
{
|
||||
unsigned idx_now = settings->uints.video_aspect_ratio_idx;
|
||||
unsigned w = settings->video_vp_custom.width;
|
||||
unsigned h = settings->video_vp_custom.height;
|
||||
one_frame();
|
||||
one_render();
|
||||
one_frame();
|
||||
one_render();
|
||||
CHECK(settings->uints.video_aspect_ratio_idx == idx_now
|
||||
&& settings->video_vp_custom.width == w
|
||||
&& settings->video_vp_custom.height == h,
|
||||
"aspect config kept moving after settling");
|
||||
}
|
||||
|
||||
snprintf(cmd, sizeof(cmd), "rm -rf %s", fixture_dir);
|
||||
if (system(cmd) != 0) { /* fixture dir left behind; harmless */ }
|
||||
|
||||
if (failures)
|
||||
{
|
||||
fprintf(stderr, "FAILURES (%u)\n", failures);
|
||||
return 1;
|
||||
}
|
||||
printf("rgui_aspect_defer: all lanes passed\n");
|
||||
return 0;
|
||||
}
|
||||
Reference in new issue
Block a user