Video: add Video Filter Threads setting

CPU video filters always ran on one worker per detected core
(RARCH_SOFTFILTER_THREADS_AUTO). That suits the heavy filters (NTSC,
NTSC CRT, 2xBR) but over-parallelises the light ones: at 256x224 EPX
is ~26% slower on eight workers than on one, and LQ2x/2xSaI lose
their four-worker gain at eight.

video_filter_threads (Settings > Video > Output, under Video Filter;
advanced) picks the count: 0 is Automatic, the previous behaviour and
the default; 1-16 forces that many workers. Changing it while a filter
is running rebuilds the filter on the new count at once; with no filter
loaded the value applies at the next load.

video_driver_init_filter() now frees a live filter before building the
new one. Re-init while content was running (the new setting, and also
a video filter path change) previously overwrote state_filter and
state_buffer, leaking the old filter, its worker pool and its output
buffer.

tools/settings_table.reference is regenerated; it had drifted on
master (HDR, custom viewport and window size rows) independently of
this change.
This commit is contained in:
LibretroAdmin authored and libretroadmin committed 2026-09-26 18:52:29 +02:00
1 parent 3c1b3a5f5e
commit 15eddd52b9
9 files changed
+74 -23

No files matched your search

+5
View File
@@ -514,6 +514,11 @@
#define MINIMUM_SWAP_INTERVAL 1
#define MAXIMUM_SWAP_INTERVAL 16
/* Worker threads a CPU video filter runs on; 0 (Automatic) uses one
* per detected CPU core. */
#define DEFAULT_VIDEO_FILTER_THREADS 0
#define MAXIMUM_VIDEO_FILTER_THREADS 16
/* Threaded video: the core runs on one thread and the video driver
* presents on another. Off by default, as it has always been; the
* Switch keeps its own default. When it is on, hardware-rendered cores
+1
View File
@@ -258,6 +258,7 @@ typedef struct settings
unsigned video_scale_integer_axis;
unsigned video_scale_integer_scaling;
unsigned video_max_swapchain_images;
unsigned video_filter_threads;
unsigned video_swap_interval;
unsigned video_hard_sync_frames;
unsigned video_frame_delay;
+6 -1
View File
@@ -2358,6 +2358,11 @@ void video_driver_init_filter(enum retro_pixel_format colfmt_int,
video_thread_wait_idle();
#endif
/* Re-init (a new filter path, a new thread count) replaces the live
* filter: release it and its output buffer rather than orphan them */
if (video_st->state_filter)
video_driver_filter_free();
if (video_driver_is_hw_context())
{
RARCH_WARN("[Video] Cannot use CPU filters when hardware rendering is used.\n");
@@ -2366,7 +2371,7 @@ void video_driver_init_filter(enum retro_pixel_format colfmt_int,
if (!(video_st->state_filter = rarch_softfilter_new(
settings->paths.path_softfilter_plugin,
RARCH_SOFTFILTER_THREADS_AUTO, colfmt, dims)))
settings->uints.video_filter_threads, colfmt, dims)))
{
RARCH_ERR("[Video] Failed to load filter.\n");
return;
+3
View File
@@ -2320,6 +2320,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs,
{ MENU_ENUM_LABEL_CHEEVOS_PASSWORD, MENU_ENUM_SUBLABEL_CHEEVOS_PASSWORD },
{ MENU_ENUM_LABEL_VIDEO_FILTER, MENU_ENUM_SUBLABEL_VIDEO_FILTER },
{ MENU_ENUM_LABEL_VIDEO_FILTER_REMOVE, MENU_ENUM_SUBLABEL_VIDEO_FILTER_REMOVE },
#ifdef HAVE_VIDEO_FILTER
{ MENU_ENUM_LABEL_VIDEO_FILTER_THREADS, MENU_ENUM_SUBLABEL_VIDEO_FILTER_THREADS },
#endif
{ MENU_ENUM_LABEL_VIDEO_CROP_OVERSCAN, MENU_ENUM_SUBLABEL_VIDEO_CROP_OVERSCAN },
{ MENU_ENUM_LABEL_VIDEO_SMOOTH, MENU_ENUM_SUBLABEL_VIDEO_SMOOTH },
{ MENU_ENUM_LABEL_VIDEO_FONT_ENABLE, MENU_ENUM_SUBLABEL_VIDEO_FONT_ENABLE },
+5
View File
@@ -9532,6 +9532,11 @@ unsigned menu_displaylist_build_list(
PARSE_ONLY_PATH, false) == 0)
count++;
if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list,
MENU_ENUM_LABEL_VIDEO_FILTER_THREADS,
PARSE_ONLY_UINT, false) == 0)
count++;
if (*settings->paths.path_softfilter_plugin)
if (menu_entries_append(list,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_FILTER_REMOVE),
+20
View File
@@ -7070,6 +7070,18 @@ static size_t setting_get_string_representation_video_swap_interval(rarch_settin
return snprintf(s, len, "%u", *setting->value.target.unsigned_integer);
}
#ifdef HAVE_VIDEO_FILTER
static size_t setting_get_string_representation_uint_video_filter_threads(
rarch_setting_t *setting, char *s, size_t len)
{
if (!setting)
return 0;
if (*setting->value.target.unsigned_integer == 0)
return strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_AUTO), len);
return snprintf(s, len, "%u", *setting->value.target.unsigned_integer);
}
#endif
static size_t setting_get_string_representation_black_frame_insertion(rarch_setting_t *setting,
char *s, size_t len)
{
@@ -9694,6 +9706,14 @@ static void general_write_handler(rarch_setting_t *setting)
log_dir);
}
break;
#ifdef HAVE_VIDEO_FILTER
case MENU_ENUM_LABEL_VIDEO_FILTER_THREADS:
/* Rebuild a running filter on the new worker count; with no
* filter loaded the value is picked up at the next load. */
if (video_state_get_ptr()->state_filter)
command_event(CMD_EVENT_VIDEO_FILTER_INIT, NULL);
break;
#endif
case MENU_ENUM_LABEL_VIDEO_SMOOTH:
case MENU_ENUM_LABEL_VIDEO_CTX_SCALING:
#if defined(DINGUX)
+1
View File
@@ -1630,6 +1630,7 @@
#define MENU_ENUM_LABEL_VIDEO_FILTER_STR "video_filter"
#define MENU_ENUM_LABEL_VIDEO_FILTER_DIR_STR "video_filter_dir"
#define MENU_ENUM_LABEL_VIDEO_FILTER_ENABLE_STR "filter_enable"
#define MENU_ENUM_LABEL_VIDEO_FILTER_THREADS_STR "video_filter_threads"
#define MENU_ENUM_LABEL_VIDEO_FONT_ENABLE_STR "video_font_enable"
#define MENU_ENUM_LABEL_VIDEO_FONT_PATH_STR "video_font_path"
#define MENU_ENUM_LABEL_VIDEO_FONT_SIZE_STR "video_font_size"
@@ -26,4 +26,9 @@ S_BOOL(video_filter_enable, VIDEO_FILTER_ENABLE,
"Video Filter Enable",
"Apply Video Filter. Is a hint that does not necessarily have to be honored by the video driver.")
#endif
S_UINT(video_filter_threads, VIDEO_FILTER_THREADS,
"video_filter_threads",
DEFAULT_VIDEO_FILTER_THREADS, SD_FLAG_ADVANCED, SDESC_RANGE_MINMAX, CMD_EVENT_NONE, 0, MAXIMUM_VIDEO_FILTER_THREADS, 1, 0, setting_action_ok_uint, setting_get_string_representation_uint_video_filter_threads,
"Video Filter Threads",
"How many threads a CPU video filter may run on. 'Automatic' uses one per CPU core. Light filters can run slower when spread over many threads, so a lower count may be faster for them; heavy filters such as NTSC gain the most from more threads. A running filter picks up the change straight away.")
#endif
+28 -22
View File
@@ -224,6 +224,8 @@ bool pause_on_disconnect 1 0
bool video_frame_delay_auto 1 0
bool audio_fastpath_s16 1 0
bool audio_resampler_hq_oversampling 1 0
bool video_hdr_use_display_peak 1 0
bool video_hdr_send_luminance 1 0
bool video_window_custom_size_enable 1 0
bool input_overlay_auto_scale 1 1
bool video_font_enable 1 1
@@ -405,6 +407,10 @@ bool netplay_request_device_p13 1 0
bool netplay_request_device_p14 1 0
bool netplay_request_device_p15 1 0
bool netplay_request_device_p16 1 0
int custom_viewport_x 0 0
int custom_viewport_y 0 0
int video_windowed_position_x 1 0
int video_windowed_position_y 1 0
int crt_switch_center_adjust 0 0
int crt_switch_porch_adjust 0 0
int crt_switch_vertical_adjust 0 0
@@ -436,12 +442,10 @@ uint microphone_latency 0 0
uint microphone_rate 1 48000
uint custom_viewport_width 0 0
uint custom_viewport_height 0 0
uint custom_viewport_x 0 0
uint custom_viewport_y 0 0
uint video_windowed_position_x 1 0
uint video_windowed_position_y 1 0
uint video_windowed_position_width 1 1280
uint video_windowed_position_height 1 720
uint video_window_auto_width_max 1 1920
uint video_window_auto_height_max 1 1080
uint video_fse_negotiation 1 VIDEO_FSE_RELAXED
uint video_fullscreen_x 1 0
uint video_fullscreen_y 1 0
@@ -577,8 +581,6 @@ uint microphone_block_frames 1 0
uint menu_thumbnail_preview_threads 1 1
uint menu_file_browser_extension_display 1 MENU_FILE_BROWSER_EXTENSION_DISPLAY_ALWAYS
uint video_scale 1 3
uint video_window_auto_width_max 1 1920
uint video_window_auto_height_max 1 1080
uint video_window_opacity 1 100
uint video_stream_port 1 56400
uint video_record_scale_factor 1 1
@@ -889,6 +891,8 @@ bool pause_on_disconnect 1 0
bool video_frame_delay_auto 1 0
bool audio_fastpath_s16 1 0
bool audio_resampler_hq_oversampling 1 0
bool video_hdr_use_display_peak 1 0
bool video_hdr_send_luminance 1 0
bool video_window_custom_size_enable 1 0
bool input_overlay_auto_scale 1 0
bool video_font_enable 1 1
@@ -1069,6 +1073,10 @@ bool netplay_request_device_p13 1 0
bool netplay_request_device_p14 1 0
bool netplay_request_device_p15 1 0
bool netplay_request_device_p16 1 0
int custom_viewport_x 0 0
int custom_viewport_y 0 0
int video_windowed_position_x 1 0
int video_windowed_position_y 1 0
int crt_switch_center_adjust 0 0
int crt_switch_porch_adjust 0 0
int crt_switch_vertical_adjust 0 0
@@ -1099,12 +1107,10 @@ uint microphone_latency 0 0
uint microphone_rate 1 48000
uint custom_viewport_width 0 0
uint custom_viewport_height 0 0
uint custom_viewport_x 0 0
uint custom_viewport_y 0 0
uint video_windowed_position_x 1 0
uint video_windowed_position_y 1 0
uint video_windowed_position_width 1 1280
uint video_windowed_position_height 1 720
uint video_window_auto_width_max 1 1920
uint video_window_auto_height_max 1 1080
uint video_fse_negotiation 1 VIDEO_FSE_RELAXED
uint video_fullscreen_x 1 0
uint video_fullscreen_y 1 0
@@ -1238,8 +1244,6 @@ uint microphone_block_frames 1 0
uint menu_thumbnail_preview_threads 1 1
uint menu_file_browser_extension_display 1 MENU_FILE_BROWSER_EXTENSION_DISPLAY_ALWAYS
uint video_scale 1 3
uint video_window_auto_width_max 1 1920
uint video_window_auto_height_max 1 1080
uint video_window_opacity 1 100
uint video_stream_port 1 56400
uint video_record_scale_factor 1 1
@@ -1559,6 +1563,8 @@ bool pause_on_disconnect 1 0
bool video_frame_delay_auto 1 0
bool audio_fastpath_s16 1 0
bool audio_resampler_hq_oversampling 1 0
bool video_hdr_use_display_peak 1 0
bool video_hdr_send_luminance 1 0
bool video_window_custom_size_enable 1 0
bool input_overlay_auto_scale 1 0
bool video_font_enable 1 1
@@ -1740,6 +1746,10 @@ bool netplay_request_device_p13 1 0
bool netplay_request_device_p14 1 0
bool netplay_request_device_p15 1 0
bool netplay_request_device_p16 1 0
int custom_viewport_x 0 0
int custom_viewport_y 0 0
int video_windowed_position_x 1 0
int video_windowed_position_y 1 0
int crt_switch_center_adjust 0 0
int crt_switch_porch_adjust 0 0
int crt_switch_vertical_adjust 0 0
@@ -1771,12 +1781,14 @@ uint microphone_latency 0 0
uint microphone_rate 1 48000
uint custom_viewport_width 0 0
uint custom_viewport_height 0 0
uint custom_viewport_x 0 0
uint custom_viewport_y 0 0
uint video_windowed_position_x 1 0
uint video_windowed_position_y 1 0
uint video_windowed_position_width 1 1280
uint video_windowed_position_height 1 720
uint video_window_auto_width_max 1 1920
uint video_window_auto_height_max 1 1080
uint desktop_menu_window_x 1 0
uint desktop_menu_window_y 1 0
uint desktop_menu_window_width 1 0
uint desktop_menu_window_height 1 0
uint video_fse_negotiation 1 VIDEO_FSE_RELAXED
uint video_fullscreen_x 1 0
uint video_fullscreen_y 1 0
@@ -1844,10 +1856,6 @@ uint desktop_menu_icon_view_zoom 1 50
uint desktop_menu_all_playlists_list_max_count 1 0
uint desktop_menu_all_playlists_grid_max_count 1 0
uint desktop_menu_theme 1 0
uint desktop_menu_window_x 1 0
uint desktop_menu_window_y 1 0
uint desktop_menu_window_width 1 0
uint desktop_menu_window_height 1 0
uint ai_service_source_lang 1 0
uint ai_service_target_lang 1 0
uint video_scale_integer_axis 1 0
@@ -1925,8 +1933,6 @@ uint microphone_block_frames 1 0
uint menu_thumbnail_preview_threads 1 1
uint menu_file_browser_extension_display 1 MENU_FILE_BROWSER_EXTENSION_DISPLAY_ALWAYS
uint video_scale 1 3
uint video_window_auto_width_max 1 1920
uint video_window_auto_height_max 1 1080
uint video_window_opacity 1 100
uint video_stream_port 1 56400
uint video_record_scale_factor 1 1