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