src/video/cocoa/SDL_cocoaopengl.m
author Jørgen P. Tjernø <jorgen@valvesoftware.com>
Wed, 07 Aug 2013 16:29:21 -0700
changeset 7594 6abcf951af68
parent 7412 50211a1fd557
child 7595 ede2237fcebf
permissions -rw-r--r--
Mac: Don't -[NSOpenGLContext update] on (potentially) the wrong thread.

If the user is using their context from a non-main thread, we could be
calling -[NSOpenGLContext update] on our thread, while they were
accessing it on their thread.

With this change, we schedule updates when the event comes in on the
main thread, and act on them when the user calls SDL_GL_MakeCurrent or
SDL_GL_SwapWindow.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2013 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_config.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 
    39 #ifndef kCGLPFAOpenGLProfile
    40 #define kCGLPFAOpenGLProfile 99
    41 #endif
    42 #ifndef kCGLOGLPVersion_Legacy
    43 #define kCGLOGLPVersion_Legacy 0x1000
    44 #endif
    45 #ifndef kCGLOGLPVersion_3_2_Core
    46 #define kCGLOGLPVersion_3_2_Core 0x3200
    47 #endif
    48 
    49 @implementation SDLOpenGLContext : NSOpenGLContext
    50 
    51 - (id)initWithFormat:(NSOpenGLPixelFormat *)format
    52         shareContext:(NSOpenGLContext *)share
    53 {
    54     SDL_AtomicSet(&self->dirty, 0);
    55     return [super initWithFormat:format shareContext:share];
    56 }
    57 
    58 - (void)scheduleUpdate
    59 {
    60     SDL_AtomicAdd(&self->dirty, 1);
    61 }
    62 
    63 /* This should only be called on the thread on which a user is using the context. */
    64 - (void)updateIfNeeded
    65 {
    66     int value = SDL_AtomicSet(&self->dirty, 0);
    67     if (value > 0) {
    68         /* We call the real underlying update here, since -[SDLOpenGLContext update] just calls us. */
    69         [super update];
    70     }
    71 }
    72 
    73 /* This should only be called on the thread on which a user is using the context. */
    74 - (void)update
    75 {
    76     /* This ensures that regular 'update' calls clear the atomic dirty flag. */
    77     [self scheduleUpdate];
    78     [self updateIfNeeded];
    79 }
    80 
    81 @end
    82 
    83 
    84 int
    85 Cocoa_GL_LoadLibrary(_THIS, const char *path)
    86 {
    87     /* Load the OpenGL library */
    88     if (path == NULL) {
    89         path = SDL_getenv("SDL_OPENGL_LIBRARY");
    90     }
    91     if (path == NULL) {
    92         path = DEFAULT_OPENGL;
    93     }
    94     _this->gl_config.dll_handle = SDL_LoadObject(path);
    95     if (!_this->gl_config.dll_handle) {
    96         return -1;
    97     }
    98     SDL_strlcpy(_this->gl_config.driver_path, path,
    99                 SDL_arraysize(_this->gl_config.driver_path));
   100     return 0;
   101 }
   102 
   103 void *
   104 Cocoa_GL_GetProcAddress(_THIS, const char *proc)
   105 {
   106     return SDL_LoadFunction(_this->gl_config.dll_handle, proc);
   107 }
   108 
   109 void
   110 Cocoa_GL_UnloadLibrary(_THIS)
   111 {
   112     SDL_UnloadObject(_this->gl_config.dll_handle);
   113     _this->gl_config.dll_handle = NULL;
   114 }
   115 
   116 SDL_GLContext
   117 Cocoa_GL_CreateContext(_THIS, SDL_Window * window)
   118 {
   119     const int wantver = (_this->gl_config.major_version << 8) |
   120                         (_this->gl_config.minor_version);
   121     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
   122     NSAutoreleasePool *pool;
   123     SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
   124     SDL_DisplayData *displaydata = (SDL_DisplayData *)display->driverdata;
   125     NSOpenGLPixelFormatAttribute attr[32];
   126     NSOpenGLPixelFormat *fmt;
   127     SDLOpenGLContext *context;
   128     NSOpenGLContext *share_context = nil;
   129     int i = 0;
   130 
   131     if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) {
   132         SDL_SetError ("OpenGL ES not supported on this platform");
   133         return NULL;
   134     }
   135 
   136     /* Sadly, we'll have to update this as life progresses, since we need to
   137        set an enum for context profiles, not a context version number */
   138     if (wantver > 0x0302) {
   139         SDL_SetError ("OpenGL > 3.2 is not supported on this platform");
   140         return NULL;
   141     }
   142 
   143     pool = [[NSAutoreleasePool alloc] init];
   144 
   145     /* specify a profile if we're on Lion (10.7) or later. */
   146     if (data->osversion >= 0x1070) {
   147         NSOpenGLPixelFormatAttribute profile = kCGLOGLPVersion_Legacy;
   148         if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_CORE) {
   149             if (wantver == 0x0302) {
   150                 profile = kCGLOGLPVersion_3_2_Core;
   151             }
   152         }
   153         attr[i++] = kCGLPFAOpenGLProfile;
   154         attr[i++] = profile;
   155     }
   156 
   157     attr[i++] = NSOpenGLPFAColorSize;
   158     attr[i++] = SDL_BYTESPERPIXEL(display->current_mode.format)*8;
   159 
   160     attr[i++] = NSOpenGLPFADepthSize;
   161     attr[i++] = _this->gl_config.depth_size;
   162 
   163     if (_this->gl_config.double_buffer) {
   164         attr[i++] = NSOpenGLPFADoubleBuffer;
   165     }
   166 
   167     if (_this->gl_config.stereo) {
   168         attr[i++] = NSOpenGLPFAStereo;
   169     }
   170 
   171     if (_this->gl_config.stencil_size) {
   172         attr[i++] = NSOpenGLPFAStencilSize;
   173         attr[i++] = _this->gl_config.stencil_size;
   174     }
   175 
   176     if ((_this->gl_config.accum_red_size +
   177          _this->gl_config.accum_green_size +
   178          _this->gl_config.accum_blue_size +
   179          _this->gl_config.accum_alpha_size) > 0) {
   180         attr[i++] = NSOpenGLPFAAccumSize;
   181         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;
   182     }
   183 
   184     if (_this->gl_config.multisamplebuffers) {
   185         attr[i++] = NSOpenGLPFASampleBuffers;
   186         attr[i++] = _this->gl_config.multisamplebuffers;
   187     }
   188 
   189     if (_this->gl_config.multisamplesamples) {
   190         attr[i++] = NSOpenGLPFASamples;
   191         attr[i++] = _this->gl_config.multisamplesamples;
   192         attr[i++] = NSOpenGLPFANoRecovery;
   193     }
   194 
   195     if (_this->gl_config.accelerated >= 0) {
   196         if (_this->gl_config.accelerated) {
   197             attr[i++] = NSOpenGLPFAAccelerated;
   198         } else {
   199             attr[i++] = NSOpenGLPFARendererID;
   200             attr[i++] = kCGLRendererGenericFloatID;
   201         }
   202     }
   203 
   204     attr[i++] = NSOpenGLPFAScreenMask;
   205     attr[i++] = CGDisplayIDToOpenGLDisplayMask(displaydata->display);
   206     attr[i] = 0;
   207 
   208     fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr];
   209     if (fmt == nil) {
   210         SDL_SetError ("Failed creating OpenGL pixel format");
   211         [pool release];
   212         return NULL;
   213     }
   214 
   215     if (_this->gl_config.share_with_current_context) {
   216         share_context = (NSOpenGLContext*)SDL_GL_GetCurrentContext();
   217     }
   218 
   219     context = [[SDLOpenGLContext alloc] initWithFormat:fmt shareContext:share_context];
   220 
   221     [fmt release];
   222 
   223     if (context == nil) {
   224         SDL_SetError ("Failed creating OpenGL context");
   225         [pool release];
   226         return NULL;
   227     }
   228 
   229     [pool release];
   230 
   231     if ( Cocoa_GL_MakeCurrent(_this, window, context) < 0 ) {
   232         Cocoa_GL_DeleteContext(_this, context);
   233         return NULL;
   234     }
   235 
   236     return context;
   237 }
   238 
   239 int
   240 Cocoa_GL_MakeCurrent(_THIS, SDL_Window * window, SDL_GLContext context)
   241 {
   242     NSAutoreleasePool *pool;
   243 
   244     pool = [[NSAutoreleasePool alloc] init];
   245 
   246     if (context) {
   247         SDL_WindowData *windowdata = (SDL_WindowData *)window->driverdata;
   248         SDLOpenGLContext *nscontext = (SDLOpenGLContext *)context;
   249         windowdata->nscontext = nscontext;
   250         if ([nscontext view] != [windowdata->nswindow contentView]) {
   251             [nscontext setView:[windowdata->nswindow contentView]];
   252             [nscontext scheduleUpdate];
   253         }
   254 
   255         [nscontext updateIfNeeded];
   256         [nscontext makeCurrentContext];
   257     } else {
   258         [NSOpenGLContext clearCurrentContext];
   259     }
   260 
   261     [pool release];
   262     return 0;
   263 }
   264 
   265 int
   266 Cocoa_GL_SetSwapInterval(_THIS, int interval)
   267 {
   268     NSAutoreleasePool *pool;
   269     NSOpenGLContext *nscontext;
   270     GLint value;
   271     int status;
   272 
   273     pool = [[NSAutoreleasePool alloc] init];
   274 
   275     nscontext = (NSOpenGLContext*)SDL_GL_GetCurrentContext();
   276     if (nscontext != nil) {
   277         value = interval;
   278         [nscontext setValues:&value forParameter:NSOpenGLCPSwapInterval];
   279         status = 0;
   280     } else {
   281         status = SDL_SetError("No current OpenGL context");
   282     }
   283 
   284     [pool release];
   285     return status;
   286 }
   287 
   288 int
   289 Cocoa_GL_GetSwapInterval(_THIS)
   290 {
   291     NSAutoreleasePool *pool;
   292     NSOpenGLContext *nscontext;
   293     GLint value;
   294     int status = 0;
   295 
   296     pool = [[NSAutoreleasePool alloc] init];
   297 
   298     nscontext = (NSOpenGLContext*)SDL_GL_GetCurrentContext();
   299     if (nscontext != nil) {
   300         [nscontext getValues:&value forParameter:NSOpenGLCPSwapInterval];
   301         status = (int)value;
   302     }
   303 
   304     [pool release];
   305     return status;
   306 }
   307 
   308 void
   309 Cocoa_GL_SwapWindow(_THIS, SDL_Window * window)
   310 {
   311     NSAutoreleasePool *pool;
   312     SDL_WindowData *windowdata = (SDL_WindowData *)window->driverdata;
   313     SDLOpenGLContext *nscontext = windowdata->nscontext;
   314 
   315     pool = [[NSAutoreleasePool alloc] init];
   316 
   317     [nscontext flushBuffer];
   318     [nscontext updateIfNeeded];
   319 
   320     [pool release];
   321 }
   322 
   323 void
   324 Cocoa_GL_DeleteContext(_THIS, SDL_GLContext context)
   325 {
   326     NSAutoreleasePool *pool;
   327     NSOpenGLContext *nscontext = (NSOpenGLContext *)context;
   328 
   329     pool = [[NSAutoreleasePool alloc] init];
   330 
   331     [nscontext clearDrawable];
   332     [nscontext release];
   333 
   334     [pool release];
   335 }
   336 
   337 #endif /* SDL_VIDEO_OPENGL_CGL */
   338 
   339 /* vi: set ts=4 sw=4 expandtab: */