src/video/cocoa/SDL_cocoaopengl.m
author Jørgen P. Tjernø <jorgen@valvesoftware.com>
Wed, 07 Aug 2013 16:29:21 -0700
changeset 7594 6abcf951af68
parent 7412 50211a1fd557
child 7595 ede2237fcebf
permissions -rw-r--r--
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.
slouken@1936
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@6885
     3
  Copyright (C) 1997-2013 Sam Lantinga <slouken@libsdl.org>
slouken@1936
     4
slouken@5535
     5
  This software is provided 'as-is', without any express or implied
slouken@5535
     6
  warranty.  In no event will the authors be held liable for any damages
slouken@5535
     7
  arising from the use of this software.
slouken@1936
     8
slouken@5535
     9
  Permission is granted to anyone to use this software for any purpose,
slouken@5535
    10
  including commercial applications, and to alter it and redistribute it
slouken@5535
    11
  freely, subject to the following restrictions:
slouken@1936
    12
slouken@5535
    13
  1. The origin of this software must not be misrepresented; you must not
slouken@5535
    14
     claim that you wrote the original software. If you use this software
slouken@5535
    15
     in a product, an acknowledgment in the product documentation would be
slouken@5535
    16
     appreciated but is not required.
slouken@5535
    17
  2. Altered source versions must be plainly marked as such, and must not be
slouken@5535
    18
     misrepresented as being the original software.
slouken@5535
    19
  3. This notice may not be removed or altered from any source distribution.
slouken@1936
    20
*/
slouken@1936
    21
#include "SDL_config.h"
slouken@1936
    22
slouken@1936
    23
/* NSOpenGL implementation of SDL OpenGL support */
slouken@1936
    24
slouken@1952
    25
#if SDL_VIDEO_OPENGL_CGL
slouken@6044
    26
#include "SDL_cocoavideo.h"
jorgen@7594
    27
#include "SDL_cocoaopengl.h"
slouken@6044
    28
slouken@1936
    29
#include <OpenGL/CGLTypes.h>
slouken@2738
    30
#include <OpenGL/OpenGL.h>
slouken@3570
    31
#include <OpenGL/CGLRenderers.h>
slouken@1936
    32
slouken@1936
    33
#include "SDL_loadso.h"
slouken@1936
    34
#include "SDL_opengl.h"
slouken@1936
    35
icculus@6567
    36
#define DEFAULT_OPENGL  "/System/Library/Frameworks/OpenGL.framework/Libraries/libGL.dylib"
slouken@1936
    37
icculus@6567
    38
slouken@6952
    39
#ifndef kCGLPFAOpenGLProfile
icculus@6567
    40
#define kCGLPFAOpenGLProfile 99
slouken@6952
    41
#endif
slouken@6952
    42
#ifndef kCGLOGLPVersion_Legacy
icculus@6567
    43
#define kCGLOGLPVersion_Legacy 0x1000
slouken@6952
    44
#endif
slouken@6952
    45
#ifndef kCGLOGLPVersion_3_2_Core
icculus@6567
    46
#define kCGLOGLPVersion_3_2_Core 0x3200
icculus@6567
    47
#endif
icculus@6567
    48
jorgen@7594
    49
@implementation SDLOpenGLContext : NSOpenGLContext
jorgen@7594
    50
jorgen@7594
    51
- (id)initWithFormat:(NSOpenGLPixelFormat *)format
jorgen@7594
    52
        shareContext:(NSOpenGLContext *)share
jorgen@7594
    53
{
jorgen@7594
    54
    SDL_AtomicSet(&self->dirty, 0);
jorgen@7594
    55
    return [super initWithFormat:format shareContext:share];
jorgen@7594
    56
}
jorgen@7594
    57
jorgen@7594
    58
- (void)scheduleUpdate
jorgen@7594
    59
{
jorgen@7594
    60
    SDL_AtomicAdd(&self->dirty, 1);
jorgen@7594
    61
}
jorgen@7594
    62
jorgen@7594
    63
/* This should only be called on the thread on which a user is using the context. */
jorgen@7594
    64
- (void)updateIfNeeded
jorgen@7594
    65
{
jorgen@7594
    66
    int value = SDL_AtomicSet(&self->dirty, 0);
jorgen@7594
    67
    if (value > 0) {
jorgen@7594
    68
        /* We call the real underlying update here, since -[SDLOpenGLContext update] just calls us. */
jorgen@7594
    69
        [super update];
jorgen@7594
    70
    }
jorgen@7594
    71
}
jorgen@7594
    72
jorgen@7594
    73
/* This should only be called on the thread on which a user is using the context. */
jorgen@7594
    74
- (void)update
jorgen@7594
    75
{
jorgen@7594
    76
    /* This ensures that regular 'update' calls clear the atomic dirty flag. */
jorgen@7594
    77
    [self scheduleUpdate];
jorgen@7594
    78
    [self updateIfNeeded];
jorgen@7594
    79
}
jorgen@7594
    80
jorgen@7594
    81
@end
jorgen@7594
    82
slouken@1936
    83
slouken@1936
    84
int
slouken@1936
    85
Cocoa_GL_LoadLibrary(_THIS, const char *path)
slouken@1936
    86
{
slouken@3057
    87
    /* Load the OpenGL library */
slouken@1936
    88
    if (path == NULL) {
slouken@1952
    89
        path = SDL_getenv("SDL_OPENGL_LIBRARY");
slouken@1952
    90
    }
slouken@1952
    91
    if (path == NULL) {
slouken@1952
    92
        path = DEFAULT_OPENGL;
slouken@1936
    93
    }
slouken@1936
    94
    _this->gl_config.dll_handle = SDL_LoadObject(path);
slouken@1936
    95
    if (!_this->gl_config.dll_handle) {
slouken@1936
    96
        return -1;
slouken@1936
    97
    }
slouken@1936
    98
    SDL_strlcpy(_this->gl_config.driver_path, path,
slouken@1936
    99
                SDL_arraysize(_this->gl_config.driver_path));
slouken@1936
   100
    return 0;
slouken@1936
   101
}
slouken@1936
   102
slouken@1936
   103
void *
slouken@1936
   104
Cocoa_GL_GetProcAddress(_THIS, const char *proc)
slouken@1936
   105
{
slouken@1936
   106
    return SDL_LoadFunction(_this->gl_config.dll_handle, proc);
slouken@1936
   107
}
slouken@1936
   108
slouken@3057
   109
void
slouken@1936
   110
Cocoa_GL_UnloadLibrary(_THIS)
slouken@1936
   111
{
slouken@3057
   112
    SDL_UnloadObject(_this->gl_config.dll_handle);
slouken@3057
   113
    _this->gl_config.dll_handle = NULL;
slouken@1936
   114
}
slouken@1936
   115
slouken@1936
   116
SDL_GLContext
slouken@1936
   117
Cocoa_GL_CreateContext(_THIS, SDL_Window * window)
slouken@1936
   118
{
icculus@6567
   119
    const int wantver = (_this->gl_config.major_version << 8) |
icculus@6567
   120
                        (_this->gl_config.minor_version);
icculus@6567
   121
    SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
slouken@6848
   122
    NSAutoreleasePool *pool;
slouken@5246
   123
    SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
slouken@1936
   124
    SDL_DisplayData *displaydata = (SDL_DisplayData *)display->driverdata;
slouken@1936
   125
    NSOpenGLPixelFormatAttribute attr[32];
slouken@1936
   126
    NSOpenGLPixelFormat *fmt;
jorgen@7594
   127
    SDLOpenGLContext *context;
flibitijibibo@7152
   128
    NSOpenGLContext *share_context = nil;
slouken@1936
   129
    int i = 0;
slouken@1936
   130
icculus@6567
   131
    if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) {
icculus@6567
   132
        SDL_SetError ("OpenGL ES not supported on this platform");
icculus@6567
   133
        return NULL;
icculus@6567
   134
    }
icculus@6567
   135
icculus@6567
   136
    /* Sadly, we'll have to update this as life progresses, since we need to
icculus@6567
   137
       set an enum for context profiles, not a context version number */
icculus@6567
   138
    if (wantver > 0x0302) {
icculus@6567
   139
        SDL_SetError ("OpenGL > 3.2 is not supported on this platform");
icculus@6567
   140
        return NULL;
icculus@6567
   141
    }
icculus@6567
   142
slouken@6848
   143
    pool = [[NSAutoreleasePool alloc] init];
slouken@6848
   144
slouken@6848
   145
    /* specify a profile if we're on Lion (10.7) or later. */
slouken@6848
   146
    if (data->osversion >= 0x1070) {
slouken@6848
   147
        NSOpenGLPixelFormatAttribute profile = kCGLOGLPVersion_Legacy;
slouken@6848
   148
        if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_CORE) {
slouken@6848
   149
            if (wantver == 0x0302) {
slouken@6848
   150
                profile = kCGLOGLPVersion_3_2_Core;
alexey@6832
   151
            }
alexey@6832
   152
        }
slouken@6848
   153
        attr[i++] = kCGLPFAOpenGLProfile;
slouken@6848
   154
        attr[i++] = profile;
slouken@6848
   155
    }
slouken@1936
   156
slouken@6848
   157
    attr[i++] = NSOpenGLPFAColorSize;
slouken@6848
   158
    attr[i++] = SDL_BYTESPERPIXEL(display->current_mode.format)*8;
slouken@6848
   159
slouken@6848
   160
    attr[i++] = NSOpenGLPFADepthSize;
slouken@6848
   161
    attr[i++] = _this->gl_config.depth_size;
slouken@6848
   162
slouken@6848
   163
    if (_this->gl_config.double_buffer) {
slouken@6848
   164
        attr[i++] = NSOpenGLPFADoubleBuffer;
slouken@6848
   165
    }
slouken@6848
   166
slouken@6848
   167
    if (_this->gl_config.stereo) {
slouken@6848
   168
        attr[i++] = NSOpenGLPFAStereo;
slouken@6848
   169
    }
slouken@6848
   170
slouken@6848
   171
    if (_this->gl_config.stencil_size) {
slouken@6848
   172
        attr[i++] = NSOpenGLPFAStencilSize;
slouken@6848
   173
        attr[i++] = _this->gl_config.stencil_size;
slouken@6848
   174
    }
slouken@6848
   175
slouken@6848
   176
    if ((_this->gl_config.accum_red_size +
slouken@6848
   177
         _this->gl_config.accum_green_size +
slouken@6848
   178
         _this->gl_config.accum_blue_size +
slouken@6848
   179
         _this->gl_config.accum_alpha_size) > 0) {
slouken@6848
   180
        attr[i++] = NSOpenGLPFAAccumSize;
slouken@6848
   181
        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;
slouken@6848
   182
    }
slouken@6848
   183
slouken@6848
   184
    if (_this->gl_config.multisamplebuffers) {
slouken@6848
   185
        attr[i++] = NSOpenGLPFASampleBuffers;
slouken@6848
   186
        attr[i++] = _this->gl_config.multisamplebuffers;
slouken@6848
   187
    }
slouken@6848
   188
slouken@6848
   189
    if (_this->gl_config.multisamplesamples) {
slouken@6848
   190
        attr[i++] = NSOpenGLPFASamples;
slouken@6848
   191
        attr[i++] = _this->gl_config.multisamplesamples;
slouken@6848
   192
        attr[i++] = NSOpenGLPFANoRecovery;
slouken@6848
   193
    }
slouken@6848
   194
slouken@6848
   195
    if (_this->gl_config.accelerated >= 0) {
slouken@6848
   196
        if (_this->gl_config.accelerated) {
slouken@6848
   197
            attr[i++] = NSOpenGLPFAAccelerated;
slouken@6848
   198
        } else {
slouken@6848
   199
            attr[i++] = NSOpenGLPFARendererID;
slouken@6848
   200
            attr[i++] = kCGLRendererGenericFloatID;
alexey@6832
   201
        }
slouken@6848
   202
    }
slouken@6848
   203
slouken@6848
   204
    attr[i++] = NSOpenGLPFAScreenMask;
slouken@6848
   205
    attr[i++] = CGDisplayIDToOpenGLDisplayMask(displaydata->display);
slouken@6848
   206
    attr[i] = 0;
slouken@6848
   207
slouken@6848
   208
    fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr];
slouken@6848
   209
    if (fmt == nil) {
slouken@6848
   210
        SDL_SetError ("Failed creating OpenGL pixel format");
slouken@6848
   211
        [pool release];
slouken@6848
   212
        return NULL;
slouken@6848
   213
    }
slouken@6848
   214
flibitijibibo@7152
   215
    if (_this->gl_config.share_with_current_context) {
slouken@7412
   216
        share_context = (NSOpenGLContext*)SDL_GL_GetCurrentContext();
flibitijibibo@7152
   217
    }
flibitijibibo@7152
   218
jorgen@7594
   219
    context = [[SDLOpenGLContext alloc] initWithFormat:fmt shareContext:share_context];
slouken@6848
   220
slouken@6848
   221
    [fmt release];
slouken@6848
   222
slouken@6848
   223
    if (context == nil) {
slouken@6848
   224
        SDL_SetError ("Failed creating OpenGL context");
slouken@6848
   225
        [pool release];
slouken@6848
   226
        return NULL;
slouken@6848
   227
    }
slouken@6848
   228
slouken@6848
   229
    [pool release];
slouken@1936
   230
slouken@2178
   231
    if ( Cocoa_GL_MakeCurrent(_this, window, context) < 0 ) {
slouken@2178
   232
        Cocoa_GL_DeleteContext(_this, context);
slouken@2178
   233
        return NULL;
slouken@2178
   234
    }
slouken@2178
   235
slouken@2178
   236
    return context;
slouken@1936
   237
}
slouken@1936
   238
slouken@1936
   239
int
slouken@1936
   240
Cocoa_GL_MakeCurrent(_THIS, SDL_Window * window, SDL_GLContext context)
slouken@1936
   241
{
slouken@6848
   242
    NSAutoreleasePool *pool;
slouken@1936
   243
slouken@6848
   244
    pool = [[NSAutoreleasePool alloc] init];
slouken@6848
   245
slouken@6848
   246
    if (context) {
slouken@6848
   247
        SDL_WindowData *windowdata = (SDL_WindowData *)window->driverdata;
jorgen@7594
   248
        SDLOpenGLContext *nscontext = (SDLOpenGLContext *)context;
slouken@7408
   249
        windowdata->nscontext = nscontext;
icculus@7347
   250
        if ([nscontext view] != [windowdata->nswindow contentView]) {
jorgen@7085
   251
            [nscontext setView:[windowdata->nswindow contentView]];
jorgen@7594
   252
            [nscontext scheduleUpdate];
slouken@1973
   253
        }
jorgen@7594
   254
jorgen@7594
   255
        [nscontext updateIfNeeded];
slouken@6848
   256
        [nscontext makeCurrentContext];
slouken@6848
   257
    } else {
slouken@6848
   258
        [NSOpenGLContext clearCurrentContext];
slouken@1936
   259
    }
slouken@1936
   260
slouken@6848
   261
    [pool release];
slouken@1936
   262
    return 0;
slouken@1936
   263
}
slouken@1936
   264
slouken@1936
   265
int
slouken@1936
   266
Cocoa_GL_SetSwapInterval(_THIS, int interval)
slouken@1936
   267
{
slouken@6848
   268
    NSAutoreleasePool *pool;
slouken@1936
   269
    NSOpenGLContext *nscontext;
slouken@2703
   270
    GLint value;
slouken@1936
   271
    int status;
slouken@1936
   272
slouken@6848
   273
    pool = [[NSAutoreleasePool alloc] init];
slouken@6848
   274
jorgen@7594
   275
    nscontext = (NSOpenGLContext*)SDL_GL_GetCurrentContext();
slouken@6848
   276
    if (nscontext != nil) {
slouken@6848
   277
        value = interval;
slouken@6848
   278
        [nscontext setValues:&value forParameter:NSOpenGLCPSwapInterval];
slouken@6848
   279
        status = 0;
slouken@6848
   280
    } else {
icculus@7037
   281
        status = SDL_SetError("No current OpenGL context");
slouken@1936
   282
    }
slouken@1936
   283
slouken@6848
   284
    [pool release];
slouken@1936
   285
    return status;
slouken@1936
   286
}
slouken@1936
   287
slouken@1936
   288
int
slouken@1936
   289
Cocoa_GL_GetSwapInterval(_THIS)
slouken@1936
   290
{
slouken@6848
   291
    NSAutoreleasePool *pool;
slouken@1936
   292
    NSOpenGLContext *nscontext;
slouken@2703
   293
    GLint value;
icculus@6382
   294
    int status = 0;
slouken@1936
   295
slouken@6848
   296
    pool = [[NSAutoreleasePool alloc] init];
slouken@6848
   297
jorgen@7594
   298
    nscontext = (NSOpenGLContext*)SDL_GL_GetCurrentContext();
slouken@6848
   299
    if (nscontext != nil) {
slouken@6848
   300
        [nscontext getValues:&value forParameter:NSOpenGLCPSwapInterval];
slouken@6848
   301
        status = (int)value;
slouken@1936
   302
    }
slouken@1936
   303
slouken@6848
   304
    [pool release];
slouken@1936
   305
    return status;
slouken@1936
   306
}
slouken@1936
   307
slouken@1936
   308
void
slouken@1936
   309
Cocoa_GL_SwapWindow(_THIS, SDL_Window * window)
slouken@1936
   310
{
slouken@6848
   311
    NSAutoreleasePool *pool;
slouken@7408
   312
    SDL_WindowData *windowdata = (SDL_WindowData *)window->driverdata;
jorgen@7594
   313
    SDLOpenGLContext *nscontext = windowdata->nscontext;
slouken@1936
   314
slouken@6848
   315
    pool = [[NSAutoreleasePool alloc] init];
slouken@6848
   316
slouken@7408
   317
    [nscontext flushBuffer];
jorgen@7594
   318
    [nscontext updateIfNeeded];
slouken@6848
   319
slouken@6848
   320
    [pool release];
slouken@1936
   321
}
slouken@1936
   322
slouken@1936
   323
void
slouken@1936
   324
Cocoa_GL_DeleteContext(_THIS, SDL_GLContext context)
slouken@1936
   325
{
slouken@6848
   326
    NSAutoreleasePool *pool;
slouken@1936
   327
    NSOpenGLContext *nscontext = (NSOpenGLContext *)context;
slouken@1936
   328
slouken@6848
   329
    pool = [[NSAutoreleasePool alloc] init];
slouken@6848
   330
slouken@6848
   331
    [nscontext clearDrawable];
slouken@6848
   332
    [nscontext release];
slouken@6848
   333
slouken@6848
   334
    [pool release];
slouken@1936
   335
}
slouken@1936
   336
slouken@1952
   337
#endif /* SDL_VIDEO_OPENGL_CGL */
slouken@1936
   338
slouken@1936
   339
/* vi: set ts=4 sw=4 expandtab: */