Revert "cocoa: Backed out CVDisplayLink code for macOS vsync."

This reverts commit 04b50f6c6b.

It turns out OpenGL vsync has broken again in macOS 12, so
we're reintroducing our CVDisplayLink code to deal with it,
again.

Reference Issue #4918.
This commit is contained in:
Ryan C. Gordon 2022-11-16 10:15:21 -05:00
parent 9d67686a5b
commit 1fd66cc890
2 changed files with 79 additions and 24 deletions

View file

@ -42,6 +42,11 @@ struct SDL_GLDriverData
@interface SDLOpenGLContext : NSOpenGLContext { @interface SDLOpenGLContext : NSOpenGLContext {
SDL_atomic_t dirty; SDL_atomic_t dirty;
SDL_Window *window; SDL_Window *window;
CVDisplayLinkRef displayLink;
@public SDL_mutex *swapIntervalMutex;
@public SDL_cond *swapIntervalCond;
@public SDL_atomic_t swapIntervalSetting;
@public SDL_atomic_t swapIntervalsPassed;
} }
- (id)initWithFormat:(NSOpenGLPixelFormat *)format - (id)initWithFormat:(NSOpenGLPixelFormat *)format
@ -51,7 +56,7 @@ struct SDL_GLDriverData
- (void)setWindow:(SDL_Window *)window; - (void)setWindow:(SDL_Window *)window;
- (SDL_Window*)window; - (SDL_Window*)window;
- (void)explicitUpdate; - (void)explicitUpdate;
- (void)dealloc;
@end @end
/* OpenGL functions */ /* OpenGL functions */

View file

@ -52,6 +52,23 @@ SDL_OpenGLAsyncDispatchChanged(void *userdata, const char *name, const char *old
SDL_opengl_async_dispatch = SDL_GetStringBoolean(hint, SDL_FALSE); SDL_opengl_async_dispatch = SDL_GetStringBoolean(hint, SDL_FALSE);
} }
static CVReturn
DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* now, const CVTimeStamp* outputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext)
{
SDLOpenGLContext *nscontext = (__bridge SDLOpenGLContext *) displayLinkContext;
/*printf("DISPLAY LINK! %u\n", (unsigned int) SDL_GetTicks()); */
const int setting = SDL_AtomicGet(&nscontext->swapIntervalSetting);
if (setting != 0) { /* nothing to do if vsync is disabled, don't even lock */
SDL_LockMutex(nscontext->swapIntervalMutex);
SDL_AtomicAdd(&nscontext->swapIntervalsPassed, 1);
SDL_CondSignal(nscontext->swapIntervalCond);
SDL_UnlockMutex(nscontext->swapIntervalMutex);
}
return kCVReturnSuccess;
}
@implementation SDLOpenGLContext : NSOpenGLContext @implementation SDLOpenGLContext : NSOpenGLContext
- (id)initWithFormat:(NSOpenGLPixelFormat *)format - (id)initWithFormat:(NSOpenGLPixelFormat *)format
@ -61,6 +78,19 @@ SDL_OpenGLAsyncDispatchChanged(void *userdata, const char *name, const char *old
if (self) { if (self) {
SDL_AtomicSet(&self->dirty, 0); SDL_AtomicSet(&self->dirty, 0);
self->window = NULL; self->window = NULL;
SDL_AtomicSet(&self->swapIntervalSetting, 0);
SDL_AtomicSet(&self->swapIntervalsPassed, 0);
self->swapIntervalCond = SDL_CreateCond();
self->swapIntervalMutex = SDL_CreateMutex();
if (!self->swapIntervalCond || !self->swapIntervalMutex) {
return nil;
}
/* !!! FIXME: check return values. */
CVDisplayLinkCreateWithActiveCGDisplays(&self->displayLink);
CVDisplayLinkSetOutputCallback(self->displayLink, &DisplayLinkCallback, (__bridge void * _Nullable) self);
CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(self->displayLink, [self CGLContextObj], [format CGLPixelFormatObj]);
CVDisplayLinkStart(displayLink);
} }
SDL_AddHintCallback(SDL_HINT_MAC_OPENGL_ASYNC_DISPATCH, SDL_OpenGLAsyncDispatchChanged, NULL); SDL_AddHintCallback(SDL_HINT_MAC_OPENGL_ASYNC_DISPATCH, SDL_OpenGLAsyncDispatchChanged, NULL);
@ -158,6 +188,15 @@ SDL_OpenGLAsyncDispatchChanged(void *userdata, const char *name, const char *old
- (void)dealloc - (void)dealloc
{ {
SDL_DelHintCallback(SDL_HINT_MAC_OPENGL_ASYNC_DISPATCH, SDL_OpenGLAsyncDispatchChanged, NULL); SDL_DelHintCallback(SDL_HINT_MAC_OPENGL_ASYNC_DISPATCH, SDL_OpenGLAsyncDispatchChanged, NULL);
if (self->displayLink) {
CVDisplayLinkRelease(self->displayLink);
}
if (self->swapIntervalCond) {
SDL_DestroyCond(self->swapIntervalCond);
}
if (self->swapIntervalMutex) {
SDL_DestroyMutex(self->swapIntervalMutex);
}
} }
@end @end
@ -211,6 +250,7 @@ Cocoa_GL_CreateContext(_THIS, SDL_Window * window)
int glversion_major; int glversion_major;
int glversion_minor; int glversion_minor;
NSOpenGLPixelFormatAttribute profile; NSOpenGLPixelFormatAttribute profile;
int interval;
if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) { if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) {
#if SDL_VIDEO_OPENGL_EGL #if SDL_VIDEO_OPENGL_EGL
@ -318,6 +358,10 @@ Cocoa_GL_CreateContext(_THIS, SDL_Window * window)
sdlcontext = (SDL_GLContext)CFBridgingRetain(context); sdlcontext = (SDL_GLContext)CFBridgingRetain(context);
/* vsync is handled separately by synchronizing with a display link. */
interval = 0;
[context setValues:&interval forParameter:NSOpenGLCPSwapInterval];
if ( Cocoa_GL_MakeCurrent(_this, window, (__bridge SDL_GLContext)context) < 0 ) { if ( Cocoa_GL_MakeCurrent(_this, window, (__bridge SDL_GLContext)context) < 0 ) {
Cocoa_GL_DeleteContext(_this, (__bridge SDL_GLContext)context); Cocoa_GL_DeleteContext(_this, (__bridge SDL_GLContext)context);
SDL_SetError("Failed making OpenGL context current"); SDL_SetError("Failed making OpenGL context current");
@ -389,21 +433,17 @@ int
Cocoa_GL_SetSwapInterval(_THIS, int interval) Cocoa_GL_SetSwapInterval(_THIS, int interval)
{ @autoreleasepool { @autoreleasepool
{ {
NSOpenGLContext *nscontext; SDLOpenGLContext *nscontext = (__bridge SDLOpenGLContext *) SDL_GL_GetCurrentContext();
GLint value;
int status; int status;
if (interval < 0) { /* no extension for this on Mac OS X at the moment. */ if (nscontext == nil) {
return SDL_SetError("Late swap tearing currently unsupported");
}
nscontext = (__bridge NSOpenGLContext*)SDL_GL_GetCurrentContext();
if (nscontext != nil) {
value = interval;
[nscontext setValues:&value forParameter:NSOpenGLCPSwapInterval];
status = 0;
} else {
status = SDL_SetError("No current OpenGL context"); status = SDL_SetError("No current OpenGL context");
} else {
SDL_LockMutex(nscontext->swapIntervalMutex);
SDL_AtomicSet(&nscontext->swapIntervalsPassed, 0);
SDL_AtomicSet(&nscontext->swapIntervalSetting, interval);
SDL_UnlockMutex(nscontext->swapIntervalMutex);
status = 0;
} }
return status; return status;
@ -413,17 +453,8 @@ int
Cocoa_GL_GetSwapInterval(_THIS) Cocoa_GL_GetSwapInterval(_THIS)
{ @autoreleasepool { @autoreleasepool
{ {
NSOpenGLContext *nscontext; SDLOpenGLContext* nscontext = (__bridge SDLOpenGLContext*)SDL_GL_GetCurrentContext();
GLint value; return nscontext ? SDL_AtomicGet(&nscontext->swapIntervalSetting) : 0;
int status = 0;
nscontext = (__bridge NSOpenGLContext*)SDL_GL_GetCurrentContext();
if (nscontext != nil) {
[nscontext getValues:&value forParameter:NSOpenGLCPSwapInterval];
status = (int)value;
}
return status;
}} }}
int int
@ -432,6 +463,25 @@ Cocoa_GL_SwapWindow(_THIS, SDL_Window * window)
{ {
SDLOpenGLContext* nscontext = (__bridge SDLOpenGLContext*)SDL_GL_GetCurrentContext(); SDLOpenGLContext* nscontext = (__bridge SDLOpenGLContext*)SDL_GL_GetCurrentContext();
SDL_VideoData *videodata = (__bridge SDL_VideoData *) _this->driverdata; SDL_VideoData *videodata = (__bridge SDL_VideoData *) _this->driverdata;
const int setting = SDL_AtomicGet(&nscontext->swapIntervalSetting);
if (setting == 0) {
/* nothing to do if vsync is disabled, don't even lock */
} else if (setting < 0) { /* late swap tearing */
SDL_LockMutex(nscontext->swapIntervalMutex);
while (SDL_AtomicGet(&nscontext->swapIntervalsPassed) == 0) {
SDL_CondWait(nscontext->swapIntervalCond, nscontext->swapIntervalMutex);
}
SDL_AtomicSet(&nscontext->swapIntervalsPassed, 0);
SDL_UnlockMutex(nscontext->swapIntervalMutex);
} else {
SDL_LockMutex(nscontext->swapIntervalMutex);
do { /* always wait here so we know we just hit a swap interval. */
SDL_CondWait(nscontext->swapIntervalCond, nscontext->swapIntervalMutex);
} while ((SDL_AtomicGet(&nscontext->swapIntervalsPassed) % setting) != 0);
SDL_AtomicSet(&nscontext->swapIntervalsPassed, 0);
SDL_UnlockMutex(nscontext->swapIntervalMutex);
}
/* on 10.14 ("Mojave") and later, this deadlocks if two contexts in two /* on 10.14 ("Mojave") and later, this deadlocks if two contexts in two
threads try to swap at the same time, so put a mutex around it. */ threads try to swap at the same time, so put a mutex around it. */