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