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.
This commit is contained in:
amarcu5 2019-01-14 02:11:28 +00:00
parent 72c3908e14
commit 5cb1a48fa3
2 changed files with 58 additions and 5 deletions

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

@ -27,6 +27,10 @@
#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
//
@ -34,6 +38,10 @@ typedef struct _GLFWcontextNSGL
{
id pixelFormat;
id object;
CVDisplayLinkRef displayLink;
atomic_int swapInterval;
int swapIntervalsPassed;
id swapIntervalCond;
} _GLFWcontextNSGL;

51
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)
{
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;
}