src/video/cocoa/SDL_cocoaopengl.m
author Ryan C. Gordon <icculus@icculus.org>
Thu, 18 Oct 2018 11:59:48 -0400
branchSDL-ryan-batching-renderer
changeset 12350 b1a820d2f1aa
parent 11811 5d94cb6b24d3
child 12343 84eaa0636bac
permissions -rw-r--r--
cocoa: GL_GetDrawableSize only uses -[NSView convertRectToBacking] for highDPI.

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