]> Pileus Git - ~andy/gtk/blob - gdk/gdkframeclock.c
gdk_frame_clock_get_frame_time(): use gint64 for time
[~andy/gtk] / gdk / gdkframeclock.c
1 /* GDK - The GIMP Drawing Kit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-2010.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
25  */
26
27 #include "config.h"
28
29 #include "gdkframeclockprivate.h"
30 #include "gdkinternals.h"
31
32 /**
33  * SECTION:frameclock
34  * @Short_description: Frame clock syncs painting to a window or display
35  * @Title: Frame clock
36  *
37  * A #GdkFrameClock tells the application when to update and repaint a
38  * window. This may be synced to the vertical refresh rate of the
39  * monitor, for example. Even when the frame clock uses a simple timer
40  * rather than a hardware-based vertical sync, the frame clock helps
41  * because it ensures everything paints at the same time (reducing the
42  * total number of frames). The frame clock can also automatically
43  * stop painting when it knows the frames will not be visible, or
44  * scale back animation framerates.
45  *
46  * #GdkFrameClock is designed to be compatible with an OpenGL-based
47  * implementation or with mozRequestAnimationFrame in Firefox,
48  * for example.
49  *
50  * A frame clock is idle until someone requests a frame with
51  * gdk_frame_clock_request_phase(). At some later point that makes
52  * sense for the synchronization being implemented, the clock will
53  * process a frame and emit signals for each phase that has been
54  * requested. (See the signals of the #GdkFrameClock class for
55  * documentation of the phases. GDK_FRAME_CLOCK_PHASE_UPDATE and the
56  * ::update signal are most interesting for application writers, and
57  * are used to update the animations, using the frame time given by
58  * gdk_frame_clock_get_frame_time().
59  *
60  * The frame time is reported in microseconds and generally in the same
61  * timescale as g_get_monotonic_time(), however, it is not the same
62  * as g_get_monotonic_time(). The frame time does not advance during
63  * the time a frame is being painted, and outside of a frame, an attempt
64  * is made so that all calls to gdk_frame_clock_get_frame_time() that
65  * are called at a "similar" time get the same value. This means that
66  * if different animations are timed by looking at the difference in
67  * time between an initial value from gdk_frame_clock_get_frame_time()
68  * and the value inside the ::update signal of the clock, they will
69  * stay exactly synchronized.
70  */
71
72 G_DEFINE_ABSTRACT_TYPE (GdkFrameClock, gdk_frame_clock, G_TYPE_OBJECT)
73
74 enum {
75   FLUSH_EVENTS,
76   BEFORE_PAINT,
77   UPDATE,
78   LAYOUT,
79   PAINT,
80   AFTER_PAINT,
81   RESUME_EVENTS,
82   LAST_SIGNAL
83 };
84
85 static guint signals[LAST_SIGNAL];
86
87 #define FRAME_HISTORY_MAX_LENGTH 16
88
89 struct _GdkFrameClockPrivate
90 {
91   gint64 frame_counter;
92   gint n_timings;
93   gint current;
94   GdkFrameTimings *timings[FRAME_HISTORY_MAX_LENGTH];
95 };
96
97 static void
98 gdk_frame_clock_finalize (GObject *object)
99 {
100   GdkFrameClockPrivate *priv = GDK_FRAME_CLOCK (object)->priv;
101   int i;
102
103   for (i = 0; i < FRAME_HISTORY_MAX_LENGTH; i++)
104     if (priv->timings[i] != 0)
105       gdk_frame_timings_unref (priv->timings[i]);
106
107   G_OBJECT_CLASS (gdk_frame_clock_parent_class)->finalize (object);
108 }
109
110 static void
111 gdk_frame_clock_class_init (GdkFrameClockClass *klass)
112 {
113   GObjectClass *gobject_class = (GObjectClass*) klass;
114
115   gobject_class->finalize     = gdk_frame_clock_finalize;
116
117   /**
118    * GdkFrameClock::flush-events:
119    * @clock: the frame clock emitting the signal
120    *
121    * This signal is used to flush pending motion events that
122    * are being batched up and compressed together. Applications
123    * should not handle this signal.
124    */
125   signals[FLUSH_EVENTS] =
126     g_signal_new (g_intern_static_string ("flush-events"),
127                   GDK_TYPE_FRAME_CLOCK,
128                   G_SIGNAL_RUN_LAST,
129                   0,
130                   NULL, NULL,
131                   g_cclosure_marshal_VOID__VOID,
132                   G_TYPE_NONE, 0);
133
134   /**
135    * GdkFrameClock::before-paint:
136    * @clock: the frame clock emitting the signal
137    *
138    * This signal begins processing of the frame. Applications
139    * should generally not handle this signal.
140    */
141   signals[BEFORE_PAINT] =
142     g_signal_new (g_intern_static_string ("before-paint"),
143                   GDK_TYPE_FRAME_CLOCK,
144                   G_SIGNAL_RUN_LAST,
145                   0,
146                   NULL, NULL,
147                   g_cclosure_marshal_VOID__VOID,
148                   G_TYPE_NONE, 0);
149
150   /**
151    * GdkFrameClock::update:
152    * @clock: the frame clock emitting the signal
153    *
154    * This signal is emitted as the first step of toolkit and
155    * application processing of the frame. Animations should
156    * be updated using gdk_frame_clock_get_frame_time().
157    * Applications can connect directly to this signal, or
158    * use gtk_widget_add_tick_callback() as a more convenient
159    * interface.
160    */
161   signals[UPDATE] =
162     g_signal_new (g_intern_static_string ("update"),
163                   GDK_TYPE_FRAME_CLOCK,
164                   G_SIGNAL_RUN_LAST,
165                   0,
166                   NULL, NULL,
167                   g_cclosure_marshal_VOID__VOID,
168                   G_TYPE_NONE, 0);
169
170   /**
171    * GdkFrameClock::layout:
172    * @clock: the frame clock emitting the signal
173    *
174    * This signal is emitted as the second step of toolkit and
175    * application processing of the frame. Any work to update
176    * sizes and positions of application elements should be
177    * performed. GTK normally handles this internally.
178    */
179   signals[LAYOUT] =
180     g_signal_new (g_intern_static_string ("layout"),
181                   GDK_TYPE_FRAME_CLOCK,
182                   G_SIGNAL_RUN_LAST,
183                   0,
184                   NULL, NULL,
185                   g_cclosure_marshal_VOID__VOID,
186                   G_TYPE_NONE, 0);
187
188   /**
189    * GdkFrameClock::layout:
190    * @clock: the frame clock emitting the signal
191    *
192    * This signal is emitted as the third step of toolkit and
193    * application processing of the frame. The frame is
194    * repainted. GDK normally handles this internally and
195    * produce expose events, which are turned into GTK
196    * GtkWidget::draw signals.
197    */
198   signals[PAINT] =
199     g_signal_new (g_intern_static_string ("paint"),
200                   GDK_TYPE_FRAME_CLOCK,
201                   G_SIGNAL_RUN_LAST,
202                   0,
203                   NULL, NULL,
204                   g_cclosure_marshal_VOID__VOID,
205                   G_TYPE_NONE, 0);
206
207   /**
208    * GdkFrameClock::after-paint:
209    * @clock: the frame clock emitting the signal
210    *
211    * This signal ends processing of the frame. Applications
212    * should generally not handle this signal.
213    */
214   signals[AFTER_PAINT] =
215     g_signal_new (g_intern_static_string ("after-paint"),
216                   GDK_TYPE_FRAME_CLOCK,
217                   G_SIGNAL_RUN_LAST,
218                   0,
219                   NULL, NULL,
220                   g_cclosure_marshal_VOID__VOID,
221                   G_TYPE_NONE, 0);
222
223   /**
224    * GdkFrameClock::resume-events:
225    * @clock: the frame clock emitting the signal
226    *
227    * This signal is emitted after processing of the frame is
228    * finished, and is handled internally by GTK+ to resume normal
229    * event processing. Applications should not handle this signal.
230    */
231   signals[RESUME_EVENTS] =
232     g_signal_new (g_intern_static_string ("resume-events"),
233                   GDK_TYPE_FRAME_CLOCK,
234                   G_SIGNAL_RUN_LAST,
235                   0,
236                   NULL, NULL,
237                   g_cclosure_marshal_VOID__VOID,
238                   G_TYPE_NONE, 0);
239
240   g_type_class_add_private (klass, sizeof (GdkFrameClockPrivate));
241 }
242
243 static void
244 gdk_frame_clock_init (GdkFrameClock *clock)
245 {
246   GdkFrameClockPrivate *priv;
247
248   clock->priv = G_TYPE_INSTANCE_GET_PRIVATE (clock,
249                                              GDK_TYPE_FRAME_CLOCK,
250                                              GdkFrameClockPrivate);
251   priv = clock->priv;
252
253   priv->frame_counter = -1;
254   priv->current = FRAME_HISTORY_MAX_LENGTH - 1;
255 }
256
257 /**
258  * gdk_frame_clock_get_frame_time:
259  * @frame_clock: a #GdkFrameClock
260  *
261  * Gets the time that should currently be used for animations.  Inside
262  * the processing of a frame, it's the time used to compute the
263  * animation position of everything in a frame. Outside of a frame, it's
264  * the time of the conceptual "previous frame," which may be either
265  * the actual previous frame time, or if that's too old, an updated
266  * time.
267  *
268  * Since: 3.8
269  * Return value: a timestamp in microseconds, in the timescale of
270  *  of g_get_monotonic_time().
271  */
272 gint64
273 gdk_frame_clock_get_frame_time (GdkFrameClock *frame_clock)
274 {
275   g_return_val_if_fail (GDK_IS_FRAME_CLOCK (frame_clock), 0);
276
277   return GDK_FRAME_CLOCK_GET_CLASS (frame_clock)->get_frame_time (frame_clock);
278 }
279
280 /**
281  * gdk_frame_clock_request_phase:
282  * @frame_clock: a #GdkFrameClock
283  *
284  * Asks the frame clock to run a particular phase. The signal
285  * corresponding the requested phase will be emitted the next
286  * time the frame clock processes. Multiple calls to
287  * gdk_frame_clock_request_phase() will be combined togethe
288  * and only one frame processed.
289  *
290  * Since: 3.8
291  */
292 void
293 gdk_frame_clock_request_phase (GdkFrameClock      *frame_clock,
294                                GdkFrameClockPhase  phase)
295 {
296   g_return_if_fail (GDK_IS_FRAME_CLOCK (frame_clock));
297
298   GDK_FRAME_CLOCK_GET_CLASS (frame_clock)->request_phase (frame_clock, phase);
299 }
300
301
302 void
303 _gdk_frame_clock_freeze (GdkFrameClock *clock)
304 {
305   g_return_if_fail (GDK_IS_FRAME_CLOCK (clock));
306
307   GDK_FRAME_CLOCK_GET_CLASS (clock)->freeze (clock);
308 }
309
310
311 void
312 _gdk_frame_clock_thaw (GdkFrameClock *clock)
313 {
314   g_return_if_fail (GDK_IS_FRAME_CLOCK (clock));
315
316   GDK_FRAME_CLOCK_GET_CLASS (clock)->thaw (clock);
317 }
318
319 /**
320  * gdk_frame_clock_get_frame_counter:
321  * @frame_clock: a #GdkFrameClock
322  *
323  * A #GdkFrameClock maintains a 64-bit counter that increments for
324  * each frame drawn.
325  *
326  * Returns: inside frame processing, the value of the frame counter
327  *  for the current frame. Outside of frame processing, the frame
328  *   counter for the last frame.
329  * Since: 3.8
330  */
331 gint64
332 gdk_frame_clock_get_frame_counter (GdkFrameClock *frame_clock)
333 {
334   GdkFrameClockPrivate *priv;
335
336   g_return_val_if_fail (GDK_IS_FRAME_CLOCK (frame_clock), 0);
337
338   priv = frame_clock->priv;
339
340   return priv->frame_counter;
341 }
342
343 /**
344  * gdk_frame_clock_get_history_start:
345  * @frame_clock: a #GdkFrameClock
346  *
347  * #GdkFrameClock internally keeps a history of #GdkFrameTiming
348  * objects for recent frames that can be retrieved with
349  * gdk_frame_clock_get_timings(). The set of stored frames
350  * is the set from the counter values given by
351  * gdk_frame_clock_get_history_start() and
352  * gdk_frame_clock_get_frame_counter(), inclusive.
353  *
354  * Return value: the frame counter value for the oldest frame
355  *  that is available in the internal frame history of the
356  *  #GdkFrameClock.
357  * Since: 3.8
358  */
359 gint64
360 gdk_frame_clock_get_history_start (GdkFrameClock *frame_clock)
361 {
362   GdkFrameClockPrivate *priv;
363
364   g_return_val_if_fail (GDK_IS_FRAME_CLOCK (frame_clock), 0);
365
366   priv = frame_clock->priv;
367
368   return priv->frame_counter + 1 - priv->n_timings;
369 }
370
371 void
372 _gdk_frame_clock_begin_frame (GdkFrameClock *frame_clock)
373 {
374   GdkFrameClockPrivate *priv;
375
376   g_return_if_fail (GDK_IS_FRAME_CLOCK (frame_clock));
377
378   priv = frame_clock->priv;
379
380   priv->frame_counter++;
381   priv->current = (priv->current + 1) % FRAME_HISTORY_MAX_LENGTH;
382
383   if (priv->n_timings < FRAME_HISTORY_MAX_LENGTH)
384     priv->n_timings++;
385   else
386     {
387       gdk_frame_timings_unref(priv->timings[priv->current]);
388     }
389
390   priv->timings[priv->current] = _gdk_frame_timings_new (priv->frame_counter);
391 }
392
393 /**
394  * gdk_frame_clock_get_timings:
395  * @frame_clock: a #GdkFrameClock
396  * @frame_counter: the frame counter value identifying the frame to
397  *  be received.
398  *
399  * Retrieves a #GdkFrameTimings object holding timing information
400  * for the current frame or a recent frame. The #GdkFrameTimings
401  * object may not yet be complete: see gdk_frame_timings_get_complete().
402  *
403  * Return value: the #GdkFrameTimings object for the specified
404  *  frame, or %NULL if it is not available. See
405  *  gdk_frame_clock_get_history_start().
406  * Since: 3.8
407  */
408 GdkFrameTimings *
409 gdk_frame_clock_get_timings (GdkFrameClock *frame_clock,
410                              gint64         frame_counter)
411 {
412   GdkFrameClockPrivate *priv;
413   gint pos;
414
415   g_return_val_if_fail (GDK_IS_FRAME_CLOCK (frame_clock), NULL);
416
417   priv = frame_clock->priv;
418
419   if (frame_counter > priv->frame_counter)
420     return NULL;
421
422   if (frame_counter <= priv->frame_counter - priv->n_timings)
423     return NULL;
424
425   pos = (priv->current - (priv->frame_counter - frame_counter) + FRAME_HISTORY_MAX_LENGTH) % FRAME_HISTORY_MAX_LENGTH;
426
427   return priv->timings[pos];
428 }
429
430 /**
431  * gdk_frame_clock_get_current_timings:
432  * @frame_clock: a #GdkFrameClock
433  *
434  * Gets the frame timings for the current frame.
435  *
436  * Returns: the #GdkFrameTimings for the frame currently being
437  *  processed, or even no frame is being processed, for the
438  *  previous frame. Before any frames have been procesed,
439  *  returns %NULL.
440  * Since: 3.8
441  */
442 GdkFrameTimings *
443 gdk_frame_clock_get_current_timings (GdkFrameClock *frame_clock)
444 {
445   GdkFrameClockPrivate *priv;
446
447   g_return_val_if_fail (GDK_IS_FRAME_CLOCK (frame_clock), 0);
448
449   priv = frame_clock->priv;
450
451   return gdk_frame_clock_get_timings (frame_clock, priv->frame_counter);
452 }
453
454
455 #ifdef G_ENABLE_DEBUG
456 void
457 _gdk_frame_clock_debug_print_timings (GdkFrameClock   *clock,
458                                       GdkFrameTimings *timings)
459 {
460   gint64 previous_frame_time = 0;
461   GdkFrameTimings *previous_timings = gdk_frame_clock_get_timings (clock,
462                                                                    timings->frame_counter - 1);
463
464   if (previous_timings != NULL)
465     previous_frame_time = previous_timings->frame_time;
466
467   g_print ("%5" G_GINT64_FORMAT ":", timings->frame_counter);
468   if (previous_frame_time != 0)
469     {
470       g_print (" interval=%-4.1f", (timings->frame_time - previous_frame_time) / 1000.);
471       g_print (timings->slept_before ?  " (sleep)" : "        ");
472     }
473   if (timings->layout_start_time != 0)
474     g_print (" layout_start=%-4.1f", (timings->layout_start_time - timings->frame_time) / 1000.);
475   if (timings->paint_start_time != 0)
476     g_print (" paint_start=%-4.1f", (timings->paint_start_time - timings->frame_time) / 1000.);
477   if (timings->frame_end_time != 0)
478     g_print (" frame_end=%-4.1f", (timings->frame_end_time - timings->frame_time) / 1000.);
479   if (timings->presentation_time != 0)
480     g_print (" present=%-4.1f", (timings->presentation_time - timings->frame_time) / 1000.);
481   if (timings->predicted_presentation_time != 0)
482     g_print (" predicted=%-4.1f", (timings->predicted_presentation_time - timings->frame_time) / 1000.);
483   if (timings->refresh_interval != 0)
484     g_print (" refresh_interval=%-4.1f", timings->refresh_interval / 1000.);
485   g_print ("\n");
486 }
487 #endif /* G_ENABLE_DEBUG */
488
489 #define DEFAULT_REFRESH_INTERVAL 16667 /* 16.7ms (1/60th second) */
490 #define MAX_HISTORY_AGE 150000         /* 150ms */
491
492 /**
493  * gdk_frame_clock_get_refresh_info:
494  * @frame_clock: a #GdkFrameClock
495  * @base_time: base time for determining a presentaton time
496  * @refresh_interval_return: a location to store the determined refresh
497  *  interval, or %NULL. A default refresh interval of 1/60th of
498  *  a second will be stored if no history is present.
499  * @presentation_time_return: a location to store the next
500  *  candidate presentation time after the given base time.
501  *  0 will be will be stored if no history is present.
502  *
503  * Using the frame history stored in the frame clock, finds the last
504  * known presentation time and refresh interval, and assuming that
505  * presentation times are separated by the refresh interval,
506  * predicts a presentation time that is a multiple of the refresh
507  * interval after the last presentation time, and later than @base_time.
508  *
509  * Since: 3.8
510  */
511 void
512 gdk_frame_clock_get_refresh_info (GdkFrameClock *frame_clock,
513                                   gint64         base_time,
514                                   gint64        *refresh_interval_return,
515                                   gint64        *presentation_time_return)
516 {
517   gint64 frame_counter;
518
519   g_return_if_fail (GDK_IS_FRAME_CLOCK (frame_clock));
520
521   frame_counter = gdk_frame_clock_get_frame_counter (frame_clock);
522
523   if (presentation_time_return)
524     *presentation_time_return = 0;
525   if (refresh_interval_return)
526     *refresh_interval_return = DEFAULT_REFRESH_INTERVAL;
527
528   while (TRUE)
529     {
530       GdkFrameTimings *timings = gdk_frame_clock_get_timings (frame_clock, frame_counter);
531       gint64 presentation_time;
532       gint64 refresh_interval;
533
534       if (timings == NULL)
535         return;
536
537       refresh_interval = timings->refresh_interval;
538       presentation_time = timings->presentation_time;
539
540       if (presentation_time != 0)
541         {
542           if (presentation_time > base_time - MAX_HISTORY_AGE &&
543               presentation_time_return)
544             {
545               if (refresh_interval == 0)
546                 refresh_interval = DEFAULT_REFRESH_INTERVAL;
547
548               if (refresh_interval_return)
549                 *refresh_interval_return = refresh_interval;
550
551               while (presentation_time < base_time)
552                 presentation_time += refresh_interval;
553
554               if (presentation_time_return)
555                 *presentation_time_return = presentation_time;
556             }
557
558           return;
559         }
560
561       frame_counter--;
562     }
563 }