Jogamp
Bug 741 HiDPI: Add ScalableSurface interface to get/set pixelScale w/ full OSX impl.
[jogl.git] / src / jogl / native / macosx / MacOSXWindowSystemInterface-calayer.m
1 #import "MacOSXWindowSystemInterface.h"
2 #import <QuartzCore/QuartzCore.h>
3 #import <pthread.h>
4 #import "NativeWindowProtocols.h"
5 #include "timespec.h"
6
7 #import <OpenGL/glext.h>
8
9 /** 
10  * Partial include of gl3.h - which we can only expect and use 
11  * in case of a GL3 core context at runtime.
12  * Otherwise we would need to have 2 modules, one including GL2
13  * and one inclusing GL3 headers.
14  */
15 #ifndef GL_ARB_vertex_array_object
16 #define GL_VERTEX_ARRAY_BINDING           0x85B5
17 extern void glBindVertexArray (GLuint array);
18 extern void glDeleteVertexArrays (GLsizei n, const GLuint *arrays);
19 extern void glGenVertexArrays (GLsizei n, GLuint *arrays);
20 extern GLboolean glIsVertexArray (GLuint array);
21 #endif
22
23 // 
24 // CADisplayLink only available on iOS >= 3.1, sad, since it's convenient.
25 // Use CVDisplayLink otherwise.
26 //
27 // #define HAS_CADisplayLink 1
28 //
29
30 // lock/sync debug output
31 //
32 // #define DBG_SYNC 1
33 //
34 #ifdef DBG_SYNC
35     // #define SYNC_PRINT(...) NSLog(@ ## __VA_ARGS__)
36     #define SYNC_PRINT(...) fprintf(stderr, __VA_ARGS__); fflush(stderr)
37 #else
38     #define SYNC_PRINT(...)
39 #endif
40
41 // fps debug output
42 //
43 // #define DBG_PERF 1
44
45 // #define DBG_LIFECYCLE 1
46
47 /**
48  * Capture setView(NULL), which produces a 'invalid drawable' message
49  *
50  * Also track lifecycle via DBG_PRINT messages, if VERBOSE is enabled!
51  */
52 @interface MyNSOpenGLContext: NSOpenGLContext
53 {
54 }
55 - (id)initWithFormat:(NSOpenGLPixelFormat *)format shareContext:(NSOpenGLContext *)share;
56 - (void)setView:(NSView *)view;
57 - (void)update;
58 #ifdef DBG_LIFECYCLE
59 - (id)retain;
60 - (oneway void)release;
61 #endif
62 - (void)dealloc;
63
64 @end
65
66 @implementation MyNSOpenGLContext
67
68 - (id)initWithFormat:(NSOpenGLPixelFormat *)format shareContext:(NSOpenGLContext *)share
69 {
70     DBG_PRINT("MyNSOpenGLContext::initWithFormat.0: format %p, share %p\n", format, share);
71     MyNSOpenGLContext * o = [super initWithFormat:format shareContext:share];
72     DBG_PRINT("MyNSOpenGLContext::initWithFormat.X: new %p\n", o);
73     return o;
74 }
75
76 - (void)setView:(NSView *)view
77 {
78     DBG_PRINT("MyNSOpenGLContext::setView: this.0 %p, view %p\n", self, view);
79     // NSLog(@"MyNSOpenGLContext::setView: %@",[NSThread callStackSymbols]);
80     if(NULL != view) {
81         [super setView:view];
82     } else {
83         [self clearDrawable];
84     }
85     DBG_PRINT("MyNSOpenGLContext::setView.X\n");
86 }
87
88 - (void)update
89 {
90     DBG_PRINT("MyNSOpenGLContext::update: this.0 %p, view %p\n", self, [self view]);
91     [super update];
92     DBG_PRINT("MyNSOpenGLContext::update.X\n");
93 }
94
95 #ifdef DBG_LIFECYCLE
96
97 - (id)retain
98 {
99     DBG_PRINT("MyNSOpenGLContext::retain.0: %p (refcnt %d)\n", self, (int)[self retainCount]);
100     // NSLog(@"MyNSOpenGLContext::retain: %@",[NSThread callStackSymbols]);
101     id o = [super retain];
102     DBG_PRINT("MyNSOpenGLContext::retain.X: %p (refcnt %d)\n", o, (int)[o retainCount]);
103     return o;
104 }
105
106 - (oneway void)release
107 {
108     DBG_PRINT("MyNSOpenGLContext::release.0: %p (refcnt %d)\n", self, (int)[self retainCount]);
109     [super release];
110     // DBG_PRINT("MyNSOpenGLContext::release.X: %p (refcnt %d)\n", self, (int)[self retainCount]);
111 }
112
113 #endif
114
115 #ifdef VERBOSE_ON
116     #define CGLRETAINCOUNT(c) (NULL!=c?(int)CGLGetContextRetainCount(c):-1)
117 #else
118     #define CGLRETAINCOUNT(c)
119 #endif
120
121 - (void)dealloc
122 {
123     /**
124      * The explicit CGLContext destruction below
125      * ensures that it is not left behind.
126      * Tests show that w/o these gymnastics, the CGLContext is not freed immediately after calling dealloc.
127      * The retain, release and dealloc ensures [super dealloc] won't message 'invalid context'.
128      */
129     CGLContextObj cglCtx = [self CGLContextObj];
130
131     DBG_PRINT("MyNSOpenGLContext::dealloc.0 %p (refcnt %d) - CGL-Ctx %p\n", self, (int)[self retainCount], cglCtx, CGLRETAINCOUNT(cglCtx));
132     // NSLog(@"MyNSOpenGLContext::dealloc: %@",[NSThread callStackSymbols]);
133
134     [self clearDrawable];
135     DBG_PRINT("MyNSOpenGLContext::dealloc.1 %d\n", CGLRETAINCOUNT(cglCtx));
136     if( NULL != cglCtx ) {
137         CGLRetainContext( cglCtx );
138         DBG_PRINT("MyNSOpenGLContext::dealloc.2 %d\n", CGLRETAINCOUNT(cglCtx));
139     }
140     [super dealloc];
141     DBG_PRINT("MyNSOpenGLContext::dealloc.3 %d\n", CGLRETAINCOUNT(cglCtx));
142     if( NULL != cglCtx ) {
143         CGLReleaseContext( cglCtx );
144         DBG_PRINT("MyNSOpenGLContext::dealloc.4 %d\n", CGLRETAINCOUNT(cglCtx));
145         CGLDestroyContext( cglCtx );
146         DBG_PRINT("MyNSOpenGLContext::dealloc.5 %d\n", CGLRETAINCOUNT(cglCtx));
147     }
148     DBG_PRINT("MyNSOpenGLContext::dealloc.X\n");
149 }
150
151 @end
152
153 @interface MyNSOpenGLLayer: NSOpenGLLayer <NWDedicatedFrame>
154 {
155 @private
156     GLfloat gl_texCoords[8];
157     NSOpenGLContext* glContext;
158     Bool isGLEnabled;
159
160 @protected
161     GLuint gl3ShaderProgramName;
162     GLuint vboBufVert;
163     GLuint vboBufTexCoord;
164     GLint vertAttrLoc;
165     GLint texCoordAttrLoc;
166     NSOpenGLPixelFormat* parentPixelFmt;
167     int texWidth;
168     int texHeight;
169     volatile Bool dedicatedFrameSet;
170     volatile CGRect dedicatedFrame;
171     volatile NSOpenGLPixelBuffer* pbuffer;
172     volatile GLuint textureID;
173     volatile NSOpenGLPixelBuffer* newPBuffer;
174 #ifdef HAS_CADisplayLink
175     CADisplayLink* displayLink;
176 #else
177     CVDisplayLinkRef displayLink;
178 #endif
179     int tc;
180     struct timespec tStart;
181 @public
182     struct timespec lastWaitTime;
183     GLint swapInterval;
184     GLint swapIntervalCounter;
185     pthread_mutex_t renderLock;
186     pthread_cond_t renderSignal;
187     volatile Bool shallDraw;
188 }
189
190 - (id) setupWithContext: (NSOpenGLContext*) parentCtx
191        gl3ShaderProgramName: (GLuint) gl3ShaderProgramName
192        pixelFormat: (NSOpenGLPixelFormat*) pfmt
193        pbuffer: (NSOpenGLPixelBuffer*) p
194        texIDArg: (GLuint) texID
195        opaque: (Bool) opaque
196        texWidth: (int) texWidth 
197        texHeight: (int) texHeight
198        winWidth: (int)winWidth 
199        winHeight: (int)winHeight;
200
201 - (void)releaseLayer;
202 - (void)deallocPBuffer;
203 - (void)disableAnimation;
204 - (void)pauseAnimation:(Bool)pause;
205 - (void)setSwapInterval:(int)interval;
206 - (void)tick;
207 - (void)waitUntilRenderSignal: (long) to_micros;
208 - (Bool)isGLSourceValid;
209
210 - (void) setGLEnabled: (Bool) enable;
211 - (Bool) validateTexSize: (int)newTexWidth height:(int)newTexHeight;
212 - (void) setTextureID: (int) _texID;
213
214 - (Bool) isSamePBuffer: (NSOpenGLPixelBuffer*) p;
215 - (void) setNewPBuffer: (NSOpenGLPixelBuffer*)p;
216 - (void) applyNewPBuffer;
217
218 - (void)setDedicatedFrame:(CGRect)frame quirks:(int)quirks; // @NWDedicatedFrame
219 - (void) setFrame:(CGRect) frame;
220 - (id<CAAction>)actionForKey:(NSString *)key ;
221 - (NSOpenGLPixelFormat *)openGLPixelFormatForDisplayMask:(uint32_t)mask;
222 - (NSOpenGLContext *)openGLContextForPixelFormat:(NSOpenGLPixelFormat *)pixelFormat;
223 - (BOOL)canDrawInOpenGLContext:(NSOpenGLContext *)context pixelFormat:(NSOpenGLPixelFormat *)pixelFormat
224         forLayerTime:(CFTimeInterval)timeInterval displayTime:(const CVTimeStamp *)timeStamp;
225 - (void)drawInOpenGLContext:(NSOpenGLContext *)context pixelFormat:(NSOpenGLPixelFormat *)pixelFormat
226         forLayerTime:(CFTimeInterval)timeInterval displayTime:(const CVTimeStamp *)timeStamp;
227
228 #ifdef DBG_LIFECYCLE
229 - (id)retain;
230 - (oneway void)release;
231 #endif
232 - (void)dealloc;
233
234 @end
235
236 #ifndef HAS_CADisplayLink
237
238 static CVReturn renderMyNSOpenGLLayer(CVDisplayLinkRef displayLink, 
239                                       const CVTimeStamp *inNow, 
240                                       const CVTimeStamp *inOutputTime, 
241                                       CVOptionFlags flagsIn, 
242                                       CVOptionFlags *flagsOut, 
243                                       void *displayLinkContext)
244 {
245     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
246     MyNSOpenGLLayer* l = (MyNSOpenGLLayer*)displayLinkContext;
247     pthread_mutex_lock(&l->renderLock);
248     if( 0 < l->swapInterval ) {
249         l->swapIntervalCounter++;
250         if( l->swapIntervalCounter >= l->swapInterval ) {
251             SYNC_PRINT("<S %d/%d>", (int)l->swapIntervalCounter, l->swapInterval);
252             l->swapIntervalCounter = 0;
253             pthread_cond_signal(&l->renderSignal); // wake up vsync
254         }
255     }
256     pthread_mutex_unlock(&l->renderLock);
257     [pool release];
258     return kCVReturnSuccess;
259 }
260
261 #endif
262
263 static const GLfloat gl_verts[] = {
264     -1.0, -1.0,
265     -1.0,  1.0,
266      1.0,  1.0,
267      1.0, -1.0
268 };
269
270 @implementation MyNSOpenGLLayer
271
272 - (id) setupWithContext: (NSOpenGLContext*) parentCtx
273        gl3ShaderProgramName: (GLuint) _gl3ShaderProgramName
274        pixelFormat: (NSOpenGLPixelFormat*) _parentPixelFmt
275        pbuffer: (NSOpenGLPixelBuffer*) p
276        texIDArg: (GLuint) texID
277        opaque: (Bool) opaque
278        texWidth: (int) _texWidth 
279        texHeight: (int) _texHeight
280        winWidth: (int) _winWidth 
281        winHeight: (int) _winHeight
282 {
283     pthread_mutexattr_t renderLockAttr;
284     pthread_mutexattr_init(&renderLockAttr);
285     pthread_mutexattr_settype(&renderLockAttr, PTHREAD_MUTEX_RECURSIVE);
286     pthread_mutex_init(&renderLock, &renderLockAttr); // recursive
287     pthread_cond_init(&renderSignal, NULL); // no attribute
288
289     {
290         int i;
291         for(i=0; i<8; i++) {
292             gl_texCoords[i] = 0.0f;
293         }
294     }
295     /** 
296      * Set via 
297      *   - OSXUtil_SetCALayerPixelScale0
298      *   - OSXUtil_AddCASublayer0 
299 NS_DURING
300     // Available >= 10.7
301     [self setContentsScale: (CGFloat)_texWidth/(CGFloat)_winWidth];
302 NS_HANDLER
303 NS_ENDHANDLER
304     */
305
306     parentPixelFmt = [_parentPixelFmt retain]; // until destruction
307     glContext = [[MyNSOpenGLContext alloc] initWithFormat:parentPixelFmt shareContext:parentCtx];
308     gl3ShaderProgramName = _gl3ShaderProgramName;
309     vboBufVert = 0;
310     vboBufTexCoord = 0;
311     vertAttrLoc = 0;
312     texCoordAttrLoc = 0;
313     swapInterval = 1; // defaults to on (as w/ new GL profiles)
314     swapIntervalCounter = 0;
315     timespec_now(&lastWaitTime);
316     shallDraw = NO;
317     isGLEnabled = YES;
318     dedicatedFrameSet = NO;
319     dedicatedFrame = CGRectMake(0, 0, _winWidth, _winHeight);
320     [self validateTexSize: _texWidth height:_texHeight];
321     [self setTextureID: texID];
322
323     newPBuffer = NULL;
324     pbuffer = p;
325     if(NULL != pbuffer) {
326         [pbuffer retain];
327     }
328
329     {
330         // no animations for add/remove/swap sublayers etc 
331         // doesn't work: [self removeAnimationForKey: kCAOnOrderIn, kCAOnOrderOut, kCATransition]
332         [self removeAllAnimations];
333     }
334
335     // instantiate a deactivated displayLink
336 #ifdef HAS_CADisplayLink
337     displayLink = [[CVDisplayLink displayLinkWithTarget:self selector:@selector(setNeedsDisplay)] retain];
338 #else
339     CVReturn cvres;
340     {
341         int allDisplaysMask = 0;
342         int virtualScreen, accelerated, displayMask;
343         for (virtualScreen = 0; virtualScreen < [parentPixelFmt  numberOfVirtualScreens]; virtualScreen++) {
344             [parentPixelFmt getValues:&displayMask forAttribute:NSOpenGLPFAScreenMask forVirtualScreen:virtualScreen];
345             [parentPixelFmt getValues:&accelerated forAttribute:NSOpenGLPFAAccelerated forVirtualScreen:virtualScreen];
346             if (accelerated) {
347                 allDisplaysMask |= displayMask;
348             }
349         }
350         cvres = CVDisplayLinkCreateWithOpenGLDisplayMask(allDisplaysMask, &displayLink);
351         if(kCVReturnSuccess != cvres) {
352             DBG_PRINT("MyNSOpenGLLayer::init %p, CVDisplayLinkCreateWithOpenGLDisplayMask %X failed: %d\n", self, allDisplaysMask, cvres);
353             displayLink = NULL;
354         }
355     }
356     if(NULL != displayLink) {
357         CVReturn cvres;
358         DBG_PRINT("MyNSOpenGLLayer::openGLContextForPixelFormat.1: setup DisplayLink %p\n", displayLink);
359         cvres = CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(displayLink, [glContext CGLContextObj], [parentPixelFmt CGLPixelFormatObj]);
360         if(kCVReturnSuccess != cvres) {
361             DBG_PRINT("MyNSOpenGLLayer::init %p, CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext failed: %d\n", self, cvres);
362         }
363     }
364     if(NULL != displayLink) {
365         cvres = CVDisplayLinkSetOutputCallback(displayLink, renderMyNSOpenGLLayer, self);
366         if(kCVReturnSuccess != cvres) {
367             DBG_PRINT("MyNSOpenGLLayer::init %p, CVDisplayLinkSetOutputCallback failed: %d\n", self, cvres);
368             displayLink = NULL;
369         }
370     }
371 #endif
372     [self pauseAnimation: YES];
373
374     [self removeAllAnimations];
375     [self setAutoresizingMask: (kCALayerWidthSizable|kCALayerHeightSizable)];
376     [self setNeedsDisplayOnBoundsChange: YES];
377
378     [self setOpaque: opaque ? YES : NO];
379
380 #ifdef VERBOSE_ON
381     CGRect lRect = [self bounds];
382     if(NULL != pbuffer) {
383         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", 
384             self, parentCtx, parentPixelFmt, pbuffer, glContext, opaque, [pbuffer pixelsWide], [pbuffer pixelsHigh], texWidth, texHeight,
385             lRect.origin.x, lRect.origin.y, lRect.size.width, lRect.size.height, displayLink, (int)[self retainCount]);
386     } else {
387         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", 
388             self, parentCtx, parentPixelFmt, glContext, opaque, (int)textureID, texWidth, texHeight,
389             lRect.origin.x, lRect.origin.y, lRect.size.width, lRect.size.height, displayLink, (int)[self retainCount]);
390     }
391 #endif
392     return self;
393 }
394
395 - (void) setGLEnabled: (Bool) enable
396 {
397     DBG_PRINT("MyNSOpenGLLayer::setGLEnabled: %p, %d -> %d\n", self, (int)isGLEnabled, (int)enable);
398     isGLEnabled = enable;
399 }
400
401 - (Bool) validateTexSize: (int)newTexWidth height:(int)newTexHeight
402 {
403     Bool changed;
404
405     if( newTexHeight != texHeight || newTexWidth != texWidth ) {
406         #ifdef VERBOSE_ON
407         const int oldTexWidth = texWidth;
408         const int oldTexHeight = texHeight;
409         #endif
410         texWidth = newTexWidth;
411         texHeight = newTexHeight;
412         changed = YES;
413
414         GLfloat texCoordWidth, texCoordHeight;
415         if(NULL != pbuffer) {
416             GLenum textureTarget = [pbuffer textureTarget] ;
417             GLsizei pwidth = [pbuffer pixelsWide];
418             GLsizei pheight = [pbuffer pixelsHigh];
419             if( GL_TEXTURE_2D == textureTarget ) {
420                 texCoordWidth  = (GLfloat)pwidth /(GLfloat)texWidth  ;
421                 texCoordHeight = (GLfloat)pheight/(GLfloat)texHeight ;
422             } else {
423                 texCoordWidth  = pwidth;
424                 texCoordHeight = pheight;
425             }
426         } else {
427             texCoordWidth  = (GLfloat)1.0f;
428             texCoordHeight = (GLfloat)1.0f;
429         }
430         gl_texCoords[3] = texCoordHeight;
431         gl_texCoords[5] = texCoordHeight;
432         gl_texCoords[4] = texCoordWidth;
433         gl_texCoords[6] = texCoordWidth;
434         #ifdef VERBOSE_ON
435 NS_DURING
436         // Available >= 10.7
437         DBG_PRINT("MyNSOpenGLLayer::validateTexSize %p: tex %dx%d -> %dx%d, dedicatedFrame set:%d %lf/%lf %lfx%lf scale %lf\n", 
438             self, oldTexWidth, oldTexHeight, newTexWidth, newTexHeight, 
439             dedicatedFrameSet, dedicatedFrame.origin.x, dedicatedFrame.origin.y, dedicatedFrame.size.width, dedicatedFrame.size.height, 
440             [self contentsScale]);
441 NS_HANDLER
442 NS_ENDHANDLER
443         #endif
444     } else {
445         changed = NO;
446     }
447     return changed;
448 }
449
450 - (void) setTextureID: (int) _texID
451 {
452     textureID = _texID;
453 }
454
455 - (Bool) isSamePBuffer: (NSOpenGLPixelBuffer*) p
456 {
457     return pbuffer == p || newPBuffer == p;
458 }
459
460 - (void)setNewPBuffer: (NSOpenGLPixelBuffer*)p
461 {
462     SYNC_PRINT("<NP-S %p -> %p>", pbuffer, p);
463     newPBuffer = p;
464     [newPBuffer retain];
465 }
466
467 - (void) applyNewPBuffer
468 {
469     if( NULL != newPBuffer ) { // volatile OK
470         SYNC_PRINT("<NP-A %p -> %p>", pbuffer, newPBuffer);
471
472         if( 0 != textureID ) {
473             glDeleteTextures(1, (GLuint *)&textureID);
474             [self setTextureID: 0];
475         }
476         [pbuffer release];
477
478         pbuffer = newPBuffer;
479         newPBuffer = NULL;
480     }
481 }
482
483 - (void)deallocPBuffer
484 {
485     if(NULL != pbuffer) {
486         NSOpenGLContext* context = [self openGLContext];
487         if(NULL!=context) {
488             [context makeCurrentContext];
489
490             DBG_PRINT("MyNSOpenGLLayer::deallocPBuffer (with ctx) %p (refcnt %d) - context %p, pbuffer %p, texID %d\n", self, (int)[self retainCount], context, pbuffer, (int)textureID);
491
492             if( 0 != textureID ) {
493                 glDeleteTextures(1, (GLuint *)&textureID);
494                 [self setTextureID: 0];
495             }
496             if(NULL != pbuffer) {
497                 [pbuffer release];
498                 pbuffer = NULL;
499             }
500             if(NULL != newPBuffer) {
501                 [newPBuffer release];
502                 newPBuffer = NULL;
503             }
504
505             [context clearDrawable];
506         } else {
507             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);
508         }
509     }
510 }
511
512 - (void)disableAnimation
513 {
514     DBG_PRINT("MyNSOpenGLLayer::disableAnimation.0: %p (refcnt %d) - displayLink %p\n", self, (int)[self retainCount], displayLink);
515     [self setAsynchronous: NO];
516     if(NULL != displayLink) {
517 #ifdef HAS_CADisplayLink
518         [displayLink setPaused: YES];
519         [displayLink release];
520 #else
521         CVDisplayLinkStop(displayLink);
522         CVDisplayLinkRelease(displayLink);
523 #endif
524         displayLink = NULL;
525     }
526     DBG_PRINT("MyNSOpenGLLayer::disableAnimation.X: %p (refcnt %d) - displayLink %p\n", self, (int)[self retainCount], displayLink);
527 }
528
529 - (void)releaseLayer
530 {
531     DBG_PRINT("MyNSOpenGLLayer::releaseLayer.0: %p (refcnt %d)\n", self, (int)[self retainCount]);
532     [self setGLEnabled: NO];
533     [self disableAnimation];
534     pthread_mutex_lock(&renderLock);
535     [self deallocPBuffer];
536     if( NULL != glContext ) {
537         [glContext release];
538         glContext = NULL;
539     }
540     if( NULL != parentPixelFmt ) {
541         [parentPixelFmt release];
542         parentPixelFmt = NULL;
543     }
544     pthread_mutex_unlock(&renderLock);
545     [self release];
546     DBG_PRINT("MyNSOpenGLLayer::releaseLayer.X: %p\n", self);
547 }
548
549 #ifdef DBG_LIFECYCLE
550
551 - (id)retain
552 {
553     DBG_PRINT("MyNSOpenGLLayer::retain.0: %p (refcnt %d)\n", self, (int)[self retainCount]);
554     // NSLog(@"MyNSOpenGLLayer::retain: %@",[NSThread callStackSymbols]);
555     id o = [super retain];
556     DBG_PRINT("MyNSOpenGLLayer::retain.X: %p (refcnt %d)\n", o, (int)[o retainCount]);
557     return o;
558 }
559
560 - (oneway void)release
561 {
562     DBG_PRINT("MyNSOpenGLLayer::release.0: %p (refcnt %d)\n", self, (int)[self retainCount]);
563     // NSLog(@"MyNSOpenGLLayer::release: %@",[NSThread callStackSymbols]);
564     [super release];
565     // DBG_PRINT("MyNSOpenGLLayer::release.X: %p (refcnt %d)\n", self, (int)[self retainCount]);
566 }
567
568 #endif
569
570 - (void)dealloc
571 {
572     DBG_PRINT("MyNSOpenGLLayer::dealloc.0 %p (refcnt %d)\n", self, (int)[self retainCount]);
573     // NSLog(@"MyNSOpenGLLayer::dealloc: %@",[NSThread callStackSymbols]);
574     [self disableAnimation];
575     pthread_mutex_lock(&renderLock);
576     [self deallocPBuffer];
577     pthread_mutex_unlock(&renderLock);
578     pthread_cond_destroy(&renderSignal);
579     pthread_mutex_destroy(&renderLock);
580     [super dealloc];
581     // DBG_PRINT("MyNSOpenGLLayer::dealloc.X %p\n", self);
582 }
583
584 - (Bool)isGLSourceValid
585 {
586     return NULL != pbuffer || NULL != newPBuffer || 0 != textureID ;
587 }
588
589 // @NWDedicatedFrame
590 - (void)setDedicatedFrame:(CGRect)dFrame quirks:(int)quirks {
591     CGRect lRect = [self frame];
592     Bool dedicatedFramePosSet  = 0 != ( NW_DEDICATEDFRAME_QUIRK_POSITION & quirks );
593     Bool dedicatedFrameSizeSet = 0 != ( NW_DEDICATEDFRAME_QUIRK_SIZE & quirks );
594     Bool dedicatedLayoutSet = 0 != ( NW_DEDICATEDFRAME_QUIRK_LAYOUT & quirks );
595     dedicatedFrameSet  = dedicatedFramePosSet || dedicatedFrameSizeSet || dedicatedLayoutSet;
596     dedicatedFrame = dFrame;
597
598     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",
599         quirks, dedicatedFramePosSet, dedicatedFrameSizeSet, dedicatedLayoutSet, self, texWidth, texHeight,
600         lRect.origin.x, lRect.origin.y, lRect.size.width, lRect.size.height,
601         dFrame.origin.x, dFrame.origin.y, dFrame.size.width, dFrame.size.height);
602     (void)lRect; // silence
603     
604     if( dedicatedFrameSet ) {
605         [super setFrame: dedicatedFrame];
606     }
607 }
608
609 - (void) setFrame:(CGRect) frame {
610     if( dedicatedFrameSet ) {
611         [super setFrame: dedicatedFrame];
612     } else {
613         [super setFrame: frame];
614     }
615 }
616
617 - (id<CAAction>)actionForKey:(NSString *)key
618 {
619     DBG_PRINT("MyNSOpenGLLayer::actionForKey.0 %p key %s -> NIL\n", self, [key UTF8String]);
620     return nil;
621     // return [super actionForKey: key];
622 }
623
624 - (NSOpenGLPixelFormat *)openGLPixelFormatForDisplayMask:(uint32_t)mask
625 {
626     DBG_PRINT("MyNSOpenGLLayer::openGLPixelFormatForDisplayMask: %p (refcnt %d) - parent-pfmt %p -> new-pfmt %p\n", 
627         self, (int)[self retainCount], parentPixelFmt, parentPixelFmt);
628     // We simply take over ownership of parent PixelFormat until releaseLayer..
629     return parentPixelFmt;
630 }
631
632 - (NSOpenGLContext *)openGLContextForPixelFormat:(NSOpenGLPixelFormat *)pixelFormat
633 {
634     DBG_PRINT("MyNSOpenGLLayer::openGLContextForPixelFormat.0: %p (refcnt %d) - pfmt %p, ctx %p, DisplayLink %p\n",
635         self, (int)[self retainCount], pixelFormat, glContext, displayLink);
636     return glContext;
637 }
638
639 - (BOOL)canDrawInOpenGLContext:(NSOpenGLContext *)context pixelFormat:(NSOpenGLPixelFormat *)pixelFormat 
640         forLayerTime:(CFTimeInterval)timeInterval displayTime:(const CVTimeStamp *)timeStamp
641 {
642     SYNC_PRINT("<? %d, %d>", (int)shallDraw, (int)isGLEnabled);
643     return shallDraw && isGLEnabled;
644 }
645
646 - (void)drawInOpenGLContext:(NSOpenGLContext *)context pixelFormat:(NSOpenGLPixelFormat *)pixelFormat 
647         forLayerTime:(CFTimeInterval)timeInterval displayTime:(const CVTimeStamp *)timeStamp
648 {
649     pthread_mutex_unlock(&renderLock);
650     SYNC_PRINT("<* ");
651     // NSLog(@"MyNSOpenGLLayer::DRAW: %@",[NSThread callStackSymbols]);
652
653     if( isGLEnabled && shallDraw && ( NULL != pbuffer || NULL != newPBuffer || 0 != textureID ) ) {
654         [context makeCurrentContext];
655
656         if( NULL != newPBuffer ) { // volatile OK
657             [self applyNewPBuffer];
658         }
659
660         GLenum textureTarget;
661
662         CGRect texDim = dedicatedFrameSet ? dedicatedFrame : [self bounds];
663         CGFloat _contentsScale = 1;
664 NS_DURING
665         // Available >= 10.7
666         _contentsScale = [self contentsScale];
667 NS_HANDLER
668 NS_ENDHANDLER
669         Bool texSizeChanged = [self validateTexSize: (int)(texDim.size.width  * _contentsScale  + 0.5f) 
670                                               height:(int)(texDim.size.height * _contentsScale  + 0.5f)];
671         if( texSizeChanged ) {
672             [context update];
673         }
674
675         if( NULL != pbuffer ) {
676             if( texSizeChanged && 0 != textureID ) {
677                 glDeleteTextures(1, (GLuint *)&textureID);
678                 [self setTextureID: 0];
679             }
680             textureTarget = [pbuffer textureTarget];
681             if( 0 != gl3ShaderProgramName ) {
682                 glUseProgram(gl3ShaderProgramName);
683                 glActiveTexture(GL_TEXTURE0);
684             }
685             if( 0 == textureID ) {
686                 glGenTextures(1, (GLuint *)&textureID);
687                 glBindTexture(textureTarget, textureID);
688                 glTexParameteri(textureTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
689                 glTexParameteri(textureTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
690                 glTexParameteri(textureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
691                 glTexParameteri(textureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
692             } else {
693                 glBindTexture(textureTarget, textureID);
694             }
695             [context setTextureImageToPixelBuffer: (NSOpenGLPixelBuffer*) pbuffer colorBuffer: GL_FRONT];
696         } else {
697             if( 0 != gl3ShaderProgramName ) {
698                 glUseProgram(gl3ShaderProgramName);
699                 glActiveTexture(GL_TEXTURE0);
700             }
701             textureTarget = GL_TEXTURE_2D;
702             glBindTexture(textureTarget, textureID);
703         }
704         SYNC_PRINT(" %d gl3Prog %d/%d*>", (int)textureID, (int)gl3ShaderProgramName, (int)glIsProgram (gl3ShaderProgramName));
705
706         if( 0 == vboBufVert ) { // Once: Init Data and Bind to Pointer
707             if( 0 != gl3ShaderProgramName ) {
708                 // Install default VAO as required by GL 3.2 core!
709                 GLuint vaoBuf = 0;
710                 glGenVertexArrays(1, &vaoBuf);
711                 glBindVertexArray(vaoBuf);
712
713                 // Set texture-unit 0
714                 GLint texUnitLoc = glGetUniformLocation (gl3ShaderProgramName, "mgl_Texture0");
715                 glUniform1i (texUnitLoc, 0);
716             }
717             glGenBuffers( 1, &vboBufVert );
718             glBindBuffer( GL_ARRAY_BUFFER, vboBufVert );
719             glBufferData( GL_ARRAY_BUFFER, 4 * 2 * sizeof(GLfloat), gl_verts, GL_STATIC_DRAW);
720             if( 0 != gl3ShaderProgramName ) {
721                 vertAttrLoc = glGetAttribLocation( gl3ShaderProgramName, "mgl_Vertex" );
722                 glVertexAttribPointer( vertAttrLoc, 2, GL_FLOAT, GL_FALSE, 0, NULL );
723             } else {
724                 glVertexPointer(2, GL_FLOAT, 0, NULL);
725             }
726
727             glGenBuffers( 1, &vboBufTexCoord );
728             glBindBuffer( GL_ARRAY_BUFFER, vboBufTexCoord );
729             glBufferData( GL_ARRAY_BUFFER, 4 * 2 * sizeof(GLfloat), gl_texCoords, GL_STATIC_DRAW);
730             if( 0 != gl3ShaderProgramName ) {
731                 texCoordAttrLoc = glGetAttribLocation( gl3ShaderProgramName, "mgl_MultiTexCoord" );
732                 glVertexAttribPointer( texCoordAttrLoc, 2, GL_FLOAT, GL_FALSE, 0, NULL );
733             } else {
734                 glTexCoordPointer(2, GL_FLOAT, 0, NULL);
735             }
736         }
737         if( texSizeChanged ) {
738             glBindBuffer( GL_ARRAY_BUFFER, vboBufTexCoord );
739             glBufferSubData( GL_ARRAY_BUFFER, 0, 4 * 2 * sizeof(GLfloat), gl_texCoords);
740             if( 0 != gl3ShaderProgramName ) {
741                 glVertexAttribPointer( texCoordAttrLoc, 2, GL_FLOAT, GL_FALSE, 0, NULL );
742             } else {
743                 glTexCoordPointer(2, GL_FLOAT, 0, NULL);
744             }
745         }
746         if( 0 != gl3ShaderProgramName ) {
747             glEnableVertexAttribArray( vertAttrLoc );
748             glEnableVertexAttribArray( texCoordAttrLoc );
749         } else {
750             glEnable(textureTarget);
751            
752             glEnableClientState(GL_VERTEX_ARRAY);
753             glEnableClientState(GL_TEXTURE_COORD_ARRAY);
754         }
755
756         glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
757            
758         if( 0 != gl3ShaderProgramName ) {
759             glDisableVertexAttribArray( vertAttrLoc );
760             glDisableVertexAttribArray( texCoordAttrLoc );
761             glUseProgram(0);
762         } else {
763             glDisableClientState(GL_VERTEX_ARRAY);
764             glDisableClientState(GL_TEXTURE_COORD_ARRAY);
765            
766             glDisable(textureTarget);
767         }
768
769         glBindTexture(textureTarget, 0);
770
771         [context clearDrawable];
772
773         [super drawInOpenGLContext: context pixelFormat: pixelFormat forLayerTime: timeInterval displayTime: timeStamp];
774
775     } else {
776         // glClear(GL_COLOR_BUFFER_BIT);
777         // glBlitFramebuffer(0, 0, texWidth, texHeight, 
778         //                   0, 0, texWidth, texHeight,
779         //                   GL_COLOR_BUFFER_BIT, GL_NEAREST);
780         SYNC_PRINT(" 0*>");
781     }
782
783     #ifdef DBG_PERF
784         [self tick];
785     #endif
786     shallDraw = NO;
787
788     if( 0 >= swapInterval ) {
789         pthread_cond_signal(&renderSignal); // wake up !vsync
790         SYNC_PRINT("<s>");
791     }
792     SYNC_PRINT("<$>\n");
793     pthread_mutex_unlock(&renderLock);
794 }
795
796 - (void)pauseAnimation:(Bool)pause
797 {
798     DBG_PRINT("MyNSOpenGLLayer::pauseAnimation: %d\n", (int)pause);
799     [self setAsynchronous: NO];
800     if(pause) {
801         if(NULL != displayLink) {
802             #ifdef HAS_CADisplayLink
803                 [displayLink setPaused: YES];
804             #else
805                 CVDisplayLinkStop(displayLink);
806             #endif
807         }
808     } else {
809         if(NULL != displayLink) {
810             #ifdef HAS_CADisplayLink
811                 [displayLink setPaused: NO];
812                 [displayLink setFrameInterval: swapInterval];
813             #else
814                 CVDisplayLinkStart(displayLink);
815             #endif
816         }
817     }
818     tc = 0;
819     timespec_now(&tStart);
820 }
821
822 - (void)setSwapInterval:(int)interval
823 {
824     /**
825      * v-sync doesn't works w/ NSOpenGLLayer's context .. well :(
826      * Using CVDisplayLink .. see setSwapInterval() below.
827      *
828         GLint si;
829         [context getValues: &si forParameter: NSOpenGLCPSwapInterval];
830         if(si != swapInterval) {
831             DBG_PRINT("MyNSOpenGLLayer::drawInOpenGLContext %p setSwapInterval: %d -> %d\n", self, si, swapInterval);
832             [context setValues: &swapInterval forParameter: NSOpenGLCPSwapInterval];
833         }
834     */
835
836     pthread_mutex_lock(&renderLock);
837     DBG_PRINT("MyNSOpenGLLayer::setSwapInterval.0: %d - displayLink %p\n", interval, displayLink);
838     swapInterval = interval;
839     swapIntervalCounter = 0;
840     pthread_mutex_unlock(&renderLock);
841
842     if(0 < swapInterval) {
843         [self pauseAnimation: NO];
844     } else {
845         [self pauseAnimation: YES];
846     }
847     DBG_PRINT("MyNSOpenGLLayer::setSwapInterval.X: %d\n", interval);
848 }
849  
850 -(void)tick
851 {
852     tc++;
853     if(tc%60==0) {
854         struct timespec t1, td;
855         timespec_now(&t1);
856         timespec_subtract(&td, &t1, &tStart);
857         long td_ms = timespec_milliseconds(&td);
858         fprintf(stderr, "NSOpenGLLayer: %ld ms / %d frames, %ld ms / frame, %f fps\n",
859             td_ms, tc, td_ms/tc, (tc * 1000.0) / (float)td_ms );
860         fflush(NULL);
861     }
862 }
863
864 - (void)waitUntilRenderSignal: (long) to_micros
865 {
866     struct timespec t0, to_until;
867     BOOL tooLate;
868     int wr;
869     if( 0 >= to_micros ) {
870         to_micros = 16666 + 1000; // defaults to 1/60s + 1ms
871         NSLog(@"MyNSOpenGLContext::waitUntilRenderSignal: to_micros was zero, using defaults");
872     }
873     pthread_mutex_lock(&renderLock);
874     timespec_now(&t0);
875     to_until = lastWaitTime;
876     timespec_addmicros(&to_until, to_micros);
877     tooLate = timespec_compare(&to_until, &t0) < 0;
878     #ifdef DBG_SYNC
879         struct timespec td_until;
880         timespec_subtract(&td_until, &to_until, &t0);
881         SYNC_PRINT("{W %ld ms, to %ld ms, late %d", to_micros/1000, timespec_milliseconds(&td_until), tooLate);
882     #endif
883     if( 0 < swapInterval ) {
884         if( tooLate ) {
885             // adjust!
886             to_until = t0;
887             timespec_addmicros(&to_until, to_micros);
888         }
889         wr = pthread_cond_timedwait(&renderSignal, &renderLock, &to_until);
890         #ifdef DBG_SYNC
891             struct timespec t1, td, td2;
892             timespec_now(&t1);
893             timespec_subtract(&td, &t1, &t0);
894             timespec_subtract(&td2, &t1, &lastWaitTime);
895             fprintf(stderr, "(%ld) / (%ld) ms", timespec_milliseconds(&td), timespec_milliseconds(&td2));
896         #endif
897     }
898     SYNC_PRINT("-%d-%d}\n", shallDraw, wr);
899     timespec_now(&lastWaitTime);
900     pthread_mutex_unlock(&renderLock);
901 }
902
903 @end
904
905 NSOpenGLLayer* createNSOpenGLLayer(NSOpenGLContext* ctx, int gl3ShaderProgramName, NSOpenGLPixelFormat* fmt, NSOpenGLPixelBuffer* p, uint32_t texID, Bool opaque, int texWidth, int texHeight, int winWidth, int winHeight) {
906   return [[[MyNSOpenGLLayer alloc] init] setupWithContext:ctx gl3ShaderProgramName: (GLuint)gl3ShaderProgramName pixelFormat: fmt pbuffer: p texIDArg: (GLuint)texID
907                                                               opaque: opaque texWidth: texWidth texHeight: texHeight
908                                                               winWidth: winWidth winHeight: winHeight];
909 }
910  
911 void setNSOpenGLLayerEnabled(NSOpenGLLayer* layer, Bool enable) {
912     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
913     MyNSOpenGLLayer* l = (MyNSOpenGLLayer*) layer;
914     [l setGLEnabled: enable];
915     [pool release];
916 }
917
918 void setNSOpenGLLayerSwapInterval(NSOpenGLLayer* layer, int interval) {
919     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
920     MyNSOpenGLLayer* l = (MyNSOpenGLLayer*) layer;
921     [l setSwapInterval: interval];
922     [pool release];
923 }
924
925 void waitUntilNSOpenGLLayerIsReady(NSOpenGLLayer* layer, long to_micros) {
926     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
927     MyNSOpenGLLayer* l = (MyNSOpenGLLayer*) layer;
928     [l waitUntilRenderSignal: to_micros];
929     [pool release];
930 }
931
932 void setNSOpenGLLayerNeedsDisplayFBO(NSOpenGLLayer* layer, uint32_t texID) {
933     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
934     MyNSOpenGLLayer* l = (MyNSOpenGLLayer*) layer;
935     Bool shallDraw;
936
937     // volatile OK
938     [l setTextureID: texID];
939     shallDraw = [l isGLSourceValid];
940     l->shallDraw = shallDraw;
941
942     SYNC_PRINT("<! T %d>", (int)shallDraw);
943     if(shallDraw) {
944         if ( [NSThread isMainThread] == YES ) {
945           [l setNeedsDisplay];
946         } else {
947           // don't wait - using doublebuffering
948           [l performSelectorOnMainThread:@selector(setNeedsDisplay) withObject:nil waitUntilDone:NO];
949         }
950     }
951     // DBG_PRINT("MyNSOpenGLLayer::setNSOpenGLLayerNeedsDisplay %p\n", l);
952     [pool release];
953 }
954
955 void setNSOpenGLLayerNeedsDisplayPBuffer(NSOpenGLLayer* layer, NSOpenGLPixelBuffer* p) {
956     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
957     MyNSOpenGLLayer* l = (MyNSOpenGLLayer*) layer;
958     Bool shallDraw;
959
960     if( NO == [l isSamePBuffer: p] ) {
961         [l setNewPBuffer: p];
962     }
963
964     // volatile OK
965     shallDraw = [l isGLSourceValid];
966     l->shallDraw = shallDraw;
967
968     SYNC_PRINT("<! T %d>", (int)shallDraw);
969     if(shallDraw) {
970         if ( [NSThread isMainThread] == YES ) {
971           [l setNeedsDisplay];
972         } else {
973           // don't wait - using doublebuffering
974           [l performSelectorOnMainThread:@selector(setNeedsDisplay) withObject:nil waitUntilDone:NO];
975         }
976     }
977     // DBG_PRINT("MyNSOpenGLLayer::setNSOpenGLLayerNeedsDisplay %p\n", l);
978     [pool release];
979 }
980
981 void releaseNSOpenGLLayer(NSOpenGLLayer* layer) {
982     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
983     MyNSOpenGLLayer* l = (MyNSOpenGLLayer*) layer;
984
985     [CATransaction begin];
986     [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
987
988     DBG_PRINT("MyNSOpenGLLayer::releaseNSOpenGLLayer.0: %p\n", l);
989     [l releaseLayer];
990     DBG_PRINT("MyNSOpenGLLayer::releaseNSOpenGLLayer.X: %p\n", l);
991
992     [CATransaction commit];
993
994     [pool release];
995 }
996
http://JogAmp.org git info: FAQ, tutorial and man pages.