Commit 75dff3e7 authored by Jonas Ådahl's avatar Jonas Ådahl Committed by Georges Basile Stavracas Neto

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.

#548
!525
parent bea76004
......@@ -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;
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),
};
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);
}
kms_fd = meta_gpu_kms_get_fd (gpu_kms);
static GList *
generate_crtc_connector_list (MetaGpu *gpu,
MetaCrtc *crtc)
{
GList *connectors = NULL;
GList *l;
for (i = 0; i < props->count_props; i++)
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)
{
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)
MetaCrtc *
meta_crtc_kms_from_kms_crtc (MetaKmsCrtc *kms_crtc)
{
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;
}
......@@ -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);
......
......@@ -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 (&params[0], G_TYPE_POINTER);
g_value_set_pointer (&params[0], flip_closure);
g_value_init (&params[1], G_TYPE_OBJECT);
g_value_set_object (&params[1], gpu_kms);
g_value_init (&params[2], G_TYPE_OBJECT);
g_value_set_object (&params[2], crtc);
g_value_init (&params[3], G_TYPE_INT64);
g_value_set_int64 (&params[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);