mirror of
https://github.com/gwm17/glfw.git
synced 2024-11-23 10:48:51 -05:00
82ca58da04
This fixes OpenGL swap interval (vsync) on macOS 10.14 Mojave by using CVDisplayLink to synchronise to the monitor refresh rate rather than setting NSOpenGLContextParameterSwapInterval. Solution based on advice provided by @rcgordon. Closes #1417.
399 lines
13 KiB
Objective-C
Executable File
399 lines
13 KiB
Objective-C
Executable File
//========================================================================
|
|
// GLFW 3.3 macOS - www.glfw.org
|
|
//------------------------------------------------------------------------
|
|
// Copyright (c) 2009-2016 Camilla Löwy <elmindreda@glfw.org>
|
|
//
|
|
// 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.
|
|
//
|
|
//========================================================================
|
|
|
|
#include "internal.h"
|
|
|
|
#if MAC_OS_X_VERSION_MAX_ALLOWED < 101400
|
|
#define NSOpenGLContextParameterSwapInterval NSOpenGLCPSwapInterval
|
|
#define NSOpenGLContextParameterSurfaceOpacity NSOpenGLCPSurfaceOpacity
|
|
#endif
|
|
|
|
static CVReturn displayLinkCallback(CVDisplayLinkRef displayLink,
|
|
const CVTimeStamp* now,
|
|
const CVTimeStamp* outputTime,
|
|
CVOptionFlags flagsIn,
|
|
CVOptionFlags* flagsOut,
|
|
void* userInfo)
|
|
{
|
|
_GLFWwindow* window = (_GLFWwindow *) userInfo;
|
|
|
|
const int setting = atomic_load(&window->context.nsgl.swapInterval);
|
|
if (setting > 0)
|
|
{
|
|
[window->context.nsgl.swapIntervalCond lock];
|
|
window->context.nsgl.swapIntervalsPassed++;
|
|
[window->context.nsgl.swapIntervalCond signal];
|
|
[window->context.nsgl.swapIntervalCond unlock];
|
|
}
|
|
|
|
return kCVReturnSuccess;
|
|
}
|
|
|
|
static void makeContextCurrentNSGL(_GLFWwindow* window)
|
|
{
|
|
@autoreleasepool {
|
|
|
|
if (window)
|
|
[window->context.nsgl.object makeCurrentContext];
|
|
else
|
|
[NSOpenGLContext clearCurrentContext];
|
|
|
|
_glfwPlatformSetTls(&_glfw.contextSlot, window);
|
|
|
|
} // autoreleasepool
|
|
}
|
|
|
|
static void swapBuffersNSGL(_GLFWwindow* window)
|
|
{
|
|
@autoreleasepool {
|
|
|
|
const int setting = atomic_load(&window->context.nsgl.swapInterval);
|
|
if (setting > 0)
|
|
{
|
|
[window->context.nsgl.swapIntervalCond lock];
|
|
do
|
|
{
|
|
[window->context.nsgl.swapIntervalCond wait];
|
|
} while (window->context.nsgl.swapIntervalsPassed % setting != 0);
|
|
window->context.nsgl.swapIntervalsPassed = 0;
|
|
[window->context.nsgl.swapIntervalCond unlock];
|
|
}
|
|
|
|
// ARP appears to be unnecessary, but this is future-proof
|
|
[window->context.nsgl.object flushBuffer];
|
|
|
|
} // autoreleasepool
|
|
}
|
|
|
|
static void swapIntervalNSGL(int interval)
|
|
{
|
|
@autoreleasepool {
|
|
_GLFWwindow* window = _glfwPlatformGetTls(&_glfw.contextSlot);
|
|
atomic_store(&window->context.nsgl.swapInterval, interval);
|
|
[window->context.nsgl.swapIntervalCond lock];
|
|
window->context.nsgl.swapIntervalsPassed = 0;
|
|
[window->context.nsgl.swapIntervalCond unlock];
|
|
} // autoreleasepool
|
|
}
|
|
|
|
static int extensionSupportedNSGL(const char* extension)
|
|
{
|
|
// There are no NSGL extensions
|
|
return GLFW_FALSE;
|
|
}
|
|
|
|
static GLFWglproc getProcAddressNSGL(const char* procname)
|
|
{
|
|
CFStringRef symbolName = CFStringCreateWithCString(kCFAllocatorDefault,
|
|
procname,
|
|
kCFStringEncodingASCII);
|
|
|
|
GLFWglproc symbol = CFBundleGetFunctionPointerForName(_glfw.nsgl.framework,
|
|
symbolName);
|
|
|
|
CFRelease(symbolName);
|
|
|
|
return symbol;
|
|
}
|
|
|
|
// Destroy the OpenGL context
|
|
//
|
|
static void destroyContextNSGL(_GLFWwindow* window)
|
|
{
|
|
@autoreleasepool {
|
|
|
|
[window->context.nsgl.pixelFormat release];
|
|
window->context.nsgl.pixelFormat = nil;
|
|
|
|
[window->context.nsgl.object release];
|
|
window->context.nsgl.object = nil;
|
|
|
|
} // autoreleasepool
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
////// GLFW internal API //////
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
// Initialize OpenGL support
|
|
//
|
|
GLFWbool _glfwInitNSGL(void)
|
|
{
|
|
if (_glfw.nsgl.framework)
|
|
return GLFW_TRUE;
|
|
|
|
_glfw.nsgl.framework =
|
|
CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl"));
|
|
if (_glfw.nsgl.framework == NULL)
|
|
{
|
|
_glfwInputError(GLFW_API_UNAVAILABLE,
|
|
"NSGL: Failed to locate OpenGL framework");
|
|
return GLFW_FALSE;
|
|
}
|
|
|
|
return GLFW_TRUE;
|
|
}
|
|
|
|
// Terminate OpenGL support
|
|
//
|
|
void _glfwTerminateNSGL(void)
|
|
{
|
|
}
|
|
|
|
// Create the OpenGL context
|
|
//
|
|
GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window,
|
|
const _GLFWctxconfig* ctxconfig,
|
|
const _GLFWfbconfig* fbconfig)
|
|
{
|
|
if (ctxconfig->client == GLFW_OPENGL_ES_API)
|
|
{
|
|
_glfwInputError(GLFW_API_UNAVAILABLE,
|
|
"NSGL: OpenGL ES is not available on macOS");
|
|
return GLFW_FALSE;
|
|
}
|
|
|
|
if (ctxconfig->major > 2)
|
|
{
|
|
if (ctxconfig->major == 3 && ctxconfig->minor < 2)
|
|
{
|
|
_glfwInputError(GLFW_VERSION_UNAVAILABLE,
|
|
"NSGL: The targeted version of macOS does not support OpenGL 3.0 or 3.1 but may support 3.2 and above");
|
|
return GLFW_FALSE;
|
|
}
|
|
|
|
if (!ctxconfig->forward || ctxconfig->profile != GLFW_OPENGL_CORE_PROFILE)
|
|
{
|
|
_glfwInputError(GLFW_VERSION_UNAVAILABLE,
|
|
"NSGL: The targeted version of macOS only supports forward-compatible core profile contexts for OpenGL 3.2 and above");
|
|
return GLFW_FALSE;
|
|
}
|
|
}
|
|
|
|
// Context robustness modes (GL_KHR_robustness) are not yet supported by
|
|
// macOS but are not a hard constraint, so ignore and continue
|
|
|
|
// Context release behaviors (GL_KHR_context_flush_control) are not yet
|
|
// 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
|
|
|
|
#define addAttrib(a) \
|
|
{ \
|
|
assert((size_t) index < sizeof(attribs) / sizeof(attribs[0])); \
|
|
attribs[index++] = a; \
|
|
}
|
|
#define setAttrib(a, v) { addAttrib(a); addAttrib(v); }
|
|
|
|
NSOpenGLPixelFormatAttribute attribs[40];
|
|
int index = 0;
|
|
|
|
addAttrib(NSOpenGLPFAAccelerated);
|
|
addAttrib(NSOpenGLPFAClosestPolicy);
|
|
|
|
if (ctxconfig->nsgl.offline)
|
|
{
|
|
addAttrib(NSOpenGLPFAAllowOfflineRenderers);
|
|
// 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
|
|
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1080
|
|
addAttrib(kCGLPFASupportsAutomaticGraphicsSwitching);
|
|
#endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/
|
|
}
|
|
|
|
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
|
|
if (ctxconfig->major >= 4)
|
|
{
|
|
setAttrib(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion4_1Core);
|
|
}
|
|
else
|
|
#endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/
|
|
if (ctxconfig->major >= 3)
|
|
{
|
|
setAttrib(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core);
|
|
}
|
|
|
|
if (ctxconfig->major <= 2)
|
|
{
|
|
if (fbconfig->auxBuffers != GLFW_DONT_CARE)
|
|
setAttrib(NSOpenGLPFAAuxBuffers, fbconfig->auxBuffers);
|
|
|
|
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;
|
|
|
|
setAttrib(NSOpenGLPFAAccumSize, accumBits);
|
|
}
|
|
}
|
|
|
|
if (fbconfig->redBits != GLFW_DONT_CARE &&
|
|
fbconfig->greenBits != GLFW_DONT_CARE &&
|
|
fbconfig->blueBits != GLFW_DONT_CARE)
|
|
{
|
|
int colorBits = fbconfig->redBits +
|
|
fbconfig->greenBits +
|
|
fbconfig->blueBits;
|
|
|
|
// macOS needs non-zero color size, so set reasonable values
|
|
if (colorBits == 0)
|
|
colorBits = 24;
|
|
else if (colorBits < 15)
|
|
colorBits = 15;
|
|
|
|
setAttrib(NSOpenGLPFAColorSize, colorBits);
|
|
}
|
|
|
|
if (fbconfig->alphaBits != GLFW_DONT_CARE)
|
|
setAttrib(NSOpenGLPFAAlphaSize, fbconfig->alphaBits);
|
|
|
|
if (fbconfig->depthBits != GLFW_DONT_CARE)
|
|
setAttrib(NSOpenGLPFADepthSize, fbconfig->depthBits);
|
|
|
|
if (fbconfig->stencilBits != GLFW_DONT_CARE)
|
|
setAttrib(NSOpenGLPFAStencilSize, fbconfig->stencilBits);
|
|
|
|
if (fbconfig->stereo)
|
|
{
|
|
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200
|
|
_glfwInputError(GLFW_FORMAT_UNAVAILABLE,
|
|
"NSGL: Stereo rendering is deprecated");
|
|
return GLFW_FALSE;
|
|
#else
|
|
addAttrib(NSOpenGLPFAStereo);
|
|
#endif
|
|
}
|
|
|
|
if (fbconfig->doublebuffer)
|
|
addAttrib(NSOpenGLPFADoubleBuffer);
|
|
|
|
if (fbconfig->samples != GLFW_DONT_CARE)
|
|
{
|
|
if (fbconfig->samples == 0)
|
|
{
|
|
setAttrib(NSOpenGLPFASampleBuffers, 0);
|
|
}
|
|
else
|
|
{
|
|
setAttrib(NSOpenGLPFASampleBuffers, 1);
|
|
setAttrib(NSOpenGLPFASamples, fbconfig->samples);
|
|
}
|
|
}
|
|
|
|
// NOTE: All NSOpenGLPixelFormats on the relevant cards support sRGB
|
|
// framebuffer, so there's no need (and no way) to request it
|
|
|
|
addAttrib(0);
|
|
|
|
#undef addAttrib
|
|
#undef setAttrib
|
|
|
|
window->context.nsgl.pixelFormat =
|
|
[[NSOpenGLPixelFormat alloc] initWithAttributes:attribs];
|
|
if (window->context.nsgl.pixelFormat == nil)
|
|
{
|
|
_glfwInputError(GLFW_FORMAT_UNAVAILABLE,
|
|
"NSGL: Failed to find a suitable pixel format");
|
|
return GLFW_FALSE;
|
|
}
|
|
|
|
NSOpenGLContext* share = NULL;
|
|
|
|
if (ctxconfig->share)
|
|
share = ctxconfig->share->context.nsgl.object;
|
|
|
|
window->context.nsgl.object =
|
|
[[NSOpenGLContext alloc] initWithFormat:window->context.nsgl.pixelFormat
|
|
shareContext:share];
|
|
if (window->context.nsgl.object == nil)
|
|
{
|
|
_glfwInputError(GLFW_VERSION_UNAVAILABLE,
|
|
"NSGL: Failed to create OpenGL context");
|
|
return GLFW_FALSE;
|
|
}
|
|
|
|
if (fbconfig->transparent)
|
|
{
|
|
GLint opaque = 0;
|
|
[window->context.nsgl.object setValues:&opaque
|
|
forParameter:NSOpenGLContextParameterSurfaceOpacity];
|
|
}
|
|
|
|
[window->context.nsgl.object setView:window->ns.view];
|
|
|
|
window->context.makeCurrent = makeContextCurrentNSGL;
|
|
window->context.swapBuffers = swapBuffersNSGL;
|
|
window->context.swapInterval = swapIntervalNSGL;
|
|
window->context.extensionSupported = extensionSupportedNSGL;
|
|
window->context.getProcAddress = getProcAddressNSGL;
|
|
window->context.destroy = destroyContextNSGL;
|
|
|
|
CVDisplayLinkCreateWithActiveCGDisplays(&window->context.nsgl.displayLink);
|
|
CVDisplayLinkSetOutputCallback(window->context.nsgl.displayLink,
|
|
&displayLinkCallback,
|
|
window);
|
|
CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(window->context.nsgl.displayLink,
|
|
(CGLContextObj) window->context.nsgl.object,
|
|
(CGLPixelFormatObj) window->context.nsgl.pixelFormat);
|
|
CVDisplayLinkStart(window->context.nsgl.displayLink);
|
|
|
|
window->context.nsgl.swapIntervalCond = [NSCondition new];
|
|
|
|
return GLFW_TRUE;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
////// GLFW native API //////
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
GLFWAPI id glfwGetNSGLContext(GLFWwindow* handle)
|
|
{
|
|
_GLFWwindow* window = (_GLFWwindow*) handle;
|
|
_GLFW_REQUIRE_INIT_OR_RETURN(nil);
|
|
|
|
if (window->context.client == GLFW_NO_API)
|
|
{
|
|
_glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL);
|
|
return NULL;
|
|
}
|
|
|
|
return window->context.nsgl.object;
|
|
}
|
|
|