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 | |
40 | typedef 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 | |
46 | struct _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 | |
77 | static gboolean gdk_frame_clock_flush_idle (void *data); |
78 | static gboolean gdk_frame_clock_paint_idle (void *data); |
79 | |
80 | G_DEFINE_TYPE_WITH_PRIVATE (GdkFrameClockIdle, gdk_frame_clock_idle, GDK_TYPE_FRAME_CLOCK) |
81 | |
82 | static gint64 sleep_serial; |
83 | static gint64 sleep_source_prepare_time; |
84 | static GSource *sleep_source; |
85 | |
86 | static gboolean |
87 | sleep_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 | |
95 | static gboolean |
96 | sleep_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 | |
104 | static gboolean |
105 | sleep_source_dispatch (GSource *source, |
106 | GSourceFunc callback, |
107 | gpointer user_data) |
108 | { |
109 | return TRUE; |
110 | } |
111 | |
112 | static GSourceFuncs sleep_source_funcs = { |
113 | sleep_source_prepare, |
114 | sleep_source_check, |
115 | sleep_source_dispatch, |
116 | NULL /* finalize */ |
117 | }; |
118 | |
119 | static gint64 |
120 | get_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 | |
134 | static void |
135 | gdk_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 | |
146 | static void |
147 | gdk_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. */ |
176 | static gint64 |
177 | compute_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 | |
244 | static gint64 |
245 | gdk_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 | |
291 | static void |
292 | maybe_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 | |
335 | static void |
336 | maybe_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 | |
353 | static gboolean |
354 | gdk_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 | */ |
395 | static int |
396 | positive_modulo (int i, int n) |
397 | { |
398 | return (i % n + n) % n; |
399 | } |
400 | |
401 | static gboolean |
402 | gdk_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 | |
675 | static void |
676 | gdk_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 | |
686 | static void |
687 | gdk_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 | |
710 | static void |
711 | gdk_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 | |
735 | static void |
736 | gdk_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 | |
751 | static void |
752 | gdk_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 | |
783 | static void |
784 | gdk_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 | |
799 | GdkFrameClock * |
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 | |