From 4ae3216143bc3a7d93c99997a7b2c8bde42422d5 Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Wed, 11 Mar 2015 12:16:19 -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 --- ...o-hobble-along-if-driver-doesn-t-sup.patch | 423 ++++++++++++++++++ cogl.spec | 7 +- 2 files changed, 429 insertions(+), 1 deletion(-) create mode 100644 0001-kms-winsys-try-to-hobble-along-if-driver-doesn-t-sup.patch diff --git a/0001-kms-winsys-try-to-hobble-along-if-driver-doesn-t-sup.patch b/0001-kms-winsys-try-to-hobble-along-if-driver-doesn-t-sup.patch new file mode 100644 index 0000000..7ae580c --- /dev/null +++ b/0001-kms-winsys-try-to-hobble-along-if-driver-doesn-t-sup.patch @@ -0,0 +1,423 @@ +From 5c22141866e5335dd111b64349f6ff2ee484a6b4 Mon Sep 17 00:00:00 2001 +From: Ray Strode +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 + #include + #include + #include + #include + #include + #include + #include + + #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 + diff --git a/cogl.spec b/cogl.spec index 28bc524..ac95ef7 100644 --- a/cogl.spec +++ b/cogl.spec @@ -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 1.20.0-2 +- Try to fix wayland on mgag200 + * Mon Feb 23 2015 Kalev Lember - 1.20.0-1 - Update to 1.20.0