From 5cb1a48fa3a82e57cd987d0cb46c177c52d0cfc3 Mon Sep 17 00:00:00 2001 From: amarcu5 Date: Mon, 14 Jan 2019 02:11:28 +0000 Subject: [PATCH] cocoa: Implement OpenGL swap interval support with CVDisplayLink Fixes VSync on macOS 10.14 (Mojave) by using CVDisplayLink to synchronise frames rather than setting NSOpenGLContextParameterSwapInterval. --- src/nsgl_context.h | 12 +++++++++-- src/nsgl_context.m | 51 +++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 58 insertions(+), 5 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 18042dee6..76dd3e880 --- 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 ec1012e9d..9e811e3fe --- 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) { if (window) @@ -43,6 +64,18 @@ static void makeContextCurrentNSGL(_GLFWwindow* window) static void swapBuffersNSGL(_GLFWwindow* window) { + 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]; } @@ -51,9 +84,10 @@ static void swapIntervalNSGL(int interval) { _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]; } static int extensionSupportedNSGL(const char* extension) @@ -316,6 +350,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, + window->context.nsgl.object, + window->context.nsgl.pixelFormat); + CVDisplayLinkStart(window->context.nsgl.displayLink); + + window->context.nsgl.swapIntervalCond = [NSCondition new]; + return GLFW_TRUE; }