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