/* * Copyright (c) 2004, 2008, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code 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 General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ #include #include #include "sun_java2d_opengl_WGLGraphicsConfig.h" #include "jni.h" #include "jni_util.h" #include "jlong.h" #include "WGLGraphicsConfig.h" #include "WGLSurfaceData.h" /** * This is a globally shared context used when creating textures. When any * new contexts are created, they specify this context as the "share list" * context, which means any texture objects created when this shared context * is current will be available to any other context in any other thread. */ HGLRC sharedContext = 0; /** * Attempts to initialize WGL and the core OpenGL library. For this method * to return JNI_TRUE, the following must be true: * - opengl32.dll must be loaded successfully (via LoadLibrary) * - all core WGL/OGL function symbols from opengl32.dll must be * available and loaded properly * If any of these requirements are not met, this method will return * JNI_FALSE, indicating there is no hope of using WGL/OpenGL for any * GraphicsConfig in the environment. */ JNIEXPORT jboolean JNICALL Java_sun_java2d_opengl_WGLGraphicsConfig_initWGL(JNIEnv *env, jclass wglgc) { J2dRlsTraceLn(J2D_TRACE_INFO, "WGLGraphicsConfig_initWGL"); if (!OGLFuncs_OpenLibrary()) { return JNI_FALSE; } if (!OGLFuncs_InitPlatformFuncs() || !OGLFuncs_InitBaseFuncs()) { OGLFuncs_CloseLibrary(); return JNI_FALSE; } return JNI_TRUE; } /** * Disposes all memory and resources allocated for the given OGLContext. */ static void WGLGC_DestroyOGLContext(OGLContext *oglc) { WGLCtxInfo *ctxinfo; J2dTraceLn(J2D_TRACE_INFO, "WGLGC_DestroyOGLContext"); if (oglc == NULL) { J2dRlsTraceLn(J2D_TRACE_ERROR, "WGLGC_DestroyOGLContext: context is null"); return; } // at this point, this context will be current to its scratch surface, // so the following operations should be safe... OGLContext_DestroyContextResources(oglc); ctxinfo = (WGLCtxInfo *)oglc->ctxInfo; if (ctxinfo != NULL) { // release the current context before we continue j2d_wglMakeCurrent(NULL, NULL); if (ctxinfo->context != 0) { j2d_wglDeleteContext(ctxinfo->context); } if (ctxinfo->scratchSurface != 0) { if (ctxinfo->scratchSurfaceDC != 0) { j2d_wglReleasePbufferDCARB(ctxinfo->scratchSurface, ctxinfo->scratchSurfaceDC); } j2d_wglDestroyPbufferARB(ctxinfo->scratchSurface); } free(ctxinfo); } free(oglc); } /** * Disposes all memory and resources associated with the given * WGLGraphicsConfigInfo (including its native OGLContext data). */ void OGLGC_DestroyOGLGraphicsConfig(jlong pConfigInfo) { WGLGraphicsConfigInfo *wglinfo = (WGLGraphicsConfigInfo *)jlong_to_ptr(pConfigInfo); J2dTraceLn(J2D_TRACE_INFO, "OGLGC_DestroyOGLGraphicsConfig"); if (wglinfo == NULL) { J2dRlsTraceLn(J2D_TRACE_ERROR, "OGLGC_DestroyOGLGraphicsConfig: info is null"); return; } if (wglinfo->context != NULL) { WGLGC_DestroyOGLContext(wglinfo->context); } free(wglinfo); } /** * Creates a temporary (non-visible) window that can be used for querying * the OpenGL capabilities of a given device. * * REMIND: should be able to create a window on a specific device... */ HWND WGLGC_CreateScratchWindow(jint screennum) { static jboolean firsttime = JNI_TRUE; J2dTraceLn(J2D_TRACE_INFO, "WGLGC_CreateScratchWindow"); if (firsttime) { WNDCLASS wc; // setup window class information ZeroMemory(&wc, sizeof(WNDCLASS)); wc.hInstance = GetModuleHandle(NULL); wc.lpfnWndProc = DefWindowProc; wc.lpszClassName = L"Tmp"; if (RegisterClass(&wc) == 0) { J2dRlsTraceLn(J2D_TRACE_ERROR, "WGLGC_CreateScratchWindow: error registering window class"); return 0; } firsttime = JNI_FALSE; } // create scratch window return CreateWindow(L"Tmp", L"Tmp", 0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, GetModuleHandle(NULL), NULL); } /** * Returns a pixel format identifier that is suitable for Java 2D's needs * (must have a depth buffer, support for pbuffers, etc). This method will * iterate through all pixel formats (if any) that match the requested * attributes and will attempt to find a pixel format with a minimal combined * depth+stencil buffer. Note that we currently only need depth capabilities * (for shape clipping purposes), but wglChoosePixelFormatARB() will often * return a list of pixel formats with the largest depth buffer (and stencil) * sizes at the top of the list. Therefore, we scan through the whole list * to find the most VRAM-efficient pixel format. If no appropriate pixel * format can be found, this method returns 0. */ static int WGLGC_GetPixelFormatForDC(HDC hdc) { int attrs[] = { WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB, WGL_DRAW_TO_WINDOW_ARB, GL_TRUE, WGL_DRAW_TO_PBUFFER_ARB, GL_TRUE, WGL_DOUBLE_BUFFER_ARB, GL_TRUE, WGL_DEPTH_BITS_ARB, 16, // anything >= 16 will work for us 0 }; int pixfmts[32]; int chosenPixFmt = 0; int nfmts, i; // this is the initial minimum value for the combined depth+stencil size // (we initialize it to some absurdly high value; realistic values will // be much less than this number) int minDepthPlusStencil = 512; J2dRlsTraceLn(J2D_TRACE_INFO, "WGLGC_GetPixelFormatForDC"); // find all pixel formats (maximum of 32) with the provided attributes if (!j2d_wglChoosePixelFormatARB(hdc, attrs, NULL, 32, pixfmts, &nfmts)) { J2dRlsTraceLn(J2D_TRACE_ERROR, "WGLGC_GetPixelFormatForDC: error choosing pixel format"); return 0; } if (nfmts <= 0) { J2dRlsTraceLn(J2D_TRACE_ERROR, "WGLGC_GetPixelFormatForDC: no pixel formats found"); return 0; } J2dRlsTraceLn(J2D_TRACE_VERBOSE, " candidate pixel formats:"); // iterate through the list of pixel formats, looking for the one that // meets our requirements while keeping the combined depth+stencil sizes // to a minimum for (i = 0; i < nfmts; i++) { int attrKeys[] = { WGL_DEPTH_BITS_ARB, WGL_STENCIL_BITS_ARB, WGL_DOUBLE_BUFFER_ARB, WGL_ALPHA_BITS_ARB }; int attrVals[4]; int pixfmt = pixfmts[i]; int depth, stencil, db, alpha; j2d_wglGetPixelFormatAttribivARB(hdc, pixfmt, 0, 4, attrKeys, attrVals); depth = attrVals[0]; stencil = attrVals[1]; db = attrVals[2]; alpha = attrVals[3]; J2dRlsTrace5(J2D_TRACE_VERBOSE, "[V] pixfmt=%d db=%d alpha=%d depth=%d stencil=%d valid=", pixfmt, db, alpha, depth, stencil); if ((depth + stencil) < minDepthPlusStencil) { J2dRlsTrace(J2D_TRACE_VERBOSE, "true\n"); minDepthPlusStencil = depth + stencil; chosenPixFmt = pixfmt; } else { J2dRlsTrace(J2D_TRACE_VERBOSE, "false (large depth)\n"); } } if (chosenPixFmt == 0) { J2dRlsTraceLn(J2D_TRACE_ERROR, "WGLGC_GetPixelFormatForDC: could not find appropriate pixfmt"); return 0; } J2dRlsTraceLn1(J2D_TRACE_INFO, "WGLGC_GetPixelFormatForDC: chose %d as the best pixel format", chosenPixFmt); return chosenPixFmt; } /** * Sets a "basic" pixel format for the given HDC. This method is used only * for initializing a scratch window far enough such that we can load * GL/WGL extension function pointers using wglGetProcAddress. (This method * differs from the one above in that it does not use wglChoosePixelFormatARB, * which is a WGL extension function, since we can't use that method without * first loading the extension functions under a "basic" pixel format.) */ static jboolean WGLGC_SetBasicPixelFormatForDC(HDC hdc) { PIXELFORMATDESCRIPTOR pfd; int pixfmt; J2dTraceLn(J2D_TRACE_INFO, "WGLGC_SetBasicPixelFormatForDC"); // find pixel format ZeroMemory(&pfd, sizeof(PIXELFORMATDESCRIPTOR)); pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); pfd.nVersion = 1; pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; pfd.iPixelType = PFD_TYPE_RGBA; pixfmt = ChoosePixelFormat(hdc, &pfd); if (!SetPixelFormat(hdc, pixfmt, &pfd)) { J2dRlsTraceLn(J2D_TRACE_ERROR, "WGLGC_SetBasicPixelFormatForDC: error setting pixel format"); return JNI_FALSE; } return JNI_TRUE; } /** * Creates a context that is compatible with the given pixel format * identifier. Returns 0 if the context could not be created properly. */ static HGLRC WGLGC_CreateContext(jint screennum, jint pixfmt) { PIXELFORMATDESCRIPTOR pfd; HWND hwnd; HDC hdc; HGLRC hglrc; J2dTraceLn(J2D_TRACE_INFO, "WGLGC_CreateContext"); hwnd = WGLGC_CreateScratchWindow(screennum); if (hwnd == 0) { J2dRlsTraceLn(J2D_TRACE_ERROR, "WGLGC_CreateContext: could not create scratch window"); return 0; } // get the HDC for the scratch window hdc = GetDC(hwnd); if (hdc == 0) { J2dRlsTraceLn(J2D_TRACE_ERROR, "WGLGC_CreateContext: could not get dc for scratch window"); DestroyWindow(hwnd); return 0; } // set the pixel format for the scratch window if (!SetPixelFormat(hdc, pixfmt, &pfd)) { J2dRlsTraceLn(J2D_TRACE_ERROR, "WGLGC_CreateContext: error setting pixel format"); ReleaseDC(hwnd, hdc); DestroyWindow(hwnd); return 0; } // create a context based on the scratch window hglrc = j2d_wglCreateContext(hdc); // release the temporary resources ReleaseDC(hwnd, hdc); DestroyWindow(hwnd); return hglrc; } /** * Initializes the extension function pointers for the given device. Note * that under WGL, extension functions have different entrypoints depending * on the device, so we must first make a context current for the given * device before attempting to load the function pointers via * wglGetProcAddress. * * REMIND: ideally the extension function pointers would not be global, but * rather would be stored in a structure associated with the * WGLGraphicsConfig, so that we use the correct function entrypoint * depending on the destination device... */ static jboolean WGLGC_InitExtFuncs(jint screennum) { HWND hwnd; HDC hdc; HGLRC context; J2dTraceLn(J2D_TRACE_INFO, "WGLGC_InitExtFuncs"); // create a scratch window hwnd = WGLGC_CreateScratchWindow(screennum); if (hwnd == 0) { return JNI_FALSE; } // get the HDC for the scratch window hdc = GetDC(hwnd); if (hdc == 0) { DestroyWindow(hwnd); return JNI_FALSE; } // find and set a basic pixel format for the scratch window if (!WGLGC_SetBasicPixelFormatForDC(hdc)) { J2dRlsTraceLn(J2D_TRACE_ERROR, "WGLGC_InitExtFuncs: could not find appropriate pixfmt"); ReleaseDC(hwnd, hdc); DestroyWindow(hwnd); return JNI_FALSE; } // create a temporary context context = j2d_wglCreateContext(hdc); if (context == 0) { J2dRlsTraceLn(J2D_TRACE_ERROR, "WGLGC_InitExtFuncs: could not create temp WGL context"); ReleaseDC(hwnd, hdc); DestroyWindow(hwnd); return JNI_FALSE; } // make the context current so that we can load the function pointers // using wglGetProcAddress if (!j2d_wglMakeCurrent(hdc, context)) { J2dRlsTraceLn(J2D_TRACE_ERROR, "WGLGC_InitExtFuncs: could not make temp context current"); j2d_wglDeleteContext(context); ReleaseDC(hwnd, hdc); DestroyWindow(hwnd); return JNI_FALSE; } if (!OGLFuncs_InitExtFuncs()) { J2dRlsTraceLn(J2D_TRACE_ERROR, "WGLGC_InitExtFuncs: could not initialize extension funcs"); j2d_wglMakeCurrent(NULL, NULL); j2d_wglDeleteContext(context); ReleaseDC(hwnd, hdc); DestroyWindow(hwnd); return JNI_FALSE; } // destroy the temporary resources j2d_wglMakeCurrent(NULL, NULL); j2d_wglDeleteContext(context); ReleaseDC(hwnd, hdc); DestroyWindow(hwnd); return JNI_TRUE; } /** * Initializes a new OGLContext, which includes the native WGL context handle * and some other important information such as the associated pixel format. */ static OGLContext * WGLGC_InitOGLContext(jint pixfmt, HGLRC context, HPBUFFERARB scratch, HDC scratchDC, jint caps) { OGLContext *oglc; WGLCtxInfo *ctxinfo; J2dTraceLn(J2D_TRACE_INFO, "WGLGC_InitOGLContext"); oglc = (OGLContext *)malloc(sizeof(OGLContext)); if (oglc == NULL) { J2dRlsTraceLn(J2D_TRACE_ERROR, "WGLGC_InitOGLContext: could not allocate memory for oglc"); return NULL; } memset(oglc, 0, sizeof(OGLContext)); ctxinfo = (WGLCtxInfo *)malloc(sizeof(WGLCtxInfo)); if (ctxinfo == NULL) { J2dRlsTraceLn(J2D_TRACE_ERROR, "WGLGC_InitOGLContext: could not allocate memory for ctxinfo"); free(oglc); return NULL; } ctxinfo->context = context; ctxinfo->scratchSurface = scratch; ctxinfo->scratchSurfaceDC = scratchDC; oglc->ctxInfo = ctxinfo; oglc->caps = caps; return oglc; } /** * Determines whether the WGL pipeline can be used for a given GraphicsConfig * provided its screen number and visual ID. If the minimum requirements are * met, the native WGLGraphicsConfigInfo structure is initialized for this * GraphicsConfig with the necessary information (pixel format, etc.) * and a pointer to this structure is returned as a jlong. If * initialization fails at any point, zero is returned, indicating that WGL * cannot be used for this GraphicsConfig (we should fallback on the existing * DX pipeline). */ JNIEXPORT jlong JNICALL Java_sun_java2d_opengl_WGLGraphicsConfig_getWGLConfigInfo(JNIEnv *env, jclass wglgc, jint screennum, jint pixfmt) { OGLContext *oglc; PIXELFORMATDESCRIPTOR pfd; HWND hwnd; HDC hdc; HGLRC context; HPBUFFERARB scratch; HDC scratchDC; WGLGraphicsConfigInfo *wglinfo; const unsigned char *versionstr; const char *extstr; jint caps = CAPS_EMPTY; int attrKeys[] = { WGL_DOUBLE_BUFFER_ARB, WGL_ALPHA_BITS_ARB }; int attrVals[2]; J2dRlsTraceLn(J2D_TRACE_INFO, "WGLGraphicsConfig_getWGLConfigInfo"); // initialize GL/WGL extension functions if (!WGLGC_InitExtFuncs(screennum)) { J2dRlsTraceLn(J2D_TRACE_ERROR, "WGLGraphicsConfig_getWGLConfigInfo: could not init ext funcs"); return 0L; } // create a scratch window hwnd = WGLGC_CreateScratchWindow(screennum); if (hwnd == 0) { return 0L; } // get the HDC for the scratch window hdc = GetDC(hwnd); if (hdc == 0) { J2dRlsTraceLn(J2D_TRACE_ERROR, "WGLGraphicsConfig_getWGLConfigInfo: could not get dc for scratch window"); DestroyWindow(hwnd); return 0L; } if (pixfmt == 0) { // find an appropriate pixel format pixfmt = WGLGC_GetPixelFormatForDC(hdc); if (pixfmt == 0) { J2dRlsTraceLn(J2D_TRACE_ERROR, "WGLGraphicsConfig_getWGLConfigInfo: could not find appropriate pixfmt"); ReleaseDC(hwnd, hdc); DestroyWindow(hwnd); return 0L; } } if (sharedContext == 0) { // create the one shared context sharedContext = WGLGC_CreateContext(screennum, pixfmt); if (sharedContext == 0) { J2dRlsTraceLn(J2D_TRACE_ERROR, "WGLGraphicsConfig_getWGLConfigInfo: could not create shared context"); ReleaseDC(hwnd, hdc); DestroyWindow(hwnd); return 0L; } } // set the pixel format for the scratch window if (!SetPixelFormat(hdc, pixfmt, &pfd)) { J2dRlsTraceLn(J2D_TRACE_ERROR, "WGLGraphicsconfig_getWGLConfigInfo: error setting pixel format"); ReleaseDC(hwnd, hdc); DestroyWindow(hwnd); return 0L; } // create the HGLRC (context) for this WGLGraphicsConfig context = j2d_wglCreateContext(hdc); if (context == 0) { J2dRlsTraceLn(J2D_TRACE_ERROR, "WGLGraphicsConfig_getWGLConfigInfo: could not create WGL context"); ReleaseDC(hwnd, hdc); DestroyWindow(hwnd); return 0L; } // REMIND: when using wglShareLists, the two contexts must use an // identical pixel format... if (!j2d_wglShareLists(sharedContext, context)) { J2dRlsTraceLn(J2D_TRACE_WARNING, "WGLGraphicsConfig_getWGLConfigInfo: unable to share lists"); } // make the context current so that we can query the OpenGL version // and extension strings if (!j2d_wglMakeCurrent(hdc, context)) { J2dRlsTraceLn(J2D_TRACE_ERROR, "WGLGraphicsConfig_getWGLConfigInfo: could not make temp context current"); j2d_wglDeleteContext(context); ReleaseDC(hwnd, hdc); DestroyWindow(hwnd); return 0L; } // get version and extension strings versionstr = j2d_glGetString(GL_VERSION); extstr = j2d_wglGetExtensionsStringARB(hdc); OGLContext_GetExtensionInfo(env, &caps); J2dRlsTraceLn1(J2D_TRACE_INFO, "WGLGraphicsConfig_getWGLConfigInfo: OpenGL version=%s", (versionstr == NULL) ? "null" : (char *)versionstr); if (!OGLContext_IsVersionSupported(versionstr)) { J2dRlsTraceLn(J2D_TRACE_ERROR, "WGLGraphicsConfig_getWGLConfigInfo: OpenGL 1.2 is required"); j2d_wglMakeCurrent(NULL, NULL); j2d_wglDeleteContext(context); ReleaseDC(hwnd, hdc); DestroyWindow(hwnd); return 0L; } // check for required WGL extensions if (!OGLContext_IsExtensionAvailable(extstr, "WGL_ARB_pbuffer") || !OGLContext_IsExtensionAvailable(extstr, "WGL_ARB_make_current_read")|| !OGLContext_IsExtensionAvailable(extstr, "WGL_ARB_pixel_format")) { J2dRlsTraceLn(J2D_TRACE_ERROR, "WGLGraphicsConfig_getWGLConfigInfo: required ext(s) unavailable"); j2d_wglMakeCurrent(NULL, NULL); j2d_wglDeleteContext(context); ReleaseDC(hwnd, hdc); DestroyWindow(hwnd); return 0L; } // get config-specific capabilities j2d_wglGetPixelFormatAttribivARB(hdc, pixfmt, 0, 2, attrKeys, attrVals); if (attrVals[0]) { caps |= CAPS_DOUBLEBUFFERED; } if (attrVals[1] > 0) { caps |= CAPS_STORED_ALPHA; } // create the scratch pbuffer scratch = j2d_wglCreatePbufferARB(hdc, pixfmt, 1, 1, NULL); // destroy the temporary resources j2d_wglMakeCurrent(NULL, NULL); ReleaseDC(hwnd, hdc); DestroyWindow(hwnd); if (scratch == 0) { J2dRlsTraceLn(J2D_TRACE_ERROR, "WGLGraphicsConfig_getWGLConfigInfo: could not create scratch surface"); j2d_wglDeleteContext(context); return 0L; } // get the HDC for the scratch pbuffer scratchDC = j2d_wglGetPbufferDCARB(scratch); if (scratchDC == 0) { J2dRlsTraceLn(J2D_TRACE_ERROR, "WGLGraphicsConfig_getWGLConfigInfo: could not get hdc for scratch surface"); j2d_wglDeleteContext(context); j2d_wglDestroyPbufferARB(scratch); return 0L; } // initialize the OGLContext, which wraps the pixfmt and HGLRC (context) oglc = WGLGC_InitOGLContext(pixfmt, context, scratch, scratchDC, caps); if (oglc == NULL) { J2dRlsTraceLn(J2D_TRACE_ERROR, "WGLGraphicsConfig_getWGLConfigInfo: could not create oglc"); j2d_wglDeleteContext(context); j2d_wglReleasePbufferDCARB(scratch, scratchDC); j2d_wglDestroyPbufferARB(scratch); return 0L; } J2dTraceLn(J2D_TRACE_VERBOSE, "WGLGraphicsConfig_getWGLConfigInfo: finished checking dependencies"); // create the WGLGraphicsConfigInfo record for this config wglinfo = (WGLGraphicsConfigInfo *)malloc(sizeof(WGLGraphicsConfigInfo)); if (wglinfo == NULL) { J2dRlsTraceLn(J2D_TRACE_ERROR, "WGLGraphicsConfig_getWGLConfigInfo: could not allocate memory for wglinfo"); WGLGC_DestroyOGLContext(oglc); return 0L; } wglinfo->screen = screennum; wglinfo->pixfmt = pixfmt; wglinfo->context = oglc; return ptr_to_jlong(wglinfo); } JNIEXPORT jint JNICALL Java_sun_java2d_opengl_WGLGraphicsConfig_getDefaultPixFmt(JNIEnv *env, jclass wglgc, jint screennum) { J2dTraceLn(J2D_TRACE_INFO, "WGLGraphicsConfig_getDefaultPixFmt"); // REMIND: eventually we should implement this method so that it finds // the most appropriate default pixel format for the given // device; for now, we'll just return 0, and then we'll find // an appropriate pixel format in WGLGC_GetWGLConfigInfo()... return 0; } JNIEXPORT jint JNICALL Java_sun_java2d_opengl_WGLGraphicsConfig_getOGLCapabilities(JNIEnv *env, jclass wglgc, jlong configInfo) { WGLGraphicsConfigInfo *wglinfo = (WGLGraphicsConfigInfo *)jlong_to_ptr(configInfo); J2dTraceLn(J2D_TRACE_INFO, "WGLGraphicsConfig_getOGLCapabilities"); if (wglinfo == NULL || wglinfo->context == NULL) { return CAPS_EMPTY; } return wglinfo->context->caps; }