From e4b4a353757daf8cd297330be3acbf4b4d6035ce Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 12 May 2021 07:29:51 +0530 Subject: [PATCH] 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 --- docs/changelog.rst | 3 +++ glfw/cocoa_monitor.m | 13 ++++++++++--- glfw/cocoa_platform.h | 3 ++- glfw/cocoa_window.m | 19 +++++++++++++++++++ 4 files changed, 34 insertions(+), 4 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 625bb04e1..984bcffb3 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -37,6 +37,9 @@ To update |kitty|, :doc:`follow the instructions `. - macOS: When the Apple Color Emoji font lacks an emoji glyph search for it in other 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 ``KITTY_PIPE_DATA`` is also available via command line argument substitution (:iss:`3593`) diff --git a/glfw/cocoa_monitor.m b/glfw/cocoa_monitor.m index 76bb92980..a9dd86117 100644 --- a/glfw/cocoa_monitor.m +++ b/glfw/cocoa_monitor.m @@ -316,6 +316,7 @@ void _glfwClearDisplayLinks() { CVDisplayLinkRelease(_glfw.ns.displayLinks.entries[i].displayLink); _glfw.ns.displayLinks.entries[i].displayLink = nil; _glfw.ns.displayLinks.entries[i].lastRenderFrameRequestedAt = 0; + _glfw.ns.displayLinks.entries[i].first_unserviced_render_frame_request_at = 0; } } _glfw.ns.displayLinks.count = 0; @@ -333,7 +334,14 @@ static CVReturn displayLinkCallback( 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; for (size_t i = 0; i < _glfw.ns.displayLinks.count; i++) { 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++]; memset(entry, 0, sizeof(_GLFWDisplayLinkNS)); entry->displayID = displayID; - CVDisplayLinkCreateWithCGDisplay(displayID, &entry->displayLink); - CVDisplayLinkSetOutputCallback(entry->displayLink, &displayLinkCallback, (void*)(uintptr_t)displayID); + _glfw_create_cv_display_link(entry); } // Poll for changes in the set of connected monitors diff --git a/glfw/cocoa_platform.h b/glfw/cocoa_platform.h index 6bbb527e0..67290e128 100644 --- a/glfw/cocoa_platform.h +++ b/glfw/cocoa_platform.h @@ -158,7 +158,7 @@ typedef struct _GLFWDisplayLinkNS { CVDisplayLinkRef displayLink; CGDirectDisplayID displayID; - monotonic_t lastRenderFrameRequestedAt; + monotonic_t lastRenderFrameRequestedAt, first_unserviced_render_frame_request_at; } _GLFWDisplayLinkNS; // Cocoa-specific global data @@ -246,3 +246,4 @@ void _glfwDispatchTickCallback(void); void _glfwDispatchRenderFrame(CGDirectDisplayID); void _glfwShutdownCVDisplayLink(unsigned long long, void*); void _glfwCocoaPostEmptyEvent(void); +void _glfw_create_cv_display_link(_GLFWDisplayLinkNS *entry); diff --git a/glfw/cocoa_window.m b/glfw/cocoa_window.m index 899f6b15d..6d6a7e589 100644 --- a/glfw/cocoa_window.m +++ b/glfw/cocoa_window.m @@ -317,6 +317,7 @@ _glfwShutdownCVDisplayLink(unsigned long long timer_id UNUSED, void *user_data U _GLFWDisplayLinkNS *dl = &_glfw.ns.displayLinks.entries[i]; if (dl->displayLink) CVDisplayLinkStop(dl->displayLink); 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]; if (dl->displayID == displayID) { 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); + 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) { CVDisplayLinkStop(dl->displayLink); dl->lastRenderFrameRequestedAt = 0; + dl->first_unserviced_render_frame_request_at = 0; } } } @@ -2141,6 +2154,12 @@ _glfwDispatchRenderFrame(CGDirectDisplayID displayID) { } 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)