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