src/video/cocoa/SDL_cocoaopengl.m
author Sam Lantinga <slouken@libsdl.org>
Tue, 26 May 2015 06:27:46 -0700
changeset 9619 b94b6d0bff0f
parent 9587 18979eac0a35
child 9998 f67cf37e9cd4
permissions -rw-r--r--
Updated the copyright year to 2015
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2015 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 @implementation SDLOpenGLContext : NSOpenGLContext
    39 
    40 - (id)initWithFormat:(NSOpenGLPixelFormat *)format
    41         shareContext:(NSOpenGLContext *)share
    42 {
    43     self = [super initWithFormat:format shareContext:share];
    44     if (self) {
    45         SDL_AtomicSet(&self->dirty, 0);
    46         self->window = NULL;
    47     }
    48     return self;
    49 }
    50 
    51 - (void)scheduleUpdate
    52 {
    53     SDL_AtomicAdd(&self->dirty, 1);
    54 }
    55 
    56 /* This should only be called on the thread on which a user is using the context. */
    57 - (void)updateIfNeeded
    58 {
    59     int value = SDL_AtomicSet(&self->dirty, 0);
    60     if (value > 0) {
    61         /* We call the real underlying update here, since -[SDLOpenGLContext update] just calls us. */
    62         [super update];
    63     }
    64 }
    65 
    66 /* This should only be called on the thread on which a user is using the context. */
    67 - (void)update
    68 {
    69     /* This ensures that regular 'update' calls clear the atomic dirty flag. */
    70     [self scheduleUpdate];
    71     [self updateIfNeeded];
    72 }
    73 
    74 /* Updates the drawable for the contexts and manages related state. */
    75 - (void)setWindow:(SDL_Window *)newWindow
    76 {
    77     if (self->window) {
    78         SDL_WindowData *oldwindowdata = (SDL_WindowData *)self->window->driverdata;
    79 
    80         /* Make sure to remove us from the old window's context list, or we'll get scheduled updates from it too. */
    81         NSMutableArray *contexts = oldwindowdata->nscontexts;
    82         @synchronized (contexts) {
    83             [contexts removeObject:self];
    84         }
    85     }
    86 
    87     self->window = newWindow;
    88 
    89     if (newWindow) {
    90         SDL_WindowData *windowdata = (SDL_WindowData *)newWindow->driverdata;
    91 
    92         /* Now sign up for scheduled updates for the new window. */
    93         NSMutableArray *contexts = windowdata->nscontexts;
    94         @synchronized (contexts) {
    95             [contexts addObject:self];
    96         }
    97 
    98         if ([self view] != [windowdata->nswindow contentView]) {
    99             [self setView:[windowdata->nswindow contentView]];
   100             if (self == [NSOpenGLContext currentContext]) {
   101                 [self update];
   102             } else {
   103                 [self scheduleUpdate];
   104             }
   105         }
   106     } else {
   107         [self clearDrawable];
   108         if (self == [NSOpenGLContext currentContext]) {
   109             [self update];
   110         } else {
   111             [self scheduleUpdate];
   112         }
   113     }
   114 }
   115 
   116 @end
   117 
   118 
   119 int
   120 Cocoa_GL_LoadLibrary(_THIS, const char *path)
   121 {
   122     /* Load the OpenGL library */
   123     if (path == NULL) {
   124         path = SDL_getenv("SDL_OPENGL_LIBRARY");
   125     }
   126     if (path == NULL) {
   127         path = DEFAULT_OPENGL;
   128     }
   129     _this->gl_config.dll_handle = SDL_LoadObject(path);
   130     if (!_this->gl_config.dll_handle) {
   131         return -1;
   132     }
   133     SDL_strlcpy(_this->gl_config.driver_path, path,
   134                 SDL_arraysize(_this->gl_config.driver_path));
   135     return 0;
   136 }
   137 
   138 void *
   139 Cocoa_GL_GetProcAddress(_THIS, const char *proc)
   140 {
   141     return SDL_LoadFunction(_this->gl_config.dll_handle, proc);
   142 }
   143 
   144 void
   145 Cocoa_GL_UnloadLibrary(_THIS)
   146 {
   147     SDL_UnloadObject(_this->gl_config.dll_handle);
   148     _this->gl_config.dll_handle = NULL;
   149 }
   150 
   151 SDL_GLContext
   152 Cocoa_GL_CreateContext(_THIS, SDL_Window * window)
   153 { @autoreleasepool
   154 {
   155     SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
   156     SDL_DisplayData *displaydata = (SDL_DisplayData *)display->driverdata;
   157     SDL_bool lion_or_later = floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6;
   158     NSOpenGLPixelFormatAttribute attr[32];
   159     NSOpenGLPixelFormat *fmt;
   160     SDLOpenGLContext *context;
   161     NSOpenGLContext *share_context = nil;
   162     int i = 0;
   163     const char *glversion;
   164     int glversion_major;
   165     int glversion_minor;
   166 
   167     if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) {
   168         SDL_SetError ("OpenGL ES is not supported on this platform");
   169         return NULL;
   170     }
   171     if ((_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_CORE) && !lion_or_later) {
   172         SDL_SetError ("OpenGL Core Profile is not supported on this platform version");
   173         return NULL;
   174     }
   175 
   176     /* specify a profile if we're on Lion (10.7) or later. */
   177     if (lion_or_later) {
   178         NSOpenGLPixelFormatAttribute profile = NSOpenGLProfileVersionLegacy;
   179         if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_CORE) {
   180             profile = NSOpenGLProfileVersion3_2Core;
   181         }
   182         attr[i++] = NSOpenGLPFAOpenGLProfile;
   183         attr[i++] = profile;
   184     }
   185 
   186     attr[i++] = NSOpenGLPFAColorSize;
   187     attr[i++] = SDL_BYTESPERPIXEL(display->current_mode.format)*8;
   188 
   189     attr[i++] = NSOpenGLPFADepthSize;
   190     attr[i++] = _this->gl_config.depth_size;
   191 
   192     if (_this->gl_config.double_buffer) {
   193         attr[i++] = NSOpenGLPFADoubleBuffer;
   194     }
   195 
   196     if (_this->gl_config.stereo) {
   197         attr[i++] = NSOpenGLPFAStereo;
   198     }
   199 
   200     if (_this->gl_config.stencil_size) {
   201         attr[i++] = NSOpenGLPFAStencilSize;
   202         attr[i++] = _this->gl_config.stencil_size;
   203     }
   204 
   205     if ((_this->gl_config.accum_red_size +
   206          _this->gl_config.accum_green_size +
   207          _this->gl_config.accum_blue_size +
   208          _this->gl_config.accum_alpha_size) > 0) {
   209         attr[i++] = NSOpenGLPFAAccumSize;
   210         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;
   211     }
   212 
   213     if (_this->gl_config.multisamplebuffers) {
   214         attr[i++] = NSOpenGLPFASampleBuffers;
   215         attr[i++] = _this->gl_config.multisamplebuffers;
   216     }
   217 
   218     if (_this->gl_config.multisamplesamples) {
   219         attr[i++] = NSOpenGLPFASamples;
   220         attr[i++] = _this->gl_config.multisamplesamples;
   221         attr[i++] = NSOpenGLPFANoRecovery;
   222     }
   223 
   224     if (_this->gl_config.accelerated >= 0) {
   225         if (_this->gl_config.accelerated) {
   226             attr[i++] = NSOpenGLPFAAccelerated;
   227         } else {
   228             attr[i++] = NSOpenGLPFARendererID;
   229             attr[i++] = kCGLRendererGenericFloatID;
   230         }
   231     }
   232 
   233     attr[i++] = NSOpenGLPFAScreenMask;
   234     attr[i++] = CGDisplayIDToOpenGLDisplayMask(displaydata->display);
   235     attr[i] = 0;
   236 
   237     fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr];
   238     if (fmt == nil) {
   239         SDL_SetError("Failed creating OpenGL pixel format");
   240         return NULL;
   241     }
   242 
   243     if (_this->gl_config.share_with_current_context) {
   244         share_context = (NSOpenGLContext*)SDL_GL_GetCurrentContext();
   245     }
   246 
   247     context = [[SDLOpenGLContext alloc] initWithFormat:fmt shareContext:share_context];
   248 
   249     [fmt release];
   250 
   251     if (context == nil) {
   252         SDL_SetError("Failed creating OpenGL context");
   253         return NULL;
   254     }
   255 
   256     if ( Cocoa_GL_MakeCurrent(_this, window, context) < 0 ) {
   257         Cocoa_GL_DeleteContext(_this, context);
   258         SDL_SetError("Failed making OpenGL context current");
   259         return NULL;
   260     }
   261 
   262     if (_this->gl_config.major_version < 3 &&
   263         _this->gl_config.profile_mask == 0 &&
   264         _this->gl_config.flags == 0) {
   265         /* This is a legacy profile, so to match other backends, we're done. */
   266     } else {
   267         const GLubyte *(APIENTRY * glGetStringFunc)(GLenum);
   268 
   269         glGetStringFunc = (const GLubyte *(APIENTRY *)(GLenum)) SDL_GL_GetProcAddress("glGetString");
   270         if (!glGetStringFunc) {
   271             Cocoa_GL_DeleteContext(_this, context);
   272             SDL_SetError ("Failed getting OpenGL glGetString entry point");
   273             return NULL;
   274         }
   275 
   276         glversion = (const char *)glGetStringFunc(GL_VERSION);
   277         if (glversion == NULL) {
   278             Cocoa_GL_DeleteContext(_this, context);
   279             SDL_SetError ("Failed getting OpenGL context version");
   280             return NULL;
   281         }
   282 
   283         if (SDL_sscanf(glversion, "%d.%d", &glversion_major, &glversion_minor) != 2) {
   284             Cocoa_GL_DeleteContext(_this, context);
   285             SDL_SetError ("Failed parsing OpenGL context version");
   286             return NULL;
   287         }
   288 
   289         if ((glversion_major < _this->gl_config.major_version) ||
   290            ((glversion_major == _this->gl_config.major_version) && (glversion_minor < _this->gl_config.minor_version))) {
   291             Cocoa_GL_DeleteContext(_this, context);
   292             SDL_SetError ("Failed creating OpenGL context at version requested");
   293             return NULL;
   294         }
   295 
   296         /* In the future we'll want to do this, but to match other platforms
   297            we'll leave the OpenGL version the way it is for now
   298          */
   299         /*_this->gl_config.major_version = glversion_major;*/
   300         /*_this->gl_config.minor_version = glversion_minor;*/
   301     }
   302     return context;
   303 }}
   304 
   305 int
   306 Cocoa_GL_MakeCurrent(_THIS, SDL_Window * window, SDL_GLContext context)
   307 { @autoreleasepool
   308 {
   309     if (context) {
   310         SDLOpenGLContext *nscontext = (SDLOpenGLContext *)context;
   311         [nscontext setWindow:window];
   312         [nscontext updateIfNeeded];
   313         [nscontext makeCurrentContext];
   314     } else {
   315         [NSOpenGLContext clearCurrentContext];
   316     }
   317 
   318     return 0;
   319 }}
   320 
   321 void
   322 Cocoa_GL_GetDrawableSize(_THIS, SDL_Window * window, int * w, int * h)
   323 {
   324     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
   325     NSView *contentView = [windata->nswindow contentView];
   326     NSRect viewport = [contentView bounds];
   327 
   328     /* This gives us the correct viewport for a Retina-enabled view, only
   329      * supported on 10.7+. */
   330     if ([contentView respondsToSelector:@selector(convertRectToBacking:)]) {
   331         viewport = [contentView convertRectToBacking:viewport];
   332     }
   333 
   334     if (w) {
   335         *w = viewport.size.width;
   336     }
   337 
   338     if (h) {
   339         *h = viewport.size.height;
   340     }
   341 }
   342 
   343 int
   344 Cocoa_GL_SetSwapInterval(_THIS, int interval)
   345 { @autoreleasepool
   346 {
   347     NSOpenGLContext *nscontext;
   348     GLint value;
   349     int status;
   350 
   351     if (interval < 0) {  /* no extension for this on Mac OS X at the moment. */
   352         return SDL_SetError("Late swap tearing currently unsupported");
   353     }
   354 
   355     nscontext = (NSOpenGLContext*)SDL_GL_GetCurrentContext();
   356     if (nscontext != nil) {
   357         value = interval;
   358         [nscontext setValues:&value forParameter:NSOpenGLCPSwapInterval];
   359         status = 0;
   360     } else {
   361         status = SDL_SetError("No current OpenGL context");
   362     }
   363 
   364     return status;
   365 }}
   366 
   367 int
   368 Cocoa_GL_GetSwapInterval(_THIS)
   369 { @autoreleasepool
   370 {
   371     NSOpenGLContext *nscontext;
   372     GLint value;
   373     int status = 0;
   374 
   375     nscontext = (NSOpenGLContext*)SDL_GL_GetCurrentContext();
   376     if (nscontext != nil) {
   377         [nscontext getValues:&value forParameter:NSOpenGLCPSwapInterval];
   378         status = (int)value;
   379     }
   380 
   381     return status;
   382 }}
   383 
   384 void
   385 Cocoa_GL_SwapWindow(_THIS, SDL_Window * window)
   386 { @autoreleasepool
   387 {
   388     SDLOpenGLContext* nscontext = (SDLOpenGLContext*)SDL_GL_GetCurrentContext();
   389     [nscontext flushBuffer];
   390     [nscontext updateIfNeeded];
   391 }}
   392 
   393 void
   394 Cocoa_GL_DeleteContext(_THIS, SDL_GLContext context)
   395 { @autoreleasepool
   396 {
   397     SDLOpenGLContext *nscontext = (SDLOpenGLContext *)context;
   398 
   399     [nscontext setWindow:NULL];
   400     [nscontext release];
   401 }}
   402 
   403 #endif /* SDL_VIDEO_OPENGL_CGL */
   404 
   405 /* vi: set ts=4 sw=4 expandtab: */