From 27ed0697663901474ac9ebbb56596290462e9b3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Wed, 26 Oct 2022 19:08:30 +0200 Subject: [PATCH] kms/impl-device: Add deadline based KMS commit scheduling This makes it possible to post KMS updates that will always defer until just before the scanout deadline. This is useful to allow queuing cursor updates where we don't want to post them to KMS immediately, but rather wait until as late as possible to get lower latency. We cannot delay primary plane compositions however, and this is due to how the kernel may prioritize GPU work - not until a pipeline gets attached to a atomic commit will it in some drivers get bumped to high priority. This means we still need to post any update that depends on OpenGL pipelines as soon as possible. To avoid working on compositing, then getting stomped on the feet by the deadline scheduler, the deadline timer is disarmed whenever there is a frame currently being painted. This will still allow new cursor updates to arrive during composition, but will delay the actual KMS commit until the primary plane update has been posted. Still, even for cursor-only we still need higher than default timing capabilities, thus the deadline scheduler depends on the KMS thread getting real-time scheduling priority. When the thread isn't realtime scheduled, the KMS thread instead asks the main thread to "flush" the commit as part of the regular frame update. A flushing update means one that isn't set to always defer and has a latching CRTC. The verbose KMS debug logging makes the processing take too long, making us more likely to miss the deadline. Avoid this by increasing the evasion length when debug logging is enabled. Not the best, but better than changing the behavior completely. Part-of: --- src/backends/native/meta-kms-crtc-private.h | 5 + src/backends/native/meta-kms-crtc.c | 93 +++ src/backends/native/meta-kms-device-private.h | 3 + src/backends/native/meta-kms-device.c | 97 ++- src/backends/native/meta-kms-device.h | 6 + src/backends/native/meta-kms-impl-device.c | 612 +++++++++++++++++- src/backends/native/meta-kms-impl-device.h | 10 + src/backends/native/meta-kms-impl.c | 72 ++- src/backends/native/meta-kms-impl.h | 20 + src/backends/native/meta-kms-page-flip.c | 6 - src/backends/native/meta-kms-update-private.h | 6 + src/backends/native/meta-kms-update.c | 62 +- src/backends/native/meta-kms-update.h | 6 + src/backends/native/meta-kms.h | 1 + src/backends/native/meta-onscreen-native.c | 30 +- src/backends/native/meta-onscreen-native.h | 3 + .../native/meta-renderer-native-private.h | 2 + src/backends/native/meta-renderer-native.c | 58 +- src/backends/native/meta-renderer-native.h | 4 + src/backends/native/meta-stage-native.c | 6 + 20 files changed, 1067 insertions(+), 35 deletions(-) diff --git a/src/backends/native/meta-kms-crtc-private.h b/src/backends/native/meta-kms-crtc-private.h index e36202be7..d4d915786 100644 --- a/src/backends/native/meta-kms-crtc-private.h +++ b/src/backends/native/meta-kms-crtc-private.h @@ -56,4 +56,9 @@ uint64_t meta_kms_crtc_get_prop_drm_value (MetaKmsCrtc *crtc, MetaKmsCrtcProp prop, uint64_t value); +gboolean meta_kms_crtc_determine_deadline (MetaKmsCrtc *crtc, + int64_t *out_next_deadline_us, + int64_t *out_next_presentation_us, + GError **error); + #endif /* META_KMS_CRTC_PRIVATE_H */ diff --git a/src/backends/native/meta-kms-crtc.c b/src/backends/native/meta-kms-crtc.c index e5405428c..ad796e3c9 100644 --- a/src/backends/native/meta-kms-crtc.c +++ b/src/backends/native/meta-kms-crtc.c @@ -28,6 +28,10 @@ #include "backends/native/meta-kms-impl-device-simple.h" #include "backends/native/meta-kms-mode.h" #include "backends/native/meta-kms-update-private.h" +#include "backends/native/meta-kms-utils.h" + +#define DEADLINE_EVASION_US 500 +#define DEADLINE_EVASION_WITH_KMS_TOPIC_US 1000 typedef struct _MetaKmsCrtcPropTable { @@ -487,3 +491,92 @@ meta_kms_crtc_class_init (MetaKmsCrtcClass *klass) object_class->finalize = meta_kms_crtc_finalize; } + +static drmVBlankSeqType +get_crtc_type_bitmask (MetaKmsCrtc *crtc) +{ + if (crtc->idx > 1) + { + return ((crtc->idx << DRM_VBLANK_HIGH_CRTC_SHIFT) & + DRM_VBLANK_HIGH_CRTC_MASK); + } + else if (crtc->idx > 0) + { + return DRM_VBLANK_SECONDARY; + } + else + { + return 0; + } +} + +gboolean +meta_kms_crtc_determine_deadline (MetaKmsCrtc *crtc, + int64_t *out_next_deadline_us, + int64_t *out_next_presentation_us, + GError **error) +{ + MetaKmsImplDevice *impl_device; + int fd; + drmVBlank vblank; + int ret; + int64_t next_presentation_us; + int64_t next_deadline_us; + drmModeModeInfo *drm_mode; + int64_t vblank_duration_us; + int64_t deadline_evasion_us; + + if (!crtc->current_state.is_drm_mode_valid) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, + "Mode invalid"); + return FALSE; + } + + impl_device = meta_kms_device_get_impl_device (crtc->device); + fd = meta_kms_impl_device_get_fd (impl_device); + + vblank = (drmVBlank) { + .request.type = DRM_VBLANK_RELATIVE | get_crtc_type_bitmask (crtc), + .request.sequence = 0, + .request.signal = 0, + }; + + ret = drmWaitVBlank (fd, &vblank); + if (ret != 0) + { + g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret), + "drmWaitVBlank failed: %s", g_strerror (-ret)); + return FALSE; + } + + drm_mode = &crtc->current_state.drm_mode; + next_presentation_us = + s2us (vblank.reply.tval_sec) + vblank.reply.tval_usec + 0.5 + + G_USEC_PER_SEC / meta_calculate_drm_mode_refresh_rate (drm_mode); + + /* + * 1 + * time per pixel = ----------------- + * Pixel clock (Hz) + * + * number of pixels = vdisplay * htotal + * + * time spent scanning out = time per pixel * number of pixels + * + */ + + if (meta_is_topic_enabled (META_DEBUG_KMS)) + deadline_evasion_us = DEADLINE_EVASION_WITH_KMS_TOPIC_US; + else + deadline_evasion_us = DEADLINE_EVASION_US; + + vblank_duration_us = meta_calculate_drm_mode_vblank_duration_us (drm_mode); + next_deadline_us = next_presentation_us - (vblank_duration_us + + deadline_evasion_us); + + *out_next_presentation_us = next_presentation_us; + *out_next_deadline_us = next_deadline_us; + + return TRUE; +} diff --git a/src/backends/native/meta-kms-device-private.h b/src/backends/native/meta-kms-device-private.h index f40e9e979..77101056d 100644 --- a/src/backends/native/meta-kms-device-private.h +++ b/src/backends/native/meta-kms-device-private.h @@ -40,4 +40,7 @@ MetaKmsCrtc * meta_kms_device_find_crtc_in_impl (MetaKmsDevice *device, MetaKmsConnector * meta_kms_device_find_connector_in_impl (MetaKmsDevice *device, uint32_t connector_id); +void meta_kms_device_set_needs_flush (MetaKmsDevice *device, + MetaKmsCrtc *crtc); + #endif /* META_KMS_DEVICE_PRIVATE_H */ diff --git a/src/backends/native/meta-kms-device.c b/src/backends/native/meta-kms-device.c index 6cf5c7141..a225db039 100644 --- a/src/backends/native/meta-kms-device.c +++ b/src/backends/native/meta-kms-device.c @@ -41,6 +41,15 @@ #include "backends/native/meta-kms-private.h" #include "backends/native/meta-kms-update-private.h" +enum +{ + CRTC_NEEDS_FLUSH, + + N_SIGNALS +}; + +static int signals[N_SIGNALS]; + struct _MetaKmsDevice { GObject parent; @@ -61,6 +70,9 @@ struct _MetaKmsDevice MetaKmsDeviceCaps caps; GList *fallback_modes; + + GHashTable *needs_flush_crtcs; + GMutex needs_flush_mutex; }; G_DEFINE_TYPE (MetaKmsDevice, meta_kms_device, G_TYPE_OBJECT); @@ -326,11 +338,8 @@ process_async_update_in_impl (MetaThreadImpl *thread_impl, MetaKmsUpdate *update = data->update; MetaKmsDevice *device = meta_kms_update_get_device (update); MetaKmsImplDevice *impl_device = meta_kms_device_get_impl_device (device); - MetaKmsFeedback *feedback; - feedback = meta_kms_impl_device_process_update (impl_device, update, - data->flags); - meta_kms_feedback_unref (feedback); + meta_kms_impl_device_handle_update (impl_device, update, data->flags); return GINT_TO_POINTER (TRUE); } @@ -357,6 +366,72 @@ meta_kms_device_post_update (MetaKmsDevice *device, NULL, NULL); } +static gpointer +await_flush_in_impl (MetaThreadImpl *thread_impl, + gpointer user_data, + GError **error) +{ + MetaKmsCrtc *crtc = META_KMS_CRTC (user_data); + MetaKmsDevice *device = meta_kms_crtc_get_device (crtc); + MetaKmsImplDevice *impl_device = meta_kms_device_get_impl_device (device); + + meta_kms_impl_device_await_flush (impl_device, crtc); + + return NULL; +} + +void +meta_kms_device_await_flush (MetaKmsDevice *device, + MetaKmsCrtc *crtc) +{ + MetaKms *kms = meta_kms_device_get_kms (device); + + meta_thread_post_impl_task (META_THREAD (kms), + await_flush_in_impl, + crtc, NULL, + NULL, NULL); +} + +static void +emit_crtc_needs_flush_in_main (MetaThread *thread, + gpointer user_data) +{ + MetaKmsCrtc *crtc = user_data; + + g_signal_emit (meta_kms_crtc_get_device (crtc), + signals[CRTC_NEEDS_FLUSH], 0, crtc); +} + +void +meta_kms_device_set_needs_flush (MetaKmsDevice *device, + MetaKmsCrtc *crtc) +{ + gboolean needs_flush; + + g_mutex_lock (&device->needs_flush_mutex); + needs_flush = g_hash_table_add (device->needs_flush_crtcs, crtc); + g_mutex_unlock (&device->needs_flush_mutex); + + if (needs_flush) + { + meta_kms_queue_callback (meta_kms_device_get_kms (device), + NULL, emit_crtc_needs_flush_in_main, crtc, NULL); + } +} + +gboolean +meta_kms_device_handle_flush (MetaKmsDevice *device, + MetaKmsCrtc *crtc) +{ + gboolean needs_flush; + + g_mutex_lock (&device->needs_flush_mutex); + needs_flush = g_hash_table_remove (device->needs_flush_crtcs, crtc); + g_mutex_unlock (&device->needs_flush_mutex); + + return needs_flush; +} + void meta_kms_device_add_fake_plane_in_impl (MetaKmsDevice *device, MetaKmsPlaneType plane_type, @@ -631,12 +706,17 @@ meta_kms_device_finalize (GObject *object) NULL); } + g_mutex_clear (&device->needs_flush_mutex); + g_hash_table_unref (device->needs_flush_crtcs); + G_OBJECT_CLASS (meta_kms_device_parent_class)->finalize (object); } static void meta_kms_device_init (MetaKmsDevice *device) { + device->needs_flush_crtcs = g_hash_table_new (NULL, NULL); + g_mutex_init (&device->needs_flush_mutex); } static void @@ -645,4 +725,13 @@ meta_kms_device_class_init (MetaKmsDeviceClass *klass) GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_kms_device_finalize; + + signals[CRTC_NEEDS_FLUSH] = + g_signal_new ("crtc-needs-flush", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 1, + META_TYPE_KMS_CRTC); } diff --git a/src/backends/native/meta-kms-device.h b/src/backends/native/meta-kms-device.h index 543c271a9..37a6af807 100644 --- a/src/backends/native/meta-kms-device.h +++ b/src/backends/native/meta-kms-device.h @@ -83,6 +83,12 @@ void meta_kms_device_post_update (MetaKmsDevice *device, MetaKmsUpdate *update, MetaKmsUpdateFlag flags); +void meta_kms_device_await_flush (MetaKmsDevice *device, + MetaKmsCrtc *crtc); + +gboolean meta_kms_device_handle_flush (MetaKmsDevice *device, + MetaKmsCrtc *crtc); + META_EXPORT_TEST void meta_kms_device_disable (MetaKmsDevice *device); diff --git a/src/backends/native/meta-kms-impl-device.c b/src/backends/native/meta-kms-impl-device.c index 4f5aa9313..d225c635e 100644 --- a/src/backends/native/meta-kms-impl-device.c +++ b/src/backends/native/meta-kms-impl-device.c @@ -22,6 +22,8 @@ #include "backends/native/meta-kms-impl-device.h" #include +#include +#include #include #include "backends/native/meta-backend-native.h" @@ -30,12 +32,14 @@ #include "backends/native/meta-kms-connector.h" #include "backends/native/meta-kms-crtc-private.h" #include "backends/native/meta-kms-crtc.h" +#include "backends/native/meta-kms-device-private.h" #include "backends/native/meta-kms-impl.h" #include "backends/native/meta-kms-mode-private.h" #include "backends/native/meta-kms-page-flip-private.h" #include "backends/native/meta-kms-plane-private.h" #include "backends/native/meta-kms-plane.h" #include "backends/native/meta-kms-private.h" +#include "backends/native/meta-thread-private.h" #include "meta-default-modes.h" #include "meta-private-enum-types.h" @@ -54,6 +58,23 @@ enum static GParamSpec *obj_props[N_PROPS]; +typedef struct _CrtcDeadline +{ + MetaKmsImplDevice *impl_device; + MetaKmsCrtc *crtc; + MetaKmsUpdate *pending_update; + gboolean await_flush; + gboolean pending_page_flip; + + struct { + int timer_fd; + GSource *source; + gboolean armed; + gboolean is_deadline_page_flip; + int64_t expected_presentation_time_us; + } deadline; +} CrtcFrame; + typedef struct _MetaKmsImplDevicePrivate { MetaKmsDevice *device; @@ -76,11 +97,16 @@ typedef struct _MetaKmsImplDevicePrivate MetaKmsDeviceCaps caps; GList *fallback_modes; + + GHashTable *crtc_frames; } MetaKmsImplDevicePrivate; static void initable_iface_init (GInitableIface *iface); +static CrtcFrame * get_crtc_frame (MetaKmsImplDevice *impl_device, + MetaKmsCrtc *latch_crtc); + G_DEFINE_TYPE_WITH_CODE (MetaKmsImplDevice, meta_kms_impl_device, G_TYPE_OBJECT, G_ADD_PRIVATE (MetaKmsImplDevice) @@ -868,6 +894,7 @@ ensure_device_file (MetaKmsImplDevice *impl_device, meta_device_file_get_fd (device_file), kms_event_dispatch_in_impl, impl_device); + g_source_set_priority (priv->fd_source, G_PRIORITY_HIGH); } return TRUE; @@ -954,6 +981,7 @@ err: g_clear_list (&priv->planes, g_object_unref); g_clear_list (&priv->crtcs, g_object_unref); g_clear_list (&priv->connectors, g_object_unref); + g_clear_pointer (&priv->crtc_frames, g_hash_table_unref); return META_KMS_RESOURCE_CHANGE_FULL; } @@ -998,6 +1026,141 @@ meta_kms_impl_device_get_fd (MetaKmsImplDevice *impl_device) return meta_device_file_get_fd (priv->device_file); } +static void +disarm_crtc_frame_deadline_timer (CrtcFrame *crtc_frame) +{ + struct itimerspec its = {}; + + if (!crtc_frame->deadline.source) + return; + + meta_topic (META_DEBUG_KMS, "Disarming deadline timer for crtc %u (%s)", + meta_kms_crtc_get_id (crtc_frame->crtc), + meta_kms_device_get_path (meta_kms_crtc_get_device (crtc_frame->crtc))); + + timerfd_settime (crtc_frame->deadline.timer_fd, + TFD_TIMER_ABSTIME, &its, NULL); + + crtc_frame->deadline.armed = FALSE; +} + +static void +arm_crtc_frame_deadline_timer (CrtcFrame *crtc_frame, + int64_t next_deadline_us, + int64_t next_presentation_us) +{ + struct itimerspec its = {}; + int64_t tv_sec; + int64_t tv_nsec; + + g_warn_if_fail (!crtc_frame->await_flush); + + if (!crtc_frame->deadline.source) + return; + + meta_topic (META_DEBUG_KMS, "Arming deadline timer for crtc %u (%s): %ld", + meta_kms_crtc_get_id (crtc_frame->crtc), + meta_kms_device_get_path (meta_kms_crtc_get_device (crtc_frame->crtc)), + next_deadline_us); + + tv_sec = us2s (next_deadline_us); + tv_nsec = us2ns (next_deadline_us - s2us (tv_sec)); + + its.it_value.tv_sec = tv_sec; + its.it_value.tv_nsec = tv_nsec; + timerfd_settime (crtc_frame->deadline.timer_fd, + TFD_TIMER_ABSTIME, &its, NULL); + + crtc_frame->deadline.expected_presentation_time_us = next_presentation_us; + crtc_frame->deadline.armed = TRUE; +} + +static void +notify_crtc_frame_ready (CrtcFrame *crtc_frame) +{ + MetaKmsCrtc *crtc = crtc_frame->crtc; + + crtc_frame->pending_page_flip = FALSE; + crtc_frame->deadline.is_deadline_page_flip = FALSE; + + if (!crtc_frame->pending_update) + return; + + if (crtc_frame->await_flush) + return; + + meta_kms_impl_device_schedule_process (crtc_frame->impl_device, crtc); +} + +static void +crtc_page_flip_feedback_flipped (MetaKmsCrtc *crtc, + unsigned int sequence, + unsigned int tv_sec, + unsigned int tv_usec, + gpointer user_data) +{ + CrtcFrame *crtc_frame = user_data; + + if (crtc_frame->deadline.is_deadline_page_flip && + meta_is_topic_enabled (META_DEBUG_KMS)) + { + struct timeval page_flip_timeval; + int64_t presentation_time_us; + + page_flip_timeval = (struct timeval) { + .tv_sec = tv_sec, + .tv_usec = tv_usec, + }; + presentation_time_us = meta_timeval_to_microseconds (&page_flip_timeval); + + meta_topic (META_DEBUG_KMS, + "Deadline page flip presentation time: %"G_GINT64_FORMAT" us, " + "expected %"G_GINT64_FORMAT" us " + "(diff: %"G_GINT64_FORMAT")", + presentation_time_us, + crtc_frame->deadline.expected_presentation_time_us, + crtc_frame->deadline.expected_presentation_time_us - + presentation_time_us); + } + + notify_crtc_frame_ready (crtc_frame); +} + +static void +crtc_page_flip_feedback_ready (MetaKmsCrtc *crtc, + gpointer user_data) +{ + CrtcFrame *crtc_frame = user_data; + + notify_crtc_frame_ready (crtc_frame); +} + +static void +crtc_page_flip_feedback_mode_set_fallback (MetaKmsCrtc *crtc, + gpointer user_data) +{ + CrtcFrame *crtc_frame = user_data; + + crtc_frame->pending_page_flip = FALSE; +} + +static void +crtc_page_flip_feedback_discarded (MetaKmsCrtc *crtc, + gpointer user_data, + const GError *error) +{ + CrtcFrame *crtc_frame = user_data; + + crtc_frame->pending_page_flip = FALSE; +} + +static const MetaKmsPageFlipListenerVtable crtc_page_flip_listener_vtable = { + .flipped = crtc_page_flip_feedback_flipped, + .ready = crtc_page_flip_feedback_ready, + .mode_set_fallback = crtc_page_flip_feedback_mode_set_fallback, + .discarded = crtc_page_flip_feedback_discarded, +}; + static void emit_resources_changed_callback (MetaThread *thread, gpointer user_data) @@ -1028,28 +1191,71 @@ queue_result_feedback (MetaKmsImplDevice *impl_device, } } - -MetaKmsFeedback * -meta_kms_impl_device_process_update (MetaKmsImplDevice *impl_device, - MetaKmsUpdate *update, - MetaKmsUpdateFlag flags) +static MetaKmsFeedback * +do_process (MetaKmsImplDevice *impl_device, + MetaKmsCrtc *latch_crtc, + MetaKmsUpdate *update, + MetaKmsUpdateFlag flags) { MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); + MetaKms *kms = meta_kms_device_get_kms (priv->device); + MetaKmsImpl *impl = meta_kms_impl_device_get_impl (impl_device); + MetaThreadImpl *thread_impl = META_THREAD_IMPL (impl); MetaKmsImplDeviceClass *klass = META_KMS_IMPL_DEVICE_GET_CLASS (impl_device); + CrtcFrame *crtc_frame = NULL; MetaKmsFeedback *feedback; - g_autoptr (GError) error = NULL; MetaKmsResourceChanges changes = META_KMS_RESOURCE_CHANGE_NONE; - if (!ensure_device_file (impl_device, &error)) + COGL_TRACE_BEGIN_SCOPED (MetaKmsImplDeviceProcess, + "KMS device impl (processing)"); + + update = meta_kms_impl_filter_update (impl, latch_crtc, update, flags); + + if (!update) { - meta_kms_update_free (update); - return meta_kms_feedback_new_failed (NULL, g_steal_pointer (&error)); + GError *error; + + error = g_error_new (META_KMS_ERROR, + META_KMS_ERROR_EMPTY_UPDATE, + "Empty update"); + return meta_kms_feedback_new_failed (NULL, error); } - meta_kms_update_realize (update, impl_device); + if (!(flags & META_KMS_UPDATE_FLAG_TEST_ONLY)) + { + if (latch_crtc) + { + crtc_frame = get_crtc_frame (impl_device, latch_crtc); + if (crtc_frame && crtc_frame->pending_update) + { + meta_kms_update_merge_from (crtc_frame->pending_update, update); + meta_kms_update_free (update); + update = g_steal_pointer (&crtc_frame->pending_update); + } + } + + if (crtc_frame) + { + GMainContext *thread_context = + meta_thread_impl_get_main_context (thread_impl); + + meta_kms_update_add_page_flip_listener (update, + crtc_frame->crtc, + &crtc_page_flip_listener_vtable, + META_KMS_PAGE_FLIP_LISTENER_FLAG_NONE, + thread_context, + crtc_frame, NULL); + crtc_frame->pending_page_flip = TRUE; + } + } feedback = klass->process_update (impl_device, update, flags); + + if (meta_kms_feedback_get_result (feedback) != META_KMS_FEEDBACK_PASSED && + crtc_frame) + crtc_frame->pending_page_flip = FALSE; + if (!(flags & META_KMS_UPDATE_FLAG_TEST_ONLY)) changes = meta_kms_impl_device_predict_states (impl_device, update); @@ -1059,8 +1265,6 @@ meta_kms_impl_device_process_update (MetaKmsImplDevice *impl_device, if (changes != META_KMS_RESOURCE_CHANGE_NONE) { - MetaKms *kms = meta_kms_device_get_kms (priv->device); - meta_kms_queue_callback (kms, NULL, emit_resources_changed_callback, @@ -1069,6 +1273,383 @@ meta_kms_impl_device_process_update (MetaKmsImplDevice *impl_device, return feedback; } +static gpointer +crtc_frame_deadline_dispatch (MetaThreadImpl *thread_impl, + gpointer user_data, + GError **error) +{ + CrtcFrame *crtc_frame = user_data; + MetaKmsDevice *device = meta_kms_crtc_get_device (crtc_frame->crtc); + MetaKmsImplDevice *impl_device = meta_kms_device_get_impl_device (device); + g_autoptr (MetaKmsFeedback) feedback = NULL; + uint64_t timer_value; + ssize_t ret; + + ret = read (crtc_frame->deadline.timer_fd, + &timer_value, + sizeof (timer_value)); + if (ret == -1) + { + g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), + "Failed to read from timerfd: %s", g_strerror (errno)); + return GINT_TO_POINTER (FALSE); + } + else if (ret != sizeof (timer_value)) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Failed to read from timerfd: unexpected size %zd", ret); + return GINT_TO_POINTER (FALSE); + } + + feedback = do_process (impl_device, + crtc_frame->crtc, + g_steal_pointer (&crtc_frame->pending_update), + META_KMS_UPDATE_FLAG_NONE); + if (meta_kms_feedback_did_pass (feedback)) + crtc_frame->deadline.is_deadline_page_flip = TRUE; + disarm_crtc_frame_deadline_timer (crtc_frame); + + return GINT_TO_POINTER (TRUE); +} + +static void +crtc_frame_free (CrtcFrame *crtc_frame) +{ + g_clear_fd (&crtc_frame->deadline.timer_fd, NULL); + g_clear_pointer (&crtc_frame->deadline.source, g_source_destroy); + g_clear_pointer (&crtc_frame->pending_update, meta_kms_update_free); + g_free (crtc_frame); +} + +static CrtcFrame * +get_crtc_frame (MetaKmsImplDevice *impl_device, + MetaKmsCrtc *latch_crtc) +{ + MetaKmsImplDevicePrivate *priv = + meta_kms_impl_device_get_instance_private (impl_device); + + return g_hash_table_lookup (priv->crtc_frames, latch_crtc); +} + +static CrtcFrame * +ensure_crtc_frame (MetaKmsImplDevice *impl_device, + MetaKmsCrtc *latch_crtc) +{ + MetaKmsImplDevicePrivate *priv = + meta_kms_impl_device_get_instance_private (impl_device); + MetaKmsImpl *impl = meta_kms_impl_device_get_impl (impl_device); + MetaThreadImpl *thread_impl = META_THREAD_IMPL (impl); + CrtcFrame *crtc_frame; + + crtc_frame = get_crtc_frame (impl_device, latch_crtc); + if (crtc_frame) + return crtc_frame; + + crtc_frame = g_new0 (CrtcFrame, 1); + crtc_frame->impl_device = impl_device; + crtc_frame->crtc = latch_crtc; + crtc_frame->deadline.timer_fd = -1; + crtc_frame->await_flush = TRUE; + + if (meta_thread_impl_is_realtime (thread_impl)) + { + int timer_fd; + GSource *source; + g_autofree char *name = NULL; + + timer_fd = timerfd_create (CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC); + source = meta_thread_impl_register_fd (thread_impl, + timer_fd, + crtc_frame_deadline_dispatch, + crtc_frame); + + name = g_strdup_printf ("[mutter] KMS deadline clock (crtc: %u, %s)", + meta_kms_crtc_get_id (latch_crtc), + priv->path); + g_source_set_name (source, name); + g_source_set_priority (source, G_PRIORITY_HIGH + 1); + g_source_set_can_recurse (source, FALSE); + g_source_set_ready_time (source, -1); + + crtc_frame->deadline.timer_fd = timer_fd; + crtc_frame->deadline.source = source; + + g_source_unref (source); + } + + g_hash_table_insert (priv->crtc_frames, latch_crtc, crtc_frame); + + return crtc_frame; +} + +static gboolean +queue_update (MetaKmsImplDevice *impl_device, + CrtcFrame *crtc_frame, + MetaKmsUpdate *update) +{ + MetaKmsImpl *impl = meta_kms_impl_device_get_impl (impl_device); + MetaThreadImpl *thread_impl = META_THREAD_IMPL (impl); + int64_t next_presentation_us = 0; + int64_t next_deadline_us = 0; + g_autoptr (GError) error = NULL; + + g_assert (update); + + if (meta_thread_impl_is_realtime (thread_impl) && + !crtc_frame->deadline.armed) + { + if (!meta_kms_crtc_determine_deadline (crtc_frame->crtc, + &next_deadline_us, + &next_presentation_us, + &error)) + { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) + g_critical ("Failed to determine deadline: %s", error->message); + return FALSE; + } + } + + if (crtc_frame->pending_update) + { + meta_kms_update_merge_from (crtc_frame->pending_update, update); + meta_kms_update_free (update); + } + else + { + crtc_frame->pending_update = update; + } + + if (meta_thread_impl_is_realtime (thread_impl) && + !crtc_frame->pending_page_flip && + !crtc_frame->await_flush && + next_deadline_us) + { + arm_crtc_frame_deadline_timer (crtc_frame, + next_deadline_us, + next_presentation_us); + } + + return TRUE; +} + +void +meta_kms_impl_device_handle_update (MetaKmsImplDevice *impl_device, + MetaKmsUpdate *update, + MetaKmsUpdateFlag flags) +{ + MetaKmsImplDevicePrivate *priv = + meta_kms_impl_device_get_instance_private (impl_device); + g_autoptr (GError) error = NULL; + MetaKmsCrtc *latch_crtc; + CrtcFrame *crtc_frame; + MetaKmsFeedback *feedback; + + meta_assert_in_kms_impl (meta_kms_impl_get_kms (priv->impl)); + + latch_crtc = meta_kms_update_get_latch_crtc (update); + if (!latch_crtc) + { + g_set_error (&error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, + "Only single-CRTC updates supported"); + goto err; + } + + if (!priv->crtc_frames) + { + g_set_error (&error, G_IO_ERROR, G_IO_ERROR_CLOSED, "Shutting down"); + goto err; + } + + if (!ensure_device_file (impl_device, &error)) + goto err; + + meta_kms_update_realize (update, impl_device); + + crtc_frame = ensure_crtc_frame (impl_device, latch_crtc); + + crtc_frame->await_flush = FALSE; + + if (crtc_frame->pending_page_flip && + !meta_kms_update_get_mode_sets (update)) + { + g_assert (latch_crtc); + + meta_topic (META_DEBUG_KMS, + "Queuing update on CRTC %u (%s): pending page flip", + meta_kms_crtc_get_id (latch_crtc), + priv->path); + + if (queue_update (impl_device, crtc_frame, update)) + return; + } + + if (crtc_frame->pending_update) + { + meta_kms_update_merge_from (crtc_frame->pending_update, update); + meta_kms_update_free (update); + update = g_steal_pointer (&crtc_frame->pending_update); + disarm_crtc_frame_deadline_timer (crtc_frame); + } + + feedback = do_process (impl_device, latch_crtc, update, flags); + meta_kms_feedback_unref (feedback); + return; + +err: + feedback = meta_kms_feedback_new_failed (NULL, g_steal_pointer (&error)); + queue_result_feedback (impl_device, update, feedback); + meta_kms_feedback_unref (feedback); + meta_kms_update_free (update); +} + +void +meta_kms_impl_device_await_flush (MetaKmsImplDevice *impl_device, + MetaKmsCrtc *crtc) +{ + MetaKmsImplDevicePrivate *priv = + meta_kms_impl_device_get_instance_private (impl_device); + CrtcFrame *crtc_frame; + + meta_topic (META_DEBUG_KMS, "Awaiting flush on CRTC %u (%s)", + meta_kms_crtc_get_id (crtc), + priv->path); + + crtc_frame = ensure_crtc_frame (impl_device, crtc); + crtc_frame->await_flush = TRUE; + + if (crtc_frame->deadline.armed) + disarm_crtc_frame_deadline_timer (crtc_frame); +} + +static gboolean +ensure_deadline_timer_armed (MetaKmsImplDevice *impl_device, + CrtcFrame *crtc_frame, + GError **error) +{ + int64_t next_deadline_us; + int64_t next_presentation_us; + + if (crtc_frame->deadline.armed) + return TRUE; + + if (!meta_kms_crtc_determine_deadline (crtc_frame->crtc, + &next_deadline_us, + &next_presentation_us, + error)) + return FALSE; + + arm_crtc_frame_deadline_timer (crtc_frame, + next_deadline_us, + next_presentation_us); + + return TRUE; +} + +void +meta_kms_impl_device_schedule_process (MetaKmsImplDevice *impl_device, + MetaKmsCrtc *crtc) +{ + MetaKmsImpl *impl = meta_kms_impl_device_get_impl (impl_device); + MetaThreadImpl *thread_impl = META_THREAD_IMPL (impl); + CrtcFrame *crtc_frame; + + crtc_frame = ensure_crtc_frame (impl_device, crtc); + if (crtc_frame->pending_page_flip) + return; + + if (crtc_frame->await_flush) + return; + + if (meta_thread_impl_is_realtime (thread_impl)) + { + g_autoptr (GError) error = NULL; + + if (ensure_deadline_timer_armed (impl_device, crtc_frame, &error)) + return; + + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) + g_critical ("Failed to determine deadline: %s", error->message); + } + + meta_kms_device_set_needs_flush (meta_kms_crtc_get_device (crtc), crtc); +} + +static MetaKmsFeedback * +process_mode_set_update (MetaKmsImplDevice *impl_device, + MetaKmsUpdate *update, + MetaKmsUpdateFlag flags) +{ + MetaKmsImplDevicePrivate *priv = + meta_kms_impl_device_get_instance_private (impl_device); + CrtcFrame *crtc_frame; + GList *l; + GHashTableIter iter; + + for (l = meta_kms_update_get_mode_sets (update); l; l = l->next) + { + MetaKmsModeSet *mode_set = l->data; + MetaKmsCrtc *crtc = mode_set->crtc; + + crtc_frame = get_crtc_frame (impl_device, crtc); + if (!crtc_frame) + continue; + + if (!crtc_frame->pending_update) + continue; + + meta_kms_update_merge_from (update, crtc_frame->pending_update); + g_clear_pointer (&crtc_frame->pending_update, meta_kms_update_free); + } + + g_hash_table_iter_init (&iter, priv->crtc_frames); + while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &crtc_frame)) + { + crtc_frame->deadline.is_deadline_page_flip = FALSE; + crtc_frame->await_flush = FALSE; + crtc_frame->pending_page_flip = FALSE; + g_clear_pointer (&crtc_frame->pending_update, meta_kms_update_free); + disarm_crtc_frame_deadline_timer (crtc_frame); + } + + return do_process (impl_device, NULL, update, flags); +} + +MetaKmsFeedback * +meta_kms_impl_device_process_update (MetaKmsImplDevice *impl_device, + MetaKmsUpdate *update, + MetaKmsUpdateFlag flags) +{ + g_autoptr (GError) error = NULL; + + if (!ensure_device_file (impl_device, &error)) + { + MetaKmsFeedback *feedback = NULL; + + feedback = meta_kms_feedback_new_failed (NULL, g_steal_pointer (&error)); + queue_result_feedback (impl_device, update, feedback); + + meta_kms_update_free (update); + return feedback; + } + + meta_kms_update_realize (update, impl_device); + + if (flags & META_KMS_UPDATE_FLAG_TEST_ONLY) + { + return do_process (impl_device, + meta_kms_update_get_latch_crtc (update), + update, flags); + } + else if (flags & META_KMS_UPDATE_FLAG_MODE_SET) + { + return process_mode_set_update (impl_device, update, flags); + } + else + { + g_assert_not_reached (); + } +} + void meta_kms_impl_device_disable (MetaKmsImplDevice *impl_device) { @@ -1270,12 +1851,15 @@ meta_kms_impl_device_init_mode_setting (MetaKmsImplDevice *impl_device, void meta_kms_impl_device_prepare_shutdown (MetaKmsImplDevice *impl_device) { + MetaKmsImplDevicePrivate *priv = + meta_kms_impl_device_get_instance_private (impl_device); MetaKmsImplDeviceClass *klass = META_KMS_IMPL_DEVICE_GET_CLASS (impl_device); if (klass->prepare_shutdown) klass->prepare_shutdown (impl_device); clear_fd_source (impl_device); + g_clear_pointer (&priv->crtc_frames, g_hash_table_unref); } static gboolean @@ -1323,6 +1907,10 @@ meta_kms_impl_device_initable_init (GInitable *initable, priv->driver_description = g_strdup ("Unknown"); } + priv->crtc_frames = + g_hash_table_new_full (NULL, NULL, + NULL, (GDestroyNotify) crtc_frame_free); + return TRUE; } diff --git a/src/backends/native/meta-kms-impl-device.h b/src/backends/native/meta-kms-impl-device.h index 840ee60b9..4e9ec1d09 100644 --- a/src/backends/native/meta-kms-impl-device.h +++ b/src/backends/native/meta-kms-impl-device.h @@ -177,6 +177,16 @@ MetaKmsFeedback * meta_kms_impl_device_process_update (MetaKmsImplDevice *impl_d MetaKmsUpdateFlag flags) G_GNUC_WARN_UNUSED_RESULT; +void meta_kms_impl_device_handle_update (MetaKmsImplDevice *impl_device, + MetaKmsUpdate *update, + MetaKmsUpdateFlag flags); + +void meta_kms_impl_device_await_flush (MetaKmsImplDevice *impl_device, + MetaKmsCrtc *crtc); + +void meta_kms_impl_device_schedule_process (MetaKmsImplDevice *impl_device, + MetaKmsCrtc *crtc); + void meta_kms_impl_device_handle_page_flip_callback (MetaKmsImplDevice *impl_device, MetaKmsPageFlipData *page_flip_data); diff --git a/src/backends/native/meta-kms-impl.c b/src/backends/native/meta-kms-impl.c index 780019c72..1b8f5493a 100644 --- a/src/backends/native/meta-kms-impl.c +++ b/src/backends/native/meta-kms-impl.c @@ -29,6 +29,8 @@ struct _MetaKmsImpl { GObject parent; + + GPtrArray *update_filters; }; typedef struct _MetaKmsImplPrivate @@ -36,6 +38,12 @@ typedef struct _MetaKmsImplPrivate GList *impl_devices; } MetaKmsImplPrivate; +struct _MetaKmsUpdateFilter +{ + MetaKmsUpdateFilterFunc func; + gpointer user_data; +}; + G_DEFINE_TYPE_WITH_PRIVATE (MetaKmsImpl, meta_kms_impl, META_TYPE_THREAD_IMPL) MetaKms * @@ -103,6 +111,30 @@ meta_kms_impl_notify_modes_set (MetaKmsImpl *impl) NULL); } +static void +meta_kms_update_filter_free (MetaKmsUpdateFilter *filter) +{ + g_free (filter); +} + +MetaKmsUpdate * +meta_kms_impl_filter_update (MetaKmsImpl *impl, + MetaKmsCrtc *crtc, + MetaKmsUpdate *update, + MetaKmsUpdateFlag flags) +{ + int i; + + for (i = 0; i < impl->update_filters->len; i++) + { + MetaKmsUpdateFilter *filter = g_ptr_array_index (impl->update_filters, i); + + update = filter->func (impl, crtc, update, flags, filter->user_data); + } + + return update; +} + MetaKmsImpl * meta_kms_impl_new (MetaKms *kms) { @@ -112,11 +144,49 @@ meta_kms_impl_new (MetaKms *kms) } static void -meta_kms_impl_init (MetaKmsImpl *kms_impl) +meta_kms_impl_init (MetaKmsImpl *impl) { + impl->update_filters = + g_ptr_array_new_with_free_func ((GDestroyNotify) meta_kms_update_filter_free); +} + +static void +meta_kms_impl_finalize (GObject *object) +{ + MetaKmsImpl *impl = META_KMS_IMPL (object); + + g_clear_pointer (&impl->update_filters, g_ptr_array_unref); + + G_OBJECT_CLASS (meta_kms_impl_parent_class)->finalize (object); } static void meta_kms_impl_class_init (MetaKmsImplClass *klass) { + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = meta_kms_impl_finalize; +} + +MetaKmsUpdateFilter * +meta_kms_impl_add_update_filter (MetaKmsImpl *impl, + MetaKmsUpdateFilterFunc func, + gpointer user_data) +{ + MetaKmsUpdateFilter *filter; + + filter = g_new0 (MetaKmsUpdateFilter, 1); + filter->func = func; + filter->user_data = user_data; + + g_ptr_array_add (impl->update_filters, filter); + + return filter; +} + +void +meta_kms_impl_remove_update_filter (MetaKmsImpl *impl, + MetaKmsUpdateFilter *filter) +{ + g_ptr_array_remove (impl->update_filters, filter); } diff --git a/src/backends/native/meta-kms-impl.h b/src/backends/native/meta-kms-impl.h index d2de35d30..04bd1b089 100644 --- a/src/backends/native/meta-kms-impl.h +++ b/src/backends/native/meta-kms-impl.h @@ -26,10 +26,18 @@ #include "backends/native/meta-kms.h" #include "backends/native/meta-thread-impl.h" +typedef struct _MetaKmsUpdateFilter MetaKmsUpdateFilter; + #define META_TYPE_KMS_IMPL (meta_kms_impl_get_type ()) G_DECLARE_FINAL_TYPE (MetaKmsImpl, meta_kms_impl, META, KMS_IMPL, MetaThreadImpl) +typedef MetaKmsUpdate * (* MetaKmsUpdateFilterFunc) (MetaKmsImpl *impl_device, + MetaKmsCrtc *crtc, + MetaKmsUpdate *update, + MetaKmsUpdateFlag flags, + gpointer user_data); + MetaKms * meta_kms_impl_get_kms (MetaKmsImpl *impl); void meta_kms_impl_add_impl_device (MetaKmsImpl *impl, @@ -46,4 +54,16 @@ void meta_kms_impl_notify_modes_set (MetaKmsImpl *impl); MetaKmsImpl * meta_kms_impl_new (MetaKms *kms); +MetaKmsUpdateFilter * meta_kms_impl_add_update_filter (MetaKmsImpl *impl, + MetaKmsUpdateFilterFunc func, + gpointer user_data); + +void meta_kms_impl_remove_update_filter (MetaKmsImpl *impl, + MetaKmsUpdateFilter *filter); + +MetaKmsUpdate * meta_kms_impl_filter_update (MetaKmsImpl *impl, + MetaKmsCrtc *crtc, + MetaKmsUpdate *update, + MetaKmsUpdateFlag flags); + #endif /* META_KMS_IMPL_H */ diff --git a/src/backends/native/meta-kms-page-flip.c b/src/backends/native/meta-kms-page-flip.c index 5eeb56c12..c2388ae4f 100644 --- a/src/backends/native/meta-kms-page-flip.c +++ b/src/backends/native/meta-kms-page-flip.c @@ -166,8 +166,6 @@ invoke_page_flip_closure_flipped (MetaThread *thread, MetaKmsPageFlipClosure *closure = user_data; MetaKmsPageFlipData *page_flip_data = closure->page_flip_data; - meta_assert_not_in_kms_impl (META_KMS (thread)); - if (page_flip_data->is_symbolic) { closure->vtable->ready (page_flip_data->crtc, @@ -251,8 +249,6 @@ invoke_page_flip_closure_mode_set_fallback (MetaThread *thread, MetaKmsPageFlipClosure *closure = user_data; MetaKmsPageFlipData *page_flip_data = closure->page_flip_data; - meta_assert_not_in_kms_impl (META_KMS (thread)); - closure->vtable->mode_set_fallback (page_flip_data->crtc, closure->user_data); } @@ -289,8 +285,6 @@ invoke_page_flip_closure_discarded (MetaThread *thread, MetaKmsPageFlipClosure *closure = user_data; MetaKmsPageFlipData *page_flip_data = closure->page_flip_data; - meta_assert_not_in_kms_impl (META_KMS (thread)); - closure->vtable->discarded (page_flip_data->crtc, closure->user_data, page_flip_data->error); diff --git a/src/backends/native/meta-kms-update-private.h b/src/backends/native/meta-kms-update-private.h index 8d7571343..6e723fd83 100644 --- a/src/backends/native/meta-kms-update-private.h +++ b/src/backends/native/meta-kms-update-private.h @@ -206,6 +206,12 @@ void meta_kms_update_realize (MetaKmsUpdate *update, gboolean meta_kms_update_get_needs_modeset (MetaKmsUpdate *update); +MetaKmsCrtc * meta_kms_update_get_latch_crtc (MetaKmsUpdate *update); + +void meta_kms_page_flip_listener_unref (MetaKmsPageFlipListener *listener); + +gboolean meta_kms_update_is_empty (MetaKmsUpdate *update); + G_DEFINE_AUTOPTR_CLEANUP_FUNC (MetaKmsPlaneFeedback, meta_kms_plane_feedback_free) diff --git a/src/backends/native/meta-kms-update.c b/src/backends/native/meta-kms-update.c index 35fe816fb..f0e3eda8b 100644 --- a/src/backends/native/meta-kms-update.c +++ b/src/backends/native/meta-kms-update.c @@ -36,6 +36,9 @@ struct _MetaKmsUpdate gboolean is_sealed; + gboolean is_latchable; + MetaKmsCrtc *latch_crtc; + GList *mode_sets; GList *plane_assignments; GList *connector_updates; @@ -199,7 +202,7 @@ meta_kms_mode_set_free (MetaKmsModeSet *mode_set) g_free (mode_set); } -static void +void meta_kms_page_flip_listener_unref (MetaKmsPageFlipListener *listener) { MetaKmsDevice *device; @@ -248,6 +251,27 @@ meta_kms_update_drop_plane_assignment (MetaKmsUpdate *update, drop_plane_assignment (update, plane, NULL); } +static void +update_latch_crtc (MetaKmsUpdate *update, + MetaKmsCrtc *crtc) +{ + if (update->is_latchable) + { + if (update->latch_crtc) + { + if (update->latch_crtc != crtc) + { + update->is_latchable = FALSE; + update->latch_crtc = NULL; + } + } + else + { + update->latch_crtc = crtc; + } + } +} + MetaKmsPlaneAssignment * meta_kms_update_assign_plane (MetaKmsUpdate *update, MetaKmsCrtc *crtc, @@ -286,6 +310,8 @@ meta_kms_update_assign_plane (MetaKmsUpdate *update, update->plane_assignments = g_list_prepend (update->plane_assignments, plane_assignment); + update_latch_crtc (update, crtc); + return plane_assignment; } @@ -310,6 +336,8 @@ meta_kms_update_unassign_plane (MetaKmsUpdate *update, update->plane_assignments = g_list_prepend (update->plane_assignments, plane_assignment); + update_latch_crtc (update, crtc); + return plane_assignment; } @@ -490,6 +518,8 @@ meta_kms_update_set_crtc_gamma (MetaKmsUpdate *update, color_update = ensure_color_update (update, crtc); color_update->gamma.state = gamma_update; color_update->gamma.has_update = TRUE; + + update_latch_crtc (update, crtc); } static void @@ -1016,6 +1046,7 @@ meta_kms_update_new (MetaKmsDevice *device) update = g_new0 (MetaKmsUpdate, 1); update->device = device; + update->is_latchable = TRUE; return update; } @@ -1049,3 +1080,32 @@ meta_kms_update_realize (MetaKmsUpdate *update, update->impl_device = impl_device; meta_kms_impl_device_hold_fd (impl_device); } + +void +meta_kms_update_set_flushing (MetaKmsUpdate *update, + MetaKmsCrtc *crtc) +{ + update_latch_crtc (update, crtc); +} + +gboolean +meta_kms_update_is_flushing (MetaKmsUpdate *update, + MetaKmsCrtc *crtc) +{ + return update->latch_crtc == crtc; +} + +MetaKmsCrtc * +meta_kms_update_get_latch_crtc (MetaKmsUpdate *update) +{ + return update->latch_crtc; +} + +gboolean +meta_kms_update_is_empty (MetaKmsUpdate *update) +{ + return (!update->mode_sets && + !update->plane_assignments && + !update->connector_updates && + !update->crtc_color_updates); +} diff --git a/src/backends/native/meta-kms-update.h b/src/backends/native/meta-kms-update.h index 31bf36bc8..10db07b7d 100644 --- a/src/backends/native/meta-kms-update.h +++ b/src/backends/native/meta-kms-update.h @@ -108,6 +108,12 @@ MetaKmsUpdate * meta_kms_update_new (MetaKmsDevice *device); META_EXPORT_TEST void meta_kms_update_free (MetaKmsUpdate *update); +void meta_kms_update_set_flushing (MetaKmsUpdate *update, + MetaKmsCrtc *crtc); + +gboolean meta_kms_update_is_flushing (MetaKmsUpdate *update, + MetaKmsCrtc *crtc); + META_EXPORT_TEST MetaKmsDevice * meta_kms_update_get_device (MetaKmsUpdate *update); diff --git a/src/backends/native/meta-kms.h b/src/backends/native/meta-kms.h index 02374b78d..8509165de 100644 --- a/src/backends/native/meta-kms.h +++ b/src/backends/native/meta-kms.h @@ -31,6 +31,7 @@ enum META_KMS_ERROR_USER_INHIBITED, META_KMS_ERROR_DENY_LISTED, META_KMS_ERROR_NOT_SUPPORTED, + META_KMS_ERROR_EMPTY_UPDATE, }; typedef enum _MetaKmsFlags diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c index eadb7e3d4..b85a8e474 100644 --- a/src/backends/native/meta-onscreen-native.c +++ b/src/backends/native/meta-onscreen-native.c @@ -1428,6 +1428,18 @@ add_onscreen_frame_info (MetaCrtc *crtc) CLUTTER_STAGE_VIEW (view)); } +void +meta_onscreen_native_before_redraw (CoglOnscreen *onscreen, + ClutterFrame *frame) +{ + 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); +} + void meta_onscreen_native_prepare_frame (CoglOnscreen *onscreen, ClutterFrame *frame) @@ -1513,7 +1525,10 @@ finish_frame_result_feedback (const MetaKmsFeedback *kms_feedback, if (!g_error_matches (error, G_IO_ERROR, - G_IO_ERROR_PERMISSION_DENIED)) + G_IO_ERROR_PERMISSION_DENIED) && + !g_error_matches (error, + META_KMS_ERROR, + META_KMS_ERROR_EMPTY_UPDATE)) g_warning ("Cursor update failed: %s", error->message); frame_info = cogl_onscreen_peek_head_frame_info (onscreen); @@ -1540,8 +1555,16 @@ meta_onscreen_native_finish_frame (CoglOnscreen *onscreen, kms_update = meta_frame_native_steal_kms_update (frame_native); if (!kms_update) { - clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_IDLE); - return; + if (meta_kms_device_handle_flush (kms_device, kms_crtc)) + { + kms_update = meta_kms_update_new (kms_device); + meta_kms_update_set_flushing (kms_update, kms_crtc); + } + else + { + clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_IDLE); + return; + } } meta_kms_update_add_result_listener (kms_update, @@ -1563,6 +1586,7 @@ meta_onscreen_native_finish_frame (CoglOnscreen *onscreen, meta_kms_crtc_get_id (kms_crtc), meta_kms_device_get_path (kms_device)); + meta_kms_update_set_flushing (kms_update, kms_crtc); meta_kms_device_post_update (kms_device, kms_update, META_KMS_UPDATE_FLAG_NONE); clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_PENDING_PRESENTED); diff --git a/src/backends/native/meta-onscreen-native.h b/src/backends/native/meta-onscreen-native.h index 05a9ecdb8..3ec3277a3 100644 --- a/src/backends/native/meta-onscreen-native.h +++ b/src/backends/native/meta-onscreen-native.h @@ -40,6 +40,9 @@ void meta_renderer_native_release_onscreen (CoglOnscreen *onscreen); void meta_onscreen_native_prepare_frame (CoglOnscreen *onscreen, ClutterFrame *frame); +void meta_onscreen_native_before_redraw (CoglOnscreen *onscreen, + ClutterFrame *frame); + void meta_onscreen_native_finish_frame (CoglOnscreen *onscreen, ClutterFrame *frame); diff --git a/src/backends/native/meta-renderer-native-private.h b/src/backends/native/meta-renderer-native-private.h index 5e741bff7..19d06e82d 100644 --- a/src/backends/native/meta-renderer-native-private.h +++ b/src/backends/native/meta-renderer-native-private.h @@ -66,6 +66,8 @@ typedef struct _MetaRendererNativeGpuData EGLContext egl_context; EGLConfig egl_config; } secondary; + + gulong crtc_needs_flush_handler_id; } MetaRendererNativeGpuData; MetaEgl * meta_renderer_native_get_egl (MetaRendererNative *renderer_native); diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c index 18b240328..fd37d00b5 100644 --- a/src/backends/native/meta-renderer-native.c +++ b/src/backends/native/meta-renderer-native.c @@ -137,7 +137,7 @@ meta_renderer_native_gpu_data_free (MetaRendererNativeGpuData *renderer_gpu_data MetaRenderer *renderer = META_RENDERER (renderer_gpu_data->renderer_native); MetaBackend *backend = meta_renderer_get_backend (renderer); MetaCursorRenderer *cursor_renderer; - MetaGpuKms *gpu_kms; + MetaGpuKms *gpu_kms = renderer_gpu_data->gpu_kms; GList *l; if (renderer_gpu_data->secondary.egl_context != EGL_NO_CONTEXT) @@ -155,7 +155,6 @@ meta_renderer_native_gpu_data_free (MetaRendererNativeGpuData *renderer_gpu_data } cursor_renderer = meta_backend_get_cursor_renderer (backend); - gpu_kms = renderer_gpu_data->gpu_kms; if (cursor_renderer && gpu_kms) { MetaCursorRendererNative *cursor_renderer_native = @@ -175,6 +174,12 @@ meta_renderer_native_gpu_data_free (MetaRendererNativeGpuData *renderer_gpu_data } } + if (renderer_gpu_data->crtc_needs_flush_handler_id) + { + g_clear_signal_handler (&renderer_gpu_data->crtc_needs_flush_handler_id, + meta_gpu_kms_get_kms_device (gpu_kms)); + } + g_clear_pointer (&renderer_gpu_data->render_device, g_object_unref); g_free (renderer_gpu_data); } @@ -1542,6 +1547,22 @@ meta_renderer_native_prepare_frame (MetaRendererNative *renderer_native, } } +void +meta_renderer_native_before_redraw (MetaRendererNative *renderer_native, + MetaRendererView *view, + ClutterFrame *frame) +{ + CoglFramebuffer *framebuffer = + clutter_stage_view_get_onscreen (CLUTTER_STAGE_VIEW (view)); + + if (COGL_IS_ONSCREEN (framebuffer)) + { + CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer); + + meta_onscreen_native_before_redraw (onscreen, frame); + } +} + void meta_renderer_native_finish_frame (MetaRendererNative *renderer_native, MetaRendererView *view, @@ -1831,6 +1852,19 @@ create_renderer_gpu_data_egl_device (MetaRendererNative *renderer_native, } #endif /* HAVE_EGL_DEVICE */ +static void +on_crtc_needs_flush (MetaKmsDevice *kms_device, + MetaKmsCrtc *kms_crtc, + MetaRenderer *renderer) +{ + MetaCrtc *crtc = META_CRTC (meta_crtc_kms_from_kms_crtc (kms_crtc)); + MetaRendererView *view; + + view = meta_renderer_get_view_for_crtc (renderer, crtc); + if (view) + clutter_stage_view_schedule_update (CLUTTER_STAGE_VIEW (view)); +} + static MetaRendererNativeGpuData * meta_renderer_native_create_renderer_gpu_data (MetaRendererNative *renderer_native, MetaGpuKms *gpu_kms, @@ -1841,6 +1875,7 @@ meta_renderer_native_create_renderer_gpu_data (MetaRendererNative *renderer_nat MetaBackendNative *backend_native = META_BACKEND_NATIVE (backend); const char *device_path; MetaRenderDevice *render_device; + MetaRendererNativeGpuData *renderer_gpu_data; if (!gpu_kms) return create_renderer_gpu_data_surfaceless (renderer_native, error); @@ -1856,16 +1891,16 @@ meta_renderer_native_create_renderer_gpu_data (MetaRendererNative *renderer_nat if (META_IS_RENDER_DEVICE_GBM (render_device)) { - return create_renderer_gpu_data_gbm (renderer_native, - render_device, - gpu_kms); + renderer_gpu_data = create_renderer_gpu_data_gbm (renderer_native, + render_device, + gpu_kms); } #ifdef HAVE_EGL_DEVICE else if (META_IS_RENDER_DEVICE_EGL_STREAM (render_device)) { - return create_renderer_gpu_data_egl_device (renderer_native, - render_device, - gpu_kms); + renderer_gpu_data = create_renderer_gpu_data_egl_device (renderer_native, + render_device, + gpu_kms); } #endif else @@ -1873,6 +1908,13 @@ meta_renderer_native_create_renderer_gpu_data (MetaRendererNative *renderer_nat g_assert_not_reached (); return NULL; } + + renderer_gpu_data->crtc_needs_flush_handler_id = + g_signal_connect (meta_gpu_kms_get_kms_device (gpu_kms), + "crtc-needs-flush", + G_CALLBACK (on_crtc_needs_flush), + renderer_native); + return renderer_gpu_data; } static const char * diff --git a/src/backends/native/meta-renderer-native.h b/src/backends/native/meta-renderer-native.h index f34881889..3af3666cc 100644 --- a/src/backends/native/meta-renderer-native.h +++ b/src/backends/native/meta-renderer-native.h @@ -61,6 +61,10 @@ void meta_renderer_native_prepare_frame (MetaRendererNative *renderer_native, MetaRendererView *view, ClutterFrame *frame); +void meta_renderer_native_before_redraw (MetaRendererNative *renderer_native, + MetaRendererView *view, + ClutterFrame *frame); + void meta_renderer_native_finish_frame (MetaRendererNative *renderer_native, MetaRendererView *view, ClutterFrame *frame); diff --git a/src/backends/native/meta-stage-native.c b/src/backends/native/meta-stage-native.c index 8cdf6cb02..031d82d8f 100644 --- a/src/backends/native/meta-stage-native.c +++ b/src/backends/native/meta-stage-native.c @@ -139,8 +139,14 @@ meta_stage_native_redraw_view (ClutterStageWindow *stage_window, ClutterStageView *view, ClutterFrame *frame) { + MetaStageImpl *stage_impl = META_STAGE_IMPL (stage_window); + MetaBackend *backend = meta_stage_impl_get_backend (stage_impl); + MetaRenderer *renderer = meta_backend_get_renderer (backend); MetaCrtc *crtc; + meta_renderer_native_before_redraw (META_RENDERER_NATIVE (renderer), + META_RENDERER_VIEW (view), frame); + clutter_stage_window_parent_iface->redraw_view (stage_window, view, frame); crtc = meta_renderer_view_get_crtc (META_RENDERER_VIEW (view));