diff options
Diffstat (limited to 'src/jogl/native/macosx/MacOSXWindowSystemInterface-pbuffer.m')
-rw-r--r-- | src/jogl/native/macosx/MacOSXWindowSystemInterface-pbuffer.m | 480 |
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]; +} + |