/**************************************************************************\ * * This file is part of the Coin 3D visualization library. * Copyright (C) 1998-2007 by Systems in Motion. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * ("GPL") version 2 as published by the Free Software Foundation. * See the file LICENSE.GPL at the root directory of this source * distribution for additional information about the GNU GPL. * * For using Coin with software that can not be combined with the GNU * GPL, and for taking advantage of the additional benefits of our * support services, please contact Systems in Motion about acquiring * a Coin Professional Edition License. * * See http://www.coin3d.org/ for more information. * * Systems in Motion, Postboks 1283, Pirsenteret, 7462 Trondheim, NORWAY. * http://www.sim.no/ sales@sim.no coin-support@coin3d.org * \**************************************************************************/ #ifdef HAVE_CONFIG_H #include #endif /* HAVE_CONFIG_H */ #include #include #include #include #include #include #include #include #include #ifdef HAVE_WIN32_API /* Conditional inclusion, as the functions in win32api.h will not be implemented unless the Win32 API is available. */ #include #endif /* HAVE_WIN32_API */ /* ********************************************************************** */ #ifndef HAVE_WGL /* Dummy implementations, for when WGL is not available: */ void * coin_wgl_getprocaddress(const char * fname) { return NULL; } void * wglglue_context_create_offscreen(unsigned int width, unsigned int height) { assert(FALSE); return NULL; } SbBool wglglue_context_make_current(void * ctx) { assert(FALSE); return FALSE; } void wglglue_context_reinstate_previous(void * ctx) { assert(FALSE); } void wglglue_context_destruct(void * ctx) { assert(FALSE); } void wglglue_context_bind_pbuffer(void * ctx) { assert(FALSE); } void wglglue_context_release_pbuffer(void * ctx) { assert(FALSE); } SbBool wglglue_context_pbuffer_is_bound(void * ctx) { assert(FALSE); return FALSE; } SbBool wglglue_context_can_render_to_texture(void * ctx) { assert(FALSE); return FALSE; } SbBool wglglue_context_pbuffer_max(void * ctx, unsigned int * lims) { assert(FALSE); return FALSE; } /* ********************************************************************** */ #else /* HAVE_WGL */ struct wglglue_contextdata; static SbBool (* wglglue_context_create)(struct wglglue_contextdata * context, SbBool warnonerrors) = NULL; /* ********************************************************************** */ /* Declared in the same manner as in the OpenGL ARB document for the pbuffer extension, but with a different name to avoid clashes upon static binding. Note that I couldn't find any documentation on the DECLARE_HANDLE thinga-majingy, so I would appreciate it if someone could confirm that this is correct. -mortene. */ DECLARE_HANDLE(WGLGLUE_HPBUFFER); /* The following are from either the WGL_ARB_pbuffer or the WGL_EXT_pbuffer extensions: */ typedef WGLGLUE_HPBUFFER (WINAPI * COIN_PFNWGLCREATEPBUFFERPROC)(HDC hDC, int iPixelFormat, int iWidth, int iHeight, const int * piAttribList); typedef HDC (WINAPI * COIN_PFNWGLGETPBUFFERDCPROC)(WGLGLUE_HPBUFFER hPbuffer); typedef int (WINAPI * COIN_PFNWGLRELEASEPBUFFERDCPROC)(WGLGLUE_HPBUFFER hPbuffer, HDC hDC); typedef BOOL (WINAPI * COIN_PFNWGLDESTROYPBUFFERPROC)(WGLGLUE_HPBUFFER hPbuffer); typedef BOOL (WINAPI * COIN_PFNWGLQUERYPBUFFERPROC)(WGLGLUE_HPBUFFER hPbuffer, int iAttribute, int * piValue); typedef BOOL (WINAPI * COIN_PFNWGLBINDTEXIMAGEARBPROC)(WGLGLUE_HPBUFFER hPbuffer, int iBuffer); typedef BOOL (WINAPI * COIN_PFNWGLRELEASETEXIMAGEARBPROC)(WGLGLUE_HPBUFFER hPbuffer, int iBuffer); static COIN_PFNWGLCREATEPBUFFERPROC wglglue_wglCreatePbuffer = NULL; static COIN_PFNWGLGETPBUFFERDCPROC wglglue_wglGetPbufferDC = NULL; static COIN_PFNWGLRELEASEPBUFFERDCPROC wglglue_wglReleasePbufferDC = NULL; static COIN_PFNWGLDESTROYPBUFFERPROC wglglue_wglDestroyPbuffer = NULL; static COIN_PFNWGLQUERYPBUFFERPROC wglglue_wglQueryPbuffer = NULL; static COIN_PFNWGLBINDTEXIMAGEARBPROC wglglue_wglBindTexImageARB = NULL; static COIN_PFNWGLBINDTEXIMAGEARBPROC wglglue_wglReleaseTexImageARB = NULL; /* The following is from either the WGL_ARB_pixel_format or the WGL_EXT_pixel_format extensions: */ typedef BOOL (WINAPI * COIN_PFNWGLCHOOSEPIXELFORMATPROC)(HDC hdc, const int * piAttribIList, const FLOAT * pfAttribFList, UINT nMaxFormats, int * piFormats, UINT * nNumFormats); typedef BOOL (WINAPI * COIN_PFNWGLGETPIXELFORMATATTRIBIVPROC)(HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttribautes, const int * piAttributes, int * piValues); static COIN_PFNWGLCHOOSEPIXELFORMATPROC wglglue_wglChoosePixelFormat = NULL; static COIN_PFNWGLGETPIXELFORMATATTRIBIVPROC wglglue_wglGetPixelFormatAttribiv = NULL; /* The function for finding extension strings is itself an extension string. */ typedef const char * (WINAPI * COIN_PFNWGLGETEXTENSIONSSTRING)(HDC hDC); static COIN_PFNWGLGETEXTENSIONSSTRING wglglue_wglGetExtensionsString = NULL; /* ********************************************************************** */ #ifdef HAVE_DYNAMIC_LINKING #define PROC(_func_) cc_glglue_getprocaddress(SO__QUOTE(_func_)) /* The OpenGL library's WGL part which we dynamically pick up symbols from /could/ have these defined. For the code below which tries to dynamically resolve the methods, we will assume that they are defined. By doing this little "trick", can we use the same code below for resolving stuff dynamically as we need anyway to resolve in a static manner. */ #define WGL_ARB_pixel_format 1 #define WGL_EXT_pixel_format 1 #define WGL_ARB_pbuffer 1 #define WGL_EXT_pbuffer 1 #define WGL_ARB_extensions_string 1 #define WGL_EXT_extensions_string 1 #define WGL_ARB_render_texture 1 #else /* static binding */ #define PROC(_func_) (&_func_) #endif /* static binding */ /* ********************************************************************** */ static SbBool attemptedextresolved = FALSE; /* ********************************************************************** */ void * coin_wgl_getprocaddress(const char * fname) { void * ptr = wglGetProcAddress(fname); /* wglGetProcAddress() seems to only be able to fetch function-addresses for *extension* functions, not "proper" OpenGL (1.1+) functions. */ if (ptr == NULL) { cc_libhandle glhnd = cc_dl_handle_with_gl_symbols(); if (!glhnd && coin_glglue_debug()) { cc_debugerror_postwarning("coin_wgl_getprocaddress", "couldn't get hold of any workable module " "handle for picking up OpenGL symbols"); } if (glhnd) { ptr = cc_dl_sym(glhnd, fname); if (ptr && coin_glglue_debug()) { cc_debugerror_postinfo("coin_wgl_getprocaddress", "wglGetProcAddress() missed \"%s\", " "but found with cc_dl_sym()", fname); } } } return ptr; } /*** WGL offscreen contexts ***********************************************/ struct wglglue_contextdata { unsigned int width, height; HDC memorydc; HWND pbufferwnd; SbBool didcreatememorydc; SbBool shouldreleasememorydc; HBITMAP bitmap, oldbitmap; HGLRC wglcontext; HGLRC storedcontext; HDC storeddc; WGLGLUE_HPBUFFER hpbuffer; SbBool noappglcontextavail; SbBool supports_render_to_texture; SbBool pbufferisbound; int pixelformat; }; static SbBool wglglue_pbuffer_symbols_resolved(void) { return (wglglue_wglCreatePbuffer && wglglue_wglGetPbufferDC && wglglue_wglReleasePbufferDC && wglglue_wglDestroyPbuffer && wglglue_wglQueryPbuffer); } static SbBool wglglue_ext_supported(struct wglglue_contextdata * context, const char * reqext) { /* * wgl extensions are not necessarily listed in the string returned * by glGetString(GL_EXTENSIONS), see e.g. * * http://www.gamedev.net/reference/articles/article1929.asp * * ..so we try to get hold of wglGetExtensionsString[ARB|EXT]() and * use that aswell. */ if (!attemptedextresolved) { attemptedextresolved = TRUE; #ifdef WGL_ARB_extensions_string wglglue_wglGetExtensionsString = (COIN_PFNWGLGETEXTENSIONSSTRING)PROC(wglGetExtensionsStringARB); #endif /* WGL_ARB_extensions_string */ #ifdef WGL_EXT_extensions_string if (!wglglue_wglGetExtensionsString) { wglglue_wglGetExtensionsString = (COIN_PFNWGLGETEXTENSIONSSTRING)PROC(wglGetExtensionsStringEXT); } #endif /* WGL_EXT_extensions_string */ } if (wglglue_wglGetExtensionsString) { const char * wglext = wglglue_wglGetExtensionsString(context->memorydc); if (coin_glglue_extension_available(wglext, reqext)) { return TRUE; } } if (coin_glglue_extension_available(glGetString(GL_EXTENSIONS), reqext)) { return TRUE; } return FALSE; } static SbBool wglglue_resolve_symbols(struct wglglue_contextdata * context) { /* Short circuit out if symbols have already been resolved. */ if (wglglue_pbuffer_symbols_resolved()) { return TRUE; } /* We need a(ny) current context to resolve symbols. */ if (!wglglue_context_make_current(context)) { return FALSE; } /* Attempt to resolve the symbols: */ /* Check EXT before ARB, to let the latter override the former if both are present, as ARB should always be more recent than EXT. */ #ifdef WGL_EXT_pixel_format if (wglglue_ext_supported(context, "WGL_EXT_pixel_format")) { wglglue_wglChoosePixelFormat = (COIN_PFNWGLCHOOSEPIXELFORMATPROC)PROC(wglChoosePixelFormatEXT); wglglue_wglGetPixelFormatAttribiv = (COIN_PFNWGLGETPIXELFORMATATTRIBIVPROC)PROC(wglGetPixelFormatAttribivEXT); } #endif /* WGL_EXT_pixel_format */ #ifdef WGL_ARB_pixel_format if (wglglue_ext_supported(context, "WGL_ARB_pixel_format")) { wglglue_wglChoosePixelFormat = (COIN_PFNWGLCHOOSEPIXELFORMATPROC)PROC(wglChoosePixelFormatARB); wglglue_wglGetPixelFormatAttribiv = (COIN_PFNWGLGETPIXELFORMATATTRIBIVPROC)PROC(wglGetPixelFormatAttribivARB); } #endif /* WGL_ARB_pixel_format */ /* Now check ARB before EXT, as EXT-check will be blocked if ARB-versions of symbols are found. */ #ifdef WGL_ARB_pbuffer if (wglglue_wglChoosePixelFormat && /* <- WGL_*_pbuffer depends on WGL_*_pixel_format */ wglglue_ext_supported(context, "WGL_ARB_pbuffer")) { wglglue_wglCreatePbuffer = (COIN_PFNWGLCREATEPBUFFERPROC)PROC(wglCreatePbufferARB); wglglue_wglGetPbufferDC = (COIN_PFNWGLGETPBUFFERDCPROC)PROC(wglGetPbufferDCARB); wglglue_wglReleasePbufferDC = (COIN_PFNWGLRELEASEPBUFFERDCPROC)PROC(wglReleasePbufferDCARB); wglglue_wglDestroyPbuffer = (COIN_PFNWGLDESTROYPBUFFERPROC)PROC(wglDestroyPbufferARB); wglglue_wglQueryPbuffer = (COIN_PFNWGLQUERYPBUFFERPROC)PROC(wglQueryPbufferARB); } #endif /* WGL_ARB_pbuffer */ #ifdef WGL_EXT_pbuffer if (!wglglue_pbuffer_symbols_resolved() && wglglue_wglChoosePixelFormat && /* <- WGL_*_pbuffer depends on WGL_*_pixel_format */ wglglue_ext_supported(context, "WGL_EXT_pbuffer")) { wglglue_wglCreatePbuffer = (COIN_PFNWGLCREATEPBUFFERPROC)PROC(wglCreatePbufferEXT); wglglue_wglGetPbufferDC = (COIN_PFNWGLGETPBUFFERDCPROC)PROC(wglGetPbufferDCEXT); wglglue_wglReleasePbufferDC = (COIN_PFNWGLRELEASEPBUFFERDCPROC)PROC(wglReleasePbufferDCEXT); wglglue_wglDestroyPbuffer = (COIN_PFNWGLDESTROYPBUFFERPROC)PROC(wglDestroyPbufferEXT); wglglue_wglQueryPbuffer = (COIN_PFNWGLQUERYPBUFFERPROC)PROC(wglQueryPbufferEXT); } #endif /* WGL_EXT_pbuffer */ #ifdef WGL_ARB_render_texture if (wglglue_ext_supported(context, "WGL_ARB_render_texture")) { wglglue_wglBindTexImageARB = (COIN_PFNWGLBINDTEXIMAGEARBPROC) PROC(wglBindTexImageARB); wglglue_wglReleaseTexImageARB = (COIN_PFNWGLBINDTEXIMAGEARBPROC) PROC(wglReleaseTexImageARB); } #endif /* WGL_ARB_render_texture */ wglglue_context_reinstate_previous(context); return wglglue_pbuffer_symbols_resolved(); } static struct wglglue_contextdata * wglglue_contextdata_init(unsigned int width, unsigned int height) { struct wglglue_contextdata * context; context = (struct wglglue_contextdata *)malloc(sizeof(struct wglglue_contextdata)); context->width = width; context->height = height; context->memorydc = NULL; context->pbufferwnd = NULL; context->didcreatememorydc = FALSE; context->shouldreleasememorydc = FALSE; context->bitmap = NULL; context->hpbuffer = NULL; context->oldbitmap = NULL; context->wglcontext = NULL; context->storedcontext = NULL; context->storeddc = NULL; context->noappglcontextavail = FALSE; context->supports_render_to_texture = FALSE; context->pbufferisbound = FALSE; context->pixelformat = 0; return context; } static void wglglue_contextdata_cleanup(struct wglglue_contextdata * ctx) { if (ctx == NULL) { return; } /* FIXME: the error handling below can and should be simplified, by implementing and using excpetion catching wrappers from glue/win32api. 20031124 mortene. */ if (ctx->wglcontext && ctx->noappglcontextavail) { const BOOL r = wglDeleteContext(ctx->wglcontext); if (!r) { cc_win32_print_error("wglglue_contextdata_cleanup", "wglDeleteContext", GetLastError()); } } if (ctx->oldbitmap) { const HGDIOBJ o = SelectObject(ctx->memorydc, ctx->oldbitmap); if (!o) { cc_win32_print_error("wglglue_contextdata_cleanup", "SelectObject", GetLastError()); } } if (ctx->bitmap) { const BOOL r = DeleteObject(ctx->bitmap); if (!r) { cc_win32_print_error("wglglue_contextdata_cleanup", "DeleteObject", GetLastError()); } } if (ctx->hpbuffer) { { const int r = wglglue_wglReleasePbufferDC(ctx->hpbuffer, ctx->memorydc); if (!r) { cc_win32_print_error("wglglue_contextdata_cleanup", "wglReleasePbufferDC", GetLastError()); } } { const BOOL r = wglglue_wglDestroyPbuffer(ctx->hpbuffer); if (!r) { cc_win32_print_error("wglglue_contextdata_cleanup", "wglDestroyPbuffer", GetLastError()); } } } if (ctx->memorydc) { if (ctx->didcreatememorydc) { const BOOL r = DeleteDC(ctx->memorydc); if (!r) { cc_win32_print_error("wglglue_contextdata_cleanup", "DeleteDC", GetLastError()); } } else if (ctx->shouldreleasememorydc && ctx->pbufferwnd) { int ok = ReleaseDC(ctx->pbufferwnd, ctx->memorydc); if (!ok) { cc_win32_print_error("wglglue_contextdata_cleanup", "ReleaseDC", GetLastError()); } } } if (ctx->pbufferwnd) { BOOL r = DestroyWindow(ctx->pbufferwnd); if (!r) { cc_win32_print_error("wglglue_contextdata_cleanup", "DestroyWindow", GetLastError()); } } free(ctx); } static SbBool wglglue_context_create_context(struct wglglue_contextdata * ctx, DWORD bitWin) { struct wglglue_contextdata * context = (struct wglglue_contextdata *)ctx; int pixelformat; /* FIXME: we're currently not giving any hint as to whether we want a single- / double- / quad-buffer visual. Using anything than a single-buffer visual for offscreen rendering is probably wasteful, so we're likely to often use up more resources than we really need to do. 20061025 mortene. */ PIXELFORMATDESCRIPTOR pfd = { sizeof(PIXELFORMATDESCRIPTOR), /* size of this pfd */ 1, /* version number */ bitWin | /* support bitmap or window */ PFD_SUPPORT_OPENGL, /* support OpenGL */ PFD_TYPE_RGBA, /* RGBA type */ 24, /* 24-bit color depth */ 0, 0, 0, 0, 0, 0, /* color bits ignored */ 8, /* 8 bit alpha channel */ 0, /* shift bit ignored */ 0, /* no accumulation buffer */ 0, 0, 0, 0, /* accum bits ignored */ 32, /* 32-bit z-buffer */ 1, /* minimum size stencil buffer */ 0, /* no auxiliary buffer */ PFD_MAIN_PLANE, /* main layer */ 0, /* reserved */ 0, 0, 0 /* layer masks ignored */ }; /* FIXME: the following is a hack to get around a problem which really demands more effort to be solved properly. The problem is that there is no way in the API of the SoOffscreenRenderer class to specify what particular attributes to request. This most often manifests itself as a problem for app programmers in that they have made some kind of extension node which uses the OpenGL stencil buffer. If no stencil buffer happens to be part of the GL context format for the offscreen renderer, these will not work properly. At the same time, we don't want to default to requesting a stencil buffer, as that takes a non-trivial amount of extra memory resources on the gfx card. So until we have implemented the proper solution for making it possible to pass in a detailed specification of which attributes to request from offscreen GL contexts, we provide this temporary work-around: the app programmer can set an envvar with a value specifying the number of stencil buffer bits he/she wants. 20060223 mortene. */ const int v = coin_glglue_stencil_bits_hack(); if (v != -1) { pfd.cStencilBits = (BYTE)v; } /* get the best available match of pixel format for the device context: */ /* FIXME: instead of ChoosePixelFormat(), we should use the code from SoWin which runs through the available formats and weights them according to our own rules. Simplifies debugging immensely when something goes wrong, and provides ways to override parts of the selection process. 20040608 mortene. */ pixelformat = ChoosePixelFormat(context->memorydc, &pfd); if (pixelformat == 0) { DWORD dwError = GetLastError(); cc_debugerror_postwarning("wglglue_context_create_context", "ChoosePixelFormat() failed with " "error code %d.", dwError); return FALSE; } /* make that the pixel format of the device context: */ /* I've seen a driver (3Dlabs GLINT R3 PT, 1.1.28) where SetPixelFormat() failed but didn't set GetLastError(), resulting in a nonsensical error message, so we reset this first. -mortene. */ SetLastError(0); if (!SetPixelFormat(context->memorydc, pixelformat, &pfd)) { DWORD dwError = GetLastError(); cc_string str; cc_string_construct(&str); cc_string_sprintf(&str, "SetPixelFormat(%p, %d, ...)", context->memorydc, pixelformat); cc_win32_print_error("wglglue_context_create_context", cc_string_get_text(&str), dwError); cc_string_clean(&str); /* We have had an external error report about this, plus I've been able to reproduce it locally on ASK (Win NT 4, 3Dlabs 1.1.28): just set the display to "Truecolor" (instead of "256 colors" or "65536 colors" in the system display settings panel) and the SetPixelFormat() above will fail. Changing the display settings to a lower color resolution fixes it. (Using a lower screen resolution makes no difference, so it doesn't seem to be related to lack of memory resources.) I tried to make less demanding requirements for the pixel format in the above PIXELFORMATDESCRIPTOR, but that didn't help either. So, this seems very much like a system / driver bug. */ cc_debugerror_post("wglglue_context_create_context", "This is most likely a bug with your system. " "Try changing display settings (to e.g. 16-bit, " "24-bit or 32-bit truecolor display) and re-run."); return FALSE; } context->pixelformat = pixelformat; context->wglcontext = wglCreateContext(context->memorydc); if (context->wglcontext == NULL) { DWORD dwError = GetLastError(); cc_win32_print_error("wglglue_context_create_context", "wglCreateContext()", dwError); return FALSE; } return TRUE; } static SbBool wglglue_context_create_software(struct wglglue_contextdata * ctx, SbBool warnonerrors) { struct wglglue_contextdata * context = (struct wglglue_contextdata *)ctx; if (coin_glglue_debug()) { cc_debugerror_postinfo("wglglue_context_create_software", "creating software buffer"); } context->memorydc = CreateCompatibleDC(NULL); context->didcreatememorydc = TRUE; context->shouldreleasememorydc = FALSE; if (context->memorydc == NULL) { if (warnonerrors || coin_glglue_debug()) { DWORD dwError = GetLastError(); cc_debugerror_postwarning("wglglue_context_create_software", "CreateCompatibleDC(NULL) failed with " "error code %d.", dwError); } return FALSE; } /* make a bitmap to draw to */ { BITMAPINFO bmi; void * pvbits; bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmi.bmiHeader.biWidth = context->width; bmi.bmiHeader.biHeight = context->height; bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biBitCount = 24; bmi.bmiHeader.biCompression = BI_RGB; bmi.bmiHeader.biSizeImage = 0; bmi.bmiHeader.biXPelsPerMeter = 0; bmi.bmiHeader.biYPelsPerMeter = 0; bmi.bmiHeader.biClrUsed = 0; bmi.bmiHeader.biClrImportant = 0; bmi.bmiColors[0].rgbBlue = 0; bmi.bmiColors[0].rgbGreen = 0; bmi.bmiColors[0].rgbRed = 0; bmi.bmiColors[0].rgbReserved = 0; context->bitmap = CreateDIBSection(context->memorydc, &bmi, DIB_RGB_COLORS, &pvbits, NULL, 0); if (context->bitmap == NULL) { if (warnonerrors || coin_glglue_debug()) { DWORD dwError = GetLastError(); cc_debugerror_postwarning("wglglue_context_create_software", "CreateDIBSection() failed with error " "code %d.", dwError); } return FALSE; } } context->oldbitmap = (HBITMAP) SelectObject(context->memorydc, context->bitmap); if (context->oldbitmap == NULL) { if (warnonerrors || coin_glglue_debug()) { DWORD dwError = GetLastError(); cc_debugerror_postwarning("wglglue_context_create_software", "SelectObject() failed with error code %d.", dwError); } return FALSE; } if (!(wglglue_context_create_context(context, PFD_DRAW_TO_BITMAP))) { return FALSE; } if (coin_glglue_debug()) { cc_debugerror_postinfo("wglglue_context_create_software", "success creating software buffer, HGLRC==%p", context->wglcontext); } return TRUE; } static SbBool wglglue_context_create_pbuffer(struct wglglue_contextdata * ctx, SbBool warnonerrors) { struct wglglue_contextdata * context = (struct wglglue_contextdata *)ctx; if (coin_glglue_debug()) { cc_debugerror_postinfo("wglglue_context_create_pbuffer", "creating pbuffer"); } if ((context->memorydc = wglGetCurrentDC())) { context->shouldreleasememorydc = FALSE; context->didcreatememorydc = FALSE; context->wglcontext = wglGetCurrentContext(); } else { context->noappglcontextavail = TRUE; } if (context->noappglcontextavail) { /* FIXME: This should be reset in wglglue_cleanup() once we also properly unregister there... 20060207 kyrah */ static int didregister = 0; if (!didregister) { WNDCLASS wc; didregister = 1; wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; wc.lpfnWndProc = DefWindowProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = GetModuleHandle(NULL); wc.hIcon = NULL; wc.hCursor = NULL; wc.hbrBackground = NULL; wc.lpszMenuName = NULL; wc.lpszClassName = "coin_gl_wgl"; if (!RegisterClass(&wc)) { DWORD dwError = GetLastError(); cc_debugerror_postwarning("wglglue_context_create_pbuffer", "RegisterClass(&wc) failed with " "error code %d.", dwError); return FALSE; } /* FIXME: unregister at app exit? pederb, 2003-12-15 */ } { HWND hWnd; HINSTANCE hInstance = GetModuleHandle(NULL); if (!(hWnd = CreateWindow( "coin_gl_wgl", /* class name */ "coin_gl_wgl", /* window title */ 0, /* selected window style */ 0, 0, /* window position */ context->width, /* calculate adjusted window width */ context->height, /* calculate adjusted window height */ NULL, /* no parent window */ NULL, /* no menu */ hInstance, /* Instance */ NULL))) /* don't pass anything to WM_CREATE */ { DWORD dwError = GetLastError(); cc_debugerror_postwarning("wglglue_context_create_pbuffer", "CreateWindow(...) failed with " "error code %d.", dwError); return FALSE; } context->pbufferwnd = hWnd; context->memorydc = GetDC(hWnd); context->shouldreleasememorydc = TRUE; context->didcreatememorydc = FALSE; if (context->memorydc == NULL) { DWORD dwError = GetLastError(); cc_debugerror_postwarning("wglglue_context_create_pbuffer", "GetDC(hWnd) failed with " "error code %d.", dwError); return FALSE; } } if (!(wglglue_context_create_context(context, PFD_DRAW_TO_WINDOW))) { return FALSE; } } { const SbBool pbuffer = wglglue_resolve_symbols(context); if (coin_glglue_debug()) { cc_debugerror_postinfo("wglglue_context_create_pbuffer", "PBuffer offscreen rendering is %ssupported " "by the OpenGL driver", pbuffer ? "" : "NOT "); } if (!pbuffer) { return FALSE; } } { GLint pixformat; unsigned int numFormats; const float fAttribList[] = { 0 }; int nontex_attrs[] = { WGL_STENCIL_BITS_ARB, 1, /* FIXME: must be first, since we may want to modify it later, and need to know where it is. This is a wart. 20060307 mortene. */ WGL_DRAW_TO_PBUFFER_ARB, TRUE, WGL_COLOR_BITS_ARB, 32, WGL_ALPHA_BITS_ARB, 8, WGL_DEPTH_BITS_ARB, 24, 0 }; int tex_attrs[] = { WGL_STENCIL_BITS_ARB, 1, /* FIXME: must be first, since we may want to modify it later, and need to know where it is. This is a wart. 20060307 mortene. */ WGL_DRAW_TO_PBUFFER_ARB, TRUE, WGL_BIND_TO_TEXTURE_RGBA_ARB, TRUE, WGL_COLOR_BITS_ARB, 32, WGL_ALPHA_BITS_ARB, 8, WGL_DEPTH_BITS_ARB, 24, 0 }; const int * attrs[] = { nontex_attrs, tex_attrs }; const int nontex_pbufferflags[] = { 0 }; const int tex_pbufferflags[] = { WGL_TEXTURE_FORMAT_ARB, WGL_TEXTURE_RGBA_ARB, WGL_TEXTURE_TARGET_ARB, WGL_TEXTURE_2D_ARB, 0 }; const int * pbufferflags[] = { nontex_pbufferflags, tex_pbufferflags }; unsigned int try; { /* FIXME: the following is a hack to get around a problem which really demands more effort to be solved properly. See further up in this source file, in the function to create a software context, for a more elaborate explanation. 20060307 mortene. */ const int v = coin_glglue_stencil_bits_hack(); if (v != -1) { assert(nontex_attrs[0] == WGL_STENCIL_BITS_ARB); nontex_attrs[1] = v; assert(tex_attrs[0] == WGL_STENCIL_BITS_ARB); tex_attrs[1] = v; } } /* iterate from end of arrays, which contains "best" option */ try = (sizeof(attrs) / sizeof(attrs[0])); /* if render-to-texture extension not supported, don't attempt to set up a pbuffer with those capabilities (could in theory cause nasty WGL errors): */ if (wglglue_wglBindTexImageARB == NULL) { try--; } while (try > 0) { try--; /* choose pixel format */ if (!wglglue_wglChoosePixelFormat(context->memorydc, attrs[try], fAttribList, 1, &pixformat, &numFormats)) { if (warnonerrors || coin_glglue_debug()) { cc_debugerror_postwarning("wglglue_context_create_pbuffer", "wglChoosePixelFormat() failed, try %u", try); } continue; } /* create the pbuffer */ context->hpbuffer = wglglue_wglCreatePbuffer(context->memorydc, pixformat, context->width, context->height, pbufferflags[try]); if (!context->hpbuffer) { if (warnonerrors || coin_glglue_debug()) { cc_debugerror_postwarning("wglglue_context_create_pbuffer", "wglCreatePbuffer(HDC, PixelFormat==%d, " "Width==%d, Height==%d, AttribList==%p) " "failed, try %u", pixformat, context->width, context->height, pbufferflags[try], try); } continue; } /* success, set capability flag, and break loop */ if (coin_glglue_debug()) { cc_debugerror_postinfo("wglglue_context_create_pbuffer", "wglCreatePbuffer() success, try %u", try); } context->supports_render_to_texture = (pbufferflags[try] == tex_pbufferflags); break; } /* if no way to construct a pbuffer was found: */ if (!context->hpbuffer) { return FALSE; } context->pixelformat = pixformat; /* delete/release device context and window in case we created it ourselves */ if (context->memorydc) { if (context->didcreatememorydc) { BOOL r = DeleteDC(context->memorydc); if (!r) { cc_win32_print_error("wglglue_context_create_pbuffer", "DeleteDC", GetLastError()); } } else if (context->shouldreleasememorydc && context->pbufferwnd) { BOOL r = ReleaseDC(context->pbufferwnd, context->memorydc); if (!r) { cc_win32_print_error("wglglue_context_create_pbuffer", "ReleaseDC", GetLastError()); } } } if (context->pbufferwnd) { BOOL r = DestroyWindow(context->pbufferwnd); if (!r) { cc_win32_print_error("wglglue_context_create_pbuffer", "DestroyWindow", GetLastError()); } context->pbufferwnd = NULL; } context->memorydc = wglglue_wglGetPbufferDC(context->hpbuffer); context->didcreatememorydc = FALSE; context->shouldreleasememorydc = FALSE; if (!context->memorydc) { if (warnonerrors || coin_glglue_debug()) { cc_debugerror_postwarning("wglglue_context_create_pbuffer", "Couldn't create pbuffer's device context."); } return FALSE; } /* delete wgl context in case we created it ourselves */ if (context->noappglcontextavail) { BOOL r = wglDeleteContext(context->wglcontext); if (!r) { if (warnonerrors || coin_glglue_debug()) { cc_debugerror_postwarning("wglglue_context_create_pbuffer", "Couldn't create pbuffer's device context."); } } } context->wglcontext = wglCreateContext(context->memorydc); if (!context->wglcontext) { if (warnonerrors || coin_glglue_debug()) { cc_debugerror_postwarning("wglglue_context_create_pbuffer", "Couldn't create rendering context for the pbuffer."); } return FALSE; } /* set and output the actual pBuffer dimensions */ if (!wglglue_wglQueryPbuffer(context->hpbuffer, WGL_PBUFFER_WIDTH_ARB, &(context->width))) { if (warnonerrors || coin_glglue_debug()) { cc_debugerror_postwarning("wglglue_context_create_pbuffer", "Couldn't query the pbuffer width."); } return FALSE; } if (!wglglue_wglQueryPbuffer(context->hpbuffer, WGL_PBUFFER_HEIGHT_ARB, &(context->height))) { if (warnonerrors || coin_glglue_debug()) { cc_debugerror_postwarning("wglglue_context_create_pbuffer", "Couldn't query the pbuffer height."); } return FALSE; } } if (coin_glglue_debug()) { cc_debugerror_postinfo("wglglue_context_create_pbuffer", "success creating pbuffer, HGLRC==%p", context->wglcontext); } return TRUE; } /* ********************************************************************** */ /* Create and return a handle to an offscreen OpenGL buffer. Where p-buffer support is available that will be used instead of a standard offscreen WGL context, as it should render much faster (due to hardware acceleration). See: http://www.oss.sgi.com/projects/ogl-sample/registry/ARB/wgl_pbuffer.txt Or the older version: http://www.oss.sgi.com/projects/ogl-sample/registry/EXT/wgl_pbuffer.txt */ void * wglglue_context_create_offscreen(unsigned int width, unsigned int height) { struct wglglue_contextdata * swctx, * pbctx; SbBool ispbuffer; if (coin_glglue_debug()) { cc_debugerror_postinfo("wglglue_context_create_offscreen", "method called "); } swctx = wglglue_contextdata_init(width, height); assert(swctx); if (wglglue_context_create != NULL) { ispbuffer = wglglue_context_create == wglglue_context_create_pbuffer; /* don't warn if we fail to open a pbuffer context. we will try software */ if (wglglue_context_create(swctx, !ispbuffer)) { return swctx; } wglglue_contextdata_cleanup(swctx); /* fall back to a software context */ if (ispbuffer) { if (coin_glglue_debug()) { cc_debugerror_postinfo("wglglue_context_create_offscreen", "pbuffer failed. Trying software "); } swctx = wglglue_contextdata_init(width, height); assert(swctx); if (wglglue_context_create_software(swctx, TRUE)) { return swctx; } wglglue_contextdata_cleanup(swctx); } return NULL; } /* As there could possibly be no valid wgl context at this moment, we have to first make a context and set it current to be able to query pbuffer extension availability. */ if (!wglglue_context_create_software(swctx, TRUE)) { wglglue_contextdata_cleanup(swctx); return NULL; } /* ok, so we can at least use a non-pbuffer offscreen context */ wglglue_context_create = wglglue_context_create_software; /* developer or user can force pbuffer support off with this envvar */ { const char * env = coin_getenv("COIN_WGLGLUE_NO_PBUFFERS"); if (env && atoi(env) > 0) { return swctx; } } /* next, check if pbuffer support is available in the OpenGL library image */ pbctx = wglglue_contextdata_init(width, height); assert(pbctx); /* attempt to create a pbuffer */ if (!wglglue_context_create_pbuffer(pbctx, FALSE)) { wglglue_contextdata_cleanup(pbctx); return swctx; } /* pbuffers are really supported, kill the software offscreen context and use the pbuffer-enabled one */ wglglue_contextdata_cleanup(swctx); wglglue_context_create = wglglue_context_create_pbuffer; return pbctx; } SbBool wglglue_context_make_current(void * ctx) { struct wglglue_contextdata * context = (struct wglglue_contextdata *)ctx; context->storedcontext = wglGetCurrentContext(); if (context->storedcontext) { context->storeddc = wglGetCurrentDC(); } return wglMakeCurrent(context->memorydc, context->wglcontext) ? TRUE : FALSE; } void wglglue_context_reinstate_previous(void * ctx) { /* The previous context is stored and reset to make it possible to use an SoOffscreenRenderer from for instance an SoCallback node callback during SoGLRenderAction traversal, without the need for any extra book-keeping on the application side. */ struct wglglue_contextdata * context = (struct wglglue_contextdata *)ctx; if (context->storedcontext && context->storeddc) { (void)wglMakeCurrent(context->storeddc, context->storedcontext); context->storedcontext = NULL; } else { (void)wglMakeCurrent(NULL, NULL); } } void wglglue_context_destruct(void * ctx) { /* FIXME: needs to call into the (as of yet unimplemented) "destructing GL context" handler. 20030310 mortene. */ struct wglglue_contextdata * context = (struct wglglue_contextdata *)ctx; if (coin_glglue_debug()) { cc_debugerror_postinfo("wglglue_context_destruct", "destroy context, HGLRC==%p", context->wglcontext); } wglglue_contextdata_cleanup(context); } /* ********************************************************************** */ void wglglue_context_bind_pbuffer(void * ctx) { BOOL ok; struct wglglue_contextdata * context = (struct wglglue_contextdata *)ctx; assert(wglglue_wglBindTexImageARB != NULL); assert(context->supports_render_to_texture); ok = wglglue_wglBindTexImageARB(context->hpbuffer, WGL_FRONT_LEFT_ARB); assert(ok); context->pbufferisbound = TRUE; } void wglglue_context_release_pbuffer(void * ctx) { BOOL ok; struct wglglue_contextdata * context = (struct wglglue_contextdata *)ctx; assert(wglglue_wglReleaseTexImageARB != NULL); ok = wglglue_wglReleaseTexImageARB(context->hpbuffer, WGL_FRONT_LEFT_ARB); assert(ok); context->pbufferisbound = FALSE; } SbBool wglglue_context_pbuffer_is_bound(void * ctx) { struct wglglue_contextdata * context = (struct wglglue_contextdata *)ctx; return context->pbufferisbound; } SbBool wglglue_context_can_render_to_texture(void * ctx) { struct wglglue_contextdata * context = (struct wglglue_contextdata *)ctx; return context->supports_render_to_texture; } /* ********************************************************************** */ /* If ctx does not point at a pbuffer context, but rather a "normal" offscreen context, it will return FALSE. Upon other error conditions, FALSE will also be returned. */ SbBool wglglue_context_pbuffer_max(void * c, unsigned int * lims) { BOOL ok; int attribvals[3], i; const int attribs[3] = { WGL_MAX_PBUFFER_WIDTH_ARB, WGL_MAX_PBUFFER_HEIGHT_ARB, WGL_MAX_PBUFFER_PIXELS_ARB }; struct wglglue_contextdata * ctx = (struct wglglue_contextdata *)c; if (!ctx->hpbuffer) { return FALSE; } if (!wglglue_wglGetPixelFormatAttribiv) { return FALSE; } ok = wglglue_wglGetPixelFormatAttribiv(ctx->memorydc, ctx->pixelformat, 0, /* main plane */ 3, attribs, attribvals); if (!ok) { if (coin_glglue_debug()) { DWORD dwError = GetLastError(); cc_debugerror_postwarning("wglglue_context_pbuffer_max", "wglGetPixelFormatAttribiv() failed with " "error code %d.", dwError); } return FALSE; } for (i = 0; i < 3; i++) { assert(attribvals[i] >= 0); lims[i] = (unsigned int)attribvals[i]; } return TRUE; } /* ********************************************************************** */ void wglglue_cleanup(void) { wglglue_context_create = NULL; wglglue_wglCreatePbuffer = NULL; wglglue_wglGetPbufferDC = NULL; wglglue_wglReleasePbufferDC = NULL; wglglue_wglDestroyPbuffer = NULL; wglglue_wglQueryPbuffer = NULL; wglglue_wglBindTexImageARB = NULL; wglglue_wglReleaseTexImageARB = NULL; wglglue_wglChoosePixelFormat = NULL; wglglue_wglGetPixelFormatAttribiv = NULL; wglglue_wglGetExtensionsString = NULL; attemptedextresolved = FALSE; /* FIXME: We should probably do an UnregisterClass() here (see wglglue_context_create_pbuffer()) -- but since I don't know the details of how to do this, I'll leave it to one of the Windows gurus... 20060207 kyrah */ } #endif /* HAVE_WGL */ /* ********************************************************************** */