mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-09 03:29:43 -10:00
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:
1 parent
9932b5fb7a
commit
d9e685bc69
3 files changed
+54
-17
No files matched your search
+20
-5
@@ -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;
|
||||
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user