kms-winsys: try to hobble along if driver doesn't support page flips

Some drivers ( like mgag200 ) don't yet support drmModePageFlip.

This commit tries to emulate the functionality using drmWaitVBlank
and drmModeSetCrtc in those cases.

Failing all else, it just falls back to flipping right away.

https://bugzilla.gnome.org/show_bug.cgi?id=746042
This commit is contained in:
Ray Strode 2015-03-11 12:16:19 -04:00
parent e215b9fefd
commit 4ae3216143
2 changed files with 429 additions and 1 deletions

View File

@ -0,0 +1,423 @@
From 5c22141866e5335dd111b64349f6ff2ee484a6b4 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Wed, 11 Mar 2015 12:09:51 -0400
Subject: [PATCH] kms-winsys: try to hobble along if driver doesn't support
page flips
Some drivers ( like mgag200 ) don't yet support drmModePageFlip.
This commit tries to emulate the functionality using drmWaitVBlank
and drmModeSetCrtc in those cases.
Failing all else, it just falls back to flipping right away.
https://bugzilla.gnome.org/show_bug.cgi?id=746042
---
cogl/winsys/cogl-winsys-egl-kms.c | 130 +++++++++++++++++++++++++++++++++-----
1 file changed, 115 insertions(+), 15 deletions(-)
diff --git a/cogl/winsys/cogl-winsys-egl-kms.c b/cogl/winsys/cogl-winsys-egl-kms.c
index b06c1da..c66eb45 100644
--- a/cogl/winsys/cogl-winsys-egl-kms.c
+++ b/cogl/winsys/cogl-winsys-egl-kms.c
@@ -45,104 +45,107 @@
#include <xf86drm.h>
#include <xf86drmMode.h>
#include <gbm.h>
#include <glib.h>
#include <sys/fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include "cogl-winsys-egl-kms-private.h"
#include "cogl-winsys-egl-private.h"
#include "cogl-renderer-private.h"
#include "cogl-framebuffer-private.h"
#include "cogl-onscreen-private.h"
#include "cogl-kms-renderer.h"
#include "cogl-kms-display.h"
#include "cogl-version.h"
#include "cogl-error-private.h"
#include "cogl-poll-private.h"
static const CoglWinsysEGLVtable _cogl_winsys_egl_vtable;
static const CoglWinsysVtable *parent_vtable;
typedef struct _CoglRendererKMS
{
int fd;
int opened_fd;
struct gbm_device *gbm;
CoglClosure *swap_notify_idle;
+ CoglBool page_flips_not_supported;
+ CoglBool vblank_not_supported;
} CoglRendererKMS;
typedef struct _CoglOutputKMS
{
drmModeConnector *connector;
drmModeEncoder *encoder;
drmModeCrtc *saved_crtc;
drmModeModeInfo *modes;
int n_modes;
drmModeModeInfo mode;
} CoglOutputKMS;
typedef struct _CoglDisplayKMS
{
GList *outputs;
GList *crtcs;
int width, height;
CoglBool pending_set_crtc;
struct gbm_surface *dummy_gbm_surface;
CoglOnscreen *onscreen;
} CoglDisplayKMS;
typedef struct _CoglFlipKMS
{
CoglOnscreen *onscreen;
int pending;
} CoglFlipKMS;
typedef struct _CoglOnscreenKMS
{
struct gbm_surface *surface;
uint32_t current_fb_id;
uint32_t next_fb_id;
struct gbm_bo *current_bo;
struct gbm_bo *next_bo;
CoglBool pending_swap_notify;
EGLSurface *pending_egl_surface;
struct gbm_surface *pending_surface;
} CoglOnscreenKMS;
static const char device_name[] = "/dev/dri/card0";
+static void setup_crtc_modes (CoglDisplay *display, int fb_id);
static void
_cogl_winsys_renderer_disconnect (CoglRenderer *renderer)
{
CoglRendererEGL *egl_renderer = renderer->winsys;
CoglRendererKMS *kms_renderer = egl_renderer->platform;
eglTerminate (egl_renderer->edpy);
if (kms_renderer->opened_fd >= 0)
close (kms_renderer->opened_fd);
g_slice_free (CoglRendererKMS, kms_renderer);
g_slice_free (CoglRendererEGL, egl_renderer);
}
static void
flush_pending_swap_notify_cb (void *data,
void *user_data)
{
CoglFramebuffer *framebuffer = data;
if (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN)
{
CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer);
CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
CoglOnscreenKMS *kms_onscreen = egl_onscreen->platform;
if (kms_onscreen->pending_swap_notify)
{
@@ -197,101 +200,168 @@ free_current_bo (CoglOnscreen *onscreen)
kms_onscreen->current_bo = NULL;
}
}
static void
queue_swap_notify_for_onscreen (CoglOnscreen *onscreen)
{
CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
CoglOnscreenKMS *kms_onscreen = egl_onscreen->platform;
CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context;
CoglRenderer *renderer = context->display->renderer;
CoglRendererEGL *egl_renderer = renderer->winsys;
CoglRendererKMS *kms_renderer = egl_renderer->platform;
/* We only want to notify that the swap is complete when the
* application calls cogl_context_dispatch so instead of
* immediately notifying we queue an idle callback */
if (!kms_renderer->swap_notify_idle)
{
kms_renderer->swap_notify_idle =
_cogl_poll_renderer_add_idle (renderer,
flush_pending_swap_notify_idle,
context,
NULL);
}
kms_onscreen->pending_swap_notify = TRUE;
}
static void
-page_flip_handler (int fd,
- unsigned int frame,
- unsigned int sec,
- unsigned int usec,
- void *data)
+process_flip (CoglFlipKMS *flip)
{
- CoglFlipKMS *flip = data;
-
/* 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
+process_vblank (CoglFlipKMS *flip)
+{
+ /* Normally the driver would set the next fb up for
+ * scan out after vblank for us and then call the
+ * page flip handler to clean things up.
+ *
+ * Not all drivers support the newer page flipping interface
+ * that provides this functionality, though, so try to emulate
+ * it
+ */
+ if (flip->pending == 1)
+ {
+ CoglOnscreen *onscreen = flip->onscreen;
+ CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
+ CoglOnscreenKMS *kms_onscreen = egl_onscreen->platform;
+ CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
+ CoglContext *context = framebuffer->context;
+ CoglDisplay *display = context->display;
+
+ setup_crtc_modes (display, kms_onscreen->next_fb_id);
+
+ process_flip (flip);
+ }
+}
+
+static void
+vblank_handler (int fd,
+ unsigned int frame,
+ unsigned int sec,
+ unsigned int usec,
+ void *data)
+{
+ CoglFlipKMS *flip = data;
+
+ process_vblank (flip);
+}
+
+static gboolean
+fake_vblank_timeout_handler (gpointer data)
+{
+ CoglFlipKMS *flip = data;
+
+ /* Normally the next fb would get setup for
+ * scan out after a vblank interval.
+ *
+ * Not all drivers support vblank notification, though,
+ * so this handler just processes things right away.
+ */
+ process_vblank (flip);
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
handle_drm_event (CoglRendererKMS *kms_renderer)
{
drmEventContext evctx;
+ if (kms_renderer->page_flips_not_supported &&
+ kms_renderer->vblank_not_supported)
+ return;
+
memset (&evctx, 0, sizeof evctx);
evctx.version = DRM_EVENT_CONTEXT_VERSION;
- evctx.page_flip_handler = page_flip_handler;
+ if (kms_renderer->page_flips_not_supported)
+ evctx.vblank_handler = vblank_handler;
+ else
+ evctx.page_flip_handler = page_flip_handler;
drmHandleEvent (kms_renderer->fd, &evctx);
}
static void
dispatch_kms_events (void *user_data, int revents)
{
CoglRenderer *renderer = user_data;
CoglRendererEGL *egl_renderer = renderer->winsys;
CoglRendererKMS *kms_renderer = egl_renderer->platform;
if (!revents)
return;
handle_drm_event (kms_renderer);
}
static CoglBool
_cogl_winsys_renderer_connect (CoglRenderer *renderer,
CoglError **error)
{
CoglRendererEGL *egl_renderer;
CoglRendererKMS *kms_renderer;
renderer->winsys = g_slice_new0 (CoglRendererEGL);
egl_renderer = renderer->winsys;
egl_renderer->platform_vtable = &_cogl_winsys_egl_vtable;
egl_renderer->platform = g_slice_new0 (CoglRendererKMS);
kms_renderer = egl_renderer->platform;
@@ -552,75 +622,105 @@ setup_crtc_modes (CoglDisplay *display, int fb_id)
CoglRendererEGL *egl_renderer = display->renderer->winsys;
CoglRendererKMS *kms_renderer = egl_renderer->platform;
GList *l;
for (l = kms_display->crtcs; l; l = l->next)
{
CoglKmsCrtc *crtc = l->data;
int ret = drmModeSetCrtc (kms_renderer->fd,
crtc->id,
fb_id, crtc->x, crtc->y,
crtc->connectors, crtc->count,
crtc->count ? &crtc->mode : NULL);
if (ret)
g_warning ("Failed to set crtc mode %s: %m", crtc->mode.name);
}
}
static void
flip_all_crtcs (CoglDisplay *display, CoglFlipKMS *flip, int fb_id)
{
CoglDisplayEGL *egl_display = display->winsys;
CoglDisplayKMS *kms_display = egl_display->platform;
CoglRendererEGL *egl_renderer = display->renderer->winsys;
CoglRendererKMS *kms_renderer = egl_renderer->platform;
GList *l;
for (l = kms_display->crtcs; l; l = l->next)
{
CoglKmsCrtc *crtc = l->data;
- int ret;
+ int ret = -1;
if (crtc->count == 0 || crtc->ignore)
continue;
- ret = drmModePageFlip (kms_renderer->fd,
- crtc->id, fb_id,
- DRM_MODE_PAGE_FLIP_EVENT, flip);
+ if (!kms_renderer->page_flips_not_supported)
+ {
+ ret = drmModePageFlip (kms_renderer->fd,
+ crtc->id, fb_id,
+ DRM_MODE_PAGE_FLIP_EVENT, flip);
+ if (ret)
+ {
+ g_warning ("Failed to flip: %m");
+ kms_renderer->page_flips_not_supported = TRUE;
+ }
+ }
- if (ret)
+ if (kms_renderer->page_flips_not_supported)
{
- g_warning ("Failed to flip: %m");
- continue;
+ drmVBlank watch_for_next_vblank_operation = { 0 };
+
+ if (!kms_renderer->vblank_not_supported)
+ {
+ /* fall back to older way of waiting for vblanks directly
+ */
+ watch_for_next_vblank_operation.request.type = DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT;
+ watch_for_next_vblank_operation.request.sequence = 1;
+ watch_for_next_vblank_operation.request.signal = (gulong) flip;
+
+ ret = drmWaitVBlank (kms_renderer->fd, &watch_for_next_vblank_operation);
+
+ if (ret)
+ {
+ kms_renderer->vblank_not_supported = TRUE;
+ g_warning ("Failed to fall back to waiting for vblanks directly: %m");
+ }
+ }
}
+ if (kms_renderer->page_flips_not_supported &&
+ kms_renderer->vblank_not_supported)
+ {
+ g_idle_add (fake_vblank_timeout_handler, flip);
+ }
+
flip->pending++;
}
}
static void
crtc_free (CoglKmsCrtc *crtc)
{
g_free (crtc->connectors);
g_slice_free (CoglKmsCrtc, crtc);
}
static CoglKmsCrtc *
crtc_copy (CoglKmsCrtc *from)
{
CoglKmsCrtc *new;
new = g_slice_new (CoglKmsCrtc);
*new = *from;
new->connectors = g_memdup (from->connectors, from->count * sizeof(uint32_t));
return new;
}
static CoglBool
_cogl_winsys_egl_display_setup (CoglDisplay *display,
CoglError **error)
{
CoglDisplayEGL *egl_display = display->winsys;
CoglDisplayKMS *kms_display;
--
2.3.1

View File

@ -6,13 +6,14 @@
Name: cogl
Version: 1.20.0
Release: 1%{?dist}
Release: 2%{?dist}
Summary: A library for using 3D graphics hardware to draw pretty pictures
Group: Development/Libraries
License: LGPLv2+
URL: http://www.clutter-project.org/
Source0: http://download.gnome.org/sources/cogl/1.20/cogl-%{version}.tar.xz
Patch0: 0001-kms-winsys-try-to-hobble-along-if-driver-doesn-t-sup.patch
BuildRequires: cairo-devel
BuildRequires: chrpath
@ -88,6 +89,7 @@ This package contains the installable tests for %{cogl}.
%prep
%setup -q
%patch0 -p1 -b .fall-back-if-no-page-flips
%build
CFLAGS="$RPM_OPT_FLAGS -fPIC"
@ -152,6 +154,9 @@ chrpath --delete $RPM_BUILD_ROOT%{_libdir}/libcogl-pango.so
%endif
%changelog
* Wed Mar 11 2015 Ray Strode <rstrode@redhat.com> 1.20.0-2
- Try to fix wayland on mgag200
* Mon Feb 23 2015 Kalev Lember <kalevlember@gmail.com> - 1.20.0-1
- Update to 1.20.0