Skip to content
Snippets Groups Projects
opengl.c 105 KiB
Newer Older
Lionel Ulmer's avatar
Lionel Ulmer committed
 * X11DRV OpenGL functions
 *
 * Copyright 2000 Lionel Ulmer
 * Copyright 2005 Alex Woods
 * Copyright 2005 Raphael Junqueira
 * Copyright 2006 Roderick Colenbrander
 * Copyright 2006 Tomas Carnecky
Lionel Ulmer's avatar
Lionel Ulmer committed
 *
 * 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.1 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, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
Lionel Ulmer's avatar
Lionel Ulmer committed
 */

#include "config.h"
#include "wine/port.h"
Lionel Ulmer's avatar
Lionel Ulmer committed
#include <stdlib.h>
#include <string.h>

#include "x11drv.h"
#include "wine/library.h"
WINE_DEFAULT_DEBUG_CHANNEL(wgl);
WINE_DECLARE_DEBUG_CHANNEL(opengl);
#undef APIENTRY
#undef CALLBACK
#undef WINAPI

#ifdef HAVE_GL_GL_H
# include <GL/gl.h>
#endif
#ifdef HAVE_GL_GLX_H
# include <GL/glx.h>
#endif
#ifdef HAVE_GL_GLEXT_H
# include <GL/glext.h>
#endif

#undef APIENTRY
#undef CALLBACK
#undef WINAPI

/* Redefines the constants */
#define CALLBACK    __stdcall
#define WINAPI      __stdcall
#define APIENTRY    WINAPI

WINE_DECLARE_DEBUG_CHANNEL(fps);
typedef struct wine_glcontext {
    HDC hdc;
    XVisualInfo *vis;
    GLXFBConfig fb_conf;
    GLXContext ctx;
    BOOL do_escape;
    X11DRV_PDEVICE *physDev;
    RECT viewport;
    RECT scissor;
    BOOL scissor_enabled;
    struct wine_glcontext *next;
    struct wine_glcontext *prev;
} Wine_GLContext;

typedef struct wine_glpbuffer {
    Drawable   drawable;
    Display*   display;
    int        pixelFormat;
    int        width;
    int        height;
    int*       attribList;
    HDC        hdc;

    int        use_render_texture;
    GLuint     texture_target;
    GLuint     texture_bind_target;
    GLuint     texture;
    int        texture_level;
    HDC        prev_hdc;
    HGLRC      prev_ctx;
    HDC        render_hdc;
    HGLRC      render_ctx;
} Wine_GLPBuffer;

typedef struct wine_glextension {
    const char *extName;
    struct {
        const char *funcName;
        void *funcAddress;
    } extEntryPoints[8];
} WineGLExtension;

struct WineGLInfo {
    const char *glVersion;
    const char *glExtensions;

    int glxVersion[2];

    const char *glxServerVersion;
    const char *glxServerExtensions;

    const char *glxClientVersion;
    const char *glxClientExtensions;

    const char *glxExtensions;

    BOOL glxDirect;
    char wglExtensions[4096];
};

typedef struct wine_glpixelformat {
    int iPixelFormat;
    int fbconfig;
    int fmt_index;
} WineGLPixelFormat;

static Wine_GLContext *context_list;
static struct WineGLInfo WineGLInfo = { 0 };
static int use_render_texture_emulation = 0;
static int use_render_texture_ati = 0;
static int swap_interval = 1;

#define MAX_EXTENSIONS 16
static const WineGLExtension *WineGLExtensionList[MAX_EXTENSIONS];
static int WineGLExtensionListSize;

#define MAX_GLPIXELFORMATS 32
static WineGLPixelFormat WineGLPixelFormatList[MAX_GLPIXELFORMATS];
static int WineGLPixelFormatListSize = 0;

static void X11DRV_WineGL_LoadExtensions(void);
Eric Pouech's avatar
Eric Pouech committed
static void dump_PIXELFORMATDESCRIPTOR(const PIXELFORMATDESCRIPTOR *ppfd) {
  TRACE("  - size / version : %d / %d\n", ppfd->nSize, ppfd->nVersion);
  TRACE("  - dwFlags : ");
#define TEST_AND_DUMP(t,tv) if ((t) & (tv)) TRACE(#tv " ")
Lionel Ulmer's avatar
Lionel Ulmer committed
  TEST_AND_DUMP(ppfd->dwFlags, PFD_DEPTH_DONTCARE);
  TEST_AND_DUMP(ppfd->dwFlags, PFD_DOUBLEBUFFER);
  TEST_AND_DUMP(ppfd->dwFlags, PFD_DOUBLEBUFFER_DONTCARE);
  TEST_AND_DUMP(ppfd->dwFlags, PFD_DRAW_TO_WINDOW);
  TEST_AND_DUMP(ppfd->dwFlags, PFD_DRAW_TO_BITMAP);
  TEST_AND_DUMP(ppfd->dwFlags, PFD_GENERIC_ACCELERATED);
  TEST_AND_DUMP(ppfd->dwFlags, PFD_GENERIC_FORMAT);
  TEST_AND_DUMP(ppfd->dwFlags, PFD_NEED_PALETTE);
  TEST_AND_DUMP(ppfd->dwFlags, PFD_NEED_SYSTEM_PALETTE);
  TEST_AND_DUMP(ppfd->dwFlags, PFD_STEREO);
  TEST_AND_DUMP(ppfd->dwFlags, PFD_STEREO_DONTCARE);
  TEST_AND_DUMP(ppfd->dwFlags, PFD_SUPPORT_GDI);
  TEST_AND_DUMP(ppfd->dwFlags, PFD_SUPPORT_OPENGL);
  TEST_AND_DUMP(ppfd->dwFlags, PFD_SWAP_COPY);
  TEST_AND_DUMP(ppfd->dwFlags, PFD_SWAP_EXCHANGE);
  TEST_AND_DUMP(ppfd->dwFlags, PFD_SWAP_LAYER_BUFFERS);
#undef TEST_AND_DUMP
  TRACE("  - iPixelType : ");
Lionel Ulmer's avatar
Lionel Ulmer committed
  switch (ppfd->iPixelType) {
  case PFD_TYPE_RGBA: TRACE("PFD_TYPE_RGBA"); break;
  case PFD_TYPE_COLORINDEX: TRACE("PFD_TYPE_COLORINDEX"); break;
  TRACE("  - Color   : %d\n", ppfd->cColorBits);
  TRACE("  - Red     : %d\n", ppfd->cRedBits);
  TRACE("  - Green   : %d\n", ppfd->cGreenBits);
  TRACE("  - Blue    : %d\n", ppfd->cBlueBits);
  TRACE("  - Alpha   : %d\n", ppfd->cAlphaBits);
  TRACE("  - Accum   : %d\n", ppfd->cAccumBits);
  TRACE("  - Depth   : %d\n", ppfd->cDepthBits);
  TRACE("  - Stencil : %d\n", ppfd->cStencilBits);
  TRACE("  - Aux     : %d\n", ppfd->cAuxBuffers);
  TRACE("  - iLayerType : ");
Lionel Ulmer's avatar
Lionel Ulmer committed
  switch (ppfd->iLayerType) {
  case PFD_MAIN_PLANE: TRACE("PFD_MAIN_PLANE"); break;
  case PFD_OVERLAY_PLANE: TRACE("PFD_OVERLAY_PLANE"); break;
  case (BYTE)PFD_UNDERLAY_PLANE: TRACE("PFD_UNDERLAY_PLANE"); break;
#define PUSH1(attribs,att)        do { attribs[nAttribs++] = (att); } while (0)
#define PUSH2(attribs,att,value)  do { attribs[nAttribs++] = (att); attribs[nAttribs++] = (value); } while(0)

#define MAKE_FUNCPTR(f) static typeof(f) * p##f;
MAKE_FUNCPTR(glXChooseVisual)
MAKE_FUNCPTR(glXCreateContext)
MAKE_FUNCPTR(glXCreateGLXPixmap)
MAKE_FUNCPTR(glXGetCurrentContext)
MAKE_FUNCPTR(glXDestroyContext)
MAKE_FUNCPTR(glXDestroyGLXPixmap)
MAKE_FUNCPTR(glXGetConfig)
MAKE_FUNCPTR(glXIsDirect)
MAKE_FUNCPTR(glXMakeCurrent)
MAKE_FUNCPTR(glXSwapBuffers)
MAKE_FUNCPTR(glXQueryExtension)
MAKE_FUNCPTR(glXQueryVersion)
MAKE_FUNCPTR(glXUseXFont)

/* GLX 1.1 */
MAKE_FUNCPTR(glXGetClientString)
MAKE_FUNCPTR(glXQueryExtensionsString)
MAKE_FUNCPTR(glXQueryServerString)
MAKE_FUNCPTR(glXGetFBConfigs)
MAKE_FUNCPTR(glXChooseFBConfig)
MAKE_FUNCPTR(glXCreatePbuffer)
MAKE_FUNCPTR(glXDestroyPbuffer)
MAKE_FUNCPTR(glXGetFBConfigAttrib)
MAKE_FUNCPTR(glXGetVisualFromFBConfig)
MAKE_FUNCPTR(glXMakeContextCurrent)
MAKE_FUNCPTR(glXQueryDrawable)
MAKE_FUNCPTR(glXGetCurrentReadDrawable)
/* GLX Extensions */
static void* (*pglXGetProcAddressARB)(const GLubyte *);
static BOOL  (*pglXBindTexImageARB)(Display *dpy, GLXPbuffer pbuffer, int buffer);
static BOOL  (*pglXReleaseTexImageARB)(Display *dpy, GLXPbuffer pbuffer, int buffer);
static BOOL  (*pglXDrawableAttribARB)(Display *dpy, GLXDrawable draw, const int *attribList);
static int   (*pglXSwapIntervalSGI)(int);

/* NV GLX Extension */
static void* (*pglXAllocateMemoryNV)(GLsizei size, GLfloat readfreq, GLfloat writefreq, GLfloat priority);
static void  (*pglXFreeMemoryNV)(GLvoid *pointer);

/* Standard OpenGL */
MAKE_FUNCPTR(glBindTexture)
MAKE_FUNCPTR(glBitmap)
MAKE_FUNCPTR(glCopyTexSubImage1D)
MAKE_FUNCPTR(glCopyTexSubImage2D)
MAKE_FUNCPTR(glDrawBuffer)
MAKE_FUNCPTR(glEndList)
MAKE_FUNCPTR(glGetError)
MAKE_FUNCPTR(glGetIntegerv)
MAKE_FUNCPTR(glGetString)
MAKE_FUNCPTR(glNewList)
MAKE_FUNCPTR(glPixelStorei)
MAKE_FUNCPTR(glScissor)
MAKE_FUNCPTR(glViewport)
static BOOL X11DRV_WineGL_InitOpenglInfo(void)
{
    static BOOL infoInitialized = FALSE;

    int screen = DefaultScreen(gdi_display);
    Window win = RootWindow(gdi_display, screen);
    Visual *visual;
    XVisualInfo template;
    XVisualInfo *vis;
    int num;
    GLXContext ctx = NULL;

    if (infoInitialized)
        return TRUE;
    infoInitialized = TRUE;

    wine_tsx11_lock();

    visual = DefaultVisual(gdi_display, screen);
    template.visualid = XVisualIDFromVisual(visual);
    vis = XGetVisualInfo(gdi_display, VisualIDMask, &template, &num);
    if (vis) {
        /* Create a GLX Context. Without one we can't query GL information */
        ctx = pglXCreateContext(gdi_display, vis, None, GL_TRUE);
        if (wine_get_fs() != old_fs)
        {
            wine_set_fs( old_fs );
            wine_tsx11_unlock();
            ERR( "%%fs register corrupted, probably broken ATI driver, disabling OpenGL.\n" );
            ERR( "You need to set the \"UseFastTls\" option to \"2\" in your X config file.\n" );
            return FALSE;
        }
    }

    if (ctx) {
        pglXMakeCurrent(gdi_display, win, ctx);
    } else {
        ERR(" couldn't initialize OpenGL, expect problems\n");
        wine_tsx11_unlock();
        return FALSE;
    }

    WineGLInfo.glVersion = (const char *) pglGetString(GL_VERSION);
    WineGLInfo.glExtensions = strdup((const char *) pglGetString(GL_EXTENSIONS));

    /* Get the common GLX version supported by GLX client and server ( major/minor) */
    pglXQueryVersion(gdi_display, &WineGLInfo.glxVersion[0], &WineGLInfo.glxVersion[1]);

    WineGLInfo.glxServerVersion = pglXQueryServerString(gdi_display, screen, GLX_VERSION);
    WineGLInfo.glxServerExtensions = pglXQueryServerString(gdi_display, screen, GLX_EXTENSIONS);

    WineGLInfo.glxClientVersion = pglXGetClientString(gdi_display, GLX_VERSION);
    WineGLInfo.glxClientExtensions = pglXGetClientString(gdi_display, GLX_EXTENSIONS);

    WineGLInfo.glxExtensions = pglXQueryExtensionsString(gdi_display, screen);
    WineGLInfo.glxDirect = pglXIsDirect(gdi_display, ctx);

    TRACE("GL version             : %s.\n", WineGLInfo.glVersion);
    TRACE("GLX version            : %d.%d.\n", WineGLInfo.glxVersion[0], WineGLInfo.glxVersion[1]);
    TRACE("Server GLX version     : %s.\n", WineGLInfo.glxServerVersion);
    TRACE("Client GLX version     : %s.\n", WineGLInfo.glxClientVersion);
    TRACE("Direct rendering enabled: %s\n", WineGLInfo.glxDirect ? "True" : "False");

    if(vis) XFree(vis);
    if(ctx) {
        pglXMakeCurrent(gdi_display, None, NULL);    
        pglXDestroyContext(gdi_display, ctx);
    }
    wine_tsx11_unlock();
static BOOL has_opengl(void)
{
    static int init_done;
    static void *opengl_handle;
    const char *glx_extensions;
    int error_base, event_base;

    if (init_done) return (opengl_handle != NULL);
    init_done = 1;

    /* No need to load any other libraries as according to the ABI, libGL should be self-sufficient
       and include all dependencies */
    opengl_handle = wine_dlopen(SONAME_LIBGL, RTLD_NOW|RTLD_GLOBAL, NULL, 0);
    if (opengl_handle == NULL) return FALSE;
    pglXGetProcAddressARB = wine_dlsym(opengl_handle, "glXGetProcAddressARB", NULL, 0);
    if (pglXGetProcAddressARB == NULL) {
        ERR("could not find glXGetProcAddressARB in libGL.\n");
        return FALSE;
    }

#define LOAD_FUNCPTR(f) if((p##f = (void*)pglXGetProcAddressARB((const unsigned char*)#f)) == NULL) goto sym_not_found;
LOAD_FUNCPTR(glXChooseVisual)
LOAD_FUNCPTR(glXCreateContext)
LOAD_FUNCPTR(glXCreateGLXPixmap)
LOAD_FUNCPTR(glXGetCurrentContext)
LOAD_FUNCPTR(glXDestroyContext)
LOAD_FUNCPTR(glXDestroyGLXPixmap)
LOAD_FUNCPTR(glXGetConfig)
LOAD_FUNCPTR(glXIsDirect)
LOAD_FUNCPTR(glXMakeCurrent)
LOAD_FUNCPTR(glXSwapBuffers)
LOAD_FUNCPTR(glXQueryExtension)
LOAD_FUNCPTR(glXQueryVersion)
LOAD_FUNCPTR(glXUseXFont)

/* GLX 1.1 */
LOAD_FUNCPTR(glXGetClientString)
LOAD_FUNCPTR(glXQueryExtensionsString)
LOAD_FUNCPTR(glXQueryServerString)
/* GLX 1.3 */
LOAD_FUNCPTR(glXCreatePbuffer)
LOAD_FUNCPTR(glXDestroyPbuffer)
LOAD_FUNCPTR(glXMakeContextCurrent)
LOAD_FUNCPTR(glXGetCurrentReadDrawable)
LOAD_FUNCPTR(glXGetFBConfigs)
/* Standard OpenGL calls */
LOAD_FUNCPTR(glBindTexture)
LOAD_FUNCPTR(glBitmap)
LOAD_FUNCPTR(glCopyTexSubImage1D)
LOAD_FUNCPTR(glCopyTexSubImage2D)
LOAD_FUNCPTR(glDrawBuffer)
LOAD_FUNCPTR(glEnable)
LOAD_FUNCPTR(glEndList)
LOAD_FUNCPTR(glGetError)
LOAD_FUNCPTR(glGetIntegerv)
LOAD_FUNCPTR(glGetString)
LOAD_FUNCPTR(glNewList)
LOAD_FUNCPTR(glPixelStorei)
LOAD_FUNCPTR(glScissor)
LOAD_FUNCPTR(glViewport)
#undef LOAD_FUNCPTR

/* It doesn't matter if these fail. They'll only be used if the driver reports
   the associated extension is available (and if a driver reports the extension
   is available but fails to provide the functions, it's quite broken) */
#define LOAD_FUNCPTR(f) p##f = (void*)pglXGetProcAddressARB((const unsigned char*)#f);
/* NV GLX Extension */
LOAD_FUNCPTR(glXAllocateMemoryNV)
LOAD_FUNCPTR(glXFreeMemoryNV)
    if(!X11DRV_WineGL_InitOpenglInfo()) {
        wine_dlclose(opengl_handle, NULL, 0);
        opengl_handle = NULL;
        return FALSE;
    }

    wine_tsx11_lock();
    if (pglXQueryExtension(gdi_display, &error_base, &event_base) == True) {
        TRACE("GLX is up and running error_base = %d\n", error_base);
    } else {
        wine_dlclose(opengl_handle, NULL, 0);
    /* In case of GLX you have direct and indirect rendering. Most of the time direct rendering is used
     * as in general only that is hardware accelerated. In some cases like in case of remote X indirect
     * rendering is used.
     *
     * The main problem for our OpenGL code is that we need certain GLX calls but their presence
     * depends on the reported GLX client / server version and on the client / server extension list.
     * Those don't have to be the same.
     *
     * In general the server GLX information should be used in case of indirect rendering. When direct
     * rendering is used, the OpenGL client library is responsible for which GLX calls are available.
     * Nvidia's OpenGL drivers are the best in terms of GLX features. At the moment of writing their
     * 8762 drivers support 1.3 for the server and 1.4 for the client and they support lots of extensions.
     * Unfortunately it is much more complicated for Mesa/DRI-based drivers and ATI's drivers.
     * Both sets of drivers report a server version of 1.2 and the client version can be 1.3 or 1.4.
     * Further, in case of at least ATI's drivers, one crucial extension needed for our pixel format code
     * is only available in the list of server extensions and not in the client list.
     *
     * The versioning checks below try to take into account the comments from above.
     */

    /* Depending on the use of direct or indirect rendering we need either the list of extensions
     * exported by the client or by the server.
     */
    if(WineGLInfo.glxDirect)
        glx_extensions = WineGLInfo.glxClientExtensions;
    else
        glx_extensions = WineGLInfo.glxServerExtensions;

    /* Based on the default opengl context we decide whether direct or indirect rendering is used.
     * In case of indirect rendering we check if the GLX version of the server is 1.2 and else
     * the client version is checked.
     */
    if ((!WineGLInfo.glxDirect && !strcmp("1.2", WineGLInfo.glxServerVersion)) ||
        (WineGLInfo.glxDirect && !strcmp("1.2", WineGLInfo.glxClientVersion)))
    {
        if (NULL != strstr(glx_extensions, "GLX_SGIX_fbconfig")) {
            pglXChooseFBConfig = (void*)pglXGetProcAddressARB((const GLubyte *) "glXChooseFBConfigSGIX");
            pglXGetFBConfigAttrib = (void*)pglXGetProcAddressARB((const GLubyte *) "glXGetFBConfigAttribSGIX");
            pglXGetVisualFromFBConfig = (void*)pglXGetProcAddressARB((const GLubyte *) "glXGetVisualFromFBConfigSGIX");
        } else {
            ERR(" glx_version is %s and GLX_SGIX_fbconfig extension is unsupported. Expect problems.\n", WineGLInfo.glxClientVersion);
    } else {
        pglXChooseFBConfig = (void*)pglXGetProcAddressARB((const GLubyte *) "glXChooseFBConfig");
        pglXGetFBConfigAttrib = (void*)pglXGetProcAddressARB((const GLubyte *) "glXGetFBConfigAttrib");
        pglXGetVisualFromFBConfig = (void*)pglXGetProcAddressARB((const GLubyte *) "glXGetVisualFromFBConfig");
    /* The mesa libGL client library seems to forward glXQueryDrawable to the Xserver, so only
     * enable this function when the Xserver understand GLX 1.3 or newer
     */
    if (!strcmp("1.2", WineGLInfo.glxServerVersion))
        pglXQueryDrawable = NULL;
    else
        pglXQueryDrawable = wine_dlsym(RTLD_DEFAULT, "glXQueryDrawable", NULL, 0);

    if (NULL != strstr(glx_extensions, "GLX_ATI_render_texture")) {
        pglXBindTexImageARB = (void*)pglXGetProcAddressARB((const GLubyte *) "glXBindTexImageARB");
        pglXReleaseTexImageARB = (void*)pglXGetProcAddressARB((const GLubyte *) "glXReleaseTexImageARB");
        pglXDrawableAttribARB = (void*)pglXGetProcAddressARB((const GLubyte *) "glXDrawableAttribARB");
    }

    X11DRV_WineGL_LoadExtensions();

    wine_tsx11_unlock();
    return (opengl_handle != NULL);

sym_not_found:
    wine_dlclose(opengl_handle, NULL, 0);
    opengl_handle = NULL;
static inline Wine_GLContext *alloc_context(void)
{
    Wine_GLContext *ret;

    if ((ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(Wine_GLContext))))
    {
        ret->next = context_list;
        if (context_list) context_list->prev = ret;
        context_list = ret;
    }
    return ret;
}

static inline void free_context(Wine_GLContext *context)
{
    if (context->next != NULL) context->next->prev = context->prev;
    if (context->prev != NULL) context->prev->next = context->next;
    else context_list = context->next;

    HeapFree(GetProcessHeap(), 0, context);
}

static inline Wine_GLContext *get_context_from_GLXContext(GLXContext ctx)
{
    Wine_GLContext *ret;
    if (!ctx) return NULL;
    for (ret = context_list; ret; ret = ret->next) if (ctx == ret->ctx) break;
    return ret;
}

/**
 * get_hdc_from_Drawable (internal)
 *
 * For use by wglGetCurrentReadDCARB.
 */
static inline HDC get_hdc_from_Drawable(GLXDrawable d)
{
    Wine_GLContext *ret;
    for (ret = context_list; ret; ret = ret->next) {
        if (d == ret->physDev->drawable) {
static inline BOOL is_valid_context( Wine_GLContext *ctx )
{
    Wine_GLContext *ptr;
    for (ptr = context_list; ptr; ptr = ptr->next) if (ptr == ctx) break;
    return (ptr != NULL);
}

static int describeContext(Wine_GLContext* ctx) {
    int tmp;
    int ctx_vis_id;
    TRACE(" Context %p have (vis:%p):\n", ctx, ctx->vis);
    pglXGetFBConfigAttrib(gdi_display, ctx->fb_conf, GLX_FBCONFIG_ID, &tmp);
    TRACE(" - FBCONFIG_ID 0x%x\n", tmp);
    pglXGetFBConfigAttrib(gdi_display, ctx->fb_conf, GLX_VISUAL_ID, &tmp);
    TRACE(" - VISUAL_ID 0x%x\n", tmp);
    ctx_vis_id = tmp;
    return ctx_vis_id;
}

static int describeDrawable(Wine_GLContext* ctx, Drawable drawable) {
    int tmp;
    int nElements;
    int attribList[3] = { GLX_FBCONFIG_ID, 0, None };
    GLXFBConfig *fbCfgs;

    if (pglXQueryDrawable == NULL)  {
        /** glXQueryDrawable not available so returns not supported */
        return -1;
    }

    TRACE(" Drawable %p have :\n", (void*) drawable);
    pglXQueryDrawable(gdi_display, drawable, GLX_WIDTH, (unsigned int*) &tmp);
    TRACE(" - WIDTH as %d\n", tmp);
    pglXQueryDrawable(gdi_display, drawable, GLX_HEIGHT, (unsigned int*) &tmp);
    TRACE(" - HEIGHT as %d\n", tmp);
    pglXQueryDrawable(gdi_display, drawable, GLX_FBCONFIG_ID, (unsigned int*) &tmp);
    TRACE(" - FBCONFIG_ID as 0x%x\n", tmp);

    attribList[1] = tmp;
    fbCfgs = pglXChooseFBConfig(gdi_display, DefaultScreen(gdi_display), attribList, &nElements);
    pglXGetFBConfigAttrib(gdi_display, fbCfgs[0], GLX_VISUAL_ID, &tmp);
    TRACE(" - VISUAL_ID as 0x%x\n", tmp);

    XFree(fbCfgs);

    return tmp;
}

static int ConvertAttribWGLtoGLX(const int* iWGLAttr, int* oGLXAttr, Wine_GLPBuffer* pbuf) {
  int nAttribs = 0;
  unsigned cur = 0; 
  int pop;
  int drawattrib = 0;
  int isColor = 0;
  int wantColorBits = 0;
  int sz_alpha = 0;

  /* The list of WGL attributes is allowed to be NULL, so don't return -1 (error) but just 0 */
  if(iWGLAttr == NULL)
    return 0;

  while (0 != iWGLAttr[cur]) {
    TRACE("pAttr[%d] = %x\n", cur, iWGLAttr[cur]);

    switch (iWGLAttr[cur]) {
    case WGL_COLOR_BITS_ARB:
      pop = iWGLAttr[++cur];
      wantColorBits = pop; /** see end */
      break;
    case WGL_BLUE_BITS_ARB:
      pop = iWGLAttr[++cur];
      PUSH2(oGLXAttr, GLX_BLUE_SIZE, pop);
      TRACE("pAttr[%d] = GLX_BLUE_SIZE: %d\n", cur, pop);
      break;
    case WGL_RED_BITS_ARB:
      pop = iWGLAttr[++cur];
      PUSH2(oGLXAttr, GLX_RED_SIZE, pop);
      TRACE("pAttr[%d] = GLX_RED_SIZE: %d\n", cur, pop);
      break;
    case WGL_GREEN_BITS_ARB:
      pop = iWGLAttr[++cur];
      PUSH2(oGLXAttr, GLX_GREEN_SIZE, pop);
      TRACE("pAttr[%d] = GLX_GREEN_SIZE: %d\n", cur, pop);
      break;
    case WGL_ALPHA_BITS_ARB:
      pop = iWGLAttr[++cur];
      sz_alpha = pop;
      PUSH2(oGLXAttr, GLX_ALPHA_SIZE, pop);
      TRACE("pAttr[%d] = GLX_ALPHA_SIZE: %d\n", cur, pop);
      break;
    case WGL_DEPTH_BITS_ARB:
      pop = iWGLAttr[++cur];
      PUSH2(oGLXAttr, GLX_DEPTH_SIZE, pop);
      TRACE("pAttr[%d] = GLX_DEPTH_SIZE: %d\n", cur, pop);
      break;
    case WGL_STENCIL_BITS_ARB:
      pop = iWGLAttr[++cur];
      PUSH2(oGLXAttr, GLX_STENCIL_SIZE, pop);
      TRACE("pAttr[%d] = GLX_STENCIL_SIZE: %d\n", cur, pop);
      break;
    case WGL_DOUBLE_BUFFER_ARB:
      pop = iWGLAttr[++cur];
      PUSH2(oGLXAttr, GLX_DOUBLEBUFFER, pop);
      TRACE("pAttr[%d] = GLX_DOUBLEBUFFER: %d\n", cur, pop);
      break;

    case WGL_PIXEL_TYPE_ARB:
      pop = iWGLAttr[++cur];
      switch (pop) {
      case WGL_TYPE_COLORINDEX_ARB: pop = GLX_COLOR_INDEX_BIT; isColor = 1; break ;
      case WGL_TYPE_RGBA_ARB: pop = GLX_RGBA_BIT; break ;
      case WGL_TYPE_RGBA_FLOAT_ATI: pop = GLX_RGBA_FLOAT_ATI_BIT; break ;
      default:
        ERR("unexpected PixelType(%x)\n", pop);	
        pop = 0;
      }
      PUSH2(oGLXAttr, GLX_RENDER_TYPE, pop);
      TRACE("pAttr[%d] = GLX_RENDER_TYPE: %d\n", cur, pop);
      break;

    case WGL_SUPPORT_GDI_ARB:
      pop = iWGLAttr[++cur];
      /* We only support a limited number of formats which are all renderable by X (similar to GDI).
       * Ignore this attribute to prevent us from not finding a match due to the limited
       * amount of formats supported right now. This option could be matched to GLX_X_RENDERABLE
       * but the issue is that when a program asks for no GDI support, there's no format we can return
       * as all our supported formats are renderable by X.
       */
      TRACE("pAttr[%d] = WGL_SUPPORT_GDI_ARB: %d\n", cur, pop);
      break;

    case WGL_DRAW_TO_BITMAP_ARB:
      pop = iWGLAttr[++cur];
      TRACE("pAttr[%d] = WGL_DRAW_TO_BITMAP_ARB: %d\n", cur, pop);
      /* GLX_DRAWABLE_TYPE flags need to be OR'd together. See below. */
        drawattrib |= GLX_PIXMAP_BIT;
      }
      break;

    case WGL_DRAW_TO_WINDOW_ARB:
      pop = iWGLAttr[++cur];
      TRACE("pAttr[%d] = WGL_DRAW_TO_WINDOW_ARB: %d\n", cur, pop);
      /* GLX_DRAWABLE_TYPE flags need to be OR'd together. See below. */
        drawattrib |= GLX_WINDOW_BIT;
      }
      break;

    case WGL_DRAW_TO_PBUFFER_ARB:
      pop = iWGLAttr[++cur];
      TRACE("pAttr[%d] = WGL_DRAW_TO_PBUFFER_ARB: %d\n", cur, pop);
      /* GLX_DRAWABLE_TYPE flags need to be OR'd together. See below. */
        drawattrib |= GLX_PBUFFER_BIT;
      }
      break;

    case WGL_ACCELERATION_ARB:
    case WGL_SUPPORT_OPENGL_ARB:
      pop = iWGLAttr[++cur];
      /** nothing to do, if we are here, supposing support Accelerated OpenGL */
      TRACE("pAttr[%d] = WGL_SUPPORT_OPENGL_ARB: %d\n", cur, pop);
      break;

    case WGL_PBUFFER_LARGEST_ARB:
      pop = iWGLAttr[++cur];
      PUSH2(oGLXAttr, GLX_LARGEST_PBUFFER, pop);
      TRACE("pAttr[%d] = GLX_LARGEST_PBUFFER: %x\n", cur, pop);
      break;

    case WGL_SAMPLE_BUFFERS_ARB:
      pop = iWGLAttr[++cur];
      PUSH2(oGLXAttr, GLX_SAMPLE_BUFFERS_ARB, pop);
      TRACE("pAttr[%d] = GLX_SAMPLE_BUFFERS_ARB: %x\n", cur, pop);
      break;

    case WGL_SAMPLES_ARB:
      pop = iWGLAttr[++cur];
      PUSH2(oGLXAttr, GLX_SAMPLES_ARB, pop);
      TRACE("pAttr[%d] = GLX_SAMPLES_ARB: %x\n", cur, pop);
      break;

    case WGL_TEXTURE_FORMAT_ARB:
    case WGL_TEXTURE_TARGET_ARB:
    case WGL_MIPMAP_TEXTURE_ARB:
      TRACE("WGL_render_texture Attributes: %x as %x\n", iWGLAttr[cur], iWGLAttr[cur + 1]);
      pop = iWGLAttr[++cur];
      if (NULL == pbuf) {
        ERR("trying to use GLX_Pbuffer Attributes without Pbuffer (was %x)\n", iWGLAttr[cur]);
      }
      if (use_render_texture_ati) {
        /** nothing to do here */
      }
      else if (!use_render_texture_emulation) {
        if (WGL_NO_TEXTURE_ARB != pop) {
          ERR("trying to use WGL_render_texture Attributes without support (was %x)\n", iWGLAttr[cur]);
          return -1; /** error: don't support it */
        } else {
          PUSH2(oGLXAttr, GLX_X_RENDERABLE, pop);
          drawattrib |= GLX_PBUFFER_BIT;
        }
      }
      break ;

    case WGL_BIND_TO_TEXTURE_RGB_ARB:
    case WGL_BIND_TO_TEXTURE_RGBA_ARB:
      pop = iWGLAttr[++cur];
      /** cannot be converted, see direct handling on 
       *   - wglGetPixelFormatAttribivARB
       *  TODO: wglChoosePixelFormat
       */
      break ;

    default:
      FIXME("unsupported %x WGL Attribute\n", iWGLAttr[cur]);
      break;
    }
    ++cur;
  }

  /**
   * Trick as WGL_COLOR_BITS_ARB != GLX_BUFFER_SIZE
   *    WGL_COLOR_BITS_ARB + WGL_ALPHA_BITS_ARB == GLX_BUFFER_SIZE
   *
   *  WGL_COLOR_BITS_ARB
   *     The number of color bitplanes in each color buffer. For RGBA
   *     pixel types, it is the size of the color buffer, excluding the
   *     alpha bitplanes. For color-index pixels, it is the size of the
   *     color index buffer.
   *
   *  GLX_BUFFER_SIZE   
   *     This attribute defines the number of bits per color buffer. 
   *     For GLX FBConfigs that correspond to a PseudoColor or StaticColor visual, 
   *     this is equal to the depth value reported in the X11 visual. 
   *     For GLX FBConfigs that correspond to TrueColor or DirectColor visual, 
   *     this is the sum of GLX_RED_SIZE, GLX_GREEN_SIZE, GLX_BLUE_SIZE, and GLX_ALPHA_SIZE.
   * 
   */
  if (0 < wantColorBits) {
    if (!isColor) { 
      wantColorBits += sz_alpha; 
    }
    if (32 < wantColorBits) {
      ERR("buggy %d GLX_BUFFER_SIZE default to 32\n", wantColorBits);
      wantColorBits = 32;
    }
    PUSH2(oGLXAttr, GLX_BUFFER_SIZE, wantColorBits);
    TRACE("pAttr[%d] = WGL_COLOR_BITS_ARB: %d\n", cur, wantColorBits);
  }

  /* Apply the OR'd drawable type bitmask now. */
  if (drawattrib) {
    PUSH2(oGLXAttr, GLX_DRAWABLE_TYPE, drawattrib);
    TRACE("pAttr[?] = GLX_DRAWABLE_TYPE: %#x\n", drawattrib);
  }

static BOOL get_fbconfig_from_visualid(Display *display, Visual *visual, int *fmt_id, int *fmt_index)
{
    GLXFBConfig* cfgs = NULL;
    int i;
    int nCfgs;
    int tmp_fmt_id;
    int tmp_vis_id;
    VisualID visualid;

    if(!display || !visual) {
        ERR("Invalid display or visual\n");
	return FALSE;
    }
    visualid = XVisualIDFromVisual(visual);

    /* Get a list of all available framebuffer configurations */
    cfgs = pglXGetFBConfigs(display, DefaultScreen(display), &nCfgs);
    if (NULL == cfgs || 0 == nCfgs) {
        ERR("glXChooseFBConfig returns NULL\n");
        if(cfgs != NULL) XFree(cfgs);
        return FALSE;
    }

    /* Find the requested offscreen format and count the number of offscreen formats */
    for(i=0; i<nCfgs; i++) {
        pglXGetFBConfigAttrib(display, cfgs[i], GLX_VISUAL_ID, &tmp_vis_id);
        pglXGetFBConfigAttrib(display, cfgs[i], GLX_FBCONFIG_ID, &tmp_fmt_id);

        /* We are looking up the GLX index of our main visual and have found it :) */
        if(visualid == tmp_vis_id) {
            TRACE("Found FBCONFIG_ID 0x%x at index %d for VISUAL_ID 0x%x\n", tmp_fmt_id, i, tmp_vis_id);
            XFree(cfgs);
            *fmt_id = tmp_fmt_id;
            *fmt_index = i;
            return TRUE;
        }
    }

    ERR("No fbconfig found for Wine's main visual (0x%lx), expect problems!\n", visualid);
    XFree(cfgs);
    return FALSE;
}

static BOOL init_formats(Display *display, int screen, Visual *visual)
{
    int fmt_id, fmt_index;

    /* Locate the fbconfig correspondig to our main visual */
    if(!get_fbconfig_from_visualid(display, visual, &fmt_id, &fmt_index)) {
        ERR("Can't get the FBCONFIG_ID for the main visual, expect problems!\n");
        return FALSE;
    }

    /* Put Wine's internal format at the first index */
    WineGLPixelFormatList[0].iPixelFormat = 1;
    WineGLPixelFormatList[0].fbconfig = fmt_id;
    WineGLPixelFormatList[0].fmt_index = fmt_index;
    WineGLPixelFormatListSize = 1;

    /* In the future test for compatible formats here */

    return TRUE;
}

/* GLX can advertise dozens of different pixelformats including offscreen and onscreen ones.
 * In our WGL implementation we only support a subset of these formats namely the format of
 * Wine's main visual and offscreen formats (if they are available).
 * This function converts a WGL format to its corresponding GLX one. It returns the index (zero-based)
 * into the GLX FB config table and it returns the number of supported WGL formats in fmt_count.
 */
static BOOL ConvertPixelFormatWGLtoGLX(Display *display, int iPixelFormat, int *fmt_index, int *fmt_count)
    /* Init the list of pixel formats when we need it */
    if(!WineGLPixelFormatListSize)
        init_formats(display, DefaultScreen(display), visual);
    if((iPixelFormat <= 0) || (iPixelFormat > WineGLPixelFormatListSize)) {
        ERR("invalid iPixelFormat %d\n", iPixelFormat);
        ret = FALSE;
        *fmt_index = -1;
    }
    else {
        ret = TRUE;
        *fmt_index = WineGLPixelFormatList[iPixelFormat-1].fmt_index;
    *fmt_count = WineGLPixelFormatListSize;
    TRACE("Returning fmt_index=%d, fmt_count=%d for iPixelFormat=%d\n", *fmt_index, *fmt_count, iPixelFormat);

}

/* Search our internal pixelformat list for the WGL format corresponding to the given fbconfig */
static int ConvertPixelFormatGLXtoWGL(Display *display, int fbconfig)
{
    int i;
    /* Init the list of pixel formats when we need it */
    if(!WineGLPixelFormatListSize)
        init_formats(display, DefaultScreen(display), visual);
    for(i=0; i<WineGLPixelFormatListSize; i++) {
        if(WineGLPixelFormatList[i].fbconfig == fbconfig) {
            TRACE("Returning iPixelFormat %d for fbconfig 0x%x\n", WineGLPixelFormatList[i].iPixelFormat, fbconfig);
            return WineGLPixelFormatList[i].iPixelFormat;
        }
    }
    TRACE("No compatible format found for FBCONFIG_ID=0x%x\n", fbconfig);
    return 0;
 */
int X11DRV_ChoosePixelFormat(X11DRV_PDEVICE *physDev, 
Lionel Ulmer's avatar
Lionel Ulmer committed
			     const PIXELFORMATDESCRIPTOR *ppfd) {
  GLXFBConfig* cfgs = NULL;
  int ret = 0;
  int fmt_index = 0;
    ERR("No libGL on this box - disabling OpenGL support !\n");
    return 0;
  }
Lionel Ulmer's avatar
Lionel Ulmer committed
  if (TRACE_ON(opengl)) {
    TRACE("(%p,%p)\n", physDev, ppfd);
Eric Pouech's avatar
Eric Pouech committed
    dump_PIXELFORMATDESCRIPTOR((const PIXELFORMATDESCRIPTOR *) ppfd);
  wine_tsx11_lock(); 
  if(!visual) {
    ERR("Can't get an opengl visual!\n");
    goto choose_exit;

  /* Get a list containing all supported FB configurations */
  cfgs = pglXGetFBConfigs(gdi_display, DefaultScreen(gdi_display), &nCfgs);
  if (NULL == cfgs || 0 == nCfgs) {
    ERR("glXGetFBConfigs returns NULL (glError: %d)\n", pglGetError());
    goto choose_exit;

  /* In case an fbconfig was found, check if it matches to the requirements of the ppfd */
  if(!ConvertPixelFormatWGLtoGLX(gdi_display, 1 /* main visual */, &fmt_index, &value)) {
    ERR("Can't find a matching FBCONFIG_ID for VISUAL_ID 0x%lx!\n", visual->visualid);
    int dwFlags = 0;
    int iPixelType = 0;
    int value = 0;

    /* Pixel type */
    pglXGetFBConfigAttrib(gdi_display, cfgs[fmt_index], GLX_RENDER_TYPE, &value);
    if (value & GLX_RGBA_BIT)
      iPixelType = PFD_TYPE_RGBA;
    else
      iPixelType = PFD_TYPE_COLORINDEX;
    if (ppfd->iPixelType != iPixelType) {
    /* Doublebuffer */
    pglXGetFBConfigAttrib(gdi_display, cfgs[fmt_index], GLX_DOUBLEBUFFER, &value); if (value) dwFlags |= PFD_DOUBLEBUFFER;
    if (!(ppfd->dwFlags & PFD_DOUBLEBUFFER_DONTCARE) && (ppfd->dwFlags & PFD_DOUBLEBUFFER)) {
      if (!(dwFlags & PFD_DOUBLEBUFFER)) {
        TRACE("dbl buffer mismatch\n");
    pglXGetFBConfigAttrib(gdi_display, cfgs[fmt_index], GLX_STEREO, &value); if (value) dwFlags |= PFD_STEREO;
    if (!(ppfd->dwFlags & PFD_STEREO_DONTCARE) && (ppfd->dwFlags & PFD_STEREO)) {
      if (!(dwFlags & PFD_STEREO)) {
        TRACE("stereo mismatch\n");