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