summaryrefslogtreecommitdiffstats
path: root/src/jogl/native/macosx/MacOSXWindowSystemInterface-pbuffer.m
diff options
context:
space:
mode:
Diffstat (limited to 'src/jogl/native/macosx/MacOSXWindowSystemInterface-pbuffer.m')
-rw-r--r--src/jogl/native/macosx/MacOSXWindowSystemInterface-pbuffer.m480
1 files changed, 480 insertions, 0 deletions
diff --git a/src/jogl/native/macosx/MacOSXWindowSystemInterface-pbuffer.m b/src/jogl/native/macosx/MacOSXWindowSystemInterface-pbuffer.m
new file mode 100644
index 000000000..fe896cc53
--- /dev/null
+++ b/src/jogl/native/macosx/MacOSXWindowSystemInterface-pbuffer.m
@@ -0,0 +1,480 @@
+#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 texWidth;
+ int texHeight;
+ GLuint textureID;
+ GLint swapInterval;
+#ifdef HAS_CADisplayLink
+ CADisplayLink* displayLink;
+#else
+ CVDisplayLinkRef displayLink;
+#endif
+ int tc;
+ struct timespec t0;
+@public
+ pthread_mutex_t renderLock;
+ pthread_cond_t renderSignal;
+ BOOL shallDraw;
+}
+
+- (id) setupWithContext: (NSOpenGLContext*) ctx
+ pixelFormat: (NSOpenGLPixelFormat*) pfmt
+ pbuffer: (NSOpenGLPixelBuffer*) p
+ opaque: (Bool) opaque
+ texWidth: (int) texWidth
+ texHeight: (int) texHeight;
+
+- (void)deallocTex;
+- (void)disableAnimation;
+- (void)releaseLayer;
+- (void)dealloc;
+- (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) setupWithContext: (NSOpenGLContext*) _ctx
+ pixelFormat: (NSOpenGLPixelFormat*) _fmt
+ pbuffer: (NSOpenGLPixelBuffer*) p
+ 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
+
+ // no animations for add/remove/swap sublayers etc
+ [self removeAnimationForKey: kCAOnOrderIn];
+ [self removeAnimationForKey: kCAOnOrderOut];
+ [self removeAnimationForKey: kCATransition];
+
+ pbuffer = p;
+ [pbuffer retain];
+
+ // 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];
+ texWidth = _texWidth;
+ texHeight = _texHeight;
+ textureID = 0;
+ swapInterval = -1;
+ shallDraw = NO;
+
+ CGRect lRect = [self frame];
+
+ DBG_PRINT("MyNSOpenGLLayer::init %p, ctx %p, pfmt %p, pbuffer %p, opaque %d, pbuffer %dx%d -> tex %dx%d, frame: %lf/%lf %lfx%lf (refcnt %d)\n",
+ self, _ctx, _fmt, pbuffer, opaque, [pbuffer pixelsWide], [pbuffer pixelsHigh], texWidth, texHeight,
+ lRect.origin.x, lRect.origin.y, lRect.size.width, lRect.size.height, (int)[self retainCount]);
+ return self;
+}
+
+- (void)disableAnimation
+{
+ DBG_PRINT("MyNSOpenGLLayer::disableAnimation: %p (refcnt %d) - displayLink %p\n", self, (int)[self retainCount], displayLink);
+ pthread_mutex_lock(&renderLock);
+ [self setAsynchronous: NO];
+ if(NULL != displayLink) {
+#ifdef HAS_CADisplayLink
+ [displayLink setPaused: YES];
+ [displayLink release];
+#else
+ if(NULL!=displayLink) {
+ CVDisplayLinkStop(displayLink);
+ CVDisplayLinkRelease(displayLink);
+ }
+#endif
+ displayLink = NULL;
+ }
+ pthread_mutex_unlock(&renderLock);
+}
+
+- (void)deallocTex
+{
+ pthread_mutex_lock(&renderLock);
+ NSOpenGLContext* context = [self openGLContext];
+ DBG_PRINT("MyNSOpenGLLayer::deallocTex %p (refcnt %d) - context %p, pbuffer %p\n", self, (int)[self retainCount], context, pbuffer);
+ if(NULL != pbuffer) {
+ if(NULL!=context) {
+ [context makeCurrentContext];
+ if(0 != textureID) {
+ glDeleteTextures(1, &textureID);
+ textureID = 0;
+ }
+ [context clearDrawable];
+ }
+ [pbuffer release];
+ pbuffer = NULL;
+ }
+ pthread_mutex_unlock(&renderLock);
+}
+
+- (void)releaseLayer
+{
+ DBG_PRINT("MyNSOpenGLLayer::releaseLayer.0: %p (refcnt %d)\n", self, (int)[self retainCount]);
+ pthread_mutex_lock(&renderLock);
+ [self disableAnimation];
+ [self deallocTex];
+ [self release];
+ DBG_PRINT("MyNSOpenGLLayer::releaseLayer.X: %p (refcnt %d)\n", self, (int)[self retainCount]);
+ pthread_mutex_unlock(&renderLock);
+}
+
+- (void)dealloc
+{
+ DBG_PRINT("MyNSOpenGLLayer::dealloc.0 %p (refcnt %d)\n", self, (int)[self retainCount]);
+ // NSLog(@"MyNSOpenGLLayer::dealloc: %@",[NSThread callStackSymbols]);
+
+ [self disableAnimation];
+ [self deallocTex];
+ pthread_cond_destroy(&renderSignal);
+ pthread_mutex_destroy(&renderLock);
+ [super dealloc];
+ DBG_PRINT("MyNSOpenGLLayer::dealloc.X %p\n", self);
+}
+
+- (BOOL)canDrawInOpenGLContext:(NSOpenGLContext *)context pixelFormat:(NSOpenGLPixelFormat *)pixelFormat
+ forLayerTime:(CFTimeInterval)timeInterval displayTime:(const CVTimeStamp *)timeStamp
+{
+ // assume both methods 'canDrawInOpenGLContext' and 'drawInOpenGLContext'
+ // are called from the same thread subsequently
+ pthread_mutex_lock(&renderLock);
+ Bool res = NULL != pbuffer && YES == shallDraw;
+ if(!res) {
+ SYNC_PRINT("<0>");
+ pthread_mutex_unlock(&renderLock);
+ } else {
+ SYNC_PRINT("<");
+ }
+ return res;
+}
+
+- (void)drawInOpenGLContext:(NSOpenGLContext *)context pixelFormat:(NSOpenGLPixelFormat *)pixelFormat
+ forLayerTime:(CFTimeInterval)timeInterval displayTime:(const CVTimeStamp *)timeStamp
+{
+ [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];
+ 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;
+
+ 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);
+
+ CGRect lRect = [self frame];
+ DBG_PRINT("MyNSOpenGLLayer::drawInOpenGLContext %p frame0: %lf/%lf %lfx%lf\n",
+ self, lRect.origin.x, lRect.origin.y, lRect.size.width, lRect.size.height);
+ }
+
+ 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);
+ 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);
+
+ glEnable(textureTarget);
+
+ static GLfloat verts[] = {
+ -1.0, -1.0,
+ -1.0, 1.0,
+ 1.0, 1.0,
+ 1.0, -1.0
+ };
+
+ GLfloat tex[] = {
+ 0.0, 0.0,
+ 0.0, tHeight,
+ tWidth, tHeight,
+ tWidth, 0.0
+ };
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glVertexPointer(2, GL_FLOAT, 0, verts);
+ glTexCoordPointer(2, GL_FLOAT, 0, tex);
+
+ glDrawArrays(GL_QUADS, 0, 4);
+
+ glDisableClientState(GL_VERTEX_ARRAY);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+
+ glDisable(textureTarget);
+ glBindTexture(textureTarget, 0);
+
+ [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 texWidth, int texHeight) {
+ // This simply crashes after dealloc() has been called .. ie. ref-count -> 0 too early ?
+ // However using alloc/init, actual dealloc happens at JAWT destruction, hence too later IMHO.
+ // return [[MyNSOpenGLLayer layer] setupWithContext:ctx pixelFormat: fmt pbuffer: p opaque: opaque texWidth: texWidth texHeight: texHeight];
+
+ return [[[MyNSOpenGLLayer alloc] init] setupWithContext: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);
+ #ifdef DBG_SYNC
+ struct timespec t1, td;
+ timespec_now(&t1);
+ timespec_subtract(&td, &t1, &to_abs);
+ long td_ms = timespec_milliseconds(&td);
+ fprintf(stderr, "%ld ms", td_ms);
+ #endif
+ } 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;
+ @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];
+ }
+}
+
+void releaseNSOpenGLLayer(NSOpenGLLayer* layer) {
+ MyNSOpenGLLayer* l = (MyNSOpenGLLayer*) layer;
+ NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+ DBG_PRINT("MyNSOpenGLLayer::releaseNSOpenGLLayer.0: %p\n", l);
+
+ if ( [NSThread isMainThread] == YES ) {
+ [l releaseLayer];
+ } else {
+ [l performSelectorOnMainThread:@selector(releaseLayer) withObject:nil waitUntilDone:NO];
+ }
+
+ DBG_PRINT("MyNSOpenGLLayer::releaseNSOpenGLLayer.X: %p\n", l);
+ [pool release];
+}
+