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