Mac: Don't -[NSOpenGLContext update] on (potentially) the wrong thread.
authorJørgen P. Tjernø <jorgen@valvesoftware.com>
Wed, 07 Aug 2013 16:29:21 -0700
changeset 75946abcf951af68
parent 7593 20298a0d8631
child 7595 ede2237fcebf
Mac: Don't -[NSOpenGLContext update] on (potentially) the wrong thread.

If the user is using their context from a non-main thread, we could be
calling -[NSOpenGLContext update] on our thread, while they were
accessing it on their thread.

With this change, we schedule updates when the event comes in on the
main thread, and act on them when the user calls SDL_GL_MakeCurrent or
SDL_GL_SwapWindow.
src/video/cocoa/SDL_cocoaopengl.h
src/video/cocoa/SDL_cocoaopengl.m
src/video/cocoa/SDL_cocoawindow.h
src/video/cocoa/SDL_cocoawindow.m
     1.1 --- a/src/video/cocoa/SDL_cocoaopengl.h	Wed Aug 07 16:29:15 2013 -0700
     1.2 +++ b/src/video/cocoa/SDL_cocoaopengl.h	Wed Aug 07 16:29:21 2013 -0700
     1.3 @@ -25,11 +25,26 @@
     1.4  
     1.5  #if SDL_VIDEO_OPENGL_CGL
     1.6  
     1.7 +#include "SDL_atomic.h"
     1.8 +#import <Cocoa/Cocoa.h>
     1.9 +
    1.10  struct SDL_GLDriverData
    1.11  {
    1.12      int initialized;
    1.13  };
    1.14  
    1.15 +@interface SDLOpenGLContext : NSOpenGLContext {
    1.16 +    SDL_atomic_t dirty;
    1.17 +}
    1.18 +
    1.19 +- (id)initWithFormat:(NSOpenGLPixelFormat *)format
    1.20 +        shareContext:(NSOpenGLContext *)share;
    1.21 +- (void)scheduleUpdate;
    1.22 +- (void)updateIfNeeded;
    1.23 +
    1.24 +@end
    1.25 +
    1.26 +
    1.27  /* OpenGL functions */
    1.28  extern int Cocoa_GL_LoadLibrary(_THIS, const char *path);
    1.29  extern void *Cocoa_GL_GetProcAddress(_THIS, const char *proc);
     2.1 --- a/src/video/cocoa/SDL_cocoaopengl.m	Wed Aug 07 16:29:15 2013 -0700
     2.2 +++ b/src/video/cocoa/SDL_cocoaopengl.m	Wed Aug 07 16:29:21 2013 -0700
     2.3 @@ -24,6 +24,7 @@
     2.4  
     2.5  #if SDL_VIDEO_OPENGL_CGL
     2.6  #include "SDL_cocoavideo.h"
     2.7 +#include "SDL_cocoaopengl.h"
     2.8  
     2.9  #include <OpenGL/CGLTypes.h>
    2.10  #include <OpenGL/OpenGL.h>
    2.11 @@ -45,6 +46,40 @@
    2.12  #define kCGLOGLPVersion_3_2_Core 0x3200
    2.13  #endif
    2.14  
    2.15 +@implementation SDLOpenGLContext : NSOpenGLContext
    2.16 +
    2.17 +- (id)initWithFormat:(NSOpenGLPixelFormat *)format
    2.18 +        shareContext:(NSOpenGLContext *)share
    2.19 +{
    2.20 +    SDL_AtomicSet(&self->dirty, 0);
    2.21 +    return [super initWithFormat:format shareContext:share];
    2.22 +}
    2.23 +
    2.24 +- (void)scheduleUpdate
    2.25 +{
    2.26 +    SDL_AtomicAdd(&self->dirty, 1);
    2.27 +}
    2.28 +
    2.29 +/* This should only be called on the thread on which a user is using the context. */
    2.30 +- (void)updateIfNeeded
    2.31 +{
    2.32 +    int value = SDL_AtomicSet(&self->dirty, 0);
    2.33 +    if (value > 0) {
    2.34 +        /* We call the real underlying update here, since -[SDLOpenGLContext update] just calls us. */
    2.35 +        [super update];
    2.36 +    }
    2.37 +}
    2.38 +
    2.39 +/* This should only be called on the thread on which a user is using the context. */
    2.40 +- (void)update
    2.41 +{
    2.42 +    /* This ensures that regular 'update' calls clear the atomic dirty flag. */
    2.43 +    [self scheduleUpdate];
    2.44 +    [self updateIfNeeded];
    2.45 +}
    2.46 +
    2.47 +@end
    2.48 +
    2.49  
    2.50  int
    2.51  Cocoa_GL_LoadLibrary(_THIS, const char *path)
    2.52 @@ -89,7 +124,7 @@
    2.53      SDL_DisplayData *displaydata = (SDL_DisplayData *)display->driverdata;
    2.54      NSOpenGLPixelFormatAttribute attr[32];
    2.55      NSOpenGLPixelFormat *fmt;
    2.56 -    NSOpenGLContext *context;
    2.57 +    SDLOpenGLContext *context;
    2.58      NSOpenGLContext *share_context = nil;
    2.59      int i = 0;
    2.60  
    2.61 @@ -181,7 +216,7 @@
    2.62          share_context = (NSOpenGLContext*)SDL_GL_GetCurrentContext();
    2.63      }
    2.64  
    2.65 -    context = [[NSOpenGLContext alloc] initWithFormat:fmt shareContext:share_context];
    2.66 +    context = [[SDLOpenGLContext alloc] initWithFormat:fmt shareContext:share_context];
    2.67  
    2.68      [fmt release];
    2.69  
    2.70 @@ -210,13 +245,14 @@
    2.71  
    2.72      if (context) {
    2.73          SDL_WindowData *windowdata = (SDL_WindowData *)window->driverdata;
    2.74 -        NSOpenGLContext *nscontext = (NSOpenGLContext *)context;
    2.75 -
    2.76 +        SDLOpenGLContext *nscontext = (SDLOpenGLContext *)context;
    2.77          windowdata->nscontext = nscontext;
    2.78          if ([nscontext view] != [windowdata->nswindow contentView]) {
    2.79              [nscontext setView:[windowdata->nswindow contentView]];
    2.80 -            [nscontext update];
    2.81 +            [nscontext scheduleUpdate];
    2.82          }
    2.83 +
    2.84 +        [nscontext updateIfNeeded];
    2.85          [nscontext makeCurrentContext];
    2.86      } else {
    2.87          [NSOpenGLContext clearCurrentContext];
    2.88 @@ -236,7 +272,7 @@
    2.89  
    2.90      pool = [[NSAutoreleasePool alloc] init];
    2.91  
    2.92 -    nscontext = [NSOpenGLContext currentContext];
    2.93 +    nscontext = (NSOpenGLContext*)SDL_GL_GetCurrentContext();
    2.94      if (nscontext != nil) {
    2.95          value = interval;
    2.96          [nscontext setValues:&value forParameter:NSOpenGLCPSwapInterval];
    2.97 @@ -259,7 +295,7 @@
    2.98  
    2.99      pool = [[NSAutoreleasePool alloc] init];
   2.100  
   2.101 -    nscontext = [NSOpenGLContext currentContext];
   2.102 +    nscontext = (NSOpenGLContext*)SDL_GL_GetCurrentContext();
   2.103      if (nscontext != nil) {
   2.104          [nscontext getValues:&value forParameter:NSOpenGLCPSwapInterval];
   2.105          status = (int)value;
   2.106 @@ -274,11 +310,12 @@
   2.107  {
   2.108      NSAutoreleasePool *pool;
   2.109      SDL_WindowData *windowdata = (SDL_WindowData *)window->driverdata;
   2.110 -    NSOpenGLContext *nscontext = windowdata->nscontext;
   2.111 +    SDLOpenGLContext *nscontext = windowdata->nscontext;
   2.112  
   2.113      pool = [[NSAutoreleasePool alloc] init];
   2.114  
   2.115      [nscontext flushBuffer];
   2.116 +    [nscontext updateIfNeeded];
   2.117  
   2.118      [pool release];
   2.119  }
     3.1 --- a/src/video/cocoa/SDL_cocoawindow.h	Wed Aug 07 16:29:15 2013 -0700
     3.2 +++ b/src/video/cocoa/SDL_cocoawindow.h	Wed Aug 07 16:29:21 2013 -0700
     3.3 @@ -77,11 +77,13 @@
     3.4  @end
     3.5  /* *INDENT-ON* */
     3.6  
     3.7 +@class SDLOpenGLContext;
     3.8 +
     3.9  struct SDL_WindowData
    3.10  {
    3.11      SDL_Window *window;
    3.12      NSWindow *nswindow;
    3.13 -    NSOpenGLContext *nscontext;
    3.14 +    SDLOpenGLContext *nscontext;
    3.15      SDL_bool created;
    3.16      Cocoa_WindowListener *listener;
    3.17      struct SDL_VideoData *videodata;
     4.1 --- a/src/video/cocoa/SDL_cocoawindow.m	Wed Aug 07 16:29:15 2013 -0700
     4.2 +++ b/src/video/cocoa/SDL_cocoawindow.m	Wed Aug 07 16:29:21 2013 -0700
     4.3 @@ -32,6 +32,7 @@
     4.4  #include "SDL_cocoavideo.h"
     4.5  #include "SDL_cocoashape.h"
     4.6  #include "SDL_cocoamouse.h"
     4.7 +#include "SDL_cocoaopengl.h"
     4.8  
     4.9  
    4.10  static Uint32 s_moveHack;
    4.11 @@ -187,7 +188,6 @@
    4.12  - (void)windowDidMove:(NSNotification *)aNotification
    4.13  {
    4.14      int x, y;
    4.15 -    SDL_VideoDevice *device = SDL_GetVideoDevice();
    4.16      SDL_Window *window = _data->window;
    4.17      NSWindow *nswindow = _data->nswindow;
    4.18      NSRect rect = [nswindow contentRectForFrameRect:[nswindow frame]];
    4.19 @@ -211,16 +211,13 @@
    4.20      x = (int)rect.origin.x;
    4.21      y = (int)rect.origin.y;
    4.22  
    4.23 -    if (window == device->current_glwin) {
    4.24 -        [((NSOpenGLContext *) device->current_glctx) update];
    4.25 -    }
    4.26 +    [_data->nscontext scheduleUpdate];
    4.27  
    4.28      SDL_SendWindowEvent(window, SDL_WINDOWEVENT_MOVED, x, y);
    4.29  }
    4.30  
    4.31  - (void)windowDidResize:(NSNotification *)aNotification
    4.32  {
    4.33 -    SDL_VideoDevice *device = SDL_GetVideoDevice();
    4.34      int x, y, w, h;
    4.35      NSRect rect = [_data->nswindow contentRectForFrameRect:[_data->nswindow frame]];
    4.36      ConvertNSRect(&rect);
    4.37 @@ -231,9 +228,7 @@
    4.38      if (SDL_IsShapedWindow(_data->window))
    4.39          Cocoa_ResizeWindowShape(_data->window);
    4.40  
    4.41 -    if (_data->window == device->current_glwin) {
    4.42 -        [((NSOpenGLContext *) device->current_glctx) update];
    4.43 -    }
    4.44 +    [_data->nscontext scheduleUpdate];
    4.45  
    4.46      /* The window can move during a resize event, such as when maximizing
    4.47         or resizing from a corner */
    4.48 @@ -788,7 +783,8 @@
    4.49  Cocoa_SetWindowPosition(_THIS, SDL_Window * window)
    4.50  {
    4.51      NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    4.52 -    NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
    4.53 +    SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
    4.54 +    NSWindow *nswindow = windata->nswindow;
    4.55      NSRect rect;
    4.56      Uint32 moveHack;
    4.57  
    4.58 @@ -803,9 +799,7 @@
    4.59      [nswindow setFrameOrigin:rect.origin];
    4.60      s_moveHack = moveHack;
    4.61  
    4.62 -    if (window == _this->current_glwin) {
    4.63 -        [((NSOpenGLContext *) _this->current_glctx) update];
    4.64 -    }
    4.65 +    [windata->nscontext scheduleUpdate];
    4.66  
    4.67      [pool release];
    4.68  }
    4.69 @@ -822,9 +816,7 @@
    4.70      size.height = window->h;
    4.71      [nswindow setContentSize:size];
    4.72  
    4.73 -    if (window == _this->current_glwin) {
    4.74 -        [((NSOpenGLContext *) _this->current_glctx) update];
    4.75 -    }
    4.76 +    [windata->nscontext scheduleUpdate];
    4.77  
    4.78      [pool release];
    4.79  }
    4.80 @@ -906,13 +898,12 @@
    4.81  Cocoa_MaximizeWindow(_THIS, SDL_Window * window)
    4.82  {
    4.83      NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    4.84 -    NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
    4.85 +    SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
    4.86 +    NSWindow *nswindow = windata->nswindow;
    4.87  
    4.88      [nswindow zoom:nil];
    4.89  
    4.90 -    if (window == _this->current_glwin) {
    4.91 -        [((NSOpenGLContext *) _this->current_glctx) update];
    4.92 -    }
    4.93 +    [windata->nscontext scheduleUpdate];
    4.94  
    4.95      [pool release];
    4.96  }
    4.97 @@ -1049,9 +1040,7 @@
    4.98      [nswindow makeKeyAndOrderFront:nil];
    4.99      [data->listener resumeVisibleObservation];
   4.100  
   4.101 -    if (window == _this->current_glwin) {
   4.102 -        [((NSOpenGLContext *) _this->current_glctx) update];
   4.103 -    }
   4.104 +    [data->nscontext scheduleUpdate];
   4.105  
   4.106      [pool release];
   4.107  }