macOS: Handle failure to create display link or invalid display link

In some rare situations the display link may fail to create, or will
be created in an uninitialized state:

  https://bugzilla.mozilla.org/show_bug.cgi?id=1201401#c123

When the latter happens the display link thread will crash in
CVCGDisplayLink::getDisplayTimes(). Based on the Mozilla bug
report, and subsequent patch, we can detect this situation via
CVDisplayLinkGetCurrentCGDisplay(), so we follow the same
approach, and then bail out:

  https://bugzilla.mozilla.org/show_bug.cgi?id=1201401#c158

Once we bail out we fall back to the timer based approach
to delivering the update request. The next requestUpdate()
will try to use the display link again, which will likely
work this time around, as the display has had time to fully
initialize.

Pick-to: 6.5
Change-Id: Ib80fd792516d1e4e7f863a82755cbf00d1eb6c34
Reviewed-by: Robert Griebl <robert.griebl@qt.io>
Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
This commit is contained in:
Tor Arne Vestbø 2023-04-24 16:36:45 +02:00
parent 11f14c3b0d
commit ac0953c34d
3 changed files with 20 additions and 6 deletions

View File

@ -47,7 +47,7 @@ public:
static NSScreen *nativeScreenForDisplayId(CGDirectDisplayID displayId); static NSScreen *nativeScreenForDisplayId(CGDirectDisplayID displayId);
NSScreen *nativeScreen() const; NSScreen *nativeScreen() const;
void requestUpdate(); bool requestUpdate();
void deliverUpdateRequests(); void deliverUpdateRequests();
bool isRunningDisplayLink() const; bool isRunningDisplayLink() const;

View File

@ -289,24 +289,33 @@ void QCocoaScreen::update(CGDirectDisplayID displayId)
Q_LOGGING_CATEGORY(lcQpaScreenUpdates, "qt.qpa.screen.updates", QtCriticalMsg); Q_LOGGING_CATEGORY(lcQpaScreenUpdates, "qt.qpa.screen.updates", QtCriticalMsg);
void QCocoaScreen::requestUpdate() bool QCocoaScreen::requestUpdate()
{ {
Q_ASSERT(m_displayId); Q_ASSERT(m_displayId);
if (!isOnline()) { if (!isOnline()) {
qCDebug(lcQpaScreenUpdates) << this << "is not online. Ignoring update request"; qCDebug(lcQpaScreenUpdates) << this << "is not online. Ignoring update request";
return; return false;
} }
if (!m_displayLink) { if (!m_displayLink) {
CVDisplayLinkCreateWithCGDisplay(m_displayId, &m_displayLink); qCDebug(lcQpaScreenUpdates) << "Creating display link for" << this;
if (CVDisplayLinkCreateWithCGDisplay(m_displayId, &m_displayLink) != kCVReturnSuccess) {
qCWarning(lcQpaScreenUpdates) << "Failed to create display link for" << this;
return false;
}
if (auto displayId = CVDisplayLinkGetCurrentCGDisplay(m_displayLink); displayId != m_displayId) {
qCWarning(lcQpaScreenUpdates) << "Unexpected display" << displayId << "for display link";
CVDisplayLinkRelease(m_displayLink);
m_displayLink = nullptr;
return false;
}
CVDisplayLinkSetOutputCallback(m_displayLink, [](CVDisplayLinkRef, const CVTimeStamp*, CVDisplayLinkSetOutputCallback(m_displayLink, [](CVDisplayLinkRef, const CVTimeStamp*,
const CVTimeStamp*, CVOptionFlags, CVOptionFlags*, void* displayLinkContext) -> int { const CVTimeStamp*, CVOptionFlags, CVOptionFlags*, void* displayLinkContext) -> int {
// FIXME: It would be nice if update requests would include timing info // FIXME: It would be nice if update requests would include timing info
static_cast<QCocoaScreen*>(displayLinkContext)->deliverUpdateRequests(); static_cast<QCocoaScreen*>(displayLinkContext)->deliverUpdateRequests();
return kCVReturnSuccess; return kCVReturnSuccess;
}, this); }, this);
qCDebug(lcQpaScreenUpdates) << "Display link created for" << this;
// During live window resizing -[NSWindow _resizeWithEvent:] will spin a local event loop // During live window resizing -[NSWindow _resizeWithEvent:] will spin a local event loop
// in event-tracking mode, dequeuing only the mouse drag events needed to update the window's // in event-tracking mode, dequeuing only the mouse drag events needed to update the window's
@ -361,6 +370,8 @@ void QCocoaScreen::requestUpdate()
qCDebug(lcQpaScreenUpdates) << "Starting display link for" << this; qCDebug(lcQpaScreenUpdates) << "Starting display link for" << this;
CVDisplayLinkStart(m_displayLink); CVDisplayLinkStart(m_displayLink);
} }
return true;
} }
// Helper to allow building up debug output in multiple steps // Helper to allow building up debug output in multiple steps

View File

@ -1531,7 +1531,10 @@ void QCocoaWindow::requestUpdate()
<< "using" << (updatesWithDisplayLink() ? "display-link" : "timer"); << "using" << (updatesWithDisplayLink() ? "display-link" : "timer");
if (updatesWithDisplayLink()) { if (updatesWithDisplayLink()) {
static_cast<QCocoaScreen *>(screen())->requestUpdate(); if (!static_cast<QCocoaScreen *>(screen())->requestUpdate()) {
qCDebug(lcQpaDrawing) << "Falling back to timer-based update request";
QPlatformWindow::requestUpdate();
}
} else { } else {
// Fall back to the un-throttled timer-based callback // Fall back to the un-throttled timer-based callback
QPlatformWindow::requestUpdate(); QPlatformWindow::requestUpdate();