/************************************************************************** * * 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. * **************************************************************************/ /** * This is an EGL driver that wraps GLX. This gives the benefit of being * completely agnostic of the direct rendering implementation. * * Authors: Alan Hourihane */ /* * TODO: * * test eglBind/ReleaseTexImage */ #include #include #include #include #include "glxclient.h" #include "eglconfigutil.h" #include "eglconfig.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)) #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) #ifndef GLX_VERSION_1_4 #error "GL/glx.h must be equal to or greater than GLX 1.4" #endif /* * report OpenGL ES bits because apps usually forget to specify * EGL_RENDERABLE_TYPE when choosing configs */ #define GLX_EGL_APIS (EGL_OPENGL_BIT | EGL_OPENGL_ES_BIT | EGL_OPENGL_ES2_BIT) /** subclass of _EGLDriver */ struct GLX_egl_driver { _EGLDriver Base; /**< base class */ }; /** driver data of _EGLDisplay */ struct GLX_egl_display { Display *dpy; XVisualInfo *visuals; GLXFBConfig *fbconfigs; int glx_maj, glx_min; const char *extensions; EGLBoolean have_1_3; EGLBoolean have_make_current_read; EGLBoolean have_fbconfig; EGLBoolean have_pbuffer; /* GLX_SGIX_pbuffer */ PFNGLXCREATEGLXPBUFFERSGIXPROC glXCreateGLXPbufferSGIX; PFNGLXDESTROYGLXPBUFFERSGIXPROC glXDestroyGLXPbufferSGIX; /* workaround quirks of different GLX implementations */ EGLBoolean single_buffered_quirk; EGLBoolean glx_window_quirk; }; /** subclass of _EGLContext */ struct GLX_egl_context { _EGLContext Base; /**< base class */ GLXContext context; }; /** subclass of _EGLSurface */ struct GLX_egl_surface { _EGLSurface Base; /**< base class */ GLXDrawable drawable; }; /** subclass of _EGLConfig */ struct GLX_egl_config { _EGLConfig Base; /**< base class */ EGLBoolean double_buffered; int index; }; /** cast wrapper */ static struct GLX_egl_driver * GLX_egl_driver(_EGLDriver *drv) { return (struct GLX_egl_driver *) drv; } static struct GLX_egl_display * GLX_egl_display(_EGLDisplay *dpy) { return (struct GLX_egl_display *) dpy->DriverData; } static struct GLX_egl_context * GLX_egl_context(_EGLContext *ctx) { return (struct GLX_egl_context *) ctx; } static struct GLX_egl_surface * GLX_egl_surface(_EGLSurface *surf) { return (struct GLX_egl_surface *) surf; } static int GLX_egl_config_index(_EGLConfig *conf) { return ((struct GLX_egl_config *) conf)->index; } #define MAP_ATTRIB(attr, memb) \ { attr, offsetof(__GLcontextModes, memb) } static const struct { int attr; int offset; } fbconfig_attributes[] = { /* table 3.1 of GLX 1.4 */ MAP_ATTRIB(GLX_FBCONFIG_ID, fbconfigID), MAP_ATTRIB(GLX_BUFFER_SIZE, rgbBits), MAP_ATTRIB(GLX_LEVEL, level), MAP_ATTRIB(GLX_DOUBLEBUFFER, doubleBufferMode), MAP_ATTRIB(GLX_STEREO, stereoMode), MAP_ATTRIB(GLX_AUX_BUFFERS, numAuxBuffers), MAP_ATTRIB(GLX_RED_SIZE, redBits), MAP_ATTRIB(GLX_GREEN_SIZE, greenBits), MAP_ATTRIB(GLX_BLUE_SIZE, blueBits), MAP_ATTRIB(GLX_ALPHA_SIZE, alphaBits), MAP_ATTRIB(GLX_DEPTH_SIZE, depthBits), MAP_ATTRIB(GLX_STENCIL_SIZE, stencilBits), MAP_ATTRIB(GLX_ACCUM_RED_SIZE, accumRedBits), MAP_ATTRIB(GLX_ACCUM_GREEN_SIZE, accumGreenBits), MAP_ATTRIB(GLX_ACCUM_BLUE_SIZE, accumBlueBits), MAP_ATTRIB(GLX_ACCUM_ALPHA_SIZE, accumAlphaBits), MAP_ATTRIB(GLX_SAMPLE_BUFFERS, sampleBuffers), MAP_ATTRIB(GLX_SAMPLES, samples), MAP_ATTRIB(GLX_RENDER_TYPE, renderType), MAP_ATTRIB(GLX_DRAWABLE_TYPE, drawableType), MAP_ATTRIB(GLX_X_RENDERABLE, xRenderable), MAP_ATTRIB(GLX_X_VISUAL_TYPE, visualType), MAP_ATTRIB(GLX_CONFIG_CAVEAT, visualRating), MAP_ATTRIB(GLX_TRANSPARENT_TYPE, transparentPixel), MAP_ATTRIB(GLX_TRANSPARENT_INDEX_VALUE, transparentIndex), MAP_ATTRIB(GLX_TRANSPARENT_RED_VALUE, transparentRed), MAP_ATTRIB(GLX_TRANSPARENT_GREEN_VALUE, transparentGreen), MAP_ATTRIB(GLX_TRANSPARENT_BLUE_VALUE, transparentBlue), MAP_ATTRIB(GLX_TRANSPARENT_ALPHA_VALUE, transparentAlpha), MAP_ATTRIB(GLX_MAX_PBUFFER_WIDTH, maxPbufferWidth), MAP_ATTRIB(GLX_MAX_PBUFFER_HEIGHT, maxPbufferHeight), MAP_ATTRIB(GLX_MAX_PBUFFER_PIXELS, maxPbufferPixels), MAP_ATTRIB(GLX_VISUAL_ID, visualID), }; static EGLBoolean convert_fbconfig(Display *dpy, GLXFBConfig fbconfig, struct GLX_egl_config *GLX_conf) { __GLcontextModes mode; int err = 0, attr, val, i; memset(&mode, 0, sizeof(mode)); for (i = 0; i < ARRAY_SIZE(fbconfig_attributes); i++) { int offset = fbconfig_attributes[i].offset; attr = fbconfig_attributes[i].attr; err = glXGetFBConfigAttrib(dpy, fbconfig, attr, &val); if (err) { if (err == GLX_BAD_ATTRIBUTE) { err = 0; continue; } break; } *((int *) ((char *) &mode + offset)) = val; } if (err) return EGL_FALSE; /* must have rgba bit */ if (!(mode.renderType & GLX_RGBA_BIT)) return EGL_FALSE; /* pixmap and pbuffer surfaces must be single-buffered in EGL */ if (mode.doubleBufferMode) { mode.drawableType &= ~(GLX_PIXMAP_BIT | GLX_PBUFFER_BIT); if (!mode.drawableType) return EGL_FALSE; } mode.rgbMode = GL_TRUE; mode.haveAccumBuffer = (mode.accumRedBits + mode.accumGreenBits + mode.accumBlueBits + mode.accumAlphaBits > 0); mode.haveDepthBuffer = (mode.depthBits > 0); mode.haveStencilBuffer = (mode.stencilBits > 0); GLX_conf->double_buffered = (mode.doubleBufferMode != 0); return _eglConfigFromContextModesRec(&GLX_conf->Base, &mode, GLX_EGL_APIS, GLX_EGL_APIS); } static const struct { int attr; int offset; } visual_attributes[] = { /* table 3.7 of GLX 1.4 */ /* no GLX_USE_GL */ MAP_ATTRIB(GLX_BUFFER_SIZE, rgbBits), MAP_ATTRIB(GLX_LEVEL, level), MAP_ATTRIB(GLX_RGBA, rgbMode), MAP_ATTRIB(GLX_DOUBLEBUFFER, doubleBufferMode), MAP_ATTRIB(GLX_STEREO, stereoMode), MAP_ATTRIB(GLX_AUX_BUFFERS, numAuxBuffers), MAP_ATTRIB(GLX_RED_SIZE, redBits), MAP_ATTRIB(GLX_GREEN_SIZE, greenBits), MAP_ATTRIB(GLX_BLUE_SIZE, blueBits), MAP_ATTRIB(GLX_ALPHA_SIZE, alphaBits), MAP_ATTRIB(GLX_DEPTH_SIZE, depthBits), MAP_ATTRIB(GLX_STENCIL_SIZE, stencilBits), MAP_ATTRIB(GLX_ACCUM_RED_SIZE, accumRedBits), MAP_ATTRIB(GLX_ACCUM_GREEN_SIZE, accumGreenBits), MAP_ATTRIB(GLX_ACCUM_BLUE_SIZE, accumBlueBits), MAP_ATTRIB(GLX_ACCUM_ALPHA_SIZE, accumAlphaBits), MAP_ATTRIB(GLX_SAMPLE_BUFFERS, sampleBuffers), MAP_ATTRIB(GLX_SAMPLES, samples), MAP_ATTRIB(GLX_FBCONFIG_ID, fbconfigID), /* GLX_EXT_visual_rating */ MAP_ATTRIB(GLX_VISUAL_CAVEAT_EXT, visualRating), }; static int get_visual_type(const XVisualInfo *vis) { int klass; #if defined(__cplusplus) || defined(c_plusplus) klass = vis->c_class; #else klass = vis->class; #endif switch (klass) { case TrueColor: return GLX_TRUE_COLOR; case DirectColor: return GLX_DIRECT_COLOR; case PseudoColor: return GLX_PSEUDO_COLOR; case StaticColor: return GLX_STATIC_COLOR; case GrayScale: return GLX_GRAY_SCALE; case StaticGray: return GLX_STATIC_GRAY; default: return GLX_NONE; } } static EGLBoolean convert_visual(Display *dpy, XVisualInfo *vinfo, struct GLX_egl_config *GLX_conf) { __GLcontextModes mode; int err, attr, val, i; /* the visual must support OpenGL */ err = glXGetConfig(dpy, vinfo, GLX_USE_GL, &val); if (err || !val) return EGL_FALSE; memset(&mode, 0, sizeof(mode)); for (i = 0; i < ARRAY_SIZE(visual_attributes); i++) { int offset = visual_attributes[i].offset; attr = visual_attributes[i].attr; err = glXGetConfig(dpy, vinfo, attr, &val); if (err) { if (err == GLX_BAD_ATTRIBUTE) { err = 0; continue; } break; } *((int *) ((char *) &mode + offset)) = val; } if (err) return EGL_FALSE; /* must be RGB mode */ if (!mode.rgbMode) return EGL_FALSE; mode.visualID = vinfo->visualid; mode.visualType = get_visual_type(vinfo); mode.redMask = vinfo->red_mask; mode.greenMask = vinfo->green_mask; mode.blueMask = vinfo->blue_mask; mode.drawableType = GLX_WINDOW_BIT; /* pixmap surfaces must be single-buffered in EGL */ if (!mode.doubleBufferMode) mode.drawableType |= GLX_PIXMAP_BIT; mode.renderType = GLX_RGBA_BIT; mode.xRenderable = GL_TRUE; mode.haveAccumBuffer = (mode.accumRedBits + mode.accumGreenBits + mode.accumBlueBits + mode.accumAlphaBits > 0); mode.haveDepthBuffer = (mode.depthBits > 0); mode.haveStencilBuffer = (mode.stencilBits > 0); GLX_conf->double_buffered = (mode.doubleBufferMode != 0); return _eglConfigFromContextModesRec(&GLX_conf->Base, &mode, GLX_EGL_APIS, GLX_EGL_APIS); } static void fix_config(struct GLX_egl_display *GLX_dpy, struct GLX_egl_config *GLX_conf) { _EGLConfig *conf = &GLX_conf->Base; EGLint surface_type, r, g, b, a; surface_type = GET_CONFIG_ATTRIB(conf, EGL_SURFACE_TYPE); if (!GLX_conf->double_buffered && GLX_dpy->single_buffered_quirk) { /* some GLX impls do not like single-buffered window surface */ surface_type &= ~EGL_WINDOW_BIT; /* pbuffer bit is usually not set */ if (GLX_dpy->have_pbuffer) surface_type |= EGL_PBUFFER_BIT; SET_CONFIG_ATTRIB(conf, EGL_SURFACE_TYPE, surface_type); } /* no visual attribs unless window bit is set */ if (!(surface_type & EGL_WINDOW_BIT)) { SET_CONFIG_ATTRIB(conf, EGL_NATIVE_VISUAL_ID, 0); SET_CONFIG_ATTRIB(conf, EGL_NATIVE_VISUAL_TYPE, EGL_NONE); } /* make sure buffer size is set correctly */ r = GET_CONFIG_ATTRIB(conf, EGL_RED_SIZE); g = GET_CONFIG_ATTRIB(conf, EGL_GREEN_SIZE); b = GET_CONFIG_ATTRIB(conf, EGL_BLUE_SIZE); a = GET_CONFIG_ATTRIB(conf, EGL_ALPHA_SIZE); SET_CONFIG_ATTRIB(conf, EGL_BUFFER_SIZE, r + g + b + a); } static EGLBoolean create_configs(_EGLDisplay *dpy, struct GLX_egl_display *GLX_dpy, EGLint screen) { EGLint num_configs = 0, i; EGLint id = 1; if (GLX_dpy->have_fbconfig) { GLX_dpy->fbconfigs = glXGetFBConfigs(GLX_dpy->dpy, screen, &num_configs); } else { XVisualInfo vinfo_template; long mask; vinfo_template.screen = screen; mask = VisualScreenMask; GLX_dpy->visuals = XGetVisualInfo(GLX_dpy->dpy, mask, &vinfo_template, &num_configs); } if (!num_configs) return EGL_FALSE; for (i = 0; i < num_configs; i++) { struct GLX_egl_config *GLX_conf, template; EGLBoolean ok; memset(&template, 0, sizeof(template)); _eglInitConfig(&template.Base, id); if (GLX_dpy->have_fbconfig) ok = convert_fbconfig(GLX_dpy->dpy, GLX_dpy->fbconfigs[i], &template); else ok = convert_visual(GLX_dpy->dpy, &GLX_dpy->visuals[i], &template); if (!ok) continue; fix_config(GLX_dpy, &template); if (!_eglValidateConfig(&template.Base, EGL_FALSE)) { _eglLog(_EGL_DEBUG, "GLX: failed to validate config %d", i); continue; } GLX_conf = CALLOC_STRUCT(GLX_egl_config); if (GLX_conf) { memcpy(GLX_conf, &template, sizeof(template)); GLX_conf->index = i; _eglAddConfig(dpy, &GLX_conf->Base); id++; } } return EGL_TRUE; } static void check_extensions(struct GLX_egl_display *GLX_dpy, EGLint screen) { GLX_dpy->extensions = glXQueryExtensionsString(GLX_dpy->dpy, screen); if (GLX_dpy->extensions) { /* glXGetProcAddress is assumed */ if (strstr(GLX_dpy->extensions, "GLX_SGI_make_current_read")) { /* GLX 1.3 entry points are used */ GLX_dpy->have_make_current_read = EGL_TRUE; } if (strstr(GLX_dpy->extensions, "GLX_SGIX_fbconfig")) { /* GLX 1.3 entry points are used */ GLX_dpy->have_fbconfig = EGL_TRUE; } if (strstr(GLX_dpy->extensions, "GLX_SGIX_pbuffer")) { GLX_dpy->glXCreateGLXPbufferSGIX = (PFNGLXCREATEGLXPBUFFERSGIXPROC) glXGetProcAddress((const GLubyte *) "glXCreateGLXPbufferSGIX"); GLX_dpy->glXDestroyGLXPbufferSGIX = (PFNGLXDESTROYGLXPBUFFERSGIXPROC) glXGetProcAddress((const GLubyte *) "glXDestroyGLXPbufferSGIX"); if (GLX_dpy->glXCreateGLXPbufferSGIX && GLX_dpy->glXDestroyGLXPbufferSGIX && GLX_dpy->have_fbconfig) GLX_dpy->have_pbuffer = EGL_TRUE; } } if (GLX_dpy->glx_maj == 1 && GLX_dpy->glx_min >= 3) { GLX_dpy->have_1_3 = EGL_TRUE; GLX_dpy->have_make_current_read = EGL_TRUE; GLX_dpy->have_fbconfig = EGL_TRUE; GLX_dpy->have_pbuffer = EGL_TRUE; } } static void check_quirks(struct GLX_egl_display *GLX_dpy, EGLint screen) { const char *vendor; GLX_dpy->single_buffered_quirk = EGL_TRUE; GLX_dpy->glx_window_quirk = EGL_TRUE; vendor = glXGetClientString(GLX_dpy->dpy, GLX_VENDOR); if (vendor && strstr(vendor, "NVIDIA")) { vendor = glXQueryServerString(GLX_dpy->dpy, screen, GLX_VENDOR); if (vendor && strstr(vendor, "NVIDIA")) { _eglLog(_EGL_DEBUG, "disable quirks"); GLX_dpy->single_buffered_quirk = EGL_FALSE; GLX_dpy->glx_window_quirk = EGL_FALSE; } } } /** * Called via eglInitialize(), GLX_drv->API.Initialize(). */ static EGLBoolean GLX_eglInitialize(_EGLDriver *drv, _EGLDisplay *disp, EGLint *major, EGLint *minor) { struct GLX_egl_display *GLX_dpy; GLX_dpy = CALLOC_STRUCT(GLX_egl_display); if (!GLX_dpy) return _eglError(EGL_BAD_ALLOC, "eglInitialize"); GLX_dpy->dpy = (Display *) disp->NativeDisplay; if (!GLX_dpy->dpy) { GLX_dpy->dpy = XOpenDisplay(NULL); if (!GLX_dpy->dpy) { _eglLog(_EGL_WARNING, "GLX: XOpenDisplay failed"); free(GLX_dpy); return EGL_FALSE; } } if (!glXQueryVersion(GLX_dpy->dpy, &GLX_dpy->glx_maj, &GLX_dpy->glx_min)) { _eglLog(_EGL_WARNING, "GLX: glXQueryVersion failed"); if (!disp->NativeDisplay) XCloseDisplay(GLX_dpy->dpy); free(GLX_dpy); return EGL_FALSE; } check_extensions(GLX_dpy, DefaultScreen(GLX_dpy->dpy)); check_quirks(GLX_dpy, DefaultScreen(GLX_dpy->dpy)); create_configs(disp, GLX_dpy, DefaultScreen(GLX_dpy->dpy)); if (!disp->NumConfigs) { _eglLog(_EGL_WARNING, "GLX: failed to create any config"); if (!disp->NativeDisplay) XCloseDisplay(GLX_dpy->dpy); free(GLX_dpy); return EGL_FALSE; } disp->DriverData = (void *) GLX_dpy; disp->ClientAPIsMask = GLX_EGL_APIS; /* we're supporting EGL 1.4 */ *major = 1; *minor = 4; return EGL_TRUE; } /** * Called via eglTerminate(), drv->API.Terminate(). */ static EGLBoolean GLX_eglTerminate(_EGLDriver *drv, _EGLDisplay *disp) { struct GLX_egl_display *GLX_dpy = GLX_egl_display(disp); _eglReleaseDisplayResources(drv, disp); _eglCleanupDisplay(disp); if (GLX_dpy->visuals) XFree(GLX_dpy->visuals); if (GLX_dpy->fbconfigs) XFree(GLX_dpy->fbconfigs); if (!disp->NativeDisplay) XCloseDisplay(GLX_dpy->dpy); free(GLX_dpy); disp->DriverData = NULL; return EGL_TRUE; } /** * Called via eglCreateContext(), drv->API.CreateContext(). */ static _EGLContext * GLX_eglCreateContext(_EGLDriver *drv, _EGLDisplay *disp, _EGLConfig *conf, _EGLContext *share_list, const EGLint *attrib_list) { struct GLX_egl_context *GLX_ctx = CALLOC_STRUCT(GLX_egl_context); struct GLX_egl_display *GLX_dpy = GLX_egl_display(disp); struct GLX_egl_context *GLX_ctx_shared = GLX_egl_context(share_list); if (!GLX_ctx) { _eglError(EGL_BAD_ALLOC, "eglCreateContext"); return NULL; } if (!_eglInitContext(drv, &GLX_ctx->Base, conf, attrib_list)) { free(GLX_ctx); return NULL; } #ifdef GLX_VERSION_1_3 if (GLX_dpy->fbconfigs) GLX_ctx->context = glXCreateNewContext(GLX_dpy->dpy, GLX_dpy->fbconfigs[GLX_egl_config_index(conf)], GLX_RGBA_TYPE, GLX_ctx_shared ? GLX_ctx_shared->context : NULL, GL_TRUE); else #endif GLX_ctx->context = glXCreateContext(GLX_dpy->dpy, &GLX_dpy->visuals[GLX_egl_config_index(conf)], GLX_ctx_shared ? GLX_ctx_shared->context : NULL, GL_TRUE); if (!GLX_ctx->context) { free(GLX_ctx); return NULL; } #if 1 /* (maybe?) need to have a direct rendering context */ if (!glXIsDirect(GLX_dpy->dpy, GLX_ctx->context)) { glXDestroyContext(GLX_dpy->dpy, GLX_ctx->context); free(GLX_ctx); return NULL; } #endif return &GLX_ctx->Base; } /** * Called via eglMakeCurrent(), drv->API.MakeCurrent(). */ static EGLBoolean GLX_eglMakeCurrent(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *dsurf, _EGLSurface *rsurf, _EGLContext *ctx) { struct GLX_egl_display *GLX_dpy = GLX_egl_display(disp); struct GLX_egl_surface *GLX_dsurf = GLX_egl_surface(dsurf); struct GLX_egl_surface *GLX_rsurf = GLX_egl_surface(rsurf); struct GLX_egl_context *GLX_ctx = GLX_egl_context(ctx); GLXDrawable ddraw, rdraw; GLXContext cctx; if (!_eglMakeCurrent(drv, disp, dsurf, rsurf, ctx)) return EGL_FALSE; ddraw = (GLX_dsurf) ? GLX_dsurf->drawable : None; rdraw = (GLX_rsurf) ? GLX_rsurf->drawable : None; cctx = (GLX_ctx) ? GLX_ctx->context : NULL; #ifdef GLX_VERSION_1_3 if (glXMakeContextCurrent(GLX_dpy->dpy, ddraw, rdraw, cctx)) return EGL_TRUE; #endif if (ddraw == rdraw && glXMakeCurrent(GLX_dpy->dpy, ddraw, cctx)) return EGL_TRUE; return EGL_FALSE; } /** 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; } /** * Called via eglCreateWindowSurface(), drv->API.CreateWindowSurface(). */ static _EGLSurface * GLX_eglCreateWindowSurface(_EGLDriver *drv, _EGLDisplay *disp, _EGLConfig *conf, NativeWindowType window, const EGLint *attrib_list) { struct GLX_egl_display *GLX_dpy = GLX_egl_display(disp); struct GLX_egl_surface *GLX_surf; uint width, height; GLX_surf = CALLOC_STRUCT(GLX_egl_surface); if (!GLX_surf) { _eglError(EGL_BAD_ALLOC, "eglCreateWindowSurface"); return NULL; } if (!_eglInitSurface(drv, &GLX_surf->Base, EGL_WINDOW_BIT, conf, attrib_list)) { free(GLX_surf); return NULL; } GLX_surf->drawable = window; get_drawable_size(GLX_dpy->dpy, window, &width, &height); GLX_surf->Base.Width = width; GLX_surf->Base.Height = height; return &GLX_surf->Base; } #ifdef GLX_VERSION_1_3 static _EGLSurface * GLX_eglCreatePixmapSurface(_EGLDriver *drv, _EGLDisplay *disp, _EGLConfig *conf, NativePixmapType pixmap, const EGLint *attrib_list) { struct GLX_egl_display *GLX_dpy = GLX_egl_display(disp); struct GLX_egl_surface *GLX_surf; int i; /* GLX must >= 1.3 */ if (!(GLX_dpy->glx_maj == 1 && GLX_dpy->glx_min >= 3)) return NULL; GLX_surf = CALLOC_STRUCT(GLX_egl_surface); if (!GLX_surf) { _eglError(EGL_BAD_ALLOC, "eglCreatePixmapSurface"); return NULL; } if (!_eglInitSurface(drv, &GLX_surf->Base, EGL_PIXMAP_BIT, conf, attrib_list)) { free(GLX_surf); return NULL; } for (i = 0; attrib_list && attrib_list[i] != EGL_NONE; i++) { switch (attrib_list[i]) { /* no attribs at this time */ default: _eglError(EGL_BAD_ATTRIBUTE, "eglCreatePixmapSurface"); free(GLX_surf); return NULL; } } GLX_surf->drawable = glXCreatePixmap(GLX_dpy->dpy, GLX_dpy->fbconfigs[GLX_egl_config_index(conf)], pixmap, NULL); if (!GLX_surf->drawable) { free(GLX_surf); return NULL; } return &GLX_surf->Base; } static _EGLSurface * GLX_eglCreatePbufferSurface(_EGLDriver *drv, _EGLDisplay *disp, _EGLConfig *conf, const EGLint *attrib_list) { struct GLX_egl_display *GLX_dpy = GLX_egl_display(disp); struct GLX_egl_surface *GLX_surf; int attribs[5]; int i = 0, j = 0; /* GLX must >= 1.3 */ if (!(GLX_dpy->glx_maj == 1 && GLX_dpy->glx_min >= 3)) return NULL; GLX_surf = CALLOC_STRUCT(GLX_egl_surface); if (!GLX_surf) { _eglError(EGL_BAD_ALLOC, "eglCreatePbufferSurface"); return NULL; } if (!_eglInitSurface(drv, &GLX_surf->Base, EGL_PBUFFER_BIT, conf, attrib_list)) { free(GLX_surf); return NULL; } while(attrib_list[i] != EGL_NONE) { switch (attrib_list[i]) { case EGL_WIDTH: attribs[j++] = GLX_PBUFFER_WIDTH; attribs[j++] = attrib_list[i+1]; break; case EGL_HEIGHT: attribs[j++] = GLX_PBUFFER_HEIGHT; attribs[j++] = attrib_list[i+1]; break; } i++; } attribs[j++] = 0; GLX_surf->drawable = glXCreatePbuffer(GLX_dpy->dpy, GLX_dpy->fbconfigs[GLX_egl_config_index(conf)], attribs); if (!GLX_surf->drawable) { free(GLX_surf); return NULL; } return &GLX_surf->Base; } #endif static EGLBoolean GLX_eglDestroySurface(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf) { struct GLX_egl_display *GLX_dpy = GLX_egl_display(disp); if (!_eglIsSurfaceBound(surf)) { struct GLX_egl_surface *GLX_surf = GLX_egl_surface(surf); switch (surf->Type) { case EGL_PBUFFER_BIT: glXDestroyPbuffer(GLX_dpy->dpy, GLX_surf->drawable); break; case EGL_PIXMAP_BIT: glXDestroyPixmap(GLX_dpy->dpy, GLX_surf->drawable); break; default: break; } free(surf); } return EGL_TRUE; } static EGLBoolean GLX_eglBindTexImage(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf, EGLint buffer) { struct GLX_egl_display *GLX_dpy = GLX_egl_display(disp); struct GLX_egl_surface *GLX_surf = GLX_egl_surface(surf); /* buffer ?? */ glXBindTexImageEXT(GLX_dpy->dpy, GLX_surf->drawable, GLX_FRONT_LEFT_EXT, NULL); return EGL_TRUE; } static EGLBoolean GLX_eglReleaseTexImage(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf, EGLint buffer) { struct GLX_egl_display *GLX_dpy = GLX_egl_display(disp); struct GLX_egl_surface *GLX_surf = GLX_egl_surface(surf); /* buffer ?? */ glXReleaseTexImageEXT(GLX_dpy->dpy, GLX_surf->drawable, GLX_FRONT_LEFT_EXT); return EGL_TRUE; } static EGLBoolean GLX_eglSwapBuffers(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *draw) { struct GLX_egl_display *GLX_dpy = GLX_egl_display(disp); struct GLX_egl_surface *GLX_surf = GLX_egl_surface(draw); _eglLog(_EGL_DEBUG, "GLX: EGL SwapBuffers 0x%x",draw); glXSwapBuffers(GLX_dpy->dpy, GLX_surf->drawable); return EGL_TRUE; } /* * Called from eglGetProcAddress() via drv->API.GetProcAddress(). */ static _EGLProc GLX_eglGetProcAddress(const char *procname) { /* This is a bit of a hack to get at the gallium/Mesa state tracker * function st_get_proc_address(). This will probably change at * some point. */ _EGLProc (*get_proc_addr)(const char *procname); _EGLProc proc_addr; get_proc_addr = dlsym(NULL, "st_get_proc_address"); if (get_proc_addr) return get_proc_addr(procname); proc_addr = glXGetProcAddress((const GLubyte *)procname); if (proc_addr) return proc_addr; return (_EGLProc)dlsym(NULL, procname); } static void GLX_Unload(_EGLDriver *drv) { struct GLX_egl_driver *GLX_drv = GLX_egl_driver(drv); free(GLX_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 GLX_egl_driver *GLX_drv = CALLOC_STRUCT(GLX_egl_driver); if (!GLX_drv) return NULL; _eglInitDriverFallbacks(&GLX_drv->Base); GLX_drv->Base.API.Initialize = GLX_eglInitialize; GLX_drv->Base.API.Terminate = GLX_eglTerminate; GLX_drv->Base.API.CreateContext = GLX_eglCreateContext; GLX_drv->Base.API.MakeCurrent = GLX_eglMakeCurrent; GLX_drv->Base.API.CreateWindowSurface = GLX_eglCreateWindowSurface; #ifdef GLX_VERSION_1_3 GLX_drv->Base.API.CreatePixmapSurface = GLX_eglCreatePixmapSurface; GLX_drv->Base.API.CreatePbufferSurface = GLX_eglCreatePbufferSurface; #endif GLX_drv->Base.API.DestroySurface = GLX_eglDestroySurface; GLX_drv->Base.API.BindTexImage = GLX_eglBindTexImage; GLX_drv->Base.API.ReleaseTexImage = GLX_eglReleaseTexImage; GLX_drv->Base.API.SwapBuffers = GLX_eglSwapBuffers; GLX_drv->Base.API.GetProcAddress = GLX_eglGetProcAddress; GLX_drv->Base.Name = "GLX"; GLX_drv->Base.Unload = GLX_Unload; return &GLX_drv->Base; }