Using DisplayLink to emulate vsync on OSX

This commit is contained in:
Bastiaan Olij 2019-01-27 22:39:32 +11:00
parent f32c826924
commit b53f2d1d59
3 changed files with 60 additions and 15 deletions

View file

@ -126,7 +126,7 @@ def configure(env):
env.Append(CPPPATH=['#platform/osx'])
env.Append(CPPFLAGS=['-DOSX_ENABLED', '-DUNIX_ENABLED', '-DGLES_ENABLED', '-DAPPLE_STYLE_KEYS', '-DCOREAUDIO_ENABLED', '-DCOREMIDI_ENABLED'])
env.Append(LINKFLAGS=['-framework', 'Cocoa', '-framework', 'Carbon', '-framework', 'OpenGL', '-framework', 'AGL', '-framework', 'AudioUnit', '-framework', 'CoreAudio', '-framework', 'CoreMIDI', '-lz', '-framework', 'IOKit', '-framework', 'ForceFeedback'])
env.Append(LINKFLAGS=['-framework', 'Cocoa', '-framework', 'Carbon', '-framework', 'OpenGL', '-framework', 'AGL', '-framework', 'AudioUnit', '-framework', 'CoreAudio', '-framework', 'CoreMIDI', '-lz', '-framework', 'IOKit', '-framework', 'ForceFeedback', '-framework', 'CoreVideo'])
env.Append(LIBS=['pthread'])
env.Append(CPPFLAGS=['-mmacosx-version-min=10.9'])

View file

@ -43,8 +43,10 @@
#include "servers/visual/rasterizer.h"
#include "servers/visual/visual_server_wrap_mt.h"
#include "servers/visual_server.h"
#include <AppKit/AppKit.h>
#include <AppKit/NSCursor.h>
#include <ApplicationServices/ApplicationServices.h>
#include <CoreVideo/CoreVideo.h>
#undef CursorShape
/**
@ -102,10 +104,13 @@ public:
id window_view;
id autoreleasePool;
id cursor;
id pixelFormat;
id context;
NSOpenGLPixelFormat *pixelFormat;
NSOpenGLContext *context;
bool layered_window;
bool waiting_for_vsync;
NSCondition *vsync_condition;
CVDisplayLinkRef displayLink;
CursorShape cursor_shape;
NSCursor *cursors[CURSOR_MAX];

View file

@ -117,6 +117,21 @@ static Vector2 get_mouse_pos(NSPoint locationInWindow, CGFloat backingScaleFacto
return Vector2(mouse_x, mouse_y);
}
// DisplayLinkCallback is called from our DisplayLink OS thread informing us right before
// a screen update is required. We can use it to work around the broken vsync.
static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp *now, const CVTimeStamp *outputTime, CVOptionFlags flagsIn, CVOptionFlags *flagsOut, void *displayLinkContext) {
OS_OSX *os = (OS_OSX *)displayLinkContext;
// Set flag so we know we can output our next frame and signal our conditional lock
// if we're not doing vsync this will be ignored
[os->vsync_condition lock];
os->waiting_for_vsync = false;
[os->vsync_condition signal];
[os->vsync_condition unlock];
return kCVReturnSuccess;
}
@interface GodotApplication : NSApplication
@end
@ -1366,6 +1381,15 @@ Error OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_a
[context makeCurrentContext];
// setup our display link, this will inform us when a refresh is needed
CVDisplayLinkCreateWithActiveCGDisplays(&displayLink);
CVDisplayLinkSetOutputCallback(displayLink, &DisplayLinkCallback, this);
CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(displayLink, context.CGLContextObj, pixelFormat.CGLPixelFormatObj);
CVDisplayLinkStart(displayLink);
// initialise a conditional lock object
vsync_condition = [[NSCondition alloc] init];
set_use_vsync(p_desired.use_vsync);
[NSApp activateIgnoringOtherApps:YES];
@ -1453,6 +1477,11 @@ void OS_OSX::finalize() {
midi_driver.close();
#endif
if (displayLink) {
CVDisplayLinkRelease(displayLink);
}
[vsync_condition release];
CFNotificationCenterRemoveObserver(CFNotificationCenterGetDistributedCenter(), NULL, kTISNotifySelectedKeyboardInputSourceChanged, NULL);
CGDisplayRemoveReconfigurationCallback(displays_arrangement_changed, NULL);
@ -1967,6 +1996,17 @@ String OS_OSX::get_locale() const {
}
void OS_OSX::swap_buffers() {
if (is_vsync_enabled()) {
// Wait until our DisplayLink callback unsets our flag...
[vsync_condition lock];
while (waiting_for_vsync)
[vsync_condition wait];
// Make sure we wait again next frame around
waiting_for_vsync = true;
[vsync_condition unlock];
}
[context flushBuffer];
}
@ -2633,22 +2673,22 @@ Error OS_OSX::move_to_trash(const String &p_path) {
}
void OS_OSX::_set_use_vsync(bool p_enable) {
CGLContextObj ctx = CGLGetCurrentContext();
// CGLCPSwapInterval broke in OSX 10.14 and it seems Apple is not interested in fixing
// it as OpenGL is now deprecated and Metal solves this differently.
// Following SDLs example we're working around this using DisplayLink
// When vsync is enabled we set a flag "waiting_for_vsync" to true.
// This flag is set to false when DisplayLink informs us our display is about to refresh.
/* CGLContextObj ctx = CGLGetCurrentContext();
if (ctx) {
GLint swapInterval = p_enable ? 1 : 0;
CGLSetParameter(ctx, kCGLCPSwapInterval, &swapInterval);
}
}*/
///TODO Maybe pause/unpause display link?
waiting_for_vsync = p_enable;
}
/*
bool OS_OSX::is_vsync_enabled() const {
GLint swapInterval = 0;
CGLContextObj ctx = CGLGetCurrentContext();
if (ctx) {
CGLGetParameter(ctx, kCGLCPSwapInterval, &swapInterval);
}
return swapInterval ? true : false;
}
*/
OS_OSX *OS_OSX::singleton = NULL;
OS_OSX::OS_OSX() {