Revert "cocoa: Backed out CVDisplayLink code for macOS vsync." This reverts commit 04b50f6c6bef67b744b192c78775771b51ff2141. 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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
diff --git a/src/video/cocoa/SDL_cocoaopengl.h b/src/video/cocoa/SDL_cocoaopengl.h
index 0f9b1c7..a483ac0 100644
--- a/src/video/cocoa/SDL_cocoaopengl.h
+++ b/src/video/cocoa/SDL_cocoaopengl.h
@@ -42,6 +42,11 @@ struct SDL_GLDriverData
@interface SDLOpenGLContext : NSOpenGLContext {
SDL_atomic_t dirty;
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
@@ -51,7 +56,7 @@ struct SDL_GLDriverData
- (void)setWindow:(SDL_Window *)window;
- (SDL_Window*)window;
- (void)explicitUpdate;
-
+- (void)dealloc;
@end
/* OpenGL functions */
diff --git a/src/video/cocoa/SDL_cocoaopengl.m b/src/video/cocoa/SDL_cocoaopengl.m
index 8aab0c4..af17ae2 100644
--- a/src/video/cocoa/SDL_cocoaopengl.m
+++ b/src/video/cocoa/SDL_cocoaopengl.m
@@ -52,6 +52,23 @@ SDL_OpenGLAsyncDispatchChanged(void *userdata, const char *name, const char *old
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
- (id)initWithFormat:(NSOpenGLPixelFormat *)format
@@ -61,6 +78,19 @@ SDL_OpenGLAsyncDispatchChanged(void *userdata, const char *name, const char *old
if (self) {
SDL_AtomicSet(&self->dirty, 0);
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);
@@ -158,6 +188,15 @@ SDL_OpenGLAsyncDispatchChanged(void *userdata, const char *name, const char *old
- (void)dealloc
{
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
@@ -211,6 +250,7 @@ Cocoa_GL_CreateContext(_THIS, SDL_Window * window)
int glversion_major;
int glversion_minor;
NSOpenGLPixelFormatAttribute profile;
+ int interval;
if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) {
#if SDL_VIDEO_OPENGL_EGL
@@ -318,6 +358,10 @@ Cocoa_GL_CreateContext(_THIS, SDL_Window * window)
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 ) {
Cocoa_GL_DeleteContext(_this, (__bridge SDL_GLContext)context);
SDL_SetError("Failed making OpenGL context current");
@@ -389,21 +433,17 @@ int
Cocoa_GL_SetSwapInterval(_THIS, int interval)
{ @autoreleasepool
{
- NSOpenGLContext *nscontext;
- GLint value;
+ SDLOpenGLContext *nscontext = (__bridge SDLOpenGLContext *) SDL_GL_GetCurrentContext();
int status;
- if (interval < 0) { /* no extension for this on Mac OS X at the moment. */
- 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 {
+ if (nscontext == nil) {
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;
@@ -413,17 +453,8 @@ int
Cocoa_GL_GetSwapInterval(_THIS)
{ @autoreleasepool
{
- NSOpenGLContext *nscontext;
- GLint value;
- int status = 0;
-
- nscontext = (__bridge NSOpenGLContext*)SDL_GL_GetCurrentContext();
- if (nscontext != nil) {
- [nscontext getValues:&value forParameter:NSOpenGLCPSwapInterval];
- status = (int)value;
- }
-
- return status;
+ SDLOpenGLContext* nscontext = (__bridge SDLOpenGLContext*)SDL_GL_GetCurrentContext();
+ return nscontext ? SDL_AtomicGet(&nscontext->swapIntervalSetting) : 0;
}}
int
@@ -432,6 +463,25 @@ Cocoa_GL_SwapWindow(_THIS, SDL_Window * window)
{
SDLOpenGLContext* nscontext = (__bridge SDLOpenGLContext*)SDL_GL_GetCurrentContext();
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
threads try to swap at the same time, so put a mutex around it. */