blob: df251d908b9c3dac420201d60b6f67b984ffe4e9 [file] [log] [blame]
/**************************************************************************
*
* Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas.
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sub license, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial portions
* of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
* IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
**************************************************************************/
/**
* Code to interface a DRI driver to libEGL.
* Note that unlike previous DRI/EGL interfaces, this one is meant to
* be used _with_ X. Applications will use eglCreateWindowSurface()
* to render into X-created windows.
*
* This is an EGL driver that, in turn, loads a regular DRI driver.
* There are some dependencies on code in libGL, but those could be
* removed with some effort.
*
* Authors: Brian Paul
*/
#include <assert.h>
#include <stdlib.h>
#include <X11/Xlib.h>
#include "glxinit.h"
#include "driinit.h"
#include "glapi/glapi.h" /* for glapi functions */
#include "eglconfig.h"
#include "eglconfigutil.h"
#include "eglcontext.h"
#include "egldisplay.h"
#include "egldriver.h"
#include "eglglobals.h"
#include "egllog.h"
#include "eglsurface.h"
#define CALLOC_STRUCT(T) (struct T *) calloc(1, sizeof(struct T))
/** subclass of _EGLDriver */
struct xdri_egl_driver
{
_EGLDriver Base; /**< base class */
void (*FlushCurrentContext)(void);
};
/** driver data of _EGLDisplay */
struct xdri_egl_display
{
Display *dpy;
__GLXdisplayPrivate *dpyPriv;
__GLXDRIdisplay *driDisplay;
int driVersion;
__GLXscreenConfigs *psc;
EGLint scr;
};
/** subclass of _EGLContext */
struct xdri_egl_context
{
_EGLContext Base; /**< base class */
/* just enough info to create dri contexts */
GLXContext dummy_gc;
__GLXDRIcontext *driContext;
};
/** subclass of _EGLSurface */
struct xdri_egl_surface
{
_EGLSurface Base; /**< base class */
Drawable drawable;
__GLXDRIdrawable *driDrawable;
};
/** subclass of _EGLConfig */
struct xdri_egl_config
{
_EGLConfig Base; /**< base class */
const __GLcontextModes *mode; /**< corresponding GLX mode */
EGLint window_render_buffer;
};
/** cast wrapper */
static INLINE struct xdri_egl_driver *
xdri_egl_driver(_EGLDriver *drv)
{
return (struct xdri_egl_driver *) drv;
}
static INLINE struct xdri_egl_display *
lookup_display(_EGLDisplay *dpy)
{
return (struct xdri_egl_display *) dpy->DriverData;
}
/** Map EGLSurface handle to xdri_egl_surface object */
static INLINE struct xdri_egl_surface *
lookup_surface(_EGLSurface *surface)
{
return (struct xdri_egl_surface *) surface;
}
/** Map EGLContext handle to xdri_egl_context object */
static INLINE struct xdri_egl_context *
lookup_context(_EGLContext *context)
{
return (struct xdri_egl_context *) context;
}
/** Map EGLConfig handle to xdri_egl_config object */
static INLINE struct xdri_egl_config *
lookup_config(_EGLConfig *conf)
{
return (struct xdri_egl_config *) conf;
}
/** Get size of given window */
static Status
get_drawable_size(Display *dpy, Drawable d, uint *width, uint *height)
{
Window root;
Status stat;
int xpos, ypos;
unsigned int w, h, bw, depth;
stat = XGetGeometry(dpy, d, &root, &xpos, &ypos, &w, &h, &bw, &depth);
*width = w;
*height = h;
return stat;
}
static EGLBoolean
convert_config(_EGLConfig *conf, EGLint id, const __GLcontextModes *m)
{
EGLint val;
_eglInitConfig(conf, id);
if (!_eglConfigFromContextModesRec(conf, m, EGL_OPENGL_BIT, EGL_OPENGL_BIT))
return EGL_FALSE;
if (m->doubleBufferMode) {
/* pixmap and pbuffer surfaces are always single-buffered */
val = GET_CONFIG_ATTRIB(conf, EGL_SURFACE_TYPE);
val &= ~(EGL_PIXMAP_BIT | EGL_PBUFFER_BIT);
SET_CONFIG_ATTRIB(conf, EGL_SURFACE_TYPE, val);
}
else {
/* EGL requires OpenGL ES context to be double-buffered */
val = GET_CONFIG_ATTRIB(conf, EGL_RENDERABLE_TYPE);
val &= ~(EGL_OPENGL_ES_BIT | EGL_OPENGL_ES2_BIT);
SET_CONFIG_ATTRIB(conf, EGL_RENDERABLE_TYPE, val);
}
/* skip "empty" config */
if (!val)
return EGL_FALSE;
val = GET_CONFIG_ATTRIB(conf, EGL_SURFACE_TYPE);
if (!(val & EGL_PBUFFER_BIT)) {
/* bind-to-texture cannot be EGL_TRUE without pbuffer bit */
SET_CONFIG_ATTRIB(conf, EGL_BIND_TO_TEXTURE_RGB, EGL_FALSE);
SET_CONFIG_ATTRIB(conf, EGL_BIND_TO_TEXTURE_RGBA, EGL_FALSE);
}
/* EGL_NATIVE_RENDERABLE is a boolean */
val = GET_CONFIG_ATTRIB(conf, EGL_NATIVE_RENDERABLE);
if (val != EGL_TRUE)
SET_CONFIG_ATTRIB(conf, EGL_NATIVE_RENDERABLE, EGL_FALSE);
return _eglValidateConfig(conf, EGL_FALSE);
}
/**
* Produce a set of EGL configs.
*/
static EGLint
create_configs(_EGLDisplay *disp, const __GLcontextModes *m, EGLint first_id)
{
struct xdri_egl_display *xdri_dpy = lookup_display(disp);
int id = first_id;
for (; m; m = m->next) {
struct xdri_egl_config *xdri_conf;
_EGLConfig conf;
EGLint rb;
if (!convert_config(&conf, id, m))
continue;
if (m->doubleBufferMode) {
rb = EGL_BACK_BUFFER;
}
else {
/* ignore single-buffered mode for DRISW */
if (xdri_dpy->driVersion == 0)
continue;
rb = EGL_SINGLE_BUFFER;
}
xdri_conf = CALLOC_STRUCT(xdri_egl_config);
if (xdri_conf) {
memcpy(&xdri_conf->Base, &conf, sizeof(conf));
xdri_conf->mode = m;
xdri_conf->window_render_buffer = rb;
_eglAddConfig(disp, &xdri_conf->Base);
id++;
}
}
return id;
}
/**
* Called via eglInitialize(), xdri_dpy->API.Initialize().
*/
static EGLBoolean
xdri_eglInitialize(_EGLDriver *drv, _EGLDisplay *dpy,
EGLint *minor, EGLint *major)
{
struct xdri_egl_display *xdri_dpy;
__GLXdisplayPrivate *dpyPriv;
__GLXDRIdisplay *driDisplay;
__GLXscreenConfigs *psc;
EGLint first_id = 1;
int scr;
xdri_dpy = CALLOC_STRUCT(xdri_egl_display);
if (!xdri_dpy)
return _eglError(EGL_BAD_ALLOC, "eglInitialize");
xdri_dpy->dpy = (Display *) dpy->NativeDisplay;
if (!xdri_dpy->dpy) {
xdri_dpy->dpy = XOpenDisplay(NULL);
if (!xdri_dpy->dpy) {
free(xdri_dpy);
return _eglError(EGL_NOT_INITIALIZED, "eglInitialize");
}
}
dpyPriv = __glXInitialize(xdri_dpy->dpy);
if (!dpyPriv) {
_eglLog(_EGL_WARNING, "failed to create GLX display");
free(xdri_dpy);
return _eglError(EGL_NOT_INITIALIZED, "eglInitialize");
}
driDisplay = __driCreateDisplay(dpyPriv, &xdri_dpy->driVersion);
if (!driDisplay) {
_eglLog(_EGL_WARNING, "failed to create DRI display");
free(xdri_dpy);
return _eglError(EGL_NOT_INITIALIZED, "eglInitialize");
}
scr = DefaultScreen(xdri_dpy->dpy);
psc = &dpyPriv->screenConfigs[scr];
xdri_dpy->dpyPriv = dpyPriv;
xdri_dpy->driDisplay = driDisplay;
xdri_dpy->psc = psc;
xdri_dpy->scr = scr;
psc->driScreen = driDisplay->createScreen(psc, scr, dpyPriv);
if (!psc->driScreen) {
_eglLog(_EGL_WARNING, "failed to create DRI screen #%d", scr);
free(xdri_dpy);
return _eglError(EGL_NOT_INITIALIZED, "eglInitialize");
}
dpy->DriverData = xdri_dpy;
dpy->ClientAPIsMask = EGL_OPENGL_BIT;
/* add visuals and fbconfigs */
first_id = create_configs(dpy, psc->visuals, first_id);
create_configs(dpy, psc->configs, first_id);
/* we're supporting EGL 1.4 */
*minor = 1;
*major = 4;
return EGL_TRUE;
}
/**
* Called via eglTerminate(), drv->API.Terminate().
*/
static EGLBoolean
xdri_eglTerminate(_EGLDriver *drv, _EGLDisplay *dpy)
{
struct xdri_egl_display *xdri_dpy = lookup_display(dpy);
__GLXscreenConfigs *psc;
_eglReleaseDisplayResources(drv, dpy);
_eglCleanupDisplay(dpy);
psc = xdri_dpy->psc;
if (psc->driver_configs) {
unsigned int i;
for (i = 0; psc->driver_configs[i]; i++)
free((__DRIconfig *) psc->driver_configs[i]);
free(psc->driver_configs);
psc->driver_configs = NULL;
}
if (psc->driScreen) {
psc->driScreen->destroyScreen(psc);
free(psc->driScreen);
psc->driScreen = NULL;
}
xdri_dpy->driDisplay->destroyDisplay(xdri_dpy->driDisplay);
free(xdri_dpy);
dpy->DriverData = NULL;
return EGL_TRUE;
}
/*
* Called from eglGetProcAddress() via drv->API.GetProcAddress().
*/
static _EGLProc
xdri_eglGetProcAddress(_EGLDriver *drv, const char *procname)
{
/* the symbol is defined in libGL.so */
return (_EGLProc) _glapi_get_proc_address(procname);
}
/**
* Called via eglCreateContext(), drv->API.CreateContext().
*/
static _EGLContext *
xdri_eglCreateContext(_EGLDriver *drv, _EGLDisplay *dpy, _EGLConfig *conf,
_EGLContext *share_list, const EGLint *attrib_list)
{
struct xdri_egl_display *xdri_dpy = lookup_display(dpy);
struct xdri_egl_config *xdri_config = lookup_config(conf);
struct xdri_egl_context *shared = lookup_context(share_list);
__GLXscreenConfigs *psc = xdri_dpy->psc;
int renderType = GLX_RGBA_BIT;
struct xdri_egl_context *xdri_ctx;
xdri_ctx = CALLOC_STRUCT(xdri_egl_context);
if (!xdri_ctx) {
_eglError(EGL_BAD_ALLOC, "eglCreateContext");
return NULL;
}
xdri_ctx->dummy_gc = CALLOC_STRUCT(__GLXcontextRec);
if (!xdri_ctx->dummy_gc) {
_eglError(EGL_BAD_ALLOC, "eglCreateContext");
free(xdri_ctx);
return NULL;
}
if (!_eglInitContext(drv, &xdri_ctx->Base, &xdri_config->Base, attrib_list)) {
free(xdri_ctx->dummy_gc);
free(xdri_ctx);
return NULL;
}
/* the config decides the render buffer for the context */
xdri_ctx->Base.WindowRenderBuffer = xdri_config->window_render_buffer;
xdri_ctx->driContext =
psc->driScreen->createContext(psc,
xdri_config->mode,
xdri_ctx->dummy_gc,
(shared) ? shared->dummy_gc : NULL,
renderType);
if (!xdri_ctx->driContext) {
free(xdri_ctx->dummy_gc);
free(xdri_ctx);
return NULL;
}
/* fill in the required field */
xdri_ctx->dummy_gc->driContext = xdri_ctx->driContext;
return &xdri_ctx->Base;
}
static EGLBoolean
xdri_eglDestroyContext(_EGLDriver *drv, _EGLDisplay *dpy, _EGLContext *ctx)
{
struct xdri_egl_display *xdri_dpy = lookup_display(dpy);
struct xdri_egl_context *xdri_ctx = lookup_context(ctx);
if (!_eglIsContextBound(ctx)) {
xdri_ctx->driContext->destroyContext(xdri_ctx->driContext,
xdri_dpy->psc, xdri_dpy->dpy);
free(xdri_ctx->dummy_gc);
free(xdri_ctx);
}
return EGL_TRUE;
}
/**
* Called via eglMakeCurrent(), drv->API.MakeCurrent().
*/
static EGLBoolean
xdri_eglMakeCurrent(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSurface *d,
_EGLSurface *r, _EGLContext *context)
{
struct xdri_egl_driver *xdri_driver = xdri_egl_driver(drv);
struct xdri_egl_context *xdri_ctx = lookup_context(context);
struct xdri_egl_surface *draw = lookup_surface(d);
struct xdri_egl_surface *read = lookup_surface(r);
_EGLContext *old = _eglGetCurrentContext();
/* an unlinked context will be invalid after context switch */
if (!_eglIsContextLinked(old))
old = NULL;
if (!_eglMakeCurrent(drv, dpy, d, r, context))
return EGL_FALSE;
/* flush before context switch */
if (old && old != context && xdri_driver->FlushCurrentContext)
xdri_driver->FlushCurrentContext();
/* the symbol is defined in libGL.so */
_glapi_check_multithread();
if (xdri_ctx) {
if (!xdri_ctx->driContext->bindContext(xdri_ctx->driContext,
draw->driDrawable,
read->driDrawable)) {
return EGL_FALSE;
}
}
else if (old) {
xdri_ctx = lookup_context(old);
xdri_ctx->driContext->unbindContext(xdri_ctx->driContext);
}
return EGL_TRUE;
}
/**
* Called via eglCreateWindowSurface(), drv->API.CreateWindowSurface().
*/
static _EGLSurface *
xdri_eglCreateWindowSurface(_EGLDriver *drv, _EGLDisplay *dpy, _EGLConfig *conf,
EGLNativeWindowType window,
const EGLint *attrib_list)
{
struct xdri_egl_display *xdri_dpy = lookup_display(dpy);
struct xdri_egl_config *xdri_config = lookup_config(conf);
struct xdri_egl_surface *xdri_surf;
uint width, height;
xdri_surf = CALLOC_STRUCT(xdri_egl_surface);
if (!xdri_surf) {
_eglError(EGL_BAD_ALLOC, "eglCreateWindowSurface");
return NULL;
}
if (!_eglInitSurface(drv, &xdri_surf->Base, EGL_WINDOW_BIT,
&xdri_config->Base, attrib_list)) {
free(xdri_surf);
return NULL;
}
xdri_surf->driDrawable =
xdri_dpy->psc->driScreen->createDrawable(xdri_dpy->psc,
(XID) window,
(GLXDrawable) window,
xdri_config->mode);
if (!xdri_surf->driDrawable) {
free(xdri_surf);
return NULL;
}
xdri_surf->drawable = (Drawable) window;
get_drawable_size(xdri_dpy->dpy, window, &width, &height);
xdri_surf->Base.Width = width;
xdri_surf->Base.Height = height;
return &xdri_surf->Base;
}
/**
* Called via eglCreatePbufferSurface(), drv->API.CreatePbufferSurface().
*/
static _EGLSurface *
xdri_eglCreatePbufferSurface(_EGLDriver *drv, _EGLDisplay *dpy, _EGLConfig *conf,
const EGLint *attrib_list)
{
return NULL;
}
static EGLBoolean
xdri_eglDestroySurface(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSurface *surface)
{
struct xdri_egl_surface *xdri_surf = lookup_surface(surface);
if (!_eglIsSurfaceBound(&xdri_surf->Base)) {
xdri_surf->driDrawable->destroyDrawable(xdri_surf->driDrawable);
free(xdri_surf);
}
return EGL_TRUE;
}
static EGLBoolean
xdri_eglBindTexImage(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSurface *surf,
EGLint buffer)
{
return EGL_FALSE;
}
static EGLBoolean
xdri_eglReleaseTexImage(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSurface *surf,
EGLint buffer)
{
return EGL_FALSE;
}
static EGLBoolean
xdri_eglSwapBuffers(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSurface *draw)
{
struct xdri_egl_driver *xdri_driver = xdri_egl_driver(drv);
struct xdri_egl_display *xdri_dpy = lookup_display(dpy);
struct xdri_egl_surface *xdri_surf = lookup_surface(draw);
/* swapBuffers does not flush commands */
if (draw == _eglGetCurrentSurface(EGL_DRAW) &&
xdri_driver->FlushCurrentContext)
xdri_driver->FlushCurrentContext();
xdri_dpy->psc->driScreen->swapBuffers(xdri_surf->driDrawable, 0, 0, 0);
return EGL_TRUE;
}
static void
xdri_Unload(_EGLDriver *drv)
{
struct xdri_egl_driver *xdri_drv = xdri_egl_driver(drv);
free(xdri_drv);
}
/**
* This is the main entrypoint into the driver, called by libEGL.
* Create a new _EGLDriver object and init its dispatch table.
*/
_EGLDriver *
_eglMain(const char *args)
{
struct xdri_egl_driver *xdri_drv = CALLOC_STRUCT(xdri_egl_driver);
if (!xdri_drv)
return NULL;
_eglInitDriverFallbacks(&xdri_drv->Base);
xdri_drv->Base.API.Initialize = xdri_eglInitialize;
xdri_drv->Base.API.Terminate = xdri_eglTerminate;
xdri_drv->Base.API.GetProcAddress = xdri_eglGetProcAddress;
xdri_drv->Base.API.CreateContext = xdri_eglCreateContext;
xdri_drv->Base.API.DestroyContext = xdri_eglDestroyContext;
xdri_drv->Base.API.MakeCurrent = xdri_eglMakeCurrent;
xdri_drv->Base.API.CreateWindowSurface = xdri_eglCreateWindowSurface;
xdri_drv->Base.API.CreatePbufferSurface = xdri_eglCreatePbufferSurface;
xdri_drv->Base.API.DestroySurface = xdri_eglDestroySurface;
xdri_drv->Base.API.BindTexImage = xdri_eglBindTexImage;
xdri_drv->Base.API.ReleaseTexImage = xdri_eglReleaseTexImage;
xdri_drv->Base.API.SwapBuffers = xdri_eglSwapBuffers;
xdri_drv->Base.Name = "X/DRI";
xdri_drv->Base.Unload = xdri_Unload;
/* we need a way to flush commands */
xdri_drv->FlushCurrentContext =
(void (*)(void)) xdri_eglGetProcAddress(&xdri_drv->Base, "glFlush");
return &xdri_drv->Base;
}