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:
parent
d8606829c4
commit
4810164885
@ -127,7 +127,7 @@ typedef enum
|
||||
struct _ClutterFrameInfo
|
||||
{
|
||||
int64_t frame_counter;
|
||||
int64_t presentation_time;
|
||||
int64_t presentation_time; /* microseconds; CLOCK_MONOTONIC */
|
||||
float refresh_rate;
|
||||
|
||||
ClutterFrameInfoFlag flags;
|
||||
|
@ -47,7 +47,7 @@ struct _CoglFrameInfo
|
||||
CoglObject _parent;
|
||||
|
||||
int64_t frame_counter;
|
||||
int64_t presentation_time_us;
|
||||
int64_t presentation_time_us; /* CLOCK_MONOTONIC */
|
||||
float refresh_rate;
|
||||
|
||||
int64_t global_frame_counter;
|
||||
|
@ -97,11 +97,9 @@ int64_t cogl_frame_info_get_frame_counter (CoglFrameInfo *info);
|
||||
* the frame became visible to the user.
|
||||
*
|
||||
* 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
|
||||
* non-monotonic timestamps being reported when using a drm based
|
||||
* OpenGL driver. Also some buggy Mesa drivers up to 9.0.1 may also
|
||||
* <note>Some buggy Mesa drivers up to 9.0.1 may
|
||||
* incorrectly report non-monotonic timestamps.</note>
|
||||
*
|
||||
* Return value: the presentation time for the frame
|
||||
|
@ -454,6 +454,17 @@ ust_to_microseconds (CoglRenderer *renderer,
|
||||
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
|
||||
_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,
|
||||
0, 1, 0,
|
||||
&ust, &msc, &sbc);
|
||||
info->presentation_time_us = ust_to_microseconds (ctx->display->renderer,
|
||||
drawable,
|
||||
ust);
|
||||
info->flags |= COGL_FRAME_INFO_FLAG_HW_CLOCK;
|
||||
|
||||
if (is_ust_monotonic (ctx->display->renderer, drawable))
|
||||
{
|
||||
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
|
||||
{
|
||||
@ -962,16 +982,20 @@ cogl_onscreen_glx_notify_swap_buffers (CoglOnscreen *onscreen,
|
||||
GLXBufferSwapComplete *swap_event)
|
||||
{
|
||||
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
|
||||
application calls cogl_context_dispatch so instead of immediately
|
||||
notifying we'll set a flag to remember to notify later */
|
||||
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;
|
||||
|
||||
info = cogl_onscreen_peek_head_frame_info (onscreen);
|
||||
|
@ -143,6 +143,12 @@ meta_gpu_kms_get_current_time_ns (MetaGpuKms *gpu_kms)
|
||||
return timespec_to_nanoseconds (&ts);
|
||||
}
|
||||
|
||||
gboolean
|
||||
meta_gpu_kms_is_clock_monotonic (MetaGpuKms *gpu_kms)
|
||||
{
|
||||
return gpu_kms->clock_id == CLOCK_MONOTONIC;
|
||||
}
|
||||
|
||||
gboolean
|
||||
meta_gpu_kms_is_boot_vga (MetaGpuKms *gpu_kms)
|
||||
{
|
||||
|
@ -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);
|
||||
|
||||
gboolean meta_gpu_kms_is_clock_monotonic (MetaGpuKms *gpu_kms);
|
||||
|
||||
void meta_gpu_kms_set_power_save_mode (MetaGpuKms *gpu_kms,
|
||||
uint64_t state,
|
||||
MetaKmsUpdate *kms_update);
|
||||
|
@ -259,15 +259,35 @@ page_flip_feedback_flipped (MetaKmsCrtc *kms_crtc,
|
||||
{
|
||||
MetaRendererView *view = user_data;
|
||||
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) {
|
||||
.tv_sec = tv_sec,
|
||||
.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,
|
||||
timeval_to_microseconds (&page_flip_time),
|
||||
COGL_FRAME_INFO_FLAG_HW_CLOCK);
|
||||
presentation_time_us,
|
||||
flags);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -291,22 +311,18 @@ page_flip_feedback_mode_set_fallback (MetaKmsCrtc *kms_crtc,
|
||||
gpointer user_data)
|
||||
{
|
||||
MetaRendererView *view = user_data;
|
||||
MetaCrtc *crtc;
|
||||
MetaGpuKms *gpu_kms;
|
||||
int64_t now_ns;
|
||||
int64_t now_us;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
crtc = META_CRTC (meta_crtc_kms_from_kms_crtc (kms_crtc));
|
||||
gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (crtc));
|
||||
now_ns = meta_gpu_kms_get_current_time_ns (gpu_kms);
|
||||
now_us = g_get_monotonic_time ();
|
||||
|
||||
notify_view_crtc_presented (view,
|
||||
kms_crtc,
|
||||
ns2us (now_ns),
|
||||
now_us,
|
||||
COGL_FRAME_INFO_FLAG_NONE);
|
||||
}
|
||||
|
||||
@ -316,9 +332,7 @@ page_flip_feedback_discarded (MetaKmsCrtc *kms_crtc,
|
||||
const GError *error)
|
||||
{
|
||||
MetaRendererView *view = user_data;
|
||||
MetaCrtc *crtc;
|
||||
MetaGpuKms *gpu_kms;
|
||||
int64_t now_ns;
|
||||
int64_t now_us;
|
||||
|
||||
/*
|
||||
* 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_warning ("Page flip discarded: %s", error->message);
|
||||
|
||||
crtc = META_CRTC (meta_crtc_kms_from_kms_crtc (kms_crtc));
|
||||
gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (crtc));
|
||||
now_ns = meta_gpu_kms_get_current_time_ns (gpu_kms);
|
||||
now_us = g_get_monotonic_time ();
|
||||
|
||||
notify_view_crtc_presented (view,
|
||||
kms_crtc,
|
||||
ns2us (now_ns),
|
||||
now_us,
|
||||
COGL_FRAME_INFO_FLAG_NONE);
|
||||
}
|
||||
|
||||
|
@ -1033,36 +1033,9 @@ on_presented (ClutterStage *stage,
|
||||
{
|
||||
MetaCompositorPrivate *priv =
|
||||
meta_compositor_get_instance_private (compositor);
|
||||
int64_t presentation_time_cogl = frame_info->presentation_time;
|
||||
int64_t presentation_time;
|
||||
int64_t presentation_time = frame_info->presentation_time;
|
||||
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)
|
||||
{
|
||||
ClutterActor *actor = l->data;
|
||||
|
Loading…
Reference in New Issue
Block a user