Headless-friendly performance benchmark for the n_gui system.
Headless-friendly performance benchmark for the n_gui system. Builds one heavy, representative context (a large datagrid, a multiline textarea, a JSON syntax view, list widgets and many static labels/buttons) and times two hot paths in isolation:
It renders to the display back buffer without flipping, so the number reflects the CPU cost of building the frame (text measurement, tokenization, primitive submission) rather than vsync/present latency. This is the metric the GUI optimisation work targets, and it is reproducible under Xvfb.
Not a correctness regression: like the other ex_gui_* programs it is excluded from run_tests.sh. Run it manually (optionally under xvfb-run) before and after an optimisation to compare numbers. It self-terminates and frees every resource, so it is also an ASan/LSan smoke test of the draw + teardown path.
#define ALLEGRO_UNSTABLE 1
#define BENCH_DISPLAY_W 1600
#define BENCH_DISPLAY_H 1000
#define BENCH_DEFAULT_DRAW_ITERS 1000
#define BENCH_DEFAULT_EVENT_ITERS 20000
#define BENCH_GRID_ROWS 500
#define BENCH_GRID_COLS 8
#define BENCH_LIST_ITEMS 200
#define BENCH_SYNTAX_LINES 200
(void)a;
(void)b;
(void)c;
(void)u;
}
(void)a;
(void)b;
(void)u;
}
int c = 0;
while ((c = getopt(argc, argv, "V:n:e:h")) != -1) {
switch (c) {
case 'V':
if (optarg) {
if (!strncmp("LOG_NULL", optarg, 8))
else if (!strncmp("LOG_NOTICE", optarg, 10))
else if (!strncmp("LOG_INFO", optarg, 8))
else if (!strncmp("LOG_ERR", optarg, 7))
else if (!strncmp("LOG_DEBUG", optarg, 9))
else {
fprintf(stderr, "%s -V (LOG_NULL|LOG_NOTICE|LOG_INFO|LOG_ERR|LOG_DEBUG)\n", argv[0]);
exit(1);
}
}
break;
case 'n':
break;
case 'e':
break;
case 'h':
default:
fprintf(stderr, "%s [-V loglevel] [-n draw_iters] [-e event_iters]\n", argv[0]);
exit(1);
}
}
}
char title[32];
snprintf(title, sizeof(title), "Column %d", c);
}
if ((c & 1) == 0)
else
snprintf(cells[c], sizeof(cells[c]), "cell r%d c%d text", r, c);
values[c] = cells[c];
}
}
}
char buf[6001];
size_t p = 0;
int word = 0;
while (p < sizeof(buf) - 16) {
int n = snprintf(buf + p, sizeof(buf) - p, "word%d ", word++);
if (n <= 0) break;
p += (size_t)n;
if ((word % 9) == 0 && p < sizeof(buf) - 2) {
buf[p++] = '\n';
}
}
buf[p] = '\0';
}
char* buf = NULL;
if (!buf) return;
size_t p = 0;
p += (size_t)snprintf(buf + p, cap - p, "{\n");
p += (size_t)snprintf(buf + p, cap - p,
" \"key_%d\": { \"id\": %d, \"name\": \"item_%d\", \"ok\": %s },\n",
i, i, i, (i & 1) ? "true" : "false");
}
p += (size_t)snprintf(buf + p, cap - p, " \"end\": null\n}\n");
buf[p] = '\0';
}
char item[48];
snprintf(item, sizeof(item), "list item number %d", i);
}
for (int i = 0; i < 20; i++) {
char item[32];
snprintf(item, sizeof(item), "radio %d", i);
}
float y = 10.0f;
for (int i = 0; i < 20; i++) {
char lab[48];
snprintf(lab, sizeof(lab), "Static label %d value", i);
y += 24.0f;
}
y = 10.0f;
for (int i = 0; i < 12; i++) {
char lab[32];
snprintf(lab, sizeof(lab), "Button %d", i);
y += 30.0f;
}
y = 10.0f;
for (int i = 0; i < 10; i++) {
char lab[32];
snprintf(lab, sizeof(lab), "Check %d", i);
y += 26.0f;
}
n_gui_add_slider(
gui, win, 270, 520, 220, 24, 0.0, 100.0, 42.0, 0, NULL, NULL);
n_gui_add_slider(
gui, win, 270, 560, 220, 24, 0.0, 100.0, 73.0, 1, NULL, NULL);
"This is a longer justified paragraph label used to exercise the "
"word wrapping and per-word text measurement path that runs every "
"single frame in the current implementation of the label draw code.",
}
int main(
int argc,
char** argv) {
if (!al_init()) {
return 1;
}
al_init_primitives_addon();
al_init_font_addon();
return 0;
}
ALLEGRO_FONT* font = al_create_builtin_font();
if (!font) {
return 1;
}
al_destroy_font(font);
return 1;
}
n_log(
LOG_NOTICE,
"n_gui benchmark: %d windows, datagrid %dx%d, %d list items, %d syntax lines",
for (int i = 0; i < 10; i++) {
al_clear_to_color(al_map_rgb(30, 30, 35));
}
double t0 = al_get_time();
al_clear_to_color(al_map_rgb(30, 30, 35));
}
double t1 = al_get_time();
al_flip_display();
double draw_ms = (t1 - t0) * 1000.0 / (
double)
draw_iters;
ALLEGRO_EVENT ev;
memset(&ev, 0, sizeof(ev));
double e0 = al_get_time();
ev.type = ALLEGRO_EVENT_MOUSE_AXES;
ev.mouse.dz = 0;
ev.mouse.dw = 0;
}
double e1 = al_get_time();
double event_us = (e1 - e0) * 1e6 / (
double)
event_iters;
n_log(
LOG_NOTICE,
"RESULT draw: %.4f ms/frame (%d frames, %.1f fps-equivalent)",
draw_ms,
draw_iters, draw_ms > 0.0 ? 1000.0 / draw_ms : 0.0);
al_destroy_font(font);
return 0;
}
void process_args(int argc, char **argv)
ALLEGRO_DISPLAY * display
static void on_noop_grid_select(int a, int b, void *u)
#define BENCH_DEFAULT_DRAW_ITERS
static void build_widgets_window(N_GUI_CTX *gui)
static void build_textarea_window(N_GUI_CTX *gui)
static void on_noop_select(int a, int b, int c, void *u)
#define BENCH_SYNTAX_LINES
static void build_syntaxview_window(N_GUI_CTX *gui)
#define BENCH_DEFAULT_EVENT_ITERS
static void build_datagrid_window(N_GUI_CTX *gui)
#define FreeNoLog(__ptr)
Free Handler without log.
#define Malloc(__ptr, __struct, __size)
Malloc Handler to get errors and set to 0.
void n_gui_set_display_size(N_GUI_CTX *ctx, float w, float h)
Set the display (viewport) size for global scrollbar computation.
#define N_GUI_ALIGN_LEFT
left aligned text
int n_gui_add_slider(N_GUI_CTX *ctx, int window_id, float x, float y, float w, float h, double min_val, double max_val, double initial, int mode, void(*on_change)(int, double, void *), void *user_data)
Add a slider widget.
void n_gui_textarea_set_text(N_GUI_CTX *ctx, int widget_id, const char *text)
set the text content of a textarea widget
void n_gui_set_display(N_GUI_CTX *ctx, ALLEGRO_DISPLAY *display)
Set the display pointer for clipboard operations (copy/paste).
int n_gui_radiolist_add_item(N_GUI_CTX *ctx, int widget_id, const char *text)
add an item to a radiolist widget
int n_gui_add_listbox(N_GUI_CTX *ctx, int window_id, float x, float y, float w, float h, int selection_mode, void(*on_select)(int, int, int, void *), void *user_data)
Add a listbox widget.
int n_gui_process_event(N_GUI_CTX *ctx, ALLEGRO_EVENT event)
Process an allegro event through the GUI system.
int n_gui_add_label(N_GUI_CTX *ctx, int window_id, const char *text, float x, float y, float w, float h, int align)
Add a static text label.
int n_gui_add_radiolist(N_GUI_CTX *ctx, int window_id, float x, float y, float w, float h, void(*on_select)(int, int, void *), void *user_data)
Add a radio list widget (single selection with radio bullets)
int n_gui_listbox_add_item(N_GUI_CTX *ctx, int widget_id, const char *text)
add an item to a listbox widget
#define N_GUI_SELECT_SINGLE
single item selection
int n_gui_datagrid_add_column(N_GUI_CTX *ctx, int widget_id, const char *title, float width)
append a column with a header title and pixel width; returns the column index or -1
void n_gui_draw(N_GUI_CTX *ctx)
Draw all visible windows and their widgets.
#define N_GUI_SYNTAX_JSON
syntax view: JSON highlighting (keys, strings, numbers, punctuation)
int n_gui_add_syntaxview(N_GUI_CTX *ctx, int window_id, float x, float y, float w, float h, int mode)
Add a read-only syntax-highlighted text view.
int n_gui_add_window(N_GUI_CTX *ctx, const char *title, float x, float y, float w, float h)
Add a new pseudo-window to the context.
N_GUI_CTX * n_gui_new_ctx(ALLEGRO_FONT *default_font)
Create a new GUI context.
void n_gui_destroy_ctx(N_GUI_CTX **ctx)
Destroy a GUI context and all its windows/widgets.
int n_gui_datagrid_add_row(N_GUI_CTX *ctx, int widget_id, const char **values)
append a row of cell strings (values must have one entry per column); returns the row index or -1
void n_gui_syntaxview_set_text(N_GUI_CTX *ctx, int widget_id, const char *text)
set (copy) the text shown by a syntax view
#define N_GUI_SHAPE_ROUNDED
rounded rectangle shape
#define N_GUI_ALIGN_JUSTIFIED
justified text (spread words to fill width)
int n_gui_add_textarea(N_GUI_CTX *ctx, int window_id, float x, float y, float w, float h, int multiline, size_t char_limit, void(*on_change)(int, const char *, void *), void *user_data)
Add a text area widget.
int n_gui_add_checkbox(N_GUI_CTX *ctx, int window_id, const char *label, float x, float y, float w, float h, int initial_checked, void(*on_toggle)(int, int, void *), void *user_data)
Add a checkbox widget.
int n_gui_add_datagrid(N_GUI_CTX *ctx, int window_id, float x, float y, float w, float h, void(*on_select)(int, int, void *), void *user_data)
Add a sortable data grid widget.
int n_gui_add_button(N_GUI_CTX *ctx, int window_id, const char *label, float x, float y, float w, float h, int shape, void(*on_click)(int, void *), void *user_data)
Add a button widget to a window.
The top-level GUI context that holds all windows.
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
#define LOG_DEBUG
debug-level messages
#define LOG_ERR
error conditions
void set_log_level(const int log_level)
Set the global log level value ( static int LOG_LEVEL )
#define LOG_NOTICE
normal but significant condition
#define LOG_NULL
no log output
#define LOG_INFO
informational
GUI system: buttons, sliders, text areas, checkboxes, scrollbars, dropdown menus, windows.