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