33#include <json-glib/json-glib.h>
34#ifdef GDK_WINDOWING_WAYLAND
35#include <gdk/gdkwayland.h>
40#define TIMELINE_MAX_ROWS 4000
45#define TIMELINE_TRIM_SLACK (TIMELINE_MAX_ROWS / 4)
46#define POLL_INTERVAL_MS 300
47#define MEMORY_MAX_ROWS 500
48#define MEMORY_REFRESH_TICKS 4
49#define MEMORY_LIST_ROWS 20
94 return g_strdup_printf(
"0x%016" G_GINT64_MODIFIER
"x", h);
99 if(!g_strcmp0(
d,
"history"))
return "#7fbfff";
100 if(!g_strcmp0(
d,
"node"))
return "#9fe0a0";
101 if(!g_strcmp0(
d,
"cacheline"))
return "#ffd080";
102 if(!g_strcmp0(
d,
"backbuf"))
return "#ff9a9a";
103 if(!g_strcmp0(
d,
"widget"))
return "#d0a8ff";
104 if(!g_strcmp0(
d,
"thumbnail"))
return "#bcbcbc";
105 if(!g_strcmp0(
d,
"mipmap"))
return "#b0c4de";
106 if(!g_strcmp0(
d,
"image"))
return "#e8c8a0";
107 if(!g_strcmp0(
d,
"form"))
return "#f0a0c0";
113 JsonParser *
p = json_parser_new();
115 if(json_parser_load_from_data(
p, compact, -1, NULL))
117 JsonGenerator *
g = json_generator_new();
118 json_generator_set_root(
g, json_parser_get_root(
p));
119 json_generator_set_pretty(
g,
TRUE);
120 out = json_generator_to_data(
g, NULL);
124 return out ?
out : g_strdup(compact);
132 if(!
row || !
_g.timeline_scroll)
return;
133 GtkAdjustment *vadj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(
_g.timeline_scroll));
136 if(!gtk_widget_translate_coordinates(
row,
_g.timeline_list, 0, 0, NULL, &y))
return;
138 gtk_widget_get_allocation(
row, &alloc);
139 const double page = gtk_adjustment_get_page_size(vadj);
140 const double upper = gtk_adjustment_get_upper(vadj);
141 double target = (
double)y + alloc.height / 2.0 - page / 2.0;
142 target = CLAMP(target, 0.0,
MAX(0.0, upper - page));
143 gtk_adjustment_set_value(vadj, target);
149 if(
_g.window &&
_g.scroll_target)
154 g_clear_pointer(&
_g.scroll_target, g_free);
155 return G_SOURCE_REMOVE;
159static gboolean
_on_link(GtkLabel *label, gchar *uri, gpointer user_data)
161 if(!
_g.decl_map)
return TRUE;
165 gtk_notebook_set_current_page(GTK_NOTEBOOK(
_g.notebook),
_g.page_timeline);
167 if(toggle) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
TRUE);
168 gtk_list_box_select_row(GTK_LIST_BOX(
_g.timeline_list), GTK_LIST_BOX_ROW(
row));
169 gtk_widget_grab_focus(
row);
172 g_free(
_g.scroll_target);
173 _g.scroll_target = g_strdup(uri);
181 const char *uri = (
const char *)g_object_get_data(G_OBJECT(item),
"uri");
182 if(uri &&
_g.search_entry)
184 gtk_entry_set_text(GTK_ENTRY(
_g.search_entry), uri);
185 gtk_notebook_set_current_page(GTK_NOTEBOOK(
_g.notebook),
_g.page_search);
192 if(e->type != GDK_BUTTON_PRESS || e->button != GDK_BUTTON_SECONDARY)
return FALSE;
193 const char *uri = gtk_label_get_current_uri(GTK_LABEL(label));
196 if(!uri || !*uri) uri = (
const char *)g_object_get_data(G_OBJECT(label),
"own-hash");
197 if(!uri || !*uri)
return FALSE;
200 GtkWidget *mi = gtk_menu_item_new_with_label(_(
"Search for this hash"));
201 g_object_set_data_full(G_OBJECT(mi),
"uri", g_strdup(uri), g_free);
203 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
204 gtk_widget_show_all(menu);
205 gtk_menu_popup_at_pointer(GTK_MENU(menu), (GdkEvent *)e);
212 gtk_label_set_track_visited_links(GTK_LABEL(label),
FALSE);
213 g_signal_connect(label,
"activate-link", G_CALLBACK(
_on_link), NULL);
214 gtk_widget_add_events(label, GDK_BUTTON_PRESS_MASK);
215 g_signal_connect(label,
"button-press-event", G_CALLBACK(
_on_label_button), NULL);
222 const gboolean a = gtk_toggle_button_get_active(
t);
227 if(a && rev && !gtk_bin_get_child(GTK_BIN(rev)))
229 const char *json = (
const char *)g_object_get_data(G_OBJECT(
t),
"lazy_json");
233 gtk_container_add(GTK_CONTAINER(rev), body);
234 gtk_widget_show_all(body);
237 if(rev) gtk_revealer_set_reveal_child(GTK_REVEALER(rev), a);
238 gtk_button_set_label(GTK_BUTTON(
t), a ?
"▾" :
"▸");
249 GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
250 GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
252 GtkWidget *toggle = gtk_toggle_button_new_with_label(expanded ?
"▾" :
"▸");
253 gtk_button_set_relief(GTK_BUTTON(toggle), GTK_RELIEF_NONE);
254 gtk_widget_set_focus_on_click(toggle,
FALSE);
257 gtk_label_set_markup(GTK_LABEL(header), header_markup);
258 gtk_label_set_xalign(GTK_LABEL(header), 0.0);
261 GtkWidget *revealer = gtk_revealer_new();
262 gtk_revealer_set_reveal_child(GTK_REVEALER(revealer), expanded);
263 if(body) gtk_container_add(GTK_CONTAINER(revealer), body);
265 g_object_set_data(G_OBJECT(toggle),
"revealer", revealer);
266 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle), expanded);
267 g_signal_connect(toggle,
"toggled", G_CALLBACK(
_on_toggle), NULL);
269 gtk_box_pack_start(GTK_BOX(hbox), toggle,
FALSE,
FALSE, 0);
270 gtk_box_pack_start(GTK_BOX(hbox), header,
TRUE,
TRUE, 0);
271 gtk_box_pack_start(GTK_BOX(vbox), hbox,
FALSE,
FALSE, 0);
272 gtk_box_pack_start(GTK_BOX(vbox), revealer,
FALSE,
FALSE, 0);
274 g_object_set_data(G_OBJECT(vbox),
"toggle", toggle);
275 g_object_set_data(G_OBJECT(vbox),
"header", header);
282 gchar *e_op = g_markup_escape_text(ev->
op, -1);
283 gchar *e_dom = g_markup_escape_text(ev->
domain, -1);
284 gchar *e_thr = g_markup_escape_text(ev->
thread, -1);
285 gchar *e_mn_raw = g_markup_escape_text(ev->
mnemonic[0] ? ev->
mnemonic :
"-", -1);
289 const gboolean is_raster_mask
290 = !g_strcmp0(ev->
domain,
"cacheline") && !g_strcmp0(ev->
cache_name,
"raster mask");
291 gchar *e_mn = is_raster_mask ? g_strdup_printf(
"%s - raster mask", e_mn_raw) : g_strdup(e_mn_raw);
293 gchar *
out = g_strdup_printf(
294 "<tt>%9.3f</tt> <b>%-7s</b> <span foreground=\"%s\">%-10s</span> <b>%-16s</b> "
295 "<a href=\"%s\"><tt>%s</tt></a> <span size=\"small\"><i>%s</i></span>",
308 GtkWidget *detail = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4);
309 gtk_widget_set_margin_start(detail, 28);
310 gtk_widget_set_margin_bottom(detail, 6);
312 if(links && links->len)
314 GString *ls = g_string_new(NULL);
315 for(guint
i = 0;
i < links->len;
i++)
319 g_string_append_printf(ls,
"%s%s → <a href=\"%s\"><tt>%s</tt></a>",
i ?
" " :
"", lk->
label,
324 gtk_label_set_markup(GTK_LABEL(
lw), ls->str);
325 gtk_label_set_xalign(GTK_LABEL(
lw), 0.0);
327 gtk_box_pack_start(GTK_BOX(detail),
lw,
FALSE,
FALSE, 0);
328 g_string_free(ls,
TRUE);
332 gchar *escaped = g_markup_escape_text(pretty, -1);
333 gchar *mono = g_strdup_printf(
"<tt>%s</tt>", escaped);
335 gtk_label_set_markup(GTK_LABEL(body), mono);
336 gtk_label_set_xalign(GTK_LABEL(body), 0.0);
337 gtk_label_set_selectable(GTK_LABEL(body),
TRUE);
338 gtk_box_pack_start(GTK_BOX(detail), body,
FALSE,
FALSE, 0);
347 if(
p) g_array_free((GArray *)
p,
TRUE);
358 g_object_set_data_full(G_OBJECT(toggle),
"lazy_json", g_strdup(ev->
json), g_free);
363 g_object_set_data_full(G_OBJECT(toggle),
"lazy_links",
copy,
_links_free);
369 if(header) g_object_set_data_full(G_OBJECT(header),
"own-hash",
_hashhex(ev->
hash), g_free);
378 gtk_widget_show_all(w);
379 gtk_container_add(GTK_CONTAINER(
_g.timeline_list), w);
381 g_object_set_data(G_OBJECT(
row),
"toggle", g_object_get_data(G_OBJECT(w),
"toggle"));
382 if(!g_strcmp0(ev->
op,
"create"))
385 g_object_set_data_full(G_OBJECT(
row),
"svhash", g_strdup(hx), g_free);
386 g_hash_table_replace(
_g.decl_map, hx,
row);
394 GList *children = gtk_container_get_children(GTK_CONTAINER(
_g.timeline_list));
395 const int rows = (int)g_list_length(children) + (int)incoming;
397 for(GList *l = children; l && excess > 0; l = l->next, excess--)
400 const char *hx = (
const char *)g_object_get_data(G_OBJECT(
row),
"svhash");
401 if(hx && g_hash_table_lookup(
_g.decl_map, hx) ==
row) g_hash_table_remove(
_g.decl_map, hx);
402 gtk_widget_destroy(
row);
404 g_list_free(children);
409 GList *children = gtk_container_get_children(GTK_CONTAINER(list));
410 for(GList *l = children; l; l = l->next) gtk_widget_destroy(GTK_WIDGET(l->data));
411 g_list_free(children);
417 gtk_label_set_text(GTK_LABEL(
_g.count_label), s);
423 if(!g_strcmp0(by,
"thread"))
return ev->
thread;
424 if(!g_strcmp0(by,
"op"))
return ev->
op;
430 gchar *e = g_markup_escape_text(
g->key && *
g->key ?
g->key :
"(none)", -1);
431 gchar *hm = g_strdup_printf(
"<b>%s</b> <span size=\"small\">(%d)</span>", e,
g->count);
432 gtk_label_set_markup(GTK_LABEL(
g->header), hm);
446 g->key = g_strdup(
k);
447 g->body = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
448 gtk_widget_set_margin_start(
g->body, 12);
450 g->header = (
GtkWidget *)g_object_get_data(G_OBJECT(coll),
"header");
451 gtk_container_add(GTK_CONTAINER(
_g.grouped_list), coll);
452 gtk_widget_show_all(coll);
453 g_hash_table_insert(
_g.group_map, g_strdup(
k),
g);
457 gtk_widget_show_all(
row);
469 gchar *by = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(
_g.groupby));
470 for(guint
i = 0;
i < evs->len;
i++)
_grouped_append(g_ptr_array_index(evs,
i), by ? by :
"domain");
472 _g.grouped_last_seq = newlast;
481 g_hash_table_remove_all(
_g.group_map);
482 _g.grouped_last_seq = 0;
495 gtk_label_set_markup(GTK_LABEL(lbl), markup);
496 gtk_label_set_xalign(GTK_LABEL(lbl), 0.0);
499 gtk_label_set_ellipsize(GTK_LABEL(lbl), PANGO_ELLIPSIZE_END);
502 if(trailing && *trailing)
504 GtkWidget *
row = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8);
505 gtk_widget_set_margin_start(
row, 12);
506 gtk_box_pack_start(GTK_BOX(
row), lbl,
TRUE,
TRUE, 0);
508 gtk_label_set_markup(GTK_LABEL(tl), trailing);
509 gtk_label_set_xalign(GTK_LABEL(tl), 1.0);
511 if(evictable) gtk_style_context_add_class(gtk_widget_get_style_context(
row),
"dt_supervisor_evictable");
516 gtk_widget_set_margin_start(lbl, 12);
517 if(evictable) gtk_style_context_add_class(gtk_widget_get_style_context(lbl),
"dt_supervisor_evictable");
518 gtk_box_pack_start(GTK_BOX(box), lbl,
FALSE,
FALSE, 0);
525 const int64_t s = us / G_USEC_PER_SEC;
526 if(s < 60)
return g_strdup_printf(
"%" G_GINT64_FORMAT
" s", s);
527 if(s < 3600)
return g_strdup_printf(
"%" G_GINT64_FORMAT
" min", s / 60);
528 return g_strdup_printf(
"%" G_GINT64_FORMAT
" h", s / 3600);
535 const gboolean x_held =
x->refcount > 0, y_held = y->refcount > 0;
536 if(x_held != y_held)
return y_held - x_held;
537 return (y->host_bytes >
x->host_bytes) - (y->host_bytes <
x->host_bytes);
543 return (y->size >
x->size) - (y->size <
x->size);
549 return x->imgid - y->imgid;
558#define BAR_SEGMENTS 4
559#define BAR_SEGMENT_GAP 2.0
560#define COLOR_PIPELINE "#3987e5"
561#define COLOR_ALLOCATIONS "#d95926"
562#define COLOR_MIPMAP "#199e70"
607 return bar->
total > used ? bar->
total - used : 0;
620 *x1 =
width - before * scale;
624 for(
int i = 0;
i <
k;
i++)
626 *x0 = before * scale;
632 const _bar_t *bar = (
const _bar_t *)g_object_get_data(G_OBJECT(widget),
"bar");
633 const double width = gtk_widget_get_allocated_width(widget);
634 const double height = gtk_widget_get_allocated_height(widget);
637 cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 0.08);
646 double x0 = 0.0, x1 = 0.0;
648 GdkRGBA c = { 1.0, 1.0, 1.0, 0.45 };
649 if(seg->
color) gdk_rgba_parse(&c, seg->
color);
653 cairo_rectangle(cr, x0, 0.0, x1 - x0,
height);
656 cairo_set_source_rgba(cr, c.red, c.green, c.blue, 0.25);
658 cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.20);
661 gdk_cairo_set_source_rgba(cr, &c);
663 cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 0.30);
664 cairo_set_line_width(cr, 1.0);
665 for(
double x = x0 -
height;
x < x1;
x += 6.0)
668 cairo_line_to(cr,
x +
height, 0.0);
675 gdk_cairo_set_source_rgba(cr, &c);
686 ? g_strdup_printf(_(
"%.1f MiB (%.1f reserved)"),
_bar_segment_size(seg) / 1048576.0,
688 : g_strdup_printf(
"%.1f MiB", seg->
bytes / 1048576.0);
694 const _bar_t *bar = (
const _bar_t *)g_object_get_data(G_OBJECT(widget),
"bar");
697 const double width = gtk_widget_get_allocated_width(widget);
702 double x0 = 0.0, x1 = 0.0;
709 gchar *text = g_strdup_printf(
"%s: %s, %.0f%%", seg->
label,
size,
711 gtk_tooltip_set_text(
tooltip, text);
721 gchar *mark = seg->
color ? g_markup_printf_escaped(
"<span foreground=\"%s\">%s</span>", seg->
color, swatch)
722 : g_markup_escape_text(swatch, -1);
723 GtkWidget *swatch_label = gtk_label_new(NULL);
724 gtk_label_set_markup(GTK_LABEL(swatch_label), mark);
725 gtk_widget_set_valign(swatch_label, GTK_ALIGN_START);
729 gchar *text = g_markup_printf_escaped(
"%s\n%s", seg->
label,
size);
730 GtkWidget *text_label = gtk_label_new(NULL);
731 gtk_label_set_markup(GTK_LABEL(text_label), text);
732 gtk_label_set_xalign(GTK_LABEL(text_label), 0.0);
736 GtkWidget *block = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
737 gtk_box_pack_start(GTK_BOX(block), swatch_label,
FALSE,
FALSE, 0);
738 gtk_box_pack_start(GTK_BOX(block), text_label,
FALSE,
FALSE, 0);
745 GtkWidget *area = gtk_drawing_area_new();
746 gtk_widget_set_size_request(area, -1, 16);
749 g_object_set_data_full(G_OBJECT(area),
"bar",
copy, g_free);
750 g_signal_connect(area,
"draw", G_CALLBACK(
_bar_draw), NULL);
751 gtk_widget_set_has_tooltip(area,
TRUE);
752 g_signal_connect(area,
"query-tooltip", G_CALLBACK(
_bar_tooltip), NULL);
753 gtk_box_pack_start(GTK_BOX(box), area,
FALSE,
FALSE, 0);
756 if(bar->
count < 2)
return;
759 GtkWidget *
row = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 24);
761 gtk_flow_box_set_selection_mode(GTK_FLOW_BOX(legend), GTK_SELECTION_NONE);
762 gtk_flow_box_set_max_children_per_line(GTK_FLOW_BOX(legend),
BAR_SEGMENTS + 1);
763 gtk_flow_box_set_column_spacing(GTK_FLOW_BOX(legend), 24);
769 gtk_box_pack_start(GTK_BOX(
row), legend,
TRUE,
TRUE, 0);
770 for(
int k = bar->
count - 1;
k >= 0;
k--)
782 return G_SOURCE_REMOVE;
790 return g_strdup_printf(
"ui/supervisor/memory/%s/%s", section, setting);
795 gchar *
key =
_mem_conf_key((
const char *)g_object_get_data(expander,
"section"),
"folded");
812 const size_t held = bar ?
_bar_held(bar) : 0;
813 gchar *
t = bar ? g_markup_printf_escaped(
"<b>%s</b> %.1f / %.1f MiB (%.0f%%)", title, held / 1048576.0,
814 bar->
total / 1048576.0,
815 bar->
total ? 100.0 * (
double)held / (
double)bar->
total : 0.0)
816 : g_markup_printf_escaped(
"<b>%s</b>", title);
817 gtk_label_set_markup(GTK_LABEL(hdr),
t);
819 gtk_label_set_xalign(GTK_LABEL(hdr), 0.0);
820 gtk_label_set_ellipsize(GTK_LABEL(hdr), PANGO_ELLIPSIZE_END);
822 gtk_widget_set_margin_start(hdr, 6);
825 const gboolean
top = parent ==
_g.mem_box;
829 GtkWidget *expander = gtk_expander_new(NULL);
830 gtk_expander_set_label_widget(GTK_EXPANDER(expander), hdr);
831 gtk_expander_set_label_fill(GTK_EXPANDER(expander),
TRUE);
832 gtk_expander_set_expanded(GTK_EXPANDER(expander), expanded);
833 g_object_set_data_full(G_OBJECT(expander),
"section", g_strdup(
key), g_free);
836 GtkWidget *section = gtk_box_new(GTK_ORIENTATION_VERTICAL, 2);
837 gtk_widget_set_margin_top(section,
top ? 8 : 6);
838 if(!
top) gtk_widget_set_margin_start(section, 16);
839 gtk_box_pack_start(GTK_BOX(section), expander,
FALSE,
FALSE, 0);
840 gtk_box_pack_start(GTK_BOX(parent), section,
FALSE,
FALSE, 0);
846 gtk_widget_show(expander);
847 gtk_widget_show(hdr);
848 int expander_width = 0, label_width = 0;
849 gtk_widget_get_preferred_width(expander, NULL, &expander_width);
850 gtk_widget_get_preferred_width(hdr, NULL, &label_width);
851 GtkWidget *bar_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 2);
852 gtk_widget_set_margin_start(bar_box,
MAX(expander_width - label_width, 0) + gtk_widget_get_margin_start(hdr));
854 gtk_box_pack_start(GTK_BOX(section), bar_box,
FALSE,
FALSE, 0);
856 if(!expanded)
return NULL;
858 GtkWidget *body = gtk_box_new(GTK_ORIENTATION_VERTICAL, 2);
859 gtk_box_pack_start(GTK_BOX(section), body,
FALSE,
FALSE, 0);
874 GtkWidget *list = gtk_box_new(GTK_ORIENTATION_VERTICAL, 2);
880 GtkWidget *probe = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
881 gtk_box_pack_start(GTK_BOX(body), probe,
FALSE,
FALSE, 0);
884 gtk_widget_show_all(probe);
886 gtk_widget_get_preferred_height(probe, NULL, &
row);
887 gtk_widget_destroy(probe);
893 g_object_set_data(G_OBJECT(area),
"list", list);
894 g_hash_table_insert(
_g.mem_lists, g_strdup(section), g_object_ref_sink(area));
898 gtk_box_pack_start(GTK_BOX(body), area,
FALSE,
FALSE, 0);
906 const int devid = GPOINTER_TO_INT(data);
907 const gboolean x_held =
x->refcount > 0, y_held = y->refcount > 0;
908 if(x_held != y_held)
return y_held - x_held;
909 const size_t xb =
x->devices[devid].cl_bytes, yb = y->devices[devid].cl_bytes;
910 return (yb > xb) - (yb < xb);
921 for(guint
i = 0;
i < pe->len;
i++)
927 .free_label = _(
"free in the budget"),
936 gchar *title =
name ? g_strdup_printf(_(
"vRAM — %s"),
name) : g_strdup_printf(_(
"vRAM — device %d"), devid);
937 gchar *
key = g_strdup_printf(
"vram-%d", devid);
945 for(guint
i = 0;
i < pe->len;
i++)
947 gchar *ltitle = g_strdup_printf(_(
"Pipeline cache — %u items"), count);
948 key = g_strdup_printf(
"vram-%d/pipeline", devid);
964 if(
d->cl_count == 0)
continue;
966 gchar *ename = g_markup_escape_text(e->
name[0] ? e->
name :
"-", -1);
967 gchar *
m = g_strdup_printf(
"<a href=\"%s\"><tt>%s</tt></a> refs=%d hits=%d <i>%s</i>", hx, hx, e->
refcount,
969 gchar *column = g_strdup_printf(
"<tt>%8.2f MiB %2d buf</tt>",
d->cl_bytes / 1048576.0,
d->cl_count);
982 if(!
_g.mem_box)
return;
985 gpointer area = NULL;
986 g_hash_table_iter_init(&it,
_g.mem_lists);
987 while(g_hash_table_iter_next(&it, NULL, &area))
989 GtkWidget *parent = gtk_widget_get_parent(GTK_WIDGET(area));
990 if(parent) gtk_container_remove(GTK_CONTAINER(parent), GTK_WIDGET(area));
995 size_t cur = 0,
max = 0;
1004 size_t in_ram = 0, reserved = 0, allocated = 0;
1006 for(guint
i = 0;
i < pe->len;
i++)
1011 allocated += e->
size;
1019 size_t mip_cur = 0, mip_max = 0;
1029 .free_label = _(
"free in the cache budgets"),
1036 { _(
"headroom for OS and applications"), total > budgets ? total - budgets : 0, NULL,
TRUE,
TRUE } },
1043 const _bar_t pipeline = {
1045 .free_label = _(
"free in the budget"),
1046 .count = gpu ? 3 : 2,
1051 gchar *ptitle = g_strdup_printf(gpu ? _(
"Pipeline cache (RAM budget) — %u items") : _(
"Pipeline cache — %u items"),
1069 gchar *
name = g_markup_escape_text(e->
name[0] ? e->
name :
"-", -1);
1074 gchar *when = g_strdup_printf(
"%s %s ago", e->
allocation ?
"allocated" :
"used", age);
1075 gchar *column = g_strdup_printf(
"<tt>%8.2f MiB%s %20s</tt>", e->
host_bytes / 1048576.0,
1082 gchar *
m = g_strdup_printf(
"<span foreground=\"%s\">■</span> %s <i>%s</i>",
COLOR_ALLOCATIONS,
1083 _(
"allocation"),
name);
1090 gchar *
m = g_strdup_printf(
"<span foreground=\"%s\">■</span> <a href=\"%s\"><tt>%s</tt></a> refs=%d "
1091 "hits=%d <i>%s</i>",
1109 .free_label = _(
"free in the budget"),
1113 gchar *mtitle = g_strdup_printf(_(
"Mipmap cache — %u items"), me->len);
1122 gchar *
m = g_strdup_printf(
"<a href=\"%s\">image #%d · mip %d</a> %.2f MiB", hx, e->
imgid, e->
mip,
1123 e->
size / 1048576.0);
1128 g_array_free(me,
TRUE);
1136 .free_label = _(
"free in the budget"),
1138 .seg = { { _(
"image cache"), cur, NULL,
FALSE,
FALSE } },
1140 gchar *ititle = g_strdup_printf(_(
"Image cache — %u items"), ie->len);
1150 gchar *
m = g_strdup_printf(
"<a href=\"%s\">image #%d</a> <i>%s</i>", hx, e->
imgid, fn);
1156 g_array_free(ie,
TRUE);
1160 g_array_free(pe,
TRUE);
1162 gtk_widget_show_all(
_g.mem_box);
1171 gchar *lh = g_ascii_strdown(hx, -1);
1172 const gboolean
m = strstr(lh, needle) != NULL;
1183 const char *text_needle)
1189 for(guint
i = 0;
i < ev->
links->len;
i++)
1192 if(*text_needle && ev->
json)
1194 gchar *jl = g_ascii_strdown(ev->
json, -1);
1195 const gboolean
m = strstr(jl, text_needle) != NULL;
1204 if(!
_g.search_list)
return;
1206 if(!query || !*query)
1208 gtk_widget_show_all(
_g.search_list);
1213 gchar *text_needle = g_ascii_strdown(query, -1);
1214 const char *hash_needle = g_str_has_prefix(text_needle,
"0x") ? text_needle + 2 : text_needle;
1217 for(guint
i = 0;
i < evs->len;
i++)
1224 g_free(text_needle);
1225 gtk_widget_show_all(
_g.search_list);
1230 const char *txt = gtk_entry_get_text(GTK_ENTRY(e));
1232 if(txt && *txt) gtk_notebook_set_current_page(GTK_NOTEBOOK(
_g.notebook),
_g.page_search);
1238 if(!
_g.timeline_scroll)
return G_SOURCE_REMOVE;
1239 GtkAdjustment *adj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(
_g.timeline_scroll));
1240 if(adj) gtk_adjustment_set_value(adj, gtk_adjustment_get_upper(adj));
1241 return G_SOURCE_REMOVE;
1248 GdkWindow *
window = gtk_widget_get_window(
_g.window);
1250 GdkDevice *pointer = gdk_seat_get_pointer(gdk_display_get_default_seat(gdk_window_get_display(
window)));
1251 GdkModifierType mask = 0;
1252 gdk_window_get_device_position(
window, pointer, NULL, NULL, &mask);
1253 return (mask & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK)) != 0;
1258 if(!
_g.window)
return G_SOURCE_REMOVE;
1261 const int page = gtk_notebook_get_current_page(GTK_NOTEBOOK(
_g.notebook));
1275 GtkAdjustment *adj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(
_g.timeline_scroll));
1276 const gboolean at_bottom = !adj
1277 || gtk_adjustment_get_value(adj)
1278 >= gtk_adjustment_get_upper(adj) - gtk_adjustment_get_page_size(adj) - 4.0;
1288 const guint incoming = evs->len - first;
1292 g_object_ref(
_g.timeline_list);
1293 gtk_container_remove(GTK_CONTAINER(viewport),
_g.timeline_list);
1299 gtk_container_add(GTK_CONTAINER(viewport),
_g.timeline_list);
1300 g_object_unref(
_g.timeline_list);
1302 _g.last_seq = newlast;
1307 return G_SOURCE_CONTINUE;
1313 g_hash_table_remove_all(
_g.decl_map);
1318 _run_search(gtk_entry_get_text(GTK_ENTRY(
_g.search_entry)));
1331#define SUPERVISOR_CONF_PAGE "ui/supervisor/page"
1332#define SUPERVISOR_CONF_GROUP_BY "ui/supervisor/group_by"
1333#define SUPERVISOR_CONF_RECORD "ui/supervisor/record"
1337 if(page ==
_g.page_timeline)
return "timeline";
1338 if(page ==
_g.page_grouped)
return "grouped";
1339 if(page ==
_g.page_search)
return "search";
1345 if(!g_strcmp0(
name,
"timeline"))
return _g.page_timeline;
1346 if(!g_strcmp0(
name,
"grouped"))
return _g.page_grouped;
1347 if(!g_strcmp0(
name,
"search"))
return _g.page_search;
1348 return _g.page_memory;
1353 gchar *by = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(c));
1361 const gboolean active = gtk_toggle_button_get_active(
t);
1369 if((
int)page_num ==
_g.page_grouped)
1371 else if((
int)page_num ==
_g.page_memory)
1373 else if((
int)page_num ==
_g.page_search)
1374 _run_search(gtk_entry_get_text(GTK_ENTRY(
_g.search_entry)));
1378#define SUPERVISOR_CONF_X "ui/supervisor/window_x"
1379#define SUPERVISOR_CONF_Y "ui/supervisor/window_y"
1380#define SUPERVISOR_CONF_WIDTH "ui/supervisor/window_width"
1381#define SUPERVISOR_CONF_HEIGHT "ui/supervisor/window_height"
1386#ifdef GDK_WINDOWING_WAYLAND
1387 return !GDK_IS_WAYLAND_DISPLAY(gtk_widget_get_display(
window));
1397 GdkWindow *gdk_window = gtk_widget_get_window(
window);
1399 || (gdk_window_get_state(gdk_window) & (GDK_WINDOW_STATE_MAXIMIZED | GDK_WINDOW_STATE_FULLSCREEN)))
1409 gtk_window_get_position(GTK_WINDOW(
window), &
x, &y);
1434 GdkDisplay *display = gtk_widget_get_display(
window);
1435 GdkMonitor *monitor = gdk_display_get_monitor_at_point(display,
x +
width / 2, y +
height / 2);
1436 if(
IS_NULL_PTR(monitor)) monitor = gdk_display_get_primary_monitor(display);
1437 if(
IS_NULL_PTR(monitor) && gdk_display_get_n_monitors(display) > 0) monitor = gdk_display_get_monitor(display, 0);
1440 GdkRectangle workarea = { 0 };
1441 gdk_monitor_get_workarea(monitor, &workarea);
1442 x = CLAMP(
x, workarea.x, workarea.x +
MAX(0, workarea.width -
width));
1443 y = CLAMP(y, workarea.y, workarea.y +
MAX(0, workarea.height -
height));
1445 gtk_window_move(GTK_WINDOW(
window),
x, y);
1450 if(
_g.timer_id) g_source_remove(
_g.timer_id);
1452 if(
_g.decl_map) g_hash_table_destroy(
_g.decl_map);
1453 if(
_g.group_map) g_hash_table_destroy(
_g.group_map);
1454 if(
_g.mem_lists) g_hash_table_destroy(
_g.mem_lists);
1455 g_free(
_g.scroll_target);
1456 memset(&
_g, 0,
sizeof(
_g));
1463 gtk_window_present(GTK_WINDOW(
_g.window));
1467 _g.decl_map = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
1468 _g.group_map = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
_group_free);
1469 _g.mem_lists = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_object_unref);
1471 _g.window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1472 gtk_window_set_title(GTK_WINDOW(
_g.window), _(
"Event supervisor"));
1475 g_signal_connect(
_g.window,
"configure-event", G_CALLBACK(
_on_configure), NULL);
1476 g_signal_connect(
_g.window,
"destroy", G_CALLBACK(
_on_destroy), NULL);
1478 GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
1479 gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);
1480 gtk_container_add(GTK_CONTAINER(
_g.window), vbox);
1483 GtkWidget *bar = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8);
1484 gtk_box_pack_start(GTK_BOX(vbox), bar,
FALSE,
FALSE, 0);
1486 GtkWidget *record = gtk_check_button_new_with_label(_(
"Record"));
1489 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(record), recording);
1492 gtk_box_pack_start(GTK_BOX(bar), record,
FALSE,
FALSE, 0);
1494 GtkWidget *refresh = gtk_button_new_with_label(_(
"Refresh"));
1495 g_signal_connect(refresh,
"clicked", G_CALLBACK(
_on_refresh), NULL);
1496 gtk_box_pack_start(GTK_BOX(bar), refresh,
FALSE,
FALSE, 0);
1498 GtkWidget *clear = gtk_button_new_with_label(_(
"Clear"));
1499 g_signal_connect(clear,
"clicked", G_CALLBACK(
_on_clear), NULL);
1500 gtk_box_pack_start(GTK_BOX(bar), clear,
FALSE,
FALSE, 0);
1502 gtk_box_pack_start(GTK_BOX(bar), gtk_label_new(_(
"Group by")),
FALSE,
FALSE, 0);
1503 _g.groupby = gtk_combo_box_text_new();
1504 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(
_g.groupby),
"domain");
1505 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(
_g.groupby),
"thread");
1506 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(
_g.groupby),
"op");
1508 gtk_combo_box_set_id_column(GTK_COMBO_BOX(
_g.groupby), 0);
1511 gtk_combo_box_set_active(GTK_COMBO_BOX(
_g.groupby), 0);
1513 gtk_box_pack_start(GTK_BOX(bar),
_g.groupby,
FALSE,
FALSE, 0);
1516 _g.search_entry = gtk_search_entry_new();
1517 gtk_entry_set_placeholder_text(GTK_ENTRY(
_g.search_entry), _(
"search hash or text…"));
1518 gtk_widget_set_size_request(
_g.search_entry, 220, -1);
1520 gtk_box_pack_end(GTK_BOX(bar),
_g.search_entry,
FALSE,
FALSE, 0);
1522 _g.count_label = gtk_label_new(
"");
1523 gtk_box_pack_end(GTK_BOX(bar),
_g.count_label,
FALSE,
FALSE, 0);
1526 _g.notebook = gtk_notebook_new();
1527 gtk_box_pack_start(GTK_BOX(vbox),
_g.notebook,
TRUE,
TRUE, 0);
1529 _g.mem_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 2);
1530 gtk_container_set_border_width(GTK_CONTAINER(
_g.mem_box), 6);
1531 GtkWidget *sw3 = gtk_scrolled_window_new(NULL, NULL);
1532 gtk_container_add(GTK_CONTAINER(sw3),
_g.mem_box);
1533 _g.page_memory = gtk_notebook_append_page(GTK_NOTEBOOK(
_g.notebook), sw3, gtk_label_new(_(
"Memory")));
1535 _g.timeline_list = gtk_list_box_new();
1536 gtk_list_box_set_selection_mode(GTK_LIST_BOX(
_g.timeline_list), GTK_SELECTION_SINGLE);
1537 _g.timeline_scroll = gtk_scrolled_window_new(NULL, NULL);
1538 gtk_container_add(GTK_CONTAINER(
_g.timeline_scroll),
_g.timeline_list);
1539 _g.page_timeline = gtk_notebook_append_page(GTK_NOTEBOOK(
_g.notebook),
_g.timeline_scroll,
1540 gtk_label_new(_(
"Timeline")));
1542 _g.grouped_list = gtk_list_box_new();
1543 gtk_list_box_set_selection_mode(GTK_LIST_BOX(
_g.grouped_list), GTK_SELECTION_NONE);
1544 GtkWidget *sw2 = gtk_scrolled_window_new(NULL, NULL);
1545 gtk_container_add(GTK_CONTAINER(sw2),
_g.grouped_list);
1546 _g.page_grouped = gtk_notebook_append_page(GTK_NOTEBOOK(
_g.notebook), sw2, gtk_label_new(_(
"Grouped")));
1548 _g.search_list = gtk_list_box_new();
1549 gtk_list_box_set_selection_mode(GTK_LIST_BOX(
_g.search_list), GTK_SELECTION_NONE);
1550 GtkWidget *sw4 = gtk_scrolled_window_new(NULL, NULL);
1551 gtk_container_add(GTK_CONTAINER(sw4),
_g.search_list);
1552 _g.page_search = gtk_notebook_append_page(GTK_NOTEBOOK(
_g.notebook), sw4, gtk_label_new(_(
"Search")));
1555 GList *
pages = gtk_container_get_children(GTK_CONTAINER(
_g.notebook));
1556 for(GList *l =
pages; l; l = l->next)
1557 gtk_container_child_set(GTK_CONTAINER(
_g.notebook), GTK_WIDGET(l->data),
"tab-expand",
TRUE,
1558 "tab-fill",
TRUE, NULL);
1561 gtk_widget_show_all(
_g.window);
1565 gtk_notebook_set_current_page(GTK_NOTEBOOK(
_g.notebook),
GtkWidget * dt_gui_main_window(void)
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
const dt_colormatrix_t dt_aligned_pixel_t out
void dt_conf_set_bool(const char *name, int val)
int dt_conf_get_bool(const char *name)
int dt_conf_key_exists(const char *key)
Does key have a value?
void dt_conf_set_int(const char *name, int val)
int dt_conf_get_int(const char *name)
Integer for name, clamped to the bounds declared in the XML.
void dt_conf_set_string(const char *name, const char *val)
const char * dt_conf_get_string_const(const char *name)
Borrow the stored string for name without copying it.
size_t dt_get_total_mem(void)
GHashTable * pages
row label -> path of the illustration, relative to the destination
void dt_image_cache_get_usage(size_t *current, size_t *max)
GArray * dt_image_cache_get_entries_stats(void)
float *const restrict const size_t k
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
void dt_mipmap_cache_get_usage(size_t *current, size_t *max)
GArray * dt_mipmap_cache_get_entries_stats(void)
const char * dt_opencl_get_device_name(const int devid)
Human-readable device name, owned by the OpenCL module.
size_t dt_opencl_get_device_headroom(const int devid)
Bytes of the device left to other applications (its forced_headroom, from its own config key or memor...
size_t dt_opencl_get_device_memory_in_use(const int devid)
Bytes Ansel holds on the device by its own accounting (dt_opencl_memory_statistics()),...
int dt_opencl_is_enabled(void)
size_t dt_opencl_get_device_max_global_mem(const int devid)
Total device memory in bytes, or 0 for an out-of-range id.
int dt_opencl_get_num_devices(void)
Number of usable OpenCL devices; 0 when OpenCL is unavailable.
GArray * dt_dev_pixelpipe_cache_get_entries_stats(void)
size_t dt_dev_pixelpipe_cache_get_max_memory(void)
void dt_dev_pixelpipe_cache_get_usage(size_t *current, size_t *max)
Pixelpipe cache for storing intermediate results in the pixelpipe.
void copy(TYPE *dest, TYPE *source, size_t num_el)
Copy a flat buffer used to initialize test matrices.
unsigned __int64 uint64_t
dt_pixel_cache_stats_device_t * devices
void dt_supervisor_events_free(GPtrArray *events)
void dt_supervisor_set_recording(const gboolean on)
void dt_supervisor_events_clear(void)
GPtrArray * dt_supervisor_events_snapshot(void)
GPtrArray * dt_supervisor_events_snapshot_since(const uint64_t after_seq, uint64_t *out_last_seq)
uint64_t dt_supervisor_image_key(const int32_t imgid)
guint dt_supervisor_events_count(void)
uint64_t dt_supervisor_mipmap_key(const int32_t imgid, const int mip)
static GtkWidget * _event_widget(const dt_sv_logged_event_t *ev)
static void _on_mem_section_expanded(GObject *expander, GParamSpec *pspec, gpointer user_data)
GtkWidget * timeline_list
#define TIMELINE_TRIM_SLACK
static GtkWidget * _add_mem_section(GtkWidget *parent, const char *key, const char *title, const _bar_t *bar)
static gboolean _on_configure(GtkWidget *window, GdkEventConfigure *event, gpointer user_data)
static gboolean _poll(gpointer u)
static gint _cmp_device_size(gconstpointer a, gconstpointer b, gpointer data)
static size_t _bar_free(const _bar_t *bar)
static gchar * _bar_segment_text(const _bar_segment_t *seg)
#define SUPERVISOR_CONF_RECORD
static void _wire_hash_label(GtkWidget *label)
static void _group_set_header(_group_t *g)
GtkWidget * timeline_scroll
static void _run_search(const char *query)
static size_t _bar_held(const _bar_t *bar)
static void _bar_segment_span(const _bar_t *bar, const double width, const int k, double *x0, double *x1)
static void _timeline_trim(const guint incoming)
#define SUPERVISOR_CONF_WIDTH
static void _add_device_section(GArray *pe, const int devid)
static void _on_groupby_changed(GtkComboBox *c, gpointer u)
static int _page_from_name(const char *name)
#define SUPERVISOR_CONF_GROUP_BY
#define COLOR_ALLOCATIONS
static gboolean _window_position_is_usable(GtkWidget *window)
uint64_t grouped_last_seq
#define SUPERVISOR_CONF_X
static void _restore_geometry(GtkWidget *window)
#define TIMELINE_MAX_ROWS
static gboolean _bar_draw(GtkWidget *widget, cairo_t *cr, gpointer user_data)
static void _clear_list(GtkWidget *list)
static void _timeline_append(const dt_sv_logged_event_t *ev)
#define MEMORY_REFRESH_TICKS
static gint _cmp_image_imgid(gconstpointer a, gconstpointer b)
static void _add_bar(GtkWidget *box, const _bar_t *bar)
static gchar * _mem_conf_key(const char *section, const char *setting)
static void _on_search_changed(GtkSearchEntry *e, gpointer u)
static void _links_free(gpointer p)
static GtkWidget * _mem_list(GtkWidget *body, const char *section)
static gint _cmp_ram_size(gconstpointer a, gconstpointer b)
static void _group_free(gpointer p)
static const char * _domain_color(const char *d)
static void _on_toggle(GtkToggleButton *t, gpointer u)
void dt_gui_supervisor_window_show(void)
static gboolean _on_label_button(GtkWidget *label, GdkEventButton *e, gpointer u)
#define SUPERVISOR_CONF_Y
static const char * _page_name(const int page)
static gboolean _on_link(GtkLabel *label, gchar *uri, gpointer user_data)
static void _grouped_append(const dt_sv_logged_event_t *ev, const char *by)
static void _update_count(void)
static gint _cmp_mip_size(gconstpointer a, gconstpointer b)
static gboolean _event_matches(const dt_sv_logged_event_t *ev, const char *hash_needle, const char *text_needle)
static gboolean _rebuild_memory_idle(gpointer user_data)
static gchar * _hashhex(const uint64_t h)
static void _on_search_this_hash(GtkMenuItem *item, gpointer u)
static void _rebuild_grouped_now(void)
static void _on_destroy(GtkWidget *w, gpointer u)
static void _on_record_toggled(GtkToggleButton *t, gpointer u)
static void _on_page_changed(GtkNotebook *nb, GtkWidget *page, guint page_num, gpointer u)
static GtkWidget * _collapsible(const char *header_markup, GtkWidget *body, const gboolean expanded)
static void _rebuild_memory(void)
static size_t _bar_used(const _bar_t *bar)
static gboolean _hex_contains(const uint64_t h, const char *needle)
#define SUPERVISOR_CONF_HEIGHT
static gchar * _age_text(const int64_t us)
static gboolean _bar_tooltip(GtkWidget *widget, gint x, gint y, gboolean keyboard, GtkTooltip *tooltip, gpointer user_data)
static const char * _group_key(const dt_sv_logged_event_t *ev, const char *by)
static void _full_reload(void)
static gboolean _pointer_button_down(void)
static void _scroll_row_into_view(GtkWidget *row)
static gchar * _header_markup(const dt_sv_logged_event_t *ev)
static GtkWidget * _bar_legend_block(const _bar_segment_t *seg, const char *swatch)
static GtkWidget * _event_body_from(const char *json, GArray *links)
static gchar * _pretty_json(const char *compact)
static void _on_refresh(GtkButton *b, gpointer u)
static gboolean _scroll_target_idle(gpointer u)
static size_t _bar_segment_size(const _bar_segment_t *seg)
static void _add_mem_item(GtkWidget *box, const char *markup, const char *trailing, const gboolean evictable)
static void _on_clear(GtkButton *b, gpointer u)
static gboolean _scroll_bottom_idle(gpointer u)
#define SUPERVISOR_CONF_PAGE
static void _grouped_catch_up(void)
typedef double((*spd)(unsigned long int wavelength, double TempK))