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 JNIEXPORT void JNICALL 378 Java_sun_java2d_opengl_CGLSurfaceData_initOps 379 (JNIEnv *env, jobject cglsd, 380 jlong pConfigInfo, jlong pPeerData, jlong layerPtr, 381 jint xoff, jint yoff, jboolean isOpaque) 382 { 383 J2dTraceLn(J2D_TRACE_INFO, "CGLSurfaceData_initOps"); 384 J2dTraceLn1(J2D_TRACE_INFO, " pPeerData=%p", jlong_to_ptr(pPeerData)); 385 J2dTraceLn2(J2D_TRACE_INFO, " xoff=%d, yoff=%d", (int)xoff, (int)yoff); 386 387 OGLSDOps *oglsdo = (OGLSDOps *) 388 SurfaceData_InitOps(env, cglsd, sizeof(OGLSDOps)); 389 CGLSDOps *cglsdo = (CGLSDOps *)malloc(sizeof(CGLSDOps)); 390 if (cglsdo == NULL) { 391 JNU_ThrowOutOfMemoryError(env, "creating native cgl ops"); 392 return; 393 } 394 395 oglsdo->privOps = cglsdo; 396 397 oglsdo->sdOps.Lock = OGLSD_Lock; 398 oglsdo->sdOps.GetRasInfo = OGLSD_GetRasInfo; 399 oglsdo->sdOps.Unlock = OGLSD_Unlock; 400 oglsdo->sdOps.Dispose = OGLSD_Dispose; 401 402 oglsdo->drawableType = OGLSD_UNDEFINED; 403 oglsdo->activeBuffer = GL_FRONT; 404 oglsdo->needsInit = JNI_TRUE; 405 oglsdo->xOffset = xoff; 406 oglsdo->yOffset = yoff; 407 oglsdo->isOpaque = isOpaque; 408 409 cglsdo->peerData = (AWTView *)jlong_to_ptr(pPeerData); 410 cglsdo->layer = (CGLLayer *)jlong_to_ptr(layerPtr); 411 cglsdo->configInfo = (CGLGraphicsConfigInfo *)jlong_to_ptr(pConfigInfo); 412 413 if (cglsdo->configInfo == NULL) { 414 free(cglsdo); 415 JNU_ThrowNullPointerException(env, "Config info is null in initOps"); 416 } 417 } 418 419 JNIEXPORT void JNICALL 420 Java_sun_java2d_opengl_CGLSurfaceData_clearWindow 421 (JNIEnv *env, jobject cglsd) 422 { 423 J2dTraceLn(J2D_TRACE_INFO, "CGLSurfaceData_clearWindow"); 424 425 OGLSDOps *oglsdo = (OGLSDOps*) SurfaceData_GetOps(env, cglsd); 426 CGLSDOps *cglsdo = (CGLSDOps*) oglsdo->privOps; 427 428 cglsdo->peerData = NULL; 429 cglsdo->layer = NULL; 430 } 431 432 #pragma mark - 433 #pragma mark "--- CGLSurfaceData methods - Mac OS X specific ---" 434 435 // Must be called on the QFT... 436 JNIEXPORT void JNICALL 437 Java_sun_java2d_opengl_CGLSurfaceData_validate 438 (JNIEnv *env, jobject jsurfacedata, 439 jint xoff, jint yoff, jint width, jint height, jboolean isOpaque) 440 { 441 J2dTraceLn2(J2D_TRACE_INFO, "CGLSurfaceData_validate: w=%d h=%d", width, height); 442 443 OGLSDOps *oglsdo = (OGLSDOps*)SurfaceData_GetOps(env, jsurfacedata); 444 oglsdo->needsInit = JNI_TRUE; 445 oglsdo->xOffset = xoff; 446 oglsdo->yOffset = yoff; 447 448 oglsdo->width = width; 449 oglsdo->height = height; 450 oglsdo->isOpaque = isOpaque; 451 452 if (oglsdo->drawableType == OGLSD_WINDOW) { 453 OGLContext_SetSurfaces(env, ptr_to_jlong(oglsdo), ptr_to_jlong(oglsdo)); 454 455 // we have to explicitly tell the NSOpenGLContext that its target 456 // drawable has changed size 457 CGLSDOps *cglsdo = (CGLSDOps *)oglsdo->privOps; 458 OGLContext *oglc = cglsdo->configInfo->context; 459 CGLCtxInfo *ctxinfo = (CGLCtxInfo *)oglc->ctxInfo; 460 461 JNF_COCOA_ENTER(env); 462 [ctxinfo->context update]; 463 JNF_COCOA_EXIT(env); 464 } 465 }