Let MetaMonitorManagerKms handle page flips

This commit completes the move of monitor logic to the monitor
mangager. The renderer now only deals with framebuffers, asking the
monitor manager to do the crtc flip tracking.

https://bugzilla.gnome.org/show_bug.cgi?id=768976
This commit is contained in:
Jonas Ådahl 2016-05-12 14:55:06 +08:00
parent 12ef1a5e4b
commit 8bebb8126c
4 changed files with 203 additions and 248 deletions

View File

@ -32,6 +32,7 @@
#include <stdlib.h>
#include <clutter/clutter.h>
#include <drm.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
@ -75,11 +76,28 @@ typedef struct {
uint32_t rotation_map[ALL_TRANSFORMS];
} MetaCRTCKms;
typedef struct
{
int pending;
MetaKmsFlipCallback callback;
void *user_data;
} MetaKmsFlipClosure;
typedef struct
{
GSource source;
gpointer fd_tag;
MetaMonitorManagerKms *manager_kms;
} MetaKmsSource;
struct _MetaMonitorManagerKms
{
MetaMonitorManager parent_instance;
int fd;
MetaKmsSource *source;
drmModeConnector **connectors;
unsigned int n_connectors;
@ -87,6 +105,8 @@ struct _MetaMonitorManagerKms
GUdevClient *udev;
GSettings *desktop_settings;
gboolean page_flips_not_supported;
};
struct _MetaMonitorManagerKmsClass
@ -954,8 +974,6 @@ meta_monitor_manager_kms_set_power_save_mode (MetaMonitorManager *manager,
MetaPowerSave mode)
{
MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (manager);
MetaBackend *backend;
MetaRenderer *renderer;
uint64_t state;
unsigned i;
@ -995,20 +1013,6 @@ meta_monitor_manager_kms_set_power_save_mode (MetaMonitorManager *manager,
meta_output->name, strerror (errno));
}
}
backend = meta_get_backend ();
renderer = meta_backend_get_renderer (backend);
for (i = 0; i < manager->n_crtcs; i++)
meta_renderer_native_set_ignore_crtc (META_RENDERER_NATIVE (renderer),
manager->crtcs[i].crtc_id,
mode != META_POWER_SAVE_ON);
}
static void
crtc_free (CoglKmsCrtc *crtc)
{
g_slice_free (CoglKmsCrtc, crtc);
}
static void
@ -1064,29 +1068,21 @@ meta_monitor_manager_kms_apply_configuration (MetaMonitorManager *manager,
MetaBackend *backend;
MetaRenderer *renderer;
unsigned i;
GPtrArray *cogl_crtcs;
int screen_width, screen_height;
gboolean ok;
GError *error;
cogl_crtcs = g_ptr_array_new_full (manager->n_crtcs, (GDestroyNotify)crtc_free);
screen_width = 0; screen_height = 0;
for (i = 0; i < n_crtcs; i++)
{
MetaCRTCInfo *crtc_info = crtcs[i];
MetaCRTC *crtc = crtc_info->crtc;
MetaCRTCKms *crtc_kms = crtc->driver_private;
CoglKmsCrtc *cogl_crtc;
crtc->is_dirty = TRUE;
cogl_crtc = g_slice_new0 (CoglKmsCrtc);
g_ptr_array_add (cogl_crtcs, cogl_crtc);
if (crtc_info->mode == NULL)
{
cogl_crtc->id = crtc->crtc_id;
crtc->rect.x = 0;
crtc->rect.y = 0;
crtc->rect.width = 0;
@ -1096,24 +1092,15 @@ meta_monitor_manager_kms_apply_configuration (MetaMonitorManager *manager,
else
{
MetaMonitorMode *mode;
uint32_t *connectors;
unsigned int j, n_connectors;
unsigned int j;
int width, height;
mode = crtc_info->mode;
n_connectors = crtc_info->outputs->len;
connectors = g_new (uint32_t, n_connectors);
cogl_crtc->id = crtc->crtc_id;
cogl_crtc->connected = n_connectors > 0;
for (j = 0; j < n_connectors; j++)
for (j = 0; j < crtc_info->outputs->len; j++)
{
MetaOutput *output = g_ptr_array_index (crtc_info->outputs, j);
connectors[j] = output->winsys_id;
output->is_dirty = TRUE;
output->crtc = crtc;
}
@ -1153,7 +1140,6 @@ meta_monitor_manager_kms_apply_configuration (MetaMonitorManager *manager,
for (i = 0; i < manager->n_crtcs; i++)
{
MetaCRTC *crtc = &manager->crtcs[i];
CoglKmsCrtc *cogl_crtc;
crtc->logical_monitor = NULL;
@ -1163,11 +1149,6 @@ meta_monitor_manager_kms_apply_configuration (MetaMonitorManager *manager,
continue;
}
cogl_crtc = g_slice_new0 (CoglKmsCrtc);
g_ptr_array_add (cogl_crtcs, cogl_crtc);
cogl_crtc->id = crtc->crtc_id;
crtc->rect.x = 0;
crtc->rect.y = 0;
crtc->rect.width = 0;
@ -1181,11 +1162,7 @@ meta_monitor_manager_kms_apply_configuration (MetaMonitorManager *manager,
error = NULL;
ok = meta_renderer_native_set_layout (META_RENDERER_NATIVE (renderer),
screen_width, screen_height,
(CoglKmsCrtc**)cogl_crtcs->pdata,
cogl_crtcs->len,
&error);
g_ptr_array_unref (cogl_crtcs);
if (!ok)
{
meta_warning ("Applying display configuration failed: %s\n", error->message);
@ -1279,12 +1256,39 @@ on_uevent (GUdevClient *client,
meta_monitor_manager_on_hotplug (manager);
}
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_monitor_manager_kms_wait_for_flip (kms_source->manager_kms);
return G_SOURCE_CONTINUE;
}
static GSourceFuncs kms_event_funcs = {
NULL,
kms_event_check,
kms_event_dispatch
};
static void
meta_monitor_manager_kms_init (MetaMonitorManagerKms *manager_kms)
{
MetaBackend *backend = meta_get_backend ();
MetaRenderer *renderer = meta_backend_get_renderer (backend);
MetaRendererNative *renderer_native = META_RENDERER_NATIVE (renderer);
GSource *source;
manager_kms->fd = meta_renderer_native_get_kms_fd (renderer_native);
@ -1295,6 +1299,14 @@ meta_monitor_manager_kms_init (MetaMonitorManagerKms *manager_kms)
g_signal_connect (manager_kms->udev, "uevent",
G_CALLBACK (on_uevent), manager_kms);
source = g_source_new (&kms_event_funcs, sizeof (MetaKmsSource));
manager_kms->source = (MetaKmsSource *) source;
manager_kms->source->fd_tag = g_source_add_unix_fd (source,
manager_kms->fd,
G_IO_IN | G_IO_ERR);
manager_kms->source->manager_kms = manager_kms;
g_source_attach (source, NULL);
manager_kms->desktop_settings = g_settings_new ("org.gnome.desktop.interface");
}
@ -1364,6 +1376,104 @@ meta_monitor_manager_kms_apply_crtc_modes (MetaMonitorManagerKms *manager_kms,
}
}
gboolean
meta_monitor_manager_kms_flip_all_crtcs (MetaMonitorManagerKms *manager_kms,
uint32_t fb_id,
MetaKmsFlipCallback flip_callback,
void *user_data)
{
MetaMonitorManager *manager = META_MONITOR_MANAGER (manager_kms);
MetaKmsFlipClosure *flip_closure = NULL;
unsigned int i;
gboolean fb_in_use = FALSE;
if (manager->power_save_mode != META_POWER_SAVE_ON)
return FALSE;
for (i = 0; i < manager->n_crtcs; i++)
{
MetaCRTC *crtc = &manager->crtcs[i];
uint32_t *connectors;
unsigned int n_connectors;
int ret;
get_crtc_connectors (manager, crtc, &connectors, &n_connectors);
if (n_connectors == 0)
continue;
fb_in_use = TRUE;
if (manager_kms->page_flips_not_supported)
continue;
if (!flip_closure)
{
flip_closure = g_new0 (MetaKmsFlipClosure, 1);
*flip_closure = (MetaKmsFlipClosure) {
.pending = 0,
.callback = flip_callback,
.user_data = user_data
};
}
ret = drmModePageFlip (manager_kms->fd,
crtc->crtc_id,
fb_id,
DRM_MODE_PAGE_FLIP_EVENT,
flip_closure);
if (ret != 0 && ret != -EACCES)
{
g_warning ("Failed to flip: %s", strerror (-ret));
manager_kms->page_flips_not_supported = TRUE;
break;
}
if (ret == 0)
flip_closure->pending++;
}
if (manager_kms->page_flips_not_supported && fb_in_use)
{
/* If the driver doesn't support page flipping, just set the mode directly
* with the new framebuffer.
*/
meta_monitor_manager_kms_apply_crtc_modes (manager_kms, fb_id);
flip_callback (user_data);
g_free (flip_closure);
}
return fb_in_use;
}
static void
page_flip_handler (int fd,
unsigned int frame,
unsigned int sec,
unsigned int usec,
void *data)
{
MetaKmsFlipClosure *flip_closure = data;
flip_closure->pending--;
if (flip_closure->pending == 0)
flip_closure->callback (flip_closure->user_data);
}
void
meta_monitor_manager_kms_wait_for_flip (MetaMonitorManagerKms *manager_kms)
{
drmEventContext evctx;
if (manager_kms->page_flips_not_supported)
return;
memset (&evctx, 0, sizeof evctx);
evctx.version = DRM_EVENT_CONTEXT_VERSION;
evctx.page_flip_handler = page_flip_handler;
drmHandleEvent (manager_kms->fd, &evctx);
}
static void
meta_monitor_manager_kms_dispose (GObject *object)
{
@ -1381,6 +1491,7 @@ meta_monitor_manager_kms_finalize (GObject *object)
MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (object);
free_resources (manager_kms);
g_source_destroy ((GSource *) manager_kms->source);
G_OBJECT_CLASS (meta_monitor_manager_kms_parent_class)->finalize (object);
}

View File

@ -37,7 +37,16 @@ typedef struct _MetaMonitorManagerKms MetaMonitorManagerKms;
GType meta_monitor_manager_kms_get_type (void);
typedef void (*MetaKmsFlipCallback) (void *user_data);
void meta_monitor_manager_kms_apply_crtc_modes (MetaMonitorManagerKms *manager_kms,
uint32_t fb_id);
gboolean meta_monitor_manager_kms_flip_all_crtcs (MetaMonitorManagerKms *manager_kms,
uint32_t fb_id,
MetaKmsFlipCallback flip_callback,
void *user_data);
void meta_monitor_manager_kms_wait_for_flip (MetaMonitorManagerKms *manager_kms);
#endif /* META_MONITOR_MANAGER_KMS_H */

View File

@ -67,9 +67,6 @@ struct _MetaRendererNative
int kms_fd;
struct gbm_device *gbm;
CoglClosure *swap_notify_idle;
gboolean page_flips_not_supported;
GList *crtcs;
int width, height;
gboolean pending_set_crtc;
@ -90,12 +87,6 @@ G_DEFINE_TYPE_WITH_CODE (MetaRendererNative,
static const CoglWinsysEGLVtable _cogl_winsys_egl_vtable;
static const CoglWinsysVtable *parent_vtable;
typedef struct _CoglFlipKMS
{
CoglOnscreen *onscreen;
int pending;
} CoglFlipKMS;
typedef struct _CoglOnscreenKMS
{
struct gbm_surface *surface;
@ -211,73 +202,6 @@ queue_swap_notify_for_onscreen (CoglOnscreen *onscreen)
kms_onscreen->pending_swap_notify = TRUE;
}
static void
process_flip (CoglFlipKMS *flip)
{
/* We're only ready to dispatch a swap notification once all outputs
* have flipped... */
flip->pending--;
if (flip->pending == 0)
{
CoglOnscreen *onscreen = flip->onscreen;
CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
CoglOnscreenKMS *kms_onscreen = egl_onscreen->platform;
queue_swap_notify_for_onscreen (onscreen);
free_current_bo (onscreen);
kms_onscreen->current_fb_id = kms_onscreen->next_fb_id;
kms_onscreen->next_fb_id = 0;
kms_onscreen->current_bo = kms_onscreen->next_bo;
kms_onscreen->next_bo = NULL;
cogl_object_unref (flip->onscreen);
g_slice_free (CoglFlipKMS, flip);
}
}
static void
page_flip_handler (int fd,
unsigned int frame,
unsigned int sec,
unsigned int usec,
void *data)
{
CoglFlipKMS *flip = data;
process_flip (flip);
}
static void
handle_drm_event (MetaRendererNative *renderer_native)
{
drmEventContext evctx;
if (renderer_native->page_flips_not_supported)
return;
memset (&evctx, 0, sizeof evctx);
evctx.version = DRM_EVENT_CONTEXT_VERSION;
evctx.page_flip_handler = page_flip_handler;
drmHandleEvent (renderer_native->kms_fd, &evctx);
}
static void
dispatch_kms_events (void *user_data, int revents)
{
CoglRenderer *renderer = user_data;
CoglRendererEGL *egl_renderer = renderer->winsys;
MetaRendererNative *renderer_native = egl_renderer->platform;
if (!revents)
return;
handle_drm_event (renderer_native);
}
static CoglBool
_cogl_winsys_renderer_connect (CoglRenderer *cogl_renderer,
CoglError **error)
@ -315,13 +239,6 @@ _cogl_winsys_renderer_connect (CoglRenderer *cogl_renderer,
if (!_cogl_winsys_egl_renderer_connect_common (cogl_renderer, error))
goto fail;
_cogl_poll_renderer_add_fd (cogl_renderer,
renderer_native->kms_fd,
COGL_POLL_FD_EVENT_IN,
NULL, /* no prepare callback */
dispatch_kms_events,
cogl_renderer);
return TRUE;
fail:
@ -342,63 +259,6 @@ setup_crtc_modes (CoglDisplay *display, int fb_id)
meta_monitor_manager_kms_apply_crtc_modes (monitor_manager_kms, fb_id);
}
static void
flip_all_crtcs (CoglDisplay *display, CoglFlipKMS *flip, int fb_id)
{
CoglDisplayEGL *egl_display = display->winsys;
MetaRendererNative *renderer_native = egl_display->platform;
GList *l;
gboolean needs_flip = FALSE;
for (l = renderer_native->crtcs; l; l = l->next)
{
CoglKmsCrtc *crtc = l->data;
int ret = 0;
if (!crtc->connected || crtc->ignore)
continue;
needs_flip = TRUE;
if (!renderer_native->page_flips_not_supported)
{
ret = drmModePageFlip (renderer_native->kms_fd,
crtc->id, fb_id,
DRM_MODE_PAGE_FLIP_EVENT, flip);
if (ret != 0 && ret != -EACCES)
{
g_warning ("Failed to flip: %m");
renderer_native->page_flips_not_supported = TRUE;
break;
}
}
if (ret == 0)
flip->pending++;
}
if (renderer_native->page_flips_not_supported && needs_flip)
flip->pending = 1;
}
static void
crtc_free (CoglKmsCrtc *crtc)
{
g_slice_free (CoglKmsCrtc, crtc);
}
static CoglKmsCrtc *
crtc_copy (CoglKmsCrtc *from)
{
CoglKmsCrtc *new;
new = g_slice_new (CoglKmsCrtc);
*new = *from;
return new;
}
static CoglBool
_cogl_winsys_egl_display_setup (CoglDisplay *display,
CoglError **error)
@ -490,11 +350,36 @@ _cogl_winsys_egl_cleanup_context (CoglDisplay *display)
}
}
static void
flip_callback (void *user_data)
{
CoglOnscreen *onscreen = user_data;
CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
CoglOnscreenKMS *kms_onscreen = egl_onscreen->platform;
queue_swap_notify_for_onscreen (onscreen);
free_current_bo (onscreen);
kms_onscreen->current_fb_id = kms_onscreen->next_fb_id;
kms_onscreen->next_fb_id = 0;
kms_onscreen->current_bo = kms_onscreen->next_bo;
kms_onscreen->next_bo = NULL;
cogl_object_unref (onscreen);
}
static void
_cogl_winsys_onscreen_swap_buffers_with_damage (CoglOnscreen *onscreen,
const int *rectangles,
int n_rectangles)
{
MetaBackend *backend = meta_get_backend ();
MetaMonitorManager *monitor_manager =
meta_backend_get_monitor_manager (backend);
MetaMonitorManagerKms *monitor_manager_kms =
META_MONITOR_MANAGER_KMS (monitor_manager);
CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context;
CoglRenderer *renderer = context->display->renderer;
CoglRendererEGL *egl_renderer = renderer->winsys;
@ -502,11 +387,12 @@ _cogl_winsys_onscreen_swap_buffers_with_damage (CoglOnscreen *onscreen,
CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
CoglOnscreenKMS *kms_onscreen = egl_onscreen->platform;
uint32_t handle, stride;
CoglFlipKMS *flip;
gboolean fb_in_use;
uint32_t next_fb_id;
/* If we already have a pending swap then block until it completes */
while (kms_onscreen->next_fb_id != 0)
handle_drm_event (renderer_native);
meta_monitor_manager_kms_wait_for_flip (monitor_manager_kms);
if (kms_onscreen->pending_egl_surface)
{
@ -564,34 +450,27 @@ _cogl_winsys_onscreen_swap_buffers_with_damage (CoglOnscreen *onscreen,
renderer_native->pending_set_crtc = FALSE;
}
flip = g_slice_new0 (CoglFlipKMS);
flip->onscreen = onscreen;
next_fb_id = kms_onscreen->next_fb_id;
flip_all_crtcs (context->display, flip, kms_onscreen->next_fb_id);
/* Reference will either be released in flip_callback, or if the fb
* wasn't used, indicated by the return value below.
*/
cogl_object_ref (onscreen);
fb_in_use = meta_monitor_manager_kms_flip_all_crtcs (monitor_manager_kms,
next_fb_id,
flip_callback, onscreen);
if (flip->pending == 0)
if (!fb_in_use)
{
drmModeRmFB (renderer_native->kms_fd, kms_onscreen->next_fb_id);
gbm_surface_release_buffer (kms_onscreen->surface,
kms_onscreen->next_bo);
kms_onscreen->next_bo = NULL;
kms_onscreen->next_fb_id = 0;
g_slice_free (CoglFlipKMS, flip);
flip = NULL;
queue_swap_notify_for_onscreen (onscreen);
}
else
{
/* Ensure the onscreen remains valid while it has any pending flips... */
cogl_object_ref (flip->onscreen);
/* Process flip right away if we can't wait for vblank */
if (renderer_native->page_flips_not_supported)
{
setup_crtc_modes (context->display, kms_onscreen->next_fb_id);
process_flip (flip);
}
g_object_unref (onscreen);
}
}
@ -764,8 +643,6 @@ gboolean
meta_renderer_native_set_layout (MetaRendererNative *renderer_native,
int width,
int height,
CoglKmsCrtc **crtcs,
int n_crtcs,
GError **error)
{
ClutterBackend *clutter_backend = clutter_get_default_backend ();
@ -774,8 +651,6 @@ meta_renderer_native_set_layout (MetaRendererNative *renderer_native,
CoglDisplayEGL *egl_display = cogl_display->winsys;
CoglRenderer *renderer = cogl_display->renderer;
CoglRendererEGL *egl_renderer = renderer->winsys;
GList *crtc_list;
int i;
if ((width != renderer_native->width ||
height != renderer_native->height) &&
@ -844,39 +719,11 @@ meta_renderer_native_set_layout (MetaRendererNative *renderer_native,
renderer_native->width = width;
renderer_native->height = height;
g_list_free_full (renderer_native->crtcs, (GDestroyNotify) crtc_free);
crtc_list = NULL;
for (i = 0; i < n_crtcs; i++)
{
crtc_list = g_list_prepend (crtc_list, crtc_copy (crtcs[i]));
}
crtc_list = g_list_reverse (crtc_list);
renderer_native->crtcs = crtc_list;
renderer_native->pending_set_crtc = TRUE;
return TRUE;
}
void
meta_renderer_native_set_ignore_crtc (MetaRendererNative *renderer_native,
uint32_t id,
gboolean ignore)
{
GList *l;
for (l = renderer_native->crtcs; l; l = l->next)
{
CoglKmsCrtc *crtc = l->data;
if (crtc->id == id)
{
crtc->ignore = ignore;
break;
}
}
}
static const CoglWinsysVtable *
get_native_cogl_winsys_vtable (void)
{
@ -966,8 +813,6 @@ meta_renderer_native_finalize (GObject *object)
{
MetaRendererNative *renderer_native = META_RENDERER_NATIVE (object);
g_list_free_full (renderer_native->crtcs, (GDestroyNotify) crtc_free);
g_clear_pointer (&renderer_native->dummy_gbm_surface, gbm_surface_destroy);
g_clear_pointer (&renderer_native->gbm, gbm_device_destroy);

View File

@ -30,14 +30,6 @@
#include "backends/meta-renderer.h"
typedef struct {
uint32_t id;
gboolean connected;
CoglBool ignore;
} CoglKmsCrtc;
#define META_TYPE_RENDERER_NATIVE (meta_renderer_native_get_type ())
G_DECLARE_FINAL_TYPE (MetaRendererNative, meta_renderer_native,
META, RENDERER_NATIVE,
@ -55,8 +47,6 @@ void meta_renderer_native_queue_modes_reset (MetaRendererNative *renderer_native
gboolean meta_renderer_native_set_layout (MetaRendererNative *renderer_native,
int width,
int height,
CoglKmsCrtc **crtcs,
int n_crtcs,
GError **error);
void meta_renderer_native_set_ignore_crtc (MetaRendererNative *renderer_native,