nsgl_context.m 12.7 KB
Newer Older
Camilla Berglund's avatar
Camilla Berglund committed
1
//========================================================================
2
// GLFW 3.3 macOS - www.glfw.org
Camilla Berglund's avatar
Camilla Berglund committed
3
//------------------------------------------------------------------------
Camilla Löwy's avatar
Camilla Löwy committed
4
// Copyright (c) 2009-2019 Camilla Löwy <elmindreda@glfw.org>
Camilla Berglund's avatar
Camilla Berglund committed
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
//    claim that you wrote the original software. If you use this software
//    in a product, an acknowledgment in the product documentation would
//    be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not
//    be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source
//    distribution.
//
//========================================================================
26
27
// It is fine to use C99 in this file because it will not be built with VS
//========================================================================
Camilla Berglund's avatar
Camilla Berglund committed
28
29
30

#include "internal.h"

31
32
33
#include <unistd.h>
#include <math.h>

34
static void makeContextCurrentNSGL(_GLFWwindow* window)
35
{
36
37
    @autoreleasepool {

38
39
40
41
42
    if (window)
        [window->context.nsgl.object makeCurrentContext];
    else
        [NSOpenGLContext clearCurrentContext];

43
    _glfwPlatformSetTls(&_glfw.contextSlot, window);
44
45

    } // autoreleasepool
46
47
}

48
static void swapBuffersNSGL(_GLFWwindow* window)
49
{
50
    @autoreleasepool {
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73

    // HACK: Simulate vsync with usleep as NSGL swap interval does not apply to
    //       windows with a non-visible occlusion state
    if (!([window->ns.object occlusionState] & NSWindowOcclusionStateVisible))
    {
        int interval = 0;
        [window->context.nsgl.object getValues:&interval
                                  forParameter:NSOpenGLContextParameterSwapInterval];

        if (interval > 0)
        {
            const double framerate = 60.0;
            const uint64_t frequency = _glfwPlatformGetTimerFrequency();
            const uint64_t value = _glfwPlatformGetTimerValue();

            const double elapsed = value / (double) frequency;
            const double period = 1.0 / framerate;
            const double delay = period - fmod(elapsed, period);

            usleep(floorl(delay * 1e6));
        }
    }

74
    [window->context.nsgl.object flushBuffer];
75

76
    } // autoreleasepool
77
78
}

79
static void swapIntervalNSGL(int interval)
80
{
81
    @autoreleasepool {
82

83
84
    _GLFWwindow* window = _glfwPlatformGetTls(&_glfw.contextSlot);
    if (window)
85
    {
86
87
        [window->context.nsgl.object setValues:&interval
                                  forParameter:NSOpenGLContextParameterSwapInterval];
88
89
    }

90
    } // autoreleasepool
91
92
}

93
static int extensionSupportedNSGL(const char* extension)
94
95
96
97
98
{
    // There are no NSGL extensions
    return GLFW_FALSE;
}

99
static GLFWglproc getProcAddressNSGL(const char* procname)
100
101
102
103
104
105
106
107
108
109
110
111
112
{
    CFStringRef symbolName = CFStringCreateWithCString(kCFAllocatorDefault,
                                                       procname,
                                                       kCFStringEncodingASCII);

    GLFWglproc symbol = CFBundleGetFunctionPointerForName(_glfw.nsgl.framework,
                                                          symbolName);

    CFRelease(symbolName);

    return symbol;
}

113
static void destroyContextNSGL(_GLFWwindow* window)
114
{
115
116
    @autoreleasepool {

117
118
119
120
121
    [window->context.nsgl.pixelFormat release];
    window->context.nsgl.pixelFormat = nil;

    [window->context.nsgl.object release];
    window->context.nsgl.object = nil;
122
123

    } // autoreleasepool
124
125
126
}


Camilla Berglund's avatar
Camilla Berglund committed
127
//////////////////////////////////////////////////////////////////////////
128
//////                       GLFW internal API                      //////
Camilla Berglund's avatar
Camilla Berglund committed
129
//////////////////////////////////////////////////////////////////////////
Camilla Berglund's avatar
Camilla Berglund committed
130

131
// Initialize OpenGL support
132
//
Camilla Berglund's avatar
Camilla Berglund committed
133
GLFWbool _glfwInitNSGL(void)
134
{
135
136
137
    if (_glfw.nsgl.framework)
        return GLFW_TRUE;

138
139
140
141
    _glfw.nsgl.framework =
        CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl"));
    if (_glfw.nsgl.framework == NULL)
    {
Camilla Berglund's avatar
Camilla Berglund committed
142
        _glfwInputError(GLFW_API_UNAVAILABLE,
143
                        "NSGL: Failed to locate OpenGL framework");
144
        return GLFW_FALSE;
145
146
    }

147
    return GLFW_TRUE;
148
149
150
}

// Terminate OpenGL support
151
//
152
void _glfwTerminateNSGL(void)
153
154
155
{
}

156
// Create the OpenGL context
157
//
Camilla Berglund's avatar
Camilla Berglund committed
158
159
160
GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window,
                                const _GLFWctxconfig* ctxconfig,
                                const _GLFWfbconfig* fbconfig)
161
{
162
    if (ctxconfig->client == GLFW_OPENGL_ES_API)
163
    {
164
        _glfwInputError(GLFW_API_UNAVAILABLE,
165
                        "NSGL: OpenGL ES is not available on macOS");
166
        return GLFW_FALSE;
167
168
    }

169
    if (ctxconfig->major > 2)
170
    {
171
        if (ctxconfig->major == 3 && ctxconfig->minor < 2)
172
        {
173
            _glfwInputError(GLFW_VERSION_UNAVAILABLE,
Camilla Löwy's avatar
Camilla Löwy committed
174
                            "NSGL: The targeted version of macOS does not support OpenGL 3.0 or 3.1 but may support 3.2 and above");
175
            return GLFW_FALSE;
176
177
        }

178
        if (!ctxconfig->forward || ctxconfig->profile != GLFW_OPENGL_CORE_PROFILE)
179
        {
180
            _glfwInputError(GLFW_VERSION_UNAVAILABLE,
181
                            "NSGL: The targeted version of macOS only supports forward-compatible core profile contexts for OpenGL 3.2 and above");
182
            return GLFW_FALSE;
183
184
185
        }
    }

Camilla Löwy's avatar
Camilla Löwy committed
186
    // Context robustness modes (GL_KHR_robustness) are not yet supported by
187
    // macOS but are not a hard constraint, so ignore and continue
188

189
    // Context release behaviors (GL_KHR_context_flush_control) are not yet
Camilla Löwy's avatar
Camilla Löwy committed
190
191
192
193
194
195
196
    // supported by macOS but are not a hard constraint, so ignore and continue

    // Debug contexts (GL_KHR_debug) are not yet supported by macOS but are not
    // a hard constraint, so ignore and continue

    // No-error contexts (GL_KHR_no_error) are not yet supported by macOS but
    // are not a hard constraint, so ignore and continue
197

Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
198
199
200
201
202
203
#define addAttrib(a) \
{ \
    assert((size_t) index < sizeof(attribs) / sizeof(attribs[0])); \
    attribs[index++] = a; \
}
#define setAttrib(a, v) { addAttrib(a); addAttrib(v); }
204

Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
205
206
    NSOpenGLPixelFormatAttribute attribs[40];
    int index = 0;
207

Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
208
209
    addAttrib(NSOpenGLPFAAccelerated);
    addAttrib(NSOpenGLPFAClosestPolicy);
210

211
212
    if (ctxconfig->nsgl.offline)
    {
Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
213
        addAttrib(NSOpenGLPFAAllowOfflineRenderers);
214
215
216
217
        // NOTE: This replaces the NSSupportsAutomaticGraphicsSwitching key in
        //       Info.plist for unbundled applications
        // HACK: This assumes that NSOpenGLPixelFormat will remain
        //       a straightforward wrapper of its CGL counterpart
Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
218
        addAttrib(kCGLPFASupportsAutomaticGraphicsSwitching);
219
220
    }

221
222
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
    if (ctxconfig->major >= 4)
223
    {
Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
224
        setAttrib(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion4_1Core);
225
226
    }
    else
227
#endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/
228
229
    if (ctxconfig->major >= 3)
    {
Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
230
        setAttrib(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core);
231
232
233
    }

    if (ctxconfig->major <= 2)
234
235
    {
        if (fbconfig->auxBuffers != GLFW_DONT_CARE)
Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
236
            setAttrib(NSOpenGLPFAAuxBuffers, fbconfig->auxBuffers);
237
238
239
240
241
242
243
244
245
246
247

        if (fbconfig->accumRedBits != GLFW_DONT_CARE &&
            fbconfig->accumGreenBits != GLFW_DONT_CARE &&
            fbconfig->accumBlueBits != GLFW_DONT_CARE &&
            fbconfig->accumAlphaBits != GLFW_DONT_CARE)
        {
            const int accumBits = fbconfig->accumRedBits +
                                  fbconfig->accumGreenBits +
                                  fbconfig->accumBlueBits +
                                  fbconfig->accumAlphaBits;

Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
248
            setAttrib(NSOpenGLPFAAccumSize, accumBits);
249
250
        }
    }
251

Camilla Berglund's avatar
Camilla Berglund committed
252
253
254
255
256
257
258
259
    if (fbconfig->redBits != GLFW_DONT_CARE &&
        fbconfig->greenBits != GLFW_DONT_CARE &&
        fbconfig->blueBits != GLFW_DONT_CARE)
    {
        int colorBits = fbconfig->redBits +
                        fbconfig->greenBits +
                        fbconfig->blueBits;

260
        // macOS needs non-zero color size, so set reasonable values
Camilla Berglund's avatar
Camilla Berglund committed
261
262
263
264
        if (colorBits == 0)
            colorBits = 24;
        else if (colorBits < 15)
            colorBits = 15;
265

Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
266
        setAttrib(NSOpenGLPFAColorSize, colorBits);
Camilla Berglund's avatar
Camilla Berglund committed
267
268
269
    }

    if (fbconfig->alphaBits != GLFW_DONT_CARE)
Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
270
        setAttrib(NSOpenGLPFAAlphaSize, fbconfig->alphaBits);
271

Camilla Berglund's avatar
Camilla Berglund committed
272
    if (fbconfig->depthBits != GLFW_DONT_CARE)
Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
273
        setAttrib(NSOpenGLPFADepthSize, fbconfig->depthBits);
274

Camilla Berglund's avatar
Camilla Berglund committed
275
    if (fbconfig->stencilBits != GLFW_DONT_CARE)
Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
276
        setAttrib(NSOpenGLPFAStencilSize, fbconfig->stencilBits);
277
278

    if (fbconfig->stereo)
279
280
281
282
283
284
    {
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200
        _glfwInputError(GLFW_FORMAT_UNAVAILABLE,
                        "NSGL: Stereo rendering is deprecated");
        return GLFW_FALSE;
#else
Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
285
        addAttrib(NSOpenGLPFAStereo);
286
287
#endif
    }
288

Camilla Berglund's avatar
Camilla Berglund committed
289
    if (fbconfig->doublebuffer)
Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
290
        addAttrib(NSOpenGLPFADoubleBuffer);
Camilla Berglund's avatar
Camilla Berglund committed
291

Camilla Berglund's avatar
Camilla Berglund committed
292
    if (fbconfig->samples != GLFW_DONT_CARE)
293
    {
Camilla Berglund's avatar
Camilla Berglund committed
294
295
        if (fbconfig->samples == 0)
        {
Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
296
            setAttrib(NSOpenGLPFASampleBuffers, 0);
Camilla Berglund's avatar
Camilla Berglund committed
297
298
299
        }
        else
        {
Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
300
301
            setAttrib(NSOpenGLPFASampleBuffers, 1);
            setAttrib(NSOpenGLPFASamples, fbconfig->samples);
Camilla Berglund's avatar
Camilla Berglund committed
302
        }
303
304
305
    }

    // NOTE: All NSOpenGLPixelFormats on the relevant cards support sRGB
306
    //       framebuffer, so there's no need (and no way) to request it
307

Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
308
    addAttrib(0);
309

Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
310
311
#undef addAttrib
#undef setAttrib
312

313
    window->context.nsgl.pixelFormat =
Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
314
        [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs];
315
    if (window->context.nsgl.pixelFormat == nil)
316
    {
317
318
        _glfwInputError(GLFW_FORMAT_UNAVAILABLE,
                        "NSGL: Failed to find a suitable pixel format");
319
        return GLFW_FALSE;
320
321
    }

Camilla Löwy's avatar
Camilla Löwy committed
322
    NSOpenGLContext* share = nil;
323

324
    if (ctxconfig->share)
325
        share = ctxconfig->share->context.nsgl.object;
326

327
328
    window->context.nsgl.object =
        [[NSOpenGLContext alloc] initWithFormat:window->context.nsgl.pixelFormat
329
                                   shareContext:share];
330
    if (window->context.nsgl.object == nil)
331
    {
Camilla Berglund's avatar
Camilla Berglund committed
332
        _glfwInputError(GLFW_VERSION_UNAVAILABLE,
Camilla Berglund's avatar
Camilla Berglund committed
333
                        "NSGL: Failed to create OpenGL context");
334
        return GLFW_FALSE;
335
336
    }

Bailey Cosier's avatar
Cleanup    
Bailey Cosier committed
337
    if (fbconfig->transparent)
Cem Karan's avatar
Cem Karan committed
338
339
    {
        GLint opaque = 0;
340
341
        [window->context.nsgl.object setValues:&opaque
                                  forParameter:NSOpenGLContextParameterSurfaceOpacity];
Cem Karan's avatar
Cem Karan committed
342
343
    }

344
    [window->ns.view setWantsBestResolutionOpenGLSurface:window->ns.retina];
345

346
    [window->context.nsgl.object setView:window->ns.view];
347

348
349
350
351
352
353
    window->context.makeCurrent = makeContextCurrentNSGL;
    window->context.swapBuffers = swapBuffersNSGL;
    window->context.swapInterval = swapIntervalNSGL;
    window->context.extensionSupported = extensionSupportedNSGL;
    window->context.getProcAddress = getProcAddressNSGL;
    window->context.destroy = destroyContextNSGL;
Camilla Berglund's avatar
Camilla Berglund committed
354

355
    return GLFW_TRUE;
Camilla Berglund's avatar
Camilla Berglund committed
356
357
}

358

Doug Binks's avatar
Doug Binks committed
359
360
361
362
363
static void _glfwMakeUserContextCurrentNSGL(_GLFWusercontext* context)
{
    @autoreleasepool {

        [context->nsgl.object makeCurrentContext];
364
365

        _glfwPlatformSetTls(&_glfw.usercontextSlot, context);
Doug Binks's avatar
Doug Binks committed
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403

    } // autoreleasepool
}

static void _glfwDestroyUserContextNSGL(_GLFWusercontext* context)
{
    @autoreleasepool {

        [context->nsgl.object release];

    } // autoreleasepool
    free(context);
}

_GLFWusercontext* _glfwCreateUserContextNSGL(_GLFWwindow* window)
{
    _GLFWusercontext* context;

    context = calloc(1, sizeof(_GLFWusercontext));
    context->window = window;

    context->nsgl.object =
        [[NSOpenGLContext alloc] initWithFormat:window->context.nsgl.pixelFormat
                                   shareContext:window->context.nsgl.object];
    if (window->context.nsgl.object == nil)
    {
        _glfwInputError(GLFW_VERSION_UNAVAILABLE,
                        "NSGL: Failed to create OpenGL user context");
        free(context);
        return NULL;
    }

    context->makeCurrent = _glfwMakeUserContextCurrentNSGL;
    context->destroy = _glfwDestroyUserContextNSGL;

    return context;
}

404
405
406
407
408
409
410
//////////////////////////////////////////////////////////////////////////
//////                        GLFW native API                       //////
//////////////////////////////////////////////////////////////////////////

GLFWAPI id glfwGetNSGLContext(GLFWwindow* handle)
{
    _GLFWwindow* window = (_GLFWwindow*) handle;
411
    _GLFW_REQUIRE_INIT_OR_RETURN(nil);
412

413
    if (window->context.client == GLFW_NO_API)
414
415
    {
        _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL);
Camilla Löwy's avatar
Camilla Löwy committed
416
        return nil;
417
418
    }

419
    return window->context.nsgl.object;
420
421
}