clutter/frame-clock: Allow scheduling an update in the future
Allow creating a queue of future times to tick at. This adds a new clock state where we let the clock go idle, but will wake at a time in the future to tick. We can still schedule ticks while in this new state, but we're assured a tick at or shortly after the scheduled time. This will be used later by wayland code that schedules future presentation. Signed-off-by: Derek Foreman <derek.foreman@collabora.com> Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3355>
This commit is contained in:
parent
8c1ec168c6
commit
5b214dc2b7
@ -52,6 +52,11 @@ typedef struct _ClutterFrameListener
|
||||
gpointer user_data;
|
||||
} ClutterFrameListener;
|
||||
|
||||
typedef struct _DeferredTime
|
||||
{
|
||||
int64_t target_time_us;
|
||||
} DeferredTime;
|
||||
|
||||
typedef struct _ClutterClockSource
|
||||
{
|
||||
GSource source;
|
||||
@ -70,6 +75,7 @@ typedef enum _ClutterFrameClockState
|
||||
CLUTTER_FRAME_CLOCK_STATE_IDLE,
|
||||
CLUTTER_FRAME_CLOCK_STATE_SCHEDULED,
|
||||
CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW,
|
||||
CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_LATER,
|
||||
CLUTTER_FRAME_CLOCK_STATE_DISPATCHING,
|
||||
CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED,
|
||||
} ClutterFrameClockState;
|
||||
@ -145,11 +151,17 @@ struct _ClutterFrameClock
|
||||
int64_t deadline_evasion_us;
|
||||
|
||||
char *output_name;
|
||||
|
||||
GQueue *deferred_times;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (ClutterFrameClock, clutter_frame_clock,
|
||||
G_TYPE_OBJECT)
|
||||
|
||||
static void
|
||||
clutter_frame_clock_schedule_update_later (ClutterFrameClock *frame_clock,
|
||||
int64_t target_us);
|
||||
|
||||
float
|
||||
clutter_frame_clock_get_refresh_rate (ClutterFrameClock *frame_clock)
|
||||
{
|
||||
@ -273,9 +285,35 @@ advance_timelines (ClutterFrameClock *frame_clock,
|
||||
g_list_free_full (timelines, g_object_unref);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
clean_deferred_times (ClutterFrameClock *frame_clock)
|
||||
{
|
||||
DeferredTime *head;
|
||||
gboolean cleaned_times = FALSE;
|
||||
int64_t current_time_us;
|
||||
|
||||
if (frame_clock->is_next_presentation_time_valid)
|
||||
current_time_us = frame_clock->next_presentation_time_us;
|
||||
else
|
||||
current_time_us = g_get_monotonic_time ();
|
||||
|
||||
while ((head = g_queue_peek_head (frame_clock->deferred_times)))
|
||||
{
|
||||
if (current_time_us < head->target_time_us)
|
||||
break;
|
||||
|
||||
g_free (g_queue_pop_head (frame_clock->deferred_times));
|
||||
cleaned_times = TRUE;
|
||||
}
|
||||
return cleaned_times;
|
||||
}
|
||||
|
||||
static void
|
||||
maybe_reschedule_update (ClutterFrameClock *frame_clock)
|
||||
{
|
||||
DeferredTime *head;
|
||||
gboolean cleaned;
|
||||
|
||||
if (frame_clock->pending_reschedule ||
|
||||
frame_clock->timelines)
|
||||
{
|
||||
@ -290,6 +328,20 @@ maybe_reschedule_update (ClutterFrameClock *frame_clock)
|
||||
{
|
||||
clutter_frame_clock_schedule_update (frame_clock);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
cleaned = clean_deferred_times (frame_clock);
|
||||
head = g_queue_peek_head (frame_clock->deferred_times);
|
||||
|
||||
if (head)
|
||||
{
|
||||
clutter_frame_clock_schedule_update_later (frame_clock,
|
||||
head->target_time_us);
|
||||
}
|
||||
else if (cleaned)
|
||||
{
|
||||
clutter_frame_clock_schedule_update (frame_clock);
|
||||
}
|
||||
}
|
||||
|
||||
@ -474,6 +526,7 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock,
|
||||
case CLUTTER_FRAME_CLOCK_STATE_IDLE:
|
||||
case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED:
|
||||
case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW:
|
||||
case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_LATER:
|
||||
g_warn_if_reached ();
|
||||
break;
|
||||
case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING:
|
||||
@ -498,6 +551,7 @@ clutter_frame_clock_notify_ready (ClutterFrameClock *frame_clock)
|
||||
case CLUTTER_FRAME_CLOCK_STATE_IDLE:
|
||||
case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED:
|
||||
case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW:
|
||||
case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_LATER:
|
||||
g_warn_if_reached ();
|
||||
break;
|
||||
case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING:
|
||||
@ -798,6 +852,7 @@ clutter_frame_clock_inhibit (ClutterFrameClock *frame_clock)
|
||||
case CLUTTER_FRAME_CLOCK_STATE_IDLE:
|
||||
break;
|
||||
case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED:
|
||||
case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_LATER:
|
||||
frame_clock->pending_reschedule = TRUE;
|
||||
frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE;
|
||||
break;
|
||||
@ -843,6 +898,7 @@ clutter_frame_clock_schedule_update_now (ClutterFrameClock *frame_clock)
|
||||
case CLUTTER_FRAME_CLOCK_STATE_INIT:
|
||||
case CLUTTER_FRAME_CLOCK_STATE_IDLE:
|
||||
case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED:
|
||||
case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_LATER:
|
||||
break;
|
||||
case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW:
|
||||
return;
|
||||
@ -898,6 +954,7 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock)
|
||||
frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED;
|
||||
return;
|
||||
case CLUTTER_FRAME_CLOCK_STATE_IDLE:
|
||||
case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_LATER:
|
||||
break;
|
||||
case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED:
|
||||
case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW:
|
||||
@ -935,6 +992,113 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock)
|
||||
frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED;
|
||||
}
|
||||
|
||||
static void
|
||||
clutter_frame_clock_schedule_update_later (ClutterFrameClock *frame_clock,
|
||||
int64_t target_us)
|
||||
{
|
||||
int64_t next_update_time_us = -1;
|
||||
int64_t next_presentation_time_us;
|
||||
int64_t next_frame_deadline_us;
|
||||
int64_t ready_time_us = 0, extrapolated_presentation_time_us;
|
||||
int64_t max_render_time_us;
|
||||
int64_t cycles;
|
||||
|
||||
if (frame_clock->inhibit_count > 0)
|
||||
{
|
||||
frame_clock->pending_reschedule = TRUE;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (frame_clock->state)
|
||||
{
|
||||
case CLUTTER_FRAME_CLOCK_STATE_INIT:
|
||||
case CLUTTER_FRAME_CLOCK_STATE_IDLE:
|
||||
case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_LATER:
|
||||
break;
|
||||
case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW:
|
||||
case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED:
|
||||
return;
|
||||
case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING:
|
||||
case CLUTTER_FRAME_CLOCK_STATE_PENDING_PRESENTED:
|
||||
frame_clock->pending_reschedule = TRUE;
|
||||
frame_clock->pending_reschedule_now = TRUE;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (frame_clock->mode)
|
||||
{
|
||||
case CLUTTER_FRAME_CLOCK_MODE_FIXED:
|
||||
calculate_next_update_time_us (frame_clock,
|
||||
&next_update_time_us,
|
||||
&next_presentation_time_us,
|
||||
&next_frame_deadline_us);
|
||||
break;
|
||||
case CLUTTER_FRAME_CLOCK_MODE_VARIABLE:
|
||||
calculate_next_variable_update_time_us (frame_clock,
|
||||
&next_update_time_us,
|
||||
&next_presentation_time_us,
|
||||
&next_frame_deadline_us);
|
||||
break;
|
||||
}
|
||||
|
||||
g_warn_if_fail (next_presentation_time_us != -1);
|
||||
|
||||
if (next_presentation_time_us >= target_us)
|
||||
{
|
||||
clutter_frame_clock_schedule_update (frame_clock);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (frame_clock->mode)
|
||||
{
|
||||
case CLUTTER_FRAME_CLOCK_MODE_FIXED:
|
||||
cycles =
|
||||
(target_us - next_presentation_time_us +
|
||||
frame_clock->refresh_interval_us - 1) /
|
||||
frame_clock->refresh_interval_us;
|
||||
extrapolated_presentation_time_us =
|
||||
next_presentation_time_us + frame_clock->refresh_interval_us * cycles;
|
||||
max_render_time_us = next_presentation_time_us - next_frame_deadline_us;
|
||||
ready_time_us = extrapolated_presentation_time_us - max_render_time_us;
|
||||
break;
|
||||
case CLUTTER_FRAME_CLOCK_MODE_VARIABLE:
|
||||
max_render_time_us =
|
||||
clutter_frame_clock_compute_max_render_time_us (frame_clock);
|
||||
ready_time_us = target_us - max_render_time_us;
|
||||
break;
|
||||
}
|
||||
|
||||
g_source_set_ready_time (frame_clock->source, ready_time_us);
|
||||
frame_clock->pending_reschedule = TRUE;
|
||||
frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_LATER;
|
||||
}
|
||||
|
||||
static int
|
||||
compare_times (const DeferredTime *a,
|
||||
const DeferredTime *b,
|
||||
void *data)
|
||||
{
|
||||
if (a->target_time_us > b->target_time_us)
|
||||
return 1;
|
||||
|
||||
if (a->target_time_us < b->target_time_us)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
clutter_frame_clock_add_future_time (ClutterFrameClock *frame_clock,
|
||||
int64_t when_us)
|
||||
{
|
||||
DeferredTime *time = g_new (DeferredTime, 1);
|
||||
time->target_time_us = when_us;
|
||||
g_queue_insert_sorted (frame_clock->deferred_times, time,
|
||||
(GCompareDataFunc)compare_times, NULL);
|
||||
|
||||
maybe_reschedule_update (frame_clock);
|
||||
}
|
||||
|
||||
void
|
||||
clutter_frame_clock_set_mode (ClutterFrameClock *frame_clock,
|
||||
ClutterFrameClockMode mode)
|
||||
@ -950,6 +1114,7 @@ clutter_frame_clock_set_mode (ClutterFrameClock *frame_clock,
|
||||
case CLUTTER_FRAME_CLOCK_STATE_IDLE:
|
||||
break;
|
||||
case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED:
|
||||
case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_LATER:
|
||||
frame_clock->pending_reschedule = TRUE;
|
||||
frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_IDLE;
|
||||
break;
|
||||
@ -1081,6 +1246,7 @@ clutter_frame_clock_dispatch (ClutterFrameClock *frame_clock,
|
||||
case CLUTTER_FRAME_CLOCK_STATE_IDLE:
|
||||
case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED:
|
||||
case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW:
|
||||
case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_LATER:
|
||||
break;
|
||||
case CLUTTER_FRAME_CLOCK_STATE_DISPATCHING:
|
||||
switch (result)
|
||||
@ -1292,6 +1458,8 @@ clutter_frame_clock_new (float refresh_rate,
|
||||
|
||||
frame_clock->output_name = g_strdup (output_name);
|
||||
|
||||
frame_clock->deferred_times = g_queue_new ();
|
||||
|
||||
return frame_clock;
|
||||
}
|
||||
|
||||
@ -1318,6 +1486,10 @@ clutter_frame_clock_dispose (GObject *object)
|
||||
|
||||
g_clear_pointer (&frame_clock->output_name, g_free);
|
||||
|
||||
if (frame_clock->deferred_times)
|
||||
g_queue_free_full (g_steal_pointer (&frame_clock->deferred_times), g_free);
|
||||
frame_clock->deferred_times = NULL;
|
||||
|
||||
G_OBJECT_CLASS (clutter_frame_clock_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
|
@ -87,6 +87,10 @@ void clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock);
|
||||
CLUTTER_EXPORT
|
||||
void clutter_frame_clock_schedule_update_now (ClutterFrameClock *frame_clock);
|
||||
|
||||
CLUTTER_EXPORT
|
||||
void clutter_frame_clock_add_future_time (ClutterFrameClock *frame_clock,
|
||||
int64_t when_us);
|
||||
|
||||
CLUTTER_EXPORT
|
||||
void clutter_frame_clock_inhibit (ClutterFrameClock *frame_clock);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user