src/video/cocoa/SDL_cocoaopengl.m
author Edward Rudd <urkle@outoforder.cc>
Fri, 20 Sep 2013 13:43:00 -0400
changeset 7746 6a05d7352575
parent 7743 360acf8c1526
child 7894 bb624b1348da
permissions -rw-r--r--
add in High DPI support (aka Retina)

- based on Jørgen's patch with a few bug fixes
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2013 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_config.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 #if MAC_OS_X_VERSION_MAX_ALLOWED < 1070
    39 /* New methods for converting to and from backing store pixels, taken from
    40  * AppKite/NSView.h in 10.8 SDK. */
    41 @interface NSView (Backing)
    42 - (NSPoint)convertPointToBacking:(NSPoint)aPoint;
    43 - (NSPoint)convertPointFromBacking:(NSPoint)aPoint;
    44 - (NSSize)convertSizeToBacking:(NSSize)aSize;
    45 - (NSSize)convertSizeFromBacking:(NSSize)aSize;
    46 - (NSRect)convertRectToBacking:(NSRect)aRect;
    47 - (NSRect)convertRectFromBacking:(NSRect)aRect;
    48 @end
    49 #endif
    50 
    51 #ifndef kCGLPFAOpenGLProfile
    52 #define kCGLPFAOpenGLProfile 99
    53 #endif
    54 #ifndef kCGLOGLPVersion_Legacy
    55 #define kCGLOGLPVersion_Legacy 0x1000
    56 #endif
    57 #ifndef kCGLOGLPVersion_3_2_Core
    58 #define kCGLOGLPVersion_3_2_Core 0x3200
    59 #endif
    60 
    61 @implementation SDLOpenGLContext : NSOpenGLContext
    62 
    63 - (id)initWithFormat:(NSOpenGLPixelFormat *)format
    64         shareContext:(NSOpenGLContext *)share
    65 {
    66     self = [super initWithFormat:format shareContext:share];
    67     if (self) {
    68         SDL_AtomicSet(&self->dirty, 0);
    69         self->window = NULL;
    70     }
    71     return self;
    72 }
    73 
    74 - (void)scheduleUpdate
    75 {
    76     SDL_AtomicAdd(&self->dirty, 1);
    77 }
    78 
    79 /* This should only be called on the thread on which a user is using the context. */
    80 - (void)updateIfNeeded
    81 {
    82     int value = SDL_AtomicSet(&self->dirty, 0);
    83     if (value > 0) {
    84         /* We call the real underlying update here, since -[SDLOpenGLContext update] just calls us. */
    85         [super update];
    86     }
    87 }
    88 
    89 /* This should only be called on the thread on which a user is using the context. */
    90 - (void)update
    91 {
    92     /* This ensures that regular 'update' calls clear the atomic dirty flag. */
    93     [self scheduleUpdate];
    94     [self updateIfNeeded];
    95 }
    96 
    97 /* Updates the drawable for the contexts and manages related state. */
    98 - (void)setWindow:(SDL_Window *)newWindow
    99 {
   100     if (self->window) {
   101         SDL_WindowData *oldwindowdata = (SDL_WindowData *)self->window->driverdata;
   102 
   103         /* Make sure to remove us from the old window's context list, or we'll get scheduled updates from it too. */
   104         NSMutableArray *contexts = oldwindowdata->nscontexts;
   105         @synchronized (contexts) {
   106             [contexts removeObject:self];
   107         }
   108     }
   109 
   110     self->window = newWindow;
   111 
   112     if (newWindow) {
   113         SDL_WindowData *windowdata = (SDL_WindowData *)newWindow->driverdata;
   114 
   115         /* Now sign up for scheduled updates for the new window. */
   116         NSMutableArray *contexts = windowdata->nscontexts;
   117         @synchronized (contexts) {
   118             [contexts addObject:self];
   119         }
   120 
   121         if ([self view] != [windowdata->nswindow contentView]) {
   122             [self setView:[windowdata->nswindow contentView]];
   123             [self scheduleUpdate];
   124         }
   125     } else {
   126         [self clearDrawable];
   127         [self scheduleUpdate];
   128     }
   129 }
   130 
   131 @end
   132 
   133 
   134 int
   135 Cocoa_GL_LoadLibrary(_THIS, const char *path)
   136 {
   137     /* Load the OpenGL library */
   138     if (path == NULL) {
   139         path = SDL_getenv("SDL_OPENGL_LIBRARY");
   140     }
   141     if (path == NULL) {
   142         path = DEFAULT_OPENGL;
   143     }
   144     _this->gl_config.dll_handle = SDL_LoadObject(path);
   145     if (!_this->gl_config.dll_handle) {
   146         return -1;
   147     }
   148     SDL_strlcpy(_this->gl_config.driver_path, path,
   149                 SDL_arraysize(_this->gl_config.driver_path));
   150     return 0;
   151 }
   152 
   153 void *
   154 Cocoa_GL_GetProcAddress(_THIS, const char *proc)
   155 {
   156     return SDL_LoadFunction(_this->gl_config.dll_handle, proc);
   157 }
   158 
   159 void
   160 Cocoa_GL_UnloadLibrary(_THIS)
   161 {
   162     SDL_UnloadObject(_this->gl_config.dll_handle);
   163     _this->gl_config.dll_handle = NULL;
   164 }
   165 
   166 SDL_GLContext
   167 Cocoa_GL_CreateContext(_THIS, SDL_Window * window)
   168 {
   169     const int wantver = (_this->gl_config.major_version << 8) |
   170                         (_this->gl_config.minor_version);
   171     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
   172     NSAutoreleasePool *pool;
   173     SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
   174     SDL_DisplayData *displaydata = (SDL_DisplayData *)display->driverdata;
   175     NSOpenGLPixelFormatAttribute attr[32];
   176     NSOpenGLPixelFormat *fmt;
   177     SDLOpenGLContext *context;
   178     NSOpenGLContext *share_context = nil;
   179     int i = 0;
   180 
   181     if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) {
   182         SDL_SetError ("OpenGL ES not supported on this platform");
   183         return NULL;
   184     }
   185 
   186     /* Sadly, we'll have to update this as life progresses, since we need to
   187        set an enum for context profiles, not a context version number */
   188     if (wantver > 0x0302) {
   189         SDL_SetError ("OpenGL > 3.2 is not supported on this platform");
   190         return NULL;
   191     }
   192 
   193     pool = [[NSAutoreleasePool alloc] init];
   194 
   195     /* specify a profile if we're on Lion (10.7) or later. */
   196     if (data->osversion >= 0x1070) {
   197         NSOpenGLPixelFormatAttribute profile = kCGLOGLPVersion_Legacy;
   198         if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_CORE) {
   199             if (wantver == 0x0302) {
   200                 profile = kCGLOGLPVersion_3_2_Core;
   201             }
   202         }
   203         attr[i++] = kCGLPFAOpenGLProfile;
   204         attr[i++] = profile;
   205     }
   206 
   207     attr[i++] = NSOpenGLPFAColorSize;
   208     attr[i++] = SDL_BYTESPERPIXEL(display->current_mode.format)*8;
   209 
   210     attr[i++] = NSOpenGLPFADepthSize;
   211     attr[i++] = _this->gl_config.depth_size;
   212 
   213     if (_this->gl_config.double_buffer) {
   214         attr[i++] = NSOpenGLPFADoubleBuffer;
   215     }
   216 
   217     if (_this->gl_config.stereo) {
   218         attr[i++] = NSOpenGLPFAStereo;
   219     }
   220 
   221     if (_this->gl_config.stencil_size) {
   222         attr[i++] = NSOpenGLPFAStencilSize;
   223         attr[i++] = _this->gl_config.stencil_size;
   224     }
   225 
   226     if ((_this->gl_config.accum_red_size +
   227          _this->gl_config.accum_green_size +
   228          _this->gl_config.accum_blue_size +
   229          _this->gl_config.accum_alpha_size) > 0) {
   230         attr[i++] = NSOpenGLPFAAccumSize;
   231         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;
   232     }
   233 
   234     if (_this->gl_config.multisamplebuffers) {
   235         attr[i++] = NSOpenGLPFASampleBuffers;
   236         attr[i++] = _this->gl_config.multisamplebuffers;
   237     }
   238 
   239     if (_this->gl_config.multisamplesamples) {
   240         attr[i++] = NSOpenGLPFASamples;
   241         attr[i++] = _this->gl_config.multisamplesamples;
   242         attr[i++] = NSOpenGLPFANoRecovery;
   243     }
   244 
   245     if (_this->gl_config.accelerated >= 0) {
   246         if (_this->gl_config.accelerated) {
   247             attr[i++] = NSOpenGLPFAAccelerated;
   248         } else {
   249             attr[i++] = NSOpenGLPFARendererID;
   250             attr[i++] = kCGLRendererGenericFloatID;
   251         }
   252     }
   253 
   254     attr[i++] = NSOpenGLPFAScreenMask;
   255     attr[i++] = CGDisplayIDToOpenGLDisplayMask(displaydata->display);
   256     attr[i] = 0;
   257 
   258     fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr];
   259     if (fmt == nil) {
   260         SDL_SetError ("Failed creating OpenGL pixel format");
   261         [pool release];
   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         [pool release];
   276         return NULL;
   277     }
   278 
   279     [pool release];
   280 
   281     if ( Cocoa_GL_MakeCurrent(_this, window, context) < 0 ) {
   282         Cocoa_GL_DeleteContext(_this, context);
   283         return NULL;
   284     }
   285 
   286     return context;
   287 }
   288 
   289 int
   290 Cocoa_GL_MakeCurrent(_THIS, SDL_Window * window, SDL_GLContext context)
   291 {
   292     NSAutoreleasePool *pool;
   293 
   294     pool = [[NSAutoreleasePool alloc] init];
   295 
   296     if (context) {
   297         SDLOpenGLContext *nscontext = (SDLOpenGLContext *)context;
   298         [nscontext setWindow:window];
   299         [nscontext updateIfNeeded];
   300         [nscontext makeCurrentContext];
   301     } else {
   302         [NSOpenGLContext clearCurrentContext];
   303     }
   304 
   305     [pool release];
   306     return 0;
   307 }
   308 
   309 void
   310 Cocoa_GL_GetDrawableSize(_THIS, SDL_Window * window, int * w, int * h)
   311 {
   312     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
   313     NSView *contentView = [windata->nswindow contentView];
   314     NSRect viewport = [contentView bounds];
   315 
   316     /* This gives us the correct viewport for a Retina-enabled view, only
   317      * supported on 10.7+. */
   318     if ([contentView respondsToSelector:@selector(convertRectToBacking:)]) {
   319         viewport = [contentView convertRectToBacking:viewport];
   320     }
   321 
   322     if (w) {
   323         *w = viewport.size.width;
   324     }
   325 
   326     if (h) {
   327         *h = viewport.size.height;
   328     }
   329 }
   330 
   331 int
   332 Cocoa_GL_SetSwapInterval(_THIS, int interval)
   333 {
   334     NSAutoreleasePool *pool;
   335     NSOpenGLContext *nscontext;
   336     GLint value;
   337     int status;
   338 
   339     if (interval < 0) {  /* no extension for this on Mac OS X at the moment. */
   340         return SDL_SetError("Late swap tearing currently unsupported");
   341     }
   342 
   343     pool = [[NSAutoreleasePool alloc] init];
   344 
   345     nscontext = (NSOpenGLContext*)SDL_GL_GetCurrentContext();
   346     if (nscontext != nil) {
   347         value = interval;
   348         [nscontext setValues:&value forParameter:NSOpenGLCPSwapInterval];
   349         status = 0;
   350     } else {
   351         status = SDL_SetError("No current OpenGL context");
   352     }
   353 
   354     [pool release];
   355     return status;
   356 }
   357 
   358 int
   359 Cocoa_GL_GetSwapInterval(_THIS)
   360 {
   361     NSAutoreleasePool *pool;
   362     NSOpenGLContext *nscontext;
   363     GLint value;
   364     int status = 0;
   365 
   366     pool = [[NSAutoreleasePool alloc] init];
   367 
   368     nscontext = (NSOpenGLContext*)SDL_GL_GetCurrentContext();
   369     if (nscontext != nil) {
   370         [nscontext getValues:&value forParameter:NSOpenGLCPSwapInterval];
   371         status = (int)value;
   372     }
   373 
   374     [pool release];
   375     return status;
   376 }
   377 
   378 void
   379 Cocoa_GL_SwapWindow(_THIS, SDL_Window * window)
   380 {
   381     NSAutoreleasePool *pool;
   382 
   383     pool = [[NSAutoreleasePool alloc] init];
   384 
   385     SDLOpenGLContext* nscontext = (SDLOpenGLContext*)SDL_GL_GetCurrentContext();
   386     [nscontext flushBuffer];
   387     [nscontext updateIfNeeded];
   388 
   389     [pool release];
   390 }
   391 
   392 void
   393 Cocoa_GL_DeleteContext(_THIS, SDL_GLContext context)
   394 {
   395     NSAutoreleasePool *pool;
   396     SDLOpenGLContext *nscontext = (SDLOpenGLContext *)context;
   397 
   398     pool = [[NSAutoreleasePool alloc] init];
   399 
   400     [nscontext setWindow:NULL];
   401     [nscontext release];
   402 
   403     [pool release];
   404 }
   405 
   406 #endif /* SDL_VIDEO_OPENGL_CGL */
   407 
   408 /* vi: set ts=4 sw=4 expandtab: */