1 /* GDK - The GIMP Drawing Kit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
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.
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.
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.
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/.
29 #include "gdkframeclockprivate.h"
30 #include "gdkinternals.h"
34 * @Short_description: Frame clock syncs painting to a window or display
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.
46 * #GdkFrameClock is designed to be compatible with an OpenGL-based
47 * implementation or with mozRequestAnimationFrame in Firefox,
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().
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.
72 G_DEFINE_ABSTRACT_TYPE (GdkFrameClock, gdk_frame_clock, G_TYPE_OBJECT)
85 static guint signals[LAST_SIGNAL];
87 #define FRAME_HISTORY_MAX_LENGTH 16
89 struct _GdkFrameClockPrivate
94 GdkFrameTimings *timings[FRAME_HISTORY_MAX_LENGTH];
98 gdk_frame_clock_finalize (GObject *object)
100 GdkFrameClockPrivate *priv = GDK_FRAME_CLOCK (object)->priv;
103 for (i = 0; i < FRAME_HISTORY_MAX_LENGTH; i++)
104 if (priv->timings[i] != 0)
105 gdk_frame_timings_unref (priv->timings[i]);
107 G_OBJECT_CLASS (gdk_frame_clock_parent_class)->finalize (object);
111 gdk_frame_clock_class_init (GdkFrameClockClass *klass)
113 GObjectClass *gobject_class = (GObjectClass*) klass;
115 gobject_class->finalize = gdk_frame_clock_finalize;
118 * GdkFrameClock::flush-events:
119 * @clock: the frame clock emitting the signal
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.
125 signals[FLUSH_EVENTS] =
126 g_signal_new (g_intern_static_string ("flush-events"),
127 GDK_TYPE_FRAME_CLOCK,
131 g_cclosure_marshal_VOID__VOID,
135 * GdkFrameClock::before-paint:
136 * @clock: the frame clock emitting the signal
138 * This signal begins processing of the frame. Applications
139 * should generally not handle this signal.
141 signals[BEFORE_PAINT] =
142 g_signal_new (g_intern_static_string ("before-paint"),
143 GDK_TYPE_FRAME_CLOCK,
147 g_cclosure_marshal_VOID__VOID,
151 * GdkFrameClock::update:
152 * @clock: the frame clock emitting the signal
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
162 g_signal_new (g_intern_static_string ("update"),
163 GDK_TYPE_FRAME_CLOCK,
167 g_cclosure_marshal_VOID__VOID,
171 * GdkFrameClock::layout:
172 * @clock: the frame clock emitting the signal
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.
180 g_signal_new (g_intern_static_string ("layout"),
181 GDK_TYPE_FRAME_CLOCK,
185 g_cclosure_marshal_VOID__VOID,
189 * GdkFrameClock::paint:
190 * @clock: the frame clock emitting the signal
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.
199 g_signal_new (g_intern_static_string ("paint"),
200 GDK_TYPE_FRAME_CLOCK,
204 g_cclosure_marshal_VOID__VOID,
208 * GdkFrameClock::after-paint:
209 * @clock: the frame clock emitting the signal
211 * This signal ends processing of the frame. Applications
212 * should generally not handle this signal.
214 signals[AFTER_PAINT] =
215 g_signal_new (g_intern_static_string ("after-paint"),
216 GDK_TYPE_FRAME_CLOCK,
220 g_cclosure_marshal_VOID__VOID,
224 * GdkFrameClock::resume-events:
225 * @clock: the frame clock emitting the signal
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.
231 signals[RESUME_EVENTS] =
232 g_signal_new (g_intern_static_string ("resume-events"),
233 GDK_TYPE_FRAME_CLOCK,
237 g_cclosure_marshal_VOID__VOID,
240 g_type_class_add_private (klass, sizeof (GdkFrameClockPrivate));
244 gdk_frame_clock_init (GdkFrameClock *clock)
246 GdkFrameClockPrivate *priv;
248 clock->priv = G_TYPE_INSTANCE_GET_PRIVATE (clock,
249 GDK_TYPE_FRAME_CLOCK,
250 GdkFrameClockPrivate);
253 priv->frame_counter = -1;
254 priv->current = FRAME_HISTORY_MAX_LENGTH - 1;
258 * gdk_frame_clock_get_frame_time:
259 * @frame_clock: a #GdkFrameClock
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
269 * Return value: a timestamp in microseconds, in the timescale of
270 * of g_get_monotonic_time().
273 gdk_frame_clock_get_frame_time (GdkFrameClock *frame_clock)
275 g_return_val_if_fail (GDK_IS_FRAME_CLOCK (frame_clock), 0);
277 return GDK_FRAME_CLOCK_GET_CLASS (frame_clock)->get_frame_time (frame_clock);
281 * gdk_frame_clock_request_phase:
282 * @frame_clock: a #GdkFrameClock
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. If you are displaying animated
289 * content and want to continually request the
290 * %GDK_FRAME_CLOCK_PHASE_UPDATE phase for a period of time,
291 * you should use gdk_frame_clock_begin_updating() instead, since
292 * this allows GTK+ to adjust system parameters to get maximally
298 gdk_frame_clock_request_phase (GdkFrameClock *frame_clock,
299 GdkFrameClockPhase phase)
301 g_return_if_fail (GDK_IS_FRAME_CLOCK (frame_clock));
303 GDK_FRAME_CLOCK_GET_CLASS (frame_clock)->request_phase (frame_clock, phase);
307 * gdk_frame_clock_begin_updating:
308 * @frame_clock: a #GdkFrameClock
310 * Starts updates for an animation. Until a matching call to
311 * gdk_frame_clock_end_updating() is made, the frame clock will continually
312 * request a new frame with the %GDK_FRAME_CLOCK_PHASE_UPDATE phase.
313 * This function may be called multiple times and frames will be
314 * requested until gdk_frame_clock_end_updating() is called the same
320 gdk_frame_clock_begin_updating (GdkFrameClock *frame_clock)
322 g_return_if_fail (GDK_IS_FRAME_CLOCK (frame_clock));
324 GDK_FRAME_CLOCK_GET_CLASS (frame_clock)->begin_updating (frame_clock);
328 * gdk_frame_clock_end_updating:
329 * @frame_clock: a #GdkFrameClock
331 * Stops updates for an animation. See the documentation for
332 * gdk_frame_clock_begin_updating().
337 gdk_frame_clock_end_updating (GdkFrameClock *frame_clock)
339 g_return_if_fail (GDK_IS_FRAME_CLOCK (frame_clock));
341 GDK_FRAME_CLOCK_GET_CLASS (frame_clock)->end_updating (frame_clock);
345 _gdk_frame_clock_freeze (GdkFrameClock *clock)
347 g_return_if_fail (GDK_IS_FRAME_CLOCK (clock));
349 GDK_FRAME_CLOCK_GET_CLASS (clock)->freeze (clock);
354 _gdk_frame_clock_thaw (GdkFrameClock *clock)
356 g_return_if_fail (GDK_IS_FRAME_CLOCK (clock));
358 GDK_FRAME_CLOCK_GET_CLASS (clock)->thaw (clock);
362 * gdk_frame_clock_get_frame_counter:
363 * @frame_clock: a #GdkFrameClock
365 * A #GdkFrameClock maintains a 64-bit counter that increments for
368 * Returns: inside frame processing, the value of the frame counter
369 * for the current frame. Outside of frame processing, the frame
370 * counter for the last frame.
374 gdk_frame_clock_get_frame_counter (GdkFrameClock *frame_clock)
376 GdkFrameClockPrivate *priv;
378 g_return_val_if_fail (GDK_IS_FRAME_CLOCK (frame_clock), 0);
380 priv = frame_clock->priv;
382 return priv->frame_counter;
386 * gdk_frame_clock_get_history_start:
387 * @frame_clock: a #GdkFrameClock
389 * #GdkFrameClock internally keeps a history of #GdkFrameTiming
390 * objects for recent frames that can be retrieved with
391 * gdk_frame_clock_get_timings(). The set of stored frames
392 * is the set from the counter values given by
393 * gdk_frame_clock_get_history_start() and
394 * gdk_frame_clock_get_frame_counter(), inclusive.
396 * Return value: the frame counter value for the oldest frame
397 * that is available in the internal frame history of the
402 gdk_frame_clock_get_history_start (GdkFrameClock *frame_clock)
404 GdkFrameClockPrivate *priv;
406 g_return_val_if_fail (GDK_IS_FRAME_CLOCK (frame_clock), 0);
408 priv = frame_clock->priv;
410 return priv->frame_counter + 1 - priv->n_timings;
414 _gdk_frame_clock_begin_frame (GdkFrameClock *frame_clock)
416 GdkFrameClockPrivate *priv;
418 g_return_if_fail (GDK_IS_FRAME_CLOCK (frame_clock));
420 priv = frame_clock->priv;
422 priv->frame_counter++;
423 priv->current = (priv->current + 1) % FRAME_HISTORY_MAX_LENGTH;
425 if (priv->n_timings < FRAME_HISTORY_MAX_LENGTH)
429 gdk_frame_timings_unref(priv->timings[priv->current]);
432 priv->timings[priv->current] = _gdk_frame_timings_new (priv->frame_counter);
436 * gdk_frame_clock_get_timings:
437 * @frame_clock: a #GdkFrameClock
438 * @frame_counter: the frame counter value identifying the frame to
441 * Retrieves a #GdkFrameTimings object holding timing information
442 * for the current frame or a recent frame. The #GdkFrameTimings
443 * object may not yet be complete: see gdk_frame_timings_get_complete().
445 * Return value: the #GdkFrameTimings object for the specified
446 * frame, or %NULL if it is not available. See
447 * gdk_frame_clock_get_history_start().
451 gdk_frame_clock_get_timings (GdkFrameClock *frame_clock,
452 gint64 frame_counter)
454 GdkFrameClockPrivate *priv;
457 g_return_val_if_fail (GDK_IS_FRAME_CLOCK (frame_clock), NULL);
459 priv = frame_clock->priv;
461 if (frame_counter > priv->frame_counter)
464 if (frame_counter <= priv->frame_counter - priv->n_timings)
467 pos = (priv->current - (priv->frame_counter - frame_counter) + FRAME_HISTORY_MAX_LENGTH) % FRAME_HISTORY_MAX_LENGTH;
469 return priv->timings[pos];
473 * gdk_frame_clock_get_current_timings:
474 * @frame_clock: a #GdkFrameClock
476 * Gets the frame timings for the current frame.
478 * Returns: the #GdkFrameTimings for the frame currently being
479 * processed, or even no frame is being processed, for the
480 * previous frame. Before any frames have been procesed,
485 gdk_frame_clock_get_current_timings (GdkFrameClock *frame_clock)
487 GdkFrameClockPrivate *priv;
489 g_return_val_if_fail (GDK_IS_FRAME_CLOCK (frame_clock), 0);
491 priv = frame_clock->priv;
493 return gdk_frame_clock_get_timings (frame_clock, priv->frame_counter);
497 #ifdef G_ENABLE_DEBUG
499 _gdk_frame_clock_debug_print_timings (GdkFrameClock *clock,
500 GdkFrameTimings *timings)
502 gint64 previous_frame_time = 0;
503 GdkFrameTimings *previous_timings = gdk_frame_clock_get_timings (clock,
504 timings->frame_counter - 1);
506 if (previous_timings != NULL)
507 previous_frame_time = previous_timings->frame_time;
509 g_print ("%5" G_GINT64_FORMAT ":", timings->frame_counter);
510 if (previous_frame_time != 0)
512 g_print (" interval=%-4.1f", (timings->frame_time - previous_frame_time) / 1000.);
513 g_print (timings->slept_before ? " (sleep)" : " ");
515 if (timings->layout_start_time != 0)
516 g_print (" layout_start=%-4.1f", (timings->layout_start_time - timings->frame_time) / 1000.);
517 if (timings->paint_start_time != 0)
518 g_print (" paint_start=%-4.1f", (timings->paint_start_time - timings->frame_time) / 1000.);
519 if (timings->frame_end_time != 0)
520 g_print (" frame_end=%-4.1f", (timings->frame_end_time - timings->frame_time) / 1000.);
521 if (timings->presentation_time != 0)
522 g_print (" present=%-4.1f", (timings->presentation_time - timings->frame_time) / 1000.);
523 if (timings->predicted_presentation_time != 0)
524 g_print (" predicted=%-4.1f", (timings->predicted_presentation_time - timings->frame_time) / 1000.);
525 if (timings->refresh_interval != 0)
526 g_print (" refresh_interval=%-4.1f", timings->refresh_interval / 1000.);
529 #endif /* G_ENABLE_DEBUG */
531 #define DEFAULT_REFRESH_INTERVAL 16667 /* 16.7ms (1/60th second) */
532 #define MAX_HISTORY_AGE 150000 /* 150ms */
535 * gdk_frame_clock_get_refresh_info:
536 * @frame_clock: a #GdkFrameClock
537 * @base_time: base time for determining a presentaton time
538 * @refresh_interval_return: a location to store the determined refresh
539 * interval, or %NULL. A default refresh interval of 1/60th of
540 * a second will be stored if no history is present.
541 * @presentation_time_return: a location to store the next
542 * candidate presentation time after the given base time.
543 * 0 will be will be stored if no history is present.
545 * Using the frame history stored in the frame clock, finds the last
546 * known presentation time and refresh interval, and assuming that
547 * presentation times are separated by the refresh interval,
548 * predicts a presentation time that is a multiple of the refresh
549 * interval after the last presentation time, and later than @base_time.
554 gdk_frame_clock_get_refresh_info (GdkFrameClock *frame_clock,
556 gint64 *refresh_interval_return,
557 gint64 *presentation_time_return)
559 gint64 frame_counter;
561 g_return_if_fail (GDK_IS_FRAME_CLOCK (frame_clock));
563 frame_counter = gdk_frame_clock_get_frame_counter (frame_clock);
565 if (presentation_time_return)
566 *presentation_time_return = 0;
567 if (refresh_interval_return)
568 *refresh_interval_return = DEFAULT_REFRESH_INTERVAL;
572 GdkFrameTimings *timings = gdk_frame_clock_get_timings (frame_clock, frame_counter);
573 gint64 presentation_time;
574 gint64 refresh_interval;
579 refresh_interval = timings->refresh_interval;
580 presentation_time = timings->presentation_time;
582 if (presentation_time != 0)
584 if (presentation_time > base_time - MAX_HISTORY_AGE &&
585 presentation_time_return)
587 if (refresh_interval == 0)
588 refresh_interval = DEFAULT_REFRESH_INTERVAL;
590 if (refresh_interval_return)
591 *refresh_interval_return = refresh_interval;
593 while (presentation_time < base_time)
594 presentation_time += refresh_interval;
596 if (presentation_time_return)
597 *presentation_time_return = presentation_time;