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