From 82ca58da04c954868172f226d7d977d7186c3ab7 Mon Sep 17 00:00:00 2001 From: amarcu5 Date: Mon, 14 Jan 2019 02:11:28 +0000 Subject: [PATCH] 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. --- src/nsgl_context.h | 12 ++++++++-- src/nsgl_context.m | 56 +++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 60 insertions(+), 8 deletions(-) mode change 100644 => 100755 src/nsgl_context.h mode change 100644 => 100755 src/nsgl_context.m diff --git a/src/nsgl_context.h b/src/nsgl_context.h old mode 100644 new mode 100755 index 18042dee..76dd3e88 --- a/src/nsgl_context.h +++ b/src/nsgl_context.h @@ -27,13 +27,21 @@ #define _GLFW_PLATFORM_CONTEXT_STATE _GLFWcontextNSGL nsgl #define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE _GLFWlibraryNSGL nsgl +#import + +#include + // 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; diff --git a/src/nsgl_context.m b/src/nsgl_context.m old mode 100644 new mode 100755 index f3bf2425..d834259d --- a/src/nsgl_context.m +++ b/src/nsgl_context.m @@ -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; }