4 * An object oriented GL/GLES Abstraction/Utility Layer
6 * Copyright (C) 2011 Intel Corporation.
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library. If not, see
20 * <http://www.gnu.org/licenses/>.
24 * Rob Bradford <rob@linux.intel.com>
25 * Kristian Høgsberg (from eglkms.c)
26 * Benjamin Franzke (from eglkms.c)
27 * Robert Bragg <robert@linux.intel.com>
28 * Neil Roberts <neil@linux.intel.com>
36 #include <EGL/eglext.h>
38 #include <xf86drmMode.h>
41 #include <sys/fcntl.h>
44 #include "cogl-winsys-egl-kms-private.h"
45 #include "cogl-winsys-egl-private.h"
46 #include "cogl-renderer-private.h"
47 #include "cogl-framebuffer-private.h"
48 #include "cogl-onscreen-private.h"
49 #include "cogl-kms-renderer.h"
51 static const CoglWinsysEGLVtable _cogl_winsys_egl_vtable;
53 typedef struct _CoglRendererKMS
56 struct gbm_device *gbm;
59 typedef struct _CoglDisplayKMS
61 drmModeConnector *connector;
62 drmModeEncoder *encoder;
64 drmModeCrtcPtr saved_crtc;
68 typedef struct _CoglOnscreenKMS
72 unsigned int fb, color_rb[2], depth_rb;
77 static const char device_name[] = "/dev/dri/card0";
80 _cogl_winsys_renderer_disconnect (CoglRenderer *renderer)
82 CoglRendererEGL *egl_renderer = renderer->winsys;
83 CoglRendererKMS *kms_renderer = egl_renderer->platform;
85 eglTerminate (egl_renderer->edpy);
87 g_slice_free (CoglRendererKMS, kms_renderer);
88 g_slice_free (CoglRendererEGL, egl_renderer);
92 _cogl_winsys_renderer_connect (CoglRenderer *renderer,
95 CoglRendererEGL *egl_renderer;
96 CoglRendererKMS *kms_renderer;
98 renderer->winsys = g_slice_new0 (CoglRendererEGL);
99 egl_renderer = renderer->winsys;
101 egl_renderer->platform_vtable = &_cogl_winsys_egl_vtable;
102 egl_renderer->platform = g_slice_new0 (CoglRendererKMS);
103 kms_renderer = egl_renderer->platform;
105 kms_renderer->fd = open (device_name, O_RDWR);
106 if (kms_renderer->fd < 0)
108 /* Probably permissions error */
109 g_set_error (error, COGL_WINSYS_ERROR,
110 COGL_WINSYS_ERROR_INIT,
111 "Couldn't open %s", device_name);
115 kms_renderer->gbm = gbm_create_device (kms_renderer->fd);
116 if (kms_renderer->gbm == NULL)
118 g_set_error (error, COGL_WINSYS_ERROR,
119 COGL_WINSYS_ERROR_INIT,
120 "Couldn't create gbm device");
124 egl_renderer->edpy = eglGetDisplay ((EGLNativeDisplayType)kms_renderer->gbm);
125 if (egl_renderer->edpy == EGL_NO_DISPLAY)
127 g_set_error (error, COGL_WINSYS_ERROR,
128 COGL_WINSYS_ERROR_INIT,
129 "Couldn't get eglDisplay");
130 goto destroy_gbm_device;
133 if (!_cogl_winsys_egl_renderer_connect_common (renderer, error))
139 eglTerminate (egl_renderer->edpy);
141 gbm_device_destroy (kms_renderer->gbm);
143 close (kms_renderer->fd);
145 _cogl_winsys_renderer_disconnect (renderer);
151 _cogl_winsys_egl_display_setup (CoglDisplay *display,
154 CoglDisplayEGL *egl_display = display->winsys;
155 CoglDisplayKMS *kms_display;
156 CoglRendererEGL *egl_renderer = display->renderer->winsys;
157 CoglRendererKMS *kms_renderer = egl_renderer->platform;
158 CoglEGLWinsysFeature surfaceless_feature = 0;
159 const char *surfaceless_feature_name = "";
160 drmModeRes *resources;
161 drmModeConnector *connector;
162 drmModeEncoder *encoder;
165 kms_display = g_slice_new0 (CoglDisplayKMS);
166 egl_display->platform = kms_display;
168 switch (display->renderer->driver)
171 surfaceless_feature = COGL_EGL_WINSYS_FEATURE_SURFACELESS_OPENGL;
172 surfaceless_feature_name = "opengl";
174 case COGL_DRIVER_GLES1:
175 surfaceless_feature = COGL_EGL_WINSYS_FEATURE_SURFACELESS_GLES1;
176 surfaceless_feature_name = "gles1";
178 case COGL_DRIVER_GLES2:
179 surfaceless_feature = COGL_EGL_WINSYS_FEATURE_SURFACELESS_GLES2;
180 surfaceless_feature_name = "gles2";
182 case COGL_DRIVER_ANY:
183 g_return_val_if_reached (FALSE);
186 if (!(egl_renderer->private_features & surfaceless_feature))
188 g_set_error (error, COGL_WINSYS_ERROR,
189 COGL_WINSYS_ERROR_INIT,
190 "EGL_KHR_surfaceless_%s extension not available",
191 surfaceless_feature_name);
195 resources = drmModeGetResources (kms_renderer->fd);
198 g_set_error (error, COGL_WINSYS_ERROR,
199 COGL_WINSYS_ERROR_INIT,
200 "drmModeGetResources failed");
204 for (i = 0; i < resources->count_connectors; i++)
206 connector = drmModeGetConnector (kms_renderer->fd,
207 resources->connectors[i]);
208 if (connector == NULL)
211 if (connector->connection == DRM_MODE_CONNECTED &&
212 connector->count_modes > 0)
215 drmModeFreeConnector(connector);
218 if (i == resources->count_connectors)
220 g_set_error (error, COGL_WINSYS_ERROR,
221 COGL_WINSYS_ERROR_INIT,
222 "No currently active connector found");
226 for (i = 0; i < resources->count_encoders; i++)
228 encoder = drmModeGetEncoder (kms_renderer->fd, resources->encoders[i]);
233 if (encoder->encoder_id == connector->encoder_id)
236 drmModeFreeEncoder (encoder);
239 kms_display->saved_crtc = drmModeGetCrtc (kms_renderer->fd,
242 kms_display->connector = connector;
243 kms_display->encoder = encoder;
244 kms_display->mode = connector->modes[0];
245 kms_display->width = kms_display->mode.hdisplay;
246 kms_display->height = kms_display->mode.vdisplay;
252 _cogl_winsys_egl_display_destroy (CoglDisplay *display)
254 CoglDisplayEGL *egl_display = display->winsys;
256 g_slice_free (CoglDisplayKMS, egl_display->platform);
260 _cogl_winsys_egl_try_create_context (CoglDisplay *display,
264 CoglRenderer *renderer = display->renderer;
265 CoglRendererEGL *egl_renderer = renderer->winsys;
266 CoglDisplayEGL *egl_display = display->winsys;
268 egl_display->egl_context = eglCreateContext (egl_renderer->edpy,
273 if (egl_display->egl_context == NULL)
275 g_set_error (error, COGL_WINSYS_ERROR,
276 COGL_WINSYS_ERROR_CREATE_CONTEXT,
277 "Couldn't create EGL context");
281 if (!eglMakeCurrent (egl_renderer->edpy,
284 egl_display->egl_context))
286 g_set_error (error, COGL_WINSYS_ERROR,
287 COGL_WINSYS_ERROR_CREATE_CONTEXT,
288 "Failed to make context current");
296 _cogl_winsys_egl_cleanup_context (CoglDisplay *display)
298 CoglDisplayEGL *egl_display = display->winsys;
299 CoglDisplayKMS *kms_display = egl_display->platform;
300 CoglRenderer *renderer = display->renderer;
301 CoglRendererEGL *egl_renderer = renderer->winsys;
302 CoglRendererKMS *kms_renderer = egl_renderer->platform;
304 /* Restore the saved CRTC - this failing should not propagate an error */
305 if (kms_display->saved_crtc)
307 int ret = drmModeSetCrtc (kms_renderer->fd,
308 kms_display->saved_crtc->crtc_id,
309 kms_display->saved_crtc->buffer_id,
310 kms_display->saved_crtc->x,
311 kms_display->saved_crtc->y,
312 &kms_display->connector->connector_id, 1,
313 &kms_display->saved_crtc->mode);
315 g_critical (G_STRLOC ": Error restoring saved CRTC");
317 drmModeFreeCrtc (kms_display->saved_crtc);
322 _cogl_winsys_onscreen_swap_buffers (CoglOnscreen *onscreen)
324 CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context;
325 CoglDisplayEGL *egl_display = context->display->winsys;
326 CoglDisplayKMS *kms_display = egl_display->platform;
327 CoglRenderer *renderer = context->display->renderer;
328 CoglRendererEGL *egl_renderer = renderer->winsys;
329 CoglRendererKMS *kms_renderer = egl_renderer->platform;
330 CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
331 CoglOnscreenKMS *kms_onscreen = egl_onscreen->platform;
333 if (drmModeSetCrtc (kms_renderer->fd,
334 kms_display->encoder->crtc_id,
335 kms_onscreen->fb_id[kms_onscreen->current_frame],
337 &kms_display->connector->connector_id,
339 &kms_display->mode) != 0)
341 g_error (G_STRLOC ": Setting CRTC failed");
344 /* Update frame that we're drawing to be the new one */
345 kms_onscreen->current_frame ^= 1;
347 context->glBindFramebuffer (GL_FRAMEBUFFER_EXT, kms_onscreen->fb);
348 context->glFramebufferRenderbuffer (GL_FRAMEBUFFER_EXT,
349 GL_COLOR_ATTACHMENT0_EXT,
352 color_rb[kms_onscreen->current_frame]);
354 if (context->glCheckFramebufferStatus (GL_FRAMEBUFFER_EXT) !=
355 GL_FRAMEBUFFER_COMPLETE)
357 g_error (G_STRLOC ": FBO not complete");
362 _cogl_winsys_onscreen_init (CoglOnscreen *onscreen,
365 CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
366 CoglContext *context = framebuffer->context;
367 CoglDisplay *display = context->display;
368 CoglDisplayEGL *egl_display = display->winsys;
369 CoglDisplayKMS *kms_display = egl_display->platform;
370 CoglRenderer *renderer = display->renderer;
371 CoglRendererEGL *egl_renderer = renderer->winsys;
372 CoglRendererKMS *kms_renderer = egl_renderer->platform;
373 CoglOnscreenEGL *egl_onscreen;
374 CoglOnscreenKMS *kms_onscreen;
377 _COGL_RETURN_VAL_IF_FAIL (egl_display->egl_context, FALSE);
379 onscreen->winsys = g_slice_new0 (CoglOnscreenEGL);
380 egl_onscreen = onscreen->winsys;
382 kms_onscreen = g_slice_new0 (CoglOnscreenKMS);
383 egl_onscreen->platform = kms_onscreen;
385 context->glGenRenderbuffers (2, kms_onscreen->color_rb);
387 for (i = 0; i < 2; i++)
389 uint32_t handle, stride;
391 kms_onscreen->bo[i] =
392 gbm_bo_create (kms_renderer->gbm,
393 kms_display->mode.hdisplay, kms_display->mode.vdisplay,
394 GBM_BO_FORMAT_XRGB8888,
395 GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
396 if (!kms_onscreen->bo[i])
398 g_set_error (error, COGL_WINSYS_ERROR,
399 COGL_WINSYS_ERROR_CREATE_CONTEXT,
400 "Failed to allocate buffer");
404 kms_onscreen->image[i] =
405 _cogl_egl_create_image (context,
406 EGL_NATIVE_PIXMAP_KHR,
410 if (kms_onscreen->image[i] == EGL_NO_IMAGE_KHR)
412 g_set_error (error, COGL_WINSYS_ERROR,
413 COGL_WINSYS_ERROR_CREATE_CONTEXT,
414 "Failed to create EGL image");
418 context->glBindRenderbuffer (GL_RENDERBUFFER_EXT,
419 kms_onscreen->color_rb[i]);
420 context->glEGLImageTargetRenderbufferStorage (GL_RENDERBUFFER,
421 kms_onscreen->image[i]);
422 context->glBindRenderbuffer (GL_RENDERBUFFER_EXT, 0);
424 handle = gbm_bo_get_handle (kms_onscreen->bo[i]).u32;
425 stride = gbm_bo_get_pitch (kms_onscreen->bo[i]);
427 if (drmModeAddFB (kms_renderer->fd,
428 kms_display->mode.hdisplay,
429 kms_display->mode.vdisplay,
433 &kms_onscreen->fb_id[i]) != 0)
435 g_set_error (error, COGL_WINSYS_ERROR,
436 COGL_WINSYS_ERROR_CREATE_CONTEXT,
437 "Failed to create framebuffer from buffer");
442 context->glGenFramebuffers (1, &kms_onscreen->fb);
443 context->glBindFramebuffer (GL_FRAMEBUFFER_EXT, kms_onscreen->fb);
445 context->glGenRenderbuffers (1, &kms_onscreen->depth_rb);
446 context->glBindRenderbuffer (GL_RENDERBUFFER_EXT, kms_onscreen->depth_rb);
447 context->glRenderbufferStorage (GL_RENDERBUFFER_EXT,
448 GL_DEPTH_COMPONENT16,
449 kms_display->mode.hdisplay,
450 kms_display->mode.vdisplay);
451 context->glBindRenderbuffer (GL_RENDERBUFFER_EXT, 0);
453 context->glFramebufferRenderbuffer (GL_FRAMEBUFFER_EXT,
454 GL_DEPTH_ATTACHMENT_EXT,
456 kms_onscreen->depth_rb);
458 kms_onscreen->current_frame = 0;
459 _cogl_winsys_onscreen_swap_buffers (onscreen);
461 _cogl_framebuffer_winsys_update_size (framebuffer,
463 kms_display->height);
469 _cogl_winsys_onscreen_deinit (CoglOnscreen *onscreen)
471 CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
472 CoglContext *context = framebuffer->context;
473 CoglRenderer *renderer = context->display->renderer;
474 CoglRendererEGL *egl_renderer = renderer->winsys;
475 CoglRendererKMS *kms_renderer = egl_renderer->platform;
476 CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
477 CoglOnscreenKMS *kms_onscreen;
480 /* If we never successfully allocated then there's nothing to do */
481 if (egl_onscreen == NULL)
484 kms_onscreen = egl_onscreen->platform;
486 context->glBindFramebuffer (GL_FRAMEBUFFER_EXT, kms_onscreen->fb);
487 context->glFramebufferRenderbuffer (GL_FRAMEBUFFER_EXT,
488 GL_COLOR_ATTACHMENT0_EXT,
491 context->glDeleteRenderbuffers(2, kms_onscreen->color_rb);
492 context->glFramebufferRenderbuffer (GL_FRAMEBUFFER_EXT,
493 GL_DEPTH_ATTACHMENT_EXT,
496 context->glDeleteRenderbuffers(1, &kms_onscreen->depth_rb);
498 for (i = 0; i < 2; i++)
500 drmModeRmFB (kms_renderer->fd, kms_onscreen->fb_id[i]);
501 _cogl_egl_destroy_image (context, kms_onscreen->image[i]);
502 gbm_bo_destroy (kms_onscreen->bo[i]);
505 g_slice_free (CoglOnscreenKMS, kms_onscreen);
506 g_slice_free (CoglOnscreenEGL, onscreen->winsys);
507 onscreen->winsys = NULL;
511 _cogl_winsys_onscreen_bind (CoglOnscreen *onscreen)
513 CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context;
514 CoglDisplayEGL *egl_display = context->display->winsys;
515 CoglRenderer *renderer = context->display->renderer;
516 CoglRendererEGL *egl_renderer = renderer->winsys;
518 eglMakeCurrent (egl_renderer->edpy,
521 egl_display->egl_context);
525 _cogl_winsys_onscreen_update_swap_throttled (CoglOnscreen *onscreen)
527 _cogl_winsys_onscreen_bind (onscreen);
530 static const CoglWinsysEGLVtable
531 _cogl_winsys_egl_vtable =
533 .display_setup = _cogl_winsys_egl_display_setup,
534 .display_destroy = _cogl_winsys_egl_display_destroy,
535 .try_create_context = _cogl_winsys_egl_try_create_context,
536 .cleanup_context = _cogl_winsys_egl_cleanup_context
539 const CoglWinsysVtable *
540 _cogl_winsys_egl_kms_get_vtable (void)
542 static gboolean vtable_inited = FALSE;
543 static CoglWinsysVtable vtable;
547 /* The EGL_KMS winsys is a subclass of the EGL winsys so we
548 start by copying its vtable */
550 vtable = *_cogl_winsys_egl_get_vtable ();
552 vtable.id = COGL_WINSYS_ID_EGL_KMS;
553 vtable.name = "EGL_KMS";
555 vtable.renderer_connect = _cogl_winsys_renderer_connect;
556 vtable.renderer_disconnect = _cogl_winsys_renderer_disconnect;
558 vtable.onscreen_init = _cogl_winsys_onscreen_init;
559 vtable.onscreen_deinit = _cogl_winsys_onscreen_deinit;
560 vtable.onscreen_bind = _cogl_winsys_onscreen_bind;
562 /* The KMS winsys doesn't support swap region */
563 vtable.onscreen_swap_region = NULL;
564 vtable.onscreen_swap_buffers = _cogl_winsys_onscreen_swap_buffers;
566 vtable.onscreen_update_swap_throttled =
567 _cogl_winsys_onscreen_update_swap_throttled;
569 vtable_inited = TRUE;
576 cogl_kms_renderer_get_kms_fd (CoglRenderer *renderer)
578 _COGL_RETURN_VAL_IF_FAIL (cogl_is_renderer (renderer), -1);
580 if (renderer->connected)
582 CoglRendererEGL *egl_renderer = renderer->winsys;
583 CoglRendererKMS *kms_renderer = egl_renderer->platform;
584 return kms_renderer->fd;