1 /*
   2  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 #include <jlong.h>
  27 
  28 #include "sun_java2d_opengl_GLXSurfaceData.h"
  29 
  30 #include "OGLRenderQueue.h"
  31 #include "GLXGraphicsConfig.h"
  32 #include "GLXSurfaceData.h"
  33 #include "awt_Component.h"
  34 #include "awt_GraphicsEnv.h"
  35 
  36 /**
  37  * The methods in this file implement the native windowing system specific
  38  * layer (GLX) for the OpenGL-based Java 2D pipeline.
  39  */
  40 
  41 #ifndef HEADLESS
  42 
  43 extern LockFunc       OGLSD_Lock;
  44 extern GetRasInfoFunc OGLSD_GetRasInfo;
  45 extern UnlockFunc     OGLSD_Unlock;
  46 extern DisposeFunc    OGLSD_Dispose;
  47 
  48 extern struct MComponentPeerIDs mComponentPeerIDs;
  49 
  50 extern void
  51     OGLSD_SetNativeDimensions(JNIEnv *env, OGLSDOps *oglsdo, jint w, jint h);
  52 
  53 jboolean surfaceCreationFailed = JNI_FALSE;
  54 
  55 #endif /* !HEADLESS */
  56 
  57 JNIEXPORT void JNICALL
  58 Java_sun_java2d_opengl_GLXSurfaceData_initOps(JNIEnv *env, jobject glxsd,
  59                                               jobject peer, jlong aData)
  60 {
  61 #ifndef HEADLESS
  62     OGLSDOps *oglsdo = (OGLSDOps *)SurfaceData_InitOps(env, glxsd,
  63                                                        sizeof(OGLSDOps));
  64     GLXSDOps *glxsdo = (GLXSDOps *)malloc(sizeof(GLXSDOps));
  65 
  66     J2dTraceLn(J2D_TRACE_INFO, "GLXSurfaceData_initOps");
  67 
  68     if (oglsdo == NULL) {
  69         JNU_ThrowOutOfMemoryError(env, "Initialization of SurfaceData failed.");
  70         return;
  71     }
  72 
  73     if (glxsdo == NULL) {
  74         JNU_ThrowOutOfMemoryError(env, "creating native GLX ops");
  75         return;
  76     }
  77 
  78     oglsdo->privOps = glxsdo;
  79 
  80     oglsdo->sdOps.Lock       = OGLSD_Lock;
  81     oglsdo->sdOps.GetRasInfo = OGLSD_GetRasInfo;
  82     oglsdo->sdOps.Unlock     = OGLSD_Unlock;
  83     oglsdo->sdOps.Dispose    = OGLSD_Dispose;
  84 
  85     oglsdo->drawableType = OGLSD_UNDEFINED;
  86     oglsdo->activeBuffer = GL_FRONT;
  87     oglsdo->needsInit = JNI_TRUE;
  88 
  89 #ifdef XAWT
  90     if (peer != NULL) {
  91         glxsdo->window = JNU_CallMethodByName(env, NULL, peer,
  92                                               "getContentWindow", "()J").j;
  93     } else {
  94         glxsdo->window = 0;
  95     }
  96 #else
  97     if (peer != NULL) {
  98         struct ComponentData *cdata;
  99         cdata = (struct ComponentData *)
 100             JNU_GetLongFieldAsPtr(env, peer, mComponentPeerIDs.pData);
 101         if (cdata == NULL) {
 102             free(glxsdo);
 103             JNU_ThrowNullPointerException(env, "Component data missing");
 104             return;
 105         }
 106         if (cdata->widget == NULL) {
 107             free(glxsdo);
 108             JNU_ThrowInternalError(env, "Widget is NULL in initOps");
 109             return;
 110         }
 111         glxsdo->widget = cdata->widget;
 112     } else {
 113         glxsdo->widget = NULL;
 114     }
 115 #endif
 116 
 117     glxsdo->configData = (AwtGraphicsConfigDataPtr)jlong_to_ptr(aData);
 118     if (glxsdo->configData == NULL) {
 119         free(glxsdo);
 120         JNU_ThrowNullPointerException(env,
 121                                  "Native GraphicsConfig data block missing");
 122         return;
 123     }
 124 
 125     if (glxsdo->configData->glxInfo == NULL) {
 126         free(glxsdo);
 127         JNU_ThrowNullPointerException(env, "GLXGraphicsConfigInfo missing");
 128         return;
 129     }
 130 #endif /* HEADLESS */
 131 }
 132 
 133 #ifndef HEADLESS
 134 
 135 /**
 136  * This function disposes of any native windowing system resources associated
 137  * with this surface.  For instance, if the given OGLSDOps is of type
 138  * OGLSD_PBUFFER, this method implementation will destroy the actual pbuffer
 139  * surface.
 140  */
 141 void
 142 OGLSD_DestroyOGLSurface(JNIEnv *env, OGLSDOps *oglsdo)
 143 {
 144     GLXSDOps *glxsdo = (GLXSDOps *)oglsdo->privOps;
 145 
 146     J2dTraceLn(J2D_TRACE_INFO, "OGLSD_DestroyOGLSurface");
 147 
 148     if (oglsdo->drawableType == OGLSD_PBUFFER) {
 149         if (glxsdo->drawable != 0) {
 150             j2d_glXDestroyPbuffer(awt_display, glxsdo->drawable);
 151             glxsdo->drawable = 0;
 152         }
 153     } else if (oglsdo->drawableType == OGLSD_WINDOW) {
 154         // X Window is free'd later by AWT code...
 155     }
 156 }
 157 
 158 /**
 159  * Makes the given context current to its associated "scratch" surface.  If
 160  * the operation is successful, this method will return JNI_TRUE; otherwise,
 161  * returns JNI_FALSE.
 162  */
 163 static jboolean
 164 GLXSD_MakeCurrentToScratch(JNIEnv *env, OGLContext *oglc)
 165 {
 166     GLXCtxInfo *ctxInfo;
 167 
 168     J2dTraceLn(J2D_TRACE_INFO, "GLXSD_MakeCurrentToScratch");
 169 
 170     if (oglc == NULL) {
 171         J2dRlsTraceLn(J2D_TRACE_ERROR,
 172                       "GLXSD_MakeCurrentToScratch: context is null");
 173         return JNI_FALSE;
 174     }
 175 
 176     ctxInfo = (GLXCtxInfo *)oglc->ctxInfo;
 177     if (!j2d_glXMakeContextCurrent(awt_display,
 178                                    ctxInfo->scratchSurface,
 179                                    ctxInfo->scratchSurface,
 180                                    ctxInfo->context))
 181     {
 182         J2dRlsTraceLn(J2D_TRACE_ERROR,
 183                       "GLXSD_MakeCurrentToScratch: could not make current");
 184         return JNI_FALSE;
 185     }
 186 
 187     return JNI_TRUE;
 188 }
 189 
 190 /**
 191  * Returns a pointer (as a jlong) to the native GLXGraphicsConfigInfo
 192  * associated with the given OGLSDOps.  This method can be called from
 193  * shared code to retrieve the native GraphicsConfig data in a platform-
 194  * independent manner.
 195  */
 196 jlong
 197 OGLSD_GetNativeConfigInfo(OGLSDOps *oglsdo)
 198 {
 199     GLXSDOps *glxsdo;
 200 
 201     if (oglsdo == NULL) {
 202         J2dRlsTraceLn(J2D_TRACE_ERROR,
 203                       "OGLSD_GetNativeConfigInfo: ops are null");
 204         return 0L;
 205     }
 206 
 207     glxsdo = (GLXSDOps *)oglsdo->privOps;
 208     if (glxsdo == NULL) {
 209         J2dRlsTraceLn(J2D_TRACE_ERROR,
 210                       "OGLSD_GetNativeConfigInfo: glx ops are null");
 211         return 0L;
 212     }
 213 
 214     if (glxsdo->configData == NULL) {
 215         J2dRlsTraceLn(J2D_TRACE_ERROR,
 216                       "OGLSD_GetNativeConfigInfo: config data is null");
 217         return 0L;
 218     }
 219 
 220     return ptr_to_jlong(glxsdo->configData->glxInfo);
 221 }
 222 
 223 /**
 224  * Makes the given GraphicsConfig's context current to its associated
 225  * "scratch" surface.  If there is a problem making the context current,
 226  * this method will return NULL; otherwise, returns a pointer to the
 227  * OGLContext that is associated with the given GraphicsConfig.
 228  */
 229 OGLContext *
 230 OGLSD_SetScratchSurface(JNIEnv *env, jlong pConfigInfo)
 231 {
 232     GLXGraphicsConfigInfo *glxInfo =
 233         (GLXGraphicsConfigInfo *)jlong_to_ptr(pConfigInfo);
 234     OGLContext *oglc;
 235 
 236     J2dTraceLn(J2D_TRACE_INFO, "OGLSD_SetScratchContext");
 237 
 238     if (glxInfo == NULL) {
 239         J2dRlsTraceLn(J2D_TRACE_ERROR,
 240                       "OGLSD_SetScratchContext: glx config info is null");
 241         return NULL;
 242     }
 243 
 244     oglc = glxInfo->context;
 245     if (!GLXSD_MakeCurrentToScratch(env, oglc)) {
 246         return NULL;
 247     }
 248 
 249     if (OGLC_IS_CAP_PRESENT(oglc, CAPS_EXT_FBOBJECT)) {
 250         // the GL_EXT_framebuffer_object extension is present, so this call
 251         // will ensure that we are bound to the scratch pbuffer (and not
 252         // some other framebuffer object)
 253         j2d_glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
 254     }
 255 
 256     return oglc;
 257 }
 258 
 259 /**
 260  * Makes a context current to the given source and destination
 261  * surfaces.  If there is a problem making the context current, this method
 262  * will return NULL; otherwise, returns a pointer to the OGLContext that is
 263  * associated with the destination surface.
 264  */
 265 OGLContext *
 266 OGLSD_MakeOGLContextCurrent(JNIEnv *env, OGLSDOps *srcOps, OGLSDOps *dstOps)
 267 {
 268     GLXSDOps *dstGLXOps = (GLXSDOps *)dstOps->privOps;
 269     OGLContext *oglc;
 270 
 271     J2dTraceLn(J2D_TRACE_INFO, "OGLSD_MakeOGLContextCurrent");
 272 
 273     oglc = dstGLXOps->configData->glxInfo->context;
 274     if (oglc == NULL) {
 275         J2dRlsTraceLn(J2D_TRACE_ERROR,
 276                       "OGLSD_MakeOGLContextCurrent: context is null");
 277         return NULL;
 278     }
 279 
 280     if (dstOps->drawableType == OGLSD_FBOBJECT) {
 281         OGLContext *currentContext = OGLRenderQueue_GetCurrentContext();
 282 
 283         // first make sure we have a current context (if the context isn't
 284         // already current to some drawable, we will make it current to
 285         // its scratch surface)
 286         if (oglc != currentContext) {
 287             if (!GLXSD_MakeCurrentToScratch(env, oglc)) {
 288                 return NULL;
 289             }
 290         }
 291 
 292         // now bind to the fbobject associated with the destination surface;
 293         // this means that all rendering will go into the fbobject destination
 294         // (note that we unbind the currently bound texture first; this is
 295         // recommended procedure when binding an fbobject)
 296         j2d_glBindTexture(dstOps->textureTarget, 0);
 297         j2d_glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, dstOps->fbobjectID);
 298     } else {
 299         GLXSDOps *srcGLXOps = (GLXSDOps *)srcOps->privOps;
 300         GLXCtxInfo *ctxinfo = (GLXCtxInfo *)oglc->ctxInfo;
 301 
 302         // make the context current
 303         if (!j2d_glXMakeContextCurrent(awt_display,
 304                                        dstGLXOps->drawable,
 305                                        srcGLXOps->drawable,
 306                                        ctxinfo->context))
 307         {
 308             J2dRlsTraceLn(J2D_TRACE_ERROR,
 309                 "OGLSD_MakeOGLContextCurrent: could not make current");
 310             return NULL;
 311         }
 312 
 313         if (OGLC_IS_CAP_PRESENT(oglc, CAPS_EXT_FBOBJECT)) {
 314             // the GL_EXT_framebuffer_object extension is present, so we
 315             // must bind to the default (windowing system provided)
 316             // framebuffer
 317             j2d_glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
 318         }
 319     }
 320 
 321     return oglc;
 322 }
 323 
 324 /**
 325  * This function initializes a native window surface and caches the window
 326  * bounds in the given OGLSDOps.  Returns JNI_TRUE if the operation was
 327  * successful; JNI_FALSE otherwise.
 328  */
 329 jboolean
 330 OGLSD_InitOGLWindow(JNIEnv *env, OGLSDOps *oglsdo)
 331 {
 332     GLXSDOps *glxsdo;
 333     Window window;
 334 #ifdef XAWT
 335     XWindowAttributes attr;
 336 #else
 337     Widget widget;
 338 #endif
 339 
 340     J2dTraceLn(J2D_TRACE_INFO, "OGLSD_InitOGLWindow");
 341 
 342     if (oglsdo == NULL) {
 343         J2dRlsTraceLn(J2D_TRACE_ERROR,
 344                       "OGLSD_InitOGLWindow: ops are null");
 345         return JNI_FALSE;
 346     }
 347 
 348     glxsdo = (GLXSDOps *)oglsdo->privOps;
 349     if (glxsdo == NULL) {
 350         J2dRlsTraceLn(J2D_TRACE_ERROR,
 351                       "OGLSD_InitOGLWindow: glx ops are null");
 352         return JNI_FALSE;
 353     }
 354 
 355 #ifdef XAWT
 356     window = glxsdo->window;
 357     if (window == 0) {
 358         J2dRlsTraceLn(J2D_TRACE_ERROR,
 359                       "OGLSD_InitOGLWindow: window is invalid");
 360         return JNI_FALSE;
 361     }
 362 
 363     XGetWindowAttributes(awt_display, window, &attr);
 364     oglsdo->width = attr.width;
 365     oglsdo->height = attr.height;
 366 #else
 367     widget = glxsdo->widget;
 368     if (widget == NULL) {
 369         J2dTraceLn(J2D_TRACE_WARNING, "OGLSD_InitOGLWindow: widget is null");
 370     }
 371 
 372     if (!XtIsRealized(widget)) {
 373         J2dRlsTraceLn(J2D_TRACE_ERROR,
 374                       "OGLSD_InitOGLWindow: widget is unrealized");
 375         return JNI_FALSE;
 376     }
 377 
 378     window = XtWindow(widget);
 379     oglsdo->width = widget->core.width;
 380     oglsdo->height = widget->core.height;
 381 #endif
 382 
 383     oglsdo->drawableType = OGLSD_WINDOW;
 384     oglsdo->isOpaque = JNI_TRUE;
 385     oglsdo->xOffset = 0;
 386     oglsdo->yOffset = 0;
 387     glxsdo->drawable = window;
 388     glxsdo->xdrawable = window;
 389 
 390     J2dTraceLn2(J2D_TRACE_VERBOSE, "  created window: w=%d h=%d",
 391                 oglsdo->width, oglsdo->height);
 392 
 393     return JNI_TRUE;
 394 }
 395 
 396 static int
 397 GLXSD_BadAllocXErrHandler(Display *display, XErrorEvent *xerr)
 398 {
 399     int ret = 0;
 400     if (xerr->error_code == BadAlloc) {
 401         surfaceCreationFailed = JNI_TRUE;
 402     } else {
 403         ret = (*xerror_saved_handler)(display, xerr);
 404     }
 405     return ret;
 406 }
 407 
 408 JNIEXPORT jboolean JNICALL
 409 Java_sun_java2d_opengl_GLXSurfaceData_initPbuffer
 410     (JNIEnv *env, jobject glxsd,
 411      jlong pData, jlong pConfigInfo,
 412      jboolean isOpaque,
 413      jint width, jint height)
 414 {
 415     OGLSDOps *oglsdo = (OGLSDOps *)jlong_to_ptr(pData);
 416     GLXGraphicsConfigInfo *glxinfo =
 417         (GLXGraphicsConfigInfo *)jlong_to_ptr(pConfigInfo);
 418     GLXSDOps *glxsdo;
 419     GLXPbuffer pbuffer;
 420     int attrlist[] = {GLX_PBUFFER_WIDTH, 0,
 421                       GLX_PBUFFER_HEIGHT, 0,
 422                       GLX_PRESERVED_CONTENTS, GL_FALSE, 0};
 423 
 424     J2dTraceLn3(J2D_TRACE_INFO,
 425                 "GLXSurfaceData_initPbuffer: w=%d h=%d opq=%d",
 426                 width, height, isOpaque);
 427 
 428     if (oglsdo == NULL) {
 429         J2dRlsTraceLn(J2D_TRACE_ERROR,
 430                       "GLXSurfaceData_initPbuffer: ops are null");
 431         return JNI_FALSE;
 432     }
 433 
 434     glxsdo = (GLXSDOps *)oglsdo->privOps;
 435     if (glxsdo == NULL) {
 436         J2dRlsTraceLn(J2D_TRACE_ERROR,
 437                       "GLXSurfaceData_initPbuffer: glx ops are null");
 438         return JNI_FALSE;
 439     }
 440 
 441     if (glxinfo == NULL) {
 442         J2dRlsTraceLn(J2D_TRACE_ERROR,
 443                       "GLXSurfaceData_initPbuffer: glx config info is null");
 444         return JNI_FALSE;
 445     }
 446 
 447     attrlist[1] = width;
 448     attrlist[3] = height;
 449 
 450     surfaceCreationFailed = JNI_FALSE;
 451     EXEC_WITH_XERROR_HANDLER(
 452         GLXSD_BadAllocXErrHandler,
 453         pbuffer = j2d_glXCreatePbuffer(awt_display,
 454                                        glxinfo->fbconfig, attrlist));
 455     if ((pbuffer == 0) || surfaceCreationFailed) {
 456         J2dRlsTraceLn(J2D_TRACE_ERROR,
 457             "GLXSurfaceData_initPbuffer: could not create glx pbuffer");
 458         return JNI_FALSE;
 459     }
 460 
 461     oglsdo->drawableType = OGLSD_PBUFFER;
 462     oglsdo->isOpaque = isOpaque;
 463     oglsdo->width = width;
 464     oglsdo->height = height;
 465     oglsdo->xOffset = 0;
 466     oglsdo->yOffset = 0;
 467 
 468     glxsdo->drawable = pbuffer;
 469     glxsdo->xdrawable = 0;
 470 
 471     OGLSD_SetNativeDimensions(env, oglsdo, width, height);
 472 
 473     return JNI_TRUE;
 474 }
 475 
 476 void
 477 OGLSD_SwapBuffers(JNIEnv *env, jlong window)
 478 {
 479     J2dTraceLn(J2D_TRACE_INFO, "OGLSD_SwapBuffers");
 480 
 481     if (window == 0L) {
 482         J2dRlsTraceLn(J2D_TRACE_ERROR,
 483                       "OGLSD_SwapBuffers: window is null");
 484         return;
 485     }
 486 
 487     j2d_glXSwapBuffers(awt_display, (Window)window);
 488 }
 489 
 490 // needed by Mac OS X port, no-op on other platforms
 491 void
 492 OGLSD_Flush(JNIEnv *env)
 493 {
 494 }
 495 
 496 #endif /* !HEADLESS */