SDL  2.0
SDL_cocoaopengl.m
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2020 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 /* We still support OpenGL as long as Apple offers it, deprecated or not, so disable deprecation warnings about it. */
40 #ifdef __clang__
41 #pragma clang diagnostic push
42 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
43 #endif
44 
45 @implementation SDLOpenGLContext : NSOpenGLContext
46 
47 - (id)initWithFormat:(NSOpenGLPixelFormat *)format
48  shareContext:(NSOpenGLContext *)share
49 {
50  self = [super initWithFormat:format shareContext:share];
51  if (self) {
52  SDL_AtomicSet(&self->dirty, 0);
53  self->window = NULL;
54  }
55  return self;
56 }
57 
58 - (void)scheduleUpdate
59 {
60  SDL_AtomicAdd(&self->dirty, 1);
61 }
62 
63 /* This should only be called on the thread on which a user is using the context. */
64 - (void)updateIfNeeded
65 {
66  const int value = SDL_AtomicSet(&self->dirty, 0);
67  if (value > 0) {
68  /* We call the real underlying update here, since -[SDLOpenGLContext update] just calls us. */
69  [self explicitUpdate];
70  }
71 }
72 
73 /* This should only be called on the thread on which a user is using the context. */
74 - (void)update
75 {
76  /* This ensures that regular 'update' calls clear the atomic dirty flag. */
77  [self scheduleUpdate];
78  [self updateIfNeeded];
79 }
80 
81 /* Updates the drawable for the contexts and manages related state. */
82 - (void)setWindow:(SDL_Window *)newWindow
83 {
84  if (self->window) {
85  SDL_WindowData *oldwindowdata = (SDL_WindowData *)self->window->driverdata;
86 
87  /* Make sure to remove us from the old window's context list, or we'll get scheduled updates from it too. */
88  NSMutableArray *contexts = oldwindowdata->nscontexts;
89  @synchronized (contexts) {
90  [contexts removeObject:self];
91  }
92  }
93 
94  self->window = newWindow;
95 
96  if (newWindow) {
97  SDL_WindowData *windowdata = (SDL_WindowData *)newWindow->driverdata;
98  NSView *contentview = windowdata->sdlContentView;
99 
100  /* Now sign up for scheduled updates for the new window. */
101  NSMutableArray *contexts = windowdata->nscontexts;
102  @synchronized (contexts) {
103  [contexts addObject:self];
104  }
105 
106  if ([self view] != contentview) {
107  if ([NSThread isMainThread]) {
108  [self setView:contentview];
109  } else {
110  dispatch_sync(dispatch_get_main_queue(), ^{ [self setView:contentview]; });
111  }
112  if (self == [NSOpenGLContext currentContext]) {
113  [self explicitUpdate];
114  } else {
115  [self scheduleUpdate];
116  }
117  }
118  } else {
119  [self clearDrawable];
120  if (self == [NSOpenGLContext currentContext]) {
121  [self explicitUpdate];
122  } else {
123  [self scheduleUpdate];
124  }
125  }
126 }
127 
128 - (SDL_Window*)window
129 {
130  return self->window;
131 }
132 
133 - (void)explicitUpdate
134 {
135  if ([NSThread isMainThread]) {
136  [super update];
137  } else {
138  dispatch_sync(dispatch_get_main_queue(), ^{ [super update]; });
139  }
140 }
141 
142 @end
143 
144 
145 int
146 Cocoa_GL_LoadLibrary(_THIS, const char *path)
147 {
148  /* Load the OpenGL library */
149  if (path == NULL) {
150  path = SDL_getenv("SDL_OPENGL_LIBRARY");
151  }
152  if (path == NULL) {
153  path = DEFAULT_OPENGL;
154  }
156  if (!_this->gl_config.dll_handle) {
157  return -1;
158  }
161  return 0;
162 }
163 
164 void *
165 Cocoa_GL_GetProcAddress(_THIS, const char *proc)
166 {
168 }
169 
170 void
171 Cocoa_GL_UnloadLibrary(_THIS)
172 {
175 }
176 
178 Cocoa_GL_CreateContext(_THIS, SDL_Window * window)
179 { @autoreleasepool
180 {
182  SDL_DisplayData *displaydata = (SDL_DisplayData *)display->driverdata;
183  SDL_bool lion_or_later = floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6;
184  NSOpenGLPixelFormatAttribute attr[32];
185  NSOpenGLPixelFormat *fmt;
186  SDLOpenGLContext *context;
187  NSOpenGLContext *share_context = nil;
188  int i = 0;
189  const char *glversion;
190  int glversion_major;
191  int glversion_minor;
192 
194 #if SDL_VIDEO_OPENGL_EGL
195  /* Switch to EGL based functions */
196  Cocoa_GL_UnloadLibrary(_this);
197  _this->GL_LoadLibrary = Cocoa_GLES_LoadLibrary;
198  _this->GL_GetProcAddress = Cocoa_GLES_GetProcAddress;
199  _this->GL_UnloadLibrary = Cocoa_GLES_UnloadLibrary;
200  _this->GL_CreateContext = Cocoa_GLES_CreateContext;
201  _this->GL_MakeCurrent = Cocoa_GLES_MakeCurrent;
202  _this->GL_SetSwapInterval = Cocoa_GLES_SetSwapInterval;
203  _this->GL_GetSwapInterval = Cocoa_GLES_GetSwapInterval;
204  _this->GL_SwapWindow = Cocoa_GLES_SwapWindow;
205  _this->GL_DeleteContext = Cocoa_GLES_DeleteContext;
206 
207  if (Cocoa_GLES_LoadLibrary(_this, NULL) != 0) {
208  return NULL;
209  }
210  return Cocoa_GLES_CreateContext(_this, window);
211 #else
212  SDL_SetError("SDL not configured with EGL support");
213  return NULL;
214 #endif
215  }
216  if ((_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_CORE) && !lion_or_later) {
217  SDL_SetError ("OpenGL Core Profile is not supported on this platform version");
218  return NULL;
219  }
220 
221  attr[i++] = NSOpenGLPFAAllowOfflineRenderers;
222 
223  /* specify a profile if we're on Lion (10.7) or later. */
224  if (lion_or_later) {
225  NSOpenGLPixelFormatAttribute profile = NSOpenGLProfileVersionLegacy;
227  profile = NSOpenGLProfileVersion3_2Core;
228  }
229  attr[i++] = NSOpenGLPFAOpenGLProfile;
230  attr[i++] = profile;
231  }
232 
233  attr[i++] = NSOpenGLPFAColorSize;
234  attr[i++] = SDL_BYTESPERPIXEL(display->current_mode.format)*8;
235 
236  attr[i++] = NSOpenGLPFADepthSize;
237  attr[i++] = _this->gl_config.depth_size;
238 
240  attr[i++] = NSOpenGLPFADoubleBuffer;
241  }
242 
243  if (_this->gl_config.stereo) {
244  attr[i++] = NSOpenGLPFAStereo;
245  }
246 
248  attr[i++] = NSOpenGLPFAStencilSize;
249  attr[i++] = _this->gl_config.stencil_size;
250  }
251 
256  attr[i++] = NSOpenGLPFAAccumSize;
257  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;
258  }
259 
261  attr[i++] = NSOpenGLPFASampleBuffers;
262  attr[i++] = _this->gl_config.multisamplebuffers;
263  }
264 
266  attr[i++] = NSOpenGLPFASamples;
267  attr[i++] = _this->gl_config.multisamplesamples;
268  attr[i++] = NSOpenGLPFANoRecovery;
269  }
270 
271  if (_this->gl_config.accelerated >= 0) {
272  if (_this->gl_config.accelerated) {
273  attr[i++] = NSOpenGLPFAAccelerated;
274  } else {
275  attr[i++] = NSOpenGLPFARendererID;
276  attr[i++] = kCGLRendererGenericFloatID;
277  }
278  }
279 
280  attr[i++] = NSOpenGLPFAScreenMask;
281  attr[i++] = CGDisplayIDToOpenGLDisplayMask(displaydata->display);
282  attr[i] = 0;
283 
284  fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr];
285  if (fmt == nil) {
286  SDL_SetError("Failed creating OpenGL pixel format");
287  return NULL;
288  }
289 
291  share_context = (NSOpenGLContext*)SDL_GL_GetCurrentContext();
292  }
293 
294  context = [[SDLOpenGLContext alloc] initWithFormat:fmt shareContext:share_context];
295 
296  [fmt release];
297 
298  if (context == nil) {
299  SDL_SetError("Failed creating OpenGL context");
300  return NULL;
301  }
302 
303  if ( Cocoa_GL_MakeCurrent(_this, window, context) < 0 ) {
304  Cocoa_GL_DeleteContext(_this, context);
305  SDL_SetError("Failed making OpenGL context current");
306  return NULL;
307  }
308 
309  if (_this->gl_config.major_version < 3 &&
310  _this->gl_config.profile_mask == 0 &&
311  _this->gl_config.flags == 0) {
312  /* This is a legacy profile, so to match other backends, we're done. */
313  } else {
314  const GLubyte *(APIENTRY * glGetStringFunc)(GLenum);
315 
316  glGetStringFunc = (const GLubyte *(APIENTRY *)(GLenum)) SDL_GL_GetProcAddress("glGetString");
317  if (!glGetStringFunc) {
318  Cocoa_GL_DeleteContext(_this, context);
319  SDL_SetError ("Failed getting OpenGL glGetString entry point");
320  return NULL;
321  }
322 
323  glversion = (const char *)glGetStringFunc(GL_VERSION);
324  if (glversion == NULL) {
325  Cocoa_GL_DeleteContext(_this, context);
326  SDL_SetError ("Failed getting OpenGL context version");
327  return NULL;
328  }
329 
330  if (SDL_sscanf(glversion, "%d.%d", &glversion_major, &glversion_minor) != 2) {
331  Cocoa_GL_DeleteContext(_this, context);
332  SDL_SetError ("Failed parsing OpenGL context version");
333  return NULL;
334  }
335 
336  if ((glversion_major < _this->gl_config.major_version) ||
337  ((glversion_major == _this->gl_config.major_version) && (glversion_minor < _this->gl_config.minor_version))) {
338  Cocoa_GL_DeleteContext(_this, context);
339  SDL_SetError ("Failed creating OpenGL context at version requested");
340  return NULL;
341  }
342 
343  /* In the future we'll want to do this, but to match other platforms
344  we'll leave the OpenGL version the way it is for now
345  */
346  /*_this->gl_config.major_version = glversion_major;*/
347  /*_this->gl_config.minor_version = glversion_minor;*/
348  }
349  return context;
350 }}
351 
352 int
353 Cocoa_GL_MakeCurrent(_THIS, SDL_Window * window, SDL_GLContext context)
354 { @autoreleasepool
355 {
356  if (context) {
357  SDLOpenGLContext *nscontext = (SDLOpenGLContext *)context;
358  if ([nscontext window] != window) {
359  [nscontext setWindow:window];
360  [nscontext updateIfNeeded];
361  }
362  [nscontext makeCurrentContext];
363  } else {
364  [NSOpenGLContext clearCurrentContext];
365  }
366 
367  return 0;
368 }}
369 
370 void
371 Cocoa_GL_GetDrawableSize(_THIS, SDL_Window * window, int * w, int * h)
372 {
373  SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
374  NSView *contentView = windata->sdlContentView;
375  NSRect viewport = [contentView bounds];
376 
377  if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
378  /* This gives us the correct viewport for a Retina-enabled view, only
379  * supported on 10.7+. */
380  if ([contentView respondsToSelector:@selector(convertRectToBacking:)]) {
381  viewport = [contentView convertRectToBacking:viewport];
382  }
383  }
384 
385  if (w) {
386  *w = viewport.size.width;
387  }
388 
389  if (h) {
390  *h = viewport.size.height;
391  }
392 }
393 
394 int
395 Cocoa_GL_SetSwapInterval(_THIS, int interval)
396 { @autoreleasepool
397 {
398  NSOpenGLContext *nscontext;
399  GLint value;
400  int status;
401 
402  if (interval < 0) { /* no extension for this on Mac OS X at the moment. */
403  return SDL_SetError("Late swap tearing currently unsupported");
404  }
405 
406  nscontext = (NSOpenGLContext*)SDL_GL_GetCurrentContext();
407  if (nscontext != nil) {
408  value = interval;
409  [nscontext setValues:&value forParameter:NSOpenGLCPSwapInterval];
410  status = 0;
411  } else {
412  status = SDL_SetError("No current OpenGL context");
413  }
414 
415  return status;
416 }}
417 
418 int
419 Cocoa_GL_GetSwapInterval(_THIS)
420 { @autoreleasepool
421 {
422  NSOpenGLContext *nscontext;
423  GLint value;
424  int status = 0;
425 
426  nscontext = (NSOpenGLContext*)SDL_GL_GetCurrentContext();
427  if (nscontext != nil) {
428  [nscontext getValues:&value forParameter:NSOpenGLCPSwapInterval];
429  status = (int)value;
430  }
431 
432  return status;
433 }}
434 
435 int
436 Cocoa_GL_SwapWindow(_THIS, SDL_Window * window)
437 { @autoreleasepool
438 {
439  SDLOpenGLContext* nscontext = (SDLOpenGLContext*)SDL_GL_GetCurrentContext();
440  SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
441 
442  /* on 10.14 ("Mojave") and later, this deadlocks if two contexts in two
443  threads try to swap at the same time, so put a mutex around it. */
444  SDL_LockMutex(videodata->swaplock);
445  [nscontext flushBuffer];
446  [nscontext updateIfNeeded];
447  SDL_UnlockMutex(videodata->swaplock);
448  return 0;
449 }}
450 
451 void
452 Cocoa_GL_DeleteContext(_THIS, SDL_GLContext context)
453 { @autoreleasepool
454 {
455  SDLOpenGLContext *nscontext = (SDLOpenGLContext *)context;
456 
457  [nscontext setWindow:NULL];
458  [nscontext release];
459 }}
460 
461 /* We still support OpenGL as long as Apple offers it, deprecated or not, so disable deprecation warnings about it. */
462 #ifdef __clang__
463 #pragma clang diagnostic pop
464 #endif
465 
466 #endif /* SDL_VIDEO_OPENGL_CGL */
467 
468 /* vi: set ts=4 sw=4 expandtab: */
#define _THIS
#define SDL_AtomicSet
#define SDL_SetError
#define SDL_LoadObject
#define SDL_LockMutex
#define SDL_UnloadObject
#define SDL_getenv
#define SDL_strlcpy
#define SDL_sscanf
#define SDL_GL_GetProcAddress
#define SDL_AtomicAdd
#define SDL_UnlockMutex
#define SDL_GL_GetCurrentContext
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
void * SDL_LoadFunction(void *handle, const char *name)
unsigned char GLubyte
Definition: SDL_opengl.h:183
#define GL_VERSION
Definition: SDL_opengl.h:715
#define APIENTRY
Definition: SDL_opengl.h:139
unsigned int GLenum
Definition: SDL_opengl.h:176
int GLint
Definition: SDL_opengl.h:182
GLuint id
GLsizei const GLchar *const * path
GLsizei const GLfloat * value
GLfloat GLfloat GLfloat GLfloat h
GLubyte GLubyte GLubyte GLubyte w
#define SDL_BYTESPERPIXEL(X)
Definition: SDL_pixels.h:128
SDL_bool
Definition: SDL_stdinc.h:168
#define SDL_arraysize(array)
Definition: SDL_stdinc.h:121
SDL_VideoDisplay * SDL_GetDisplayForWindow(SDL_Window *window)
Definition: SDL_video.c:1130
static SDL_VideoDevice * _this
Definition: SDL_video.c:126
@ SDL_WINDOW_ALLOW_HIGHDPI
Definition: SDL_video.h:112
void * SDL_GLContext
An opaque handle to an OpenGL context.
Definition: SDL_video.h:195
@ SDL_GL_CONTEXT_PROFILE_ES
Definition: SDL_video.h:235
@ SDL_GL_CONTEXT_PROFILE_CORE
Definition: SDL_video.h:233
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
#define NULL
Definition: begin_code.h:163
EGLSurface EGLNativeWindowType * window
Definition: eglext.h:1025
set set set set set set set macro pixldst1 abits if abits op else op endif endm macro pixldst2 abits if abits op else op endif endm macro pixldst4 abits if abits op else op endif endm macro pixldst0 abits op endm macro pixldst3 mem_operand op endm macro pixldst30 mem_operand op endm macro pixldst abits if abits elseif abits elseif abits elseif abits elseif abits pixldst0 abits else pixldst0 abits pixldst0 abits pixldst0 abits pixldst0 abits endif elseif abits else pixldst0 abits pixldst0 abits endif elseif abits else error unsupported bpp *numpix else pixst endif endm macro pixld1_s mem_operand if asr adds SRC_WIDTH_FIXED bpl add asl mov asr adds SRC_WIDTH_FIXED bpl add asl mov asr adds SRC_WIDTH_FIXED bpl add asl mov asr adds SRC_WIDTH_FIXED bpl add asl elseif asr adds SRC_WIDTH_FIXED bpl add asl mov asr adds SRC_WIDTH_FIXED bpl add asl else error unsupported endif endm macro pixld2_s mem_operand if mov asr add asl add asl mov asr sub UNIT_X add asl mov asr add asl add asl mov asr add UNIT_X add asl else pixld1_s mem_operand pixld1_s mem_operand endif endm macro pixld0_s mem_operand if asr adds SRC_WIDTH_FIXED bpl add asl elseif asr adds SRC_WIDTH_FIXED bpl add asl endif endm macro pixld_s_internal mem_operand if mem_operand pixld2_s mem_operand pixdeinterleave basereg elseif mem_operand elseif mem_operand elseif mem_operand elseif mem_operand pixld0_s mem_operand else pixld0_s mem_operand pixld0_s mem_operand pixld0_s mem_operand pixld0_s mem_operand endif elseif mem_operand else pixld0_s mem_operand pixld0_s mem_operand endif elseif mem_operand else error unsupported mem_operand if bpp mem_operand endif endm macro vuzp8 reg2 vuzp d d &reg2 endm macro vzip8 reg2 vzip d d &reg2 endm macro pixdeinterleave basereg basereg basereg basereg basereg endif endm macro pixinterleave basereg basereg basereg basereg basereg endif endm macro PF boost_increment endif if endif PF tst PF addne PF subne PF cmp ORIG_W if endif if endif if endif PF subge ORIG_W PF subges if endif if endif if endif endif endm macro cache_preload_simple endif if dst_r_bpp pld[DST_R, #(PREFETCH_DISTANCE_SIMPLE *dst_r_bpp/8)] endif if mask_bpp pld if[MASK, #(PREFETCH_DISTANCE_SIMPLE *mask_bpp/8)] endif endif endm macro fetch_mask_pixblock pixld mask_basereg pixblock_size MASK endm macro ensure_destination_ptr_alignment process_pixblock_tail_head if beq irp skip1(dst_w_bpp<=(lowbit *8)) &&((lowbit *8)<(pixblock_size *dst_w_bpp)) .if lowbit< 16 tst DST_R
double floor(double x)
Definition: s_floor.c:33
CGDirectDisplayID display
Uint32 format
Definition: SDL_video.h:55
SDL_mutex * swaplock
char driver_path[256]
Definition: SDL_sysvideo.h:364
void(* GL_DeleteContext)(_THIS, SDL_GLContext context)
Definition: SDL_sysvideo.h:265
int(* GL_MakeCurrent)(_THIS, SDL_Window *window, SDL_GLContext context)
Definition: SDL_sysvideo.h:260
struct SDL_VideoDevice::@440 gl_config
SDL_GLContext(* GL_CreateContext)(_THIS, SDL_Window *window)
Definition: SDL_sysvideo.h:259
int(* GL_SetSwapInterval)(_THIS, int interval)
Definition: SDL_sysvideo.h:262
void(* GL_UnloadLibrary)(_THIS)
Definition: SDL_sysvideo.h:258
void *(* GL_GetProcAddress)(_THIS, const char *proc)
Definition: SDL_sysvideo.h:257
int(* GL_GetSwapInterval)(_THIS)
Definition: SDL_sysvideo.h:263
int(* GL_LoadLibrary)(_THIS, const char *path)
Definition: SDL_sysvideo.h:256
int(* GL_SwapWindow)(_THIS, SDL_Window *window)
Definition: SDL_sysvideo.h:264
int share_with_current_context
Definition: SDL_sysvideo.h:357
SDL_DisplayMode current_mode
Definition: SDL_sysvideo.h:133
NSMutableArray * nscontexts
SDL_Window * window
NSView * sdlContentView
The type used to identify a window.
Definition: SDL_sysvideo.h:75
void * driverdata
Definition: SDL_sysvideo.h:112
static SDL_Rect viewport
Definition: testviewport.c:28
static screen_context_t context
Definition: video.c:25
typedef int(__stdcall *FARPROC)()