diff options
Diffstat (limited to 'src/jogl/classes/jogamp/opengl/macosx')
-rw-r--r-- | src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java | 358 |
1 files changed, 224 insertions, 134 deletions
diff --git a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java index f01e03a2f..33893e456 100644 --- a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java +++ b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java @@ -71,6 +71,7 @@ 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.GLExtensions; @@ -461,10 +462,13 @@ public abstract class MacOSXCGLContext extends GLContextImpl // NSOpenGLContext-based implementation class NSOpenGLImpl implements GLBackendImpl { - private long pixelFormat = 0; // lifecycle: [create - destroy] - private volatile long nsOpenGLLayer = 0; // lifecycle: [associateDrawable_true - associateDrawable_false] - private float screenVSyncTimeout = 16666; // microSec - defaults to 1/60s - private volatile int vsyncTimeout = 16666 + 1000; // microSec - for nsOpenGLLayer mode - defaults to 1/60s + 1ms + 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; @@ -562,7 +566,7 @@ public abstract class MacOSXCGLContext extends GLContextImpl } final int sRefreshRate = OSXUtil.GetScreenRefreshRate(drawable.getNativeSurface().getGraphicsConfiguration().getScreen().getIndex()); if( 0 < sRefreshRate ) { - screenVSyncTimeout = 1000000f / sRefreshRate; + screenVSyncTimeout = 1000000 / sRefreshRate; } if(DEBUG) { System.err.println("NS create OSX>=lion "+isLionOrLater); @@ -608,32 +612,147 @@ public abstract class MacOSXCGLContext extends GLContextImpl 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 AttachNSOpenGLLayer 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; + + AttachNSOpenGLLayer(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; + } + + @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())+" - "+Thread.currentThread().getName()); + } + } 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())+" - "+Thread.currentThread().getName()); + } + OSXUtil.RunLater(this, 1); + } + } + } + } + } + AttachNSOpenGLLayer attachCALayerCmd = null; + + class DetachNSOpenGLLayer implements Runnable { + final AttachNSOpenGLLayer cmd; + + DetachNSOpenGLLayer(AttachNSOpenGLLayer cmd) { + this.cmd = cmd; + } + @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 @ "+Thread.currentThread().getName()+": "); + t.printStackTrace(); + } + CGL.releaseNSOpenGLLayer(cmd.nsOpenGLLayer); + if(DEBUG) { + System.err.println("NSOpenGLLayer.Detach: OK, layer "+toHexString(cmd.nsOpenGLLayer)+", drawableHandle "+toHexString(drawable.getHandle())+" - "+Thread.currentThread().getName()); + } + cmd.nsOpenGLLayer = 0; + cmd.valid = false; + } else if(DEBUG) { + System.err.println("NSOpenGLLayer.Detach: Skipped "+toHexString(cmd.nsOpenGLLayer)+", drawableHandle "+toHexString(drawable.getHandle())+" - "+Thread.currentThread().getName()); + } + } + } + } + @Override public void associateDrawable(boolean bound) { - final OffscreenLayerSurface backingLayerHost = NativeWindowFactory.getOffscreenLayerSurface(drawable.getNativeSurface(), true); + backingLayerHost = NativeWindowFactory.getOffscreenLayerSurface(drawable.getNativeSurface(), true); if(DEBUG) { System.err.println("MaxOSXCGLContext.NSOpenGLImpl.associateDrawable: "+bound+", ctx "+toHexString(contextHandle)+", hasBackingLayerHost "+(null!=backingLayerHost)); + // Thread.dumpStack(); } if( bound ) { - if( null != backingLayerHost ) { - if( 0 != nsOpenGLLayer ) { // FIXME: redundant - throw new InternalError("Lifecycle: bound=true, hasBackingLayerHost=true, but 'nsOpenGLLayer' is already/still set local: "+nsOpenGLLayer+", "+this); - } - nsOpenGLLayer = backingLayerHost.getAttachedSurfaceLayer(); - if( 0 != nsOpenGLLayer ) { // FIXME: redundant - throw new InternalError("Lifecycle: bound=true, hasBackingLayerHost=true, but 'nsOpenGLLayer' is already/still set on backingLayerHost: "+nsOpenGLLayer+", "+this); - } + if( null != backingLayerHost ) { + final GLCapabilitiesImmutable chosenCaps; + final long ctx; + final int texID; + final long pbufferHandle; + final int gl3ShaderProgramName; // // handled layered surface // - final GLCapabilitiesImmutable chosenCaps = drawable.getChosenGLCapabilities(); - final long ctx = MacOSXCGLContext.this.getHandle(); - final int texID; + chosenCaps = drawable.getChosenGLCapabilities(); + ctx = MacOSXCGLContext.this.getHandle(); final long drawableHandle = drawable.getHandle(); - final long pbufferHandle; if(drawable instanceof GLFBODrawableImpl) { final GLFBODrawableImpl fbod = (GLFBODrawableImpl)drawable; texID = fbod.getTextureBuffer(GL.GL_FRONT).getName(); @@ -657,7 +776,6 @@ public abstract class MacOSXCGLContext extends GLContextImpl if(0>=lastWidth || 0>=lastHeight || !drawable.isRealized()) { throw new GLException("Drawable not realized yet or invalid texture size, texSize "+lastWidth+"x"+lastHeight+", "+drawable); } - final int gl3ShaderProgramName; if( MacOSXCGLContext.this.isGL3core() ) { if( null == gl3ShaderProgram) { gl3ShaderProgram = createCALayerShader(MacOSXCGLContext.this.gl.getGL3()); @@ -665,88 +783,42 @@ public abstract class MacOSXCGLContext extends GLContextImpl gl3ShaderProgramName = gl3ShaderProgram.program(); } else { gl3ShaderProgramName = 0; - } + } - /** - * NSOpenGLLayer creation and it's attachment is performed on the main w/o blocking, - * due to OSX main-thread requirements. - * Note: It somewhat works from another thread, however, - * GC-dealloc of the 'released' resources would happen very late! - * - * NSOpenGLLayer initialization creates it's own GL ctx sharing - * this ctx, hence we have to lock this ctx in the main-thread. - * - * Locking of this ctx while creation and attachment - * also gives us good means of synchronization, i.e. it will be - * performed after this thread ends it's associateDrawable() [makeCurrent(), setDrawable(..)] - * and before the next display cycle involving makeCurrent(). - */ - OSXUtil.RunOnMainThread(false, new Runnable() { - public void run() { - if (DEBUG) { - System.err.println("NS create nsOpenGLLayer.0 "+Thread.currentThread().getName()); - } - final long cglCtx = CGL.getCGLContext(ctx); - if(0 == cglCtx) { - throw new GLException("Null CGLContext for: "+MacOSXCGLContext.this); - } - if( CGL.kCGLNoError != CGL.CGLLockContext(cglCtx) ) { - throw new GLException("Could not lock CGLContext for: "+MacOSXCGLContext.this); - } - try { - nsOpenGLLayer = CGL.createNSOpenGLLayer(ctx, gl3ShaderProgramName, pixelFormat, pbufferHandle, texID, chosenCaps.isBackgroundOpaque(), lastWidth, lastHeight); - if (DEBUG) { - System.err.println("NS create nsOpenGLLayer.2 "+Thread.currentThread().getName()+": "+toHexString(nsOpenGLLayer)+" w/ pbuffer "+toHexString(pbufferHandle)+", texID "+texID+", texSize "+lastWidth+"x"+lastHeight+", "+drawable); - } - backingLayerHost.attachSurfaceLayer(nsOpenGLLayer); - setSwapInterval(1); // enabled per default in layered surface - } catch (Throwable t) { - throw new GLException("createNSOpenGLLayer failed for: "+MacOSXCGLContext.this, t); - } finally { - if(CGL.kCGLNoError != CGL.CGLUnlockContext(cglCtx)) { - throw new GLException("Could not unlock CGLContext for: "+MacOSXCGLContext.this); - } - } - if (DEBUG) { - System.err.println("NS create nsOpenGLLayer.X "+Thread.currentThread().getName()); - } - } - }); - CGL.setContextView(contextHandle, 0); // [ctx clearDrawable] + // All CALayer lifecycle ops are deferred on main-thread + attachCALayerCmd = new AttachNSOpenGLLayer( + backingLayerHost, ctx, gl3ShaderProgramName, pixelFormat, pbufferHandle, texID, + chosenCaps.isBackgroundOpaque(), lastWidth, lastHeight ); + OSXUtil.RunOnMainThread(false, attachCALayerCmd); } else { // -> null == backingLayerHost lastWidth = drawable.getWidth(); lastHeight = drawable.getHeight(); boolean[] isPBuffer = { false }; boolean[] isFBO = { false }; - CGL.setContextView(contextHandle, getNSViewHandle(isPBuffer, isFBO)); // will call [ctx clearDrawable] if view == 0, otherwise [ctx setView: view] if valid + CGL.setContextView(contextHandle, getNSViewHandle(isPBuffer, isFBO)); } } else { // -> !bound - if( 0 != nsOpenGLLayer ) { - if( null == backingLayerHost ) { // FIXME: redundant - throw new InternalError("Lifecycle: bound=false, hasNSOpneGLLayer=true, but 'backingLayerHost' is null local: "+nsOpenGLLayer+", "+this); + if( null != backingLayerHost ) { + final AttachNSOpenGLLayer cmd = attachCALayerCmd; + attachCALayerCmd = null; + if( 0 != cmd.pbuffer ) { + CGL.setContextPBuffer(contextHandle, 0); } - - if (DEBUG) { - System.err.println("NS destroy nsOpenGLLayer "+toHexString(nsOpenGLLayer)+", "+drawable); - } - if( backingLayerHost.isSurfaceLayerAttached() ) { - // still having a valid OLS attached to surface (parent OLS could have been removed) - backingLayerHost.detachSurfaceLayer(); - } - // All CALayer lifecycle calls are deferred on main-thread, so is this. - final long _nsOpenGLLayer = nsOpenGLLayer; - nsOpenGLLayer = 0; - OSXUtil.RunOnMainThread(false, new Runnable() { - public void run() { - CGL.releaseNSOpenGLLayer(_nsOpenGLLayer); - } - }); - if( null != gl3ShaderProgram ) { - gl3ShaderProgram.destroy(MacOSXCGLContext.this.gl.getGL3()); - gl3ShaderProgram = null; + synchronized(cmd) { + if( !cmd.valid ) { + cmd.valid = true; // skip pending creation + } else { + // All CALayer lifecycle ops are deferred on main-thread + OSXUtil.RunOnMainThread(false, new DetachNSOpenGLLayer(cmd)); + if( null != gl3ShaderProgram ) { + gl3ShaderProgram.destroy(MacOSXCGLContext.this.gl.getGL3()); + gl3ShaderProgram = null; + } + } } } - CGL.setContextView(contextHandle, 0); // [ctx clearDrawable] + CGL.clearDrawable(contextHandle); + backingLayerHost = null; } } @@ -832,65 +904,83 @@ public abstract class MacOSXCGLContext extends GLContextImpl @Override public boolean setSwapInterval(int interval) { - if(0 != nsOpenGLLayer) { - CGL.setNSOpenGLLayerSwapInterval(nsOpenGLLayer, interval); + final AttachNSOpenGLLayer cmd = attachCALayerCmd; + 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 * (int)screenVSyncTimeout + 1000; // +1ms + vsyncTimeout = interval * 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); - return true; } - + private int skipSync=0; @Override public boolean swapBuffers() { - final boolean res; - if( 0 != 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 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) { - if(0 == skipSync) { - // 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(nsOpenGLLayer, vsyncTimeout); - } else { - skipSync--; - } - res = CGL.flushBuffer(contextHandle); - if(res) { - if(isFBO) { - // trigger CALayer to update incl. possible surface change (texture) - CGL.setNSOpenGLLayerNeedsDisplayFBO(nsOpenGLLayer, texID); + final AttachNSOpenGLLayer cmd = attachCALayerCmd; + 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) { + // 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()); + } + } } else { - // trigger CALayer to update incl. possible surface change (new pbuffer handle) - CGL.setNSOpenGLLayerNeedsDisplayPBuffer(nsOpenGLLayer, drawable.getHandle()); + res = true; } + return res; } - } else { - res = true; } - } else { - res = CGL.flushBuffer(contextHandle); } - return res; + return CGL.flushBuffer(contextHandle); } } |