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