mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-08 08:10:30 -10:00
libretro-common/file/nbio/nbio_intf.c — Replaced the _linux__ typo hack with #if 0 to cleanly disable the Linux AIO backend. Benchmarking showed io_setup/io_destroy adds ~35µs per file open/close, making AIO 2x slower than stdio for the 40-file burst pattern menu icons use.
menu/drivers/xmb.c — Four changes: Added if (!texture) return; at the top of xmb_draw_icon to skip drawing when an async texture load hasn't completed yet, preventing gray placeholder squares. Replaced the broken one-shot tab icon fixup in xmb_frame with per-slot continuous sync. The old code checked only XMB_TEXTURE_MAIN_MENU and copied all tab icon slots in one shot — but other slots were still 0 because their async loads hadn't finished. The new code checks each slot independently every frame. Added a draw-time cursor texture fallback for .lvw explore view entries in the horizontal tab drawing loop. These entries copy textures.list[XMB_TEXTURE_CURSOR] during list build, which is 0 if the async context texture load hasn't completed yet. The fallback retries each frame until the texture arrives. Changed TEXTURE_FILTER_MIPMAP_LINEAR to TEXTURE_FILTER_LINEAR for wallpaper and dynamic wallpaper texture loads — menu textures are 2D UI elements at fixed size, mipmaps are never sampled. menu/drivers/ozone.c — Two changes: Added if (!texture) return; at the top of ozone_draw_icon to skip drawing unloaded textures. Added a draw-time cursor texture fallback for .lvw explore view nodes in the sidebar drawing loop, same pattern as the XMB fix. menu/drivers/materialui.c — Two changes: Added if (!texture) return; at the top of materialui_draw_icon to skip drawing unloaded textures. Changed TEXTURE_FILTER_MIPMAP_LINEAR to TEXTURE_FILTER_LINEAR for background texture load. gfx/gfx_widgets.c — Two changes: Replaced the MENU_WIDGETS_ICON_INFO single-canary HAS_ICONS check with an all-icons-loaded loop. The old code flipped the layout flag as soon as one icon loaded, but gfx_widgets_update_icon_layout reads other icon slots (e.g. hourglass) to compute text offsets. If those hadn't loaded yet, task progress text was positioned wrong. The new code waits for all 10 widget icons before flipping the flag. Changed TEXTURE_FILTER_MIPMAP_LINEAR to TEXTURE_FILTER_LINEAR for AI service overlay texture load. gfx/gfx_display.c — Changed TEXTURE_FILTER_MIPMAP_LINEAR to TEXTURE_FILTER_LINEAR in the synchronous icon load path (gfx_display_load_icon on Android). gfx/gfx_thumbnail.c — Changed TEXTURE_FILTER_MIPMAP_LINEAR to TEXTURE_FILTER_LINEAR in the thumbnail upload callback. Thumbnails are displayed at a fixed size in the UI and never minified below their display resolution. tasks/task_image.c — Changed TEXTURE_FILTER_MIPMAP_LINEAR to TEXTURE_FILTER_LINEAR in the async icon upload callback (cb_task_icon_load). gfx/widgets/gfx_widget_load_content_animation.c — Changed TEXTURE_FILTER_MIPMAP_LINEAR to TEXTURE_FILTER_LINEAR for the content load animation icon. gfx/widgets/gfx_widget_screenshot.c — Changed TEXTURE_FILTER_MIPMAP_LINEAR to TEXTURE_FILTER_LINEAR for the screenshot widget texture. The MIPMAP_LINEAR → LINEAR changes — This is the one to watch. It's safe in terms of correctness — every driver handles TEXTURE_FILTER_LINEAR correctly, and the textures still look fine at their display size. The only risk is a subtle quality difference: if a thumbnail is displayed at significantly smaller than its native resolution (e.g. a 512x384 thumbnail drawn into a 128x96 area), bilinear filtering without mipmaps can produce shimmer/aliasing that mipmapped filtering would smooth out. In practice RetroArch thumbnails are displayed close to their native size so this shouldn't be noticeable, but if someone reports slightly sparkly thumbnails on very high-DPI displays where thumbnails are heavily downscaled, this is why. The upside is significant: ~33% less VRAM per texture, no compute shader dispatch per texture on D3D12, no UAV format support requirement. If you want to be extra cautious, you could keep MIPMAP_LINEAR for just gfx_thumbnail.c (thumbnails can be large and get downscaled) and use LINEAR everywhere else (icons are always small). But I'd ship it as-is and only revert the thumbnail filter type if someone actually reports a quality regression.
This commit is contained in:
1 parent
a1e7960c88
commit
92ab698f26
10 files changed
+97
-40
No files matched your search
+1
-1
@@ -1213,7 +1213,7 @@ bool gfx_display_load_icon(
|
||||
(void)generation_ptr;
|
||||
return gfx_display_reset_icon_texture(
|
||||
fullpath, target_texture,
|
||||
TEXTURE_FILTER_MIPMAP_LINEAR, NULL, NULL);
|
||||
TEXTURE_FILTER_LINEAR, NULL, NULL);
|
||||
#else
|
||||
return task_push_icon_load(
|
||||
fullpath, supports_rgba,
|
||||
|
||||
+1
-1
@@ -188,7 +188,7 @@ static void gfx_thumbnail_handle_upload(
|
||||
|
||||
/* Upload texture to GPU */
|
||||
if (!video_driver_texture_load(
|
||||
img, TEXTURE_FILTER_MIPMAP_LINEAR,
|
||||
img, TEXTURE_FILTER_LINEAR,
|
||||
&thumbnail_tag->thumbnail->texture))
|
||||
goto end;
|
||||
|
||||
|
||||
+21
-8
@@ -1576,14 +1576,27 @@ void gfx_widgets_frame(void *data)
|
||||
p_dispwidget->gfx_widgets_frame_count++;
|
||||
|
||||
/* Second-pass icon layout: when async widget icons finish loading,
|
||||
* detect the transition and recompute icon-dependent layout once.
|
||||
* This avoids the old approach of either blocking on load or
|
||||
* setting the flag optimistically (which caused text/icon overlap). */
|
||||
if (!(p_dispwidget->flags & DISPGFX_WIDGET_FLAG_MSG_QUEUE_HAS_ICONS)
|
||||
&& p_dispwidget->gfx_widgets_icons_textures[MENU_WIDGETS_ICON_INFO])
|
||||
* detect the transition and recompute icon-dependent layout.
|
||||
* Wait until ALL icons are loaded before flipping the flag —
|
||||
* the layout function checks individual textures (e.g. hourglass)
|
||||
* so partial loads produce wrong text offsets. */
|
||||
if (!(p_dispwidget->flags & DISPGFX_WIDGET_FLAG_MSG_QUEUE_HAS_ICONS))
|
||||
{
|
||||
p_dispwidget->flags |= DISPGFX_WIDGET_FLAG_MSG_QUEUE_HAS_ICONS;
|
||||
gfx_widgets_update_icon_layout(p_dispwidget);
|
||||
size_t _i;
|
||||
bool all_loaded = true;
|
||||
for (_i = 0; _i < MENU_WIDGETS_ICON_LAST; _i++)
|
||||
{
|
||||
if (!p_dispwidget->gfx_widgets_icons_textures[_i])
|
||||
{
|
||||
all_loaded = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (all_loaded)
|
||||
{
|
||||
p_dispwidget->flags |= DISPGFX_WIDGET_FLAG_MSG_QUEUE_HAS_ICONS;
|
||||
gfx_widgets_update_icon_layout(p_dispwidget);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_MENU
|
||||
@@ -2225,7 +2238,7 @@ bool gfx_widgets_ai_service_overlay_load(
|
||||
{
|
||||
if (!gfx_display_reset_textures_list_buffer(
|
||||
&p_dispwidget->ai_service_overlay_texture,
|
||||
TEXTURE_FILTER_MIPMAP_LINEAR,
|
||||
TEXTURE_FILTER_LINEAR,
|
||||
(void *) buffer, buffer_len, image_type,
|
||||
&p_dispwidget->ai_service_overlay_width,
|
||||
&p_dispwidget->ai_service_overlay_height))
|
||||
|
||||
@@ -221,7 +221,7 @@ static void gfx_widget_load_content_animation_load_icon(void)
|
||||
gfx_display_reset_textures_list(
|
||||
state->icon_file, state->icon_directory,
|
||||
&state->icon_texture,
|
||||
TEXTURE_FILTER_MIPMAP_LINEAR, NULL, NULL);
|
||||
TEXTURE_FILTER_LINEAR, NULL, NULL);
|
||||
}
|
||||
|
||||
/* Callbacks */
|
||||
|
||||
@@ -388,7 +388,7 @@ static void gfx_widget_screenshot_iterate(
|
||||
state->y = 0.0f;
|
||||
|
||||
gfx_display_reset_textures_list(state->filename,
|
||||
"", &state->texture, TEXTURE_FILTER_MIPMAP_LINEAR,
|
||||
"", &state->texture, TEXTURE_FILTER_LINEAR,
|
||||
&state->texture_width, &state->texture_height);
|
||||
|
||||
state->height = font_regular->line_height * 4;
|
||||
|
||||
@@ -52,13 +52,13 @@ extern nbio_intf_t nbio_stdio;
|
||||
|
||||
#endif
|
||||
|
||||
/* NOTE: Yes, this is a typo that never got hit,
|
||||
but this is intentional. Apparently this driver
|
||||
never got picked before on Android and Linux but
|
||||
now that it did we suddenly get huge file I/O
|
||||
issues, so try to avoid it to see if the issues
|
||||
go away */
|
||||
#if defined(_linux__)
|
||||
/* Disabled: the Linux AIO backend calls io_setup/io_destroy per
|
||||
* file handle, adding ~35us of kernel overhead per open+close.
|
||||
* For the small-file burst pattern used by menu icon loading
|
||||
* (40+ files of 4-64KB), this makes it ~2x slower than stdio.
|
||||
* Fall through to nbio_stdio on Linux until the AIO backend
|
||||
* shares a single context across handles. */
|
||||
#if 0 /* was: defined(__linux__) */
|
||||
static nbio_intf_t *internal_nbio = &nbio_linux;
|
||||
#elif defined(HAVE_MMAP) && defined(BSD)
|
||||
static nbio_intf_t *internal_nbio = &nbio_mmap_unix;
|
||||
|
||||
@@ -2591,6 +2591,11 @@ static void materialui_draw_icon(
|
||||
struct video_coords coords;
|
||||
gfx_display_ctx_driver_t *dispctx = p_disp->dispctx;
|
||||
|
||||
/* Skip drawing when the texture hasn't loaded yet
|
||||
* (async loads start at 0 and are written on completion) */
|
||||
if (!texture)
|
||||
return;
|
||||
|
||||
if (dispctx && dispctx->blend_begin)
|
||||
dispctx->blend_begin(userdata);
|
||||
|
||||
@@ -9285,7 +9290,7 @@ static bool materialui_load_image(void *userdata,
|
||||
{
|
||||
materialui_context_bg_destroy(mui);
|
||||
video_driver_texture_load(data,
|
||||
TEXTURE_FILTER_MIPMAP_LINEAR, &mui->textures.bg);
|
||||
TEXTURE_FILTER_LINEAR, &mui->textures.bg);
|
||||
gfx_display_deinit_white_texture();
|
||||
gfx_display_init_white_texture();
|
||||
}
|
||||
|
||||
+24
-9
@@ -3036,6 +3036,11 @@ static void ozone_draw_icon(
|
||||
gfx_display_ctx_driver_t
|
||||
*dispctx = p_disp->dispctx;
|
||||
|
||||
/* Skip drawing when the texture hasn't loaded yet
|
||||
* (async loads start at 0 and are written on completion) */
|
||||
if (!texture)
|
||||
return;
|
||||
|
||||
coords.vertices = 4;
|
||||
coords.vertex = NULL;
|
||||
coords.tex_coord = NULL;
|
||||
@@ -3730,15 +3735,24 @@ static void ozone_draw_sidebar(
|
||||
|
||||
gfx_display_set_alpha(col, ozone->animations.alpha);
|
||||
|
||||
/* Icon */
|
||||
ozone_draw_icon(
|
||||
p_disp,
|
||||
userdata,
|
||||
video_width,
|
||||
video_height,
|
||||
ozone->dimensions.sidebar_entry_icon_size,
|
||||
ozone->dimensions.sidebar_entry_icon_size,
|
||||
node->icon,
|
||||
/* Icon — if node->icon is still 0 (explore view entry whose
|
||||
* cursor texture hadn't loaded during list build), retry now. */
|
||||
{
|
||||
uintptr_t icon_tex = node->icon;
|
||||
if (!icon_tex
|
||||
&& ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CURSOR])
|
||||
{
|
||||
icon_tex = ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CURSOR];
|
||||
node->icon = icon_tex;
|
||||
}
|
||||
ozone_draw_icon(
|
||||
p_disp,
|
||||
userdata,
|
||||
video_width,
|
||||
video_height,
|
||||
ozone->dimensions.sidebar_entry_icon_size,
|
||||
ozone->dimensions.sidebar_entry_icon_size,
|
||||
icon_tex,
|
||||
ozone->sidebar_offset
|
||||
+ ozone->dimensions.sidebar_padding_horizontal
|
||||
+ ozone->dimensions.sidebar_entry_icon_padding,
|
||||
@@ -3752,6 +3766,7 @@ static void ozone_draw_sidebar(
|
||||
1.0f,
|
||||
col,
|
||||
mymat);
|
||||
}
|
||||
|
||||
/* Text */
|
||||
if (ozone->sidebar_collapsed)
|
||||
|
||||
+34
-10
@@ -951,6 +951,11 @@ static void xmb_draw_icon(
|
||||
struct video_coords coords;
|
||||
math_matrix_4x4 mymat_tmp;
|
||||
|
||||
/* Skip drawing when the texture hasn't loaded yet
|
||||
* (async loads start at 0 and are written on completion) */
|
||||
if (!texture)
|
||||
return;
|
||||
|
||||
if ( (x < (-icon_size_x / 2.0f))
|
||||
|| (x > width)
|
||||
|| (y < (icon_size_y / 2.0f))
|
||||
@@ -6195,7 +6200,7 @@ static bool xmb_load_dynamic_icon(const char *icon_path,
|
||||
if (gfx_display_reset_icon_texture(
|
||||
icon_path,
|
||||
&icon->texture,
|
||||
TEXTURE_FILTER_MIPMAP_LINEAR,
|
||||
TEXTURE_FILTER_LINEAR,
|
||||
&width,
|
||||
&height))
|
||||
{
|
||||
@@ -8019,33 +8024,42 @@ static void xmb_frame(void *data, video_frame_info_t *video_info)
|
||||
if (!xmb)
|
||||
return;
|
||||
|
||||
/* One-shot async icon fixup: when context textures finish loading
|
||||
* asynchronously, tab node .icon fields are still zero (they were
|
||||
* cleared in xmb_context_reset_textures). Detect arrival by
|
||||
* checking the main menu texture and populate all tab nodes once. */
|
||||
if ( !xmb->main_menu_node.icon
|
||||
&& xmb->textures.list[XMB_TEXTURE_MAIN_MENU])
|
||||
{
|
||||
/* Async icon sync: context textures are loaded asynchronously and
|
||||
* may complete across multiple frames. Continuously propagate
|
||||
* finished textures into the tab node icon fields. The writes
|
||||
* are single-pointer stores so there is no race with the async
|
||||
* callback, and the per-frame cost is negligible (a few pointer
|
||||
* comparisons). */
|
||||
if (xmb->textures.list[XMB_TEXTURE_MAIN_MENU])
|
||||
xmb->main_menu_node.icon = xmb->textures.list[XMB_TEXTURE_MAIN_MENU];
|
||||
if (xmb->textures.list[XMB_TEXTURE_SETTINGS])
|
||||
xmb->settings_tab_node.icon = xmb->textures.list[XMB_TEXTURE_SETTINGS];
|
||||
if (xmb->textures.list[XMB_TEXTURE_HISTORY])
|
||||
xmb->history_tab_node.icon = xmb->textures.list[XMB_TEXTURE_HISTORY];
|
||||
if (xmb->textures.list[XMB_TEXTURE_FAVORITES])
|
||||
xmb->favorites_tab_node.icon = xmb->textures.list[XMB_TEXTURE_FAVORITES];
|
||||
#ifdef HAVE_IMAGEVIEWER
|
||||
if (xmb->textures.list[XMB_TEXTURE_IMAGES])
|
||||
xmb->images_tab_node.icon = xmb->textures.list[XMB_TEXTURE_IMAGES];
|
||||
#endif
|
||||
if (xmb->textures.list[XMB_TEXTURE_MUSICS])
|
||||
xmb->music_tab_node.icon = xmb->textures.list[XMB_TEXTURE_MUSICS];
|
||||
#if defined(HAVE_FFMPEG) || defined(HAVE_MPV)
|
||||
if (xmb->textures.list[XMB_TEXTURE_MOVIES])
|
||||
xmb->video_tab_node.icon = xmb->textures.list[XMB_TEXTURE_MOVIES];
|
||||
#endif
|
||||
if (xmb->textures.list[XMB_TEXTURE_ADD])
|
||||
xmb->add_tab_node.icon = xmb->textures.list[XMB_TEXTURE_ADD];
|
||||
if (xmb->textures.list[XMB_TEXTURE_CORE])
|
||||
xmb->contentless_cores_tab_node.icon = xmb->textures.list[XMB_TEXTURE_CORE];
|
||||
#if defined(HAVE_LIBRETRODB)
|
||||
if (xmb->textures.list[XMB_TEXTURE_RDB])
|
||||
xmb->explore_tab_node.icon = xmb->textures.list[XMB_TEXTURE_RDB];
|
||||
#endif
|
||||
#ifdef HAVE_NETWORKING
|
||||
if (xmb->textures.list[XMB_TEXTURE_NETPLAY])
|
||||
xmb->netplay_tab_node.icon = xmb->textures.list[XMB_TEXTURE_NETPLAY];
|
||||
#endif
|
||||
}
|
||||
|
||||
msg[0] = '\0';
|
||||
title_msg[0] = '\0';
|
||||
@@ -8340,6 +8354,16 @@ static void xmb_frame(void *data, video_frame_info_t *video_info)
|
||||
if (xmb_item_color[3] != 0)
|
||||
{
|
||||
uintptr_t texture = node->icon;
|
||||
|
||||
/* Fixup for explore view (.lvw) nodes: their icon was
|
||||
* copied from textures.list[CURSOR] during list build,
|
||||
* but if the async load hadn't completed yet the copy
|
||||
* was 0. Retry each frame until the texture arrives. */
|
||||
if (!texture && xmb->textures.list[XMB_TEXTURE_CURSOR])
|
||||
{
|
||||
texture = xmb->textures.list[XMB_TEXTURE_CURSOR];
|
||||
node->icon = texture;
|
||||
}
|
||||
float x = xmb->x + xmb->categories_x_pos
|
||||
+ xmb->margins_screen_left
|
||||
+ xmb->icon_spacing_horizontal * (i + 1)
|
||||
@@ -9300,7 +9324,7 @@ static bool xmb_load_image(void *userdata, void *data,
|
||||
case MENU_IMAGE_WALLPAPER:
|
||||
xmb_context_bg_destroy(xmb);
|
||||
video_driver_texture_load(data,
|
||||
TEXTURE_FILTER_MIPMAP_LINEAR,
|
||||
TEXTURE_FILTER_LINEAR,
|
||||
&xmb->textures.bg);
|
||||
gfx_display_init_white_texture();
|
||||
break;
|
||||
|
||||
+1
-1
@@ -571,7 +571,7 @@ static void cb_task_icon_load(retro_task_t *task,
|
||||
if (tag->target && *tag->target)
|
||||
video_driver_texture_unload(tag->target);
|
||||
|
||||
video_driver_texture_load(img, TEXTURE_FILTER_MIPMAP_LINEAR,
|
||||
video_driver_texture_load(img, TEXTURE_FILTER_LINEAR,
|
||||
tag->target);
|
||||
|
||||
end:
|
||||
|
||||
Reference in new issue
Block a user