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