typedefenum {
SMOOTH_PHASE_STATE_VALID = 0, /* explicit, since we count on zero-init */
SMOOTH_PHASE_STATE_AWAIT_FIRST,
SMOOTH_PHASE_STATE_AWAIT_DRAWN,
} SmoothDeltaState;
struct _GdkFrameClockIdlePrivate
{
gint64 frame_time; /* The exact time we last ran the clock cycle, or 0 if never */
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 */
gint64 smoothed_frame_time_period; /* The grid size that smoothed_frame_time_base is aligned to */
gint64 smoothed_frame_time_reported; /* Ensures we are always monotonic */
gint64 smoothed_frame_time_phase; /* The offset of the first reported frame time, in the current animation sequence, from the preceding vsync */
gint64 min_next_frame_time; /* We're not synced to vblank, so wait at least until this before next cycle to avoid busy looping */
SmoothDeltaState smooth_phase_state; /* The state of smoothed_frame_time_phase - is it valid, awaiting vsync etc. Thanks to zero-init, the initial value ofsmoothed_frame_time_phaseis`0`.Thisisvalid,sincewedidn'tgeta"framedrawn"eventyet.Accordingly, theinitialvalueofsmooth_phase_stateisSMOOTH_PHASE_STATE_VALID.Seethecommentingdk_frame_clock_paint_idle()
for details. */
/* Note: This is never called on first frame, so
* smoothed_frame_time_base != 0 and we have a valid frame_interval. */ static gint64
compute_smooth_frame_time (GdkFrameClock *clock,
gint64 new_frame_time,
gboolean new_frame_time_is_vsync_related,
gint64 smoothed_frame_time_base,
gint64 frame_interval)
{
GdkFrameClockIdle *self = GDK_FRAME_CLOCK_IDLE (clock);
GdkFrameClockIdlePrivate *priv = gdk_frame_clock_idle_get_instance_private (self); int frames_passed;
gint64 new_smoothed_time;
gint64 current_error;
gint64 correction_magnitude;
/* Consecutive frame, assume it is an integer number of frames later, so round to nearest such */ /* NOTE: This is >= 0, because smoothed_frame_time_base is < frame_interval/2 from old_frame_time
* and new_frame_time >= old_frame_time. */
frames_passed = (new_frame_time - smoothed_frame_time_base + frame_interval / 2) / frame_interval;
/* We use an approximately whole number of frames in the future from *lastsmoothedframetime.Thiswayweavoidminorjitterinthe *frametimesmakingtheanimationspeeduneven,butstillanimate
* evenly in case of whole frame skips. */
new_smoothed_time = smoothed_frame_time_base + frames_passed * frame_interval;
/* However, sometimes the smoothed time is too much off from the *realtime.Forexample,ifthefirstframeclockcyclehappened *notduetoaframerenderingbutaninputevent,then *new_frame_timecouldhappentobenearthemiddlebetweentwo *frames.Ifthathappensandwethenstartregularlyanimatingat *therefresh_rate,thenthejitterintherealtimemaycauseus *torandomlysometimesroundup,andsometimesdown. * *Tocombatthisweconvergethesmoothtimetowardstherealtime *inawaythatisslowwhentheyarenearandfastwhentheyare *farfromeachother. * *Thisisdonebyusingthesquareoftheerrorasthecorrection *magnitude.I.e.iftheerroris0.5frame,wecorrectby *0.5*0.5=0.25frame,iftheerroris0.25wecorrectby0.125,if *theerroris0.1,framewecorrectby0.01frame,etc. * *Theactualcomputationis: *(current_error/frame_interval)*(current_error/frame_interval)*frame_interval *Butthiscanbesimplifiedasbelow. * *Note:Weonlydothiscorrectionifthenewframeiscausedbya *thawoftheframeclock,sothatweknowthetimeisactually *relatedtothephysicalvblank.Forframeclockcyclestriggered *byothereventswealwaysstepupinwholeframesfromthelast *reportedtime.
*/ if (new_frame_time_is_vsync_related)
{
current_error = new_smoothed_time - new_frame_time;
correction_magnitude = current_error * current_error / frame_interval; /* Note, this is always > 0 due to the square */ if (current_error > 0)
new_smoothed_time -= correction_magnitude; else
new_smoothed_time += correction_magnitude;
}
/* can't change frame time during a paint */ if (priv->stage != GDK_FRAME_STAGE_NONE &&
priv->stage != GDK_FRAME_STAGE_FLUSH_EVENTS &&
(priv->stage != GDK_FRAME_STAGE_BEFORE_PAINT || priv->in_frame)) return priv->smoothed_frame_time_base;
/* Outside a paint, pick something smoothed close to now */
now = g_get_monotonic_time ();
/* First time frame, just return something */ if (priv->smoothed_frame_time_base == 0)
{
priv->smoothed_frame_time_reported = now; return now;
}
/* Since time is monotonic this is <= what we will pick for the next cycle, but
more likely than not it will be equal if we're doing a constant animation. */
new_smoothed_time = compute_smooth_frame_time (clock, now, FALSE,
priv->smoothed_frame_time_base,
priv->smoothed_frame_time_period);
_gdk_frame_clock_begin_frame (clock, priv->frame_time); /* Note "current" is different now so timings != prev_timings */
timings = gdk_frame_clock_get_current_timings (clock);
if (GDK_DEBUG_CHECK (FRAMES))
{ if (priv->stage != GDK_FRAME_STAGE_LAYOUT &&
(priv->requested & GDK_FRAME_CLOCK_PHASE_LAYOUT))
{ if (timings)
timings->layout_start_time = g_get_monotonic_time ();
}
}
/* We loop in the layout phase, because we don't want to progress *intothepaintphasewithinvalidsizeallocations.Thismay *happeninsomesituationlikeracesbetweenuserwindow *resizesandnaturalsizechanges.
*/
iter = 0; while ((priv->requested & GDK_FRAME_CLOCK_PHASE_LAYOUT) &&
!gdk_frame_clock_is_stopped (clock) &&
iter++ < 4)
{
priv->requested &= ~GDK_FRAME_CLOCK_PHASE_LAYOUT;
_gdk_frame_clock_emit_layout (clock);
} if (iter == 5)
g_warning ("gdk-frame-clock: layout continuously requested, giving up after 4 tries");
if (priv->stage == GDK_FRAME_STAGE_NONE)
priv->stage = GDK_FRAME_STAGE_FLUSH_EVENTS;
switch (priv->stage)
{ case GDK_FRAME_STAGE_FLUSH_EVENTS:
gdk_frame_clock_idle_run_flush_events (self);
G_GNUC_FALLTHROUGH;
case GDK_FRAME_STAGE_BEFORE_PAINT:
gdk_frame_clock_idle_run_before_paint (self);
G_GNUC_FALLTHROUGH;
case GDK_FRAME_STAGE_UPDATE:
gdk_frame_clock_idle_run_update (self);
G_GNUC_FALLTHROUGH;
case GDK_FRAME_STAGE_LAYOUT:
gdk_frame_clock_idle_run_layout (self);
G_GNUC_FALLTHROUGH;
case GDK_FRAME_STAGE_PAINT:
gdk_frame_clock_idle_run_paint (self);
G_GNUC_FALLTHROUGH;
case GDK_FRAME_STAGE_AFTER_PAINT:
gdk_frame_clock_idle_run_after_paint (self);
G_GNUC_FALLTHROUGH;
case GDK_FRAME_STAGE_RESUME_EVENTS:
gdk_frame_clock_idle_run_resume_events (self); break;
case GDK_FRAME_STAGE_NONE: default:
g_assert_not_reached ();
}
priv->in_frame = FALSE;
/* If there is throttling in the backend layer, then we'll do another *updateassoonasthebackendunthrottles(ifthereisworktodo), *otherwiseweneedtofigurewhenthenextframeshouldbe.
*/ if (!gdk_frame_clock_is_stopped (clock))
{ /* *Ifwedon'treceive"framedrawn"events,smooth_cycle_startwillsimplybeadvancedinconstantincrementsof *therefreshinterval.Thatwaywegetabsolutetargettimesforthenextcycles,whichshouldpreventskewing *intheschedulingoftheframeclock. * *Oncewedoreceive"framedrawn"events,smooth_cycle_startwilltrackthevsync,anddosoinamorestable *waycomparedtoframe_time.Ifwethennolongerreceive"framedrawn"events,smooth_cycle_startwillagainbe *simplyadvancedinincrementsoftherefreshinterval,butthistimeweareinsyncwiththevsync.Ifwestart *receiving"framedrawn"eventsshortlyafterlosingthem,thenweshouldstillbeinsync.
*/
gint64 smooth_cycle_start = priv->smoothed_frame_time_base - priv->smoothed_frame_time_phase;
priv->min_next_frame_time = smooth_cycle_start + priv->smoothed_frame_time_period;
/* If nothing is requested so we didn't start an idle, we need *toskiptotheendofthestatechain,sincetheidlewon't *runanddoitforus.
*/ if (priv->source == NULL)
priv->stage = GDK_FRAME_STAGE_NONE;
if (GDK_PROFILER_IS_RUNNING)
{ if (priv->freeze_time != 0)
{
gdk_profiler_end_mark (priv->freeze_time, "frameclock frozen", NULL);
priv->freeze_time = 0;
}
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.