cogl/0001-kms-winsys-try-to-hobble-along-if-driver-doesn-t-sup.patch
2015-03-23 16:35:58 -04:00

367 lines
10 KiB
Diff

From 68d9ba3b653a2fe0f0992ea2b6df753b3bfc9d61 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 forgoes waiting for vblank and flips right away
in those cases. That prevents the hardware from freezing up the screen,
but does mean there will be some visible tearing.
https://bugzilla.gnome.org/show_bug.cgi?id=746042
---
cogl/winsys/cogl-winsys-egl-kms.c | 52 +++++++++++++++++++++++++++++----------
1 file changed, 39 insertions(+), 13 deletions(-)
diff --git a/cogl/winsys/cogl-winsys-egl-kms.c b/cogl/winsys/cogl-winsys-egl-kms.c
index b06c1da..c5d014a 100644
--- a/cogl/winsys/cogl-winsys-egl-kms.c
+++ b/cogl/winsys/cogl-winsys-egl-kms.c
@@ -45,60 +45,61 @@
#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;
} 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;
@@ -197,98 +198,107 @@ 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
handle_drm_event (CoglRendererKMS *kms_renderer)
{
drmEventContext evctx;
+ if (kms_renderer->page_flips_not_supported)
+ return;
+
memset (&evctx, 0, sizeof evctx);
evctx.version = DRM_EVENT_CONTEXT_VERSION;
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;
@@ -548,81 +558,90 @@ static void
setup_crtc_modes (CoglDisplay *display, 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 = 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;
+ gboolean needs_flip = FALSE;
for (l = kms_display->crtcs; l; l = l->next)
{
CoglKmsCrtc *crtc = l->data;
int ret;
if (crtc->count == 0 || crtc->ignore)
continue;
- ret = drmModePageFlip (kms_renderer->fd,
- crtc->id, fb_id,
- DRM_MODE_PAGE_FLIP_EVENT, flip);
+ needs_flip = TRUE;
- if (ret)
+ if (!kms_renderer->page_flips_not_supported)
{
- g_warning ("Failed to flip: %m");
- continue;
+ 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;
+ break;
+ }
}
flip->pending++;
}
+
+ if (kms_renderer->page_flips_not_supported && needs_flip)
+ flip->pending = 1;
}
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;
CoglRendererEGL *egl_renderer = display->renderer->winsys;
CoglRendererKMS *kms_renderer = egl_renderer->platform;
@@ -918,60 +937,67 @@ _cogl_winsys_onscreen_swap_buffers_with_damage (CoglOnscreen *onscreen,
/* If this is the first framebuffer to be presented then we now setup the
* crtc modes, else we flip from the previous buffer */
if (kms_display->pending_set_crtc)
{
setup_crtc_modes (context->display, kms_onscreen->next_fb_id);
kms_display->pending_set_crtc = FALSE;
}
flip = g_slice_new0 (CoglFlipKMS);
flip->onscreen = onscreen;
flip_all_crtcs (context->display, flip, kms_onscreen->next_fb_id);
if (flip->pending == 0)
{
drmModeRmFB (kms_renderer->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 (kms_renderer->page_flips_not_supported)
+ {
+ setup_crtc_modes (context->display, kms_onscreen->next_fb_id);
+ process_flip (flip);
+ }
}
}
static CoglBool
_cogl_winsys_egl_context_init (CoglContext *context,
CoglError **error)
{
COGL_FLAGS_SET (context->features,
COGL_FEATURE_ID_SWAP_BUFFERS_EVENT, TRUE);
/* TODO: remove this deprecated feature */
COGL_FLAGS_SET (context->winsys_features,
COGL_WINSYS_FEATURE_SWAP_BUFFERS_EVENT,
TRUE);
COGL_FLAGS_SET (context->winsys_features,
COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT,
TRUE);
return TRUE;
}
static CoglBool
_cogl_winsys_onscreen_init (CoglOnscreen *onscreen,
CoglError **error)
{
CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
CoglContext *context = framebuffer->context;
CoglDisplay *display = context->display;
CoglDisplayEGL *egl_display = display->winsys;
CoglDisplayKMS *kms_display = egl_display->platform;
CoglRenderer *renderer = display->renderer;
--
2.3.3