aboutsummaryrefslogtreecommitdiffstats
path: root/src/jogl/native
diff options
context:
space:
mode:
Diffstat (limited to 'src/jogl/native')
-rw-r--r--src/jogl/native/macosx/ContextUpdater.h6
-rw-r--r--src/jogl/native/macosx/ContextUpdater.m57
-rw-r--r--src/jogl/native/macosx/MacOSXCustomCGLCode.c5
-rw-r--r--src/jogl/native/macosx/MacOSXWindowSystemInterface-nsview.m140
-rw-r--r--src/jogl/native/macosx/MacOSXWindowSystemInterface-pbuffer.m466
-rw-r--r--src/jogl/native/macosx/MacOSXWindowSystemInterface.h20
-rw-r--r--src/jogl/native/macosx/MacOSXWindowSystemInterface.m235
-rw-r--r--src/jogl/native/timespec.c63
-rw-r--r--src/jogl/native/timespec.h19
9 files changed, 835 insertions, 176 deletions
diff --git a/src/jogl/native/macosx/ContextUpdater.h b/src/jogl/native/macosx/ContextUpdater.h
index 3cf7315af..f00b2be57 100644
--- a/src/jogl/native/macosx/ContextUpdater.h
+++ b/src/jogl/native/macosx/ContextUpdater.h
@@ -25,17 +25,13 @@ This notification is sent whenever an NSView that has an attached NSSurface chan
@interface ContextUpdater : NSObject
{
@protected
+ pthread_mutex_t resourceLock;
NSView * view;
NSRect viewRect;
NSOpenGLContext *ctx;
BOOL viewUpdated;
}
-- (void) lock;
-- (void) lockInFunction:(char *)func atLine:(int)line;
-- (void) unlock;
-- (void) unlockInFunction:(char *)func atLine:(int)line;
-
- (id) initWithContext:(NSOpenGLContext *)context view: (NSView *)nsView;
- (void) update:(NSNotification *)notification;
diff --git a/src/jogl/native/macosx/ContextUpdater.m b/src/jogl/native/macosx/ContextUpdater.m
index 21f98ad5e..a3b9b5c8c 100644
--- a/src/jogl/native/macosx/ContextUpdater.m
+++ b/src/jogl/native/macosx/ContextUpdater.m
@@ -1,43 +1,26 @@
#import "ContextUpdater.h"
#import <pthread.h>
-@implementation ContextUpdater
-{
-}
-
-static pthread_mutex_t resourceLock = PTHREAD_MUTEX_INITIALIZER;
-
-static void printLockDebugInfo(char *message, char *func, int line)
-{
- fprintf(stderr, "%s in function: \"%s\" at line: %d\n", message, func, line);
- fflush(NULL);
-}
-
-- (void) lock
-{
- pthread_mutex_lock(&resourceLock);
-}
+#define VERBOSE_ON 1
-- (void) lockInFunction:(char *)func atLine:(int)line
-{
- printLockDebugInfo("locked ", func, line);
- [self lock];
-}
+#ifdef VERBOSE_ON
+ #define DBG_PRINT(...) NSLog(@ __VA_ARGS__)
+ // #define DBG_PRINT(...) fprintf(stderr, __VA_ARGS__); fflush(stderr)
+#else
+ #define DBG_PRINT(...)
+#endif
-- (void) unlock
-{
- pthread_mutex_unlock(&resourceLock);
-}
+#ifndef CGL_VERSION_1_3
+ #warning this SDK doesn't support OpenGL profile
+#endif
-- (void) unlockInFunction:(char *)func atLine:(int)line
+@implementation ContextUpdater
{
- printLockDebugInfo("unlocked", func, line);
- [self unlock];
}
- (void) update:(NSNotification *)notification
{
- [self lock];
+ pthread_mutex_lock(&resourceLock);
NSRect r = [view frame];
if(viewRect.origin.x != r.origin.x ||
@@ -47,41 +30,47 @@ static void printLockDebugInfo(char *message, char *func, int line)
viewUpdated = TRUE;
viewRect = r;
}
-
- [self unlock];
+
+ pthread_mutex_unlock(&resourceLock);
}
- (BOOL) needsUpdate
{
BOOL r;
- [self lock];
+ pthread_mutex_lock(&resourceLock);
r = viewUpdated;
viewUpdated = FALSE;
- [self unlock];
+ pthread_mutex_unlock(&resourceLock);
return r;
}
- (id) initWithContext:(NSOpenGLContext *)context view: (NSView *)nsView
{
+ DBG_PRINT("ContextUpdater::init.0 view %p, ctx %p\n", view, ctx);
+ pthread_mutex_init(&resourceLock, NULL); // fast non-recursive
ctx = context;
view = nsView;
[ctx retain];
[view retain];
viewRect = [view frame];
- viewUpdated = FALSE;
+ viewUpdated = TRUE;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(update:) name:NSViewGlobalFrameDidChangeNotification object: view];
+ DBG_PRINT("ContextUpdater::init.X\n");
return [super init];
}
- (void) dealloc
{
+ DBG_PRINT("ContextUpdater::dealloc.0 view %p, ctx %p\n", view, ctx);
[[NSNotificationCenter defaultCenter] removeObserver:self];
[view release];
[ctx release];
+ pthread_mutex_destroy(&resourceLock);
+ DBG_PRINT("ContextUpdater::dealloc.X\n");
[super dealloc];
}
diff --git a/src/jogl/native/macosx/MacOSXCustomCGLCode.c b/src/jogl/native/macosx/MacOSXCustomCGLCode.c
index c29be889d..f8b7a800f 100644
--- a/src/jogl/native/macosx/MacOSXCustomCGLCode.c
+++ b/src/jogl/native/macosx/MacOSXCustomCGLCode.c
@@ -5,8 +5,7 @@
#include </usr/include/machine/types.h>
#include "macosx-window-system.h"
-void CGLQueryPixelFormat(void* pixelFormat, int* iattrs, int niattrs, int* ivalues) {
- CGLPixelFormatObj pix = (CGLPixelFormatObj) pixelFormat;
+void CGLQueryPixelFormat(CGLPixelFormatObj fmt, int* iattrs, int niattrs, int* ivalues) {
// FIXME: think about how specifying this might affect the API
int virtualScreen = 0;
@@ -14,7 +13,7 @@ void CGLQueryPixelFormat(void* pixelFormat, int* iattrs, int niattrs, int* ivalu
GLint value;
for (i = 0; i < niattrs && iattrs[i]>0; i++) {
CGLPixelFormatAttribute attr = (CGLPixelFormatAttribute) iattrs[i];
- if ( kCGLNoError == CGLDescribePixelFormat(pix, virtualScreen, attr, &value) ) {
+ if ( kCGLNoError == CGLDescribePixelFormat(fmt, virtualScreen, attr, &value) ) {
ivalues[i] = value;
} else {
ivalues[i] = 0;
diff --git a/src/jogl/native/macosx/MacOSXWindowSystemInterface-nsview.m b/src/jogl/native/macosx/MacOSXWindowSystemInterface-nsview.m
new file mode 100644
index 000000000..7233f40ce
--- /dev/null
+++ b/src/jogl/native/macosx/MacOSXWindowSystemInterface-nsview.m
@@ -0,0 +1,140 @@
+
+@interface MyNSOpenGLLayer: NSOpenGLLayer
+{
+@protected
+ NSOpenGLContext* ctxShared;
+ NSView* view;
+ NSOpenGLPixelFormat* fmt;
+@public
+ volatile BOOL shallDraw;
+}
+
+- (id) initWithContext: (NSOpenGLContext*) ctx
+ pixelFormat: (NSOpenGLPixelFormat*) pfmt
+ view: (NSView*) v
+ opaque: (Bool) opaque;
+
+@end
+
+@implementation MyNSOpenGLLayer
+
+- (id) initWithContext: (NSOpenGLContext*) _ctx
+ pixelFormat: (NSOpenGLPixelFormat*) _fmt
+ view: (NSView*) _view
+ opaque: (Bool) opaque
+{
+ self = [super init];
+ ctxShared = _ctx;
+ [ctxShared retain];
+
+ fmt = _fmt;
+ [fmt retain];
+
+ view = _view;
+ [view retain];
+ [self setView: view];
+ [view setLayer: self];
+ [view setWantsLayer: YES];
+
+ [self setAsynchronous: NO];
+ // [self setAsynchronous: YES]; // FIXME: JAU
+ [self setNeedsDisplayOnBoundsChange: NO];
+ [self setOpaque: opaque ? YES : NO];
+ shallDraw = NO;
+ textureID = 0;
+ DBG_PRINT("MyNSOpenGLLayer::init %p, ctx %p, pfmt %p, view %p, opaque %d\n", self, ctx, fmt, view, opaque);
+ return self;
+}
+
+- (void)dealloc
+{
+ [fmt release];
+ [ctxShared release];
+ [view release];
+ DBG_PRINT("MyNSOpenGLLayer::dealloc %p\n", self);
+ [super dealloc];
+}
+
+- (void) setOpenGLContext: (NSOpenGLContext*) _ctx
+{
+ DBG_PRINT("MyNSOpenGLLayer::setOpenGLContext: %p %p -> %p\n", self, [self openGLContext], _ctx);
+ [super setOpenGLContext: _ctx];
+}
+
+- (void) setOpenGLPixelFormat: (NSOpenGLPixelFormat*) _fmt
+{
+ DBG_PRINT("MyNSOpenGLLayer::setOpenGLPixelFormat %p %p -> %p\n", self, fmt, _fmt);
+ [super setOpenGLPixelFormat: fmt];
+}
+
+- (NSOpenGLPixelFormat *) openGLPixelFormat
+{
+ return fmt;
+}
+
+- (void) setView: (NSView*) v
+{
+ DBG_PRINT("MyNSOpenGLLayer::setView %p %p -> %p (ignored/propagated)\n", self, view, v);
+ [super setView: view]; // propagate
+}
+
+- (NSOpenGLPixelFormat *)openGLPixelFormatForDisplayMask:(uint32_t)mask
+{
+ DBG_PRINT("MyNSOpenGLLayer::openGLPixelFormatForDisplayMask %p %d -> %p\n", self, mask, fmt);
+ return fmt;
+}
+
+- (NSOpenGLContext *)openGLContextForPixelFormat:(NSOpenGLPixelFormat *)pixelFormat
+{
+ NSOpenGLContext* ctx = NULL;
+ if(NULL == ctx) {
+ int viewNotReady[] = { 0 };
+ ctx = createContext(ctxShared, view, true, fmt, [self isOpaque], viewNotReady);
+ }
+ DBG_PRINT("MyNSOpenGLLayer::openGLContextForPixelFormat %p, fmt %p/%p, view %p, shared %p -> %p\n",
+ self, fmt, pixelFormat, view, ctxShared, ctx);
+ return ctx;
+}
+
+- (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;
+}
+
+- (void)drawInOpenGLContext:(NSOpenGLContext *)context pixelFormat:(NSOpenGLPixelFormat *)pixelFormat
+ forLayerTime:(CFTimeInterval)timeInterval displayTime:(const CVTimeStamp *)timeStamp
+{
+ self->shallDraw = NO;
+
+ DBG_PRINT("MyNSOpenGLLayer::drawInOpenGLContext %p, ctx %p, pfmt %p\n", self, context, pixelFormat);
+
+ [super drawInOpenGLContext: context pixelFormat: pixelFormat forLayerTime: timeInterval displayTime: timeStamp];
+}
+
+@end
+
+NSOpenGLLayer* createNSOpenGLLayer(NSOpenGLContext* ctx, NSOpenGLPixelFormat* fmt, NSView* view, Bool opaque) {
+ return [[MyNSOpenGLLayer alloc] initWithContext:ctx pixelFormat: fmt view: view opaque: opaque];
+}
+
+void setNSOpenGLLayerNeedsDisplay(NSOpenGLLayer* layer) {
+ MyNSOpenGLLayer* l = (MyNSOpenGLLayer*) layer;
+ NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+ l->shallDraw = YES;
+ [l performSelectorOnMainThread:@selector(setNeedsDisplay) withObject:nil waitUntilDone:YES];
+ // 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) {
+ NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+ [l release];
+ [pool release];
+}
+
diff --git a/src/jogl/native/macosx/MacOSXWindowSystemInterface-pbuffer.m b/src/jogl/native/macosx/MacOSXWindowSystemInterface-pbuffer.m
new file mode 100644
index 000000000..47f679fac
--- /dev/null
+++ b/src/jogl/native/macosx/MacOSXWindowSystemInterface-pbuffer.m
@@ -0,0 +1,466 @@
+#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;
+- (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_mutex_init(&renderLock, NULL); // fast non-recursive
+ pthread_cond_init(&renderSignal, NULL); // no attribute
+
+ 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)dealloc
+{
+ DBG_PRINT("MyNSOpenGLLayer::dealloc.0 %p (refcnt %d)\n", self, (int)[self retainCount]);
+ [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];
+ // FIXME ?? [context update];
+
+ /**
+ * 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);
+ if(lRect.origin.x<0 || lRect.origin.y<0) {
+ lRect.origin.x = 0;
+ lRect.origin.y = 0;
+ [self setFrame: lRect];
+ DBG_PRINT("MyNSOpenGLLayer::drawInOpenGLContext %p frame*: %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 (refcnt %d)\n", l, (int)[l retainCount]);
+
+ [l performSelectorOnMainThread:@selector(disableAnimation) withObject:nil waitUntilDone:YES];
+ // [l disableAnimation];
+
+ [l performSelectorOnMainThread:@selector(deallocTex) withObject:nil waitUntilDone:YES];
+ // [l deallocTex];
+
+ [l release];
+ DBG_PRINT("MyNSOpenGLLayer::releaseNSOpenGLLayer.X: %p (refcnt %d)\n", l, (int)[l retainCount]);
+ [pool release];
+}
+
diff --git a/src/jogl/native/macosx/MacOSXWindowSystemInterface.h b/src/jogl/native/macosx/MacOSXWindowSystemInterface.h
new file mode 100644
index 000000000..b2d7f9db8
--- /dev/null
+++ b/src/jogl/native/macosx/MacOSXWindowSystemInterface.h
@@ -0,0 +1,20 @@
+#import <Cocoa/Cocoa.h>
+#import <OpenGL/gl.h>
+#import <OpenGL/CGLTypes.h>
+#import <jni.h>
+
+#define VERBOSE_ON 1
+
+#ifdef VERBOSE_ON
+ #define DBG_PRINT(...) NSLog(@ __VA_ARGS__)
+ // #define DBG_PRINT(...) fprintf(stderr, __VA_ARGS__); fflush(stderr)
+#else
+ #define DBG_PRINT(...)
+#endif
+
+#ifndef CGL_VERSION_1_3
+ #warning this SDK doesn't support OpenGL profile
+#endif
+
+#import "macosx-window-system.h"
+
diff --git a/src/jogl/native/macosx/MacOSXWindowSystemInterface.m b/src/jogl/native/macosx/MacOSXWindowSystemInterface.m
index 751fba9c0..af269a4b5 100644
--- a/src/jogl/native/macosx/MacOSXWindowSystemInterface.m
+++ b/src/jogl/native/macosx/MacOSXWindowSystemInterface.m
@@ -14,19 +14,10 @@
#endif
*/
-#import <Cocoa/Cocoa.h>
-#import <OpenGL/gl.h>
-#import <OpenGL/CGLTypes.h>
-#import <jni.h>
-
-#ifndef CGL_VERSION_1_3
- #warning this SDK doesn't support OpenGL profile
-#endif
+#import "MacOSXWindowSystemInterface.h"
#import "ContextUpdater.h"
-#import "macosx-window-system.h"
-
// see MacOSXPbufferGLContext.java createPbuffer
#define USE_GL_TEXTURE_RECTANGLE_EXT
@@ -377,7 +368,7 @@ long validateParameter(NSOpenGLPixelFormatAttribute attribute, long value)
return value;
}
-void* createPixelFormat(int* iattrs, int niattrs, int* ivalues) {
+NSOpenGLPixelFormat* createPixelFormat(int* iattrs, int niattrs, int* ivalues) {
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
getRendererInfo();
@@ -390,6 +381,18 @@ void* createPixelFormat(int* iattrs, int niattrs, int* ivalues) {
for (i = 0; i < niattrs && iattrs[i]>0; i++) {
int attr = iattrs[i];
switch (attr) {
+ case NSOpenGLPFANoRecovery:
+ if (ivalues[i] != 0) {
+ attribs[idx++] = NSOpenGLPFANoRecovery;
+ }
+ break;
+
+ case NSOpenGLPFAAccelerated:
+ if (ivalues[i] != 0) {
+ attribs[idx++] = NSOpenGLPFAAccelerated;
+ }
+ break;
+
case NSOpenGLPFAPixelBuffer:
if (ivalues[i] != 0) {
attribs[idx++] = NSOpenGLPFAPixelBuffer;
@@ -451,9 +454,8 @@ void* createPixelFormat(int* iattrs, int niattrs, int* ivalues) {
return fmt;
}
-void queryPixelFormat(void* pixelFormat, int* iattrs, int niattrs, int* ivalues) {
+void queryPixelFormat(NSOpenGLPixelFormat* fmt, int* iattrs, int niattrs, int* ivalues) {
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
- NSOpenGLPixelFormat* fmt = (NSOpenGLPixelFormat*) pixelFormat;
GLint tmp;
// FIXME: think about how specifying this might affect the API
GLint virtualScreen = 0;
@@ -468,16 +470,43 @@ void queryPixelFormat(void* pixelFormat, int* iattrs, int niattrs, int* ivalues)
[pool release];
}
-void deletePixelFormat(void* pixelFormat) {
+void deletePixelFormat(NSOpenGLPixelFormat* fmt) {
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
- NSOpenGLPixelFormat* fmt = (NSOpenGLPixelFormat*) pixelFormat;
[fmt release];
[pool release];
}
-void* createContext(void* shareContext,
- void* view,
- void* pixelFormat,
+NSOpenGLContext* getCurrentContext() {
+ NSOpenGLContext *ctx = NULL;
+
+ NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+ ctx = [NSOpenGLContext currentContext];
+ [pool release];
+ return ctx;
+}
+
+CGLContextObj getCGLContext(NSOpenGLContext* ctx) {
+ void * cglContext = NULL;
+
+ NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+ cglContext = [ctx CGLContextObj];
+ [pool release];
+ return cglContext;
+}
+
+NSView* getNSView(NSOpenGLContext* ctx) {
+ NSView* view = NULL;
+
+ NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+ view = [ctx view];
+ [pool release];
+ return view;
+}
+
+NSOpenGLContext* createContext(NSOpenGLContext* share,
+ NSView* view,
+ Bool isBackingLayerView,
+ NSOpenGLPixelFormat* fmt,
Bool opaque,
int* viewNotReady)
{
@@ -485,27 +514,22 @@ void* createContext(void* shareContext,
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
- NSView *nsView = NULL;
- NSObject *nsObj = (NSObject*) view;
-
- if( nsObj != NULL && [nsObj isKindOfClass:[NSView class]] ) {
- nsView = (NSView*)nsObj;
- }
-
- if (nsView != NULL)
- {
+ if (view != NULL) {
Bool viewReady = true;
- if ([nsView lockFocusIfCanDraw] == NO)
- {
- viewReady = false;
+ if(!isBackingLayerView) {
+ if ([view lockFocusIfCanDraw] == NO) {
+ DBG_PRINT("createContext [view lockFocusIfCanDraw] failed\n");
+ viewReady = false;
+ }
}
- else
- {
- NSRect frame = [nsView frame];
- if ((frame.size.width == 0) || (frame.size.height == 0))
- {
- [nsView unlockFocus];
+ if(viewReady) {
+ NSRect frame = [view frame];
+ if ((frame.size.width == 0) || (frame.size.height == 0)) {
+ if(!isBackingLayerView) {
+ [view unlockFocus];
+ }
+ DBG_PRINT("createContext view.frame size %dx%d\n", (int)frame.size.width, (int)frame.size.height);
viewReady = false;
}
}
@@ -523,126 +547,80 @@ void* createContext(void* shareContext,
}
}
- NSOpenGLContext* nsContext = [[NSOpenGLContext alloc]
- initWithFormat: (NSOpenGLPixelFormat*) pixelFormat
- shareContext: (NSOpenGLContext*) shareContext];
+ NSOpenGLContext* ctx = [[NSOpenGLContext alloc] initWithFormat:fmt shareContext:share];
- if (nsContext != nil) {
- if (nsView != nil) {
- if(!opaque) {
- GLint zeroOpacity = 0;
- [nsContext setValues:&zeroOpacity forParameter:NSOpenGLCPSurfaceOpacity];
- }
- [nsContext setView:nsView];
- [nsView unlockFocus];
- }
+ if (ctx != nil) {
+ if (view != nil) {
+ if(!opaque) {
+ GLint zeroOpacity = 0;
+ [ctx setValues:&zeroOpacity forParameter:NSOpenGLCPSurfaceOpacity];
+ }
+ [ctx setView:view];
+ if(!isBackingLayerView) {
+ [view unlockFocus];
}
+ }
+ }
[pool release];
- return nsContext;
-}
-
-void * getCurrentContext() {
- NSOpenGLContext *nsContext = NULL;
-
- NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
- nsContext = [NSOpenGLContext currentContext];
- [pool release];
- return nsContext;;
-}
-
-void * getCGLContext(void* nsJContext) {
- NSOpenGLContext *nsContext = (NSOpenGLContext*)nsJContext;
- void * cglContext = NULL;
-
- NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
- cglContext = [nsContext CGLContextObj];
- [pool release];
- return cglContext;
+ return ctx;
}
-void * getNSView(void* nsJContext) {
- NSOpenGLContext *nsContext = (NSOpenGLContext*)nsJContext;
- void * view = NULL;
-
+Bool makeCurrentContext(NSOpenGLContext* ctx) {
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
- view = [nsContext view];
- [pool release];
- return view;
-}
-
-Bool makeCurrentContext(void* nsJContext) {
- NSOpenGLContext *nsContext = (NSOpenGLContext*)nsJContext;
-
- NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
- [nsContext makeCurrentContext];
+ [ctx makeCurrentContext];
[pool release];
return true;
}
-Bool clearCurrentContext(void* nsJContext) {
- NSOpenGLContext *nsContext = (NSOpenGLContext*)nsJContext;
-
+Bool clearCurrentContext(NSOpenGLContext* ctx) {
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
NSOpenGLContext *currentNSContext = [NSOpenGLContext currentContext];
- if( currentNSContext != nsContext ) {
- [nsContext makeCurrentContext];
+ if( currentNSContext != ctx ) {
+ [ctx makeCurrentContext];
}
[NSOpenGLContext clearCurrentContext];
[pool release];
return true;
}
-Bool deleteContext(void* nsJContext, Bool releaseOnMainThread) {
- NSOpenGLContext *nsContext = (NSOpenGLContext*)nsJContext;
-
+Bool deleteContext(NSOpenGLContext* ctx, Bool releaseOnMainThread) {
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
- [nsContext clearDrawable];
+ [ctx clearDrawable];
if(releaseOnMainThread && NO == [NSThread isMainThread]) {
- [nsContext performSelectorOnMainThread:@selector(release) withObject:nil waitUntilDone:YES];
+ [ctx performSelectorOnMainThread:@selector(release) withObject:nil waitUntilDone:YES];
} else {
// would hangs for ~10s for 1 of a shared context set or offscreen context, set releaseOnMainThread=true !
- [nsContext release];
+ [ctx release];
}
[pool release];
return true;
}
-Bool flushBuffer(void* nsJContext) {
- NSOpenGLContext *nsContext = (NSOpenGLContext*)nsJContext;
-
+Bool flushBuffer(NSOpenGLContext* ctx) {
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
- [nsContext flushBuffer];
+ [ctx flushBuffer];
[pool release];
return true;
}
-void setContextOpacity(void* nsJContext, int opacity) {
- NSOpenGLContext *nsContext = (NSOpenGLContext*)nsJContext;
-
- [nsContext setValues:&opacity forParameter:NSOpenGLCPSurfaceOpacity];
+void setContextOpacity(NSOpenGLContext* ctx, int opacity) {
+ [ctx setValues:&opacity forParameter:NSOpenGLCPSurfaceOpacity];
}
-void updateContext(void* nsJContext) {
- NSOpenGLContext *nsContext = (NSOpenGLContext*)nsJContext;
-
+void updateContext(NSOpenGLContext* ctx) {
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
- [nsContext update];
+ [ctx update];
[pool release];
}
-void copyContext(void* destContext, void* srcContext, int mask) {
- NSOpenGLContext *src = (NSOpenGLContext*) srcContext;
- NSOpenGLContext *dst = (NSOpenGLContext*) destContext;
- [dst copyAttributesFromContext: src withMask: mask];
+void copyContext(NSOpenGLContext* dest, NSOpenGLContext* src, int mask) {
+ [dest copyAttributesFromContext: src withMask: mask];
}
-void* updateContextRegister(void* nsJContext, void* nsJView) {
- NSOpenGLContext *nsContext = (NSOpenGLContext*)nsJContext;
- NSView *nsView = (NSView*)nsJView;
-
+void* updateContextRegister(NSOpenGLContext* ctx, NSView* view) {
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
- ContextUpdater *contextUpdater = [[ContextUpdater alloc] initWithContext: nsContext view: nsView];
+ ContextUpdater *contextUpdater = [[ContextUpdater alloc] initWithContext: ctx view: view];
[pool release];
return contextUpdater;
}
@@ -666,7 +644,7 @@ void updateContextUnregister(void* updater) {
[pool release];
}
-void* createPBuffer(int renderTarget, int internalFormat, int width, int height) {
+NSOpenGLPixelBuffer* createPBuffer(int renderTarget, int internalFormat, int width, int height) {
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
NSOpenGLPixelBuffer* pBuffer = [[NSOpenGLPixelBuffer alloc]
initWithTextureTarget:renderTarget
@@ -678,35 +656,25 @@ void* createPBuffer(int renderTarget, int internalFormat, int width, int height)
return pBuffer;
}
-Bool destroyPBuffer(void* buffer) {
- NSOpenGLPixelBuffer *pBuffer = (NSOpenGLPixelBuffer*)buffer;
-
+Bool destroyPBuffer(NSOpenGLPixelBuffer* pBuffer) {
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
[pBuffer release];
[pool release];
-
return true;
}
-void setContextPBuffer(void* nsJContext, void* buffer) {
- NSOpenGLContext *nsContext = (NSOpenGLContext*)nsJContext;
- NSOpenGLPixelBuffer *pBuffer = (NSOpenGLPixelBuffer*)buffer;
-
+void setContextPBuffer(NSOpenGLContext* ctx, NSOpenGLPixelBuffer* pBuffer) {
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
- [nsContext setPixelBuffer: pBuffer
+ [ctx setPixelBuffer: pBuffer
cubeMapFace: 0
mipMapLevel: 0
- currentVirtualScreen: [nsContext currentVirtualScreen]];
+ currentVirtualScreen: [ctx currentVirtualScreen]];
[pool release];
}
-void setContextTextureImageToPBuffer(void* nsJContext, void* buffer, int colorBuffer) {
- NSOpenGLContext *nsContext = (NSOpenGLContext*)nsJContext;
- NSOpenGLPixelBuffer *pBuffer = (NSOpenGLPixelBuffer*)buffer;
-
+void setContextTextureImageToPBuffer(NSOpenGLContext* ctx, NSOpenGLPixelBuffer* pBuffer, GLenum colorBuffer) {
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
- [nsContext setTextureImageToPixelBuffer: pBuffer
- colorBuffer: (unsigned long) colorBuffer];
+ [ctx setTextureImageToPixelBuffer: pBuffer colorBuffer: colorBuffer];
[pool release];
}
@@ -746,10 +714,9 @@ void* getProcAddress(const char *procname) {
return NULL;
}
-void setSwapInterval(void* nsJContext, int interval) {
- NSOpenGLContext *nsContext = (NSOpenGLContext*)nsJContext;
+void setSwapInterval(NSOpenGLContext* ctx, int interval) {
GLint swapInterval = interval;
- [nsContext setValues: &swapInterval forParameter: NSOpenGLCPSwapInterval];
+ [ctx setValues: &swapInterval forParameter: NSOpenGLCPSwapInterval];
}
Bool setGammaRamp(int tableSize, float* redRamp, float* greenRamp, float* blueRamp) {
diff --git a/src/jogl/native/timespec.c b/src/jogl/native/timespec.c
new file mode 100644
index 000000000..74c1a6901
--- /dev/null
+++ b/src/jogl/native/timespec.c
@@ -0,0 +1,63 @@
+#include "timespec.h"
+#include <sys/time.h>
+
+void timespec_now(struct timespec *ts)
+{
+ struct timeval tv;
+
+ // not using clock_gettime() [of rt library] due to portability
+ gettimeofday(&tv, NULL);
+ ts->tv_sec = tv.tv_sec;
+ ts->tv_nsec = tv.tv_usec*1000;
+}
+
+void timespec_addms(struct timespec *ts, long ms)
+{
+ int sec=ms/1000;
+ ms=ms-sec*1000;
+
+ // perform the addition
+ ts->tv_nsec+=ms*1000000;
+
+ // adjust the time
+ ts->tv_sec+=ts->tv_nsec/1000000000 + sec;
+ ts->tv_nsec=ts->tv_nsec%1000000000;
+}
+
+void timespec_addns(struct timespec *ts, long ns)
+{
+ int sec=ns/1000000000;
+ ns=ns - sec*1000000000;
+
+ // perform the addition
+ ts->tv_nsec+=ns;
+
+ // adjust the time
+ ts->tv_sec+=ts->tv_nsec/1000000000 + sec;
+ ts->tv_nsec=ts->tv_nsec%1000000000;
+
+}
+
+int timespec_compare(struct timespec *a, struct timespec *b)
+{
+ if (a->tv_sec!=b->tv_sec)
+ return a->tv_sec-b->tv_sec;
+ return a->tv_nsec-b->tv_nsec;
+}
+
+void timespec_subtract(struct timespec *r, struct timespec *a, struct timespec *b)
+{
+ r->tv_sec = a->tv_sec;
+ r->tv_nsec = a->tv_nsec - b->tv_nsec;
+ if (r->tv_nsec < 0) {
+ // borrow.
+ r->tv_nsec += 1000000000;
+ r->tv_sec --;
+ }
+ r->tv_sec = r->tv_sec - b->tv_sec;
+}
+
+long timespec_milliseconds(struct timespec *a)
+{
+ return a->tv_sec*1000 + a->tv_nsec/1000000;
+}
diff --git a/src/jogl/native/timespec.h b/src/jogl/native/timespec.h
new file mode 100644
index 000000000..671eb4716
--- /dev/null
+++ b/src/jogl/native/timespec.h
@@ -0,0 +1,19 @@
+#ifndef _timespec_h
+#define _timespec_h
+
+#include <time.h>
+
+void timespec_now(struct timespec *ts);
+void timespec_addms(struct timespec *ts, long ms);
+void timespec_addns(struct timespec *ts, long ns);
+
+/** returns 0: a==b, >0: a>b, <0: a<b */
+int timespec_compare(struct timespec *a, struct timespec *b);
+
+/** computes r = a - b */
+void timespec_subtract(struct timespec *r, struct timespec *a, struct timespec *b);
+
+/** convert the timespec into milliseconds (may overflow) */
+long timespec_milliseconds(struct timespec *a);
+
+#endif /* _timespec_h */