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