summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPppp1116 <pcaiadoguerreiro@gmail.com>2026-04-03 13:26:49 +0100
committerVaxry <43317083+vaxerski@users.noreply.github.com>2026-04-04 08:26:24 -0400
commit4c42269ce6c44af40ea63f048d67dc0b898c8213 (patch)
tree469602f310004ec1c41bc99432abae9828b2012a /src
parentd5b00bbc1759d2e8f6486152c0e622e57fc54b72 (diff)
monitor: centralize solitary and scanout eligibility checks
Diffstat (limited to 'src')
-rw-r--r--src/helpers/Drm.cpp49
-rw-r--r--src/helpers/Drm.hpp6
-rw-r--r--src/helpers/Monitor.cpp113
-rw-r--r--src/helpers/Monitor.hpp9
-rw-r--r--src/managers/PointerManager.cpp2
-rw-r--r--src/render/Renderer.cpp27
6 files changed, 163 insertions, 43 deletions
diff --git a/src/helpers/Drm.cpp b/src/helpers/Drm.cpp
index 207b5e3d..f91c5692 100644
--- a/src/helpers/Drm.cpp
+++ b/src/helpers/Drm.cpp
@@ -1,7 +1,50 @@
#include <xf86drm.h>
+#include <array>
+#include <map>
+#include <mutex>
+#include <optional>
+#include <sys/stat.h>
#include "Drm.hpp"
+namespace {
+ using SDRMNodePair = std::array<dev_t, 2>;
+
+ std::optional<SDRMNodePair> getDrmNodePair(int fd1, int fd2) {
+ const auto DEVA = DRM::devIDFromFD(fd1);
+ const auto DEVB = DRM::devIDFromFD(fd2);
+ if (!DEVA || !DEVB)
+ return std::nullopt;
+
+ SDRMNodePair pair = {*DEVA, *DEVB};
+ if (pair[0] > pair[1])
+ std::swap(pair[0], pair[1]);
+
+ return pair;
+ }
+}
+
+std::optional<dev_t> DRM::devIDFromFD(int fd) {
+ struct stat stat = {};
+ if (fstat(fd, &stat) != 0 || !S_ISCHR(stat.st_mode))
+ return std::nullopt;
+
+ return stat.st_rdev;
+}
+
bool DRM::sameGpu(int fd1, int fd2) {
+ if (fd1 >= 0 && fd1 == fd2)
+ return true;
+
+ static std::mutex cacheMutex;
+ static std::map<SDRMNodePair, bool> sameGpuCache;
+
+ const auto NODEPAIR = getDrmNodePair(fd1, fd2);
+ if (NODEPAIR) {
+ std::scoped_lock lock(cacheMutex);
+ if (const auto it = sameGpuCache.find(*NODEPAIR); it != sameGpuCache.end())
+ return it->second;
+ }
+
drmDevice* devA = nullptr;
drmDevice* devB = nullptr;
@@ -16,5 +59,11 @@ bool DRM::sameGpu(int fd1, int fd2) {
drmFreeDevice(&devA);
drmFreeDevice(&devB);
+
+ if (NODEPAIR) {
+ std::scoped_lock lock(cacheMutex);
+ sameGpuCache[*NODEPAIR] = same;
+ }
+
return same;
}
diff --git a/src/helpers/Drm.hpp b/src/helpers/Drm.hpp
index bc56b1ee..755ee050 100644
--- a/src/helpers/Drm.hpp
+++ b/src/helpers/Drm.hpp
@@ -1,5 +1,9 @@
#pragma once
+#include <optional>
+#include <sys/types.h>
+
namespace DRM {
- bool sameGpu(int fd1, int fd2);
+ std::optional<dev_t> devIDFromFD(int fd);
+ bool sameGpu(int fd1, int fd2);
}
diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp
index 33c2151c..39951028 100644
--- a/src/helpers/Monitor.cpp
+++ b/src/helpers/Monitor.cpp
@@ -1661,46 +1661,46 @@ void CMonitor::setCTM(const Mat3x3& ctm_) {
}
uint32_t CMonitor::isSolitaryBlocked(bool full) {
- uint32_t reasons = 0;
+ uint32_t reasons = 0;
- if (g_pHyprNotificationOverlay->hasAny()) {
- reasons |= SC_NOTIFICATION;
- if (!full)
- return reasons;
+ const auto PWORKSPACE = m_activeWorkspace;
+ if (!PWORKSPACE) {
+ reasons |= SC_WORKSPACE;
+ return reasons;
}
- if (g_pHyprError->active() && Desktop::focusState()->monitor() == m_self) {
- reasons |= SC_ERRORBAR;
+ if (!PWORKSPACE->m_hasFullscreenWindow) {
+ reasons |= SC_WINDOWED;
if (!full)
return reasons;
}
- if (g_pSessionLockManager->isSessionLocked()) {
- reasons |= SC_LOCK;
+ if (m_activeSpecialWorkspace) {
+ reasons |= SC_SPECIAL;
if (!full)
return reasons;
}
- const auto PWORKSPACE = m_activeWorkspace;
- if (!PWORKSPACE) {
- reasons |= SC_WORKSPACE;
- return reasons;
+ if (g_pHyprNotificationOverlay->hasAny()) {
+ reasons |= SC_NOTIFICATION;
+ if (!full)
+ return reasons;
}
- if (!PWORKSPACE->m_hasFullscreenWindow) {
- reasons |= SC_WINDOWED;
+ if (g_pHyprError->active() && Desktop::focusState()->monitor() == m_self) {
+ reasons |= SC_ERRORBAR;
if (!full)
return reasons;
}
- if (PROTO::data->dndActive()) {
- reasons |= SC_DND;
+ if (g_pSessionLockManager->isSessionLocked()) {
+ reasons |= SC_LOCK;
if (!full)
return reasons;
}
- if (m_activeSpecialWorkspace) {
- reasons |= SC_SPECIAL;
+ if (PROTO::data->dndActive()) {
+ reasons |= SC_DND;
if (!full)
return reasons;
}
@@ -1780,10 +1780,15 @@ uint32_t CMonitor::isSolitaryBlocked(bool full) {
void CMonitor::recheckSolitary() {
m_solitaryClient.reset(); // reset it, if we find one it will be set.
+
+ const auto PWORKSPACE = m_activeWorkspace;
+ if (!PWORKSPACE)
+ return;
+
if (isSolitaryBlocked())
return;
- m_solitaryClient = m_activeWorkspace->getFullscreenWindow();
+ m_solitaryClient = PWORKSPACE->getFullscreenWindow();
}
uint8_t CMonitor::isTearingBlocked(bool full) {
@@ -1847,6 +1852,15 @@ uint16_t CMonitor::isDSBlocked(bool full) {
static auto PDIRECTSCANOUT = CConfigValue<Hyprlang::INT>("render:direct_scanout");
static auto PPASS = CConfigValue<Hyprlang::INT>("render:cm_fs_passthrough");
static auto PNONSHADER = CConfigValue<Hyprlang::INT>("render:non_shader_cm");
+ const auto PWORKSPACE = m_activeWorkspace;
+
+ // Fast reject for the hot render path; full=true callers still collect
+ // the remaining blockers for hyprctl/debug output below.
+ if (!canAttemptDirectScanoutFast()) {
+ reasons |= DS_BLOCK_CANDIDATE;
+ if (!full)
+ return reasons;
+ }
if (*PDIRECTSCANOUT == 0) {
reasons |= DS_BLOCK_USER;
@@ -1855,11 +1869,11 @@ uint16_t CMonitor::isDSBlocked(bool full) {
}
if (*PDIRECTSCANOUT == 2) {
- if (!m_activeWorkspace || !m_activeWorkspace->m_hasFullscreenWindow || m_activeWorkspace->m_fullscreenMode != FSMODE_FULLSCREEN) {
+ if (!PWORKSPACE || !PWORKSPACE->m_hasFullscreenWindow || PWORKSPACE->m_fullscreenMode != FSMODE_FULLSCREEN) {
reasons |= DS_BLOCK_WINDOWED;
if (!full)
return reasons;
- } else if (m_activeWorkspace->getFullscreenWindow()->getContentType() != CONTENT_TYPE_GAME) {
+ } else if (PWORKSPACE->getFullscreenWindow()->getContentType() != CONTENT_TYPE_GAME) {
reasons |= DS_BLOCK_CONTENT;
if (!full)
return reasons;
@@ -1930,12 +1944,7 @@ bool CMonitor::attemptDirectScanout() {
const auto PCANDIDATE = m_solitaryClient.lock();
const auto PSURFACE = PCANDIDATE->getSolitaryResource();
- const auto params = PSURFACE->m_current.buffer->dmabuf();
-
- Log::logger->log(Log::TRACE, "attemptDirectScanout: surface {:x} passed, will attempt, buffer {} fmt: {} -> {} (mod {})", rc<uintptr_t>(PSURFACE.get()),
- rc<uintptr_t>(PSURFACE->m_current.buffer.m_buffer.get()), m_drmFormat, params.format, params.modifier);
-
- auto PBUFFER = PSURFACE->m_current.buffer.m_buffer;
+ auto PBUFFER = PSURFACE->m_current.buffer.m_buffer;
// #TODO this entire bit needs figuring out, vrr goes down the drain without it
if (PBUFFER == m_output->state->state().buffer && *PSAME) {
@@ -1963,6 +1972,11 @@ bool CMonitor::attemptDirectScanout() {
return true;
}
+ const auto params = PSURFACE->m_current.buffer->dmabuf();
+
+ Log::logger->log(Log::TRACE, "attemptDirectScanout: surface {:x} passed, will attempt, buffer {} fmt: {} -> {} (mod {})", rc<uintptr_t>(PSURFACE.get()),
+ rc<uintptr_t>(PSURFACE->m_current.buffer.m_buffer.get()), m_drmFormat, params.format, params.modifier);
+
// FIXME: make sure the buffer actually follows the available scanout dmabuf formats
// and comes from the appropriate device. This may implode on multi-gpu!!
@@ -1990,7 +2004,7 @@ bool CMonitor::attemptDirectScanout() {
m_output->state->addDamage(PSURFACE->m_current.accumulateBufferDamage());
// multigpu needs a fence to trigger fence syncing blits and also committing with the recreated dgpu fence
- if (!DRM::sameGpu(m_output->getBackend()->preferredAllocator()->drmFD(), g_pCompositor->m_drm.fd) && g_pHyprRenderer->explicitSyncSupported()) {
+ if (g_pHyprRenderer->explicitSyncSupported() && isMultiGPU()) {
auto sync = g_pHyprRenderer->createSyncFDManager();
if (sync->fd().isValid()) {
@@ -2032,6 +2046,47 @@ bool CMonitor::attemptDirectScanout() {
return true;
}
+bool CMonitor::canAttemptDirectScanoutFast() const {
+ return !m_solitaryClient.expired() || !m_lastScanout.expired() || m_directScanoutIsActive;
+}
+
+bool CMonitor::isMultiGPU() {
+ if (!m_output || !g_pCompositor)
+ return false;
+
+ const auto PREFERREDALLOCATOR = m_output->getBackend()->preferredAllocator();
+ const int allocatorFD = PREFERREDALLOCATOR ? PREFERREDALLOCATOR->drmFD() : -1;
+ const int compositorFD = g_pCompositor->m_drm.fd;
+
+ if (allocatorFD < 0 || compositorFD < 0) {
+ m_cachedAllocatorDRMDev.reset();
+ m_cachedCompositorDRMDev.reset();
+ m_cachedAllocatorDRMFD = allocatorFD;
+ m_cachedCompositorDRMFD = compositorFD;
+ m_cachedSameGPU = true;
+ return false;
+ }
+
+ const auto allocatorDev = DRM::devIDFromFD(allocatorFD);
+ const auto compositorDev = DRM::devIDFromFD(compositorFD);
+
+ // AQ can reopen DRM nodes for refcounting, so raw fd numbers are not a stable cache key.
+ const bool useDeviceIDCache = allocatorDev.has_value() && compositorDev.has_value();
+ const bool cacheStale = !m_cachedSameGPU ||
+ (useDeviceIDCache ? m_cachedAllocatorDRMDev != allocatorDev || m_cachedCompositorDRMDev != compositorDev :
+ m_cachedAllocatorDRMFD != allocatorFD || m_cachedCompositorDRMFD != compositorFD);
+
+ if (cacheStale) {
+ m_cachedAllocatorDRMDev = allocatorDev;
+ m_cachedCompositorDRMDev = compositorDev;
+ m_cachedAllocatorDRMFD = allocatorFD;
+ m_cachedCompositorDRMFD = compositorFD;
+ m_cachedSameGPU = DRM::sameGpu(allocatorFD, compositorFD);
+ }
+
+ return !*m_cachedSameGPU;
+}
+
bool CMonitor::shouldUseSoftwareCursors() {
static auto PNOHW = CConfigValue<Hyprlang::INT>("cursor:no_hardware_cursors");
static auto PINVISIBLE = CConfigValue<Hyprlang::INT>("cursor:invisible");
diff --git a/src/helpers/Monitor.hpp b/src/helpers/Monitor.hpp
index bd4f043b..12a5de6b 100644
--- a/src/helpers/Monitor.hpp
+++ b/src/helpers/Monitor.hpp
@@ -25,6 +25,7 @@
#include <aquamarine/output/Output.hpp>
#include <aquamarine/allocator/Swapchain.hpp>
#include <hyprutils/os/FileDescriptor.hpp>
+#include <sys/types.h>
#include "../helpers/TransferFunction.hpp"
#include "../config/shared/monitor/MonitorRule.hpp"
@@ -323,6 +324,8 @@ class CMonitor {
bool updateTearing();
uint16_t isDSBlocked(bool full = false);
bool attemptDirectScanout();
+ bool canAttemptDirectScanoutFast() const;
+ bool isMultiGPU();
void setCTM(const Mat3x3& ctm);
void onCursorMovedOnMonitor();
void setDPMS(bool on);
@@ -370,6 +373,12 @@ class CMonitor {
PHLWINDOWREF m_previousFSWindow;
bool m_needsHDRupdate = false;
+ std::optional<dev_t> m_cachedAllocatorDRMDev;
+ std::optional<dev_t> m_cachedCompositorDRMDev;
+ int m_cachedAllocatorDRMFD = -1;
+ int m_cachedCompositorDRMFD = -1;
+ std::optional<bool> m_cachedSameGPU;
+
NColorManagement::PImageDescription m_imageDescription = NColorManagement::CImageDescription::from(NColorManagement::SImageDescription{});
bool m_noShaderCTM = false; // sets drm CTM, restore needed
diff --git a/src/managers/PointerManager.cpp b/src/managers/PointerManager.cpp
index 1122a443..8d3c83f0 100644
--- a/src/managers/PointerManager.cpp
+++ b/src/managers/PointerManager.cpp
@@ -455,7 +455,7 @@ SP<Aquamarine::IBuffer> CPointerManager::renderHWCursorBuffer(SP<CPointerManager
options.length = 2;
options.scanout = true;
options.cursor = true;
- options.multigpu = !DRM::sameGpu(state->monitor->m_output->getBackend()->preferredAllocator()->drmFD(), g_pCompositor->m_drm.fd);
+ options.multigpu = state->monitor->isMultiGPU();
// We do not set the format (unless shm). If it's unset (DRM_FORMAT_INVALID) then the swapchain will pick for us,
// but if it's set, we don't wanna change it.
if (shouldUseCpuBuffer)
diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp
index a9643df0..b6f4acb7 100644
--- a/src/render/Renderer.cpp
+++ b/src/render/Renderer.cpp
@@ -1901,21 +1901,24 @@ void IHyprRenderer::renderMonitor(PHLMONITOR pMonitor, bool commit) {
return;
// tearing and DS first
- bool shouldTear = pMonitor->updateTearing();
+ bool shouldTear = pMonitor->updateTearing();
+ const bool canAttemptDirectScanout = pMonitor->canAttemptDirectScanoutFast();
- if (pMonitor->attemptDirectScanout()) {
- pMonitor->m_directScanoutIsActive = true;
- return;
- } else if (!pMonitor->m_lastScanout.expired() || pMonitor->m_directScanoutIsActive) {
- Log::logger->log(Log::DEBUG, "Left a direct scanout.");
- pMonitor->m_lastScanout.reset();
- pMonitor->m_directScanoutIsActive = false;
+ if (canAttemptDirectScanout) {
+ if (pMonitor->attemptDirectScanout()) {
+ pMonitor->m_directScanoutIsActive = true;
+ return;
+ } else if (!pMonitor->m_lastScanout.expired() || pMonitor->m_directScanoutIsActive) {
+ Log::logger->log(Log::DEBUG, "Left a direct scanout.");
+ pMonitor->m_lastScanout.reset();
+ pMonitor->m_directScanoutIsActive = false;
- // reset DRM format, but only if needed since it might modeset
- if (pMonitor->m_output->state->state().drmFormat != pMonitor->m_prevDrmFormat)
- pMonitor->m_output->state->setFormat(pMonitor->m_prevDrmFormat);
+ // reset DRM format, but only if needed since it might modeset
+ if (pMonitor->m_output->state->state().drmFormat != pMonitor->m_prevDrmFormat)
+ pMonitor->m_output->state->setFormat(pMonitor->m_prevDrmFormat);
- pMonitor->m_drmFormat = pMonitor->m_prevDrmFormat;
+ pMonitor->m_drmFormat = pMonitor->m_prevDrmFormat;
+ }
}
Event::bus()->m_events.render.pre.emit(pMonitor);