Compare commits
10 commits
cd81b7538c
...
29a72bfb0b
Author | SHA1 | Date | |
---|---|---|---|
|
29a72bfb0b | ||
|
d4832ab578 | ||
|
8124914758 | ||
|
bd3b5b64ee | ||
|
17648c4b2d | ||
|
0cb8c3783f | ||
|
87947f7fc5 | ||
|
cc9fdb67ce | ||
|
53888593a8 | ||
|
5fb8b37183 |
6 changed files with 238 additions and 75 deletions
|
@ -42,6 +42,15 @@ enum
|
||||||
|
|
||||||
static guint signals[N_SIGNALS];
|
static guint signals[N_SIGNALS];
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
TRIPLE_BUFFERING_MODE_NEVER,
|
||||||
|
TRIPLE_BUFFERING_MODE_AUTO,
|
||||||
|
TRIPLE_BUFFERING_MODE_ALWAYS,
|
||||||
|
} TripleBufferingMode;
|
||||||
|
|
||||||
|
static TripleBufferingMode triple_buffering_mode = TRIPLE_BUFFERING_MODE_AUTO;
|
||||||
|
|
||||||
#define SYNC_DELAY_FALLBACK_FRACTION 0.875
|
#define SYNC_DELAY_FALLBACK_FRACTION 0.875
|
||||||
|
|
||||||
#define MINIMUM_REFRESH_RATE 30.f
|
#define MINIMUM_REFRESH_RATE 30.f
|
||||||
|
@ -118,6 +127,8 @@ struct _ClutterFrameClock
|
||||||
int64_t last_flip_time_us;
|
int64_t last_flip_time_us;
|
||||||
int64_t prev_last_flip_time_us;
|
int64_t prev_last_flip_time_us;
|
||||||
|
|
||||||
|
ClutterFrameHint last_flip_hints;
|
||||||
|
|
||||||
/* Last time we promoted short-term maximum to long-term one */
|
/* Last time we promoted short-term maximum to long-term one */
|
||||||
int64_t longterm_promotion_us;
|
int64_t longterm_promotion_us;
|
||||||
/* Long-term maximum update duration */
|
/* Long-term maximum update duration */
|
||||||
|
@ -251,10 +262,6 @@ static void
|
||||||
maybe_update_longterm_max_duration_us (ClutterFrameClock *frame_clock,
|
maybe_update_longterm_max_duration_us (ClutterFrameClock *frame_clock,
|
||||||
ClutterFrameInfo *frame_info)
|
ClutterFrameInfo *frame_info)
|
||||||
{
|
{
|
||||||
/* Do not update long-term max if there has been no measurement */
|
|
||||||
if (!frame_clock->shortterm_max_update_duration_us)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if ((frame_info->presentation_time - frame_clock->longterm_promotion_us) <
|
if ((frame_info->presentation_time - frame_clock->longterm_promotion_us) <
|
||||||
G_USEC_PER_SEC)
|
G_USEC_PER_SEC)
|
||||||
return;
|
return;
|
||||||
|
@ -376,8 +383,9 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock,
|
||||||
|
|
||||||
frame_clock->got_measurements_last_frame = FALSE;
|
frame_clock->got_measurements_last_frame = FALSE;
|
||||||
|
|
||||||
if (frame_info->cpu_time_before_buffer_swap_us != 0 &&
|
if ((frame_info->cpu_time_before_buffer_swap_us != 0 &&
|
||||||
frame_info->has_valid_gpu_rendering_duration)
|
frame_info->has_valid_gpu_rendering_duration) ||
|
||||||
|
frame_clock->ever_got_measurements)
|
||||||
{
|
{
|
||||||
int64_t dispatch_to_swap_us, swap_to_rendering_done_us, swap_to_flip_us;
|
int64_t dispatch_to_swap_us, swap_to_rendering_done_us, swap_to_flip_us;
|
||||||
int64_t dispatch_time_us = 0, flip_time_us = 0;
|
int64_t dispatch_time_us = 0, flip_time_us = 0;
|
||||||
|
@ -402,14 +410,21 @@ clutter_frame_clock_notify_presented (ClutterFrameClock *frame_clock,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch_to_swap_us =
|
if (frame_info->cpu_time_before_buffer_swap_us == 0)
|
||||||
frame_info->cpu_time_before_buffer_swap_us -
|
{
|
||||||
dispatch_time_us;
|
/* Cursor-only updates with no "swap" or "flip" */
|
||||||
|
dispatch_to_swap_us = 0;
|
||||||
|
swap_to_flip_us = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dispatch_to_swap_us = frame_info->cpu_time_before_buffer_swap_us -
|
||||||
|
dispatch_time_us;
|
||||||
|
swap_to_flip_us = flip_time_us -
|
||||||
|
frame_info->cpu_time_before_buffer_swap_us;
|
||||||
|
}
|
||||||
swap_to_rendering_done_us =
|
swap_to_rendering_done_us =
|
||||||
frame_info->gpu_rendering_duration_ns / 1000;
|
frame_info->gpu_rendering_duration_ns / 1000;
|
||||||
swap_to_flip_us =
|
|
||||||
flip_time_us -
|
|
||||||
frame_info->cpu_time_before_buffer_swap_us;
|
|
||||||
|
|
||||||
CLUTTER_NOTE (FRAME_TIMINGS,
|
CLUTTER_NOTE (FRAME_TIMINGS,
|
||||||
"%s: update2dispatch %ld µs, dispatch2swap %ld µs, swap2render %ld µs, swap2flip %ld µs",
|
"%s: update2dispatch %ld µs, dispatch2swap %ld µs, swap2render %ld µs, swap2flip %ld µs",
|
||||||
|
@ -503,25 +518,18 @@ clutter_frame_clock_notify_ready (ClutterFrameClock *frame_clock)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int64_t
|
static gboolean
|
||||||
clutter_frame_clock_compute_max_render_time_us (ClutterFrameClock *frame_clock)
|
clutter_frame_clock_compute_max_render_time_us (ClutterFrameClock *frame_clock,
|
||||||
|
int64_t *max_render_time_us)
|
||||||
{
|
{
|
||||||
int64_t refresh_interval_us;
|
int64_t refresh_interval_us;
|
||||||
int64_t max_render_time_us;
|
|
||||||
|
|
||||||
refresh_interval_us = frame_clock->refresh_interval_us;
|
refresh_interval_us = frame_clock->refresh_interval_us;
|
||||||
|
|
||||||
if (!frame_clock->ever_got_measurements ||
|
if (!frame_clock->ever_got_measurements ||
|
||||||
G_UNLIKELY (clutter_paint_debug_flags &
|
G_UNLIKELY (clutter_paint_debug_flags &
|
||||||
CLUTTER_DEBUG_DISABLE_DYNAMIC_MAX_RENDER_TIME))
|
CLUTTER_DEBUG_DISABLE_DYNAMIC_MAX_RENDER_TIME))
|
||||||
{
|
return FALSE;
|
||||||
int64_t ret = refresh_interval_us * SYNC_DELAY_FALLBACK_FRACTION;
|
|
||||||
|
|
||||||
if (frame_clock->state == CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE)
|
|
||||||
ret += refresh_interval_us;
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Max render time shows how early the frame clock needs to be dispatched
|
/* Max render time shows how early the frame clock needs to be dispatched
|
||||||
* to make it to the predicted next presentation time. It is an estimate of
|
* to make it to the predicted next presentation time. It is an estimate of
|
||||||
|
@ -535,15 +543,15 @@ clutter_frame_clock_compute_max_render_time_us (ClutterFrameClock *frame_clock)
|
||||||
* - The duration of vertical blank.
|
* - The duration of vertical blank.
|
||||||
* - A constant to account for variations in the above estimates.
|
* - A constant to account for variations in the above estimates.
|
||||||
*/
|
*/
|
||||||
max_render_time_us =
|
*max_render_time_us =
|
||||||
MAX (frame_clock->longterm_max_update_duration_us,
|
MAX (frame_clock->longterm_max_update_duration_us,
|
||||||
frame_clock->shortterm_max_update_duration_us) +
|
frame_clock->shortterm_max_update_duration_us) +
|
||||||
frame_clock->vblank_duration_us +
|
frame_clock->vblank_duration_us +
|
||||||
clutter_max_render_time_constant_us;
|
clutter_max_render_time_constant_us;
|
||||||
|
|
||||||
max_render_time_us = CLAMP (max_render_time_us, 0, 2 * refresh_interval_us);
|
*max_render_time_us = CLAMP (*max_render_time_us, 0, 2 * refresh_interval_us);
|
||||||
|
|
||||||
return max_render_time_us;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -560,6 +568,7 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock,
|
||||||
int64_t next_presentation_time_us;
|
int64_t next_presentation_time_us;
|
||||||
int64_t next_smooth_presentation_time_us = 0;
|
int64_t next_smooth_presentation_time_us = 0;
|
||||||
int64_t next_update_time_us;
|
int64_t next_update_time_us;
|
||||||
|
gboolean max_render_time_is_known;
|
||||||
|
|
||||||
now_us = g_get_monotonic_time ();
|
now_us = g_get_monotonic_time ();
|
||||||
|
|
||||||
|
@ -579,10 +588,13 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock,
|
||||||
}
|
}
|
||||||
|
|
||||||
min_render_time_allowed_us = refresh_interval_us / 2;
|
min_render_time_allowed_us = refresh_interval_us / 2;
|
||||||
max_render_time_allowed_us =
|
|
||||||
clutter_frame_clock_compute_max_render_time_us (frame_clock);
|
|
||||||
|
|
||||||
if (min_render_time_allowed_us > max_render_time_allowed_us)
|
max_render_time_is_known =
|
||||||
|
clutter_frame_clock_compute_max_render_time_us (frame_clock,
|
||||||
|
&max_render_time_allowed_us);
|
||||||
|
|
||||||
|
if (max_render_time_is_known &&
|
||||||
|
min_render_time_allowed_us > max_render_time_allowed_us)
|
||||||
min_render_time_allowed_us = max_render_time_allowed_us;
|
min_render_time_allowed_us = max_render_time_allowed_us;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -703,6 +715,24 @@ calculate_next_update_time_us (ClutterFrameClock *frame_clock,
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
/* If the max render time isn't known then using the current value of
|
||||||
|
* next_presentation_time_us is suboptimal. Targeting always one frame
|
||||||
|
* prior to that we'd lose the ability to scale up to triple buffering
|
||||||
|
* on late presentation. But targeting two frames prior we would be
|
||||||
|
* always triple buffering even when not required.
|
||||||
|
* So the algorithm for deciding when to scale up to triple buffering
|
||||||
|
* in the absence of render time measurements is to simply target full
|
||||||
|
* frame rate. If we're keeping up then we'll stay double buffering. If
|
||||||
|
* we're not keeping up then this will switch us to triple buffering.
|
||||||
|
*/
|
||||||
|
if (!max_render_time_is_known)
|
||||||
|
{
|
||||||
|
max_render_time_allowed_us =
|
||||||
|
refresh_interval_us * SYNC_DELAY_FALLBACK_FRACTION;
|
||||||
|
next_presentation_time_us =
|
||||||
|
last_presentation_time_us + refresh_interval_us;
|
||||||
|
}
|
||||||
|
|
||||||
while (next_presentation_time_us - min_render_time_allowed_us < now_us)
|
while (next_presentation_time_us - min_render_time_allowed_us < now_us)
|
||||||
next_presentation_time_us += refresh_interval_us;
|
next_presentation_time_us += refresh_interval_us;
|
||||||
|
|
||||||
|
@ -734,7 +764,9 @@ calculate_next_variable_update_time_us (ClutterFrameClock *frame_clock,
|
||||||
|
|
||||||
refresh_interval_us = frame_clock->refresh_interval_us;
|
refresh_interval_us = frame_clock->refresh_interval_us;
|
||||||
|
|
||||||
if (frame_clock->last_presentation_time_us == 0)
|
if (frame_clock->last_presentation_time_us == 0 ||
|
||||||
|
!clutter_frame_clock_compute_max_render_time_us (frame_clock,
|
||||||
|
&max_render_time_allowed_us))
|
||||||
{
|
{
|
||||||
*out_next_update_time_us =
|
*out_next_update_time_us =
|
||||||
frame_clock->last_dispatch_time_us ?
|
frame_clock->last_dispatch_time_us ?
|
||||||
|
@ -747,9 +779,6 @@ calculate_next_variable_update_time_us (ClutterFrameClock *frame_clock,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
max_render_time_allowed_us =
|
|
||||||
clutter_frame_clock_compute_max_render_time_us (frame_clock);
|
|
||||||
|
|
||||||
last_presentation_time_us = frame_clock->last_presentation_time_us;
|
last_presentation_time_us = frame_clock->last_presentation_time_us;
|
||||||
next_presentation_time_us = last_presentation_time_us + refresh_interval_us;
|
next_presentation_time_us = last_presentation_time_us + refresh_interval_us;
|
||||||
|
|
||||||
|
@ -852,6 +881,25 @@ clutter_frame_clock_uninhibit (ClutterFrameClock *frame_clock)
|
||||||
maybe_reschedule_update (frame_clock);
|
maybe_reschedule_update (frame_clock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
want_triple_buffering (ClutterFrameClock *frame_clock)
|
||||||
|
{
|
||||||
|
switch (triple_buffering_mode)
|
||||||
|
{
|
||||||
|
case TRIPLE_BUFFERING_MODE_NEVER:
|
||||||
|
return FALSE;
|
||||||
|
case TRIPLE_BUFFERING_MODE_AUTO:
|
||||||
|
return frame_clock->mode == CLUTTER_FRAME_CLOCK_MODE_FIXED &&
|
||||||
|
!(frame_clock->last_flip_hints &
|
||||||
|
CLUTTER_FRAME_HINT_DIRECT_SCANOUT_ATTEMPTED);
|
||||||
|
case TRIPLE_BUFFERING_MODE_ALWAYS:
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_assert_not_reached ();
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
clutter_frame_clock_schedule_update_now (ClutterFrameClock *frame_clock)
|
clutter_frame_clock_schedule_update_now (ClutterFrameClock *frame_clock)
|
||||||
{
|
{
|
||||||
|
@ -874,12 +922,18 @@ clutter_frame_clock_schedule_update_now (ClutterFrameClock *frame_clock)
|
||||||
case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW:
|
case CLUTTER_FRAME_CLOCK_STATE_SCHEDULED_NOW:
|
||||||
case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW:
|
case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW:
|
||||||
return;
|
return;
|
||||||
case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE:
|
|
||||||
case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED:
|
case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED:
|
||||||
next_update_time_us = g_get_monotonic_time ();
|
|
||||||
frame_clock->state =
|
frame_clock->state =
|
||||||
CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW;
|
CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW;
|
||||||
break;
|
break;
|
||||||
|
case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE:
|
||||||
|
if (want_triple_buffering (frame_clock))
|
||||||
|
{
|
||||||
|
frame_clock->state =
|
||||||
|
CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
G_GNUC_FALLTHROUGH;
|
||||||
case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO:
|
case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO:
|
||||||
frame_clock->pending_reschedule = TRUE;
|
frame_clock->pending_reschedule = TRUE;
|
||||||
frame_clock->pending_reschedule_now = TRUE;
|
frame_clock->pending_reschedule_now = TRUE;
|
||||||
|
@ -915,6 +969,11 @@ void
|
||||||
clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock)
|
clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock)
|
||||||
{
|
{
|
||||||
int64_t next_update_time_us = -1;
|
int64_t next_update_time_us = -1;
|
||||||
|
TripleBufferingMode current_mode = triple_buffering_mode;
|
||||||
|
|
||||||
|
if (current_mode == TRIPLE_BUFFERING_MODE_AUTO &&
|
||||||
|
!want_triple_buffering (frame_clock))
|
||||||
|
current_mode = TRIPLE_BUFFERING_MODE_NEVER;
|
||||||
|
|
||||||
if (frame_clock->inhibit_count > 0)
|
if (frame_clock->inhibit_count > 0)
|
||||||
{
|
{
|
||||||
|
@ -938,13 +997,23 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock)
|
||||||
case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW:
|
case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED_NOW:
|
||||||
return;
|
return;
|
||||||
case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE:
|
case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE:
|
||||||
calculate_next_update_time_us (frame_clock,
|
switch (current_mode)
|
||||||
&next_update_time_us,
|
{
|
||||||
&frame_clock->next_presentation_time_us,
|
case TRIPLE_BUFFERING_MODE_NEVER:
|
||||||
&frame_clock->next_frame_deadline_us);
|
frame_clock->pending_reschedule = TRUE;
|
||||||
frame_clock->is_next_presentation_time_valid =
|
return;
|
||||||
(frame_clock->next_presentation_time_us != 0);
|
case TRIPLE_BUFFERING_MODE_AUTO:
|
||||||
frame_clock->state = CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED;
|
frame_clock->state =
|
||||||
|
CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED;
|
||||||
|
break;
|
||||||
|
case TRIPLE_BUFFERING_MODE_ALWAYS:
|
||||||
|
next_update_time_us = g_get_monotonic_time ();
|
||||||
|
frame_clock->next_presentation_time_us = 0;
|
||||||
|
frame_clock->is_next_presentation_time_valid = FALSE;
|
||||||
|
frame_clock->state =
|
||||||
|
CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_ONE_AND_SCHEDULED;
|
||||||
|
goto got_update_time;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO:
|
case CLUTTER_FRAME_CLOCK_STATE_DISPATCHED_TWO:
|
||||||
frame_clock->pending_reschedule = TRUE;
|
frame_clock->pending_reschedule = TRUE;
|
||||||
|
@ -971,6 +1040,7 @@ clutter_frame_clock_schedule_update (ClutterFrameClock *frame_clock)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
got_update_time:
|
||||||
g_warn_if_fail (next_update_time_us != -1);
|
g_warn_if_fail (next_update_time_us != -1);
|
||||||
|
|
||||||
frame_clock->next_update_time_us = next_update_time_us;
|
frame_clock->next_update_time_us = next_update_time_us;
|
||||||
|
@ -1179,22 +1249,31 @@ frame_clock_source_dispatch (GSource *source,
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
clutter_frame_clock_record_flip_time (ClutterFrameClock *frame_clock,
|
clutter_frame_clock_record_flip (ClutterFrameClock *frame_clock,
|
||||||
int64_t flip_time_us)
|
int64_t flip_time_us,
|
||||||
|
ClutterFrameHint hints)
|
||||||
{
|
{
|
||||||
frame_clock->prev_last_flip_time_us = frame_clock->last_flip_time_us;
|
frame_clock->prev_last_flip_time_us = frame_clock->last_flip_time_us;
|
||||||
frame_clock->last_flip_time_us = flip_time_us;
|
frame_clock->last_flip_time_us = flip_time_us;
|
||||||
|
frame_clock->last_flip_hints = hints;
|
||||||
}
|
}
|
||||||
|
|
||||||
GString *
|
GString *
|
||||||
clutter_frame_clock_get_max_render_time_debug_info (ClutterFrameClock *frame_clock)
|
clutter_frame_clock_get_max_render_time_debug_info (ClutterFrameClock *frame_clock)
|
||||||
{
|
{
|
||||||
|
int64_t max_render_time_us;
|
||||||
int64_t max_update_duration_us;
|
int64_t max_update_duration_us;
|
||||||
GString *string;
|
GString *string;
|
||||||
|
|
||||||
string = g_string_new (NULL);
|
string = g_string_new ("Max render time: ");
|
||||||
g_string_append_printf (string, "Max render time: %ld µs",
|
if (!clutter_frame_clock_compute_max_render_time_us (frame_clock,
|
||||||
clutter_frame_clock_compute_max_render_time_us (frame_clock));
|
&max_render_time_us))
|
||||||
|
{
|
||||||
|
g_string_append (string, "unknown");
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_string_append_printf (string, "%ld µs", max_render_time_us);
|
||||||
|
|
||||||
if (frame_clock->got_measurements_last_frame)
|
if (frame_clock->got_measurements_last_frame)
|
||||||
g_string_append_printf (string, " =");
|
g_string_append_printf (string, " =");
|
||||||
|
@ -1384,6 +1463,15 @@ static void
|
||||||
clutter_frame_clock_class_init (ClutterFrameClockClass *klass)
|
clutter_frame_clock_class_init (ClutterFrameClockClass *klass)
|
||||||
{
|
{
|
||||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
const char *mode_str;
|
||||||
|
|
||||||
|
mode_str = g_getenv ("MUTTER_DEBUG_TRIPLE_BUFFERING");
|
||||||
|
if (!g_strcmp0 (mode_str, "never"))
|
||||||
|
triple_buffering_mode = TRIPLE_BUFFERING_MODE_NEVER;
|
||||||
|
else if (!g_strcmp0 (mode_str, "auto"))
|
||||||
|
triple_buffering_mode = TRIPLE_BUFFERING_MODE_AUTO;
|
||||||
|
else if (!g_strcmp0 (mode_str, "always"))
|
||||||
|
triple_buffering_mode = TRIPLE_BUFFERING_MODE_ALWAYS;
|
||||||
|
|
||||||
object_class->dispose = clutter_frame_clock_dispose;
|
object_class->dispose = clutter_frame_clock_dispose;
|
||||||
|
|
||||||
|
|
|
@ -108,7 +108,8 @@ void clutter_frame_clock_remove_timeline (ClutterFrameClock *frame_clock,
|
||||||
CLUTTER_EXPORT
|
CLUTTER_EXPORT
|
||||||
float clutter_frame_clock_get_refresh_rate (ClutterFrameClock *frame_clock);
|
float clutter_frame_clock_get_refresh_rate (ClutterFrameClock *frame_clock);
|
||||||
|
|
||||||
void clutter_frame_clock_record_flip_time (ClutterFrameClock *frame_clock,
|
void clutter_frame_clock_record_flip (ClutterFrameClock *frame_clock,
|
||||||
int64_t flip_time_us);
|
int64_t flip_time_us,
|
||||||
|
ClutterFrameHint hints);
|
||||||
|
|
||||||
GString * clutter_frame_clock_get_max_render_time_debug_info (ClutterFrameClock *frame_clock);
|
GString * clutter_frame_clock_get_max_render_time_debug_info (ClutterFrameClock *frame_clock);
|
||||||
|
|
|
@ -898,14 +898,21 @@ handle_frame_clock_frame (ClutterFrameClock *frame_clock,
|
||||||
|
|
||||||
_clutter_stage_window_redraw_view (stage_window, view, frame);
|
_clutter_stage_window_redraw_view (stage_window, view, frame);
|
||||||
|
|
||||||
clutter_frame_clock_record_flip_time (frame_clock,
|
clutter_frame_clock_record_flip (frame_clock,
|
||||||
g_get_monotonic_time ());
|
g_get_monotonic_time (),
|
||||||
|
clutter_frame_get_hints (frame));
|
||||||
|
|
||||||
clutter_stage_emit_after_paint (stage, view, frame);
|
clutter_stage_emit_after_paint (stage, view, frame);
|
||||||
|
|
||||||
if (_clutter_context_get_show_fps ())
|
if (_clutter_context_get_show_fps ())
|
||||||
end_frame_timing_measurement (view);
|
end_frame_timing_measurement (view);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
clutter_frame_clock_record_flip (frame_clock,
|
||||||
|
g_get_monotonic_time (),
|
||||||
|
clutter_frame_get_hints (frame));
|
||||||
|
}
|
||||||
|
|
||||||
_clutter_stage_window_finish_frame (stage_window, view, frame);
|
_clutter_stage_window_finish_frame (stage_window, view, frame);
|
||||||
|
|
||||||
|
|
|
@ -1586,9 +1586,11 @@ meta_kms_impl_device_handle_update (MetaKmsImplDevice *impl_device,
|
||||||
meta_kms_update_merge_from (crtc_frame->pending_update, update);
|
meta_kms_update_merge_from (crtc_frame->pending_update, update);
|
||||||
meta_kms_update_free (update);
|
meta_kms_update_free (update);
|
||||||
update = g_steal_pointer (&crtc_frame->pending_update);
|
update = g_steal_pointer (&crtc_frame->pending_update);
|
||||||
disarm_crtc_frame_deadline_timer (crtc_frame);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (crtc_frame->deadline.armed)
|
||||||
|
disarm_crtc_frame_deadline_timer (crtc_frame);
|
||||||
|
|
||||||
meta_kms_device_handle_flush (priv->device, latch_crtc);
|
meta_kms_device_handle_flush (priv->device, latch_crtc);
|
||||||
|
|
||||||
feedback = do_process (impl_device, latch_crtc, update, flags);
|
feedback = do_process (impl_device, latch_crtc, update, flags);
|
||||||
|
|
|
@ -1525,6 +1525,7 @@ try_post_latest_swap (CoglOnscreen *onscreen)
|
||||||
MetaFrameNative *frame_native;
|
MetaFrameNative *frame_native;
|
||||||
|
|
||||||
if (onscreen_native->next_post.frame == NULL ||
|
if (onscreen_native->next_post.frame == NULL ||
|
||||||
|
onscreen_native->view == NULL ||
|
||||||
meta_kms_is_shutting_down (kms))
|
meta_kms_is_shutting_down (kms))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -1898,11 +1899,7 @@ meta_onscreen_native_before_redraw (CoglOnscreen *onscreen,
|
||||||
ClutterFrame *frame)
|
ClutterFrame *frame)
|
||||||
{
|
{
|
||||||
MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
|
MetaOnscreenNative *onscreen_native = META_ONSCREEN_NATIVE (onscreen);
|
||||||
MetaCrtcKms *crtc_kms = META_CRTC_KMS (onscreen_native->crtc);
|
|
||||||
MetaKmsCrtc *kms_crtc = meta_crtc_kms_get_kms_crtc (crtc_kms);
|
|
||||||
|
|
||||||
meta_kms_device_await_flush (meta_kms_crtc_get_device (kms_crtc),
|
|
||||||
kms_crtc);
|
|
||||||
maybe_update_frame_sync (onscreen_native, frame);
|
maybe_update_frame_sync (onscreen_native, frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,8 @@
|
||||||
#include "tests/meta-wayland-test-driver.h"
|
#include "tests/meta-wayland-test-driver.h"
|
||||||
#include "tests/meta-wayland-test-utils.h"
|
#include "tests/meta-wayland-test-utils.h"
|
||||||
|
|
||||||
|
#define N_FRAMES_PER_TEST 30
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
int number_of_frames_left;
|
int number_of_frames_left;
|
||||||
|
@ -46,12 +48,15 @@ typedef struct
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
int n_paints;
|
int n_paints;
|
||||||
uint32_t fb_id;
|
int n_presentations;
|
||||||
|
int n_direct_scanouts;
|
||||||
|
GList *fb_ids;
|
||||||
} scanout;
|
} scanout;
|
||||||
|
|
||||||
gboolean wait_for_scanout;
|
gboolean wait_for_scanout;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
int scanouts_attempted;
|
||||||
gboolean scanout_sabotaged;
|
gboolean scanout_sabotaged;
|
||||||
gboolean fallback_painted;
|
gboolean fallback_painted;
|
||||||
guint repaint_guard_id;
|
guint repaint_guard_id;
|
||||||
|
@ -101,7 +106,7 @@ meta_test_kms_render_basic (void)
|
||||||
gulong handler_id;
|
gulong handler_id;
|
||||||
|
|
||||||
test = (KmsRenderingTest) {
|
test = (KmsRenderingTest) {
|
||||||
.number_of_frames_left = 10,
|
.number_of_frames_left = N_FRAMES_PER_TEST,
|
||||||
.loop = g_main_loop_new (NULL, FALSE),
|
.loop = g_main_loop_new (NULL, FALSE),
|
||||||
};
|
};
|
||||||
handler_id = g_signal_connect (stage, "after-update",
|
handler_id = g_signal_connect (stage, "after-update",
|
||||||
|
@ -123,7 +128,6 @@ on_scanout_before_update (ClutterStage *stage,
|
||||||
KmsRenderingTest *test)
|
KmsRenderingTest *test)
|
||||||
{
|
{
|
||||||
test->scanout.n_paints = 0;
|
test->scanout.n_paints = 0;
|
||||||
test->scanout.fb_id = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -135,6 +139,7 @@ on_scanout_before_paint (ClutterStage *stage,
|
||||||
CoglScanout *scanout;
|
CoglScanout *scanout;
|
||||||
CoglScanoutBuffer *scanout_buffer;
|
CoglScanoutBuffer *scanout_buffer;
|
||||||
MetaDrmBuffer *buffer;
|
MetaDrmBuffer *buffer;
|
||||||
|
uint32_t fb_id;
|
||||||
|
|
||||||
scanout = clutter_stage_view_peek_scanout (stage_view);
|
scanout = clutter_stage_view_peek_scanout (stage_view);
|
||||||
if (!scanout)
|
if (!scanout)
|
||||||
|
@ -143,8 +148,13 @@ on_scanout_before_paint (ClutterStage *stage,
|
||||||
scanout_buffer = cogl_scanout_get_buffer (scanout);
|
scanout_buffer = cogl_scanout_get_buffer (scanout);
|
||||||
g_assert_true (META_IS_DRM_BUFFER (scanout_buffer));
|
g_assert_true (META_IS_DRM_BUFFER (scanout_buffer));
|
||||||
buffer = META_DRM_BUFFER (scanout_buffer);
|
buffer = META_DRM_BUFFER (scanout_buffer);
|
||||||
test->scanout.fb_id = meta_drm_buffer_get_fb_id (buffer);
|
fb_id = meta_drm_buffer_get_fb_id (buffer);
|
||||||
g_assert_cmpuint (test->scanout.fb_id, >, 0);
|
g_assert_cmpuint (fb_id, >, 0);
|
||||||
|
test->scanout.fb_ids = g_list_append (test->scanout.fb_ids,
|
||||||
|
GUINT_TO_POINTER (fb_id));
|
||||||
|
|
||||||
|
/* Triple buffering, but no higher */
|
||||||
|
g_assert_cmpuint (g_list_length (test->scanout.fb_ids), <=, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -173,12 +183,12 @@ on_scanout_presented (ClutterStage *stage,
|
||||||
MetaDeviceFile *device_file;
|
MetaDeviceFile *device_file;
|
||||||
GError *error = NULL;
|
GError *error = NULL;
|
||||||
drmModeCrtc *drm_crtc;
|
drmModeCrtc *drm_crtc;
|
||||||
|
uint32_t first_fb_id_expected;
|
||||||
|
|
||||||
if (test->wait_for_scanout && test->scanout.n_paints > 0)
|
if (test->wait_for_scanout && test->scanout.fb_ids == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (test->wait_for_scanout && test->scanout.fb_id == 0)
|
test->scanout.n_presentations++;
|
||||||
return;
|
|
||||||
|
|
||||||
device_pool = meta_backend_native_get_device_pool (backend_native);
|
device_pool = meta_backend_native_get_device_pool (backend_native);
|
||||||
|
|
||||||
|
@ -197,15 +207,41 @@ on_scanout_presented (ClutterStage *stage,
|
||||||
drm_crtc = drmModeGetCrtc (meta_device_file_get_fd (device_file),
|
drm_crtc = drmModeGetCrtc (meta_device_file_get_fd (device_file),
|
||||||
meta_kms_crtc_get_id (kms_crtc));
|
meta_kms_crtc_get_id (kms_crtc));
|
||||||
g_assert_nonnull (drm_crtc);
|
g_assert_nonnull (drm_crtc);
|
||||||
if (test->scanout.fb_id == 0)
|
|
||||||
g_assert_cmpuint (drm_crtc->buffer_id, !=, test->scanout.fb_id);
|
if (test->scanout.fb_ids)
|
||||||
|
{
|
||||||
|
test->scanout.n_direct_scanouts++;
|
||||||
|
first_fb_id_expected = GPOINTER_TO_UINT (test->scanout.fb_ids->data);
|
||||||
|
test->scanout.fb_ids = g_list_delete_link (test->scanout.fb_ids,
|
||||||
|
test->scanout.fb_ids);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
g_assert_cmpuint (drm_crtc->buffer_id, ==, test->scanout.fb_id);
|
{
|
||||||
|
first_fb_id_expected = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The buffer ID won't match on the first frame because switching from
|
||||||
|
* triple buffered compositing to double buffered direct scanout takes
|
||||||
|
* an extra frame to drain the queue. Thereafter we are in direct scanout
|
||||||
|
* mode and expect the buffer IDs to match.
|
||||||
|
*/
|
||||||
|
if (test->scanout.n_presentations > 1)
|
||||||
|
{
|
||||||
|
if (first_fb_id_expected == 0)
|
||||||
|
g_assert_cmpuint (drm_crtc->buffer_id, !=, first_fb_id_expected);
|
||||||
|
else
|
||||||
|
g_assert_cmpuint (drm_crtc->buffer_id, ==, first_fb_id_expected);
|
||||||
|
}
|
||||||
|
|
||||||
drmModeFreeCrtc (drm_crtc);
|
drmModeFreeCrtc (drm_crtc);
|
||||||
|
|
||||||
meta_device_file_release (device_file);
|
meta_device_file_release (device_file);
|
||||||
|
|
||||||
g_main_loop_quit (test->loop);
|
test->number_of_frames_left--;
|
||||||
|
if (test->number_of_frames_left <= 0)
|
||||||
|
g_main_loop_quit (test->loop);
|
||||||
|
else
|
||||||
|
clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
|
@ -244,7 +280,9 @@ meta_test_kms_render_client_scanout (void)
|
||||||
g_assert_nonnull (wayland_test_client);
|
g_assert_nonnull (wayland_test_client);
|
||||||
|
|
||||||
test = (KmsRenderingTest) {
|
test = (KmsRenderingTest) {
|
||||||
|
.number_of_frames_left = N_FRAMES_PER_TEST,
|
||||||
.loop = g_main_loop_new (NULL, FALSE),
|
.loop = g_main_loop_new (NULL, FALSE),
|
||||||
|
.scanout = {0},
|
||||||
.wait_for_scanout = TRUE,
|
.wait_for_scanout = TRUE,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -270,7 +308,8 @@ meta_test_kms_render_client_scanout (void)
|
||||||
clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
|
clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
|
||||||
g_main_loop_run (test.loop);
|
g_main_loop_run (test.loop);
|
||||||
|
|
||||||
g_assert_cmpuint (test.scanout.fb_id, >, 0);
|
g_assert_cmpint (test.scanout.n_presentations, ==, N_FRAMES_PER_TEST);
|
||||||
|
g_assert_cmpint (test.scanout.n_direct_scanouts, ==, N_FRAMES_PER_TEST);
|
||||||
|
|
||||||
g_debug ("Unmake fullscreen");
|
g_debug ("Unmake fullscreen");
|
||||||
window = meta_find_window_from_title (test_context, "dma-buf-scanout-test");
|
window = meta_find_window_from_title (test_context, "dma-buf-scanout-test");
|
||||||
|
@ -292,10 +331,15 @@ meta_test_kms_render_client_scanout (void)
|
||||||
g_assert_cmpint (buffer_rect.y, ==, 10);
|
g_assert_cmpint (buffer_rect.y, ==, 10);
|
||||||
|
|
||||||
test.wait_for_scanout = FALSE;
|
test.wait_for_scanout = FALSE;
|
||||||
|
test.number_of_frames_left = N_FRAMES_PER_TEST;
|
||||||
|
test.scanout.n_presentations = 0;
|
||||||
|
test.scanout.n_direct_scanouts = 0;
|
||||||
|
|
||||||
clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
|
clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
|
||||||
g_main_loop_run (test.loop);
|
g_main_loop_run (test.loop);
|
||||||
|
|
||||||
g_assert_cmpuint (test.scanout.fb_id, ==, 0);
|
g_assert_cmpint (test.scanout.n_presentations, ==, N_FRAMES_PER_TEST);
|
||||||
|
g_assert_cmpint (test.scanout.n_direct_scanouts, ==, 0);
|
||||||
|
|
||||||
g_debug ("Moving back to 0, 0");
|
g_debug ("Moving back to 0, 0");
|
||||||
meta_window_move_frame (window, TRUE, 0, 0);
|
meta_window_move_frame (window, TRUE, 0, 0);
|
||||||
|
@ -307,10 +351,15 @@ meta_test_kms_render_client_scanout (void)
|
||||||
g_assert_cmpint (buffer_rect.y, ==, 0);
|
g_assert_cmpint (buffer_rect.y, ==, 0);
|
||||||
|
|
||||||
test.wait_for_scanout = TRUE;
|
test.wait_for_scanout = TRUE;
|
||||||
|
test.number_of_frames_left = N_FRAMES_PER_TEST;
|
||||||
|
test.scanout.n_presentations = 0;
|
||||||
|
test.scanout.n_direct_scanouts = 0;
|
||||||
|
|
||||||
clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
|
clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
|
||||||
g_main_loop_run (test.loop);
|
g_main_loop_run (test.loop);
|
||||||
|
|
||||||
g_assert_cmpuint (test.scanout.fb_id, >, 0);
|
g_assert_cmpint (test.scanout.n_presentations, ==, N_FRAMES_PER_TEST);
|
||||||
|
g_assert_cmpint (test.scanout.n_direct_scanouts, ==, N_FRAMES_PER_TEST);
|
||||||
|
|
||||||
g_signal_handler_disconnect (stage, before_update_handler_id);
|
g_signal_handler_disconnect (stage, before_update_handler_id);
|
||||||
g_signal_handler_disconnect (stage, before_paint_handler_id);
|
g_signal_handler_disconnect (stage, before_paint_handler_id);
|
||||||
|
@ -364,6 +413,15 @@ on_scanout_fallback_before_paint (ClutterStage *stage,
|
||||||
if (!scanout)
|
if (!scanout)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
test->scanout_fallback.scanouts_attempted++;
|
||||||
|
|
||||||
|
/* The first scanout candidate frame will get composited due to triple
|
||||||
|
* buffering draining the queue to drop to double buffering. So don't
|
||||||
|
* sabotage that first frame.
|
||||||
|
*/
|
||||||
|
if (test->scanout_fallback.scanouts_attempted < 2)
|
||||||
|
return;
|
||||||
|
|
||||||
g_assert_false (test->scanout_fallback.scanout_sabotaged);
|
g_assert_false (test->scanout_fallback.scanout_sabotaged);
|
||||||
|
|
||||||
if (is_atomic_mode_setting (kms_device))
|
if (is_atomic_mode_setting (kms_device))
|
||||||
|
@ -401,6 +459,15 @@ on_scanout_fallback_paint_view (ClutterStage *stage,
|
||||||
g_clear_handle_id (&test->scanout_fallback.repaint_guard_id,
|
g_clear_handle_id (&test->scanout_fallback.repaint_guard_id,
|
||||||
g_source_remove);
|
g_source_remove);
|
||||||
test->scanout_fallback.fallback_painted = TRUE;
|
test->scanout_fallback.fallback_painted = TRUE;
|
||||||
|
test->scanout_fallback.scanout_sabotaged = FALSE;
|
||||||
|
}
|
||||||
|
else if (test->scanout_fallback.scanouts_attempted == 1)
|
||||||
|
{
|
||||||
|
/* Now that we've seen the first scanout attempt that was inhibited by
|
||||||
|
* triple buffering, try a second frame. The second one should scanout
|
||||||
|
* and will be sabotaged.
|
||||||
|
*/
|
||||||
|
clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -410,11 +477,11 @@ on_scanout_fallback_presented (ClutterStage *stage,
|
||||||
ClutterFrameInfo *frame_info,
|
ClutterFrameInfo *frame_info,
|
||||||
KmsRenderingTest *test)
|
KmsRenderingTest *test)
|
||||||
{
|
{
|
||||||
if (!test->scanout_fallback.scanout_sabotaged)
|
if (test->scanout_fallback.fallback_painted)
|
||||||
return;
|
g_main_loop_quit (test->loop);
|
||||||
|
|
||||||
g_assert_true (test->scanout_fallback.fallback_painted);
|
test->number_of_frames_left--;
|
||||||
g_main_loop_quit (test->loop);
|
g_assert_cmpint (test->number_of_frames_left, >, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -443,6 +510,7 @@ meta_test_kms_render_client_scanout_fallback (void)
|
||||||
g_assert_nonnull (wayland_test_client);
|
g_assert_nonnull (wayland_test_client);
|
||||||
|
|
||||||
test = (KmsRenderingTest) {
|
test = (KmsRenderingTest) {
|
||||||
|
.number_of_frames_left = N_FRAMES_PER_TEST,
|
||||||
.loop = g_main_loop_new (NULL, FALSE),
|
.loop = g_main_loop_new (NULL, FALSE),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue