SDL_cocoaopengl.m (12745B)
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21#include "../../SDL_internal.h" 22 23/* NSOpenGL implementation of SDL OpenGL support */ 24 25#if SDL_VIDEO_OPENGL_CGL 26#include "SDL_cocoavideo.h" 27#include "SDL_cocoaopengl.h" 28 29#include <OpenGL/CGLTypes.h> 30#include <OpenGL/OpenGL.h> 31#include <OpenGL/CGLRenderers.h> 32 33#include "SDL_loadso.h" 34#include "SDL_opengl.h" 35 36#define DEFAULT_OPENGL "/System/Library/Frameworks/OpenGL.framework/Libraries/libGL.dylib" 37 38@implementation SDLOpenGLContext : NSOpenGLContext 39 40- (id)initWithFormat:(NSOpenGLPixelFormat *)format 41 shareContext:(NSOpenGLContext *)share 42{ 43 self = [super initWithFormat:format shareContext:share]; 44 if (self) { 45 SDL_AtomicSet(&self->dirty, 0); 46 self->window = NULL; 47 } 48 return self; 49} 50 51- (void)scheduleUpdate 52{ 53 SDL_AtomicAdd(&self->dirty, 1); 54} 55 56/* This should only be called on the thread on which a user is using the context. */ 57- (void)updateIfNeeded 58{ 59 int value = SDL_AtomicSet(&self->dirty, 0); 60 if (value > 0) { 61 /* We call the real underlying update here, since -[SDLOpenGLContext update] just calls us. */ 62 [super update]; 63 } 64} 65 66/* This should only be called on the thread on which a user is using the context. */ 67- (void)update 68{ 69 /* This ensures that regular 'update' calls clear the atomic dirty flag. */ 70 [self scheduleUpdate]; 71 [self updateIfNeeded]; 72} 73 74/* Updates the drawable for the contexts and manages related state. */ 75- (void)setWindow:(SDL_Window *)newWindow 76{ 77 if (self->window) { 78 SDL_WindowData *oldwindowdata = (SDL_WindowData *)self->window->driverdata; 79 80 /* Make sure to remove us from the old window's context list, or we'll get scheduled updates from it too. */ 81 NSMutableArray *contexts = oldwindowdata->nscontexts; 82 @synchronized (contexts) { 83 [contexts removeObject:self]; 84 } 85 } 86 87 self->window = newWindow; 88 89 if (newWindow) { 90 SDL_WindowData *windowdata = (SDL_WindowData *)newWindow->driverdata; 91 92 /* Now sign up for scheduled updates for the new window. */ 93 NSMutableArray *contexts = windowdata->nscontexts; 94 @synchronized (contexts) { 95 [contexts addObject:self]; 96 } 97 98 if ([self view] != [windowdata->nswindow contentView]) { 99 [self setView:[windowdata->nswindow contentView]]; 100 if (self == [NSOpenGLContext currentContext]) { 101 [self update]; 102 } else { 103 [self scheduleUpdate]; 104 } 105 } 106 } else { 107 [self clearDrawable]; 108 if (self == [NSOpenGLContext currentContext]) { 109 [self update]; 110 } else { 111 [self scheduleUpdate]; 112 } 113 } 114} 115 116@end 117 118 119int 120Cocoa_GL_LoadLibrary(_THIS, const char *path) 121{ 122 /* Load the OpenGL library */ 123 if (path == NULL) { 124 path = SDL_getenv("SDL_OPENGL_LIBRARY"); 125 } 126 if (path == NULL) { 127 path = DEFAULT_OPENGL; 128 } 129 _this->gl_config.dll_handle = SDL_LoadObject(path); 130 if (!_this->gl_config.dll_handle) { 131 return -1; 132 } 133 SDL_strlcpy(_this->gl_config.driver_path, path, 134 SDL_arraysize(_this->gl_config.driver_path)); 135 return 0; 136} 137 138void * 139Cocoa_GL_GetProcAddress(_THIS, const char *proc) 140{ 141 return SDL_LoadFunction(_this->gl_config.dll_handle, proc); 142} 143 144void 145Cocoa_GL_UnloadLibrary(_THIS) 146{ 147 SDL_UnloadObject(_this->gl_config.dll_handle); 148 _this->gl_config.dll_handle = NULL; 149} 150 151SDL_GLContext 152Cocoa_GL_CreateContext(_THIS, SDL_Window * window) 153{ 154 NSAutoreleasePool *pool; 155 SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window); 156 SDL_DisplayData *displaydata = (SDL_DisplayData *)display->driverdata; 157 SDL_bool lion_or_later = floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6; 158 NSOpenGLPixelFormatAttribute attr[32]; 159 NSOpenGLPixelFormat *fmt; 160 SDLOpenGLContext *context; 161 NSOpenGLContext *share_context = nil; 162 int i = 0; 163 const char *glversion; 164 int glversion_major; 165 int glversion_minor; 166 167 if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) { 168 SDL_SetError ("OpenGL ES is not supported on this platform"); 169 return NULL; 170 } 171 if ((_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_CORE) && !lion_or_later) { 172 SDL_SetError ("OpenGL Core Profile is not supported on this platform version"); 173 return NULL; 174 } 175 176 pool = [[NSAutoreleasePool alloc] init]; 177 178 /* specify a profile if we're on Lion (10.7) or later. */ 179 if (lion_or_later) { 180 NSOpenGLPixelFormatAttribute profile = NSOpenGLProfileVersionLegacy; 181 if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_CORE) { 182 profile = NSOpenGLProfileVersion3_2Core; 183 } 184 attr[i++] = NSOpenGLPFAOpenGLProfile; 185 attr[i++] = profile; 186 } 187 188 attr[i++] = NSOpenGLPFAColorSize; 189 attr[i++] = SDL_BYTESPERPIXEL(display->current_mode.format)*8; 190 191 attr[i++] = NSOpenGLPFADepthSize; 192 attr[i++] = _this->gl_config.depth_size; 193 194 if (_this->gl_config.double_buffer) { 195 attr[i++] = NSOpenGLPFADoubleBuffer; 196 } 197 198 if (_this->gl_config.stereo) { 199 attr[i++] = NSOpenGLPFAStereo; 200 } 201 202 if (_this->gl_config.stencil_size) { 203 attr[i++] = NSOpenGLPFAStencilSize; 204 attr[i++] = _this->gl_config.stencil_size; 205 } 206 207 if ((_this->gl_config.accum_red_size + 208 _this->gl_config.accum_green_size + 209 _this->gl_config.accum_blue_size + 210 _this->gl_config.accum_alpha_size) > 0) { 211 attr[i++] = NSOpenGLPFAAccumSize; 212 attr[i++] = _this->gl_config.accum_red_size + _this->gl_config.accum_green_size + _this->gl_config.accum_blue_size + _this->gl_config.accum_alpha_size; 213 } 214 215 if (_this->gl_config.multisamplebuffers) { 216 attr[i++] = NSOpenGLPFASampleBuffers; 217 attr[i++] = _this->gl_config.multisamplebuffers; 218 } 219 220 if (_this->gl_config.multisamplesamples) { 221 attr[i++] = NSOpenGLPFASamples; 222 attr[i++] = _this->gl_config.multisamplesamples; 223 attr[i++] = NSOpenGLPFANoRecovery; 224 } 225 226 if (_this->gl_config.accelerated >= 0) { 227 if (_this->gl_config.accelerated) { 228 attr[i++] = NSOpenGLPFAAccelerated; 229 } else { 230 attr[i++] = NSOpenGLPFARendererID; 231 attr[i++] = kCGLRendererGenericFloatID; 232 } 233 } 234 235 attr[i++] = NSOpenGLPFAScreenMask; 236 attr[i++] = CGDisplayIDToOpenGLDisplayMask(displaydata->display); 237 attr[i] = 0; 238 239 fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr]; 240 if (fmt == nil) { 241 SDL_SetError("Failed creating OpenGL pixel format"); 242 [pool release]; 243 return NULL; 244 } 245 246 if (_this->gl_config.share_with_current_context) { 247 share_context = (NSOpenGLContext*)SDL_GL_GetCurrentContext(); 248 } 249 250 context = [[SDLOpenGLContext alloc] initWithFormat:fmt shareContext:share_context]; 251 252 [fmt release]; 253 254 if (context == nil) { 255 SDL_SetError("Failed creating OpenGL context"); 256 [pool release]; 257 return NULL; 258 } 259 260 [pool release]; 261 262 if ( Cocoa_GL_MakeCurrent(_this, window, context) < 0 ) { 263 Cocoa_GL_DeleteContext(_this, context); 264 SDL_SetError("Failed making OpenGL context current"); 265 return NULL; 266 } 267 268 if (_this->gl_config.major_version < 3 && 269 _this->gl_config.profile_mask == 0 && 270 _this->gl_config.flags == 0) { 271 /* This is a legacy profile, so to match other backends, we're done. */ 272 } else { 273 const GLubyte *(APIENTRY * glGetStringFunc)(GLenum); 274 275 glGetStringFunc = (const GLubyte *(APIENTRY *)(GLenum)) SDL_GL_GetProcAddress("glGetString"); 276 if (!glGetStringFunc) { 277 Cocoa_GL_DeleteContext(_this, context); 278 SDL_SetError ("Failed getting OpenGL glGetString entry point"); 279 return NULL; 280 } 281 282 glversion = (const char *)glGetStringFunc(GL_VERSION); 283 if (glversion == NULL) { 284 Cocoa_GL_DeleteContext(_this, context); 285 SDL_SetError ("Failed getting OpenGL context version"); 286 return NULL; 287 } 288 289 if (SDL_sscanf(glversion, "%d.%d", &glversion_major, &glversion_minor) != 2) { 290 Cocoa_GL_DeleteContext(_this, context); 291 SDL_SetError ("Failed parsing OpenGL context version"); 292 return NULL; 293 } 294 295 if ((glversion_major < _this->gl_config.major_version) || 296 ((glversion_major == _this->gl_config.major_version) && (glversion_minor < _this->gl_config.minor_version))) { 297 Cocoa_GL_DeleteContext(_this, context); 298 SDL_SetError ("Failed creating OpenGL context at version requested"); 299 return NULL; 300 } 301 302 /* In the future we'll want to do this, but to match other platforms 303 we'll leave the OpenGL version the way it is for now 304 */ 305 /*_this->gl_config.major_version = glversion_major;*/ 306 /*_this->gl_config.minor_version = glversion_minor;*/ 307 } 308 return context; 309} 310 311int 312Cocoa_GL_MakeCurrent(_THIS, SDL_Window * window, SDL_GLContext context) 313{ 314 NSAutoreleasePool *pool; 315 316 pool = [[NSAutoreleasePool alloc] init]; 317 318 if (context) { 319 SDLOpenGLContext *nscontext = (SDLOpenGLContext *)context; 320 [nscontext setWindow:window]; 321 [nscontext updateIfNeeded]; 322 [nscontext makeCurrentContext]; 323 } else { 324 [NSOpenGLContext clearCurrentContext]; 325 } 326 327 [pool release]; 328 return 0; 329} 330 331void 332Cocoa_GL_GetDrawableSize(_THIS, SDL_Window * window, int * w, int * h) 333{ 334 SDL_WindowData *windata = (SDL_WindowData *) window->driverdata; 335 NSView *contentView = [windata->nswindow contentView]; 336 NSRect viewport = [contentView bounds]; 337 338 /* This gives us the correct viewport for a Retina-enabled view, only 339 * supported on 10.7+. */ 340 if ([contentView respondsToSelector:@selector(convertRectToBacking:)]) { 341 viewport = [contentView convertRectToBacking:viewport]; 342 } 343 344 if (w) { 345 *w = viewport.size.width; 346 } 347 348 if (h) { 349 *h = viewport.size.height; 350 } 351} 352 353int 354Cocoa_GL_SetSwapInterval(_THIS, int interval) 355{ 356 NSAutoreleasePool *pool; 357 NSOpenGLContext *nscontext; 358 GLint value; 359 int status; 360 361 if (interval < 0) { /* no extension for this on Mac OS X at the moment. */ 362 return SDL_SetError("Late swap tearing currently unsupported"); 363 } 364 365 pool = [[NSAutoreleasePool alloc] init]; 366 367 nscontext = (NSOpenGLContext*)SDL_GL_GetCurrentContext(); 368 if (nscontext != nil) { 369 value = interval; 370 [nscontext setValues:&value forParameter:NSOpenGLCPSwapInterval]; 371 status = 0; 372 } else { 373 status = SDL_SetError("No current OpenGL context"); 374 } 375 376 [pool release]; 377 return status; 378} 379 380int 381Cocoa_GL_GetSwapInterval(_THIS) 382{ 383 NSAutoreleasePool *pool; 384 NSOpenGLContext *nscontext; 385 GLint value; 386 int status = 0; 387 388 pool = [[NSAutoreleasePool alloc] init]; 389 390 nscontext = (NSOpenGLContext*)SDL_GL_GetCurrentContext(); 391 if (nscontext != nil) { 392 [nscontext getValues:&value forParameter:NSOpenGLCPSwapInterval]; 393 status = (int)value; 394 } 395 396 [pool release]; 397 return status; 398} 399 400void 401Cocoa_GL_SwapWindow(_THIS, SDL_Window * window) 402{ 403 NSAutoreleasePool *pool; 404 405 pool = [[NSAutoreleasePool alloc] init]; 406 407 SDLOpenGLContext* nscontext = (SDLOpenGLContext*)SDL_GL_GetCurrentContext(); 408 [nscontext flushBuffer]; 409 [nscontext updateIfNeeded]; 410 411 [pool release]; 412} 413 414void 415Cocoa_GL_DeleteContext(_THIS, SDL_GLContext context) 416{ 417 NSAutoreleasePool *pool; 418 SDLOpenGLContext *nscontext = (SDLOpenGLContext *)context; 419 420 pool = [[NSAutoreleasePool alloc] init]; 421 422 [nscontext setWindow:NULL]; 423 [nscontext release]; 424 425 [pool release]; 426} 427 428#endif /* SDL_VIDEO_OPENGL_CGL */ 429 430/* vi: set ts=4 sw=4 expandtab: */