1 /*
   2  * Copyright (c) 2011, 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 #import <stdlib.h>
  27 #import <string.h>
  28 #import <ApplicationServices/ApplicationServices.h>
  29 #import <JavaNativeFoundation/JavaNativeFoundation.h>
  30 
  31 #import "sun_java2d_opengl_CGLGraphicsConfig.h"
  32 
  33 #import "jni.h"
  34 #import "jni_util.h"
  35 #import "CGLGraphicsConfig.h"
  36 #import "CGLSurfaceData.h"
  37 #import "LWCToolkit.h"
  38 #import "ThreadUtilities.h"
  39 
  40 #pragma mark -
  41 #pragma mark "--- Mac OS X specific methods for GL pipeline ---"
  42 
  43 /**
  44  * Disposes all memory and resources associated with the given
  45  * CGLGraphicsConfigInfo (including its native OGLContext data).
  46  */
  47 void
  48 OGLGC_DestroyOGLGraphicsConfig(jlong pConfigInfo)
  49 {
  50     J2dTraceLn(J2D_TRACE_INFO, "OGLGC_DestroyOGLGraphicsConfig");
  51 
  52     CGLGraphicsConfigInfo *cglinfo =
  53         (CGLGraphicsConfigInfo *)jlong_to_ptr(pConfigInfo);
  54     if (cglinfo == NULL) {
  55         J2dRlsTraceLn(J2D_TRACE_ERROR,
  56                       "OGLGC_DestroyOGLGraphicsConfig: info is null");
  57         return;
  58     }
  59 
  60     OGLContext *oglc = (OGLContext*)cglinfo->context;
  61     if (oglc != NULL) {
  62         OGLContext_DestroyContextResources(oglc);
  63 
  64         CGLCtxInfo *ctxinfo = (CGLCtxInfo *)oglc->ctxInfo;
  65         if (ctxinfo != NULL) {
  66             NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];        
  67             [NSOpenGLContext clearCurrentContext];
  68             [ctxinfo->context clearDrawable];
  69             [ctxinfo->context release];
  70             if (ctxinfo->scratchSurface != 0) {
  71                 [ctxinfo->scratchSurface release];
  72             }
  73             [pool drain];
  74             free(ctxinfo);
  75         }
  76     }
  77 
  78     free(cglinfo);
  79 }
  80 
  81 #pragma mark -
  82 #pragma mark "--- CGLGraphicsConfig methods ---"
  83 
  84 #ifdef REMOTELAYER
  85 mach_port_t JRSRemotePort;
  86 int remoteSocketFD = -1;
  87 
  88 static void *JRSRemoteThreadFn(void *data) {
  89     NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
  90 
  91     // Negotiate a unix domain socket to communicate the
  92     // out of band data: to read the mach port server name, and
  93     // subsequently write out the layer ID.
  94     static char* sock_path = "/tmp/JRSRemoteDemoSocket";
  95     struct sockaddr_un address;
  96     int  socket_fd, nbytes;
  97     int BUFLEN = 256;
  98     char buffer[BUFLEN];
  99 
 100     remoteSocketFD = socket(PF_LOCAL, SOCK_STREAM, 0);
 101     if (remoteSocketFD < 0) {
 102         NSLog(@"socket() failed");
 103         return NULL;
 104     }
 105     memset(&address, 0, sizeof(struct sockaddr_un));
 106     address.sun_family = AF_UNIX;
 107     memcpy(address.sun_path, sock_path, strlen(sock_path)+1);
 108     int tries=0, status=-1;
 109     while (status !=0 && tries<600) {
 110         status = connect(remoteSocketFD, (struct sockaddr *) &address,
 111                          sizeof(struct sockaddr_un));
 112         if (status != 0) {
 113             tries++;
 114             NSLog(@"connection attempt %d failed.", tries);
 115             usleep(5000000);
 116         }
 117     }
 118     if (status != 0) {
 119         NSLog(@"failed to connect");
 120         return NULL;
 121     }
 122     nbytes = read(remoteSocketFD, buffer, BUFLEN);
 123     NSString* serverString = [[NSString alloc] initWithUTF8String:buffer];
 124     CFRetain(serverString);
 125     NSLog(@"Read server name %@", serverString);
 126     JRSRemotePort = [JRSRenderServer recieveRenderServer:serverString];
 127     NSLog(@"Read server port %d", JRSRemotePort);
 128 
 129     [pool drain];
 130     return NULL;
 131 }
 132 
 133 void sendLayerID(int layerID) {
 134     if (JRSRemotePort == 0 || remoteSocketFD < 0) {
 135         NSLog(@"No connection to send ID");
 136         return;
 137     }
 138     int BUFLEN = 256;
 139     char buffer[BUFLEN];
 140     snprintf(buffer, BUFLEN, "%d", layerID);
 141     write(remoteSocketFD, buffer, BUFLEN);
 142 }
 143 #endif  /* REMOTELAYER */
 144 
 145 /**
 146  * This is a globally shared context used when creating textures.  When any
 147  * new contexts are created, they specify this context as the "share list"
 148  * context, which means any texture objects created when this shared context
 149  * is current will be available to any other context in any other thread.
 150  */
 151 NSOpenGLContext *sharedContext = NULL;
 152 NSOpenGLPixelFormat *sharedPixelFormat = NULL;
 153 
 154 /**
 155  * Attempts to initialize CGL and the core OpenGL library.
 156  */
 157 JNIEXPORT jboolean JNICALL
 158 Java_sun_java2d_opengl_CGLGraphicsConfig_initCGL
 159     (JNIEnv *env, jclass cglgc)
 160 {
 161     J2dRlsTraceLn(J2D_TRACE_INFO, "CGLGraphicsConfig_initCGL");
 162 
 163     if (!OGLFuncs_OpenLibrary()) {
 164         return JNI_FALSE;
 165     }
 166 
 167     if (!OGLFuncs_InitPlatformFuncs() ||
 168         !OGLFuncs_InitBaseFuncs() ||
 169         !OGLFuncs_InitExtFuncs())
 170     {
 171         OGLFuncs_CloseLibrary();
 172         return JNI_FALSE;
 173     }
 174 #ifdef REMOTELAYER
 175     pthread_t jrsRemoteThread;
 176     pthread_create(&jrsRemoteThread, NULL, JRSRemoteThreadFn, NULL);
 177 #endif
 178     return JNI_TRUE;
 179 }
 180 
 181 
 182 /**
 183  * Determines whether the CGL pipeline can be used for a given GraphicsConfig
 184  * provided its screen number and visual ID.  If the minimum requirements are
 185  * met, the native CGLGraphicsConfigInfo structure is initialized for this
 186  * GraphicsConfig with the necessary information (pixel format, etc.)
 187  * and a pointer to this structure is returned as a jlong.  If
 188  * initialization fails at any point, zero is returned, indicating that CGL
 189  * cannot be used for this GraphicsConfig (we should fallback on an existing
 190  * 2D pipeline).
 191  */
 192 JNIEXPORT jlong JNICALL
 193 Java_sun_java2d_opengl_CGLGraphicsConfig_getCGLConfigInfo
 194     (JNIEnv *env, jclass cglgc,
 195      jint screennum, jint pixfmt, jint swapInterval)
 196 {
 197   jlong ret = 0L;
 198   JNF_COCOA_ENTER(env);
 199   NSMutableArray * retArray = [NSMutableArray arrayWithCapacity:3];
 200   [retArray addObject: [NSNumber numberWithInt: (int)screennum]];
 201   [retArray addObject: [NSNumber numberWithInt: (int)pixfmt]];
 202   [retArray addObject: [NSNumber numberWithInt: (int)swapInterval]];
 203   if ([NSThread isMainThread]) {
 204       [GraphicsConfigUtil _getCGLConfigInfo: retArray];
 205   } else {
 206       [GraphicsConfigUtil performSelectorOnMainThread: @selector(_getCGLConfigInfo:) withObject: retArray waitUntilDone: YES];
 207   }
 208   NSNumber * num = (NSNumber *)[retArray objectAtIndex: 0];
 209   ret = (jlong)[num longValue];
 210   JNF_COCOA_EXIT(env);
 211   return ret;
 212 }
 213 
 214 
 215 
 216 @implementation GraphicsConfigUtil
 217 + (void) _getCGLConfigInfo: (NSMutableArray *)argValue {
 218     AWT_ASSERT_APPKIT_THREAD;
 219 
 220     jint screennum = (jint)[(NSNumber *)[argValue objectAtIndex: 0] intValue];
 221     jint pixfmt = (jint)[(NSNumber *)[argValue objectAtIndex: 1] intValue];
 222     jint swapInterval = (jint)[(NSNumber *)[argValue objectAtIndex: 2] intValue];
 223     JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
 224     [argValue removeAllObjects];
 225 
 226     J2dRlsTraceLn(J2D_TRACE_INFO, "CGLGraphicsConfig_getCGLConfigInfo");
 227 
 228     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
 229 
 230     CGOpenGLDisplayMask glMask = (CGOpenGLDisplayMask)pixfmt;
 231     if (sharedContext == NULL) {
 232         if (glMask == 0) {
 233             CGDirectDisplayID id =
 234                 FindCGDirectDisplayIDForScreenIndex(screennum);
 235             glMask = CGDisplayIDToOpenGLDisplayMask(id);
 236         }
 237 
 238         NSOpenGLPixelFormatAttribute attrs[] = {
 239             NSOpenGLPFAClosestPolicy,
 240             NSOpenGLPFANoRecovery,
 241             NSOpenGLPFAAccelerated,
 242             NSOpenGLPFAFullScreen,
 243             NSOpenGLPFAWindow,
 244             NSOpenGLPFAPixelBuffer,
 245             NSOpenGLPFADoubleBuffer,
 246             NSOpenGLPFAColorSize, 32,
 247             NSOpenGLPFAAlphaSize, 8,
 248             NSOpenGLPFADepthSize, 16,
 249             NSOpenGLPFAScreenMask, glMask,
 250             0
 251         };
 252 
 253         sharedPixelFormat =
 254             [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
 255         if (sharedPixelFormat == nil) {
 256             J2dRlsTraceLn(J2D_TRACE_ERROR, "CGLGraphicsConfig_getCGLConfigInfo: shared NSOpenGLPixelFormat is NULL");
 257             [argValue addObject: [NSNumber numberWithLong: 0L]];
 258             return;
 259         }
 260 
 261         sharedContext =
 262             [[NSOpenGLContext alloc]
 263                 initWithFormat:sharedPixelFormat
 264                 shareContext: NULL];
 265         if (sharedContext == nil) {
 266             J2dRlsTraceLn(J2D_TRACE_ERROR, "CGLGraphicsConfig_getCGLConfigInfo: shared NSOpenGLContext is NULL");
 267             [argValue addObject: [NSNumber numberWithLong: 0L]];
 268             return;
 269         }
 270     }
 271 
 272 #if USE_NSVIEW_FOR_SCRATCH
 273     NSRect contentRect = NSMakeRect(0, 0, 64, 64);
 274     NSWindow *window =
 275         [[NSWindow alloc]
 276             initWithContentRect: contentRect
 277             styleMask: NSBorderlessWindowMask
 278             backing: NSBackingStoreBuffered
 279             defer: false];
 280     if (window == nil) {
 281         J2dRlsTraceLn(J2D_TRACE_ERROR, "CGLGraphicsConfig_getCGLConfigInfo: NSWindow is NULL");
 282         [argValue addObject: [NSNumber numberWithLong: 0L]];
 283         return;
 284     }
 285 
 286     NSView *scratchSurface =
 287         [[NSView alloc]
 288             initWithFrame: contentRect];
 289     if (scratchSurface == nil) {
 290         J2dRlsTraceLn(J2D_TRACE_ERROR, "CGLGraphicsConfig_getCGLConfigInfo: NSView is NULL");
 291         [argValue addObject: [NSNumber numberWithLong: 0L]];
 292         return;
 293     }
 294     [window setContentView: scratchSurface];
 295 #else
 296     NSOpenGLPixelBuffer *scratchSurface =
 297         [[NSOpenGLPixelBuffer alloc]
 298             initWithTextureTarget:GL_TEXTURE_2D
 299             textureInternalFormat:GL_RGB
 300             textureMaxMipMapLevel:0
 301             pixelsWide:64
 302             pixelsHigh:64];
 303 #endif
 304 
 305     NSOpenGLContext *context =
 306         [[NSOpenGLContext alloc]
 307             initWithFormat: sharedPixelFormat
 308             shareContext: sharedContext];
 309     if (context == nil) {
 310         J2dRlsTraceLn(J2D_TRACE_ERROR, "CGLGraphicsConfig_getCGLConfigInfo: NSOpenGLContext is NULL");
 311         [argValue addObject: [NSNumber numberWithLong: 0L]];
 312         return;
 313     }
 314 
 315     GLint contextVirtualScreen = [context currentVirtualScreen];
 316 #if USE_NSVIEW_FOR_SCRATCH
 317     [context setView: scratchSurface];
 318 #else
 319     [context
 320         setPixelBuffer: scratchSurface
 321         cubeMapFace:0
 322         mipMapLevel:0
 323         currentVirtualScreen: contextVirtualScreen];
 324 #endif
 325     [context makeCurrentContext];
 326 
 327     // get version and extension strings
 328     const unsigned char *versionstr = j2d_glGetString(GL_VERSION);
 329     if (!OGLContext_IsVersionSupported(versionstr)) {
 330         J2dRlsTraceLn(J2D_TRACE_ERROR, "CGLGraphicsConfig_getCGLConfigInfo: OpenGL 1.2 is required");
 331         [NSOpenGLContext clearCurrentContext];
 332         [argValue addObject: [NSNumber numberWithLong: 0L]];
 333         return;
 334     }
 335     J2dRlsTraceLn1(J2D_TRACE_INFO, "CGLGraphicsConfig_getCGLConfigInfo: OpenGL version=%s", versionstr);
 336 
 337     jint caps = CAPS_EMPTY;
 338     OGLContext_GetExtensionInfo(env, &caps);
 339 
 340     GLint value = 0;
 341     [sharedPixelFormat
 342         getValues: &value
 343         forAttribute: NSOpenGLPFADoubleBuffer
 344         forVirtualScreen: contextVirtualScreen];
 345     if (value != 0) {
 346         caps |= CAPS_DOUBLEBUFFERED;
 347     }
 348     [sharedPixelFormat
 349         getValues: &value
 350         forAttribute: NSOpenGLPFAAlphaSize
 351         forVirtualScreen: contextVirtualScreen];
 352     if (value != 0) {
 353         caps |= CAPS_STORED_ALPHA;
 354     }
 355 
 356     J2dRlsTraceLn2(J2D_TRACE_INFO,
 357                    "CGLGraphicsConfig_getCGLConfigInfo: db=%d alpha=%d",
 358                    (caps & CAPS_DOUBLEBUFFERED) != 0,
 359                    (caps & CAPS_STORED_ALPHA) != 0);
 360 
 361     // remove before shipping (?)
 362 #if 1
 363     [sharedPixelFormat
 364         getValues: &value
 365         forAttribute: NSOpenGLPFAAccelerated
 366         forVirtualScreen: contextVirtualScreen];
 367     if (value == 0) {
 368         [sharedPixelFormat
 369             getValues: &value
 370             forAttribute: NSOpenGLPFARendererID
 371             forVirtualScreen: contextVirtualScreen];
 372         fprintf(stderr, "WARNING: GL pipe is running in software mode (Renderer ID=0x%x)\n", (int)value);
 373     }
 374 #endif
 375 
 376     // 0: the buffers are swapped with no regard to the vertical refresh rate
 377     // 1: the buffers are swapped only during the vertical retrace
 378     GLint params = swapInterval;
 379     [context setValues: &params forParameter: NSOpenGLCPSwapInterval];
 380 
 381     CGLCtxInfo *ctxinfo = (CGLCtxInfo *)malloc(sizeof(CGLCtxInfo));
 382     if (ctxinfo == NULL) {
 383         J2dRlsTraceLn(J2D_TRACE_ERROR, "CGLGC_InitOGLContext: could not allocate memory for ctxinfo");
 384         [NSOpenGLContext clearCurrentContext];
 385         [argValue addObject: [NSNumber numberWithLong: 0L]];
 386         return;
 387     }
 388     memset(ctxinfo, 0, sizeof(CGLCtxInfo));
 389     ctxinfo->context = context;
 390     ctxinfo->scratchSurface = scratchSurface;
 391 
 392     OGLContext *oglc = (OGLContext *)malloc(sizeof(OGLContext));
 393     if (oglc == 0L) {
 394         J2dRlsTraceLn(J2D_TRACE_ERROR, "CGLGC_InitOGLContext: could not allocate memory for oglc");
 395         [NSOpenGLContext clearCurrentContext];
 396         free(ctxinfo);
 397         [argValue addObject: [NSNumber numberWithLong: 0L]];
 398         return;
 399     }
 400     memset(oglc, 0, sizeof(OGLContext));
 401     oglc->ctxInfo = ctxinfo;
 402     oglc->caps = caps;
 403 
 404     // create the CGLGraphicsConfigInfo record for this config
 405     CGLGraphicsConfigInfo *cglinfo = (CGLGraphicsConfigInfo *)malloc(sizeof(CGLGraphicsConfigInfo));
 406     if (cglinfo == NULL) {
 407         J2dRlsTraceLn(J2D_TRACE_ERROR, "CGLGraphicsConfig_getCGLConfigInfo: could not allocate memory for cglinfo");
 408         [NSOpenGLContext clearCurrentContext];
 409         free(oglc);
 410         free(ctxinfo);
 411         [argValue addObject: [NSNumber numberWithLong: 0L]];
 412         return;
 413     }
 414     memset(cglinfo, 0, sizeof(CGLGraphicsConfigInfo));
 415     cglinfo->screen = screennum;
 416     cglinfo->pixfmt = sharedPixelFormat;
 417     cglinfo->context = oglc;
 418 
 419     [NSOpenGLContext clearCurrentContext];
 420     [argValue addObject: [NSNumber numberWithLong:ptr_to_jlong(cglinfo)]];
 421     [pool drain];
 422 }
 423 @end //GraphicsConfigUtil
 424 
 425 
 426 JNIEXPORT jint JNICALL
 427 Java_sun_java2d_opengl_CGLGraphicsConfig_getDefaultPixFmt
 428     (JNIEnv *env, jclass cglgc, jint screennum)
 429 {
 430     J2dTraceLn(J2D_TRACE_INFO, "CGLGraphicsConfig_getDefaultPixFmt");
 431 
 432     CGDirectDisplayID id = FindCGDirectDisplayIDForScreenIndex(screennum);
 433     return (jint)CGDisplayIDToOpenGLDisplayMask(id);
 434 }
 435 
 436 JNIEXPORT jint JNICALL
 437 Java_sun_java2d_opengl_CGLGraphicsConfig_getOGLCapabilities
 438     (JNIEnv *env, jclass cglgc, jlong configInfo)
 439 {
 440     J2dTraceLn(J2D_TRACE_INFO, "CGLGraphicsConfig_getOGLCapabilities");
 441 
 442     CGLGraphicsConfigInfo *cglinfo =
 443         (CGLGraphicsConfigInfo *)jlong_to_ptr(configInfo);
 444     if ((cglinfo == NULL) || (cglinfo->context == NULL)) {
 445         return CAPS_EMPTY;
 446     } else {
 447         return cglinfo->context->caps;
 448     }
 449 }
 450 
 451 JNIEXPORT jint JNICALL
 452 Java_sun_java2d_opengl_CGLGraphicsConfig_getMaxTextureSize
 453     (JNIEnv *env, jclass cglgc)
 454 {
 455     J2dTraceLn(J2D_TRACE_INFO, "CGLGraphicsConfig_getMaxTextureSize");
 456 
 457     __block int max = 0;
 458 
 459     [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){
 460         [sharedContext makeCurrentContext];
 461         j2d_glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max);
 462     }];
 463 
 464     return (jint)max;
 465 }
 466