jdk/src/java.desktop/windows/native/libawt/java2d/opengl/WGLGraphicsConfig.c
changeset 26751 70bac69b37c9
parent 25859 3317bb8137f4
child 31661 a5cb86f2253b
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/windows/native/libawt/java2d/opengl/WGLGraphicsConfig.c	Fri Sep 19 09:41:05 2014 -0700
@@ -0,0 +1,719 @@
+/*
+ * 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 <stdlib.h>
+#include <string.h>
+
+#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;
+}