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