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)
91 static guint signals[LAST_SIGNAL];
93 #define FRAME_HISTORY_MAX_LENGTH 16
95 struct _GdkFrameClockPrivate
100 GdkFrameTimings *timings[FRAME_HISTORY_MAX_LENGTH];
104 gdk_frame_clock_finalize (GObject *object)
106 GdkFrameClockPrivate *priv = GDK_FRAME_CLOCK (object)->priv;
109 for (i = 0; i < FRAME_HISTORY_MAX_LENGTH; i++)
110 if (priv->timings[i] != 0)
111 gdk_frame_timings_unref (priv->timings[i]);
113 G_OBJECT_CLASS (gdk_frame_clock_parent_class)->finalize (object);
117 gdk_frame_clock_class_init (GdkFrameClockClass *klass)
119 GObjectClass *gobject_class = (GObjectClass*) klass;
121 gobject_class->finalize = gdk_frame_clock_finalize;
124 * GdkFrameClock::flush-events:
125 * @clock: the frame clock emitting the signal
129 signals[FLUSH_EVENTS] =
130 g_signal_new (g_intern_static_string ("flush-events"),
131 GDK_TYPE_FRAME_CLOCK,
135 g_cclosure_marshal_VOID__VOID,
139 * GdkFrameClock::before-paint:
140 * @clock: the frame clock emitting the signal
142 * This signal is emitted immediately before the paint signal and
143 * indicates that the frame time has been updated, and signal
144 * handlers should perform any preparatory work before painting.
146 signals[BEFORE_PAINT] =
147 g_signal_new (g_intern_static_string ("before-paint"),
148 GDK_TYPE_FRAME_CLOCK,
152 g_cclosure_marshal_VOID__VOID,
156 * GdkFrameClock::update:
157 * @clock: the frame clock emitting the signal
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 immediately before the paint signal and
175 * indicates that the frame time has been updated, and signal
176 * handlers should perform any preparatory work before painting.
179 g_signal_new (g_intern_static_string ("layout"),
180 GDK_TYPE_FRAME_CLOCK,
184 g_cclosure_marshal_VOID__VOID,
188 * GdkFrameClock::paint:
189 * @clock: the frame clock emitting the signal
191 * Signal handlers for this signal should paint the window, screen,
192 * or whatever they normally paint.
195 g_signal_new (g_intern_static_string ("paint"),
196 GDK_TYPE_FRAME_CLOCK,
200 g_cclosure_marshal_VOID__VOID,
204 * GdkFrameClock::after-paint:
205 * @clock: the frame clock emitting the signal
207 * This signal is emitted immediately after the paint signal and
208 * allows signal handlers to do anything they'd like to do after
209 * painting has been completed. This is a relatively good time to do
210 * "expensive" processing in order to get it done in between frames.
212 signals[AFTER_PAINT] =
213 g_signal_new (g_intern_static_string ("after-paint"),
214 GDK_TYPE_FRAME_CLOCK,
218 g_cclosure_marshal_VOID__VOID,
222 * GdkFrameClock::resume-events:
223 * @clock: the frame clock emitting the signal
227 signals[RESUME_EVENTS] =
228 g_signal_new (g_intern_static_string ("resume-events"),
229 GDK_TYPE_FRAME_CLOCK,
233 g_cclosure_marshal_VOID__VOID,
236 g_type_class_add_private (klass, sizeof (GdkFrameClockPrivate));
240 gdk_frame_clock_init (GdkFrameClock *clock)
242 GdkFrameClockPrivate *priv;
244 clock->priv = G_TYPE_INSTANCE_GET_PRIVATE (clock,
245 GDK_TYPE_FRAME_CLOCK,
246 GdkFrameClockPrivate);
249 priv->frame_counter = -1;
250 priv->current = FRAME_HISTORY_MAX_LENGTH - 1;
254 * gdk_frame_clock_get_frame_time:
257 * Gets the time that should currently be used for animations. Inside
258 * a paint, it's the time used to compute the animation position of
259 * everything in a frame. Outside a paint, it's the time of the
260 * conceptual "previous frame," which may be either the actual
261 * previous frame time, or if that's too old, an updated time.
263 * The returned time has no relationship to wall clock time. It
264 * increases roughly at 1 millisecond per wall clock millisecond, and
265 * it never decreases, but its value is only meaningful relative to
266 * previous frame clock times.
270 * Return value: a timestamp in milliseconds
273 gdk_frame_clock_get_frame_time (GdkFrameClock *clock)
275 g_return_val_if_fail (GDK_IS_FRAME_CLOCK (clock), 0);
277 return GDK_FRAME_CLOCK_GET_CLASS (clock)->get_frame_time (clock);
281 * gdk_frame_clock_request_phase:
284 * Asks the frame clock to paint a frame. The frame
285 * may or may not ever be painted (the frame clock may
286 * stop itself for whatever reason), but the goal in
287 * normal circumstances would be to paint the frame
288 * at the next expected frame time. For example
289 * if the clock is running at 60fps the frame would
290 * ideally be painted within 1000/60=16 milliseconds.
295 gdk_frame_clock_request_phase (GdkFrameClock *clock,
296 GdkFrameClockPhase phase)
298 g_return_if_fail (GDK_IS_FRAME_CLOCK (clock));
300 GDK_FRAME_CLOCK_GET_CLASS (clock)->request_phase (clock, phase);
305 _gdk_frame_clock_freeze (GdkFrameClock *clock)
307 g_return_if_fail (GDK_IS_FRAME_CLOCK (clock));
309 GDK_FRAME_CLOCK_GET_CLASS (clock)->freeze (clock);
314 _gdk_frame_clock_thaw (GdkFrameClock *clock)
316 g_return_if_fail (GDK_IS_FRAME_CLOCK (clock));
318 GDK_FRAME_CLOCK_GET_CLASS (clock)->thaw (clock);
322 gdk_frame_clock_get_frame_counter (GdkFrameClock *clock)
324 GdkFrameClockPrivate *priv;
326 g_return_val_if_fail (GDK_IS_FRAME_CLOCK (clock), 0);
330 return priv->frame_counter;
334 gdk_frame_clock_get_history_start (GdkFrameClock *clock)
336 GdkFrameClockPrivate *priv;
338 g_return_val_if_fail (GDK_IS_FRAME_CLOCK (clock), 0);
342 return priv->frame_counter + 1 - priv->n_timings;
346 _gdk_frame_clock_begin_frame (GdkFrameClock *clock)
348 GdkFrameClockPrivate *priv;
350 g_return_if_fail (GDK_IS_FRAME_CLOCK (clock));
354 priv->frame_counter++;
355 priv->current = (priv->current + 1) % FRAME_HISTORY_MAX_LENGTH;
357 if (priv->n_timings < FRAME_HISTORY_MAX_LENGTH)
361 gdk_frame_timings_unref(priv->timings[priv->current]);
364 priv->timings[priv->current] = _gdk_frame_timings_new (priv->frame_counter);
368 gdk_frame_clock_get_timings (GdkFrameClock *clock,
369 gint64 frame_counter)
371 GdkFrameClockPrivate *priv;
374 g_return_val_if_fail (GDK_IS_FRAME_CLOCK (clock), NULL);
378 if (frame_counter > priv->frame_counter)
381 if (frame_counter <= priv->frame_counter - priv->n_timings)
384 pos = (priv->current - (priv->frame_counter - frame_counter) + FRAME_HISTORY_MAX_LENGTH) % FRAME_HISTORY_MAX_LENGTH;
386 return priv->timings[pos];
390 gdk_frame_clock_get_frame_timings (GdkFrameClock *clock)
392 GdkFrameClockPrivate *priv;
394 g_return_val_if_fail (GDK_IS_FRAME_CLOCK (clock), 0);
398 return gdk_frame_clock_get_timings (clock, priv->frame_counter);
402 #ifdef G_ENABLE_DEBUG
404 _gdk_frame_clock_debug_print_timings (GdkFrameClock *clock,
405 GdkFrameTimings *timings)
407 gint64 previous_frame_time = 0;
408 GdkFrameTimings *previous_timings = gdk_frame_clock_get_timings (clock,
409 timings->frame_counter - 1);
411 if (previous_timings != NULL)
412 previous_frame_time = previous_timings->frame_time;
414 g_print ("%5" G_GINT64_FORMAT ":", timings->frame_counter);
415 if (previous_frame_time != 0)
417 g_print (" interval=%-4.1f", (timings->frame_time - previous_frame_time) / 1000.);
418 g_print (timings->slept_before ? " (sleep)" : " ");
420 if (timings->layout_start_time != 0)
421 g_print (" layout_start=%-4.1f", (timings->layout_start_time - timings->frame_time) / 1000.);
422 if (timings->paint_start_time != 0)
423 g_print (" paint_start=%-4.1f", (timings->paint_start_time - timings->frame_time) / 1000.);
424 if (timings->frame_end_time != 0)
425 g_print (" frame_end=%-4.1f", (timings->frame_end_time - timings->frame_time) / 1000.);
426 if (timings->presentation_time != 0)
427 g_print (" present=%-4.1f", (timings->presentation_time - timings->frame_time) / 1000.);
428 if (timings->predicted_presentation_time != 0)
429 g_print (" predicted=%-4.1f", (timings->predicted_presentation_time - timings->frame_time) / 1000.);
430 if (timings->refresh_interval != 0)
431 g_print (" refresh_interval=%-4.1f", timings->refresh_interval / 1000.);
434 #endif /* G_ENABLE_DEBUG */
436 #define DEFAULT_REFRESH_INTERVAL 16667 /* 16.7ms (1/60th second) */
437 #define MAX_HISTORY_AGE 150000 /* 150ms */
440 gdk_frame_clock_get_refresh_info (GdkFrameClock *clock,
442 gint64 *refresh_interval_return,
443 gint64 *presentation_time_return)
445 gint64 frame_counter;
447 g_return_if_fail (GDK_IS_FRAME_CLOCK (clock));
449 frame_counter = gdk_frame_clock_get_frame_counter (clock);
451 if (presentation_time_return)
452 *presentation_time_return = 0;
453 if (refresh_interval_return)
454 *refresh_interval_return = DEFAULT_REFRESH_INTERVAL;
458 GdkFrameTimings *timings = gdk_frame_clock_get_timings (clock, frame_counter);
459 gint64 presentation_time;
460 gint64 refresh_interval;
465 refresh_interval = timings->refresh_interval;
466 presentation_time = timings->presentation_time;
468 if (presentation_time != 0)
470 if (presentation_time > base_time - MAX_HISTORY_AGE &&
471 presentation_time_return)
473 if (refresh_interval == 0)
474 refresh_interval = DEFAULT_REFRESH_INTERVAL;
476 if (refresh_interval_return)
477 *refresh_interval_return = refresh_interval;
479 while (presentation_time < base_time)
480 presentation_time += refresh_interval;
482 if (presentation_time_return)
483 *presentation_time_return = presentation_time;