/* $Id: toglWGL.c,v 1.8 2009/12/23 21:50:49 gregcouch Exp $ */ /* vi:set sw=4 expandtab: */ /* * Togl - a Tk OpenGL widget * * Copyright (C) 1996-2002 Brian Paul and Ben Bederson * Copyright (C) 2005-2009 Greg Couch * See the LICENSE file for copyright details. */ /* TODO: fullscreen support */ #include #include #include #include #include #include #ifndef PFD_SUPPORT_COMPOSITION /* for Vista -- not needed because we don't use PFD_SUPPORT_GDI/BITMAP */ # define PFD_SUPPORT_COMPOSITION 0x00008000 #endif /* TODO: move these statics into global structure */ static PFNWGLGETEXTENSIONSSTRINGARBPROC getExtensionsString = NULL; static PFNWGLCHOOSEPIXELFORMATARBPROC choosePixelFormat; static PFNWGLGETPIXELFORMATATTRIBIVARBPROC getPixelFormatAttribiv; static PFNWGLCREATEPBUFFERARBPROC createPbuffer = NULL; static PFNWGLDESTROYPBUFFERARBPROC destroyPbuffer = NULL; static PFNWGLGETPBUFFERDCARBPROC getPbufferDC = NULL; static PFNWGLRELEASEPBUFFERDCARBPROC releasePbufferDC = NULL; static PFNWGLQUERYPBUFFERARBPROC queryPbuffer = NULL; static int hasMultisampling = FALSE; static int hasPbuffer = FALSE; static int hasARBPbuffer = FALSE; static HWND toglCreateTestWindow(HWND parent) { static char ClassName[] = "ToglTestWindow"; WNDCLASS wc; HINSTANCE instance = GetModuleHandle(NULL); HWND wnd; HDC dc; PIXELFORMATDESCRIPTOR pfd; int pixelFormat; wc.style = CS_OWNDC; wc.lpfnWndProc = DefWindowProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = instance; wc.hIcon = LoadIcon(NULL, IDI_WINLOGO); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = NULL; wc.lpszMenuName = NULL; wc.lpszClassName = ClassName; if (!RegisterClass(&wc)) { DWORD err = GetLastError(); if (err != ERROR_CLASS_ALREADY_EXISTS) { fprintf(stderr, "Unable to register Togl Test Window class\n"); return NULL; } } wnd = CreateWindow(ClassName, "test OpenGL capabilities", WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, 0, 0, 1, 1, parent, NULL, instance, NULL); if (wnd == NULL) { fprintf(stderr, "Unable to create temporary OpenGL window\n"); return NULL; } dc = GetDC(wnd); memset(&pfd, 0, sizeof pfd); pfd.nSize = sizeof pfd; pfd.nVersion = 1; pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; pfd.iPixelType = PFD_TYPE_RGBA; pfd.cColorBits = 3; pfd.iLayerType = PFD_MAIN_PLANE; pixelFormat = ChoosePixelFormat(dc, &pfd); if (pixelFormat == 0) { fprintf(stderr, "Unable to choose simple pixel format\n"); ReleaseDC(wnd, dc); return NULL; } if (!SetPixelFormat(dc, pixelFormat, &pfd)) { fprintf(stderr, "Unable to set simple pixel format\n"); ReleaseDC(wnd, dc); return NULL; } ShowWindow(wnd, SW_HIDE); // make sure it's hidden ReleaseDC(wnd, dc); return wnd; } struct FBInfo { int stereo; int acceleration; int colors; int depth; int samples; int pixelFormat; }; typedef struct FBInfo FBInfo; static int FBAttribs[] = { /* must match order in FBInfo structure */ WGL_STEREO_ARB, WGL_ACCELERATION_ARB, WGL_COLOR_BITS_ARB, WGL_DEPTH_BITS_ARB, WGL_SAMPLES_ARB, }; #define NUM_FBAttribs (sizeof FBAttribs / sizeof FBAttribs[0]) static int FBInfoCmp(const void *a, const void *b) { /* * 1. stereo is better * 2. full acceleration is better * 3. greater color bits is better * 4. greater depth bits is better * 5. more multisampling is better */ const FBInfo *x = (const FBInfo *) a; const FBInfo *y = (const FBInfo *) b; if (x->stereo != y->stereo) return y->stereo - x->stereo; if (x->acceleration != y->acceleration) return y->acceleration - x->acceleration; if (x->colors != y->colors) return y->colors - x->colors; if (x->depth != y->depth) return y->depth - x->depth; if (x->samples != y->samples) return y->samples - x->samples; return 0; } static int togl_pixelFormat(Togl *togl, HWND hwnd) { /* return 0 when pixel format is unavailable. */ int pixelformat = 0; static int loadedOpenGL = FALSE; int formats[256]; UINT numFormats; FBInfo *info; UINT i; int attribs[128]; int na = 0; if (!loadedOpenGL) { HWND test = NULL; HDC dc; HGLRC rc; if (wglGetCurrentContext() != NULL) { dc = wglGetCurrentDC(); } else { /* HWND hwnd = Tk_GetHWND(Tk_WindowId(togl->TkWin)); */ test = toglCreateTestWindow(hwnd); if (test == NULL) { Tcl_SetResult(togl->Interp, TCL_STUPID "can't create dummy OpenGL window", TCL_STATIC); return 0; } dc = GetDC(test); rc = wglCreateContext(dc); wglMakeCurrent(dc, rc); } loadedOpenGL = TRUE; /* * Now that we have an OpenGL window, we can initialize all * OpenGL information and figure out if multisampling is supported. */ getExtensionsString = (PFNWGLGETEXTENSIONSSTRINGARBPROC) wglGetProcAddress("wglGetExtensionsStringARB"); if (getExtensionsString == NULL) getExtensionsString = (PFNWGLGETEXTENSIONSSTRINGARBPROC) wglGetProcAddress("wglGetExtensionsStringEXT"); if (getExtensionsString) { const char *extensions = getExtensionsString(dc); if (strstr(extensions, "WGL_ARB_multisample") != NULL || strstr(extensions, "WGL_EXT_multisample") != NULL) hasMultisampling = TRUE; if (strstr(extensions, "WGL_ARB_pixel_format") != NULL) { choosePixelFormat = (PFNWGLCHOOSEPIXELFORMATARBPROC) wglGetProcAddress("wglChoosePixelFormatARB"); getPixelFormatAttribiv = (PFNWGLGETPIXELFORMATATTRIBIVARBPROC) wglGetProcAddress("wglGetPixelFormatAttribivARB"); if (choosePixelFormat == NULL || getPixelFormatAttribiv == NULL) { choosePixelFormat = NULL; getPixelFormatAttribiv = NULL; } } if (choosePixelFormat == NULL && strstr(extensions, "WGL_EXT_pixel_format") != NULL) { choosePixelFormat = (PFNWGLCHOOSEPIXELFORMATARBPROC) wglGetProcAddress("wglChoosePixelFormatEXT"); getPixelFormatAttribiv = (PFNWGLGETPIXELFORMATATTRIBIVARBPROC) wglGetProcAddress("wglGetPixelFormatAttribivEXT"); if (choosePixelFormat == NULL || getPixelFormatAttribiv == NULL) { choosePixelFormat = NULL; getPixelFormatAttribiv = NULL; } } if (createPbuffer == NULL && strstr(extensions, "WGL_ARB_pbuffer") != NULL) { createPbuffer = (PFNWGLCREATEPBUFFERARBPROC) wglGetProcAddress("wglCreatePbufferARB"); destroyPbuffer = (PFNWGLDESTROYPBUFFERARBPROC) wglGetProcAddress("wglDestroyPbufferARB"); getPbufferDC = (PFNWGLGETPBUFFERDCARBPROC) wglGetProcAddress("wglGetPbufferDCARB"); releasePbufferDC = (PFNWGLRELEASEPBUFFERDCARBPROC) wglGetProcAddress("wglReleasePbufferDCARB"); queryPbuffer = (PFNWGLQUERYPBUFFERARBPROC) wglGetProcAddress("wglQueryPbufferARB"); if (createPbuffer == NULL || destroyPbuffer == NULL || getPbufferDC == NULL || releasePbufferDC == NULL || queryPbuffer == NULL) { createPbuffer = NULL; destroyPbuffer = NULL; getPbufferDC = NULL; releasePbufferDC = NULL; queryPbuffer = NULL; } else { hasPbuffer = TRUE; hasARBPbuffer = TRUE; } } if (createPbuffer == NULL && strstr(extensions, "WGL_EXT_pbuffer") != NULL) { createPbuffer = (PFNWGLCREATEPBUFFERARBPROC) wglGetProcAddress("wglCreatePbufferEXT"); destroyPbuffer = (PFNWGLDESTROYPBUFFERARBPROC) wglGetProcAddress("wglDestroyPbufferEXT"); getPbufferDC = (PFNWGLGETPBUFFERDCARBPROC) wglGetProcAddress("wglGetPbufferDCEXT"); releasePbufferDC = (PFNWGLRELEASEPBUFFERDCARBPROC) wglGetProcAddress("wglReleasePbufferDCEXT"); queryPbuffer = (PFNWGLQUERYPBUFFERARBPROC) wglGetProcAddress("wglQueryPbufferEXT"); if (createPbuffer == NULL || destroyPbuffer == NULL || getPbufferDC == NULL || releasePbufferDC == NULL || queryPbuffer == NULL) { createPbuffer = NULL; destroyPbuffer = NULL; getPbufferDC = NULL; releasePbufferDC = NULL; queryPbuffer = NULL; } else { hasPbuffer = TRUE; } } } /* No need to confirm multisampling is in glGetString(GL_EXTENSIONS) * because OpenGL driver is local */ if (test != NULL) { /* cleanup by removing temporary OpenGL window */ wglMakeCurrent(NULL, NULL); wglDeleteContext(rc); ReleaseDC(test, dc); DestroyWindow(test); } } if (togl->MultisampleFlag && !hasMultisampling) { Tcl_SetResult(togl->Interp, TCL_STUPID "multisampling not supported", TCL_STATIC); return 0; } if (togl->PbufferFlag && !hasPbuffer) { Tcl_SetResult(togl->Interp, TCL_STUPID "pbuffers are not supported", TCL_STATIC); return 0; } if (choosePixelFormat == NULL) { PIXELFORMATDESCRIPTOR pfd; /* Don't have the great wglChoosePixelFormatARB() function, so do it * the old way. */ if (togl->MultisampleFlag) { Tcl_SetResult(togl->Interp, TCL_STUPID "multisampling not supported", TCL_STATIC); return 0; } memset(&pfd, 0, sizeof pfd); pfd.nSize = sizeof pfd; pfd.nVersion = 1; pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_SUPPORT_COMPOSITION; if (togl->DoubleFlag) { pfd.dwFlags |= PFD_DOUBLEBUFFER; } if (togl->Stereo == TOGL_STEREO_NATIVE) { pfd.dwFlags |= PFD_STEREO; } pfd.iPixelType = togl->RgbaFlag ? PFD_TYPE_RGBA : PFD_TYPE_COLORINDEX; pfd.cColorBits = togl->RgbaRed + togl->RgbaGreen + togl->RgbaBlue; /* Alpha bitplanes are not supported in the current generic OpenGL * implementation, but may be supported by specific hardware devices. */ pfd.cAlphaBits = togl->AlphaFlag ? togl->AlphaSize : 0; pfd.cAccumBits = togl->AccumFlag ? (togl->AccumRed + togl->AccumGreen + togl->AccumBlue + togl->AccumAlpha) : 0; pfd.cDepthBits = togl->DepthFlag ? togl->DepthSize : 0; pfd.cStencilBits = togl->StencilFlag ? togl->StencilSize : 0; /* Auxiliary buffers are not supported in the current generic OpenGL * implementation, but may be supported by specific hardware devices. */ pfd.cAuxBuffers = togl->AuxNumber; pfd.iLayerType = PFD_MAIN_PLANE; if ((pixelformat = ChoosePixelFormat(togl->tglGLHdc, &pfd)) == 0) { Tcl_SetResult(togl->Interp, TCL_STUPID "couldn't choose pixel format", TCL_STATIC); return 0; } /* double check that we got the stereo format we requested */ if (togl->Stereo == TOGL_STEREO_NATIVE) { DescribePixelFormat(togl->tglGLHdc, pixelformat, sizeof (pfd), &pfd); if ((pfd.dwFlags & PFD_STEREO) == 0) { Tcl_SetResult(togl->Interp, TCL_STUPID "couldn't choose stereo pixel format", TCL_STATIC); return 0; } } return pixelformat; } // We have the new wglChoosePixelFormat!! if (togl->MultisampleFlag && !hasMultisampling) { Tcl_SetResult(togl->Interp, TCL_STUPID "multisampling not supported", TCL_STATIC); return 0; } if (togl->PbufferFlag) attribs[na++] = WGL_DRAW_TO_PBUFFER_ARB; else attribs[na++] = WGL_DRAW_TO_WINDOW_ARB; attribs[na++] = GL_TRUE; attribs[na++] = WGL_SUPPORT_OPENGL_ARB; attribs[na++] = GL_TRUE; attribs[na++] = WGL_PIXEL_TYPE_ARB; if (!togl->RgbaFlag) { attribs[na++] = WGL_TYPE_COLORINDEX_ARB; } else { attribs[na++] = WGL_TYPE_RGBA_ARB; attribs[na++] = WGL_RED_BITS_ARB; attribs[na++] = togl->RgbaRed; attribs[na++] = WGL_GREEN_BITS_ARB; attribs[na++] = togl->RgbaGreen; attribs[na++] = WGL_BLUE_BITS_ARB; attribs[na++] = togl->RgbaBlue; if (togl->AlphaFlag) { attribs[na++] = WGL_ALPHA_BITS_ARB; attribs[na++] = togl->AlphaSize; } } if (togl->DepthFlag) { attribs[na++] = WGL_DEPTH_BITS_ARB; attribs[na++] = togl->DepthSize; } if (togl->DoubleFlag) { attribs[na++] = WGL_DOUBLE_BUFFER_ARB; attribs[na++] = GL_TRUE; } if (togl->StencilFlag) { attribs[na++] = WGL_STENCIL_BITS_ARB; attribs[na++] = togl->StencilSize; } if (togl->AccumFlag) { attribs[na++] = WGL_ACCUM_RED_BITS_ARB; attribs[na++] = togl->AccumRed; attribs[na++] = WGL_ACCUM_GREEN_BITS_ARB; attribs[na++] = togl->AccumGreen; attribs[na++] = WGL_ACCUM_BLUE_BITS_ARB; attribs[na++] = togl->AccumBlue; if (togl->AlphaFlag) { attribs[na++] = WGL_ACCUM_ALPHA_BITS_ARB; attribs[na++] = togl->AccumAlpha; } } if (togl->Stereo == TOGL_STEREO_NATIVE) { attribs[na++] = WGL_STEREO_ARB; attribs[na++] = GL_TRUE; } if (togl->MultisampleFlag) { attribs[na++] = WGL_SAMPLE_BUFFERS_ARB; attribs[na++] = 1; attribs[na++] = WGL_SAMPLES_ARB; attribs[na++] = 2; } if (togl->AuxNumber) { attribs[na++] = WGL_AUX_BUFFERS_ARB; attribs[na++] = togl->AuxNumber; } attribs[na++] = 0; // must be last if (!choosePixelFormat(togl->tglGLHdc, &attribs[0], NULL, 256, formats, &numFormats) || numFormats == 0) { Tcl_SetResult(togl->Interp, TCL_STUPID "couldn't choose pixel format", TCL_STATIC); return 0; } /* * Pick best format */ info = (FBInfo *) malloc(numFormats * sizeof (FBInfo)); for (i = 0; i != numFormats; ++i) { info[i].pixelFormat = formats[i]; getPixelFormatAttribiv(togl->tglGLHdc, formats[i], 0, NUM_FBAttribs, FBAttribs, &info[i].stereo); /* revise attributes so larger is better */ if (!togl->DepthFlag) info[i].depth = -info[i].depth; if (!togl->MultisampleFlag) info[i].samples = -info[i].samples; if (togl->Stereo != TOGL_STEREO_NATIVE) info[i].stereo = -info[i].stereo; } qsort(info, numFormats, sizeof info[0], FBInfoCmp); pixelformat = info[0].pixelFormat; /* double check that we got the stereo format we requested */ if (togl->Stereo == TOGL_STEREO_NATIVE && !info[0].stereo) { Tcl_SetResult(togl->Interp, TCL_STUPID "couldn't choose stereo pixel format", TCL_STATIC); free(info); return 0; } free(info); return pixelformat; } static int togl_describePixelFormat(Togl *togl) { if (getPixelFormatAttribiv == NULL) { PIXELFORMATDESCRIPTOR pfd; DescribePixelFormat(togl->tglGLHdc, (int) togl->PixelFormat, sizeof (pfd), &pfd); /* fill in flags normally passed in that affect behavior */ togl->RgbaFlag = pfd.iPixelType == PFD_TYPE_RGBA; togl->DoubleFlag = (pfd.dwFlags & PFD_DOUBLEBUFFER) != 0; togl->DepthFlag = (pfd.cDepthBits != 0); togl->AccumFlag = (pfd.cAccumBits != 0); togl->AlphaFlag = (pfd.cAlphaBits != 0); togl->StencilFlag = (pfd.cStencilBits != 0); if ((pfd.dwFlags & PFD_STEREO) != 0) togl->Stereo = TOGL_STEREO_NATIVE; else togl->Stereo = TOGL_STEREO_NONE; } else { static int attribs[] = { WGL_PIXEL_TYPE_ARB, WGL_DOUBLE_BUFFER_ARB, WGL_DEPTH_BITS_ARB, WGL_ACCUM_RED_BITS_ARB, WGL_ALPHA_BITS_ARB, WGL_STENCIL_BITS_ARB, WGL_STEREO_ARB, WGL_SAMPLES_ARB }; #define NUM_ATTRIBS (sizeof attribs / sizeof attribs[0]) int info[NUM_ATTRIBS]; getPixelFormatAttribiv(togl->tglGLHdc, (int) togl->PixelFormat, 0, NUM_ATTRIBS, attribs, info); #undef NUM_ATTRIBS togl->RgbaFlag = info[0]; togl->DoubleFlag = info[1]; togl->DepthFlag = (info[2] != 0); togl->AccumFlag = (info[3] != 0); togl->AlphaFlag = (info[4] != 0); togl->StencilFlag = (info[5] != 0); togl->Stereo = info[6] ? TOGL_STEREO_NATIVE : TOGL_STEREO_NONE; togl->MultisampleFlag = (info[7] != 0); } return True; } static HPBUFFERARB togl_createPbuffer(Togl *togl) { int attribs[32]; int na = 0; HPBUFFERARB pbuf; if (togl->LargestPbufferFlag) { attribs[na++] = WGL_PBUFFER_LARGEST_ARB; attribs[na++] = 1; } attribs[na] = 0; pbuf = createPbuffer(togl->tglGLHdc, (int) togl->PixelFormat, togl->Width, togl->Height, attribs); if (pbuf && togl->LargestPbufferFlag) { queryPbuffer(pbuf, WGL_PBUFFER_WIDTH_ARB, &togl->Width); queryPbuffer(pbuf, WGL_PBUFFER_HEIGHT_ARB, &togl->Height); } return pbuf; } static void togl_destroyPbuffer(Togl *togl) { destroyPbuffer(togl->pbuf); } #if 0 // From nvidia.com Multisampling requires WGL_ARB_extension_string and WGL_ARB_pixel_format wglGetProcAddress("wglGetExtensionsStringARB") // From msdn.microsoft.com, various ways to enumerate PixelFormats void CPixForm::OnClickedLastPfd() { COpenGL gl; PIXELFORMATDESCRIPTOR pfd; // // Get the hwnd of the view window. // HWND hwndview = GetViewHwnd(); // // Get a DC associated with the view window. // HDC dc =::GetDC(hwndview); int nID = (m_nNextID > 1) ? m_nNextID-- : 1; // // Get a description of the pixel format. If it is valid, then go and // update the controls in the dialog box, otherwise do nothing. // if (gl.DescribePixelFormat(dc, nID, sizeof (PIXELFORMATDESCRIPTOR), &pfd)) UpdateDlg(&pfd); // // Release the DC. // ::ReleaseDC(hwndview, dc); } ---------------------- // local variables int iMax; PIXELFORMATDESCRIPTOR pfd; int iPixelFormat; // initialize a pixel format index variable iPixelFormat = 1; // keep obtaining and examining pixel format data... do { // try to obtain some pixel format data iMax = DescribePixelFormat(dc, iPixelFormat, sizeof (pfd), &pfd); // if there was some problem with that... if (iMax == 0) // return indicating failure return (FALSE); // we have successfully obtained pixel format data // let's examine the pixel format data... myPixelFormatExaminer(&pfd); } // ...until we've looked at all the device context's pixel formats while (++iPixelFormat <= iMax); #endif