SDL  2.0
SDL_cocoaopengl.m
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2017 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  attr[i++] = NSOpenGLPFAAllowOfflineRenderers;
177 
178  /* specify a profile if we're on Lion (10.7) or later. */
179  if (lion_or_later) {
180  NSOpenGLPixelFormatAttribute profile = NSOpenGLProfileVersionLegacy;
182  profile = NSOpenGLProfileVersion3_2Core;
183  }
184  attr[i++] = NSOpenGLPFAOpenGLProfile;
185  attr[i++] = profile;
186  }
187 
188  attr[i++] = NSOpenGLPFAColorSize;
189  attr[i++] = SDL_BYTESPERPIXEL(display->current_mode.format)*8;
190 
191  attr[i++] = NSOpenGLPFADepthSize;
192  attr[i++] = _this->gl_config.depth_size;
193 
195  attr[i++] = NSOpenGLPFADoubleBuffer;
196  }
197 
198  if (_this->gl_config.stereo) {
199  attr[i++] = NSOpenGLPFAStereo;
200  }
201 
203  attr[i++] = NSOpenGLPFAStencilSize;
204  attr[i++] = _this->gl_config.stencil_size;
205  }
206 
211  attr[i++] = NSOpenGLPFAAccumSize;
212  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;
213  }
214 
216  attr[i++] = NSOpenGLPFASampleBuffers;
217  attr[i++] = _this->gl_config.multisamplebuffers;
218  }
219 
221  attr[i++] = NSOpenGLPFASamples;
222  attr[i++] = _this->gl_config.multisamplesamples;
223  attr[i++] = NSOpenGLPFANoRecovery;
224  }
225 
226  if (_this->gl_config.accelerated >= 0) {
227  if (_this->gl_config.accelerated) {
228  attr[i++] = NSOpenGLPFAAccelerated;
229  } else {
230  attr[i++] = NSOpenGLPFARendererID;
231  attr[i++] = kCGLRendererGenericFloatID;
232  }
233  }
234 
235  attr[i++] = NSOpenGLPFAScreenMask;
236  attr[i++] = CGDisplayIDToOpenGLDisplayMask(displaydata->display);
237  attr[i] = 0;
238 
239  fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr];
240  if (fmt == nil) {
241  SDL_SetError("Failed creating OpenGL pixel format");
242  return NULL;
243  }
244 
246  share_context = (NSOpenGLContext*)SDL_GL_GetCurrentContext();
247  }
248 
249  context = [[SDLOpenGLContext alloc] initWithFormat:fmt shareContext:share_context];
250 
251  [fmt release];
252 
253  if (context == nil) {
254  SDL_SetError("Failed creating OpenGL context");
255  return NULL;
256  }
257 
258  if ( Cocoa_GL_MakeCurrent(_this, window, context) < 0 ) {
259  Cocoa_GL_DeleteContext(_this, context);
260  SDL_SetError("Failed making OpenGL context current");
261  return NULL;
262  }
263 
264  if (_this->gl_config.major_version < 3 &&
265  _this->gl_config.profile_mask == 0 &&
266  _this->gl_config.flags == 0) {
267  /* This is a legacy profile, so to match other backends, we're done. */
268  } else {
269  const GLubyte *(APIENTRY * glGetStringFunc)(GLenum);
270 
271  glGetStringFunc = (const GLubyte *(APIENTRY *)(GLenum)) SDL_GL_GetProcAddress("glGetString");
272  if (!glGetStringFunc) {
273  Cocoa_GL_DeleteContext(_this, context);
274  SDL_SetError ("Failed getting OpenGL glGetString entry point");
275  return NULL;
276  }
277 
278  glversion = (const char *)glGetStringFunc(GL_VERSION);
279  if (glversion == NULL) {
280  Cocoa_GL_DeleteContext(_this, context);
281  SDL_SetError ("Failed getting OpenGL context version");
282  return NULL;
283  }
284 
285  if (SDL_sscanf(glversion, "%d.%d", &glversion_major, &glversion_minor) != 2) {
286  Cocoa_GL_DeleteContext(_this, context);
287  SDL_SetError ("Failed parsing OpenGL context version");
288  return NULL;
289  }
290 
291  if ((glversion_major < _this->gl_config.major_version) ||
292  ((glversion_major == _this->gl_config.major_version) && (glversion_minor < _this->gl_config.minor_version))) {
293  Cocoa_GL_DeleteContext(_this, context);
294  SDL_SetError ("Failed creating OpenGL context at version requested");
295  return NULL;
296  }
297 
298  /* In the future we'll want to do this, but to match other platforms
299  we'll leave the OpenGL version the way it is for now
300  */
301  /*_this->gl_config.major_version = glversion_major;*/
302  /*_this->gl_config.minor_version = glversion_minor;*/
303  }
304  return context;
305 }}
306 
307 int
308 Cocoa_GL_MakeCurrent(_THIS, SDL_Window * window, SDL_GLContext context)
309 { @autoreleasepool
310 {
311  if (context) {
312  SDLOpenGLContext *nscontext = (SDLOpenGLContext *)context;
313  [nscontext setWindow:window];
314  [nscontext updateIfNeeded];
315  [nscontext makeCurrentContext];
316  } else {
317  [NSOpenGLContext clearCurrentContext];
318  }
319 
320  return 0;
321 }}
322 
323 void
324 Cocoa_GL_GetDrawableSize(_THIS, SDL_Window * window, int * w, int * h)
325 {
326  SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
327  NSView *contentView = [windata->nswindow contentView];
328  NSRect viewport = [contentView bounds];
329 
330  /* This gives us the correct viewport for a Retina-enabled view, only
331  * supported on 10.7+. */
332  if ([contentView respondsToSelector:@selector(convertRectToBacking:)]) {
333  viewport = [contentView convertRectToBacking:viewport];
334  }
335 
336  if (w) {
337  *w = viewport.size.width;
338  }
339 
340  if (h) {
341  *h = viewport.size.height;
342  }
343 }
344 
345 int
346 Cocoa_GL_SetSwapInterval(_THIS, int interval)
347 { @autoreleasepool
348 {
349  NSOpenGLContext *nscontext;
350  GLint value;
351  int status;
352 
353  if (interval < 0) { /* no extension for this on Mac OS X at the moment. */
354  return SDL_SetError("Late swap tearing currently unsupported");
355  }
356 
357  nscontext = (NSOpenGLContext*)SDL_GL_GetCurrentContext();
358  if (nscontext != nil) {
359  value = interval;
360  [nscontext setValues:&value forParameter:NSOpenGLCPSwapInterval];
361  status = 0;
362  } else {
363  status = SDL_SetError("No current OpenGL context");
364  }
365 
366  return status;
367 }}
368 
369 int
370 Cocoa_GL_GetSwapInterval(_THIS)
371 { @autoreleasepool
372 {
373  NSOpenGLContext *nscontext;
374  GLint value;
375  int status = 0;
376 
377  nscontext = (NSOpenGLContext*)SDL_GL_GetCurrentContext();
378  if (nscontext != nil) {
379  [nscontext getValues:&value forParameter:NSOpenGLCPSwapInterval];
380  status = (int)value;
381  }
382 
383  return status;
384 }}
385 
386 int
387 Cocoa_GL_SwapWindow(_THIS, SDL_Window * window)
388 { @autoreleasepool
389 {
390  SDLOpenGLContext* nscontext = (SDLOpenGLContext*)SDL_GL_GetCurrentContext();
391  [nscontext flushBuffer];
392  [nscontext updateIfNeeded];
393  return 0;
394 }}
395 
396 void
397 Cocoa_GL_DeleteContext(_THIS, SDL_GLContext context)
398 { @autoreleasepool
399 {
400  SDLOpenGLContext *nscontext = (SDLOpenGLContext *)context;
401 
402  [nscontext setWindow:NULL];
403  [nscontext release];
404 }}
405 
406 #endif /* SDL_VIDEO_OPENGL_CGL */
407 
408 /* 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
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
struct SDL_VideoDevice::@31 gl_config
static SDL_VideoDevice * _this
Definition: SDL_video.c:121
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
void * SDL_GLContext
An opaque handle to an OpenGL context.
Definition: SDL_video.h:173
#define _THIS
#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
unsigned char GLubyte
Definition: SDL_opengl.h:183
NSWindow * nswindow
#define SDL_getenv
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
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
void * SDL_LoadFunction(void *handle, const char *name)
void * driverdata
Definition: SDL_sysvideo.h:111
Uint32 format
Definition: SDL_video.h:55
#define floor
Definition: math_private.h:37