src/video/cocoa/SDL_cocoaopengl.m
author Jørgen P. Tjernø <jorgen@valvesoftware.com>
Tue, 25 Feb 2014 15:28:12 -0800
changeset 8258 569354dec4e9
parent 8149 681eb46b8ac4
child 8276 9bd65b58278e
permissions -rw-r--r--
Mac: Immediately update current OpenGL context's shape.

Previously we were postponing our -[NSOpenGLContext update] call to the next
SDL_GL_SwapWindow, even if the context was current on the current thread. This
changes it so that we will do the update immediately if it's the current
context.

If you're rendering on another thread, you need to call SDL_GL_SwapWindow once
after a resize event to ensure your drawable will produce non-garbage data.

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