/* * Copyright (c) 2003, 2015, 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_GLXGraphicsConfig.h" #include "jni.h" #include "jlong.h" #include "GLXGraphicsConfig.h" #include "GLXSurfaceData.h" #include "awt_GraphicsEnv.h" #include "awt_util.h" #ifndef HEADLESS extern Bool usingXinerama; /** * 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. */ static GLXContext sharedContext = 0; /** * Attempts to initialize GLX and the core OpenGL library. For this method * to return JNI_TRUE, the following must be true: * - libGL must be loaded successfully (via dlopen) * - all function symbols from libGL must be available and loaded properly * - the GLX extension must be available through X11 * - client GLX version must be >= 1.3 * If any of these requirements are not met, this method will return * JNI_FALSE, indicating there is no hope of using GLX/OpenGL for any * GraphicsConfig in the environment. */ static jboolean GLXGC_InitGLX() { int errorbase, eventbase; const char *version; J2dRlsTraceLn(J2D_TRACE_INFO, "GLXGC_InitGLX"); if (!OGLFuncs_OpenLibrary()) { return JNI_FALSE; } if (!OGLFuncs_InitPlatformFuncs() || !OGLFuncs_InitBaseFuncs() || !OGLFuncs_InitExtFuncs()) { OGLFuncs_CloseLibrary(); return JNI_FALSE; } if (!j2d_glXQueryExtension(awt_display, &errorbase, &eventbase)) { J2dRlsTraceLn(J2D_TRACE_ERROR, "GLXGC_InitGLX: GLX extension is not present"); OGLFuncs_CloseLibrary(); return JNI_FALSE; } version = j2d_glXGetClientString(awt_display, GLX_VERSION); if (version == NULL) { J2dRlsTraceLn(J2D_TRACE_ERROR, "GLXGC_InitGLX: could not query GLX version"); OGLFuncs_CloseLibrary(); return JNI_FALSE; } // we now only verify that the client GLX version is >= 1.3 (if the // server does not support GLX 1.3, then we will find that out later // when we attempt to create a GLXFBConfig) J2dRlsTraceLn1(J2D_TRACE_INFO, "GLXGC_InitGLX: client GLX version=%s", version); if (!((version[0] == '1' && version[2] >= '3') || (version[0] > '1'))) { J2dRlsTraceLn(J2D_TRACE_ERROR, "GLXGC_InitGLX: invalid GLX version; 1.3 is required"); OGLFuncs_CloseLibrary(); return JNI_FALSE; } return JNI_TRUE; } /** * Returns JNI_TRUE if GLX is available for the current display. Note that * this method will attempt to initialize GLX (and all the necessary function * symbols) if it has not been already. The AWT_LOCK must be acquired before * calling this method. */ jboolean GLXGC_IsGLXAvailable() { static jboolean glxAvailable = JNI_FALSE; static jboolean firstTime = JNI_TRUE; J2dTraceLn(J2D_TRACE_INFO, "GLXGC_IsGLXAvailable"); if (firstTime) { glxAvailable = GLXGC_InitGLX(); firstTime = JNI_FALSE; } return glxAvailable; } /** * Disposes all memory and resources allocated for the given OGLContext. */ static void GLXGC_DestroyOGLContext(OGLContext *oglc) { GLXCtxInfo *ctxinfo; J2dTraceLn(J2D_TRACE_INFO, "GLXGC_DestroyOGLContext"); if (oglc == NULL) { J2dRlsTraceLn(J2D_TRACE_ERROR, "GLXGC_DestroyOGLContext: context is null"); return; } // at this point, this context will be current to its scratch surface // so the following GL/GLX operations should be safe... OGLContext_DestroyContextResources(oglc); ctxinfo = (GLXCtxInfo *)oglc->ctxInfo; if (ctxinfo != NULL) { // release the current context before we continue j2d_glXMakeContextCurrent(awt_display, None, None, NULL); if (ctxinfo->context != 0) { j2d_glXDestroyContext(awt_display, ctxinfo->context); } if (ctxinfo->scratchSurface != 0) { j2d_glXDestroyPbuffer(awt_display, ctxinfo->scratchSurface); } free(ctxinfo); } free(oglc); } /** * Disposes all memory and resources associated with the given * GLXGraphicsConfigInfo (including its native OGLContext data). */ void OGLGC_DestroyOGLGraphicsConfig(jlong pConfigInfo) { GLXGraphicsConfigInfo *glxinfo = (GLXGraphicsConfigInfo *)jlong_to_ptr(pConfigInfo); J2dTraceLn(J2D_TRACE_INFO, "OGLGC_DestroyOGLGraphicsConfig"); if (glxinfo == NULL) { J2dRlsTraceLn(J2D_TRACE_ERROR, "OGLGC_DestroyOGLGraphicsConfig: info is null"); return; } if (glxinfo->context != NULL) { GLXGC_DestroyOGLContext(glxinfo->context); } free(glxinfo); } /** * Attempts to create a new GLXFBConfig for the requested screen and visual. * If visualid is 0, this method will iterate through all GLXFBConfigs (if * any) that match the requested attributes and will attempt to find an * fbconfig with a minimal combined depth+stencil buffer. Note that we * currently only need depth capabilities (for shape clipping purposes), but * glXChooseFBConfig() will often return a list of fbconfigs 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 fbconfig. * If visualid is non-zero, the GLXFBConfig associated with the given visual * is chosen (assuming it meets the requested attributes). If there are no * valid GLXFBConfigs available, this method returns 0. */ static GLXFBConfig GLXGC_InitFBConfig(JNIEnv *env, jint screennum, VisualID visualid) { GLXFBConfig *fbconfigs; GLXFBConfig chosenConfig = 0; int nconfs, i; int attrlist[] = {GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT | GLX_PBUFFER_BIT, GLX_RENDER_TYPE, GLX_RGBA_BIT, GLX_CONFIG_CAVEAT, GLX_NONE, // avoid "slow" configs GLX_DEPTH_SIZE, 16, // anything >= 16 will work for us 0}; // 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; J2dRlsTraceLn2(J2D_TRACE_INFO, "GLXGC_InitFBConfig: scn=%d vis=0x%x", screennum, visualid); // find all fbconfigs for this screen with the provided attributes fbconfigs = j2d_glXChooseFBConfig(awt_display, screennum, attrlist, &nconfs); if ((fbconfigs == NULL) || (nconfs <= 0)) { J2dRlsTraceLn(J2D_TRACE_ERROR, "GLXGC_InitFBConfig: could not find any valid fbconfigs"); return 0; } J2dRlsTraceLn(J2D_TRACE_VERBOSE, " candidate fbconfigs:"); // iterate through the list of fbconfigs, looking for the one that matches // the requested VisualID and supports RGBA rendering as well as the // creation of windows and pbuffers for (i = 0; i < nconfs; i++) { XVisualInfo *xvi; VisualID fbvisualid; GLXFBConfig fbc = fbconfigs[i]; // get VisualID from GLXFBConfig xvi = j2d_glXGetVisualFromFBConfig(awt_display, fbc); if (xvi == NULL) { continue; } fbvisualid = xvi->visualid; XFree(xvi); if (visualid == 0 || visualid == fbvisualid) { int dtype, rtype, depth, stencil, db, alpha, gamma; // get GLX-specific attributes from GLXFBConfig j2d_glXGetFBConfigAttrib(awt_display, fbc, GLX_DRAWABLE_TYPE, &dtype); j2d_glXGetFBConfigAttrib(awt_display, fbc, GLX_RENDER_TYPE, &rtype); j2d_glXGetFBConfigAttrib(awt_display, fbc, GLX_DEPTH_SIZE, &depth); j2d_glXGetFBConfigAttrib(awt_display, fbc, GLX_STENCIL_SIZE, &stencil); // these attributes don't affect our decision, but they are // interesting for trace logs, so we will query them anyway j2d_glXGetFBConfigAttrib(awt_display, fbc, GLX_DOUBLEBUFFER, &db); j2d_glXGetFBConfigAttrib(awt_display, fbc, GLX_ALPHA_SIZE, &alpha); J2dRlsTrace5(J2D_TRACE_VERBOSE, "[V] id=0x%x db=%d alpha=%d depth=%d stencil=%d valid=", fbvisualid, db, alpha, depth, stencil); #ifdef __sparc /* * Sun's OpenGL implementation will always * return at least two GLXFBConfigs (visuals) from * glXChooseFBConfig(). The first will be a linear (gamma * corrected) visual; the second will have the same capabilities * as the first, except it will be a non-linear (non-gamma * corrected) visual, which is the one we want, otherwise * everything will look "washed out". So we will reject any * visuals that have gamma values other than 1.0 (the value * returned by glXGetFBConfigAttrib() will be scaled * by 100, so 100 corresponds to a gamma value of 1.0, 220 * corresponds to 2.2, and so on). */ j2d_glXGetFBConfigAttrib(awt_display, fbc, GLX_GAMMA_VALUE_SUN, &gamma); if (gamma != 100) { J2dRlsTrace(J2D_TRACE_VERBOSE, "false (linear visual)\n"); continue; } #endif /* __sparc */ if ((dtype & GLX_WINDOW_BIT) && (dtype & GLX_PBUFFER_BIT) && (rtype & GLX_RGBA_BIT) && (depth >= 16)) { if (visualid == 0) { // when visualid == 0, we loop through all configs // looking for an fbconfig that has the smallest combined // depth+stencil size (this keeps VRAM usage to a minimum) if ((depth + stencil) < minDepthPlusStencil) { J2dRlsTrace(J2D_TRACE_VERBOSE, "true\n"); minDepthPlusStencil = depth + stencil; chosenConfig = fbc; } else { J2dRlsTrace(J2D_TRACE_VERBOSE, "false (large depth)\n"); } continue; } else { // in this case, visualid == fbvisualid, which means // we've found a valid fbconfig corresponding to the // requested VisualID, so break out of the loop J2dRlsTrace(J2D_TRACE_VERBOSE, "true\n"); chosenConfig = fbc; break; } } else { J2dRlsTrace(J2D_TRACE_VERBOSE, "false (bad match)\n"); } } } // free the list of fbconfigs XFree(fbconfigs); if (chosenConfig == 0) { J2dRlsTraceLn(J2D_TRACE_ERROR, "GLXGC_InitFBConfig: could not find an appropriate fbconfig"); return 0; } return chosenConfig; } /** * Returns the X11 VisualID that corresponds to the best GLXFBConfig for the * given screen. If no valid visual could be found, this method returns zero. * Note that this method will attempt to initialize GLX (and all the * necessary function symbols) if it has not been already. The AWT_LOCK * must be acquired before calling this method. */ VisualID GLXGC_FindBestVisual(JNIEnv *env, jint screen) { GLXFBConfig fbc; XVisualInfo *xvi; VisualID visualid; J2dRlsTraceLn1(J2D_TRACE_INFO, "GLXGC_FindBestVisual: scn=%d", screen); if (!GLXGC_IsGLXAvailable()) { J2dRlsTraceLn(J2D_TRACE_ERROR, "GLXGC_FindBestVisual: could not initialize GLX"); return 0; } fbc = GLXGC_InitFBConfig(env, screen, 0); if (fbc == 0) { J2dRlsTraceLn(J2D_TRACE_ERROR, "GLXGC_FindBestVisual: could not find best visual"); return 0; } xvi = j2d_glXGetVisualFromFBConfig(awt_display, fbc); if (xvi == NULL) { J2dRlsTraceLn(J2D_TRACE_ERROR, "GLXGC_FindBestVisual: could not get visual for fbconfig"); return 0; } visualid = xvi->visualid; XFree(xvi); J2dRlsTraceLn2(J2D_TRACE_INFO, "GLXGC_FindBestVisual: chose 0x%x as the best visual for screen %d", visualid, screen); return visualid; } /** * Creates a scratch pbuffer, which can be used to make a context current * for extension queries, etc. */ static GLXPbuffer GLXGC_InitScratchPbuffer(GLXFBConfig fbconfig) { int pbattrlist[] = {GLX_PBUFFER_WIDTH, 4, GLX_PBUFFER_HEIGHT, 4, GLX_PRESERVED_CONTENTS, GL_FALSE, 0}; J2dTraceLn(J2D_TRACE_INFO, "GLXGC_InitScratchPbuffer"); return j2d_glXCreatePbuffer(awt_display, fbconfig, pbattrlist); } /** * Initializes a new OGLContext, which includes the native GLXContext handle * and some other important information such as the associated GLXFBConfig. */ static OGLContext * GLXGC_InitOGLContext(GLXFBConfig fbconfig, GLXContext context, GLXPbuffer scratch, jint caps) { OGLContext *oglc; GLXCtxInfo *ctxinfo; J2dTraceLn(J2D_TRACE_INFO, "GLXGC_InitOGLContext"); oglc = (OGLContext *)malloc(sizeof(OGLContext)); if (oglc == NULL) { J2dRlsTraceLn(J2D_TRACE_ERROR, "GLXGC_InitOGLContext: could not allocate memory for oglc"); return NULL; } memset(oglc, 0, sizeof(OGLContext)); ctxinfo = (GLXCtxInfo *)malloc(sizeof(GLXCtxInfo)); if (ctxinfo == NULL) { J2dRlsTraceLn(J2D_TRACE_ERROR, "GLXGC_InitOGLContext: could not allocate memory for ctxinfo"); free(oglc); return NULL; } ctxinfo->fbconfig = fbconfig; ctxinfo->context = context; ctxinfo->scratchSurface = scratch; oglc->ctxInfo = ctxinfo; oglc->caps = caps; return oglc; } #endif /* !HEADLESS */ /** * Determines whether the GLX pipeline can be used for a given GraphicsConfig * provided its screen number and visual ID. If the minimum requirements are * met, the native GLXGraphicsConfigInfo structure is initialized for this * GraphicsConfig with the necessary information (GLXFBConfig, etc.) * and a pointer to this structure is returned as a jlong. If * initialization fails at any point, zero is returned, indicating that GLX * cannot be used for this GraphicsConfig (we should fallback on the existing * X11 pipeline). */ JNIEXPORT jlong JNICALL Java_sun_java2d_opengl_GLXGraphicsConfig_getGLXConfigInfo(JNIEnv *env, jclass glxgc, jint screennum, jint visnum) { #ifndef HEADLESS OGLContext *oglc; GLXFBConfig fbconfig; GLXContext context; GLXPbuffer scratch; GLXGraphicsConfigInfo *glxinfo; jint caps = CAPS_EMPTY; int db; const unsigned char *versionstr; J2dRlsTraceLn(J2D_TRACE_INFO, "GLXGraphicsConfig_getGLXConfigInfo"); if (usingXinerama) { // when Xinerama is enabled, the screen ID needs to be 0 screennum = 0; } fbconfig = GLXGC_InitFBConfig(env, screennum, (VisualID)visnum); if (fbconfig == 0) { J2dRlsTraceLn(J2D_TRACE_ERROR, "GLXGraphicsConfig_getGLXConfigInfo: could not create fbconfig"); return 0L; } if (sharedContext == 0) { // create the one shared context sharedContext = j2d_glXCreateNewContext(awt_display, fbconfig, GLX_RGBA_TYPE, 0, GL_TRUE); if (sharedContext == 0) { J2dRlsTraceLn(J2D_TRACE_ERROR, "GLXGraphicsConfig_getGLXConfigInfo: could not create shared context"); return 0L; } } // create the GLXContext for this GLXGraphicsConfig context = j2d_glXCreateNewContext(awt_display, fbconfig, GLX_RGBA_TYPE, sharedContext, GL_TRUE); if (context == 0) { J2dRlsTraceLn(J2D_TRACE_ERROR, "GLXGraphicsConfig_getGLXConfigInfo: could not create GLX context"); return 0L; } // this is pretty sketchy, but it seems to be the easiest way to create // some form of GLXDrawable using only the display and a GLXFBConfig // (in order to make the context current for checking the version, // extensions, etc)... scratch = GLXGC_InitScratchPbuffer(fbconfig); if (scratch == 0) { J2dRlsTraceLn(J2D_TRACE_ERROR, "GLXGraphicsConfig_getGLXConfigInfo: could not create scratch pbuffer"); j2d_glXDestroyContext(awt_display, context); return 0L; } // the context must be made current before we can query the // version and extension strings j2d_glXMakeContextCurrent(awt_display, scratch, scratch, context); #ifdef __sparc /* * 6438225: The software rasterizer used by Sun's OpenGL libraries * for certain boards has quality issues, and besides, performance * of these boards is not high enough to justify the use of the * OpenGL-based Java 2D pipeline. If we detect one of the following * boards via the GL_RENDERER string, just give up: * - FFB[2[+]] ("Creator[3D]") * - PGX-series ("m64") * - AFB ("Elite3D") */ { const char *renderer = (const char *)j2d_glGetString(GL_RENDERER); J2dRlsTraceLn1(J2D_TRACE_VERBOSE, "GLXGraphicsConfig_getGLXConfigInfo: detected renderer (%s)", (renderer == NULL) ? "null" : renderer); if (renderer == NULL || strncmp(renderer, "Creator", 7) == 0 || strncmp(renderer, "SUNWm64", 7) == 0 || strncmp(renderer, "Elite", 5) == 0) { J2dRlsTraceLn1(J2D_TRACE_ERROR, "GLXGraphicsConfig_getGLXConfigInfo: unsupported board (%s)", (renderer == NULL) ? "null" : renderer); j2d_glXMakeContextCurrent(awt_display, None, None, NULL); j2d_glXDestroyPbuffer(awt_display, scratch); j2d_glXDestroyContext(awt_display, context); return 0L; } } #endif /* __sparc */ versionstr = j2d_glGetString(GL_VERSION); OGLContext_GetExtensionInfo(env, &caps); // destroy the temporary resources j2d_glXMakeContextCurrent(awt_display, None, None, NULL); J2dRlsTraceLn1(J2D_TRACE_INFO, "GLXGraphicsConfig_getGLXConfigInfo: OpenGL version=%s", (versionstr == NULL) ? "null" : (char *)versionstr); if (!OGLContext_IsVersionSupported(versionstr)) { J2dRlsTraceLn(J2D_TRACE_ERROR, "GLXGraphicsConfig_getGLXConfigInfo: OpenGL 1.2 is required"); j2d_glXDestroyPbuffer(awt_display, scratch); j2d_glXDestroyContext(awt_display, context); return 0L; } // get config-specific capabilities j2d_glXGetFBConfigAttrib(awt_display, fbconfig, GLX_DOUBLEBUFFER, &db); if (db) { caps |= CAPS_DOUBLEBUFFERED; } // initialize the OGLContext, which wraps the GLXFBConfig and GLXContext oglc = GLXGC_InitOGLContext(fbconfig, context, scratch, caps); if (oglc == NULL) { J2dRlsTraceLn(J2D_TRACE_ERROR, "GLXGraphicsConfig_getGLXConfigInfo: could not create oglc"); j2d_glXDestroyPbuffer(awt_display, scratch); j2d_glXDestroyContext(awt_display, context); return 0L; } J2dTraceLn(J2D_TRACE_VERBOSE, "GLXGraphicsConfig_getGLXConfigInfo: finished checking dependencies"); // create the GLXGraphicsConfigInfo record for this config glxinfo = (GLXGraphicsConfigInfo *)malloc(sizeof(GLXGraphicsConfigInfo)); if (glxinfo == NULL) { J2dRlsTraceLn(J2D_TRACE_ERROR, "GLXGraphicsConfig_getGLXConfigInfo: could not allocate memory for glxinfo"); GLXGC_DestroyOGLContext(oglc); return 0L; } glxinfo->screen = screennum; glxinfo->visual = visnum; glxinfo->context = oglc; glxinfo->fbconfig = fbconfig; return ptr_to_jlong(glxinfo); #else return 0L; #endif /* !HEADLESS */ } JNIEXPORT void JNICALL Java_sun_java2d_opengl_GLXGraphicsConfig_initConfig(JNIEnv *env, jobject glxgc, jlong aData, jlong configInfo) { #ifndef HEADLESS GLXGraphicsConfigInfo *glxinfo; AwtGraphicsConfigDataPtr configData = (AwtGraphicsConfigDataPtr)jlong_to_ptr(aData); J2dTraceLn(J2D_TRACE_INFO, "GLXGraphicsConfig_initConfig"); if (configData == NULL) { JNU_ThrowNullPointerException(env, "Native GraphicsConfig missing"); return; } glxinfo = (GLXGraphicsConfigInfo *)jlong_to_ptr(configInfo); if (glxinfo == NULL) { JNU_ThrowNullPointerException(env, "GLXGraphicsConfigInfo data missing"); return; } configData->glxInfo = glxinfo; #endif /* !HEADLESS */ } JNIEXPORT jint JNICALL Java_sun_java2d_opengl_GLXGraphicsConfig_getOGLCapabilities(JNIEnv *env, jclass glxgc, jlong configInfo) { #ifndef HEADLESS GLXGraphicsConfigInfo *glxinfo = (GLXGraphicsConfigInfo *)jlong_to_ptr(configInfo); J2dTraceLn(J2D_TRACE_INFO, "GLXGraphicsConfig_getOGLCapabilities"); if (glxinfo == NULL || glxinfo->context == NULL) { return CAPS_EMPTY; } return glxinfo->context->caps; #else return CAPS_EMPTY; #endif /* !HEADLESS */ }