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: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2777>
This commit is contained in:
Jonas Ådahl 2022-10-26 19:08:30 +02:00
parent 48501236ec
commit 27ed069766
20 changed files with 1067 additions and 35 deletions

View File

@ -56,4 +56,9 @@ uint64_t meta_kms_crtc_get_prop_drm_value (MetaKmsCrtc *crtc,
MetaKmsCrtcProp prop, MetaKmsCrtcProp prop,
uint64_t value); 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 */ #endif /* META_KMS_CRTC_PRIVATE_H */

View File

@ -28,6 +28,10 @@
#include "backends/native/meta-kms-impl-device-simple.h" #include "backends/native/meta-kms-impl-device-simple.h"
#include "backends/native/meta-kms-mode.h" #include "backends/native/meta-kms-mode.h"
#include "backends/native/meta-kms-update-private.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 typedef struct _MetaKmsCrtcPropTable
{ {
@ -487,3 +491,92 @@ meta_kms_crtc_class_init (MetaKmsCrtcClass *klass)
object_class->finalize = meta_kms_crtc_finalize; 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;
}

View File

@ -40,4 +40,7 @@ MetaKmsCrtc * meta_kms_device_find_crtc_in_impl (MetaKmsDevice *device,
MetaKmsConnector * meta_kms_device_find_connector_in_impl (MetaKmsDevice *device, MetaKmsConnector * meta_kms_device_find_connector_in_impl (MetaKmsDevice *device,
uint32_t connector_id); uint32_t connector_id);
void meta_kms_device_set_needs_flush (MetaKmsDevice *device,
MetaKmsCrtc *crtc);
#endif /* META_KMS_DEVICE_PRIVATE_H */ #endif /* META_KMS_DEVICE_PRIVATE_H */

View File

@ -41,6 +41,15 @@
#include "backends/native/meta-kms-private.h" #include "backends/native/meta-kms-private.h"
#include "backends/native/meta-kms-update-private.h" #include "backends/native/meta-kms-update-private.h"
enum
{
CRTC_NEEDS_FLUSH,
N_SIGNALS
};
static int signals[N_SIGNALS];
struct _MetaKmsDevice struct _MetaKmsDevice
{ {
GObject parent; GObject parent;
@ -61,6 +70,9 @@ struct _MetaKmsDevice
MetaKmsDeviceCaps caps; MetaKmsDeviceCaps caps;
GList *fallback_modes; GList *fallback_modes;
GHashTable *needs_flush_crtcs;
GMutex needs_flush_mutex;
}; };
G_DEFINE_TYPE (MetaKmsDevice, meta_kms_device, G_TYPE_OBJECT); 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; MetaKmsUpdate *update = data->update;
MetaKmsDevice *device = meta_kms_update_get_device (update); MetaKmsDevice *device = meta_kms_update_get_device (update);
MetaKmsImplDevice *impl_device = meta_kms_device_get_impl_device (device); MetaKmsImplDevice *impl_device = meta_kms_device_get_impl_device (device);
MetaKmsFeedback *feedback;
feedback = meta_kms_impl_device_process_update (impl_device, update, meta_kms_impl_device_handle_update (impl_device, update, data->flags);
data->flags);
meta_kms_feedback_unref (feedback);
return GINT_TO_POINTER (TRUE); return GINT_TO_POINTER (TRUE);
} }
@ -357,6 +366,72 @@ meta_kms_device_post_update (MetaKmsDevice *device,
NULL, NULL); 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 void
meta_kms_device_add_fake_plane_in_impl (MetaKmsDevice *device, meta_kms_device_add_fake_plane_in_impl (MetaKmsDevice *device,
MetaKmsPlaneType plane_type, MetaKmsPlaneType plane_type,
@ -631,12 +706,17 @@ meta_kms_device_finalize (GObject *object)
NULL); 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); G_OBJECT_CLASS (meta_kms_device_parent_class)->finalize (object);
} }
static void static void
meta_kms_device_init (MetaKmsDevice *device) 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 static void
@ -645,4 +725,13 @@ meta_kms_device_class_init (MetaKmsDeviceClass *klass)
GObjectClass *object_class = G_OBJECT_CLASS (klass); GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = meta_kms_device_finalize; 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);
} }

View File

@ -83,6 +83,12 @@ void meta_kms_device_post_update (MetaKmsDevice *device,
MetaKmsUpdate *update, MetaKmsUpdate *update,
MetaKmsUpdateFlag flags); 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 META_EXPORT_TEST
void meta_kms_device_disable (MetaKmsDevice *device); void meta_kms_device_disable (MetaKmsDevice *device);

View File

@ -22,6 +22,8 @@
#include "backends/native/meta-kms-impl-device.h" #include "backends/native/meta-kms-impl-device.h"
#include <errno.h> #include <errno.h>
#include <glib/gstdio.h>
#include <sys/timerfd.h>
#include <xf86drm.h> #include <xf86drm.h>
#include "backends/native/meta-backend-native.h" #include "backends/native/meta-backend-native.h"
@ -30,12 +32,14 @@
#include "backends/native/meta-kms-connector.h" #include "backends/native/meta-kms-connector.h"
#include "backends/native/meta-kms-crtc-private.h" #include "backends/native/meta-kms-crtc-private.h"
#include "backends/native/meta-kms-crtc.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-impl.h"
#include "backends/native/meta-kms-mode-private.h" #include "backends/native/meta-kms-mode-private.h"
#include "backends/native/meta-kms-page-flip-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-private.h"
#include "backends/native/meta-kms-plane.h" #include "backends/native/meta-kms-plane.h"
#include "backends/native/meta-kms-private.h" #include "backends/native/meta-kms-private.h"
#include "backends/native/meta-thread-private.h"
#include "meta-default-modes.h" #include "meta-default-modes.h"
#include "meta-private-enum-types.h" #include "meta-private-enum-types.h"
@ -54,6 +58,23 @@ enum
static GParamSpec *obj_props[N_PROPS]; 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 typedef struct _MetaKmsImplDevicePrivate
{ {
MetaKmsDevice *device; MetaKmsDevice *device;
@ -76,11 +97,16 @@ typedef struct _MetaKmsImplDevicePrivate
MetaKmsDeviceCaps caps; MetaKmsDeviceCaps caps;
GList *fallback_modes; GList *fallback_modes;
GHashTable *crtc_frames;
} MetaKmsImplDevicePrivate; } MetaKmsImplDevicePrivate;
static void static void
initable_iface_init (GInitableIface *iface); 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_DEFINE_TYPE_WITH_CODE (MetaKmsImplDevice, meta_kms_impl_device,
G_TYPE_OBJECT, G_TYPE_OBJECT,
G_ADD_PRIVATE (MetaKmsImplDevice) G_ADD_PRIVATE (MetaKmsImplDevice)
@ -868,6 +894,7 @@ ensure_device_file (MetaKmsImplDevice *impl_device,
meta_device_file_get_fd (device_file), meta_device_file_get_fd (device_file),
kms_event_dispatch_in_impl, kms_event_dispatch_in_impl,
impl_device); impl_device);
g_source_set_priority (priv->fd_source, G_PRIORITY_HIGH);
} }
return TRUE; return TRUE;
@ -954,6 +981,7 @@ err:
g_clear_list (&priv->planes, g_object_unref); g_clear_list (&priv->planes, g_object_unref);
g_clear_list (&priv->crtcs, g_object_unref); g_clear_list (&priv->crtcs, g_object_unref);
g_clear_list (&priv->connectors, 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; 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); 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 static void
emit_resources_changed_callback (MetaThread *thread, emit_resources_changed_callback (MetaThread *thread,
gpointer user_data) gpointer user_data)
@ -1028,28 +1191,71 @@ queue_result_feedback (MetaKmsImplDevice *impl_device,
} }
} }
static MetaKmsFeedback *
MetaKmsFeedback * do_process (MetaKmsImplDevice *impl_device,
meta_kms_impl_device_process_update (MetaKmsImplDevice *impl_device, MetaKmsCrtc *latch_crtc,
MetaKmsUpdate *update, MetaKmsUpdate *update,
MetaKmsUpdateFlag flags) MetaKmsUpdateFlag flags)
{ {
MetaKmsImplDevicePrivate *priv = MetaKmsImplDevicePrivate *priv =
meta_kms_impl_device_get_instance_private (impl_device); 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); MetaKmsImplDeviceClass *klass = META_KMS_IMPL_DEVICE_GET_CLASS (impl_device);
CrtcFrame *crtc_frame = NULL;
MetaKmsFeedback *feedback; MetaKmsFeedback *feedback;
g_autoptr (GError) error = NULL;
MetaKmsResourceChanges changes = META_KMS_RESOURCE_CHANGE_NONE; 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); GError *error;
return meta_kms_feedback_new_failed (NULL, g_steal_pointer (&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); 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)) if (!(flags & META_KMS_UPDATE_FLAG_TEST_ONLY))
changes = meta_kms_impl_device_predict_states (impl_device, update); 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) if (changes != META_KMS_RESOURCE_CHANGE_NONE)
{ {
MetaKms *kms = meta_kms_device_get_kms (priv->device);
meta_kms_queue_callback (kms, meta_kms_queue_callback (kms,
NULL, NULL,
emit_resources_changed_callback, emit_resources_changed_callback,
@ -1069,6 +1273,383 @@ meta_kms_impl_device_process_update (MetaKmsImplDevice *impl_device,
return feedback; 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 void
meta_kms_impl_device_disable (MetaKmsImplDevice *impl_device) meta_kms_impl_device_disable (MetaKmsImplDevice *impl_device)
{ {
@ -1270,12 +1851,15 @@ meta_kms_impl_device_init_mode_setting (MetaKmsImplDevice *impl_device,
void void
meta_kms_impl_device_prepare_shutdown (MetaKmsImplDevice *impl_device) 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); MetaKmsImplDeviceClass *klass = META_KMS_IMPL_DEVICE_GET_CLASS (impl_device);
if (klass->prepare_shutdown) if (klass->prepare_shutdown)
klass->prepare_shutdown (impl_device); klass->prepare_shutdown (impl_device);
clear_fd_source (impl_device); clear_fd_source (impl_device);
g_clear_pointer (&priv->crtc_frames, g_hash_table_unref);
} }
static gboolean static gboolean
@ -1323,6 +1907,10 @@ meta_kms_impl_device_initable_init (GInitable *initable,
priv->driver_description = g_strdup ("Unknown"); priv->driver_description = g_strdup ("Unknown");
} }
priv->crtc_frames =
g_hash_table_new_full (NULL, NULL,
NULL, (GDestroyNotify) crtc_frame_free);
return TRUE; return TRUE;
} }

View File

@ -177,6 +177,16 @@ MetaKmsFeedback * meta_kms_impl_device_process_update (MetaKmsImplDevice *impl_d
MetaKmsUpdateFlag flags) MetaKmsUpdateFlag flags)
G_GNUC_WARN_UNUSED_RESULT; 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, void meta_kms_impl_device_handle_page_flip_callback (MetaKmsImplDevice *impl_device,
MetaKmsPageFlipData *page_flip_data); MetaKmsPageFlipData *page_flip_data);

View File

@ -29,6 +29,8 @@
struct _MetaKmsImpl struct _MetaKmsImpl
{ {
GObject parent; GObject parent;
GPtrArray *update_filters;
}; };
typedef struct _MetaKmsImplPrivate typedef struct _MetaKmsImplPrivate
@ -36,6 +38,12 @@ typedef struct _MetaKmsImplPrivate
GList *impl_devices; GList *impl_devices;
} MetaKmsImplPrivate; } MetaKmsImplPrivate;
struct _MetaKmsUpdateFilter
{
MetaKmsUpdateFilterFunc func;
gpointer user_data;
};
G_DEFINE_TYPE_WITH_PRIVATE (MetaKmsImpl, meta_kms_impl, META_TYPE_THREAD_IMPL) G_DEFINE_TYPE_WITH_PRIVATE (MetaKmsImpl, meta_kms_impl, META_TYPE_THREAD_IMPL)
MetaKms * MetaKms *
@ -103,6 +111,30 @@ meta_kms_impl_notify_modes_set (MetaKmsImpl *impl)
NULL); 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 * MetaKmsImpl *
meta_kms_impl_new (MetaKms *kms) meta_kms_impl_new (MetaKms *kms)
{ {
@ -112,11 +144,49 @@ meta_kms_impl_new (MetaKms *kms)
} }
static void 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 static void
meta_kms_impl_class_init (MetaKmsImplClass *klass) 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);
} }

View File

@ -26,10 +26,18 @@
#include "backends/native/meta-kms.h" #include "backends/native/meta-kms.h"
#include "backends/native/meta-thread-impl.h" #include "backends/native/meta-thread-impl.h"
typedef struct _MetaKmsUpdateFilter MetaKmsUpdateFilter;
#define META_TYPE_KMS_IMPL (meta_kms_impl_get_type ()) #define META_TYPE_KMS_IMPL (meta_kms_impl_get_type ())
G_DECLARE_FINAL_TYPE (MetaKmsImpl, meta_kms_impl, G_DECLARE_FINAL_TYPE (MetaKmsImpl, meta_kms_impl,
META, KMS_IMPL, MetaThreadImpl) 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); MetaKms * meta_kms_impl_get_kms (MetaKmsImpl *impl);
void meta_kms_impl_add_impl_device (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); 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 */ #endif /* META_KMS_IMPL_H */

View File

@ -166,8 +166,6 @@ invoke_page_flip_closure_flipped (MetaThread *thread,
MetaKmsPageFlipClosure *closure = user_data; MetaKmsPageFlipClosure *closure = user_data;
MetaKmsPageFlipData *page_flip_data = closure->page_flip_data; MetaKmsPageFlipData *page_flip_data = closure->page_flip_data;
meta_assert_not_in_kms_impl (META_KMS (thread));
if (page_flip_data->is_symbolic) if (page_flip_data->is_symbolic)
{ {
closure->vtable->ready (page_flip_data->crtc, closure->vtable->ready (page_flip_data->crtc,
@ -251,8 +249,6 @@ invoke_page_flip_closure_mode_set_fallback (MetaThread *thread,
MetaKmsPageFlipClosure *closure = user_data; MetaKmsPageFlipClosure *closure = user_data;
MetaKmsPageFlipData *page_flip_data = closure->page_flip_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->vtable->mode_set_fallback (page_flip_data->crtc,
closure->user_data); closure->user_data);
} }
@ -289,8 +285,6 @@ invoke_page_flip_closure_discarded (MetaThread *thread,
MetaKmsPageFlipClosure *closure = user_data; MetaKmsPageFlipClosure *closure = user_data;
MetaKmsPageFlipData *page_flip_data = closure->page_flip_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->vtable->discarded (page_flip_data->crtc,
closure->user_data, closure->user_data,
page_flip_data->error); page_flip_data->error);

View File

@ -206,6 +206,12 @@ void meta_kms_update_realize (MetaKmsUpdate *update,
gboolean meta_kms_update_get_needs_modeset (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, G_DEFINE_AUTOPTR_CLEANUP_FUNC (MetaKmsPlaneFeedback,
meta_kms_plane_feedback_free) meta_kms_plane_feedback_free)

View File

@ -36,6 +36,9 @@ struct _MetaKmsUpdate
gboolean is_sealed; gboolean is_sealed;
gboolean is_latchable;
MetaKmsCrtc *latch_crtc;
GList *mode_sets; GList *mode_sets;
GList *plane_assignments; GList *plane_assignments;
GList *connector_updates; GList *connector_updates;
@ -199,7 +202,7 @@ meta_kms_mode_set_free (MetaKmsModeSet *mode_set)
g_free (mode_set); g_free (mode_set);
} }
static void void
meta_kms_page_flip_listener_unref (MetaKmsPageFlipListener *listener) meta_kms_page_flip_listener_unref (MetaKmsPageFlipListener *listener)
{ {
MetaKmsDevice *device; MetaKmsDevice *device;
@ -248,6 +251,27 @@ meta_kms_update_drop_plane_assignment (MetaKmsUpdate *update,
drop_plane_assignment (update, plane, NULL); 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 * MetaKmsPlaneAssignment *
meta_kms_update_assign_plane (MetaKmsUpdate *update, meta_kms_update_assign_plane (MetaKmsUpdate *update,
MetaKmsCrtc *crtc, MetaKmsCrtc *crtc,
@ -286,6 +310,8 @@ meta_kms_update_assign_plane (MetaKmsUpdate *update,
update->plane_assignments = g_list_prepend (update->plane_assignments, update->plane_assignments = g_list_prepend (update->plane_assignments,
plane_assignment); plane_assignment);
update_latch_crtc (update, crtc);
return plane_assignment; return plane_assignment;
} }
@ -310,6 +336,8 @@ meta_kms_update_unassign_plane (MetaKmsUpdate *update,
update->plane_assignments = g_list_prepend (update->plane_assignments, update->plane_assignments = g_list_prepend (update->plane_assignments,
plane_assignment); plane_assignment);
update_latch_crtc (update, crtc);
return plane_assignment; return plane_assignment;
} }
@ -490,6 +518,8 @@ meta_kms_update_set_crtc_gamma (MetaKmsUpdate *update,
color_update = ensure_color_update (update, crtc); color_update = ensure_color_update (update, crtc);
color_update->gamma.state = gamma_update; color_update->gamma.state = gamma_update;
color_update->gamma.has_update = TRUE; color_update->gamma.has_update = TRUE;
update_latch_crtc (update, crtc);
} }
static void static void
@ -1016,6 +1046,7 @@ meta_kms_update_new (MetaKmsDevice *device)
update = g_new0 (MetaKmsUpdate, 1); update = g_new0 (MetaKmsUpdate, 1);
update->device = device; update->device = device;
update->is_latchable = TRUE;
return update; return update;
} }
@ -1049,3 +1080,32 @@ meta_kms_update_realize (MetaKmsUpdate *update,
update->impl_device = impl_device; update->impl_device = impl_device;
meta_kms_impl_device_hold_fd (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);
}

View File

@ -108,6 +108,12 @@ MetaKmsUpdate * meta_kms_update_new (MetaKmsDevice *device);
META_EXPORT_TEST META_EXPORT_TEST
void meta_kms_update_free (MetaKmsUpdate *update); 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 META_EXPORT_TEST
MetaKmsDevice * meta_kms_update_get_device (MetaKmsUpdate *update); MetaKmsDevice * meta_kms_update_get_device (MetaKmsUpdate *update);

View File

@ -31,6 +31,7 @@ enum
META_KMS_ERROR_USER_INHIBITED, META_KMS_ERROR_USER_INHIBITED,
META_KMS_ERROR_DENY_LISTED, META_KMS_ERROR_DENY_LISTED,
META_KMS_ERROR_NOT_SUPPORTED, META_KMS_ERROR_NOT_SUPPORTED,
META_KMS_ERROR_EMPTY_UPDATE,
}; };
typedef enum _MetaKmsFlags typedef enum _MetaKmsFlags

View File

@ -1428,6 +1428,18 @@ add_onscreen_frame_info (MetaCrtc *crtc)
CLUTTER_STAGE_VIEW (view)); 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 void
meta_onscreen_native_prepare_frame (CoglOnscreen *onscreen, meta_onscreen_native_prepare_frame (CoglOnscreen *onscreen,
ClutterFrame *frame) ClutterFrame *frame)
@ -1513,7 +1525,10 @@ finish_frame_result_feedback (const MetaKmsFeedback *kms_feedback,
if (!g_error_matches (error, if (!g_error_matches (error,
G_IO_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); g_warning ("Cursor update failed: %s", error->message);
frame_info = cogl_onscreen_peek_head_frame_info (onscreen); frame_info = cogl_onscreen_peek_head_frame_info (onscreen);
@ -1539,10 +1554,18 @@ meta_onscreen_native_finish_frame (CoglOnscreen *onscreen,
kms_update = meta_frame_native_steal_kms_update (frame_native); kms_update = meta_frame_native_steal_kms_update (frame_native);
if (!kms_update) if (!kms_update)
{
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); clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_IDLE);
return; return;
} }
}
meta_kms_update_add_result_listener (kms_update, meta_kms_update_add_result_listener (kms_update,
&finish_frame_result_listener_vtable, &finish_frame_result_listener_vtable,
@ -1563,6 +1586,7 @@ meta_onscreen_native_finish_frame (CoglOnscreen *onscreen,
meta_kms_crtc_get_id (kms_crtc), meta_kms_crtc_get_id (kms_crtc),
meta_kms_device_get_path (kms_device)); 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_device_post_update (kms_device, kms_update,
META_KMS_UPDATE_FLAG_NONE); META_KMS_UPDATE_FLAG_NONE);
clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_PENDING_PRESENTED); clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_PENDING_PRESENTED);

View File

@ -40,6 +40,9 @@ void meta_renderer_native_release_onscreen (CoglOnscreen *onscreen);
void meta_onscreen_native_prepare_frame (CoglOnscreen *onscreen, void meta_onscreen_native_prepare_frame (CoglOnscreen *onscreen,
ClutterFrame *frame); ClutterFrame *frame);
void meta_onscreen_native_before_redraw (CoglOnscreen *onscreen,
ClutterFrame *frame);
void meta_onscreen_native_finish_frame (CoglOnscreen *onscreen, void meta_onscreen_native_finish_frame (CoglOnscreen *onscreen,
ClutterFrame *frame); ClutterFrame *frame);

View File

@ -66,6 +66,8 @@ typedef struct _MetaRendererNativeGpuData
EGLContext egl_context; EGLContext egl_context;
EGLConfig egl_config; EGLConfig egl_config;
} secondary; } secondary;
gulong crtc_needs_flush_handler_id;
} MetaRendererNativeGpuData; } MetaRendererNativeGpuData;
MetaEgl * meta_renderer_native_get_egl (MetaRendererNative *renderer_native); MetaEgl * meta_renderer_native_get_egl (MetaRendererNative *renderer_native);

View File

@ -137,7 +137,7 @@ meta_renderer_native_gpu_data_free (MetaRendererNativeGpuData *renderer_gpu_data
MetaRenderer *renderer = META_RENDERER (renderer_gpu_data->renderer_native); MetaRenderer *renderer = META_RENDERER (renderer_gpu_data->renderer_native);
MetaBackend *backend = meta_renderer_get_backend (renderer); MetaBackend *backend = meta_renderer_get_backend (renderer);
MetaCursorRenderer *cursor_renderer; MetaCursorRenderer *cursor_renderer;
MetaGpuKms *gpu_kms; MetaGpuKms *gpu_kms = renderer_gpu_data->gpu_kms;
GList *l; GList *l;
if (renderer_gpu_data->secondary.egl_context != EGL_NO_CONTEXT) 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); cursor_renderer = meta_backend_get_cursor_renderer (backend);
gpu_kms = renderer_gpu_data->gpu_kms;
if (cursor_renderer && gpu_kms) if (cursor_renderer && gpu_kms)
{ {
MetaCursorRendererNative *cursor_renderer_native = 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_clear_pointer (&renderer_gpu_data->render_device, g_object_unref);
g_free (renderer_gpu_data); 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 void
meta_renderer_native_finish_frame (MetaRendererNative *renderer_native, meta_renderer_native_finish_frame (MetaRendererNative *renderer_native,
MetaRendererView *view, MetaRendererView *view,
@ -1831,6 +1852,19 @@ create_renderer_gpu_data_egl_device (MetaRendererNative *renderer_native,
} }
#endif /* HAVE_EGL_DEVICE */ #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 * static MetaRendererNativeGpuData *
meta_renderer_native_create_renderer_gpu_data (MetaRendererNative *renderer_native, meta_renderer_native_create_renderer_gpu_data (MetaRendererNative *renderer_native,
MetaGpuKms *gpu_kms, MetaGpuKms *gpu_kms,
@ -1841,6 +1875,7 @@ meta_renderer_native_create_renderer_gpu_data (MetaRendererNative *renderer_nat
MetaBackendNative *backend_native = META_BACKEND_NATIVE (backend); MetaBackendNative *backend_native = META_BACKEND_NATIVE (backend);
const char *device_path; const char *device_path;
MetaRenderDevice *render_device; MetaRenderDevice *render_device;
MetaRendererNativeGpuData *renderer_gpu_data;
if (!gpu_kms) if (!gpu_kms)
return create_renderer_gpu_data_surfaceless (renderer_native, error); return create_renderer_gpu_data_surfaceless (renderer_native, error);
@ -1856,14 +1891,14 @@ meta_renderer_native_create_renderer_gpu_data (MetaRendererNative *renderer_nat
if (META_IS_RENDER_DEVICE_GBM (render_device)) if (META_IS_RENDER_DEVICE_GBM (render_device))
{ {
return create_renderer_gpu_data_gbm (renderer_native, renderer_gpu_data = create_renderer_gpu_data_gbm (renderer_native,
render_device, render_device,
gpu_kms); gpu_kms);
} }
#ifdef HAVE_EGL_DEVICE #ifdef HAVE_EGL_DEVICE
else if (META_IS_RENDER_DEVICE_EGL_STREAM (render_device)) else if (META_IS_RENDER_DEVICE_EGL_STREAM (render_device))
{ {
return create_renderer_gpu_data_egl_device (renderer_native, renderer_gpu_data = create_renderer_gpu_data_egl_device (renderer_native,
render_device, render_device,
gpu_kms); gpu_kms);
} }
@ -1873,6 +1908,13 @@ meta_renderer_native_create_renderer_gpu_data (MetaRendererNative *renderer_nat
g_assert_not_reached (); g_assert_not_reached ();
return NULL; 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 * static const char *

View File

@ -61,6 +61,10 @@ void meta_renderer_native_prepare_frame (MetaRendererNative *renderer_native,
MetaRendererView *view, MetaRendererView *view,
ClutterFrame *frame); ClutterFrame *frame);
void meta_renderer_native_before_redraw (MetaRendererNative *renderer_native,
MetaRendererView *view,
ClutterFrame *frame);
void meta_renderer_native_finish_frame (MetaRendererNative *renderer_native, void meta_renderer_native_finish_frame (MetaRendererNative *renderer_native,
MetaRendererView *view, MetaRendererView *view,
ClutterFrame *frame); ClutterFrame *frame);

View File

@ -139,8 +139,14 @@ meta_stage_native_redraw_view (ClutterStageWindow *stage_window,
ClutterStageView *view, ClutterStageView *view,
ClutterFrame *frame) 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; 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); clutter_stage_window_parent_iface->redraw_view (stage_window, view, frame);
crtc = meta_renderer_view_get_crtc (META_RENDERER_VIEW (view)); crtc = meta_renderer_view_get_crtc (META_RENDERER_VIEW (view));