src/video/cocoa/SDL_cocoaopengl.m
author Ryan C. Gordon <icculus@icculus.org>
Mon, 28 Oct 2013 22:43:39 -0400
changeset 7894 bb624b1348da
parent 7746 6a05d7352575
child 7897 622d75108e60
permissions -rw-r--r--
Updated GL version tests for Mac OS X 10.9 ("Mavericks").

They changed the name of their GL 3.2 enum, added the GL 4.1 core profile.
     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 #if MAC_OS_X_VERSION_MAX_ALLOWED < 1070
    39 /* New methods for converting to and from backing store pixels, taken from
    40  * AppKite/NSView.h in 10.8 SDK. */
    41 @interface NSView (Backing)
    42 - (NSPoint)convertPointToBacking:(NSPoint)aPoint;
    43 - (NSPoint)convertPointFromBacking:(NSPoint)aPoint;
    44 - (NSSize)convertSizeToBacking:(NSSize)aSize;
    45 - (NSSize)convertSizeFromBacking:(NSSize)aSize;
    46 - (NSRect)convertRectToBacking:(NSRect)aRect;
    47 - (NSRect)convertRectFromBacking:(NSRect)aRect;
    48 @end
    49 #endif
    50 
    51 #ifndef kCGLPFAOpenGLProfile
    52 #define kCGLPFAOpenGLProfile 99
    53 #endif
    54 #ifndef kCGLOGLPVersion_Legacy
    55 #define kCGLOGLPVersion_Legacy 0x1000
    56 #endif
    57 #ifndef kCGLOGLPVersion_GL3_Core
    58 #define kCGLOGLPVersion_GL3_Core 0x3200
    59 #endif
    60 #ifndef kCGLOGLPVersion_GL4_Core
    61 #define kCGLOGLPVersion_GL4_Core 0x4100
    62 #endif
    63 
    64 @implementation SDLOpenGLContext : NSOpenGLContext
    65 
    66 - (id)initWithFormat:(NSOpenGLPixelFormat *)format
    67         shareContext:(NSOpenGLContext *)share
    68 {
    69     self = [super initWithFormat:format shareContext:share];
    70     if (self) {
    71         SDL_AtomicSet(&self->dirty, 0);
    72         self->window = NULL;
    73     }
    74     return self;
    75 }
    76 
    77 - (void)scheduleUpdate
    78 {
    79     SDL_AtomicAdd(&self->dirty, 1);
    80 }
    81 
    82 /* This should only be called on the thread on which a user is using the context. */
    83 - (void)updateIfNeeded
    84 {
    85     int value = SDL_AtomicSet(&self->dirty, 0);
    86     if (value > 0) {
    87         /* We call the real underlying update here, since -[SDLOpenGLContext update] just calls us. */
    88         [super update];
    89     }
    90 }
    91 
    92 /* This should only be called on the thread on which a user is using the context. */
    93 - (void)update
    94 {
    95     /* This ensures that regular 'update' calls clear the atomic dirty flag. */
    96     [self scheduleUpdate];
    97     [self updateIfNeeded];
    98 }
    99 
   100 /* Updates the drawable for the contexts and manages related state. */
   101 - (void)setWindow:(SDL_Window *)newWindow
   102 {
   103     if (self->window) {
   104         SDL_WindowData *oldwindowdata = (SDL_WindowData *)self->window->driverdata;
   105 
   106         /* Make sure to remove us from the old window's context list, or we'll get scheduled updates from it too. */
   107         NSMutableArray *contexts = oldwindowdata->nscontexts;
   108         @synchronized (contexts) {
   109             [contexts removeObject:self];
   110         }
   111     }
   112 
   113     self->window = newWindow;
   114 
   115     if (newWindow) {
   116         SDL_WindowData *windowdata = (SDL_WindowData *)newWindow->driverdata;
   117 
   118         /* Now sign up for scheduled updates for the new window. */
   119         NSMutableArray *contexts = windowdata->nscontexts;
   120         @synchronized (contexts) {
   121             [contexts addObject:self];
   122         }
   123 
   124         if ([self view] != [windowdata->nswindow contentView]) {
   125             [self setView:[windowdata->nswindow contentView]];
   126             [self scheduleUpdate];
   127         }
   128     } else {
   129         [self clearDrawable];
   130         [self scheduleUpdate];
   131     }
   132 }
   133 
   134 @end
   135 
   136 
   137 int
   138 Cocoa_GL_LoadLibrary(_THIS, const char *path)
   139 {
   140     /* Load the OpenGL library */
   141     if (path == NULL) {
   142         path = SDL_getenv("SDL_OPENGL_LIBRARY");
   143     }
   144     if (path == NULL) {
   145         path = DEFAULT_OPENGL;
   146     }
   147     _this->gl_config.dll_handle = SDL_LoadObject(path);
   148     if (!_this->gl_config.dll_handle) {
   149         return -1;
   150     }
   151     SDL_strlcpy(_this->gl_config.driver_path, path,
   152                 SDL_arraysize(_this->gl_config.driver_path));
   153     return 0;
   154 }
   155 
   156 void *
   157 Cocoa_GL_GetProcAddress(_THIS, const char *proc)
   158 {
   159     return SDL_LoadFunction(_this->gl_config.dll_handle, proc);
   160 }
   161 
   162 void
   163 Cocoa_GL_UnloadLibrary(_THIS)
   164 {
   165     SDL_UnloadObject(_this->gl_config.dll_handle);
   166     _this->gl_config.dll_handle = NULL;
   167 }
   168 
   169 SDL_GLContext
   170 Cocoa_GL_CreateContext(_THIS, SDL_Window * window)
   171 {
   172     const int wantver = (_this->gl_config.major_version << 8) |
   173                         (_this->gl_config.minor_version);
   174     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
   175     NSAutoreleasePool *pool;
   176     SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
   177     SDL_DisplayData *displaydata = (SDL_DisplayData *)display->driverdata;
   178     NSOpenGLPixelFormatAttribute attr[32];
   179     NSOpenGLPixelFormat *fmt;
   180     SDLOpenGLContext *context;
   181     NSOpenGLContext *share_context = nil;
   182     int i = 0;
   183 
   184     if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) {
   185         SDL_SetError ("OpenGL ES is not supported on this platform");
   186         return NULL;
   187     }
   188 
   189     /* Sadly, we'll have to update this as life progresses, since we need to
   190        set an enum for context profiles, not a context version number */
   191     if (wantver > 0x0401) {
   192         SDL_SetError ("OpenGL > 4.1 is not supported on this platform");
   193         return NULL;
   194     }
   195 
   196     pool = [[NSAutoreleasePool alloc] init];
   197 
   198     /* specify a profile if we're on Lion (10.7) or later. */
   199     if (data->osversion >= 0x1070) {
   200         NSOpenGLPixelFormatAttribute profile = kCGLOGLPVersion_Legacy;
   201         if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_CORE) {
   202             if (wantver == 0x0302) {
   203                 profile = kCGLOGLPVersion_GL3_Core;
   204             } else if ((wantver == 0x0401) && (data->osversion >= 0x1090)) {
   205                 profile = kCGLOGLPVersion_GL4_Core;
   206             } else {
   207                 SDL_SetError("Requested GL version is not supported on this platform");
   208                 [pool release];
   209                 return NULL;
   210             }
   211         }
   212         attr[i++] = kCGLPFAOpenGLProfile;
   213         attr[i++] = profile;
   214     }
   215 
   216     attr[i++] = NSOpenGLPFAColorSize;
   217     attr[i++] = SDL_BYTESPERPIXEL(display->current_mode.format)*8;
   218 
   219     attr[i++] = NSOpenGLPFADepthSize;
   220     attr[i++] = _this->gl_config.depth_size;
   221 
   222     if (_this->gl_config.double_buffer) {
   223         attr[i++] = NSOpenGLPFADoubleBuffer;
   224     }
   225 
   226     if (_this->gl_config.stereo) {
   227         attr[i++] = NSOpenGLPFAStereo;
   228     }
   229 
   230     if (_this->gl_config.stencil_size) {
   231         attr[i++] = NSOpenGLPFAStencilSize;
   232         attr[i++] = _this->gl_config.stencil_size;
   233     }
   234 
   235     if ((_this->gl_config.accum_red_size +
   236          _this->gl_config.accum_green_size +
   237          _this->gl_config.accum_blue_size +
   238          _this->gl_config.accum_alpha_size) > 0) {
   239         attr[i++] = NSOpenGLPFAAccumSize;
   240         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;
   241     }
   242 
   243     if (_this->gl_config.multisamplebuffers) {
   244         attr[i++] = NSOpenGLPFASampleBuffers;
   245         attr[i++] = _this->gl_config.multisamplebuffers;
   246     }
   247 
   248     if (_this->gl_config.multisamplesamples) {
   249         attr[i++] = NSOpenGLPFASamples;
   250         attr[i++] = _this->gl_config.multisamplesamples;
   251         attr[i++] = NSOpenGLPFANoRecovery;
   252     }
   253 
   254     if (_this->gl_config.accelerated >= 0) {
   255         if (_this->gl_config.accelerated) {
   256             attr[i++] = NSOpenGLPFAAccelerated;
   257         } else {
   258             attr[i++] = NSOpenGLPFARendererID;
   259             attr[i++] = kCGLRendererGenericFloatID;
   260         }
   261     }
   262 
   263     attr[i++] = NSOpenGLPFAScreenMask;
   264     attr[i++] = CGDisplayIDToOpenGLDisplayMask(displaydata->display);
   265     attr[i] = 0;
   266 
   267     fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr];
   268     if (fmt == nil) {
   269         SDL_SetError ("Failed creating OpenGL pixel format");
   270         [pool release];
   271         return NULL;
   272     }
   273 
   274     if (_this->gl_config.share_with_current_context) {
   275         share_context = (NSOpenGLContext*)SDL_GL_GetCurrentContext();
   276     }
   277 
   278     context = [[SDLOpenGLContext alloc] initWithFormat:fmt shareContext:share_context];
   279 
   280     [fmt release];
   281 
   282     if (context == nil) {
   283         SDL_SetError ("Failed creating OpenGL context");
   284         [pool release];
   285         return NULL;
   286     }
   287 
   288     [pool release];
   289 
   290     if ( Cocoa_GL_MakeCurrent(_this, window, context) < 0 ) {
   291         Cocoa_GL_DeleteContext(_this, context);
   292         return NULL;
   293     }
   294 
   295     return context;
   296 }
   297 
   298 int
   299 Cocoa_GL_MakeCurrent(_THIS, SDL_Window * window, SDL_GLContext context)
   300 {
   301     NSAutoreleasePool *pool;
   302 
   303     pool = [[NSAutoreleasePool alloc] init];
   304 
   305     if (context) {
   306         SDLOpenGLContext *nscontext = (SDLOpenGLContext *)context;
   307         [nscontext setWindow:window];
   308         [nscontext updateIfNeeded];
   309         [nscontext makeCurrentContext];
   310     } else {
   311         [NSOpenGLContext clearCurrentContext];
   312     }
   313 
   314     [pool release];
   315     return 0;
   316 }
   317 
   318 void
   319 Cocoa_GL_GetDrawableSize(_THIS, SDL_Window * window, int * w, int * h)
   320 {
   321     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
   322     NSView *contentView = [windata->nswindow contentView];
   323     NSRect viewport = [contentView bounds];
   324 
   325     /* This gives us the correct viewport for a Retina-enabled view, only
   326      * supported on 10.7+. */
   327     if ([contentView respondsToSelector:@selector(convertRectToBacking:)]) {
   328         viewport = [contentView convertRectToBacking:viewport];
   329     }
   330 
   331     if (w) {
   332         *w = viewport.size.width;
   333     }
   334 
   335     if (h) {
   336         *h = viewport.size.height;
   337     }
   338 }
   339 
   340 int
   341 Cocoa_GL_SetSwapInterval(_THIS, int interval)
   342 {
   343     NSAutoreleasePool *pool;
   344     NSOpenGLContext *nscontext;
   345     GLint value;
   346     int status;
   347 
   348     if (interval < 0) {  /* no extension for this on Mac OS X at the moment. */
   349         return SDL_SetError("Late swap tearing currently unsupported");
   350     }
   351 
   352     pool = [[NSAutoreleasePool alloc] init];
   353 
   354     nscontext = (NSOpenGLContext*)SDL_GL_GetCurrentContext();
   355     if (nscontext != nil) {
   356         value = interval;
   357         [nscontext setValues:&value forParameter:NSOpenGLCPSwapInterval];
   358         status = 0;
   359     } else {
   360         status = SDL_SetError("No current OpenGL context");
   361     }
   362 
   363     [pool release];
   364     return status;
   365 }
   366 
   367 int
   368 Cocoa_GL_GetSwapInterval(_THIS)
   369 {
   370     NSAutoreleasePool *pool;
   371     NSOpenGLContext *nscontext;
   372     GLint value;
   373     int status = 0;
   374 
   375     pool = [[NSAutoreleasePool alloc] init];
   376 
   377     nscontext = (NSOpenGLContext*)SDL_GL_GetCurrentContext();
   378     if (nscontext != nil) {
   379         [nscontext getValues:&value forParameter:NSOpenGLCPSwapInterval];
   380         status = (int)value;
   381     }
   382 
   383     [pool release];
   384     return status;
   385 }
   386 
   387 void
   388 Cocoa_GL_SwapWindow(_THIS, SDL_Window * window)
   389 {
   390     NSAutoreleasePool *pool;
   391 
   392     pool = [[NSAutoreleasePool alloc] init];
   393 
   394     SDLOpenGLContext* nscontext = (SDLOpenGLContext*)SDL_GL_GetCurrentContext();
   395     [nscontext flushBuffer];
   396     [nscontext updateIfNeeded];
   397 
   398     [pool release];
   399 }
   400 
   401 void
   402 Cocoa_GL_DeleteContext(_THIS, SDL_GLContext context)
   403 {
   404     NSAutoreleasePool *pool;
   405     SDLOpenGLContext *nscontext = (SDLOpenGLContext *)context;
   406 
   407     pool = [[NSAutoreleasePool alloc] init];
   408 
   409     [nscontext setWindow:NULL];
   410     [nscontext release];
   411 
   412     [pool release];
   413 }
   414 
   415 #endif /* SDL_VIDEO_OPENGL_CGL */
   416 
   417 /* vi: set ts=4 sw=4 expandtab: */