From ade4e67b51922f9e43ce30917ca3f4681e9676c3 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 1 Aug 2021 11:10:46 +0530 Subject: [PATCH] Possible fix for #3890 Try to ensure we have a functioning displaylink always. GLFW skips over sleeping monitors during a poll and also had a bug where the display link was not re-created for a monitor that already had a glfw monitor entry. Also add a bunch more debug reporting --- glfw/cocoa_monitor.m | 48 ++++++++++++++++++++++++++++--------------- glfw/cocoa_platform.h | 1 + glfw/cocoa_window.m | 12 ++++++++++- 3 files changed, 43 insertions(+), 18 deletions(-) diff --git a/glfw/cocoa_monitor.m b/glfw/cocoa_monitor.m index 5873a6005..1117ededa 100644 --- a/glfw/cocoa_monitor.m +++ b/glfw/cocoa_monitor.m @@ -41,7 +41,8 @@ // Get the name of the specified display, or NULL // -static char* getDisplayName(CGDirectDisplayID displayID, NSScreen* screen) +static char* +getDisplayName(CGDirectDisplayID displayID, NSScreen* screen) { // IOKit doesn't work on Apple Silicon anymore // Luckily, 10.15 introduced -[NSScreen localizedName]. @@ -51,8 +52,9 @@ static char* getDisplayName(CGDirectDisplayID displayID, NSScreen* screen) if ([screen respondsToSelector:@selector(localizedName)]) { NSString* name = [screen valueForKey:@"localizedName"]; - if (name) + if (name) { return _glfw_strdup([name UTF8String]); + } } } io_iterator_t it; @@ -64,7 +66,7 @@ static char* getDisplayName(CGDirectDisplayID displayID, NSScreen* screen) &it) != 0) { // This may happen if a desktop Mac is running headless - return _glfw_strdup("Display"); + return NULL; } while ((service = IOIteratorNext(it)) != 0) @@ -102,7 +104,7 @@ static char* getDisplayName(CGDirectDisplayID displayID, NSScreen* screen) { _glfwInputError(GLFW_PLATFORM_ERROR, "Cocoa: Failed to find service port for display"); - return _glfw_strdup("Display"); + return NULL; } CFDictionaryRef names = @@ -115,7 +117,7 @@ static char* getDisplayName(CGDirectDisplayID displayID, NSScreen* screen) { // This may happen if a desktop Mac is running headless CFRelease(info); - return _glfw_strdup("Display"); + return NULL; } const CFIndex size = @@ -326,11 +328,9 @@ void _glfwClearDisplayLinks() { if (_glfw.ns.displayLinks.entries[i].displayLink) { CVDisplayLinkStop(_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].lastRenderFrameRequestedAt = 0; - _glfw.ns.displayLinks.entries[i].first_unserviced_render_frame_request_at = 0; } } + memset(_glfw.ns.displayLinks.entries, 0, sizeof(_GLFWDisplayLinkNS) * _glfw.ns.displayLinks.count); _glfw.ns.displayLinks.count = 0; } @@ -352,16 +352,21 @@ _glfw_create_cv_display_link(_GLFWDisplayLinkNS *entry) { 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; +_GLFWDisplayLinkNS* +_glfw_create_display_link(CGDirectDisplayID displayID) { + if (_glfw.ns.displayLinks.count >= arraysz(_glfw.ns.displayLinks.entries) - 1) { + _glfwInputError(GLFW_PLATFORM_ERROR, "Too many monitors cannot create display link"); + return NULL; + } for (size_t i = 0; i < _glfw.ns.displayLinks.count; i++) { - if (_glfw.ns.displayLinks.entries[i].displayID == displayID) return; + // already created in this run + if (_glfw.ns.displayLinks.entries[i].displayID == displayID) return _glfw.ns.displayLinks.entries + i; } _GLFWDisplayLinkNS *entry = &_glfw.ns.displayLinks.entries[_glfw.ns.displayLinks.count++]; memset(entry, 0, sizeof(_GLFWDisplayLinkNS)); entry->displayID = displayID; _glfw_create_cv_display_link(entry); + return entry; } // Poll for changes in the set of connected monitors @@ -369,11 +374,13 @@ createDisplayLink(CGDirectDisplayID displayID) { void _glfwPollMonitorsNS(void) { uint32_t displayCount; - CGGetOnlineDisplayList(0, NULL, &displayCount); CGDirectDisplayID* displays = calloc(displayCount, sizeof(CGDirectDisplayID)); CGGetOnlineDisplayList(displayCount, displays, &displayCount); _glfwClearDisplayLinks(); + if (_glfw.hints.init.debugRendering) { + fprintf(stderr, "Polling for monitors: %u found\n", displayCount); + } for (int i = 0; i < _glfw.monitorCount; i++) _glfw.monitors[i]->ns.screen = nil; @@ -390,8 +397,10 @@ void _glfwPollMonitorsNS(void) for (uint32_t i = 0; i < displayCount; i++) { - if (CGDisplayIsAsleep(displays[i])) + if (CGDisplayIsAsleep(displays[i])) { + if (_glfw.hints.init.debugRendering) fprintf(stderr, "Ignoring sleeping display: %u", displays[i]); continue; + } const uint32_t unitNumber = CGDisplayUnitNumber(displays[i]); NSScreen* screen = nil; @@ -415,7 +424,9 @@ void _glfwPollMonitorsNS(void) { if (disconnected[j] && disconnected[j]->ns.unitNumber == unitNumber) { + disconnected[j]->ns.displayID = displays[i]; disconnected[j]->ns.screen = screen; + _glfw_create_display_link(displays[i]); disconnected[j] = NULL; break; } @@ -426,14 +437,17 @@ void _glfwPollMonitorsNS(void) const CGSize size = CGDisplayScreenSize(displays[i]); char* name = getDisplayName(displays[i], screen); - if (!name) - continue; + if (!name) { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Failed to get name for display, using generic name"); + name = _glfw_strdup("Display with no name"); + } _GLFWmonitor* monitor = _glfwAllocMonitor(name, (int)size.width, (int)size.height); monitor->ns.displayID = displays[i]; monitor->ns.unitNumber = unitNumber; monitor->ns.screen = screen; - createDisplayLink(monitor->ns.displayID); + _glfw_create_display_link(monitor->ns.displayID); free(name); diff --git a/glfw/cocoa_platform.h b/glfw/cocoa_platform.h index 9533c9589..8f6d7a2cf 100644 --- a/glfw/cocoa_platform.h +++ b/glfw/cocoa_platform.h @@ -250,3 +250,4 @@ void _glfwDispatchRenderFrame(CGDirectDisplayID); void _glfwShutdownCVDisplayLink(unsigned long long, void*); void _glfwCocoaPostEmptyEvent(void); void _glfw_create_cv_display_link(_GLFWDisplayLinkNS *entry); +_GLFWDisplayLinkNS* _glfw_create_display_link(CGDirectDisplayID); diff --git a/glfw/cocoa_window.m b/glfw/cocoa_window.m index 123da2a3f..f1d62965a 100644 --- a/glfw/cocoa_window.m +++ b/glfw/cocoa_window.m @@ -337,9 +337,12 @@ requestRenderFrame(_GLFWwindow *w, GLFWcocoarenderframefun callback) { display_link_shutdown_timer = _glfwPlatformAddTimer(DISPLAY_LINK_SHUTDOWN_CHECK_INTERVAL, false, _glfwShutdownCVDisplayLink, NULL, NULL); } monotonic_t now = glfwGetTime(); + bool found_display_link = false; + _GLFWDisplayLinkNS *dl = NULL; for (size_t i = 0; i < _glfw.ns.displayLinks.count; i++) { - _GLFWDisplayLinkNS *dl = &_glfw.ns.displayLinks.entries[i]; + dl = &_glfw.ns.displayLinks.entries[i]; if (dl->displayID == displayID) { + found_display_link = true; 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); @@ -359,6 +362,13 @@ requestRenderFrame(_GLFWwindow *w, GLFWcocoarenderframefun callback) { dl->first_unserviced_render_frame_request_at = 0; } } + if (!found_display_link) { + dl = _glfw_create_display_link(displayID); + if (dl) { + dl->lastRenderFrameRequestedAt = now; + if (!CVDisplayLinkIsRunning(dl->displayLink)) CVDisplayLinkStart(dl->displayLink); + } + } } void