]> Pileus Git - ~andy/gtk/blob - gdk/gdkframeclock.c
Merge GdkFrameHistory into GdkFrameClock
[~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 repaint a window.
38  * This may be synced to the vertical refresh rate of the monitor, for
39  * example. Even when the frame clock uses a simple timer rather than
40  * a hardware-based vertical sync, the frame clock helps because it
41  * ensures everything paints at the same time (reducing the total
42  * number of frames). The frame clock can also automatically stop
43  * painting when it knows the frames will not be visible, or scale back
44  * 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 that time, the frame clock
52  * emits its GdkFrameClock:frame-requested signal if no frame was
53  * already pending.
54  *
55  * At some later time after the frame is requested, the frame clock
56  * MAY indicate that a frame should be painted. To paint a frame the
57  * clock will: Emit GdkFrameClock:before-paint; update the frame time
58  * in the default handler for GdkFrameClock:before-paint; emit
59  * GdkFrameClock:paint; emit GdkFrameClock:after-paint.  The app
60  * should paint in a handler for the paint signal.
61  *
62  * If a given frame is not painted (the clock is idle), the frame time
63  * should still update to a conceptual "last frame." i.e. the frame
64  * time will keep moving forward roughly with wall clock time.
65  *
66  * The frame time is in milliseconds. However, it should not be
67  * thought of as having any particular relationship to wall clock
68  * time. Unlike wall clock time, it "snaps" to conceptual frame times
69  * so is low-resolution; it is guaranteed to never move backward (so
70  * say you reset your computer clock, the frame clock will not reset);
71  * and the frame clock is allowed to drift. For example nicer
72  * results when painting with vertical refresh sync may be obtained by
73  * painting as rapidly as possible, but always incrementing the frame
74  * time by the frame length on each frame. This results in a frame
75  * time that doesn't have a lot to do with wall clock time.
76  */
77
78 G_DEFINE_ABSTRACT_TYPE (GdkFrameClock, gdk_frame_clock, G_TYPE_OBJECT)
79
80 enum {
81   FRAME_REQUESTED,
82   FLUSH_EVENTS,
83   BEFORE_PAINT,
84   UPDATE,
85   LAYOUT,
86   PAINT,
87   AFTER_PAINT,
88   RESUME_EVENTS,
89   LAST_SIGNAL
90 };
91
92 static guint signals[LAST_SIGNAL];
93
94 #define FRAME_HISTORY_MAX_LENGTH 16
95
96 struct _GdkFrameClockPrivate
97 {
98   gint64 frame_counter;
99   gint n_timings;
100   gint current;
101   GdkFrameTimings *timings[FRAME_HISTORY_MAX_LENGTH];
102 };
103
104 static void
105 gdk_frame_clock_finalize (GObject *object)
106 {
107   GdkFrameClockPrivate *priv = GDK_FRAME_CLOCK (object)->priv;
108   int i;
109
110   for (i = 0; i < FRAME_HISTORY_MAX_LENGTH; i++)
111     if (priv->timings[i] != 0)
112       gdk_frame_timings_unref (priv->timings[i]);
113
114   G_OBJECT_CLASS (gdk_frame_clock_parent_class)->finalize (object);
115 }
116
117 static void
118 gdk_frame_clock_class_init (GdkFrameClockClass *klass)
119 {
120   GObjectClass *gobject_class = (GObjectClass*) klass;
121
122   gobject_class->finalize     = gdk_frame_clock_finalize;
123
124   /**
125    * GdkFrameClock::frame-requested:
126    * @clock: the frame clock emitting the signal
127    *
128    * This signal is emitted when a frame is not pending, and
129    * gdk_frame_clock_request_frame() is called to request a frame.
130    */
131   signals[FRAME_REQUESTED] =
132     g_signal_new (g_intern_static_string ("frame-requested"),
133                   GDK_TYPE_FRAME_CLOCK,
134                   G_SIGNAL_RUN_LAST,
135                   0,
136                   NULL, NULL,
137                   g_cclosure_marshal_VOID__VOID,
138                   G_TYPE_NONE, 0);
139
140   /**
141    * GdkFrameClock::flush-events:
142    * @clock: the frame clock emitting the signal
143    *
144    * FIXME.
145    */
146   signals[FLUSH_EVENTS] =
147     g_signal_new (g_intern_static_string ("flush-events"),
148                   GDK_TYPE_FRAME_CLOCK,
149                   G_SIGNAL_RUN_LAST,
150                   0,
151                   NULL, NULL,
152                   g_cclosure_marshal_VOID__VOID,
153                   G_TYPE_NONE, 0);
154
155   /**
156    * GdkFrameClock::before-paint:
157    * @clock: the frame clock emitting the signal
158    *
159    * This signal is emitted immediately before the paint signal and
160    * indicates that the frame time has been updated, and signal
161    * handlers should perform any preparatory work before painting.
162    */
163   signals[BEFORE_PAINT] =
164     g_signal_new (g_intern_static_string ("before-paint"),
165                   GDK_TYPE_FRAME_CLOCK,
166                   G_SIGNAL_RUN_LAST,
167                   0,
168                   NULL, NULL,
169                   g_cclosure_marshal_VOID__VOID,
170                   G_TYPE_NONE, 0);
171
172   /**
173    * GdkFrameClock::update:
174    * @clock: the frame clock emitting the signal
175    *
176    * FIXME.
177    */
178   signals[UPDATE] =
179     g_signal_new (g_intern_static_string ("update"),
180                   GDK_TYPE_FRAME_CLOCK,
181                   G_SIGNAL_RUN_LAST,
182                   0,
183                   NULL, NULL,
184                   g_cclosure_marshal_VOID__VOID,
185                   G_TYPE_NONE, 0);
186
187   /**
188    * GdkFrameClock::layout:
189    * @clock: the frame clock emitting the signal
190    *
191    * This signal is emitted immediately before the paint signal and
192    * indicates that the frame time has been updated, and signal
193    * handlers should perform any preparatory work before painting.
194    */
195   signals[LAYOUT] =
196     g_signal_new (g_intern_static_string ("layout"),
197                   GDK_TYPE_FRAME_CLOCK,
198                   G_SIGNAL_RUN_LAST,
199                   0,
200                   NULL, NULL,
201                   g_cclosure_marshal_VOID__VOID,
202                   G_TYPE_NONE, 0);
203
204   /**
205    * GdkFrameClock::paint:
206    * @clock: the frame clock emitting the signal
207    *
208    * Signal handlers for this signal should paint the window, screen,
209    * or whatever they normally paint.
210    */
211   signals[PAINT] =
212     g_signal_new (g_intern_static_string ("paint"),
213                   GDK_TYPE_FRAME_CLOCK,
214                   G_SIGNAL_RUN_LAST,
215                   0,
216                   NULL, NULL,
217                   g_cclosure_marshal_VOID__VOID,
218                   G_TYPE_NONE, 0);
219
220   /**
221    * GdkFrameClock::after-paint:
222    * @clock: the frame clock emitting the signal
223    *
224    * This signal is emitted immediately after the paint signal and
225    * allows signal handlers to do anything they'd like to do after
226    * painting has been completed. This is a relatively good time to do
227    * "expensive" processing in order to get it done in between frames.
228    */
229   signals[AFTER_PAINT] =
230     g_signal_new (g_intern_static_string ("after-paint"),
231                   GDK_TYPE_FRAME_CLOCK,
232                   G_SIGNAL_RUN_LAST,
233                   0,
234                   NULL, NULL,
235                   g_cclosure_marshal_VOID__VOID,
236                   G_TYPE_NONE, 0);
237
238   /**
239    * GdkFrameClock::resume-events:
240    * @clock: the frame clock emitting the signal
241    *
242    * FIXME.
243    */
244   signals[RESUME_EVENTS] =
245     g_signal_new (g_intern_static_string ("resume-events"),
246                   GDK_TYPE_FRAME_CLOCK,
247                   G_SIGNAL_RUN_LAST,
248                   0,
249                   NULL, NULL,
250                   g_cclosure_marshal_VOID__VOID,
251                   G_TYPE_NONE, 0);
252
253   g_type_class_add_private (klass, sizeof (GdkFrameClockPrivate));
254 }
255
256 static void
257 gdk_frame_clock_init (GdkFrameClock *clock)
258 {
259   GdkFrameClockPrivate *priv;
260
261   clock->priv = G_TYPE_INSTANCE_GET_PRIVATE (clock,
262                                              GDK_TYPE_FRAME_CLOCK,
263                                              GdkFrameClockPrivate);
264   priv = clock->priv;
265
266   priv->frame_counter = -1;
267   priv->current = FRAME_HISTORY_MAX_LENGTH - 1;
268 }
269
270 /**
271  * gdk_frame_clock_get_frame_time:
272  * @clock: the clock
273  *
274  * Gets the time that should currently be used for animations.  Inside
275  * a paint, it's the time used to compute the animation position of
276  * everything in a frame. Outside a paint, it's the time of the
277  * conceptual "previous frame," which may be either the actual
278  * previous frame time, or if that's too old, an updated time.
279  *
280  * The returned time has no relationship to wall clock time.  It
281  * increases roughly at 1 millisecond per wall clock millisecond, and
282  * it never decreases, but its value is only meaningful relative to
283  * previous frame clock times.
284  *
285  *
286  * Since: 3.0
287  * Return value: a timestamp in milliseconds
288  */
289 guint64
290 gdk_frame_clock_get_frame_time (GdkFrameClock *clock)
291 {
292   g_return_val_if_fail (GDK_IS_FRAME_CLOCK (clock), 0);
293
294   return GDK_FRAME_CLOCK_GET_CLASS (clock)->get_frame_time (clock);
295 }
296
297 /**
298  * gdk_frame_clock_request_phase:
299  * @clock: the clock
300  *
301  * Asks the frame clock to paint a frame. The frame
302  * may or may not ever be painted (the frame clock may
303  * stop itself for whatever reason), but the goal in
304  * normal circumstances would be to paint the frame
305  * at the next expected frame time. For example
306  * if the clock is running at 60fps the frame would
307  * ideally be painted within 1000/60=16 milliseconds.
308  *
309  * Since: 3.0
310  */
311 void
312 gdk_frame_clock_request_phase (GdkFrameClock      *clock,
313                                GdkFrameClockPhase  phase)
314 {
315   g_return_if_fail (GDK_IS_FRAME_CLOCK (clock));
316
317   GDK_FRAME_CLOCK_GET_CLASS (clock)->request_phase (clock, phase);
318 }
319
320
321 void
322 gdk_frame_clock_freeze (GdkFrameClock *clock)
323 {
324   g_return_if_fail (GDK_IS_FRAME_CLOCK (clock));
325
326   GDK_FRAME_CLOCK_GET_CLASS (clock)->freeze (clock);
327 }
328
329
330 void
331 gdk_frame_clock_thaw (GdkFrameClock *clock)
332 {
333   g_return_if_fail (GDK_IS_FRAME_CLOCK (clock));
334
335   GDK_FRAME_CLOCK_GET_CLASS (clock)->thaw (clock);
336 }
337
338 /**
339  * gdk_frame_clock_get_requested:
340  * @clock: the clock
341  *
342  * Gets whether a frame paint has been requested but has not been
343  * performed.
344  *
345  *
346  * Since: 3.0
347  * Return value: TRUE if a frame paint is pending
348  */
349 GdkFrameClockPhase
350 gdk_frame_clock_get_requested (GdkFrameClock *clock)
351 {
352   g_return_val_if_fail (GDK_IS_FRAME_CLOCK (clock), FALSE);
353
354   return GDK_FRAME_CLOCK_GET_CLASS (clock)->get_requested (clock);
355 }
356
357 /**
358  * gdk_frame_clock_get_frame_time_val:
359  * @clock: the clock
360  * @timeval: #GTimeVal to fill in with frame time
361  *
362  * Like gdk_frame_clock_get_frame_time() but returns the time as a
363  * #GTimeVal which may be handy with some APIs (such as
364  * #GdkPixbufAnimation).
365  */
366 void
367 gdk_frame_clock_get_frame_time_val (GdkFrameClock *clock,
368                                     GTimeVal      *timeval)
369 {
370   guint64 time_ms;
371
372   g_return_if_fail (GDK_IS_FRAME_CLOCK (clock));
373
374   time_ms = gdk_frame_clock_get_frame_time (clock);
375
376   timeval->tv_sec = time_ms / 1000;
377   timeval->tv_usec = (time_ms % 1000) * 1000;
378 }
379
380 gint64
381 gdk_frame_clock_get_frame_counter (GdkFrameClock *clock)
382 {
383   GdkFrameClockPrivate *priv;
384
385   g_return_val_if_fail (GDK_IS_FRAME_CLOCK (clock), 0);
386
387   priv = clock->priv;
388
389   return priv->frame_counter;
390 }
391
392 gint64
393 gdk_frame_clock_get_start (GdkFrameClock *clock)
394 {
395   GdkFrameClockPrivate *priv;
396
397   g_return_val_if_fail (GDK_IS_FRAME_CLOCK (clock), 0);
398
399   priv = clock->priv;
400
401   return priv->frame_counter + 1 - priv->n_timings;
402 }
403
404 void
405 _gdk_frame_clock_begin_frame (GdkFrameClock *clock)
406 {
407   GdkFrameClockPrivate *priv;
408
409   g_return_if_fail (GDK_IS_FRAME_CLOCK (clock));
410
411   priv = clock->priv;
412
413   priv->frame_counter++;
414   priv->current = (priv->current + 1) % FRAME_HISTORY_MAX_LENGTH;
415
416   if (priv->n_timings < FRAME_HISTORY_MAX_LENGTH)
417     priv->n_timings++;
418   else
419     {
420       gdk_frame_timings_unref(priv->timings[priv->current]);
421     }
422
423   priv->timings[priv->current] = gdk_frame_timings_new (priv->frame_counter);
424 }
425
426 GdkFrameTimings *
427 gdk_frame_clock_get_timings (GdkFrameClock *clock,
428                              gint64         frame_counter)
429 {
430   GdkFrameClockPrivate *priv;
431   gint pos;
432
433   g_return_val_if_fail (GDK_IS_FRAME_CLOCK (clock), NULL);
434
435   priv = clock->priv;
436
437   if (frame_counter > priv->frame_counter)
438     return NULL;
439
440   if (frame_counter <= priv->frame_counter - priv->n_timings)
441     return NULL;
442
443   pos = (priv->current - (priv->frame_counter - frame_counter) + FRAME_HISTORY_MAX_LENGTH) % FRAME_HISTORY_MAX_LENGTH;
444
445   return priv->timings[pos];
446 }
447
448 GdkFrameTimings *
449 gdk_frame_clock_get_current_frame_timings (GdkFrameClock *clock)
450 {
451   GdkFrameClockPrivate *priv;
452
453   g_return_val_if_fail (GDK_IS_FRAME_CLOCK (clock), 0);
454
455   priv = clock->priv;
456
457   return gdk_frame_clock_get_timings (clock, priv->frame_counter);
458 }
459
460
461 GdkFrameTimings *
462 gdk_frame_clock_get_last_complete (GdkFrameClock *clock)
463 {
464   GdkFrameClockPrivate *priv;
465   gint i;
466
467   g_return_val_if_fail (GDK_IS_FRAME_CLOCK (clock), NULL);
468
469   priv = clock->priv;
470
471   for (i = 0; i < priv->n_timings; i++)
472     {
473       gint pos = ((priv->current - i) + FRAME_HISTORY_MAX_LENGTH) % FRAME_HISTORY_MAX_LENGTH;
474       if (gdk_frame_timings_get_complete (priv->timings[pos]))
475         return priv->timings[pos];
476     }
477
478   return NULL;
479 }
480
481 #ifdef G_ENABLE_DEBUG
482 void
483 _gdk_frame_clock_debug_print_timings (GdkFrameClock   *clock,
484                                       GdkFrameTimings *timings)
485 {
486   gint64 frame_counter = gdk_frame_timings_get_frame_counter (timings);
487   gint64 layout_start_time = _gdk_frame_timings_get_layout_start_time (timings);
488   gint64 paint_start_time = _gdk_frame_timings_get_paint_start_time (timings);
489   gint64 frame_end_time = _gdk_frame_timings_get_frame_end_time (timings);
490   gint64 frame_time = gdk_frame_timings_get_frame_time (timings);
491   gint64 presentation_time = gdk_frame_timings_get_presentation_time (timings);
492   gint64 predicted_presentation_time = gdk_frame_timings_get_predicted_presentation_time (timings);
493   gint64 refresh_interval = gdk_frame_timings_get_refresh_interval (timings);
494   gint64 previous_frame_time = 0;
495   gboolean slept_before = gdk_frame_timings_get_slept_before (timings);
496   GdkFrameTimings *previous_timings = gdk_frame_clock_get_timings (clock,
497                                                                    frame_counter - 1);
498
499   if (previous_timings != NULL)
500     previous_frame_time = gdk_frame_timings_get_frame_time (previous_timings);
501
502   g_print ("%5" G_GINT64_FORMAT ":", frame_counter);
503   if (previous_frame_time != 0)
504     {
505       g_print (" interval=%-4.1f", (frame_time - previous_frame_time) / 1000.);
506       g_print (slept_before ?  " (sleep)" : "        ");
507     }
508   if (layout_start_time != 0)
509     g_print (" layout_start=%-4.1f", (layout_start_time - frame_time) / 1000.);
510   if (paint_start_time != 0)
511     g_print (" paint_start=%-4.1f", (paint_start_time - frame_time) / 1000.);
512   if (frame_end_time != 0)
513     g_print (" frame_end=%-4.1f", (frame_end_time - frame_time) / 1000.);
514   if (presentation_time != 0)
515     g_print (" present=%-4.1f", (presentation_time - frame_time) / 1000.);
516   if (predicted_presentation_time != 0)
517     g_print (" predicted=%-4.1f", (predicted_presentation_time - frame_time) / 1000.);
518   if (refresh_interval != 0)
519     g_print (" refresh_interval=%-4.1f", refresh_interval / 1000.);
520   g_print ("\n");
521 }
522 #endif /* G_ENABLE_DEBUG */
523
524 #define DEFAULT_REFRESH_INTERVAL 16667 /* 16.7ms (1/60th second) */
525 #define MAX_HISTORY_AGE 150000         /* 150ms */
526
527 void
528 gdk_frame_clock_get_refresh_info (GdkFrameClock *clock,
529                                   gint64         base_time,
530                                   gint64        *refresh_interval_return,
531                                   gint64        *presentation_time_return)
532 {
533   gint64 frame_counter;
534
535   g_return_if_fail (GDK_IS_FRAME_CLOCK (clock));
536
537   frame_counter = gdk_frame_clock_get_frame_counter (clock);
538
539   if (presentation_time_return)
540     *presentation_time_return = 0;
541   if (refresh_interval_return)
542     *refresh_interval_return = DEFAULT_REFRESH_INTERVAL;
543
544   while (TRUE)
545     {
546       GdkFrameTimings *timings = gdk_frame_clock_get_timings (clock, frame_counter);
547       gint64 presentation_time;
548       gint64 refresh_interval;
549
550       if (timings == NULL)
551         return;
552
553       refresh_interval = gdk_frame_timings_get_refresh_interval (timings);
554       presentation_time = gdk_frame_timings_get_presentation_time (timings);
555
556       if (presentation_time != 0)
557         {
558           if (presentation_time > base_time - MAX_HISTORY_AGE &&
559               presentation_time_return)
560             {
561               if (refresh_interval == 0)
562                 refresh_interval = DEFAULT_REFRESH_INTERVAL;
563
564               if (refresh_interval_return)
565                 *refresh_interval_return = refresh_interval;
566
567               while (presentation_time < base_time)
568                 presentation_time += refresh_interval;
569
570               if (presentation_time_return)
571                 *presentation_time_return = presentation_time;
572             }
573
574           return;
575         }
576
577       frame_counter--;
578     }
579 }