1 /* 2 * Copyright (c) 2011, 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_PBUFFER, 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_PBUFFER) { 126 if (oglsdo->textureID != 0) { 127 j2d_glDeleteTextures(1, &oglsdo->textureID); 128 oglsdo->textureID = 0; 129 } 130 if (cglsdo->pbuffer != NULL) { 131 [cglsdo->pbuffer release]; 132 cglsdo->pbuffer = NULL; 133 } 134 } else if (oglsdo->drawableType == OGLSD_WINDOW) { 135 #if USE_INTERMEDIATE_BUFFER 136 // REMIND: duplicates code in OGLSD_Delete invoked from the Dispose thread 137 if (oglsdo->textureID != 0) { 138 j2d_glDeleteTextures(1, &oglsdo->textureID); 139 oglsdo->textureID = 0; 140 } 141 if (oglsdo->depthID != 0) { 142 j2d_glDeleteRenderbuffersEXT(1, &oglsdo->depthID); 143 oglsdo->depthID = 0; 144 } 145 if (oglsdo->fbobjectID != 0) { 146 j2d_glDeleteFramebuffersEXT(1, &oglsdo->fbobjectID); 147 oglsdo->fbobjectID = 0; 148 } 149 #else 150 // detach the NSView from the NSOpenGLContext 151 CGLGraphicsConfigInfo *cglInfo = cglsdo->configInfo; 152 OGLContext *oglc = cglInfo->context; 153 CGLCtxInfo *ctxinfo = (CGLCtxInfo *)oglc->ctxInfo; 154 [ctxinfo->context clearDrawable]; 155 #endif 156 } 157 158 oglsdo->drawableType = OGLSD_UNDEFINED; 159 160 JNF_COCOA_EXIT(env); 161 } 162 163 /** 164 * Returns a pointer (as a jlong) to the native CGLGraphicsConfigInfo 165 * associated with the given OGLSDOps. This method can be called from 166 * shared code to retrieve the native GraphicsConfig data in a platform- 167 * independent manner. 168 */ 169 jlong 170 OGLSD_GetNativeConfigInfo(OGLSDOps *oglsdo) 171 { 172 J2dTraceLn(J2D_TRACE_INFO, "OGLSD_GetNativeConfigInfo"); 173 174 if (oglsdo == NULL) { 175 J2dRlsTraceLn(J2D_TRACE_ERROR, "OGLSD_GetNativeConfigInfo: ops are null"); 176 return 0L; 177 } 178 179 CGLSDOps *cglsdo = (CGLSDOps *)oglsdo->privOps; 180 if (cglsdo == NULL) { 181 J2dRlsTraceLn(J2D_TRACE_ERROR, "OGLSD_GetNativeConfigInfo: cgl ops are null"); 182 return 0L; 183 } 184 185 return ptr_to_jlong(cglsdo->configInfo); 186 } 187 188 /** 189 * Makes the given GraphicsConfig's context current to its associated 190 * "scratch" surface. If there is a problem making the context current, 191 * this method will return NULL; otherwise, returns a pointer to the 192 * OGLContext that is associated with the given GraphicsConfig. 193 */ 194 OGLContext * 195 OGLSD_SetScratchSurface(JNIEnv *env, jlong pConfigInfo) 196 { 197 J2dTraceLn(J2D_TRACE_INFO, "OGLSD_SetScratchContext"); 198 199 CGLGraphicsConfigInfo *cglInfo = (CGLGraphicsConfigInfo *)jlong_to_ptr(pConfigInfo); 200 if (cglInfo == NULL) { 201 J2dRlsTraceLn(J2D_TRACE_ERROR, "OGLSD_SetScratchContext: cgl config info is null"); 202 return NULL; 203 } 204 205 OGLContext *oglc = cglInfo->context; 206 CGLCtxInfo *ctxinfo = (CGLCtxInfo *)oglc->ctxInfo; 207 208 JNF_COCOA_ENTER(env); 209 210 // avoid changing the context's target view whenever possible, since 211 // calling setView causes flickering; as long as our context is current 212 // to some view, it's not necessary to switch to the scratch surface 213 if ([ctxinfo->context view] == nil) { 214 // it seems to be necessary to explicitly flush between context changes 215 OGLContext *currentContext = OGLRenderQueue_GetCurrentContext(); 216 if (currentContext != NULL) { 217 j2d_glFlush(); 218 } 219 220 if (!CGLSD_MakeCurrentToScratch(env, oglc)) { 221 return NULL; 222 } 223 } else if ([NSOpenGLContext currentContext] == nil) { 224 [ctxinfo->context makeCurrentContext]; 225 } 226 227 if (OGLC_IS_CAP_PRESENT(oglc, CAPS_EXT_FBOBJECT)) { 228 // the GL_EXT_framebuffer_object extension is present, so this call 229 // will ensure that we are bound to the scratch surface (and not 230 // some other framebuffer object) 231 j2d_glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); 232 } 233 234 JNF_COCOA_EXIT(env); 235 236 return oglc; 237 } 238 239 /** 240 * Makes a context current to the given source and destination 241 * surfaces. If there is a problem making the context current, this method 242 * will return NULL; otherwise, returns a pointer to the OGLContext that is 243 * associated with the destination surface. 244 */ 245 OGLContext * 246 OGLSD_MakeOGLContextCurrent(JNIEnv *env, OGLSDOps *srcOps, OGLSDOps *dstOps) 247 { 248 J2dTraceLn(J2D_TRACE_INFO, "OGLSD_MakeOGLContextCurrent"); 249 250 CGLSDOps *dstCGLOps = (CGLSDOps *)dstOps->privOps; 251 252 J2dTraceLn4(J2D_TRACE_VERBOSE, " src: %d %p dst: %d %p", srcOps->drawableType, srcOps, dstOps->drawableType, dstOps); 253 254 OGLContext *oglc = dstCGLOps->configInfo->context; 255 if (oglc == NULL) { 256 J2dRlsTraceLn(J2D_TRACE_ERROR, "OGLSD_MakeOGLContextCurrent: context is null"); 257 return NULL; 258 } 259 260 CGLCtxInfo *ctxinfo = (CGLCtxInfo *)oglc->ctxInfo; 261 262 // it seems to be necessary to explicitly flush between context changes 263 OGLContext *currentContext = OGLRenderQueue_GetCurrentContext(); 264 if (currentContext != NULL) { 265 j2d_glFlush(); 266 } 267 268 if (dstOps->drawableType == OGLSD_FBOBJECT) { 269 // first make sure we have a current context (if the context isn't 270 // already current to some drawable, we will make it current to 271 // its scratch surface) 272 if (oglc != currentContext) { 273 if (!CGLSD_MakeCurrentToScratch(env, oglc)) { 274 return NULL; 275 } 276 } 277 278 // now bind to the fbobject associated with the destination surface; 279 // this means that all rendering will go into the fbobject destination 280 // (note that we unbind the currently bound texture first; this is 281 // recommended procedure when binding an fbobject) 282 #ifndef USE_IOS 283 j2d_glBindTexture(GL_TEXTURE_2D, 0); 284 #else 285 GLenum target = GL_TEXTURE_RECTANGLE_ARB; 286 j2d_glBindTexture(target, 0); 287 j2d_glDisable(target); 288 #endif 289 j2d_glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, dstOps->fbobjectID); 290 291 return oglc; 292 } 293 294 JNF_COCOA_ENTER(env); 295 296 // set the current surface 297 if (dstOps->drawableType == OGLSD_PBUFFER) { 298 // REMIND: pbuffers are not fully tested yet... 299 [ctxinfo->context clearDrawable]; 300 [ctxinfo->context makeCurrentContext]; 301 [ctxinfo->context setPixelBuffer: dstCGLOps->pbuffer 302 cubeMapFace: 0 303 mipMapLevel: 0 304 currentVirtualScreen: [ctxinfo->context currentVirtualScreen]]; 305 } else { 306 #if USE_INTERMEDIATE_BUFFER 307 #ifndef USE_IOS 308 j2d_glBindTexture(GL_TEXTURE_2D, 0); 309 #else 310 GLenum target = GL_TEXTURE_RECTANGLE_ARB; 311 j2d_glBindTexture(target, 0); 312 j2d_glDisable(target); 313 #endif 314 j2d_glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, dstOps->fbobjectID); 315 #else 316 CGLSDOps *cglsdo = (CGLSDOps *)dstOps->privOps; 317 NSView *nsView = (NSView *)cglsdo->peerData; 318 319 if ([ctxinfo->context view] != nsView) { 320 [ctxinfo->context makeCurrentContext]; 321 [ctxinfo->context setView: nsView]; 322 } 323 #endif 324 } 325 326 #ifndef USE_INTERMEDIATE_BUFFER 327 if (OGLC_IS_CAP_PRESENT(oglc, CAPS_EXT_FBOBJECT)) { 328 // the GL_EXT_framebuffer_object extension is present, so we 329 // must bind to the default (windowing system provided) 330 // framebuffer 331 j2d_glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); 332 } 333 #endif 334 335 if ((srcOps != dstOps) && (srcOps->drawableType == OGLSD_PBUFFER)) { 336 // bind pbuffer to the render texture object (since we are preparing 337 // to copy from the pbuffer) 338 CGLSDOps *srcCGLOps = (CGLSDOps *)srcOps->privOps; 339 j2d_glBindTexture(GL_TEXTURE_2D, srcOps->textureID); 340 [ctxinfo->context 341 setTextureImageToPixelBuffer: srcCGLOps->pbuffer 342 colorBuffer: GL_FRONT]; 343 } 344 345 JNF_COCOA_EXIT(env); 346 347 return oglc; 348 } 349 350 /** 351 * Returns true if OpenGL textures can have non-power-of-two dimensions 352 * when using the basic GL_TEXTURE_2D target. 353 */ 354 BOOL isTexNonPow2Available(CGLGraphicsConfigInfo *cglinfo) { 355 jint caps; 356 if ((cglinfo == NULL) || (cglinfo->context == NULL)) { 357 return FALSE; 358 } else { 359 caps = cglinfo->context->caps; 360 } 361 return ((caps & CAPS_TEXNONPOW2) != 0); 362 } 363 364 /** 365 * Returns true if OpenGL textures can have non-power-of-two dimensions 366 * when using the GL_TEXTURE_RECTANGLE_ARB target (only available when the 367 * GL_ARB_texture_rectangle extension is present). 368 */ 369 BOOL isTexRectAvailable(CGLGraphicsConfigInfo *cglinfo) { 370 jint caps; 371 if ((cglinfo == NULL) || (cglinfo->context == NULL)) { 372 return FALSE; 373 } else { 374 caps = cglinfo->context->caps; 375 } 376 return ((caps & CAPS_EXT_TEXRECT) != 0); 377 } 378 379 /** 380 * Recreates the intermediate buffer associated with the given OGLSDOps 381 * and with the buffer's new size specified in OGLSDOps. 382 */ 383 jboolean RecreateBuffer(JNIEnv *env, OGLSDOps *oglsdo) 384 { 385 // destroy previous buffer first 386 OGLSD_DestroyOGLSurface(env, oglsdo); 387 388 CGLSDOps *cglsdo = (CGLSDOps *)oglsdo->privOps; 389 jboolean result = 390 OGLSurfaceData_initFBObject(env, NULL, ptr_to_jlong(oglsdo), oglsdo->isOpaque, 391 isTexNonPow2Available(cglsdo->configInfo), 392 isTexRectAvailable(cglsdo->configInfo), 393 oglsdo->width, oglsdo->height); 394 395 // NOTE: OGLSD_WINDOW type is reused for offscreen rendering 396 // when intermediate buffer is enabled 397 oglsdo->drawableType = OGLSD_WINDOW; 398 399 AWTView *view = cglsdo->peerData; 400 CGLLayer *layer = (CGLLayer *)view.cglLayer; 401 layer.textureID = oglsdo->textureID; 402 layer.target = GL_TEXTURE_2D; 403 layer.textureWidth = oglsdo->width; 404 layer.textureHeight = oglsdo->height; 405 406 return result; 407 } 408 409 410 static inline IOSurfaceRef createIoSurface(int width, int height) 411 { 412 // Get an error return for 0 size. Maybe should skip creation 413 // for that case, but for now make a 1X1 surface 414 if (width <= 0) width = 1; 415 if (height <= 0) height = 1; 416 417 NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 418 NSMutableDictionary *properties = [NSMutableDictionary dictionaryWithCapacity:4]; 419 [properties setObject:[NSNumber numberWithInt:width] forKey:(id)kIOSurfaceWidth]; 420 [properties setObject:[NSNumber numberWithInt:height] forKey:(id)kIOSurfaceHeight]; 421 [properties setObject:[NSNumber numberWithInt:4] forKey:(id)kIOSurfaceBytesPerElement]; 422 IOSurfaceRef surface = IOSurfaceCreate((CFDictionaryRef)properties); 423 CFRetain(surface); // REMIND: do I need to do this ? 424 [pool drain]; 425 426 if (surface == NULL) { 427 NSLog(@"IOSurfaceCreate error, surface: %p", surface); 428 } else { 429 //NSLog(@"Plugin iosurface OK"); 430 //NSLog(@" surface: %p", surface); 431 //NSLog(@" IOSurfaceGetID(self->surface): %d", IOSurfaceGetID(surface)); 432 } 433 return surface; 434 } 435 436 /** 437 * Recreates the intermediate buffer associated with the given OGLSDOps 438 * and with the buffer's new size specified in OGLSDOps. 439 */ 440 jboolean RecreateIOSBuffer(JNIEnv *env, OGLSDOps *oglsdo) 441 { 442 CGLSDOps *cglsdo = (CGLSDOps *)oglsdo->privOps; 443 // destroy previous buffer first 444 if (oglsdo->textureID != 0) { 445 OGLSD_DestroyOGLSurface(env, oglsdo); 446 if (cglsdo->surfaceRef != NULL) { 447 CFRelease(cglsdo->surfaceRef); 448 cglsdo->surfaceRef = NULL; 449 } 450 } 451 452 oglsdo->textureID = 0; 453 cglsdo->surfaceRef = NULL; 454 int width = oglsdo->width; 455 int height = oglsdo->height; 456 if (width <= 0) width = 1; 457 if (height <= 0) height = 1; 458 IOSurfaceRef _surfaceRef = createIoSurface(width, height); 459 if (_surfaceRef == NULL) { 460 return JNI_FALSE; 461 } 462 463 GLenum target = GL_TEXTURE_RECTANGLE_ARB; 464 glEnable(target); 465 GLuint texture; 466 glGenTextures(1, &texture); 467 glBindTexture(target, texture); 468 glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 469 glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 470 glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 471 glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 472 473 OGLContext *oglc = cglsdo->configInfo->context; 474 CGLCtxInfo *ctxinfo = (CGLCtxInfo *)oglc->ctxInfo; 475 CGLContextObj context = ctxinfo->context.CGLContextObj; 476 477 /* These parameters are documented only in the header file 478 * and apart from the requirement that it must be one of 479 * the combinations listed there its not as clear as I'd like 480 * what the choices mean. 481 */ 482 GLenum format = GL_BGRA; 483 GLenum internal_format = GL_RGB; 484 GLenum type = GL_UNSIGNED_INT_8_8_8_8_REV; 485 486 CGLError err = 487 CGLTexImageIOSurface2D(context, target, internal_format, 488 width, height, format, type, _surfaceRef, 0); 489 490 if (err != kCGLNoError) { 491 J2dRlsTraceLn(J2D_TRACE_ERROR, 492 "OGLSurfaceData_RecreateIOSBuffer: could not init texture"); 493 j2d_glDeleteTextures(1, &texture); 494 CFRelease(_surfaceRef); 495 return JNI_FALSE; 496 } 497 498 oglsdo->drawableType = OGLSD_FBOBJECT; 499 oglsdo->xOffset = 0; 500 oglsdo->yOffset = 0; 501 oglsdo->width = width; 502 oglsdo->height = height; 503 oglsdo->textureID = texture; 504 oglsdo->textureWidth = width; 505 oglsdo->textureHeight = height; 506 // init_FBO fails if we don't use target GL_TEXTURE_RECTANGLE_ARB for the IOS texture. 507 oglsdo->textureTarget = target; 508 OGLSD_INIT_TEXTURE_FILTER(oglsdo, GL_NEAREST); 509 OGLSD_RESET_TEXTURE_WRAP(target); 510 511 // initialize framebuffer object using color texture created above 512 GLuint fbobjectID, depthID; 513 if (!OGLSD_InitFBObject(&fbobjectID, &depthID, 514 oglsdo->textureID, oglsdo->textureTarget, 515 oglsdo->textureWidth, oglsdo->textureHeight)) 516 { 517 J2dRlsTraceLn(J2D_TRACE_ERROR, 518 "OGLSurfaceData_RecreateIOSBuffer: could not init fbobject"); 519 j2d_glDeleteTextures(1, &oglsdo->textureID); 520 CFRelease(_surfaceRef); 521 return JNI_FALSE; 522 } 523 524 oglsdo->fbobjectID = fbobjectID; 525 oglsdo->depthID = depthID; 526 // NOTE: OGLSD_WINDOW type is reused for offscreen rendering 527 // when intermediate buffer is enabled 528 oglsdo->drawableType = OGLSD_WINDOW; 529 530 OGLSD_SetNativeDimensions(env, oglsdo, 531 oglsdo->textureWidth, oglsdo->textureHeight); 532 533 cglsdo->surfaceRef = _surfaceRef; 534 AWTView *view = cglsdo->peerData; 535 CGLLayer *layer = (CGLLayer *)view.cglLayer; 536 layer.textureID = oglsdo->textureID; 537 layer.target = target; 538 layer.textureWidth = oglsdo->width; 539 layer.textureHeight = oglsdo->height; 540 541 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); 542 543 return JNI_TRUE; 544 } 545 546 /** 547 * This function initializes a native window surface and caches the window 548 * bounds in the given OGLSDOps. Returns JNI_TRUE if the operation was 549 * successful; JNI_FALSE otherwise. 550 */ 551 jboolean 552 OGLSD_InitOGLWindow(JNIEnv *env, OGLSDOps *oglsdo) 553 { 554 J2dTraceLn(J2D_TRACE_INFO, "OGLSD_InitOGLWindow"); 555 556 if (oglsdo == NULL) { 557 J2dRlsTraceLn(J2D_TRACE_ERROR, "OGLSD_InitOGLWindow: ops are null"); 558 return JNI_FALSE; 559 } 560 561 CGLSDOps *cglsdo = (CGLSDOps *)oglsdo->privOps; 562 if (cglsdo == NULL) { 563 J2dRlsTraceLn(J2D_TRACE_ERROR, "OGLSD_InitOGLWindow: cgl ops are null"); 564 return JNI_FALSE; 565 } 566 567 AWTView *v = cglsdo->peerData; 568 if (v == NULL) { 569 J2dRlsTraceLn(J2D_TRACE_ERROR, "OGLSD_InitOGLWindow: view is invalid"); 570 return JNI_FALSE; 571 } 572 573 JNF_COCOA_ENTER(env); 574 NSRect surfaceBounds = [v bounds]; 575 oglsdo->drawableType = OGLSD_WINDOW; 576 #ifndef USE_INTERMEDIATE_BUFFER 577 oglsdo->isOpaque = JNI_TRUE; 578 #endif 579 580 oglsdo->width = surfaceBounds.size.width; 581 oglsdo->height = surfaceBounds.size.height; 582 JNF_COCOA_EXIT(env); 583 584 jboolean result = JNI_TRUE; 585 586 #if USE_INTERMEDIATE_BUFFER 587 #ifdef USE_IOS 588 result = RecreateIOSBuffer(env, oglsdo); 589 #else 590 result = RecreateBuffer(env, oglsdo); 591 #endif 592 #endif 593 594 J2dTraceLn2(J2D_TRACE_VERBOSE, " created window: w=%d h=%d", oglsdo->width, oglsdo->height); 595 596 return result; 597 } 598 599 void 600 OGLSD_SwapBuffers(JNIEnv *env, jlong pPeerData) 601 { 602 J2dTraceLn(J2D_TRACE_INFO, "OGLSD_SwapBuffers"); 603 604 JNF_COCOA_ENTER(env); 605 [[NSOpenGLContext currentContext] flushBuffer]; 606 JNF_COCOA_EXIT(env); 607 } 608 609 void 610 OGLSD_Flush(JNIEnv *env) 611 { 612 OGLSDOps *dstOps = OGLRenderQueue_GetCurrentDestination(); 613 if (dstOps != NULL) { 614 CGLSDOps *dstCGLOps = (CGLSDOps *)dstOps->privOps; 615 CGLLayer *layer = (CGLLayer*)dstCGLOps->layer; 616 if (layer != NULL) { 617 [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ 618 AWT_ASSERT_APPKIT_THREAD; 619 [layer setNeedsDisplay]; 620 }]; 621 } 622 } 623 #if USE_INTERMEDIATE_BUFFER 624 OGLSDOps *dstOps = OGLRenderQueue_GetCurrentDestination(); 625 if (dstOps != NULL) { 626 [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ 627 AWT_ASSERT_APPKIT_THREAD; 628 CGLSDOps *dstCGLOps = (CGLSDOps *)dstOps->privOps; 629 AWTView *view = dstCGLOps->peerData; 630 [view.cglLayer setNeedsDisplay]; 631 #ifdef REMOTELAYER 632 /* If there's a remote layer (being used for testing) 633 * then we want to have that also receive the texture. 634 * First sync. up its dimensions with that of the layer 635 * we have attached to the local window and tell it that 636 * it also needs to copy the texture. 637 */ 638 CGLLayer* cglLayer = (CGLLayer*)view.cglLayer; 639 if (cglLayer.remoteLayer != nil) { 640 CGLLayer* remoteLayer = cglLayer.remoteLayer; 641 remoteLayer.target = GL_TEXTURE_2D; 642 remoteLayer.textureID = cglLayer.textureID; 643 remoteLayer.textureWidth = cglLayer.textureWidth; 644 remoteLayer.textureHeight = cglLayer.textureHeight; 645 [remoteLayer setNeedsDisplay]; 646 } 647 #endif /* REMOTELAYER */ 648 }]; 649 } 650 #endif 651 } 652 653 #pragma mark - 654 #pragma mark "--- CGLSurfaceData methods ---" 655 656 extern LockFunc OGLSD_Lock; 657 extern GetRasInfoFunc OGLSD_GetRasInfo; 658 extern UnlockFunc OGLSD_Unlock; 659 extern DisposeFunc OGLSD_Dispose; 660 661 JNIEXPORT void JNICALL 662 Java_sun_java2d_opengl_CGLSurfaceData_initOps 663 (JNIEnv *env, jobject cglsd, 664 jlong pConfigInfo, jlong pPeerData, jlong layerPtr, 665 jint xoff, jint yoff, jboolean isOpaque) 666 { 667 J2dTraceLn(J2D_TRACE_INFO, "CGLSurfaceData_initOps"); 668 J2dTraceLn1(J2D_TRACE_INFO, " pPeerData=%p", jlong_to_ptr(pPeerData)); 669 J2dTraceLn2(J2D_TRACE_INFO, " xoff=%d, yoff=%d", (int)xoff, (int)yoff); 670 671 OGLSDOps *oglsdo = (OGLSDOps *) 672 SurfaceData_InitOps(env, cglsd, sizeof(OGLSDOps)); 673 CGLSDOps *cglsdo = (CGLSDOps *)malloc(sizeof(CGLSDOps)); 674 if (cglsdo == NULL) { 675 JNU_ThrowOutOfMemoryError(env, "creating native cgl ops"); 676 return; 677 } 678 679 oglsdo->privOps = cglsdo; 680 681 oglsdo->sdOps.Lock = OGLSD_Lock; 682 oglsdo->sdOps.GetRasInfo = OGLSD_GetRasInfo; 683 oglsdo->sdOps.Unlock = OGLSD_Unlock; 684 oglsdo->sdOps.Dispose = OGLSD_Dispose; 685 686 oglsdo->drawableType = OGLSD_UNDEFINED; 687 oglsdo->activeBuffer = GL_FRONT; 688 oglsdo->needsInit = JNI_TRUE; 689 oglsdo->xOffset = xoff; 690 oglsdo->yOffset = yoff; 691 oglsdo->isOpaque = isOpaque; 692 693 cglsdo->peerData = (AWTView *)jlong_to_ptr(pPeerData); 694 cglsdo->layer = (CGLLayer *)jlong_to_ptr(layerPtr); 695 cglsdo->configInfo = (CGLGraphicsConfigInfo *)jlong_to_ptr(pConfigInfo); 696 697 if (cglsdo->configInfo == NULL) { 698 free(cglsdo); 699 JNU_ThrowNullPointerException(env, "Config info is null in initOps"); 700 } 701 } 702 703 JNIEXPORT void JNICALL 704 Java_sun_java2d_opengl_CGLSurfaceData_clearWindow 705 (JNIEnv *env, jobject cglsd) 706 { 707 J2dTraceLn(J2D_TRACE_INFO, "CGLSurfaceData_clearWindow"); 708 709 OGLSDOps *oglsdo = (OGLSDOps*) SurfaceData_GetOps(env, cglsd); 710 CGLSDOps *cglsdo = (CGLSDOps*) oglsdo->privOps; 711 712 cglsdo->peerData = NULL; 713 cglsdo->layer = NULL; 714 } 715 716 JNIEXPORT jboolean JNICALL 717 Java_sun_java2d_opengl_CGLSurfaceData_initPbuffer 718 (JNIEnv *env, jobject cglsd, 719 jlong pData, jlong pConfigInfo, jboolean isOpaque, 720 jint width, jint height) 721 { 722 J2dTraceLn3(J2D_TRACE_INFO, "CGLSurfaceData_initPbuffer: w=%d h=%d opq=%d", width, height, isOpaque); 723 724 OGLSDOps *oglsdo = (OGLSDOps *)jlong_to_ptr(pData); 725 if (oglsdo == NULL) { 726 J2dRlsTraceLn(J2D_TRACE_ERROR, "CGLSurfaceData_initPbuffer: ops are null"); 727 return JNI_FALSE; 728 } 729 730 CGLSDOps *cglsdo = (CGLSDOps *)oglsdo->privOps; 731 if (cglsdo == NULL) { 732 J2dRlsTraceLn(J2D_TRACE_ERROR, "CGLSurfaceData_initPbuffer: cgl ops are null"); 733 return JNI_FALSE; 734 } 735 736 CGLGraphicsConfigInfo *cglInfo = (CGLGraphicsConfigInfo *) 737 jlong_to_ptr(pConfigInfo); 738 if (cglInfo == NULL) { 739 J2dRlsTraceLn(J2D_TRACE_ERROR, "CGLSurfaceData_initPbuffer: cgl config info is null"); 740 return JNI_FALSE; 741 } 742 743 // find the maximum allowable texture dimensions (this value ultimately 744 // determines our maximum pbuffer size) 745 int pbMax = 0; 746 j2d_glGetIntegerv(GL_MAX_TEXTURE_SIZE, &pbMax); 747 748 int pbWidth = 0; 749 int pbHeight = 0; 750 if (OGLC_IS_CAP_PRESENT(cglInfo->context, CAPS_TEXNONPOW2)) { 751 // use non-power-of-two dimensions directly 752 pbWidth = (width <= pbMax) ? width : 0; 753 pbHeight = (height <= pbMax) ? height : 0; 754 } else { 755 // find the appropriate power-of-two dimensions 756 pbWidth = OGLSD_NextPowerOfTwo(width, pbMax); 757 pbHeight = OGLSD_NextPowerOfTwo(height, pbMax); 758 } 759 760 J2dTraceLn3(J2D_TRACE_VERBOSE, " desired pbuffer dimensions: w=%d h=%d max=%d", pbWidth, pbHeight, pbMax); 761 762 // if either dimension is 0, we cannot allocate a pbuffer/texture with the 763 // requested dimensions 764 if (pbWidth == 0 || pbHeight == 0) { 765 J2dRlsTraceLn(J2D_TRACE_ERROR, "CGLSurfaceData_initPbuffer: dimensions too large"); 766 return JNI_FALSE; 767 } 768 769 int format = isOpaque ? GL_RGB : GL_RGBA; 770 771 JNF_COCOA_ENTER(env); 772 773 cglsdo->pbuffer = 774 [[NSOpenGLPixelBuffer alloc] 775 initWithTextureTarget: GL_TEXTURE_2D 776 textureInternalFormat: format 777 textureMaxMipMapLevel: 0 778 pixelsWide: pbWidth 779 pixelsHigh: pbHeight]; 780 if (cglsdo->pbuffer == nil) { 781 J2dRlsTraceLn(J2D_TRACE_ERROR, "CGLSurfaceData_initPbuffer: could not create pbuffer"); 782 return JNI_FALSE; 783 } 784 785 // make sure the actual dimensions match those that we requested 786 GLsizei actualWidth = [cglsdo->pbuffer pixelsWide]; 787 GLsizei actualHeight = [cglsdo->pbuffer pixelsHigh]; 788 if (actualWidth != pbWidth || actualHeight != pbHeight) { 789 J2dRlsTraceLn2(J2D_TRACE_ERROR, "CGLSurfaceData_initPbuffer: actual (w=%d h=%d) != requested", actualWidth, actualHeight); 790 [cglsdo->pbuffer release]; 791 return JNI_FALSE; 792 } 793 794 GLuint texID = 0; 795 j2d_glGenTextures(1, &texID); 796 j2d_glBindTexture(GL_TEXTURE_2D, texID); 797 798 oglsdo->drawableType = OGLSD_PBUFFER; 799 oglsdo->isOpaque = isOpaque; 800 oglsdo->width = width; 801 oglsdo->height = height; 802 oglsdo->textureID = texID; 803 oglsdo->textureWidth = pbWidth; 804 oglsdo->textureHeight = pbHeight; 805 oglsdo->activeBuffer = GL_FRONT; 806 oglsdo->needsInit = JNI_TRUE; 807 808 OGLSD_INIT_TEXTURE_FILTER(oglsdo, GL_NEAREST); 809 810 JNF_COCOA_EXIT(env); 811 812 return JNI_TRUE; 813 } 814 815 #pragma mark - 816 #pragma mark "--- CGLSurfaceData methods - Mac OS X specific ---" 817 818 // Must be called on the QFT... 819 JNIEXPORT void JNICALL 820 Java_sun_java2d_opengl_CGLSurfaceData_validate 821 (JNIEnv *env, jobject jsurfacedata, 822 jint xoff, jint yoff, jint width, jint height, jboolean isOpaque) 823 { 824 J2dTraceLn2(J2D_TRACE_INFO, "CGLSurfaceData_validate: w=%d h=%d", width, height); 825 826 OGLSDOps *oglsdo = (OGLSDOps*)SurfaceData_GetOps(env, jsurfacedata); 827 oglsdo->needsInit = JNI_TRUE; 828 oglsdo->xOffset = xoff; 829 oglsdo->yOffset = yoff; 830 831 BOOL newSize = (oglsdo->width != width || oglsdo->height != height); 832 BOOL newOpaque = (oglsdo->isOpaque != isOpaque); 833 834 oglsdo->width = width; 835 oglsdo->height = height; 836 oglsdo->isOpaque = isOpaque; 837 838 if (oglsdo->drawableType == OGLSD_WINDOW) { 839 JNF_COCOA_ENTER(env); 840 #ifdef USE_INTERMEDIATE_BUFFER 841 if (newSize || newOpaque) { 842 #ifdef USE_IOS 843 RecreateIOSBuffer(env, oglsdo); 844 #else 845 RecreateBuffer(env, oglsdo); 846 #endif 847 } 848 #endif 849 JNF_COCOA_EXIT(env); 850 851 OGLContext_SetSurfaces(env, ptr_to_jlong(oglsdo), ptr_to_jlong(oglsdo)); 852 853 // we have to explicitly tell the NSOpenGLContext that its target 854 // drawable has changed size 855 CGLSDOps *cglsdo = (CGLSDOps *)oglsdo->privOps; 856 OGLContext *oglc = cglsdo->configInfo->context; 857 CGLCtxInfo *ctxinfo = (CGLCtxInfo *)oglc->ctxInfo; 858 859 JNF_COCOA_ENTER(env); 860 #ifndef USE_INTERMEDIATE_BUFFER 861 [ctxinfo->context update]; 862 #endif 863 JNF_COCOA_EXIT(env); 864 } 865 }