From 28c6472335b924080d638b33a28f8f4eedb459b1 Mon Sep 17 00:00:00 2001
From: Sven Gothel <sgothel@jausoft.com>
Date: Fri, 15 Mar 2013 19:08:22 +0100
Subject: Remodel OSX/CALayer Threading (commit
 896e8b021b39e9415040a57a1d540d7d24b02db1): Run on main-thread w/o blocking ;
 Misc Changes

Commit 896e8b021b39e9415040a57a1d540d7d24b02db1 moved all native CALayer calls to the current thread
to avoid deadlocks.

Even though this seemed to be fine at least resource GC (release/dealloc calls) were issued
very late in time, probably due to multithreading synchronization of JAWT and/or OSX API.

Example: Our 'TestAddRemove01GLCanvasSwingAWT' test didn't freed CALayer resources incl. GL ctx
         when destroying the objects (AWT Frame, GLCanvas, ..), leading to resource starvation .. eventually.

Remedy is a compromise of behavior before commit 896e8b021b39e9415040a57a1d540d7d24b02db1
and that commit, i.e. to run CALayer lifecycle methods on main-thread, but do not block!

The careful part within MacOSXCGLContext.associateDrawable(..) performs the following block on main-thread:
  - lock the context
  - create NSOpenGLLayer (incl. it's own shared GL context and the DisplayLink)
  - attach NSOpenGLLayer to root CALayer
  - unlock the context

Due to the GL ctx locking, this async offthread operation is safe within our course of operations.

Details:
  - NSOpenGLContext
    - Context and CVDisplayLink creation at init
    - Call [ctx update] if texture/frame size changed
    - 'waitUntilRenderSignal' uses default TO value if given TO is 0 to avoid deadlocks

+++

Misc Changes:
  - Fix object type detection: isMemberOfClass -> isKindOfClass
    - OSXUtil_isNSView0
      OSXUtil_isNSWindow0,
      CGL_isNSOpenGLPixelBuffer

  - MacOSXCGLDrawable/MacOSXPbufferCGLDrawable: remove getNSViewHandle() method.
    MacOSXCGLContext uses common code to detect nature of the drawable handle.

  - MacOSXCGLContext/CALayer: Use safe screenVSyncTimeout values, never 0 to avoid deadlock!

  - JAWTWindow.invalidate: Call detachSurfaceLayer() if not done yet
---
 .../classes/javax/media/opengl/awt/GLCanvas.java   |   1 +
 .../jogamp/opengl/macosx/cgl/MacOSXCGLContext.java | 127 +++++++++++++--------
 .../opengl/macosx/cgl/MacOSXCGLDrawable.java       |   4 -
 .../macosx/cgl/MacOSXPbufferCGLDrawable.java       |   6 -
 .../macosx/MacOSXWindowSystemInterface-calayer.m   |  85 +++++++-------
 .../native/macosx/MacOSXWindowSystemInterface.m    |  83 ++++++++------
 6 files changed, 166 insertions(+), 140 deletions(-)

(limited to 'src/jogl')

diff --git a/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java b/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java
index 278e2dc37..2fdf18404 100644
--- a/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java
+++ b/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java
@@ -946,6 +946,7 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing
                 animatorPaused = false;
             }
             
+            // OLS will be detached by disposeGL's context destruction below
             if( null != context ) {
                 if( context.isCreated() ) {
                     // Catch dispose GLExceptions by GLEventListener, just 'print' them
diff --git a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java
index 9e6085030..f01e03a2f 100644
--- a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java
+++ b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java
@@ -462,9 +462,9 @@ public abstract class MacOSXCGLContext extends GLContextImpl
   // NSOpenGLContext-based implementation
   class NSOpenGLImpl implements GLBackendImpl {
       private long pixelFormat = 0; // lifecycle:  [create - destroy]
-      private long nsOpenGLLayer = 0; // lifecycle:  [associateDrawable_true - associateDrawable_false]
-      private float screenVSyncTimeout; // microSec
-      private int vsyncTimeout;    // microSec - for nsOpenGLLayer mode
+      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 int lastWidth=0, lastHeight=0; // allowing to detect size change
       private boolean needsSetContextPBuffer = false;
       private ShaderProgram gl3ShaderProgram = null;
@@ -472,6 +472,8 @@ public abstract class MacOSXCGLContext extends GLContextImpl
       @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) {
@@ -479,27 +481,15 @@ public abstract class MacOSXCGLContext extends GLContextImpl
               isPBuffer[0] = false;
               isFBO[0] = true;
               if(DEBUG) {
-                  System.err.println("NS create GLFBODrawableImpl drawable: isFBO "+isFBO+", isPBuffer "+isPBuffer+", "+drawable.getClass().getName()+",\n\t"+drawable);
-              }
-          } else if(drawable instanceof MacOSXCGLDrawable) {
-              // we allow null here! (special pbuffer case)
-              nsViewHandle = ((MacOSXCGLDrawable)MacOSXCGLContext.this.drawable).getNSViewHandle();
-              isPBuffer[0] = CGL.isNSOpenGLPixelBuffer(drawable.getHandle());
-              isFBO[0] = false;
-              if(DEBUG) {
-                  System.err.println("NS create MacOSXCGLDrawable drawable handle isFBO "+isFBO+", isPBuffer "+isPBuffer+", "+drawable.getClass().getName()+",\n\t"+drawable);
+                  System.err.println("NS viewHandle.1: GLFBODrawableImpl drawable: isFBO "+isFBO+", isPBuffer "+isPBuffer+", "+drawable.getClass().getName()+",\n\t"+drawable);
               }
           } else {
-              // we only allow a valid NSView here
               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(DEBUG) {
-                  System.err.println("NS create Anonymous drawable handle "+toHexString(drawableHandle)+": isNSView "+isNSView+", isNSWindow "+isNSWindow+", isFBO "+isFBO+", isPBuffer "+isPBuffer+", "+drawable.getClass().getName()+",\n\t"+drawable);
-              }
               if( isNSView ) {
                   nsViewHandle = drawableHandle;
               } else if( isNSWindow ) {
@@ -507,7 +497,10 @@ public abstract class MacOSXCGLContext extends GLContextImpl
               } else if( isPBuffer[0] ) {
                   nsViewHandle = 0;
               } else {
-                  throw new RuntimeException("Anonymous drawable instance's handle neither NSView, NSWindow nor PBuffer: "+toHexString(drawableHandle)+", "+drawable.getClass().getName()+",\n\t"+drawable);
+                  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];
@@ -567,8 +560,10 @@ public abstract class MacOSXCGLContext extends GLContextImpl
               _fixedCaps.setOnscreen( !isFBO && !isPBuffer );
               fixedCaps = GLGraphicsConfigurationUtil.fixOpaqueGLCapabilities(_fixedCaps, chosenCaps.isBackgroundOpaque());
           }
-          int sRefreshRate = OSXUtil.GetScreenRefreshRate(drawable.getNativeSurface().getGraphicsConfiguration().getScreen().getIndex());
-          screenVSyncTimeout = 1000000f / sRefreshRate;
+          final int sRefreshRate = OSXUtil.GetScreenRefreshRate(drawable.getNativeSurface().getGraphicsConfiguration().getScreen().getIndex());
+          if( 0 < sRefreshRate ) {
+              screenVSyncTimeout = 1000000f / sRefreshRate;
+          }
           if(DEBUG) {
               System.err.println("NS create OSX>=lion "+isLionOrLater);
               System.err.println("NS create incompleteView: "+incompleteView);
@@ -611,7 +606,6 @@ public abstract class MacOSXCGLContext extends GLContextImpl
               pixelFormat = 0;
           }
           return CGL.deleteContext(ctx, true);
-          
       }
 
       @Override
@@ -622,10 +616,8 @@ public abstract class MacOSXCGLContext extends GLContextImpl
               System.err.println("MaxOSXCGLContext.NSOpenGLImpl.associateDrawable: "+bound+", ctx "+toHexString(contextHandle)+", hasBackingLayerHost "+(null!=backingLayerHost));
           }          
           
-          if( bound ) {
-              
-              if( null != backingLayerHost ) {
-                  
+          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);
                   }
@@ -676,32 +668,59 @@ public abstract class MacOSXCGLContext extends GLContextImpl
                   }
                    
                   /**
-                   * NSOpenGLLayer creation is performed on the current thread,
-                   * which immediately creates it's own GL ctx sharing this ctx
-                   * not causing any locking issues.
+                   * 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!
                    * 
-                   * Subsequent attaching is performed on main-thread w/o blocking.
+                   * NSOpenGLLayer initialization creates it's own GL ctx sharing 
+                   * this ctx, hence we have to lock this ctx in the main-thread.
                    * 
-                   * This is a lock free operation.
-                   */
-                  final long cglCtx = CGL.getCGLContext(ctx);
-                  if(0 == cglCtx) {
-                      throw new InternalError("Null CGLContext for: "+this);
-                  }
-                  nsOpenGLLayer = CGL.createNSOpenGLLayer(ctx, gl3ShaderProgramName, pixelFormat, pbufferHandle, texID, chosenCaps.isBackgroundOpaque(), lastWidth, lastHeight);
-                  if (DEBUG) {
-                      System.err.println("NS create nsOpenGLLayer "+toHexString(nsOpenGLLayer)+" w/ pbuffer "+toHexString(pbufferHandle)+", texID "+texID+", texSize "+lastWidth+"x"+lastHeight+", "+drawable);
-                  }
-                  backingLayerHost.attachSurfaceLayer(nsOpenGLLayer);
-                  setSwapInterval(1); // enabled per default in layered surface
-              } else {
+                   * 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]
+              } else { // -> null == backingLayerHost                  
                   lastWidth = drawable.getWidth();
                   lastHeight = drawable.getHeight();                  
                   boolean[] isPBuffer = { false };
                   boolean[] isFBO = { false };
-                  CGL.setContextView(contextHandle, getNSViewHandle(isPBuffer, isFBO));
+                  CGL.setContextView(contextHandle, getNSViewHandle(isPBuffer, isFBO)); // will call [ctx clearDrawable] if view == 0, otherwise [ctx setView: view] if valid
               }
-          } else {
+          } 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);
@@ -714,13 +733,20 @@ public abstract class MacOSXCGLContext extends GLContextImpl
                       // still having a valid OLS attached to surface (parent OLS could have been removed)
                       backingLayerHost.detachSurfaceLayer();
                   }
-                  CGL.releaseNSOpenGLLayer(nsOpenGLLayer);
+                  // 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;
                   }
-                  nsOpenGLLayer = 0;
               }
+              CGL.setContextView(contextHandle, 0); // [ctx clearDrawable]             
           }
       }
 
@@ -808,8 +834,10 @@ public abstract class MacOSXCGLContext extends GLContextImpl
       public boolean setSwapInterval(int interval) {
           if(0 != nsOpenGLLayer) {
               CGL.setNSOpenGLLayerSwapInterval(nsOpenGLLayer, interval);
-              vsyncTimeout = interval * (int)screenVSyncTimeout + 1000; // +1ms
-              if(DEBUG) { System.err.println("NS setSwapInterval: "+vsyncTimeout+" micros"); }
+              if( 0 < interval ) {
+                  vsyncTimeout = interval * (int)screenVSyncTimeout + 1000; // +1ms
+              }
+              if(DEBUG) { System.err.println("NS setSwapInterval: "+interval+" -> "+vsyncTimeout+" micros"); }
           }
           CGL.setSwapInterval(contextHandle, interval);
           return true;
@@ -839,8 +867,9 @@ public abstract class MacOSXCGLContext extends GLContextImpl
               }
               if(valid) {
                   if(0 == skipSync) {
-                      // 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).
+                      // 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--;
diff --git a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLDrawable.java b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLDrawable.java
index 0f282d33f..1daa892ba 100644
--- a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLDrawable.java
+++ b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLDrawable.java
@@ -106,10 +106,6 @@ public abstract class MacOSXCGLDrawable extends GLDrawableImpl {
   protected void setRealizedImpl() {
   }
   
-  protected long getNSViewHandle() {
-      return GLBackendType.NSOPENGL == openGLMode ? getHandle() : 0;
-  }
-
   @Override
   protected void associateContext(GLContext ctx, boolean bound) {
     // NOTE: we need to keep track of the created contexts in order to
diff --git a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXPbufferCGLDrawable.java b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXPbufferCGLDrawable.java
index ddff43031..1e845d179 100644
--- a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXPbufferCGLDrawable.java
+++ b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXPbufferCGLDrawable.java
@@ -92,12 +92,6 @@ public class MacOSXPbufferCGLDrawable extends MacOSXCGLDrawable {
     return new MacOSXPbufferCGLContext(this, shareWith);
   }
   
-  @Override
-  protected long getNSViewHandle() {
-    // pbuffer handle is NSOpenGLPixelBuffer
-    return 0;
-  }
-
   protected int getTextureTarget() { return pBufferTexTarget;  }
   protected int getTextureWidth() { return pBufferTexWidth; }
   protected int getTextureHeight() { return pBufferTexHeight; }
diff --git a/src/jogl/native/macosx/MacOSXWindowSystemInterface-calayer.m b/src/jogl/native/macosx/MacOSXWindowSystemInterface-calayer.m
index 125ca8af8..96783c75d 100644
--- a/src/jogl/native/macosx/MacOSXWindowSystemInterface-calayer.m
+++ b/src/jogl/native/macosx/MacOSXWindowSystemInterface-calayer.m
@@ -117,6 +117,7 @@ extern GLboolean glIsVertexArray (GLuint array);
     CGLContextObj cglCtx = [self CGLContextObj];
 
     DBG_PRINT("MyNSOpenGLContext::dealloc.0 %p (refcnt %d) - CGL-Ctx %p\n", self, (int)[self retainCount], cglCtx);
+    // NSLog(@"MyNSOpenGLContext::dealloc: %@",[NSThread callStackSymbols]);
     [self clearDrawable];
     if( NULL != cglCtx ) {
         CGLDestroyContext( cglCtx );
@@ -143,8 +144,8 @@ extern GLboolean glIsVertexArray (GLuint array);
     NSOpenGLPixelFormat* parentPixelFmt;
     int texWidth;
     int texHeight;
-    int dedicatedWidth;
-    int dedicatedHeight;
+    volatile int dedicatedWidth;
+    volatile int dedicatedHeight;
     volatile NSOpenGLPixelBuffer* pbuffer;
     volatile GLuint textureID;
     volatile NSOpenGLPixelBuffer* newPBuffer;
@@ -184,7 +185,6 @@ extern GLboolean glIsVertexArray (GLuint array);
 
 - (void) setGLEnabled: (Bool) enable;
 - (Bool) validateTexSizeWithDedicatedSize;
-- (Bool) validateTexSize: (int) _texWidth texHeight: (int) _texHeight;
 - (void) setTextureID: (int) _texID;
 
 - (Bool) isSamePBuffer: (NSOpenGLPixelBuffer*) p;
@@ -315,6 +315,14 @@ static const GLfloat gl_verts[] = {
             displayLink = NULL;
         }
     }
+    if(NULL != displayLink) {
+        CVReturn cvres;
+        DBG_PRINT("MyNSOpenGLLayer::openGLContextForPixelFormat.1: setup DisplayLink %p\n", displayLink);
+        cvres = CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(displayLink, [glContext CGLContextObj], [parentPixelFmt CGLPixelFormatObj]);
+        if(kCVReturnSuccess != cvres) {
+            DBG_PRINT("MyNSOpenGLLayer::init %p, CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext failed: %d\n", self, cvres);
+        }
+    }
     if(NULL != displayLink) {
         cvres = CVDisplayLinkSetOutputCallback(displayLink, renderMyNSOpenGLLayer, self);
         if(kCVReturnSuccess != cvres) {
@@ -354,14 +362,9 @@ static const GLfloat gl_verts[] = {
 
 - (Bool) validateTexSizeWithDedicatedSize
 {
-    return [self validateTexSize: dedicatedWidth texHeight: dedicatedHeight];
-}
-
-- (Bool) validateTexSize: (int) _texWidth texHeight: (int) _texHeight
-{
-    if(_texHeight != texHeight || _texWidth != texWidth) {
-        texWidth = _texWidth;
-        texHeight = _texHeight;
+    if( dedicatedHeight != texHeight || dedicatedWidth != texWidth ) {
+        texWidth = dedicatedWidth;
+        texHeight = dedicatedHeight;
 
         GLfloat texCoordWidth, texCoordHeight;
         if(NULL != pbuffer) {
@@ -477,10 +480,10 @@ static const GLfloat gl_verts[] = {
 - (void)releaseLayer
 {
     DBG_PRINT("MyNSOpenGLLayer::releaseLayer.0: %p (refcnt %d)\n", self, (int)[self retainCount]);
+    [self setGLEnabled: NO];
     [self disableAnimation];
     pthread_mutex_lock(&renderLock);
     [self deallocPBuffer];
-    // [[self openGLContext] release];
     if( NULL != glContext ) {
         [glContext release];
         glContext = NULL;
@@ -570,16 +573,6 @@ static const GLfloat gl_verts[] = {
 {
     DBG_PRINT("MyNSOpenGLLayer::openGLContextForPixelFormat.0: %p (refcnt %d) - pfmt %p, ctx %p, DisplayLink %p\n",
         self, (int)[self retainCount], pixelFormat, glContext, displayLink);
-#ifndef HAS_CADisplayLink
-    if(NULL != displayLink) {
-        CVReturn cvres;
-        DBG_PRINT("MyNSOpenGLLayer::openGLContextForPixelFormat.1: setup DisplayLink %p\n", displayLink);
-        cvres = CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(displayLink, [glContext CGLContextObj], [pixelFormat CGLPixelFormatObj]);
-        if(kCVReturnSuccess != cvres) {
-            DBG_PRINT("MyNSOpenGLLayer::init %p, CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext failed: %d\n", self, cvres);
-        }
-    }
-#endif
     return glContext;
 }
 
@@ -607,6 +600,9 @@ static const GLfloat gl_verts[] = {
         GLenum textureTarget;
 
         Bool texSizeChanged = [self validateTexSizeWithDedicatedSize];
+        if( texSizeChanged ) {
+            [context update];
+        }
 
         if( NULL != pbuffer ) {
             if( texSizeChanged && 0 != textureID ) {
@@ -812,29 +808,23 @@ static const GLfloat gl_verts[] = {
                 struct timespec t0, t1, td, td2;
                 timespec_now(&t0);
             #endif
-            if(0 < to_micros) {
-                struct timespec to_abs = lastWaitTime;
-                timespec_addmicros(&to_abs, to_micros);
-                #ifdef DBG_SYNC
-                    timespec_subtract(&td, &to_abs, &t0);
-                    fprintf(stderr, ", (%ld) / ", timespec_milliseconds(&td));
-                #endif
-                wr = pthread_cond_timedwait(&renderSignal, &renderLock, &to_abs);
-                #ifdef DBG_SYNC
-                    timespec_now(&t1);
-                    timespec_subtract(&td, &t1, &t0);
-                    timespec_subtract(&td2, &t1, &lastWaitTime);
-                    fprintf(stderr, "(%ld) / (%ld) ms", timespec_milliseconds(&td), timespec_milliseconds(&td2));
-                #endif
-            } else {
-                pthread_cond_wait (&renderSignal, &renderLock);
-                #ifdef DBG_SYNC
-                    timespec_now(&t1);
-                    timespec_subtract(&td, &t1, &t0);
-                    timespec_subtract(&td2, &t1, &lastWaitTime);
-                    fprintf(stderr, "(%ld) / (%ld) ms", timespec_milliseconds(&td), timespec_milliseconds(&td2));
-                #endif
+            if( 0 >= to_micros ) {
+                to_micros = 16666 + 1000; // defaults to 1/60s + 1ms
+                NSLog(@"MyNSOpenGLContext::waitUntilRenderSignal: to_micros was zero, using defaults");
             }
+            struct timespec to_abs = lastWaitTime;
+            timespec_addmicros(&to_abs, to_micros);
+            #ifdef DBG_SYNC
+                timespec_subtract(&td, &to_abs, &t0);
+                fprintf(stderr, ", (%ld) / ", timespec_milliseconds(&td));
+            #endif
+            wr = pthread_cond_timedwait(&renderSignal, &renderLock, &to_abs);
+            #ifdef DBG_SYNC
+                timespec_now(&t1);
+                timespec_subtract(&td, &t1, &t0);
+                timespec_subtract(&td2, &t1, &lastWaitTime);
+                fprintf(stderr, "(%ld) / (%ld) ms", timespec_milliseconds(&td), timespec_milliseconds(&td2));
+            #endif
             ready = YES;
         }
     } while (NO == ready && 0 == wr) ;
@@ -923,9 +913,16 @@ void setNSOpenGLLayerNeedsDisplayPBuffer(NSOpenGLLayer* layer, NSOpenGLPixelBuff
 void releaseNSOpenGLLayer(NSOpenGLLayer* layer) {
     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
     MyNSOpenGLLayer* l = (MyNSOpenGLLayer*) layer;
+
+    [CATransaction begin];
+    [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
+
     DBG_PRINT("MyNSOpenGLLayer::releaseNSOpenGLLayer.0: %p\n", l);
     [l releaseLayer];
     DBG_PRINT("MyNSOpenGLLayer::releaseNSOpenGLLayer.X: %p\n", l);
+
+    [CATransaction commit];
+
     [pool release];
 }
 
diff --git a/src/jogl/native/macosx/MacOSXWindowSystemInterface.m b/src/jogl/native/macosx/MacOSXWindowSystemInterface.m
index f8faeb8d0..38f1789e3 100644
--- a/src/jogl/native/macosx/MacOSXWindowSystemInterface.m
+++ b/src/jogl/native/macosx/MacOSXWindowSystemInterface.m
@@ -501,6 +501,28 @@ NSView* getNSView(NSOpenGLContext* ctx) {
   return view;
 }
 
+static Bool lockViewIfReady(NSView *view) {
+    Bool viewReady = false;
+
+    if (view != nil) {
+        if ([view lockFocusIfCanDraw] == NO) {
+            DBG_PRINT("lockViewIfReady.1 [view lockFocusIfCanDraw] failed\n");
+        } else {
+            NSRect frame = [view frame];
+            if ((frame.size.width == 0) || (frame.size.height == 0)) {
+                [view unlockFocus];
+                DBG_PRINT("lockViewIfReady.2 view.frame size %dx%d\n", (int)frame.size.width, (int)frame.size.height);
+            } else {
+                DBG_PRINT("lockViewIfReady.X ready and locked\n");
+                viewReady = true;
+            }
+        }
+    } else {
+        DBG_PRINT("lockViewIfReady.3 nil view\n");
+    }
+    return viewReady;
+}
+
 NSOpenGLContext* createContext(NSOpenGLContext* share,
                     NSView* view,
                     Bool incompleteView,
@@ -515,40 +537,18 @@ NSOpenGLContext* createContext(NSOpenGLContext* share,
     DBG_PRINT("createContext.0: share %p, view %p, incompleteView %d, pixfmt %p, opaque %d\n",
         share, view, (int)incompleteView, fmt, opaque);
 
-    if (view != nil) {
-        Bool viewReady = true;
-
-        if(!incompleteView) {
-            if ([view lockFocusIfCanDraw] == NO) {
-                DBG_PRINT("createContext.1 [view lockFocusIfCanDraw] failed\n");
-                viewReady = false;
-            }
-        }
-        if(viewReady) {
-            NSRect frame = [view frame];
-            if ((frame.size.width == 0) || (frame.size.height == 0)) {
-                if(!incompleteView) {
-                    [view unlockFocus];
-                }
-                DBG_PRINT("createContext.2 view.frame size %dx%d\n", (int)frame.size.width, (int)frame.size.height);
-                viewReady = false;
-            }
-        }
+    Bool viewReadyAndLocked = incompleteView ? false : lockViewIfReady(view);
 
-        if (!viewReady)
-        {
-            if (viewNotReady != NULL)
-            {
-                *viewNotReady = 1;
-            }
+    if (nil != viewNotReady) {
+        *viewNotReady = 1;
+    }
 
-            // the view is not ready yet
-            DBG_PRINT("createContext.X: view not ready yet\n");
-            [pool release];
-            return NULL;
-        }
+    if (nil != view && !incompleteView && !viewReadyAndLocked) {
+        DBG_PRINT("createContext.X: Assumed complete view not ready yet\n");
+        [pool release];
+        return NULL;
     }
-    
+
     NSOpenGLContext* ctx = [[NSOpenGLContext alloc] initWithFormat:fmt shareContext:share];
         
     if ( nil != ctx && nil != view ) {
@@ -556,10 +556,8 @@ NSOpenGLContext* createContext(NSOpenGLContext* share,
             GLint zeroOpacity = 0;
             [ctx setValues:&zeroOpacity forParameter:NSOpenGLCPSurfaceOpacity];
         }
-        if(!incompleteView) {
-            DBG_PRINT("createContext.3.0: setView\n");
+        if( viewReadyAndLocked ) {
             [ctx setView:view];
-            DBG_PRINT("createContext.3.X: setView\n");
             [view unlockFocus];        
         }
     }
@@ -571,8 +569,19 @@ NSOpenGLContext* createContext(NSOpenGLContext* share,
 
 void setContextView(NSOpenGLContext* ctx, NSView* view) {
     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
-    if ( nil != ctx && nil != view ) {
-        [ctx setView:view];
+    if ( nil != ctx ) {
+        if ( nil != view ) {
+            Bool viewReadyAndLocked = lockViewIfReady(view);
+            DBG_PRINT("setContextView.0: ctx %p, view %p: setView: %d\n", ctx, view, viewReadyAndLocked);
+            if( viewReadyAndLocked ) {
+                [ctx setView:view];
+                [view unlockFocus];        
+            }
+        } else {
+            DBG_PRINT("setContextView.1: ctx %p, view nil: clearDrawable\n", ctx);
+            [ctx clearDrawable];
+        }
+        DBG_PRINT("setContextView.X\n");
     }
     [pool release];
 }
@@ -725,7 +734,7 @@ void setContextTextureImageToPBuffer(NSOpenGLContext* ctx, NSOpenGLPixelBuffer*
 Bool isNSOpenGLPixelBuffer(uint64_t object) {
   NSObject *nsObj = (NSObject*) (intptr_t) object;
   DBG_PRINT("isNSOpenGLPixelBuffer.0: obj %p\n", object);
-  Bool res = [nsObj isMemberOfClass:[NSOpenGLPixelBuffer class]];
+  Bool res = [nsObj isKindOfClass:[NSOpenGLPixelBuffer class]];
   DBG_PRINT("isNSOpenGLPixelBuffer.X: res %d\n", (int)res);
   return res;
 }
-- 
cgit v1.2.3