Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
ex_gui_bench.c
Go to the documentation of this file.
1/*
2 * Nilorea Library
3 * Copyright (C) 2005-2026 Castagnier Mickael
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14 * implied. See the License for the specific language governing
15 * permissions and limitations under the License.
16 *
17 * SPDX-License-Identifier: Apache-2.0
18 */
19
45#define ALLEGRO_UNSTABLE 1
46
47#include "nilorea/n_gui.h"
48
49#define BENCH_DISPLAY_W 1600
50#define BENCH_DISPLAY_H 1000
51
52/* default workload sizes, overridable on the command line */
53#define BENCH_DEFAULT_DRAW_ITERS 1000
54#define BENCH_DEFAULT_EVENT_ITERS 20000
55
56#define BENCH_GRID_ROWS 500
57#define BENCH_GRID_COLS 8
58#define BENCH_LIST_ITEMS 200
59#define BENCH_SYNTAX_LINES 200
60
61static int log_level = LOG_NOTICE;
64
65/* no-op callbacks: the benchmark never fires them, they only exist so the
66 * widgets are built exactly as a real application would build them */
67static void on_noop_select(int a, int b, int c, void* u) {
68 (void)a;
69 (void)b;
70 (void)c;
71 (void)u;
72}
73static void on_noop_grid_select(int a, int b, void* u) {
74 (void)a;
75 (void)b;
76 (void)u;
77}
78
79static void process_args(int argc, char** argv) {
80 int c = 0;
81 while ((c = getopt(argc, argv, "V:n:e:h")) != -1) {
82 switch (c) {
83 case 'V':
84 if (optarg) {
85 if (!strncmp("LOG_NULL", optarg, 8))
87 else if (!strncmp("LOG_NOTICE", optarg, 10))
89 else if (!strncmp("LOG_INFO", optarg, 8))
91 else if (!strncmp("LOG_ERR", optarg, 7))
93 else if (!strncmp("LOG_DEBUG", optarg, 9))
95 else {
96 fprintf(stderr, "%s -V (LOG_NULL|LOG_NOTICE|LOG_INFO|LOG_ERR|LOG_DEBUG)\n", argv[0]);
97 exit(1);
98 }
99 }
100 break;
101 case 'n':
102 if (optarg) draw_iters = atoi(optarg);
103 break;
104 case 'e':
105 if (optarg) event_iters = atoi(optarg);
106 break;
107 case 'h':
108 default:
109 fprintf(stderr, "%s [-V loglevel] [-n draw_iters] [-e event_iters]\n", argv[0]);
110 exit(1);
111 }
112 }
113 if (draw_iters < 1) draw_iters = 1;
114 if (event_iters < 1) event_iters = 1;
115}
116
117/* build a datagrid with many rows and columns */
119 int win = n_gui_add_window(gui, "Data Grid", 20, 20, 780, 940);
120 int grid = n_gui_add_datagrid(gui, win, 10, 10, 760, 900, on_noop_grid_select, NULL);
121 for (int c = 0; c < BENCH_GRID_COLS; c++) {
122 char title[32];
123 snprintf(title, sizeof(title), "Column %d", c);
124 n_gui_datagrid_add_column(gui, grid, title, 90.0f);
125 }
126 for (int r = 0; r < BENCH_GRID_ROWS; r++) {
127 char cells[BENCH_GRID_COLS][40];
128 const char* values[BENCH_GRID_COLS];
129 for (int c = 0; c < BENCH_GRID_COLS; c++) {
130 if ((c & 1) == 0)
131 snprintf(cells[c], sizeof(cells[c]), "%d", r * BENCH_GRID_COLS + c);
132 else
133 snprintf(cells[c], sizeof(cells[c]), "cell r%d c%d text", r, c);
134 values[c] = cells[c];
135 }
136 n_gui_datagrid_add_row(gui, grid, values);
137 }
138}
139
140/* build a multiline textarea filled with a few KB of wrapping text */
142 int win = n_gui_add_window(gui, "Text", 820, 20, 760, 460);
143 int ta = n_gui_add_textarea(gui, win, 10, 10, 740, 430, 1, 6000, NULL, NULL);
144 char buf[6001];
145 size_t p = 0;
146 int word = 0;
147 while (p < sizeof(buf) - 16) {
148 int n = snprintf(buf + p, sizeof(buf) - p, "word%d ", word++);
149 if (n <= 0) break;
150 p += (size_t)n;
151 if ((word % 9) == 0 && p < sizeof(buf) - 2) {
152 buf[p++] = '\n';
153 }
154 }
155 buf[p] = '\0';
157}
158
159/* build a JSON syntax view with a couple hundred highlighted lines */
161 int win = n_gui_add_window(gui, "JSON", 820, 500, 760, 460);
162 int sv = n_gui_add_syntaxview(gui, win, 10, 10, 740, 430, N_GUI_SYNTAX_JSON);
163 /* ~200 lines of small, dense JSON so tokenization has real work to do */
164 size_t cap = (size_t)BENCH_SYNTAX_LINES * 80 + 64;
165 char* buf = NULL;
166 Malloc(buf, char, cap);
167 if (!buf) return;
168 size_t p = 0;
169 p += (size_t)snprintf(buf + p, cap - p, "{\n");
170 for (int i = 0; i < BENCH_SYNTAX_LINES; i++) {
171 p += (size_t)snprintf(buf + p, cap - p,
172 " \"key_%d\": { \"id\": %d, \"name\": \"item_%d\", \"ok\": %s },\n",
173 i, i, i, (i & 1) ? "true" : "false");
174 }
175 p += (size_t)snprintf(buf + p, cap - p, " \"end\": null\n}\n");
176 buf[p] = '\0';
178 FreeNoLog(buf);
179}
180
181/* build list widgets plus many static labels/buttons/checkboxes/sliders */
183 int win = n_gui_add_window(gui, "Widgets", 400, 100, 760, 800);
184
185 int lb = n_gui_add_listbox(gui, win, 10, 10, 240, 400, N_GUI_SELECT_SINGLE, on_noop_select, NULL);
186 for (int i = 0; i < BENCH_LIST_ITEMS; i++) {
187 char item[48];
188 snprintf(item, sizeof(item), "list item number %d", i);
189 n_gui_listbox_add_item(gui, lb, item);
190 }
191
192 int rl = n_gui_add_radiolist(gui, win, 10, 420, 240, 300, NULL, NULL);
193 for (int i = 0; i < 20; i++) {
194 char item[32];
195 snprintf(item, sizeof(item), "radio %d", i);
196 n_gui_radiolist_add_item(gui, rl, item);
197 }
198
199 /* a column of static labels + buttons + checkboxes: exercises the
200 * per-widget "measure the label every frame" path */
201 float y = 10.0f;
202 for (int i = 0; i < 20; i++) {
203 char lab[48];
204 snprintf(lab, sizeof(lab), "Static label %d value", i);
205 n_gui_add_label(gui, win, lab, 270, y, 220, 20, N_GUI_ALIGN_LEFT);
206 y += 24.0f;
207 }
208 y = 10.0f;
209 for (int i = 0; i < 12; i++) {
210 char lab[32];
211 snprintf(lab, sizeof(lab), "Button %d", i);
212 n_gui_add_button(gui, win, lab, 500, y, 120, 26, N_GUI_SHAPE_ROUNDED, NULL, NULL);
213 y += 30.0f;
214 }
215 y = 10.0f;
216 for (int i = 0; i < 10; i++) {
217 char lab[32];
218 snprintf(lab, sizeof(lab), "Check %d", i);
219 n_gui_add_checkbox(gui, win, lab, 640, y, 100, 22, i & 1, NULL, NULL);
220 y += 26.0f;
221 }
222 n_gui_add_slider(gui, win, 270, 520, 220, 24, 0.0, 100.0, 42.0, 0, NULL, NULL);
223 n_gui_add_slider(gui, win, 270, 560, 220, 24, 0.0, 100.0, 73.0, 1, NULL, NULL);
224
225 /* a justified multi-line label: the word-wrap + per-word measure path */
226 n_gui_add_label(gui, win,
227 "This is a longer justified paragraph label used to exercise the "
228 "word wrapping and per-word text measurement path that runs every "
229 "single frame in the current implementation of the label draw code.",
230 270, 600, 470, 180, N_GUI_ALIGN_JUSTIFIED);
231}
232
233int main(int argc, char** argv) {
235 process_args(argc, argv);
237
238 if (!al_init()) {
239 n_log(LOG_ERR, "could not init allegro");
240 return 1;
241 }
242 al_init_primitives_addon();
243 al_init_font_addon();
244
245 /* Headless-safe: under a CI runner with no X server al_create_display
246 * fails. Skip cleanly (exit 0) exactly like ex_gui_multiwin. */
247 ALLEGRO_DISPLAY* display = al_create_display(BENCH_DISPLAY_W, BENCH_DISPLAY_H);
248 if (!display) {
249 n_log(LOG_NOTICE, "no display available (headless): skipping benchmark");
250 return 0;
251 }
252
253 ALLEGRO_FONT* font = al_create_builtin_font();
254 if (!font) {
255 n_log(LOG_ERR, "could not create builtin font");
256 al_destroy_display(display);
257 return 1;
258 }
259
260 N_GUI_CTX* gui = n_gui_new_ctx(font);
261 if (!gui) {
262 n_log(LOG_ERR, "could not create gui context");
263 al_destroy_font(font);
264 al_destroy_display(display);
265 return 1;
266 }
269
274
275 n_log(LOG_NOTICE, "n_gui benchmark: %d windows, datagrid %dx%d, %d list items, %d syntax lines",
277
278 al_set_target_backbuffer(display);
279
280 /* warm-up: prime font glyph caches and any first-frame lazy work */
281 for (int i = 0; i < 10; i++) {
282 al_clear_to_color(al_map_rgb(30, 30, 35));
284 }
285
286 /* draw benchmark: measure CPU cost of building each frame */
287 double t0 = al_get_time();
288 for (int i = 0; i < draw_iters; i++) {
289 al_clear_to_color(al_map_rgb(30, 30, 35));
291 }
292 double t1 = al_get_time();
293 al_flip_display();
294 double draw_ms = (t1 - t0) * 1000.0 / (double)draw_iters;
295
296 /* event benchmark: sweep synthetic mouse-motion events across the GUI */
297 ALLEGRO_EVENT ev;
298 memset(&ev, 0, sizeof(ev));
299 ev.mouse.display = display;
300 double e0 = al_get_time();
301 for (int i = 0; i < event_iters; i++) {
302 ev.type = ALLEGRO_EVENT_MOUSE_AXES;
303 ev.mouse.x = (i * 37) % BENCH_DISPLAY_W;
304 ev.mouse.y = (i * 53) % BENCH_DISPLAY_H;
305 ev.mouse.dz = 0;
306 ev.mouse.dw = 0;
308 }
309 double e1 = al_get_time();
310 double event_us = (e1 - e0) * 1e6 / (double)event_iters;
311
312 n_log(LOG_NOTICE, "RESULT draw: %.4f ms/frame (%d frames, %.1f fps-equivalent)",
313 draw_ms, draw_iters, draw_ms > 0.0 ? 1000.0 / draw_ms : 0.0);
314 n_log(LOG_NOTICE, "RESULT events: %.4f us/event (%d MOUSE_AXES events)",
315 event_us, event_iters);
316
318 al_destroy_font(font);
319 al_destroy_display(display);
320
321 return 0;
322}
int main(void)
void process_args(int argc, char **argv)
Definition ex_common.c:48
int log_level
Definition ex_fluid.c:60
ALLEGRO_DISPLAY * display
Definition ex_fluid.c:54
static void on_noop_grid_select(int a, int b, void *u)
#define BENCH_GRID_COLS
#define BENCH_DEFAULT_DRAW_ITERS
static void build_widgets_window(N_GUI_CTX *gui)
#define BENCH_GRID_ROWS
#define BENCH_DISPLAY_W
static void build_textarea_window(N_GUI_CTX *gui)
static void on_noop_select(int a, int b, int c, void *u)
static int draw_iters
static int event_iters
#define BENCH_LIST_ITEMS
#define BENCH_SYNTAX_LINES
static void build_syntaxview_window(N_GUI_CTX *gui)
#define BENCH_DEFAULT_EVENT_ITERS
#define BENCH_DISPLAY_H
static void build_datagrid_window(N_GUI_CTX *gui)
static N_GUI_CTX * gui
#define FreeNoLog(__ptr)
Free Handler without log.
Definition n_common.h:272
#define Malloc(__ptr, __struct, __size)
Malloc Handler to get errors and set to 0.
Definition n_common.h:204
void n_gui_set_display_size(N_GUI_CTX *ctx, float w, float h)
Set the display (viewport) size for global scrollbar computation.
Definition n_gui.c:9034
#define N_GUI_ALIGN_LEFT
left aligned text
Definition n_gui.h:189
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.
Definition n_gui.c:2442
void n_gui_textarea_set_text(N_GUI_CTX *ctx, int widget_id, const char *text)
set the text content of a textarea widget
Definition n_gui.c:3263
void n_gui_set_display(N_GUI_CTX *ctx, ALLEGRO_DISPLAY *display)
Set the display pointer for clipboard operations (copy/paste).
Definition n_gui.c:9071
int n_gui_radiolist_add_item(N_GUI_CTX *ctx, int widget_id, const char *text)
add an item to a radiolist widget
Definition n_gui.c:4349
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.
Definition n_gui.c:2620
int n_gui_process_event(N_GUI_CTX *ctx, ALLEGRO_EVENT event)
Process an allegro event through the GUI system.
Definition n_gui.c:9836
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.
Definition n_gui.c:2966
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)
Definition n_gui.c:2839
int n_gui_listbox_add_item(N_GUI_CTX *ctx, int widget_id, const char *text)
add an item to a listbox widget
Definition n_gui.c:3371
#define N_GUI_SELECT_SINGLE
single item selection
Definition n_gui.h:175
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
Definition n_gui.c:3805
void n_gui_draw(N_GUI_CTX *ctx)
Draw all visible windows and their widgets.
Definition n_gui.c:8542
#define N_GUI_SYNTAX_JSON
syntax view: JSON highlighting (keys, strings, numbers, punctuation)
Definition n_gui.h:137
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.
Definition n_gui.c:2745
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.
Definition n_gui.c:1063
N_GUI_CTX * n_gui_new_ctx(ALLEGRO_FONT *default_font)
Create a new GUI context.
Definition n_gui.c:903
void n_gui_destroy_ctx(N_GUI_CTX **ctx)
Destroy a GUI context and all its windows/widgets.
Definition n_gui.c:995
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
Definition n_gui.c:3862
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
Definition n_gui.c:3624
#define N_GUI_SHAPE_ROUNDED
rounded rectangle shape
Definition n_gui.h:149
#define N_GUI_ALIGN_JUSTIFIED
justified text (spread words to fill width)
Definition n_gui.h:195
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.
Definition n_gui.c:2498
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.
Definition n_gui.c:2543
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.
Definition n_gui.c:2787
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.
Definition n_gui.c:2124
The top-level GUI context that holds all windows.
Definition n_gui.h:1354
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
Definition n_log.h:89
#define LOG_DEBUG
debug-level messages
Definition n_log.h:84
#define LOG_ERR
error conditions
Definition n_log.h:76
void set_log_level(const int log_level)
Set the global log level value ( static int LOG_LEVEL )
Definition n_log.c:121
#define LOG_NOTICE
normal but significant condition
Definition n_log.h:80
#define LOG_NULL
no log output
Definition n_log.h:46
#define LOG_INFO
informational
Definition n_log.h:82
GUI system: buttons, sliders, text areas, checkboxes, scrollbars, dropdown menus, windows.