/*
* Cogl
*
* An object oriented GL/GLES Abstraction/Utility Layer
*
* Copyright (C) 2011 Intel Corporation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* .
*
* Some code inspired by mesa demos eglkms.c.
*
* Authors:
* Rob Bradford
* Kristian Høgsberg (from eglkms.c)
* Benjamin Franzke (from eglkms.c)
*/
#include
#include
#include
#include
#include
#include
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "cogl.h"
#include "cogl-util.h"
#include "cogl-winsys-egl-private.h"
#include "cogl-winsys-private.h"
#include "cogl-feature-private.h"
#include "cogl-context-private.h"
#include "cogl-framebuffer.h"
#include "cogl-onscreen-private.h"
#include "cogl-swap-chain-private.h"
#include "cogl-renderer-private.h"
#include "cogl-private.h"
#include "cogl-winsys-kms.h"
static const char device_name[] = "/dev/dri/card0";
gboolean
_cogl_winsys_kms_connect (CoglRendererKMS *kms_renderer,
GError **error)
{
EGLint major, minor;
kms_renderer->fd = open (device_name, O_RDWR);
if (kms_renderer->fd < 0)
{
/* Probably permissions error */
g_set_error (error, COGL_WINSYS_ERROR,
COGL_WINSYS_ERROR_INIT,
"Couldn't open %s", device_name);
return FALSE;
}
kms_renderer->gbm = gbm_create_device (kms_renderer->fd);
if (kms_renderer->gbm == NULL)
{
g_set_error (error, COGL_WINSYS_ERROR,
COGL_WINSYS_ERROR_INIT,
"Couldn't create gbm device");
goto close_fd;
}
kms_renderer->dpy = eglGetDisplay ((EGLNativeDisplayType)kms_renderer->gbm);
if (kms_renderer->dpy == EGL_NO_DISPLAY)
{
g_set_error (error, COGL_WINSYS_ERROR,
COGL_WINSYS_ERROR_INIT,
"Couldn't get eglDisplay");
goto destroy_gbm_device;
}
if (!eglInitialize (kms_renderer->dpy, &major, &minor))
{
g_set_error (error, COGL_WINSYS_ERROR,
COGL_WINSYS_ERROR_INIT,
"Couldn't initialize EGL");
goto egl_terminate;
}
return TRUE;
egl_terminate:
eglTerminate (kms_renderer->dpy);
destroy_gbm_device:
gbm_device_destroy (kms_renderer->gbm);
close_fd:
close (kms_renderer->fd);
return FALSE;
}
gboolean
_cogl_winsys_kms_display_setup (CoglDisplay *display, GError **error)
{
CoglDisplayEGL *egl_display = display->winsys;
CoglDisplayKMS *kms_display = &egl_display->kms_display;
CoglRendererEGL *egl_renderer = display->renderer->winsys;
CoglRendererKMS *kms_renderer = &egl_renderer->kms_renderer;
drmModeRes *resources;
drmModeConnector *connector;
drmModeEncoder *encoder;
int i;
if (!(egl_renderer->private_features &
COGL_EGL_WINSYS_FEATURE_SURFACELESS_OPENGL))
{
g_set_error (error, COGL_WINSYS_ERROR,
COGL_WINSYS_ERROR_INIT,
"EGL_KHR_surfaceless_opengl extension not available");
return FALSE;
}
resources = drmModeGetResources (kms_renderer->fd);
if (!resources)
{
g_set_error (error, COGL_WINSYS_ERROR,
COGL_WINSYS_ERROR_INIT,
"drmModeGetResources failed");
return FALSE;
}
for (i = 0; i < resources->count_connectors; i++)
{
connector = drmModeGetConnector (kms_renderer->fd, resources->connectors[i]);
if (connector == NULL)
continue;
if (connector->connection == DRM_MODE_CONNECTED &&
connector->count_modes > 0)
break;
drmModeFreeConnector(connector);
}
if (i == resources->count_connectors)
{
g_set_error (error, COGL_WINSYS_ERROR,
COGL_WINSYS_ERROR_INIT,
"No currently active connector found");
return FALSE;
}
for (i = 0; i < resources->count_encoders; i++)
{
encoder = drmModeGetEncoder (kms_renderer->fd, resources->encoders[i]);
if (encoder == NULL)
continue;
if (encoder->encoder_id == connector->encoder_id)
break;
drmModeFreeEncoder (encoder);
}
kms_display->saved_crtc = drmModeGetCrtc (kms_renderer->fd,
encoder->crtc_id);
kms_display->connector = connector;
kms_display->encoder = encoder;
kms_display->mode = connector->modes[0];
kms_display->width = kms_display->mode.hdisplay;
kms_display->height = kms_display->mode.vdisplay;
return TRUE;
}
gboolean
_cogl_winsys_kms_create_context (CoglRendererKMS *kms_renderer,
CoglDisplayKMS *kms_display,
GError **error)
{
kms_display->egl_context = eglCreateContext (kms_renderer->dpy, NULL, EGL_NO_CONTEXT, NULL);
if (kms_display->egl_context == NULL)
{
g_set_error (error, COGL_WINSYS_ERROR,
COGL_WINSYS_ERROR_CREATE_CONTEXT,
"Couldn't create EGL context");
return FALSE;
}
if (!eglMakeCurrent (kms_renderer->dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, kms_display->egl_context))
{
g_set_error (error, COGL_WINSYS_ERROR,
COGL_WINSYS_ERROR_CREATE_CONTEXT,
"Failed to make context current");
return FALSE;
}
return TRUE;
}
gboolean
_cogl_winsys_kms_onscreen_init (CoglContext *context,
CoglRendererKMS *kms_renderer,
CoglDisplayKMS *kms_display,
CoglOnscreenKMS *kms_onscreen,
GError **error)
{
int i;
kms_onscreen->cogl_context = context;
context->glGenRenderbuffers (2, kms_onscreen->color_rb);
for (i = 0; i < 2; i++)
{
uint32_t handle, stride;
kms_onscreen->bo[i] =
gbm_bo_create (kms_renderer->gbm,
kms_display->mode.hdisplay, kms_display->mode.vdisplay,
GBM_BO_FORMAT_XRGB8888,
GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
if (!kms_onscreen->bo[i])
{
g_set_error (error, COGL_WINSYS_ERROR,
COGL_WINSYS_ERROR_CREATE_CONTEXT,
"Failed to allocate buffer");
return FALSE;
}
kms_onscreen->image[i] = _cogl_egl_create_image (context,
EGL_NATIVE_PIXMAP_KHR,
kms_onscreen->bo[i],
NULL);
if (kms_onscreen->image[i] == EGL_NO_IMAGE_KHR)
{
g_set_error (error, COGL_WINSYS_ERROR,
COGL_WINSYS_ERROR_CREATE_CONTEXT,
"Failed to create EGL image");
return FALSE;
}
context->glBindRenderbuffer (GL_RENDERBUFFER_EXT, kms_onscreen->color_rb[i]);
context->glEGLImageTargetRenderbufferStorage (GL_RENDERBUFFER, kms_onscreen->image[i]);
context->glBindRenderbuffer (GL_RENDERBUFFER_EXT, 0);
handle = gbm_bo_get_handle (kms_onscreen->bo[i]).u32;
stride = gbm_bo_get_pitch (kms_onscreen->bo[i]);
if (drmModeAddFB (kms_renderer->fd,
kms_display->mode.hdisplay, kms_display->mode.vdisplay,
24, 32,
stride,
handle,
&kms_onscreen->fb_id[i]) != 0)
{
g_set_error (error, COGL_WINSYS_ERROR,
COGL_WINSYS_ERROR_CREATE_CONTEXT,
"Failed to create framebuffer from buffer");
return FALSE;
}
}
context->glGenFramebuffers (1, &kms_onscreen->fb);
context->glBindFramebuffer (GL_FRAMEBUFFER_EXT, kms_onscreen->fb);
context->glGenRenderbuffers(1, &kms_onscreen->depth_rb);
context->glBindRenderbuffer(GL_RENDERBUFFER_EXT, kms_onscreen->depth_rb);
context->glRenderbufferStorage(GL_RENDERBUFFER_EXT,
GL_DEPTH_COMPONENT,
kms_display->mode.hdisplay, kms_display->mode.vdisplay);
context->glBindRenderbuffer (GL_RENDERBUFFER_EXT, 0);
context->glFramebufferRenderbuffer (GL_FRAMEBUFFER_EXT,
GL_DEPTH_ATTACHMENT_EXT,
GL_RENDERBUFFER_EXT,
kms_onscreen->depth_rb);
kms_onscreen->current_frame = 0;
_cogl_winsys_kms_swap_buffers (kms_renderer, kms_display, kms_onscreen);
return TRUE;
}
void
_cogl_winsys_kms_onscreen_deinit (CoglRendererKMS *kms_renderer,
CoglOnscreenKMS *kms_onscreen)
{
int i;
CoglContext *context = kms_onscreen->cogl_context;
context->glBindFramebuffer (GL_FRAMEBUFFER_EXT, kms_onscreen->fb);
context->glFramebufferRenderbuffer (GL_FRAMEBUFFER_EXT,
GL_COLOR_ATTACHMENT0_EXT,
GL_RENDERBUFFER_EXT,
0);
context->glDeleteRenderbuffers(2, kms_onscreen->color_rb);
context->glFramebufferRenderbuffer (GL_FRAMEBUFFER_EXT,
GL_DEPTH_ATTACHMENT_EXT,
GL_RENDERBUFFER_EXT,
0);
context->glDeleteRenderbuffers(1, &kms_onscreen->depth_rb);
for (i = 0; i < 2; i++)
{
drmModeRmFB (kms_renderer->fd, kms_onscreen->fb_id[i]);
_cogl_egl_destroy_image (context, kms_onscreen->image[i]);
gbm_bo_destroy (kms_onscreen->bo[i]);
}
}
gboolean
_cogl_winsys_kms_destroy_context (CoglRendererKMS *kms_renderer,
CoglDisplayKMS *kms_display,
GError **error)
{
int ret;
/* Restore the saved CRTC - this failing should not propagate an error */
ret = drmModeSetCrtc (kms_renderer->fd,
kms_display->saved_crtc->crtc_id,
kms_display->saved_crtc->buffer_id,
kms_display->saved_crtc->x, kms_display->saved_crtc->y,
&kms_display->connector->connector_id, 1,
&kms_display->saved_crtc->mode);
if (ret)
{
g_critical (G_STRLOC ": Error restoring saved CRTC");
}
drmModeFreeCrtc (kms_display->saved_crtc);
return TRUE;
}
void
_cogl_winsys_kms_swap_buffers (CoglRendererKMS *kms_renderer,
CoglDisplayKMS *kms_display,
CoglOnscreenKMS *kms_onscreen)
{
CoglContext *context = kms_onscreen->cogl_context;
if (drmModeSetCrtc (kms_renderer->fd,
kms_display->encoder->crtc_id,
kms_onscreen->fb_id[kms_onscreen->current_frame],
0, 0,
&kms_display->connector->connector_id,
1,
&kms_display->mode) != 0)
{
g_error (G_STRLOC ": Setting CRTC failed");
}
/* Update frame that we're drawing to be the new one */
kms_onscreen->current_frame ^= 1;
context->glBindFramebuffer (GL_FRAMEBUFFER_EXT, kms_onscreen->fb);
context->glFramebufferRenderbuffer (GL_FRAMEBUFFER_EXT,
GL_COLOR_ATTACHMENT0_EXT,
GL_RENDERBUFFER_EXT,
kms_onscreen->color_rb[kms_onscreen->current_frame]);
if (context->glCheckFramebufferStatus (GL_FRAMEBUFFER_EXT) !=
GL_FRAMEBUFFER_COMPLETE)
{
g_error (G_STRLOC ": FBO not complete");
}
}
void
_cogl_winsys_kms_bind (CoglRendererKMS *kms_renderer,
CoglDisplayKMS *kms_display)
{
eglMakeCurrent (kms_renderer->dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, kms_display->egl_context);
}