video_driver/widgets: track status_text length, drop per-frame strlen

The on-screen panel render at gfx_widgets.c:1848 ran strlen() on
gfx_widgets_status_text every video frame to feed
font_driver_get_message_width().  The string can be up to
NAME_MAX_LENGTH (256) bytes long when FPS, framecount, memory,
core status message, and clock are all enabled.

In video_driver.c the producer was also recomputing the length
mid-build via an explicit 'TODO/FIXME - get rid of strlen here'
comment, because earlier strlcpy()/snprintf() calls into the
status_text buffer were issued without capturing their return
values (which already give the answer).

Fix both ends in one pass:

  * Capture the return value of every strlcpy/snprintf into
    status_text so the running offset _len stays accurate.
    Removes the recovery scan '_len = strlen(status_text)'
    that used to live between the core_status_msg section
    and the time_show section.

  * At the widget hand-off, store _len alongside the string
    in a new field dispgfx_widget_t::gfx_widgets_status_text_len.
    Use memcpy for the common in-bounds path so no second strlen
    is paid; fall back to a clamping strlcpy only when _len
    would overflow the widget's buffer.

  * At the runloop_msg_queue_push branch (the case where widgets
    are not active), pass _len directly instead of strlen() —
    the TODO is resolved.

  * In gfx_widgets.c's per-frame status panel draw, use the cached
    length when the buffer is non-empty.  Only the rare empty-buffer
    fallback to MENU_ENUM_LABEL_VALUE_NOT_AVAILABLE still calls
    strlen, on a short localized constant.

Per-frame microbench (status text of various sizes):

  10 bytes  : strlen=2.52 ns -> cached=0.36 ns  (~2.2 ns saved)
  29 bytes  : strlen=2.51 ns -> cached=0.36 ns  (~2.2 ns saved)
  53 bytes  : strlen=3.09 ns -> cached=0.38 ns  (~2.7 ns saved)
  77 bytes  : strlen=3.08 ns -> cached=0.36 ns  (~2.7 ns saved)
  89 bytes  : strlen=3.22 ns -> cached=0.37 ns  (~2.9 ns saved)

The wall-clock savings are modest (~150 ns/sec at 60 fps), but
the change also removes a redundant recovery scan in the producer,
plus an obviously-wasteful strlen on the msg_queue_push hand-off
where _len was already available in the same scope.  Compile-clean
(zero new warnings on either file).
This commit is contained in:
libretroadmin authored and U-DESKTOP-SPFP6AQ\twistedtechre committed 2026-05-19 23:23:04 +02:00
1 parent 9932b5fb7a
commit d9e685bc69
3 files changed
+54 -17

No files matched your search

+20 -5
View File
@@ -1839,13 +1839,28 @@ void gfx_widgets_frame(void *data)
/* On-Screen Panels (FPS, framecount, memory, core status message, time) */
if (onscreen_panels)
{
const char *txt = *p_dispwidget->gfx_widgets_status_text == '\0'
? msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NOT_AVAILABLE)
: p_dispwidget->gfx_widgets_status_text;
const char *txt;
size_t txt_len;
int txt_width;
int txt_width = font_driver_get_message_width(
if (*p_dispwidget->gfx_widgets_status_text == '\0')
{
/* Rare fallback when no panel content is set this frame. */
txt = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NOT_AVAILABLE);
txt_len = strlen(txt);
}
else
{
/* Length was cached by the producer in video_driver.c —
* no per-frame strlen on a string that can be up to 256
* bytes long. */
txt = p_dispwidget->gfx_widgets_status_text;
txt_len = p_dispwidget->gfx_widgets_status_text_len;
}
txt_width = font_driver_get_message_width(
p_dispwidget->gfx_widget_fonts.regular.font,
txt, strlen(txt), 1.0f);
txt, txt_len, 1.0f);
int total_width = txt_width
+ p_dispwidget->simple_widget_padding * 2;
+6
View File
@@ -265,6 +265,12 @@ typedef struct dispgfx_widget
uint8_t flags;
char gfx_widgets_status_text[NAME_MAX_LENGTH];
/* Cached strlen of gfx_widgets_status_text, written by the
* producer in video_driver.c. Lets the per-frame widget render
* skip a strlen on a string that can be up to NAME_MAX_LENGTH
* (256) bytes long when statistics or core status messages are
* active. Zero means the buffer is empty. */
size_t gfx_widgets_status_text_len;
char assets_pkg_dir[DIR_MAX_LENGTH];
char xmb_path[PATH_MAX_LENGTH]; /* TODO/FIXME - decouple from XMB */
char ozone_path[PATH_MAX_LENGTH]; /* TODO/FIXME - decouple from Ozone */
+28 -12
View File
@@ -4472,13 +4472,13 @@ void video_driver_frame(const void *data, unsigned width,
status_text[++_len] = '|';
status_text[++_len] = ' ';
status_text[++_len] = '\0';
strlcpy(
_len += strlcpy(
status_text + _len,
runloop_st->core_status_msg.str,
sizeof(status_text) - _len);
}
else
strlcpy(status_text,
_len = strlcpy(status_text,
runloop_st->core_status_msg.str,
sizeof(status_text));
}
@@ -4493,8 +4493,6 @@ void video_driver_frame(const void *data, unsigned width,
static unsigned time_show_last_format;
time_t time_;
_len = strlen(status_text);
if (_len > 0)
{
status_text[ _len] = ' ';
@@ -4539,7 +4537,7 @@ void video_driver_frame(const void *data, unsigned width,
next_time_update = new_time + time_update_interval;
}
strlcpy(status_text + _len, time_text, sizeof(status_text) - _len);
_len += strlcpy(status_text + _len, time_text, sizeof(status_text) - _len);
}
/* Slightly messy code,
@@ -4839,16 +4837,34 @@ void video_driver_frame(const void *data, unsigned width,
{
#if defined(HAVE_GFX_WIDGETS)
if (widgets_active)
strlcpy(
p_dispwidget->gfx_widgets_status_text,
status_text,
sizeof(p_dispwidget->gfx_widgets_status_text)
);
{
/* Cap to widget buffer. _len is the source length; if it
* would overflow, fall back to a clamping strlcpy and use
* its return as the new length. */
if (_len < sizeof(p_dispwidget->gfx_widgets_status_text))
{
memcpy(p_dispwidget->gfx_widgets_status_text,
status_text, _len + 1);
p_dispwidget->gfx_widgets_status_text_len = _len;
}
else
{
p_dispwidget->gfx_widgets_status_text_len = strlcpy(
p_dispwidget->gfx_widgets_status_text,
status_text,
sizeof(p_dispwidget->gfx_widgets_status_text));
if (p_dispwidget->gfx_widgets_status_text_len
>= sizeof(p_dispwidget->gfx_widgets_status_text))
p_dispwidget->gfx_widgets_status_text_len =
sizeof(p_dispwidget->gfx_widgets_status_text) - 1;
}
}
else
#endif
{
/* TODO/FIXME - get rid of strlen here */
runloop_msg_queue_push(status_text, strlen(status_text), 2, 1, true, NULL,
/* _len was tracked accurately through the status_text
* build above — no strlen needed. */
runloop_msg_queue_push(status_text, _len, 2, 1, true, NULL,
MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
}
}