diff options
author | Sven Gothel <[email protected]> | 2014-02-23 14:51:06 +0100 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2014-02-23 14:51:06 +0100 |
commit | 3352601e0860584509adf2b76f993d03893ded4b (patch) | |
tree | 974fccc8c0eb2f5ad9d4ffd741dfc35869ed67b5 /src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java | |
parent | f51933f0ebe9ae030c26c066e59a728ce08b8559 (diff) | |
parent | c67de337a8aaf52e36104c3f13e273aa19d21f1f (diff) |
Merge branch 'master' into stash_glyphcache
Conflicts:
make/scripts/tests.sh
src/jogl/classes/com/jogamp/graph/curve/OutlineShape.java
src/jogl/classes/com/jogamp/graph/curve/Region.java
src/jogl/classes/com/jogamp/graph/curve/opengl/GLRegion.java
src/jogl/classes/com/jogamp/graph/curve/opengl/RegionRenderer.java
src/jogl/classes/com/jogamp/graph/curve/opengl/Renderer.java
src/jogl/classes/com/jogamp/graph/curve/opengl/TextRenderer.java
src/jogl/classes/com/jogamp/graph/font/Font.java
src/jogl/classes/com/jogamp/opengl/math/VectorUtil.java
src/jogl/classes/jogamp/graph/curve/text/GlyphShape.java
src/jogl/classes/jogamp/graph/curve/text/GlyphString.java
src/jogl/classes/jogamp/graph/font/typecast/TypecastFont.java
src/jogl/classes/jogamp/graph/font/typecast/TypecastGlyph.java
src/jogl/classes/jogamp/graph/font/typecast/TypecastRenderer.java
Diffstat (limited to 'src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java')
-rw-r--r-- | src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java | 1239 |
1 files changed, 901 insertions, 338 deletions
diff --git a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java index 3cd4b340c..259c70641 100644 --- a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java +++ b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java @@ -1,22 +1,22 @@ /* * 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 @@ -29,11 +29,11 @@ * 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. */ @@ -41,6 +41,7 @@ package jogamp.opengl.macosx.cgl; import java.nio.ByteBuffer; +import java.nio.IntBuffer; import java.util.Map; import javax.media.nativewindow.AbstractGraphicsConfiguration; @@ -48,110 +49,180 @@ 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 abstract class MacOSXCGLContext extends GLContextImpl -{ +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.getMajor() > 10 || ( osvn.getMajor() == 10 && osvn.getMinor() >= 4 ); - isLionOrLater = osvn.getMajor() > 10 || ( osvn.getMajor() == 10 && osvn.getMinor() >= 7 ); + 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 && 1<=minor && minor<=2) { - // [3.1..3.2] -> GL3* + 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 GL3* on pre lion + // no GL Profile >= GL3 core on pre lion return false; } - if(ctBwdCompat) { - // no compatibility profile on OS X + if(3 < major && !isMavericksOrLater) { + // no GL Profile >= GL4 core on pre mavericks return false; } - return ctCore; - } else if(major<3) { + // [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(int ctp, int major, int minor) { + 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")); } - boolean ctCore = 0 != ( CTX_PROFILE_CORE & ctp ) ; - if( major == 3 && minor >= 1 && ctCore ) { - return CGL.kCGLOGLPVersion_3_2_Core; + 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() { + protected void resetStates(boolean isInit) { // no inner state _cglExt = null; cglExtProcAddressTable = null; - super.resetStates(); + super.resetStates(isInit); } + @Override public Object getPlatformGLExtensions() { return getCGLExt(); } - protected boolean isNSContext() { - return (null != impl) ? impl.isNSContext() : this.openGLMode == GLBackendType.NSOPENGL; + protected boolean isNSContext() { + return (null != impl) ? impl.isNSContext() : this.openGLMode == GLBackendType.NSOPENGL; } public CGLExt getCGLExt() { @@ -161,6 +232,7 @@ public abstract class MacOSXCGLContext extends GLContextImpl return _cglExt; } + @Override public final ProcAddressTable getPlatformExtProcAddressTable() { return getCGLExtProcAddressTable(); } @@ -169,10 +241,13 @@ public abstract class MacOSXCGLContext extends GLContextImpl return cglExtProcAddressTable; } + @Override protected Map<String, String> getFunctionNameMap() { return null; } + @Override protected Map<String, String> getExtensionNameMap() { return null; } + @Override protected long createContextARBImpl(long share, boolean direct, int ctp, int major, int minor) { if(!isGLProfileSupported(ctp, major, minor)) { if(DEBUG) { @@ -200,69 +275,130 @@ public abstract class MacOSXCGLContext extends GLContextImpl 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(); - if (capabilitiesChosen.getPbufferFloatingPointBuffers() && !isTigerOrLater) { - throw new GLException("Floating-point pbuffers supported only on OS X 10.4 or later"); - } - GLProfile glp = capabilitiesChosen.getGLProfile(); - if(glp.isGLES1() || glp.isGLES2() || glp.isGL4() || glp.isGL3() && !isLionOrLater) { + @Override + protected boolean createImpl(final long shareWithHandle) throws GLException { + final MacOSXCGLGraphicsConfiguration config = (MacOSXCGLGraphicsConfiguration) drawable.getNativeSurface().getGraphicsConfiguration(); + final GLCapabilitiesImmutable capabilitiesChosen = (GLCapabilitiesImmutable) config.getChosenCapabilities(); + final GLProfile glp = capabilitiesChosen.getGLProfile(); + if( glp.isGLES() || + ( 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); + if( 0 != shareWithHandle && GLBackendType.NSOPENGL != getOpenGLMode() ) { + throw new GLException("Context sharing only supported in mode "+GLBackendType.NSOPENGL+": "+this); } - return share; - } - - protected boolean createImpl(GLContextImpl shareWith) throws GLException { - long share = createImplPreset(shareWith); - contextHandle = createContextARB(share, true); + contextHandle = createContextARB(shareWithHandle, 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); @@ -275,7 +411,7 @@ public abstract class MacOSXCGLContext extends GLContextImpl protected void swapBuffers() { // single-buffer is already filtered out @ GLDrawableImpl#swapBuffers() if(!impl.swapBuffers()) { - throw new GLException("Error swapping buffers: "+this); + throw new GLException("Error swapping buffers: "+this); } } @@ -284,11 +420,19 @@ public abstract class MacOSXCGLContext extends GLContextImpl return impl.setSwapInterval(interval); } - public ByteBuffer glAllocateMemoryNV(int arg0, float arg1, float arg2, float arg3) { + @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(); @@ -316,39 +460,12 @@ public abstract class MacOSXCGLContext extends GLContextImpl } } } - + + @Override protected final StringBuilder getPlatformExtensionsStringImpl() { return new StringBuilder(); } - - public boolean isExtensionAvailable(String glExtensionName) { - if (glExtensionName.equals("GL_ARB_pbuffer") || - glExtensionName.equals("GL_ARB_pixel_format")) { - return true; - } - return super.isExtensionAvailable(glExtensionName); - } - - public int getOffscreenContextPixelDataType() { - throw new GLException("Should not call this"); - } - - public int getOffscreenContextReadBuffer() { - throw new GLException("Should not call this"); - } - public boolean offscreenImageNeedsVerticalFlip() { - throw new GLException("Should not call this"); - } - - public void bindPbufferToTexture() { - throw new GLException("Should not call this"); - } - - public void releasePbufferFromTexture() { - throw new GLException("Should not call this"); - } - // Support for "mode switching" as described in MacOSXCGLDrawable public void setOpenGLMode(GLBackendType mode) { if (mode == openGLMode) { @@ -364,10 +481,10 @@ public abstract class MacOSXCGLContext extends GLContextImpl } initOpenGLImpl(mode); openGLMode = mode; - haveSetOpenGLMode = true; + haveSetOpenGLMode = true; } public final GLBackendType getOpenGLMode() { return openGLMode; } - + protected void initOpenGLImpl(GLBackendType backend) { switch (backend) { case NSOPENGL: @@ -379,8 +496,9 @@ public abstract class MacOSXCGLContext extends GLContextImpl default: throw new InternalError("Illegal implementation mode " + backend); } - } - + } + + @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); @@ -391,291 +509,736 @@ public abstract class MacOSXCGLContext extends GLContextImpl sb.append("] "); return sb.toString(); } - + // NSOpenGLContext-based implementation class NSOpenGLImpl implements GLBackendImpl { - long nsOpenGLLayer = 0; - long nsOpenGLLayerPFmt = 0; - float screenVSyncTimeout; // microSec - int vsyncTimeout; // microSec - for nsOpenGLLayer mode - - public boolean isNSContext() { return true; } - - public long create(long share, int ctp, int major, int minor) { - long ctx = 0; - final MacOSXCGLDrawable drawable = (MacOSXCGLDrawable) MacOSXCGLContext.this.drawable; - final NativeSurface surface = drawable.getNativeSurface(); - final MacOSXCGLGraphicsConfiguration config = (MacOSXCGLGraphicsConfiguration) surface.getGraphicsConfiguration(); - final OffscreenLayerSurface backingLayerHost = NativeWindowFactory.getOffscreenLayerSurface(surface, true); - final GLCapabilitiesImmutable chosenCaps = (GLCapabilitiesImmutable) config.getChosenCapabilities(); - long pixelFormat = MacOSXCGLGraphicsConfiguration.GLCapabilities2NSPixelFormat(chosenCaps, ctp, major, minor); - if (pixelFormat == 0) { + 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("Unable to allocate pixel format with requested GLCapabilities: "+chosenCaps); + 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(); } - return 0; - } - config.setChosenPixelFormat(pixelFormat); - int sRefreshRate = CGL.getScreenRefreshRate(drawable.getNativeSurface().getGraphicsConfiguration().getScreen().getIndex()); - screenVSyncTimeout = 1000000f / (float)sRefreshRate; - if(DEBUG) { - System.err.println("NS create OSX>=lion "+isLionOrLater); - System.err.println("NS create backendType: "+drawable.getOpenGLMode()); - System.err.println("NS create backingLayerHost: "+backingLayerHost); - System.err.println("NS create share: "+share); - System.err.println("NS create chosenCaps: "+chosenCaps); - System.err.println("NS create pixelFormat: "+toHexString(pixelFormat)); - System.err.println("NS create drawable native-handle: "+toHexString(drawable.getHandle())); - System.err.println("NS create drawable NSView-handle: "+toHexString(drawable.getNSViewHandle())); - System.err.println("NS create screen refresh-rate: "+sRefreshRate+" hz, "+screenVSyncTimeout+" micros"); - // Thread.dumpStack(); - } - try { - int[] viewNotReady = new int[1]; + config.setChosenCapabilities(fixedCaps); + + final IntBuffer viewNotReady = Buffers.newDirectIntBuffer(1); // Try to allocate a context with this - ctx = CGL.createContext(share, - drawable.getNSViewHandle(), null!=backingLayerHost, - pixelFormat, - chosenCaps.isBackgroundOpaque(), - viewNotReady, 0); + ctx = CGL.createContext(share, nsViewHandle, incompleteView, + pixelFormat, chosenCaps.isBackgroundOpaque(), viewNotReady); if (0 == ctx) { - if(DEBUG) { - System.err.println("NS create failed: viewNotReady: "+ (1 == viewNotReady[0])); - } - return 0; + if(DEBUG) { + System.err.println("NS create failed: viewNotReady: "+ (1 == viewNotReady.get(0))); + } + return 0; } - - if (!chosenCaps.isPBuffer() && !chosenCaps.isBackgroundOpaque()) { + + if (chosenCaps.isOnscreen() && !chosenCaps.isBackgroundOpaque()) { // Set the context opacity CGL.setContextOpacity(ctx, 0); } + return ctx; + } - if(DEBUG) { - GLCapabilitiesImmutable caps0 = MacOSXCGLGraphicsConfiguration.NSPixelFormat2GLCapabilities(null, pixelFormat); - System.err.println("NS create pixelformat2GLCaps: "+caps0); + @Override + public boolean destroy(long ctx) { + if(0!=pixelFormat) { + CGL.deletePixelFormat(pixelFormat); + pixelFormat = 0; } - GLCapabilitiesImmutable fixedCaps = MacOSXCGLGraphicsConfiguration.NSPixelFormat2GLCapabilities(chosenCaps.getGLProfile(), pixelFormat); - fixedCaps = GLGraphicsConfigurationUtil.fixOpaqueGLCapabilities(fixedCaps, chosenCaps.isBackgroundOpaque()); - config.setChosenCapabilities(fixedCaps); - if(DEBUG) { - System.err.println("NS create fixedCaps: "+fixedCaps); + return CGL.deleteContext(ctx, true); + } + + /** + * NSOpenGLLayer creation and it's attachment is performed on the main-thread w/o [infinite] blocking. + * <p> + * 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. + * </p> + * <p> + * Hence this method blocks the main-thread only for a short period of time. + * </p> + */ + 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; } - if(fixedCaps.isPBuffer()) { - // Must now associate the pbuffer with our newly-created context - CGL.setContextPBuffer(ctx, drawable.getHandle()); + + public final String contentToString() { + return "valid "+valid+", size "+width+"x"+height+", ctx "+toHexString(ctx)+", opaque "+isOpaque+", texID "+texID+", pbuffer "+toHexString(pbuffer)+", nsOpenGLLayer "+toHexString(nsOpenGLLayer); } - // - // handled layered surface - // - if(null != backingLayerHost) { - nsOpenGLLayerPFmt = pixelFormat; - pixelFormat = 0; - final int texWidth, texHeight; - if(drawable instanceof MacOSXPbufferCGLDrawable) { - final MacOSXPbufferCGLDrawable osxPDrawable = (MacOSXPbufferCGLDrawable)drawable; - texWidth = osxPDrawable.getTextureWidth(); - texHeight = osxPDrawable.getTextureHeight(); - } else { - texWidth = drawable.getWidth(); - texHeight = drawable.getHeight(); - } - nsOpenGLLayer = CGL.createNSOpenGLLayer(ctx, nsOpenGLLayerPFmt, drawable.getHandle(), fixedCaps.isBackgroundOpaque(), texWidth, texHeight); - if(0>=texWidth || 0>=texHeight || !drawable.isRealized()) { - throw new GLException("Drawable not realized yet or invalid texture size, texSize "+texWidth+"x"+texHeight+", "+drawable); - } - if (DEBUG) { - System.err.println("NS create nsOpenGLLayer "+toHexString(nsOpenGLLayer)+", texSize "+texWidth+"x"+texHeight+", "+drawable); - } - backingLayerHost.attachSurfaceLayer(nsOpenGLLayer); - setSwapInterval(1); // enabled per default in layered surface + + @Override + public final String toString() { + return "AttachGLLayerCmd["+contentToString()+"]"; } - } finally { - if(0!=pixelFormat) { - CGL.deletePixelFormat(pixelFormat); + + @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()); + } + } } - return ctx; - } - - public boolean destroy(long ctx) { - if(0 != nsOpenGLLayer) { - final NativeSurface surface = drawable.getNativeSurface(); - if (DEBUG) { - System.err.println("NS destroy nsOpenGLLayer "+toHexString(nsOpenGLLayer)); - } - final OffscreenLayerSurface ols = NativeWindowFactory.getOffscreenLayerSurface(surface, true); - if(null == ols) { - throw new InternalError("XXX: "+ols); - } - CGL.releaseNSOpenGLLayer(nsOpenGLLayer); - ols.detachSurfaceLayer(nsOpenGLLayer); - CGL.deletePixelFormat(nsOpenGLLayerPFmt); - nsOpenGLLayerPFmt = 0; - nsOpenGLLayer = 0; } - return CGL.deleteContext(ctx, true); - } - public boolean copyImpl(long src, int mask) { - CGL.copyContext(contextHandle, src, mask); - return true; - } - - public boolean makeCurrent(long ctx) { - final long cglCtx = CGL.getCGLContext(ctx); - if(0 == cglCtx) { - throw new InternalError("Null CGLContext for: "+this); + @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; + } } - int err = CGL.CGLLockContext(cglCtx); - if(CGL.kCGLNoError == err) { - return CGL.makeCurrentContext(ctx); - } else if(DEBUG) { - System.err.println("NSGL: Could not lock context: err 0x"+Integer.toHexString(err)+": "+this); + + 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)); + } + } } - return false; - } - public boolean release(long ctx) { - try { - gl.glFlush(); // w/o glFlush()/glFinish() OSX < 10.7 (NVidia driver) may freeze - } catch (GLException gle) { - if(DEBUG) { - System.err.println("MacOSXCGLContext.NSOpenGLImpl.release: INFO: glFlush() catched exception:"); - gle.printStackTrace(); + /** 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; } - final boolean res = CGL.clearCurrentContext(ctx); - final long cglCtx = CGL.getCGLContext(ctx); - if(0 == cglCtx) { - throw new InternalError("Null CGLContext for: "+this); + + @Override + public boolean copyImpl(long src, int mask) { + CGL.copyContext(contextHandle, src, mask); + return true; } - 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; - } - public boolean setSwapInterval(int interval) { - if(0 != nsOpenGLLayer) { - CGL.setNSOpenGLLayerSwapInterval(nsOpenGLLayer, interval); - vsyncTimeout = interval * (int)screenVSyncTimeout; - if(DEBUG) { System.err.println("NS setSwapInterval: "+vsyncTimeout+" micros"); } + @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; } - CGL.setSwapInterval(contextHandle, interval); - return true; - } - - public boolean swapBuffers() { - if( 0 != nsOpenGLLayer ) { - // If v-sync is disabled, frames will be drawn as quickly as possible - // w/o delay but in sync w/ CALayer. Otherwise wait until next swap interval (v-sync). - CGL.waitUntilNSOpenGLLayerIsReady(nsOpenGLLayer, vsyncTimeout); + + @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; } - if(CGL.flushBuffer(contextHandle)) { - if(0 != nsOpenGLLayer) { - // trigger CALayer to update - CGL.setNSOpenGLLayerNeedsDisplay(nsOpenGLLayer); + + @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; } - return false; - } + + 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 { - public boolean isNSContext() { return false; } - - public long create(long share, int ctp, int major, int minor) { - long ctx = 0; - MacOSXCGLGraphicsConfiguration config = (MacOSXCGLGraphicsConfiguration) drawable.getNativeSurface().getGraphicsConfiguration(); - GLCapabilitiesImmutable chosenCaps = (GLCapabilitiesImmutable)config.getChosenCapabilities(); - long pixelFormat = MacOSXCGLGraphicsConfiguration.GLCapabilities2CGLPixelFormat(chosenCaps, ctp, major, minor); - if (pixelFormat == 0) { - throw new GLException("Unable to allocate pixel format with requested GLCapabilities"); - } - config.setChosenPixelFormat(pixelFormat); - 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"); + @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"); } - if(chosenCaps.isPBuffer()) { - // Attach newly-created context to the pbuffer - res = CGL.CGLSetPBuffer(ctxPB.get(0), drawable.getHandle(), 0, 0, 0); + 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 attaching context to pbuffer"); + 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); } - ctx = ctxPB.get(0); - if(0!=ctx) { - if(DEBUG) { - GLCapabilitiesImmutable caps0 = MacOSXCGLGraphicsConfiguration.CGLPixelFormat2GLCapabilities(pixelFormat); - System.err.println("NS created: "+caps0); - } - } - } finally { - CGL.CGLDestroyPixelFormat(pixelFormat); + return ctx; } - return ctx; - } - - public boolean destroy(long ctx) { - return CGL.CGLDestroyContext(ctx) == CGL.kCGLNoError; - } - public boolean copyImpl(long src, int mask) { - CGL.CGLCopyContext(src, contextHandle, mask); - return true; - } - - public boolean makeCurrent(long ctx) { - int err = CGL.CGLLockContext(ctx); - if(CGL.kCGLNoError == err) { - err = CGL.CGLSetCurrentContext(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) { - return true; + 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 make context current: err 0x"+Integer.toHexString(err)+": "+this); + System.err.println("CGL: Could not lock context: 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; } - return false; - } - public boolean release(long ctx) { - try { - gl.glFlush(); // w/o glFlush()/glFinish() OSX < 10.7 (NVidia driver) may freeze - } catch (GLException gle) { - if(DEBUG) { - System.err.println("MacOSXCGLContext.CGLImpl.release: INFO: glFlush() catched exception:"); - gle.printStackTrace(); + @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; } - 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); + + @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; } - int err2 = CGL.CGLUnlockContext(ctx); - if(DEBUG && CGL.kCGLNoError != err2) { - System.err.println("CGL: Could not unlock context: err 0x"+Integer.toHexString(err2)+": "+this); + + @Override + public boolean setSwapInterval(int interval) { + final IntBuffer lval = Buffers.newDirectIntBuffer(1); + lval.put(0, interval); + CGL.CGLSetParameter(contextHandle, CGL.kCGLCPSwapInterval, lval); + return true; } - return CGL.kCGLNoError == err && CGL.kCGLNoError == err2; - } - - public boolean setSwapInterval(int interval) { - int[] lval = new int[] { interval } ; - CGL.CGLSetParameter(contextHandle, CGL.kCGLCPSwapInterval, lval, 0); - return true; - } - public boolean swapBuffers() { - return CGL.kCGLNoError == CGL.CGLFlushDrawable(contextHandle); - } - } + @Override + public boolean swapBuffers() { + return CGL.kCGLNoError == CGL.CGLFlushDrawable(contextHandle); + } + } } |