
jogamp.opengl.macosx.cgl.MacOSXCGLContext Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jogl-all Show documentation
Show all versions of jogl-all Show documentation
Java™ Binding for the OpenGL® API
/*
* Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved.
* Copyright (c) 2010 JogAmp Community. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
* INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
* MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
* ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
* DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
* ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
* DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
* DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
* ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
* SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed or intended for use
* in the design, construction, operation or maintenance of any nuclear
* facility.
*
* Sun gratefully acknowledges that this software was originally authored
* and developed by Kenneth Bradley Russell and Christopher John Kline.
*/
package jogamp.opengl.macosx.cgl;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.Map;
import javax.media.nativewindow.AbstractGraphicsConfiguration;
import javax.media.nativewindow.AbstractGraphicsDevice;
import javax.media.nativewindow.NativeSurface;
import javax.media.nativewindow.NativeWindowFactory;
import javax.media.nativewindow.OffscreenLayerSurface;
import javax.media.nativewindow.ProxySurface;
import javax.media.opengl.GL;
import javax.media.opengl.GL2ES2;
import javax.media.opengl.GL3ES3;
import javax.media.opengl.GLCapabilities;
import javax.media.opengl.GLCapabilitiesImmutable;
import javax.media.opengl.GLContext;
import javax.media.opengl.GLException;
import javax.media.opengl.GLProfile;
import javax.media.opengl.GLUniformData;
import jogamp.nativewindow.macosx.OSXUtil;
import jogamp.opengl.GLContextImpl;
import jogamp.opengl.GLDrawableImpl;
import jogamp.opengl.GLFBODrawableImpl;
import jogamp.opengl.GLGraphicsConfigurationUtil;
import jogamp.opengl.macosx.cgl.MacOSXCGLDrawable.GLBackendType;
import com.jogamp.common.nio.Buffers;
import com.jogamp.common.nio.PointerBuffer;
import com.jogamp.common.os.Platform;
import com.jogamp.common.util.VersionNumber;
import com.jogamp.common.util.locks.RecursiveLock;
import com.jogamp.gluegen.runtime.ProcAddressTable;
import com.jogamp.gluegen.runtime.opengl.GLProcAddressResolver;
import com.jogamp.opengl.GLRendererQuirks;
import com.jogamp.opengl.util.PMVMatrix;
import com.jogamp.opengl.util.glsl.ShaderCode;
import com.jogamp.opengl.util.glsl.ShaderProgram;
public class MacOSXCGLContext extends GLContextImpl
{
// Abstract interface for implementation of this context (either
// NSOpenGL-based or CGL-based)
protected interface GLBackendImpl {
boolean isNSContext();
long create(long share, int ctp, int major, int minor);
boolean destroy(long ctx);
void associateDrawable(boolean bound);
boolean copyImpl(long src, int mask);
boolean makeCurrent(long ctx);
boolean release(long ctx);
boolean detachPBuffer();
boolean setSwapInterval(int interval);
boolean swapBuffers();
}
/* package */ static final boolean isTigerOrLater;
/* package */ static final boolean isLionOrLater;
/* package */ static final boolean isMavericksOrLater;
static {
final VersionNumber osvn = Platform.getOSVersionNumber();
isTigerOrLater = osvn.compareTo(Platform.OSXVersion.Tiger) >= 0;
isLionOrLater = osvn.compareTo(Platform.OSXVersion.Lion) >= 0;
isMavericksOrLater = osvn.compareTo(Platform.OSXVersion.Mavericks) >= 0;
}
static boolean isGLProfileSupported(int ctp, int major, int minor) {
boolean ctBwdCompat = 0 != ( CTX_PROFILE_COMPAT & ctp ) ;
boolean ctCore = 0 != ( CTX_PROFILE_CORE & ctp ) ;
// We exclude 3.0, since we would map it's core to GL2. Hence we force mapping 2.1 to GL2
if( 3 < major || 3 == major && 1 <= minor ) {
if(ctBwdCompat || !ctCore) {
// No compatibility profile on OS X
// Only core is supported
return false;
}
if(!isLionOrLater) {
// no GL Profile >= GL3 core on pre lion
return false;
}
if(3 < major && !isMavericksOrLater) {
// no GL Profile >= GL4 core on pre mavericks
return false;
}
// [3.1..3.x] -> GL3
// [4.0..4.x] -> GL4
return true;
} else if( major < 3 ) {
// < 3.0 -> GL2
return true;
}
return false; // 3.0 && > 3.2
}
static int GLProfile2CGLOGLProfileValue(AbstractGraphicsDevice device, int ctp, int major, int minor) {
if(!MacOSXCGLContext.isGLProfileSupported(ctp, major, minor)) {
throw new GLException("OpenGL profile not supported: "+getGLVersion(major, minor, ctp, "@GLProfile2CGLOGLProfileVersion"));
}
final boolean ctCore = 0 != ( CTX_PROFILE_CORE & ctp ) ;
if( major == 4 && ctCore ) {
if( GLRendererQuirks.existStickyDeviceQuirk(device, GLRendererQuirks.GL4NeedsGL3Request) ) {
// Thread safe GLRendererQuirks sticky access, since we are only interested of the result _after_ GL version mapping,
// i.e. after single threaded initialization!
return CGL.kCGLOGLPVersion_GL3_Core;
} else {
return CGL.kCGLOGLPVersion_GL4_Core;
}
} else if( major == 3 && minor >= 1 && ctCore ) {
return CGL.kCGLOGLPVersion_GL3_Core;
} else {
return CGL.kCGLOGLPVersion_Legacy;
}
}
private static final String shaderBasename = "texture01_xxx";
private static ShaderProgram createCALayerShader(GL3ES3 gl) {
// Create & Link the shader program
final ShaderProgram sp = new ShaderProgram();
final ShaderCode vp = ShaderCode.create(gl, GL2ES2.GL_VERTEX_SHADER, MacOSXCGLContext.class,
"../../shader", "../../shader/bin", shaderBasename, true);
final ShaderCode fp = ShaderCode.create(gl, GL2ES2.GL_FRAGMENT_SHADER, MacOSXCGLContext.class,
"../../shader", "../../shader/bin", shaderBasename, true);
vp.defaultShaderCustomization(gl, true, true);
fp.defaultShaderCustomization(gl, true, true);
sp.add(vp);
sp.add(fp);
if(!sp.link(gl, System.err)) {
throw new GLException("Couldn't link program: "+sp);
}
sp.useProgram(gl, true);
// setup mgl_PMVMatrix
final PMVMatrix pmvMatrix = new PMVMatrix();
pmvMatrix.glMatrixMode(PMVMatrix.GL_PROJECTION);
pmvMatrix.glLoadIdentity();
pmvMatrix.glMatrixMode(PMVMatrix.GL_MODELVIEW);
pmvMatrix.glLoadIdentity();
final GLUniformData pmvMatrixUniform = new GLUniformData("mgl_PMVMatrix", 4, 4, pmvMatrix.glGetPMvMatrixf()); // P, Mv
pmvMatrixUniform.setLocation(gl, sp.program());
gl.glUniform(pmvMatrixUniform);
sp.useProgram(gl, false);
return sp;
}
private boolean haveSetOpenGLMode = false;
private GLBackendType openGLMode = GLBackendType.NSOPENGL;
// Implementation object (either NSOpenGL-based or CGL-based)
protected GLBackendImpl impl;
private CGLExt _cglExt;
// Table that holds the addresses of the native C-language entry points for
// CGL extension functions.
private CGLExtProcAddressTable cglExtProcAddressTable;
private long updateHandle = 0;
private int lastWidth, lastHeight;
protected MacOSXCGLContext(GLDrawableImpl drawable,
GLContext shareWith) {
super(drawable, shareWith);
initOpenGLImpl(getOpenGLMode());
}
@Override
protected void resetStates(boolean isInit) {
// no inner state _cglExt = null;
cglExtProcAddressTable = null;
super.resetStates(isInit);
}
@Override
public Object getPlatformGLExtensions() {
return getCGLExt();
}
protected boolean isNSContext() {
return (null != impl) ? impl.isNSContext() : this.openGLMode == GLBackendType.NSOPENGL;
}
public CGLExt getCGLExt() {
if (_cglExt == null) {
_cglExt = new CGLExtImpl(this);
}
return _cglExt;
}
@Override
public final ProcAddressTable getPlatformExtProcAddressTable() {
return getCGLExtProcAddressTable();
}
public final CGLExtProcAddressTable getCGLExtProcAddressTable() {
return cglExtProcAddressTable;
}
@Override
protected Map getFunctionNameMap() { return null; }
@Override
protected Map getExtensionNameMap() { return null; }
@Override
protected long createContextARBImpl(long share, boolean direct, int ctp, int major, int minor) {
if(!isGLProfileSupported(ctp, major, minor)) {
if(DEBUG) {
System.err.println(getThreadName() + ": createContextARBImpl: Not supported "+getGLVersion(major, minor, ctp, "@creation on OSX "+Platform.getOSVersionNumber()));
}
return 0;
}
// Will throw exception upon error
long ctx = impl.create(share, ctp, major, minor);
if(0 != ctx) {
if (!impl.makeCurrent(ctx)) {
if(DEBUG) {
System.err.println(getThreadName() + ": createContextARB couldn't make current "+getGLVersion(major, minor, ctp, "@creation"));
}
impl.release(ctx);
impl.destroy(ctx);
ctx = 0;
} else if(DEBUG) {
System.err.println(getThreadName() + ": createContextARBImpl: OK "+getGLVersion(major, minor, ctp, "@creation")+", share "+share+", direct "+direct+" on OSX "+Platform.getOSVersionNumber());
}
} else if(DEBUG) {
System.err.println(getThreadName() + ": createContextARBImpl: NO "+getGLVersion(major, minor, ctp, "@creation on OSX "+Platform.getOSVersionNumber()));
}
return ctx;
}
@Override
protected void destroyContextARBImpl(long _context) {
impl.release(_context);
impl.destroy(_context);
}
@Override
public final boolean isGLReadDrawableAvailable() {
return false;
}
protected long createImplPreset(GLContextImpl shareWith) throws GLException {
long share = 0;
if (shareWith != null) {
// Change our OpenGL mode to match that of any share context before we create ourselves
setOpenGLMode(((MacOSXCGLContext)shareWith).getOpenGLMode());
share = shareWith.getHandle();
if (share == 0) {
throw new GLException("GLContextShareSet returned a NULL OpenGL context");
}
}
MacOSXCGLGraphicsConfiguration config = (MacOSXCGLGraphicsConfiguration) drawable.getNativeSurface().getGraphicsConfiguration();
GLCapabilitiesImmutable capabilitiesChosen = (GLCapabilitiesImmutable) config.getChosenCapabilities();
GLProfile glp = capabilitiesChosen.getGLProfile();
if( glp.isGLES1() || glp.isGLES2() || glp.isGLES3() ||
( glp.isGL3() && !isLionOrLater ) || ( glp.isGL4() && !isMavericksOrLater ) ) {
throw new GLException("OpenGL profile not supported on MacOSX "+Platform.getOSVersionNumber()+": "+glp);
}
if (DEBUG) {
System.err.println("Share context is " + toHexString(share) + " for " + this);
}
return share;
}
@Override
protected boolean createImpl(GLContextImpl shareWith) throws GLException {
long share = createImplPreset(shareWith);
contextHandle = createContextARB(share, true);
return 0 != contextHandle;
}
@Override
protected void makeCurrentImpl() throws GLException {
/** FIXME: won't work w/ special drawables (like FBO) - check for CGL mode regressions!
*
if (getOpenGLMode() != ((MacOSXCGLDrawable)drawable).getOpenGLMode()) {
setOpenGLMode(((MacOSXCGLDrawable)drawable).getOpenGLMode());
} */
if (!impl.makeCurrent(contextHandle)) {
throw new GLException("Error making Context current: "+this);
}
drawableUpdatedNotify();
}
@Override
protected void releaseImpl() throws GLException {
if (!impl.release(contextHandle)) {
throw new GLException("Error releasing OpenGL Context: "+this);
}
}
@Override
protected void destroyImpl() throws GLException {
releaseUpdateHandle();
if(!impl.destroy(contextHandle)) {
throw new GLException("Error destroying OpenGL Context: "+this);
}
}
private final long getUpdateHandle() {
if( 0 == updateHandle ) {
lastWidth = -1;
lastHeight = -1;
if( isCreated() && drawable.getChosenGLCapabilities().isOnscreen() && isNSContext() ) {
final boolean incompleteView;
final NativeSurface surface = drawable.getNativeSurface();
if( surface instanceof ProxySurface ) {
incompleteView = ((ProxySurface)surface).containsUpstreamOptionBits( ProxySurface.OPT_UPSTREAM_WINDOW_INVISIBLE );
} else {
incompleteView = false;
}
if(!incompleteView) {
updateHandle = CGL.updateContextRegister(contextHandle, drawable.getHandle());
if(0 == updateHandle) {
throw new InternalError("XXX2");
}
}
}
}
return updateHandle;
}
private final void releaseUpdateHandle() {
if ( 0 != updateHandle ) {
CGL.updateContextUnregister(updateHandle);
updateHandle = 0;
}
}
@Override
protected void drawableUpdatedNotify() throws GLException {
if( drawable.getChosenGLCapabilities().isOnscreen() ) {
final long _updateHandle = getUpdateHandle();
final int w = drawable.getWidth();
final int h = drawable.getHeight();
final boolean updateContext = ( 0!=_updateHandle && CGL.updateContextNeedsUpdate(_updateHandle) ) ||
w != lastWidth || h != lastHeight;
if(updateContext) {
lastWidth = w;
lastHeight = h;
if (contextHandle == 0) {
throw new GLException("Context not created");
}
CGL.updateContext(contextHandle);
}
}
}
@Override
protected void associateDrawable(boolean bound) {
// context stuff depends on drawable stuff
if(bound) {
super.associateDrawable(true); // 1) init drawable stuff
impl.associateDrawable(true); // 2) init context stuff
getUpdateHandle();
} else {
releaseUpdateHandle();
impl.associateDrawable(false); // 1) free context stuff
super.associateDrawable(false); // 2) free drawable stuff
}
}
/* pp */ void detachPBuffer() {
impl.detachPBuffer();
}
@Override
protected void copyImpl(GLContext source, int mask) throws GLException {
if( isNSContext() != ((MacOSXCGLContext)source).isNSContext() ) {
throw new GLException("Source/Destination OpenGL Context tyoe mismatch: source "+source+", dest: "+this);
}
if(!impl.copyImpl(source.getHandle(), mask)) {
throw new GLException("Error copying OpenGL Context: source "+source+", dest: "+this);
}
}
protected void swapBuffers() {
// single-buffer is already filtered out @ GLDrawableImpl#swapBuffers()
if(!impl.swapBuffers()) {
throw new GLException("Error swapping buffers: "+this);
}
}
@Override
protected boolean setSwapIntervalImpl(int interval) {
return impl.setSwapInterval(interval);
}
@Override
public final ByteBuffer glAllocateMemoryNV(int size, float readFrequency, float writeFrequency, float priority) {
// FIXME: apparently the Apple extension doesn't require a custom memory allocator
throw new GLException("Not yet implemented");
}
@Override
public final void glFreeMemoryNV(ByteBuffer pointer) {
// FIXME: apparently the Apple extension doesn't require a custom memory allocator
throw new GLException("Not yet implemented");
}
@Override
protected final void updateGLXProcAddressTable() {
final AbstractGraphicsConfiguration aconfig = drawable.getNativeSurface().getGraphicsConfiguration();
final AbstractGraphicsDevice adevice = aconfig.getScreen().getDevice();
final String key = "MacOSX-"+adevice.getUniqueID();
if (DEBUG) {
System.err.println(getThreadName() + ": Initializing CGL extension address table: "+key);
}
ProcAddressTable table = null;
synchronized(mappedContextTypeObjectLock) {
table = mappedGLXProcAddress.get( key );
}
if(null != table) {
cglExtProcAddressTable = (CGLExtProcAddressTable) table;
if(DEBUG) {
System.err.println(getThreadName() + ": GLContext CGL ProcAddressTable reusing key("+key+") -> "+toHexString(table.hashCode()));
}
} else {
cglExtProcAddressTable = new CGLExtProcAddressTable(new GLProcAddressResolver());
resetProcAddressTable(getCGLExtProcAddressTable());
synchronized(mappedContextTypeObjectLock) {
mappedGLXProcAddress.put(key, getCGLExtProcAddressTable());
if(DEBUG) {
System.err.println(getThreadName() + ": GLContext CGL ProcAddressTable mapping key("+key+") -> "+toHexString(getCGLExtProcAddressTable().hashCode()));
}
}
}
}
@Override
protected final StringBuilder getPlatformExtensionsStringImpl() {
return new StringBuilder();
}
// Support for "mode switching" as described in MacOSXCGLDrawable
public void setOpenGLMode(GLBackendType mode) {
if (mode == openGLMode) {
return;
}
if (haveSetOpenGLMode) {
throw new GLException("Can't switch between using NSOpenGLPixelBuffer and CGLPBufferObj more than once");
}
destroyImpl();
((MacOSXCGLDrawable)drawable).setOpenGLMode(mode);
if (DEBUG) {
System.err.println("MacOSXCGLContext: Switching context mode " + openGLMode + " -> " + mode);
}
initOpenGLImpl(mode);
openGLMode = mode;
haveSetOpenGLMode = true;
}
public final GLBackendType getOpenGLMode() { return openGLMode; }
protected void initOpenGLImpl(GLBackendType backend) {
switch (backend) {
case NSOPENGL:
impl = new NSOpenGLImpl();
break;
case CGL:
impl = new CGLImpl();
break;
default:
throw new InternalError("Illegal implementation mode " + backend);
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(getClass().getSimpleName());
sb.append(" [");
super.append(sb);
sb.append(", mode ");
sb.append(openGLMode);
sb.append("] ");
return sb.toString();
}
// NSOpenGLContext-based implementation
class NSOpenGLImpl implements GLBackendImpl {
private OffscreenLayerSurface backingLayerHost = null;
/** lifecycle: [create - destroy] */
private long pixelFormat = 0;
/** microSec - defaults to 1/60s */
private int screenVSyncTimeout = 16666;
/** microSec - for nsOpenGLLayer mode - defaults to 1/60s + 1ms */
private volatile int vsyncTimeout = 16666 + 1000;
private int lastWidth=0, lastHeight=0; // allowing to detect size change
private boolean needsSetContextPBuffer = false;
private ShaderProgram gl3ShaderProgram = null;
@Override
public boolean isNSContext() { return true; }
/** Only returns a valid NSView. If !NSView, return null and mark either pbuffer and FBO. */
private long getNSViewHandle(boolean[] isPBuffer, boolean[] isFBO) {
final long nsViewHandle;
if(drawable instanceof GLFBODrawableImpl) {
nsViewHandle = 0;
isPBuffer[0] = false;
isFBO[0] = true;
if(DEBUG) {
System.err.println("NS viewHandle.1: GLFBODrawableImpl drawable: isFBO "+isFBO+", isPBuffer "+isPBuffer+", "+drawable.getClass().getName()+",\n\t"+drawable);
}
} else {
final long drawableHandle = drawable.getHandle();
final boolean isNSView = OSXUtil.isNSView(drawableHandle);
final boolean isNSWindow = OSXUtil.isNSWindow(drawableHandle);
isPBuffer[0] = CGL.isNSOpenGLPixelBuffer(drawableHandle);
isFBO[0] = false;
if( isNSView ) {
nsViewHandle = drawableHandle;
} else if( isNSWindow ) {
nsViewHandle = OSXUtil.GetNSView(drawableHandle);
} else if( isPBuffer[0] ) {
nsViewHandle = 0;
} else {
throw new RuntimeException("Drawable's handle neither NSView, NSWindow nor PBuffer: drawableHandle "+toHexString(drawableHandle)+", isNSView "+isNSView+", isNSWindow "+isNSWindow+", isFBO "+isFBO[0]+", isPBuffer "+isPBuffer[0]+", "+drawable.getClass().getName()+",\n\t"+drawable);
}
if(DEBUG) {
System.err.println("NS viewHandle.2: drawableHandle "+toHexString(drawableHandle)+" -> nsViewHandle "+toHexString(nsViewHandle)+": isNSView "+isNSView+", isNSWindow "+isNSWindow+", isFBO "+isFBO[0]+", isPBuffer "+isPBuffer[0]+", "+drawable.getClass().getName()+",\n\t"+drawable);
}
}
needsSetContextPBuffer = isPBuffer[0];
return nsViewHandle;
}
@Override
public long create(long share, int ctp, int major, int minor) {
long ctx = 0;
final NativeSurface surface = drawable.getNativeSurface();
final MacOSXCGLGraphicsConfiguration config = (MacOSXCGLGraphicsConfiguration) surface.getGraphicsConfiguration();
final GLCapabilitiesImmutable chosenCaps = (GLCapabilitiesImmutable) config.getChosenCapabilities();
final long nsViewHandle;
final boolean isPBuffer;
final boolean isFBO;
{
boolean[] _isPBuffer = { false };
boolean[] _isFBO = { false };
nsViewHandle = getNSViewHandle(_isPBuffer, _isFBO);
isPBuffer = _isPBuffer[0];
isFBO = _isFBO[0];
}
final OffscreenLayerSurface backingLayerHost = NativeWindowFactory.getOffscreenLayerSurface(surface, true);
boolean incompleteView = null != backingLayerHost;
if( !incompleteView && surface instanceof ProxySurface ) {
incompleteView = ((ProxySurface)surface).containsUpstreamOptionBits( ProxySurface.OPT_UPSTREAM_WINDOW_INVISIBLE );
}
{
final GLCapabilitiesImmutable targetCaps;
if( isFBO ) {
// Use minimum GLCapabilities for the target surface w/ same profile
targetCaps = new GLCapabilities( chosenCaps.getGLProfile() );
} else {
targetCaps = chosenCaps;
}
pixelFormat = MacOSXCGLGraphicsConfiguration.GLCapabilities2NSPixelFormat(config.getScreen().getDevice(), targetCaps, ctp, major, minor);
}
if (pixelFormat == 0) {
if(DEBUG) {
System.err.println("Unable to allocate pixel format with requested GLCapabilities: "+chosenCaps);
}
return 0;
}
final GLCapabilitiesImmutable fixedCaps;
if( isFBO ) {
// pixelformat of target doesn't affect caps w/ FBO
fixedCaps = chosenCaps;
} else {
final GLCapabilities _fixedCaps = MacOSXCGLGraphicsConfiguration.NSPixelFormat2GLCapabilities(chosenCaps.getGLProfile(), pixelFormat);
if( !_fixedCaps.isPBuffer() && isPBuffer ) {
throw new InternalError("handle is PBuffer, fixedCaps not: "+drawable);
}
// determine on-/offscreen caps, since pformat is ambiguous
_fixedCaps.setPBuffer( isPBuffer ); // exclusive
_fixedCaps.setBitmap( false ); // n/a in our OSX impl.
_fixedCaps.setOnscreen( !isFBO && !isPBuffer );
fixedCaps = GLGraphicsConfigurationUtil.fixOpaqueGLCapabilities(_fixedCaps, chosenCaps.isBackgroundOpaque());
}
final int sRefreshRate = OSXUtil.GetScreenRefreshRate(drawable.getNativeSurface().getGraphicsConfiguration().getScreen().getIndex());
if( 0 < sRefreshRate ) {
screenVSyncTimeout = 1000000 / sRefreshRate;
}
if(DEBUG) {
System.err.println("NS create OSX>=lion "+isLionOrLater+", OSX>=mavericks "+isMavericksOrLater);
System.err.println("NS create incompleteView: "+incompleteView);
System.err.println("NS create backingLayerHost: "+backingLayerHost);
System.err.println("NS create share: "+share);
System.err.println("NS create drawable type: "+drawable.getClass().getName());
System.err.println("NS create drawable handle: isPBuffer "+isPBuffer+", isFBO "+isFBO);
System.err.println("NS create pixelFormat: "+toHexString(pixelFormat));
System.err.println("NS create chosenCaps: "+chosenCaps);
System.err.println("NS create fixedCaps: "+fixedCaps);
System.err.println("NS create drawable native-handle: "+toHexString(drawable.getHandle()));
System.err.println("NS create drawable NSView-handle: "+toHexString(nsViewHandle));
System.err.println("NS create screen refresh-rate: "+sRefreshRate+" hz, "+screenVSyncTimeout+" micros");
// Thread.dumpStack();
}
config.setChosenCapabilities(fixedCaps);
final IntBuffer viewNotReady = Buffers.newDirectIntBuffer(1);
// Try to allocate a context with this
ctx = CGL.createContext(share, nsViewHandle, incompleteView,
pixelFormat, chosenCaps.isBackgroundOpaque(), viewNotReady);
if (0 == ctx) {
if(DEBUG) {
System.err.println("NS create failed: viewNotReady: "+ (1 == viewNotReady.get(0)));
}
return 0;
}
if (chosenCaps.isOnscreen() && !chosenCaps.isBackgroundOpaque()) {
// Set the context opacity
CGL.setContextOpacity(ctx, 0);
}
return ctx;
}
@Override
public boolean destroy(long ctx) {
if(0!=pixelFormat) {
CGL.deletePixelFormat(pixelFormat);
pixelFormat = 0;
}
return CGL.deleteContext(ctx, true);
}
/**
* NSOpenGLLayer creation and it's attachment is performed on the main-thread w/o [infinite] blocking.
*
* Since NSOpenGLLayer creation requires this context for it's shared context creation,
* this method attempts to acquire the surface and context lock with {@link #screenVSyncTimeout}/2 maximum wait time.
* If the surface and context lock could not be acquired, this runnable is being re-queued for later execution.
*
*
* Hence this method blocks the main-thread only for a short period of time.
*
*/
class AttachGLLayerCmd implements Runnable {
final OffscreenLayerSurface ols;
final long ctx;
final int shaderProgram;
final long pfmt;
final long pbuffer;
final int texID;
final boolean isOpaque;
final int width;
final int height;
/** Synchronized by instance's monitor */
long nsOpenGLLayer;
/** Synchronized by instance's monitor */
boolean valid;
AttachGLLayerCmd(OffscreenLayerSurface ols, long ctx, int shaderProgram, long pfmt, long pbuffer, int texID, boolean isOpaque, int width, int height) {
this.ols = ols;
this.ctx = ctx;
this.shaderProgram = shaderProgram;
this.pfmt = pfmt;
this.pbuffer = pbuffer;
this.texID = texID;
this.isOpaque = isOpaque;
this.width = width;
this.height = height;
this.valid = false;
this.nsOpenGLLayer = 0;
}
public final String contentToString() {
return "valid "+valid+", size "+width+"x"+height+", ctx "+toHexString(ctx)+", opaque "+isOpaque+", texID "+texID+", pbuffer "+toHexString(pbuffer)+", nsOpenGLLayer "+toHexString(nsOpenGLLayer);
}
@Override
public final String toString() {
return "AttachGLLayerCmd["+contentToString()+"]";
}
@Override
public void run() {
synchronized(this) {
if( !valid ) {
try {
final int maxwait = screenVSyncTimeout/2000; // TO 1/2 of current screen-vsync in [ms]
final RecursiveLock surfaceLock = ols.getLock();
if( surfaceLock.tryLock( maxwait ) ) {
try {
if( MacOSXCGLContext.this.lock.tryLock( maxwait ) ) {
try {
nsOpenGLLayer = CGL.createNSOpenGLLayer(ctx, shaderProgram, pfmt, pbuffer, texID, isOpaque, width, height);
ols.attachSurfaceLayer(nsOpenGLLayer);
final int currentInterval = MacOSXCGLContext.this.getSwapInterval();
final int interval = 0 <= currentInterval ? currentInterval : 1;
setSwapIntervalImpl(nsOpenGLLayer, interval); // enabled per default in layered surface
valid = true;
if (DEBUG) {
System.err.println("NSOpenGLLayer.Attach: OK, layer "+toHexString(nsOpenGLLayer)+" w/ pbuffer "+toHexString(pbuffer)+", texID "+texID+", texSize "+lastWidth+"x"+lastHeight+", drawableHandle "+toHexString(drawable.getHandle())+" - "+getThreadName());
}
} finally {
MacOSXCGLContext.this.lock.unlock();
}
}
} finally {
surfaceLock.unlock();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
if( !valid ) {
// could not acquire lock, re-queue
if (DEBUG) {
System.err.println("NSOpenGLLayer.Attach: Re-Queue, drawableHandle "+toHexString(drawable.getHandle())+" - "+getThreadName());
}
OSXUtil.RunLater(true /* onMain */, this, 1);
}
}
}
}
}
AttachGLLayerCmd attachGLLayerCmd = null;
class DetachGLLayerCmd implements Runnable {
final AttachGLLayerCmd cmd;
DetachGLLayerCmd(AttachGLLayerCmd cmd) {
this.cmd = cmd;
}
@Override
public final String toString() {
return "DetachGLLayerCmd["+cmd.contentToString()+"]";
}
@Override
public void run() {
synchronized( cmd ) {
if( cmd.valid ) {
// still having a valid OLS attached to surface (parent OLS could have been removed)
try {
final OffscreenLayerSurface ols = cmd.ols;
final long l = ols.getAttachedSurfaceLayer();
if( 0 != l ) {
ols.detachSurfaceLayer();
}
} catch(Throwable t) {
System.err.println("Catched Exception on thread "+getThreadName());
t.printStackTrace();
}
CGL.releaseNSOpenGLLayer(cmd.nsOpenGLLayer);
if(DEBUG) {
System.err.println("NSOpenGLLayer.Detach: OK, layer "+toHexString(cmd.nsOpenGLLayer)+" - "+getThreadName());
}
cmd.nsOpenGLLayer = 0;
cmd.valid = false;
} else if(DEBUG) {
System.err.println("NSOpenGLLayer.Detach: Skipped "+toHexString(cmd.nsOpenGLLayer)+" - "+getThreadName());
}
}
}
}
@Override
public void associateDrawable(boolean bound) {
backingLayerHost = NativeWindowFactory.getOffscreenLayerSurface(drawable.getNativeSurface(), true);
if(DEBUG) {
System.err.println("MaxOSXCGLContext.NSOpenGLImpl.associateDrawable: "+bound+", ctx "+toHexString(contextHandle)+
", hasBackingLayerHost "+(null!=backingLayerHost)+", attachGLLayerCmd "+attachGLLayerCmd);
// Thread.dumpStack();
}
if( bound ) {
if( null != backingLayerHost ) {
final GLCapabilitiesImmutable chosenCaps;
final long ctx;
final int texID;
final long pbufferHandle;
final int gl3ShaderProgramName;
//
// handled layered surface
//
chosenCaps = drawable.getChosenGLCapabilities();
ctx = MacOSXCGLContext.this.getHandle();
final long drawableHandle = drawable.getHandle();
if(drawable instanceof GLFBODrawableImpl) {
final GLFBODrawableImpl fbod = (GLFBODrawableImpl)drawable;
texID = fbod.getTextureBuffer(GL.GL_FRONT).getName();
pbufferHandle = 0;
fbod.setSwapBufferContext(new GLFBODrawableImpl.SwapBufferContext() {
@Override
public void swapBuffers(boolean doubleBuffered) {
MacOSXCGLContext.NSOpenGLImpl.this.swapBuffers();
} } ) ;
} else if( CGL.isNSOpenGLPixelBuffer(drawableHandle) ) {
texID = 0;
pbufferHandle = drawableHandle;
if(0 != drawableHandle) { // complete 'validatePBufferConfig(..)' procedure
CGL.setContextPBuffer(ctx, pbufferHandle);
needsSetContextPBuffer = false;
}
} else {
throw new GLException("BackingLayerHost w/ unknown handle (!FBO, !PBuffer): "+drawable);
}
lastWidth = drawable.getWidth();
lastHeight = drawable.getHeight();
if(0>=lastWidth || 0>=lastHeight || !drawable.isRealized()) {
throw new GLException("Drawable not realized yet or invalid texture size, texSize "+lastWidth+"x"+lastHeight+", "+drawable);
}
if( MacOSXCGLContext.this.isGL3core() ) {
if( null == gl3ShaderProgram) {
gl3ShaderProgram = createCALayerShader(MacOSXCGLContext.this.gl.getGL3ES3());
}
gl3ShaderProgramName = gl3ShaderProgram.program();
} else {
gl3ShaderProgramName = 0;
}
// All CALayer lifecycle ops are deferred on main-thread
attachGLLayerCmd = new AttachGLLayerCmd(
backingLayerHost, ctx, gl3ShaderProgramName, pixelFormat, pbufferHandle, texID,
chosenCaps.isBackgroundOpaque(), lastWidth, lastHeight );
if(DEBUG) {
System.err.println("MaxOSXCGLContext.NSOpenGLImpl.associateDrawable(true): "+attachGLLayerCmd);
}
OSXUtil.RunOnMainThread(false, attachGLLayerCmd);
} else { // -> null == backingLayerHost
lastWidth = drawable.getWidth();
lastHeight = drawable.getHeight();
boolean[] isPBuffer = { false };
boolean[] isFBO = { false };
CGL.setContextView(contextHandle, getNSViewHandle(isPBuffer, isFBO));
}
} else { // -> !bound
if( null != backingLayerHost ) {
final AttachGLLayerCmd cmd = attachGLLayerCmd;
attachGLLayerCmd = null;
if( null == cmd ) {
throw new GLException("Null attachGLLayerCmd: "+drawable);
}
if( 0 != cmd.pbuffer ) {
CGL.setContextPBuffer(contextHandle, 0);
}
synchronized(cmd) {
if( !cmd.valid ) {
cmd.valid = true; // skip pending creation
} else {
// All CALayer lifecycle ops are deferred on main-thread
final DetachGLLayerCmd dCmd = new DetachGLLayerCmd(cmd);
if(DEBUG) {
System.err.println("MaxOSXCGLContext.NSOpenGLImpl.associateDrawable(false): "+dCmd);
}
OSXUtil.RunOnMainThread(false, dCmd);
if( null != gl3ShaderProgram ) {
gl3ShaderProgram.destroy(MacOSXCGLContext.this.gl.getGL3());
gl3ShaderProgram = null;
}
}
}
}
CGL.clearDrawable(contextHandle);
backingLayerHost = null;
}
}
private final void validatePBufferConfig(long ctx) {
final long drawableHandle = drawable.getHandle();
if( needsSetContextPBuffer && 0 != drawableHandle && CGL.isNSOpenGLPixelBuffer(drawableHandle) ) {
// Must associate the pbuffer with our newly-created context
needsSetContextPBuffer = false;
CGL.setContextPBuffer(ctx, drawableHandle);
if(DEBUG) {
System.err.println("NS.validateDrawableConfig bind pbuffer "+toHexString(drawableHandle)+" -> ctx "+toHexString(ctx));
}
}
}
/** Returns true if size has been updated, otherwise false (same size). */
private final boolean validateDrawableSizeConfig(long ctx) {
final int width = drawable.getWidth();
final int height = drawable.getHeight();
if( lastWidth != width || lastHeight != height ) {
lastWidth = drawable.getWidth();
lastHeight = drawable.getHeight();
if(DEBUG) {
System.err.println("NS.validateDrawableConfig size changed");
}
return true;
}
return false;
}
@Override
public boolean copyImpl(long src, int mask) {
CGL.copyContext(contextHandle, src, mask);
return true;
}
@Override
public boolean makeCurrent(long ctx) {
final long cglCtx = CGL.getCGLContext(ctx);
if(0 == cglCtx) {
throw new InternalError("Null CGLContext for: "+this);
}
final int err = CGL.CGLLockContext(cglCtx);
if(CGL.kCGLNoError == err) {
validatePBufferConfig(ctx); // required to handle pbuffer change ASAP
return CGL.makeCurrentContext(ctx);
} else if(DEBUG) {
System.err.println("NSGL: Could not lock context: err 0x"+Integer.toHexString(err)+": "+this);
}
return false;
}
@Override
public boolean release(long ctx) {
try {
if( hasRendererQuirk(GLRendererQuirks.GLFlushBeforeRelease) && null != MacOSXCGLContext.this.getGLProcAddressTable() ) {
gl.glFlush();
}
} catch (GLException gle) {
if(DEBUG) {
System.err.println("MacOSXCGLContext.NSOpenGLImpl.release: INFO: glFlush() catched exception:");
gle.printStackTrace();
}
}
final boolean res = CGL.clearCurrentContext(ctx);
final long cglCtx = CGL.getCGLContext(ctx);
if(0 == cglCtx) {
throw new InternalError("Null CGLContext for: "+this);
}
final int err = CGL.CGLUnlockContext(cglCtx);
if(DEBUG && CGL.kCGLNoError != err) {
System.err.println("CGL: Could not unlock context: err 0x"+Integer.toHexString(err)+": "+this);
}
return res && CGL.kCGLNoError == err;
}
@Override
public boolean detachPBuffer() {
needsSetContextPBuffer = true;
// CGL.setContextPBuffer(contextHandle, 0); // doesn't work, i.e. not taking nil
return true;
}
@Override
public boolean setSwapInterval(int interval) {
final AttachGLLayerCmd cmd = attachGLLayerCmd;
if(null != cmd) {
synchronized(cmd) {
if( cmd.valid && 0 != cmd.nsOpenGLLayer) {
setSwapIntervalImpl(cmd.nsOpenGLLayer, interval);
return true;
}
}
}
setSwapIntervalImpl(0, interval);
return true;
}
private void setSwapIntervalImpl(final long l, int interval) {
if( 0 != l ) {
CGL.setNSOpenGLLayerSwapInterval(l, interval);
if( 0 < interval ) {
vsyncTimeout = interval * screenVSyncTimeout + 1000; // +1ms
} else {
vsyncTimeout = 1 * screenVSyncTimeout + 1000; // +1ms
}
if(DEBUG) { System.err.println("NS setSwapInterval: "+interval+" -> "+vsyncTimeout+" micros"); }
}
if(DEBUG) { System.err.println("CGL setSwapInterval: "+interval); }
CGL.setSwapInterval(contextHandle, interval);
}
private int skipSync=0;
/** TODO: Remove after discussion
private boolean perfIterReset = false;
private int perfIter = 0;
private long waitGLS = 0;
private long finishGLS = 0;
private long frameXS = 0;
private long lastFrameStart = 0;
*/
@Override
public boolean swapBuffers() {
final AttachGLLayerCmd cmd = attachGLLayerCmd;
if(null != cmd) {
synchronized(cmd) {
if( cmd.valid && 0 != cmd.nsOpenGLLayer) {
if( validateDrawableSizeConfig(contextHandle) ) {
// skip wait-for-vsync for a few frames if size has changed,
// allowing to update the texture IDs ASAP.
skipSync = 10;
}
final boolean res;
final int texID;
final boolean valid;
final boolean isFBO = drawable instanceof GLFBODrawableImpl;
if( isFBO ){
texID = ((GLFBODrawableImpl)drawable).getTextureBuffer(GL.GL_FRONT).getName();
valid = 0 != texID;
} else {
texID = 0;
valid = 0 != drawable.getHandle();
}
if(valid) {
res = CGL.flushBuffer(contextHandle);
if(res) {
if(0 == skipSync) {
/** TODO: Remove after discussion
perfIter++;
if( !perfIterReset && 100 == perfIter ) {
perfIterReset = true;
perfIter = 1;
waitGLS = 0;
finishGLS = 0;
frameXS = 0;
}
final long lastFramePeriod0 = TimeUnit.NANOSECONDS.toMicros(System.nanoTime()) - lastFrameStart;
gl.glFinish(); // Require to finish previous GL rendering to give CALayer proper result
final long lastFramePeriod1 = TimeUnit.NANOSECONDS.toMicros(System.nanoTime()) - lastFrameStart;
// If v-sync is disabled, frames will be drawn as quickly as possible w/o delay,
// while still synchronizing w/ CALayer.
// If v-sync is enabled wait until next swap interval (v-sync).
CGL.waitUntilNSOpenGLLayerIsReady(cmd.nsOpenGLLayer, vsyncTimeout);
final long lastFramePeriodX = TimeUnit.NANOSECONDS.toMicros(System.nanoTime()) - lastFrameStart;
final long finishGL = lastFramePeriod1 - lastFramePeriod0;
final long waitGL = lastFramePeriodX - lastFramePeriod1;
finishGLS += finishGL;
waitGLS += waitGL;
frameXS += lastFramePeriodX;
System.err.println("XXX["+perfIter+"] TO "+vsyncTimeout/1000+" ms, "+
"lFrame0 "+lastFramePeriod0/1000+" ms, "+
"lFrameX "+lastFramePeriodX/1000+" / "+frameXS/1000+" ~"+(frameXS/perfIter)/1000.0+" ms, "+
"finishGL "+finishGL/1000+" / "+finishGLS/1000+" ~"+(finishGLS/perfIter)/1000.0+" ms, "+
"waitGL "+waitGL/1000+" / "+waitGLS/1000+" ~"+(waitGLS/perfIter)/1000.0+" ms");
*/
//
// Required(?) to finish previous GL rendering to give CALayer proper result,
// i.e. synchronize both threads each w/ their GLContext sharing same resources.
//
// FIXME: IMHO this synchronization should be implicitly performed via 'CGL.flushBuffer(contextHandle)' above,
// in case this will be determined a driver bug - use a QUIRK entry in GLRendererQuirks!
gl.glFinish();
// If v-sync is disabled, frames will be drawn as quickly as possible w/o delay,
// while still synchronizing w/ CALayer.
// If v-sync is enabled wait until next swap interval (v-sync).
CGL.waitUntilNSOpenGLLayerIsReady(cmd.nsOpenGLLayer, vsyncTimeout);
} else {
skipSync--;
}
if(isFBO) {
// trigger CALayer to update incl. possible surface change (texture)
CGL.setNSOpenGLLayerNeedsDisplayFBO(cmd.nsOpenGLLayer, texID);
} else {
// trigger CALayer to update incl. possible surface change (new pbuffer handle)
CGL.setNSOpenGLLayerNeedsDisplayPBuffer(cmd.nsOpenGLLayer, drawable.getHandle());
}
// lastFrameStart = TimeUnit.NANOSECONDS.toMicros(System.nanoTime());
}
} else {
res = true;
}
return res;
}
}
}
return CGL.flushBuffer(contextHandle);
}
}
class CGLImpl implements GLBackendImpl {
@Override
public boolean isNSContext() { return false; }
@Override
public long create(long share, int ctp, int major, int minor) {
long ctx = 0;
final MacOSXCGLGraphicsConfiguration config = (MacOSXCGLGraphicsConfiguration) drawable.getNativeSurface().getGraphicsConfiguration();
final GLCapabilitiesImmutable chosenCaps = (GLCapabilitiesImmutable)config.getChosenCapabilities();
final long pixelFormat = MacOSXCGLGraphicsConfiguration.GLCapabilities2CGLPixelFormat(config.getScreen().getDevice(),
chosenCaps, ctp, major, minor);
if (pixelFormat == 0) {
throw new GLException("Unable to allocate pixel format with requested GLCapabilities");
}
try {
// Create new context
PointerBuffer ctxPB = PointerBuffer.allocateDirect(1);
if (DEBUG) {
System.err.println("Share context for CGL-based pbuffer context is " + toHexString(share));
}
int res = CGL.CGLCreateContext(pixelFormat, share, ctxPB);
if (res != CGL.kCGLNoError) {
throw new GLException("Error code " + res + " while creating context");
}
ctx = ctxPB.get(0);
if (0 != ctx) {
GLCapabilities fixedCaps = MacOSXCGLGraphicsConfiguration.CGLPixelFormat2GLCapabilities(pixelFormat);
fixedCaps = GLGraphicsConfigurationUtil.fixOpaqueGLCapabilities(fixedCaps, chosenCaps.isBackgroundOpaque());
{ // determine on-/offscreen caps, since pformat is ambiguous
fixedCaps.setFBO( false ); // n/a for CGLImpl
fixedCaps.setPBuffer( fixedCaps.isPBuffer() && !chosenCaps.isOnscreen() );
fixedCaps.setBitmap( false ); // n/a in our OSX impl.
fixedCaps.setOnscreen( !fixedCaps.isPBuffer() );
}
config.setChosenCapabilities(fixedCaps);
if(DEBUG) {
System.err.println("CGL create fixedCaps: "+fixedCaps);
}
if(fixedCaps.isPBuffer()) {
// Must now associate the pbuffer with our newly-created context
res = CGL.CGLSetPBuffer(ctx, drawable.getHandle(), 0, 0, 0);
if (res != CGL.kCGLNoError) {
throw new GLException("Error code " + res + " while attaching context to pbuffer");
}
}
}
} finally {
CGL.CGLDestroyPixelFormat(pixelFormat);
}
return ctx;
}
@Override
public boolean destroy(long ctx) {
return CGL.CGLDestroyContext(ctx) == CGL.kCGLNoError;
}
@Override
public void associateDrawable(boolean bound) {
}
@Override
public boolean copyImpl(long src, int mask) {
CGL.CGLCopyContext(src, contextHandle, mask);
return true;
}
@Override
public boolean makeCurrent(long ctx) {
int err = CGL.CGLLockContext(ctx);
if(CGL.kCGLNoError == err) {
err = CGL.CGLSetCurrentContext(ctx);
if(CGL.kCGLNoError == err) {
return true;
} else if(DEBUG) {
System.err.println("CGL: Could not make context current: err 0x"+Integer.toHexString(err)+": "+this);
}
} else if(DEBUG) {
System.err.println("CGL: Could not lock context: err 0x"+Integer.toHexString(err)+": "+this);
}
return false;
}
@Override
public boolean release(long ctx) {
try {
if( hasRendererQuirk(GLRendererQuirks.GLFlushBeforeRelease) && null != MacOSXCGLContext.this.getGLProcAddressTable() ) {
gl.glFlush();
}
} catch (GLException gle) {
if(DEBUG) {
System.err.println("MacOSXCGLContext.CGLImpl.release: INFO: glFlush() catched exception:");
gle.printStackTrace();
}
}
int err = CGL.CGLSetCurrentContext(0);
if(DEBUG && CGL.kCGLNoError != err) {
System.err.println("CGL: Could not release current context: err 0x"+Integer.toHexString(err)+": "+this);
}
int err2 = CGL.CGLUnlockContext(ctx);
if(DEBUG && CGL.kCGLNoError != err2) {
System.err.println("CGL: Could not unlock context: err 0x"+Integer.toHexString(err2)+": "+this);
}
return CGL.kCGLNoError == err && CGL.kCGLNoError == err2;
}
@Override
public boolean detachPBuffer() {
/* Doesn't work, i.e. not taking NULL
final int res = CGL.CGLSetPBuffer(contextHandle, 0, 0, 0, 0);
if (res != CGL.kCGLNoError) {
throw new GLException("Error code " + res + " while detaching context from pbuffer");
} */
return true;
}
@Override
public boolean setSwapInterval(int interval) {
final IntBuffer lval = Buffers.newDirectIntBuffer(1);
lval.put(0, interval);
CGL.CGLSetParameter(contextHandle, CGL.kCGLCPSwapInterval, lval);
return true;
}
@Override
public boolean swapBuffers() {
return CGL.kCGLNoError == CGL.CGLFlushDrawable(contextHandle);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy