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