From 75dff3e7c9b1ea210205de71f8291c15e6813feb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Thu, 4 Apr 2019 22:36:41 +0200 Subject: [PATCH] backend/native: Add and use transactional KMS API This commit introduces, and makes use of, a transactional API used for setting up KMS state, later to be applied, potentially atomically. From an API point of view, so is always the case, but in the current implementation, it still uses legacy drmMode* API to apply the state non-atomically. The API consists of various buliding blocks: * MetaKmsUpdate - a set of configuration changes, the higher level handle for handing over configuration to the impl backend. It's used to set mode, assign framebuffers to planes, queue page flips and set connector properties. * MetaKmsPlaneAssignment - the assignment of a framebuffer to a plane. Currently used to map a framebuffer to the primary plane of a CRTC. In the legacy KMS implementation, the plane assignment is used to derive the framebuffer used for mode setting and page flipping. This also means various high level changes: State, excluding configuring the cursor plane and creating/destroying DRM framebuffer handles, are applied in the end of a clutter frame, in one go. From an API point of view, this is done atomically, but as mentioned, only the non-atomic implementation exists so far. From MetaRendererNative's point of view, a page flip now initially always succeeds; the handling of EBUSY errors are done asynchronously in the MetaKmsImpl backend (still by retrying at refresh rate, but postponing flip callbacks instead of manipulating the frame clock). Handling of falling back to mode setting instead of page flipping is notified after the fact by a more precise page flip feedback API. EGLStream based page flipping relies on the impl backend not being atomic, as the page flipping is done in the EGLStream backend (e.g. nvidia driver). It uses a 'custom' page flip queueing method, keeping the EGLStream logic inside meta-renderer-native.c. Page flip handling is moved to meta-kms-impl-device.c from meta-gpu-kms.c. It goes via an extra idle callback before reaching meta-renderer-native.c to make sure callbacks are invoked outside of the impl context. While dummy power save page flipping is kept in meta-renderer-native.c, the EBUSY handling is moved to meta-kms-impl-simple.c. Instead of freezing the frame clock, actual page flip callbacks are postponed until all EBUSY retries have either succeeded or failed due to some other error than EBUSY. This effectively inhibits new frames to be drawn, meaning we won't stall waiting on the file descriptor for pending page flips. https://gitlab.gnome.org/GNOME/mutter/issues/548 https://gitlab.gnome.org/GNOME/mutter/merge_requests/525 --- src/backends/native/meta-crtc-kms.c | 203 +++-- src/backends/native/meta-crtc-kms.h | 20 +- src/backends/native/meta-gpu-kms.c | 313 +------ src/backends/native/meta-gpu-kms.h | 18 +- src/backends/native/meta-kms-connector.c | 92 ++ src/backends/native/meta-kms-connector.h | 14 + src/backends/native/meta-kms-crtc.c | 1 + src/backends/native/meta-kms-device.c | 29 + src/backends/native/meta-kms-device.h | 3 + src/backends/native/meta-kms-impl-device.c | 84 +- src/backends/native/meta-kms-impl-device.h | 4 + src/backends/native/meta-kms-impl-simple.c | 774 +++++++++++++++++ src/backends/native/meta-kms-impl.c | 22 + src/backends/native/meta-kms-impl.h | 17 + .../native/meta-kms-page-flip-private.h | 57 ++ src/backends/native/meta-kms-page-flip.c | 196 +++++ src/backends/native/meta-kms-plane.c | 19 + src/backends/native/meta-kms-plane.h | 6 + src/backends/native/meta-kms-private.h | 9 +- src/backends/native/meta-kms-types.h | 19 + src/backends/native/meta-kms-update-private.h | 89 ++ src/backends/native/meta-kms-update.c | 236 +++++ src/backends/native/meta-kms-update.h | 92 ++ src/backends/native/meta-kms.c | 131 ++- src/backends/native/meta-kms.h | 9 + .../native/meta-monitor-manager-kms.c | 19 +- src/backends/native/meta-output-kms.c | 134 +-- src/backends/native/meta-output-kms.h | 11 +- src/backends/native/meta-renderer-native.c | 813 ++++++------------ src/meson.build | 13 +- src/meta-marshal.list | 1 - 31 files changed, 2364 insertions(+), 1084 deletions(-) create mode 100644 src/backends/native/meta-kms-page-flip-private.h create mode 100644 src/backends/native/meta-kms-page-flip.c create mode 100644 src/backends/native/meta-kms-update-private.h create mode 100644 src/backends/native/meta-kms-update.c create mode 100644 src/backends/native/meta-kms-update.h delete mode 100644 src/meta-marshal.list diff --git a/src/backends/native/meta-crtc-kms.c b/src/backends/native/meta-crtc-kms.c index 7d5e72198..10d344e62 100644 --- a/src/backends/native/meta-crtc-kms.c +++ b/src/backends/native/meta-crtc-kms.c @@ -25,9 +25,12 @@ #include "backends/native/meta-crtc-kms.h" #include "backends/meta-backend-private.h" +#include "backends/meta-logical-monitor.h" #include "backends/native/meta-gpu-kms.h" +#include "backends/native/meta-output-kms.h" #include "backends/native/meta-kms-device.h" #include "backends/native/meta-kms-plane.h" +#include "backends/native/meta-kms-update.h" #define ALL_TRANSFORMS_MASK ((1 << META_MONITOR_N_TRANSFORMS) - 1) @@ -35,12 +38,11 @@ typedef struct _MetaCrtcKms { MetaKmsCrtc *kms_crtc; - uint32_t rotation_prop_id; - uint32_t rotation_map[META_MONITOR_N_TRANSFORMS]; - MetaKmsPlane *primary_plane; } MetaCrtcKms; +static GQuark kms_crtc_crtc_kms_quark; + gboolean meta_crtc_kms_is_transform_handled (MetaCrtc *crtc, MetaMonitorTransform transform) @@ -55,60 +57,123 @@ meta_crtc_kms_is_transform_handled (MetaCrtc *crtc, } void -meta_crtc_kms_apply_transform (MetaCrtc *crtc) +meta_crtc_kms_apply_transform (MetaCrtc *crtc, + MetaKmsPlaneAssignment *kms_plane_assignment) { MetaCrtcKms *crtc_kms = crtc->driver_private; - MetaGpu *gpu = meta_crtc_get_gpu (crtc); - MetaGpuKms *gpu_kms = META_GPU_KMS (gpu); - int kms_fd; MetaMonitorTransform hw_transform; - kms_fd = meta_gpu_kms_get_fd (gpu_kms); - hw_transform = crtc->transform; if (!meta_crtc_kms_is_transform_handled (crtc, hw_transform)) hw_transform = META_MONITOR_TRANSFORM_NORMAL; if (!meta_crtc_kms_is_transform_handled (crtc, hw_transform)) return; - if (drmModeObjectSetProperty (kms_fd, - meta_kms_plane_get_id (crtc_kms->primary_plane), - DRM_MODE_OBJECT_PLANE, - crtc_kms->rotation_prop_id, - crtc_kms->rotation_map[hw_transform]) != 0) - g_warning ("Failed to apply DRM plane transform %d: %m", hw_transform); + meta_kms_plane_update_set_rotation (crtc_kms->primary_plane, + kms_plane_assignment, + hw_transform); } -static int -find_property_index (MetaGpu *gpu, - drmModeObjectPropertiesPtr props, - const char *prop_name, - drmModePropertyPtr *out_prop) +void +meta_crtc_kms_assign_primary_plane (MetaCrtc *crtc, + uint32_t fb_id, + MetaKmsUpdate *kms_update) { - MetaGpuKms *gpu_kms = META_GPU_KMS (gpu); - int kms_fd; - unsigned int i; + MetaRectangle logical_monitor_rect; + int x, y; + MetaFixed16Rectangle src_rect; + MetaFixed16Rectangle dst_rect; + MetaKmsCrtc *kms_crtc; + MetaKmsDevice *kms_device; + MetaKmsPlane *primary_kms_plane; + MetaKmsPlaneAssignment *plane_assignment; - kms_fd = meta_gpu_kms_get_fd (gpu_kms); + logical_monitor_rect = + meta_logical_monitor_get_layout (crtc->logical_monitor); + x = crtc->rect.x - logical_monitor_rect.x; + y = crtc->rect.y - logical_monitor_rect.y; + src_rect = (MetaFixed16Rectangle) { + .x = meta_fixed_16_from_int (x), + .y = meta_fixed_16_from_int (y), + .width = meta_fixed_16_from_int (crtc->rect.width), + .height = meta_fixed_16_from_int (crtc->rect.height), + }; + dst_rect = (MetaFixed16Rectangle) { + .x = meta_fixed_16_from_int (0), + .y = meta_fixed_16_from_int (0), + .width = meta_fixed_16_from_int (crtc->rect.width), + .height = meta_fixed_16_from_int (crtc->rect.height), + }; - for (i = 0; i < props->count_props; i++) + kms_crtc = meta_crtc_kms_get_kms_crtc (crtc); + kms_device = meta_kms_crtc_get_device (kms_crtc); + primary_kms_plane = meta_kms_device_get_primary_plane_for (kms_device, + kms_crtc); + plane_assignment = meta_kms_update_assign_plane (kms_update, + kms_crtc, + primary_kms_plane, + fb_id, + src_rect, + dst_rect); + meta_crtc_kms_apply_transform (crtc, plane_assignment); +} + +static GList * +generate_crtc_connector_list (MetaGpu *gpu, + MetaCrtc *crtc) +{ + GList *connectors = NULL; + GList *l; + + for (l = meta_gpu_get_outputs (gpu); l; l = l->next) { - drmModePropertyPtr prop; + MetaOutput *output = l->data; + MetaCrtc *assigned_crtc; - prop = drmModeGetProperty (kms_fd, props->props[i]); - if (!prop) - continue; - - if (strcmp (prop->name, prop_name) == 0) + assigned_crtc = meta_output_get_assigned_crtc (output); + if (assigned_crtc == crtc) { - *out_prop = prop; - return i; - } + MetaKmsConnector *kms_connector = + meta_output_kms_get_kms_connector (output); - drmModeFreeProperty (prop); + connectors = g_list_prepend (connectors, kms_connector); + } } - return -1; + return connectors; +} + +void +meta_crtc_kms_set_mode (MetaCrtc *crtc, + MetaKmsUpdate *kms_update) +{ + MetaGpu *gpu = meta_crtc_get_gpu (crtc); + GList *connectors; + drmModeModeInfo *mode; + + connectors = generate_crtc_connector_list (gpu, crtc); + + if (connectors) + mode = crtc->current_mode->driver_private; + else + mode = NULL; + + meta_kms_update_mode_set (kms_update, + meta_crtc_kms_get_kms_crtc (crtc), + g_steal_pointer (&connectors), + mode); +} + +void +meta_crtc_kms_page_flip (MetaCrtc *crtc, + const MetaKmsPageFlipFeedback *page_flip_feedback, + gpointer user_data, + MetaKmsUpdate *kms_update) +{ + meta_kms_update_page_flip (kms_update, + meta_crtc_kms_get_kms_crtc (crtc), + page_flip_feedback, + user_data); } MetaKmsCrtc * @@ -176,62 +241,10 @@ meta_crtc_kms_supports_format (MetaCrtc *crtc, drm_format); } -static void -parse_transforms (MetaCrtc *crtc, - drmModePropertyPtr prop) +MetaCrtc * +meta_crtc_kms_from_kms_crtc (MetaKmsCrtc *kms_crtc) { - MetaCrtcKms *crtc_kms = crtc->driver_private; - int i; - - for (i = 0; i < prop->count_enums; i++) - { - int transform = -1; - - if (strcmp (prop->enums[i].name, "rotate-0") == 0) - transform = META_MONITOR_TRANSFORM_NORMAL; - else if (strcmp (prop->enums[i].name, "rotate-90") == 0) - transform = META_MONITOR_TRANSFORM_90; - else if (strcmp (prop->enums[i].name, "rotate-180") == 0) - transform = META_MONITOR_TRANSFORM_180; - else if (strcmp (prop->enums[i].name, "rotate-270") == 0) - transform = META_MONITOR_TRANSFORM_270; - - if (transform != -1) - crtc_kms->rotation_map[transform] = 1 << prop->enums[i].value; - } -} - -static void -init_crtc_rotations (MetaCrtc *crtc, - MetaGpu *gpu) -{ - MetaCrtcKms *crtc_kms = crtc->driver_private; - MetaGpuKms *gpu_kms = META_GPU_KMS (gpu); - int kms_fd; - uint32_t primary_plane_id; - drmModePlane *drm_plane; - drmModeObjectPropertiesPtr props; - drmModePropertyPtr prop; - int rotation_idx; - - kms_fd = meta_gpu_kms_get_fd (gpu_kms); - primary_plane_id = meta_kms_plane_get_id (crtc_kms->primary_plane); - drm_plane = drmModeGetPlane (kms_fd, primary_plane_id); - props = drmModeObjectGetProperties (kms_fd, - primary_plane_id, - DRM_MODE_OBJECT_PLANE); - - rotation_idx = find_property_index (gpu, props, - "rotation", &prop); - if (rotation_idx >= 0) - { - crtc_kms->rotation_prop_id = props->props[rotation_idx]; - parse_transforms (crtc, prop); - drmModeFreeProperty (prop); - } - - drmModeFreeObjectProperties (props); - drmModeFreePlane (drm_plane); + return g_object_get_qdata (G_OBJECT (kms_crtc), kms_crtc_crtc_kms_quark); } static void @@ -287,7 +300,13 @@ meta_create_kms_crtc (MetaGpuKms *gpu_kms, crtc->driver_private = crtc_kms; crtc->driver_notify = (GDestroyNotify) meta_crtc_destroy_notify; - init_crtc_rotations (crtc, gpu); + if (!kms_crtc_crtc_kms_quark) + { + kms_crtc_crtc_kms_quark = + g_quark_from_static_string ("meta-kms-crtc-crtc-kms-quark"); + } + + g_object_set_qdata (G_OBJECT (kms_crtc), kms_crtc_crtc_kms_quark, crtc); return crtc; } diff --git a/src/backends/native/meta-crtc-kms.h b/src/backends/native/meta-crtc-kms.h index 178c282c3..898622478 100644 --- a/src/backends/native/meta-crtc-kms.h +++ b/src/backends/native/meta-crtc-kms.h @@ -34,7 +34,23 @@ gboolean meta_crtc_kms_is_transform_handled (MetaCrtc *crtc, MetaMonitorTransform transform); -void meta_crtc_kms_apply_transform (MetaCrtc *crtc); +void meta_crtc_kms_apply_transform (MetaCrtc *crtc, + MetaKmsPlaneAssignment *kms_plane_assignment); + +void meta_crtc_kms_assign_primary_plane (MetaCrtc *crtc, + uint32_t fb_id, + MetaKmsUpdate *kms_update); + +void meta_crtc_kms_set_mode (MetaCrtc *crtc, + MetaKmsUpdate *kms_update); + +void meta_crtc_kms_page_flip (MetaCrtc *crtc, + const MetaKmsPageFlipFeedback *page_flip_feedback, + gpointer user_data, + MetaKmsUpdate *kms_update); + +void meta_crtc_kms_set_is_underscanning (MetaCrtc *crtc, + gboolean is_underscanning); MetaKmsCrtc * meta_crtc_kms_get_kms_crtc (MetaCrtc *crtc); @@ -48,6 +64,8 @@ gboolean meta_crtc_kms_supports_format (MetaCrtc *crtc, uint32_t drm_format); +MetaCrtc * meta_crtc_kms_from_kms_crtc (MetaKmsCrtc *kms_crtc); + MetaCrtc * meta_create_kms_crtc (MetaGpuKms *gpu_kms, MetaKmsCrtc *kms_crtc); diff --git a/src/backends/native/meta-gpu-kms.c b/src/backends/native/meta-gpu-kms.c index 596a775f3..ea937ba3e 100644 --- a/src/backends/native/meta-gpu-kms.c +++ b/src/backends/native/meta-gpu-kms.c @@ -39,6 +39,7 @@ #include "backends/native/meta-crtc-kms.h" #include "backends/native/meta-kms-connector.h" #include "backends/native/meta-kms-device.h" +#include "backends/native/meta-kms-update.h" #include "backends/native/meta-kms-utils.h" #include "backends/native/meta-kms.h" #include "backends/native/meta-launcher.h" @@ -46,21 +47,6 @@ #include "meta-default-modes.h" -typedef struct _MetaKmsSource -{ - GSource source; - - gpointer fd_tag; - MetaGpuKms *gpu_kms; -} MetaKmsSource; - -typedef struct _MetaGpuKmsFlipClosureContainer -{ - GClosure *flip_closure; - MetaGpuKms *gpu_kms; - MetaCrtc *crtc; -} MetaGpuKmsFlipClosureContainer; - struct _MetaGpuKms { MetaGpu parent; @@ -69,7 +55,6 @@ struct _MetaGpuKms uint32_t id; int fd; - GSource *source; clockid_t clock_id; @@ -78,124 +63,6 @@ struct _MetaGpuKms G_DEFINE_TYPE (MetaGpuKms, meta_gpu_kms, META_TYPE_GPU) -static gboolean -kms_event_check (GSource *source) -{ - MetaKmsSource *kms_source = (MetaKmsSource *) source; - - return g_source_query_unix_fd (source, kms_source->fd_tag) & G_IO_IN; -} - -static gboolean -kms_event_dispatch (GSource *source, - GSourceFunc callback, - gpointer user_data) -{ - MetaKmsSource *kms_source = (MetaKmsSource *) source; - - meta_gpu_kms_wait_for_flip (kms_source->gpu_kms, NULL); - - return G_SOURCE_CONTINUE; -} - -static GSourceFuncs kms_event_funcs = { - NULL, - kms_event_check, - kms_event_dispatch -}; - -static void -get_crtc_drm_connectors (MetaGpu *gpu, - MetaCrtc *crtc, - uint32_t **connectors, - unsigned int *n_connectors) -{ - GArray *connectors_array = g_array_new (FALSE, FALSE, sizeof (uint32_t)); - GList *l; - - for (l = meta_gpu_get_outputs (gpu); l; l = l->next) - { - MetaOutput *output = l->data; - MetaCrtc *assigned_crtc; - - assigned_crtc = meta_output_get_assigned_crtc (output); - if (assigned_crtc == crtc) - { - uint32_t connector_id; - - connector_id = meta_output_kms_get_connector_id (output); - g_array_append_val (connectors_array, connector_id); - } - } - - *n_connectors = connectors_array->len; - *connectors = (uint32_t *) g_array_free (connectors_array, FALSE); -} - -gboolean -meta_gpu_kms_apply_crtc_mode (MetaGpuKms *gpu_kms, - MetaCrtc *crtc, - int x, - int y, - uint32_t fb_id) -{ - MetaGpu *gpu = meta_crtc_get_gpu (crtc); - int kms_fd = meta_gpu_kms_get_fd (gpu_kms); - uint32_t *connectors; - unsigned int n_connectors; - drmModeModeInfo *mode; - - get_crtc_drm_connectors (gpu, crtc, &connectors, &n_connectors); - - if (connectors) - mode = crtc->current_mode->driver_private; - else - mode = NULL; - - if (drmModeSetCrtc (kms_fd, - crtc->crtc_id, - fb_id, - x, y, - connectors, n_connectors, - mode) != 0) - { - if (mode) - g_warning ("Failed to set CRTC mode %s: %m", crtc->current_mode->name); - else - g_warning ("Failed to disable CRTC"); - g_free (connectors); - return FALSE; - } - - g_free (connectors); - - return TRUE; -} - -static void -invoke_flip_closure (GClosure *flip_closure, - MetaGpuKms *gpu_kms, - MetaCrtc *crtc, - int64_t page_flip_time_ns) -{ - GValue params[] = { - G_VALUE_INIT, - G_VALUE_INIT, - G_VALUE_INIT, - G_VALUE_INIT, - }; - - g_value_init (¶ms[0], G_TYPE_POINTER); - g_value_set_pointer (¶ms[0], flip_closure); - g_value_init (¶ms[1], G_TYPE_OBJECT); - g_value_set_object (¶ms[1], gpu_kms); - g_value_init (¶ms[2], G_TYPE_OBJECT); - g_value_set_object (¶ms[2], crtc); - g_value_init (¶ms[3], G_TYPE_INT64); - g_value_set_int64 (¶ms[3], page_flip_time_ns); - g_closure_invoke (flip_closure, NULL, 4, params, NULL); -} - gboolean meta_gpu_kms_is_crtc_active (MetaGpuKms *gpu_kms, MetaCrtc *crtc) @@ -232,79 +99,6 @@ meta_gpu_kms_is_crtc_active (MetaGpuKms *gpu_kms, return TRUE; } -MetaGpuKmsFlipClosureContainer * -meta_gpu_kms_wrap_flip_closure (MetaGpuKms *gpu_kms, - MetaCrtc *crtc, - GClosure *flip_closure) -{ - MetaGpuKmsFlipClosureContainer *closure_container; - - closure_container = g_new0 (MetaGpuKmsFlipClosureContainer, 1); - *closure_container = (MetaGpuKmsFlipClosureContainer) { - .flip_closure = g_closure_ref (flip_closure), - .gpu_kms = gpu_kms, - .crtc = crtc - }; - - return closure_container; -} - -void -meta_gpu_kms_flip_closure_container_free (MetaGpuKmsFlipClosureContainer *closure_container) -{ - g_closure_unref (closure_container->flip_closure); - g_free (closure_container); -} - -gboolean -meta_gpu_kms_flip_crtc (MetaGpuKms *gpu_kms, - MetaCrtc *crtc, - uint32_t fb_id, - GClosure *flip_closure, - GError **error) -{ - MetaGpu *gpu = META_GPU (gpu_kms); - MetaBackend *backend = meta_gpu_get_backend (gpu); - MetaMonitorManager *monitor_manager = - meta_backend_get_monitor_manager (backend); - MetaGpuKmsFlipClosureContainer *closure_container; - int kms_fd = meta_gpu_kms_get_fd (gpu_kms); - uint32_t *connectors; - unsigned int n_connectors; - int ret = -1; - - g_assert (meta_crtc_get_gpu (crtc) == gpu); - g_assert (monitor_manager); - g_assert (meta_monitor_manager_get_power_save_mode (monitor_manager) == - META_POWER_SAVE_ON); - - get_crtc_drm_connectors (gpu, crtc, &connectors, &n_connectors); - g_assert (n_connectors > 0); - g_free (connectors); - - g_assert (fb_id != 0); - - closure_container = meta_gpu_kms_wrap_flip_closure (gpu_kms, - crtc, - flip_closure); - - ret = drmModePageFlip (kms_fd, - crtc->crtc_id, - fb_id, - DRM_MODE_PAGE_FLIP_EVENT, - closure_container); - if (ret != 0) - { - meta_gpu_kms_flip_closure_container_free (closure_container); - g_set_error (error, G_IO_ERROR, - g_io_error_from_errno (-ret), - "drmModePageFlip failed: %s", g_strerror (-ret)); - return FALSE; - } - - return TRUE; -} - static int64_t timespec_to_nanoseconds (const struct timespec *ts) { @@ -313,71 +107,12 @@ timespec_to_nanoseconds (const struct timespec *ts) return ((int64_t) ts->tv_sec) * one_billion + ts->tv_nsec; } -static int64_t -timeval_to_nanoseconds (const struct timeval *tv) -{ - int64_t usec = ((int64_t) tv->tv_sec) * G_USEC_PER_SEC + tv->tv_usec; - int64_t nsec = usec * 1000; - - return nsec; -} - -static void -page_flip_handler (int fd, - unsigned int frame, - unsigned int sec, - unsigned int usec, - void *user_data) -{ - MetaGpuKmsFlipClosureContainer *closure_container = user_data; - GClosure *flip_closure = closure_container->flip_closure; - MetaGpuKms *gpu_kms = closure_container->gpu_kms; - struct timeval page_flip_time = {sec, usec}; - - invoke_flip_closure (flip_closure, - gpu_kms, - closure_container->crtc, - timeval_to_nanoseconds (&page_flip_time)); - meta_gpu_kms_flip_closure_container_free (closure_container); -} - gboolean meta_gpu_kms_wait_for_flip (MetaGpuKms *gpu_kms, GError **error) { - drmEventContext evctx; - - memset (&evctx, 0, sizeof evctx); - evctx.version = 2; - evctx.page_flip_handler = page_flip_handler; - - while (TRUE) - { - if (drmHandleEvent (gpu_kms->fd, &evctx) != 0) - { - struct pollfd pfd; - int ret; - - if (errno != EAGAIN) - { - g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, - strerror (errno)); - return FALSE; - } - - pfd.fd = gpu_kms->fd; - pfd.events = POLL_IN | POLL_ERR; - do - { - ret = poll (&pfd, 1, -1); - } - while (ret == -1 && errno == EINTR); - } - else - { - break; - } - } + if (meta_kms_device_dispatch_sync (gpu_kms->kms_device, error) < 0) + return FALSE; return TRUE; } @@ -418,8 +153,9 @@ meta_gpu_kms_get_current_time_ns (MetaGpuKms *gpu_kms) } void -meta_gpu_kms_set_power_save_mode (MetaGpuKms *gpu_kms, - uint64_t state) +meta_gpu_kms_set_power_save_mode (MetaGpuKms *gpu_kms, + uint64_t state, + MetaKmsUpdate *kms_update) { GList *l; @@ -427,7 +163,7 @@ meta_gpu_kms_set_power_save_mode (MetaGpuKms *gpu_kms, { MetaOutput *output = l->data; - meta_output_kms_set_power_save_mode (output, state); + meta_output_kms_set_power_save_mode (output, state, kms_update); } } @@ -707,16 +443,13 @@ init_outputs (MetaGpuKms *gpu_kms) for (l = meta_kms_device_get_connectors (gpu_kms->kms_device); l; l = l->next) { MetaKmsConnector *kms_connector = l->data; + const MetaKmsConnectorState *connector_state; MetaOutput *output; MetaOutput *old_output; GError *error = NULL; - uint32_t connector_id; - drmModeConnector *connector; - connector_id = meta_kms_connector_get_id (kms_connector); - connector = drmModeGetConnector (gpu_kms->fd, connector_id); - - if (!connector || connector->connection != DRM_MODE_CONNECTED) + connector_state = meta_kms_connector_get_current_state (kms_connector); + if (!connector_state) continue; old_output = @@ -724,7 +457,6 @@ init_outputs (MetaGpuKms *gpu_kms) meta_kms_connector_get_id (kms_connector)); output = meta_create_kms_output (gpu_kms, kms_connector, - connector, old_output, &error); if (!output) @@ -787,8 +519,6 @@ meta_gpu_kms_new (MetaBackendNative *backend_native, MetaKmsDevice *kms_device, GError **error) { - GSource *source; - MetaKmsSource *kms_source; MetaGpuKms *gpu_kms; int kms_fd; @@ -803,29 +533,9 @@ meta_gpu_kms_new (MetaBackendNative *backend_native, meta_gpu_kms_read_current (META_GPU (gpu_kms), NULL); - source = g_source_new (&kms_event_funcs, sizeof (MetaKmsSource)); - kms_source = (MetaKmsSource *) source; - kms_source->fd_tag = g_source_add_unix_fd (source, - gpu_kms->fd, - G_IO_IN | G_IO_ERR); - kms_source->gpu_kms = gpu_kms; - - gpu_kms->source = source; - g_source_attach (gpu_kms->source, NULL); - return gpu_kms; } -static void -meta_gpu_kms_finalize (GObject *object) -{ - MetaGpuKms *gpu_kms = META_GPU_KMS (object); - - g_source_destroy (gpu_kms->source); - - G_OBJECT_CLASS (meta_gpu_kms_parent_class)->finalize (object); -} - static void meta_gpu_kms_init (MetaGpuKms *gpu_kms) { @@ -838,10 +548,7 @@ meta_gpu_kms_init (MetaGpuKms *gpu_kms) static void meta_gpu_kms_class_init (MetaGpuKmsClass *klass) { - GObjectClass *object_class = G_OBJECT_CLASS (klass); MetaGpuClass *gpu_class = META_GPU_CLASS (klass); - object_class->finalize = meta_gpu_kms_finalize; - gpu_class->read_current = meta_gpu_kms_read_current; } diff --git a/src/backends/native/meta-gpu-kms.h b/src/backends/native/meta-gpu-kms.h index 1dcdc50aa..6646ead93 100644 --- a/src/backends/native/meta-gpu-kms.h +++ b/src/backends/native/meta-gpu-kms.h @@ -29,6 +29,7 @@ #include "backends/meta-gpu.h" #include "backends/native/meta-backend-native.h" +#include "backends/native/meta-kms-types.h" #define META_TYPE_GPU_KMS (meta_gpu_kms_get_type ()) G_DECLARE_FINAL_TYPE (MetaGpuKms, meta_gpu_kms, META, GPU_KMS, MetaGpu) @@ -39,12 +40,6 @@ MetaGpuKms * meta_gpu_kms_new (MetaBackendNative *backend_native, MetaKmsDevice *kms_device, GError **error); -gboolean meta_gpu_kms_apply_crtc_mode (MetaGpuKms *gpu_kms, - MetaCrtc *crtc, - int x, - int y, - uint32_t fb_id); - gboolean meta_gpu_kms_can_have_outputs (MetaGpuKms *gpu_kms); gboolean meta_gpu_kms_is_crtc_active (MetaGpuKms *gpu_kms, @@ -53,12 +48,6 @@ gboolean meta_gpu_kms_is_crtc_active (MetaGpuKms *gpu_kms, gboolean meta_gpu_kms_is_boot_vga (MetaGpuKms *gpu_kms); gboolean meta_gpu_kms_is_platform_device (MetaGpuKms *gpu_kms); -gboolean meta_gpu_kms_flip_crtc (MetaGpuKms *gpu_kms, - MetaCrtc *crtc, - uint32_t fb_id, - GClosure *flip_closure, - GError **error); - gboolean meta_gpu_kms_wait_for_flip (MetaGpuKms *gpu_kms, GError **error); @@ -72,8 +61,9 @@ const char * meta_gpu_kms_get_file_path (MetaGpuKms *gpu_kms); int64_t meta_gpu_kms_get_current_time_ns (MetaGpuKms *gpu_kms); -void meta_gpu_kms_set_power_save_mode (MetaGpuKms *gpu_kms, - uint64_t state); +void meta_gpu_kms_set_power_save_mode (MetaGpuKms *gpu_kms, + uint64_t state, + MetaKmsUpdate *kms_update); MetaCrtcMode * meta_gpu_kms_get_mode_from_drm_mode (MetaGpuKms *gpu_kms, const drmModeModeInfo *drm_mode); diff --git a/src/backends/native/meta-kms-connector.c b/src/backends/native/meta-kms-connector.c index bcd97e1c3..70d3336d3 100644 --- a/src/backends/native/meta-kms-connector.c +++ b/src/backends/native/meta-kms-connector.c @@ -26,6 +26,7 @@ #include "backends/native/meta-kms-device-private.h" #include "backends/native/meta-kms-impl-device.h" +#include "backends/native/meta-kms-update-private.h" struct _MetaKmsConnector { @@ -38,6 +39,13 @@ struct _MetaKmsConnector char *name; MetaKmsConnectorState *current_state; + + uint32_t dpms_prop_id; + uint32_t underscan_prop_id; + uint32_t underscan_hborder_prop_id; + uint32_t underscan_vborder_prop_id; + uint32_t edid_blob_id; + uint32_t tile_blob_id; }; G_DEFINE_TYPE (MetaKmsConnector, meta_kms_connector, G_TYPE_OBJECT) @@ -48,6 +56,47 @@ meta_kms_connector_get_device (MetaKmsConnector *connector) return connector->device; } +void +meta_kms_connector_update_set_dpms_state (MetaKmsConnector *connector, + MetaKmsUpdate *update, + uint64_t state) +{ + meta_kms_update_set_connector_property (update, + connector, + connector->dpms_prop_id, + state); +} + +void +meta_kms_connector_set_underscanning (MetaKmsConnector *connector, + MetaKmsUpdate *update, + uint64_t hborder, + uint64_t vborder) +{ + meta_kms_update_set_connector_property (update, + connector, + connector->underscan_prop_id, + 1); + meta_kms_update_set_connector_property (update, + connector, + connector->underscan_hborder_prop_id, + hborder); + meta_kms_update_set_connector_property (update, + connector, + connector->underscan_vborder_prop_id, + vborder); +} + +void +meta_kms_connector_unset_underscanning (MetaKmsConnector *connector, + MetaKmsUpdate *update) +{ + meta_kms_update_set_connector_property (update, + connector, + connector->underscan_prop_id, + 0); +} + MetaConnectorType meta_kms_connector_get_connector_type (MetaKmsConnector *connector) { @@ -89,6 +138,12 @@ meta_kms_connector_get_current_state (MetaKmsConnector *connector) return connector->current_state; } +gboolean +meta_kms_connector_is_underscanning_supported (MetaKmsConnector *connector) +{ + return connector->underscan_prop_id != 0; +} + static void set_panel_orientation (MetaKmsConnectorState *state, drmModePropertyPtr prop, @@ -437,6 +492,41 @@ meta_kms_connector_update_state (MetaKmsConnector *connector, drm_resources); } +static void +find_property_ids (MetaKmsConnector *connector, + MetaKmsImplDevice *impl_device, + drmModeConnector *drm_connector) +{ + int fd; + int i; + + fd = meta_kms_impl_device_get_fd (impl_device); + + for (i = 0; i < drm_connector->count_props; i++) + { + drmModePropertyPtr prop; + + prop = drmModeGetProperty (fd, drm_connector->props[i]); + if (!prop) + continue; + + if ((prop->flags & DRM_MODE_PROP_ENUM) && + strcmp (prop->name, "DPMS") == 0) + connector->dpms_prop_id = prop->prop_id; + else if ((prop->flags & DRM_MODE_PROP_ENUM) && + strcmp (prop->name, "underscan") == 0) + connector->underscan_prop_id = prop->prop_id; + else if ((prop->flags & DRM_MODE_PROP_RANGE) && + strcmp (prop->name, "underscan hborder") == 0) + connector->underscan_hborder_prop_id = prop->prop_id; + else if ((prop->flags & DRM_MODE_PROP_RANGE) && + strcmp (prop->name, "underscan vborder") == 0) + connector->underscan_vborder_prop_id = prop->prop_id; + + drmModeFreeProperty (prop); + } +} + static char * make_connector_name (drmModeConnector *drm_connector) { @@ -483,6 +573,8 @@ meta_kms_connector_new (MetaKmsImplDevice *impl_device, connector->type = (MetaConnectorType) drm_connector->connector_type; connector->name = make_connector_name (drm_connector); + find_property_ids (connector, impl_device, drm_connector); + meta_kms_connector_read_state (connector, impl_device, drm_connector, drm_resources); diff --git a/src/backends/native/meta-kms-connector.h b/src/backends/native/meta-kms-connector.h index d536608e1..b6198b467 100644 --- a/src/backends/native/meta-kms-connector.h +++ b/src/backends/native/meta-kms-connector.h @@ -61,6 +61,18 @@ typedef struct _MetaKmsConnectorState MetaKmsDevice * meta_kms_connector_get_device (MetaKmsConnector *connector); +void meta_kms_connector_update_set_dpms_state (MetaKmsConnector *connector, + MetaKmsUpdate *update, + uint64_t state); + +void meta_kms_connector_set_underscanning (MetaKmsConnector *connector, + MetaKmsUpdate *update, + uint64_t hborder, + uint64_t vborder); + +void meta_kms_connector_unset_underscanning (MetaKmsConnector *connector, + MetaKmsUpdate *update); + MetaConnectorType meta_kms_connector_get_connector_type (MetaKmsConnector *connector); uint32_t meta_kms_connector_get_id (MetaKmsConnector *connector); @@ -72,4 +84,6 @@ gboolean meta_kms_connector_can_clone (MetaKmsConnector *connector, const MetaKmsConnectorState * meta_kms_connector_get_current_state (MetaKmsConnector *connector); +gboolean meta_kms_connector_is_underscanning_supported (MetaKmsConnector *connector); + #endif /* META_KMS_CONNECTOR_H */ diff --git a/src/backends/native/meta-kms-crtc.c b/src/backends/native/meta-kms-crtc.c index 910a5a84a..5006a1816 100644 --- a/src/backends/native/meta-kms-crtc.c +++ b/src/backends/native/meta-kms-crtc.c @@ -24,6 +24,7 @@ #include "backends/native/meta-kms-device-private.h" #include "backends/native/meta-kms-impl-device.h" +#include "backends/native/meta-kms-update-private.h" struct _MetaKmsCrtc { diff --git a/src/backends/native/meta-kms-device.c b/src/backends/native/meta-kms-device.c index a4dd0006d..3a626c58c 100644 --- a/src/backends/native/meta-kms-device.c +++ b/src/backends/native/meta-kms-device.c @@ -108,6 +108,35 @@ meta_kms_device_get_primary_plane_for (MetaKmsDevice *device, return NULL; } +static gboolean +dispatch_in_impl (MetaKmsImpl *impl, + gpointer user_data, + GError **error) +{ + MetaKmsImplDevice *impl_device = META_KMS_IMPL_DEVICE (user_data); + + return meta_kms_impl_device_dispatch (impl_device, error); +} + +int +meta_kms_device_dispatch_sync (MetaKmsDevice *device, + GError **error) +{ + int callback_count; + + callback_count = meta_kms_flush_callbacks (device->kms); + if (callback_count > 0) + return TRUE; + + if (!meta_kms_run_impl_task_sync (device->kms, + dispatch_in_impl, + device->impl_device, + error)) + return -1; + + return meta_kms_flush_callbacks (device->kms); +} + typedef struct _CreateImplDeviceData { MetaKmsDevice *device; diff --git a/src/backends/native/meta-kms-device.h b/src/backends/native/meta-kms-device.h index c4ec034c3..ea0f9aab5 100644 --- a/src/backends/native/meta-kms-device.h +++ b/src/backends/native/meta-kms-device.h @@ -42,6 +42,9 @@ GList * meta_kms_device_get_crtcs (MetaKmsDevice *device); MetaKmsPlane * meta_kms_device_get_primary_plane_for (MetaKmsDevice *device, MetaKmsCrtc *crtc); +int meta_kms_device_dispatch_sync (MetaKmsDevice *device, + GError **error); + MetaKmsDevice * meta_kms_device_new (MetaKms *kms, const char *path, MetaKmsDeviceFlag flags, diff --git a/src/backends/native/meta-kms-impl-device.c b/src/backends/native/meta-kms-impl-device.c index 6593f2aee..5f7a43daf 100644 --- a/src/backends/native/meta-kms-impl-device.c +++ b/src/backends/native/meta-kms-impl-device.c @@ -21,6 +21,7 @@ #include "backends/native/meta-kms-impl-device.h" +#include #include #include "backends/native/meta-kms-connector-private.h" @@ -28,8 +29,10 @@ #include "backends/native/meta-kms-crtc-private.h" #include "backends/native/meta-kms-crtc.h" #include "backends/native/meta-kms-impl.h" +#include "backends/native/meta-kms-page-flip-private.h" #include "backends/native/meta-kms-plane.h" #include "backends/native/meta-kms-private.h" +#include "backends/native/meta-kms-update.h" struct _MetaKmsImplDevice { @@ -39,6 +42,7 @@ struct _MetaKmsImplDevice MetaKmsImpl *impl; int fd; + GSource *fd_source; GList *crtcs; GList *connectors; @@ -71,6 +75,77 @@ meta_kms_impl_device_copy_planes (MetaKmsImplDevice *impl_device) return g_list_copy (impl_device->planes); } +static void +page_flip_handler (int fd, + unsigned int sequence, + unsigned int sec, + unsigned int usec, + void *user_data) +{ + MetaKmsPageFlipData *page_flip_data = user_data; + MetaKmsImpl *impl; + + meta_kms_page_flip_data_set_timings_in_impl (page_flip_data, + sequence, sec, usec); + + impl = meta_kms_page_flip_data_get_kms_impl (page_flip_data); + meta_kms_impl_handle_page_flip_callback (impl, page_flip_data); +} + +gboolean +meta_kms_impl_device_dispatch (MetaKmsImplDevice *impl_device, + GError **error) +{ + drmEventContext drm_event_context; + + meta_assert_in_kms_impl (meta_kms_impl_get_kms (impl_device->impl)); + + drm_event_context = (drmEventContext) { 0 }; + drm_event_context.version = 2; + drm_event_context.page_flip_handler = page_flip_handler; + + while (TRUE) + { + if (drmHandleEvent (impl_device->fd, &drm_event_context) != 0) + { + struct pollfd pfd; + int ret; + + if (errno != EAGAIN) + { + g_set_error_literal (error, G_IO_ERROR, + g_io_error_from_errno (errno), + strerror (errno)); + return FALSE; + } + + pfd.fd = impl_device->fd; + pfd.events = POLL_IN | POLL_ERR; + do + { + ret = poll (&pfd, 1, -1); + } + while (ret == -1 && errno == EINTR); + } + else + { + break; + } + } + + return TRUE; +} + +static gboolean +kms_event_dispatch_in_impl (MetaKmsImpl *impl, + gpointer user_data, + GError **error) +{ + MetaKmsImplDevice *impl_device = user_data; + + return meta_kms_impl_device_dispatch (impl_device, error); +} + drmModePropertyPtr meta_kms_impl_device_find_property (MetaKmsImplDevice *impl_device, drmModeObjectProperties *props, @@ -236,10 +311,11 @@ meta_kms_impl_device_new (MetaKmsDevice *device, MetaKmsImpl *impl, int fd) { + MetaKms *kms = meta_kms_impl_get_kms (impl); MetaKmsImplDevice *impl_device; drmModeRes *drm_resources; - meta_assert_in_kms_impl (meta_kms_impl_get_kms (impl)); + meta_assert_in_kms_impl (kms); impl_device = g_object_new (META_TYPE_KMS_IMPL_DEVICE, NULL); impl_device->device = device; @@ -256,6 +332,11 @@ meta_kms_impl_device_new (MetaKmsDevice *device, drmModeFreeResources (drm_resources); + impl_device->fd_source = + meta_kms_register_fd_in_impl (kms, fd, + kms_event_dispatch_in_impl, + impl_device); + return impl_device; } @@ -280,6 +361,7 @@ meta_kms_impl_device_close (MetaKmsImplDevice *impl_device) meta_assert_in_kms_impl (meta_kms_impl_get_kms (impl_device->impl)); + g_clear_pointer (&impl_device->fd_source, g_source_destroy); fd = impl_device->fd; impl_device->fd = -1; diff --git a/src/backends/native/meta-kms-impl-device.h b/src/backends/native/meta-kms-impl-device.h index 974915c3d..6c4b4e540 100644 --- a/src/backends/native/meta-kms-impl-device.h +++ b/src/backends/native/meta-kms-impl-device.h @@ -26,6 +26,7 @@ #include "backends/native/meta-kms-device.h" #include "backends/native/meta-kms-types.h" +#include "backends/native/meta-kms-update.h" #define META_TYPE_KMS_IMPL_DEVICE (meta_kms_impl_device_get_type ()) G_DECLARE_FINAL_TYPE (MetaKmsImplDevice, meta_kms_impl_device, @@ -40,6 +41,9 @@ GList * meta_kms_impl_device_copy_crtcs (MetaKmsImplDevice *impl_device); GList * meta_kms_impl_device_copy_planes (MetaKmsImplDevice *impl_device); +gboolean meta_kms_impl_device_dispatch (MetaKmsImplDevice *impl_device, + GError **error); + drmModePropertyPtr meta_kms_impl_device_find_property (MetaKmsImplDevice *impl_device, drmModeObjectProperties *props, const char *prop_name, diff --git a/src/backends/native/meta-kms-impl-simple.c b/src/backends/native/meta-kms-impl-simple.c index 6432cbb7a..ddbee6b81 100644 --- a/src/backends/native/meta-kms-impl-simple.c +++ b/src/backends/native/meta-kms-impl-simple.c @@ -21,16 +21,47 @@ #include "backends/native/meta-kms-impl-simple.h" +#include #include +#include + +#include "backends/native/meta-kms-connector.h" +#include "backends/native/meta-kms-crtc.h" +#include "backends/native/meta-kms-device-private.h" +#include "backends/native/meta-kms-page-flip-private.h" +#include "backends/native/meta-kms-plane.h" +#include "backends/native/meta-kms-private.h" +#include "backends/native/meta-kms-update-private.h" +#include "backends/native/meta-kms-utils.h" + +typedef struct _CachedModeSet +{ + GList *connectors; + drmModeModeInfo *drm_mode; +} CachedModeSet; struct _MetaKmsImplSimple { MetaKmsImpl parent; + + GSource *mode_set_fallback_feedback_source; + GList *mode_set_fallback_page_flip_datas; + + GList *pending_page_flip_retries; + GSource *retry_page_flips_source; + + GList *postponed_page_flip_datas; + GList *postponed_mode_set_fallback_datas; + + GHashTable *cached_mode_sets; }; G_DEFINE_TYPE (MetaKmsImplSimple, meta_kms_impl_simple, META_TYPE_KMS_IMPL) +static void +flush_postponed_page_flip_datas (MetaKmsImplSimple *impl_simple); + MetaKmsImplSimple * meta_kms_impl_simple_new (MetaKms *kms, GError **error) @@ -40,12 +71,755 @@ meta_kms_impl_simple_new (MetaKms *kms, NULL); } +static gboolean +process_connector_property (MetaKmsImpl *impl, + MetaKmsUpdate *update, + MetaKmsConnectorProperty *connector_property, + GError **error) +{ + MetaKmsConnector *connector = connector_property->connector; + MetaKmsDevice *device = meta_kms_connector_get_device (connector); + MetaKmsImplDevice *impl_device = meta_kms_device_get_impl_device (device); + int fd; + int ret; + + fd = meta_kms_impl_device_get_fd (impl_device); + + ret = drmModeObjectSetProperty (fd, + meta_kms_connector_get_id (connector), + DRM_MODE_OBJECT_CONNECTOR, + connector_property->prop_id, + connector_property->value); + if (ret != 0) + { + g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret), + "Failed to set connector %u property %u: %s", + meta_kms_connector_get_id (connector), + connector_property->prop_id, + g_strerror (-ret)); + return FALSE; + } + + return TRUE; +} + +static gboolean +process_plane_property (MetaKmsImpl *impl, + MetaKmsPlane *plane, + MetaKmsProperty *prop, + GError **error) +{ + MetaKmsDevice *device = meta_kms_plane_get_device (plane); + MetaKmsImplDevice *impl_device = meta_kms_device_get_impl_device (device); + int fd; + int ret; + + fd = meta_kms_impl_device_get_fd (impl_device); + + ret = drmModeObjectSetProperty (fd, + meta_kms_plane_get_id (plane), + DRM_MODE_OBJECT_PLANE, + prop->prop_id, + prop->value); + if (ret != 0) + { + g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret), + "Failed to set plane %u property %u: %s", + meta_kms_plane_get_id (plane), + prop->prop_id, + g_strerror (-ret)); + return FALSE; + } + + return TRUE; +} + +static MetaKmsPlaneAssignment * +get_primary_plane_assignment (MetaKmsImpl *impl, + MetaKmsUpdate *update, + MetaKmsCrtc *crtc) +{ + GList *l; + + for (l = meta_kms_update_get_plane_assignments (update); l; l = l->next) + { + MetaKmsPlaneAssignment *plane_assignment = l->data; + + if (plane_assignment->crtc == crtc) + return plane_assignment; + } + + return NULL; +} + +static CachedModeSet * +cached_mode_set_new (GList *connectors, + const drmModeModeInfo *drm_mode) +{ + CachedModeSet *cached_mode_set; + + cached_mode_set = g_new0 (CachedModeSet, 1); + *cached_mode_set = (CachedModeSet) { + .connectors = g_list_copy (connectors), + .drm_mode = g_memdup (drm_mode, sizeof *drm_mode), + }; + + return cached_mode_set; +} + +static void +cached_mode_set_free (CachedModeSet *cached_mode_set) +{ + g_list_free (cached_mode_set->connectors); + g_free (cached_mode_set->drm_mode); + g_free (cached_mode_set); +} + +static void +fill_connector_ids_array (GList *connectors, + uint32_t **out_connectors, + int *out_n_connectors) +{ + GList *l; + int i; + + *out_n_connectors = g_list_length (connectors); + *out_connectors = g_new0 (uint32_t, *out_n_connectors); + i = 0; + for (l = connectors; l; l = l->next) + { + MetaKmsConnector *connector = l->data; + + (*out_connectors)[i++] = meta_kms_connector_get_id (connector); + } +} + +static gboolean +process_mode_set (MetaKmsImpl *impl, + MetaKmsUpdate *update, + MetaKmsModeSet *mode_set, + GError **error) +{ + MetaKmsImplSimple *impl_simple = META_KMS_IMPL_SIMPLE (impl); + MetaKmsCrtc *crtc = mode_set->crtc;; + MetaKmsDevice *device = meta_kms_crtc_get_device (crtc); + MetaKmsImplDevice *impl_device = meta_kms_device_get_impl_device (device); + g_autofree uint32_t *connectors = NULL; + int n_connectors; + MetaKmsPlaneAssignment *plane_assignment; + uint32_t x, y; + uint32_t fb_id; + int fd; + int ret; + + crtc = mode_set->crtc; + + if (mode_set->drm_mode) + { + GList *l; + + fill_connector_ids_array (mode_set->connectors, + &connectors, + &n_connectors); + + plane_assignment = get_primary_plane_assignment (impl, update, crtc); + if (!plane_assignment) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Missing primary plane assignment for legacy mode set on CRTC %u", + meta_kms_crtc_get_id (crtc)); + return FALSE; + } + + x = meta_fixed_16_to_int (plane_assignment->src_rect.x); + y = meta_fixed_16_to_int (plane_assignment->src_rect.y); + + for (l = plane_assignment->plane_properties; l; l = l->next) + { + MetaKmsProperty *prop = l->data; + + if (!process_plane_property (impl, plane_assignment->plane, + prop, error)) + return FALSE; + } + + fb_id = plane_assignment->fb_id; + } + else + { + x = y = 0; + n_connectors = 0; + connectors = NULL; + fb_id = 0; + } + + fd = meta_kms_impl_device_get_fd (impl_device); + ret = drmModeSetCrtc (fd, + meta_kms_crtc_get_id (crtc), + fb_id, + x, y, + connectors, n_connectors, + mode_set->drm_mode); + if (ret != 0) + { + g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret), + "Failed to set mode on CRTC %u: %s", + meta_kms_crtc_get_id (crtc), + g_strerror (-ret)); + return FALSE; + } + + if (mode_set->drm_mode) + { + g_hash_table_replace (impl_simple->cached_mode_sets, + crtc, + cached_mode_set_new (mode_set->connectors, + mode_set->drm_mode)); + } + else + { + g_hash_table_remove (impl_simple->cached_mode_sets, crtc); + } + + return TRUE; +} + +static gboolean +is_timestamp_earlier_than (uint64_t ts1, + uint64_t ts2) +{ + if (ts1 == ts2) + return FALSE; + else + return ts2 - ts1 < UINT64_MAX / 2; +} + +typedef struct _RetryPageFlipData +{ + MetaKmsCrtc *crtc; + uint32_t fb_id; + MetaKmsPageFlipData *page_flip_data; + float refresh_rate; + uint64_t retry_time_us; +} RetryPageFlipData; + +static void +retry_page_flip_data_free (RetryPageFlipData *retry_page_flip_data) +{ + g_assert (!retry_page_flip_data->page_flip_data); + g_free (retry_page_flip_data); +} + +static float +get_cached_crtc_refresh_rate (MetaKmsImplSimple *impl_simple, + MetaKmsCrtc *crtc) +{ + CachedModeSet *cached_mode_set; + + cached_mode_set = g_hash_table_lookup (impl_simple->cached_mode_sets, + crtc); + g_assert (cached_mode_set); + + return meta_calculate_drm_mode_refresh_rate (cached_mode_set->drm_mode); +} + +static gboolean +retry_page_flips (gpointer user_data) +{ + MetaKmsImplSimple *impl_simple = META_KMS_IMPL_SIMPLE (user_data); + uint64_t now_us; + GList *l; + + meta_assert_in_kms_impl (meta_kms_impl_get_kms (META_KMS_IMPL (impl_simple))); + + now_us = g_source_get_time (impl_simple->retry_page_flips_source); + + l = impl_simple->pending_page_flip_retries; + while (l) + { + RetryPageFlipData *retry_page_flip_data = l->data; + MetaKmsCrtc *crtc = retry_page_flip_data->crtc; + MetaKmsDevice *device = meta_kms_crtc_get_device (crtc); + MetaKmsImplDevice *impl_device = meta_kms_device_get_impl_device (device); + GList *l_next = l->next; + int fd; + int ret; + MetaKmsPageFlipData *page_flip_data; + + if (is_timestamp_earlier_than (now_us, + retry_page_flip_data->retry_time_us)) + { + l = l_next; + continue; + } + + fd = meta_kms_impl_device_get_fd (impl_device); + ret = drmModePageFlip (fd, + meta_kms_crtc_get_id (crtc), + retry_page_flip_data->fb_id, + DRM_MODE_PAGE_FLIP_EVENT, + retry_page_flip_data->page_flip_data); + if (ret == -EBUSY) + { + float refresh_rate; + + refresh_rate = get_cached_crtc_refresh_rate (impl_simple, crtc); + retry_page_flip_data->retry_time_us += + (uint64_t) (G_USEC_PER_SEC / refresh_rate); + l = l_next; + continue; + } + + impl_simple->pending_page_flip_retries = + g_list_remove_link (impl_simple->pending_page_flip_retries, l); + + page_flip_data = g_steal_pointer (&retry_page_flip_data->page_flip_data); + if (ret != 0) + { + g_autoptr (GError) error = NULL; + + g_set_error (&error, G_IO_ERROR, g_io_error_from_errno (-ret), + "drmModePageFlip on CRTC %u failed: %s", + meta_kms_crtc_get_id (crtc), + g_strerror (-ret)); + if (!g_error_matches (error, + G_IO_ERROR, + G_IO_ERROR_PERMISSION_DENIED)) + g_critical ("Failed to page flip: %s", error->message); + + meta_kms_page_flip_data_discard_in_impl (page_flip_data, error); + } + + retry_page_flip_data_free (retry_page_flip_data); + + l = l_next; + } + + if (impl_simple->pending_page_flip_retries) + { + GList *l; + uint64_t earliest_retry_time_us = 0; + + for (l = impl_simple->pending_page_flip_retries; l; l = l->next) + { + RetryPageFlipData *retry_page_flip_data = l->data; + + if (l == impl_simple->pending_page_flip_retries || + is_timestamp_earlier_than (retry_page_flip_data->retry_time_us, + earliest_retry_time_us)) + earliest_retry_time_us = retry_page_flip_data->retry_time_us; + } + + g_source_set_ready_time (impl_simple->retry_page_flips_source, + earliest_retry_time_us); + return G_SOURCE_CONTINUE; + } + else + { + g_clear_pointer (&impl_simple->retry_page_flips_source, + g_source_unref); + + flush_postponed_page_flip_datas (impl_simple); + + return G_SOURCE_REMOVE; + } +} + +static void +schedule_retry_page_flip (MetaKmsImplSimple *impl_simple, + MetaKmsCrtc *crtc, + uint32_t fb_id, + float refresh_rate, + MetaKmsPageFlipData *page_flip_data) +{ + RetryPageFlipData *retry_page_flip_data; + uint64_t now_us; + uint64_t retry_time_us; + + now_us = g_get_monotonic_time (); + retry_time_us = now_us + (uint64_t) (G_USEC_PER_SEC / refresh_rate); + + retry_page_flip_data = g_new0 (RetryPageFlipData, 1); + *retry_page_flip_data = (RetryPageFlipData) { + .crtc = crtc, + .fb_id = fb_id, + .page_flip_data = meta_kms_page_flip_data_ref (page_flip_data), + .refresh_rate = refresh_rate, + .retry_time_us = retry_time_us, + }; + + if (!impl_simple->retry_page_flips_source) + { + MetaKms *kms = meta_kms_impl_get_kms (META_KMS_IMPL (impl_simple)); + GSource *source; + + source = meta_kms_add_source_in_impl (kms, retry_page_flips, + impl_simple, NULL); + g_source_set_ready_time (source, retry_time_us); + + impl_simple->retry_page_flips_source = source; + } + else + { + GList *l; + + for (l = impl_simple->pending_page_flip_retries; l; l = l->next) + { + RetryPageFlipData *pending_retry_page_flip_data = l->data; + uint64_t pending_retry_time_us = + pending_retry_page_flip_data->retry_time_us; + + if (is_timestamp_earlier_than (retry_time_us, pending_retry_time_us)) + { + g_source_set_ready_time (impl_simple->retry_page_flips_source, + retry_time_us); + break; + } + } + } + + impl_simple->pending_page_flip_retries = + g_list_append (impl_simple->pending_page_flip_retries, + retry_page_flip_data); +} + +static void +invoke_page_flip_datas (GList *page_flip_datas, + MetaPageFlipDataFeedbackFunc func) +{ + g_list_foreach (page_flip_datas, (GFunc) func, NULL); +} + +static void +clear_page_flip_datas (GList **page_flip_datas) +{ + g_list_free_full (*page_flip_datas, + (GDestroyNotify) meta_kms_page_flip_data_unref); + *page_flip_datas = NULL; +} + +static gboolean +mode_set_fallback_feedback_idle (gpointer user_data) +{ + MetaKmsImplSimple *impl_simple = user_data; + + g_clear_pointer (&impl_simple->mode_set_fallback_feedback_source, + g_source_unref); + + if (!impl_simple->pending_page_flip_retries) + { + impl_simple->postponed_mode_set_fallback_datas = + g_steal_pointer (&impl_simple->mode_set_fallback_page_flip_datas); + } + else + { + invoke_page_flip_datas (impl_simple->mode_set_fallback_page_flip_datas, + meta_kms_page_flip_data_mode_set_fallback_in_impl); + clear_page_flip_datas (&impl_simple->mode_set_fallback_page_flip_datas); + } + + return G_SOURCE_REMOVE; +} + +static gboolean +mode_set_fallback (MetaKmsImplSimple *impl_simple, + MetaKmsUpdate *update, + MetaKmsPageFlip *page_flip, + MetaKmsPlaneAssignment *plane_assignment, + MetaKmsPageFlipData *page_flip_data, + GError **error) +{ + MetaKms *kms = meta_kms_impl_get_kms (META_KMS_IMPL (impl_simple)); + MetaKmsCrtc *crtc = page_flip->crtc; + MetaKmsDevice *device = meta_kms_crtc_get_device (crtc); + MetaKmsImplDevice *impl_device = meta_kms_device_get_impl_device (device); + CachedModeSet *cached_mode_set; + g_autofree uint32_t *connectors = NULL; + int n_connectors; + uint32_t x, y; + int fd; + int ret; + + cached_mode_set = g_hash_table_lookup (impl_simple->cached_mode_sets, + crtc); + g_assert (cached_mode_set); + + fill_connector_ids_array (cached_mode_set->connectors, + &connectors, + &n_connectors); + + x = meta_fixed_16_to_int (plane_assignment->src_rect.x); + y = meta_fixed_16_to_int (plane_assignment->src_rect.y); + + fd = meta_kms_impl_device_get_fd (impl_device); + ret = drmModeSetCrtc (fd, + meta_kms_crtc_get_id (crtc), + plane_assignment->fb_id, + x, y, + connectors, n_connectors, + cached_mode_set->drm_mode); + if (ret != 0) + { + g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret), + "drmModeSetCrtc mode '%s' on CRTC %u failed: %s", + cached_mode_set->drm_mode->name, + meta_kms_crtc_get_id (crtc), + g_strerror (-ret)); + return FALSE; + } + + if (!impl_simple->mode_set_fallback_feedback_source) + { + GSource *source; + + source = meta_kms_add_source_in_impl (kms, + mode_set_fallback_feedback_idle, + impl_simple, + NULL); + impl_simple->mode_set_fallback_feedback_source = source; + } + + impl_simple->mode_set_fallback_page_flip_datas = + g_list_prepend (impl_simple->mode_set_fallback_page_flip_datas, + meta_kms_page_flip_data_ref (page_flip_data)); + + return TRUE; +} + +static gboolean +process_page_flip (MetaKmsImpl *impl, + MetaKmsUpdate *update, + MetaKmsPageFlip *page_flip, + GError **error) +{ + MetaKmsImplSimple *impl_simple = META_KMS_IMPL_SIMPLE (impl); + MetaKmsCrtc *crtc; + MetaKmsDevice *device; + MetaKmsImplDevice *impl_device; + MetaKmsPlaneAssignment *plane_assignment; + MetaKmsPageFlipData *page_flip_data; + MetaKmsCustomPageFlipFunc custom_page_flip_func; + int fd; + int ret; + + crtc = page_flip->crtc; + plane_assignment = get_primary_plane_assignment (impl, update, crtc); + + page_flip_data = meta_kms_page_flip_data_new (impl, + crtc, + page_flip->feedback, + page_flip->user_data); + + device = meta_kms_crtc_get_device (crtc); + impl_device = meta_kms_device_get_impl_device (device); + fd = meta_kms_impl_device_get_fd (impl_device); + custom_page_flip_func = page_flip->custom_page_flip_func; + if (custom_page_flip_func) + { + ret = custom_page_flip_func (page_flip->custom_page_flip_user_data, + meta_kms_page_flip_data_ref (page_flip_data)); + } + else + { + ret = drmModePageFlip (fd, + meta_kms_crtc_get_id (crtc), + plane_assignment->fb_id, + DRM_MODE_PAGE_FLIP_EVENT, + meta_kms_page_flip_data_ref (page_flip_data)); + } + + if (ret == -EBUSY) + { + float refresh_rate; + + refresh_rate = get_cached_crtc_refresh_rate (impl_simple, crtc); + schedule_retry_page_flip (impl_simple, + crtc, + plane_assignment->fb_id, + refresh_rate, + page_flip_data); + } + else if (ret == -EINVAL) + { + if (!mode_set_fallback (impl_simple, + update, + page_flip, + plane_assignment, + page_flip_data, + error)) + { + meta_kms_page_flip_data_unref (page_flip_data); + return FALSE; + } + } + else if (ret != 0) + { + g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret), + "drmModePageFlip on CRTC %u failed: %s", + meta_kms_crtc_get_id (crtc), + g_strerror (-ret)); + meta_kms_page_flip_data_unref (page_flip_data); + return FALSE; + } + + meta_kms_page_flip_data_unref (page_flip_data); + return TRUE; +} + +static void +discard_page_flip (MetaKmsImpl *impl, + MetaKmsUpdate *update, + MetaKmsPageFlip *page_flip) +{ + MetaKmsCrtc *crtc; + MetaKmsPageFlipData *page_flip_data; + + crtc = page_flip->crtc; + page_flip_data = meta_kms_page_flip_data_new (impl, + crtc, + page_flip->feedback, + page_flip->user_data); + meta_kms_page_flip_data_discard_in_impl (page_flip_data, NULL); + meta_kms_page_flip_data_unref (page_flip_data); +} + +static gboolean +meta_kms_impl_simple_process_update (MetaKmsImpl *impl, + MetaKmsUpdate *update, + GError **error) +{ + GList *l; + + meta_assert_in_kms_impl (meta_kms_impl_get_kms (impl)); + + for (l = meta_kms_update_get_connector_properties (update); l; l = l->next) + { + MetaKmsConnectorProperty *connector_property = l->data; + + if (!process_connector_property (impl, update, connector_property, error)) + goto discard_page_flips; + } + + for (l = meta_kms_update_get_mode_sets (update); l; l = l->next) + { + MetaKmsModeSet *mode_set = l->data; + + if (!process_mode_set (impl, update, mode_set, error)) + goto discard_page_flips; + } + + for (l = meta_kms_update_get_page_flips (update); l; l = l->next) + { + MetaKmsPageFlip *page_flip = l->data; + + if (!process_page_flip (impl, update, page_flip, error)) + goto discard_page_flips; + } + + return TRUE; + +discard_page_flips: + for (l = meta_kms_update_get_page_flips (update); l; l = l->next) + { + MetaKmsPageFlip *page_flip = l->data; + + discard_page_flip (impl, update, page_flip); + } + + return FALSE; +} + +static void +flush_postponed_page_flip_datas (MetaKmsImplSimple *impl_simple) +{ + invoke_page_flip_datas (impl_simple->postponed_page_flip_datas, + meta_kms_page_flip_data_flipped_in_impl); + clear_page_flip_datas (&impl_simple->postponed_page_flip_datas); + + invoke_page_flip_datas (impl_simple->postponed_mode_set_fallback_datas, + meta_kms_page_flip_data_mode_set_fallback_in_impl); + clear_page_flip_datas (&impl_simple->postponed_mode_set_fallback_datas); +} + +static void +meta_kms_impl_simple_handle_page_flip_callback (MetaKmsImpl *impl, + MetaKmsPageFlipData *page_flip_data) +{ + MetaKmsImplSimple *impl_simple = META_KMS_IMPL_SIMPLE (impl); + + if (impl_simple->pending_page_flip_retries) + { + impl_simple->postponed_page_flip_datas = + g_list_append (impl_simple->postponed_page_flip_datas, + page_flip_data); + } + else + { + meta_kms_page_flip_data_flipped_in_impl (page_flip_data); + } +} + +static void +meta_kms_impl_simple_discard_pending_page_flips (MetaKmsImpl *impl) +{ + MetaKmsImplSimple *impl_simple = META_KMS_IMPL_SIMPLE (impl); + GList *l; + + if (!impl_simple->pending_page_flip_retries) + return; + + for (l = impl_simple->pending_page_flip_retries; l; l = l->next) + { + RetryPageFlipData *retry_page_flip_data = l->data; + MetaKmsPageFlipData *page_flip_data; + + page_flip_data = g_steal_pointer (&retry_page_flip_data->page_flip_data); + meta_kms_page_flip_data_discard_in_impl (page_flip_data, NULL); + retry_page_flip_data_free (retry_page_flip_data); + } + g_clear_pointer (&impl_simple->pending_page_flip_retries, g_list_free); + + g_clear_pointer (&impl_simple->retry_page_flips_source, + g_source_destroy); +} + +static void +meta_kms_impl_simple_finalize (GObject *object) +{ + MetaKmsImplSimple *impl_simple = META_KMS_IMPL_SIMPLE (object); + + g_list_free_full (impl_simple->pending_page_flip_retries, + (GDestroyNotify) retry_page_flip_data_free); + g_list_free_full (impl_simple->postponed_page_flip_datas, + (GDestroyNotify) meta_kms_page_flip_data_unref); + g_list_free_full (impl_simple->postponed_mode_set_fallback_datas, + (GDestroyNotify) meta_kms_page_flip_data_unref); + g_clear_pointer (&impl_simple->mode_set_fallback_feedback_source, + g_source_destroy); + g_hash_table_destroy (impl_simple->cached_mode_sets); + + G_OBJECT_CLASS (meta_kms_impl_simple_parent_class)->finalize (object); +} + static void meta_kms_impl_simple_init (MetaKmsImplSimple *impl_simple) { + impl_simple->cached_mode_sets = + g_hash_table_new_full (NULL, + NULL, + NULL, + (GDestroyNotify) cached_mode_set_free); } static void meta_kms_impl_simple_class_init (MetaKmsImplSimpleClass *klass) { + GObjectClass *object_class = G_OBJECT_CLASS (klass); + MetaKmsImplClass *impl_class = META_KMS_IMPL_CLASS (klass); + + object_class->finalize = meta_kms_impl_simple_finalize; + + impl_class->process_update = meta_kms_impl_simple_process_update; + impl_class->handle_page_flip_callback = meta_kms_impl_simple_handle_page_flip_callback; + impl_class->discard_pending_page_flips = meta_kms_impl_simple_discard_pending_page_flips; } diff --git a/src/backends/native/meta-kms-impl.c b/src/backends/native/meta-kms-impl.c index b6f9c2509..1934d21ae 100644 --- a/src/backends/native/meta-kms-impl.c +++ b/src/backends/native/meta-kms-impl.c @@ -43,6 +43,28 @@ meta_kms_impl_get_kms (MetaKmsImpl *impl) return priv->kms; } +gboolean +meta_kms_impl_process_update (MetaKmsImpl *impl, + MetaKmsUpdate *update, + GError **error) +{ + return META_KMS_IMPL_GET_CLASS (impl)->process_update (impl, update, error); +} + +void +meta_kms_impl_handle_page_flip_callback (MetaKmsImpl *impl, + MetaKmsPageFlipData *page_flip_data) +{ + META_KMS_IMPL_GET_CLASS (impl)->handle_page_flip_callback (impl, + page_flip_data); +} + +void +meta_kms_impl_discard_pending_page_flips (MetaKmsImpl *impl) +{ + META_KMS_IMPL_GET_CLASS (impl)->discard_pending_page_flips (impl); +} + static void meta_kms_impl_set_property (GObject *object, guint prop_id, diff --git a/src/backends/native/meta-kms-impl.h b/src/backends/native/meta-kms-impl.h index fa774af17..7816253d9 100644 --- a/src/backends/native/meta-kms-impl.h +++ b/src/backends/native/meta-kms-impl.h @@ -21,6 +21,7 @@ #define META_KMS_IMPL_H #include "backends/native/meta-kms-impl-device.h" +#include "backends/native/meta-kms-page-flip-private.h" #include "backends/native/meta-kms.h" #define META_TYPE_KMS_IMPL (meta_kms_impl_get_type ()) @@ -30,8 +31,24 @@ G_DECLARE_DERIVABLE_TYPE (MetaKmsImpl, meta_kms_impl, struct _MetaKmsImplClass { GObjectClass parent_class; + + gboolean (* process_update) (MetaKmsImpl *impl, + MetaKmsUpdate *update, + GError **error); + void (* handle_page_flip_callback) (MetaKmsImpl *impl, + MetaKmsPageFlipData *page_flip_data); + void (* discard_pending_page_flips) (MetaKmsImpl *impl); }; MetaKms * meta_kms_impl_get_kms (MetaKmsImpl *impl); +gboolean meta_kms_impl_process_update (MetaKmsImpl *impl, + MetaKmsUpdate *update, + GError **error); + +void meta_kms_impl_handle_page_flip_callback (MetaKmsImpl *impl, + MetaKmsPageFlipData *page_flip_data); + +void meta_kms_impl_discard_pending_page_flips (MetaKmsImpl *impl); + #endif /* META_KMS_IMPL_H */ diff --git a/src/backends/native/meta-kms-page-flip-private.h b/src/backends/native/meta-kms-page-flip-private.h new file mode 100644 index 000000000..ef8faf7ad --- /dev/null +++ b/src/backends/native/meta-kms-page-flip-private.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2019 Red Hat + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef META_KMS_PAGE_FLIP_H +#define META_KMS_PAGE_FLIP_H + +#include + +#include "backends/native/meta-kms-types.h" + +typedef struct _MetaKmsPageFlipData MetaKmsPageFlipData; + +typedef void (* MetaPageFlipDataFeedbackFunc) (MetaKmsPageFlipData *page_flip_data); + +MetaKmsPageFlipData * meta_kms_page_flip_data_new (MetaKmsImpl *impl, + MetaKmsCrtc *crtc, + const MetaKmsPageFlipFeedback *feedback, + gpointer user_data); + +MetaKmsPageFlipData * meta_kms_page_flip_data_ref (MetaKmsPageFlipData *page_flip_data); + +void meta_kms_page_flip_data_unref (MetaKmsPageFlipData *page_flip_data); + +MetaKmsImpl * meta_kms_page_flip_data_get_kms_impl (MetaKmsPageFlipData *page_flip_data); + +void meta_kms_page_flip_data_set_timings_in_impl (MetaKmsPageFlipData *page_flip_data, + unsigned int sequence, + unsigned int sec, + unsigned int usec); + +void meta_kms_page_flip_data_flipped_in_impl (MetaKmsPageFlipData *page_flip_data); + +void meta_kms_page_flip_data_mode_set_fallback_in_impl (MetaKmsPageFlipData *page_flip_data); + +void meta_kms_page_flip_data_discard_in_impl (MetaKmsPageFlipData *page_flip_data, + const GError *error); + +void meta_kms_page_flip_data_take_error (MetaKmsPageFlipData *page_flip_data, + GError *error); + +#endif /* META_KMS_PAGE_FLIP_H */ diff --git a/src/backends/native/meta-kms-page-flip.c b/src/backends/native/meta-kms-page-flip.c new file mode 100644 index 000000000..997c3fca5 --- /dev/null +++ b/src/backends/native/meta-kms-page-flip.c @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2019 Red Hat + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include "config.h" + +#include "backends/native/meta-kms-page-flip-private.h" + +#include "backends/native/meta-kms-impl.h" +#include "backends/native/meta-kms-private.h" +#include "backends/native/meta-kms-update.h" + +struct _MetaKmsPageFlipData +{ + int ref_count; + + MetaKmsImpl *impl; + MetaKmsCrtc *crtc; + + const MetaKmsPageFlipFeedback *feedback; + gpointer user_data; + + unsigned int sequence; + unsigned int sec; + unsigned int usec; + + GError *error; +}; + +MetaKmsPageFlipData * +meta_kms_page_flip_data_new (MetaKmsImpl *impl, + MetaKmsCrtc *crtc, + const MetaKmsPageFlipFeedback *feedback, + gpointer user_data) +{ + MetaKmsPageFlipData *page_flip_data; + + page_flip_data = g_new0 (MetaKmsPageFlipData , 1); + *page_flip_data = (MetaKmsPageFlipData) { + .ref_count = 1, + .impl = impl, + .crtc = crtc, + .feedback = feedback, + .user_data = user_data, + }; + + return page_flip_data; +} + +MetaKmsPageFlipData * +meta_kms_page_flip_data_ref (MetaKmsPageFlipData *page_flip_data) +{ + page_flip_data->ref_count++; + + return page_flip_data; +} + +void +meta_kms_page_flip_data_unref (MetaKmsPageFlipData *page_flip_data) +{ + page_flip_data->ref_count--; + + if (page_flip_data->ref_count == 0) + { + g_clear_error (&page_flip_data->error); + g_free (page_flip_data); + } +} + +MetaKmsImpl * +meta_kms_page_flip_data_get_kms_impl (MetaKmsPageFlipData *page_flip_data) +{ + return page_flip_data->impl; +} + +static void +meta_kms_page_flip_data_flipped (MetaKms *kms, + gpointer user_data) +{ + MetaKmsPageFlipData *page_flip_data = user_data; + + meta_assert_not_in_kms_impl (kms); + + page_flip_data->feedback->flipped (page_flip_data->crtc, + page_flip_data->sequence, + page_flip_data->sec, + page_flip_data->usec, + page_flip_data->user_data); +} + +void +meta_kms_page_flip_data_set_timings_in_impl (MetaKmsPageFlipData *page_flip_data, + unsigned int sequence, + unsigned int sec, + unsigned int usec) +{ + MetaKms *kms = meta_kms_impl_get_kms (page_flip_data->impl); + + meta_assert_in_kms_impl (kms); + + page_flip_data->sequence = sequence; + page_flip_data->sec = sec; + page_flip_data->usec = usec; +} + +void +meta_kms_page_flip_data_flipped_in_impl (MetaKmsPageFlipData *page_flip_data) +{ + MetaKms *kms = meta_kms_impl_get_kms (page_flip_data->impl); + + meta_assert_in_kms_impl (kms); + + meta_kms_queue_callback (kms, + meta_kms_page_flip_data_flipped, + meta_kms_page_flip_data_ref (page_flip_data), + (GDestroyNotify) meta_kms_page_flip_data_unref); +} + +static void +meta_kms_page_flip_data_mode_set_fallback (MetaKms *kms, + gpointer user_data) +{ + MetaKmsPageFlipData *page_flip_data = user_data; + + meta_assert_not_in_kms_impl (kms); + + page_flip_data->feedback->mode_set_fallback (page_flip_data->crtc, + page_flip_data->user_data); +} + +void +meta_kms_page_flip_data_mode_set_fallback_in_impl (MetaKmsPageFlipData *page_flip_data) +{ + MetaKms *kms = meta_kms_impl_get_kms (page_flip_data->impl); + + meta_assert_in_kms_impl (kms); + + meta_kms_queue_callback (kms, + meta_kms_page_flip_data_mode_set_fallback, + meta_kms_page_flip_data_ref (page_flip_data), + (GDestroyNotify) meta_kms_page_flip_data_unref); +} + +static void +meta_kms_page_flip_data_discard (MetaKms *kms, + gpointer user_data) +{ + MetaKmsPageFlipData *page_flip_data = user_data; + + meta_assert_not_in_kms_impl (kms); + + page_flip_data->feedback->discarded (page_flip_data->crtc, + page_flip_data->user_data, + page_flip_data->error); +} + +void +meta_kms_page_flip_data_take_error (MetaKmsPageFlipData *page_flip_data, + GError *error) +{ + g_assert (!page_flip_data->error); + + page_flip_data->error = error; +} + +void +meta_kms_page_flip_data_discard_in_impl (MetaKmsPageFlipData *page_flip_data, + const GError *error) +{ + MetaKms *kms = meta_kms_impl_get_kms (page_flip_data->impl); + + meta_assert_in_kms_impl (kms); + + if (error) + meta_kms_page_flip_data_take_error (page_flip_data, g_error_copy (error)); + + meta_kms_queue_callback (kms, + meta_kms_page_flip_data_discard, + meta_kms_page_flip_data_ref (page_flip_data), + (GDestroyNotify) meta_kms_page_flip_data_unref); +} diff --git a/src/backends/native/meta-kms-plane.c b/src/backends/native/meta-kms-plane.c index 94a407bd8..d34d8c194 100644 --- a/src/backends/native/meta-kms-plane.c +++ b/src/backends/native/meta-kms-plane.c @@ -27,6 +27,7 @@ #include "backends/meta-monitor-transform.h" #include "backends/native/meta-kms-crtc.h" #include "backends/native/meta-kms-impl-device.h" +#include "backends/native/meta-kms-update-private.h" struct _MetaKmsPlane { @@ -53,6 +54,12 @@ struct _MetaKmsPlane G_DEFINE_TYPE (MetaKmsPlane, meta_kms_plane, G_TYPE_OBJECT) +MetaKmsDevice * +meta_kms_plane_get_device (MetaKmsPlane *plane) +{ + return plane->device; +} + uint32_t meta_kms_plane_get_id (MetaKmsPlane *plane) { @@ -65,6 +72,18 @@ meta_kms_plane_get_plane_type (MetaKmsPlane *plane) return plane->type; } +void +meta_kms_plane_update_set_rotation (MetaKmsPlane *plane, + MetaKmsPlaneAssignment *plane_assignment, + MetaMonitorTransform transform) +{ + g_return_if_fail (meta_kms_plane_is_transform_handled (plane, transform)); + + meta_kms_plane_assignment_set_plane_property (plane_assignment, + plane->rotation_prop_id, + plane->rotation_map[transform]); +} + gboolean meta_kms_plane_is_transform_handled (MetaKmsPlane *plane, MetaMonitorTransform transform) diff --git a/src/backends/native/meta-kms-plane.h b/src/backends/native/meta-kms-plane.h index 11cb3ad2f..e360f83ca 100644 --- a/src/backends/native/meta-kms-plane.h +++ b/src/backends/native/meta-kms-plane.h @@ -43,6 +43,8 @@ MetaKmsPlane * meta_kms_plane_new (MetaKmsPlaneType type, drmModePlane *drm_plane, drmModeObjectProperties *drm_plane_props); +MetaKmsDevice * meta_kms_plane_get_device (MetaKmsPlane *plane); + uint32_t meta_kms_plane_get_id (MetaKmsPlane *plane); MetaKmsPlaneType meta_kms_plane_get_plane_type (MetaKmsPlane *plane); @@ -61,4 +63,8 @@ gboolean meta_kms_plane_is_format_supported (MetaKmsPlane *plane, gboolean meta_kms_plane_is_usable_with (MetaKmsPlane *plane, MetaKmsCrtc *crtc); +void meta_kms_plane_update_set_rotation (MetaKmsPlane *plane, + MetaKmsPlaneAssignment *plane_assignment, + MetaMonitorTransform transform); + #endif /* META_KMS_PLANE_H */ diff --git a/src/backends/native/meta-kms-private.h b/src/backends/native/meta-kms-private.h index 910779ea7..fa632e1f7 100644 --- a/src/backends/native/meta-kms-private.h +++ b/src/backends/native/meta-kms-private.h @@ -36,14 +36,17 @@ void meta_kms_queue_callback (MetaKms *kms, gpointer user_data, GDestroyNotify user_data_destroy); +int meta_kms_flush_callbacks (MetaKms *kms); + gboolean meta_kms_run_impl_task_sync (MetaKms *kms, MetaKmsImplTaskFunc func, gpointer user_data, GError **error); -GSource * meta_kms_add_source_in_impl (MetaKms *kms, - GSourceFunc func, - gpointer user_data); +GSource * meta_kms_add_source_in_impl (MetaKms *kms, + GSourceFunc func, + gpointer user_data, + GDestroyNotify user_data_destroy); GSource * meta_kms_register_fd_in_impl (MetaKms *kms, int fd, diff --git a/src/backends/native/meta-kms-types.h b/src/backends/native/meta-kms-types.h index 7a9264934..dd14a7be7 100644 --- a/src/backends/native/meta-kms-types.h +++ b/src/backends/native/meta-kms-types.h @@ -20,6 +20,8 @@ #ifndef META_KMS_IMPL_TYPES_H #define META_KMS_IMPL_TYPES_H +#include + typedef struct _MetaKms MetaKms; typedef struct _MetaKmsDevice MetaKmsDevice; @@ -27,9 +29,26 @@ typedef struct _MetaKmsPlane MetaKmsPlane; typedef struct _MetaKmsCrtc MetaKmsCrtc; typedef struct _MetaKmsConnector MetaKmsConnector; +typedef struct _MetaKmsUpdate MetaKmsUpdate; +typedef struct _MetaKmsPlaneAssignment MetaKmsPlaneAssignment; +typedef struct _MetaKmsModeSet MetaKmsModeSet; + +typedef struct _MetaKmsPageFlipFeedback MetaKmsPageFlipFeedback; + typedef struct _MetaKmsImpl MetaKmsImpl; typedef struct _MetaKmsImplDevice MetaKmsImplDevice; +/* 16:16 fixed point */ +typedef int32_t MetaFixed16; + +typedef struct _MetaFixed16Rectangle +{ + MetaFixed16 x; + MetaFixed16 y; + MetaFixed16 width; + MetaFixed16 height; +} MetaFixed16Rectangle; + typedef enum _MetaKmsDeviceFlag { META_KMS_DEVICE_FLAG_NONE = 0, diff --git a/src/backends/native/meta-kms-update-private.h b/src/backends/native/meta-kms-update-private.h new file mode 100644 index 000000000..bf3326ac4 --- /dev/null +++ b/src/backends/native/meta-kms-update-private.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2019 Red Hat + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef META_KMS_UPDATE_PRIVATE_H +#define META_KMS_UPDATE_PRIVATE_H + +#include +#include + +#include "backends/native/meta-kms-types.h" +#include "backends/native/meta-kms-update.h" + +typedef struct _MetaKmsProperty +{ + uint32_t prop_id; + uint64_t value; +} MetaKmsProperty; + +typedef struct _MetaKmsPlaneAssignment +{ + MetaKmsCrtc *crtc; + MetaKmsPlane *plane; + uint32_t fb_id; + MetaFixed16Rectangle src_rect; + MetaFixed16Rectangle dst_rect; + + GList *plane_properties; +} MetaKmsPlaneAssignment; + +typedef struct _MetaKmsModeSet +{ + MetaKmsCrtc *crtc; + GList *connectors; + drmModeModeInfo *drm_mode; +} MetaKmsModeSet; + +typedef struct _MetaKmsConnectorProperty +{ + MetaKmsDevice *device; + MetaKmsConnector *connector; + uint32_t prop_id; + uint64_t value; +} MetaKmsConnectorProperty; + +typedef struct _MetaKmsPageFlip +{ + MetaKmsCrtc *crtc; + const MetaKmsPageFlipFeedback *feedback; + gpointer user_data; + MetaKmsCustomPageFlipFunc custom_page_flip_func; + gpointer custom_page_flip_user_data; +} MetaKmsPageFlip; + +void meta_kms_update_set_connector_property (MetaKmsUpdate *update, + MetaKmsConnector *connector, + uint32_t prop_id, + uint64_t value); + +void meta_kms_plane_assignment_set_plane_property (MetaKmsPlaneAssignment *plane_assignment, + uint32_t prop_id, + uint64_t value); + +GList * meta_kms_update_get_plane_assignments (MetaKmsUpdate *update); + +GList * meta_kms_update_get_mode_sets (MetaKmsUpdate *update); + +GList * meta_kms_update_get_page_flips (MetaKmsUpdate *update); + +GList * meta_kms_update_get_connector_properties (MetaKmsUpdate *update); + +gboolean meta_kms_update_has_mode_set (MetaKmsUpdate *update); + +#endif /* META_KMS_UPDATE_PRIVATE_H */ diff --git a/src/backends/native/meta-kms-update.c b/src/backends/native/meta-kms-update.c new file mode 100644 index 000000000..439e917ba --- /dev/null +++ b/src/backends/native/meta-kms-update.c @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2018 Red Hat + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include "config.h" + +#include "backends/native/meta-kms-update.h" +#include "backends/native/meta-kms-update-private.h" + +#include "backends/meta-display-config-shared.h" +#include "backends/native/meta-kms-plane.h" + +struct _MetaKmsUpdate +{ + MetaPowerSave power_save; + GList *mode_sets; + GList *plane_assignments; + GList *page_flips; + GList *connector_properties; +}; + +static MetaKmsProperty * +meta_kms_property_new (uint32_t prop_id, + uint64_t value) +{ + MetaKmsProperty *prop; + + prop = g_new0 (MetaKmsProperty, 1); + *prop = (MetaKmsProperty) { + .prop_id = prop_id, + .value = value, + }; + + return prop; +} + +static void +meta_kms_property_free (MetaKmsProperty *prop) +{ + g_free (prop); +} + +static void +meta_kms_plane_assignment_free (MetaKmsPlaneAssignment *plane_assignment) +{ + g_list_free_full (plane_assignment->plane_properties, + (GDestroyNotify) meta_kms_property_free); + g_free (plane_assignment); +} + +static void +meta_kms_mode_set_free (MetaKmsModeSet *mode_set) +{ + g_free (mode_set->drm_mode); + g_list_free (mode_set->connectors); + g_free (mode_set); +} + +MetaKmsPlaneAssignment * +meta_kms_update_assign_plane (MetaKmsUpdate *update, + MetaKmsCrtc *crtc, + MetaKmsPlane *plane, + uint32_t fb_id, + MetaFixed16Rectangle src_rect, + MetaFixed16Rectangle dst_rect) +{ + MetaKmsPlaneAssignment *plane_assignment; + + plane_assignment = g_new0 (MetaKmsPlaneAssignment, 1); + *plane_assignment = (MetaKmsPlaneAssignment) { + .crtc = crtc, + .plane = plane, + .fb_id = fb_id, + .src_rect = src_rect, + .dst_rect = dst_rect, + }; + + update->plane_assignments = g_list_prepend (update->plane_assignments, + plane_assignment); + + return plane_assignment; +} + +void +meta_kms_update_mode_set (MetaKmsUpdate *update, + MetaKmsCrtc *crtc, + GList *connectors, + drmModeModeInfo *drm_mode) +{ + MetaKmsModeSet *mode_set; + + mode_set = g_new0 (MetaKmsModeSet, 1); + *mode_set = (MetaKmsModeSet) { + .crtc = crtc, + .connectors = connectors, + .drm_mode = drm_mode ? g_memdup (drm_mode, sizeof *drm_mode) : NULL, + }; + + update->mode_sets = g_list_prepend (update->mode_sets, mode_set); +} + +void +meta_kms_update_set_connector_property (MetaKmsUpdate *update, + MetaKmsConnector *connector, + uint32_t prop_id, + uint64_t value) +{ + MetaKmsConnectorProperty *prop; + + prop = g_new0 (MetaKmsConnectorProperty, 1); + *prop = (MetaKmsConnectorProperty) { + .connector = connector, + .prop_id = prop_id, + .value = value, + }; + + update->connector_properties = g_list_prepend (update->connector_properties, + prop); +} + +void +meta_kms_update_page_flip (MetaKmsUpdate *update, + MetaKmsCrtc *crtc, + const MetaKmsPageFlipFeedback *feedback, + gpointer user_data) +{ + MetaKmsPageFlip *page_flip; + + page_flip = g_new0 (MetaKmsPageFlip, 1); + *page_flip = (MetaKmsPageFlip) { + .crtc = crtc, + .feedback = feedback, + .user_data = user_data, + }; + + update->page_flips = g_list_prepend (update->page_flips, page_flip); +} + +void +meta_kms_update_custom_page_flip (MetaKmsUpdate *update, + MetaKmsCrtc *crtc, + const MetaKmsPageFlipFeedback *feedback, + gpointer user_data, + MetaKmsCustomPageFlipFunc custom_page_flip_func, + gpointer custom_page_flip_user_data) +{ + MetaKmsPageFlip *page_flip; + + page_flip = g_new0 (MetaKmsPageFlip, 1); + *page_flip = (MetaKmsPageFlip) { + .crtc = crtc, + .feedback = feedback, + .user_data = user_data, + .custom_page_flip_func = custom_page_flip_func, + .custom_page_flip_user_data = custom_page_flip_user_data, + }; + + update->page_flips = g_list_prepend (update->page_flips, page_flip); +} + +void +meta_kms_plane_assignment_set_plane_property (MetaKmsPlaneAssignment *plane_assignment, + uint32_t prop_id, + uint64_t value) +{ + MetaKmsProperty *plane_prop; + + plane_prop = meta_kms_property_new (prop_id, value); + + plane_assignment->plane_properties = + g_list_prepend (plane_assignment->plane_properties, plane_prop); +} + +GList * +meta_kms_update_get_plane_assignments (MetaKmsUpdate *update) +{ + return update->plane_assignments; +} + +GList * +meta_kms_update_get_mode_sets (MetaKmsUpdate *update) +{ + return update->mode_sets; +} + +GList * +meta_kms_update_get_page_flips (MetaKmsUpdate *update) +{ + return update->page_flips; +} + +GList * +meta_kms_update_get_connector_properties (MetaKmsUpdate *update) +{ + return update->connector_properties; +} + +gboolean +meta_kms_update_has_mode_set (MetaKmsUpdate *update) +{ + return !!update->mode_sets; +} + +MetaKmsUpdate * +meta_kms_update_new (void) +{ + return g_new0 (MetaKmsUpdate, 1); +} + +void +meta_kms_update_free (MetaKmsUpdate *update) +{ + g_list_free_full (update->plane_assignments, + (GDestroyNotify) meta_kms_plane_assignment_free); + g_list_free_full (update->mode_sets, + (GDestroyNotify) meta_kms_mode_set_free); + g_list_free_full (update->page_flips, g_free); + g_list_free_full (update->connector_properties, g_free); + + g_free (update); +} diff --git a/src/backends/native/meta-kms-update.h b/src/backends/native/meta-kms-update.h new file mode 100644 index 000000000..fc7d83f33 --- /dev/null +++ b/src/backends/native/meta-kms-update.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2018 Red Hat + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef META_KMS_UPDATE_H +#define META_KMS_UPDATE_H + +#include +#include +#include +#include + +#include "backends/meta-monitor-transform.h" +#include "backends/native/meta-kms-types.h" + +struct _MetaKmsPageFlipFeedback +{ + void (* flipped) (MetaKmsCrtc *crtc, + unsigned int sequence, + unsigned int tv_sec, + unsigned int tv_usec, + gpointer user_data); + + void (* mode_set_fallback) (MetaKmsCrtc *crtc, + gpointer user_data); + + void (* discarded) (MetaKmsCrtc *crtc, + gpointer user_data, + const GError *error); +}; + +typedef int (* MetaKmsCustomPageFlipFunc) (gpointer custom_page_flip_data, + gpointer user_data); + +MetaKmsUpdate * meta_kms_update_new (void); + +void meta_kms_update_free (MetaKmsUpdate *update); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (MetaKmsUpdate, meta_kms_update_free) + +void meta_kms_update_mode_set (MetaKmsUpdate *update, + MetaKmsCrtc *crtc, + GList *connectors, + drmModeModeInfo *drm_mode); + +MetaKmsPlaneAssignment * meta_kms_update_assign_plane (MetaKmsUpdate *update, + MetaKmsCrtc *crtc, + MetaKmsPlane *plane, + uint32_t fb_id, + MetaFixed16Rectangle src_rect, + MetaFixed16Rectangle dst_rect); + +void meta_kms_update_page_flip (MetaKmsUpdate *update, + MetaKmsCrtc *crtc, + const MetaKmsPageFlipFeedback *feedback, + gpointer user_data); + +void meta_kms_update_custom_page_flip (MetaKmsUpdate *update, + MetaKmsCrtc *crtc, + const MetaKmsPageFlipFeedback *feedback, + gpointer user_data, + MetaKmsCustomPageFlipFunc custom_page_flip_func, + gpointer custom_page_flip_user_data); + +static inline MetaFixed16 +meta_fixed_16_from_int (int16_t d) +{ + return d * 65536; +} + +static inline int16_t +meta_fixed_16_to_int (MetaFixed16 fixed) +{ + return fixed / 65536; +} + +#endif /* META_KMS_UPDATE_H */ diff --git a/src/backends/native/meta-kms.c b/src/backends/native/meta-kms.c index 31bafd53b..c13360a1d 100644 --- a/src/backends/native/meta-kms.c +++ b/src/backends/native/meta-kms.c @@ -25,6 +25,7 @@ #include "backends/native/meta-kms-device-private.h" #include "backends/native/meta-kms-impl.h" #include "backends/native/meta-kms-impl-simple.h" +#include "backends/native/meta-kms-update-private.h" #include "backends/native/meta-udev.h" typedef struct _MetaKmsCallbackData @@ -64,12 +65,86 @@ struct _MetaKms GList *devices; + MetaKmsUpdate *pending_update; + GList *pending_callbacks; guint callback_source_id; }; G_DEFINE_TYPE (MetaKms, meta_kms, G_TYPE_OBJECT) +static void +meta_kms_update_states_in_impl (MetaKms *kms); + +MetaKmsUpdate * +meta_kms_ensure_pending_update (MetaKms *kms) +{ + if (!kms->pending_update) + kms->pending_update = meta_kms_update_new (); + + return meta_kms_get_pending_update (kms); +} + +MetaKmsUpdate * +meta_kms_get_pending_update (MetaKms *kms) +{ + return kms->pending_update; +} + +static gboolean +meta_kms_update_process_in_impl (MetaKmsImpl *impl, + gpointer user_data, + GError **error) +{ + g_autoptr (MetaKmsUpdate) update = user_data; + gboolean ret; + + ret = meta_kms_impl_process_update (impl, update, error); + + if (meta_kms_update_has_mode_set (update)) + meta_kms_update_states_in_impl (meta_kms_impl_get_kms (impl)); + + return ret; +} + +static gboolean +meta_kms_post_update_sync (MetaKms *kms, + MetaKmsUpdate *update, + GError **error) +{ + return meta_kms_run_impl_task_sync (kms, + meta_kms_update_process_in_impl, + update, + error); +} + +gboolean +meta_kms_post_pending_update_sync (MetaKms *kms, + GError **error) +{ + return meta_kms_post_update_sync (kms, + g_steal_pointer (&kms->pending_update), + error); +} + +static gboolean +meta_kms_discard_pending_page_flips_in_impl (MetaKmsImpl *impl, + gpointer user_data, + GError **error) +{ + meta_kms_impl_discard_pending_page_flips (impl); + return TRUE; +} + +void +meta_kms_discard_pending_page_flips (MetaKms *kms) +{ + meta_kms_run_impl_task_sync (kms, + meta_kms_discard_pending_page_flips_in_impl, + NULL, + NULL); +} + static void meta_kms_callback_data_free (MetaKmsCallbackData *callback_data) { @@ -78,11 +153,11 @@ meta_kms_callback_data_free (MetaKmsCallbackData *callback_data) g_slice_free (MetaKmsCallbackData, callback_data); } -static gboolean -callback_idle (gpointer user_data) +static int +flush_callbacks (MetaKms *kms) { - MetaKms *kms = user_data; GList *l; + int callback_count = 0; for (l = kms->pending_callbacks; l; l = l->next) { @@ -90,11 +165,22 @@ callback_idle (gpointer user_data) callback_data->callback (kms, callback_data->user_data); meta_kms_callback_data_free (callback_data); + callback_count++; } g_list_free (kms->pending_callbacks); kms->pending_callbacks = NULL; + return callback_count; +} + +static gboolean +callback_idle (gpointer user_data) +{ + MetaKms *kms = user_data; + + flush_callbacks (kms); + kms->callback_source_id = 0; return G_SOURCE_REMOVE; } @@ -119,6 +205,17 @@ meta_kms_queue_callback (MetaKms *kms, kms->callback_source_id = g_idle_add (callback_idle, kms); } +int +meta_kms_flush_callbacks (MetaKms *kms) +{ + int callback_count; + + callback_count = flush_callbacks (kms); + g_clear_handle_id (&kms->callback_source_id, g_source_remove); + + return callback_count; +} + gboolean meta_kms_run_impl_task_sync (MetaKms *kms, MetaKmsImplTaskFunc func, @@ -156,9 +253,10 @@ static GSourceFuncs simple_impl_source_funcs = { }; GSource * -meta_kms_add_source_in_impl (MetaKms *kms, - GSourceFunc func, - gpointer user_data) +meta_kms_add_source_in_impl (MetaKms *kms, + GSourceFunc func, + gpointer user_data, + GDestroyNotify user_data_destroy) { GSource *source; MetaKmsSimpleImplSource *simple_impl_source; @@ -170,7 +268,7 @@ meta_kms_add_source_in_impl (MetaKms *kms, simple_impl_source = (MetaKmsSimpleImplSource *) source; simple_impl_source->kms = kms; - g_source_set_callback (source, func, user_data, NULL); + g_source_set_callback (source, func, user_data, user_data_destroy); g_source_attach (source, g_main_context_get_thread_default ()); return source; @@ -245,14 +343,13 @@ meta_kms_in_impl_task (MetaKms *kms) return kms->in_impl_task; } -static gboolean -update_states_in_impl (MetaKmsImpl *impl, - gpointer user_data, - GError **error) +static void +meta_kms_update_states_in_impl (MetaKms *kms) { - MetaKms *kms = user_data; GList *l; + meta_assert_in_kms_impl (kms); + for (l = kms->devices; l; l = l->next) { MetaKmsDevice *device = l->data; @@ -260,6 +357,16 @@ update_states_in_impl (MetaKmsImpl *impl, meta_kms_impl_device_update_states (impl_device); } +} + +static gboolean +update_states_in_impl (MetaKmsImpl *impl, + gpointer user_data, + GError **error) +{ + MetaKms *kms = user_data; + + meta_kms_update_states_in_impl (kms); return TRUE; } diff --git a/src/backends/native/meta-kms.h b/src/backends/native/meta-kms.h index 7c4073256..cfe4e9105 100644 --- a/src/backends/native/meta-kms.h +++ b/src/backends/native/meta-kms.h @@ -28,6 +28,15 @@ #define META_TYPE_KMS (meta_kms_get_type ()) G_DECLARE_FINAL_TYPE (MetaKms, meta_kms, META, KMS, GObject) +MetaKmsUpdate * meta_kms_ensure_pending_update (MetaKms *kms); + +MetaKmsUpdate * meta_kms_get_pending_update (MetaKms *kms); + +gboolean meta_kms_post_pending_update_sync (MetaKms *kms, + GError **error); + +void meta_kms_discard_pending_page_flips (MetaKms *kms); + MetaBackend * meta_kms_get_backend (MetaKms *kms); MetaKmsDevice * meta_kms_create_device (MetaKms *kms, diff --git a/src/backends/native/meta-monitor-manager-kms.c b/src/backends/native/meta-monitor-manager-kms.c index 6fa230020..58fd93fcc 100644 --- a/src/backends/native/meta-monitor-manager-kms.c +++ b/src/backends/native/meta-monitor-manager-kms.c @@ -54,6 +54,8 @@ #include "backends/native/meta-backend-native.h" #include "backends/native/meta-crtc-kms.h" #include "backends/native/meta-gpu-kms.h" +#include "backends/native/meta-kms-update.h" +#include "backends/native/meta-kms.h" #include "backends/native/meta-launcher.h" #include "backends/native/meta-output-kms.h" #include "backends/native/meta-renderer-native.h" @@ -115,6 +117,11 @@ static void meta_monitor_manager_kms_set_power_save_mode (MetaMonitorManager *manager, MetaPowerSave mode) { + MetaBackend *backend = meta_monitor_manager_get_backend (manager); + MetaBackendNative *backend_native = META_BACKEND_NATIVE (backend); + MetaKms *kms = meta_backend_native_get_kms (backend_native); + MetaKmsUpdate *kms_update; + g_autoptr (GError) error = NULL; uint64_t state; GList *l; @@ -135,12 +142,16 @@ meta_monitor_manager_kms_set_power_save_mode (MetaMonitorManager *manager, return; } - for (l = meta_backend_get_gpus (manager->backend); l; l = l->next) + kms_update = meta_kms_ensure_pending_update (kms); + for (l = meta_backend_get_gpus (backend); l; l = l->next) { MetaGpuKms *gpu_kms = l->data; - meta_gpu_kms_set_power_save_mode (gpu_kms, state); + meta_gpu_kms_set_power_save_mode (gpu_kms, state, kms_update); } + + if (!meta_kms_post_pending_update_sync (kms, &error)) + g_warning ("Failed to DPMS: %s", error->message); } static void @@ -214,8 +225,6 @@ apply_crtc_assignments (MetaMonitorManager *manager, meta_output_assign_crtc (output, crtc); } } - - meta_crtc_kms_apply_transform (crtc); } /* Disable CRTCs not mentioned in the list (they have is_dirty == FALSE, because they weren't seen in the first loop) */ @@ -253,8 +262,6 @@ apply_crtc_assignments (MetaMonitorManager *manager, output->is_primary = output_info->is_primary; output->is_presentation = output_info->is_presentation; output->is_underscanning = output_info->is_underscanning; - - meta_output_kms_set_underscan (output); } /* Disable outputs not mentioned in the list */ diff --git a/src/backends/native/meta-output-kms.c b/src/backends/native/meta-output-kms.c index 1d3386387..42633702c 100644 --- a/src/backends/native/meta-output-kms.c +++ b/src/backends/native/meta-output-kms.c @@ -42,67 +42,42 @@ typedef struct _MetaOutputKms MetaOutput parent; MetaKmsConnector *kms_connector; - - drmModeConnector *connector; - - uint32_t dpms_prop_id; - - uint32_t underscan_prop_id; - uint32_t underscan_hborder_prop_id; - uint32_t underscan_vborder_prop_id; } MetaOutputKms; -void -meta_output_kms_set_underscan (MetaOutput *output) +MetaKmsConnector * +meta_output_kms_get_kms_connector (MetaOutput *output) { MetaOutputKms *output_kms = output->driver_private; - MetaGpu *gpu = meta_output_get_gpu (output); - MetaGpuKms *gpu_kms = META_GPU_KMS (gpu); - MetaCrtc *crtc; - int kms_fd; - uint32_t connector_id; - if (!output_kms->underscan_prop_id) + return output_kms->kms_connector; +} + +void +meta_output_kms_set_underscan (MetaOutput *output, + MetaKmsUpdate *kms_update) +{ + MetaOutputKms *output_kms = output->driver_private; + + if (!output->supports_underscanning) return; - crtc = meta_output_get_assigned_crtc (output); - kms_fd = meta_gpu_kms_get_fd (gpu_kms); - connector_id = output_kms->connector->connector_id; - - if (output->is_underscanning && crtc && crtc->current_mode) + if (output->is_underscanning) { - drmModeObjectSetProperty (kms_fd, connector_id, - DRM_MODE_OBJECT_CONNECTOR, - output_kms->underscan_prop_id, - (uint64_t) 1); + MetaCrtc *crtc; + uint64_t hborder, vborder; - if (output_kms->underscan_hborder_prop_id) - { - uint64_t value; - - value = MIN (128, crtc->current_mode->width * 0.05); - drmModeObjectSetProperty (kms_fd, connector_id, - DRM_MODE_OBJECT_CONNECTOR, - output_kms->underscan_hborder_prop_id, - value); - } - if (output_kms->underscan_vborder_prop_id) - { - uint64_t value; - - value = MIN (128, crtc->current_mode->height * 0.05); - drmModeObjectSetProperty (kms_fd, connector_id, - DRM_MODE_OBJECT_CONNECTOR, - output_kms->underscan_vborder_prop_id, - value); - } + crtc = meta_output_get_assigned_crtc (output); + hborder = MIN (128, (uint64_t) round (crtc->current_mode->width * 0.05)); + vborder = MIN (128, (uint64_t) round (crtc->current_mode->height * 0.05)); + meta_kms_connector_set_underscanning (output_kms->kms_connector, + kms_update, + hborder, + vborder); } else { - drmModeObjectSetProperty (kms_fd, connector_id, - DRM_MODE_OBJECT_CONNECTOR, - output_kms->underscan_prop_id, - (uint64_t) 0); + meta_kms_connector_unset_underscanning (output_kms->kms_connector, + kms_update); } } @@ -115,24 +90,15 @@ meta_output_kms_get_connector_id (MetaOutput *output) } void -meta_output_kms_set_power_save_mode (MetaOutput *output, - uint64_t state) +meta_output_kms_set_power_save_mode (MetaOutput *output, + uint64_t dpms_state, + MetaKmsUpdate *kms_update) { MetaOutputKms *output_kms = output->driver_private; - MetaGpu *gpu = meta_output_get_gpu (output); - MetaGpuKms *gpu_kms = META_GPU_KMS (gpu); - if (output_kms->dpms_prop_id != 0) - { - int fd; - - fd = meta_gpu_kms_get_fd (gpu_kms); - if (drmModeObjectSetProperty (fd, output_kms->connector->connector_id, - DRM_MODE_OBJECT_CONNECTOR, - output_kms->dpms_prop_id, state) < 0) - g_warning ("Failed to set power save mode for output %s: %s", - output->name, strerror (errno)); - } + meta_kms_connector_update_set_dpms_state (output_kms->kms_connector, + kms_update, + dpms_state); } gboolean @@ -162,40 +128,6 @@ meta_output_kms_read_edid (MetaOutput *output) return g_bytes_new_from_bytes (edid_data, 0, g_bytes_get_size (edid_data)); } -static void -find_connector_properties (MetaGpuKms *gpu_kms, - MetaOutput *output, - drmModeConnector *connector) -{ - MetaOutputKms *output_kms = output->driver_private; - int fd; - int i; - - fd = meta_gpu_kms_get_fd (gpu_kms); - - for (i = 0; i < connector->count_props; i++) - { - drmModePropertyPtr prop = drmModeGetProperty (fd, connector->props[i]); - if (!prop) - continue; - - if ((prop->flags & DRM_MODE_PROP_ENUM) && - strcmp (prop->name, "DPMS") == 0) - output_kms->dpms_prop_id = prop->prop_id; - else if ((prop->flags & DRM_MODE_PROP_ENUM) && - strcmp (prop->name, "underscan") == 0) - output_kms->underscan_prop_id = prop->prop_id; - else if ((prop->flags & DRM_MODE_PROP_RANGE) && - strcmp (prop->name, "underscan hborder") == 0) - output_kms->underscan_hborder_prop_id = prop->prop_id; - else if ((prop->flags & DRM_MODE_PROP_RANGE) && - strcmp (prop->name, "underscan vborder") == 0) - output_kms->underscan_vborder_prop_id = prop->prop_id; - - drmModeFreeProperty (prop); - } -} - static void meta_output_destroy_notify (MetaOutput *output) { @@ -344,7 +276,6 @@ init_output_modes (MetaOutput *output, MetaOutput * meta_create_kms_output (MetaGpuKms *gpu_kms, MetaKmsConnector *kms_connector, - drmModeConnector *connector, MetaOutput *old_output, GError **error) { @@ -373,8 +304,6 @@ meta_create_kms_output (MetaGpuKms *gpu_kms, output_kms->kms_connector = kms_connector; - find_connector_properties (gpu_kms, output, connector); - connector_state = meta_kms_connector_get_current_state (kms_connector); panel_orientation_transform = connector_state->panel_orientation_transform; @@ -443,7 +372,8 @@ meta_create_kms_output (MetaGpuKms *gpu_kms, output->suggested_x = connector_state->suggested_x; output->suggested_y = connector_state->suggested_y; output->hotplug_mode_update = connector_state->hotplug_mode_update; - output->supports_underscanning = output_kms->underscan_prop_id != 0; + output->supports_underscanning = + meta_kms_connector_is_underscanning_supported (kms_connector); meta_output_parse_edid (output, connector_state->edid_data); diff --git a/src/backends/native/meta-output-kms.h b/src/backends/native/meta-output-kms.h index f35aa130f..47ce68a3c 100644 --- a/src/backends/native/meta-output-kms.h +++ b/src/backends/native/meta-output-kms.h @@ -27,21 +27,24 @@ #include "backends/native/meta-gpu-kms.h" #include "backends/native/meta-kms-types.h" -void meta_output_kms_set_underscan (MetaOutput *output); +void meta_output_kms_set_power_save_mode (MetaOutput *output, + uint64_t dpms_state, + MetaKmsUpdate *kms_update); -void meta_output_kms_set_power_save_mode (MetaOutput *output, - uint64_t state); +void meta_output_kms_set_underscan (MetaOutput *output, + MetaKmsUpdate *kms_update); gboolean meta_output_kms_can_clone (MetaOutput *output, MetaOutput *other_output); +MetaKmsConnector * meta_output_kms_get_kms_connector (MetaOutput *output); + uint32_t meta_output_kms_get_connector_id (MetaOutput *output); GBytes * meta_output_kms_read_edid (MetaOutput *output); MetaOutput * meta_create_kms_output (MetaGpuKms *gpu_kms, MetaKmsConnector *kms_connector, - drmModeConnector *connector, MetaOutput *old_output, GError **error); diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c index 2cb3f1cf7..5228310fc 100644 --- a/src/backends/native/meta-renderer-native.c +++ b/src/backends/native/meta-renderer-native.c @@ -62,11 +62,12 @@ #include "backends/native/meta-drm-buffer-gbm.h" #include "backends/native/meta-drm-buffer.h" #include "backends/native/meta-gpu-kms.h" +#include "backends/native/meta-kms-update.h" #include "backends/native/meta-kms-utils.h" -#include "backends/native/meta-monitor-manager-kms.h" +#include "backends/native/meta-kms.h" +#include "backends/native/meta-output-kms.h" #include "backends/native/meta-renderer-native-gles3.h" #include "backends/native/meta-renderer-native.h" -#include "meta-marshal.h" #include "cogl/cogl.h" #include "core/boxes-private.h" @@ -182,7 +183,6 @@ typedef struct _MetaOnscreenNative } egl; #endif - gboolean pending_queue_swap_notify; gboolean pending_swap_notify; gboolean pending_set_crtc; @@ -190,9 +190,6 @@ typedef struct _MetaOnscreenNative int64_t pending_queue_swap_notify_frame_count; int64_t pending_swap_notify_frame_count; - GList *pending_page_flip_retries; - GSource *retry_page_flips_source; - MetaRendererView *view; int total_pending_flips; } MetaOnscreenNative; @@ -215,7 +212,7 @@ struct _MetaRendererNative int64_t frame_counter; gboolean pending_unset_disabled_crtcs; - GList *power_save_page_flip_closures; + GList *power_save_page_flip_onscreens; guint power_save_page_flip_source_id; }; @@ -1292,11 +1289,9 @@ meta_onscreen_native_swap_drm_fb (CoglOnscreen *onscreen) } static void -on_crtc_flipped (GClosure *closure, - MetaGpuKms *gpu_kms, - MetaCrtc *crtc, - int64_t page_flip_time_ns, - MetaRendererView *view) +notify_view_crtc_presented (MetaRendererView *view, + MetaKmsCrtc *kms_crtc, + int64_t time_ns) { ClutterStageView *stage_view = CLUTTER_STAGE_VIEW (view); CoglFramebuffer *framebuffer = @@ -1307,24 +1302,28 @@ on_crtc_flipped (GClosure *closure, MetaRendererNative *renderer_native = onscreen_native->renderer_native; MetaGpuKms *render_gpu = onscreen_native->render_gpu; CoglFrameInfo *frame_info; + MetaCrtc *crtc; float refresh_rate; - - frame_info = g_queue_peek_tail (&onscreen->pending_frame_infos); - refresh_rate = crtc && crtc->current_mode ? - crtc->current_mode->refresh_rate : - 0.0f; + MetaGpuKms *gpu_kms; /* Only keep the frame info for the fastest CRTC in use, which may not be * the first one to complete a flip. By only telling the compositor about the * fastest monitor(s) we direct it to produce new frames fast enough to * satisfy all monitors. */ + frame_info = g_queue_peek_tail (&onscreen->pending_frame_infos); + + crtc = meta_crtc_kms_from_kms_crtc (kms_crtc); + refresh_rate = crtc && crtc->current_mode ? + crtc->current_mode->refresh_rate : + 0.0f; if (refresh_rate >= frame_info->refresh_rate) { - frame_info->presentation_time = page_flip_time_ns; + frame_info->presentation_time = time_ns; frame_info->refresh_rate = refresh_rate; } + gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (crtc)); if (gpu_kms != render_gpu) { MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state; @@ -1338,8 +1337,6 @@ on_crtc_flipped (GClosure *closure, { MetaRendererNativeGpuData *renderer_gpu_data; - onscreen_native->pending_queue_swap_notify = FALSE; - meta_onscreen_native_queue_swap_notify (onscreen); renderer_gpu_data = @@ -1358,6 +1355,92 @@ on_crtc_flipped (GClosure *closure, } } +static int64_t +timeval_to_nanoseconds (const struct timeval *tv) +{ + int64_t usec = ((int64_t) tv->tv_sec) * G_USEC_PER_SEC + tv->tv_usec; + int64_t nsec = usec * 1000; + + return nsec; +} + +static void +page_flip_feedback_flipped (MetaKmsCrtc *kms_crtc, + unsigned int sequence, + unsigned int tv_sec, + unsigned int tv_usec, + gpointer user_data) +{ + MetaRendererView *view = user_data; + struct timeval page_flip_time; + + page_flip_time = (struct timeval) { + .tv_sec = tv_sec, + .tv_usec = tv_usec, + }; + + notify_view_crtc_presented (view, kms_crtc, + timeval_to_nanoseconds (&page_flip_time)); + + g_object_unref (view); +} + +static void +page_flip_feedback_mode_set_fallback (MetaKmsCrtc *kms_crtc, + gpointer user_data) +{ + MetaRendererView *view = user_data; + MetaCrtc *crtc; + MetaGpuKms *gpu_kms; + int64_t now_ns; + + /* + * We ended up not page flipping, thus we don't have a presentation time to + * use. Lets use the next best thing: the current time. + */ + + crtc = meta_crtc_kms_from_kms_crtc (kms_crtc); + gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (crtc)); + now_ns = meta_gpu_kms_get_current_time_ns (gpu_kms); + + notify_view_crtc_presented (view, kms_crtc, now_ns); + + g_object_unref (view); +} + +static void +page_flip_feedback_discarded (MetaKmsCrtc *kms_crtc, + gpointer user_data, + const GError *error) +{ + MetaRendererView *view = user_data; + MetaCrtc *crtc; + MetaGpuKms *gpu_kms; + int64_t now_ns; + + /* + * Page flipping failed, but we want to fail gracefully, so to avoid freezing + * the frame clack, pretend we flipped. + */ + + if (error) + g_warning ("Page flip discarded: %s", error->message); + + crtc = meta_crtc_kms_from_kms_crtc (kms_crtc); + gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (crtc)); + now_ns = meta_gpu_kms_get_current_time_ns (gpu_kms); + + notify_view_crtc_presented (view, kms_crtc, now_ns); + + g_object_unref (view); +} + +static const MetaKmsPageFlipFeedback page_flip_feedback = { + .flipped = page_flip_feedback_flipped, + .mode_set_fallback = page_flip_feedback_mode_set_fallback, + .discarded = page_flip_feedback_discarded, +}; + static void free_next_secondary_bo (MetaGpuKms *gpu_kms, MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state) @@ -1375,73 +1458,29 @@ free_next_secondary_bo (MetaGpuKms *gpu_kms, } } -static void -flip_closure_destroyed (MetaRendererView *view) -{ - ClutterStageView *stage_view = CLUTTER_STAGE_VIEW (view); - CoglFramebuffer *framebuffer = - clutter_stage_view_get_onscreen (stage_view); - CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer); - CoglOnscreenEGL *onscreen_egl = onscreen->winsys; - MetaOnscreenNative *onscreen_native = onscreen_egl->platform; - MetaRendererNative *renderer_native = onscreen_native->renderer_native; - MetaGpuKms *render_gpu = onscreen_native->render_gpu; - MetaRendererNativeGpuData *renderer_gpu_data; - - renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native, - render_gpu); - switch (renderer_gpu_data->mode) - { - case META_RENDERER_NATIVE_MODE_GBM: - g_clear_object (&onscreen_native->gbm.next_fb); - - g_hash_table_foreach (onscreen_native->secondary_gpu_states, - (GHFunc) free_next_secondary_bo, - NULL); - - break; #ifdef HAVE_EGL_DEVICE - case META_RENDERER_NATIVE_MODE_EGL_DEVICE: - break; -#endif - } - - if (onscreen_native->pending_queue_swap_notify) - { - meta_onscreen_native_queue_swap_notify (onscreen); - onscreen_native->pending_queue_swap_notify = FALSE; - } - - g_object_unref (view); -} - -#ifdef HAVE_EGL_DEVICE -static gboolean -flip_egl_stream (MetaOnscreenNative *onscreen_native, - GClosure *flip_closure) +static int +custom_egl_stream_page_flip (gpointer custom_page_flip_data, + gpointer user_data) { + MetaOnscreenNative *onscreen_native = custom_page_flip_data; + MetaRendererView *view = user_data; + MetaEgl *egl = meta_onscreen_native_get_egl (onscreen_native); MetaRendererNativeGpuData *renderer_gpu_data; EGLDisplay *egl_display; - MetaEgl *egl = meta_onscreen_native_get_egl (onscreen_native); - MetaGpuKmsFlipClosureContainer *closure_container; EGLAttrib *acquire_attribs; - GError *error = NULL; + g_autoptr (GError) error = NULL; + + acquire_attribs = (EGLAttrib[]) { + EGL_DRM_FLIP_EVENT_DATA_NV, + (EGLAttrib) view, + EGL_NONE + }; renderer_gpu_data = meta_renderer_native_get_gpu_data (onscreen_native->renderer_native, onscreen_native->render_gpu); - closure_container = - meta_gpu_kms_wrap_flip_closure (onscreen_native->render_gpu, - NULL, - flip_closure); - - acquire_attribs = (EGLAttrib[]) { - EGL_DRM_FLIP_EVENT_DATA_NV, - (EGLAttrib) closure_container, - EGL_NONE - }; - egl_display = renderer_gpu_data->egl_display; if (!meta_egl_stream_consumer_acquire_attrib (egl, egl_display, @@ -1449,28 +1488,21 @@ flip_egl_stream (MetaOnscreenNative *onscreen_native, acquire_attribs, &error)) { - if (error->domain != META_EGL_ERROR || - error->code != EGL_RESOURCE_BUSY_EXT) - { - g_warning ("Failed to flip EGL stream: %s", error->message); - } - g_error_free (error); - meta_gpu_kms_flip_closure_container_free (closure_container); - return FALSE; + if (g_error_matches (error, META_EGL_ERROR, EGL_RESOURCE_BUSY_EXT)) + return -EBUSY; + else + return -EINVAL; } - return TRUE; + return 0; } #endif /* HAVE_EGL_DEVICE */ -static gboolean -is_timestamp_earlier_than (uint64_t ts1, - uint64_t ts2) +static void +dummy_power_save_page_flip (CoglOnscreen *onscreen) { - if (ts1 == ts2) - return FALSE; - else - return ts2 - ts1 < UINT64_MAX / 2; + meta_onscreen_native_swap_drm_fb (onscreen); + meta_onscreen_native_queue_swap_notify (onscreen); } static gboolean @@ -1478,18 +1510,22 @@ dummy_power_save_page_flip_cb (gpointer user_data) { MetaRendererNative *renderer_native = user_data; - g_list_free_full (renderer_native->power_save_page_flip_closures, - (GDestroyNotify) g_closure_unref); - renderer_native->power_save_page_flip_closures = NULL; + g_list_foreach (renderer_native->power_save_page_flip_onscreens, + (GFunc) dummy_power_save_page_flip, NULL); + g_list_free_full (renderer_native->power_save_page_flip_onscreens, + (GDestroyNotify) cogl_object_unref); + renderer_native->power_save_page_flip_onscreens = NULL; renderer_native->power_save_page_flip_source_id = 0; return G_SOURCE_REMOVE; } static void -queue_dummy_power_save_page_flip (MetaRendererNative *renderer_native, - GClosure *flip_closure) +queue_dummy_power_save_page_flip (CoglOnscreen *onscreen) { + CoglOnscreenEGL *onscreen_egl = onscreen->winsys; + MetaOnscreenNative *onscreen_native = onscreen_egl->platform; + MetaRendererNative *renderer_native = onscreen_native->renderer_native; const unsigned int timeout_ms = 100; if (!renderer_native->power_save_page_flip_source_id) @@ -1500,225 +1536,16 @@ queue_dummy_power_save_page_flip (MetaRendererNative *renderer_native, renderer_native); } - renderer_native->power_save_page_flip_closures = - g_list_prepend (renderer_native->power_save_page_flip_closures, - g_closure_ref (flip_closure)); -} - -typedef struct _RetryPageFlipData -{ - MetaCrtc *crtc; - uint32_t fb_id; - GClosure *flip_closure; - uint64_t retry_time_us; -} RetryPageFlipData; - -static void -retry_page_flip_data_free (RetryPageFlipData *retry_page_flip_data) -{ - g_closure_unref (retry_page_flip_data->flip_closure); - g_free (retry_page_flip_data); + renderer_native->power_save_page_flip_onscreens = + g_list_prepend (renderer_native->power_save_page_flip_onscreens, + cogl_object_ref (onscreen)); } static void -retry_page_flip_data_fake_flipped (RetryPageFlipData *retry_page_flip_data, - MetaOnscreenNative *onscreen_native) -{ - MetaCrtc *crtc = retry_page_flip_data->crtc; - MetaGpuKms *gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (crtc)); - - if (gpu_kms != onscreen_native->render_gpu) - { - MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state; - - secondary_gpu_state = - meta_onscreen_native_get_secondary_gpu_state (onscreen_native, - gpu_kms); - secondary_gpu_state->pending_flips--; - } - - onscreen_native->total_pending_flips--; -} - -static gboolean -retry_page_flips (gpointer user_data) -{ - MetaOnscreenNative *onscreen_native = user_data; - MetaRendererNative *renderer_native = onscreen_native->renderer_native; - MetaMonitorManager *monitor_manager = - meta_backend_get_monitor_manager (renderer_native->backend); - uint64_t now_us; - MetaPowerSave power_save_mode; - GList *l; - - now_us = g_source_get_time (onscreen_native->retry_page_flips_source); - power_save_mode = meta_monitor_manager_get_power_save_mode (monitor_manager); - - l = onscreen_native->pending_page_flip_retries; - while (l) - { - RetryPageFlipData *retry_page_flip_data = l->data; - MetaCrtc *crtc = retry_page_flip_data->crtc; - MetaGpuKms *gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (crtc)); - GList *l_next = l->next; - g_autoptr (GError) error = NULL; - gboolean did_flip; - - if (power_save_mode != META_POWER_SAVE_ON) - { - onscreen_native->pending_page_flip_retries = - g_list_remove_link (onscreen_native->pending_page_flip_retries, l); - - retry_page_flip_data_fake_flipped (retry_page_flip_data, - onscreen_native); - retry_page_flip_data_free (retry_page_flip_data); - - l = l_next; - continue; - } - - if (is_timestamp_earlier_than (now_us, - retry_page_flip_data->retry_time_us)) - { - l = l_next; - continue; - } - - did_flip = meta_gpu_kms_flip_crtc (gpu_kms, - crtc, - retry_page_flip_data->fb_id, - retry_page_flip_data->flip_closure, - &error); - if (!did_flip && - g_error_matches (error, G_IO_ERROR, G_IO_ERROR_BUSY)) - { - retry_page_flip_data->retry_time_us += - (uint64_t) (G_USEC_PER_SEC / crtc->current_mode->refresh_rate); - l = l_next; - continue; - } - - onscreen_native->pending_page_flip_retries = - g_list_remove_link (onscreen_native->pending_page_flip_retries, l); - - if (!did_flip) - { - if (!g_error_matches (error, - G_IO_ERROR, - G_IO_ERROR_PERMISSION_DENIED)) - g_critical ("Failed to page flip: %s", error->message); - - retry_page_flip_data_fake_flipped (retry_page_flip_data, - onscreen_native); - } - - retry_page_flip_data_free (retry_page_flip_data); - - l = l_next; - } - - if (onscreen_native->pending_page_flip_retries) - { - GList *l; - uint64_t earliest_retry_time_us = 0; - - for (l = onscreen_native->pending_page_flip_retries; l; l = l->next) - { - RetryPageFlipData *retry_page_flip_data = l->data; - - if (l == onscreen_native->pending_page_flip_retries || - is_timestamp_earlier_than (retry_page_flip_data->retry_time_us, - earliest_retry_time_us)) - earliest_retry_time_us = retry_page_flip_data->retry_time_us; - } - - g_source_set_ready_time (onscreen_native->retry_page_flips_source, - earliest_retry_time_us); - return G_SOURCE_CONTINUE; - } - else - { - meta_backend_thaw_updates (renderer_native->backend); - g_clear_pointer (&onscreen_native->retry_page_flips_source, - g_source_unref); - return G_SOURCE_REMOVE; - } -} - -static gboolean -retry_page_flips_source_dispatch (GSource *source, - GSourceFunc callback, - gpointer user_data) -{ - return callback (user_data); -} - -static GSourceFuncs retry_page_flips_source_funcs = { - .dispatch = retry_page_flips_source_dispatch, -}; - -static void -schedule_retry_page_flip (MetaOnscreenNative *onscreen_native, - MetaCrtc *crtc, - uint32_t fb_id, - GClosure *flip_closure) -{ - MetaRendererNative *renderer_native = onscreen_native->renderer_native; - RetryPageFlipData *retry_page_flip_data; - uint64_t now_us; - uint64_t retry_time_us; - - now_us = g_get_monotonic_time (); - retry_time_us = - now_us + (uint64_t) (G_USEC_PER_SEC / crtc->current_mode->refresh_rate); - - retry_page_flip_data = g_new0 (RetryPageFlipData, 1); - retry_page_flip_data->crtc = crtc; - retry_page_flip_data->fb_id = fb_id; - retry_page_flip_data->flip_closure = g_closure_ref (flip_closure); - retry_page_flip_data->retry_time_us = retry_time_us; - - if (!onscreen_native->retry_page_flips_source) - { - GSource *source; - - source = g_source_new (&retry_page_flips_source_funcs, sizeof (GSource)); - g_source_set_callback (source, retry_page_flips, onscreen_native, NULL); - g_source_set_ready_time (source, retry_time_us); - g_source_attach (source, NULL); - - onscreen_native->retry_page_flips_source = source; - meta_backend_freeze_updates (renderer_native->backend); - } - else - { - GList *l; - - for (l = onscreen_native->pending_page_flip_retries; l; l = l->next) - { - RetryPageFlipData *pending_retry_page_flip_data = l->data; - uint64_t pending_retry_time_us = - pending_retry_page_flip_data->retry_time_us; - - if (is_timestamp_earlier_than (retry_time_us, pending_retry_time_us)) - { - g_source_set_ready_time (onscreen_native->retry_page_flips_source, - retry_time_us); - break; - } - } - } - - onscreen_native->pending_page_flip_retries = - g_list_append (onscreen_native->pending_page_flip_retries, - retry_page_flip_data); -} - -static gboolean -meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen, - GClosure *flip_closure, - MetaCrtc *crtc, - GError **error) +meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen, + MetaRendererView *view, + MetaCrtc *crtc, + MetaKmsUpdate *kms_update) { CoglOnscreenEGL *onscreen_egl = onscreen->winsys; MetaOnscreenNative *onscreen_native = onscreen_egl->platform; @@ -1748,25 +1575,11 @@ meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen, fb_id = meta_drm_buffer_get_fb_id (secondary_gpu_state->gbm.next_fb); } - if (!meta_gpu_kms_flip_crtc (gpu_kms, - crtc, - fb_id, - flip_closure, - error)) - { - if (g_error_matches (*error, - G_IO_ERROR, - G_IO_ERROR_BUSY)) - { - g_clear_error (error); - schedule_retry_page_flip (onscreen_native, crtc, - fb_id, flip_closure); - } - else - { - return FALSE; - } - } + meta_crtc_kms_assign_primary_plane (crtc, fb_id, kms_update); + meta_crtc_kms_page_flip (crtc, + &page_flip_feedback, + g_object_ref (view), + kms_update); onscreen_native->total_pending_flips++; if (secondary_gpu_state) @@ -1775,166 +1588,78 @@ meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen, break; #ifdef HAVE_EGL_DEVICE case META_RENDERER_NATIVE_MODE_EGL_DEVICE: - if (flip_egl_stream (onscreen_native, - flip_closure)) - onscreen_native->total_pending_flips++; + meta_kms_update_custom_page_flip (kms_update, + meta_crtc_kms_get_kms_crtc (crtc), + &page_flip_feedback, + g_object_ref (view), + custom_egl_stream_page_flip, + onscreen_native); + onscreen_native->total_pending_flips++; break; #endif } - - return TRUE; } -static void -set_crtc_fb (CoglOnscreen *onscreen, - MetaLogicalMonitor *logical_monitor, - MetaCrtc *crtc, - uint32_t render_fb_id) +typedef struct _SetCrtcModeData { - CoglOnscreenEGL *onscreen_egl = onscreen->winsys; - MetaOnscreenNative *onscreen_native = onscreen_egl->platform; - MetaGpuKms *render_gpu = onscreen_native->render_gpu; - MetaGpuKms *gpu_kms; - int x, y; - uint32_t fb_id; - - gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (crtc)); - if (gpu_kms == render_gpu) - { - fb_id = render_fb_id; - } - else - { - MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state; - - secondary_gpu_state = get_secondary_gpu_state (onscreen, gpu_kms); - if (!secondary_gpu_state) - return; - - fb_id = meta_drm_buffer_get_fb_id (secondary_gpu_state->gbm.next_fb); - } - - x = crtc->rect.x - logical_monitor->rect.x; - y = crtc->rect.y - logical_monitor->rect.y; - - meta_gpu_kms_apply_crtc_mode (gpu_kms, crtc, x, y, fb_id); -} - -typedef struct _SetCrtcFbData -{ - CoglOnscreen *onscreen; - uint32_t fb_id; -} SetCrtcFbData; - -static void -set_crtc_fb_cb (MetaLogicalMonitor *logical_monitor, - MetaOutput *output, - MetaCrtc *crtc, - gpointer user_data) -{ - SetCrtcFbData *data = user_data; - CoglOnscreen *onscreen = data->onscreen; - - set_crtc_fb (onscreen, logical_monitor, crtc, data->fb_id); -} - -static void -meta_onscreen_native_set_crtc_modes (CoglOnscreen *onscreen) -{ - CoglOnscreenEGL *onscreen_egl = onscreen->winsys; - MetaOnscreenNative *onscreen_native = onscreen_egl->platform; - MetaRendererNative *renderer_native = onscreen_native->renderer_native; - MetaGpuKms *render_gpu = onscreen_native->render_gpu; MetaRendererNativeGpuData *renderer_gpu_data; - MetaRendererView *view = onscreen_native->view; - uint32_t fb_id = 0; - MetaLogicalMonitor *logical_monitor; + MetaOnscreenNative *onscreen_native; + MetaKmsUpdate *kms_update; +} SetCrtcModeData; - renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native, - render_gpu); - switch (renderer_gpu_data->mode) +static void +set_crtc_mode (MetaLogicalMonitor *logical_monitor, + MetaOutput *output, + MetaCrtc *crtc, + gpointer user_data) +{ + SetCrtcModeData *data = user_data; + + switch (data->renderer_gpu_data->mode) { case META_RENDERER_NATIVE_MODE_GBM: - fb_id = meta_drm_buffer_get_fb_id (onscreen_native->gbm.next_fb); break; #ifdef HAVE_EGL_DEVICE case META_RENDERER_NATIVE_MODE_EGL_DEVICE: - fb_id = onscreen_native->egl.dumb_fb.fb_id; - break; + { + uint32_t fb_id; + + fb_id = data->onscreen_native->egl.dumb_fb.fb_id; + meta_crtc_kms_assign_primary_plane (crtc, fb_id, data->kms_update); + break; + } #endif } - g_assert (fb_id != 0); - - logical_monitor = meta_renderer_view_get_logical_monitor (view); - if (logical_monitor) - { - SetCrtcFbData data = { - .onscreen = onscreen, - .fb_id = fb_id - }; - - meta_logical_monitor_foreach_crtc (logical_monitor, - set_crtc_fb_cb, - &data); - } - else - { - GList *l; - - for (l = meta_gpu_get_crtcs (META_GPU (render_gpu)); l; l = l->next) - { - MetaCrtc *crtc = l->data; - - meta_gpu_kms_apply_crtc_mode (render_gpu, - crtc, - crtc->rect.x, crtc->rect.y, - fb_id); - } - } + meta_crtc_kms_set_mode (crtc, data->kms_update); + meta_output_kms_set_underscan (output, data->kms_update); } -static gboolean -crtc_mode_set_fallback (CoglOnscreen *onscreen, - MetaLogicalMonitor *logical_monitor, - MetaCrtc *crtc) +static void +meta_onscreen_native_set_crtc_modes (CoglOnscreen *onscreen, + MetaRendererNativeGpuData *renderer_gpu_data, + MetaKmsUpdate *kms_update) { CoglOnscreenEGL *onscreen_egl = onscreen->winsys; MetaOnscreenNative *onscreen_native = onscreen_egl->platform; - MetaGpuKms *render_gpu = onscreen_native->render_gpu; - MetaRendererNative *renderer_native; - MetaRendererNativeGpuData *renderer_gpu_data; - uint32_t fb_id; - static gboolean warned_once = FALSE; + MetaRendererView *view = onscreen_native->view; + MetaLogicalMonitor *logical_monitor; + SetCrtcModeData data; - if (!warned_once) - { - g_warning ("Page flipping not supported by driver, " - "relying on the clock from now on"); - warned_once = TRUE; - } - - renderer_native = meta_renderer_native_from_gpu (render_gpu); - renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native, - render_gpu); - if (renderer_gpu_data->mode != META_RENDERER_NATIVE_MODE_GBM) - { - g_warning ("Mode set fallback not handled for EGLStreams"); - return FALSE; - } - - fb_id = meta_drm_buffer_get_fb_id (onscreen_native->gbm.next_fb); - set_crtc_fb (onscreen, logical_monitor, crtc, fb_id); - return TRUE; + logical_monitor = meta_renderer_view_get_logical_monitor (view); + data = (SetCrtcModeData) { + .renderer_gpu_data = renderer_gpu_data, + .onscreen_native = onscreen_native, + .kms_update = kms_update, + }; + meta_logical_monitor_foreach_crtc (logical_monitor, set_crtc_mode, &data); } typedef struct _FlipCrtcData { + MetaRendererView *view; CoglOnscreen *onscreen; - GClosure *flip_closure; - - gboolean did_flip; - gboolean did_mode_set; + MetaKmsUpdate *kms_update; } FlipCrtcData; static void @@ -1944,32 +1669,16 @@ flip_crtc (MetaLogicalMonitor *logical_monitor, gpointer user_data) { FlipCrtcData *data = user_data; - GError *error = NULL; - if (!meta_onscreen_native_flip_crtc (data->onscreen, - data->flip_closure, - crtc, - &error)) - { - if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT)) - { - if (crtc_mode_set_fallback (data->onscreen, logical_monitor, crtc)) - data->did_mode_set = TRUE; - } - else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED)) - { - g_warning ("Failed to flip onscreen: %s", error->message); - } - g_error_free (error); - } - else - { - data->did_flip = TRUE; - } + meta_onscreen_native_flip_crtc (data->onscreen, + data->view, + crtc, + data->kms_update); } static void -meta_onscreen_native_flip_crtcs (CoglOnscreen *onscreen) +meta_onscreen_native_flip_crtcs (CoglOnscreen *onscreen, + MetaKmsUpdate *kms_update) { CoglOnscreenEGL *onscreen_egl = onscreen->winsys; MetaOnscreenNative *onscreen_native = onscreen_egl->platform; @@ -1977,51 +1686,24 @@ meta_onscreen_native_flip_crtcs (CoglOnscreen *onscreen) MetaRendererNative *renderer_native = onscreen_native->renderer_native; MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (renderer_native->backend); - GClosure *flip_closure; MetaPowerSave power_save_mode; MetaLogicalMonitor *logical_monitor; - /* - * Create a closure that either will be invoked or destructed. - * Invoking the closure represents a completed flip. If the closure - * is destructed before being invoked, the framebuffer references will be - * cleaned up accordingly. - * - * Each successful flip will each own one reference to the closure, thus keep - * it alive until either invoked or destructed. If flipping failed, the - * closure will be destructed before this function goes out of scope. - */ - flip_closure = g_cclosure_new (G_CALLBACK (on_crtc_flipped), - g_object_ref (view), - (GClosureNotify) flip_closure_destroyed); - g_closure_set_marshal (flip_closure, meta_marshal_VOID__OBJECT_OBJECT_INT64); - power_save_mode = meta_monitor_manager_get_power_save_mode (monitor_manager); if (power_save_mode == META_POWER_SAVE_ON) { FlipCrtcData data = { .onscreen = onscreen, - .flip_closure = flip_closure, + .view = view, + .kms_update = kms_update, }; logical_monitor = meta_renderer_view_get_logical_monitor (view); meta_logical_monitor_foreach_crtc (logical_monitor, flip_crtc, &data); - - /* - * If we didn't queue a page flip, but instead directly changed the mode - * due to the driver not supporting mode setting, we must swap the - * buffers directly as we won't get a page flip callback. - */ - if (!data.did_flip && data.did_mode_set) - meta_onscreen_native_swap_drm_fb (onscreen); } else { - queue_dummy_power_save_page_flip (renderer_native, flip_closure); + queue_dummy_power_save_page_flip (onscreen); } - - onscreen_native->pending_queue_swap_notify = TRUE; - - g_closure_unref (flip_closure); } static void @@ -2031,6 +1713,7 @@ wait_for_pending_flips (CoglOnscreen *onscreen) MetaOnscreenNative *onscreen_native = onscreen_egl->platform; MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state; GHashTableIter iter; + GError *error = NULL; g_hash_table_iter_init (&iter, onscreen_native->secondary_gpu_states); while (g_hash_table_iter_next (&iter, @@ -2038,11 +1721,26 @@ wait_for_pending_flips (CoglOnscreen *onscreen) (gpointer *) &secondary_gpu_state)) { while (secondary_gpu_state->pending_flips) - meta_gpu_kms_wait_for_flip (secondary_gpu_state->gpu_kms, NULL); + { + if (!meta_gpu_kms_wait_for_flip (secondary_gpu_state->gpu_kms, &error)) + { + g_warning ("Failed to wait for flip on secondary GPU: %s", + error->message); + g_clear_error (&error); + break; + } + } } while (onscreen_native->total_pending_flips) - meta_gpu_kms_wait_for_flip (onscreen_native->render_gpu, NULL); + { + if (!meta_gpu_kms_wait_for_flip (onscreen_native->render_gpu, &error)) + { + g_warning ("Failed to wait for flip: %s", error->message); + g_clear_error (&error); + break; + } + } } static void @@ -2305,17 +2003,23 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys; MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform; MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native; + MetaBackend *backend = renderer_native->backend; MetaMonitorManager *monitor_manager = - meta_backend_get_monitor_manager (renderer_native->backend); + meta_backend_get_monitor_manager (backend); + MetaBackendNative *backend_native = META_BACKEND_NATIVE (backend); + MetaKms *kms = meta_backend_native_get_kms (backend_native); CoglOnscreenEGL *onscreen_egl = onscreen->winsys; MetaOnscreenNative *onscreen_native = onscreen_egl->platform; MetaGpuKms *render_gpu = onscreen_native->render_gpu; CoglFrameInfo *frame_info; gboolean egl_context_changed = FALSE; + MetaKmsUpdate *kms_update; MetaPowerSave power_save_mode; g_autoptr (GError) error = NULL; MetaDrmBufferGbm *buffer_gbm; + kms_update = meta_kms_ensure_pending_update (kms); + /* * Wait for the flip callback before continuing, as we might have started the * animation earlier due to the animation being driven by some other monitor. @@ -2367,12 +2071,14 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, if (onscreen_native->pending_set_crtc && power_save_mode == META_POWER_SAVE_ON) { - meta_onscreen_native_set_crtc_modes (onscreen); + meta_onscreen_native_set_crtc_modes (onscreen, + renderer_gpu_data, + kms_update); onscreen_native->pending_set_crtc = FALSE; } onscreen_native->pending_queue_swap_notify_frame_count = renderer_native->frame_counter; - meta_onscreen_native_flip_crtcs (onscreen); + meta_onscreen_native_flip_crtcs (onscreen, kms_update); /* * If we changed EGL context, cogl will have the wrong idea about what is @@ -2382,6 +2088,12 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, */ if (egl_context_changed) _cogl_winsys_egl_ensure_current (cogl_display); + + if (!meta_kms_post_pending_update_sync (kms, &error)) + { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED)) + g_warning ("Failed to post KMS update: %s", error->message); + } } static gboolean @@ -2946,15 +2658,6 @@ meta_renderer_native_release_onscreen (CoglOnscreen *onscreen) g_warning ("Failed to clear current context"); } - g_list_free_full (onscreen_native->pending_page_flip_retries, - (GDestroyNotify) retry_page_flip_data_free); - if (onscreen_native->retry_page_flips_source) - { - meta_backend_thaw_updates (renderer_native->backend); - g_clear_pointer (&onscreen_native->retry_page_flips_source, - g_source_destroy); - } - renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native, onscreen_native->render_gpu); @@ -3410,6 +3113,12 @@ meta_renderer_native_create_view (MetaRenderer *renderer, void meta_renderer_native_finish_frame (MetaRendererNative *renderer_native) { + MetaBackend *backend = renderer_native->backend; + MetaBackendNative *backend_native = META_BACKEND_NATIVE (backend); + MetaKms *kms = meta_backend_native_get_kms (backend_native); + MetaKmsUpdate *kms_update = NULL; + GError *error = NULL; + renderer_native->frame_counter++; if (renderer_native->pending_unset_disabled_crtcs) @@ -3419,7 +3128,6 @@ meta_renderer_native_finish_frame (MetaRendererNative *renderer_native) for (l = meta_backend_get_gpus (renderer_native->backend); l; l = l->next) { MetaGpu *gpu = l->data; - MetaGpuKms *gpu_kms = META_GPU_KMS (gpu); GList *k; for (k = meta_gpu_get_crtcs (gpu); k; k = k->next) @@ -3429,12 +3137,23 @@ meta_renderer_native_finish_frame (MetaRendererNative *renderer_native) if (crtc->current_mode) continue; - meta_gpu_kms_apply_crtc_mode (gpu_kms, crtc, 0, 0, 0); + kms_update = meta_kms_ensure_pending_update (kms); + meta_crtc_kms_set_mode (crtc, kms_update); } } renderer_native->pending_unset_disabled_crtcs = FALSE; } + + if (kms_update) + { + if (!meta_kms_post_pending_update_sync (kms, &error)) + { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED)) + g_warning ("Failed to post KMS update: %s", error->message); + g_error_free (error); + } + } } int64_t @@ -4031,6 +3750,22 @@ on_gpu_added (MetaBackendNative *backend_native, _cogl_winsys_egl_ensure_current (cogl_display); } +static void +on_power_save_mode_changed (MetaMonitorManager *monitor_manager, + MetaRendererNative *renderer_native) +{ + MetaBackendNative *backend_native = + META_BACKEND_NATIVE (renderer_native->backend); + MetaKms *kms = meta_backend_native_get_kms (backend_native); + MetaPowerSave power_save_mode; + + power_save_mode = meta_monitor_manager_get_power_save_mode (monitor_manager); + if (power_save_mode == META_POWER_SAVE_ON) + meta_renderer_native_queue_modes_reset (renderer_native); + else + meta_kms_discard_pending_page_flips (kms); +} + static MetaGpuKms * choose_primary_gpu_unchecked (MetaBackend *backend, MetaRendererNative *renderer_native) @@ -4143,10 +3878,10 @@ meta_renderer_native_finalize (GObject *object) { MetaRendererNative *renderer_native = META_RENDERER_NATIVE (object); - if (renderer_native->power_save_page_flip_closures) + if (renderer_native->power_save_page_flip_onscreens) { - g_list_free_full (renderer_native->power_save_page_flip_closures, - (GDestroyNotify) g_closure_unref); + g_list_free_full (renderer_native->power_save_page_flip_onscreens, + (GDestroyNotify) cogl_object_unref); g_source_remove (renderer_native->power_save_page_flip_source_id); } @@ -4162,6 +3897,8 @@ meta_renderer_native_constructed (GObject *object) MetaRendererNative *renderer_native = META_RENDERER_NATIVE (object); MetaBackend *backend = renderer_native->backend; MetaSettings *settings = meta_backend_get_settings (backend); + MetaMonitorManager *monitor_manager = + meta_backend_get_monitor_manager (backend); if (meta_settings_is_experimental_feature_enabled ( settings, META_EXPERIMENTAL_FEATURE_KMS_MODIFIERS)) @@ -4169,6 +3906,8 @@ meta_renderer_native_constructed (GObject *object) g_signal_connect (backend, "gpu-added", G_CALLBACK (on_gpu_added), renderer_native); + g_signal_connect (monitor_manager, "power-save-mode-changed", + G_CALLBACK (on_power_save_mode_changed), renderer_native); G_OBJECT_CLASS (meta_renderer_native_parent_class)->constructed (object); } diff --git a/src/meson.build b/src/meson.build index acfd879ef..515638fe5 100644 --- a/src/meson.build +++ b/src/meson.build @@ -610,10 +610,15 @@ if have_native_backend 'backends/native/meta-kms-impl-simple.h', 'backends/native/meta-kms-impl.c', 'backends/native/meta-kms-impl.h', + 'backends/native/meta-kms-page-flip.c', + 'backends/native/meta-kms-page-flip-private.h', 'backends/native/meta-kms-plane.c', 'backends/native/meta-kms-plane.h', 'backends/native/meta-kms-private.h', 'backends/native/meta-kms-types.h', + 'backends/native/meta-kms-update-private.h', + 'backends/native/meta-kms-update.c', + 'backends/native/meta-kms-update.h', 'backends/native/meta-kms-utils.c', 'backends/native/meta-kms-utils.h', 'backends/native/meta-kms.c', @@ -801,14 +806,6 @@ endif subdir('meta') -mutter_marshal = gnome.genmarshal('meta-marshal', - sources: ['meta-marshal.list'], - prefix: 'meta_marshal', - extra_args: ['--quiet'], - internal: true, -) -mutter_built_sources += mutter_marshal - mutter_built_sources += mutter_enum_types mutter_built_sources += mutter_version diff --git a/src/meta-marshal.list b/src/meta-marshal.list deleted file mode 100644 index c1f4781d2..000000000 --- a/src/meta-marshal.list +++ /dev/null @@ -1 +0,0 @@ -VOID:OBJECT,OBJECT,INT64