]> Pileus Git - ~andy/gtk/blob - perf/gtkwidgetprofiler.c
stylecontext: Do invalidation on first resize container
[~andy/gtk] / perf / gtkwidgetprofiler.c
1 #include "config.h"
2 #include <string.h>
3 #include "gtkwidgetprofiler.h"
4 #include "typebuiltins.h"
5
6 typedef enum {
7   STATE_NOT_CREATED,
8   STATE_INSTRUMENTED_NOT_MAPPED,
9   STATE_INSTRUMENTED_MAPPED
10 } State;
11
12 struct _GtkWidgetProfilerPrivate {
13   State state;
14
15   GtkWidget *profiled_widget;
16   GtkWidget *toplevel;
17
18   int n_iterations;
19
20   GTimer *timer;
21
22   gulong toplevel_draw_id;
23   gulong toplevel_property_notify_event_id;
24
25   GdkAtom profiler_atom;
26
27   guint profiling : 1;
28 };
29
30 G_DEFINE_TYPE (GtkWidgetProfiler, gtk_widget_profiler, G_TYPE_OBJECT);
31
32 static void gtk_widget_profiler_finalize (GObject *object);
33
34 enum {
35   CREATE_WIDGET,
36   REPORT,
37   LAST_SIGNAL
38 };
39
40 static guint signals[LAST_SIGNAL];
41
42 static void
43 gtk_widget_profiler_class_init (GtkWidgetProfilerClass *class)
44 {
45   GObjectClass *object_class;
46
47   object_class = (GObjectClass *) class;
48
49   signals[CREATE_WIDGET] =
50     g_signal_new ("create-widget",
51                   G_OBJECT_CLASS_TYPE (object_class),
52                   G_SIGNAL_RUN_LAST,
53                   G_STRUCT_OFFSET (GtkWidgetProfilerClass, create_widget),
54                   NULL, NULL,
55                   NULL,
56                   G_TYPE_OBJECT, 0);
57
58   signals[REPORT] =
59     g_signal_new ("report",
60                   G_OBJECT_CLASS_TYPE (object_class),
61                   G_SIGNAL_RUN_FIRST,
62                   G_STRUCT_OFFSET (GtkWidgetProfilerClass, report),
63                   NULL, NULL,
64                   NULL,
65                   G_TYPE_NONE, 3,
66                   GTK_TYPE_WIDGET_PROFILER_REPORT,
67                   G_TYPE_OBJECT,
68                   G_TYPE_DOUBLE);
69
70   object_class->finalize = gtk_widget_profiler_finalize;
71 }
72
73 static void
74 gtk_widget_profiler_init (GtkWidgetProfiler *profiler)
75 {
76   GtkWidgetProfilerPrivate *priv;
77
78   priv = g_new0 (GtkWidgetProfilerPrivate, 1);
79   profiler->priv = priv;
80
81   priv->state = STATE_NOT_CREATED;
82   priv->n_iterations = 1;
83
84   priv->timer = g_timer_new ();
85
86   priv->profiler_atom = gdk_atom_intern ("GtkWidgetProfiler", FALSE);
87 }
88
89 static void
90 reset_state (GtkWidgetProfiler *profiler)
91 {
92   GtkWidgetProfilerPrivate *priv;
93
94   priv = profiler->priv;
95
96   if (priv->toplevel)
97     {
98       g_signal_handler_disconnect (priv->toplevel, priv->toplevel_draw_id);
99       priv->toplevel_draw_id = 0;
100
101       g_signal_handler_disconnect (priv->toplevel, priv->toplevel_property_notify_event_id);
102       priv->toplevel_property_notify_event_id = 0;
103
104       gtk_widget_destroy (priv->toplevel);
105       priv->toplevel = NULL;
106       priv->profiled_widget = NULL;
107     }
108
109   priv->state = STATE_NOT_CREATED;
110 }
111
112 static void
113 gtk_widget_profiler_finalize (GObject *object)
114 {
115   GtkWidgetProfiler *profiler;
116   GtkWidgetProfilerPrivate *priv;
117
118   profiler = GTK_WIDGET_PROFILER (object);
119   priv = profiler->priv;
120
121   reset_state (profiler);
122   g_timer_destroy (priv->timer);
123
124   g_free (priv);
125
126   G_OBJECT_CLASS (gtk_widget_profiler_parent_class)->finalize (object);
127 }
128
129 GtkWidgetProfiler *
130 gtk_widget_profiler_new (void)
131 {
132   return g_object_new (GTK_TYPE_WIDGET_PROFILER, NULL);
133 }
134
135 void
136 gtk_widget_profiler_set_num_iterations (GtkWidgetProfiler *profiler,
137                                         gint               n_iterations)
138 {
139   GtkWidgetProfilerPrivate *priv;
140
141   g_return_if_fail (GTK_IS_WIDGET_PROFILER (profiler));
142   g_return_if_fail (n_iterations > 0);
143
144   priv = profiler->priv;
145   priv->n_iterations = n_iterations;
146 }
147
148 static void
149 report (GtkWidgetProfiler      *profiler,
150         GtkWidgetProfilerReport report,
151         gdouble                 elapsed)
152 {
153   GtkWidgetProfilerPrivate *priv;
154
155   priv = profiler->priv;
156
157   g_signal_emit (profiler, signals[REPORT], 0, report, priv->profiled_widget, elapsed);
158 }
159
160 static GtkWidget *
161 create_widget_via_emission (GtkWidgetProfiler *profiler)
162 {
163   GtkWidget *widget;
164
165   widget = NULL;
166   g_signal_emit (profiler, signals[CREATE_WIDGET], 0, &widget);
167   if (!widget)
168     g_error ("The profiler emitted the \"create-widget\" signal but the signal handler returned no widget!");
169
170   if (gtk_widget_get_visible (widget) || gtk_widget_get_mapped (widget))
171     g_error ("The handler for \"create-widget\" must return an unmapped and unshown widget");
172
173   return widget;
174 }
175
176 static gboolean
177 toplevel_property_notify_event_cb (GtkWidget *widget, GdkEventProperty *event, gpointer data)
178 {
179   GtkWidgetProfiler *profiler;
180   GtkWidgetProfilerPrivate *priv;
181   gdouble elapsed;
182
183   profiler = GTK_WIDGET_PROFILER (data);
184   priv = profiler->priv;
185
186   if (event->atom != priv->profiler_atom)
187     return FALSE;
188
189   /* Finish timing map/expose */
190
191   elapsed = g_timer_elapsed (priv->timer, NULL);
192   report (profiler, GTK_WIDGET_PROFILER_REPORT_EXPOSE, elapsed);
193
194   gtk_main_quit (); /* This will get us back to the end of profile_map_expose() */
195   return TRUE;
196 }
197
198 static gboolean
199 toplevel_idle_after_draw_cb (gpointer data)
200 {
201   GtkWidgetProfiler *profiler;
202   GtkWidgetProfilerPrivate *priv;
203
204   profiler = GTK_WIDGET_PROFILER (data);
205   priv = profiler->priv;
206
207   gdk_property_change (gtk_widget_get_window (priv->toplevel),
208                        priv->profiler_atom,
209                        gdk_atom_intern ("STRING", FALSE),
210                        8,
211                        GDK_PROP_MODE_REPLACE,
212                        (guchar *) "hello",
213                        strlen ("hello"));
214
215   return FALSE;
216 }
217
218 static gboolean
219 toplevel_draw_cb (GtkWidget *widget, cairo_t *cr, gpointer data)
220 {
221   GtkWidgetProfiler *profiler;
222
223   profiler = GTK_WIDGET_PROFILER (data);
224
225   gdk_threads_add_idle_full (G_PRIORITY_HIGH, toplevel_idle_after_draw_cb, profiler, NULL);
226   return FALSE;
227 }
228
229 static void
230 instrument_toplevel (GtkWidgetProfiler *profiler,
231                      GtkWidget         *toplevel)
232 {
233   GtkWidgetProfilerPrivate *priv;
234
235   priv = profiler->priv;
236
237   priv->toplevel_draw_id = g_signal_connect (toplevel, "draw",
238                                              G_CALLBACK (toplevel_draw_cb), profiler);
239
240   gtk_widget_add_events (toplevel, GDK_PROPERTY_CHANGE_MASK);
241   priv->toplevel_property_notify_event_id = g_signal_connect (toplevel, "property-notify-event",
242                                                               G_CALLBACK (toplevel_property_notify_event_cb), profiler);
243 }
244
245 static GtkWidget *
246 ensure_and_get_toplevel (GtkWidget *widget)
247 {
248         GtkWidget *toplevel;
249         GtkWidget *window;
250
251         toplevel = gtk_widget_get_toplevel (widget);
252         if (gtk_widget_is_toplevel (toplevel))
253                 return toplevel;
254
255         g_assert (toplevel == widget); /* we don't want extraneous ancestors */
256
257         window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
258         gtk_container_add (GTK_CONTAINER (window), widget);
259
260         return window;
261 }
262
263 static GtkWidget *
264 get_instrumented_toplevel (GtkWidgetProfiler *profiler,
265                            GtkWidget         *widget)
266 {
267   GtkWidget *window;
268
269   window = ensure_and_get_toplevel (widget);
270   instrument_toplevel (profiler, window);
271
272   return window;
273 }
274
275 static void
276 map_widget (GtkWidgetProfiler *profiler)
277 {
278   GtkWidgetProfilerPrivate *priv;
279
280   priv = profiler->priv;
281   g_assert (priv->state == STATE_INSTRUMENTED_NOT_MAPPED);
282
283   /* Time map.
284    *
285    * FIXME: we are really timing a show_all(); we don't really wait for all the "map_event" signals
286    * to happen.  Should we rename GTK_WIDGET_PROFILER_REPORT_MAP report to something else?
287    */
288
289   gtk_widget_show_all (priv->toplevel);
290   priv->state = STATE_INSTRUMENTED_MAPPED;
291 }
292
293 static void
294 profile_map_expose (GtkWidgetProfiler *profiler)
295 {
296   GtkWidgetProfilerPrivate *priv;
297   gdouble elapsed;
298
299   priv = profiler->priv;
300
301   g_assert (priv->state == STATE_INSTRUMENTED_NOT_MAPPED);
302
303   g_timer_reset (priv->timer);
304   map_widget (profiler);
305   elapsed = g_timer_elapsed (priv->timer, NULL);
306
307   report (profiler, GTK_WIDGET_PROFILER_REPORT_MAP, elapsed);
308
309   /* Time expose; this gets recorded in toplevel_property_notify_event_cb() */
310
311   g_timer_reset (priv->timer);
312   gtk_main ();
313 }
314
315 static void
316 profile_destroy (GtkWidgetProfiler *profiler)
317 {
318   GtkWidgetProfilerPrivate *priv;
319   gdouble elapsed;
320
321   priv = profiler->priv;
322
323   g_assert (priv->state != STATE_NOT_CREATED);
324
325   g_timer_reset (priv->timer);
326   reset_state (profiler);
327   elapsed = g_timer_elapsed (priv->timer, NULL);
328
329   report (profiler, GTK_WIDGET_PROFILER_REPORT_DESTROY, elapsed);
330 }
331
332 static void
333 create_widget (GtkWidgetProfiler *profiler)
334 {
335   GtkWidgetProfilerPrivate *priv;
336
337   priv = profiler->priv;
338
339   g_assert (priv->state == STATE_NOT_CREATED);
340
341   priv->profiled_widget = create_widget_via_emission (profiler);
342   priv->toplevel = get_instrumented_toplevel (profiler, priv->profiled_widget);
343
344   priv->state = STATE_INSTRUMENTED_NOT_MAPPED;
345 }
346
347 /* The "boot time" of a widget is the time needed to
348  *
349  *   1. Create the widget
350  *   2. Map it
351  *   3. Expose it
352  *   4. Destroy it.
353  *
354  * This runs a lot of interesting code:  instantiation, size requisition and
355  * allocation, realization, mapping, exposing, destruction.
356  */
357 static void
358 profile_boot (GtkWidgetProfiler *profiler)
359 {
360   GtkWidgetProfilerPrivate *priv;
361   gdouble elapsed;
362
363   priv = profiler->priv;
364
365   g_assert (priv->state == STATE_NOT_CREATED);
366
367   /* Time creation */
368
369   g_timer_reset (priv->timer);
370   create_widget (profiler);
371   elapsed = g_timer_elapsed (priv->timer, NULL);
372
373   report (profiler, GTK_WIDGET_PROFILER_REPORT_CREATE, elapsed);
374
375   /* Start timing map/expose */
376
377   profile_map_expose (profiler);
378
379   /* Profile destruction */
380
381   profile_destroy (profiler);
382 }
383
384 /* To measure expose time, we trigger a full expose on the toplevel window.  We
385  * do the same as xrefresh(1), i.e. we map and unmap a window to make the other
386  * one expose.
387  */
388 static void
389 profile_expose (GtkWidgetProfiler *profiler)
390 {
391   GtkWidgetProfilerPrivate *priv;
392   GtkAllocation allocation;
393   GdkWindow *window;
394   GdkWindowAttr attr;
395   int attr_mask;
396
397   priv = profiler->priv;
398
399   g_assert (priv->state == STATE_INSTRUMENTED_MAPPED);
400
401   /* Time creation */
402
403   gtk_widget_get_allocation (priv->toplevel, &allocation);
404
405   attr.x = 0;
406   attr.y = 0;
407   attr.width = allocation.width;
408   attr.height = allocation.width;
409   attr.wclass = GDK_INPUT_OUTPUT;
410   attr.window_type = GDK_WINDOW_TEMP;
411
412   attr_mask = GDK_WA_X | GDK_WA_Y;
413
414   window = gdk_window_new (gdk_screen_get_root_window (gtk_widget_get_screen (priv->toplevel)),
415                            &attr, attr_mask);
416
417   gdk_window_show (window);
418   gdk_window_hide (window);
419   gdk_window_destroy (window);
420
421   /* Time expose; this gets recorded in toplevel_property_notify_event_cb() */
422
423   g_timer_reset (priv->timer);
424   gtk_main ();
425 }
426
427 void
428 gtk_widget_profiler_profile_boot (GtkWidgetProfiler *profiler)
429 {
430   GtkWidgetProfilerPrivate *priv;
431   int i, n;
432
433   g_return_if_fail (GTK_IS_WIDGET_PROFILER (profiler));
434
435   priv = profiler->priv;
436   g_return_if_fail (!priv->profiling);
437
438   reset_state (profiler);
439   priv->profiling = TRUE;
440
441   n = priv->n_iterations;
442   for (i = 0; i < n; i++)
443     profile_boot (profiler);
444
445   priv->profiling = FALSE;
446 }
447
448 void
449 gtk_widget_profiler_profile_expose (GtkWidgetProfiler *profiler)
450 {
451   GtkWidgetProfilerPrivate *priv;
452   int i, n;
453
454   g_return_if_fail (GTK_IS_WIDGET_PROFILER (profiler));
455
456   priv = profiler->priv;
457   g_return_if_fail (!priv->profiling);
458
459   reset_state (profiler);
460   priv->profiling = TRUE;
461
462   create_widget (profiler);
463   map_widget (profiler);
464
465   n = priv->n_iterations;
466   for (i = 0; i < n; i++)
467     profile_expose (profiler);
468
469   priv->profiling = FALSE;
470
471   reset_state (profiler);
472 }