src/video/cocoa/SDL_cocoaopengl.m
author Sam Lantinga <slouken@libsdl.org>
Mon, 10 Mar 2014 18:45:07 -0700
changeset 8601 4c04c7785cba
parent 8278 e60a7bb2b80b
child 8604 459bd7365e9d
permissions -rw-r--r--
Fixed regression causing the renderer to recreate its window since it's not getting an OpenGL 2.1 context anymore.
     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 /*
   166     const GLubyte *(APIENTRY * glGetStringFunc)(GLenum) = NULL;
   167 */
   168     NSAutoreleasePool *pool;
   169     SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
   170     SDL_DisplayData *displaydata = (SDL_DisplayData *)display->driverdata;
   171     NSOpenGLPixelFormatAttribute attr[32];
   172     NSOpenGLPixelFormat *fmt;
   173     SDLOpenGLContext *context;
   174     NSOpenGLContext *share_context = nil;
   175     int i = 0;
   176     const char *glversion;
   177     int glversion_major;
   178     int glversion_minor;
   179 
   180     if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) {
   181         SDL_SetError ("OpenGL ES is not supported on this platform");
   182         return NULL;
   183     }
   184     if ((_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_CORE) && (data->osversion < 0x1070)) {
   185         SDL_SetError ("OpenGL Core Profile is not supported on this platform version");
   186         return NULL;
   187     }
   188 
   189     pool = [[NSAutoreleasePool alloc] init];
   190 
   191     /* specify a profile if we're on Lion (10.7) or later. */
   192     if (data->osversion >= 0x1070) {
   193         NSOpenGLPixelFormatAttribute profile = NSOpenGLProfileVersionLegacy;
   194         if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_CORE) {
   195             profile = NSOpenGLProfileVersion3_2Core;
   196         }
   197         attr[i++] = NSOpenGLPFAOpenGLProfile;
   198         attr[i++] = profile;
   199     }
   200 
   201     attr[i++] = NSOpenGLPFAColorSize;
   202     attr[i++] = SDL_BYTESPERPIXEL(display->current_mode.format)*8;
   203 
   204     attr[i++] = NSOpenGLPFADepthSize;
   205     attr[i++] = _this->gl_config.depth_size;
   206 
   207     if (_this->gl_config.double_buffer) {
   208         attr[i++] = NSOpenGLPFADoubleBuffer;
   209     }
   210 
   211     if (_this->gl_config.stereo) {
   212         attr[i++] = NSOpenGLPFAStereo;
   213     }
   214 
   215     if (_this->gl_config.stencil_size) {
   216         attr[i++] = NSOpenGLPFAStencilSize;
   217         attr[i++] = _this->gl_config.stencil_size;
   218     }
   219 
   220     if ((_this->gl_config.accum_red_size +
   221          _this->gl_config.accum_green_size +
   222          _this->gl_config.accum_blue_size +
   223          _this->gl_config.accum_alpha_size) > 0) {
   224         attr[i++] = NSOpenGLPFAAccumSize;
   225         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;
   226     }
   227 
   228     if (_this->gl_config.multisamplebuffers) {
   229         attr[i++] = NSOpenGLPFASampleBuffers;
   230         attr[i++] = _this->gl_config.multisamplebuffers;
   231     }
   232 
   233     if (_this->gl_config.multisamplesamples) {
   234         attr[i++] = NSOpenGLPFASamples;
   235         attr[i++] = _this->gl_config.multisamplesamples;
   236         attr[i++] = NSOpenGLPFANoRecovery;
   237     }
   238 
   239     if (_this->gl_config.accelerated >= 0) {
   240         if (_this->gl_config.accelerated) {
   241             attr[i++] = NSOpenGLPFAAccelerated;
   242         } else {
   243             attr[i++] = NSOpenGLPFARendererID;
   244             attr[i++] = kCGLRendererGenericFloatID;
   245         }
   246     }
   247 
   248     attr[i++] = NSOpenGLPFAScreenMask;
   249     attr[i++] = CGDisplayIDToOpenGLDisplayMask(displaydata->display);
   250     attr[i] = 0;
   251 
   252     fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr];
   253     if (fmt == nil) {
   254         SDL_SetError("Failed creating OpenGL pixel format");
   255         [pool release];
   256         return NULL;
   257     }
   258 
   259     if (_this->gl_config.share_with_current_context) {
   260         share_context = (NSOpenGLContext*)SDL_GL_GetCurrentContext();
   261     }
   262 
   263     context = [[SDLOpenGLContext alloc] initWithFormat:fmt shareContext:share_context];
   264 
   265     [fmt release];
   266 
   267     if (context == nil) {
   268         SDL_SetError("Failed creating OpenGL context");
   269         [pool release];
   270         return NULL;
   271     }
   272 
   273     [pool release];
   274 
   275     if ( Cocoa_GL_MakeCurrent(_this, window, context) < 0 ) {
   276         Cocoa_GL_DeleteContext(_this, context);
   277         SDL_SetError("Failed making OpenGL context current");
   278         return NULL;
   279     }
   280 
   281 /* No other backend does this version checking.
   282    If we enable it, we should consider whether it should be done at a 
   283    higher level for all platforms. We'll have to think through the implications
   284    of this.
   285 
   286    For example, Mac OS X 10.6 will only report OpenGL 2.0, but we ask for 2.1
   287    by default. If we don't get 2.1, then the renderer will set the requested
   288    version and try to recreate the window, which causes all kinds of problems.
   289 
   290    For now, we'll just disable this code until we can think about it more.
   291 */
   292 #if 0
   293     glGetStringFunc = (const GLubyte *(APIENTRY *)(GLenum)) SDL_GL_GetProcAddress("glGetString");
   294     if (!glGetStringFunc) {
   295         Cocoa_GL_DeleteContext(_this, context);
   296         SDL_SetError ("Failed getting OpenGL glGetString entry point");
   297         return NULL;
   298     }
   299 
   300     glversion = (const char *)glGetStringFunc(GL_VERSION);
   301     if (glversion == NULL) {
   302         Cocoa_GL_DeleteContext(_this, context);
   303         SDL_SetError ("Failed getting OpenGL context version");
   304         return NULL;
   305     }
   306 
   307     if (SDL_sscanf(glversion, "%d.%d", &glversion_major, &glversion_minor) != 2) {
   308         Cocoa_GL_DeleteContext(_this, context);
   309         SDL_SetError ("Failed parsing OpenGL context version");
   310         return NULL;
   311     }
   312 
   313     if ((glversion_major <  _this->gl_config.major_version) ||
   314        ((glversion_major == _this->gl_config.major_version) && (glversion_minor < _this->gl_config.minor_version))) {
   315         Cocoa_GL_DeleteContext(_this, context);
   316         SDL_SetError ("Failed creating OpenGL context at version requested");
   317         return NULL;
   318     }
   319 
   320     _this->gl_config.major_version = glversion_major;
   321     _this->gl_config.minor_version = glversion_minor;
   322 #endif
   323 
   324     return context;
   325 }
   326 
   327 int
   328 Cocoa_GL_MakeCurrent(_THIS, SDL_Window * window, SDL_GLContext context)
   329 {
   330     NSAutoreleasePool *pool;
   331 
   332     pool = [[NSAutoreleasePool alloc] init];
   333 
   334     if (context) {
   335         SDLOpenGLContext *nscontext = (SDLOpenGLContext *)context;
   336         [nscontext setWindow:window];
   337         [nscontext updateIfNeeded];
   338         [nscontext makeCurrentContext];
   339     } else {
   340         [NSOpenGLContext clearCurrentContext];
   341     }
   342 
   343     [pool release];
   344     return 0;
   345 }
   346 
   347 void
   348 Cocoa_GL_GetDrawableSize(_THIS, SDL_Window * window, int * w, int * h)
   349 {
   350     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
   351     NSView *contentView = [windata->nswindow contentView];
   352     NSRect viewport = [contentView bounds];
   353 
   354     /* This gives us the correct viewport for a Retina-enabled view, only
   355      * supported on 10.7+. */
   356     if ([contentView respondsToSelector:@selector(convertRectToBacking:)]) {
   357         viewport = [contentView convertRectToBacking:viewport];
   358     }
   359 
   360     if (w) {
   361         *w = viewport.size.width;
   362     }
   363 
   364     if (h) {
   365         *h = viewport.size.height;
   366     }
   367 }
   368 
   369 int
   370 Cocoa_GL_SetSwapInterval(_THIS, int interval)
   371 {
   372     NSAutoreleasePool *pool;
   373     NSOpenGLContext *nscontext;
   374     GLint value;
   375     int status;
   376 
   377     if (interval < 0) {  /* no extension for this on Mac OS X at the moment. */
   378         return SDL_SetError("Late swap tearing currently unsupported");
   379     }
   380 
   381     pool = [[NSAutoreleasePool alloc] init];
   382 
   383     nscontext = (NSOpenGLContext*)SDL_GL_GetCurrentContext();
   384     if (nscontext != nil) {
   385         value = interval;
   386         [nscontext setValues:&value forParameter:NSOpenGLCPSwapInterval];
   387         status = 0;
   388     } else {
   389         status = SDL_SetError("No current OpenGL context");
   390     }
   391 
   392     [pool release];
   393     return status;
   394 }
   395 
   396 int
   397 Cocoa_GL_GetSwapInterval(_THIS)
   398 {
   399     NSAutoreleasePool *pool;
   400     NSOpenGLContext *nscontext;
   401     GLint value;
   402     int status = 0;
   403 
   404     pool = [[NSAutoreleasePool alloc] init];
   405 
   406     nscontext = (NSOpenGLContext*)SDL_GL_GetCurrentContext();
   407     if (nscontext != nil) {
   408         [nscontext getValues:&value forParameter:NSOpenGLCPSwapInterval];
   409         status = (int)value;
   410     }
   411 
   412     [pool release];
   413     return status;
   414 }
   415 
   416 void
   417 Cocoa_GL_SwapWindow(_THIS, SDL_Window * window)
   418 {
   419     NSAutoreleasePool *pool;
   420 
   421     pool = [[NSAutoreleasePool alloc] init];
   422 
   423     SDLOpenGLContext* nscontext = (SDLOpenGLContext*)SDL_GL_GetCurrentContext();
   424     [nscontext flushBuffer];
   425     [nscontext updateIfNeeded];
   426 
   427     [pool release];
   428 }
   429 
   430 void
   431 Cocoa_GL_DeleteContext(_THIS, SDL_GLContext context)
   432 {
   433     NSAutoreleasePool *pool;
   434     SDLOpenGLContext *nscontext = (SDLOpenGLContext *)context;
   435 
   436     pool = [[NSAutoreleasePool alloc] init];
   437 
   438     [nscontext setWindow:NULL];
   439     [nscontext release];
   440 
   441     [pool release];
   442 }
   443 
   444 #endif /* SDL_VIDEO_OPENGL_CGL */
   445 
   446 /* vi: set ts=4 sw=4 expandtab: */