From d20697ff5972b363679597372d1a98ff3074085c Mon Sep 17 00:00:00 2001 From: Daniel van Vugt Date: Wed, 27 Jun 2018 17:19:27 +0800 Subject: [PATCH 1/2] renderer-native: Add hardware presentation timing Add support for getting hardware presentation times from KMS (Wayland sessions). Also implement cogl_get_clock_time which is required to compare and judge the age of presentation timestamps. For single monitor systems this is straightforward. For multi-monitor systems though we have to choose a display to sync to. The compositor already partially solves this for us in the case of only one display updating because it will only use the subset of monitors that are changing. In the case of multiple monitors consuming the same frame concurrently however, we choose the fastest one (in use at the time). Note however that we also need !73 to land in order to fully realize multiple monitors running at full speed. --- src/Makefile.am | 25 +++++++- src/backends/native/meta-gpu-kms.c | 69 ++++++++++++++++++++-- src/backends/native/meta-gpu-kms.h | 3 + src/backends/native/meta-renderer-native.c | 38 +++++++++++- src/meta-marshal.list | 1 + 5 files changed, 128 insertions(+), 8 deletions(-) create mode 100644 src/meta-marshal.list diff --git a/src/Makefile.am b/src/Makefile.am index 811e2b86b..56d539836 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -51,6 +51,8 @@ mutter_built_sources = \ $(dbus_login1_built_sources) \ meta/meta-enum-types.h \ meta-enum-types.c \ + meta-marshal.c \ + meta-marshal.h \ $(NULL) if HAVE_REMOTE_DESKTOP @@ -677,6 +679,7 @@ EXTRA_DIST += \ libmutter.pc.in \ meta-enum-types.h.in \ meta-enum-types.c.in \ + meta-marshal.list \ org.freedesktop.login1.xml \ org.gnome.Mutter.DisplayConfig.xml \ org.gnome.Mutter.IdleMonitor.xml \ @@ -690,7 +693,10 @@ BUILT_SOURCES = \ $(libmutterinclude_built_headers) MUTTER_STAMP_FILES = stamp-meta-enum-types.h -CLEANFILES += $(MUTTER_STAMP_FILES) +CLEANFILES += \ + $(MUTTER_STAMP_FILES) \ + meta-marshal.c \ + meta-marshal.h meta/meta-enum-types.h: stamp-meta-enum-types.h Makefile @true @@ -786,3 +792,20 @@ endef $(AM_V_GEN)$(WAYLAND_SCANNER) server-header $< $@ %-server-protocol.h : $(WAYLAND_EGLSTREAM_DATADIR)/%.xml $(AM_V_GEN)$(WAYLAND_SCANNER) server-header $< $@ + +meta_marshal_opts = --prefix=meta_marshal --internal + +meta-marshal.h: meta-marshal.list + $(AM_V_GEN)$(GLIB_GENMARSHAL) \ + --header \ + $(meta_marshal_opts) \ + --output=$@ \ + $< + +meta-marshal.c: meta-marshal.list meta-marshal.h + $(AM_V_GEN)$(GLIB_GENMARSHAL) \ + --include-header=meta-marshal.h \ + $(meta_marshal_opts) \ + --body \ + --output=$@ \ + $< diff --git a/src/backends/native/meta-gpu-kms.c b/src/backends/native/meta-gpu-kms.c index c3c5d0d68..26c759031 100644 --- a/src/backends/native/meta-gpu-kms.c +++ b/src/backends/native/meta-gpu-kms.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -51,6 +52,7 @@ typedef struct _MetaGpuKmsFlipClosureContainer { GClosure *flip_closure; MetaGpuKms *gpu_kms; + MetaCrtc *crtc; } MetaGpuKmsFlipClosureContainer; struct _MetaGpuKms @@ -61,6 +63,8 @@ struct _MetaGpuKms char *file_path; GSource *source; + clockid_t clock_id; + drmModeConnector **connectors; unsigned int n_connectors; @@ -165,18 +169,26 @@ meta_gpu_kms_apply_crtc_mode (MetaGpuKms *gpu_kms, static void invoke_flip_closure (GClosure *flip_closure, - MetaGpuKms *gpu_kms) + MetaGpuKms *gpu_kms, + MetaCrtc *crtc, + int64_t page_flip_time_ns) { GValue params[] = { G_VALUE_INIT, - G_VALUE_INIT + G_VALUE_INIT, + G_VALUE_INIT, + G_VALUE_INIT, }; g_value_init (¶ms[0], G_TYPE_POINTER); g_value_set_pointer (¶ms[0], flip_closure); g_value_init (¶ms[1], G_TYPE_OBJECT); g_value_set_object (¶ms[1], gpu_kms); - g_closure_invoke (flip_closure, NULL, 2, params, NULL); + g_value_init (¶ms[2], G_TYPE_OBJECT); + g_value_set_object (¶ms[2], crtc); + g_value_init (¶ms[3], G_TYPE_INT64); + g_value_set_int64 (¶ms[3], page_flip_time_ns); + g_closure_invoke (flip_closure, NULL, 4, params, NULL); g_closure_unref (flip_closure); } @@ -216,6 +228,7 @@ meta_gpu_kms_is_crtc_active (MetaGpuKms *gpu_kms, MetaGpuKmsFlipClosureContainer * meta_gpu_kms_wrap_flip_closure (MetaGpuKms *gpu_kms, + MetaCrtc *crtc, GClosure *flip_closure) { MetaGpuKmsFlipClosureContainer *closure_container; @@ -223,7 +236,8 @@ meta_gpu_kms_wrap_flip_closure (MetaGpuKms *gpu_kms, closure_container = g_new0 (MetaGpuKmsFlipClosureContainer, 1); *closure_container = (MetaGpuKmsFlipClosureContainer) { .flip_closure = flip_closure, - .gpu_kms = gpu_kms + .gpu_kms = gpu_kms, + .crtc = crtc }; return closure_container; @@ -263,6 +277,7 @@ meta_gpu_kms_flip_crtc (MetaGpuKms *gpu_kms, int kms_fd = meta_gpu_kms_get_fd (gpu_kms); closure_container = meta_gpu_kms_wrap_flip_closure (gpu_kms, + crtc, flip_closure); ret = drmModePageFlip (kms_fd, @@ -296,6 +311,23 @@ meta_gpu_kms_flip_crtc (MetaGpuKms *gpu_kms, return TRUE; } +static int64_t +timespec_to_nanoseconds (const struct timespec *ts) +{ + const int64_t one_billion = 1000000000; + + return ((int64_t) ts->tv_sec) * one_billion + ts->tv_nsec; +} + +static int64_t +timeval_to_nanoseconds (const struct timeval *tv) +{ + int64_t usec = ((int64_t) tv->tv_sec) * G_USEC_PER_SEC + tv->tv_usec; + int64_t nsec = usec * 1000; + + return nsec; +} + static void page_flip_handler (int fd, unsigned int frame, @@ -306,8 +338,12 @@ page_flip_handler (int fd, MetaGpuKmsFlipClosureContainer *closure_container = user_data; GClosure *flip_closure = closure_container->flip_closure; MetaGpuKms *gpu_kms = closure_container->gpu_kms; + struct timeval page_flip_time = {sec, usec}; - invoke_flip_closure (flip_closure, gpu_kms); + invoke_flip_closure (flip_closure, + gpu_kms, + closure_container->crtc, + timeval_to_nanoseconds (&page_flip_time)); meta_gpu_kms_flip_closure_container_free (closure_container); } @@ -380,6 +416,17 @@ meta_gpu_kms_get_file_path (MetaGpuKms *gpu_kms) return gpu_kms->file_path; } +int64_t +meta_gpu_kms_get_current_time_ns (MetaGpuKms *gpu_kms) +{ + struct timespec ts; + + if (clock_gettime (gpu_kms->clock_id, &ts)) + return 0; + + return timespec_to_nanoseconds (&ts); +} + void meta_gpu_kms_set_power_save_mode (MetaGpuKms *gpu_kms, uint64_t state) @@ -679,6 +726,17 @@ init_crtcs (MetaGpuKms *gpu_kms, meta_gpu_take_crtcs (gpu, crtcs); } +static void +init_frame_clock (MetaGpuKms *gpu_kms) +{ + uint64_t uses_monotonic; + + if (drmGetCap (gpu_kms->fd, DRM_CAP_TIMESTAMP_MONOTONIC, &uses_monotonic) != 0) + uses_monotonic = 0; + + gpu_kms->clock_id = uses_monotonic ? CLOCK_MONOTONIC : CLOCK_REALTIME; +} + static void init_outputs (MetaGpuKms *gpu_kms, MetaKmsResources *resources) @@ -806,6 +864,7 @@ meta_gpu_kms_read_current (MetaGpu *gpu, init_modes (gpu_kms, resources.resources); init_crtcs (gpu_kms, &resources); init_outputs (gpu_kms, &resources); + init_frame_clock (gpu_kms); meta_kms_resources_release (&resources); diff --git a/src/backends/native/meta-gpu-kms.h b/src/backends/native/meta-gpu-kms.h index 5f77f191a..3e3f45c7e 100644 --- a/src/backends/native/meta-gpu-kms.h +++ b/src/backends/native/meta-gpu-kms.h @@ -73,6 +73,8 @@ int meta_gpu_kms_get_fd (MetaGpuKms *gpu_kms); const char * meta_gpu_kms_get_file_path (MetaGpuKms *gpu_kms); +int64_t meta_gpu_kms_get_current_time_ns (MetaGpuKms *gpu_kms); + void meta_gpu_kms_get_max_buffer_size (MetaGpuKms *gpu_kms, int *max_width, int *max_height); @@ -89,6 +91,7 @@ gboolean meta_drm_mode_equal (const drmModeModeInfo *one, float meta_calculate_drm_mode_refresh_rate (const drmModeModeInfo *mode); MetaGpuKmsFlipClosureContainer * meta_gpu_kms_wrap_flip_closure (MetaGpuKms *gpu_kms, + MetaCrtc *crtc, GClosure *flip_closure); void meta_gpu_kms_flip_closure_container_free (MetaGpuKmsFlipClosureContainer *closure_container); diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c index f58e6b3e0..b192c3641 100644 --- a/src/backends/native/meta-renderer-native.c +++ b/src/backends/native/meta-renderer-native.c @@ -62,6 +62,7 @@ #include "backends/native/meta-monitor-manager-kms.h" #include "backends/native/meta-renderer-native.h" #include "backends/native/meta-renderer-native-gles3.h" +#include "meta-marshal.h" #include "cogl/cogl.h" #include "core/boxes-private.h" @@ -1159,6 +1160,8 @@ meta_onscreen_native_swap_drm_fb (CoglOnscreen *onscreen) static void on_crtc_flipped (GClosure *closure, MetaGpuKms *gpu_kms, + MetaCrtc *crtc, + int64_t page_flip_time_ns, MetaRendererView *view) { ClutterStageView *stage_view = CLUTTER_STAGE_VIEW (view); @@ -1169,6 +1172,24 @@ on_crtc_flipped (GClosure *closure, MetaOnscreenNative *onscreen_native = onscreen_egl->platform; MetaRendererNative *renderer_native = onscreen_native->renderer_native; MetaGpuKms *render_gpu = onscreen_native->render_gpu; + CoglFrameInfo *frame_info; + float refresh_rate; + + frame_info = g_queue_peek_tail (&onscreen->pending_frame_infos); + refresh_rate = crtc && crtc->current_mode ? + crtc->current_mode->refresh_rate : + 0.0f; + + /* Only keep the frame info for the fastest CRTC in use, which may not be + * the first one to complete a flip. By only telling the compositor about the + * fastest monitor(s) we direct it to produce new frames fast enough to + * satisfy all monitors. + */ + if (refresh_rate >= frame_info->refresh_rate) + { + frame_info->presentation_time = page_flip_time_ns; + frame_info->refresh_rate = refresh_rate; + } if (gpu_kms != render_gpu) { @@ -1299,7 +1320,9 @@ flip_egl_stream (MetaOnscreenNative *onscreen_native, return FALSE; closure_container = - meta_gpu_kms_wrap_flip_closure (onscreen_native->render_gpu, flip_closure); + meta_gpu_kms_wrap_flip_closure (onscreen_native->render_gpu, + NULL, + flip_closure); acquire_attribs = (EGLAttrib[]) { EGL_DRM_FLIP_EVENT_DATA_NV, @@ -1542,7 +1565,7 @@ meta_onscreen_native_flip_crtcs (CoglOnscreen *onscreen) flip_closure = g_cclosure_new (G_CALLBACK (on_crtc_flipped), g_object_ref (view), (GClosureNotify) flip_closure_destroyed); - g_closure_set_marshal (flip_closure, g_cclosure_marshal_VOID__OBJECT); + g_closure_set_marshal (flip_closure, meta_marshal_VOID__OBJECT_OBJECT_INT64); /* Either flip the CRTC's of the monitor info, if we are drawing just part * of the stage, or all of the CRTC's if we are drawing the whole stage. @@ -2687,6 +2710,15 @@ meta_renderer_native_create_offscreen (MetaRendererNative *renderer, return fb; } +static int64_t +meta_renderer_native_get_clock_time (CoglContext *context) +{ + CoglRenderer *cogl_renderer = cogl_context_get_renderer (context); + MetaGpuKms *gpu_kms = cogl_renderer->custom_winsys_user_data; + + return meta_gpu_kms_get_current_time_ns (gpu_kms); +} + static const CoglWinsysVtable * get_native_cogl_winsys_vtable (CoglRenderer *cogl_renderer) { @@ -2715,6 +2747,8 @@ get_native_cogl_winsys_vtable (CoglRenderer *cogl_renderer) vtable.onscreen_swap_buffers_with_damage = meta_onscreen_native_swap_buffers_with_damage; + vtable.context_get_clock_time = meta_renderer_native_get_clock_time; + vtable_inited = TRUE; } diff --git a/src/meta-marshal.list b/src/meta-marshal.list new file mode 100644 index 000000000..c1f4781d2 --- /dev/null +++ b/src/meta-marshal.list @@ -0,0 +1 @@ +VOID:OBJECT,OBJECT,INT64 -- 2.18.1 From 1b3109809167c17bfa8713ce906c95e66d2c042f Mon Sep 17 00:00:00 2001 From: Daniel van Vugt Date: Tue, 10 Jul 2018 17:46:02 +0800 Subject: [PATCH 2/2] renderer-native: Advertise _FEATURE_SWAP_THROTTLE Because it is implemented and always on. By advertising this fact the master clock is able to sync to the native refresh rate instead of always using the fallback of 60.00Hz. https://bugzilla.gnome.org/show_bug.cgi?id=781296 --- src/backends/native/meta-renderer-native.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c index b192c3641..929e9ca90 100644 --- a/src/backends/native/meta-renderer-native.c +++ b/src/backends/native/meta-renderer-native.c @@ -2005,6 +2005,13 @@ meta_renderer_native_init_egl_context (CoglContext *cogl_context, COGL_WINSYS_FEATURE_MULTIPLE_ONSCREEN, TRUE); + /* COGL_WINSYS_FEATURE_SWAP_THROTTLE is always true for this renderer + * because we have the call to wait_for_pending_flips on every frame. + */ + COGL_FLAGS_SET (cogl_context->winsys_features, + COGL_WINSYS_FEATURE_SWAP_THROTTLE, + TRUE); + #ifdef HAVE_EGL_DEVICE if (renderer_gpu_data->mode == META_RENDERER_NATIVE_MODE_EGL_DEVICE) COGL_FLAGS_SET (cogl_context->features, @@ -2639,8 +2646,12 @@ meta_renderer_native_create_onscreen (MetaRendererNative *renderer_native, } onscreen = cogl_onscreen_new (context, width, height); - cogl_onscreen_set_swap_throttled (onscreen, - _clutter_get_sync_to_vblank ()); + + /* We have wait_for_pending_flips hardcoded, so throttling always. */ + cogl_onscreen_set_swap_throttled (onscreen, TRUE); + if (!_clutter_get_sync_to_vblank ()) + g_warning ("Request to disable sync-to-vblank is being ignored. " + "MetaRendererNative does not support disabling it."); if (!cogl_framebuffer_allocate (COGL_FRAMEBUFFER (onscreen), error)) { -- 2.18.1