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