/*
* Copyright (c) 2003, 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_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, 1,
GLX_PBUFFER_HEIGHT, 1,
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, alpha;
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;
}
j2d_glXGetFBConfigAttrib(awt_display, fbconfig, GLX_ALPHA_SIZE, &alpha);
if (alpha > 0) {
caps |= CAPS_STORED_ALPHA;
}
// 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 */
}