aboutsummaryrefslogtreecommitdiffstats
path: root/src/jogl/native/macosx/MacOSXWindowSystemInterface-calayer.m
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2014-02-23 14:51:06 +0100
committerSven Gothel <[email protected]>2014-02-23 14:51:06 +0100
commit3352601e0860584509adf2b76f993d03893ded4b (patch)
tree974fccc8c0eb2f5ad9d4ffd741dfc35869ed67b5 /src/jogl/native/macosx/MacOSXWindowSystemInterface-calayer.m
parentf51933f0ebe9ae030c26c066e59a728ce08b8559 (diff)
parentc67de337a8aaf52e36104c3f13e273aa19d21f1f (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/native/macosx/MacOSXWindowSystemInterface-calayer.m')
-rw-r--r--src/jogl/native/macosx/MacOSXWindowSystemInterface-calayer.m966
1 files changed, 966 insertions, 0 deletions
diff --git a/src/jogl/native/macosx/MacOSXWindowSystemInterface-calayer.m b/src/jogl/native/macosx/MacOSXWindowSystemInterface-calayer.m
new file mode 100644
index 000000000..7ce8c58cf
--- /dev/null
+++ b/src/jogl/native/macosx/MacOSXWindowSystemInterface-calayer.m
@@ -0,0 +1,966 @@
+#import "MacOSXWindowSystemInterface.h"
+#import <QuartzCore/QuartzCore.h>
+#import <pthread.h>
+#import "NativeWindowProtocols.h"
+#include "timespec.h"
+
+#import <OpenGL/glext.h>
+
+/**
+ * Partial include of gl3.h - which we can only expect and use
+ * in case of a GL3 core context at runtime.
+ * Otherwise we would need to have 2 modules, one including GL2
+ * and one inclusing GL3 headers.
+ */
+#ifndef GL_ARB_vertex_array_object
+#define GL_VERTEX_ARRAY_BINDING 0x85B5
+extern void glBindVertexArray (GLuint array);
+extern void glDeleteVertexArrays (GLsizei n, const GLuint *arrays);
+extern void glGenVertexArrays (GLsizei n, GLuint *arrays);
+extern GLboolean glIsVertexArray (GLuint array);
+#endif
+
+//
+// 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
+
+// #define DBG_LIFECYCLE 1
+
+/**
+ * Capture setView(NULL), which produces a 'invalid drawable' message
+ *
+ * Also track lifecycle via DBG_PRINT messages, if VERBOSE is enabled!
+ */
+@interface MyNSOpenGLContext: NSOpenGLContext
+{
+}
+- (id)initWithFormat:(NSOpenGLPixelFormat *)format shareContext:(NSOpenGLContext *)share;
+- (void)setView:(NSView *)view;
+- (void)update;
+#ifdef DBG_LIFECYCLE
+- (id)retain;
+- (oneway void)release;
+#endif
+- (void)dealloc;
+
+@end
+
+@implementation MyNSOpenGLContext
+
+- (id)initWithFormat:(NSOpenGLPixelFormat *)format shareContext:(NSOpenGLContext *)share
+{
+ DBG_PRINT("MyNSOpenGLContext::initWithFormat.0: format %p, share %p\n", format, share);
+ MyNSOpenGLContext * o = [super initWithFormat:format shareContext:share];
+ DBG_PRINT("MyNSOpenGLContext::initWithFormat.X: new %p\n", o);
+ return o;
+}
+
+- (void)setView:(NSView *)view
+{
+ DBG_PRINT("MyNSOpenGLContext::setView: this.0 %p, view %p\n", self, view);
+ // NSLog(@"MyNSOpenGLContext::setView: %@",[NSThread callStackSymbols]);
+ if(NULL != view) {
+ [super setView:view];
+ } else {
+ [self clearDrawable];
+ }
+ DBG_PRINT("MyNSOpenGLContext::setView.X\n");
+}
+
+- (void)update
+{
+ DBG_PRINT("MyNSOpenGLContext::update: this.0 %p, view %p\n", self, [self view]);
+ [super update];
+ DBG_PRINT("MyNSOpenGLContext::update.X\n");
+}
+
+#ifdef DBG_LIFECYCLE
+
+- (id)retain
+{
+ DBG_PRINT("MyNSOpenGLContext::retain.0: %p (refcnt %d)\n", self, (int)[self retainCount]);
+ // NSLog(@"MyNSOpenGLContext::retain: %@",[NSThread callStackSymbols]);
+ id o = [super retain];
+ DBG_PRINT("MyNSOpenGLContext::retain.X: %p (refcnt %d)\n", o, (int)[o retainCount]);
+ return o;
+}
+
+- (oneway void)release
+{
+ DBG_PRINT("MyNSOpenGLContext::release.0: %p (refcnt %d)\n", self, (int)[self retainCount]);
+ [super release];
+ // DBG_PRINT("MyNSOpenGLContext::release.X: %p (refcnt %d)\n", self, (int)[self retainCount]);
+}
+
+#endif
+
+#ifdef VERBOSE_ON
+ #define CGLRETAINCOUNT(c) (NULL!=c?(int)CGLGetContextRetainCount(c):-1)
+#else
+ #define CGLRETAINCOUNT(c)
+#endif
+
+- (void)dealloc
+{
+ /**
+ * The explicit CGLContext destruction below
+ * ensures that it is not left behind.
+ * Tests show that w/o these gymnastics, the CGLContext is not freed immediately after calling dealloc.
+ * The retain, release and dealloc ensures [super dealloc] won't message 'invalid context'.
+ */
+ CGLContextObj cglCtx = [self CGLContextObj];
+
+ DBG_PRINT("MyNSOpenGLContext::dealloc.0 %p (refcnt %d) - CGL-Ctx %p\n", self, (int)[self retainCount], cglCtx, CGLRETAINCOUNT(cglCtx));
+ // NSLog(@"MyNSOpenGLContext::dealloc: %@",[NSThread callStackSymbols]);
+
+ [self clearDrawable];
+ DBG_PRINT("MyNSOpenGLContext::dealloc.1 %d\n", CGLRETAINCOUNT(cglCtx));
+ if( NULL != cglCtx ) {
+ CGLRetainContext( cglCtx );
+ DBG_PRINT("MyNSOpenGLContext::dealloc.2 %d\n", CGLRETAINCOUNT(cglCtx));
+ }
+ [super dealloc];
+ DBG_PRINT("MyNSOpenGLContext::dealloc.3 %d\n", CGLRETAINCOUNT(cglCtx));
+ if( NULL != cglCtx ) {
+ CGLReleaseContext( cglCtx );
+ DBG_PRINT("MyNSOpenGLContext::dealloc.4 %d\n", CGLRETAINCOUNT(cglCtx));
+ CGLDestroyContext( cglCtx );
+ DBG_PRINT("MyNSOpenGLContext::dealloc.5 %d\n", CGLRETAINCOUNT(cglCtx));
+ }
+ DBG_PRINT("MyNSOpenGLContext::dealloc.X\n");
+}
+
+@end
+
+@interface MyNSOpenGLLayer: NSOpenGLLayer <NWDedicatedFrame>
+{
+@private
+ GLfloat gl_texCoords[8];
+ NSOpenGLContext* glContext;
+ Bool isGLEnabled;
+
+@protected
+ GLuint gl3ShaderProgramName;
+ GLuint vboBufVert;
+ GLuint vboBufTexCoord;
+ GLint vertAttrLoc;
+ GLint texCoordAttrLoc;
+ NSOpenGLPixelFormat* parentPixelFmt;
+ int texWidth;
+ int texHeight;
+ volatile Bool dedicatedFrameSet;
+ volatile CGRect dedicatedFrame;
+ volatile NSOpenGLPixelBuffer* pbuffer;
+ volatile GLuint textureID;
+ volatile NSOpenGLPixelBuffer* newPBuffer;
+#ifdef HAS_CADisplayLink
+ CADisplayLink* displayLink;
+#else
+ CVDisplayLinkRef displayLink;
+#endif
+ int tc;
+ struct timespec tStart;
+@public
+ struct timespec lastWaitTime;
+ GLint swapInterval;
+ GLint swapIntervalCounter;
+ pthread_mutex_t renderLock;
+ pthread_cond_t renderSignal;
+ volatile Bool shallDraw;
+}
+
+- (id) setupWithContext: (NSOpenGLContext*) parentCtx
+ gl3ShaderProgramName: (GLuint) gl3ShaderProgramName
+ pixelFormat: (NSOpenGLPixelFormat*) pfmt
+ pbuffer: (NSOpenGLPixelBuffer*) p
+ texIDArg: (GLuint) texID
+ opaque: (Bool) opaque
+ texWidth: (int) texWidth
+ texHeight: (int) texHeight;
+
+- (void)releaseLayer;
+- (void)deallocPBuffer;
+- (void)disableAnimation;
+- (void)pauseAnimation:(Bool)pause;
+- (void)setSwapInterval:(int)interval;
+- (void)tick;
+- (void)waitUntilRenderSignal: (long) to_micros;
+- (Bool)isGLSourceValid;
+
+- (void) setGLEnabled: (Bool) enable;
+- (Bool) validateTexSize: (CGRect) lRect;
+- (void) setTextureID: (int) _texID;
+
+- (Bool) isSamePBuffer: (NSOpenGLPixelBuffer*) p;
+- (void) setNewPBuffer: (NSOpenGLPixelBuffer*)p;
+- (void) applyNewPBuffer;
+
+- (void)setDedicatedFrame:(CGRect)frame quirks:(int)quirks; // @NWDedicatedFrame
+- (void) setFrame:(CGRect) frame;
+- (id<CAAction>)actionForKey:(NSString *)key ;
+- (NSOpenGLPixelFormat *)openGLPixelFormatForDisplayMask:(uint32_t)mask;
+- (NSOpenGLContext *)openGLContextForPixelFormat:(NSOpenGLPixelFormat *)pixelFormat;
+- (BOOL)canDrawInOpenGLContext:(NSOpenGLContext *)context pixelFormat:(NSOpenGLPixelFormat *)pixelFormat
+ forLayerTime:(CFTimeInterval)timeInterval displayTime:(const CVTimeStamp *)timeStamp;
+- (void)drawInOpenGLContext:(NSOpenGLContext *)context pixelFormat:(NSOpenGLPixelFormat *)pixelFormat
+ forLayerTime:(CFTimeInterval)timeInterval displayTime:(const CVTimeStamp *)timeStamp;
+
+#ifdef DBG_LIFECYCLE
+- (id)retain;
+- (oneway void)release;
+#endif
+- (void)dealloc;
+
+@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);
+ if( 0 < l->swapInterval ) {
+ l->swapIntervalCounter++;
+ if( l->swapIntervalCounter >= l->swapInterval ) {
+ SYNC_PRINT("<S %d/%d>", (int)l->swapIntervalCounter, l->swapInterval);
+ l->swapIntervalCounter = 0;
+ pthread_cond_signal(&l->renderSignal); // wake up vsync
+ }
+ }
+ pthread_mutex_unlock(&l->renderLock);
+ [pool release];
+ return kCVReturnSuccess;
+}
+
+#endif
+
+static const GLfloat gl_verts[] = {
+ -1.0, -1.0,
+ -1.0, 1.0,
+ 1.0, 1.0,
+ 1.0, -1.0
+};
+
+@implementation MyNSOpenGLLayer
+
+- (id) setupWithContext: (NSOpenGLContext*) parentCtx
+ gl3ShaderProgramName: (GLuint) _gl3ShaderProgramName
+ pixelFormat: (NSOpenGLPixelFormat*) _parentPixelFmt
+ pbuffer: (NSOpenGLPixelBuffer*) p
+ texIDArg: (GLuint) texID
+ opaque: (Bool) opaque
+ texWidth: (int) _texWidth
+ texHeight: (int) _texHeight;
+{
+ pthread_mutexattr_t renderLockAttr;
+ pthread_mutexattr_init(&renderLockAttr);
+ pthread_mutexattr_settype(&renderLockAttr, PTHREAD_MUTEX_RECURSIVE);
+ pthread_mutex_init(&renderLock, &renderLockAttr); // recursive
+ pthread_cond_init(&renderSignal, NULL); // no attribute
+
+ {
+ int i;
+ for(i=0; i<8; i++) {
+ gl_texCoords[i] = 0.0f;
+ }
+ }
+ parentPixelFmt = [_parentPixelFmt retain]; // until destruction
+ glContext = [[MyNSOpenGLContext alloc] initWithFormat:parentPixelFmt shareContext:parentCtx];
+ gl3ShaderProgramName = _gl3ShaderProgramName;
+ vboBufVert = 0;
+ vboBufTexCoord = 0;
+ vertAttrLoc = 0;
+ texCoordAttrLoc = 0;
+ swapInterval = 1; // defaults to on (as w/ new GL profiles)
+ swapIntervalCounter = 0;
+ timespec_now(&lastWaitTime);
+ shallDraw = NO;
+ isGLEnabled = YES;
+ dedicatedFrameSet = NO;
+ dedicatedFrame = CGRectMake(0, 0, _texWidth, _texHeight);
+ [self validateTexSize: dedicatedFrame];
+ [self setTextureID: texID];
+
+ newPBuffer = NULL;
+ pbuffer = p;
+ if(NULL != pbuffer) {
+ [pbuffer retain];
+ }
+
+ {
+ // no animations for add/remove/swap sublayers etc
+ // doesn't work: [self removeAnimationForKey: kCAOnOrderIn, kCAOnOrderOut, kCATransition]
+ [self removeAllAnimations];
+ }
+
+ // instantiate a deactivated displayLink
+#ifdef HAS_CADisplayLink
+ displayLink = [[CVDisplayLink displayLinkWithTarget:self selector:@selector(setNeedsDisplay)] retain];
+#else
+ CVReturn cvres;
+ {
+ int allDisplaysMask = 0;
+ int virtualScreen, accelerated, displayMask;
+ for (virtualScreen = 0; virtualScreen < [parentPixelFmt numberOfVirtualScreens]; virtualScreen++) {
+ [parentPixelFmt getValues:&displayMask forAttribute:NSOpenGLPFAScreenMask forVirtualScreen:virtualScreen];
+ [parentPixelFmt 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) {
+ 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) {
+ DBG_PRINT("MyNSOpenGLLayer::init %p, CVDisplayLinkSetOutputCallback failed: %d\n", self, cvres);
+ displayLink = NULL;
+ }
+ }
+#endif
+ [self pauseAnimation: YES];
+
+ [self removeAllAnimations];
+ [self setAutoresizingMask: (kCALayerWidthSizable|kCALayerHeightSizable)];
+ [self setNeedsDisplayOnBoundsChange: YES];
+
+ [self setOpaque: opaque ? YES : NO];
+
+#ifdef VERBOSE_ON
+ CGRect lRect = [self bounds];
+ if(NULL != pbuffer) {
+ DBG_PRINT("MyNSOpenGLLayer::init (pbuffer) %p, pctx %p, pfmt %p, pbuffer %p, ctx %p, opaque %d, pbuffer %dx%d -> tex %dx%d, bounds: %lf/%lf %lfx%lf, displayLink %p (refcnt %d)\n",
+ self, parentCtx, parentPixelFmt, pbuffer, glContext, opaque, [pbuffer pixelsWide], [pbuffer pixelsHigh], texWidth, texHeight,
+ lRect.origin.x, lRect.origin.y, lRect.size.width, lRect.size.height, displayLink, (int)[self retainCount]);
+ } else {
+ DBG_PRINT("MyNSOpenGLLayer::init (texture) %p, pctx %p, pfmt %p, ctx %p, opaque %d, tex[id %d, %dx%d], bounds: %lf/%lf %lfx%lf, displayLink %p (refcnt %d)\n",
+ self, parentCtx, parentPixelFmt, glContext, opaque, (int)textureID, texWidth, texHeight,
+ lRect.origin.x, lRect.origin.y, lRect.size.width, lRect.size.height, displayLink, (int)[self retainCount]);
+ }
+#endif
+ return self;
+}
+
+- (void) setGLEnabled: (Bool) enable
+{
+ DBG_PRINT("MyNSOpenGLLayer::setGLEnabled: %p, %d -> %d\n", self, (int)isGLEnabled, (int)enable);
+ isGLEnabled = enable;
+}
+
+- (Bool) validateTexSize: (CGRect) lRect
+{
+ const int lRectW = (int) (lRect.size.width + 0.5f);
+ const int lRectH = (int) (lRect.size.height + 0.5f);
+ Bool changed;
+
+ if( lRectH != texHeight || lRectW != texWidth ) {
+ texWidth = lRectW;
+ texHeight = lRectH;
+ changed = YES;
+
+ GLfloat texCoordWidth, texCoordHeight;
+ if(NULL != pbuffer) {
+ GLenum textureTarget = [pbuffer textureTarget] ;
+ GLsizei pwidth = [pbuffer pixelsWide];
+ GLsizei pheight = [pbuffer pixelsHigh];
+ if( GL_TEXTURE_2D == textureTarget ) {
+ texCoordWidth = (GLfloat)pwidth /(GLfloat)texWidth ;
+ texCoordHeight = (GLfloat)pheight/(GLfloat)texHeight ;
+ } else {
+ texCoordWidth = pwidth;
+ texCoordHeight = pheight;
+ }
+ } else {
+ texCoordWidth = (GLfloat)1.0f;
+ texCoordHeight = (GLfloat)1.0f;
+ }
+ gl_texCoords[3] = texCoordHeight;
+ gl_texCoords[5] = texCoordHeight;
+ gl_texCoords[4] = texCoordWidth;
+ gl_texCoords[6] = texCoordWidth;
+ #ifdef VERBOSE_ON
+ DBG_PRINT("MyNSOpenGLLayer::validateTexSize %p -> tex %dx%d, bounds: %lf/%lf %lfx%lf (%dx%d), dedicatedFrame set:%d %lf/%lf %lfx%lf\n",
+ self, texWidth, texHeight,
+ lRect.origin.x, lRect.origin.y, lRect.size.width, lRect.size.height, lRectW, lRectH,
+ dedicatedFrameSet, dedicatedFrame.origin.x, dedicatedFrame.origin.y, dedicatedFrame.size.width, dedicatedFrame.size.height);
+ #endif
+ } else {
+ changed = NO;
+ }
+ return changed;
+}
+
+- (void) setTextureID: (int) _texID
+{
+ textureID = _texID;
+}
+
+- (Bool) isSamePBuffer: (NSOpenGLPixelBuffer*) p
+{
+ return pbuffer == p || newPBuffer == p;
+}
+
+- (void)setNewPBuffer: (NSOpenGLPixelBuffer*)p
+{
+ SYNC_PRINT("<NP-S %p -> %p>", pbuffer, p);
+ newPBuffer = p;
+ [newPBuffer retain];
+}
+
+- (void) applyNewPBuffer
+{
+ if( NULL != newPBuffer ) { // volatile OK
+ SYNC_PRINT("<NP-A %p -> %p>", pbuffer, newPBuffer);
+
+ if( 0 != textureID ) {
+ glDeleteTextures(1, (GLuint *)&textureID);
+ [self setTextureID: 0];
+ }
+ [pbuffer release];
+
+ pbuffer = newPBuffer;
+ newPBuffer = NULL;
+ }
+}
+
+- (void)deallocPBuffer
+{
+ if(NULL != pbuffer) {
+ NSOpenGLContext* context = [self openGLContext];
+ if(NULL!=context) {
+ [context makeCurrentContext];
+
+ DBG_PRINT("MyNSOpenGLLayer::deallocPBuffer (with ctx) %p (refcnt %d) - context %p, pbuffer %p, texID %d\n", self, (int)[self retainCount], context, pbuffer, (int)textureID);
+
+ if( 0 != textureID ) {
+ glDeleteTextures(1, (GLuint *)&textureID);
+ [self setTextureID: 0];
+ }
+ if(NULL != pbuffer) {
+ [pbuffer release];
+ pbuffer = NULL;
+ }
+ if(NULL != newPBuffer) {
+ [newPBuffer release];
+ newPBuffer = NULL;
+ }
+
+ [context clearDrawable];
+ } else {
+ DBG_PRINT("MyNSOpenGLLayer::deallocPBuffer (w/o ctx) %p (refcnt %d) - context %p, pbuffer %p, texID %d\n", self, (int)[self retainCount], context, pbuffer, (int)textureID);
+ }
+ }
+}
+
+- (void)disableAnimation
+{
+ DBG_PRINT("MyNSOpenGLLayer::disableAnimation.0: %p (refcnt %d) - displayLink %p\n", self, (int)[self retainCount], displayLink);
+ [self setAsynchronous: NO];
+ if(NULL != displayLink) {
+#ifdef HAS_CADisplayLink
+ [displayLink setPaused: YES];
+ [displayLink release];
+#else
+ CVDisplayLinkStop(displayLink);
+ CVDisplayLinkRelease(displayLink);
+#endif
+ displayLink = NULL;
+ }
+ DBG_PRINT("MyNSOpenGLLayer::disableAnimation.X: %p (refcnt %d) - displayLink %p\n", self, (int)[self retainCount], displayLink);
+}
+
+- (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];
+ if( NULL != glContext ) {
+ [glContext release];
+ glContext = NULL;
+ }
+ if( NULL != parentPixelFmt ) {
+ [parentPixelFmt release];
+ parentPixelFmt = NULL;
+ }
+ pthread_mutex_unlock(&renderLock);
+ [self release];
+ DBG_PRINT("MyNSOpenGLLayer::releaseLayer.X: %p\n", self);
+}
+
+#ifdef DBG_LIFECYCLE
+
+- (id)retain
+{
+ DBG_PRINT("MyNSOpenGLLayer::retain.0: %p (refcnt %d)\n", self, (int)[self retainCount]);
+ // NSLog(@"MyNSOpenGLLayer::retain: %@",[NSThread callStackSymbols]);
+ id o = [super retain];
+ DBG_PRINT("MyNSOpenGLLayer::retain.X: %p (refcnt %d)\n", o, (int)[o retainCount]);
+ return o;
+}
+
+- (oneway void)release
+{
+ DBG_PRINT("MyNSOpenGLLayer::release.0: %p (refcnt %d)\n", self, (int)[self retainCount]);
+ // NSLog(@"MyNSOpenGLLayer::release: %@",[NSThread callStackSymbols]);
+ [super release];
+ // DBG_PRINT("MyNSOpenGLLayer::release.X: %p (refcnt %d)\n", self, (int)[self retainCount]);
+}
+
+#endif
+
+- (void)dealloc
+{
+ DBG_PRINT("MyNSOpenGLLayer::dealloc.0 %p (refcnt %d)\n", self, (int)[self retainCount]);
+ // NSLog(@"MyNSOpenGLLayer::dealloc: %@",[NSThread callStackSymbols]);
+ [self disableAnimation];
+ pthread_mutex_lock(&renderLock);
+ [self deallocPBuffer];
+ pthread_mutex_unlock(&renderLock);
+ pthread_cond_destroy(&renderSignal);
+ pthread_mutex_destroy(&renderLock);
+ [super dealloc];
+ // DBG_PRINT("MyNSOpenGLLayer::dealloc.X %p\n", self);
+}
+
+- (Bool)isGLSourceValid
+{
+ return NULL != pbuffer || NULL != newPBuffer || 0 != textureID ;
+}
+
+// @NWDedicatedFrame
+- (void)setDedicatedFrame:(CGRect)dFrame quirks:(int)quirks {
+ CGRect lRect = [self frame];
+ Bool dedicatedFramePosSet = 0 != ( NW_DEDICATEDFRAME_QUIRK_POSITION & quirks );
+ Bool dedicatedFrameSizeSet = 0 != ( NW_DEDICATEDFRAME_QUIRK_SIZE & quirks );
+ Bool dedicatedLayoutSet = 0 != ( NW_DEDICATEDFRAME_QUIRK_LAYOUT & quirks );
+ dedicatedFrameSet = dedicatedFramePosSet || dedicatedFrameSizeSet || dedicatedLayoutSet;
+ dedicatedFrame = dFrame;
+
+ DBG_PRINT("MyNSOpenGLLayer::setDedicatedFrame: Quirks [%d, pos %d, size %d, lout %d], %p, texSize %dx%d, %lf/%lf %lfx%lf -> %lf/%lf %lfx%lf\n",
+ quirks, dedicatedFramePosSet, dedicatedFrameSizeSet, dedicatedLayoutSet, self, texWidth, texHeight,
+ lRect.origin.x, lRect.origin.y, lRect.size.width, lRect.size.height,
+ dFrame.origin.x, dFrame.origin.y, dFrame.size.width, dFrame.size.height);
+ (void)lRect; // silence
+
+ if( dedicatedFrameSet ) {
+ [super setFrame: dedicatedFrame];
+ }
+}
+
+- (void) setFrame:(CGRect) frame {
+ if( dedicatedFrameSet ) {
+ [super setFrame: dedicatedFrame];
+ } else {
+ [super setFrame: frame];
+ }
+}
+
+- (id<CAAction>)actionForKey:(NSString *)key
+{
+ DBG_PRINT("MyNSOpenGLLayer::actionForKey.0 %p key %s -> NIL\n", self, [key UTF8String]);
+ return nil;
+ // return [super actionForKey: key];
+}
+
+- (NSOpenGLPixelFormat *)openGLPixelFormatForDisplayMask:(uint32_t)mask
+{
+ DBG_PRINT("MyNSOpenGLLayer::openGLPixelFormatForDisplayMask: %p (refcnt %d) - parent-pfmt %p -> new-pfmt %p\n",
+ self, (int)[self retainCount], parentPixelFmt, parentPixelFmt);
+ // We simply take over ownership of parent PixelFormat until releaseLayer..
+ return parentPixelFmt;
+}
+
+- (NSOpenGLContext *)openGLContextForPixelFormat:(NSOpenGLPixelFormat *)pixelFormat
+{
+ DBG_PRINT("MyNSOpenGLLayer::openGLContextForPixelFormat.0: %p (refcnt %d) - pfmt %p, ctx %p, DisplayLink %p\n",
+ self, (int)[self retainCount], pixelFormat, glContext, displayLink);
+ return glContext;
+}
+
+- (BOOL)canDrawInOpenGLContext:(NSOpenGLContext *)context pixelFormat:(NSOpenGLPixelFormat *)pixelFormat
+ forLayerTime:(CFTimeInterval)timeInterval displayTime:(const CVTimeStamp *)timeStamp
+{
+ SYNC_PRINT("<? %d, %d>", (int)shallDraw, (int)isGLEnabled);
+ return shallDraw && isGLEnabled;
+}
+
+- (void)drawInOpenGLContext:(NSOpenGLContext *)context pixelFormat:(NSOpenGLPixelFormat *)pixelFormat
+ forLayerTime:(CFTimeInterval)timeInterval displayTime:(const CVTimeStamp *)timeStamp
+{
+ pthread_mutex_unlock(&renderLock);
+ SYNC_PRINT("<* ");
+ // NSLog(@"MyNSOpenGLLayer::DRAW: %@",[NSThread callStackSymbols]);
+
+ if( isGLEnabled && shallDraw && ( NULL != pbuffer || NULL != newPBuffer || 0 != textureID ) ) {
+ [context makeCurrentContext];
+
+ if( NULL != newPBuffer ) { // volatile OK
+ [self applyNewPBuffer];
+ }
+
+ GLenum textureTarget;
+
+ Bool texSizeChanged = [self validateTexSize: ( dedicatedFrameSet ? dedicatedFrame : [self bounds] ) ];
+ if( texSizeChanged ) {
+ [context update];
+ }
+
+ if( NULL != pbuffer ) {
+ if( texSizeChanged && 0 != textureID ) {
+ glDeleteTextures(1, (GLuint *)&textureID);
+ [self setTextureID: 0];
+ }
+ textureTarget = [pbuffer textureTarget];
+ if( 0 != gl3ShaderProgramName ) {
+ glUseProgram(gl3ShaderProgramName);
+ glActiveTexture(GL_TEXTURE0);
+ }
+ if( 0 == textureID ) {
+ glGenTextures(1, (GLuint *)&textureID);
+ glBindTexture(textureTarget, textureID);
+ glTexParameteri(textureTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(textureTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(textureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(textureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ } else {
+ glBindTexture(textureTarget, textureID);
+ }
+ [context setTextureImageToPixelBuffer: (NSOpenGLPixelBuffer*) pbuffer colorBuffer: GL_FRONT];
+ } else {
+ if( 0 != gl3ShaderProgramName ) {
+ glUseProgram(gl3ShaderProgramName);
+ glActiveTexture(GL_TEXTURE0);
+ }
+ textureTarget = GL_TEXTURE_2D;
+ glBindTexture(textureTarget, textureID);
+ }
+ SYNC_PRINT(" %d gl3Prog %d/%d*>", (int)textureID, (int)gl3ShaderProgramName, (int)glIsProgram (gl3ShaderProgramName));
+
+ if( 0 == vboBufVert ) { // Once: Init Data and Bind to Pointer
+ if( 0 != gl3ShaderProgramName ) {
+ // Install default VAO as required by GL 3.2 core!
+ GLuint vaoBuf = 0;
+ glGenVertexArrays(1, &vaoBuf);
+ glBindVertexArray(vaoBuf);
+
+ // Set texture-unit 0
+ GLint texUnitLoc = glGetUniformLocation (gl3ShaderProgramName, "mgl_Texture0");
+ glUniform1i (texUnitLoc, 0);
+ }
+ glGenBuffers( 1, &vboBufVert );
+ glBindBuffer( GL_ARRAY_BUFFER, vboBufVert );
+ glBufferData( GL_ARRAY_BUFFER, 4 * 2 * sizeof(GLfloat), gl_verts, GL_STATIC_DRAW);
+ if( 0 != gl3ShaderProgramName ) {
+ vertAttrLoc = glGetAttribLocation( gl3ShaderProgramName, "mgl_Vertex" );
+ glVertexAttribPointer( vertAttrLoc, 2, GL_FLOAT, GL_FALSE, 0, NULL );
+ } else {
+ glVertexPointer(2, GL_FLOAT, 0, NULL);
+ }
+
+ glGenBuffers( 1, &vboBufTexCoord );
+ glBindBuffer( GL_ARRAY_BUFFER, vboBufTexCoord );
+ glBufferData( GL_ARRAY_BUFFER, 4 * 2 * sizeof(GLfloat), gl_texCoords, GL_STATIC_DRAW);
+ if( 0 != gl3ShaderProgramName ) {
+ texCoordAttrLoc = glGetAttribLocation( gl3ShaderProgramName, "mgl_MultiTexCoord" );
+ glVertexAttribPointer( texCoordAttrLoc, 2, GL_FLOAT, GL_FALSE, 0, NULL );
+ } else {
+ glTexCoordPointer(2, GL_FLOAT, 0, NULL);
+ }
+ }
+ if( texSizeChanged ) {
+ glBindBuffer( GL_ARRAY_BUFFER, vboBufTexCoord );
+ glBufferSubData( GL_ARRAY_BUFFER, 0, 4 * 2 * sizeof(GLfloat), gl_texCoords);
+ if( 0 != gl3ShaderProgramName ) {
+ glVertexAttribPointer( texCoordAttrLoc, 2, GL_FLOAT, GL_FALSE, 0, NULL );
+ } else {
+ glTexCoordPointer(2, GL_FLOAT, 0, NULL);
+ }
+ }
+ if( 0 != gl3ShaderProgramName ) {
+ glEnableVertexAttribArray( vertAttrLoc );
+ glEnableVertexAttribArray( texCoordAttrLoc );
+ } else {
+ glEnable(textureTarget);
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ }
+
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+
+ if( 0 != gl3ShaderProgramName ) {
+ glDisableVertexAttribArray( vertAttrLoc );
+ glDisableVertexAttribArray( texCoordAttrLoc );
+ glUseProgram(0);
+ } else {
+ glDisableClientState(GL_VERTEX_ARRAY);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+
+ glDisable(textureTarget);
+ }
+
+ glBindTexture(textureTarget, 0);
+
+ [context clearDrawable];
+
+ [super drawInOpenGLContext: context pixelFormat: pixelFormat forLayerTime: timeInterval displayTime: timeStamp];
+
+ } else {
+ // glClear(GL_COLOR_BUFFER_BIT);
+ // glBlitFramebuffer(0, 0, texWidth, texHeight,
+ // 0, 0, texWidth, texHeight,
+ // GL_COLOR_BUFFER_BIT, GL_NEAREST);
+ SYNC_PRINT(" 0*>");
+ }
+
+ #ifdef DBG_PERF
+ [self tick];
+ #endif
+ shallDraw = NO;
+
+ if( 0 >= swapInterval ) {
+ pthread_cond_signal(&renderSignal); // wake up !vsync
+ SYNC_PRINT("<s>");
+ }
+ SYNC_PRINT("<$>\n");
+ pthread_mutex_unlock(&renderLock);
+}
+
+- (void)pauseAnimation:(Bool)pause
+{
+ DBG_PRINT("MyNSOpenGLLayer::pauseAnimation: %d\n", (int)pause);
+ [self setAsynchronous: NO];
+ if(pause) {
+ if(NULL != displayLink) {
+ #ifdef HAS_CADisplayLink
+ [displayLink setPaused: YES];
+ #else
+ CVDisplayLinkStop(displayLink);
+ #endif
+ }
+ } else {
+ if(NULL != displayLink) {
+ #ifdef HAS_CADisplayLink
+ [displayLink setPaused: NO];
+ [displayLink setFrameInterval: swapInterval];
+ #else
+ CVDisplayLinkStart(displayLink);
+ #endif
+ }
+ }
+ tc = 0;
+ timespec_now(&tStart);
+}
+
+- (void)setSwapInterval:(int)interval
+{
+ /**
+ * v-sync doesn't works w/ NSOpenGLLayer's context .. well :(
+ * Using CVDisplayLink .. see setSwapInterval() below.
+ *
+ 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];
+ }
+ */
+
+ pthread_mutex_lock(&renderLock);
+ DBG_PRINT("MyNSOpenGLLayer::setSwapInterval.0: %d - displayLink %p\n", interval, displayLink);
+ swapInterval = interval;
+ swapIntervalCounter = 0;
+ pthread_mutex_unlock(&renderLock);
+
+ if(0 < swapInterval) {
+ [self pauseAnimation: NO];
+ } else {
+ [self pauseAnimation: YES];
+ }
+ DBG_PRINT("MyNSOpenGLLayer::setSwapInterval.X: %d\n", interval);
+}
+
+-(void)tick
+{
+ tc++;
+ if(tc%60==0) {
+ struct timespec t1, td;
+ timespec_now(&t1);
+ timespec_subtract(&td, &t1, &tStart);
+ 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);
+ }
+}
+
+- (void)waitUntilRenderSignal: (long) to_micros
+{
+ struct timespec t0, to_until;
+ BOOL tooLate;
+ int wr;
+ if( 0 >= to_micros ) {
+ to_micros = 16666 + 1000; // defaults to 1/60s + 1ms
+ NSLog(@"MyNSOpenGLContext::waitUntilRenderSignal: to_micros was zero, using defaults");
+ }
+ pthread_mutex_lock(&renderLock);
+ timespec_now(&t0);
+ to_until = lastWaitTime;
+ timespec_addmicros(&to_until, to_micros);
+ tooLate = timespec_compare(&to_until, &t0) < 0;
+ #ifdef DBG_SYNC
+ struct timespec td_until;
+ timespec_subtract(&td_until, &to_until, &t0);
+ SYNC_PRINT("{W %ld ms, to %ld ms, late %d", to_micros/1000, timespec_milliseconds(&td_until), tooLate);
+ #endif
+ if( 0 < swapInterval ) {
+ if( tooLate ) {
+ // adjust!
+ to_until = t0;
+ timespec_addmicros(&to_until, to_micros);
+ }
+ wr = pthread_cond_timedwait(&renderSignal, &renderLock, &to_until);
+ #ifdef DBG_SYNC
+ struct timespec t1, td, td2;
+ 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
+ }
+ SYNC_PRINT("-%d-%d}\n", shallDraw, wr);
+ timespec_now(&lastWaitTime);
+ pthread_mutex_unlock(&renderLock);
+}
+
+@end
+
+NSOpenGLLayer* createNSOpenGLLayer(NSOpenGLContext* ctx, int gl3ShaderProgramName, NSOpenGLPixelFormat* fmt, NSOpenGLPixelBuffer* p, uint32_t texID, Bool opaque, int texWidth, int texHeight) {
+ return [[[MyNSOpenGLLayer alloc] init] setupWithContext:ctx gl3ShaderProgramName: (GLuint)gl3ShaderProgramName pixelFormat: fmt pbuffer: p texIDArg: (GLuint)texID
+ opaque: opaque texWidth: texWidth texHeight: texHeight];
+}
+
+void setNSOpenGLLayerEnabled(NSOpenGLLayer* layer, Bool enable) {
+ NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+ MyNSOpenGLLayer* l = (MyNSOpenGLLayer*) layer;
+ [l setGLEnabled: enable];
+ [pool release];
+}
+
+void setNSOpenGLLayerSwapInterval(NSOpenGLLayer* layer, int interval) {
+ NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+ MyNSOpenGLLayer* l = (MyNSOpenGLLayer*) layer;
+ [l setSwapInterval: interval];
+ [pool release];
+}
+
+void waitUntilNSOpenGLLayerIsReady(NSOpenGLLayer* layer, long to_micros) {
+ NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+ MyNSOpenGLLayer* l = (MyNSOpenGLLayer*) layer;
+ [l waitUntilRenderSignal: to_micros];
+ [pool release];
+}
+
+void setNSOpenGLLayerNeedsDisplayFBO(NSOpenGLLayer* layer, uint32_t texID) {
+ NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+ MyNSOpenGLLayer* l = (MyNSOpenGLLayer*) layer;
+ Bool shallDraw;
+
+ // volatile OK
+ [l setTextureID: texID];
+ shallDraw = [l isGLSourceValid];
+ l->shallDraw = shallDraw;
+
+ SYNC_PRINT("<! T %d>", (int)shallDraw);
+ if(shallDraw) {
+ if ( [NSThread isMainThread] == YES ) {
+ [l setNeedsDisplay];
+ } else {
+ // don't wait - using doublebuffering
+ [l performSelectorOnMainThread:@selector(setNeedsDisplay) withObject:nil waitUntilDone:NO];
+ }
+ }
+ // DBG_PRINT("MyNSOpenGLLayer::setNSOpenGLLayerNeedsDisplay %p\n", l);
+ [pool release];
+}
+
+void setNSOpenGLLayerNeedsDisplayPBuffer(NSOpenGLLayer* layer, NSOpenGLPixelBuffer* p) {
+ NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+ MyNSOpenGLLayer* l = (MyNSOpenGLLayer*) layer;
+ Bool shallDraw;
+
+ if( NO == [l isSamePBuffer: p] ) {
+ [l setNewPBuffer: p];
+ }
+
+ // volatile OK
+ shallDraw = [l isGLSourceValid];
+ l->shallDraw = shallDraw;
+
+ SYNC_PRINT("<! T %d>", (int)shallDraw);
+ if(shallDraw) {
+ if ( [NSThread isMainThread] == YES ) {
+ [l setNeedsDisplay];
+ } else {
+ // don't wait - using doublebuffering
+ [l performSelectorOnMainThread:@selector(setNeedsDisplay) withObject:nil waitUntilDone:NO];
+ }
+ }
+ // DBG_PRINT("MyNSOpenGLLayer::setNSOpenGLLayerNeedsDisplay %p\n", l);
+ [pool release];
+}
+
+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];
+}
+