[cogl] kms: add public API to override the default configuration of outputs
- From: Robert Bragg <rbragg src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [cogl] kms: add public API to override the default configuration of outputs
- Date: Fri, 23 Aug 2013 12:49:10 +0000 (UTC)
commit 52fb8e1c33d8c83c731c05cee767928fdd5991d7
Author: Giovanni Campagna <gcampagn redhat com>
Date: Tue Jul 30 17:20:01 2013 +0200
kms: add public API to override the default configuration of outputs
Add API to allow complex applications using the KMS backend
to go almost straight to direct configuration (which is not possible
because Cogl needs to be in charge of buffers and FB objects).
https://bugzilla.gnome.org/show_bug.cgi?id=705837
Reviewed-by: Robert Bragg <robert linux intel com>
cogl/cogl-kms-display.h | 39 ++++++
cogl/winsys/cogl-winsys-egl-kms.c | 232 +++++++++++++++++++++++++++++++-----
2 files changed, 238 insertions(+), 33 deletions(-)
---
diff --git a/cogl/cogl-kms-display.h b/cogl/cogl-kms-display.h
index d8bfd9e..cfa33ea 100644
--- a/cogl/cogl-kms-display.h
+++ b/cogl/cogl-kms-display.h
@@ -32,6 +32,8 @@
#include <cogl/cogl-types.h>
#include <cogl/cogl-display.h>
+#include <xf86drmMode.h>
+
COGL_BEGIN_DECLS
/**
@@ -54,5 +56,42 @@ COGL_BEGIN_DECLS
void
cogl_kms_display_queue_modes_reset (CoglDisplay *display);
+typedef struct {
+ uint32_t id;
+ uint32_t x, y;
+ drmModeModeInfo mode;
+
+ uint32_t *connectors;
+ uint32_t count;
+} CoglKmsCrtc;
+
+/**
+ * cogl_kms_display_set_layout:
+ * @onscreen: a #CoglDisplay
+ * @width: the framebuffer width
+ * @height: the framebuffer height
+ * @crtcs: the array of #CoglKmsCrtc structure with the desired CRTC layout
+ *
+ * Configures @display to use a framebuffer sized @width x @height, covering
+ * the CRTCS in @crtcs.
+ * @width and @height must be within the driver framebuffer limits, and @crtcs
+ * must be valid KMS API IDs.
+ *
+ * Calling this function overrides the automatic mode setting done by Cogl,
+ * and for this reason must be called before the first call to cogl_onscreen_swap_buffers().
+ *
+ * If you want to restore the default behaviour, you can call this function
+ * with @width and @height set to -1.
+ *
+ * Stability: unstable
+ */
+CoglBool
+cogl_kms_display_set_layout (CoglDisplay *display,
+ int width,
+ int height,
+ CoglKmsCrtc **crtcs,
+ int n_crtcs,
+ CoglError **error);
+
COGL_END_DECLS
#endif /* __COGL_KMS_DISPLAY_H__ */
diff --git a/cogl/winsys/cogl-winsys-egl-kms.c b/cogl/winsys/cogl-winsys-egl-kms.c
index 2cc74a5..2305070 100644
--- a/cogl/winsys/cogl-winsys-egl-kms.c
+++ b/cogl/winsys/cogl-winsys-egl-kms.c
@@ -79,9 +79,13 @@ typedef struct _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
@@ -387,13 +391,13 @@ find_mirror_modes (drmModeModeInfo *modes0,
static drmModeModeInfo builtin_1024x768 =
{
- 63500, /* clock */
- 1024, 1072, 1176, 1328, 0,
- 768, 771, 775, 798, 0,
- 59920,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC,
- 0,
- "1024x768"
+ 63500, /* clock */
+ 1024, 1072, 1176, 1328, 0,
+ 768, 771, 775, 798, 0,
+ 59920,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC,
+ 0,
+ "1024x768"
};
static CoglBool
@@ -514,19 +518,71 @@ setup_crtc_modes (CoglDisplay *display, int fb_id)
CoglRendererKMS *kms_renderer = egl_renderer->platform;
GList *l;
- for (l = kms_display->outputs; l; l = l->next)
+ for (l = kms_display->crtcs; l; l = l->next)
{
- CoglOutputKMS *output = l->data;
+ CoglKmsCrtc *crtc = l->data;
+
int ret = drmModeSetCrtc (kms_renderer->fd,
- output->encoder->crtc_id,
- fb_id, 0, 0,
- &output->connector->connector_id, 1,
- &output->mode);
+ 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;
+
+ if (crtc->count == 0)
+ continue;
+
+ ret = drmModePageFlip (kms_renderer->fd,
+ crtc->id, fb_id,
+ DRM_MODE_PAGE_FLIP_EVENT, flip);
+
if (ret)
- g_warning ("Failed to set crtc mode %s: %m", output->mode.name);
+ {
+ g_warning ("Failed to flip: %m");
+ continue;
+ }
+
+ 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)
@@ -538,6 +594,7 @@ _cogl_winsys_egl_display_setup (CoglDisplay *display,
drmModeRes *resources;
CoglOutputKMS *output0, *output1;
CoglBool mirror;
+ CoglKmsCrtc *crtc0, *crtc1;
kms_display = g_slice_new0 (CoglDisplayKMS);
egl_display->platform = kms_display;
@@ -592,7 +649,33 @@ _cogl_winsys_egl_display_setup (CoglDisplay *display,
}
}
else
- output0->mode = output0->modes[0];
+ {
+ output0->mode = output0->modes[0];
+ output1 = NULL;
+ }
+
+ crtc0 = g_slice_new (CoglKmsCrtc);
+ crtc0->id = output0->encoder->crtc_id;
+ crtc0->x = 0;
+ crtc0->y = 0;
+ crtc0->mode = output0->mode;
+ crtc0->connectors = g_new (uint32_t, 1);
+ crtc0->connectors[0] = output0->connector->connector_id;
+ crtc0->count = 1;
+ kms_display->crtcs = g_list_prepend (kms_display->crtcs, crtc0);
+
+ if (output1)
+ {
+ crtc1 = g_slice_new (CoglKmsCrtc);
+ crtc1->id = output1->encoder->crtc_id;
+ crtc1->x = 0;
+ crtc1->y = 0;
+ crtc1->mode = output1->mode;
+ crtc1->connectors = g_new (uint32_t, 1);
+ crtc1->connectors[0] = output1->connector->connector_id;
+ crtc1->count = 1;
+ kms_display->crtcs = g_list_prepend (kms_display->crtcs, crtc1);
+ }
kms_display->width = output0->mode.hdisplay;
kms_display->height = output0->mode.vdisplay;
@@ -648,6 +731,8 @@ _cogl_winsys_egl_display_destroy (CoglDisplay *display)
g_list_free (kms_display->outputs);
kms_display->outputs = NULL;
+ g_list_free_full (kms_display->crtcs, (GDestroyNotify) crtc_free);
+
g_slice_free (CoglDisplayKMS, egl_display->platform);
}
@@ -736,7 +821,6 @@ _cogl_winsys_onscreen_swap_buffers_with_damage (CoglOnscreen *onscreen,
CoglOnscreenKMS *kms_onscreen = egl_onscreen->platform;
uint32_t handle, stride;
CoglFlipKMS *flip;
- GList *l;
/* If we already have a pending swap then block until it completes */
while (kms_onscreen->next_fb_id != 0)
@@ -775,7 +859,7 @@ _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... */
+ * 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);
@@ -785,22 +869,7 @@ _cogl_winsys_onscreen_swap_buffers_with_damage (CoglOnscreen *onscreen,
flip = g_slice_new0 (CoglFlipKMS);
flip->onscreen = onscreen;
- for (l = kms_display->outputs; l; l = l->next)
- {
- CoglOutputKMS *output = l->data;
-
- if (drmModePageFlip (kms_renderer->fd,
- output->encoder->crtc_id,
- kms_onscreen->next_fb_id,
- DRM_MODE_PAGE_FLIP_EVENT,
- flip))
- {
- g_warning ("Failed to flip: %m");
- continue;
- }
-
- flip->pending++;
- }
+ flip_all_crtcs (context->display, flip, kms_onscreen->next_fb_id);
if (flip->pending == 0)
{
@@ -847,6 +916,16 @@ _cogl_winsys_onscreen_init (CoglOnscreen *onscreen,
_COGL_RETURN_VAL_IF_FAIL (egl_display->egl_context, FALSE);
+ if (kms_display->onscreen)
+ {
+ _cogl_set_error (error, COGL_WINSYS_ERROR,
+ COGL_WINSYS_ERROR_CREATE_ONSCREEN,
+ "Cannot have multiple onscreens in the KMS platform");
+ return FALSE;
+ }
+
+ kms_display->onscreen = onscreen;
+
onscreen->winsys = g_slice_new0 (CoglOnscreenEGL);
egl_onscreen = onscreen->winsys;
@@ -894,6 +973,9 @@ _cogl_winsys_onscreen_deinit (CoglOnscreen *onscreen)
{
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 = context->display->renderer;
CoglRendererEGL *egl_renderer = renderer->winsys;
CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
@@ -903,6 +985,8 @@ _cogl_winsys_onscreen_deinit (CoglOnscreen *onscreen)
if (egl_onscreen == NULL)
return;
+ kms_display->onscreen = NULL;
+
kms_onscreen = egl_onscreen->platform;
/* flip state takes a reference on the onscreen so there should
@@ -997,3 +1081,85 @@ cogl_kms_display_queue_modes_reset (CoglDisplay *display)
kms_display->pending_set_crtc = TRUE;
}
}
+
+CoglBool
+cogl_kms_display_set_layout (CoglDisplay *display,
+ int width,
+ int height,
+ CoglKmsCrtc **crtcs,
+ int n_crtcs,
+ CoglError **error)
+{
+ CoglDisplayEGL *egl_display = display->winsys;
+ CoglDisplayKMS *kms_display = egl_display->platform;
+ CoglRenderer *renderer = display->renderer;
+ CoglRendererEGL *egl_renderer = renderer->winsys;
+ CoglRendererKMS *kms_renderer = egl_renderer->platform;
+ GList *crtc_list;
+ int i;
+
+ if ((width != kms_display->width ||
+ height != kms_display->height) &&
+ kms_display->onscreen)
+ {
+ CoglOnscreenEGL *egl_onscreen = kms_display->onscreen->winsys;
+ CoglOnscreenKMS *kms_onscreen = egl_onscreen->platform;
+ struct gbm_surface *new_surface;
+ EGLSurface new_egl_surface;
+
+ /* Need to drop the GBM surface and create a new one */
+
+ new_surface = gbm_surface_create (kms_renderer->gbm,
+ width, height,
+ GBM_BO_FORMAT_XRGB8888,
+ GBM_BO_USE_SCANOUT |
+ GBM_BO_USE_RENDERING);
+
+ if (!new_surface)
+ {
+ _cogl_set_error (error, COGL_WINSYS_ERROR,
+ COGL_WINSYS_ERROR_CREATE_ONSCREEN,
+ "Failed to allocate new surface");
+ return FALSE;
+ }
+
+ new_egl_surface =
+ eglCreateWindowSurface (egl_renderer->edpy,
+ egl_display->egl_config,
+ (NativeWindowType) new_surface,
+ NULL);
+ if (new_egl_surface == EGL_NO_SURFACE)
+ {
+ _cogl_set_error (error, COGL_WINSYS_ERROR,
+ COGL_WINSYS_ERROR_CREATE_ONSCREEN,
+ "Failed to allocate new surface");
+ gbm_surface_destroy (new_surface);
+ return FALSE;
+ }
+
+ eglDestroySurface (egl_renderer->edpy, egl_onscreen->egl_surface);
+ gbm_surface_destroy (kms_onscreen->surface);
+
+ kms_onscreen->surface = new_surface;
+ egl_onscreen->egl_surface = new_egl_surface;
+
+ _cogl_framebuffer_winsys_update_size (COGL_FRAMEBUFFER (kms_display->onscreen), width, height);
+ }
+
+ kms_display->width = width;
+ kms_display->height = height;
+
+ g_list_free_full (kms_display->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);
+ kms_display->crtcs = crtc_list;
+
+ kms_display->pending_set_crtc = TRUE;
+
+ return TRUE;
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]