1 /* 2 * Copyright (c) 2011, 2015, 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 <JavaNativeFoundation/JavaNativeFoundation.h> 28 29 #import "sun_java2d_opengl_CGLSurfaceData.h" 30 31 #import "jni.h" 32 #import "jni_util.h" 33 #import "OGLRenderQueue.h" 34 #import "CGLGraphicsConfig.h" 35 #import "CGLSurfaceData.h" 36 #import "CGLLayer.h" 37 #import "ThreadUtilities.h" 38 39 /* JDK's glext.h is already included and will prevent the Apple glext.h 40 * being included, so define the externs directly 41 */ 42 extern void glBindFramebufferEXT(GLenum target, GLuint framebuffer); 43 extern CGLError CGLTexImageIOSurface2D( 44 CGLContextObj ctx, GLenum target, GLenum internal_format, 45 GLsizei width, GLsizei height, GLenum format, GLenum type, 46 IOSurfaceRef ioSurface, GLuint plane); 47 48 /** 49 * The methods in this file implement the native windowing system specific 50 * layer (CGL) for the OpenGL-based Java 2D pipeline. 51 */ 52 53 #pragma mark - 54 #pragma mark "--- Mac OS X specific methods for GL pipeline ---" 55 56 // TODO: hack that's called from OGLRenderQueue to test out unlockFocus behavior 57 #if 0 58 void 59 OGLSD_UnlockFocus(OGLContext *oglc, OGLSDOps *dstOps) 60 { 61 CGLCtxInfo *ctxinfo = (CGLCtxInfo *)oglc->ctxInfo; 62 CGLSDOps *cglsdo = (CGLSDOps *)dstOps->privOps; 63 fprintf(stderr, "about to unlock focus: %p %p\n", 64 cglsdo->peerData, ctxinfo->context); 65 66 NSOpenGLView *nsView = cglsdo->peerData; 67 if (nsView != NULL) { 68 JNF_COCOA_ENTER(env); 69 [nsView unlockFocus]; 70 JNF_COCOA_EXIT(env); 71 } 72 } 73 #endif 74 75 /** 76 * Makes the given context current to its associated "scratch" surface. If 77 * the operation is successful, this method will return JNI_TRUE; otherwise, 78 * returns JNI_FALSE. 79 */ 80 static jboolean 81 CGLSD_MakeCurrentToScratch(JNIEnv *env, OGLContext *oglc) 82 { 83 J2dTraceLn(J2D_TRACE_INFO, "CGLSD_MakeCurrentToScratch"); 84 85 if (oglc == NULL) { 86 J2dRlsTraceLn(J2D_TRACE_ERROR, 87 "CGLSD_MakeCurrentToScratch: context is null"); 88 return JNI_FALSE; 89 } 90 91 JNF_COCOA_ENTER(env); 92 93 CGLCtxInfo *ctxinfo = (CGLCtxInfo *)oglc->ctxInfo; 94 #if USE_NSVIEW_FOR_SCRATCH 95 [ctxinfo->context makeCurrentContext]; 96 [ctxinfo->context setView: ctxinfo->scratchSurface]; 97 #else 98 [ctxinfo->context clearDrawable]; 99 [ctxinfo->context makeCurrentContext]; 100 [ctxinfo->context setPixelBuffer: ctxinfo->scratchSurface 101 cubeMapFace: 0 102 mipMapLevel: 0 103 currentVirtualScreen: [ctxinfo->context currentVirtualScreen]]; 104 #endif 105 106 JNF_COCOA_EXIT(env); 107 108 return JNI_TRUE; 109 } 110 111 /** 112 * This function disposes of any native windowing system resources associated 113 * with this surface. 114 */ 115 void 116 OGLSD_DestroyOGLSurface(JNIEnv *env, OGLSDOps *oglsdo) 117 { 118 J2dTraceLn(J2D_TRACE_INFO, "OGLSD_DestroyOGLSurface"); 119 120 JNF_COCOA_ENTER(env); 121 122 CGLSDOps *cglsdo = (CGLSDOps *)oglsdo->privOps; 123 if (oglsdo->drawableType == OGLSD_WINDOW) { 124 // detach the NSView from the NSOpenGLContext 125 CGLGraphicsConfigInfo *cglInfo = cglsdo->configInfo; 126 OGLContext *oglc = cglInfo->context; 127 CGLCtxInfo *ctxinfo = (CGLCtxInfo *)oglc->ctxInfo; 128 [ctxinfo->context clearDrawable]; 129 } 130 131 oglsdo->drawableType = OGLSD_UNDEFINED; 132 133 JNF_COCOA_EXIT(env); 134 } 135 136 /** 137 * Returns a pointer (as a jlong) to the native CGLGraphicsConfigInfo 138 * associated with the given OGLSDOps. This method can be called from 139 * shared code to retrieve the native GraphicsConfig data in a platform- 140 * independent manner. 141 */ 142 jlong 143 OGLSD_GetNativeConfigInfo(OGLSDOps *oglsdo) 144 { 145 J2dTraceLn(J2D_TRACE_INFO, "OGLSD_GetNativeConfigInfo"); 146 147 if (oglsdo == NULL) { 148 J2dRlsTraceLn(J2D_TRACE_ERROR, "OGLSD_GetNativeConfigInfo: ops are null"); 149 return 0L; 150 } 151 152 CGLSDOps *cglsdo = (CGLSDOps *)oglsdo->privOps; 153 if (cglsdo == NULL) { 154 J2dRlsTraceLn(J2D_TRACE_ERROR, "OGLSD_GetNativeConfigInfo: cgl ops are null"); 155 return 0L; 156 } 157 158 return ptr_to_jlong(cglsdo->configInfo); 159 } 160 161 /** 162 * Makes the given GraphicsConfig's context current to its associated 163 * "scratch" surface. If there is a problem making the context current, 164 * this method will return NULL; otherwise, returns a pointer to the 165 * OGLContext that is associated with the given GraphicsConfig. 166 */ 167 OGLContext * 168 OGLSD_SetScratchSurface(JNIEnv *env, jlong pConfigInfo) 169 { 170 J2dTraceLn(J2D_TRACE_INFO, "OGLSD_SetScratchContext"); 171 172 CGLGraphicsConfigInfo *cglInfo = (CGLGraphicsConfigInfo *)jlong_to_ptr(pConfigInfo); 173 if (cglInfo == NULL) { 174 J2dRlsTraceLn(J2D_TRACE_ERROR, "OGLSD_SetScratchContext: cgl config info is null"); 175 return NULL; 176 } 177 178 OGLContext *oglc = cglInfo->context; 179 if (oglc == NULL) { 180 J2dRlsTraceLn(J2D_TRACE_ERROR, "OGLSD_SetScratchContext: ogl context is null"); 181 return NULL; 182 } 183 184 CGLCtxInfo *ctxinfo = (CGLCtxInfo *)oglc->ctxInfo; 185 186 JNF_COCOA_ENTER(env); 187 188 // avoid changing the context's target view whenever possible, since 189 // calling setView causes flickering; as long as our context is current 190 // to some view, it's not necessary to switch to the scratch surface 191 if ([ctxinfo->context view] == nil) { 192 // it seems to be necessary to explicitly flush between context changes 193 OGLContext *currentContext = OGLRenderQueue_GetCurrentContext(); 194 if (currentContext != NULL) { 195 j2d_glFlush(); 196 } 197 198 if (!CGLSD_MakeCurrentToScratch(env, oglc)) { 199 return NULL; 200 } 201 // make sure our context is current 202 } else if ([NSOpenGLContext currentContext] != ctxinfo->context) { 203 [ctxinfo->context makeCurrentContext]; 204 } 205 206 if (OGLC_IS_CAP_PRESENT(oglc, CAPS_EXT_FBOBJECT)) { 207 // the GL_EXT_framebuffer_object extension is present, so this call 208 // will ensure that we are bound to the scratch surface (and not 209 // some other framebuffer object) 210 j2d_glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); 211 } 212 213 JNF_COCOA_EXIT(env); 214 215 return oglc; 216 } 217 218 /** 219 * Makes a context current to the given source and destination 220 * surfaces. If there is a problem making the context current, this method 221 * will return NULL; otherwise, returns a pointer to the OGLContext that is 222 * associated with the destination surface. 223 */ 224 OGLContext * 225 OGLSD_MakeOGLContextCurrent(JNIEnv *env, OGLSDOps *srcOps, OGLSDOps *dstOps) 226 { 227 J2dTraceLn(J2D_TRACE_INFO, "OGLSD_MakeOGLContextCurrent"); 228 229 CGLSDOps *dstCGLOps = (CGLSDOps *)dstOps->privOps; 230 231 J2dTraceLn4(J2D_TRACE_VERBOSE, " src: %d %p dst: %d %p", srcOps->drawableType, srcOps, dstOps->drawableType, dstOps); 232 233 OGLContext *oglc = dstCGLOps->configInfo->context; 234 if (oglc == NULL) { 235 J2dRlsTraceLn(J2D_TRACE_ERROR, "OGLSD_MakeOGLContextCurrent: context is null"); 236 return NULL; 237 } 238 239 CGLCtxInfo *ctxinfo = (CGLCtxInfo *)oglc->ctxInfo; 240 241 // it seems to be necessary to explicitly flush between context changes 242 OGLContext *currentContext = OGLRenderQueue_GetCurrentContext(); 243 if (currentContext != NULL) { 244 j2d_glFlush(); 245 } 246 247 if (dstOps->drawableType == OGLSD_FBOBJECT) { 248 // first make sure we have a current context (if the context isn't 249 // already current to some drawable, we will make it current to 250 // its scratch surface) 251 if (oglc != currentContext) { 252 if (!CGLSD_MakeCurrentToScratch(env, oglc)) { 253 return NULL; 254 } 255 } 256 257 // now bind to the fbobject associated with the destination surface; 258 // this means that all rendering will go into the fbobject destination 259 // (note that we unbind the currently bound texture first; this is 260 // recommended procedure when binding an fbobject) 261 j2d_glBindTexture(GL_TEXTURE_2D, 0); 262 j2d_glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, dstOps->fbobjectID); 263 264 return oglc; 265 } 266 267 JNF_COCOA_ENTER(env); 268 269 CGLSDOps *cglsdo = (CGLSDOps *)dstOps->privOps; 270 NSView *nsView = (NSView *)cglsdo->peerData; 271 272 if ([ctxinfo->context view] != nsView) { 273 [ctxinfo->context makeCurrentContext]; 274 [ctxinfo->context setView: nsView]; 275 } 276 277 if (OGLC_IS_CAP_PRESENT(oglc, CAPS_EXT_FBOBJECT)) { 278 // the GL_EXT_framebuffer_object extension is present, so we 279 // must bind to the default (windowing system provided) 280 // framebuffer 281 j2d_glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); 282 } 283 284 JNF_COCOA_EXIT(env); 285 286 return oglc; 287 } 288 289 /** 290 * This function initializes a native window surface and caches the window 291 * bounds in the given OGLSDOps. Returns JNI_TRUE if the operation was 292 * successful; JNI_FALSE otherwise. 293 */ 294 jboolean 295 OGLSD_InitOGLWindow(JNIEnv *env, OGLSDOps *oglsdo) 296 { 297 J2dTraceLn(J2D_TRACE_INFO, "OGLSD_InitOGLWindow"); 298 299 if (oglsdo == NULL) { 300 J2dRlsTraceLn(J2D_TRACE_ERROR, "OGLSD_InitOGLWindow: ops are null"); 301 return JNI_FALSE; 302 } 303 304 CGLSDOps *cglsdo = (CGLSDOps *)oglsdo->privOps; 305 if (cglsdo == NULL) { 306 J2dRlsTraceLn(J2D_TRACE_ERROR, "OGLSD_InitOGLWindow: cgl ops are null"); 307 return JNI_FALSE; 308 } 309 310 AWTView *v = cglsdo->peerData; 311 if (v == NULL) { 312 J2dRlsTraceLn(J2D_TRACE_ERROR, "OGLSD_InitOGLWindow: view is invalid"); 313 return JNI_FALSE; 314 } 315 316 JNF_COCOA_ENTER(env); 317 NSRect surfaceBounds = [v bounds]; 318 oglsdo->drawableType = OGLSD_WINDOW; 319 oglsdo->isOpaque = JNI_TRUE; 320 oglsdo->width = surfaceBounds.size.width; 321 oglsdo->height = surfaceBounds.size.height; 322 JNF_COCOA_EXIT(env); 323 324 J2dTraceLn2(J2D_TRACE_VERBOSE, " created window: w=%d h=%d", oglsdo->width, oglsdo->height); 325 326 return JNI_TRUE; 327 } 328 329 void 330 OGLSD_SwapBuffers(JNIEnv *env, jlong pPeerData) 331 { 332 J2dTraceLn(J2D_TRACE_INFO, "OGLSD_SwapBuffers"); 333 334 JNF_COCOA_ENTER(env); 335 [[NSOpenGLContext currentContext] flushBuffer]; 336 JNF_COCOA_EXIT(env); 337 } 338 339 void 340 OGLSD_Flush(JNIEnv *env) 341 { 342 OGLSDOps *dstOps = OGLRenderQueue_GetCurrentDestination(); 343 if (dstOps != NULL) { 344 CGLSDOps *dstCGLOps = (CGLSDOps *)dstOps->privOps; 345 CGLLayer *layer = (CGLLayer*)dstCGLOps->layer; 346 if (layer != NULL) { 347 [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ 348 AWT_ASSERT_APPKIT_THREAD; 349 [layer setNeedsDisplay]; 350 351 #ifdef REMOTELAYER 352 /* If there's a remote layer (being used for testing) 353 * then we want to have that also receive the texture. 354 * First sync. up its dimensions with that of the layer 355 * we have attached to the local window and tell it that 356 * it also needs to copy the texture. 357 */ 358 if (layer.remoteLayer != nil) { 359 CGLLayer* remoteLayer = layer.remoteLayer; 360 remoteLayer.target = GL_TEXTURE_2D; 361 remoteLayer.textureID = layer.textureID; 362 remoteLayer.textureWidth = layer.textureWidth; 363 remoteLayer.textureHeight = layer.textureHeight; 364 [remoteLayer setNeedsDisplay]; 365 } 366 #endif /* REMOTELAYER */ 367 }]; 368 } 369 } 370 } 371 372 #pragma mark - 373 #pragma mark "--- CGLSurfaceData methods ---" 374 375 extern LockFunc OGLSD_Lock; 376 extern GetRasInfoFunc OGLSD_GetRasInfo; 377 extern UnlockFunc OGLSD_Unlock; 378 extern DisposeFunc OGLSD_Dispose; 379 380 JNIEXPORT void JNICALL 381 Java_sun_java2d_opengl_CGLSurfaceData_initOps 382 (JNIEnv *env, jobject cglsd, 383 jlong pConfigInfo, jlong pPeerData, jlong layerPtr, 384 jint xoff, jint yoff, jboolean isOpaque) 385 { 386 J2dTraceLn(J2D_TRACE_INFO, "CGLSurfaceData_initOps"); 387 J2dTraceLn1(J2D_TRACE_INFO, " pPeerData=%p", jlong_to_ptr(pPeerData)); 388 J2dTraceLn2(J2D_TRACE_INFO, " xoff=%d, yoff=%d", (int)xoff, (int)yoff); 389 390 OGLSDOps *oglsdo = (OGLSDOps *) 391 SurfaceData_InitOps(env, cglsd, sizeof(OGLSDOps)); 392 CGLSDOps *cglsdo = (CGLSDOps *)malloc(sizeof(CGLSDOps)); 393 if (cglsdo == NULL) { 394 JNU_ThrowOutOfMemoryError(env, "creating native cgl ops"); 395 return; 396 } 397 398 oglsdo->privOps = cglsdo; 399 400 oglsdo->sdOps.Lock = OGLSD_Lock; 401 oglsdo->sdOps.GetRasInfo = OGLSD_GetRasInfo; 402 oglsdo->sdOps.Unlock = OGLSD_Unlock; 403 oglsdo->sdOps.Dispose = OGLSD_Dispose; 404 405 oglsdo->drawableType = OGLSD_UNDEFINED; 406 oglsdo->activeBuffer = GL_FRONT; 407 oglsdo->needsInit = JNI_TRUE; 408 oglsdo->xOffset = xoff; 409 oglsdo->yOffset = yoff; 410 oglsdo->isOpaque = isOpaque; 411 412 cglsdo->peerData = (AWTView *)jlong_to_ptr(pPeerData); 413 cglsdo->layer = (CGLLayer *)jlong_to_ptr(layerPtr); 414 cglsdo->configInfo = (CGLGraphicsConfigInfo *)jlong_to_ptr(pConfigInfo); 415 416 if (cglsdo->configInfo == NULL) { 417 free(cglsdo); 418 JNU_ThrowNullPointerException(env, "Config info is null in initOps"); 419 } 420 } 421 422 JNIEXPORT void JNICALL 423 Java_sun_java2d_opengl_CGLSurfaceData_clearWindow 424 (JNIEnv *env, jobject cglsd) 425 { 426 J2dTraceLn(J2D_TRACE_INFO, "CGLSurfaceData_clearWindow"); 427 428 OGLSDOps *oglsdo = (OGLSDOps*) SurfaceData_GetOps(env, cglsd); 429 CGLSDOps *cglsdo = (CGLSDOps*) oglsdo->privOps; 430 431 cglsdo->peerData = NULL; 432 cglsdo->layer = NULL; 433 } 434 435 #pragma mark - 436 #pragma mark "--- CGLSurfaceData methods - Mac OS X specific ---" 437 438 // Must be called on the QFT... 439 JNIEXPORT void JNICALL 440 Java_sun_java2d_opengl_CGLSurfaceData_validate 441 (JNIEnv *env, jobject jsurfacedata, 442 jint xoff, jint yoff, jint width, jint height, jboolean isOpaque) 443 { 444 J2dTraceLn2(J2D_TRACE_INFO, "CGLSurfaceData_validate: w=%d h=%d", width, height); 445 446 OGLSDOps *oglsdo = (OGLSDOps*)SurfaceData_GetOps(env, jsurfacedata); 447 oglsdo->needsInit = JNI_TRUE; 448 oglsdo->xOffset = xoff; 449 oglsdo->yOffset = yoff; 450 451 oglsdo->width = width; 452 oglsdo->height = height; 453 oglsdo->isOpaque = isOpaque; 454 455 if (oglsdo->drawableType == OGLSD_WINDOW) { 456 OGLContext_SetSurfaces(env, ptr_to_jlong(oglsdo), ptr_to_jlong(oglsdo)); 457 458 // we have to explicitly tell the NSOpenGLContext that its target 459 // drawable has changed size 460 CGLSDOps *cglsdo = (CGLSDOps *)oglsdo->privOps; 461 OGLContext *oglc = cglsdo->configInfo->context; 462 CGLCtxInfo *ctxinfo = (CGLCtxInfo *)oglc->ctxInfo; 463 464 JNF_COCOA_ENTER(env); 465 [ctxinfo->context update]; 466 JNF_COCOA_EXIT(env); 467 } 468 }