diff --git a/Makefile.common b/Makefile.common index cde29a815d..37544e28f2 100644 --- a/Makefile.common +++ b/Makefile.common @@ -1391,7 +1391,8 @@ OBJ += gfx/drivers_context/gfx_null_ctx.o ifeq ($(HAVE_KMS), 1) HAVE_AND_WILL_USE_DRM = 1 - OBJ += gfx/drivers_context/drm_ctx.o + OBJ += gfx/drivers_context/drm_ctx.o \ + gfx/display_servers/dispserv_kms.o ifeq ($(HAVE_ODROIDGO2), 1) OBJ += gfx/drivers_context/drm_go2_ctx.o diff --git a/gfx/display_servers/dispserv_kms.c b/gfx/display_servers/dispserv_kms.c new file mode 100644 index 0000000000..e434501847 --- /dev/null +++ b/gfx/display_servers/dispserv_kms.c @@ -0,0 +1,233 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2017 - Daniel De Matteis + * Copyright (C) 2016-2019 - Brad Parker + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include + +#include +#include + +#include +#include + +#ifdef HAVE_CONFIG_H +#include "../../config.h" +#endif + + +#include "../video_display_server.h" +#include "../../retroarch.h" +#include "../video_crt_switch.h" /* Needed to set aspect for low resolution in Linux */ +#include "../common/drm_common.h" +#include "../../verbosity.h" + +typedef struct +{ + int crt_name_id; + int monitor_index; + unsigned opacity; + uint8_t flags; + char crt_name[16]; + char new_mode[256]; + char old_mode[256]; + char orig_output[256]; +} dispserv_kms_t; + +static bool kms_display_server_set_resolution(void *data, + unsigned width, unsigned height, int int_hz, float hz, + int center, int monitor_index, int xoffset, int padjust) +{ + unsigned curr_width = 0; + unsigned curr_height = 0; + unsigned curr_bpp = 0; + float curr_refreshrate = 0; + bool retval = false; + int reinit_flags = DRIVERS_CMD_ALL; + dispserv_kms_t *dispserv = (dispserv_kms_t*)data; + + if (!dispserv) + return false; + + if (g_drm_mode) + { + curr_refreshrate = drm_calc_refresh_rate(g_drm_mode); + curr_width = g_drm_mode->hdisplay; + curr_height = g_drm_mode->vdisplay; + curr_bpp = 32; + } + RARCH_DBG("[DRM]: Display server set resolution - incoming: %d x %d, %f Hz\n",width, height, hz); + + if (width == 0) + width = curr_width; + if (height == 0) + height = curr_height; + if (curr_bpp == 0) + curr_bpp = curr_bpp; + if (hz == 0) + hz = curr_refreshrate; + + /* set core refresh from hz */ + video_monitor_set_refresh_rate(hz); + + RARCH_DBG("[DRM]: Display server set resolution - actual: %d x %d, %f Hz\n",width, height, hz); + + retval = video_driver_set_video_mode(width, height, true); + + /* Reinitialize drivers. */ + command_event(CMD_EVENT_REINIT, &reinit_flags); + + return retval; +} + +/* TODO: move to somewhere common as it is reused from dispserv_win32.c */ +/* Display resolution list qsort helper function */ +static int resolution_list_qsort_func( + const video_display_config_t *a, const video_display_config_t *b) +{ + char str_a[64]; + char str_b[64]; + + if (!a || !b) + return 0; + + str_a[0] = str_b[0] = '\0'; + + snprintf(str_a, sizeof(str_a), "%04dx%04d (%d Hz)", + a->width, + a->height, + a->refreshrate); + + snprintf(str_b, sizeof(str_b), "%04dx%04d (%d Hz)", + b->width, + b->height, + b->refreshrate); + + return strcasecmp(str_a, str_b); +} + +static void *kms_display_server_get_resolution_list( + void *data, unsigned *len) +{ + unsigned i = 0; + unsigned j = 0; + unsigned count = 0; + unsigned curr_width = 0; + unsigned curr_height = 0; + unsigned curr_bpp = 0; + float curr_refreshrate = 0; + unsigned curr_orientation = 0; + struct video_display_config *conf = NULL; + + + if (g_drm_mode) + { + curr_refreshrate = drm_calc_refresh_rate(g_drm_mode); + curr_width = g_drm_mode->hdisplay; + curr_height = g_drm_mode->vdisplay; + curr_bpp = 32; + } + + *len = g_drm_connector->count_modes; + if (!(conf = (struct video_display_config*) + calloc(*len, sizeof(struct video_display_config)))) + return NULL; + + for (i = 0, j = 0; (int)i < g_drm_connector->count_modes; i++) + { + conf[j].width = g_drm_connector->modes[i].hdisplay; + conf[j].height = g_drm_connector->modes[i].vdisplay; + conf[j].bpp = 32; + conf[j].refreshrate = floor(drm_calc_refresh_rate(&g_drm_connector->modes[i])); + conf[j].idx = j; + conf[j].current = false; + + if ( (conf[j].width == curr_width) + && (conf[j].height == curr_height) + && (conf[j].bpp == curr_bpp) + && (drm_calc_refresh_rate(&g_drm_connector->modes[i]) == curr_refreshrate) + ) + conf[j].current = true; + j++; + } + + qsort( + conf, count, + sizeof(video_display_config_t), + (int (*)(const void *, const void *)) + resolution_list_qsort_func); + + return conf; +} + +/* TODO: screen orientation has support in DRM via planes, although not really exposed via xf86drm */ +#if 0 +static void kms_display_server_set_screen_orientation(void *data, + enum rotation rotation) +{ +} + +static enum rotation kms_display_server_get_screen_orientation(void *data) +{ + int i, j; + enum rotation rotation = ORIENTATION_NORMAL; + dispserv_kms_t *dispserv = (dispserv_kms_t*)data; + return rotation; +} +#endif + +static void* kms_display_server_init(void) +{ + dispserv_kms_t *dispserv = (dispserv_kms_t*)calloc(1, sizeof(*dispserv)); + + if (dispserv) + return dispserv; + return NULL; +} + +static void kms_display_server_destroy(void *data) +{ + dispserv_kms_t *dispserv = (dispserv_kms_t*)data; + if (dispserv) + free(dispserv); +} + +static bool kms_display_server_set_window_opacity(void *data, unsigned opacity) +{ + return true; +} + +static uint32_t kms_display_server_get_flags(void *data) +{ + uint32_t flags = 0; + BIT32_SET(flags, DISPSERV_CTX_CRT_SWITCHRES); + + return flags; +} + +const video_display_server_t dispserv_kms = { + kms_display_server_init, + kms_display_server_destroy, + kms_display_server_set_window_opacity, + NULL, /* set_window_progress */ + NULL, /* set window decorations */ + kms_display_server_set_resolution, + kms_display_server_get_resolution_list, + NULL, /* get output options */ + NULL, /* kms_display_server_set_screen_orientation */ + NULL, /* kms_display_server_get_screen_orientation */ + kms_display_server_get_flags, + "kms" +}; diff --git a/gfx/drivers_context/drm_ctx.c b/gfx/drivers_context/drm_ctx.c index 5b59e4c129..c11c5e90c8 100644 --- a/gfx/drivers_context/drm_ctx.c +++ b/gfx/drivers_context/drm_ctx.c @@ -634,6 +634,19 @@ static void gfx_ctx_drm_get_video_size(void *data, *height = drm->fb_height; } +static void gfx_ctx_drm_get_video_output_size(void *data, + unsigned *width, unsigned *height, char *desc, size_t desc_len) +{ + gfx_ctx_drm_data_t *drm = (gfx_ctx_drm_data_t*)data; + + if (!drm) + return; + + *width = drm->fb_width; + *height = drm->fb_height; + +} + static void free_drm_resources(gfx_ctx_drm_data_t *drm) { if (!drm) @@ -783,6 +796,8 @@ nextgpu: g_drm_fd = fd; + video_driver_display_type_set(RARCH_DISPLAY_KMS); + return drm; error: @@ -831,7 +846,10 @@ static bool gfx_ctx_drm_set_video_mode(void *data, return true; } if ((width == 0 && height == 0) || !fullscreen) + { g_drm_mode = &g_drm_connector->modes[0]; + RARCH_WARN("[KMS]: Falling back to mode 0 (default)\n"); + } else { /* check if custom HDMI timings were asked */ @@ -916,6 +934,7 @@ static bool gfx_ctx_drm_set_video_mode(void *data, error: gfx_ctx_drm_destroy_resources(drm); + RARCH_ERR("[KMS]: Error when switching mode.\n"); if (drm) free(drm); @@ -1074,7 +1093,7 @@ const gfx_ctx_driver_t gfx_ctx_drm = { gfx_ctx_drm_set_video_mode, gfx_ctx_drm_get_video_size, drm_get_refresh_rate, - NULL, /* get_video_output_size */ + gfx_ctx_drm_get_video_output_size, NULL, /* get_video_output_prev */ NULL, /* get_video_output_next */ NULL, /* get_metrics */ diff --git a/gfx/video_defines.h b/gfx/video_defines.h index 6bf67c7ccb..0dcdc694e3 100644 --- a/gfx/video_defines.h +++ b/gfx/video_defines.h @@ -101,7 +101,8 @@ enum rarch_display_type /* video_display => N/A, video_window => HWND */ RARCH_DISPLAY_WIN32, RARCH_DISPLAY_WAYLAND, - RARCH_DISPLAY_OSX + RARCH_DISPLAY_OSX, + RARCH_DISPLAY_KMS }; enum font_driver_render_api diff --git a/gfx/video_display_server.h b/gfx/video_display_server.h index 52392a1736..f29ac527e3 100644 --- a/gfx/video_display_server.h +++ b/gfx/video_display_server.h @@ -99,6 +99,7 @@ enum rotation video_display_server_get_screen_orientation(void); extern const video_display_server_t dispserv_win32; extern const video_display_server_t dispserv_x11; +extern const video_display_server_t dispserv_kms; extern const video_display_server_t dispserv_android; RETRO_END_DECLS diff --git a/gfx/video_driver.c b/gfx/video_driver.c index 160029c4c1..3b03f81fe7 100644 --- a/gfx/video_driver.c +++ b/gfx/video_driver.c @@ -686,21 +686,22 @@ void video_monitor_compute_fps_statistics(uint64_t void video_monitor_set_refresh_rate(float hz) { - char msg[128]; + char msg[256]; + char rate[8]; settings_t *settings = config_get_ptr(); - /* TODO/FIXME - localize */ - size_t _len = strlcpy(msg, "Setting refresh rate to", sizeof(msg)); - msg[_len ] = ':'; - msg[++_len] = ' '; - msg[++_len] = '\0'; - _len += snprintf(msg + _len, sizeof(msg) - _len, "%.3f", hz); - msg[_len ] = ' '; - msg[_len+1] = 'H'; - msg[_len+2] = 'z'; - msg[_len+3] = '.'; - msg[_len+4] = '\0'; + + /* Avoid message spamming if there is no change. */ + if (settings->floats.video_refresh_rate == hz) + return; + + snprintf(rate, sizeof(rate), "%.3f", hz); + snprintf(msg, sizeof(msg), + msg_hash_to_str(MSG_VIDEO_REFRESH_RATE_CHANGED), rate); + + /* Message is visible for twice the usual duration */ + /* as modeswitch will cause monitors to go blank for a while */ if (settings->bools.notification_show_refresh_rate) - runloop_msg_queue_push(msg, 1, 180, false, NULL, + runloop_msg_queue_push(msg, 1, 360, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); RARCH_LOG("[Video]: %s\n", msg); @@ -1064,6 +1065,11 @@ void* video_display_server_init(enum rarch_display_type type) case RARCH_DISPLAY_X11: #if defined(HAVE_X11) current_display_server = &dispserv_x11; +#endif + break; + case RARCH_DISPLAY_KMS: +#if defined(HAVE_KMS) + current_display_server = &dispserv_kms; #endif break; default: @@ -1140,6 +1146,7 @@ bool video_display_server_set_resolution(unsigned width, unsigned height, int int_hz, float hz, int center, int monitor_index, int xoffset, int padjust) { video_driver_state_t *video_st = &video_driver_st; + RARCH_DBG("[Video]: Display server set resolution, hz: %f\n",hz); if (current_display_server && current_display_server->set_resolution) return current_display_server->set_resolution( video_st->current_display_server_data, width, height, int_hz, @@ -1254,6 +1261,7 @@ void video_switch_refresh_rate_maybe( bool video_display_server_set_refresh_rate(float hz) { video_driver_state_t *video_st = &video_driver_st; + RARCH_DBG("[Video]: Display server set refresh rate %f\n", hz); if (current_display_server && current_display_server->set_resolution) return current_display_server->set_resolution( video_st->current_display_server_data, 0, 0, (int)hz, @@ -1270,7 +1278,7 @@ void video_display_server_restore_refresh_rate(void) if (!refresh_rate_original || refresh_rate_current == refresh_rate_original) return; - + RARCH_DBG("[Video]: Display server restore refresh rate %f\n", refresh_rate_original); video_monitor_set_refresh_rate(refresh_rate_original); video_display_server_set_refresh_rate(refresh_rate_original); } diff --git a/griffin/griffin.c b/griffin/griffin.c index 3b5e667c72..9e7eb0bf65 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -311,6 +311,7 @@ VIDEO CONTEXT #if defined(HAVE_KMS) #include "../gfx/drivers_context/drm_ctx.c" +#include "../gfx/display_servers/dispserv_kms.c" #endif #if defined(HAVE_EGL) diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index d984796d37..359b576236 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -14713,6 +14713,10 @@ MSG_HASH( MSG_VRR_RUNLOOP_DISABLED, "Sync to exact content framerate disabled." ) +MSG_HASH( + MSG_VIDEO_REFRESH_RATE_CHANGED, + "Video refresh rate changed to %s Hz." + ) /* Lakka */ @@ -14797,7 +14801,7 @@ MSG_HASH( ) MSG_HASH( MENU_ENUM_SUBLABEL_SCREEN_RESOLUTION, - "Select display mode." + "Select display mode (Restart required)" ) MSG_HASH( MENU_ENUM_LABEL_VALUE_SHUTDOWN, diff --git a/msg_hash.h b/msg_hash.h index 548e47197b..240f7eb5ac 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -560,6 +560,7 @@ enum msg_hash_enums MSG_FAILED_TO_ENTER_GAMEMODE_LINUX, MSG_VRR_RUNLOOP_ENABLED, MSG_VRR_RUNLOOP_DISABLED, + MSG_VIDEO_REFRESH_RATE_CHANGED, MSG_IOS_TOUCH_MOUSE_ENABLED, MSG_IOS_TOUCH_MOUSE_DISABLED, diff --git a/retroarch.c b/retroarch.c index 125f693280..f029da52c8 100644 --- a/retroarch.c +++ b/retroarch.c @@ -802,6 +802,7 @@ void drivers_init( bool windowed_fullscreen = settings->bools.video_fullscreen && settings->bools.video_windowed_fullscreen; bool all_fullscreen = settings->bools.video_fullscreen || settings->bools.video_windowed_fullscreen; + /* Making a switch from PC standard 60 Hz to NTSC 59.94 is excluded by the last condition. */ if ( refresh_rate > 0.0 && !settings->uints.crt_switch_resolution && !settings->bools.vrr_runloop_enable @@ -7013,6 +7014,7 @@ bool retroarch_main_quit(void) video_driver_state_t*video_st = video_state_get_ptr(); settings_t *settings = config_get_ptr(); bool config_save_on_exit = settings->bools.config_save_on_exit; + struct retro_system_av_info *av_info = &video_st->av_info; /* Restore video driver before saving */ video_driver_restore_cached(settings); @@ -7052,9 +7054,15 @@ bool retroarch_main_quit(void) /* Restore original refresh rate, if it has been changed * automatically in SET_SYSTEM_AV_INFO */ - if (video_st->video_refresh_rate_original) - video_display_server_restore_refresh_rate(); + if (video_st->video_refresh_rate_original) + { + RARCH_DBG("[Video]: Restoring original refresh rate: %f Hz\n", video_st->video_refresh_rate_original); + /* Set the av_info fps also to the original refresh rate */ + /* to avoid re-initialization problems */ + av_info->timing.fps = video_st->video_refresh_rate_original; + video_display_server_restore_refresh_rate(); + } if (!(runloop_st->flags & RUNLOOP_FLAG_SHUTDOWN_INITIATED)) { /* Save configs before quitting diff --git a/runloop.c b/runloop.c index 49124726c1..a396ddc3a6 100644 --- a/runloop.c +++ b/runloop.c @@ -3912,8 +3912,13 @@ void runloop_event_deinit_core(void) /* Restore original refresh rate, if it has been changed * automatically in SET_SYSTEM_AV_INFO */ if (video_st->video_refresh_rate_original) + { + /* Set the av_info fps also to the original refresh rate */ + /* to avoid re-initialization problems */ + struct retro_system_av_info *av_info = &video_st->av_info; + av_info->timing.fps = video_st->video_refresh_rate_original; video_display_server_restore_refresh_rate(); - + } /* Recalibrate frame delay target */ if (settings->bools.video_frame_delay_auto) video_st->frame_delay_target = 0;