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 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.
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 that time, the frame clock
52 * emits its GdkFrameClock:frame-requested signal if no frame was
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.
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.
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.
78 G_DEFINE_ABSTRACT_TYPE (GdkFrameClock, gdk_frame_clock, G_TYPE_OBJECT)
92 static guint signals[LAST_SIGNAL];
94 #define FRAME_HISTORY_MAX_LENGTH 16
96 struct _GdkFrameClockPrivate
101 GdkFrameTimings *timings[FRAME_HISTORY_MAX_LENGTH];
105 gdk_frame_clock_finalize (GObject *object)
107 GdkFrameClockPrivate *priv = GDK_FRAME_CLOCK (object)->priv;
110 for (i = 0; i < FRAME_HISTORY_MAX_LENGTH; i++)
111 if (priv->timings[i] != 0)
112 gdk_frame_timings_unref (priv->timings[i]);
114 G_OBJECT_CLASS (gdk_frame_clock_parent_class)->finalize (object);
118 gdk_frame_clock_class_init (GdkFrameClockClass *klass)
120 GObjectClass *gobject_class = (GObjectClass*) klass;
122 gobject_class->finalize = gdk_frame_clock_finalize;
125 * GdkFrameClock::frame-requested:
126 * @clock: the frame clock emitting the signal
128 * This signal is emitted when a frame is not pending, and
129 * gdk_frame_clock_request_frame() is called to request a frame.
131 signals[FRAME_REQUESTED] =
132 g_signal_new (g_intern_static_string ("frame-requested"),
133 GDK_TYPE_FRAME_CLOCK,
137 g_cclosure_marshal_VOID__VOID,
141 * GdkFrameClock::flush-events:
142 * @clock: the frame clock emitting the signal
146 signals[FLUSH_EVENTS] =
147 g_signal_new (g_intern_static_string ("flush-events"),
148 GDK_TYPE_FRAME_CLOCK,
152 g_cclosure_marshal_VOID__VOID,
156 * GdkFrameClock::before-paint:
157 * @clock: the frame clock emitting the signal
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.
163 signals[BEFORE_PAINT] =
164 g_signal_new (g_intern_static_string ("before-paint"),
165 GDK_TYPE_FRAME_CLOCK,
169 g_cclosure_marshal_VOID__VOID,
173 * GdkFrameClock::update:
174 * @clock: the frame clock emitting the signal
179 g_signal_new (g_intern_static_string ("update"),
180 GDK_TYPE_FRAME_CLOCK,
184 g_cclosure_marshal_VOID__VOID,
188 * GdkFrameClock::layout:
189 * @clock: the frame clock emitting the signal
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.
196 g_signal_new (g_intern_static_string ("layout"),
197 GDK_TYPE_FRAME_CLOCK,
201 g_cclosure_marshal_VOID__VOID,
205 * GdkFrameClock::paint:
206 * @clock: the frame clock emitting the signal
208 * Signal handlers for this signal should paint the window, screen,
209 * or whatever they normally paint.
212 g_signal_new (g_intern_static_string ("paint"),
213 GDK_TYPE_FRAME_CLOCK,
217 g_cclosure_marshal_VOID__VOID,
221 * GdkFrameClock::after-paint:
222 * @clock: the frame clock emitting the signal
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.
229 signals[AFTER_PAINT] =
230 g_signal_new (g_intern_static_string ("after-paint"),
231 GDK_TYPE_FRAME_CLOCK,
235 g_cclosure_marshal_VOID__VOID,
239 * GdkFrameClock::resume-events:
240 * @clock: the frame clock emitting the signal
244 signals[RESUME_EVENTS] =
245 g_signal_new (g_intern_static_string ("resume-events"),
246 GDK_TYPE_FRAME_CLOCK,
250 g_cclosure_marshal_VOID__VOID,
253 g_type_class_add_private (klass, sizeof (GdkFrameClockPrivate));
257 gdk_frame_clock_init (GdkFrameClock *clock)
259 GdkFrameClockPrivate *priv;
261 clock->priv = G_TYPE_INSTANCE_GET_PRIVATE (clock,
262 GDK_TYPE_FRAME_CLOCK,
263 GdkFrameClockPrivate);
266 priv->frame_counter = -1;
267 priv->current = FRAME_HISTORY_MAX_LENGTH - 1;
271 * gdk_frame_clock_get_frame_time:
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.
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.
287 * Return value: a timestamp in milliseconds
290 gdk_frame_clock_get_frame_time (GdkFrameClock *clock)
292 g_return_val_if_fail (GDK_IS_FRAME_CLOCK (clock), 0);
294 return GDK_FRAME_CLOCK_GET_CLASS (clock)->get_frame_time (clock);
298 * gdk_frame_clock_request_phase:
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.
312 gdk_frame_clock_request_phase (GdkFrameClock *clock,
313 GdkFrameClockPhase phase)
315 g_return_if_fail (GDK_IS_FRAME_CLOCK (clock));
317 GDK_FRAME_CLOCK_GET_CLASS (clock)->request_phase (clock, phase);
322 gdk_frame_clock_freeze (GdkFrameClock *clock)
324 g_return_if_fail (GDK_IS_FRAME_CLOCK (clock));
326 GDK_FRAME_CLOCK_GET_CLASS (clock)->freeze (clock);
331 gdk_frame_clock_thaw (GdkFrameClock *clock)
333 g_return_if_fail (GDK_IS_FRAME_CLOCK (clock));
335 GDK_FRAME_CLOCK_GET_CLASS (clock)->thaw (clock);
339 * gdk_frame_clock_get_requested:
342 * Gets whether a frame paint has been requested but has not been
347 * Return value: TRUE if a frame paint is pending
350 gdk_frame_clock_get_requested (GdkFrameClock *clock)
352 g_return_val_if_fail (GDK_IS_FRAME_CLOCK (clock), FALSE);
354 return GDK_FRAME_CLOCK_GET_CLASS (clock)->get_requested (clock);
358 * gdk_frame_clock_get_frame_time_val:
360 * @timeval: #GTimeVal to fill in with frame time
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).
367 gdk_frame_clock_get_frame_time_val (GdkFrameClock *clock,
372 g_return_if_fail (GDK_IS_FRAME_CLOCK (clock));
374 time_ms = gdk_frame_clock_get_frame_time (clock);
376 timeval->tv_sec = time_ms / 1000;
377 timeval->tv_usec = (time_ms % 1000) * 1000;
381 gdk_frame_clock_get_frame_counter (GdkFrameClock *clock)
383 GdkFrameClockPrivate *priv;
385 g_return_val_if_fail (GDK_IS_FRAME_CLOCK (clock), 0);
389 return priv->frame_counter;
393 gdk_frame_clock_get_start (GdkFrameClock *clock)
395 GdkFrameClockPrivate *priv;
397 g_return_val_if_fail (GDK_IS_FRAME_CLOCK (clock), 0);
401 return priv->frame_counter + 1 - priv->n_timings;
405 _gdk_frame_clock_begin_frame (GdkFrameClock *clock)
407 GdkFrameClockPrivate *priv;
409 g_return_if_fail (GDK_IS_FRAME_CLOCK (clock));
413 priv->frame_counter++;
414 priv->current = (priv->current + 1) % FRAME_HISTORY_MAX_LENGTH;
416 if (priv->n_timings < FRAME_HISTORY_MAX_LENGTH)
420 gdk_frame_timings_unref(priv->timings[priv->current]);
423 priv->timings[priv->current] = gdk_frame_timings_new (priv->frame_counter);
427 gdk_frame_clock_get_timings (GdkFrameClock *clock,
428 gint64 frame_counter)
430 GdkFrameClockPrivate *priv;
433 g_return_val_if_fail (GDK_IS_FRAME_CLOCK (clock), NULL);
437 if (frame_counter > priv->frame_counter)
440 if (frame_counter <= priv->frame_counter - priv->n_timings)
443 pos = (priv->current - (priv->frame_counter - frame_counter) + FRAME_HISTORY_MAX_LENGTH) % FRAME_HISTORY_MAX_LENGTH;
445 return priv->timings[pos];
449 gdk_frame_clock_get_current_frame_timings (GdkFrameClock *clock)
451 GdkFrameClockPrivate *priv;
453 g_return_val_if_fail (GDK_IS_FRAME_CLOCK (clock), 0);
457 return gdk_frame_clock_get_timings (clock, priv->frame_counter);
462 gdk_frame_clock_get_last_complete (GdkFrameClock *clock)
464 GdkFrameClockPrivate *priv;
467 g_return_val_if_fail (GDK_IS_FRAME_CLOCK (clock), NULL);
471 for (i = 0; i < priv->n_timings; i++)
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];
481 #ifdef G_ENABLE_DEBUG
483 _gdk_frame_clock_debug_print_timings (GdkFrameClock *clock,
484 GdkFrameTimings *timings)
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,
499 if (previous_timings != NULL)
500 previous_frame_time = gdk_frame_timings_get_frame_time (previous_timings);
502 g_print ("%5" G_GINT64_FORMAT ":", frame_counter);
503 if (previous_frame_time != 0)
505 g_print (" interval=%-4.1f", (frame_time - previous_frame_time) / 1000.);
506 g_print (slept_before ? " (sleep)" : " ");
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.);
522 #endif /* G_ENABLE_DEBUG */
524 #define DEFAULT_REFRESH_INTERVAL 16667 /* 16.7ms (1/60th second) */
525 #define MAX_HISTORY_AGE 150000 /* 150ms */
528 gdk_frame_clock_get_refresh_info (GdkFrameClock *clock,
530 gint64 *refresh_interval_return,
531 gint64 *presentation_time_return)
533 gint64 frame_counter;
535 g_return_if_fail (GDK_IS_FRAME_CLOCK (clock));
537 frame_counter = gdk_frame_clock_get_frame_counter (clock);
539 if (presentation_time_return)
540 *presentation_time_return = 0;
541 if (refresh_interval_return)
542 *refresh_interval_return = DEFAULT_REFRESH_INTERVAL;
546 GdkFrameTimings *timings = gdk_frame_clock_get_timings (clock, frame_counter);
547 gint64 presentation_time;
548 gint64 refresh_interval;
553 refresh_interval = gdk_frame_timings_get_refresh_interval (timings);
554 presentation_time = gdk_frame_timings_get_presentation_time (timings);
556 if (presentation_time != 0)
558 if (presentation_time > base_time - MAX_HISTORY_AGE &&
559 presentation_time_return)
561 if (refresh_interval == 0)
562 refresh_interval = DEFAULT_REFRESH_INTERVAL;
564 if (refresh_interval_return)
565 *refresh_interval_return = refresh_interval;
567 while (presentation_time < base_time)
568 presentation_time += refresh_interval;
570 if (presentation_time_return)
571 *presentation_time_return = presentation_time;