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