NSGL: Implement swap interval with CVDisplayLink

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.
This commit is contained in:
amarcu5 2019-01-14 02:11:28 +00:00 committed by Camilla Löwy
parent 4e3204d86d
commit 82ca58da04
2 changed files with 60 additions and 8 deletions

12
src/nsgl_context.h Normal file → Executable file
View File

@ -27,13 +27,21 @@
#define _GLFW_PLATFORM_CONTEXT_STATE _GLFWcontextNSGL nsgl
#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE _GLFWlibraryNSGL nsgl
#import <CoreVideo/CoreVideo.h>
#include <stdatomic.h>
// NSGL-specific per-context data
//
typedef struct _GLFWcontextNSGL
{
id pixelFormat;
id object;
id pixelFormat;
id object;
CVDisplayLinkRef displayLink;
atomic_int swapInterval;
int swapIntervalsPassed;
id swapIntervalCond;
} _GLFWcontextNSGL;

56
src/nsgl_context.m Normal file → Executable file
View File

@ -31,6 +31,27 @@
#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 {
@ -48,21 +69,33 @@ static void makeContextCurrentNSGL(_GLFWwindow* window)
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);
GLint sync = interval;
[window->context.nsgl.object setValues:&sync
forParameter:NSOpenGLContextParameterSwapInterval];
atomic_store(&window->context.nsgl.swapInterval, interval);
[window->context.nsgl.swapIntervalCond lock];
window->context.nsgl.swapIntervalsPassed = 0;
[window->context.nsgl.swapIntervalCond unlock];
} // autoreleasepool
}
@ -330,6 +363,17 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window,
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;
}