From 1055ce051fd7bd3c88724888cf8f46c75535a249 Mon Sep 17 00:00:00 2001
From: Sven Gothel <sgothel@jausoft.com>
Date: Wed, 9 Nov 2011 00:14:01 +0100
Subject: OS X Layered View: Part2 Java/Native MacOSXCGLContext /
 MacOSXPbufferCGLContext

Undo 368cbf4462d7f3635c1ef4497424c360b5ccc203:
    - "use SurfaceUpdateListener() to notify layer", simply use MacOSXCGLContext's swap impl.

MacOSXCGLContext:
  - NSOpenGLImpl.create():
    - issues createNSOpenGLLayer() using tex-size (maybe POT) as args
    - attaches the NSOpenGLLayer to the JAWT layered surface host

  - NSOpenGLImpl.setSwapInterval() propagates interval to our NSOpenGLLayer impl.

  - NSOpenGLImpl.swapBuffer() (layer case):
    - waits for v-sync if enabled (v-sync), or until native 'draw' is finished (tearing)
      using our pthread/ NSOpenGLLayer synchronization.
      The latter uses CADisplayLink or CVDisplayLinkRef for v-sync synchronization.

    - flushes our local ctx

    - triggers our NSOpenGLLayer to 'draw'

- MacOSXPbufferCGLContext create() sets the texture size independently of pbuffer size (POT/NPOT)
---
 make/stub_includes/opengl/macosx-window-system.h   |   4 +-
 .../jogamp/opengl/macosx/cgl/MacOSXCGLContext.java |  54 ++--
 .../opengl/macosx/cgl/MacOSXPbufferCGLContext.java |  27 +-
 .../macosx/MacOSXWindowSystemInterface-pbuffer.m   | 327 ++++++++++++++++++---
 4 files changed, 329 insertions(+), 83 deletions(-)

diff --git a/make/stub_includes/opengl/macosx-window-system.h b/make/stub_includes/opengl/macosx-window-system.h
index 959b44d8d..347de6299 100644
--- a/make/stub_includes/opengl/macosx-window-system.h
+++ b/make/stub_includes/opengl/macosx-window-system.h
@@ -53,7 +53,9 @@ void setContextPBuffer(NSOpenGLContext* ctx, NSOpenGLPixelBuffer* pBuffer);
 void setContextTextureImageToPBuffer(NSOpenGLContext* ctx, NSOpenGLPixelBuffer* pBuffer, GLenum colorBuffer);
 
 // NSOpenGLLayer* createNSOpenGLLayer(NSOpenGLContext* ctx, NSOpenGLPixelFormat* fmt, NSView* view, Bool opaque);
-NSOpenGLLayer* createNSOpenGLLayer(NSOpenGLContext* ctx, NSOpenGLPixelFormat* fmt, NSOpenGLPixelBuffer* pbuffer, Bool opaque, int width, int height);
+NSOpenGLLayer* createNSOpenGLLayer(NSOpenGLContext* ctx, NSOpenGLPixelFormat* fmt, NSOpenGLPixelBuffer* pbuffer, Bool opaque, int texWidth, int texHeight);
+void setNSOpenGLLayerSwapInterval(NSOpenGLLayer* layer, int interval);
+void waitUntilNSOpenGLLayerIsReady(NSOpenGLLayer* layer, long to_ms);
 void setNSOpenGLLayerNeedsDisplay(NSOpenGLLayer* glLayer);
 void releaseNSOpenGLLayer(NSOpenGLLayer *glLayer);
 
diff --git a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java
index b942fb904..4ab81e5ff 100644
--- a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java
+++ b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java
@@ -45,9 +45,7 @@ import java.util.Map;
 
 import javax.media.nativewindow.AbstractGraphicsConfiguration;
 import javax.media.nativewindow.AbstractGraphicsDevice;
-import javax.media.nativewindow.DefaultGraphicsConfiguration;
 import javax.media.nativewindow.NativeSurface;
-import javax.media.nativewindow.SurfaceUpdatedListener;
 import javax.media.opengl.GLCapabilitiesImmutable;
 import javax.media.opengl.GLContext;
 import javax.media.opengl.GLException;
@@ -67,7 +65,6 @@ import com.jogamp.common.util.VersionNumber;
 import com.jogamp.gluegen.runtime.ProcAddressTable;
 import com.jogamp.gluegen.runtime.opengl.GLProcAddressResolver;
 
-
 public abstract class MacOSXCGLContext extends GLContextImpl
 {    
   // Abstract interface for implementation of this context (either
@@ -285,12 +282,6 @@ public abstract class MacOSXCGLContext extends GLContextImpl
     if(!impl.setSwapInterval(interval)) {
         throw new GLException("Error set swap-interval: "+this);        
     }
-    if ( isNSContext() ) {
-        CGL.setSwapInterval(contextHandle, interval);
-    } else {
-        int[] lval = new int[] { (int) interval } ;
-        CGL.CGLSetParameter(contextHandle, CGL.kCGLCPSwapInterval, lval, 0);
-    }
     currentSwapInterval = interval ;
   }
 
@@ -431,6 +422,7 @@ public abstract class MacOSXCGLContext extends GLContextImpl
             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()));
+            Thread.dumpStack();
         }
         try {
           int[] viewNotReady = new int[1];
@@ -473,21 +465,24 @@ public abstract class MacOSXCGLContext extends GLContextImpl
               final MacOSXJAWTWindow lsh = MacOSXCGLDrawableFactory.getLayeredSurfaceHost(surface);
               nsOpenGLLayerPFmt = pixelFormat;
               pixelFormat = 0;
-              // final long nsView = drawable.getNSViewHandle();
-              // nsOpenGLLayer = CGL.createNSOpenGLLayer(ctx, nsOpenGLLayerPFmt, nsView, fixedCaps.isBackgroundOpaque());
-              nsOpenGLLayer = CGL.createNSOpenGLLayer(ctx, nsOpenGLLayerPFmt, drawable.getHandle(), fixedCaps.isBackgroundOpaque(),
-                                                      drawable.getWidth(), drawable.getHeight());
+              /*
+                final long nsView = drawable.getNSViewHandle();
+                nsOpenGLLayer = CGL.createNSOpenGLLayer(ctx, nsOpenGLLayerPFmt, nsView, fixedCaps.isBackgroundOpaque());
+               */
+              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 (DEBUG) {
                   System.err.println("NS create nsOpenGLLayer "+toHexString(nsOpenGLLayer));
               }
               lsh.attachSurfaceLayer(nsOpenGLLayer);
-              lsh.addSurfaceUpdatedListener(new SurfaceUpdatedListener() {
-                public void surfaceUpdated(Object updater, NativeSurface ns, long when) {
-                  if(0 != nsOpenGLLayer) {
-                      CGL.setNSOpenGLLayerNeedsDisplay(nsOpenGLLayer);
-                  }
-                }                  
-              });
           }
         } finally {
           if(0!=pixelFormat) {
@@ -527,11 +522,26 @@ public abstract class MacOSXCGLContext extends GLContextImpl
     }
 
     public boolean setSwapInterval(int interval) {
-      CGL.setSwapInterval(contextHandle, interval);      
+      if(0 != nsOpenGLLayer) {
+        CGL.setNSOpenGLLayerSwapInterval(nsOpenGLLayer, interval);
+      }
+      CGL.setSwapInterval(contextHandle, interval);
       return true;
     }
+    
     public boolean swapBuffers() {
-      return CGL.flushBuffer(contextHandle);
+      if(0 != nsOpenGLLayer) {
+        // sync w/ CALayer renderer - wait until next frame is required (v-sync)
+        CGL.waitUntilNSOpenGLLayerIsReady(nsOpenGLLayer, 16); // timeout 16ms -> 60Hz
+      }
+      if(CGL.flushBuffer(contextHandle)) {
+          if(0 != nsOpenGLLayer) {
+              // trigger CALayer to update
+              CGL.setNSOpenGLLayerNeedsDisplay(nsOpenGLLayer);
+          }
+          return true;
+      }
+      return false;
     }
   }
 
diff --git a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXPbufferCGLContext.java b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXPbufferCGLContext.java
index c5743b923..f4b71d37d 100644
--- a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXPbufferCGLContext.java
+++ b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXPbufferCGLContext.java
@@ -33,19 +33,14 @@
 
 package jogamp.opengl.macosx.cgl;
 
-import javax.media.nativewindow.DefaultGraphicsConfiguration;
 import javax.media.opengl.GL;
-import javax.media.opengl.GL2;
-import javax.media.opengl.GLCapabilitiesImmutable;
 import javax.media.opengl.GLContext;
 import javax.media.opengl.GLException;
 import javax.media.opengl.GLPbuffer;
 
-
 public class MacOSXPbufferCGLContext extends MacOSXCGLContext {
 
   // State for render-to-texture and render-to-texture-rectangle support
-  private int textureTarget; // e.g. GL_TEXTURE_2D, GL_TEXTURE_RECTANGLE_NV
   private int texture;       // actual texture object
 
   public MacOSXPbufferCGLContext(MacOSXPbufferCGLDrawable drawable,
@@ -55,7 +50,7 @@ public class MacOSXPbufferCGLContext extends MacOSXCGLContext {
 
   public void bindPbufferToTexture() {
     GL gl = getGL();
-    gl.glBindTexture(textureTarget, texture);
+    gl.glBindTexture(((MacOSXPbufferCGLDrawable)drawable).getTextureTarget(), texture);
     // FIXME: not clear whether this is really necessary, but since
     // the API docs seem to imply it is and since it doesn't seem to
     // impact performance, leaving it in
@@ -70,18 +65,10 @@ public class MacOSXPbufferCGLContext extends MacOSXCGLContext {
     
     if (newCreated) {
       // Initialize render-to-texture support if requested
-      DefaultGraphicsConfiguration config = (DefaultGraphicsConfiguration) drawable.getNativeSurface().getGraphicsConfiguration().getNativeGraphicsConfiguration();
-      GLCapabilitiesImmutable capabilities = (GLCapabilitiesImmutable)config.getChosenCapabilities();
-      GL gl = getGL();
-      boolean rect = gl.isGL2GL3() && capabilities.getPbufferRenderToTextureRectangle();
-      if (rect) {
-        if (!gl.isExtensionAvailable("GL_EXT_texture_rectangle")) {
-          System.err.println("MacOSXPbufferCGLContext: WARNING: GL_EXT_texture_rectangle extension not " +
-                             "supported; skipping requested render_to_texture_rectangle support for pbuffer");
-          rect = false;
-        }
-      }
-      textureTarget = (rect ? GL2.GL_TEXTURE_RECTANGLE : GL.GL_TEXTURE_2D);
+      final GL gl = getGL();
+      final MacOSXPbufferCGLDrawable osxPDrawable = (MacOSXPbufferCGLDrawable)drawable;
+      final int textureTarget = osxPDrawable.getTextureTarget();
+  
       int[] tmp = new int[1];
       gl.glGenTextures(1, tmp, 0);
       texture = tmp[0];
@@ -90,7 +77,9 @@ public class MacOSXPbufferCGLContext extends MacOSXCGLContext {
       gl.glTexParameteri(textureTarget, GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST);
       gl.glTexParameteri(textureTarget, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE);
       gl.glTexParameteri(textureTarget, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_EDGE);
-      gl.glCopyTexImage2D(textureTarget, 0, GL.GL_RGB, 0, 0, drawable.getWidth(), drawable.getHeight(), 0);
+      gl.glTexImage2D(textureTarget, 0, GL.GL_RGB, osxPDrawable.getTextureWidth(), osxPDrawable.getTextureHeight(), 
+                      0, GL.GL_RGB, GL.GL_UNSIGNED_BYTE, null);
+      gl.glCopyTexSubImage2D(textureTarget, 0, 0, 0, 0, 0, drawable.getWidth(), drawable.getHeight());
     }
   }
 
diff --git a/src/jogl/native/macosx/MacOSXWindowSystemInterface-pbuffer.m b/src/jogl/native/macosx/MacOSXWindowSystemInterface-pbuffer.m
index 16e5829ba..6e6e1e2c2 100644
--- a/src/jogl/native/macosx/MacOSXWindowSystemInterface-pbuffer.m
+++ b/src/jogl/native/macosx/MacOSXWindowSystemInterface-pbuffer.m
@@ -1,54 +1,175 @@
 #import "MacOSXWindowSystemInterface.h"
+#import <QuartzCore/QuartzCore.h>
+#import <pthread.h>
+#include "timespec.h"
+
+// 
+// CADisplayLink only available on iOS >= 3.1, sad, since it's convenient.
+// Use CVDisplayLink otherwise.
+//
+// #define HAS_CADisplayLink 1
+//
+
+// lock/sync debug output
+//
+// #define DBG_SYNC 1
+//
+#ifdef DBG_SYNC
+    // #define SYNC_PRINT(...) NSLog(@ ## __VA_ARGS__)
+    #define SYNC_PRINT(...) fprintf(stderr, __VA_ARGS__); fflush(stderr)
+#else
+    #define SYNC_PRINT(...)
+#endif
+
+// fps debug output
+//
+// #define DBG_PERF 1
 
 @interface MyNSOpenGLLayer: NSOpenGLLayer
 {
 @protected
     NSOpenGLPixelBuffer* pbuffer;
-    int width;
-    int height;
+    int texWidth;
+    int texHeight;
     GLuint textureID;
+    GLint swapInterval;
+#ifdef HAS_CADisplayLink
+    CADisplayLink* displayLink;
+#else
+    CVDisplayLinkRef displayLink;
+#endif
+    int tc;
+    struct timespec t0;
 @public
-    volatile BOOL        shallDraw;
+    pthread_mutex_t renderLock;
+    pthread_cond_t renderSignal;
+    BOOL shallDraw;
 }
 
 - (id) initWithContext: (NSOpenGLContext*) ctx
        pixelFormat: (NSOpenGLPixelFormat*) pfmt
        pbuffer: (NSOpenGLPixelBuffer*) p
        opaque: (Bool) opaque
-       width: (int) width 
-       height: (int) height;
+       texWidth: (int) texWidth 
+       texHeight: (int) texHeight;
+
+- (int)getSwapInterval;
+- (void)setSwapInterval:(int)interval;
+- (void)tick;
 
 @end
 
+#ifndef HAS_CADisplayLink
+
+static CVReturn renderMyNSOpenGLLayer(CVDisplayLinkRef displayLink, 
+                                      const CVTimeStamp *inNow, 
+                                      const CVTimeStamp *inOutputTime, 
+                                      CVOptionFlags flagsIn, 
+                                      CVOptionFlags *flagsOut, 
+                                      void *displayLinkContext)
+{
+    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+    MyNSOpenGLLayer* l = (MyNSOpenGLLayer*)displayLinkContext;
+    pthread_mutex_lock(&l->renderLock);
+    #ifdef DBG_PERF
+        [l tick];
+    #endif
+    pthread_cond_signal(&l->renderSignal);
+    SYNC_PRINT("-*-");
+    pthread_mutex_unlock(&l->renderLock);
+    [pool release];
+    return kCVReturnSuccess;
+}
+
+#endif
+
 @implementation MyNSOpenGLLayer
 
 - (id) initWithContext: (NSOpenGLContext*) _ctx
        pixelFormat: (NSOpenGLPixelFormat*) _fmt
        pbuffer: (NSOpenGLPixelBuffer*) p
        opaque: (Bool) opaque
-       width: (int) _width 
-       height: (int) _height;
+       texWidth: (int) _texWidth 
+       texHeight: (int) _texHeight;
 {
     self = [super init];
+
+    pthread_mutex_init(&renderLock, NULL); // fast non-recursive
+    pthread_cond_init(&renderSignal, NULL); // no attribute
+
     pbuffer = p;
     [pbuffer retain];
 
-    [self setAsynchronous: NO];
-    // [self setAsynchronous: YES]; // FIXME: JAU
-    [self setNeedsDisplayOnBoundsChange: NO];
+    // instantiate a deactivated displayLink
+#ifdef HAS_CADisplayLink
+    displayLink = [[CVDisplayLink displayLinkWithTarget:self selector:@selector(setNeedsDisplay)] retain];
+    [displayLink setPaused: YES];
+#else
+    CVReturn cvres;
+    {
+        int allDisplaysMask = 0;
+        int virtualScreen, accelerated, displayMask;
+        for (virtualScreen = 0; virtualScreen < [_fmt  numberOfVirtualScreens]; virtualScreen++) {
+            [_fmt getValues:&displayMask forAttribute:NSOpenGLPFAScreenMask forVirtualScreen:virtualScreen];
+            [_fmt getValues:&accelerated forAttribute:NSOpenGLPFAAccelerated forVirtualScreen:virtualScreen];
+            if (accelerated) {
+                allDisplaysMask |= displayMask;
+            }
+        }
+        cvres = CVDisplayLinkCreateWithOpenGLDisplayMask(allDisplaysMask, &displayLink);
+        if(kCVReturnSuccess != cvres) {
+            DBG_PRINT("MyNSOpenGLLayer::init %p, CVDisplayLinkCreateWithOpenGLDisplayMask %X failed: %d\n", self, allDisplaysMask, cvres);
+            displayLink = NULL;
+        }
+    }
+    /**
+    if(NULL != displayLink) {
+        cvres = CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(displayLink, [_ctx CGLContextObj], [_fmt CGLPixelFormatObj]);
+        if(kCVReturnSuccess != cvres) {
+            DBG_PRINT("MyNSOpenGLLayer::init %p, CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext failed: %d\n", self, cvres);
+            displayLink = NULL;
+        }
+    } */
+    if(NULL != displayLink) {
+        cvres = CVDisplayLinkSetOutputCallback(displayLink, renderMyNSOpenGLLayer, self);
+        if(kCVReturnSuccess != cvres) {
+            DBG_PRINT("MyNSOpenGLLayer::init %p, CVDisplayLinkSetOutputCallback failed: %d\n", self, cvres);
+            displayLink = NULL;
+        }
+    }
+    if(NULL != displayLink) {
+        CVDisplayLinkStop(displayLink);
+    }
+#endif
+    [self setAsynchronous: YES];
+
+    [self setNeedsDisplayOnBoundsChange: YES]; // FIXME: learn how to recreate on size change!
     [self setOpaque: opaque ? YES : NO];
-    width = _width;
-    height = _height;
+    texWidth = _texWidth;
+    texHeight = _texHeight;
     textureID = 0;
+    swapInterval = -1;
     shallDraw = NO;
-    DBG_PRINT("MyNSOpenGLLayer::init %p, ctx %p, pfmt %p, pbuffer %p, opaque %d, %dx%d -> %dx%d\n", 
-        self, _ctx, _fmt, pbuffer, opaque, width, height, [pbuffer pixelsWide], [pbuffer pixelsHigh]);
+    DBG_PRINT("MyNSOpenGLLayer::init %p, ctx %p, pfmt %p, pbuffer %p, opaque %d, pbuffer %dx%d -> tex %dx%d\n", 
+        self, _ctx, _fmt, pbuffer, opaque, [pbuffer pixelsWide], [pbuffer pixelsHigh], texWidth, texHeight);
     return self;
 }
 
 - (void)dealloc
 {
+    [self setAsynchronous: NO];
+#ifdef HAS_CADisplayLink
+    [displayLink setPaused: YES];
+    [displayLink release];
+#else
+    if(NULL!=displayLink) {
+        CVDisplayLinkStop(displayLink);
+        CVDisplayLinkRelease(displayLink);
+    }
+#endif
     [pbuffer release];
+    pthread_cond_destroy(&renderSignal);
+    pthread_mutex_destroy(&renderLock);
     DBG_PRINT("MyNSOpenGLLayer::dealloc %p\n", self);
     [super dealloc];
 }
@@ -56,31 +177,59 @@
 - (BOOL)canDrawInOpenGLContext:(NSOpenGLContext *)context pixelFormat:(NSOpenGLPixelFormat *)pixelFormat 
         forLayerTime:(CFTimeInterval)timeInterval displayTime:(const CVTimeStamp *)timeStamp
 {
-    DBG_PRINT("MyNSOpenGLLayer::canDrawInOpenGLContext %p: %d\n", self, self->shallDraw);
-    return self->shallDraw;
+    // assume both methods 'canDrawInOpenGLContext' and 'drawInOpenGLContext' 
+    // are called from the same thread subsequently
+    pthread_mutex_lock(&renderLock);
+    if(NO == shallDraw) {
+        SYNC_PRINT("<0>");
+        pthread_mutex_unlock(&renderLock);
+    } else {
+        SYNC_PRINT("<");
+    }
+    return shallDraw;
 }
 
 - (void)drawInOpenGLContext:(NSOpenGLContext *)context pixelFormat:(NSOpenGLPixelFormat *)pixelFormat 
         forLayerTime:(CFTimeInterval)timeInterval displayTime:(const CVTimeStamp *)timeStamp
 {
-    self->shallDraw = NO;
-
     [context makeCurrentContext];
    
+    /**
+     * v-sync doesn't works w/ NSOpenGLLayer's context .. well :(
+     * Using CVDisplayLink .. see setSwapInterval() below.
+     *
+    if(0 <= swapInterval) {
+        GLint si;
+        [context getValues: &si forParameter: NSOpenGLCPSwapInterval];
+        if(si != swapInterval) {
+            DBG_PRINT("MyNSOpenGLLayer::drawInOpenGLContext %p setSwapInterval: %d -> %d\n", self, si, swapInterval);
+            [context setValues: &swapInterval forParameter: NSOpenGLCPSwapInterval];
+        }
+    } */
     GLenum textureTarget = [pbuffer textureTarget];
-    GLsizei pwidth = [pbuffer pixelsWide];
-    GLsizei pheight = [pbuffer pixelsHigh];
-    GLfloat tWidth = textureTarget == GL_TEXTURE_2D ? (GLfloat)width/(GLfloat)pwidth : pwidth;
-    GLfloat tHeight = textureTarget == GL_TEXTURE_2D ? (GLfloat)height/(GLfloat)pheight : pheight;
+    GLfloat tWidth, tHeight;
+    {
+        GLsizei pwidth = [pbuffer pixelsWide];
+        GLsizei pheight = [pbuffer pixelsHigh];
+        tWidth  = textureTarget == GL_TEXTURE_2D ? (GLfloat)pwidth /(GLfloat)texWidth  : pwidth;
+        tHeight = textureTarget == GL_TEXTURE_2D ? (GLfloat)pheight/(GLfloat)texHeight : pheight;
+    }
+    Bool texCreated = 0 == textureID;
    
-    DBG_PRINT("MyNSOpenGLLayer::drawInOpenGLContext %p, ctx %p, pfmt %p %dx%d -> %fx%f 0x%X\n", 
-        self, context, pixelFormat, width, height, tWidth, tHeight, textureTarget);
-
-    if(0 == textureID) {
+    if(texCreated) {
         glGenTextures(1, &textureID);
+        DBG_PRINT("MyNSOpenGLLayer::drawInOpenGLContext %p, ctx %p, pfmt %p tex %dx%d -> %fx%f 0x%X: creating texID 0x%X\n", 
+            self, context, pixelFormat, texWidth, texHeight, tWidth, tHeight, textureTarget, textureID);
     }
+
     glBindTexture(textureTarget, textureID);
 
+    /**
+    if(texCreated) {
+      // proper tex size setup
+      glTexImage2D(textureTarget, 0, GL_RGB, texWidth, texHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
+    } */
+
     [context setTextureImageToPixelBuffer: pbuffer colorBuffer: GL_FRONT];
 
     glTexParameteri(textureTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
@@ -117,30 +266,126 @@
     glDisable(textureTarget);
    
     [super drawInOpenGLContext: context pixelFormat: pixelFormat forLayerTime: timeInterval displayTime: timeStamp];
+    shallDraw = NO;
+    if(0 >= swapInterval) {
+        pthread_cond_signal(&renderSignal);
+        SYNC_PRINT("*");
+    }
+    SYNC_PRINT("1>");
+    pthread_mutex_unlock(&renderLock);
+}
+
+- (int)getSwapInterval
+{
+    return swapInterval;
+}
+
+- (void)setSwapInterval:(int)interval
+{
+    DBG_PRINT("MyNSOpenGLLayer::setSwapInterval: %d\n", interval);
+    swapInterval = interval;
+    if(0 < swapInterval) {
+        tc = 0;
+        timespec_now(&t0);
+
+        [self setAsynchronous: NO];
+        #ifdef HAS_CADisplayLink
+            [displayLink setPaused: NO];
+            [displayLink setFrameInterval: interval];
+        #else
+            if(NULL!=displayLink) {
+                CVDisplayLinkStart(displayLink);
+                // FIXME: doesn't support interval ..
+            }
+        #endif
+    } else {
+        #ifdef HAS_CADisplayLink
+            [displayLink setPaused: YES];
+        #else
+            if(NULL!=displayLink) {
+                CVDisplayLinkStop(displayLink);
+            }
+        #endif
+        [self setAsynchronous: YES];
+    }
+}
+
+-(void)tick
+{
+    tc++;
+    if(tc%60==0) {
+        struct timespec t1, td;
+        timespec_now(&t1);
+        timespec_subtract(&td, &t1, &t0);
+        long td_ms = timespec_milliseconds(&td);
+        fprintf(stderr, "NSOpenGLLayer: %ld ms / %d frames, %ld ms / frame, %f fps\n",
+            td_ms, tc, td_ms/tc, (tc * 1000.0) / (float)td_ms );
+        fflush(NULL);
+    }
 }
 
 @end
 
-NSOpenGLLayer* createNSOpenGLLayer(NSOpenGLContext* ctx, NSOpenGLPixelFormat* fmt, NSOpenGLPixelBuffer* p, Bool opaque, int width, int height) {
-  return [[MyNSOpenGLLayer alloc] initWithContext:ctx pixelFormat: fmt pbuffer: p opaque: opaque width: width height: height];
+NSOpenGLLayer* createNSOpenGLLayer(NSOpenGLContext* ctx, NSOpenGLPixelFormat* fmt, NSOpenGLPixelBuffer* p, Bool opaque, int texWidth, int texHeight) {
+  return [[MyNSOpenGLLayer alloc] initWithContext:ctx pixelFormat: fmt pbuffer: p opaque: opaque texWidth: texWidth texHeight: texHeight];
+}
+
+void setNSOpenGLLayerSwapInterval(NSOpenGLLayer* layer, int interval) {
+  MyNSOpenGLLayer* l = (MyNSOpenGLLayer*) layer;
+  pthread_mutex_lock(&l->renderLock);
+  [l setSwapInterval: interval];
+  pthread_mutex_unlock(&l->renderLock);
+}
+
+void waitUntilNSOpenGLLayerIsReady(NSOpenGLLayer* layer, long to_ms) {
+    MyNSOpenGLLayer* l = (MyNSOpenGLLayer*) layer;
+    BOOL ready = NO;
+    int wr = 0;
+    pthread_mutex_lock(&l->renderLock);
+    SYNC_PRINT("{");
+    do {
+        if([l getSwapInterval] <= 0) {
+            ready = !l->shallDraw;
+        }
+        if(NO == ready) {
+            if(0 < to_ms) {
+                struct timespec to_abs;
+                timespec_now(&to_abs);
+                timespec_addms(&to_abs, to_ms);
+                wr = pthread_cond_timedwait(&l->renderSignal, &l->renderLock, &to_abs);
+            } else {
+                pthread_cond_wait (&l->renderSignal, &l->renderLock);
+            }
+            ready = !l->shallDraw;
+        }
+    } while (NO == ready && 0 == wr) ;
+    SYNC_PRINT("%d}", ready);
+    pthread_mutex_unlock(&l->renderLock);
 }
 
 void setNSOpenGLLayerNeedsDisplay(NSOpenGLLayer* layer) {
   MyNSOpenGLLayer* l = (MyNSOpenGLLayer*) layer;
-  NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
-  l->shallDraw = YES;
-  if ( [NSThread isMainThread] == YES ) {
-      [l setNeedsDisplay];
-  } else {
-      // [l performSelectorOnMainThread:@selector(setNeedsDisplay) withObject:nil waitUntilDone:YES];
-      [l performSelectorOnMainThread:@selector(setNeedsDisplay) withObject:nil waitUntilDone:NO];
+  @synchronized(l) {
+      NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+      pthread_mutex_lock(&l->renderLock);
+      SYNC_PRINT("[");
+      l->shallDraw = YES;
+      if([l getSwapInterval] > 0) {
+          // only trigger update if async mode is off (swapInterval>0)
+          if ( [NSThread isMainThread] == YES ) {
+              [l setNeedsDisplay];
+          } else {
+              // can't wait, otherwise we may deadlock AWT
+              [l performSelectorOnMainThread:@selector(setNeedsDisplay) withObject:nil waitUntilDone:NO];
+          }
+          SYNC_PRINT("1]");
+      } else {
+          SYNC_PRINT("0]");
+      }
+      pthread_mutex_unlock(&l->renderLock);
+      // DBG_PRINT("MyNSOpenGLLayer::setNSOpenGLLayerNeedsDisplay %p\n", l);
+      [pool release];
   }
-  // NSView* view = [l view];
-  // [view setNeedsDisplay: YES]; // FIXME: JAU
-  // [view performSelectorOnMainThread:@selector(setNeedsDisplay:) withObject:YES waitUntilDone:YES];
-  // [view performSelectorOnMainThread:@selector(display) withObject:nil waitUntilDone:YES];
-  DBG_PRINT("MyNSOpenGLLayer::setNSOpenGLLayerNeedsDisplay %p\n", l);
-  [pool release];
 }
 
 void releaseNSOpenGLLayer(NSOpenGLLayer* l) {
-- 
cgit v1.2.3