cogl: Make presentation time always MONOTONIC

This concerns only the cases when the presentation timestamp is received
directly from the device (from KMS or from GLX). In the majority of
cases this timestamp is already MONOTONIC. When it isn't, after this
commit, the current value of the MONOTONIC clock is sampled instead.

The alternative is to store the clock id alongside the timestamp, with
possible values of MONOTONIC, REALTIME (from KMS) and GETTIMEOFDAY (from
GLX; this might be the same as REALTIME, I'm not sure), and then
"convert" the timestamp to MONOTONIC when needed. An example of such a
conversion was done in compositor.c (removed in this commit). It would
also be needed for the presentation-time Wayland protocol. However, it
seems that the vast majority of up-to-date systems are using MONOTONIC
anyway, making this effort not justified.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1484>
This commit is contained in:
Ivan Molodetskikh 2021-01-30 23:14:55 +03:00 committed by Marge Bot
parent d8606829c4
commit 4810164885
8 changed files with 72 additions and 57 deletions

View File

@ -127,7 +127,7 @@ typedef enum
struct _ClutterFrameInfo struct _ClutterFrameInfo
{ {
int64_t frame_counter; int64_t frame_counter;
int64_t presentation_time; int64_t presentation_time; /* microseconds; CLOCK_MONOTONIC */
float refresh_rate; float refresh_rate;
ClutterFrameInfoFlag flags; ClutterFrameInfoFlag flags;

View File

@ -47,7 +47,7 @@ struct _CoglFrameInfo
CoglObject _parent; CoglObject _parent;
int64_t frame_counter; int64_t frame_counter;
int64_t presentation_time_us; int64_t presentation_time_us; /* CLOCK_MONOTONIC */
float refresh_rate; float refresh_rate;
int64_t global_frame_counter; int64_t global_frame_counter;

View File

@ -97,11 +97,9 @@ int64_t cogl_frame_info_get_frame_counter (CoglFrameInfo *info);
* the frame became visible to the user. * the frame became visible to the user.
* *
* The presentation time measured in microseconds, is based on * The presentation time measured in microseconds, is based on
* cogl_get_clock_time(). * CLOCK_MONOTONIC.
* *
* <note>Linux kernel version less that 3.8 can result in * <note>Some buggy Mesa drivers up to 9.0.1 may
* non-monotonic timestamps being reported when using a drm based
* OpenGL driver. Also some buggy Mesa drivers up to 9.0.1 may also
* incorrectly report non-monotonic timestamps.</note> * incorrectly report non-monotonic timestamps.</note>
* *
* Return value: the presentation time for the frame * Return value: the presentation time for the frame

View File

@ -454,6 +454,17 @@ ust_to_microseconds (CoglRenderer *renderer,
return 0; return 0;
} }
static gboolean
is_ust_monotonic (CoglRenderer *renderer,
GLXDrawable drawable)
{
CoglGLXRenderer *glx_renderer = renderer->winsys;
ensure_ust_type (renderer, drawable);
return (glx_renderer->ust_type == COGL_GLX_UST_IS_MONOTONIC_TIME);
}
static void static void
_cogl_winsys_wait_for_vblank (CoglOnscreen *onscreen) _cogl_winsys_wait_for_vblank (CoglOnscreen *onscreen)
{ {
@ -482,10 +493,19 @@ _cogl_winsys_wait_for_vblank (CoglOnscreen *onscreen)
glx_renderer->glXWaitForMsc (xlib_renderer->xdpy, drawable, glx_renderer->glXWaitForMsc (xlib_renderer->xdpy, drawable,
0, 1, 0, 0, 1, 0,
&ust, &msc, &sbc); &ust, &msc, &sbc);
info->presentation_time_us = ust_to_microseconds (ctx->display->renderer,
drawable, if (is_ust_monotonic (ctx->display->renderer, drawable))
ust); {
info->flags |= COGL_FRAME_INFO_FLAG_HW_CLOCK; info->presentation_time_us =
ust_to_microseconds (ctx->display->renderer,
drawable,
ust);
info->flags |= COGL_FRAME_INFO_FLAG_HW_CLOCK;
}
else
{
info->presentation_time_us = g_get_monotonic_time ();
}
} }
else else
{ {
@ -962,16 +982,20 @@ cogl_onscreen_glx_notify_swap_buffers (CoglOnscreen *onscreen,
GLXBufferSwapComplete *swap_event) GLXBufferSwapComplete *swap_event)
{ {
CoglOnscreenGlx *onscreen_glx = COGL_ONSCREEN_GLX (onscreen); CoglOnscreenGlx *onscreen_glx = COGL_ONSCREEN_GLX (onscreen);
CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
CoglContext *context = cogl_framebuffer_get_context (framebuffer);
gboolean ust_is_monotonic;
/* We only want to notify that the swap is complete when the /* We only want to notify that the swap is complete when the
application calls cogl_context_dispatch so instead of immediately application calls cogl_context_dispatch so instead of immediately
notifying we'll set a flag to remember to notify later */ notifying we'll set a flag to remember to notify later */
set_sync_pending (onscreen); set_sync_pending (onscreen);
if (swap_event->ust != 0) ust_is_monotonic = is_ust_monotonic (context->display->renderer,
onscreen_glx->glxwin);
if (swap_event->ust != 0 && ust_is_monotonic)
{ {
CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
CoglContext *context = cogl_framebuffer_get_context (framebuffer);
CoglFrameInfo *info; CoglFrameInfo *info;
info = cogl_onscreen_peek_head_frame_info (onscreen); info = cogl_onscreen_peek_head_frame_info (onscreen);

View File

@ -143,6 +143,12 @@ meta_gpu_kms_get_current_time_ns (MetaGpuKms *gpu_kms)
return timespec_to_nanoseconds (&ts); return timespec_to_nanoseconds (&ts);
} }
gboolean
meta_gpu_kms_is_clock_monotonic (MetaGpuKms *gpu_kms)
{
return gpu_kms->clock_id == CLOCK_MONOTONIC;
}
gboolean gboolean
meta_gpu_kms_is_boot_vga (MetaGpuKms *gpu_kms) meta_gpu_kms_is_boot_vga (MetaGpuKms *gpu_kms)
{ {

View File

@ -59,6 +59,8 @@ const char * meta_gpu_kms_get_file_path (MetaGpuKms *gpu_kms);
int64_t meta_gpu_kms_get_current_time_ns (MetaGpuKms *gpu_kms); int64_t meta_gpu_kms_get_current_time_ns (MetaGpuKms *gpu_kms);
gboolean meta_gpu_kms_is_clock_monotonic (MetaGpuKms *gpu_kms);
void meta_gpu_kms_set_power_save_mode (MetaGpuKms *gpu_kms, void meta_gpu_kms_set_power_save_mode (MetaGpuKms *gpu_kms,
uint64_t state, uint64_t state,
MetaKmsUpdate *kms_update); MetaKmsUpdate *kms_update);

View File

@ -259,15 +259,35 @@ page_flip_feedback_flipped (MetaKmsCrtc *kms_crtc,
{ {
MetaRendererView *view = user_data; MetaRendererView *view = user_data;
struct timeval page_flip_time; struct timeval page_flip_time;
MetaCrtc *crtc;
MetaGpuKms *gpu_kms;
int64_t presentation_time_us;
CoglFrameInfoFlag flags = COGL_FRAME_INFO_FLAG_NONE;
page_flip_time = (struct timeval) { page_flip_time = (struct timeval) {
.tv_sec = tv_sec, .tv_sec = tv_sec,
.tv_usec = tv_usec, .tv_usec = tv_usec,
}; };
crtc = META_CRTC (meta_crtc_kms_from_kms_crtc (kms_crtc));
gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (crtc));
if (meta_gpu_kms_is_clock_monotonic (gpu_kms))
{
presentation_time_us = timeval_to_microseconds (&page_flip_time);
flags |= COGL_FRAME_INFO_FLAG_HW_CLOCK;
}
else
{
/*
* Other parts of the code assume MONOTONIC timestamps. So, if the device
* timestamp isn't MONOTONIC, don't use it.
*/
presentation_time_us = g_get_monotonic_time ();
}
notify_view_crtc_presented (view, kms_crtc, notify_view_crtc_presented (view, kms_crtc,
timeval_to_microseconds (&page_flip_time), presentation_time_us,
COGL_FRAME_INFO_FLAG_HW_CLOCK); flags);
} }
static void static void
@ -291,22 +311,18 @@ page_flip_feedback_mode_set_fallback (MetaKmsCrtc *kms_crtc,
gpointer user_data) gpointer user_data)
{ {
MetaRendererView *view = user_data; MetaRendererView *view = user_data;
MetaCrtc *crtc; int64_t now_us;
MetaGpuKms *gpu_kms;
int64_t now_ns;
/* /*
* We ended up not page flipping, thus we don't have a presentation time to * We ended up not page flipping, thus we don't have a presentation time to
* use. Lets use the next best thing: the current time. * use. Lets use the next best thing: the current time.
*/ */
crtc = META_CRTC (meta_crtc_kms_from_kms_crtc (kms_crtc)); now_us = g_get_monotonic_time ();
gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (crtc));
now_ns = meta_gpu_kms_get_current_time_ns (gpu_kms);
notify_view_crtc_presented (view, notify_view_crtc_presented (view,
kms_crtc, kms_crtc,
ns2us (now_ns), now_us,
COGL_FRAME_INFO_FLAG_NONE); COGL_FRAME_INFO_FLAG_NONE);
} }
@ -316,9 +332,7 @@ page_flip_feedback_discarded (MetaKmsCrtc *kms_crtc,
const GError *error) const GError *error)
{ {
MetaRendererView *view = user_data; MetaRendererView *view = user_data;
MetaCrtc *crtc; int64_t now_us;
MetaGpuKms *gpu_kms;
int64_t now_ns;
/* /*
* Page flipping failed, but we want to fail gracefully, so to avoid freezing * Page flipping failed, but we want to fail gracefully, so to avoid freezing
@ -331,13 +345,11 @@ page_flip_feedback_discarded (MetaKmsCrtc *kms_crtc,
G_IO_ERROR_PERMISSION_DENIED)) G_IO_ERROR_PERMISSION_DENIED))
g_warning ("Page flip discarded: %s", error->message); g_warning ("Page flip discarded: %s", error->message);
crtc = META_CRTC (meta_crtc_kms_from_kms_crtc (kms_crtc)); now_us = g_get_monotonic_time ();
gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (crtc));
now_ns = meta_gpu_kms_get_current_time_ns (gpu_kms);
notify_view_crtc_presented (view, notify_view_crtc_presented (view,
kms_crtc, kms_crtc,
ns2us (now_ns), now_us,
COGL_FRAME_INFO_FLAG_NONE); COGL_FRAME_INFO_FLAG_NONE);
} }

View File

@ -1033,36 +1033,9 @@ on_presented (ClutterStage *stage,
{ {
MetaCompositorPrivate *priv = MetaCompositorPrivate *priv =
meta_compositor_get_instance_private (compositor); meta_compositor_get_instance_private (compositor);
int64_t presentation_time_cogl = frame_info->presentation_time; int64_t presentation_time = frame_info->presentation_time;
int64_t presentation_time;
GList *l; GList *l;
if (presentation_time_cogl != 0)
{
int64_t current_cogl_time;
int64_t current_monotonic_time;
/* Cogl reports presentation in terms of its own clock, which is
* guaranteed to be in nanoseconds but with no specified base. The
* normal case with the open source GPU drivers on Linux 3.8 and
* newer is that the base of cogl_get_clock_time() is that of
* clock_gettime(CLOCK_MONOTONIC), so the same as g_get_monotonic_time),
* but there's no exposure of that through the API. clock_gettime()
* is fairly fast, so calling it twice and subtracting to get a
* nearly-zero number is acceptable, if a little ugly.
*/
current_cogl_time = cogl_get_clock_time (priv->context);
current_monotonic_time = g_get_monotonic_time ();
presentation_time =
current_monotonic_time +
(presentation_time_cogl - current_cogl_time) / 1000;
}
else
{
presentation_time = 0;
}
for (l = priv->windows; l; l = l->next) for (l = priv->windows; l; l = l->next)
{ {
ClutterActor *actor = l->data; ClutterActor *actor = l->data;