diff --git a/config.def.h b/config.def.h index ccde7b1d66..708a55be1c 100644 --- a/config.def.h +++ b/config.def.h @@ -379,6 +379,8 @@ static unsigned rgui_thumbnail_downscaler = RGUI_THUMB_SCALE_POINT; static bool rgui_lock_aspect = false; +static unsigned rgui_internal_upscale_level = RGUI_UPSCALE_NONE; + #else static bool default_block_config_read = false; static bool automatically_add_content_to_playlist = false; diff --git a/configuration.c b/configuration.c index ef9b6c3259..0f413de803 100644 --- a/configuration.c +++ b/configuration.c @@ -1669,6 +1669,7 @@ static struct config_uint_setting *populate_settings_uint(settings_t *settings, #ifdef HAVE_RGUI SETTING_UINT("rgui_menu_color_theme", &settings->uints.menu_rgui_color_theme, true, rgui_color_theme, false); SETTING_UINT("rgui_thumbnail_downscaler", &settings->uints.menu_rgui_thumbnail_downscaler, true, rgui_thumbnail_downscaler, false); + SETTING_UINT("rgui_internal_upscale_level", &settings->uints.menu_rgui_internal_upscale_level, true, rgui_internal_upscale_level, false); #endif #ifdef HAVE_LIBNX SETTING_UINT("split_joycon_p1", &settings->uints.input_split_joycon[0], true, 0, false); diff --git a/configuration.h b/configuration.h index e0fb5e9cba..30b9e17a57 100644 --- a/configuration.h +++ b/configuration.h @@ -426,6 +426,7 @@ typedef struct settings unsigned menu_font_color_red; unsigned menu_font_color_green; unsigned menu_font_color_blue; + unsigned menu_rgui_internal_upscale_level; unsigned camera_width; unsigned camera_height; diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h index 891c3da8e7..85ee44e7c4 100644 --- a/intl/msg_hash_lbl.h +++ b/intl/msg_hash_lbl.h @@ -1595,6 +1595,8 @@ MSG_HASH(MENU_ENUM_LABEL_MENU_RGUI_BACKGROUND_FILLER_THICKNESS_ENABLE, "menu_rgui_background_filler_thickness_enable") MSG_HASH(MENU_ENUM_LABEL_MENU_RGUI_LOCK_ASPECT, "menu_rgui_lock_aspect") +MSG_HASH(MENU_ENUM_LABEL_MENU_RGUI_INTERNAL_UPSCALE_LEVEL, + "rgui_internal_upscale_level") MSG_HASH(MENU_ENUM_LABEL_CONTENT_SHOW_REWIND, "menu_show_rewind_settings") MSG_HASH(MENU_ENUM_LABEL_CONTENT_SHOW_LATENCY, diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 9f608faa49..80cf097929 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -2970,6 +2970,46 @@ MSG_HASH( MENU_ENUM_LABEL_VALUE_RGUI_THUMB_SCALE_SINC, "Sinc/Lanczos3 (Slow)" ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_RGUI_UPSCALE_NONE, + "None" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_RGUI_UPSCALE_AUTO, + "Auto" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_RGUI_UPSCALE_X2, + "x2" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_RGUI_UPSCALE_X3, + "x3" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_RGUI_UPSCALE_X4, + "x4" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_RGUI_UPSCALE_X5, + "x5" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_RGUI_UPSCALE_X6, + "x6" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_RGUI_UPSCALE_X7, + "x7" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_RGUI_UPSCALE_X8, + "x8" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_RGUI_UPSCALE_X9, + "x9" + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_THUMBNAILS_DIRECTORY, "Thumbnails" @@ -6692,6 +6732,14 @@ MSG_HASH( MENU_ENUM_SUBLABEL_MENU_RGUI_LOCK_ASPECT, "Ensures that the menu is always displayed with the correct aspect ratio. If disabled, the quick menu will be stretched to match the currently loaded content." ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_MENU_RGUI_INTERNAL_UPSCALE_LEVEL, + "Internal Upscaling" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_MENU_RGUI_INTERNAL_UPSCALE_LEVEL, + "Upscale menu interface before drawing to screen. When used with 'Menu Linear Filter' enabled, removes scaling artefacts (uneven pixels) while maintaining a sharp image. Has a significant performance impact that increases with upscaling level." + ) MSG_HASH( MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION, "For CRT displays only. Attempts to use exact core/game resolution and refresh rate." diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index afa4ec1a6b..4d54455e53 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -517,6 +517,7 @@ default_sublabel_macro(action_bind_sublabel_rgui_menu_color_theme, default_sublabel_macro(action_bind_sublabel_rgui_menu_theme_preset, MENU_ENUM_SUBLABEL_RGUI_MENU_THEME_PRESET) default_sublabel_macro(action_bind_sublabel_menu_rgui_thumbnail_downscaler, MENU_ENUM_SUBLABEL_MENU_RGUI_THUMBNAIL_DOWNSCALER) default_sublabel_macro(action_bind_sublabel_content_runtime_log, MENU_ENUM_SUBLABEL_CONTENT_RUNTIME_LOG) +default_sublabel_macro(action_bind_sublabel_menu_rgui_internal_upscale_level, MENU_ENUM_SUBLABEL_MENU_RGUI_INTERNAL_UPSCALE_LEVEL) static int action_bind_sublabel_systeminfo_controller_entry( file_list_t *list, @@ -2252,6 +2253,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_CONTENT_RUNTIME_LOG: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_content_runtime_log); break; + case MENU_ENUM_LABEL_MENU_RGUI_INTERNAL_UPSCALE_LEVEL: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_menu_rgui_internal_upscale_level); + break; default: case MSG_UNKNOWN: return -1; diff --git a/menu/drivers/rgui.c b/menu/drivers/rgui.c index 1596cd2d3b..1538d8812c 100644 --- a/menu/drivers/rgui.c +++ b/menu/drivers/rgui.c @@ -469,6 +469,19 @@ static wallpaper_t wallpaper = { {0} }; +typedef struct +{ + unsigned width; + unsigned height; + uint16_t *data; +} upscale_buf_t; + +static upscale_buf_t upscale_buf = { + 0, + 0, + NULL +}; + static uint16_t *rgui_framebuf_data = NULL; #if defined(PS2) @@ -1927,12 +1940,19 @@ static void rgui_free(void *data) if (!string_is_empty(wallpaper.path)) free(wallpaper.path); + + if (upscale_buf.data) + { + free(upscale_buf.data); + upscale_buf.data = NULL; + } } static void rgui_set_texture(void) { size_t fb_pitch; unsigned fb_width, fb_height; + settings_t *settings = config_get_ptr(); if (!menu_display_get_framebuffer_dirty_flag()) return; @@ -1942,8 +1962,90 @@ static void rgui_set_texture(void) menu_display_unset_framebuffer_dirty_flag(); - video_driver_set_texture_frame(rgui_framebuf_data, + if (settings->uints.menu_rgui_internal_upscale_level == RGUI_UPSCALE_NONE) + { + video_driver_set_texture_frame(rgui_framebuf_data, false, fb_width, fb_height, 1.0f); + } + else + { + /* Get viewport dimensions */ + struct video_viewport vp; + video_driver_get_viewport_info(&vp); + + /* If viewport is currently the same size (or smaller) + * than the menu framebuffer, no scaling is required */ + if ((vp.width <= fb_width) && (vp.height <= fb_height)) + { + video_driver_set_texture_frame(rgui_framebuf_data, + false, fb_width, fb_height, 1.0f); + } + else + { + unsigned out_width; + unsigned out_height; + uint32_t x_ratio, y_ratio; + unsigned x_src, y_src; + unsigned x_dst, y_dst; + + /* Determine output size */ + if (settings->uints.menu_rgui_internal_upscale_level == RGUI_UPSCALE_AUTO) + { + out_width = ((vp.width / fb_width) + 1) * fb_width; + out_height = ((vp.height / fb_height) + 1) * fb_height; + } + else + { + out_width = settings->uints.menu_rgui_internal_upscale_level * fb_width; + out_height = settings->uints.menu_rgui_internal_upscale_level * fb_height; + } + + /* Allocate upscaling buffer, if required */ + if ((upscale_buf.width != out_width) || (upscale_buf.height != out_height) || !upscale_buf.data) + { + upscale_buf.width = out_width; + upscale_buf.height = out_height; + + if (upscale_buf.data) + { + free(upscale_buf.data); + upscale_buf.data = NULL; + } + + upscale_buf.data = (uint16_t*)calloc(out_width * out_height, sizeof(uint16_t)); + if (!upscale_buf.data) + { + /* Uh oh... This could mean we don't have enough + * memory, so disable upscaling and draw the usual + * framebuffer... */ + settings->uints.menu_rgui_internal_upscale_level = RGUI_UPSCALE_NONE; + video_driver_set_texture_frame(rgui_framebuf_data, + false, fb_width, fb_height, 1.0f); + return; + } + } + + /* Perform nearest neighbour upscaling + * NB: We're duplicating code here, but trying to handle + * this with a polymorphic function is too much of a drag... */ + x_ratio = ((fb_width << 16) / out_width); + y_ratio = ((fb_height << 16) / out_height); + + for (y_dst = 0; y_dst < out_height; y_dst++) + { + y_src = (y_dst * y_ratio) >> 16; + for (x_dst = 0; x_dst < out_width; x_dst++) + { + x_src = (x_dst * x_ratio) >> 16; + upscale_buf.data[(y_dst * out_width) + x_dst] = rgui_framebuf_data[(y_src * fb_width) + x_src]; + } + } + + /* Draw upscaled texture */ + video_driver_set_texture_frame(upscale_buf.data, + false, out_width, out_height, 1.0f); + } + } } static void rgui_navigation_clear(void *data, bool pending_push) @@ -2300,6 +2402,15 @@ static void rgui_toggle(void *userdata, bool menu_on) video_driver_set_aspect_ratio(); } } + + /* Upscaling buffer is only required while menu is on. Save + * memory by freeing it whenever we switch back to the current + * content */ + if (!menu_on && upscale_buf.data) + { + free(upscale_buf.data); + upscale_buf.data = NULL; + } } menu_ctx_driver_t menu_ctx_rgui = { diff --git a/menu/menu_defines.h b/menu/menu_defines.h index ae0c09f13a..e1e9323626 100644 --- a/menu/menu_defines.h +++ b/menu/menu_defines.h @@ -233,6 +233,21 @@ enum rgui_thumbnail_scaler RGUI_THUMB_SCALE_LAST }; +enum rgui_upscale_level +{ + RGUI_UPSCALE_NONE = 0, + RGUI_UPSCALE_AUTO, + RGUI_UPSCALE_X2, + RGUI_UPSCALE_X3, + RGUI_UPSCALE_X4, + RGUI_UPSCALE_X5, + RGUI_UPSCALE_X6, + RGUI_UPSCALE_X7, + RGUI_UPSCALE_X8, + RGUI_UPSCALE_X9, /* All the way to 4k */ + RGUI_UPSCALE_LAST +}; + enum menu_action { MENU_ACTION_NOOP = 0, diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 5fd206765f..ef0fcedcd3 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -6020,6 +6020,10 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, menu_displaylist MENU_ENUM_LABEL_MENU_LINEAR_FILTER, PARSE_ONLY_BOOL, false) == 0) count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_MENU_RGUI_INTERNAL_UPSCALE_LEVEL, + PARSE_ONLY_UINT, false) == 0) + count++; if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_MENU_RGUI_LOCK_ASPECT, PARSE_ONLY_BOOL, false) == 0) diff --git a/menu/menu_setting.c b/menu/menu_setting.c index b45fa78c1c..4b60a54f49 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -819,6 +819,78 @@ static void setting_get_string_representation_uint_rgui_thumbnail_scaler( } } +static void setting_get_string_representation_uint_rgui_internal_upscale_level( + rarch_setting_t *setting, + char *s, size_t len) +{ + if (!setting) + return; + + switch (*setting->value.target.unsigned_integer) + { + case RGUI_UPSCALE_NONE: + strlcpy(s, + msg_hash_to_str( + MENU_ENUM_LABEL_VALUE_RGUI_UPSCALE_NONE), + len); + break; + case RGUI_UPSCALE_AUTO: + strlcpy(s, + msg_hash_to_str( + MENU_ENUM_LABEL_VALUE_RGUI_UPSCALE_AUTO), + len); + break; + case RGUI_UPSCALE_X2: + strlcpy(s, + msg_hash_to_str( + MENU_ENUM_LABEL_VALUE_RGUI_UPSCALE_X2), + len); + break; + case RGUI_UPSCALE_X3: + strlcpy(s, + msg_hash_to_str( + MENU_ENUM_LABEL_VALUE_RGUI_UPSCALE_X3), + len); + break; + case RGUI_UPSCALE_X4: + strlcpy(s, + msg_hash_to_str( + MENU_ENUM_LABEL_VALUE_RGUI_UPSCALE_X4), + len); + break; + case RGUI_UPSCALE_X5: + strlcpy(s, + msg_hash_to_str( + MENU_ENUM_LABEL_VALUE_RGUI_UPSCALE_X5), + len); + break; + case RGUI_UPSCALE_X6: + strlcpy(s, + msg_hash_to_str( + MENU_ENUM_LABEL_VALUE_RGUI_UPSCALE_X6), + len); + break; + case RGUI_UPSCALE_X7: + strlcpy(s, + msg_hash_to_str( + MENU_ENUM_LABEL_VALUE_RGUI_UPSCALE_X7), + len); + break; + case RGUI_UPSCALE_X8: + strlcpy(s, + msg_hash_to_str( + MENU_ENUM_LABEL_VALUE_RGUI_UPSCALE_X8), + len); + break; + case RGUI_UPSCALE_X9: + strlcpy(s, + msg_hash_to_str( + MENU_ENUM_LABEL_VALUE_RGUI_UPSCALE_X9), + len); + break; + } +} + #ifdef HAVE_XMB static void setting_get_string_representation_uint_xmb_icon_theme( rarch_setting_t *setting, @@ -7990,6 +8062,7 @@ static bool setting_append_list( ); if (video_driver_get_all_flags(&flags, GFX_CTX_FLAGS_MENU_FRAME_FILTERING)) + { CONFIG_BOOL( list, list_info, &settings->bools.menu_linear_filter, @@ -8006,6 +8079,24 @@ static bool setting_append_list( SD_FLAG_NONE ); + CONFIG_UINT( + list, list_info, + &settings->uints.menu_rgui_internal_upscale_level, + MENU_ENUM_LABEL_MENU_RGUI_INTERNAL_UPSCALE_LEVEL, + MENU_ENUM_LABEL_VALUE_MENU_RGUI_INTERNAL_UPSCALE_LEVEL, + rgui_internal_upscale_level, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler); + (*list)[list_info->index - 1].action_ok = &setting_action_ok_uint; + (*list)[list_info->index - 1].get_string_representation = + &setting_get_string_representation_uint_rgui_internal_upscale_level; + menu_settings_list_current_add_range(list, list_info, 0, RGUI_UPSCALE_LAST-1, 1, true, true); + settings_data_list_current_add_flags(list, list_info, SD_FLAG_ADVANCED); + } + CONFIG_BOOL( list, list_info, &settings->bools.menu_rgui_lock_aspect, diff --git a/msg_hash.h b/msg_hash.h index 5ddf2fed13..6bf72c5a48 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -858,6 +858,7 @@ enum msg_hash_enums MENU_LABEL(MENU_RGUI_BACKGROUND_FILLER_THICKNESS_ENABLE), MENU_LABEL(MENU_RGUI_BORDER_FILLER_THICKNESS_ENABLE), MENU_LABEL(MENU_RGUI_LOCK_ASPECT), + MENU_LABEL(MENU_RGUI_INTERNAL_UPSCALE_LEVEL), MENU_LABEL(MENU_LINEAR_FILTER), MENU_LABEL(MENU_HORIZONTAL_ANIMATION), MENU_LABEL(NAVIGATION_WRAPAROUND), @@ -1956,6 +1957,17 @@ enum msg_hash_enums MENU_ENUM_LABEL_VALUE_RGUI_THUMB_SCALE_BILINEAR, MENU_ENUM_LABEL_VALUE_RGUI_THUMB_SCALE_SINC, + MENU_ENUM_LABEL_VALUE_RGUI_UPSCALE_NONE, + MENU_ENUM_LABEL_VALUE_RGUI_UPSCALE_AUTO, + MENU_ENUM_LABEL_VALUE_RGUI_UPSCALE_X2, + MENU_ENUM_LABEL_VALUE_RGUI_UPSCALE_X3, + MENU_ENUM_LABEL_VALUE_RGUI_UPSCALE_X4, + MENU_ENUM_LABEL_VALUE_RGUI_UPSCALE_X5, + MENU_ENUM_LABEL_VALUE_RGUI_UPSCALE_X6, + MENU_ENUM_LABEL_VALUE_RGUI_UPSCALE_X7, + MENU_ENUM_LABEL_VALUE_RGUI_UPSCALE_X8, + MENU_ENUM_LABEL_VALUE_RGUI_UPSCALE_X9, + /* Callback strings */ MENU_ENUM_LABEL_CB_CORE_CONTENT_DIRS_LIST, MENU_ENUM_LABEL_CB_CORE_CONTENT_DOWNLOAD,