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, see <http://www.gnu.org/licenses/>.
16 */
17
18/*
19 * Modified by the GTK+ Team and others 1997-2010. See the AUTHORS
20 * file for a list of people on the GTK+ Team. See the ChangeLog
21 * files for a list of changes. These files are distributed with
22 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
23 */
24
25#include "config.h"
26
27#include "gdkframeclockprivate.h"
28#include "gdkinternals.h"
29
30/**
31 * SECTION:gdkframeclock
32 * @Short_description: Frame clock syncs painting to a window or display
33 * @Title: Frame clock
34 *
35 * A #GdkFrameClock tells the application when to update and repaint a
36 * window. This may be synced to the vertical refresh rate of the
37 * monitor, for example. Even when the frame clock uses a simple timer
38 * rather than a hardware-based vertical sync, the frame clock helps
39 * because it ensures everything paints at the same time (reducing the
40 * total number of frames). The frame clock can also automatically
41 * stop painting when it knows the frames will not be visible, or
42 * scale back animation framerates.
43 *
44 * #GdkFrameClock is designed to be compatible with an OpenGL-based
45 * implementation or with mozRequestAnimationFrame in Firefox,
46 * for example.
47 *
48 * A frame clock is idle until someone requests a frame with
49 * gdk_frame_clock_request_phase(). At some later point that makes
50 * sense for the synchronization being implemented, the clock will
51 * process a frame and emit signals for each phase that has been
52 * requested. (See the signals of the #GdkFrameClock class for
53 * documentation of the phases. %GDK_FRAME_CLOCK_PHASE_UPDATE and the
54 * #GdkFrameClock::update signal are most interesting for application
55 * writers, and are used to update the animations, using the frame time
56 * given by gdk_frame_clock_get_frame_time().
57 *
58 * The frame time is reported in microseconds and generally in the same
59 * timescale as g_get_monotonic_time(), however, it is not the same
60 * as g_get_monotonic_time(). The frame time does not advance during
61 * the time a frame is being painted, and outside of a frame, an attempt
62 * is made so that all calls to gdk_frame_clock_get_frame_time() that
63 * are called at a “similar” time get the same value. This means that
64 * if different animations are timed by looking at the difference in
65 * time between an initial value from gdk_frame_clock_get_frame_time()
66 * and the value inside the #GdkFrameClock::update signal of the clock,
67 * they will stay exactly synchronized.
68 */
69
70enum {
71 FLUSH_EVENTS,
72 BEFORE_PAINT,
73 UPDATE,
74 LAYOUT,
75 PAINT,
76 AFTER_PAINT,
77 RESUME_EVENTS,
78 LAST_SIGNAL
79};
80
81static guint signals[LAST_SIGNAL];
82
83#define FRAME_HISTORY_MAX_LENGTH 16
84
85struct _GdkFrameClockPrivate
86{
87 gint64 frame_counter;
88 gint n_timings;
89 gint current;
90 GdkFrameTimings *timings[FRAME_HISTORY_MAX_LENGTH];
91};
92
93G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GdkFrameClock, gdk_frame_clock, G_TYPE_OBJECT)
94
95static void
96gdk_frame_clock_finalize (GObject *object)
97{
98 GdkFrameClockPrivate *priv = GDK_FRAME_CLOCK (object)->priv;
99 int i;
100
101 for (i = 0; i < FRAME_HISTORY_MAX_LENGTH; i++)
102 if (priv->timings[i] != 0)
103 gdk_frame_timings_unref (priv->timings[i]);
104
105 G_OBJECT_CLASS (gdk_frame_clock_parent_class)->finalize (object);
106}
107
108static void
109gdk_frame_clock_class_init (GdkFrameClockClass *klass)
110{
111 GObjectClass *gobject_class = (GObjectClass*) klass;
112
113 gobject_class->finalize = gdk_frame_clock_finalize;
114
115 /**
116 * GdkFrameClock::flush-events:
117 * @clock: the frame clock emitting the signal
118 *
119 * This signal is used to flush pending motion events that
120 * are being batched up and compressed together. Applications
121 * should not handle this signal.
122 */
123 signals[FLUSH_EVENTS] =
124 g_signal_new (g_intern_static_string ("flush-events"),
125 GDK_TYPE_FRAME_CLOCK,
126 G_SIGNAL_RUN_LAST,
127 0,
128 NULL, NULL,
129 g_cclosure_marshal_VOID__VOID,
130 G_TYPE_NONE, 0);
131
132 /**
133 * GdkFrameClock::before-paint:
134 * @clock: the frame clock emitting the signal
135 *
136 * This signal begins processing of the frame. Applications
137 * should generally not handle this signal.
138 */
139 signals[BEFORE_PAINT] =
140 g_signal_new (g_intern_static_string ("before-paint"),
141 GDK_TYPE_FRAME_CLOCK,
142 G_SIGNAL_RUN_LAST,
143 0,
144 NULL, NULL,
145 g_cclosure_marshal_VOID__VOID,
146 G_TYPE_NONE, 0);
147
148 /**
149 * GdkFrameClock::update:
150 * @clock: the frame clock emitting the signal
151 *
152 * This signal is emitted as the first step of toolkit and
153 * application processing of the frame. Animations should
154 * be updated using gdk_frame_clock_get_frame_time().
155 * Applications can connect directly to this signal, or
156 * use gtk_widget_add_tick_callback() as a more convenient
157 * interface.
158 */
159 signals[UPDATE] =
160 g_signal_new (g_intern_static_string ("update"),
161 GDK_TYPE_FRAME_CLOCK,
162 G_SIGNAL_RUN_LAST,
163 0,
164 NULL, NULL,
165 g_cclosure_marshal_VOID__VOID,
166 G_TYPE_NONE, 0);
167
168 /**
169 * GdkFrameClock::layout:
170 * @clock: the frame clock emitting the signal
171 *
172 * This signal is emitted as the second step of toolkit and
173 * application processing of the frame. Any work to update
174 * sizes and positions of application elements should be
175 * performed. GTK+ normally handles this internally.
176 */
177 signals[LAYOUT] =
178 g_signal_new (g_intern_static_string ("layout"),
179 GDK_TYPE_FRAME_CLOCK,
180 G_SIGNAL_RUN_LAST,
181 0,
182 NULL, NULL,
183 g_cclosure_marshal_VOID__VOID,
184 G_TYPE_NONE, 0);
185
186 /**
187 * GdkFrameClock::paint:
188 * @clock: the frame clock emitting the signal
189 *
190 * This signal is emitted as the third step of toolkit and
191 * application processing of the frame. The frame is
192 * repainted. GDK normally handles this internally and
193 * produces expose events, which are turned into GTK+
194 * #GtkWidget::draw signals.
195 */
196 signals[PAINT] =
197 g_signal_new (g_intern_static_string ("paint"),
198 GDK_TYPE_FRAME_CLOCK,
199 G_SIGNAL_RUN_LAST,
200 0,
201 NULL, NULL,
202 g_cclosure_marshal_VOID__VOID,
203 G_TYPE_NONE, 0);
204
205 /**
206 * GdkFrameClock::after-paint:
207 * @clock: the frame clock emitting the signal
208 *
209 * This signal ends processing of the frame. Applications
210 * should generally not handle this signal.
211 */
212 signals[AFTER_PAINT] =
213 g_signal_new (g_intern_static_string ("after-paint"),
214 GDK_TYPE_FRAME_CLOCK,
215 G_SIGNAL_RUN_LAST,
216 0,
217 NULL, NULL,
218 g_cclosure_marshal_VOID__VOID,
219 G_TYPE_NONE, 0);
220
221 /**
222 * GdkFrameClock::resume-events:
223 * @clock: the frame clock emitting the signal
224 *
225 * This signal is emitted after processing of the frame is
226 * finished, and is handled internally by GTK+ to resume normal
227 * event processing. Applications should not handle this signal.
228 */
229 signals[RESUME_EVENTS] =
230 g_signal_new (g_intern_static_string ("resume-events"),
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
239static void
240gdk_frame_clock_init (GdkFrameClock *clock)
241{
242 GdkFrameClockPrivate *priv;
243
244 clock->priv = priv = gdk_frame_clock_get_instance_private (clock);
245
246 priv->frame_counter = -1;
247 priv->current = FRAME_HISTORY_MAX_LENGTH - 1;
248}
249
250/**
251 * gdk_frame_clock_get_frame_time:
252 * @frame_clock: a #GdkFrameClock
253 *
254 * Gets the time that should currently be used for animations. Inside
255 * the processing of a frame, it’s the time used to compute the
256 * animation position of everything in a frame. Outside of a frame, it's
257 * the time of the conceptual “previous frame,” which may be either
258 * the actual previous frame time, or if that’s too old, an updated
259 * time.
260 *
261 * Since: 3.8
262 * Returns: a timestamp in microseconds, in the timescale of
263 * of g_get_monotonic_time().
264 */
265gint64
266gdk_frame_clock_get_frame_time (GdkFrameClock *frame_clock)
267{
268 g_return_val_if_fail (GDK_IS_FRAME_CLOCK (frame_clock), 0);
269
270 return GDK_FRAME_CLOCK_GET_CLASS (frame_clock)->get_frame_time (frame_clock);
271}
272
273/**
274 * gdk_frame_clock_request_phase:
275 * @frame_clock: a #GdkFrameClock
276 * @phase: the phase that is requested
277 *
278 * Asks the frame clock to run a particular phase. The signal
279 * corresponding the requested phase will be emitted the next
280 * time the frame clock processes. Multiple calls to
281 * gdk_frame_clock_request_phase() will be combined together
282 * and only one frame processed. If you are displaying animated
283 * content and want to continually request the
284 * %GDK_FRAME_CLOCK_PHASE_UPDATE phase for a period of time,
285 * you should use gdk_frame_clock_begin_updating() instead, since
286 * this allows GTK+ to adjust system parameters to get maximally
287 * smooth animations.
288 *
289 * Since: 3.8
290 */
291void
292gdk_frame_clock_request_phase (GdkFrameClock *frame_clock,
293 GdkFrameClockPhase phase)
294{
295 g_return_if_fail (GDK_IS_FRAME_CLOCK (frame_clock));
296
297 GDK_FRAME_CLOCK_GET_CLASS (frame_clock)->request_phase (frame_clock, phase);
298}
299
300/**
301 * gdk_frame_clock_begin_updating:
302 * @frame_clock: a #GdkFrameClock
303 *
304 * Starts updates for an animation. Until a matching call to
305 * gdk_frame_clock_end_updating() is made, the frame clock will continually
306 * request a new frame with the %GDK_FRAME_CLOCK_PHASE_UPDATE phase.
307 * This function may be called multiple times and frames will be
308 * requested until gdk_frame_clock_end_updating() is called the same
309 * number of times.
310 *
311 * Since: 3.8
312 */
313void
314gdk_frame_clock_begin_updating (GdkFrameClock *frame_clock)
315{
316 g_return_if_fail (GDK_IS_FRAME_CLOCK (frame_clock));
317
318 GDK_FRAME_CLOCK_GET_CLASS (frame_clock)->begin_updating (frame_clock);
319}
320
321/**
322 * gdk_frame_clock_end_updating:
323 * @frame_clock: a #GdkFrameClock
324 *
325 * Stops updates for an animation. See the documentation for
326 * gdk_frame_clock_begin_updating().
327 *
328 * Since: 3.8
329 */
330void
331gdk_frame_clock_end_updating (GdkFrameClock *frame_clock)
332{
333 g_return_if_fail (GDK_IS_FRAME_CLOCK (frame_clock));
334
335 GDK_FRAME_CLOCK_GET_CLASS (frame_clock)->end_updating (frame_clock);
336}
337
338void
339_gdk_frame_clock_freeze (GdkFrameClock *clock)
340{
341 g_return_if_fail (GDK_IS_FRAME_CLOCK (clock));
342
343 GDK_FRAME_CLOCK_GET_CLASS (clock)->freeze (clock);
344}
345
346
347void
348_gdk_frame_clock_thaw (GdkFrameClock *clock)
349{
350 g_return_if_fail (GDK_IS_FRAME_CLOCK (clock));
351
352 GDK_FRAME_CLOCK_GET_CLASS (clock)->thaw (clock);
353}
354
355/**
356 * gdk_frame_clock_get_frame_counter:
357 * @frame_clock: a #GdkFrameClock
358 *
359 * A #GdkFrameClock maintains a 64-bit counter that increments for
360 * each frame drawn.
361 *
362 * Returns: inside frame processing, the value of the frame counter
363 * for the current frame. Outside of frame processing, the frame
364 * counter for the last frame.
365 * Since: 3.8
366 */
367gint64
368gdk_frame_clock_get_frame_counter (GdkFrameClock *frame_clock)
369{
370 GdkFrameClockPrivate *priv;
371
372 g_return_val_if_fail (GDK_IS_FRAME_CLOCK (frame_clock), 0);
373
374 priv = frame_clock->priv;
375
376 return priv->frame_counter;
377}
378
379/**
380 * gdk_frame_clock_get_history_start:
381 * @frame_clock: a #GdkFrameClock
382 *
383 * #GdkFrameClock internally keeps a history of #GdkFrameTimings
384 * objects for recent frames that can be retrieved with
385 * gdk_frame_clock_get_timings(). The set of stored frames
386 * is the set from the counter values given by
387 * gdk_frame_clock_get_history_start() and
388 * gdk_frame_clock_get_frame_counter(), inclusive.
389 *
390 * Returns: the frame counter value for the oldest frame
391 * that is available in the internal frame history of the
392 * #GdkFrameClock.
393 * Since: 3.8
394 */
395gint64
396gdk_frame_clock_get_history_start (GdkFrameClock *frame_clock)
397{
398 GdkFrameClockPrivate *priv;
399
400 g_return_val_if_fail (GDK_IS_FRAME_CLOCK (frame_clock), 0);
401
402 priv = frame_clock->priv;
403
404 return priv->frame_counter + 1 - priv->n_timings;
405}
406
407void
408_gdk_frame_clock_begin_frame (GdkFrameClock *frame_clock)
409{
410 GdkFrameClockPrivate *priv;
411
412 g_return_if_fail (GDK_IS_FRAME_CLOCK (frame_clock));
413
414 priv = frame_clock->priv;
415
416 priv->frame_counter++;
417 priv->current = (priv->current + 1) % FRAME_HISTORY_MAX_LENGTH;
418
419 /* Try to steal the previous frame timing instead of discarding
420 * and allocating a new one.
421 */
422 if G_LIKELY (priv->n_timings == FRAME_HISTORY_MAX_LENGTH &&
423 _gdk_frame_timings_steal (priv->timings[priv->current],
424 priv->frame_counter))
425 return;
426
427 if (priv->n_timings < FRAME_HISTORY_MAX_LENGTH)
428 priv->n_timings++;
429 else
430 gdk_frame_timings_unref (priv->timings[priv->current]);
431
432 priv->timings[priv->current] = _gdk_frame_timings_new (priv->frame_counter);
433}
434
435/**
436 * gdk_frame_clock_get_timings:
437 * @frame_clock: a #GdkFrameClock
438 * @frame_counter: the frame counter value identifying the frame to
439 * be received.
440 *
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().
444 *
445 * Returns: (nullable): the #GdkFrameTimings object for the specified
446 * frame, or %NULL if it is not available. See
447 * gdk_frame_clock_get_history_start().
448 * Since: 3.8
449 */
450GdkFrameTimings *
451gdk_frame_clock_get_timings (GdkFrameClock *frame_clock,
452 gint64 frame_counter)
453{
454 GdkFrameClockPrivate *priv;
455 gint pos;
456
457 g_return_val_if_fail (GDK_IS_FRAME_CLOCK (frame_clock), NULL);
458
459 priv = frame_clock->priv;
460
461 if (frame_counter > priv->frame_counter)
462 return NULL;
463
464 if (frame_counter <= priv->frame_counter - priv->n_timings)
465 return NULL;
466
467 pos = (priv->current - (priv->frame_counter - frame_counter) + FRAME_HISTORY_MAX_LENGTH) % FRAME_HISTORY_MAX_LENGTH;
468
469 return priv->timings[pos];
470}
471
472/**
473 * gdk_frame_clock_get_current_timings:
474 * @frame_clock: a #GdkFrameClock
475 *
476 * Gets the frame timings for the current frame.
477 *
478 * Returns: (nullable): the #GdkFrameTimings for the frame currently
479 * being processed, or even no frame is being processed, for the
480 * previous frame. Before any frames have been procesed, returns
481 * %NULL.
482 * Since: 3.8
483 */
484GdkFrameTimings *
485gdk_frame_clock_get_current_timings (GdkFrameClock *frame_clock)
486{
487 GdkFrameClockPrivate *priv;
488
489 g_return_val_if_fail (GDK_IS_FRAME_CLOCK (frame_clock), 0);
490
491 priv = frame_clock->priv;
492
493 return gdk_frame_clock_get_timings (frame_clock, priv->frame_counter);
494}
495
496
497#ifdef G_ENABLE_DEBUG
498void
499_gdk_frame_clock_debug_print_timings (GdkFrameClock *clock,
500 GdkFrameTimings *timings)
501{
502 GString *str;
503
504 gint64 previous_frame_time = 0;
505 GdkFrameTimings *previous_timings = gdk_frame_clock_get_timings (clock,
506 timings->frame_counter - 1);
507
508 if (previous_timings != NULL)
509 previous_frame_time = previous_timings->frame_time;
510
511 str = g_string_new ("");
512
513 g_string_append_printf (str, "%5" G_GINT64_FORMAT ":", timings->frame_counter);
514 if (previous_frame_time != 0)
515 {
516 g_string_append_printf (str, " interval=%-4.1f", (timings->frame_time - previous_frame_time) / 1000.);
517 g_string_append_printf (str, timings->slept_before ? " (sleep)" : " ");
518 }
519 if (timings->layout_start_time != 0)
520 g_string_append_printf (str, " layout_start=%-4.1f", (timings->layout_start_time - timings->frame_time) / 1000.);
521 if (timings->paint_start_time != 0)
522 g_string_append_printf (str, " paint_start=%-4.1f", (timings->paint_start_time - timings->frame_time) / 1000.);
523 if (timings->frame_end_time != 0)
524 g_string_append_printf (str, " frame_end=%-4.1f", (timings->frame_end_time - timings->frame_time) / 1000.);
525 if (timings->presentation_time != 0)
526 g_string_append_printf (str, " present=%-4.1f", (timings->presentation_time - timings->frame_time) / 1000.);
527 if (timings->predicted_presentation_time != 0)
528 g_string_append_printf (str, " predicted=%-4.1f", (timings->predicted_presentation_time - timings->frame_time) / 1000.);
529 if (timings->refresh_interval != 0)
530 g_string_append_printf (str, " refresh_interval=%-4.1f", timings->refresh_interval / 1000.);
531
532 g_message ("%s", str->str);
533 g_string_free (str, TRUE);
534}
535#endif /* G_ENABLE_DEBUG */
536
537#define DEFAULT_REFRESH_INTERVAL 16667 /* 16.7ms (1/60th second) */
538#define MAX_HISTORY_AGE 150000 /* 150ms */
539
540/**
541 * gdk_frame_clock_get_refresh_info:
542 * @frame_clock: a #GdkFrameClock
543 * @base_time: base time for determining a presentaton time
544 * @refresh_interval_return: a location to store the determined refresh
545 * interval, or %NULL. A default refresh interval of 1/60th of
546 * a second will be stored if no history is present.
547 * @presentation_time_return: a location to store the next
548 * candidate presentation time after the given base time.
549 * 0 will be will be stored if no history is present.
550 *
551 * Using the frame history stored in the frame clock, finds the last
552 * known presentation time and refresh interval, and assuming that
553 * presentation times are separated by the refresh interval,
554 * predicts a presentation time that is a multiple of the refresh
555 * interval after the last presentation time, and later than @base_time.
556 *
557 * Since: 3.8
558 */
559void
560gdk_frame_clock_get_refresh_info (GdkFrameClock *frame_clock,
561 gint64 base_time,
562 gint64 *refresh_interval_return,
563 gint64 *presentation_time_return)
564{
565 gint64 frame_counter;
566
567 g_return_if_fail (GDK_IS_FRAME_CLOCK (frame_clock));
568
569 frame_counter = gdk_frame_clock_get_frame_counter (frame_clock);
570
571 if (presentation_time_return)
572 *presentation_time_return = 0;
573 if (refresh_interval_return)
574 *refresh_interval_return = DEFAULT_REFRESH_INTERVAL;
575
576 while (TRUE)
577 {
578 GdkFrameTimings *timings = gdk_frame_clock_get_timings (frame_clock, frame_counter);
579 gint64 presentation_time;
580 gint64 refresh_interval;
581
582 if (timings == NULL)
583 return;
584
585 refresh_interval = timings->refresh_interval;
586 presentation_time = timings->presentation_time;
587
588 if (presentation_time != 0)
589 {
590 if (presentation_time > base_time - MAX_HISTORY_AGE &&
591 presentation_time_return)
592 {
593 if (refresh_interval == 0)
594 refresh_interval = DEFAULT_REFRESH_INTERVAL;
595
596 if (refresh_interval_return)
597 *refresh_interval_return = refresh_interval;
598
599 while (presentation_time < base_time)
600 presentation_time += refresh_interval;
601
602 if (presentation_time_return)
603 *presentation_time_return = presentation_time;
604 }
605
606 return;
607 }
608
609 frame_counter--;
610 }
611}
612
613void
614_gdk_frame_clock_emit_flush_events (GdkFrameClock *frame_clock)
615{
616 g_signal_emit (frame_clock, signals[FLUSH_EVENTS], 0);
617}
618
619void
620_gdk_frame_clock_emit_before_paint (GdkFrameClock *frame_clock)
621{
622 g_signal_emit (frame_clock, signals[BEFORE_PAINT], 0);
623}
624
625void
626_gdk_frame_clock_emit_update (GdkFrameClock *frame_clock)
627{
628 g_signal_emit (frame_clock, signals[UPDATE], 0);
629}
630
631void
632_gdk_frame_clock_emit_layout (GdkFrameClock *frame_clock)
633{
634 g_signal_emit (frame_clock, signals[LAYOUT], 0);
635}
636
637void
638_gdk_frame_clock_emit_paint (GdkFrameClock *frame_clock)
639{
640 g_signal_emit (frame_clock, signals[PAINT], 0);
641}
642
643void
644_gdk_frame_clock_emit_after_paint (GdkFrameClock *frame_clock)
645{
646 g_signal_emit (frame_clock, signals[AFTER_PAINT], 0);
647}
648
649void
650_gdk_frame_clock_emit_resume_events (GdkFrameClock *frame_clock)
651{
652 g_signal_emit (frame_clock, signals[RESUME_EVENTS], 0);
653}
654