slouken@1936: /* slouken@5535: Simple DirectMedia Layer slouken@8149: Copyright (C) 1997-2014 Sam Lantinga slouken@1936: slouken@5535: This software is provided 'as-is', without any express or implied slouken@5535: warranty. In no event will the authors be held liable for any damages slouken@5535: arising from the use of this software. slouken@1936: slouken@5535: Permission is granted to anyone to use this software for any purpose, slouken@5535: including commercial applications, and to alter it and redistribute it slouken@5535: freely, subject to the following restrictions: slouken@1936: slouken@5535: 1. The origin of this software must not be misrepresented; you must not slouken@5535: claim that you wrote the original software. If you use this software slouken@5535: in a product, an acknowledgment in the product documentation would be slouken@5535: appreciated but is not required. slouken@5535: 2. Altered source versions must be plainly marked as such, and must not be slouken@5535: misrepresented as being the original software. slouken@5535: 3. This notice may not be removed or altered from any source distribution. slouken@1936: */ icculus@8093: #include "../../SDL_internal.h" slouken@1936: slouken@1936: /* NSOpenGL implementation of SDL OpenGL support */ slouken@1936: slouken@1952: #if SDL_VIDEO_OPENGL_CGL slouken@6044: #include "SDL_cocoavideo.h" jorgen@7594: #include "SDL_cocoaopengl.h" slouken@6044: slouken@1936: #include slouken@2738: #include slouken@3570: #include slouken@1936: slouken@1936: #include "SDL_loadso.h" slouken@1936: #include "SDL_opengl.h" slouken@1936: icculus@6567: #define DEFAULT_OPENGL "/System/Library/Frameworks/OpenGL.framework/Libraries/libGL.dylib" slouken@1936: icculus@8276: #ifndef NSOpenGLPFAOpenGLProfile icculus@8276: #define NSOpenGLPFAOpenGLProfile 99 slouken@6952: #endif icculus@8276: #ifndef NSOpenGLProfileVersionLegacy icculus@8276: #define NSOpenGLProfileVersionLegacy 0x1000 slouken@6952: #endif icculus@8276: #ifndef NSOpenGLProfileVersion3_2Core icculus@8276: #define NSOpenGLProfileVersion3_2Core 0x3200 icculus@6567: #endif icculus@6567: jorgen@7594: @implementation SDLOpenGLContext : NSOpenGLContext jorgen@7594: jorgen@7594: - (id)initWithFormat:(NSOpenGLPixelFormat *)format jorgen@7594: shareContext:(NSOpenGLContext *)share jorgen@7594: { jorgen@7595: self = [super initWithFormat:format shareContext:share]; jorgen@7595: if (self) { jorgen@7595: SDL_AtomicSet(&self->dirty, 0); jorgen@7595: self->window = NULL; jorgen@7595: } jorgen@7595: return self; jorgen@7594: } jorgen@7594: jorgen@7594: - (void)scheduleUpdate jorgen@7594: { jorgen@7594: SDL_AtomicAdd(&self->dirty, 1); jorgen@7594: } jorgen@7594: jorgen@7594: /* This should only be called on the thread on which a user is using the context. */ jorgen@7594: - (void)updateIfNeeded jorgen@7594: { jorgen@7594: int value = SDL_AtomicSet(&self->dirty, 0); jorgen@7594: if (value > 0) { jorgen@7594: /* We call the real underlying update here, since -[SDLOpenGLContext update] just calls us. */ jorgen@7594: [super update]; jorgen@7594: } jorgen@7594: } jorgen@7594: jorgen@7594: /* This should only be called on the thread on which a user is using the context. */ jorgen@7594: - (void)update jorgen@7594: { jorgen@7594: /* This ensures that regular 'update' calls clear the atomic dirty flag. */ jorgen@7594: [self scheduleUpdate]; jorgen@7594: [self updateIfNeeded]; jorgen@7594: } jorgen@7594: jorgen@7595: /* Updates the drawable for the contexts and manages related state. */ jorgen@7595: - (void)setWindow:(SDL_Window *)newWindow jorgen@7595: { jorgen@7595: if (self->window) { jorgen@7595: SDL_WindowData *oldwindowdata = (SDL_WindowData *)self->window->driverdata; jorgen@7595: jorgen@7595: /* Make sure to remove us from the old window's context list, or we'll get scheduled updates from it too. */ jorgen@7595: NSMutableArray *contexts = oldwindowdata->nscontexts; jorgen@7595: @synchronized (contexts) { jorgen@7595: [contexts removeObject:self]; jorgen@7595: } jorgen@7595: } jorgen@7595: jorgen@7595: self->window = newWindow; jorgen@7595: jorgen@7595: if (newWindow) { jorgen@7595: SDL_WindowData *windowdata = (SDL_WindowData *)newWindow->driverdata; jorgen@7595: jorgen@7595: /* Now sign up for scheduled updates for the new window. */ jorgen@7595: NSMutableArray *contexts = windowdata->nscontexts; jorgen@7595: @synchronized (contexts) { jorgen@7595: [contexts addObject:self]; jorgen@7595: } jorgen@7595: jorgen@7595: if ([self view] != [windowdata->nswindow contentView]) { jorgen@7595: [self setView:[windowdata->nswindow contentView]]; jorgen@8258: if (self == [NSOpenGLContext currentContext]) { jorgen@8258: [self update]; jorgen@8258: } else { jorgen@8258: [self scheduleUpdate]; jorgen@8258: } jorgen@7595: } jorgen@7595: } else { jorgen@7595: [self clearDrawable]; jorgen@8258: if (self == [NSOpenGLContext currentContext]) { jorgen@8258: [self update]; jorgen@8258: } else { jorgen@8258: [self scheduleUpdate]; jorgen@8258: } jorgen@7595: } jorgen@7595: } jorgen@7595: jorgen@7594: @end jorgen@7594: slouken@1936: slouken@1936: int slouken@1936: Cocoa_GL_LoadLibrary(_THIS, const char *path) slouken@1936: { slouken@3057: /* Load the OpenGL library */ slouken@1936: if (path == NULL) { slouken@1952: path = SDL_getenv("SDL_OPENGL_LIBRARY"); slouken@1952: } slouken@1952: if (path == NULL) { slouken@1952: path = DEFAULT_OPENGL; slouken@1936: } slouken@1936: _this->gl_config.dll_handle = SDL_LoadObject(path); slouken@1936: if (!_this->gl_config.dll_handle) { slouken@1936: return -1; slouken@1936: } slouken@1936: SDL_strlcpy(_this->gl_config.driver_path, path, slouken@1936: SDL_arraysize(_this->gl_config.driver_path)); slouken@1936: return 0; slouken@1936: } slouken@1936: slouken@1936: void * slouken@1936: Cocoa_GL_GetProcAddress(_THIS, const char *proc) slouken@1936: { slouken@1936: return SDL_LoadFunction(_this->gl_config.dll_handle, proc); slouken@1936: } slouken@1936: slouken@3057: void slouken@1936: Cocoa_GL_UnloadLibrary(_THIS) slouken@1936: { slouken@3057: SDL_UnloadObject(_this->gl_config.dll_handle); slouken@3057: _this->gl_config.dll_handle = NULL; slouken@1936: } slouken@1936: slouken@1936: SDL_GLContext slouken@1936: Cocoa_GL_CreateContext(_THIS, SDL_Window * window) slouken@1936: { icculus@6567: SDL_VideoData *data = (SDL_VideoData *) _this->driverdata; slouken@6848: NSAutoreleasePool *pool; slouken@5246: SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window); slouken@1936: SDL_DisplayData *displaydata = (SDL_DisplayData *)display->driverdata; slouken@1936: NSOpenGLPixelFormatAttribute attr[32]; slouken@1936: NSOpenGLPixelFormat *fmt; jorgen@7594: SDLOpenGLContext *context; flibitijibibo@7152: NSOpenGLContext *share_context = nil; slouken@1936: int i = 0; icculus@8276: const char *glversion; icculus@8276: int glversion_major; icculus@8276: int glversion_minor; slouken@1936: icculus@6567: if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) { icculus@7894: SDL_SetError ("OpenGL ES is not supported on this platform"); icculus@6567: return NULL; icculus@6567: } icculus@8276: if ((_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_CORE) && (data->osversion < 0x1070)) { icculus@8276: SDL_SetError ("OpenGL Core Profile is not supported on this platform version"); icculus@6567: return NULL; icculus@6567: } icculus@6567: slouken@6848: pool = [[NSAutoreleasePool alloc] init]; slouken@6848: slouken@6848: /* specify a profile if we're on Lion (10.7) or later. */ slouken@6848: if (data->osversion >= 0x1070) { icculus@8276: NSOpenGLPixelFormatAttribute profile = NSOpenGLProfileVersionLegacy; slouken@6848: if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_CORE) { icculus@8276: profile = NSOpenGLProfileVersion3_2Core; alexey@6832: } icculus@8276: attr[i++] = NSOpenGLPFAOpenGLProfile; slouken@6848: attr[i++] = profile; slouken@6848: } slouken@1936: slouken@6848: attr[i++] = NSOpenGLPFAColorSize; slouken@6848: attr[i++] = SDL_BYTESPERPIXEL(display->current_mode.format)*8; slouken@6848: slouken@6848: attr[i++] = NSOpenGLPFADepthSize; slouken@6848: attr[i++] = _this->gl_config.depth_size; slouken@6848: slouken@6848: if (_this->gl_config.double_buffer) { slouken@6848: attr[i++] = NSOpenGLPFADoubleBuffer; slouken@6848: } slouken@6848: slouken@6848: if (_this->gl_config.stereo) { slouken@6848: attr[i++] = NSOpenGLPFAStereo; slouken@6848: } slouken@6848: slouken@6848: if (_this->gl_config.stencil_size) { slouken@6848: attr[i++] = NSOpenGLPFAStencilSize; slouken@6848: attr[i++] = _this->gl_config.stencil_size; slouken@6848: } slouken@6848: slouken@6848: if ((_this->gl_config.accum_red_size + slouken@6848: _this->gl_config.accum_green_size + slouken@6848: _this->gl_config.accum_blue_size + slouken@6848: _this->gl_config.accum_alpha_size) > 0) { slouken@6848: attr[i++] = NSOpenGLPFAAccumSize; slouken@6848: 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; slouken@6848: } slouken@6848: slouken@6848: if (_this->gl_config.multisamplebuffers) { slouken@6848: attr[i++] = NSOpenGLPFASampleBuffers; slouken@6848: attr[i++] = _this->gl_config.multisamplebuffers; slouken@6848: } slouken@6848: slouken@6848: if (_this->gl_config.multisamplesamples) { slouken@6848: attr[i++] = NSOpenGLPFASamples; slouken@6848: attr[i++] = _this->gl_config.multisamplesamples; slouken@6848: attr[i++] = NSOpenGLPFANoRecovery; slouken@6848: } slouken@6848: slouken@6848: if (_this->gl_config.accelerated >= 0) { slouken@6848: if (_this->gl_config.accelerated) { slouken@6848: attr[i++] = NSOpenGLPFAAccelerated; slouken@6848: } else { slouken@6848: attr[i++] = NSOpenGLPFARendererID; slouken@6848: attr[i++] = kCGLRendererGenericFloatID; alexey@6832: } slouken@6848: } slouken@6848: slouken@6848: attr[i++] = NSOpenGLPFAScreenMask; slouken@6848: attr[i++] = CGDisplayIDToOpenGLDisplayMask(displaydata->display); slouken@6848: attr[i] = 0; slouken@6848: slouken@6848: fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr]; slouken@6848: if (fmt == nil) { slouken@6848: SDL_SetError ("Failed creating OpenGL pixel format"); slouken@6848: [pool release]; slouken@6848: return NULL; slouken@6848: } slouken@6848: flibitijibibo@7152: if (_this->gl_config.share_with_current_context) { slouken@7412: share_context = (NSOpenGLContext*)SDL_GL_GetCurrentContext(); flibitijibibo@7152: } flibitijibibo@7152: jorgen@7594: context = [[SDLOpenGLContext alloc] initWithFormat:fmt shareContext:share_context]; slouken@6848: slouken@6848: [fmt release]; slouken@6848: slouken@6848: if (context == nil) { slouken@6848: SDL_SetError ("Failed creating OpenGL context"); slouken@6848: [pool release]; slouken@6848: return NULL; slouken@6848: } slouken@6848: slouken@6848: [pool release]; slouken@1936: slouken@2178: if ( Cocoa_GL_MakeCurrent(_this, window, context) < 0 ) { slouken@2178: Cocoa_GL_DeleteContext(_this, context); icculus@8276: SDL_SetError ("Failed making OpenGL context current"); slouken@2178: return NULL; slouken@2178: } slouken@2178: icculus@8276: glversion = (const char *)glGetString(GL_VERSION); icculus@8276: if (glversion == NULL) { icculus@8276: Cocoa_GL_DeleteContext(_this, context); icculus@8276: SDL_SetError ("Failed getting OpenGL context version"); icculus@8276: return NULL; icculus@8276: } icculus@8276: icculus@8276: if (SDL_sscanf(glversion, "%d.%d", &glversion_major, &glversion_minor) != 2) { icculus@8276: Cocoa_GL_DeleteContext(_this, context); icculus@8276: SDL_SetError ("Failed parsing OpenGL context version"); icculus@8276: return NULL; icculus@8276: } icculus@8276: icculus@8276: if ((glversion_major < _this->gl_config.major_version) || icculus@8276: ((glversion_major == _this->gl_config.major_version) && (glversion_minor < _this->gl_config.minor_version))) { icculus@8276: Cocoa_GL_DeleteContext(_this, context); icculus@8276: SDL_SetError ("Failed creating OpenGL context at version requested"); icculus@8276: return NULL; icculus@8276: } icculus@8276: icculus@8276: _this->gl_config.major_version = glversion_major; icculus@8276: _this->gl_config.minor_version = glversion_minor; icculus@8276: slouken@2178: return context; slouken@1936: } slouken@1936: slouken@1936: int slouken@1936: Cocoa_GL_MakeCurrent(_THIS, SDL_Window * window, SDL_GLContext context) slouken@1936: { slouken@6848: NSAutoreleasePool *pool; slouken@1936: slouken@6848: pool = [[NSAutoreleasePool alloc] init]; slouken@6848: slouken@6848: if (context) { jorgen@7594: SDLOpenGLContext *nscontext = (SDLOpenGLContext *)context; jorgen@7595: [nscontext setWindow:window]; jorgen@7594: [nscontext updateIfNeeded]; slouken@6848: [nscontext makeCurrentContext]; slouken@6848: } else { slouken@6848: [NSOpenGLContext clearCurrentContext]; slouken@1936: } slouken@1936: slouken@6848: [pool release]; slouken@1936: return 0; slouken@1936: } slouken@1936: urkle@7746: void urkle@7746: Cocoa_GL_GetDrawableSize(_THIS, SDL_Window * window, int * w, int * h) urkle@7746: { urkle@7746: SDL_WindowData *windata = (SDL_WindowData *) window->driverdata; urkle@7746: NSView *contentView = [windata->nswindow contentView]; urkle@7746: NSRect viewport = [contentView bounds]; urkle@7746: urkle@7746: /* This gives us the correct viewport for a Retina-enabled view, only urkle@7746: * supported on 10.7+. */ urkle@7746: if ([contentView respondsToSelector:@selector(convertRectToBacking:)]) { urkle@7746: viewport = [contentView convertRectToBacking:viewport]; urkle@7746: } urkle@7746: urkle@7746: if (w) { urkle@7746: *w = viewport.size.width; urkle@7746: } urkle@7746: urkle@7746: if (h) { urkle@7746: *h = viewport.size.height; urkle@7746: } urkle@7746: } urkle@7746: slouken@1936: int slouken@1936: Cocoa_GL_SetSwapInterval(_THIS, int interval) slouken@1936: { slouken@6848: NSAutoreleasePool *pool; slouken@1936: NSOpenGLContext *nscontext; slouken@2703: GLint value; slouken@1936: int status; slouken@1936: icculus@7743: if (interval < 0) { /* no extension for this on Mac OS X at the moment. */ icculus@7743: return SDL_SetError("Late swap tearing currently unsupported"); icculus@7743: } icculus@7743: slouken@6848: pool = [[NSAutoreleasePool alloc] init]; slouken@6848: jorgen@7594: nscontext = (NSOpenGLContext*)SDL_GL_GetCurrentContext(); slouken@6848: if (nscontext != nil) { slouken@6848: value = interval; slouken@6848: [nscontext setValues:&value forParameter:NSOpenGLCPSwapInterval]; slouken@6848: status = 0; slouken@6848: } else { icculus@7037: status = SDL_SetError("No current OpenGL context"); slouken@1936: } slouken@1936: slouken@6848: [pool release]; slouken@1936: return status; slouken@1936: } slouken@1936: slouken@1936: int slouken@1936: Cocoa_GL_GetSwapInterval(_THIS) slouken@1936: { slouken@6848: NSAutoreleasePool *pool; slouken@1936: NSOpenGLContext *nscontext; slouken@2703: GLint value; icculus@6382: int status = 0; slouken@1936: slouken@6848: pool = [[NSAutoreleasePool alloc] init]; slouken@6848: jorgen@7594: nscontext = (NSOpenGLContext*)SDL_GL_GetCurrentContext(); slouken@6848: if (nscontext != nil) { slouken@6848: [nscontext getValues:&value forParameter:NSOpenGLCPSwapInterval]; slouken@6848: status = (int)value; slouken@1936: } slouken@1936: slouken@6848: [pool release]; slouken@1936: return status; slouken@1936: } slouken@1936: slouken@1936: void slouken@1936: Cocoa_GL_SwapWindow(_THIS, SDL_Window * window) slouken@1936: { slouken@6848: NSAutoreleasePool *pool; slouken@1936: slouken@6848: pool = [[NSAutoreleasePool alloc] init]; slouken@6848: slouken@7738: SDLOpenGLContext* nscontext = (SDLOpenGLContext*)SDL_GL_GetCurrentContext(); slouken@7408: [nscontext flushBuffer]; jorgen@7594: [nscontext updateIfNeeded]; slouken@6848: slouken@6848: [pool release]; slouken@1936: } slouken@1936: slouken@1936: void slouken@1936: Cocoa_GL_DeleteContext(_THIS, SDL_GLContext context) slouken@1936: { slouken@6848: NSAutoreleasePool *pool; jorgen@7595: SDLOpenGLContext *nscontext = (SDLOpenGLContext *)context; slouken@1936: slouken@6848: pool = [[NSAutoreleasePool alloc] init]; slouken@6848: jorgen@7595: [nscontext setWindow:NULL]; slouken@6848: [nscontext release]; slouken@6848: slouken@6848: [pool release]; slouken@1936: } slouken@1936: slouken@1952: #endif /* SDL_VIDEO_OPENGL_CGL */ slouken@1936: slouken@1936: /* vi: set ts=4 sw=4 expandtab: */