Skip to content

Commit f340815

Browse files
committed
MENU/XMB: async-only icon thumbnail loading, lazy path resolution, input-repeat populate defer
Three related changes addressing animation smoothness and fps issues around XMB playlist icon thumbnails, plus replacing a reverted-on-master defer commit with a correct mechanism. **Drop synchronous icon loads in favour of async stream-only** xmb_render's icon dispatcher previously sync-loaded textures via xmb_load_dynamic_icon (gfx_display_reset_icon_texture → image_texture_load + video_driver_texture_load), with gfx_thumbnail_request_stream as a fallback only when the sync path failed. Earlier iterations of this code added and removed a 2-load-per-frame cap on the sync path; both extremes had problems: - Capped sync (2/frame): 10–20 visible entries take 5–10 frames to drip-load, each frame still doing enough work to dip below refresh rate. Result: a quarter-second of visible stutter in what is supposed to be steady-state 60 Hz list display. - Uncapped sync (one-frame burst): the whole load lands in a single frame which blocks the main thread long enough that the runloop misses several gfx_animation_update ticks. The next tick advances delta_time by the full elapsed wall-clock and tween easing functions evaluate near or past their duration, snapping the in-flight enter-playlist or tab-switch animation straight to its end state. Async stream requests don't have either problem. Worker threads do the decode + upload in parallel with rendering, results land via task callback over the next several frames. The main thread stays responsive throughout, animations tick smoothly, and total time-to-thumbnail is roughly the same as the uncapped sync burst (typically ~50–200 ms) but distributed across frames instead of concentrated in one. Always dispatch async, never sync. Path resolution stays inline (it's a few stat syscalls per unresolved entry, cheap enough not to block a frame). xmb_load_dynamic_icon is no longer used anywhere and is removed. **Lazy path resolution** Previously xmb_populate_dynamic_icons and xmb_selection_pointer_changed both called xmb_refresh_visible_icon_paths, which eagerly walked the visible range (~20 entries) and ran gfx_thumbnail_update_path per entry. That function does path_is_valid checks — one stat syscall per entry, or five per entry with playlist_allow_non_png enabled across the .png/.jpg/.jpeg/.bmp/.tga fallback chain. On a populate frame, 20–100 stat syscalls executed before any work was dispatched. Worse: selection_pointer_changed fires on every vertical scroll step, so held scrolling paid the full syscall storm repeatedly — because gfx_thumbnail_set_icon_playlist unconditionally clears icon_path and re-derives from scratch. Remove the eager refresh. Resolve paths lazily, inline in the render dispatcher, only for entries whose icon_path is empty (the signal that they have never been resolved or were just wiped by an unload). This pays the resolution cost at most once per entry-per-list-population, eliminating the scroll-storm entirely. xmb_unload_icon_thumbnail_textures now clears node->thumbnail_icon.thumbnail_path_data.icon_path[0] per node to establish the "empty icon_path = needs resolution" signal the dispatcher relies on. xmb_refresh_visible_icon_paths is no longer used and is removed; the is_playlist / Images-Music-Video tab gates it checked internally are reproduced inline at the two former call sites. **Input-repeat gated populate defer (replaces reverted animation defer)** 910c8d0 ("defer xmb_populate_dynamic_icons until horizontal tab animation settles") attempted to skip wasted populate work during rapid held-left/right tab traversal by deferring until categories_x_pos reached its target. Per-frame user screenshots showed that predicate caused a visible delay between the end of the enter-playlist animation and the appearance of per-entry thumbnails — for a few frames the list renders with console-logo fallbacks before swapping to real thumbnails. The animation-state predicate is the wrong signal: 1. It only tracks the horizontal tab-row slide, which does not run when the user navigates into a playlist (depth change, not tab change). On that path the predicate fires immediately, so the defer contributes nothing for the entering-playlist case. 2. On the held-left/right tab traversal case the defer was actually meant to address, the predicate fires when the last-in-flight animation completes — not when input actually settles. That can still be during a rapid-mash window, so the populate still runs on tabs the user blew past. Switch the predicate to menu_st->scroll.acceleration == 0. That field increments when a navigation button's held-repeat fires and resets to 0 on release — the system's existing authoritative answer to "is the user currently mid-mash?" Consequences: - Single-press navigation: acceleration is 0 at the populate_entries call. Work runs synchronously, same frame as the action. No defer, no delay. - Held-repeat traversal: acceleration is > 0 on intermediate tabs. populate_entries sets the flag instead of doing the work. On the frame after release, acceleration drops to 0 and xmb_render fires the deferred populate once, for the tab the user actually stopped on. - Context reset and context destroy still clear the flag so the synchronous populate in xmb_context_reset_internal isn't duplicated by a deferred fire.
1 parent a52ea1b commit f340815

1 file changed

Lines changed: 141 additions & 177 deletions

File tree

0 commit comments

Comments
 (0)