SDL  2.0
SDL_cocoaopengl.m
Go to the documentation of this file.
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  }
131  if (!_this->gl_config.dll_handle) {
132  return -1;
133  }
136  return 0;
137 }
138 
139 void *
140 Cocoa_GL_GetProcAddress(_THIS, const char *proc)
141 {
143 }
144 
145 void
146 Cocoa_GL_UnloadLibrary(_THIS)
147 {
150 }
151 
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 
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;
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 
215  attr[i++] = NSOpenGLPFADoubleBuffer;
216  }
217 
218  if (_this->gl_config.stereo) {
219  attr[i++] = NSOpenGLPFAStereo;
220  }
221 
223  attr[i++] = NSOpenGLPFAStencilSize;
224  attr[i++] = _this->gl_config.stencil_size;
225  }
226 
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 
236  attr[i++] = NSOpenGLPFASampleBuffers;
237  attr[i++] = _this->gl_config.multisamplebuffers;
238  }
239 
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 
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  SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
414 
415  /* on 10.14 ("Mojave") and later, this deadlocks if two contexts in two
416  threads try to swap at the same time, so put a mutex around it. */
417  SDL_LockMutex(videodata->swaplock);
418  [nscontext flushBuffer];
419  [nscontext updateIfNeeded];
420  SDL_UnlockMutex(videodata->swaplock);
421  return 0;
422 }}
423 
424 void
425 Cocoa_GL_DeleteContext(_THIS, SDL_GLContext context)
426 { @autoreleasepool
427 {
428  SDLOpenGLContext *nscontext = (SDLOpenGLContext *)context;
429 
430  [nscontext setWindow:NULL];
431  [nscontext release];
432 }}
433 
434 #endif /* SDL_VIDEO_OPENGL_CGL */
435 
436 /* vi: set ts=4 sw=4 expandtab: */
#define SDL_strlcpy
#define SDL_LockMutex
GLuint id
NSMutableArray * nscontexts
GLfloat GLfloat GLfloat GLfloat h
#define APIENTRY
Definition: SDL_opengl.h:139
int GLint
Definition: SDL_opengl.h:182
int(* GL_SetSwapInterval)(_THIS, int interval)
Definition: SDL_sysvideo.h:261
static screen_context_t context
Definition: video.c:25
#define SDL_BYTESPERPIXEL(X)
Definition: SDL_pixels.h:128
SDL_Window * window
#define GL_VERSION
Definition: SDL_opengl.h:715
CGDirectDisplayID display
#define SDL_LoadObject
#define SDL_UnloadObject
int(* GL_LoadLibrary)(_THIS, const char *path)
Definition: SDL_sysvideo.h:255
static SDL_VideoDevice * _this
Definition: SDL_video.c:121
void * SDL_GLContext
An opaque handle to an OpenGL context.
Definition: SDL_video.h:193
SDL_GLContext(* GL_CreateContext)(_THIS, SDL_Window *window)
Definition: SDL_sysvideo.h:258
#define _THIS
int(* GL_MakeCurrent)(_THIS, SDL_Window *window, SDL_GLContext context)
Definition: SDL_sysvideo.h:259
#define SDL_GL_GetProcAddress
char driver_path[256]
Definition: SDL_sysvideo.h:354
SDL_DisplayMode current_mode
Definition: SDL_sysvideo.h:132
GLubyte GLubyte GLubyte GLubyte w
GLsizei const GLfloat * value
#define SDL_sscanf
double floor(double x)
Definition: s_floor.c:29
SDL_mutex * swaplock
unsigned char GLubyte
Definition: SDL_opengl.h:183
NSWindow * nswindow
#define SDL_getenv
void(* GL_UnloadLibrary)(_THIS)
Definition: SDL_sysvideo.h:257
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int in i)
Definition: SDL_x11sym.h:50
int share_with_current_context
Definition: SDL_sysvideo.h:347
unsigned int GLenum
Definition: SDL_opengl.h:176
#define NULL
Definition: begin_code.h:164
#define SDL_AtomicAdd
SDL_bool
Definition: SDL_stdinc.h:161
#define SDL_GL_GetCurrentContext
#define SDL_SetError
SDL_VideoDisplay * SDL_GetDisplayForWindow(SDL_Window *window)
Definition: SDL_video.c:1092
EGLSurface EGLNativeWindowType * window
Definition: eglext.h:1025
int(* GL_SwapWindow)(_THIS, SDL_Window *window)
Definition: SDL_sysvideo.h:263
SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char const char SDL_SCANF_FORMAT_STRING const char return SDL_ThreadFunction const char void return Uint32 return Uint32 void
The type used to identify a window.
Definition: SDL_sysvideo.h:73
SDL_Rect viewport
Definition: testviewport.c:28
#define SDL_AtomicSet
#define SDL_UnlockMutex
#define SDL_arraysize(array)
Definition: SDL_stdinc.h:115
GLsizei const GLchar *const * path
struct SDL_VideoDevice::@34 gl_config
void * SDL_LoadFunction(void *handle, const char *name)
void * driverdata
Definition: SDL_sysvideo.h:111
void(* GL_DeleteContext)(_THIS, SDL_GLContext context)
Definition: SDL_sysvideo.h:264
Uint32 format
Definition: SDL_video.h:55
int(* GL_GetSwapInterval)(_THIS)
Definition: SDL_sysvideo.h:262
Uint32 flags
Definition: SDL_sysvideo.h:83
void *(* GL_GetProcAddress)(_THIS, const char *proc)
Definition: SDL_sysvideo.h:256