src/video/cocoa/SDL_cocoaopengl.m
author Ryan C. Gordon <icculus@icculus.org>
Mon, 04 Jan 2016 22:00:04 -0500
changeset 10008 fb4e35d0d523
parent 9998 f67cf37e9cd4
child 10690 23a825f341e6
permissions -rw-r--r--
Mac: allows apps to use OpenGL on a slower, integrated GPU.

This is often useful for SDL apps that aren't meant to be games: the
integrated GPU starts up faster, uses less power, and is often more than
fast enough.

Note that even with this change, the app will still default to the more
powerful, discrete GPU if one is available; an app that prefers the integrated
GPU will still need the NSSupportsAutomaticGraphicsSwitching key properly
set in its Info.plist and Mac OS X 10.7 or later.

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