1/* -*- mode: C; c-basic-offset: 2; indent-tabs-mode: nil; -*- */
2
3#include <gtk/gtk.h>
4#include <stdlib.h>
5
6#include "frame-stats.h"
7#include "variable.h"
8
9typedef struct FrameStats FrameStats;
10
11struct FrameStats
12{
13 GdkFrameClock *frame_clock;
14
15 int num_stats;
16 double last_print_time;
17 int frames_since_last_print;
18 gint64 last_handled_frame;
19
20 Variable latency;
21};
22
23static int max_stats = -1;
24static double statistics_time = 5.;
25static gboolean machine_readable = FALSE;
26
27static GOptionEntry frame_sync_options[] = {
28 { "max-statistics", 'm', 0, G_OPTION_ARG_INT, &max_stats, "Maximum statistics printed", NULL },
29 { "machine-readable", 0, 0, G_OPTION_ARG_NONE, &machine_readable, "Print statistics in columns", NULL },
30 { "statistics-time", 's', 0, G_OPTION_ARG_DOUBLE, &statistics_time, "Statistics accumulation time", "TIME" },
31 { NULL }
32};
33
34void
35frame_stats_add_options (GOptionGroup *group)
36{
37 g_option_group_add_entries (group, entries: frame_sync_options);
38}
39
40static void
41print_double (const char *description,
42 double value)
43{
44 if (machine_readable)
45 g_print (format: "%g\t", value);
46 else
47 g_print (format: "%s: %g\n", description, value);
48}
49
50static void
51print_variable (const char *description,
52 Variable *variable)
53{
54 if (variable->weight != 0)
55 {
56 if (machine_readable)
57 g_print (format: "%g\t%g\t",
58 variable_mean (variable),
59 variable_standard_deviation (variable));
60 else
61 g_print (format: "%s: %g +/- %g\n", description,
62 variable_mean (variable),
63 variable_standard_deviation (variable));
64 }
65 else
66 {
67 if (machine_readable)
68 g_print (format: "-\t-\t");
69 else
70 g_print (format: "%s: <n/a>\n", description);
71 }
72}
73
74static void
75on_frame_clock_after_paint (GdkFrameClock *frame_clock,
76 FrameStats *frame_stats)
77{
78 gint64 frame_counter;
79 gint64 current_time;
80
81 current_time = g_get_monotonic_time ();
82 if (current_time >= frame_stats->last_print_time + 1000000 * statistics_time)
83 {
84 if (frame_stats->frames_since_last_print)
85 {
86 if (frame_stats->num_stats == 0 && machine_readable)
87 {
88 g_print (format: "# load_factor frame_rate latency\n");
89 }
90
91 frame_stats->num_stats++;
92 print_double (description: "Frame rate ",
93 value: frame_stats->frames_since_last_print /
94 ((current_time - frame_stats->last_print_time) / 1000000.));
95
96 print_variable (description: "Latency", variable: &frame_stats->latency);
97
98 g_print (format: "\n");
99 }
100
101 frame_stats->last_print_time = current_time;
102 frame_stats->frames_since_last_print = 0;
103 variable_init (variable: &frame_stats->latency);
104
105 if (frame_stats->num_stats == max_stats)
106 exit (status: 0);
107 }
108
109 frame_stats->frames_since_last_print++;
110
111 for (frame_counter = frame_stats->last_handled_frame;
112 frame_counter < gdk_frame_clock_get_frame_counter (frame_clock);
113 frame_counter++)
114 {
115 GdkFrameTimings *timings = gdk_frame_clock_get_timings (frame_clock, frame_counter);
116 GdkFrameTimings *previous_timings = gdk_frame_clock_get_timings (frame_clock, frame_counter: frame_counter - 1);
117
118 if (!timings || gdk_frame_timings_get_complete (timings))
119 frame_stats->last_handled_frame = frame_counter;
120
121 if (timings && gdk_frame_timings_get_complete (timings) && previous_timings &&
122 gdk_frame_timings_get_presentation_time (timings) != 0 &&
123 gdk_frame_timings_get_presentation_time (timings: previous_timings) != 0)
124 {
125 double display_time = (gdk_frame_timings_get_presentation_time (timings) - gdk_frame_timings_get_presentation_time (timings: previous_timings)) / 1000.;
126 double frame_latency = (gdk_frame_timings_get_presentation_time (timings: previous_timings) - gdk_frame_timings_get_frame_time (timings: previous_timings)) / 1000. + display_time / 2;
127
128 variable_add_weighted (variable: &frame_stats->latency, value: frame_latency, weight: display_time);
129 }
130 }
131}
132
133static void
134on_window_realize (GtkWidget *window,
135 FrameStats *frame_stats)
136{
137 frame_stats->frame_clock = gtk_widget_get_frame_clock (GTK_WIDGET (window));
138 g_signal_connect (frame_stats->frame_clock, "after-paint",
139 G_CALLBACK (on_frame_clock_after_paint), frame_stats);
140}
141
142static void
143on_window_unrealize (GtkWidget *window,
144 FrameStats *frame_stats)
145{
146 g_signal_handlers_disconnect_by_func (frame_stats->frame_clock,
147 (gpointer) on_frame_clock_after_paint,
148 frame_stats);
149 frame_stats->frame_clock = NULL;
150}
151
152static void
153on_window_destroy (GtkWidget *window,
154 FrameStats *stats)
155{
156 g_free (mem: stats);
157}
158
159void
160frame_stats_ensure (GtkWindow *window)
161{
162 FrameStats *frame_stats;
163
164 frame_stats = g_object_get_data (G_OBJECT (window), key: "frame-stats");
165 if (frame_stats != NULL)
166 return;
167
168 frame_stats = g_new0 (FrameStats, 1);
169 g_object_set_data (G_OBJECT (window), key: "frame-stats", data: frame_stats);
170
171 variable_init (variable: &frame_stats->latency);
172 frame_stats->last_handled_frame = -1;
173
174 g_signal_connect (window, "realize",
175 G_CALLBACK (on_window_realize), frame_stats);
176 g_signal_connect (window, "unrealize",
177 G_CALLBACK (on_window_unrealize), frame_stats);
178 g_signal_connect (window, "destroy",
179 G_CALLBACK (on_window_destroy), frame_stats);
180
181 if (gtk_widget_get_realized (GTK_WIDGET (window)))
182 on_window_realize (GTK_WIDGET (window), frame_stats);
183}
184

source code of gtk/tests/frame-stats.c