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  /* This gives us the correct viewport for a Retina-enabled view, only
351  * supported on 10.7+. */
352  if ([contentView respondsToSelector:@selector(convertRectToBacking:)]) {
353  viewport = [contentView convertRectToBacking:viewport];
354  }
355 
356  if (w) {
357  *w = viewport.size.width;
358  }
359 
360  if (h) {
361  *h = viewport.size.height;
362  }
363 }
364 
365 int
366 Cocoa_GL_SetSwapInterval(_THIS, int interval)
367 { @autoreleasepool
368 {
369  NSOpenGLContext *nscontext;
370  GLint value;
371  int status;
372 
373  if (interval < 0) { /* no extension for this on Mac OS X at the moment. */
374  return SDL_SetError("Late swap tearing currently unsupported");
375  }
376 
377  nscontext = (NSOpenGLContext*)SDL_GL_GetCurrentContext();
378  if (nscontext != nil) {
379  value = interval;
380  [nscontext setValues:&value forParameter:NSOpenGLCPSwapInterval];
381  status = 0;
382  } else {
383  status = SDL_SetError("No current OpenGL context");
384  }
385 
386  return status;
387 }}
388 
389 int
390 Cocoa_GL_GetSwapInterval(_THIS)
391 { @autoreleasepool
392 {
393  NSOpenGLContext *nscontext;
394  GLint value;
395  int status = 0;
396 
397  nscontext = (NSOpenGLContext*)SDL_GL_GetCurrentContext();
398  if (nscontext != nil) {
399  [nscontext getValues:&value forParameter:NSOpenGLCPSwapInterval];
400  status = (int)value;
401  }
402 
403  return status;
404 }}
405 
406 int
407 Cocoa_GL_SwapWindow(_THIS, SDL_Window * window)
408 { @autoreleasepool
409 {
410  SDLOpenGLContext* nscontext = (SDLOpenGLContext*)SDL_GL_GetCurrentContext();
411  [nscontext flushBuffer];
412  [nscontext updateIfNeeded];
413  return 0;
414 }}
415 
416 void
417 Cocoa_GL_DeleteContext(_THIS, SDL_GLContext context)
418 { @autoreleasepool
419 {
420  SDLOpenGLContext *nscontext = (SDLOpenGLContext *)context;
421 
422  [nscontext setWindow:NULL];
423  [nscontext release];
424 }}
425 
426 #endif /* SDL_VIDEO_OPENGL_CGL */
427 
428 /* vi: set ts=4 sw=4 expandtab: */
#define SDL_strlcpy
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:260
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:254
static SDL_VideoDevice * _this
Definition: SDL_video.c:121
void * SDL_GLContext
An opaque handle to an OpenGL context.
Definition: SDL_video.h:175
SDL_GLContext(* GL_CreateContext)(_THIS, SDL_Window *window)
Definition: SDL_sysvideo.h:257
#define _THIS
int(* GL_MakeCurrent)(_THIS, SDL_Window *window, SDL_GLContext context)
Definition: SDL_sysvideo.h:258
#define SDL_GL_GetProcAddress
char driver_path[256]
Definition: SDL_sysvideo.h:350
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
unsigned char GLubyte
Definition: SDL_opengl.h:183
NSWindow * nswindow
#define SDL_getenv
void(* GL_UnloadLibrary)(_THIS)
Definition: SDL_sysvideo.h:256
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:343
unsigned int GLenum
Definition: SDL_opengl.h:176
#define NULL
Definition: begin_code.h:164
#define SDL_AtomicAdd
SDL_bool
Definition: SDL_stdinc.h:139
#define SDL_GL_GetCurrentContext
#define SDL_SetError
SDL_VideoDisplay * SDL_GetDisplayForWindow(SDL_Window *window)
Definition: SDL_video.c:1073
EGLSurface EGLNativeWindowType * window
Definition: eglext.h:1025
int(* GL_SwapWindow)(_THIS, SDL_Window *window)
Definition: SDL_sysvideo.h:262
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_arraysize(array)
Definition: SDL_stdinc.h:93
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:263
Uint32 format
Definition: SDL_video.h:55
int(* GL_GetSwapInterval)(_THIS)
Definition: SDL_sysvideo.h:261
void *(* GL_GetProcAddress)(_THIS, const char *proc)
Definition: SDL_sysvideo.h:255