/*
* Copyright (c) 2011, 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.
*/
#import <stdlib.h>
#import <string.h>
#import <ApplicationServices/ApplicationServices.h>
#import <JavaNativeFoundation/JavaNativeFoundation.h>
#import "sun_java2d_opengl_CGLGraphicsConfig.h"
#import "jni.h"
#import "jni_util.h"
#import "CGLGraphicsConfig.h"
#import "CGLSurfaceData.h"
#import "LWCToolkit.h"
#import "ThreadUtilities.h"
#pragma mark -
#pragma mark "--- Mac OS X specific methods for GL pipeline ---"
/**
* Disposes all memory and resources associated with the given
* CGLGraphicsConfigInfo (including its native OGLContext data).
*/
void
OGLGC_DestroyOGLGraphicsConfig(jlong pConfigInfo)
{
J2dTraceLn(J2D_TRACE_INFO, "OGLGC_DestroyOGLGraphicsConfig");
CGLGraphicsConfigInfo *cglinfo =
(CGLGraphicsConfigInfo *)jlong_to_ptr(pConfigInfo);
if (cglinfo == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR,
"OGLGC_DestroyOGLGraphicsConfig: info is null");
return;
}
OGLContext *oglc = (OGLContext*)cglinfo->context;
if (oglc != NULL) {
OGLContext_DestroyContextResources(oglc);
CGLCtxInfo *ctxinfo = (CGLCtxInfo *)oglc->ctxInfo;
if (ctxinfo != NULL) {
[NSOpenGLContext clearCurrentContext];
[ctxinfo->context clearDrawable];
[ctxinfo->context release];
if (ctxinfo->scratchSurface != 0) {
[ctxinfo->scratchSurface release];
}
free(ctxinfo);
}
}
free(cglinfo);
}
#pragma mark -
#pragma mark "--- CGLGraphicsConfig methods ---"
#ifdef REMOTELAYER
mach_port_t JRSRemotePort;
int remoteSocketFD = -1;
static void *JRSRemoteThreadFn(void *data) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// Negotiate a unix domain socket to communicate the
// out of band data: to read the mach port server name, and
// subsequently write out the layer ID.
static char* sock_path = "/tmp/JRSRemoteDemoSocket";
struct sockaddr_un address;
int socket_fd, nbytes;
int BUFLEN = 256;
char buffer[BUFLEN];
remoteSocketFD = socket(PF_LOCAL, SOCK_STREAM, 0);
if (remoteSocketFD < 0) {
NSLog(@"socket() failed");
return NULL;
}
memset(&address, 0, sizeof(struct sockaddr_un));
address.sun_family = AF_UNIX;
memcpy(address.sun_path, sock_path, strlen(sock_path)+1);
int tries=0, status=-1;
while (status !=0 && tries<600) {
status = connect(remoteSocketFD, (struct sockaddr *) &address,
sizeof(struct sockaddr_un));
if (status != 0) {
tries++;
NSLog(@"connection attempt %d failed.", tries);
usleep(5000000);
}
}
if (status != 0) {
NSLog(@"failed to connect");
return NULL;
}
nbytes = read(remoteSocketFD, buffer, BUFLEN);
NSString* serverString = [[NSString alloc] initWithUTF8String:buffer];
CFRetain(serverString);
NSLog(@"Read server name %@", serverString);
JRSRemotePort = [JRSRenderServer recieveRenderServer:serverString];
NSLog(@"Read server port %d", JRSRemotePort);
[pool drain];
return NULL;
}
void sendLayerID(int layerID) {
if (JRSRemotePort == 0 || remoteSocketFD < 0) {
NSLog(@"No connection to send ID");
return;
}
int BUFLEN = 256;
char buffer[BUFLEN];
snprintf(buffer, BUFLEN, "%d", layerID);
write(remoteSocketFD, buffer, BUFLEN);
}
#endif /* REMOTELAYER */
/**
* 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.
*/
NSOpenGLContext *sharedContext = NULL;
NSOpenGLPixelFormat *sharedPixelFormat = NULL;
/**
* Attempts to initialize CGL and the core OpenGL library.
*/
JNIEXPORT jboolean JNICALL
Java_sun_java2d_opengl_CGLGraphicsConfig_initCGL
(JNIEnv *env, jclass cglgc)
{
J2dRlsTraceLn(J2D_TRACE_INFO, "CGLGraphicsConfig_initCGL");
if (!OGLFuncs_OpenLibrary()) {
return JNI_FALSE;
}
if (!OGLFuncs_InitPlatformFuncs() ||
!OGLFuncs_InitBaseFuncs() ||
!OGLFuncs_InitExtFuncs())
{
OGLFuncs_CloseLibrary();
return JNI_FALSE;
}
#ifdef REMOTELAYER
pthread_t jrsRemoteThread;
pthread_create(&jrsRemoteThread, NULL, JRSRemoteThreadFn, NULL);
#endif
return JNI_TRUE;
}
/**
* Determines whether the CGL pipeline can be used for a given GraphicsConfig
* provided its screen number and visual ID. If the minimum requirements are
* met, the native CGLGraphicsConfigInfo 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 CGL
* cannot be used for this GraphicsConfig (we should fallback on an existing
* 2D pipeline).
*/
JNIEXPORT jlong JNICALL
Java_sun_java2d_opengl_CGLGraphicsConfig_getCGLConfigInfo
(JNIEnv *env, jclass cglgc,
jint displayID, jint pixfmt, jint swapInterval)
{
jlong ret = 0L;
JNF_COCOA_ENTER(env);
NSMutableArray * retArray = [NSMutableArray arrayWithCapacity:3];
[retArray addObject: [NSNumber numberWithInt: (int)displayID]];
[retArray addObject: [NSNumber numberWithInt: (int)pixfmt]];
[retArray addObject: [NSNumber numberWithInt: (int)swapInterval]];
if ([NSThread isMainThread]) {
[GraphicsConfigUtil _getCGLConfigInfo: retArray];
} else {
[GraphicsConfigUtil performSelectorOnMainThread: @selector(_getCGLConfigInfo:) withObject: retArray waitUntilDone: YES];
}
NSNumber * num = (NSNumber *)[retArray objectAtIndex: 0];
ret = (jlong)[num longValue];
JNF_COCOA_EXIT(env);
return ret;
}
@implementation GraphicsConfigUtil
+ (void) _getCGLConfigInfo: (NSMutableArray *)argValue {
AWT_ASSERT_APPKIT_THREAD;
jint displayID = (jint)[(NSNumber *)[argValue objectAtIndex: 0] intValue];
jint pixfmt = (jint)[(NSNumber *)[argValue objectAtIndex: 1] intValue];
jint swapInterval = (jint)[(NSNumber *)[argValue objectAtIndex: 2] intValue];
JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
[argValue removeAllObjects];
J2dRlsTraceLn(J2D_TRACE_INFO, "CGLGraphicsConfig_getCGLConfigInfo");
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
CGOpenGLDisplayMask glMask = (CGOpenGLDisplayMask)pixfmt;
if (sharedContext == NULL) {
if (glMask == 0) {
glMask = CGDisplayIDToOpenGLDisplayMask(displayID);
}
NSOpenGLPixelFormatAttribute attrs[] = {
NSOpenGLPFAClosestPolicy,
NSOpenGLPFAWindow,
NSOpenGLPFAPixelBuffer,
NSOpenGLPFADoubleBuffer,
NSOpenGLPFAColorSize, 32,
NSOpenGLPFAAlphaSize, 8,
NSOpenGLPFADepthSize, 16,
NSOpenGLPFAScreenMask, glMask,
0
};
sharedPixelFormat =
[[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
if (sharedPixelFormat == nil) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "CGLGraphicsConfig_getCGLConfigInfo: shared NSOpenGLPixelFormat is NULL");
[argValue addObject: [NSNumber numberWithLong: 0L]];
return;
}
sharedContext =
[[NSOpenGLContext alloc]
initWithFormat:sharedPixelFormat
shareContext: NULL];
if (sharedContext == nil) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "CGLGraphicsConfig_getCGLConfigInfo: shared NSOpenGLContext is NULL");
[argValue addObject: [NSNumber numberWithLong: 0L]];
return;
}
}
#if USE_NSVIEW_FOR_SCRATCH
NSRect contentRect = NSMakeRect(0, 0, 64, 64);
NSWindow *window =
[[NSWindow alloc]
initWithContentRect: contentRect
styleMask: NSBorderlessWindowMask
backing: NSBackingStoreBuffered
defer: false];
if (window == nil) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "CGLGraphicsConfig_getCGLConfigInfo: NSWindow is NULL");
[argValue addObject: [NSNumber numberWithLong: 0L]];
return;
}
NSView *scratchSurface =
[[NSView alloc]
initWithFrame: contentRect];
if (scratchSurface == nil) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "CGLGraphicsConfig_getCGLConfigInfo: NSView is NULL");
[argValue addObject: [NSNumber numberWithLong: 0L]];
return;
}
[window setContentView: scratchSurface];
#else
NSOpenGLPixelBuffer *scratchSurface =
[[NSOpenGLPixelBuffer alloc]
initWithTextureTarget:GL_TEXTURE_2D
textureInternalFormat:GL_RGB
textureMaxMipMapLevel:0
pixelsWide:64
pixelsHigh:64];
#endif
NSOpenGLContext *context =
[[NSOpenGLContext alloc]
initWithFormat: sharedPixelFormat
shareContext: sharedContext];
if (context == nil) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "CGLGraphicsConfig_getCGLConfigInfo: NSOpenGLContext is NULL");
[argValue addObject: [NSNumber numberWithLong: 0L]];
return;
}
GLint contextVirtualScreen = [context currentVirtualScreen];
#if USE_NSVIEW_FOR_SCRATCH
[context setView: scratchSurface];
#else
[context
setPixelBuffer: scratchSurface
cubeMapFace:0
mipMapLevel:0
currentVirtualScreen: contextVirtualScreen];
#endif
[context makeCurrentContext];
// get version and extension strings
const unsigned char *versionstr = j2d_glGetString(GL_VERSION);
if (!OGLContext_IsVersionSupported(versionstr)) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "CGLGraphicsConfig_getCGLConfigInfo: OpenGL 1.2 is required");
[NSOpenGLContext clearCurrentContext];
[argValue addObject: [NSNumber numberWithLong: 0L]];
return;
}
J2dRlsTraceLn1(J2D_TRACE_INFO, "CGLGraphicsConfig_getCGLConfigInfo: OpenGL version=%s", versionstr);
jint caps = CAPS_EMPTY;
OGLContext_GetExtensionInfo(env, &caps);
GLint value = 0;
[sharedPixelFormat
getValues: &value
forAttribute: NSOpenGLPFADoubleBuffer
forVirtualScreen: contextVirtualScreen];
if (value != 0) {
caps |= CAPS_DOUBLEBUFFERED;
}
[sharedPixelFormat
getValues: &value
forAttribute: NSOpenGLPFAAlphaSize
forVirtualScreen: contextVirtualScreen];
if (value != 0) {
caps |= CAPS_STORED_ALPHA;
}
J2dRlsTraceLn2(J2D_TRACE_INFO,
"CGLGraphicsConfig_getCGLConfigInfo: db=%d alpha=%d",
(caps & CAPS_DOUBLEBUFFERED) != 0,
(caps & CAPS_STORED_ALPHA) != 0);
// remove before shipping (?)
#if 1
[sharedPixelFormat
getValues: &value
forAttribute: NSOpenGLPFAAccelerated
forVirtualScreen: contextVirtualScreen];
if (value == 0) {
[sharedPixelFormat
getValues: &value
forAttribute: NSOpenGLPFARendererID
forVirtualScreen: contextVirtualScreen];
fprintf(stderr, "WARNING: GL pipe is running in software mode (Renderer ID=0x%x)\n", (int)value);
}
#endif
// 0: the buffers are swapped with no regard to the vertical refresh rate
// 1: the buffers are swapped only during the vertical retrace
GLint params = swapInterval;
[context setValues: &params forParameter: NSOpenGLCPSwapInterval];
CGLCtxInfo *ctxinfo = (CGLCtxInfo *)malloc(sizeof(CGLCtxInfo));
if (ctxinfo == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "CGLGC_InitOGLContext: could not allocate memory for ctxinfo");
[NSOpenGLContext clearCurrentContext];
[argValue addObject: [NSNumber numberWithLong: 0L]];
return;
}
memset(ctxinfo, 0, sizeof(CGLCtxInfo));
ctxinfo->context = context;
ctxinfo->scratchSurface = scratchSurface;
OGLContext *oglc = (OGLContext *)malloc(sizeof(OGLContext));
if (oglc == 0L) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "CGLGC_InitOGLContext: could not allocate memory for oglc");
[NSOpenGLContext clearCurrentContext];
free(ctxinfo);
[argValue addObject: [NSNumber numberWithLong: 0L]];
return;
}
memset(oglc, 0, sizeof(OGLContext));
oglc->ctxInfo = ctxinfo;
oglc->caps = caps;
// create the CGLGraphicsConfigInfo record for this config
CGLGraphicsConfigInfo *cglinfo = (CGLGraphicsConfigInfo *)malloc(sizeof(CGLGraphicsConfigInfo));
if (cglinfo == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "CGLGraphicsConfig_getCGLConfigInfo: could not allocate memory for cglinfo");
[NSOpenGLContext clearCurrentContext];
free(oglc);
free(ctxinfo);
[argValue addObject: [NSNumber numberWithLong: 0L]];
return;
}
memset(cglinfo, 0, sizeof(CGLGraphicsConfigInfo));
cglinfo->screen = displayID;
cglinfo->pixfmt = sharedPixelFormat;
cglinfo->context = oglc;
[NSOpenGLContext clearCurrentContext];
[argValue addObject: [NSNumber numberWithLong:ptr_to_jlong(cglinfo)]];
[pool drain];
}
@end //GraphicsConfigUtil
JNIEXPORT jint JNICALL
Java_sun_java2d_opengl_CGLGraphicsConfig_getOGLCapabilities
(JNIEnv *env, jclass cglgc, jlong configInfo)
{
J2dTraceLn(J2D_TRACE_INFO, "CGLGraphicsConfig_getOGLCapabilities");
CGLGraphicsConfigInfo *cglinfo =
(CGLGraphicsConfigInfo *)jlong_to_ptr(configInfo);
if ((cglinfo == NULL) || (cglinfo->context == NULL)) {
return CAPS_EMPTY;
} else {
return cglinfo->context->caps;
}
}
JNIEXPORT jint JNICALL
Java_sun_java2d_opengl_CGLGraphicsConfig__1getMaxTextureSize
(JNIEnv *env, jclass cglgc)
{
J2dTraceLn(J2D_TRACE_INFO, "CGLGraphicsConfig__1getMaxTextureSize");
__block int max = 0;
[JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){
[sharedContext makeCurrentContext];
j2d_glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max);
}];
return (jint)max;
}