macOS: Fix rendering getting stuck on some machines after sleep/screensaver

This is caused, as far as I can tell, by CVDisplayLink getting stuck.
Apple apparently are incapable of writing a simple timer robustly.
So if it remains stuck after a second delete and recreate it to force it
to restart.

Fixes #2016
This commit is contained in:
Kovid Goyal 2021-05-12 07:29:51 +05:30
parent cc2afef390
commit e4b4a35375
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
4 changed files with 34 additions and 4 deletions

View File

@ -37,6 +37,9 @@ To update |kitty|, :doc:`follow the instructions <binary>`.
- macOS: When the Apple Color Emoji font lacks an emoji glyph search for it in other - macOS: When the Apple Color Emoji font lacks an emoji glyph search for it in other
installed fonts (:iss:`3591`) installed fonts (:iss:`3591`)
- macOS: Fix rendering getting stuck on some machines after sleep/screensaver
(:iss:`2016`)
- Add a few more special commandline arguments for the launch command. Now all - Add a few more special commandline arguments for the launch command. Now all
``KITTY_PIPE_DATA`` is also available via command line argument substitution ``KITTY_PIPE_DATA`` is also available via command line argument substitution
(:iss:`3593`) (:iss:`3593`)

View File

@ -316,6 +316,7 @@ void _glfwClearDisplayLinks() {
CVDisplayLinkRelease(_glfw.ns.displayLinks.entries[i].displayLink); CVDisplayLinkRelease(_glfw.ns.displayLinks.entries[i].displayLink);
_glfw.ns.displayLinks.entries[i].displayLink = nil; _glfw.ns.displayLinks.entries[i].displayLink = nil;
_glfw.ns.displayLinks.entries[i].lastRenderFrameRequestedAt = 0; _glfw.ns.displayLinks.entries[i].lastRenderFrameRequestedAt = 0;
_glfw.ns.displayLinks.entries[i].first_unserviced_render_frame_request_at = 0;
} }
} }
_glfw.ns.displayLinks.count = 0; _glfw.ns.displayLinks.count = 0;
@ -333,7 +334,14 @@ static CVReturn displayLinkCallback(
return kCVReturnSuccess; return kCVReturnSuccess;
} }
static inline void createDisplayLink(CGDirectDisplayID displayID) { void
_glfw_create_cv_display_link(_GLFWDisplayLinkNS *entry) {
CVDisplayLinkCreateWithCGDisplay(entry->displayID, &entry->displayLink);
CVDisplayLinkSetOutputCallback(entry->displayLink, &displayLinkCallback, (void*)(uintptr_t)entry->displayID);
}
static void
createDisplayLink(CGDirectDisplayID displayID) {
if (_glfw.ns.displayLinks.count >= sizeof(_glfw.ns.displayLinks.entries)/sizeof(_glfw.ns.displayLinks.entries[0]) - 1) return; if (_glfw.ns.displayLinks.count >= sizeof(_glfw.ns.displayLinks.entries)/sizeof(_glfw.ns.displayLinks.entries[0]) - 1) return;
for (size_t i = 0; i < _glfw.ns.displayLinks.count; i++) { for (size_t i = 0; i < _glfw.ns.displayLinks.count; i++) {
if (_glfw.ns.displayLinks.entries[i].displayID == displayID) return; if (_glfw.ns.displayLinks.entries[i].displayID == displayID) return;
@ -341,8 +349,7 @@ static inline void createDisplayLink(CGDirectDisplayID displayID) {
_GLFWDisplayLinkNS *entry = &_glfw.ns.displayLinks.entries[_glfw.ns.displayLinks.count++]; _GLFWDisplayLinkNS *entry = &_glfw.ns.displayLinks.entries[_glfw.ns.displayLinks.count++];
memset(entry, 0, sizeof(_GLFWDisplayLinkNS)); memset(entry, 0, sizeof(_GLFWDisplayLinkNS));
entry->displayID = displayID; entry->displayID = displayID;
CVDisplayLinkCreateWithCGDisplay(displayID, &entry->displayLink); _glfw_create_cv_display_link(entry);
CVDisplayLinkSetOutputCallback(entry->displayLink, &displayLinkCallback, (void*)(uintptr_t)displayID);
} }
// Poll for changes in the set of connected monitors // Poll for changes in the set of connected monitors

View File

@ -158,7 +158,7 @@ typedef struct _GLFWDisplayLinkNS
{ {
CVDisplayLinkRef displayLink; CVDisplayLinkRef displayLink;
CGDirectDisplayID displayID; CGDirectDisplayID displayID;
monotonic_t lastRenderFrameRequestedAt; monotonic_t lastRenderFrameRequestedAt, first_unserviced_render_frame_request_at;
} _GLFWDisplayLinkNS; } _GLFWDisplayLinkNS;
// Cocoa-specific global data // Cocoa-specific global data
@ -246,3 +246,4 @@ void _glfwDispatchTickCallback(void);
void _glfwDispatchRenderFrame(CGDirectDisplayID); void _glfwDispatchRenderFrame(CGDirectDisplayID);
void _glfwShutdownCVDisplayLink(unsigned long long, void*); void _glfwShutdownCVDisplayLink(unsigned long long, void*);
void _glfwCocoaPostEmptyEvent(void); void _glfwCocoaPostEmptyEvent(void);
void _glfw_create_cv_display_link(_GLFWDisplayLinkNS *entry);

View File

@ -317,6 +317,7 @@ _glfwShutdownCVDisplayLink(unsigned long long timer_id UNUSED, void *user_data U
_GLFWDisplayLinkNS *dl = &_glfw.ns.displayLinks.entries[i]; _GLFWDisplayLinkNS *dl = &_glfw.ns.displayLinks.entries[i];
if (dl->displayLink) CVDisplayLinkStop(dl->displayLink); if (dl->displayLink) CVDisplayLinkStop(dl->displayLink);
dl->lastRenderFrameRequestedAt = 0; dl->lastRenderFrameRequestedAt = 0;
dl->first_unserviced_render_frame_request_at = 0;
} }
} }
@ -340,10 +341,22 @@ requestRenderFrame(_GLFWwindow *w, GLFWcocoarenderframefun callback) {
_GLFWDisplayLinkNS *dl = &_glfw.ns.displayLinks.entries[i]; _GLFWDisplayLinkNS *dl = &_glfw.ns.displayLinks.entries[i];
if (dl->displayID == displayID) { if (dl->displayID == displayID) {
dl->lastRenderFrameRequestedAt = now; dl->lastRenderFrameRequestedAt = now;
if (!dl->first_unserviced_render_frame_request_at) dl->first_unserviced_render_frame_request_at = now;
if (!CVDisplayLinkIsRunning(dl->displayLink)) CVDisplayLinkStart(dl->displayLink); if (!CVDisplayLinkIsRunning(dl->displayLink)) CVDisplayLinkStart(dl->displayLink);
else if (now - dl->first_unserviced_render_frame_request_at > s_to_monotonic_t(1ll)) {
// display link is stuck need to recreate it because Apple cant even
// get a simple timer right
CVDisplayLinkRelease(dl->displayLink); dl->displayLink = nil;
dl->first_unserviced_render_frame_request_at = now;
_glfw_create_cv_display_link(dl);
_glfwInputError(GLFW_PLATFORM_ERROR,
"CVDisplayLink stuck possibly because of sleep/screensaver + Apple's incompetence, recreating.");
if (!CVDisplayLinkIsRunning(dl->displayLink)) CVDisplayLinkStart(dl->displayLink);
}
} else if (dl->displayLink && dl->lastRenderFrameRequestedAt && now - dl->lastRenderFrameRequestedAt >= DISPLAY_LINK_SHUTDOWN_CHECK_INTERVAL) { } else if (dl->displayLink && dl->lastRenderFrameRequestedAt && now - dl->lastRenderFrameRequestedAt >= DISPLAY_LINK_SHUTDOWN_CHECK_INTERVAL) {
CVDisplayLinkStop(dl->displayLink); CVDisplayLinkStop(dl->displayLink);
dl->lastRenderFrameRequestedAt = 0; dl->lastRenderFrameRequestedAt = 0;
dl->first_unserviced_render_frame_request_at = 0;
} }
} }
} }
@ -2141,6 +2154,12 @@ _glfwDispatchRenderFrame(CGDirectDisplayID displayID) {
} }
w = w->next; w = w->next;
} }
for (size_t i = 0; i < _glfw.ns.displayLinks.count; i++) {
_GLFWDisplayLinkNS *dl = &_glfw.ns.displayLinks.entries[i];
if (dl->displayID == displayID) {
dl->first_unserviced_render_frame_request_at = 0;
}
}
} }
void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos)