diff --git a/config.def.h b/config.def.h index 2f16ca74257e..e499f594954c 100644 --- a/config.def.h +++ b/config.def.h @@ -259,12 +259,13 @@ */ #define DEFAULT_FRAME_DELAY 0 -/* Inserts a black frame inbetween frames. - * Useful for 120 Hz monitors who want to play 60 Hz material with eliminated - * ghosting. video_refresh_rate should still be configured as if it - * is a 60 Hz monitor (divide refresh rate by 2). +/* Inserts black frame(s) inbetween frames. + * Useful for Higher Hz monitors (set to multiples of 60 Hz) who want to play 60 Hz + * material with eliminated ghosting. video_refresh_rate should still be configured + * as if it is a 60 Hz monitor (divide refresh rate by multiple of 60 Hz). */ -#define DEFAULT_BLACK_FRAME_INSERTION false +#define DEFAULT_BLACK_FRAME_INSERTION 0 + /* Uses a custom swap interval for VSync. * Set this to effectively halve monitor refresh rate. diff --git a/configuration.c b/configuration.c index 09286645e438..64de34c478fb 100644 --- a/configuration.c +++ b/configuration.c @@ -1466,7 +1466,6 @@ static struct config_bool_setting *populate_settings_bool( SETTING_BOOL("video_vsync", &settings->bools.video_vsync, true, DEFAULT_VSYNC, false); SETTING_BOOL("video_adaptive_vsync", &settings->bools.video_adaptive_vsync, true, DEFAULT_ADAPTIVE_VSYNC, false); SETTING_BOOL("video_hard_sync", &settings->bools.video_hard_sync, true, DEFAULT_HARD_SYNC, false); - SETTING_BOOL("video_black_frame_insertion", &settings->bools.video_black_frame_insertion, true, DEFAULT_BLACK_FRAME_INSERTION, false); SETTING_BOOL("video_disable_composition", &settings->bools.video_disable_composition, true, DEFAULT_DISABLE_COMPOSITION, false); SETTING_BOOL("pause_nonactive", &settings->bools.pause_nonactive, true, DEFAULT_PAUSE_NONACTIVE, false); SETTING_BOOL("video_gpu_screenshot", &settings->bools.video_gpu_screenshot, true, DEFAULT_GPU_SCREENSHOT, false); @@ -2008,6 +2007,7 @@ static struct config_uint_setting *populate_settings_uint( SETTING_UINT("core_updater_auto_backup_history_size", &settings->uints.core_updater_auto_backup_history_size, true, DEFAULT_CORE_UPDATER_AUTO_BACKUP_HISTORY_SIZE, false); + SETTING_UINT("video_black_frame_insertion", &settings->uints.video_black_frame_insertion, true, DEFAULT_BLACK_FRAME_INSERTION, false); *size = count; return tmp; diff --git a/configuration.h b/configuration.h index 91fb05d8962b..d63cf24e0074 100644 --- a/configuration.h +++ b/configuration.h @@ -269,6 +269,7 @@ typedef struct settings unsigned ai_service_source_lang; unsigned core_updater_auto_backup_history_size; + unsigned video_black_frame_insertion; } uints; struct @@ -447,7 +448,6 @@ typedef struct settings bool video_vsync; bool video_adaptive_vsync; bool video_hard_sync; - bool video_black_frame_insertion; bool video_vfilter; bool video_smooth; bool video_ctx_scaling; diff --git a/gfx/common/win32_common.c b/gfx/common/win32_common.c index 8713de9d7fcc..3874ed99134c 100644 --- a/gfx/common/win32_common.c +++ b/gfx/common/win32_common.c @@ -1600,7 +1600,7 @@ void win32_set_style(MONITORINFOEX *current_mon, HMONITOR *hm_to_use, bool video_window_save_positions = settings->bools.video_window_save_positions; float video_refresh = settings->floats.video_refresh_rate; unsigned swap_interval = settings->uints.video_swap_interval; - bool bfi = settings->bools.video_black_frame_insertion; + unsigned bfi = settings->uints.video_black_frame_insertion; unsigned window_position_x = settings->uints.window_position_x; unsigned window_position_y = settings->uints.window_position_y; unsigned window_position_width = settings->uints.window_position_width; @@ -1610,9 +1610,9 @@ void win32_set_style(MONITORINFOEX *current_mon, HMONITOR *hm_to_use, { /* Windows only reports the refresh rates for modelines as * an integer, so video_refresh_rate needs to be rounded. Also, account - * for black frame insertion using video_refresh_rate set to half + * for black frame insertion using video_refresh_rate set to a portion * of the display refresh rate, as well as higher vsync swap intervals. */ - float refresh_mod = bfi ? 2.0f : 1.0f; + float refresh_mod = bfi + 1.0f; unsigned refresh = roundf(video_refresh * refresh_mod * swap_interval); diff --git a/gfx/common/x11_common.c b/gfx/common/x11_common.c index 6f6b61957608..67110059f175 100644 --- a/gfx/common/x11_common.c +++ b/gfx/common/x11_common.c @@ -268,7 +268,7 @@ static bool get_video_mode( float minimum_fps_diff = 0.0f; XF86VidModeModeInfo **modes = NULL; settings_t *settings = config_get_ptr(); - bool black_frame_insertion = settings->bools.video_black_frame_insertion; + unsigned black_frame_insertion = settings->uints.video_black_frame_insertion; float video_refresh_rate = settings->floats.video_refresh_rate; XF86VidModeGetAllModeLines(dpy, DefaultScreen(dpy), &num_modes, &modes); @@ -283,7 +283,7 @@ static bool get_video_mode( /* If we use black frame insertion, we fake a 60 Hz monitor * for 120 Hz one, etc, so try to match that. */ - refresh_mod = black_frame_insertion ? 0.5f : 1.0f; + refresh_mod = 1.0f / (black_frame_insertion + 1.0f); for (i = 0; i < num_modes; i++) { diff --git a/gfx/drivers/d3d8.c b/gfx/drivers/d3d8.c index ea485e64cc7c..89d86280cb74 100644 --- a/gfx/drivers/d3d8.c +++ b/gfx/drivers/d3d8.c @@ -1497,7 +1497,7 @@ static bool d3d8_frame(void *data, const void *frame, &video_info->osd_stat_params; const char *stat_text = video_info->stat_text; bool statistics_show = video_info->statistics_show; - bool black_frame_insertion = video_info->black_frame_insertion; + unsigned black_frame_insertion = video_info->black_frame_insertion; #ifdef HAVE_MENU bool menu_is_alive = video_info->menu_is_alive; #endif @@ -1538,14 +1538,6 @@ static bool d3d8_frame(void *data, const void *frame, d3d8_set_viewports(d3d->dev, &screen_vp); d3d8_clear(d3d->dev, 0, 0, D3DCLEAR_TARGET, 0, 1, 0); - /* Insert black frame first, so we - * can screenshot, etc. */ - if (black_frame_insertion) - { - if (!d3d8_swap(d3d, d3d->dev) || d3d->needs_restore) - return true; - d3d8_clear(d3d->dev, 0, 0, D3DCLEAR_TARGET, 0, 1, 0); - } if (!d3d8_renderchain_render( d3d, @@ -1556,6 +1548,17 @@ static bool d3d8_frame(void *data, const void *frame, return false; } + if (black_frame_insertion && !d3d->menu->enabled) + { + unsigned n; + for (n = 0; n < video_info->black_frame_insertion; ++n) + { + if (!d3d8_swap(d3d, d3d->dev) || d3d->needs_restore) + return true; + d3d8_clear(d3d->dev, 0, 0, D3DCLEAR_TARGET, 0, 1, 0); + } + } + #ifdef HAVE_MENU if (d3d->menu && d3d->menu->enabled) { diff --git a/gfx/drivers/d3d9.c b/gfx/drivers/d3d9.c index 19f2aa71a279..f05e74d56de0 100644 --- a/gfx/drivers/d3d9.c +++ b/gfx/drivers/d3d9.c @@ -1532,7 +1532,7 @@ static bool d3d9_frame(void *data, const void *frame, unsigned width = video_info->width; unsigned height = video_info->height; bool statistics_show = video_info->statistics_show; - bool black_frame_insertion = video_info->black_frame_insertion; + unsigned black_frame_insertion = video_info->black_frame_insertion; struct font_params *osd_params = (struct font_params*) &video_info->osd_stat_params; const char *stat_text = video_info->stat_text; @@ -1581,15 +1581,6 @@ static bool d3d9_frame(void *data, const void *frame, d3d9_set_viewports(d3d->dev, &screen_vp); d3d9_clear(d3d->dev, 0, 0, D3DCLEAR_TARGET, 0, 1, 0); - /* Insert black frame first, so we - * can screenshot, etc. */ - if (black_frame_insertion) - { - if (!d3d9_swap(d3d, d3d->dev) || d3d->needs_restore) - return true; - d3d9_clear(d3d->dev, 0, 0, D3DCLEAR_TARGET, 0, 1, 0); - } - if (!d3d->renderchain_driver->render( d3d, frame, frame_width, frame_height, pitch, d3d->dev_rotation)) @@ -1597,6 +1588,17 @@ static bool d3d9_frame(void *data, const void *frame, RARCH_ERR("[D3D9]: Failed to render scene.\n"); return false; } + + if (black_frame_insertion && !d3d->menu->enabled) + { + unsigned n; + for (n = 0; n < video_info->black_frame_insertion; ++n) + { + if (!d3d9_swap(d3d, d3d->dev) || d3d->needs_restore) + return true; + d3d9_clear(d3d->dev, 0, 0, D3DCLEAR_TARGET, 0, 1, 0); + } + } #ifdef HAVE_MENU if (d3d->menu && d3d->menu->enabled) diff --git a/gfx/drivers/gl.c b/gfx/drivers/gl.c index 46ab5c5295b5..ccb8d3a2c1c5 100644 --- a/gfx/drivers/gl.c +++ b/gfx/drivers/gl.c @@ -2808,6 +2808,7 @@ static bool gl2_frame(void *data, const void *frame, bool use_rgba = video_info->use_rgba; bool statistics_show = video_info->statistics_show; bool msg_bgcolor_enable = video_info->msg_bgcolor_enable; + unsigned black_frame_insertion = video_info->black_frame_insertion; bool input_driver_nonblock_state = video_info->input_driver_nonblock_state; bool hard_sync = video_info->hard_sync; unsigned hard_sync_frames = video_info->hard_sync_frames; @@ -2823,7 +2824,6 @@ static bool gl2_frame(void *data, const void *frame, #ifndef EMSCRIPTEN bool runloop_is_slowmotion = video_info->runloop_is_slowmotion; bool runloop_is_paused = video_info->runloop_is_paused; - bool black_frame_insertion = video_info->black_frame_insertion; #endif if (!gl) @@ -3088,24 +3088,31 @@ static bool gl2_frame(void *data, const void *frame, #endif gl2_pbo_async_readback(gl); - /* Emscripten has to do black frame insertion in its main loop */ + if (gl->ctx_driver->swap_buffers) + gl->ctx_driver->swap_buffers(gl->ctx_data); + + /* Emscripten has to do black frame insertion in its main loop */ #ifndef EMSCRIPTEN /* Disable BFI during fast forward, slow-motion, * and pause to prevent flicker. */ - if ( + if ( black_frame_insertion && !input_driver_nonblock_state && !runloop_is_slowmotion - && !runloop_is_paused) - { - if (gl->ctx_driver->swap_buffers) - gl->ctx_driver->swap_buffers(gl->ctx_data); - glClear(GL_COLOR_BUFFER_BIT); - } -#endif - - if (gl->ctx_driver->swap_buffers) - gl->ctx_driver->swap_buffers(gl->ctx_data); + && !runloop_is_paused + && !gl->menu_texture_enable) + { + unsigned n; + for (n = 0; n < black_frame_insertion; ++n) + { + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + if (gl->ctx_driver->swap_buffers) + gl->ctx_driver->swap_buffers(gl->ctx_data); + } + } +#endif /* check if we are fast forwarding or in menu, * if we are ignore hard sync */ diff --git a/gfx/drivers/gl1.c b/gfx/drivers/gl1.c index d92cf3c549e1..0904ef2b2394 100644 --- a/gfx/drivers/gl1.c +++ b/gfx/drivers/gl1.c @@ -899,7 +899,11 @@ static bool gl1_gfx_frame(void *data, const void *frame, 4, GL_RGBA, GL_UNSIGNED_BYTE, gl1->readback_buffer_screenshot); - /* Emscripten has to do black frame insertion in its main loop */ + + if (gl1->ctx_driver->swap_buffers) + gl1->ctx_driver->swap_buffers(gl1->ctx_data); + + /* Emscripten has to do black frame insertion in its main loop */ #ifndef EMSCRIPTEN /* Disable BFI during fast forward, slow-motion, * and pause to prevent flicker. */ @@ -907,16 +911,21 @@ static bool gl1_gfx_frame(void *data, const void *frame, video_info->black_frame_insertion && !video_info->input_driver_nonblock_state && !video_info->runloop_is_slowmotion - && !video_info->runloop_is_paused) + && !video_info->runloop_is_paused + && !gl1->menu_texture_enable) { - if (gl1->ctx_driver->swap_buffers) - gl1->ctx_driver->swap_buffers(gl1->ctx_data); - glClear(GL_COLOR_BUFFER_BIT); - } -#endif - if (gl1->ctx_driver->swap_buffers) - gl1->ctx_driver->swap_buffers(gl1->ctx_data); + unsigned n; + for (n = 0; n < video_info->black_frame_insertion; ++n) + { + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + if (gl1->ctx_driver->swap_buffers) + gl1->ctx_driver->swap_buffers(gl1->ctx_data); + } + } +#endif /* check if we are fast forwarding or in menu, if we are ignore hard sync */ if (hard_sync diff --git a/gfx/drivers/gl_core.c b/gfx/drivers/gl_core.c index da3d383c7b9f..6ae26a4869cc 100644 --- a/gfx/drivers/gl_core.c +++ b/gfx/drivers/gl_core.c @@ -1853,7 +1853,8 @@ static bool gl_core_frame(void *data, const void *frame, const char *stat_text = video_info->stat_text; bool statistics_show = video_info->statistics_show; bool msg_bgcolor_enable = video_info->msg_bgcolor_enable; - bool black_frame_insertion = video_info->black_frame_insertion; + unsigned black_frame_insertion = video_info->black_frame_insertion; + unsigned hard_sync_frames = video_info->hard_sync_frames; bool runloop_is_paused = video_info->runloop_is_paused; bool runloop_is_slowmotion = video_info->runloop_is_slowmotion; @@ -1989,21 +1990,32 @@ static bool gl_core_frame(void *data, const void *frame, gl_core_pbo_async_readback(gl); } + + if (gl->ctx_driver->swap_buffers) + gl->ctx_driver->swap_buffers(gl->ctx_data); + + /* Emscripten has to do black frame insertion in its main loop */ +#ifndef EMSCRIPTEN /* Disable BFI during fast forward, slow-motion, * and pause to prevent flicker. */ - if ( + if ( black_frame_insertion && !input_driver_nonblock_state && !runloop_is_slowmotion - && !runloop_is_paused) - { - if (gl->ctx_driver->swap_buffers) - gl->ctx_driver->swap_buffers(gl->ctx_data); - glClear(GL_COLOR_BUFFER_BIT); - } - - if (gl->ctx_driver->swap_buffers) - gl->ctx_driver->swap_buffers(gl->ctx_data); + && !runloop_is_paused + && !gl->menu_texture_enable) + { + unsigned n; + for (n = 0; n < black_frame_insertion; ++n) + { + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + if (gl->ctx_driver->swap_buffers) + gl->ctx_driver->swap_buffers(gl->ctx_data); + } + } +#endif if (hard_sync && !input_driver_nonblock_state && diff --git a/gfx/drivers/vulkan.c b/gfx/drivers/vulkan.c index d77b386066d0..7238143ea898 100644 --- a/gfx/drivers/vulkan.c +++ b/gfx/drivers/vulkan.c @@ -1724,7 +1724,7 @@ static bool vulkan_frame(void *data, const void *frame, unsigned height = video_info->height; bool statistics_show = video_info->statistics_show; const char *stat_text = video_info->stat_text; - bool black_frame_insertion = video_info->black_frame_insertion; + unsigned black_frame_insertion = video_info->black_frame_insertion; bool input_driver_nonblock_state = video_info->input_driver_nonblock_state; bool runloop_is_slowmotion = video_info->runloop_is_slowmotion; bool runloop_is_paused = video_info->runloop_is_paused; @@ -2258,18 +2258,25 @@ static bool vulkan_frame(void *data, const void *frame, vk->should_resize = false; } - vulkan_check_swapchain(vk); + vulkan_check_swapchain(vk); /* Disable BFI during fast forward, slow-motion, * and pause to prevent flicker. */ if ( - backbuffer->image != VK_NULL_HANDLE + backbuffer->image != VK_NULL_HANDLE && vk->context->has_acquired_swapchain && black_frame_insertion && !input_driver_nonblock_state && !runloop_is_slowmotion - && !runloop_is_paused) - vulkan_inject_black_frame(vk, video_info, vk->ctx_data); + && !runloop_is_paused + && !vk->menu.enable) + { + unsigned n; + for (n = 0; n < black_frame_insertion; ++n) + { + vulkan_inject_black_frame(vk, video_info, vk->ctx_data); + } + } /* Vulkan doesn't directly support swap_interval > 1, * so we fake it by duping out more frames. */ diff --git a/gfx/drivers_context/drm_ctx.c b/gfx/drivers_context/drm_ctx.c index 48c6038bbf02..7c055a5e5a5e 100644 --- a/gfx/drivers_context/drm_ctx.c +++ b/gfx/drivers_context/drm_ctx.c @@ -620,7 +620,7 @@ static bool gfx_ctx_drm_set_video_mode(void *data, struct drm_fb *fb = NULL; gfx_ctx_drm_data_t *drm = (gfx_ctx_drm_data_t*)data; settings_t *settings = config_get_ptr(); - bool black_frame_insertion = settings->bools.video_black_frame_insertion; + unsigned black_frame_insertion = settings->uints.video_black_frame_insertion; float video_refresh_rate = settings->floats.video_refresh_rate; if (!drm) @@ -631,8 +631,7 @@ static bool gfx_ctx_drm_set_video_mode(void *data, /* If we use black frame insertion, * we fake a 60 Hz monitor for 120 Hz one, * etc, so try to match that. */ - refresh_mod = black_frame_insertion - ? 0.5f : 1.0f; + refresh_mod = 1.0f / (black_frame_insertion + 1.0f); /* Find desired video mode, and use that. * If not fullscreen, we get desired windowed size, diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index d0541a00c7db..322cb573f367 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -5673,7 +5673,7 @@ unsigned menu_displaylist_build_list( count++; if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list, MENU_ENUM_LABEL_VIDEO_BLACK_FRAME_INSERTION, - PARSE_ONLY_BOOL, false) == 0) + PARSE_ONLY_UINT, false) == 0) count++; #ifdef HAVE_SCREENSHOTS if (video_driver_supports_viewport_read()) diff --git a/menu/menu_setting.c b/menu/menu_setting.c index 4a07c5770eb3..946da4877d34 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -10835,23 +10835,21 @@ static bool setting_append_list( #if !defined(RARCH_MOBILE) if (video_driver_test_all_flags(GFX_CTX_FLAGS_BLACK_FRAME_INSERTION)) { - CONFIG_BOOL( + + CONFIG_UINT( list, list_info, - &settings->bools.video_black_frame_insertion, + &settings->uints.video_black_frame_insertion, MENU_ENUM_LABEL_VIDEO_BLACK_FRAME_INSERTION, MENU_ENUM_LABEL_VALUE_VIDEO_BLACK_FRAME_INSERTION, DEFAULT_BLACK_FRAME_INSERTION, - MENU_ENUM_LABEL_VALUE_OFF, - MENU_ENUM_LABEL_VALUE_ON, &group_info, &subgroup_info, parent_group, general_write_handler, - general_read_handler, - SD_FLAG_NONE - ); - SETTINGS_DATA_LIST_CURRENT_ADD_FLAGS(list, list_info, SD_FLAG_LAKKA_ADVANCED); - } + general_read_handler); + (*list)[list_info->index - 1].action_ok = &setting_action_ok_uint; + menu_settings_list_current_add_range(list, list_info, 0, 5, 1, true, true); + } #endif END_SUB_GROUP(list, list_info, parent_group); START_SUB_GROUP( diff --git a/retroarch.c b/retroarch.c index d539d3d4a483..b7df94c54656 100644 --- a/retroarch.c +++ b/retroarch.c @@ -17842,7 +17842,7 @@ void emscripten_mainloop(void) static unsigned emscripten_frame_count = 0; struct rarch_state *p_rarch = &rarch_st; settings_t *settings = p_rarch->configuration_settings; - bool black_frame_insertion = settings->bools.video_black_frame_insertion; + bool black_frame_insertion = settings->uints.video_black_frame_insertion; bool input_driver_nonblock_state = p_rarch->input_driver_nonblock_state; bool runloop_is_slowmotion = p_rarch->runloop_slowmotion; bool runloop_is_paused = p_rarch->runloop_paused; @@ -17859,7 +17859,7 @@ void emscripten_mainloop(void) && !runloop_is_slowmotion && !runloop_is_paused) { - if ((emscripten_frame_count & 1) == 0) + if ((emscripten_frame_count % (black_frame_insertion+1)) != 0) { glClear(GL_COLOR_BUFFER_BIT); if (p_rarch->current_video_context.swap_buffers) @@ -33869,7 +33869,7 @@ void video_driver_build_info(video_frame_info_t *video_info) video_info->crt_switch_resolution_super = settings->uints.crt_switch_resolution_super; video_info->crt_switch_center_adjust = settings->ints.crt_switch_center_adjust; video_info->crt_switch_porch_adjust = settings->ints.crt_switch_porch_adjust; - video_info->black_frame_insertion = settings->bools.video_black_frame_insertion; + video_info->black_frame_insertion = settings->uints.video_black_frame_insertion; video_info->hard_sync = settings->bools.video_hard_sync; video_info->hard_sync_frames = settings->uints.video_hard_sync_frames; video_info->fps_show = settings->bools.video_fps_show; @@ -39740,7 +39740,7 @@ static enum runloop_state runloop_check_state( if (p_rarch->runloop_slowmotion) { - if (settings->bools.video_black_frame_insertion) + if (settings->uints.video_black_frame_insertion) if (!p_rarch->runloop_idle) video_driver_cached_frame(); diff --git a/retroarch.h b/retroarch.h index 6a9aee109624..26d203306e1a 100644 --- a/retroarch.h +++ b/retroarch.h @@ -1137,6 +1137,7 @@ typedef struct video_frame_info unsigned custom_vp_height; unsigned custom_vp_full_width; unsigned custom_vp_full_height; + unsigned black_frame_insertion; float menu_wallpaper_opacity; float menu_framebuffer_opacity; @@ -1178,7 +1179,6 @@ typedef struct video_frame_info bool widgets_is_rewinding; bool input_menu_swap_ok_cancel_buttons; bool input_driver_nonblock_state; - bool black_frame_insertion; bool hard_sync; bool fps_show; bool memory_show;