sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

SDL_cocoaopengl.m (14427B)


      1 /*
      2   Simple DirectMedia Layer
      3   Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
      4 
      5   This software is provided 'as-is', without any express or implied
      6   warranty.  In no event will the authors be held liable for any damages
      7   arising from the use of this software.
      8 
      9   Permission is granted to anyone to use this software for any purpose,
     10   including commercial applications, and to alter it and redistribute it
     11   freely, subject to the following restrictions:
     12 
     13   1. The origin of this software must not be misrepresented; you must not
     14      claim that you wrote the original software. If you use this software
     15      in a product, an acknowledgment in the product documentation would be
     16      appreciated but is not required.
     17   2. Altered source versions must be plainly marked as such, and must not be
     18      misrepresented as being the original software.
     19   3. This notice may not be removed or altered from any source distribution.
     20 */
     21 #include "../../SDL_internal.h"
     22 
     23 /* NSOpenGL implementation of SDL OpenGL support */
     24 
     25 #if SDL_VIDEO_OPENGL_CGL
     26 #include "SDL_cocoavideo.h"
     27 #include "SDL_cocoaopengl.h"
     28 #include "SDL_cocoaopengles.h"
     29 
     30 #include <OpenGL/CGLTypes.h>
     31 #include <OpenGL/OpenGL.h>
     32 #include <OpenGL/CGLRenderers.h>
     33 
     34 #include "SDL_loadso.h"
     35 #include "SDL_opengl.h"
     36 
     37 #define DEFAULT_OPENGL  "/System/Library/Frameworks/OpenGL.framework/Libraries/libGL.dylib"
     38 
     39 /* We still support OpenGL as long as Apple offers it, deprecated or not, so disable deprecation warnings about it. */
     40 #ifdef __clang__
     41 #pragma clang diagnostic push
     42 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
     43 #endif
     44 
     45 @implementation SDLOpenGLContext : NSOpenGLContext
     46 
     47 - (id)initWithFormat:(NSOpenGLPixelFormat *)format
     48         shareContext:(NSOpenGLContext *)share
     49 {
     50     self = [super initWithFormat:format shareContext:share];
     51     if (self) {
     52         SDL_AtomicSet(&self->dirty, 0);
     53         self->window = NULL;
     54     }
     55     return self;
     56 }
     57 
     58 - (void)scheduleUpdate
     59 {
     60     SDL_AtomicAdd(&self->dirty, 1);
     61 }
     62 
     63 /* This should only be called on the thread on which a user is using the context. */
     64 - (void)updateIfNeeded
     65 {
     66     const int value = SDL_AtomicSet(&self->dirty, 0);
     67     if (value > 0) {
     68         /* We call the real underlying update here, since -[SDLOpenGLContext update] just calls us. */
     69         [self explicitUpdate];
     70     }
     71 }
     72 
     73 /* This should only be called on the thread on which a user is using the context. */
     74 - (void)update
     75 {
     76     /* This ensures that regular 'update' calls clear the atomic dirty flag. */
     77     [self scheduleUpdate];
     78     [self updateIfNeeded];
     79 }
     80 
     81 /* Updates the drawable for the contexts and manages related state. */
     82 - (void)setWindow:(SDL_Window *)newWindow
     83 {
     84     if (self->window) {
     85         SDL_WindowData *oldwindowdata = (SDL_WindowData *)self->window->driverdata;
     86 
     87         /* Make sure to remove us from the old window's context list, or we'll get scheduled updates from it too. */
     88         NSMutableArray *contexts = oldwindowdata->nscontexts;
     89         @synchronized (contexts) {
     90             [contexts removeObject:self];
     91         }
     92     }
     93 
     94     self->window = newWindow;
     95 
     96     if (newWindow) {
     97         SDL_WindowData *windowdata = (SDL_WindowData *)newWindow->driverdata;
     98         NSView *contentview = windowdata->sdlContentView;
     99 
    100         /* Now sign up for scheduled updates for the new window. */
    101         NSMutableArray *contexts = windowdata->nscontexts;
    102         @synchronized (contexts) {
    103             [contexts addObject:self];
    104         }
    105 
    106         if ([self view] != contentview) {
    107             if ([NSThread isMainThread]) {
    108                 [self setView:contentview];
    109             } else {
    110                 dispatch_sync(dispatch_get_main_queue(), ^{ [self setView:contentview]; });
    111             }
    112             if (self == [NSOpenGLContext currentContext]) {
    113                 [self explicitUpdate];
    114             } else {
    115                 [self scheduleUpdate];
    116             }
    117         }
    118     } else {
    119         [self clearDrawable];
    120         if (self == [NSOpenGLContext currentContext]) {
    121             [self explicitUpdate];
    122         } else {
    123             [self scheduleUpdate];
    124         }
    125     }
    126 }
    127 
    128 - (SDL_Window*)window
    129 {
    130     return self->window;
    131 }
    132 
    133 - (void)explicitUpdate
    134 {
    135     if ([NSThread isMainThread]) {
    136         [super update];
    137     } else {
    138         dispatch_sync(dispatch_get_main_queue(), ^{ [super update]; });
    139     }
    140 }
    141 
    142 @end
    143 
    144 
    145 int
    146 Cocoa_GL_LoadLibrary(_THIS, const char *path)
    147 {
    148     /* Load the OpenGL library */
    149     if (path == NULL) {
    150         path = SDL_getenv("SDL_OPENGL_LIBRARY");
    151     }
    152     if (path == NULL) {
    153         path = DEFAULT_OPENGL;
    154     }
    155     _this->gl_config.dll_handle = SDL_LoadObject(path);
    156     if (!_this->gl_config.dll_handle) {
    157         return -1;
    158     }
    159     SDL_strlcpy(_this->gl_config.driver_path, path,
    160                 SDL_arraysize(_this->gl_config.driver_path));
    161     return 0;
    162 }
    163 
    164 void *
    165 Cocoa_GL_GetProcAddress(_THIS, const char *proc)
    166 {
    167     return SDL_LoadFunction(_this->gl_config.dll_handle, proc);
    168 }
    169 
    170 void
    171 Cocoa_GL_UnloadLibrary(_THIS)
    172 {
    173     SDL_UnloadObject(_this->gl_config.dll_handle);
    174     _this->gl_config.dll_handle = NULL;
    175 }
    176 
    177 SDL_GLContext
    178 Cocoa_GL_CreateContext(_THIS, SDL_Window * window)
    179 { @autoreleasepool
    180 {
    181     SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
    182     SDL_DisplayData *displaydata = (SDL_DisplayData *)display->driverdata;
    183     SDL_bool lion_or_later = floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6;
    184     NSOpenGLPixelFormatAttribute attr[32];
    185     NSOpenGLPixelFormat *fmt;
    186     SDLOpenGLContext *context;
    187     NSOpenGLContext *share_context = nil;
    188     int i = 0;
    189     const char *glversion;
    190     int glversion_major;
    191     int glversion_minor;
    192 
    193     if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) {
    194 #if SDL_VIDEO_OPENGL_EGL
    195         /* Switch to EGL based functions */
    196         Cocoa_GL_UnloadLibrary(_this);
    197         _this->GL_LoadLibrary = Cocoa_GLES_LoadLibrary;
    198         _this->GL_GetProcAddress = Cocoa_GLES_GetProcAddress;
    199         _this->GL_UnloadLibrary = Cocoa_GLES_UnloadLibrary;
    200         _this->GL_CreateContext = Cocoa_GLES_CreateContext;
    201         _this->GL_MakeCurrent = Cocoa_GLES_MakeCurrent;
    202         _this->GL_SetSwapInterval = Cocoa_GLES_SetSwapInterval;
    203         _this->GL_GetSwapInterval = Cocoa_GLES_GetSwapInterval;
    204         _this->GL_SwapWindow = Cocoa_GLES_SwapWindow;
    205         _this->GL_DeleteContext = Cocoa_GLES_DeleteContext;
    206         
    207         if (Cocoa_GLES_LoadLibrary(_this, NULL) != 0) {
    208             return NULL;
    209         }
    210         return Cocoa_GLES_CreateContext(_this, window);
    211 #else
    212         SDL_SetError("SDL not configured with EGL support");
    213         return NULL;
    214 #endif
    215     }
    216     if ((_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_CORE) && !lion_or_later) {
    217         SDL_SetError ("OpenGL Core Profile is not supported on this platform version");
    218         return NULL;
    219     }
    220 
    221     attr[i++] = NSOpenGLPFAAllowOfflineRenderers;
    222 
    223     /* specify a profile if we're on Lion (10.7) or later. */
    224     if (lion_or_later) {
    225         NSOpenGLPixelFormatAttribute profile = NSOpenGLProfileVersionLegacy;
    226         if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_CORE) {
    227             profile = NSOpenGLProfileVersion3_2Core;
    228         }
    229         attr[i++] = NSOpenGLPFAOpenGLProfile;
    230         attr[i++] = profile;
    231     }
    232 
    233     attr[i++] = NSOpenGLPFAColorSize;
    234     attr[i++] = SDL_BYTESPERPIXEL(display->current_mode.format)*8;
    235 
    236     attr[i++] = NSOpenGLPFADepthSize;
    237     attr[i++] = _this->gl_config.depth_size;
    238 
    239     if (_this->gl_config.double_buffer) {
    240         attr[i++] = NSOpenGLPFADoubleBuffer;
    241     }
    242 
    243     if (_this->gl_config.stereo) {
    244         attr[i++] = NSOpenGLPFAStereo;
    245     }
    246 
    247     if (_this->gl_config.stencil_size) {
    248         attr[i++] = NSOpenGLPFAStencilSize;
    249         attr[i++] = _this->gl_config.stencil_size;
    250     }
    251 
    252     if ((_this->gl_config.accum_red_size +
    253          _this->gl_config.accum_green_size +
    254          _this->gl_config.accum_blue_size +
    255          _this->gl_config.accum_alpha_size) > 0) {
    256         attr[i++] = NSOpenGLPFAAccumSize;
    257         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;
    258     }
    259 
    260     if (_this->gl_config.multisamplebuffers) {
    261         attr[i++] = NSOpenGLPFASampleBuffers;
    262         attr[i++] = _this->gl_config.multisamplebuffers;
    263     }
    264 
    265     if (_this->gl_config.multisamplesamples) {
    266         attr[i++] = NSOpenGLPFASamples;
    267         attr[i++] = _this->gl_config.multisamplesamples;
    268         attr[i++] = NSOpenGLPFANoRecovery;
    269     }
    270 
    271     if (_this->gl_config.accelerated >= 0) {
    272         if (_this->gl_config.accelerated) {
    273             attr[i++] = NSOpenGLPFAAccelerated;
    274         } else {
    275             attr[i++] = NSOpenGLPFARendererID;
    276             attr[i++] = kCGLRendererGenericFloatID;
    277         }
    278     }
    279 
    280     attr[i++] = NSOpenGLPFAScreenMask;
    281     attr[i++] = CGDisplayIDToOpenGLDisplayMask(displaydata->display);
    282     attr[i] = 0;
    283 
    284     fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr];
    285     if (fmt == nil) {
    286         SDL_SetError("Failed creating OpenGL pixel format");
    287         return NULL;
    288     }
    289 
    290     if (_this->gl_config.share_with_current_context) {
    291         share_context = (NSOpenGLContext*)SDL_GL_GetCurrentContext();
    292     }
    293 
    294     context = [[SDLOpenGLContext alloc] initWithFormat:fmt shareContext:share_context];
    295 
    296     [fmt release];
    297 
    298     if (context == nil) {
    299         SDL_SetError("Failed creating OpenGL context");
    300         return NULL;
    301     }
    302 
    303     if ( Cocoa_GL_MakeCurrent(_this, window, context) < 0 ) {
    304         Cocoa_GL_DeleteContext(_this, context);
    305         SDL_SetError("Failed making OpenGL context current");
    306         return NULL;
    307     }
    308 
    309     if (_this->gl_config.major_version < 3 &&
    310         _this->gl_config.profile_mask == 0 &&
    311         _this->gl_config.flags == 0) {
    312         /* This is a legacy profile, so to match other backends, we're done. */
    313     } else {
    314         const GLubyte *(APIENTRY * glGetStringFunc)(GLenum);
    315 
    316         glGetStringFunc = (const GLubyte *(APIENTRY *)(GLenum)) SDL_GL_GetProcAddress("glGetString");
    317         if (!glGetStringFunc) {
    318             Cocoa_GL_DeleteContext(_this, context);
    319             SDL_SetError ("Failed getting OpenGL glGetString entry point");
    320             return NULL;
    321         }
    322 
    323         glversion = (const char *)glGetStringFunc(GL_VERSION);
    324         if (glversion == NULL) {
    325             Cocoa_GL_DeleteContext(_this, context);
    326             SDL_SetError ("Failed getting OpenGL context version");
    327             return NULL;
    328         }
    329 
    330         if (SDL_sscanf(glversion, "%d.%d", &glversion_major, &glversion_minor) != 2) {
    331             Cocoa_GL_DeleteContext(_this, context);
    332             SDL_SetError ("Failed parsing OpenGL context version");
    333             return NULL;
    334         }
    335 
    336         if ((glversion_major < _this->gl_config.major_version) ||
    337            ((glversion_major == _this->gl_config.major_version) && (glversion_minor < _this->gl_config.minor_version))) {
    338             Cocoa_GL_DeleteContext(_this, context);
    339             SDL_SetError ("Failed creating OpenGL context at version requested");
    340             return NULL;
    341         }
    342 
    343         /* In the future we'll want to do this, but to match other platforms
    344            we'll leave the OpenGL version the way it is for now
    345          */
    346         /*_this->gl_config.major_version = glversion_major;*/
    347         /*_this->gl_config.minor_version = glversion_minor;*/
    348     }
    349     return context;
    350 }}
    351 
    352 int
    353 Cocoa_GL_MakeCurrent(_THIS, SDL_Window * window, SDL_GLContext context)
    354 { @autoreleasepool
    355 {
    356     if (context) {
    357         SDLOpenGLContext *nscontext = (SDLOpenGLContext *)context;
    358         if ([nscontext window] != window) {
    359             [nscontext setWindow:window];
    360             [nscontext updateIfNeeded];
    361         }
    362         [nscontext makeCurrentContext];
    363     } else {
    364         [NSOpenGLContext clearCurrentContext];
    365     }
    366 
    367     return 0;
    368 }}
    369 
    370 void
    371 Cocoa_GL_GetDrawableSize(_THIS, SDL_Window * window, int * w, int * h)
    372 {
    373     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
    374     NSView *contentView = windata->sdlContentView;
    375     NSRect viewport = [contentView bounds];
    376 
    377     if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
    378         /* This gives us the correct viewport for a Retina-enabled view, only
    379          * supported on 10.7+. */
    380         if ([contentView respondsToSelector:@selector(convertRectToBacking:)]) {
    381             viewport = [contentView convertRectToBacking:viewport];
    382         }
    383     }
    384 
    385     if (w) {
    386         *w = viewport.size.width;
    387     }
    388 
    389     if (h) {
    390         *h = viewport.size.height;
    391     }
    392 }
    393 
    394 int
    395 Cocoa_GL_SetSwapInterval(_THIS, int interval)
    396 { @autoreleasepool
    397 {
    398     NSOpenGLContext *nscontext;
    399     GLint value;
    400     int status;
    401 
    402     if (interval < 0) {  /* no extension for this on Mac OS X at the moment. */
    403         return SDL_SetError("Late swap tearing currently unsupported");
    404     }
    405 
    406     nscontext = (NSOpenGLContext*)SDL_GL_GetCurrentContext();
    407     if (nscontext != nil) {
    408         value = interval;
    409         [nscontext setValues:&value forParameter:NSOpenGLCPSwapInterval];
    410         status = 0;
    411     } else {
    412         status = SDL_SetError("No current OpenGL context");
    413     }
    414 
    415     return status;
    416 }}
    417 
    418 int
    419 Cocoa_GL_GetSwapInterval(_THIS)
    420 { @autoreleasepool
    421 {
    422     NSOpenGLContext *nscontext;
    423     GLint value;
    424     int status = 0;
    425 
    426     nscontext = (NSOpenGLContext*)SDL_GL_GetCurrentContext();
    427     if (nscontext != nil) {
    428         [nscontext getValues:&value forParameter:NSOpenGLCPSwapInterval];
    429         status = (int)value;
    430     }
    431 
    432     return status;
    433 }}
    434 
    435 int
    436 Cocoa_GL_SwapWindow(_THIS, SDL_Window * window)
    437 { @autoreleasepool
    438 {
    439     SDLOpenGLContext* nscontext = (SDLOpenGLContext*)SDL_GL_GetCurrentContext();
    440     SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
    441 
    442     /* on 10.14 ("Mojave") and later, this deadlocks if two contexts in two
    443        threads try to swap at the same time, so put a mutex around it. */
    444     SDL_LockMutex(videodata->swaplock);
    445     [nscontext flushBuffer];
    446     [nscontext updateIfNeeded];
    447     SDL_UnlockMutex(videodata->swaplock);
    448     return 0;
    449 }}
    450 
    451 void
    452 Cocoa_GL_DeleteContext(_THIS, SDL_GLContext context)
    453 { @autoreleasepool
    454 {
    455     SDLOpenGLContext *nscontext = (SDLOpenGLContext *)context;
    456 
    457     [nscontext setWindow:NULL];
    458     [nscontext release];
    459 }}
    460 
    461 /* We still support OpenGL as long as Apple offers it, deprecated or not, so disable deprecation warnings about it. */
    462 #ifdef __clang__
    463 #pragma clang diagnostic pop
    464 #endif
    465 
    466 #endif /* SDL_VIDEO_OPENGL_CGL */
    467 
    468 /* vi: set ts=4 sw=4 expandtab: */