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 "gdkframeclockidleprivate.h"
28
29#include "gdkdebug.h"
30#include "gdkframeclockprivate.h"
31#include "gdk-private.h"
32#include "gdkprofilerprivate.h"
33
34#ifdef G_OS_WIN32
35#include <windows.h>
36#endif
37
38#define FRAME_INTERVAL 16667 /* microseconds */
39
40typedef enum {
41 SMOOTH_PHASE_STATE_VALID = 0, /* explicit, since we count on zero-init */
42 SMOOTH_PHASE_STATE_AWAIT_FIRST,
43 SMOOTH_PHASE_STATE_AWAIT_DRAWN,
44} SmoothDeltaState;
45
46struct _GdkFrameClockIdlePrivate
47{
48 gint64 frame_time; /* The exact time we last ran the clock cycle, or 0 if never */
49 gint64 smoothed_frame_time_base; /* A grid-aligned version of frame_time (grid size == refresh period), never more than half a grid from frame_time */
50 gint64 smoothed_frame_time_period; /* The grid size that smoothed_frame_time_base is aligned to */
51 gint64 smoothed_frame_time_reported; /* Ensures we are always monotonic */
52 gint64 smoothed_frame_time_phase; /* The offset of the first reported frame time, in the current animation sequence, from the preceding vsync */
53 gint64 min_next_frame_time; /* We're not synced to vblank, so wait at least until this before next cycle to avoid busy looping */
54 SmoothDeltaState smooth_phase_state; /* The state of smoothed_frame_time_phase - is it valid, awaiting vsync etc. Thanks to zero-init, the initial value
55 of smoothed_frame_time_phase is `0`. This is valid, since we didn't get a "frame drawn" event yet. Accordingly,
56 the initial value of smooth_phase_state is SMOOTH_PHASE_STATE_VALID. See the comment in gdk_frame_clock_paint_idle()
57 for details. */
58
59 gint64 sleep_serial;
60 gint64 freeze_time; /* in microseconds */
61
62 guint flush_idle_id;
63 guint paint_idle_id;
64 guint freeze_count;
65 guint updating_count;
66
67 GdkFrameClockPhase requested;
68 GdkFrameClockPhase phase;
69
70 guint in_paint_idle : 1;
71 guint paint_is_thaw : 1;
72#ifdef G_OS_WIN32
73 guint begin_period : 1;
74#endif
75};
76
77static gboolean gdk_frame_clock_flush_idle (void *data);
78static gboolean gdk_frame_clock_paint_idle (void *data);
79
80G_DEFINE_TYPE_WITH_PRIVATE (GdkFrameClockIdle, gdk_frame_clock_idle, GDK_TYPE_FRAME_CLOCK)
81
82static gint64 sleep_serial;
83static gint64 sleep_source_prepare_time;
84static GSource *sleep_source;
85
86static gboolean
87sleep_source_prepare (GSource *source,
88 int *timeout)
89{
90 sleep_source_prepare_time = g_source_get_time (source);
91 *timeout = -1;
92 return FALSE;
93}
94
95static gboolean
96sleep_source_check (GSource *source)
97{
98 if (g_source_get_time (source) != sleep_source_prepare_time)
99 sleep_serial++;
100
101 return FALSE;
102}
103
104static gboolean
105sleep_source_dispatch (GSource *source,
106 GSourceFunc callback,
107 gpointer user_data)
108{
109 return TRUE;
110}
111
112static GSourceFuncs sleep_source_funcs = {
113 sleep_source_prepare,
114 sleep_source_check,
115 sleep_source_dispatch,
116 NULL /* finalize */
117};
118
119static gint64
120get_sleep_serial (void)
121{
122 if (sleep_source == NULL)
123 {
124 sleep_source = g_source_new (source_funcs: &sleep_source_funcs, struct_size: sizeof (GSource));
125
126 g_source_set_priority (source: sleep_source, G_PRIORITY_HIGH);
127 g_source_attach (source: sleep_source, NULL);
128 g_source_unref (source: sleep_source);
129 }
130
131 return sleep_serial;
132}
133
134static void
135gdk_frame_clock_idle_init (GdkFrameClockIdle *frame_clock_idle)
136{
137 GdkFrameClockIdlePrivate *priv;
138
139 frame_clock_idle->priv = priv =
140 gdk_frame_clock_idle_get_instance_private (self: frame_clock_idle);
141
142 priv->freeze_count = 0;
143 priv->smoothed_frame_time_period = FRAME_INTERVAL;
144}
145
146static void
147gdk_frame_clock_idle_dispose (GObject *object)
148{
149 GdkFrameClockIdlePrivate *priv = GDK_FRAME_CLOCK_IDLE (object)->priv;
150
151 if (priv->flush_idle_id != 0)
152 {
153 g_source_remove (tag: priv->flush_idle_id);
154 priv->flush_idle_id = 0;
155 }
156
157 if (priv->paint_idle_id != 0)
158 {
159 g_source_remove (tag: priv->paint_idle_id);
160 priv->paint_idle_id = 0;
161 }
162
163#ifdef G_OS_WIN32
164 if (priv->begin_period)
165 {
166 timeEndPeriod(1);
167 priv->begin_period = FALSE;
168 }
169#endif
170
171 G_OBJECT_CLASS (gdk_frame_clock_idle_parent_class)->dispose (object);
172}
173
174/* Note: This is never called on first frame, so
175 * smoothed_frame_time_base != 0 and we have a valid frame_interval. */
176static gint64
177compute_smooth_frame_time (GdkFrameClock *clock,
178 gint64 new_frame_time,
179 gboolean new_frame_time_is_vsync_related,
180 gint64 smoothed_frame_time_base,
181 gint64 frame_interval)
182{
183 GdkFrameClockIdlePrivate *priv = GDK_FRAME_CLOCK_IDLE (clock)->priv;
184 int frames_passed;
185 gint64 new_smoothed_time;
186 gint64 current_error;
187 gint64 correction_magnitude;
188
189 /* Consecutive frame, assume it is an integer number of frames later, so round to nearest such */
190 /* NOTE: This is >= 0, because smoothed_frame_time_base is < frame_interval/2 from old_frame_time
191 * and new_frame_time >= old_frame_time. */
192 frames_passed = (new_frame_time - smoothed_frame_time_base + frame_interval / 2) / frame_interval;
193
194 /* We use an approximately whole number of frames in the future from
195 * last smoothed frame time. This way we avoid minor jitter in the
196 * frame times making the animation speed uneven, but still animate
197 * evenly in case of whole frame skips. */
198 new_smoothed_time = smoothed_frame_time_base + frames_passed * frame_interval;
199
200 /* However, sometimes the smoothed time is too much off from the
201 * real time. For example, if the first frame clock cycle happened
202 * not due to a frame rendering but an input event, then
203 * new_frame_time could happen to be near the middle between two
204 * frames. If that happens and we then start regularly animating at
205 * the refresh_rate, then the jitter in the real time may cause us
206 * to randomly sometimes round up, and sometimes down.
207 *
208 * To combat this we converge the smooth time towards the real time
209 * in a way that is slow when they are near and fast when they are
210 * far from each other.
211 *
212 * This is done by using the square of the error as the correction
213 * magnitude. I.e. if the error is 0.5 frame, we correct by
214 * 0.5*0.5=0.25 frame, if the error is 0.25 we correct by 0.125, if
215 * the error is 0.1, frame we correct by 0.01 frame, etc.
216 *
217 * The actual computation is:
218 * (current_error/frame_interval)*(current_error/frame_interval)*frame_interval
219 * But this can be simplified as below.
220 *
221 * Note: We only do this correction if the new frame is caused by a
222 * thaw of the frame clock, so that we know the time is actually
223 * related to the physical vblank. For frameclock cycles triggered
224 * by other events we always step up in whole frames from the last
225 * reported time.
226 */
227 if (new_frame_time_is_vsync_related)
228 {
229 current_error = new_smoothed_time - new_frame_time;
230 correction_magnitude = current_error * current_error / frame_interval; /* Note, this is always > 0 due to the square */
231 if (current_error > 0)
232 new_smoothed_time -= correction_magnitude;
233 else
234 new_smoothed_time += correction_magnitude;
235 }
236
237 /* Ensure we're always monotonic */
238 if (new_smoothed_time <= priv->smoothed_frame_time_reported)
239 new_smoothed_time = priv->smoothed_frame_time_reported;
240
241 return new_smoothed_time;
242}
243
244static gint64
245gdk_frame_clock_idle_get_frame_time (GdkFrameClock *clock)
246{
247 GdkFrameClockIdlePrivate *priv = GDK_FRAME_CLOCK_IDLE (clock)->priv;
248 gint64 now;
249 gint64 new_smoothed_time;
250
251 /* can't change frame time during a paint */
252 if (priv->phase != GDK_FRAME_CLOCK_PHASE_NONE &&
253 priv->phase != GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS &&
254 (priv->phase != GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT || priv->in_paint_idle))
255 return priv->smoothed_frame_time_base;
256
257 /* Outside a paint, pick something smoothed close to now */
258 now = g_get_monotonic_time ();
259
260 /* First time frame, just return something */
261 if (priv->smoothed_frame_time_base == 0)
262 {
263 priv->smoothed_frame_time_reported = now;
264 return now;
265 }
266
267 /* Since time is monotonic this is <= what we will pick for the next cycle, but
268 more likely than not it will be equal if we're doing a constant animation. */
269 new_smoothed_time = compute_smooth_frame_time (clock, new_frame_time: now, FALSE,
270 smoothed_frame_time_base: priv->smoothed_frame_time_base,
271 frame_interval: priv->smoothed_frame_time_period);
272
273 priv->smoothed_frame_time_reported = new_smoothed_time;
274 return new_smoothed_time;
275}
276
277#define RUN_FLUSH_IDLE(priv) \
278 ((priv)->freeze_count == 0 && \
279 ((priv)->requested & GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS) != 0)
280
281/* The reason why we track updating_count separately here and don't
282 * just add GDK_FRAME_CLOCK_PHASE_UPDATE into ->request on every frame
283 * is so that we can avoid doing one more frame when an animation
284 * is cancelled.
285 */
286#define RUN_PAINT_IDLE(priv) \
287 ((priv)->freeze_count == 0 && \
288 (((priv)->requested & ~GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS) != 0 || \
289 (priv)->updating_count > 0))
290
291static void
292maybe_start_idle (GdkFrameClockIdle *clock_idle,
293 gboolean caused_by_thaw)
294{
295 GdkFrameClockIdlePrivate *priv = clock_idle->priv;
296
297 if (RUN_FLUSH_IDLE (priv) || RUN_PAINT_IDLE (priv))
298 {
299 guint min_interval = 0;
300
301 if (priv->min_next_frame_time != 0)
302 {
303 gint64 now = g_get_monotonic_time ();
304 gint64 min_interval_us = MAX (priv->min_next_frame_time, now) - now;
305 min_interval = (min_interval_us + 500) / 1000;
306 }
307
308 if (priv->flush_idle_id == 0 && RUN_FLUSH_IDLE (priv))
309 {
310 GSource *source;
311
312 priv->flush_idle_id = g_timeout_add_full (GDK_PRIORITY_EVENTS + 1,
313 interval: min_interval,
314 function: gdk_frame_clock_flush_idle,
315 g_object_ref (clock_idle),
316 notify: (GDestroyNotify) g_object_unref);
317 source = g_main_context_find_source_by_id (NULL, source_id: priv->flush_idle_id);
318 g_source_set_static_name (source, "[gtk] gdk_frame_clock_flush_idle");
319 }
320
321 if (!priv->in_paint_idle &&
322 priv->paint_idle_id == 0 && RUN_PAINT_IDLE (priv))
323 {
324 priv->paint_is_thaw = caused_by_thaw;
325 priv->paint_idle_id = g_timeout_add_full (GDK_PRIORITY_REDRAW,
326 interval: min_interval,
327 function: gdk_frame_clock_paint_idle,
328 g_object_ref (clock_idle),
329 notify: (GDestroyNotify) g_object_unref);
330 gdk_source_set_static_name_by_id (tag: priv->paint_idle_id, name: "[gtk] gdk_frame_clock_paint_idle");
331 }
332 }
333}
334
335static void
336maybe_stop_idle (GdkFrameClockIdle *clock_idle)
337{
338 GdkFrameClockIdlePrivate *priv = clock_idle->priv;
339
340 if (priv->flush_idle_id != 0 && !RUN_FLUSH_IDLE (priv))
341 {
342 g_source_remove (tag: priv->flush_idle_id);
343 priv->flush_idle_id = 0;
344 }
345
346 if (priv->paint_idle_id != 0 && !RUN_PAINT_IDLE (priv))
347 {
348 g_source_remove (tag: priv->paint_idle_id);
349 priv->paint_idle_id = 0;
350 }
351}
352
353static gboolean
354gdk_frame_clock_flush_idle (void *data)
355{
356 GdkFrameClock *clock = GDK_FRAME_CLOCK (data);
357 GdkFrameClockIdle *clock_idle = GDK_FRAME_CLOCK_IDLE (clock);
358 GdkFrameClockIdlePrivate *priv = clock_idle->priv;
359
360 priv->flush_idle_id = 0;
361
362 if (priv->phase != GDK_FRAME_CLOCK_PHASE_NONE)
363 return FALSE;
364
365 priv->phase = GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS;
366 priv->requested &= ~GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS;
367
368 _gdk_frame_clock_emit_flush_events (frame_clock: clock);
369
370 if ((priv->requested & ~GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS) != 0 ||
371 priv->updating_count > 0)
372 priv->phase = GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT;
373 else
374 priv->phase = GDK_FRAME_CLOCK_PHASE_NONE;
375
376 g_clear_handle_id (&priv->paint_idle_id, g_source_remove);
377 gdk_frame_clock_paint_idle (data);
378
379 return FALSE;
380}
381
382/*
383 * Returns the positive remainder.
384 *
385 * As an example, lets consider (-5) % 16:
386 *
387 * (-5) % 16 = (0 * 16) + (-5) = -5
388 *
389 * If we only want positive remainders, we can instead calculate
390 *
391 * (-5) % 16 = (1 * 16) + (-5) = 11
392 *
393 * The built-in `%` operator returns the former, positive_modulo() returns the latter.
394 */
395static int
396positive_modulo (int i, int n)
397{
398 return (i % n + n) % n;
399}
400
401static gboolean
402gdk_frame_clock_paint_idle (void *data)
403{
404 GdkFrameClock *clock = GDK_FRAME_CLOCK (data);
405 GdkFrameClockIdle *clock_idle = GDK_FRAME_CLOCK_IDLE (clock);
406 GdkFrameClockIdlePrivate *priv = clock_idle->priv;
407 gboolean skip_to_resume_events;
408 GdkFrameTimings *timings = NULL;
409 gint64 before G_GNUC_UNUSED;
410
411 before = GDK_PROFILER_CURRENT_TIME;
412
413 priv->paint_idle_id = 0;
414 priv->in_paint_idle = TRUE;
415 priv->min_next_frame_time = 0;
416
417 skip_to_resume_events =
418 (priv->requested & ~(GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS | GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS)) == 0 &&
419 priv->updating_count == 0;
420
421 if (priv->phase > GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT)
422 {
423 timings = gdk_frame_clock_get_current_timings (frame_clock: clock);
424 }
425
426 if (!skip_to_resume_events)
427 {
428 switch (priv->phase)
429 {
430 case GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS:
431 break;
432 case GDK_FRAME_CLOCK_PHASE_NONE:
433 case GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT:
434 if (priv->freeze_count == 0)
435 {
436 gint64 frame_interval = FRAME_INTERVAL;
437 GdkFrameTimings *prev_timings = gdk_frame_clock_get_current_timings (frame_clock: clock);
438
439 if (prev_timings && prev_timings->refresh_interval)
440 frame_interval = prev_timings->refresh_interval;
441
442 priv->frame_time = g_get_monotonic_time ();
443
444 /*
445 * The first clock cycle of an animation might have been triggered by some external event. An external
446 * event can be an input event, an expired timer, data arriving over the network etc. This can happen at
447 * any time, so the cycle could have been scheduled at some random time rather then immediately after a
448 * frame completion. The offset between the start of the first animation cycle and the preceding vsync is
449 * called the "phase" of the clock cycle start time (not to be confused with the phase of the frame
450 * clock).
451 *
452 * In this first clock cycle, the "smooth" frame time is simply the time when the cycle was started. This
453 * could be followed by several cycles which are not vsync-related. As long as we don't get a "frame
454 * drawn" signal from the compositor, the clock cycles will occur every about frame_interval. Once we do
455 * get a "frame drawn" signal, from this point on the frame clock cycles will start shortly after the
456 * corresponding vsync signals, again every about frame_interval. The first vsync-related clock cycle
457 * might occur less than a refresh interval away from the last non-vsync-related cycle. See the diagram
458 * below for details. So while the cadence stays the same - a frame clock cycle every about frame_interval
459 * - the phase of the cycles start time has changed.
460 *
461 * Since we might have already reported the frame time to the application in the previous clock cycles, we
462 * have to adjust future reported frame times. We want the first vsync-related smooth time to be separated
463 * by exactly 1 frame_interval from the previous one, in order to maintain the regularity of the reported
464 * frame times. To achieve that, from this point on we add the phase of the first clock cycle start time to
465 * the smooth time. In order to compute that phase, accounting for possible skipped frames (e.g. due to
466 * compositor stalls), we want the following to be true:
467 *
468 * first_vsync_smooth_time = last_non_vsync_smooth_time + frame_interval * (1 + frames_skipped)
469 *
470 * We can assign the following known/desired values to the above equation:
471 *
472 * last_non_vsync_smooth_time = smoothed_frame_time_base
473 * first_vsync_smooth_time = frame_time + smoothed_frame_time_phase
474 *
475 * That leads us to the following, from which we can extract smoothed_frame_time_phase:
476 *
477 * frame_time + smoothed_frame_time_phase = smoothed_frame_time_base +
478 * frame_interval * (1 + frames_skipped)
479 *
480 * In the following diagram, '|' mark a vsync, '*' mark the start of a clock cycle, '+' is the adjusted
481 * frame time, '!' marks the reception of "frame drawn" events from the compositor. Note that the clock
482 * cycle cadence changed after the first vsync-related cycle. This cadence is kept even if we don't
483 * receive a 'frame drawn' signal in a subsequent frame, since then we schedule the clock at intervals of
484 * refresh_interval.
485 *
486 * vsync | | | | | |...
487 * frame drawn | | |! |! | |...
488 * cycle start | * | * |* |* |* |...
489 * adjusted times | * | * | + | + | + |...
490 * phase ^------^
491 */
492 if (priv->smooth_phase_state == SMOOTH_PHASE_STATE_AWAIT_FIRST)
493 {
494 /* First animation cycle - usually unrelated to vsync */
495 priv->smoothed_frame_time_base = 0;
496 priv->smoothed_frame_time_phase = 0;
497 priv->smooth_phase_state = SMOOTH_PHASE_STATE_AWAIT_DRAWN;
498 }
499 else if (priv->smooth_phase_state == SMOOTH_PHASE_STATE_AWAIT_DRAWN &&
500 priv->paint_is_thaw)
501 {
502 /* First vsync-related animation cycle, we can now compute the phase. We want the phase to satisfy
503 0 <= phase < frame_interval */
504 priv->smoothed_frame_time_phase =
505 positive_modulo (i: priv->smoothed_frame_time_base - priv->frame_time,
506 n: frame_interval);
507 priv->smooth_phase_state = SMOOTH_PHASE_STATE_VALID;
508 }
509
510 if (priv->smoothed_frame_time_base == 0)
511 {
512 /* First frame ever, or first cycle in a new animation sequence. Ensure monotonicity */
513 priv->smoothed_frame_time_base = MAX (priv->frame_time, priv->smoothed_frame_time_reported);
514 }
515 else
516 {
517 /* compute_smooth_frame_time() ensures monotonicity */
518 priv->smoothed_frame_time_base =
519 compute_smooth_frame_time (clock, new_frame_time: priv->frame_time + priv->smoothed_frame_time_phase,
520 new_frame_time_is_vsync_related: priv->paint_is_thaw,
521 smoothed_frame_time_base: priv->smoothed_frame_time_base,
522 frame_interval: priv->smoothed_frame_time_period);
523 }
524
525 priv->smoothed_frame_time_period = frame_interval;
526 priv->smoothed_frame_time_reported = priv->smoothed_frame_time_base;
527
528 _gdk_frame_clock_begin_frame (clock);
529 /* Note "current" is different now so timings != prev_timings */
530 timings = gdk_frame_clock_get_current_timings (frame_clock: clock);
531
532 timings->frame_time = priv->frame_time;
533 timings->smoothed_frame_time = priv->smoothed_frame_time_base;
534 timings->slept_before = priv->sleep_serial != get_sleep_serial ();
535
536 priv->phase = GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT;
537
538 /* We always emit ::before-paint and ::after-paint if
539 * any of the intermediate phases are requested and
540 * they don't get repeated if you freeze/thaw while
541 * in them.
542 */
543 priv->requested &= ~GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT;
544 _gdk_frame_clock_emit_before_paint (frame_clock: clock);
545 priv->phase = GDK_FRAME_CLOCK_PHASE_UPDATE;
546 }
547 G_GNUC_FALLTHROUGH;
548
549 case GDK_FRAME_CLOCK_PHASE_UPDATE:
550 if (priv->freeze_count == 0)
551 {
552 if ((priv->requested & GDK_FRAME_CLOCK_PHASE_UPDATE) != 0 ||
553 priv->updating_count > 0)
554 {
555 priv->requested &= ~GDK_FRAME_CLOCK_PHASE_UPDATE;
556 _gdk_frame_clock_emit_update (frame_clock: clock);
557 }
558 }
559 G_GNUC_FALLTHROUGH;
560
561 case GDK_FRAME_CLOCK_PHASE_LAYOUT:
562 if (priv->freeze_count == 0)
563 {
564 int iter;
565#ifdef G_ENABLE_DEBUG
566 if (GDK_DEBUG_CHECK (FRAMES))
567 {
568 if (priv->phase != GDK_FRAME_CLOCK_PHASE_LAYOUT &&
569 (priv->requested & GDK_FRAME_CLOCK_PHASE_LAYOUT))
570 timings->layout_start_time = g_get_monotonic_time ();
571 }
572#endif
573
574 priv->phase = GDK_FRAME_CLOCK_PHASE_LAYOUT;
575 /* We loop in the layout phase, because we don't want to progress
576 * into the paint phase with invalid size allocations. This may
577 * happen in some situation like races between user window
578 * resizes and natural size changes.
579 */
580 iter = 0;
581 while ((priv->requested & GDK_FRAME_CLOCK_PHASE_LAYOUT) &&
582 priv->freeze_count == 0 && iter++ < 4)
583 {
584 priv->requested &= ~GDK_FRAME_CLOCK_PHASE_LAYOUT;
585 _gdk_frame_clock_emit_layout (frame_clock: clock);
586 }
587 if (iter == 5)
588 g_warning ("gdk-frame-clock: layout continuously requested, giving up after 4 tries");
589 }
590 G_GNUC_FALLTHROUGH;
591
592 case GDK_FRAME_CLOCK_PHASE_PAINT:
593 if (priv->freeze_count == 0)
594 {
595#ifdef G_ENABLE_DEBUG
596 if (GDK_DEBUG_CHECK (FRAMES))
597 {
598 if (priv->phase != GDK_FRAME_CLOCK_PHASE_PAINT &&
599 (priv->requested & GDK_FRAME_CLOCK_PHASE_PAINT))
600 timings->paint_start_time = g_get_monotonic_time ();
601 }
602#endif
603
604 priv->phase = GDK_FRAME_CLOCK_PHASE_PAINT;
605 if (priv->requested & GDK_FRAME_CLOCK_PHASE_PAINT)
606 {
607 priv->requested &= ~GDK_FRAME_CLOCK_PHASE_PAINT;
608 _gdk_frame_clock_emit_paint (frame_clock: clock);
609 }
610 }
611 G_GNUC_FALLTHROUGH;
612
613 case GDK_FRAME_CLOCK_PHASE_AFTER_PAINT:
614 if (priv->freeze_count == 0)
615 {
616 priv->requested &= ~GDK_FRAME_CLOCK_PHASE_AFTER_PAINT;
617 _gdk_frame_clock_emit_after_paint (frame_clock: clock);
618 /* the ::after-paint phase doesn't get repeated on freeze/thaw,
619 */
620 priv->phase = GDK_FRAME_CLOCK_PHASE_NONE;
621 }
622#ifdef G_ENABLE_DEBUG
623 if (GDK_DEBUG_CHECK (FRAMES))
624 timings->frame_end_time = g_get_monotonic_time ();
625#endif /* G_ENABLE_DEBUG */
626 G_GNUC_FALLTHROUGH;
627
628 case GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS:
629 default:
630 ;
631 }
632 }
633
634 if (priv->requested & GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS)
635 {
636 priv->requested &= ~GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS;
637 _gdk_frame_clock_emit_resume_events (frame_clock: clock);
638 }
639
640 if (priv->freeze_count == 0)
641 priv->phase = GDK_FRAME_CLOCK_PHASE_NONE;
642
643 priv->in_paint_idle = FALSE;
644
645 /* If there is throttling in the backend layer, then we'll do another
646 * update as soon as the backend unthrottles (if there is work to do),
647 * otherwise we need to figure when the next frame should be.
648 */
649 if (priv->freeze_count == 0)
650 {
651 /*
652 * If we don't receive "frame drawn" events, smooth_cycle_start will simply be advanced in constant increments of
653 * the refresh interval. That way we get absolute target times for the next cycles, which should prevent skewing
654 * in the scheduling of the frame clock.
655 *
656 * Once we do receive "frame drawn" events, smooth_cycle_start will track the vsync, and do so in a more stable
657 * way compared to frame_time. If we then no longer receive "frame drawn" events, smooth_cycle_start will again be
658 * simply advanced in increments of the refresh interval, but this time we are in sync with the vsync. If we start
659 * receiving "frame drawn" events shortly after losing them, then we should still be in sync.
660 */
661 gint64 smooth_cycle_start = priv->smoothed_frame_time_base - priv->smoothed_frame_time_phase;
662 priv->min_next_frame_time = smooth_cycle_start + priv->smoothed_frame_time_period;
663
664 maybe_start_idle (clock_idle, FALSE);
665 }
666
667 if (priv->freeze_count == 0)
668 priv->sleep_serial = get_sleep_serial ();
669
670 gdk_profiler_end_mark (before, "frameclock cycle", NULL);
671
672 return FALSE;
673}
674
675static void
676gdk_frame_clock_idle_request_phase (GdkFrameClock *clock,
677 GdkFrameClockPhase phase)
678{
679 GdkFrameClockIdle *clock_idle = GDK_FRAME_CLOCK_IDLE (clock);
680 GdkFrameClockIdlePrivate *priv = clock_idle->priv;
681
682 priv->requested |= phase;
683 maybe_start_idle (clock_idle, FALSE);
684}
685
686static void
687gdk_frame_clock_idle_begin_updating (GdkFrameClock *clock)
688{
689 GdkFrameClockIdle *clock_idle = GDK_FRAME_CLOCK_IDLE (clock);
690 GdkFrameClockIdlePrivate *priv = clock_idle->priv;
691
692#ifdef G_OS_WIN32
693 /* We need a higher resolution timer while doing animations */
694 if (priv->updating_count == 0 && !priv->begin_period)
695 {
696 timeBeginPeriod(1);
697 priv->begin_period = TRUE;
698 }
699#endif
700
701 if (priv->updating_count == 0)
702 {
703 priv->smooth_phase_state = SMOOTH_PHASE_STATE_AWAIT_FIRST;
704 }
705
706 priv->updating_count++;
707 maybe_start_idle (clock_idle, FALSE);
708}
709
710static void
711gdk_frame_clock_idle_end_updating (GdkFrameClock *clock)
712{
713 GdkFrameClockIdle *clock_idle = GDK_FRAME_CLOCK_IDLE (clock);
714 GdkFrameClockIdlePrivate *priv = clock_idle->priv;
715
716 g_return_if_fail (priv->updating_count > 0);
717
718 priv->updating_count--;
719 maybe_stop_idle (clock_idle);
720
721 if (priv->updating_count == 0)
722 {
723 priv->smooth_phase_state = SMOOTH_PHASE_STATE_VALID;
724 }
725
726#ifdef G_OS_WIN32
727 if (priv->updating_count == 0 && priv->begin_period)
728 {
729 timeEndPeriod(1);
730 priv->begin_period = FALSE;
731 }
732#endif
733}
734
735static void
736gdk_frame_clock_idle_freeze (GdkFrameClock *clock)
737{
738 GdkFrameClockIdle *clock_idle = GDK_FRAME_CLOCK_IDLE (clock);
739 GdkFrameClockIdlePrivate *priv = clock_idle->priv;
740
741 if (priv->freeze_count == 0)
742 {
743 if (GDK_PROFILER_IS_RUNNING)
744 priv->freeze_time = g_get_monotonic_time ();
745 }
746
747 priv->freeze_count++;
748 maybe_stop_idle (clock_idle);
749}
750
751static void
752gdk_frame_clock_idle_thaw (GdkFrameClock *clock)
753{
754 GdkFrameClockIdle *clock_idle = GDK_FRAME_CLOCK_IDLE (clock);
755 GdkFrameClockIdlePrivate *priv = clock_idle->priv;
756
757 g_return_if_fail (priv->freeze_count > 0);
758
759 priv->freeze_count--;
760 if (priv->freeze_count == 0)
761 {
762 maybe_start_idle (clock_idle, TRUE);
763 /* If nothing is requested so we didn't start an idle, we need
764 * to skip to the end of the state chain, since the idle won't
765 * run and do it for us.
766 */
767 if (priv->paint_idle_id == 0)
768 priv->phase = GDK_FRAME_CLOCK_PHASE_NONE;
769
770 priv->sleep_serial = get_sleep_serial ();
771
772 if (GDK_PROFILER_IS_RUNNING)
773 {
774 if (priv->freeze_time != 0)
775 {
776 gdk_profiler_end_mark (priv->freeze_time * 1000, "frameclock frozen", NULL);
777 priv->freeze_time = 0;
778 }
779 }
780 }
781}
782
783static void
784gdk_frame_clock_idle_class_init (GdkFrameClockIdleClass *klass)
785{
786 GObjectClass *gobject_class = (GObjectClass*) klass;
787 GdkFrameClockClass *frame_clock_class = (GdkFrameClockClass *)klass;
788
789 gobject_class->dispose = gdk_frame_clock_idle_dispose;
790
791 frame_clock_class->get_frame_time = gdk_frame_clock_idle_get_frame_time;
792 frame_clock_class->request_phase = gdk_frame_clock_idle_request_phase;
793 frame_clock_class->begin_updating = gdk_frame_clock_idle_begin_updating;
794 frame_clock_class->end_updating = gdk_frame_clock_idle_end_updating;
795 frame_clock_class->freeze = gdk_frame_clock_idle_freeze;
796 frame_clock_class->thaw = gdk_frame_clock_idle_thaw;
797}
798
799GdkFrameClock *
800_gdk_frame_clock_idle_new (void)
801{
802 GdkFrameClockIdle *clock;
803
804 clock = g_object_new (GDK_TYPE_FRAME_CLOCK_IDLE, NULL);
805
806 return GDK_FRAME_CLOCK (clock);
807}
808

source code of gtk/gdk/gdkframeclockidle.c