diff --git a/config.def.h b/config.def.h index fbe30149cc..aee8eb6aee 100644 --- a/config.def.h +++ b/config.def.h @@ -225,6 +225,11 @@ static const bool vsync = true; // Attempts to hard-synchronize CPU and GPU. Can reduce latency at cost of performance. static const bool hard_sync = false; +// Configures how many frames the GPU can run ahead of CPU. +// 0: Syncs to GPU immediately. +// 1: Syncs to previous frame. +// 2: Etc ... +static const unsigned hard_sync_frames = 0; // Threaded video. Will possibly increase performance significantly at cost of worse synchronization and latency. static const bool video_threaded = false; diff --git a/frontend/menu/menu_common.h b/frontend/menu/menu_common.h index 96ffd6845b..382baeca2b 100644 --- a/frontend/menu/menu_common.h +++ b/frontend/menu/menu_common.h @@ -117,6 +117,7 @@ typedef enum RGUI_SETTINGS_VIDEO_ROTATION, RGUI_SETTINGS_VIDEO_VSYNC, RGUI_SETTINGS_VIDEO_HARD_SYNC, + RGUI_SETTINGS_VIDEO_HARD_SYNC_FRAMES, RGUI_SETTINGS_VIDEO_OPTIONS_LAST, #ifdef HAVE_SHADER_MANAGER RGUI_SETTINGS_SHADER_OPTIONS, diff --git a/frontend/menu/rgui.c b/frontend/menu/rgui.c index eea15b03f7..3c748f5b74 100644 --- a/frontend/menu/rgui.c +++ b/frontend/menu/rgui.c @@ -592,10 +592,13 @@ static void render_text(rgui_handle_t *rgui) snprintf(type_str, sizeof(type_str), "%d", g_extern.console.screen.gamma_correction); break; case RGUI_SETTINGS_VIDEO_VSYNC: - snprintf(type_str, sizeof(type_str), g_settings.video.vsync ? "ON" : "OFF"); + strlcpy(type_str, g_settings.video.vsync ? "ON" : "OFF", sizeof(type_str)); break; case RGUI_SETTINGS_VIDEO_HARD_SYNC: - snprintf(type_str, sizeof(type_str), g_settings.video.hard_sync ? "ON" : "OFF"); + strlcpy(type_str, g_settings.video.hard_sync ? "ON" : "OFF", sizeof(type_str)); + break; + case RGUI_SETTINGS_VIDEO_HARD_SYNC_FRAMES: + snprintf(type_str, sizeof(type_str), "%u", g_settings.video.hard_sync_frames); break; case RGUI_SETTINGS_VIDEO_INTEGER_SCALE: strlcpy(type_str, g_settings.video.scale_integer ? "ON" : "OFF", sizeof(type_str)); @@ -1499,6 +1502,7 @@ static void rgui_settings_video_options_populate_entries(rgui_handle_t *rgui) rgui_list_push(rgui->selection_buf, "Rotation", RGUI_SETTINGS_VIDEO_ROTATION, 0); rgui_list_push(rgui->selection_buf, "VSync", RGUI_SETTINGS_VIDEO_VSYNC, 0); rgui_list_push(rgui->selection_buf, "Hard GPU Sync", RGUI_SETTINGS_VIDEO_HARD_SYNC, 0); + rgui_list_push(rgui->selection_buf, "Hard GPU Sync Frames", RGUI_SETTINGS_VIDEO_HARD_SYNC_FRAMES, 0); } #ifdef HAVE_SHADER_MANAGER @@ -1927,6 +1931,29 @@ static int video_option_toggle_setting(rgui_handle_t *rgui, unsigned setting, rg } break; + case RGUI_SETTINGS_VIDEO_HARD_SYNC_FRAMES: + switch (action) + { + case RGUI_ACTION_START: + g_settings.video.hard_sync_frames = 0; + break; + + case RGUI_ACTION_LEFT: + if (g_settings.video.hard_sync_frames > 0) + g_settings.video.hard_sync_frames--; + break; + + case RGUI_ACTION_RIGHT: + case RGUI_ACTION_OK: + if (g_settings.video.hard_sync_frames < 3) + g_settings.video.hard_sync_frames++; + break; + + default: + break; + } + break; + default: break; } diff --git a/general.h b/general.h index 4d253f450b..6f43980f1d 100644 --- a/general.h +++ b/general.h @@ -155,6 +155,7 @@ struct settings unsigned fullscreen_y; bool vsync; bool hard_sync; + unsigned hard_sync_frames; bool smooth; bool force_aspect; bool crop_overscan; diff --git a/gfx/gl.c b/gfx/gl.c index fd3ab5f6d2..5c790817b2 100644 --- a/gfx/gl.c +++ b/gfx/gl.c @@ -1546,9 +1546,17 @@ static bool gl_frame(void *data, const void *frame, unsigned width, unsigned hei RARCH_PERFORMANCE_INIT(gl_fence); RARCH_PERFORMANCE_START(gl_fence); glClear(GL_COLOR_BUFFER_BIT); - GLsync sync = pglFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); - pglClientWaitSync(sync, GL_SYNC_FLUSH_COMMANDS_BIT, 1000000000); - pglDeleteSync(sync); + gl->fences[gl->fence_count++] = pglFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + + while (gl->fence_count > g_settings.video.hard_sync_frames) + { + pglClientWaitSync(gl->fences[0], GL_SYNC_FLUSH_COMMANDS_BIT, 1000000000); + pglDeleteSync(gl->fences[0]); + + gl->fence_count--; + memmove(gl->fences, gl->fences + 1, gl->fence_count * sizeof(GLsync)); + } + RARCH_PERFORMANCE_STOP(gl_fence); } #endif @@ -1565,6 +1573,18 @@ static void gl_free(void *data) gl_t *gl = (gl_t*)data; +#ifdef HAVE_GL_SYNC + if (gl->have_sync) + { + for (unsigned i = 0; i < gl->fence_count; i++) + { + pglClientWaitSync(gl->fences[i], GL_SYNC_FLUSH_COMMANDS_BIT, 1000000000); + pglDeleteSync(gl->fences[i]); + } + gl->fence_count = 0; + } +#endif + if (gl->font_ctx) gl->font_ctx->deinit(gl); gl_shader_deinit(gl); diff --git a/gfx/gl_common.h b/gfx/gl_common.h index 0e9d2f9eac..42e1313f80 100644 --- a/gfx/gl_common.h +++ b/gfx/gl_common.h @@ -284,7 +284,10 @@ typedef struct gl #endif #ifdef HAVE_GL_SYNC +#define MAX_FENCES 4 bool have_sync; + GLsync fences[MAX_FENCES]; + unsigned fence_count; #endif } gl_t; diff --git a/retroarch.cfg b/retroarch.cfg index 46b14c9542..13aceb964e 100644 --- a/retroarch.cfg +++ b/retroarch.cfg @@ -76,6 +76,10 @@ # Attempts to hard-synchronize CPU and GPU. Can reduce latency at cost of performance. # video_hard_sync = false +# Sets how many frames CPU can run ahead of GPU when using video_hard_sync. +# Maximum is 3. +# video_hard_sync_frames = 0 + # Use threaded video driver. Using this might improve performance at possible cost of latency and more video stuttering. # video_threaded = false diff --git a/settings.c b/settings.c index 4850f0b972..1c9c33d1ef 100644 --- a/settings.c +++ b/settings.c @@ -165,6 +165,7 @@ void config_set_defaults(void) g_settings.video.disable_composition = disable_composition; g_settings.video.vsync = vsync; g_settings.video.hard_sync = hard_sync; + g_settings.video.hard_sync_frames = hard_sync_frames; g_settings.video.threaded = video_threaded; g_settings.video.smooth = video_smooth; g_settings.video.force_aspect = force_aspect; @@ -469,6 +470,11 @@ bool config_load_file(const char *path) CONFIG_GET_BOOL(video.disable_composition, "video_disable_composition"); CONFIG_GET_BOOL(video.vsync, "video_vsync"); CONFIG_GET_BOOL(video.hard_sync, "video_hard_sync"); + + CONFIG_GET_INT(video.hard_sync_frames, "video_hard_sync_frames"); + if (g_settings.video.hard_sync_frames > 3) + g_settings.video.hard_sync_frames = 3; + CONFIG_GET_BOOL(video.threaded, "video_threaded"); CONFIG_GET_BOOL(video.smooth, "video_smooth"); CONFIG_GET_BOOL(video.force_aspect, "video_force_aspect"); @@ -954,6 +960,7 @@ bool config_save_file(const char *path) config_set_bool(conf, "video_smooth", g_settings.video.smooth); config_set_bool(conf, "video_vsync", g_settings.video.vsync); config_set_bool(conf, "video_hard_sync", g_settings.video.hard_sync); + config_set_int(conf, "video_hard_sync_frames", g_settings.video.hard_sync_frames); config_set_int(conf, "aspect_ratio_index", g_settings.video.aspect_ratio_idx); config_set_string(conf, "audio_device", g_settings.audio.device); config_set_bool(conf, "audio_rate_control", g_settings.audio.rate_control);