/* * Copyright (c) 2002-2008 LWJGL Project * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of 'LWJGL' nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * $Id$ * * @author elias_naue * @version $Revision$ */ #include #include #include "common_tools.h" #include "extgl.h" #include "extgl_wgl.h" #include "context.h" extern HINSTANCE dll_handle; // Handle to the LWJGL dll #define _CONTEXT_PRIVATE_CLASS_NAME "__lwjgl_context_class_name" /* * Register the LWJGL window class. * Returns true for success, or false for failure */ bool registerWindow(WNDPROC win_proc, LPCTSTR class_name) { WNDCLASS windowClass; memset(&windowClass, 0, sizeof(windowClass)); windowClass.style = CS_OWNDC; windowClass.lpfnWndProc = win_proc; windowClass.cbClsExtra = 0; windowClass.cbWndExtra = 0; windowClass.hInstance = dll_handle; windowClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); windowClass.hCursor = LoadCursor(NULL, IDC_ARROW); windowClass.hbrBackground = NULL; windowClass.lpszMenuName = NULL; windowClass.lpszClassName = class_name; if (RegisterClass(&windowClass) == 0) { printfDebug("Failed to register window class\n"); return false; } return true; } static LRESULT CALLBACK dummyWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { return DefWindowProc(hwnd, msg, wParam, lParam); } bool applyPixelFormat(JNIEnv *env, HDC hdc, int iPixelFormat) { PIXELFORMATDESCRIPTOR desc; if (DescribePixelFormat(hdc, iPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &desc) == 0) { throwFormattedException(env, "DescribePixelFormat failed (%d)", GetLastError()); return false; } // make that the pixel format of the device context if (SetPixelFormat(hdc, iPixelFormat, &desc) == FALSE) { throwFormattedException(env, "SetPixelFormat failed (%d)", GetLastError()); return false; } return true; } /* * Close the window */ void closeWindow(HWND *hwnd, HDC *hdc) { // Release device context if (*hdc != NULL && *hwnd != NULL) { ReleaseDC(*hwnd, *hdc); *hdc = NULL; } // Close the window if (*hwnd != NULL) { ShowWindow(*hwnd, SW_HIDE); DestroyWindow(*hwnd); *hwnd = NULL; } } void getWindowFlags(DWORD *windowflags_return, DWORD *exstyle_return, bool undecorated, bool child_window) { DWORD exstyle, windowflags; if (undecorated) { exstyle = WS_EX_APPWINDOW; windowflags = WS_POPUP; } else if (child_window) { exstyle = 0; windowflags = WS_CHILDWINDOW; } else { exstyle = WS_EX_APPWINDOW; windowflags = WS_OVERLAPPED | WS_BORDER | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU; } windowflags = windowflags | WS_CLIPCHILDREN | WS_CLIPSIBLINGS; *windowflags_return = windowflags; *exstyle_return = exstyle; } HWND createWindow(LPCTSTR window_class_name, int x, int y, int width, int height, bool undecorated, bool child_window, HWND parent) { RECT clientSize; DWORD exstyle, windowflags; HWND new_hwnd; getWindowFlags(&windowflags, &exstyle, undecorated, child_window); clientSize.bottom = height; clientSize.left = 0; clientSize.right = width; clientSize.top = 0; AdjustWindowRectEx( &clientSize, // client-rectangle structure windowflags, // window styles FALSE, // menu-present option exstyle // extended window style ); // Create the window now, using that class: new_hwnd = CreateWindowEx ( exstyle, window_class_name, "", windowflags, x, y, clientSize.right - clientSize.left, clientSize.bottom - clientSize.top, parent, NULL, dll_handle, NULL); return new_hwnd; } static int convertToBPE(int bpp) { int bpe; switch (bpp) { case 0: bpe = 0; break; case 32: case 24: bpe = 8; break; case 16: /* Fall through */ default: bpe = 4; break; } return bpe; } static int findPixelFormatARBFromBPP(JNIEnv *env, HDC hdc, WGLExtensions *extensions, jobject pixel_format, jobject pixelFormatCaps, int bpp, bool window, bool pbuffer, bool double_buffer) { jclass cls_pixel_format = (*env)->GetObjectClass(env, pixel_format); int alpha = (int)(*env)->GetIntField(env, pixel_format, (*env)->GetFieldID(env, cls_pixel_format, "alpha", "I")); int depth = (int)(*env)->GetIntField(env, pixel_format, (*env)->GetFieldID(env, cls_pixel_format, "depth", "I")); int stencil = (int)(*env)->GetIntField(env, pixel_format, (*env)->GetFieldID(env, cls_pixel_format, "stencil", "I")); int samples = (int)(*env)->GetIntField(env, pixel_format, (*env)->GetFieldID(env, cls_pixel_format, "samples", "I")); int num_aux_buffers = (int)(*env)->GetIntField(env, pixel_format, (*env)->GetFieldID(env, cls_pixel_format, "num_aux_buffers", "I")); int accum_bpp = (int)(*env)->GetIntField(env, pixel_format, (*env)->GetFieldID(env, cls_pixel_format, "accum_bpp", "I")); int accum_alpha = (int)(*env)->GetIntField(env, pixel_format, (*env)->GetFieldID(env, cls_pixel_format, "accum_alpha", "I")); jboolean stereo = (*env)->GetBooleanField(env, pixel_format, (*env)->GetFieldID(env, cls_pixel_format, "stereo", "Z")); jboolean floating_point = (*env)->GetBooleanField(env, pixel_format, (*env)->GetFieldID(env, cls_pixel_format, "floating_point", "Z")); jboolean floating_point_packed = (*env)->GetBooleanField(env, pixel_format, (*env)->GetFieldID(env, cls_pixel_format, "floating_point_packed", "Z")); jboolean sRGB = (*env)->GetBooleanField(env, pixel_format, (*env)->GetFieldID(env, cls_pixel_format, "sRGB", "Z")); int pixel_type; int iPixelFormat; unsigned int num_formats_returned; attrib_list_t attrib_list; GLuint *pixelFormatCaps_ptr; jlong pixelFormatCapsSize; BOOL result; jlong i; int bpe = convertToBPE(bpp); if ( floating_point ) pixel_type = WGL_TYPE_RGBA_FLOAT_ARB; else if ( floating_point_packed ) pixel_type = WGL_TYPE_RGBA_UNSIGNED_FLOAT_EXT; else pixel_type = WGL_TYPE_RGBA_ARB; initAttribList(&attrib_list); if (window) { putAttrib(&attrib_list, WGL_DRAW_TO_WINDOW_ARB); putAttrib(&attrib_list, TRUE); } if (pbuffer) { putAttrib(&attrib_list, WGL_DRAW_TO_PBUFFER_ARB); putAttrib(&attrib_list, TRUE); } if (!getBooleanProperty(env, "org.lwjgl.opengl.Display.allowSoftwareOpenGL")) putAttrib(&attrib_list, WGL_ACCELERATION_ARB); putAttrib(&attrib_list, WGL_FULL_ACCELERATION_ARB); putAttrib(&attrib_list, WGL_PIXEL_TYPE_ARB); putAttrib(&attrib_list, pixel_type); putAttrib(&attrib_list, WGL_DOUBLE_BUFFER_ARB); putAttrib(&attrib_list, double_buffer ? TRUE : FALSE); putAttrib(&attrib_list, WGL_SUPPORT_OPENGL_ARB); putAttrib(&attrib_list, TRUE); putAttrib(&attrib_list, WGL_RED_BITS_ARB); putAttrib(&attrib_list, bpe); putAttrib(&attrib_list, WGL_GREEN_BITS_ARB); putAttrib(&attrib_list, bpe); putAttrib(&attrib_list, WGL_BLUE_BITS_ARB); putAttrib(&attrib_list, bpe); putAttrib(&attrib_list, WGL_ALPHA_BITS_ARB); putAttrib(&attrib_list, alpha); putAttrib(&attrib_list, WGL_DEPTH_BITS_ARB); putAttrib(&attrib_list, depth); putAttrib(&attrib_list, WGL_STENCIL_BITS_ARB); putAttrib(&attrib_list, stencil); // Assume caller checked extension availability if (samples > 0) { putAttrib(&attrib_list, WGL_SAMPLE_BUFFERS_ARB); putAttrib(&attrib_list, 1); putAttrib(&attrib_list, WGL_SAMPLES_ARB); putAttrib(&attrib_list, samples); } putAttrib(&attrib_list, WGL_ACCUM_BITS_ARB); putAttrib(&attrib_list, accum_bpp); putAttrib(&attrib_list, WGL_ACCUM_ALPHA_BITS_ARB); putAttrib(&attrib_list, accum_alpha); putAttrib(&attrib_list, WGL_STEREO_ARB); putAttrib(&attrib_list, stereo ? TRUE : FALSE); putAttrib(&attrib_list, WGL_AUX_BUFFERS_ARB); putAttrib(&attrib_list, num_aux_buffers); if (sRGB) { putAttrib(&attrib_list, WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB); putAttrib(&attrib_list, TRUE); } // Assume caller checked extension availability if (pixelFormatCaps != NULL) { pixelFormatCaps_ptr = (GLuint *)(*env)->GetDirectBufferAddress(env, pixelFormatCaps); pixelFormatCapsSize = (*env)->GetDirectBufferCapacity(env, pixelFormatCaps); for (i = 0; i < pixelFormatCapsSize; i++) putAttrib(&attrib_list, pixelFormatCaps_ptr[i]); } putAttrib(&attrib_list, 0); putAttrib(&attrib_list, 0); result = extensions->wglChoosePixelFormatARB(hdc, attrib_list.attribs, NULL, 1, &iPixelFormat, &num_formats_returned); if (result == FALSE || num_formats_returned < 1) { throwFormattedException(env, "Failed to find ARB pixel format %d %d\n", result, num_formats_returned); return -1; } return iPixelFormat; } static int findPixelFormatARB(JNIEnv *env, HDC hdc, WGLExtensions *extensions, jobject pixel_format, jobject pixelFormatCaps, bool use_hdc_bpp, bool window, bool pbuffer, bool double_buffer) { int bpp; int iPixelFormat; int fallback_bpp = 16; jclass cls_pixel_format = (*env)->GetObjectClass(env, pixel_format); if (use_hdc_bpp) { bpp = GetDeviceCaps(hdc, BITSPIXEL); iPixelFormat = findPixelFormatARBFromBPP(env, hdc, extensions, pixel_format, pixelFormatCaps, bpp, window, pbuffer, double_buffer); if ((*env)->ExceptionOccurred(env)) { (*env)->ExceptionClear(env); printfDebugJava(env, "Failed to find ARB pixel format with HDC depth %d, falling back to %d\n", bpp, fallback_bpp); bpp = fallback_bpp; } else return iPixelFormat; } else bpp = (int)(*env)->GetIntField(env, pixel_format, (*env)->GetFieldID(env, cls_pixel_format, "bpp", "I")); return findPixelFormatARBFromBPP(env, hdc, extensions, pixel_format, pixelFormatCaps, bpp, window, pbuffer, double_buffer); } /* * Find an appropriate pixel format */ static int findPixelFormatFromBPP(JNIEnv *env, HDC hdc, jobject pixel_format, int bpp, bool double_buffer) { jclass cls_pixel_format = (*env)->GetObjectClass(env, pixel_format); int alpha = (int)(*env)->GetIntField(env, pixel_format, (*env)->GetFieldID(env, cls_pixel_format, "alpha", "I")); int depth = (int)(*env)->GetIntField(env, pixel_format, (*env)->GetFieldID(env, cls_pixel_format, "depth", "I")); int stencil = (int)(*env)->GetIntField(env, pixel_format, (*env)->GetFieldID(env, cls_pixel_format, "stencil", "I")); int num_aux_buffers = (int)(*env)->GetIntField(env, pixel_format, (*env)->GetFieldID(env, cls_pixel_format, "num_aux_buffers", "I")); int accum_bpp = (int)(*env)->GetIntField(env, pixel_format, (*env)->GetFieldID(env, cls_pixel_format, "accum_bpp", "I")); int accum_alpha = (int)(*env)->GetIntField(env, pixel_format, (*env)->GetFieldID(env, cls_pixel_format, "accum_alpha", "I")); jboolean stereo = (*env)->GetBooleanField(env, pixel_format, (*env)->GetFieldID(env, cls_pixel_format, "stereo", "Z")); unsigned int flags = PFD_DRAW_TO_WINDOW | // support window PFD_SUPPORT_OPENGL | (double_buffer ? PFD_DOUBLEBUFFER : 0) | (stereo ? PFD_STEREO : 0); PIXELFORMATDESCRIPTOR desc; int iPixelFormat; PIXELFORMATDESCRIPTOR pfd = { sizeof(PIXELFORMATDESCRIPTOR), // size of this pfd 1, // version number flags, // RGBA type PFD_TYPE_RGBA, (BYTE)bpp, 0, 0, 0, 0, 0, 0, // color bits ignored (BYTE)alpha, 0, // shift bit ignored accum_bpp + accum_alpha, // no accumulation buffer 0, 0, 0, 0, // accum bits ignored (BYTE)depth, (BYTE)stencil, num_aux_buffers, PFD_MAIN_PLANE, // main layer 0, // reserved 0, 0, 0 // layer masks ignored }; // get the best available match of pixel format for the device context iPixelFormat = ChoosePixelFormat(hdc, &pfd); if (iPixelFormat == 0) { throwException(env, "Failed to choose pixel format"); return -1; } if (DescribePixelFormat(hdc, iPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &desc) == 0) { throwException(env, "Could not describe pixel format"); return -1; } if (desc.cColorBits < bpp) { throwException(env, "Insufficient color precision"); return -1; } if (desc.cAlphaBits < alpha) { throwException(env, "Insufficient alpha precision"); return -1; } if (desc.cStencilBits < stencil) { throwException(env, "Insufficient stencil precision"); return -1; } if (desc.cDepthBits < depth) { throwException(env, "Insufficient depth buffer precision"); return -1; } if ((desc.dwFlags & PFD_GENERIC_FORMAT) != 0) { jboolean allowSoftwareOpenGL = getBooleanProperty(env, "org.lwjgl.opengl.Display.allowSoftwareOpenGL"); // secondary check for software override if(!allowSoftwareOpenGL) { throwException(env, "Pixel format not accelerated"); return -1; } } if ((desc.dwFlags & flags) != flags) { throwException(env, "Capabilities not supported"); return -1; } return iPixelFormat; } static int findPixelFormatDefault(JNIEnv *env, HDC hdc, jobject pixel_format, bool use_hdc_bpp, bool double_buffer) { int bpp; int iPixelFormat; int fallback_bpp = 16; jclass cls_pixel_format = (*env)->GetObjectClass(env, pixel_format); if (use_hdc_bpp) { bpp = GetDeviceCaps(hdc, BITSPIXEL); iPixelFormat = findPixelFormatFromBPP(env, hdc, pixel_format, bpp, double_buffer); if ((*env)->ExceptionOccurred(env)) { (*env)->ExceptionClear(env); printfDebugJava(env, "Failed to find pixel format with HDC depth %d, falling back to %d\n", bpp, fallback_bpp); bpp = fallback_bpp; } else return iPixelFormat; } else bpp = (int)(*env)->GetIntField(env, pixel_format, (*env)->GetFieldID(env, cls_pixel_format, "bpp", "I")); return findPixelFormatFromBPP(env, hdc, pixel_format, bpp, double_buffer); } static bool validateAndGetExtensions(JNIEnv *env, WGLExtensions *extensions, HDC dummy_hdc, HGLRC dummy_hglrc, int samples, bool floating_point, bool floating_point_packed, bool sRGB, jobject pixelFormatCaps) { if (!wglMakeCurrent(dummy_hdc, dummy_hglrc)) { throwException(env, "Could not bind context to dummy window"); return false; } extgl_InitWGL(extensions); if (!extensions->WGL_ARB_pixel_format) { throwException(env, "No support for WGL_ARB_pixel_format"); return false; } if (samples > 0 && !extensions->WGL_ARB_multisample) { throwException(env, "No support for WGL_ARB_multisample"); return false; } /* * Apparently, some drivers don't report WGL_ARB_pixel_format_float * even though GL_ARB_color_buffer_float and WGL_ATI_color_format_float * is supported. */ if (floating_point && !(extensions->WGL_ARB_pixel_format_float || extensions->WGL_ATI_pixel_format_float)) { throwException(env, "No support for WGL_ARB_pixel_format_float nor WGL_ATI_pixel_format_float"); return false; } if (floating_point_packed && !(extensions->WGL_EXT_pixel_format_packed_float)) { throwException(env, "No support for WGL_EXT_pixel_format_packed_float"); return false; } if (sRGB && !(extensions->WGL_ARB_framebuffer_sRGB)) { throwException(env, "No support for WGL_ARB_framebuffer_sRGB"); return false; } if (pixelFormatCaps != NULL && !extensions->WGL_ARB_render_texture) { throwException(env, "No support for WGL_ARB_render_texture"); return false; } return true; } int findPixelFormatOnDC(JNIEnv *env, HDC hdc, int origin_x, int origin_y, jobject pixel_format, jobject pixelFormatCaps, bool use_hdc_bpp, bool window, bool pbuffer, bool double_buffer) { HGLRC dummy_hglrc; HDC saved_current_hdc; HGLRC saved_current_hglrc; WGLExtensions extensions; HWND dummy_hwnd; HDC dummy_hdc; int pixel_format_id; jclass cls_pixel_format = (*env)->GetObjectClass(env, pixel_format); int samples = (int)(*env)->GetIntField(env, pixel_format, (*env)->GetFieldID(env, cls_pixel_format, "samples", "I")); bool floating_point = (bool)(*env)->GetBooleanField(env, pixel_format, (*env)->GetFieldID(env, cls_pixel_format, "floating_point", "Z")); bool floating_point_packed = (bool)(*env)->GetBooleanField(env, pixel_format, (*env)->GetFieldID(env, cls_pixel_format, "floating_point_packed", "Z")); bool sRGB = (bool)(*env)->GetBooleanField(env, pixel_format, (*env)->GetFieldID(env, cls_pixel_format, "sRGB", "Z")); bool use_arb_selection = samples > 0 || floating_point || floating_point_packed || sRGB || pbuffer || pixelFormatCaps != NULL; pixel_format_id = findPixelFormatDefault(env, hdc, pixel_format, use_hdc_bpp, double_buffer); if (!(*env)->ExceptionOccurred(env) && use_arb_selection) { dummy_hwnd = createDummyWindow(origin_x, origin_y); if (dummy_hwnd == NULL) { throwException(env, "Could not create dummy window"); return -1; } dummy_hdc = GetDC(dummy_hwnd); if (!applyPixelFormat(env, dummy_hdc, pixel_format_id)) { closeWindow(&dummy_hwnd, &dummy_hdc); return -1; } dummy_hglrc = wglCreateContext(dummy_hdc); if (dummy_hglrc == NULL) { closeWindow(&dummy_hwnd, &dummy_hdc); throwException(env, "Failed to create OpenGL rendering context"); return -1; } // Save the current HDC and HGLRC to avoid disruption saved_current_hdc = wglGetCurrentDC(); saved_current_hglrc = wglGetCurrentContext(); if (validateAndGetExtensions(env, &extensions, dummy_hdc, dummy_hglrc, samples, floating_point, floating_point_packed, sRGB, pixelFormatCaps)) { pixel_format_id = findPixelFormatARB(env, hdc, &extensions, pixel_format, pixelFormatCaps, use_hdc_bpp, window, pbuffer, double_buffer); } wglMakeCurrent(saved_current_hdc, saved_current_hglrc); wglDeleteContext(dummy_hglrc); closeWindow(&dummy_hwnd, &dummy_hdc); } return pixel_format_id; } static bool registerDummyWindow() { static bool window_registered = false; if (!window_registered) { if (!registerWindow(dummyWindowProc, _CONTEXT_PRIVATE_CLASS_NAME)) { return false; } window_registered = true; } return true; } HWND createDummyWindow(int origin_x, int origin_y) { if (!registerDummyWindow()) return NULL; return createWindow(_CONTEXT_PRIVATE_CLASS_NAME, origin_x, origin_y, 1, 1, false, false, NULL); }