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));