macOS: Use CVDisplayLink for vsync

This commit is contained in:
Philip Rader 2023-02-18 16:42:42 -07:00
parent 8f470597d6
commit 1c6231aad4
11 changed files with 237 additions and 61 deletions

View File

@ -159,10 +159,11 @@ endif()
if (GLFW_BUILD_COCOA)
target_link_libraries(glfw PRIVATE "-framework Cocoa"
"-framework IOKit"
"-framework CoreFoundation")
"-framework CoreFoundation"
"-framework CoreVideo")
set(glfw_PKG_DEPS "")
set(glfw_PKG_LIBS "-framework Cocoa -framework IOKit -framework CoreFoundation")
set(glfw_PKG_LIBS "-framework Cocoa -framework IOKit -framework CoreFoundation -framework CoreVideo")
endif()
if (GLFW_BUILD_WAYLAND)

View File

@ -37,8 +37,10 @@
#if defined(__OBJC__)
#import <Cocoa/Cocoa.h>
#import <CoreVideo/CoreVideo.h>
#else
typedef void* id;
typedef void* CVDisplayLinkRef;
#endif
// NOTE: Many Cocoa enum values have been renamed and we need to build across
@ -124,6 +126,11 @@ typedef struct _GLFWcontextNSGL
{
id pixelFormat;
id object;
int swapInterval;
int swapIntervalsPassed;
_GLFWmutex swapIntervalLock;
_GLFWcondvar swapIntervalCond;
CVDisplayLinkRef displayLink;
} _GLFWcontextNSGL;
// NSGL-specific global data
@ -299,4 +306,5 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window,
const _GLFWctxconfig* ctxconfig,
const _GLFWfbconfig* fbconfig);
void _glfwDestroyContextNSGL(_GLFWwindow* window);
void _glfwUpdateDisplayLinkNSGL(_GLFWwindow* window);

View File

@ -276,6 +276,12 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
_glfwInputWindowPos(window, x, y);
}
- (void)windowDidChangeScreen:(NSNotification *)notification
{
if(window->context.source == GLFW_NATIVE_CONTEXT_API)
_glfwUpdateDisplayLinkNSGL(window);
}
- (void)windowDidMiniaturize:(NSNotification *)notification
{
if (window->monitor)

View File

@ -77,6 +77,7 @@ typedef struct _GLFWmapping _GLFWmapping;
typedef struct _GLFWjoystick _GLFWjoystick;
typedef struct _GLFWtls _GLFWtls;
typedef struct _GLFWmutex _GLFWmutex;
typedef struct _GLFWcondvar _GLFWcondvar;
#define GL_VERSION 0x1f02
#define GL_NONE 0
@ -328,6 +329,32 @@ typedef PFN_vkVoidFunction (APIENTRY * PFN_vkGetInstanceProcAddr)(VkInstance,con
typedef VkResult (APIENTRY * PFN_vkEnumerateInstanceExtensionProperties)(const char*,uint32_t*,VkExtensionProperties*);
#define vkGetInstanceProcAddr _glfw.vk.GetInstanceProcAddr
#include "platform_thread.h"
// Thread local storage structure
//
struct _GLFWtls
{
// This is defined in platform_thread.h
GLFW_PLATFORM_TLS_STATE
};
// Mutex structure
//
struct _GLFWmutex
{
// This is defined in platform_thread.h
GLFW_PLATFORM_MUTEX_STATE
};
// Conditional variable structure
//
struct _GLFWcondvar
{
// This is defined in platform_thread.h
GLFW_PLATFORM_CONDVAR_STATE
};
#include "platform.h"
// Checks for whether the library has been initialized
@ -649,22 +676,6 @@ struct _GLFWjoystick
GLFW_PLATFORM_JOYSTICK_STATE
};
// Thread local storage structure
//
struct _GLFWtls
{
// This is defined in platform.h
GLFW_PLATFORM_TLS_STATE
};
// Mutex structure
//
struct _GLFWmutex
{
// This is defined in platform.h
GLFW_PLATFORM_MUTEX_STATE
};
// Platform API structure
//
struct _GLFWplatform
@ -894,6 +905,11 @@ void _glfwPlatformDestroyMutex(_GLFWmutex* mutex);
void _glfwPlatformLockMutex(_GLFWmutex* mutex);
void _glfwPlatformUnlockMutex(_GLFWmutex* mutex);
GLFWbool _glfwPlatformCreateCondVar(_GLFWcondvar* condvar);
void _glfwPlatformDestroyCondvar(_GLFWcondvar* condvar);
void _glfwPlatformCondWait(_GLFWcondvar* condvar, _GLFWmutex* mutex);
void _glfwPlatformCondSignal(_GLFWcondvar* condvar);
void* _glfwPlatformLoadModule(const char* path);
void _glfwPlatformFreeModule(void* module);
GLFWproc _glfwPlatformGetModuleSymbol(void* module, const char* name);

View File

@ -33,6 +33,22 @@
#include <unistd.h>
#include <math.h>
static CVReturn nsglDisplayLinkCallback(CVDisplayLinkRef displayLink,
const CVTimeStamp* now,
const CVTimeStamp* outputTime,
CVOptionFlags flagsIn,
CVOptionFlags* flagsOut,
void* userContext) {
_GLFWcontextNSGL* nsgl = (_GLFWcontextNSGL*)userContext;
_glfwPlatformLockMutex(&nsgl->swapIntervalLock);
nsgl->swapIntervalsPassed = nsgl->swapIntervalsPassed + 1;
_glfwPlatformCondSignal(&nsgl->swapIntervalCond);
_glfwPlatformUnlockMutex(&nsgl->swapIntervalLock);
return kCVReturnSuccess;
}
static void makeContextCurrentNSGL(_GLFWwindow* window)
{
@autoreleasepool {
@ -51,28 +67,18 @@ static void swapBuffersNSGL(_GLFWwindow* window)
{
@autoreleasepool {
// HACK: Simulate vsync with usleep as NSGL swap interval does not apply to
// windows with a non-visible occlusion state
if (window->ns.occluded)
{
int interval = 0;
[window->context.nsgl.object getValues:&interval
forParameter:NSOpenGLContextParameterSwapInterval];
if (interval > 0)
{
const double framerate = 60.0;
const uint64_t frequency = _glfwPlatformGetTimerFrequency();
const uint64_t value = _glfwPlatformGetTimerValue();
const double elapsed = value / (double) frequency;
const double period = 1.0 / framerate;
const double delay = period - fmod(elapsed, period);
usleep(floorl(delay * 1e6));
}
if(window->context.nsgl.swapInterval > 0) {
_glfwPlatformLockMutex(&window->context.nsgl.swapIntervalLock);
do {
// do-while guarantees at least one swap interval
// has occurred.
_glfwPlatformCondWait(&window->context.nsgl.swapIntervalCond,
&window->context.nsgl.swapIntervalLock);
} while(window->context.nsgl.swapIntervalsPassed
% window->context.nsgl.swapInterval != 0);
window->context.nsgl.swapIntervalsPassed = 0;
_glfwPlatformUnlockMutex(&window->context.nsgl.swapIntervalLock);
}
[window->context.nsgl.object flushBuffer];
} // autoreleasepool
@ -85,8 +91,7 @@ static void swapIntervalNSGL(int interval)
_GLFWwindow* window = _glfwPlatformGetTls(&_glfw.contextSlot);
if (window)
{
[window->context.nsgl.object setValues:&interval
forParameter:NSOpenGLContextParameterSwapInterval];
window->context.nsgl.swapInterval = interval;
}
} // autoreleasepool
@ -115,6 +120,10 @@ static GLFWglproc getProcAddressNSGL(const char* procname)
static void destroyContextNSGL(_GLFWwindow* window)
{
@autoreleasepool {
CVDisplayLinkStop(window->context.nsgl.displayLink);
_glfwPlatformDestroyCondvar(&window->context.nsgl.swapIntervalCond);
_glfwPlatformDestroyMutex(&window->context.nsgl.swapIntervalLock);
[window->context.nsgl.pixelFormat release];
window->context.nsgl.pixelFormat = nil;
@ -340,6 +349,24 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window,
[window->context.nsgl.object setView:window->ns.view];
window->context.nsgl.swapInterval = 0; // Default value of NSGL swap interval
window->context.nsgl.swapIntervalsPassed = 0;
_glfwPlatformCreateMutex(&window->context.nsgl.swapIntervalLock);
_glfwPlatformCreateCondVar(&window->context.nsgl.swapIntervalCond);
// Explicitly set NSGL swap interval to 0, since CVDisplayLink will be used
// instead.
int swapInterval = 0;
[window->context.nsgl.object setValues:&swapInterval
forParameter:NSOpenGLContextParameterSwapInterval];
CVDisplayLinkCreateWithActiveCGDisplays(&window->context.nsgl.displayLink);
CVDisplayLinkSetOutputCallback(window->context.nsgl.displayLink,
&nsglDisplayLinkCallback,
(void*)&window->context.nsgl);
_glfwUpdateDisplayLinkNSGL(window);
CVDisplayLinkStart(window->context.nsgl.displayLink);
window->context.makeCurrent = makeContextCurrentNSGL;
window->context.swapBuffers = swapBuffersNSGL;
window->context.swapInterval = swapIntervalNSGL;
@ -350,6 +377,12 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window,
return GLFW_TRUE;
}
void _glfwUpdateDisplayLinkNSGL(_GLFWwindow* window) {
CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(window->context.nsgl.displayLink,
[window->context.nsgl.object CGLContextObj],
[window->context.nsgl.pixelFormat CGLPixelFormatObj]);
}
//////////////////////////////////////////////////////////////////////////
////// GLFW native API //////

View File

@ -27,11 +27,9 @@
#if defined(GLFW_BUILD_WIN32_TIMER) || \
defined(GLFW_BUILD_WIN32_MODULE) || \
defined(GLFW_BUILD_WIN32_THREAD) || \
defined(GLFW_BUILD_COCOA_TIMER) || \
defined(GLFW_BUILD_POSIX_TIMER) || \
defined(GLFW_BUILD_POSIX_MODULE) || \
defined(GLFW_BUILD_POSIX_THREAD) || \
defined(GLFW_BUILD_POSIX_POLL) || \
defined(GLFW_BUILD_LINUX_JOYSTICK)
#error "You must not define these; define zero or more _GLFW_<platform> macros instead"
@ -156,22 +154,6 @@
GLFW_NSGL_LIBRARY_CONTEXT_STATE \
GLFW_GLX_LIBRARY_CONTEXT_STATE
#if defined(_WIN32)
#define GLFW_BUILD_WIN32_THREAD
#else
#define GLFW_BUILD_POSIX_THREAD
#endif
#if defined(GLFW_BUILD_WIN32_THREAD)
#include "win32_thread.h"
#define GLFW_PLATFORM_TLS_STATE GLFW_WIN32_TLS_STATE
#define GLFW_PLATFORM_MUTEX_STATE GLFW_WIN32_MUTEX_STATE
#elif defined(GLFW_BUILD_POSIX_THREAD)
#include "posix_thread.h"
#define GLFW_PLATFORM_TLS_STATE GLFW_POSIX_TLS_STATE
#define GLFW_PLATFORM_MUTEX_STATE GLFW_POSIX_MUTEX_STATE
#endif
#if defined(_WIN32)
#define GLFW_BUILD_WIN32_TIMER
#elif defined(__APPLE__)

48
src/platform_thread.h Normal file
View File

@ -0,0 +1,48 @@
//========================================================================
// GLFW 3.4 - www.glfw.org
//------------------------------------------------------------------------
// Copyright (c) 2023 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.
//
//========================================================================
#if defined(GLFW_BUILD_WIN32_THREAD) || \
defined(GLFW_BUILD_POSIX_THREAD)
#error "You must not define these; define zero or more _GLFW_<platform> macros instead"
#endif
#if defined(_WIN32)
#define GLFW_BUILD_WIN32_THREAD
#else
#define GLFW_BUILD_POSIX_THREAD
#endif
#if defined(GLFW_BUILD_WIN32_THREAD)
#include "win32_thread.h"
#define GLFW_PLATFORM_TLS_STATE GLFW_WIN32_TLS_STATE
#define GLFW_PLATFORM_MUTEX_STATE GLFW_WIN32_MUTEX_STATE
#define GLFW_PLATFORM_CONDVAR_STATE GLFW_WIN32_CONDVAR_STATE
#elif defined(GLFW_BUILD_POSIX_THREAD)
#include "posix_thread.h"
#define GLFW_PLATFORM_TLS_STATE GLFW_POSIX_TLS_STATE
#define GLFW_PLATFORM_MUTEX_STATE GLFW_POSIX_MUTEX_STATE
#define GLFW_PLATFORM_CONDVAR_STATE GLFW_POSIX_CONDVAR_STATE
#endif

View File

@ -105,5 +105,41 @@ void _glfwPlatformUnlockMutex(_GLFWmutex* mutex)
pthread_mutex_unlock(&mutex->posix.handle);
}
GLFWbool _glfwPlatformCreateCondVar(_GLFWcondvar* condvar)
{
assert(condvar->posix.allocated == GLFW_FALSE);
if(pthread_cond_init(&condvar->posix.handle, NULL) != 0)
{
_glfwInputError(GLFW_PLATFORM_ERROR, "POSIX: Failed to create conditional variable");
return GLFW_FALSE;
}
return condvar->posix.allocated = GLFW_TRUE;
}
void _glfwPlatformDestroyCondvar(_GLFWcondvar* condvar)
{
if (condvar->posix.allocated)
pthread_cond_destroy(&condvar->posix.handle);
memset(condvar, 0, sizeof(_GLFWcondvar));
}
void _glfwPlatformCondWait(_GLFWcondvar* condvar, _GLFWmutex* mutex)
{
assert(condvar->posix.allocated == GLFW_TRUE);
assert(mutex->posix.allocated == GLFW_TRUE);
pthread_cond_wait(&condvar->posix.handle,
&mutex->posix.handle);
}
void _glfwPlatformCondSignal(_GLFWcondvar* condvar)
{
assert(condvar->posix.allocated == GLFW_TRUE);
pthread_cond_signal(&condvar->posix.handle);
}
#endif // GLFW_BUILD_POSIX_THREAD

View File

@ -27,8 +27,9 @@
#include <pthread.h>
#define GLFW_POSIX_TLS_STATE _GLFWtlsPOSIX posix;
#define GLFW_POSIX_MUTEX_STATE _GLFWmutexPOSIX posix;
#define GLFW_POSIX_TLS_STATE _GLFWtlsPOSIX posix;
#define GLFW_POSIX_MUTEX_STATE _GLFWmutexPOSIX posix;
#define GLFW_POSIX_CONDVAR_STATE _GLFWcondvarPOSIX posix;
// POSIX-specific thread local storage data
@ -47,3 +48,9 @@ typedef struct _GLFWmutexPOSIX
pthread_mutex_t handle;
} _GLFWmutexPOSIX;
// POSIX-specific conditional variable data
typedef struct _GLFWcondvarPOSIX
{
GLFWbool allocated;
pthread_cond_t handle;
} _GLFWcondvarPOSIX;

View File

@ -98,5 +98,36 @@ void _glfwPlatformUnlockMutex(_GLFWmutex* mutex)
LeaveCriticalSection(&mutex->win32.section);
}
GLFWbool _glfwPlatformCreateCondVar(_GLFWcondvar* condvar)
{
assert(condvar->win32.allocated == GLFW_FALSE);
InitializeConditionVariable(condvar->win32.condvar);
return condvar->win32.allocated = GLFW_TRUE;
}
void _glfwPlatformDestroyCondvar(_GLFWcondvar* condvar)
{
if (condvar->win32.allocated)
DeleteConditionVariable(condvar->win32.condvar);
memset(condvar, 0, sizeof(_GLFWcondvar));
}
void _glfwPlatformCondWait(_GLFWcondvar* condvar, _GLFWmutex* mutex)
{
assert(condvar->win32.allocated == GLFW_TRUE);
assert(mutex->win32.allocated == GLFW_TRUE);
SleepConditionVariableCS(&condvar->win32.condvar,
&mutex->win32.section,
INFINITE);
}
void _glfwPlatformCondSignal(_GLFWcondvar* condvar)
{
assert(condvar->win32.allocated == GLFW_TRUE);
WakeConditionVariable(&condvar->win32.condvar);
}
#endif // GLFW_BUILD_WIN32_THREAD

View File

@ -29,6 +29,7 @@
#define GLFW_WIN32_TLS_STATE _GLFWtlsWin32 win32;
#define GLFW_WIN32_MUTEX_STATE _GLFWmutexWin32 win32;
#define GLFW_WIN32_CONDVAR_STATE _GLFWcondvarWin32 win32;
// Win32-specific thread local storage data
//
@ -46,3 +47,10 @@ typedef struct _GLFWmutexWin32
CRITICAL_SECTION section;
} _GLFWmutexWin32;
// Win32-specific conditional variable data
//
typedef struct _GLFWcondvarWin32
{
GLFWbool allocated;
CONDITION_VARIABLE condvar;
} _GLFWcondvarWin32;