From 09b0fbcdedb68f33c7cedaabecf0dda83d846786 Mon Sep 17 00:00:00 2001 From: Matthias von Arx Date: Sun, 20 Jul 2025 00:06:12 +0200 Subject: [PATCH 1/5] drm: check syncobj timeline support before advertising protocol Prevents crashes on systems where DRM driver lacks syncobj timeline support (e.g., Apple Silicon with Honeykrisp driver). Applications like Zed and WezTerm would crash with 'Timeline failed importing' when trying to use explicit sync. Fixes #8158 #8803 --- src/helpers/sync/SyncTimeline.cpp | 12 ++++++++++++ src/managers/ProtocolManager.cpp | 14 ++++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/helpers/sync/SyncTimeline.cpp b/src/helpers/sync/SyncTimeline.cpp index 1b089caaf4b..912799f3fb1 100644 --- a/src/helpers/sync/SyncTimeline.cpp +++ b/src/helpers/sync/SyncTimeline.cpp @@ -6,7 +6,16 @@ #include using namespace Hyprutils::OS; +static bool checkDrmSyncobjTimelineSupport(int drmFD) { + uint64_t cap = 0; + int ret = drmGetCap(drmFD, DRM_CAP_SYNCOBJ_TIMELINE, &cap); + return (ret == 0 && cap != 0); +} + SP CSyncTimeline::create(int drmFD_) { + if (!checkDrmSyncobjTimelineSupport(drmFD_)) + return nullptr; + auto timeline = SP(new CSyncTimeline); timeline->m_drmFD = drmFD_; timeline->m_self = timeline; @@ -20,6 +29,9 @@ SP CSyncTimeline::create(int drmFD_) { } SP CSyncTimeline::create(int drmFD_, CFileDescriptor&& drmSyncobjFD) { + if (!checkDrmSyncobjTimelineSupport(drmFD_)) + return nullptr; + auto timeline = SP(new CSyncTimeline); timeline->m_drmFD = drmFD_; timeline->m_syncobjFD = std::move(drmSyncobjFD); diff --git a/src/managers/ProtocolManager.cpp b/src/managers/ProtocolManager.cpp index eac6dee0838..beafb05d166 100644 --- a/src/managers/ProtocolManager.cpp +++ b/src/managers/ProtocolManager.cpp @@ -70,6 +70,7 @@ #include "content-type-v1.hpp" #include +#include #include #include @@ -210,8 +211,17 @@ CProtocolManager::CProtocolManager() { else lease.reset(); - if (g_pHyprOpenGL->m_exts.EGL_ANDROID_native_fence_sync_ext && !PROTO::sync) - PROTO::sync = makeUnique(&wp_linux_drm_syncobj_manager_v1_interface, 1, "DRMSyncobj"); + if (g_pHyprOpenGL->m_exts.EGL_ANDROID_native_fence_sync_ext && !PROTO::sync) { + // Check if DRM supports syncobj timelines before creating the protocol + uint64_t cap = 0; + int ret = drmGetCap(g_pCompositor->m_drmFD, DRM_CAP_SYNCOBJ_TIMELINE, &cap); + if (ret == 0 && cap != 0) { + PROTO::sync = makeUnique(&wp_linux_drm_syncobj_manager_v1_interface, 1, "DRMSyncobj"); + Debug::log(LOG, "DRM Syncobj Timeline support detected, enabling explicit sync protocol"); + } else { + Debug::log(WARN, "DRM Syncobj Timeline not supported, skipping explicit sync protocol"); + } + } } if (!g_pHyprOpenGL->getDRMFormats().empty()) { From 666e668b4f80a97b598a8aebcda9584a5c450564 Mon Sep 17 00:00:00 2001 From: Matthias von Arx Date: Mon, 21 Jul 2025 20:32:14 +0200 Subject: [PATCH 2/5] drm: centralize syncobj timeline check in compositor --- src/Compositor.cpp | 11 +++++++++++ src/Compositor.hpp | 4 ++++ src/helpers/sync/SyncTimeline.cpp | 11 +++-------- src/managers/ProtocolManager.cpp | 5 +---- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/Compositor.cpp b/src/Compositor.cpp index abfa8d848e7..06c6722ebaf 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -73,6 +73,7 @@ #include #include #include +#include using namespace Hyprutils::String; using namespace Aquamarine; @@ -354,6 +355,16 @@ void CCompositor::initServer(std::string socketName, int socketFd) { m_drmFD = m_aqBackend->drmFD(); Debug::log(LOG, "Running on DRMFD: {}", m_drmFD); + if (m_drmFD >= 0) { + uint64_t cap = 0; + int ret = drmGetCap(m_drmFD, DRM_CAP_SYNCOBJ_TIMELINE, &cap); + m_bDrmSyncobjTimelineSupported = (ret == 0 && cap != 0); + Debug::log(LOG, "DRM syncobj timeline support: {}", m_bDrmSyncobjTimelineSupported ? "yes" : "no"); + } else { + m_bDrmSyncobjTimelineSupported = false; + Debug::log(LOG, "DRM syncobj timeline support: no (no DRM FD)"); + } + if (!socketName.empty() && socketFd != -1) { fcntl(socketFd, F_SETFD, FD_CLOEXEC); const auto RETVAL = wl_display_add_socket_fd(m_wlDisplay, socketFd); diff --git a/src/Compositor.hpp b/src/Compositor.hpp index b4a0c51cbc8..3274b7f4680 100644 --- a/src/Compositor.hpp +++ b/src/Compositor.hpp @@ -152,6 +152,8 @@ class CCompositor { NColorManagement::SImageDescription getPreferredImageDescription(); bool shouldChangePreferredImageDescription(); + bool supportsDrmSyncobjTimeline() const { return m_bDrmSyncobjTimelineSupported; } + std::string m_explicitConfigPath; private: @@ -165,6 +167,8 @@ class CCompositor { void removeLockFile(); void setMallocThreshold(); + bool m_bDrmSyncobjTimelineSupported = false; + uint64_t m_hyprlandPID = 0; wl_event_source* m_critSigSource = nullptr; rlimit m_originalNofile = {}; diff --git a/src/helpers/sync/SyncTimeline.cpp b/src/helpers/sync/SyncTimeline.cpp index 912799f3fb1..614a7c5be50 100644 --- a/src/helpers/sync/SyncTimeline.cpp +++ b/src/helpers/sync/SyncTimeline.cpp @@ -1,19 +1,14 @@ #include "SyncTimeline.hpp" #include "../../defines.hpp" #include "../../managers/eventLoop/EventLoopManager.hpp" +#include "../../Compositor.hpp" #include #include using namespace Hyprutils::OS; -static bool checkDrmSyncobjTimelineSupport(int drmFD) { - uint64_t cap = 0; - int ret = drmGetCap(drmFD, DRM_CAP_SYNCOBJ_TIMELINE, &cap); - return (ret == 0 && cap != 0); -} - SP CSyncTimeline::create(int drmFD_) { - if (!checkDrmSyncobjTimelineSupport(drmFD_)) + if (!g_pCompositor->supportsDrmSyncobjTimeline()) return nullptr; auto timeline = SP(new CSyncTimeline); @@ -29,7 +24,7 @@ SP CSyncTimeline::create(int drmFD_) { } SP CSyncTimeline::create(int drmFD_, CFileDescriptor&& drmSyncobjFD) { - if (!checkDrmSyncobjTimelineSupport(drmFD_)) + if (!g_pCompositor->supportsDrmSyncobjTimeline()) return nullptr; auto timeline = SP(new CSyncTimeline); diff --git a/src/managers/ProtocolManager.cpp b/src/managers/ProtocolManager.cpp index beafb05d166..2b6b5a7726d 100644 --- a/src/managers/ProtocolManager.cpp +++ b/src/managers/ProtocolManager.cpp @@ -212,10 +212,7 @@ CProtocolManager::CProtocolManager() { lease.reset(); if (g_pHyprOpenGL->m_exts.EGL_ANDROID_native_fence_sync_ext && !PROTO::sync) { - // Check if DRM supports syncobj timelines before creating the protocol - uint64_t cap = 0; - int ret = drmGetCap(g_pCompositor->m_drmFD, DRM_CAP_SYNCOBJ_TIMELINE, &cap); - if (ret == 0 && cap != 0) { + if (g_pCompositor->supportsDrmSyncobjTimeline()) { PROTO::sync = makeUnique(&wp_linux_drm_syncobj_manager_v1_interface, 1, "DRMSyncobj"); Debug::log(LOG, "DRM Syncobj Timeline support detected, enabling explicit sync protocol"); } else { From 4d9901655a606260bd3a55cb9b5bda9e7a4fea86 Mon Sep 17 00:00:00 2001 From: Matthias von Arx Date: Mon, 21 Jul 2025 21:37:10 +0200 Subject: [PATCH 3/5] style: remove unnecessary braces --- src/managers/ProtocolManager.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/managers/ProtocolManager.cpp b/src/managers/ProtocolManager.cpp index 2b6b5a7726d..ebd90a45b07 100644 --- a/src/managers/ProtocolManager.cpp +++ b/src/managers/ProtocolManager.cpp @@ -215,9 +215,8 @@ CProtocolManager::CProtocolManager() { if (g_pCompositor->supportsDrmSyncobjTimeline()) { PROTO::sync = makeUnique(&wp_linux_drm_syncobj_manager_v1_interface, 1, "DRMSyncobj"); Debug::log(LOG, "DRM Syncobj Timeline support detected, enabling explicit sync protocol"); - } else { + } else Debug::log(WARN, "DRM Syncobj Timeline not supported, skipping explicit sync protocol"); - } } } From 384b608d6c0f78442fd89fd09877af5a53f5bb8c Mon Sep 17 00:00:00 2001 From: mvonarx Date: Wed, 23 Jul 2025 08:29:20 +0200 Subject: [PATCH 4/5] Fix clang-format violations --- src/Compositor.cpp | 4 ++-- src/Compositor.hpp | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Compositor.cpp b/src/Compositor.cpp index 06c6722ebaf..4a4607afc8f 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -356,8 +356,8 @@ void CCompositor::initServer(std::string socketName, int socketFd) { Debug::log(LOG, "Running on DRMFD: {}", m_drmFD); if (m_drmFD >= 0) { - uint64_t cap = 0; - int ret = drmGetCap(m_drmFD, DRM_CAP_SYNCOBJ_TIMELINE, &cap); + uint64_t cap = 0; + int ret = drmGetCap(m_drmFD, DRM_CAP_SYNCOBJ_TIMELINE, &cap); m_bDrmSyncobjTimelineSupported = (ret == 0 && cap != 0); Debug::log(LOG, "DRM syncobj timeline support: {}", m_bDrmSyncobjTimelineSupported ? "yes" : "no"); } else { diff --git a/src/Compositor.hpp b/src/Compositor.hpp index 3274b7f4680..d56bfc6d5d0 100644 --- a/src/Compositor.hpp +++ b/src/Compositor.hpp @@ -152,9 +152,11 @@ class CCompositor { NColorManagement::SImageDescription getPreferredImageDescription(); bool shouldChangePreferredImageDescription(); - bool supportsDrmSyncobjTimeline() const { return m_bDrmSyncobjTimelineSupported; } + bool supportsDrmSyncobjTimeline() const { + return m_bDrmSyncobjTimelineSupported; + } - std::string m_explicitConfigPath; + std::string m_explicitConfigPath; private: void initAllSignals(); From 97b3a3f8d2edd9a9fd7e0fedd33d4a12c2a05dfe Mon Sep 17 00:00:00 2001 From: Matthias von Arx Date: Wed, 23 Jul 2025 17:55:27 +0200 Subject: [PATCH 5/5] move function body outside of header --- src/Compositor.cpp | 4 ++++ src/Compositor.hpp | 7 ++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Compositor.cpp b/src/Compositor.cpp index 4a4607afc8f..d082b621afb 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -168,6 +168,10 @@ void CCompositor::restoreNofile() { Debug::log(ERR, "Failed restoring NOFILE limits"); } +bool CCompositor::supportsDrmSyncobjTimeline() const { + return m_bDrmSyncobjTimelineSupported; +} + void CCompositor::setMallocThreshold() { #ifdef M_TRIM_THRESHOLD // The default is 128 pages, diff --git a/src/Compositor.hpp b/src/Compositor.hpp index d56bfc6d5d0..23d1c365f28 100644 --- a/src/Compositor.hpp +++ b/src/Compositor.hpp @@ -152,11 +152,8 @@ class CCompositor { NColorManagement::SImageDescription getPreferredImageDescription(); bool shouldChangePreferredImageDescription(); - bool supportsDrmSyncobjTimeline() const { - return m_bDrmSyncobjTimelineSupported; - } - - std::string m_explicitConfigPath; + bool supportsDrmSyncobjTimeline() const; + std::string m_explicitConfigPath; private: void initAllSignals();