mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-28 03:01:57 +02:00
Improve macOS idle wakeups handling
This commit is contained in:
@@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
#include <CoreVideo/CVDisplayLink.h>
|
#include <CoreVideo/CVDisplayLink.h>
|
||||||
|
#include <os/lock.h>
|
||||||
|
|
||||||
#define DISPLAY_LINK_SHUTDOWN_CHECK_INTERVAL s_to_monotonic_t(30ll)
|
#define DISPLAY_LINK_SHUTDOWN_CHECK_INTERVAL s_to_monotonic_t(30ll)
|
||||||
|
|
||||||
@@ -18,6 +19,7 @@ typedef struct _GLFWDisplayLinkNS
|
|||||||
CVDisplayLinkRef displayLink;
|
CVDisplayLinkRef displayLink;
|
||||||
CGDirectDisplayID displayID;
|
CGDirectDisplayID displayID;
|
||||||
monotonic_t lastRenderFrameRequestedAt, first_unserviced_render_frame_request_at;
|
monotonic_t lastRenderFrameRequestedAt, first_unserviced_render_frame_request_at;
|
||||||
|
bool pending_dispatch;
|
||||||
} _GLFWDisplayLinkNS;
|
} _GLFWDisplayLinkNS;
|
||||||
|
|
||||||
static struct {
|
static struct {
|
||||||
@@ -25,6 +27,18 @@ static struct {
|
|||||||
size_t count;
|
size_t count;
|
||||||
} displayLinks = {0};
|
} displayLinks = {0};
|
||||||
|
|
||||||
|
static os_unfair_lock displayLinkLocks[256];
|
||||||
|
|
||||||
|
static inline size_t
|
||||||
|
index_for_entry(_GLFWDisplayLinkNS *entry) {
|
||||||
|
return (size_t)(entry - displayLinks.entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline os_unfair_lock*
|
||||||
|
lock_for_entry(_GLFWDisplayLinkNS *entry) {
|
||||||
|
return &displayLinkLocks[index_for_entry(entry)];
|
||||||
|
}
|
||||||
|
|
||||||
static CGDirectDisplayID
|
static CGDirectDisplayID
|
||||||
displayIDForWindow(_GLFWwindow *w) {
|
displayIDForWindow(_GLFWwindow *w) {
|
||||||
NSWindow *nw = w->ns.object;
|
NSWindow *nw = w->ns.object;
|
||||||
@@ -37,12 +51,21 @@ displayIDForWindow(_GLFWwindow *w) {
|
|||||||
void
|
void
|
||||||
_glfwClearDisplayLinks(void) {
|
_glfwClearDisplayLinks(void) {
|
||||||
for (size_t i = 0; i < displayLinks.count; i++) {
|
for (size_t i = 0; i < displayLinks.count; i++) {
|
||||||
if (displayLinks.entries[i].displayLink) {
|
_GLFWDisplayLinkNS *entry = &displayLinks.entries[i];
|
||||||
CVDisplayLinkStop(displayLinks.entries[i].displayLink);
|
os_unfair_lock *lock = &displayLinkLocks[i];
|
||||||
CVDisplayLinkRelease(displayLinks.entries[i].displayLink);
|
os_unfair_lock_lock(lock);
|
||||||
|
CVDisplayLinkRef link = entry->displayLink;
|
||||||
|
entry->displayLink = NULL;
|
||||||
|
entry->displayID = (CGDirectDisplayID)0;
|
||||||
|
entry->lastRenderFrameRequestedAt = 0;
|
||||||
|
entry->first_unserviced_render_frame_request_at = 0;
|
||||||
|
entry->pending_dispatch = false;
|
||||||
|
os_unfair_lock_unlock(lock);
|
||||||
|
if (link) {
|
||||||
|
CVDisplayLinkStop(link);
|
||||||
|
CVDisplayLinkRelease(link);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
memset(displayLinks.entries, 0, sizeof(_GLFWDisplayLinkNS) * displayLinks.count);
|
|
||||||
displayLinks.count = 0;
|
displayLinks.count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,7 +74,16 @@ displayLinkCallback(
|
|||||||
CVDisplayLinkRef displayLink UNUSED,
|
CVDisplayLinkRef displayLink UNUSED,
|
||||||
const CVTimeStamp* now UNUSED, const CVTimeStamp* outputTime UNUSED,
|
const CVTimeStamp* now UNUSED, const CVTimeStamp* outputTime UNUSED,
|
||||||
CVOptionFlags flagsIn UNUSED, CVOptionFlags* flagsOut UNUSED, void* userInfo) {
|
CVOptionFlags flagsIn UNUSED, CVOptionFlags* flagsOut UNUSED, void* userInfo) {
|
||||||
CGDirectDisplayID displayID = (uintptr_t)userInfo;
|
_GLFWDisplayLinkNS *entry = (_GLFWDisplayLinkNS *)userInfo;
|
||||||
|
if (!entry) return kCVReturnSuccess;
|
||||||
|
os_unfair_lock *lock = lock_for_entry(entry);
|
||||||
|
os_unfair_lock_lock(lock);
|
||||||
|
bool should_dispatch = entry->first_unserviced_render_frame_request_at &&
|
||||||
|
!entry->pending_dispatch;
|
||||||
|
CGDirectDisplayID displayID = entry->displayID;
|
||||||
|
if (should_dispatch) entry->pending_dispatch = true;
|
||||||
|
os_unfair_lock_unlock(lock);
|
||||||
|
if (!should_dispatch) return kCVReturnSuccess;
|
||||||
NSNumber *arg = [NSNumber numberWithUnsignedInt:displayID];
|
NSNumber *arg = [NSNumber numberWithUnsignedInt:displayID];
|
||||||
[NSApp performSelectorOnMainThread:@selector(render_frame_received:) withObject:arg waitUntilDone:NO];
|
[NSApp performSelectorOnMainThread:@selector(render_frame_received:) withObject:arg waitUntilDone:NO];
|
||||||
[arg release];
|
[arg release];
|
||||||
@@ -61,7 +93,7 @@ displayLinkCallback(
|
|||||||
static void
|
static void
|
||||||
_glfw_create_cv_display_link(_GLFWDisplayLinkNS *entry) {
|
_glfw_create_cv_display_link(_GLFWDisplayLinkNS *entry) {
|
||||||
CVDisplayLinkCreateWithCGDisplay(entry->displayID, &entry->displayLink);
|
CVDisplayLinkCreateWithCGDisplay(entry->displayID, &entry->displayLink);
|
||||||
CVDisplayLinkSetOutputCallback(entry->displayLink, &displayLinkCallback, (void*)(uintptr_t)entry->displayID);
|
if (entry->displayLink) CVDisplayLinkSetOutputCallback(entry->displayLink, &displayLinkCallback, entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned
|
unsigned
|
||||||
@@ -71,14 +103,25 @@ _glfwCreateDisplayLink(CGDirectDisplayID displayID) {
|
|||||||
return displayLinks.count;
|
return displayLinks.count;
|
||||||
}
|
}
|
||||||
for (unsigned i = 0; i < displayLinks.count; i++) {
|
for (unsigned i = 0; i < displayLinks.count; i++) {
|
||||||
// already created in this run
|
os_unfair_lock *existing_lock = &displayLinkLocks[i];
|
||||||
if (displayLinks.entries[i].displayID == displayID) return i;
|
os_unfair_lock_lock(existing_lock);
|
||||||
|
bool already_created = displayLinks.entries[i].displayID == displayID;
|
||||||
|
os_unfair_lock_unlock(existing_lock);
|
||||||
|
if (already_created) return i;
|
||||||
}
|
}
|
||||||
_GLFWDisplayLinkNS *entry = &displayLinks.entries[displayLinks.count++];
|
unsigned idx = displayLinks.count;
|
||||||
memset(entry, 0, sizeof(_GLFWDisplayLinkNS));
|
_GLFWDisplayLinkNS *entry = &displayLinks.entries[idx];
|
||||||
|
os_unfair_lock *lock = &displayLinkLocks[idx];
|
||||||
|
os_unfair_lock_lock(lock);
|
||||||
|
entry->displayLink = NULL;
|
||||||
entry->displayID = displayID;
|
entry->displayID = displayID;
|
||||||
|
entry->lastRenderFrameRequestedAt = 0;
|
||||||
|
entry->first_unserviced_render_frame_request_at = 0;
|
||||||
|
entry->pending_dispatch = false;
|
||||||
|
displayLinks.count++;
|
||||||
_glfw_create_cv_display_link(entry);
|
_glfw_create_cv_display_link(entry);
|
||||||
return displayLinks.count - 1;
|
os_unfair_lock_unlock(lock);
|
||||||
|
return idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned long long display_link_shutdown_timer = 0;
|
static unsigned long long display_link_shutdown_timer = 0;
|
||||||
@@ -88,9 +131,18 @@ _glfwShutdownCVDisplayLink(unsigned long long timer_id UNUSED, void *user_data U
|
|||||||
display_link_shutdown_timer = 0;
|
display_link_shutdown_timer = 0;
|
||||||
for (size_t i = 0; i < displayLinks.count; i++) {
|
for (size_t i = 0; i < displayLinks.count; i++) {
|
||||||
_GLFWDisplayLinkNS *dl = &displayLinks.entries[i];
|
_GLFWDisplayLinkNS *dl = &displayLinks.entries[i];
|
||||||
if (dl->displayLink) CVDisplayLinkStop(dl->displayLink);
|
os_unfair_lock *lock = &displayLinkLocks[i];
|
||||||
|
os_unfair_lock_lock(lock);
|
||||||
|
CVDisplayLinkRef link = dl->displayLink;
|
||||||
|
if (link) CVDisplayLinkRetain(link);
|
||||||
dl->lastRenderFrameRequestedAt = 0;
|
dl->lastRenderFrameRequestedAt = 0;
|
||||||
dl->first_unserviced_render_frame_request_at = 0;
|
dl->first_unserviced_render_frame_request_at = 0;
|
||||||
|
dl->pending_dispatch = false;
|
||||||
|
os_unfair_lock_unlock(lock);
|
||||||
|
if (link) {
|
||||||
|
CVDisplayLinkStop(link);
|
||||||
|
CVDisplayLinkRelease(link);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,34 +159,82 @@ _glfwRequestRenderFrame(_GLFWwindow *w) {
|
|||||||
_GLFWDisplayLinkNS *dl = NULL;
|
_GLFWDisplayLinkNS *dl = NULL;
|
||||||
for (size_t i = 0; i < displayLinks.count; i++) {
|
for (size_t i = 0; i < displayLinks.count; i++) {
|
||||||
dl = &displayLinks.entries[i];
|
dl = &displayLinks.entries[i];
|
||||||
|
bool need_start = false, need_stop = false, need_recreate = false;
|
||||||
|
bool retain_link = false;
|
||||||
|
CVDisplayLinkRef link = NULL;
|
||||||
|
CVDisplayLinkRef new_link = NULL;
|
||||||
|
bool new_link_retained = false;
|
||||||
|
os_unfair_lock *lock = &displayLinkLocks[i];
|
||||||
|
os_unfair_lock_lock(lock);
|
||||||
|
link = dl->displayLink;
|
||||||
if (dl->displayID == displayID) {
|
if (dl->displayID == displayID) {
|
||||||
found_display_link = true;
|
found_display_link = true;
|
||||||
|
monotonic_t first_unserviced = dl->first_unserviced_render_frame_request_at;
|
||||||
dl->lastRenderFrameRequestedAt = now;
|
dl->lastRenderFrameRequestedAt = now;
|
||||||
if (!dl->first_unserviced_render_frame_request_at) dl->first_unserviced_render_frame_request_at = now;
|
if (!first_unserviced) {
|
||||||
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 can't even
|
|
||||||
// get a simple timer right
|
|
||||||
CVDisplayLinkRelease(dl->displayLink); dl->displayLink = nil;
|
|
||||||
dl->first_unserviced_render_frame_request_at = now;
|
dl->first_unserviced_render_frame_request_at = now;
|
||||||
_glfw_create_cv_display_link(dl);
|
first_unserviced = now;
|
||||||
_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) {
|
dl->pending_dispatch = false;
|
||||||
CVDisplayLinkStop(dl->displayLink);
|
if (link) {
|
||||||
|
if (!CVDisplayLinkIsRunning(link)) {
|
||||||
|
need_start = true;
|
||||||
|
retain_link = true;
|
||||||
|
} else if (now - first_unserviced > s_to_monotonic_t(1ll)) {
|
||||||
|
need_recreate = true;
|
||||||
|
dl->first_unserviced_render_frame_request_at = now;
|
||||||
|
dl->displayLink = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (link && dl->lastRenderFrameRequestedAt && now - dl->lastRenderFrameRequestedAt >= DISPLAY_LINK_SHUTDOWN_CHECK_INTERVAL) {
|
||||||
|
need_stop = true;
|
||||||
|
retain_link = true;
|
||||||
dl->lastRenderFrameRequestedAt = 0;
|
dl->lastRenderFrameRequestedAt = 0;
|
||||||
dl->first_unserviced_render_frame_request_at = 0;
|
dl->first_unserviced_render_frame_request_at = 0;
|
||||||
|
dl->pending_dispatch = false;
|
||||||
|
}
|
||||||
|
if (retain_link && link) CVDisplayLinkRetain(link);
|
||||||
|
os_unfair_lock_unlock(lock);
|
||||||
|
if (need_recreate && link) {
|
||||||
|
CVDisplayLinkStop(link);
|
||||||
|
CVDisplayLinkRelease(link);
|
||||||
|
os_unfair_lock_lock(lock);
|
||||||
|
_glfw_create_cv_display_link(dl);
|
||||||
|
new_link = dl->displayLink;
|
||||||
|
if (new_link) {
|
||||||
|
CVDisplayLinkRetain(new_link);
|
||||||
|
new_link_retained = true;
|
||||||
|
}
|
||||||
|
dl->pending_dispatch = false;
|
||||||
|
os_unfair_lock_unlock(lock);
|
||||||
|
if (new_link) {
|
||||||
|
if (!CVDisplayLinkIsRunning(new_link)) CVDisplayLinkStart(new_link);
|
||||||
|
if (new_link_retained) CVDisplayLinkRelease(new_link);
|
||||||
|
}
|
||||||
|
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||||
|
"CVDisplayLink stuck possibly because of sleep/screensaver + Apple's incompetence, recreating.");
|
||||||
|
} else {
|
||||||
|
if (need_start && link) CVDisplayLinkStart(link);
|
||||||
|
else if (need_stop && link) CVDisplayLinkStop(link);
|
||||||
|
if (retain_link && link) CVDisplayLinkRelease(link);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!found_display_link) {
|
if (!found_display_link) {
|
||||||
unsigned idx = _glfwCreateDisplayLink(displayID);
|
unsigned idx = _glfwCreateDisplayLink(displayID);
|
||||||
if (idx < displayLinks.count) {
|
if (idx < displayLinks.count) {
|
||||||
dl = &displayLinks.entries[idx];
|
dl = &displayLinks.entries[idx];
|
||||||
|
os_unfair_lock *lock = &displayLinkLocks[idx];
|
||||||
|
os_unfair_lock_lock(lock);
|
||||||
dl->lastRenderFrameRequestedAt = now;
|
dl->lastRenderFrameRequestedAt = now;
|
||||||
dl->first_unserviced_render_frame_request_at = now;
|
dl->first_unserviced_render_frame_request_at = now;
|
||||||
if (!CVDisplayLinkIsRunning(dl->displayLink)) CVDisplayLinkStart(dl->displayLink);
|
dl->pending_dispatch = false;
|
||||||
|
CVDisplayLinkRef link = dl->displayLink;
|
||||||
|
if (link) CVDisplayLinkRetain(link);
|
||||||
|
os_unfair_lock_unlock(lock);
|
||||||
|
if (link) {
|
||||||
|
if (!CVDisplayLinkIsRunning(link)) CVDisplayLinkStart(link);
|
||||||
|
CVDisplayLinkRelease(link);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -151,10 +251,33 @@ _glfwDispatchRenderFrame(CGDirectDisplayID displayID) {
|
|||||||
}
|
}
|
||||||
for (size_t i = 0; i < displayLinks.count; i++) {
|
for (size_t i = 0; i < displayLinks.count; i++) {
|
||||||
_GLFWDisplayLinkNS *dl = &displayLinks.entries[i];
|
_GLFWDisplayLinkNS *dl = &displayLinks.entries[i];
|
||||||
|
bool need_stop = false;
|
||||||
|
CVDisplayLinkRef link = NULL;
|
||||||
|
os_unfair_lock *lock = &displayLinkLocks[i];
|
||||||
|
os_unfair_lock_lock(lock);
|
||||||
if (dl->displayID == displayID) {
|
if (dl->displayID == displayID) {
|
||||||
dl->first_unserviced_render_frame_request_at = 0;
|
dl->first_unserviced_render_frame_request_at = 0;
|
||||||
|
dl->pending_dispatch = false;
|
||||||
|
bool any_pending_request = false;
|
||||||
|
_GLFWwindow *window = _glfw.windowListHead;
|
||||||
|
while (window) {
|
||||||
|
if (window->ns.renderFrameRequested && displayID == displayIDForWindow(window)) {
|
||||||
|
any_pending_request = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
window = window->next;
|
||||||
|
}
|
||||||
|
link = dl->displayLink;
|
||||||
|
if (!any_pending_request && link && CVDisplayLinkIsRunning(link)) {
|
||||||
|
need_stop = true;
|
||||||
|
CVDisplayLinkRetain(link);
|
||||||
|
dl->lastRenderFrameRequestedAt = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
os_unfair_lock_unlock(lock);
|
||||||
|
if (need_stop && link) {
|
||||||
|
CVDisplayLinkStop(link);
|
||||||
|
CVDisplayLinkRelease(link);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user