SDL 2.0
SDL_cocoaopengl.m
Go to the documentation of this file.
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2019 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 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 [super update];
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 /* This should never be nil since sdlContentView is only nil if the
101 window was created via SDL_CreateWindowFrom, and SDL doesn't allow
102 OpenGL contexts to be created in that case. However, it doesn't hurt
103 to check. */
104 if (contentview == nil) {
105 /* Prefer to access the cached content view above instead of this,
106 since as of Xcode 11 + SDK 10.15, [window contentView] causes
107 Apple's Main Thread Checker to output a warning. */
108 contentview = [windowdata->nswindow contentView];
109 }
110
111 /* Now sign up for scheduled updates for the new window. */
112 NSMutableArray *contexts = windowdata->nscontexts;
113 @synchronized (contexts) {
114 [contexts addObject:self];
115 }
116
117 if ([self view] != contentview) {
118 [self setView:contentview];
119 if (self == [NSOpenGLContext currentContext]) {
120 [self update];
121 } else {
122 [self scheduleUpdate];
123 }
124 }
125 } else {
126 [self clearDrawable];
127 if (self == [NSOpenGLContext currentContext]) {
128 [self update];
129 } else {
130 [self scheduleUpdate];
131 }
132 }
133}
134
135@end
136
137
138int
139Cocoa_GL_LoadLibrary(_THIS, const char *path)
140{
141 /* Load the OpenGL library */
142 if (path == NULL) {
143 path = SDL_getenv("SDL_OPENGL_LIBRARY");
144 }
145 if (path == NULL) {
146 path = DEFAULT_OPENGL;
147 }
149 if (!_this->gl_config.dll_handle) {
150 return -1;
151 }
154 return 0;
155}
156
157void *
158Cocoa_GL_GetProcAddress(_THIS, const char *proc)
159{
161}
162
163void
164Cocoa_GL_UnloadLibrary(_THIS)
165{
168}
169
171Cocoa_GL_CreateContext(_THIS, SDL_Window * window)
172{ @autoreleasepool
173{
175 SDL_DisplayData *displaydata = (SDL_DisplayData *)display->driverdata;
176 SDL_bool lion_or_later = floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6;
177 NSOpenGLPixelFormatAttribute attr[32];
178 NSOpenGLPixelFormat *fmt;
179 SDLOpenGLContext *context;
180 NSOpenGLContext *share_context = nil;
181 int i = 0;
182 const char *glversion;
183 int glversion_major;
184 int glversion_minor;
185
187#if SDL_VIDEO_OPENGL_EGL
188 /* Switch to EGL based functions */
189 Cocoa_GL_UnloadLibrary(_this);
190 _this->GL_LoadLibrary = Cocoa_GLES_LoadLibrary;
191 _this->GL_GetProcAddress = Cocoa_GLES_GetProcAddress;
192 _this->GL_UnloadLibrary = Cocoa_GLES_UnloadLibrary;
193 _this->GL_CreateContext = Cocoa_GLES_CreateContext;
194 _this->GL_MakeCurrent = Cocoa_GLES_MakeCurrent;
195 _this->GL_SetSwapInterval = Cocoa_GLES_SetSwapInterval;
196 _this->GL_GetSwapInterval = Cocoa_GLES_GetSwapInterval;
197 _this->GL_SwapWindow = Cocoa_GLES_SwapWindow;
198 _this->GL_DeleteContext = Cocoa_GLES_DeleteContext;
199
200 if (Cocoa_GLES_LoadLibrary(_this, NULL) != 0) {
201 return NULL;
202 }
203 return Cocoa_GLES_CreateContext(_this, window);
204#else
205 SDL_SetError("SDL not configured with EGL support");
206 return NULL;
207#endif
208 }
209 if ((_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_CORE) && !lion_or_later) {
210 SDL_SetError ("OpenGL Core Profile is not supported on this platform version");
211 return NULL;
212 }
213
214 attr[i++] = NSOpenGLPFAAllowOfflineRenderers;
215
216 /* specify a profile if we're on Lion (10.7) or later. */
217 if (lion_or_later) {
218 NSOpenGLPixelFormatAttribute profile = NSOpenGLProfileVersionLegacy;
220 profile = NSOpenGLProfileVersion3_2Core;
221 }
222 attr[i++] = NSOpenGLPFAOpenGLProfile;
223 attr[i++] = profile;
224 }
225
226 attr[i++] = NSOpenGLPFAColorSize;
227 attr[i++] = SDL_BYTESPERPIXEL(display->current_mode.format)*8;
228
229 attr[i++] = NSOpenGLPFADepthSize;
230 attr[i++] = _this->gl_config.depth_size;
231
233 attr[i++] = NSOpenGLPFADoubleBuffer;
234 }
235
236 if (_this->gl_config.stereo) {
237 attr[i++] = NSOpenGLPFAStereo;
238 }
239
241 attr[i++] = NSOpenGLPFAStencilSize;
242 attr[i++] = _this->gl_config.stencil_size;
243 }
244
249 attr[i++] = NSOpenGLPFAAccumSize;
250 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;
251 }
252
254 attr[i++] = NSOpenGLPFASampleBuffers;
256 }
257
259 attr[i++] = NSOpenGLPFASamples;
261 attr[i++] = NSOpenGLPFANoRecovery;
262 }
263
264 if (_this->gl_config.accelerated >= 0) {
266 attr[i++] = NSOpenGLPFAAccelerated;
267 } else {
268 attr[i++] = NSOpenGLPFARendererID;
269 attr[i++] = kCGLRendererGenericFloatID;
270 }
271 }
272
273 attr[i++] = NSOpenGLPFAScreenMask;
274 attr[i++] = CGDisplayIDToOpenGLDisplayMask(displaydata->display);
275 attr[i] = 0;
276
277 fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr];
278 if (fmt == nil) {
279 SDL_SetError("Failed creating OpenGL pixel format");
280 return NULL;
281 }
282
284 share_context = (NSOpenGLContext*)SDL_GL_GetCurrentContext();
285 }
286
287 context = [[SDLOpenGLContext alloc] initWithFormat:fmt shareContext:share_context];
288
289 [fmt release];
290
291 if (context == nil) {
292 SDL_SetError("Failed creating OpenGL context");
293 return NULL;
294 }
295
296 if ( Cocoa_GL_MakeCurrent(_this, window, context) < 0 ) {
297 Cocoa_GL_DeleteContext(_this, context);
298 SDL_SetError("Failed making OpenGL context current");
299 return NULL;
300 }
301
302 if (_this->gl_config.major_version < 3 &&
304 _this->gl_config.flags == 0) {
305 /* This is a legacy profile, so to match other backends, we're done. */
306 } else {
307 const GLubyte *(APIENTRY * glGetStringFunc)(GLenum);
308
309 glGetStringFunc = (const GLubyte *(APIENTRY *)(GLenum)) SDL_GL_GetProcAddress("glGetString");
310 if (!glGetStringFunc) {
311 Cocoa_GL_DeleteContext(_this, context);
312 SDL_SetError ("Failed getting OpenGL glGetString entry point");
313 return NULL;
314 }
315
316 glversion = (const char *)glGetStringFunc(GL_VERSION);
317 if (glversion == NULL) {
318 Cocoa_GL_DeleteContext(_this, context);
319 SDL_SetError ("Failed getting OpenGL context version");
320 return NULL;
321 }
322
323 if (SDL_sscanf(glversion, "%d.%d", &glversion_major, &glversion_minor) != 2) {
324 Cocoa_GL_DeleteContext(_this, context);
325 SDL_SetError ("Failed parsing OpenGL context version");
326 return NULL;
327 }
328
329 if ((glversion_major < _this->gl_config.major_version) ||
330 ((glversion_major == _this->gl_config.major_version) && (glversion_minor < _this->gl_config.minor_version))) {
331 Cocoa_GL_DeleteContext(_this, context);
332 SDL_SetError ("Failed creating OpenGL context at version requested");
333 return NULL;
334 }
335
336 /* In the future we'll want to do this, but to match other platforms
337 we'll leave the OpenGL version the way it is for now
338 */
339 /*_this->gl_config.major_version = glversion_major;*/
340 /*_this->gl_config.minor_version = glversion_minor;*/
341 }
342 return context;
343}}
344
345int
346Cocoa_GL_MakeCurrent(_THIS, SDL_Window * window, SDL_GLContext context)
347{ @autoreleasepool
348{
349 if (context) {
350 SDLOpenGLContext *nscontext = (SDLOpenGLContext *)context;
351 [nscontext setWindow:window];
352 [nscontext updateIfNeeded];
353 [nscontext makeCurrentContext];
354 } else {
355 [NSOpenGLContext clearCurrentContext];
356 }
357
358 return 0;
359}}
360
361void
362Cocoa_GL_GetDrawableSize(_THIS, SDL_Window * window, int * w, int * h)
363{
364 SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
365 NSView *contentView = [windata->nswindow contentView];
366 NSRect viewport = [contentView bounds];
367
368 if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
369 /* This gives us the correct viewport for a Retina-enabled view, only
370 * supported on 10.7+. */
371 if ([contentView respondsToSelector:@selector(convertRectToBacking:)]) {
372 viewport = [contentView convertRectToBacking:viewport];
373 }
374 }
375
376 if (w) {
377 *w = viewport.size.width;
378 }
379
380 if (h) {
381 *h = viewport.size.height;
382 }
383}
384
385int
386Cocoa_GL_SetSwapInterval(_THIS, int interval)
387{ @autoreleasepool
388{
389 NSOpenGLContext *nscontext;
390 GLint value;
391 int status;
392
393 if (interval < 0) { /* no extension for this on Mac OS X at the moment. */
394 return SDL_SetError("Late swap tearing currently unsupported");
395 }
396
397 nscontext = (NSOpenGLContext*)SDL_GL_GetCurrentContext();
398 if (nscontext != nil) {
399 value = interval;
400 [nscontext setValues:&value forParameter:NSOpenGLCPSwapInterval];
401 status = 0;
402 } else {
403 status = SDL_SetError("No current OpenGL context");
404 }
405
406 return status;
407}}
408
409int
410Cocoa_GL_GetSwapInterval(_THIS)
411{ @autoreleasepool
412{
413 NSOpenGLContext *nscontext;
414 GLint value;
415 int status = 0;
416
417 nscontext = (NSOpenGLContext*)SDL_GL_GetCurrentContext();
418 if (nscontext != nil) {
419 [nscontext getValues:&value forParameter:NSOpenGLCPSwapInterval];
420 status = (int)value;
421 }
422
423 return status;
424}}
425
426int
427Cocoa_GL_SwapWindow(_THIS, SDL_Window * window)
428{ @autoreleasepool
429{
430 SDLOpenGLContext* nscontext = (SDLOpenGLContext*)SDL_GL_GetCurrentContext();
432
433 /* on 10.14 ("Mojave") and later, this deadlocks if two contexts in two
434 threads try to swap at the same time, so put a mutex around it. */
435 SDL_LockMutex(videodata->swaplock);
436 [nscontext flushBuffer];
437 [nscontext updateIfNeeded];
438 SDL_UnlockMutex(videodata->swaplock);
439 return 0;
440}}
441
442void
443Cocoa_GL_DeleteContext(_THIS, SDL_GLContext context)
444{ @autoreleasepool
445{
446 SDLOpenGLContext *nscontext = (SDLOpenGLContext *)context;
447
448 [nscontext setWindow:NULL];
449 [nscontext release];
450}}
451
452/* We still support OpenGL as long as Apple offers it, deprecated or not, so disable deprecation warnings about it. */
453#ifdef __clang__
454#pragma clang diagnostic pop
455#endif
456
457#endif /* SDL_VIDEO_OPENGL_CGL */
458
459/* 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:162
#define SDL_arraysize(array)
Definition: SDL_stdinc.h:115
SDL_VideoDisplay * SDL_GetDisplayForWindow(SDL_Window *window)
Definition: SDL_video.c:1089
static SDL_VideoDevice * _this
Definition: SDL_video.c:118
@ SDL_WINDOW_ALLOW_HIGHDPI
Definition: SDL_video.h:113
void * SDL_GLContext
An opaque handle to an OpenGL context.
Definition: SDL_video.h:193
@ SDL_GL_CONTEXT_PROFILE_ES
Definition: SDL_video.h:233
@ SDL_GL_CONTEXT_PROFILE_CORE
Definition: SDL_video.h:231
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:167
EGLSurface EGLNativeWindowType * window
Definition: eglext.h:1025
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:354
void(* GL_DeleteContext)(_THIS, SDL_GLContext context)
Definition: SDL_sysvideo.h:264
int(* GL_MakeCurrent)(_THIS, SDL_Window *window, SDL_GLContext context)
Definition: SDL_sysvideo.h:259
SDL_GLContext(* GL_CreateContext)(_THIS, SDL_Window *window)
Definition: SDL_sysvideo.h:258
int(* GL_SetSwapInterval)(_THIS, int interval)
Definition: SDL_sysvideo.h:261
void(* GL_UnloadLibrary)(_THIS)
Definition: SDL_sysvideo.h:257
int(* GL_GetSwapInterval)(_THIS)
Definition: SDL_sysvideo.h:262
int(* GL_LoadLibrary)(_THIS, const char *path)
Definition: SDL_sysvideo.h:255
int(* GL_SwapWindow)(_THIS, SDL_Window *window)
Definition: SDL_sysvideo.h:263
struct SDL_VideoDevice::@262 gl_config
void *(* GL_GetProcAddress)(_THIS, const char *proc)
Definition: SDL_sysvideo.h:256
int share_with_current_context
Definition: SDL_sysvideo.h:347
SDL_DisplayMode current_mode
Definition: SDL_sysvideo.h:132
NSMutableArray * nscontexts
SDL_Window * window
NSView * sdlContentView
NSWindow * nswindow
The type used to identify a window.
Definition: SDL_sysvideo.h:74
void * driverdata
Definition: SDL_sysvideo.h:111
SDL_Rect viewport
Definition: testviewport.c:28
static screen_context_t context
Definition: video.c:25