src/video/cocoa/SDL_cocoaopengl.m
author Sam Lantinga <slouken@libsdl.org>
Fri, 09 Dec 2016 01:47:43 -0800
changeset 10690 23a825f341e6
parent 10008 fb4e35d0d523
child 10737 3406a0f8b041
permissions -rw-r--r--
Fixed bug 3513 - SDL_GL_SwapWindow does not return error status

Return an error code from SDL_GL_SwapWindow(), like the other SDL APIs.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2016 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     attr[i++] = NSOpenGLPFAAllowOfflineRenderers;
   177 
   178     /* specify a profile if we're on Lion (10.7) or later. */
   179     if (lion_or_later) {
   180         NSOpenGLPixelFormatAttribute profile = NSOpenGLProfileVersionLegacy;
   181         if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_CORE) {
   182             profile = NSOpenGLProfileVersion3_2Core;
   183         }
   184         attr[i++] = NSOpenGLPFAOpenGLProfile;
   185         attr[i++] = profile;
   186     }
   187 
   188     attr[i++] = NSOpenGLPFAColorSize;
   189     attr[i++] = SDL_BYTESPERPIXEL(display->current_mode.format)*8;
   190 
   191     attr[i++] = NSOpenGLPFADepthSize;
   192     attr[i++] = _this->gl_config.depth_size;
   193 
   194     if (_this->gl_config.double_buffer) {
   195         attr[i++] = NSOpenGLPFADoubleBuffer;
   196     }
   197 
   198     if (_this->gl_config.stereo) {
   199         attr[i++] = NSOpenGLPFAStereo;
   200     }
   201 
   202     if (_this->gl_config.stencil_size) {
   203         attr[i++] = NSOpenGLPFAStencilSize;
   204         attr[i++] = _this->gl_config.stencil_size;
   205     }
   206 
   207     if ((_this->gl_config.accum_red_size +
   208          _this->gl_config.accum_green_size +
   209          _this->gl_config.accum_blue_size +
   210          _this->gl_config.accum_alpha_size) > 0) {
   211         attr[i++] = NSOpenGLPFAAccumSize;
   212         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;
   213     }
   214 
   215     if (_this->gl_config.multisamplebuffers) {
   216         attr[i++] = NSOpenGLPFASampleBuffers;
   217         attr[i++] = _this->gl_config.multisamplebuffers;
   218     }
   219 
   220     if (_this->gl_config.multisamplesamples) {
   221         attr[i++] = NSOpenGLPFASamples;
   222         attr[i++] = _this->gl_config.multisamplesamples;
   223         attr[i++] = NSOpenGLPFANoRecovery;
   224     }
   225 
   226     if (_this->gl_config.accelerated >= 0) {
   227         if (_this->gl_config.accelerated) {
   228             attr[i++] = NSOpenGLPFAAccelerated;
   229         } else {
   230             attr[i++] = NSOpenGLPFARendererID;
   231             attr[i++] = kCGLRendererGenericFloatID;
   232         }
   233     }
   234 
   235     attr[i++] = NSOpenGLPFAScreenMask;
   236     attr[i++] = CGDisplayIDToOpenGLDisplayMask(displaydata->display);
   237     attr[i] = 0;
   238 
   239     fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr];
   240     if (fmt == nil) {
   241         SDL_SetError("Failed creating OpenGL pixel format");
   242         return NULL;
   243     }
   244 
   245     if (_this->gl_config.share_with_current_context) {
   246         share_context = (NSOpenGLContext*)SDL_GL_GetCurrentContext();
   247     }
   248 
   249     context = [[SDLOpenGLContext alloc] initWithFormat:fmt shareContext:share_context];
   250 
   251     [fmt release];
   252 
   253     if (context == nil) {
   254         SDL_SetError("Failed creating OpenGL context");
   255         return NULL;
   256     }
   257 
   258     if ( Cocoa_GL_MakeCurrent(_this, window, context) < 0 ) {
   259         Cocoa_GL_DeleteContext(_this, context);
   260         SDL_SetError("Failed making OpenGL context current");
   261         return NULL;
   262     }
   263 
   264     if (_this->gl_config.major_version < 3 &&
   265         _this->gl_config.profile_mask == 0 &&
   266         _this->gl_config.flags == 0) {
   267         /* This is a legacy profile, so to match other backends, we're done. */
   268     } else {
   269         const GLubyte *(APIENTRY * glGetStringFunc)(GLenum);
   270 
   271         glGetStringFunc = (const GLubyte *(APIENTRY *)(GLenum)) SDL_GL_GetProcAddress("glGetString");
   272         if (!glGetStringFunc) {
   273             Cocoa_GL_DeleteContext(_this, context);
   274             SDL_SetError ("Failed getting OpenGL glGetString entry point");
   275             return NULL;
   276         }
   277 
   278         glversion = (const char *)glGetStringFunc(GL_VERSION);
   279         if (glversion == NULL) {
   280             Cocoa_GL_DeleteContext(_this, context);
   281             SDL_SetError ("Failed getting OpenGL context version");
   282             return NULL;
   283         }
   284 
   285         if (SDL_sscanf(glversion, "%d.%d", &glversion_major, &glversion_minor) != 2) {
   286             Cocoa_GL_DeleteContext(_this, context);
   287             SDL_SetError ("Failed parsing OpenGL context version");
   288             return NULL;
   289         }
   290 
   291         if ((glversion_major < _this->gl_config.major_version) ||
   292            ((glversion_major == _this->gl_config.major_version) && (glversion_minor < _this->gl_config.minor_version))) {
   293             Cocoa_GL_DeleteContext(_this, context);
   294             SDL_SetError ("Failed creating OpenGL context at version requested");
   295             return NULL;
   296         }
   297 
   298         /* In the future we'll want to do this, but to match other platforms
   299            we'll leave the OpenGL version the way it is for now
   300          */
   301         /*_this->gl_config.major_version = glversion_major;*/
   302         /*_this->gl_config.minor_version = glversion_minor;*/
   303     }
   304     return context;
   305 }}
   306 
   307 int
   308 Cocoa_GL_MakeCurrent(_THIS, SDL_Window * window, SDL_GLContext context)
   309 { @autoreleasepool
   310 {
   311     if (context) {
   312         SDLOpenGLContext *nscontext = (SDLOpenGLContext *)context;
   313         [nscontext setWindow:window];
   314         [nscontext updateIfNeeded];
   315         [nscontext makeCurrentContext];
   316     } else {
   317         [NSOpenGLContext clearCurrentContext];
   318     }
   319 
   320     return 0;
   321 }}
   322 
   323 void
   324 Cocoa_GL_GetDrawableSize(_THIS, SDL_Window * window, int * w, int * h)
   325 {
   326     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
   327     NSView *contentView = [windata->nswindow contentView];
   328     NSRect viewport = [contentView bounds];
   329 
   330     /* This gives us the correct viewport for a Retina-enabled view, only
   331      * supported on 10.7+. */
   332     if ([contentView respondsToSelector:@selector(convertRectToBacking:)]) {
   333         viewport = [contentView convertRectToBacking:viewport];
   334     }
   335 
   336     if (w) {
   337         *w = viewport.size.width;
   338     }
   339 
   340     if (h) {
   341         *h = viewport.size.height;
   342     }
   343 }
   344 
   345 int
   346 Cocoa_GL_SetSwapInterval(_THIS, int interval)
   347 { @autoreleasepool
   348 {
   349     NSOpenGLContext *nscontext;
   350     GLint value;
   351     int status;
   352 
   353     if (interval < 0) {  /* no extension for this on Mac OS X at the moment. */
   354         return SDL_SetError("Late swap tearing currently unsupported");
   355     }
   356 
   357     nscontext = (NSOpenGLContext*)SDL_GL_GetCurrentContext();
   358     if (nscontext != nil) {
   359         value = interval;
   360         [nscontext setValues:&value forParameter:NSOpenGLCPSwapInterval];
   361         status = 0;
   362     } else {
   363         status = SDL_SetError("No current OpenGL context");
   364     }
   365 
   366     return status;
   367 }}
   368 
   369 int
   370 Cocoa_GL_GetSwapInterval(_THIS)
   371 { @autoreleasepool
   372 {
   373     NSOpenGLContext *nscontext;
   374     GLint value;
   375     int status = 0;
   376 
   377     nscontext = (NSOpenGLContext*)SDL_GL_GetCurrentContext();
   378     if (nscontext != nil) {
   379         [nscontext getValues:&value forParameter:NSOpenGLCPSwapInterval];
   380         status = (int)value;
   381     }
   382 
   383     return status;
   384 }}
   385 
   386 int
   387 Cocoa_GL_SwapWindow(_THIS, SDL_Window * window)
   388 { @autoreleasepool
   389 {
   390     SDLOpenGLContext* nscontext = (SDLOpenGLContext*)SDL_GL_GetCurrentContext();
   391     [nscontext flushBuffer];
   392     [nscontext updateIfNeeded];
   393     return 0;
   394 }}
   395 
   396 void
   397 Cocoa_GL_DeleteContext(_THIS, SDL_GLContext context)
   398 { @autoreleasepool
   399 {
   400     SDLOpenGLContext *nscontext = (SDLOpenGLContext *)context;
   401 
   402     [nscontext setWindow:NULL];
   403     [nscontext release];
   404 }}
   405 
   406 #endif /* SDL_VIDEO_OPENGL_CGL */
   407 
   408 /* vi: set ts=4 sw=4 expandtab: */