/* * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ #import #import "sun_java2d_opengl_CGLSurfaceData.h" #import "jni_util.h" #import "OGLRenderQueue.h" #import "CGLGraphicsConfig.h" #import "CGLSurfaceData.h" #import "ThreadUtilities.h" /* JDK's glext.h is already included and will prevent the Apple glext.h * being included, so define the externs directly */ extern void glBindFramebufferEXT(GLenum target, GLuint framebuffer); extern CGLError CGLTexImageIOSurface2D( CGLContextObj ctx, GLenum target, GLenum internal_format, GLsizei width, GLsizei height, GLenum format, GLenum type, IOSurfaceRef ioSurface, GLuint plane); /** * The methods in this file implement the native windowing system specific * layer (CGL) for the OpenGL-based Java 2D pipeline. */ #pragma mark - #pragma mark "--- Mac OS X specific methods for GL pipeline ---" // TODO: hack that's called from OGLRenderQueue to test out unlockFocus behavior #if 0 void OGLSD_UnlockFocus(OGLContext *oglc, OGLSDOps *dstOps) { CGLCtxInfo *ctxinfo = (CGLCtxInfo *)oglc->ctxInfo; CGLSDOps *cglsdo = (CGLSDOps *)dstOps->privOps; fprintf(stderr, "about to unlock focus: %p %p\n", cglsdo->peerData, ctxinfo->context); NSOpenGLView *nsView = cglsdo->peerData; if (nsView != NULL) { JNF_COCOA_ENTER(env); [nsView unlockFocus]; JNF_COCOA_EXIT(env); } } #endif /** * Makes the given context current to its associated "scratch" surface. If * the operation is successful, this method will return JNI_TRUE; otherwise, * returns JNI_FALSE. */ static jboolean CGLSD_MakeCurrentToScratch(JNIEnv *env, OGLContext *oglc) { J2dTraceLn(J2D_TRACE_INFO, "CGLSD_MakeCurrentToScratch"); if (oglc == NULL) { J2dRlsTraceLn(J2D_TRACE_ERROR, "CGLSD_MakeCurrentToScratch: context is null"); return JNI_FALSE; } JNF_COCOA_ENTER(env); CGLCtxInfo *ctxinfo = (CGLCtxInfo *)oglc->ctxInfo; #if USE_NSVIEW_FOR_SCRATCH [ctxinfo->context makeCurrentContext]; #else [ctxinfo->context clearDrawable]; [ctxinfo->context makeCurrentContext]; [ctxinfo->context setPixelBuffer: ctxinfo->scratchSurface cubeMapFace: 0 mipMapLevel: 0 currentVirtualScreen: [ctxinfo->context currentVirtualScreen]]; #endif JNF_COCOA_EXIT(env); return JNI_TRUE; } /** * This function disposes of any native windowing system resources associated * with this surface. */ void OGLSD_DestroyOGLSurface(JNIEnv *env, OGLSDOps *oglsdo) { J2dTraceLn(J2D_TRACE_INFO, "OGLSD_DestroyOGLSurface"); JNF_COCOA_ENTER(env); CGLSDOps *cglsdo = (CGLSDOps *)oglsdo->privOps; if (oglsdo->drawableType == OGLSD_WINDOW) { // detach the NSView from the NSOpenGLContext CGLGraphicsConfigInfo *cglInfo = cglsdo->configInfo; OGLContext *oglc = cglInfo->context; CGLCtxInfo *ctxinfo = (CGLCtxInfo *)oglc->ctxInfo; [ctxinfo->context clearDrawable]; } oglsdo->drawableType = OGLSD_UNDEFINED; JNF_COCOA_EXIT(env); } /** * Makes the given GraphicsConfig's context current to its associated * "scratch" surface. If there is a problem making the context current, * this method will return NULL; otherwise, returns a pointer to the * OGLContext that is associated with the given GraphicsConfig. */ OGLContext * OGLSD_SetScratchSurface(JNIEnv *env, jlong pConfigInfo) { J2dTraceLn(J2D_TRACE_INFO, "OGLSD_SetScratchContext"); CGLGraphicsConfigInfo *cglInfo = (CGLGraphicsConfigInfo *)jlong_to_ptr(pConfigInfo); if (cglInfo == NULL) { J2dRlsTraceLn(J2D_TRACE_ERROR, "OGLSD_SetScratchContext: cgl config info is null"); return NULL; } OGLContext *oglc = cglInfo->context; if (oglc == NULL) { J2dRlsTraceLn(J2D_TRACE_ERROR, "OGLSD_SetScratchContext: ogl context is null"); return NULL; } CGLCtxInfo *ctxinfo = (CGLCtxInfo *)oglc->ctxInfo; JNF_COCOA_ENTER(env); // avoid changing the context's target view whenever possible, since // calling setView causes flickering; as long as our context is current // to some view, it's not necessary to switch to the scratch surface if ([ctxinfo->context view] == nil) { // it seems to be necessary to explicitly flush between context changes OGLContext *currentContext = OGLRenderQueue_GetCurrentContext(); if (currentContext != NULL) { j2d_glFlush(); } if (!CGLSD_MakeCurrentToScratch(env, oglc)) { return NULL; } // make sure our context is current } else if ([NSOpenGLContext currentContext] != ctxinfo->context) { [ctxinfo->context makeCurrentContext]; } if (OGLC_IS_CAP_PRESENT(oglc, CAPS_EXT_FBOBJECT)) { // the GL_EXT_framebuffer_object extension is present, so this call // will ensure that we are bound to the scratch surface (and not // some other framebuffer object) j2d_glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); } JNF_COCOA_EXIT(env); return oglc; } /** * Makes a context current to the given source and destination * surfaces. If there is a problem making the context current, this method * will return NULL; otherwise, returns a pointer to the OGLContext that is * associated with the destination surface. */ OGLContext * OGLSD_MakeOGLContextCurrent(JNIEnv *env, OGLSDOps *srcOps, OGLSDOps *dstOps) { J2dTraceLn(J2D_TRACE_INFO, "OGLSD_MakeOGLContextCurrent"); CGLSDOps *dstCGLOps = (CGLSDOps *)dstOps->privOps; J2dTraceLn4(J2D_TRACE_VERBOSE, " src: %d %p dst: %d %p", srcOps->drawableType, srcOps, dstOps->drawableType, dstOps); OGLContext *oglc = dstCGLOps->configInfo->context; if (oglc == NULL) { J2dRlsTraceLn(J2D_TRACE_ERROR, "OGLSD_MakeOGLContextCurrent: context is null"); return NULL; } CGLCtxInfo *ctxinfo = (CGLCtxInfo *)oglc->ctxInfo; // it seems to be necessary to explicitly flush between context changes OGLContext *currentContext = OGLRenderQueue_GetCurrentContext(); if (currentContext != NULL) { j2d_glFlush(); } if (dstOps->drawableType == OGLSD_FBOBJECT) { // first make sure we have a current context (if the context isn't // already current to some drawable, we will make it current to // its scratch surface) if (oglc != currentContext) { if (!CGLSD_MakeCurrentToScratch(env, oglc)) { return NULL; } } // now bind to the fbobject associated with the destination surface; // this means that all rendering will go into the fbobject destination // (note that we unbind the currently bound texture first; this is // recommended procedure when binding an fbobject) j2d_glBindTexture(GL_TEXTURE_2D, 0); j2d_glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, dstOps->fbobjectID); return oglc; } JNF_COCOA_ENTER(env); CGLSDOps *cglsdo = (CGLSDOps *)dstOps->privOps; NSView *nsView = (NSView *)cglsdo->peerData; if ([ctxinfo->context view] != nsView) { [ctxinfo->context makeCurrentContext]; [ctxinfo->context setView: nsView]; } if (OGLC_IS_CAP_PRESENT(oglc, CAPS_EXT_FBOBJECT)) { // the GL_EXT_framebuffer_object extension is present, so we // must bind to the default (windowing system provided) // framebuffer j2d_glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); } JNF_COCOA_EXIT(env); return oglc; } /** * This function initializes a native window surface and caches the window * bounds in the given OGLSDOps. Returns JNI_TRUE if the operation was * successful; JNI_FALSE otherwise. */ jboolean OGLSD_InitOGLWindow(JNIEnv *env, OGLSDOps *oglsdo) { J2dTraceLn(J2D_TRACE_INFO, "OGLSD_InitOGLWindow"); if (oglsdo == NULL) { J2dRlsTraceLn(J2D_TRACE_ERROR, "OGLSD_InitOGLWindow: ops are null"); return JNI_FALSE; } CGLSDOps *cglsdo = (CGLSDOps *)oglsdo->privOps; if (cglsdo == NULL) { J2dRlsTraceLn(J2D_TRACE_ERROR, "OGLSD_InitOGLWindow: cgl ops are null"); return JNI_FALSE; } AWTView *v = cglsdo->peerData; if (v == NULL) { J2dRlsTraceLn(J2D_TRACE_ERROR, "OGLSD_InitOGLWindow: view is invalid"); return JNI_FALSE; } JNF_COCOA_ENTER(env); NSRect surfaceBounds = [v bounds]; oglsdo->drawableType = OGLSD_WINDOW; oglsdo->isOpaque = JNI_TRUE; oglsdo->width = surfaceBounds.size.width; oglsdo->height = surfaceBounds.size.height; JNF_COCOA_EXIT(env); J2dTraceLn2(J2D_TRACE_VERBOSE, " created window: w=%d h=%d", oglsdo->width, oglsdo->height); return JNI_TRUE; } void OGLSD_SwapBuffers(JNIEnv *env, jlong pPeerData) { J2dTraceLn(J2D_TRACE_INFO, "OGLSD_SwapBuffers"); JNF_COCOA_ENTER(env); [[NSOpenGLContext currentContext] flushBuffer]; JNF_COCOA_EXIT(env); } void OGLSD_Flush(JNIEnv *env) { OGLSDOps *dstOps = OGLRenderQueue_GetCurrentDestination(); if (dstOps != NULL) { CGLSDOps *dstCGLOps = (CGLSDOps *)dstOps->privOps; CGLLayer *layer = (CGLLayer*)dstCGLOps->layer; if (layer != NULL) { [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ AWT_ASSERT_APPKIT_THREAD; [layer setNeedsDisplay]; }]; } } } #pragma mark - #pragma mark "--- CGLSurfaceData methods ---" extern LockFunc OGLSD_Lock; extern GetRasInfoFunc OGLSD_GetRasInfo; extern UnlockFunc OGLSD_Unlock; extern DisposeFunc OGLSD_Dispose; JNIEXPORT void JNICALL Java_sun_java2d_opengl_CGLSurfaceData_initOps (JNIEnv *env, jobject cglsd, jobject gc, jlong pConfigInfo, jlong pPeerData, jlong layerPtr, jint xoff, jint yoff, jboolean isOpaque) { J2dTraceLn(J2D_TRACE_INFO, "CGLSurfaceData_initOps"); J2dTraceLn1(J2D_TRACE_INFO, " pPeerData=%p", jlong_to_ptr(pPeerData)); J2dTraceLn2(J2D_TRACE_INFO, " xoff=%d, yoff=%d", (int)xoff, (int)yoff); gc = (*env)->NewGlobalRef(env, gc); if (gc == NULL) { JNU_ThrowOutOfMemoryError(env, "Initialization of SurfaceData failed."); return; } OGLSDOps *oglsdo = (OGLSDOps *) SurfaceData_InitOps(env, cglsd, sizeof(OGLSDOps)); if (oglsdo == NULL) { (*env)->DeleteGlobalRef(env, gc); JNU_ThrowOutOfMemoryError(env, "Initialization of SurfaceData failed."); return; } // later the graphicsConfig will be used for deallocation of oglsdo oglsdo->graphicsConfig = gc; CGLSDOps *cglsdo = (CGLSDOps *)malloc(sizeof(CGLSDOps)); if (cglsdo == NULL) { JNU_ThrowOutOfMemoryError(env, "creating native cgl ops"); return; } oglsdo->privOps = cglsdo; oglsdo->sdOps.Lock = OGLSD_Lock; oglsdo->sdOps.GetRasInfo = OGLSD_GetRasInfo; oglsdo->sdOps.Unlock = OGLSD_Unlock; oglsdo->sdOps.Dispose = OGLSD_Dispose; oglsdo->drawableType = OGLSD_UNDEFINED; oglsdo->activeBuffer = GL_FRONT; oglsdo->needsInit = JNI_TRUE; oglsdo->xOffset = xoff; oglsdo->yOffset = yoff; oglsdo->isOpaque = isOpaque; cglsdo->peerData = (AWTView *)jlong_to_ptr(pPeerData); cglsdo->layer = (CGLLayer *)jlong_to_ptr(layerPtr); cglsdo->configInfo = (CGLGraphicsConfigInfo *)jlong_to_ptr(pConfigInfo); if (cglsdo->configInfo == NULL) { free(cglsdo); JNU_ThrowNullPointerException(env, "Config info is null in initOps"); } } JNIEXPORT void JNICALL Java_sun_java2d_opengl_CGLSurfaceData_clearWindow (JNIEnv *env, jobject cglsd) { J2dTraceLn(J2D_TRACE_INFO, "CGLSurfaceData_clearWindow"); OGLSDOps *oglsdo = (OGLSDOps*) SurfaceData_GetOps(env, cglsd); CGLSDOps *cglsdo = (CGLSDOps*) oglsdo->privOps; cglsdo->peerData = NULL; cglsdo->layer = NULL; }