src/video/cocoa/SDL_cocoaopengl.m
author Ryan C. Gordon <icculus@icculus.org>
Sat, 01 Mar 2014 20:46:45 -0500
changeset 8276 9bd65b58278e
parent 8258 569354dec4e9
child 8278 e60a7bb2b80b
permissions -rw-r--r--
Mac OS X: Make OpenGL context selection more robust, avoid software fallback.

Fixes Bugzilla #2197.
     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     glversion = (const char *)glGetString(GL_VERSION);
   279     if (glversion == NULL) {
   280         Cocoa_GL_DeleteContext(_this, context);
   281         SDL_SetError ("Failed getting OpenGL context version");
   282         return NULL;
   283     }
   284 
   285     if (SDL_sscanf(glversion, "%d.%d", &glversion_major, &glversion_minor) != 2) {
   286         Cocoa_GL_DeleteContext(_this, context);
   287         SDL_SetError ("Failed parsing OpenGL context version");
   288         return NULL;
   289     }
   290 
   291     if ((glversion_major <  _this->gl_config.major_version) ||
   292        ((glversion_major == _this->gl_config.major_version) && (glversion_minor < _this->gl_config.minor_version))) {
   293         Cocoa_GL_DeleteContext(_this, context);
   294         SDL_SetError ("Failed creating OpenGL context at version requested");
   295         return NULL;
   296     }
   297 
   298     _this->gl_config.major_version = glversion_major;
   299     _this->gl_config.minor_version = glversion_minor;
   300 
   301     return context;
   302 }
   303 
   304 int
   305 Cocoa_GL_MakeCurrent(_THIS, SDL_Window * window, SDL_GLContext context)
   306 {
   307     NSAutoreleasePool *pool;
   308 
   309     pool = [[NSAutoreleasePool alloc] init];
   310 
   311     if (context) {
   312         SDLOpenGLContext *nscontext = (SDLOpenGLContext *)context;
   313         [nscontext setWindow:window];
   314         [nscontext updateIfNeeded];
   315         [nscontext makeCurrentContext];
   316     } else {
   317         [NSOpenGLContext clearCurrentContext];
   318     }
   319 
   320     [pool release];
   321     return 0;
   322 }
   323 
   324 void
   325 Cocoa_GL_GetDrawableSize(_THIS, SDL_Window * window, int * w, int * h)
   326 {
   327     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
   328     NSView *contentView = [windata->nswindow contentView];
   329     NSRect viewport = [contentView bounds];
   330 
   331     /* This gives us the correct viewport for a Retina-enabled view, only
   332      * supported on 10.7+. */
   333     if ([contentView respondsToSelector:@selector(convertRectToBacking:)]) {
   334         viewport = [contentView convertRectToBacking:viewport];
   335     }
   336 
   337     if (w) {
   338         *w = viewport.size.width;
   339     }
   340 
   341     if (h) {
   342         *h = viewport.size.height;
   343     }
   344 }
   345 
   346 int
   347 Cocoa_GL_SetSwapInterval(_THIS, int interval)
   348 {
   349     NSAutoreleasePool *pool;
   350     NSOpenGLContext *nscontext;
   351     GLint value;
   352     int status;
   353 
   354     if (interval < 0) {  /* no extension for this on Mac OS X at the moment. */
   355         return SDL_SetError("Late swap tearing currently unsupported");
   356     }
   357 
   358     pool = [[NSAutoreleasePool alloc] init];
   359 
   360     nscontext = (NSOpenGLContext*)SDL_GL_GetCurrentContext();
   361     if (nscontext != nil) {
   362         value = interval;
   363         [nscontext setValues:&value forParameter:NSOpenGLCPSwapInterval];
   364         status = 0;
   365     } else {
   366         status = SDL_SetError("No current OpenGL context");
   367     }
   368 
   369     [pool release];
   370     return status;
   371 }
   372 
   373 int
   374 Cocoa_GL_GetSwapInterval(_THIS)
   375 {
   376     NSAutoreleasePool *pool;
   377     NSOpenGLContext *nscontext;
   378     GLint value;
   379     int status = 0;
   380 
   381     pool = [[NSAutoreleasePool alloc] init];
   382 
   383     nscontext = (NSOpenGLContext*)SDL_GL_GetCurrentContext();
   384     if (nscontext != nil) {
   385         [nscontext getValues:&value forParameter:NSOpenGLCPSwapInterval];
   386         status = (int)value;
   387     }
   388 
   389     [pool release];
   390     return status;
   391 }
   392 
   393 void
   394 Cocoa_GL_SwapWindow(_THIS, SDL_Window * window)
   395 {
   396     NSAutoreleasePool *pool;
   397 
   398     pool = [[NSAutoreleasePool alloc] init];
   399 
   400     SDLOpenGLContext* nscontext = (SDLOpenGLContext*)SDL_GL_GetCurrentContext();
   401     [nscontext flushBuffer];
   402     [nscontext updateIfNeeded];
   403 
   404     [pool release];
   405 }
   406 
   407 void
   408 Cocoa_GL_DeleteContext(_THIS, SDL_GLContext context)
   409 {
   410     NSAutoreleasePool *pool;
   411     SDLOpenGLContext *nscontext = (SDLOpenGLContext *)context;
   412 
   413     pool = [[NSAutoreleasePool alloc] init];
   414 
   415     [nscontext setWindow:NULL];
   416     [nscontext release];
   417 
   418     [pool release];
   419 }
   420 
   421 #endif /* SDL_VIDEO_OPENGL_CGL */
   422 
   423 /* vi: set ts=4 sw=4 expandtab: */