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 | |
9 | typedef struct FrameStats FrameStats; |
10 | |
11 | struct 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 | |
23 | static int max_stats = -1; |
24 | static double statistics_time = 5.; |
25 | static gboolean machine_readable = FALSE; |
26 | |
27 | static 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 | |
34 | void |
35 | frame_stats_add_options (GOptionGroup *group) |
36 | { |
37 | g_option_group_add_entries (group, entries: frame_sync_options); |
38 | } |
39 | |
40 | static void |
41 | print_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 | |
50 | static void |
51 | print_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 | |
74 | static void |
75 | on_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 | |
133 | static void |
134 | on_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 | |
142 | static void |
143 | on_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 | |
152 | static void |
153 | on_window_destroy (GtkWidget *window, |
154 | FrameStats *stats) |
155 | { |
156 | g_free (mem: stats); |
157 | } |
158 | |
159 | void |
160 | frame_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 | |