From 93a288377a028bdafa4747949417a792150ad617 Mon Sep 17 00:00:00 2001 From: Tom Englund Date: Tue, 14 Apr 2026 16:11:20 +0200 Subject: [PATCH 1/9] fences: move to libdrm functions no need to reinvent the wheel, use libdrm sync_accumulate and move the helpers to drm namespace. --- src/helpers/Drm.cpp | 52 +++++++++++++++++++++++++++++++ src/helpers/Drm.hpp | 8 +++-- src/helpers/sync/SyncReleaser.cpp | 41 +++--------------------- src/protocols/types/DMABuffer.cpp | 39 +++-------------------- 4 files changed, 67 insertions(+), 73 deletions(-) diff --git a/src/helpers/Drm.cpp b/src/helpers/Drm.cpp index 4f3119f83..7398c1f9e 100644 --- a/src/helpers/Drm.cpp +++ b/src/helpers/Drm.cpp @@ -4,8 +4,16 @@ #include #include #include +#include + +#if defined(__linux__) +#include +#endif + #include "Drm.hpp" +using namespace Hyprutils::OS; + namespace { using SDRMNodePair = std::array; @@ -67,3 +75,47 @@ bool DRM::sameGpu(int fd1, int fd2) { return same; } + +int DRM::doIoctl(int fd, unsigned long request, void* arg) { + int ret; + + do { + ret = ioctl(fd, request, arg); + } while (ret == -1 && (errno == EINTR || errno == EAGAIN)); + return ret; +} + +// https://www.kernel.org/doc/html/latest/driver-api/dma-buf.html#c.dma_buf_export_sync_file +// returns a sync file that will be signalled when dmabuf is ready to be read +CFileDescriptor DRM::exportFence(int fd) { +#ifndef __linux__ + return {}; +#endif + +#ifdef DMA_BUF_IOCTL_EXPORT_SYNC_FILE + if (fd < 0) + return {}; + + dma_buf_export_sync_file request{ + .flags = DMA_BUF_SYNC_READ, + .fd = -1, + }; + + if (doIoctl(fd, DMA_BUF_IOCTL_EXPORT_SYNC_FILE, &request) == 0) + return CFileDescriptor{request.fd}; +#endif + return {}; +} + +CFileDescriptor DRM::mergeFence(int fence1, int fence2) { +#ifdef __linux__ + sync_accumulate("merged release fence", &fence1, fence2); + + if (fence2 >= 0) + close(fence2); + + return CFileDescriptor{fence1}; +#else + return {}; +#endif +} \ No newline at end of file diff --git a/src/helpers/Drm.hpp b/src/helpers/Drm.hpp index 755ee0502..3181c5ebf 100644 --- a/src/helpers/Drm.hpp +++ b/src/helpers/Drm.hpp @@ -2,8 +2,12 @@ #include #include +#include namespace DRM { - std::optional devIDFromFD(int fd); - bool sameGpu(int fd1, int fd2); + std::optional devIDFromFD(int fd); + bool sameGpu(int fd1, int fd2); + int doIoctl(int fd, unsigned long request, void* arg); + Hyprutils::OS::CFileDescriptor exportFence(int fd); + Hyprutils::OS::CFileDescriptor mergeFence(int fence1, int fence2); } diff --git a/src/helpers/sync/SyncReleaser.cpp b/src/helpers/sync/SyncReleaser.cpp index fbc585d0b..a8112741a 100644 --- a/src/helpers/sync/SyncReleaser.cpp +++ b/src/helpers/sync/SyncReleaser.cpp @@ -1,21 +1,7 @@ #include "SyncReleaser.hpp" #include "SyncTimeline.hpp" #include "../../render/OpenGL.hpp" -#include - -#if defined(__linux__) -#include -#else -struct sync_merge_data { - char name[32]; - __s32 fd2; - __s32 fence; - __u32 flags; - __u32 pad; -}; -#define SYNC_IOC_MAGIC '>' -#define SYNC_IOC_MERGE _IOWR(SYNC_IOC_MAGIC, 3, struct sync_merge_data) -#endif +#include "../../helpers/Drm.hpp" using namespace Hyprutils::OS; @@ -35,30 +21,11 @@ CSyncReleaser::~CSyncReleaser() { m_timeline->signal(m_point); } -static CFileDescriptor mergeSyncFds(const CFileDescriptor& fd1, const CFileDescriptor& fd2) { - // combines the fences of both sync_fds into a dma_fence_array (https://www.kernel.org/doc/html/latest/driver-api/dma-buf.html#c.dma_fence_array_create) - // with the signal_on_any param set to false, so the new sync_fd will "signal when all fences in the array signal." - - struct sync_merge_data data{ - .name = "merged release fence", - .fd2 = fd2.get(), - .fence = -1, - }; - int err = -1; - do { - err = ioctl(fd1.get(), SYNC_IOC_MERGE, &data); - } while (err == -1 && (errno == EINTR || errno == EAGAIN)); - if (err < 0) - return CFileDescriptor{}; - else - return CFileDescriptor(data.fence); -} - void CSyncReleaser::addSyncFileFd(const Hyprutils::OS::CFileDescriptor& syncFd) { - if (m_fd.isValid()) - m_fd = mergeSyncFds(m_fd, syncFd); - else + if (!m_fd.isValid()) m_fd = syncFd.duplicate(); + else + m_fd = DRM::mergeFence(m_fd.take(), syncFd.duplicate().take()); } void CSyncReleaser::drop() { diff --git a/src/protocols/types/DMABuffer.cpp b/src/protocols/types/DMABuffer.cpp index 37c463387..c9afde78d 100644 --- a/src/protocols/types/DMABuffer.cpp +++ b/src/protocols/types/DMABuffer.cpp @@ -4,12 +4,7 @@ #include "../../render/Renderer.hpp" #include "../../helpers/Format.hpp" #include - -#if defined(__linux__) -#include -#include -#endif -#include +#include "../../helpers/Drm.hpp" using namespace Hyprutils::OS; using namespace Hyprgraphics::Egl; @@ -92,15 +87,6 @@ void CDMABuffer::closeFDs() { m_attrs.planes = 0; } -static int doIoctl(int fd, unsigned long request, void* arg) { - int ret; - - do { - ret = ioctl(fd, request, arg); - } while (ret == -1 && (errno == EINTR || errno == EAGAIN)); - return ret; -} - // https://www.kernel.org/doc/html/latest/driver-api/dma-buf.html#c.dma_buf_export_sync_file // returns a sync file that will be signalled when dmabuf is ready to be read CFileDescriptor CDMABuffer::exportSyncFile() { @@ -124,13 +110,10 @@ CFileDescriptor CDMABuffer::exportSyncFile() { continue; } - dma_buf_export_sync_file request{ - .flags = DMA_BUF_SYNC_READ, - .fd = -1, - }; + auto fence = DRM::exportFence(fd); - if (doIoctl(fd, DMA_BUF_IOCTL_EXPORT_SYNC_FILE, &request) == 0) - syncFds.emplace_back(request.fd); + if (fence.isValid()) + syncFds.emplace_back(std::move(fence)); } if (syncFds.empty()) @@ -143,19 +126,7 @@ CFileDescriptor CDMABuffer::exportSyncFile() { continue; } - const std::string name = "merged release fence"; - struct sync_merge_data data{ - .name = {}, // zero-initialize name[] - .fd2 = fd.get(), - .fence = -1, - }; - - std::ranges::copy_n(name.c_str(), std::min(name.size() + 1, sizeof(data.name)), data.name); - - if (doIoctl(syncFd.get(), SYNC_IOC_MERGE, &data) == 0) - syncFd = CFileDescriptor(data.fence); - else - syncFd = {}; + syncFd = DRM::mergeFence(syncFd.take(), fd.take()); } return syncFd; From c0d658abc6c96b411ab7827c8f1b0f261d611a97 Mon Sep 17 00:00:00 2001 From: Tom Englund Date: Tue, 14 Apr 2026 16:21:57 +0200 Subject: [PATCH 2/9] fences: wait on each fence in turn isntead of merging instead of merging fences, now wait on each of them individually until ready. --- src/protocols/core/Compositor.cpp | 29 ++++++++++++++++++++++++---- src/protocols/types/Buffer.hpp | 32 +++++++++++++++---------------- src/protocols/types/DMABuffer.cpp | 21 ++++---------------- src/protocols/types/DMABuffer.hpp | 2 +- 4 files changed, 46 insertions(+), 38 deletions(-) diff --git a/src/protocols/core/Compositor.cpp b/src/protocols/core/Compositor.cpp index 0200ebd4a..b675b2825 100644 --- a/src/protocols/core/Compositor.cpp +++ b/src/protocols/core/Compositor.cpp @@ -149,8 +149,8 @@ CWLSurfaceResource::CWLSurfaceResource(SP resource_) : m_resource(re m_events.stateCommit.emit(state); if (state->buffer && state->buffer->type() == Aquamarine::BUFFER_TYPE_DMABUF && state->buffer->dmabuf().success && !state->updated.bits.acquire) { - state->buffer->m_syncFd = dc(state->buffer.m_buffer.get())->exportSyncFile(); - if (state->buffer->m_syncFd.isValid()) + state->buffer->m_syncFds = dc(state->buffer.m_buffer.get())->exportSyncFiles(); + if (!state->buffer->m_syncFds.empty()) m_stateQueue.lock(state, LOCK_REASON_FENCE); } @@ -542,9 +542,30 @@ void CWLSurfaceResource::scheduleState(WP state) { } else if (state->buffer && state->buffer->isSynchronous()) { // synchronous (shm) buffers can be read immediately m_stateQueue.unlock(state, LOCK_REASON_FENCE); - } else if (state->buffer && state->buffer->m_syncFd.isValid()) { + } else if (state->buffer && !state->buffer->m_syncFds.empty()) { // async buffer and is dmabuf, then we can wait on implicit fences - g_pEventLoopManager->doOnReadable(std::move(state->buffer->m_syncFd), [state, whenReadable]() { whenReadable(state, LOCK_REASON_FENCE); }); + auto fence = std::ranges::find_if(state->buffer->m_syncFds, [](auto& f) { return !Hyprutils::OS::CFileDescriptor::isReadable(f.get()); }); + if (fence == state->buffer->m_syncFds.end()) + m_stateQueue.unlock(state, LOCK_REASON_FENCE); + else { + //#TODO: if multiple fences are not ready, wait for them.. + auto checkFences = [state, whenReadable](auto&& self) -> void { + if (!state || !state->buffer.m_buffer) + return; + + auto fence = std::ranges::find_if(state->buffer->m_syncFds, [](auto& f) { return !Hyprutils::OS::CFileDescriptor::isReadable(f.get()); }); + + if (fence == state->buffer->m_syncFds.end()) { + // All fences readable + whenReadable(state, LOCK_REASON_FENCE); + return; + } + + g_pEventLoopManager->doOnReadable(fence->duplicate(), [state, whenReadable, self]() mutable { self(self); }); + }; + + checkFences(checkFences); + } } else { // state commit without a buffer. m_stateQueue.tryProcess(); diff --git a/src/protocols/types/Buffer.hpp b/src/protocols/types/Buffer.hpp index 927cd2591..e93240b1e 100644 --- a/src/protocols/types/Buffer.hpp +++ b/src/protocols/types/Buffer.hpp @@ -13,24 +13,24 @@ class CHLBufferReference; class IHLBuffer : public Aquamarine::IBuffer { public: virtual ~IHLBuffer(); - virtual Aquamarine::eBufferCapability caps() = 0; - virtual Aquamarine::eBufferType type() = 0; - virtual void update(const CRegion& damage) = 0; - virtual bool isSynchronous() = 0; // whether the updates to this buffer are synchronous, aka happen over cpu - virtual bool good() = 0; - virtual void sendRelease(); - virtual void lock(); - virtual void unlock(); - virtual bool locked(); + virtual Aquamarine::eBufferCapability caps() = 0; + virtual Aquamarine::eBufferType type() = 0; + virtual void update(const CRegion& damage) = 0; + virtual bool isSynchronous() = 0; // whether the updates to this buffer are synchronous, aka happen over cpu + virtual bool good() = 0; + virtual void sendRelease(); + virtual void lock(); + virtual void unlock(); + virtual bool locked(); - void onBackendRelease(const std::function& fn); - void addReleasePoint(CDRMSyncPointState& point); + void onBackendRelease(const std::function& fn); + void addReleasePoint(CDRMSyncPointState& point); - SP m_texture; - bool m_opaque = false; - SP m_resource; - std::vector> m_syncReleasers; - Hyprutils::OS::CFileDescriptor m_syncFd; + SP m_texture; + bool m_opaque = false; + SP m_resource; + std::vector> m_syncReleasers; + std::vector m_syncFds; struct { CHyprSignalListener backendRelease; diff --git a/src/protocols/types/DMABuffer.cpp b/src/protocols/types/DMABuffer.cpp index c9afde78d..a3b976104 100644 --- a/src/protocols/types/DMABuffer.cpp +++ b/src/protocols/types/DMABuffer.cpp @@ -89,7 +89,7 @@ void CDMABuffer::closeFDs() { // https://www.kernel.org/doc/html/latest/driver-api/dma-buf.html#c.dma_buf_export_sync_file // returns a sync file that will be signalled when dmabuf is ready to be read -CFileDescriptor CDMABuffer::exportSyncFile() { +std::vector CDMABuffer::exportSyncFiles() { if (!good()) return {}; @@ -99,7 +99,8 @@ CFileDescriptor CDMABuffer::exportSyncFile() { std::vector syncFds; syncFds.reserve(m_attrs.fds.size()); - for (const auto& fd : m_attrs.fds) { + for (auto i = 0ul; i < m_attrs.fds.size(); i++) { + auto fd = m_attrs.fds[i]; if (fd == -1) continue; @@ -111,24 +112,10 @@ CFileDescriptor CDMABuffer::exportSyncFile() { } auto fence = DRM::exportFence(fd); - if (fence.isValid()) syncFds.emplace_back(std::move(fence)); } - if (syncFds.empty()) - return {}; - - CFileDescriptor syncFd; - for (auto& fd : syncFds) { - if (!syncFd.isValid()) { - syncFd = std::move(fd); - continue; - } - - syncFd = DRM::mergeFence(syncFd.take(), fd.take()); - } - - return syncFd; + return syncFds; #endif } diff --git a/src/protocols/types/DMABuffer.hpp b/src/protocols/types/DMABuffer.hpp index 5a0064da5..cba157ab1 100644 --- a/src/protocols/types/DMABuffer.hpp +++ b/src/protocols/types/DMABuffer.hpp @@ -17,7 +17,7 @@ class CDMABuffer : public IHLBuffer { virtual void endDataPtr(); bool good(); void closeFDs(); - Hyprutils::OS::CFileDescriptor exportSyncFile(); + std::vector exportSyncFiles(); bool m_success = false; private: From f62189c7958a136bc157ba145dbe63508b50d954 Mon Sep 17 00:00:00 2001 From: Tom Englund Date: Tue, 14 Apr 2026 18:08:51 +0200 Subject: [PATCH 3/9] deadline: set deadline on main buffer fence attempt to calculate an estimated vblank period. and set deadline for it. to do that we need to get the fence from the buffer fd if there is one. the idea here is this tells the kernel when a buffer should be done rendering, which allows it to for example increase GPU clocks and or schedule it earlier in order to hopefully hit our deadlines. --- src/helpers/Drm.cpp | 19 ++++++- src/helpers/Drm.hpp | 2 + src/helpers/Monitor.cpp | 3 ++ src/helpers/Monitor.hpp | 101 +++++++++++++++++++------------------- src/render/GLRenderer.cpp | 8 +-- src/render/Renderer.cpp | 16 ++++-- src/render/Renderer.hpp | 6 ++- 7 files changed, 96 insertions(+), 59 deletions(-) diff --git a/src/helpers/Drm.cpp b/src/helpers/Drm.cpp index 7398c1f9e..38a854367 100644 --- a/src/helpers/Drm.cpp +++ b/src/helpers/Drm.cpp @@ -4,12 +4,15 @@ #include #include #include -#include #if defined(__linux__) #include +#include #endif +#include +#include + #include "Drm.hpp" using namespace Hyprutils::OS; @@ -118,4 +121,18 @@ CFileDescriptor DRM::mergeFence(int fence1, int fence2) { #else return {}; #endif +} + +void DRM::setDeadline(const Time::steady_tp& deadline, const Hyprutils::OS::CFileDescriptor& fence) { +#ifdef SYNC_IOC_SET_DEADLINE + if (!fence.isValid()) + return; + + sync_set_deadline args{ + .deadline_ns = uint64_t(deadline.time_since_epoch().count()), + .pad = 0, + }; + + doIoctl(fence.get(), SYNC_IOC_SET_DEADLINE, &args); +#endif } \ No newline at end of file diff --git a/src/helpers/Drm.hpp b/src/helpers/Drm.hpp index 3181c5ebf..5f5ecc90f 100644 --- a/src/helpers/Drm.hpp +++ b/src/helpers/Drm.hpp @@ -3,6 +3,7 @@ #include #include #include +#include "time/Time.hpp" namespace DRM { std::optional devIDFromFD(int fd); @@ -10,4 +11,5 @@ namespace DRM { int doIoctl(int fd, unsigned long request, void* arg); Hyprutils::OS::CFileDescriptor exportFence(int fd); Hyprutils::OS::CFileDescriptor mergeFence(int fence1, int fence2); + void setDeadline(const Time::steady_tp& deadline, const Hyprutils::OS::CFileDescriptor& fence); } diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp index 37606d4f6..fdb52b3ca 100644 --- a/src/helpers/Monitor.cpp +++ b/src/helpers/Monitor.cpp @@ -158,6 +158,9 @@ void CMonitor::onConnect(bool noRule) { }); } + auto minVBlankInterval = std::chrono::duration_cast(std::chrono::duration(1.0 / m_refreshRate)); + m_estimatedNextVblank = (ts ? Time::fromTimespec(ts) : Time::steadyNow()) + minVBlankInterval; + m_frameScheduler->onPresented(); m_events.presented.emit(); diff --git a/src/helpers/Monitor.hpp b/src/helpers/Monitor.hpp index 59a64237e..3ae1877b9 100644 --- a/src/helpers/Monitor.hpp +++ b/src/helpers/Monitor.hpp @@ -106,71 +106,72 @@ class CMonitor { CMonitor(SP output); ~CMonitor(); - Vector2D m_position = Vector2D(-1, -1); // means unset - Vector2D m_xwaylandPosition = Vector2D(-1, -1); // means unset - Config::eAutoDirs m_autoDir = Config::DIR_AUTO_NONE; - Vector2D m_size = Vector2D(0, 0); - Vector2D m_pixelSize = Vector2D(0, 0); - Vector2D m_transformedSize = Vector2D(0, 0); + Vector2D m_position = Vector2D(-1, -1); // means unset + Vector2D m_xwaylandPosition = Vector2D(-1, -1); // means unset + Config::eAutoDirs m_autoDir = Config::DIR_AUTO_NONE; + Vector2D m_size = Vector2D(0, 0); + Vector2D m_pixelSize = Vector2D(0, 0); + Vector2D m_transformedSize = Vector2D(0, 0); - MONITORID m_id = MONITOR_INVALID; - PHLWORKSPACE m_activeWorkspace = nullptr; - PHLWORKSPACE m_activeSpecialWorkspace = nullptr; - float m_setScale = 1; // scale set by cfg - float m_scale = 1; // real scale + MONITORID m_id = MONITOR_INVALID; + PHLWORKSPACE m_activeWorkspace = nullptr; + PHLWORKSPACE m_activeSpecialWorkspace = nullptr; + float m_setScale = 1; // scale set by cfg + float m_scale = 1; // real scale - std::string m_name = ""; - std::string m_description = ""; - std::string m_shortDescription = ""; + std::string m_name = ""; + std::string m_description = ""; + std::string m_shortDescription = ""; - drmModeModeInfo m_customDrmMode = {}; + drmModeModeInfo m_customDrmMode = {}; - Desktop::CReservedArea m_reservedArea; + Desktop::CReservedArea m_reservedArea; - CMonitorState m_state; - CDamageRing m_damage; + CMonitorState m_state; + CDamageRing m_damage; - SP m_output; - float m_refreshRate = 60; // Hz - int m_forceFullFrames = 0; - bool m_scheduledRecalc = false; - wl_output_transform m_transform = WL_OUTPUT_TRANSFORM_NORMAL; - float m_xwaylandScale = 1.f; + SP m_output; + float m_refreshRate = 60; // Hz + int m_forceFullFrames = 0; + bool m_scheduledRecalc = false; + wl_output_transform m_transform = WL_OUTPUT_TRANSFORM_NORMAL; + float m_xwaylandScale = 1.f; - std::optional m_forceSize; - SP m_currentMode; - SP m_cursorSwapchain; - uint32_t m_drmFormat = DRM_FORMAT_INVALID; - uint32_t m_prevDrmFormat = DRM_FORMAT_INVALID; + std::optional m_forceSize; + SP m_currentMode; + SP m_cursorSwapchain; + uint32_t m_drmFormat = DRM_FORMAT_INVALID; + uint32_t m_prevDrmFormat = DRM_FORMAT_INVALID; - CMonitorZoomController m_zoomController; + CMonitorZoomController m_zoomController; - bool m_dpmsStatus = true; - bool m_vrrActive = false; // this can be TRUE even if VRR is not active in the case that this display does not support it. - bool m_enabled10bit = false; // as above, this can be TRUE even if 10 bit failed. - NCMType::eCMType m_cmType = NCMType::CM_SRGB; - NTransferFunction::eTF m_sdrEotf = NTransferFunction::TF_DEFAULT; - float m_sdrSaturation = 1.0f; - float m_sdrBrightness = 1.0f; - float m_sdrMinLuminance = 0.2f; - int m_sdrMaxLuminance = 80; - bool m_createdByUser = false; - bool m_isUnsafeFallback = false; + bool m_dpmsStatus = true; + bool m_vrrActive = false; // this can be TRUE even if VRR is not active in the case that this display does not support it. + bool m_enabled10bit = false; // as above, this can be TRUE even if 10 bit failed. + NCMType::eCMType m_cmType = NCMType::CM_SRGB; + NTransferFunction::eTF m_sdrEotf = NTransferFunction::TF_DEFAULT; + float m_sdrSaturation = 1.0f; + float m_sdrBrightness = 1.0f; + float m_sdrMinLuminance = 0.2f; + int m_sdrMaxLuminance = 80; + bool m_createdByUser = false; + bool m_isUnsafeFallback = false; - SP m_dpmsRetryTimer; + SP m_dpmsRetryTimer; - bool m_pendingFrame = false; // if we schedule a frame during rendering, reschedule it after - bool m_renderingActive = false; + bool m_pendingFrame = false; // if we schedule a frame during rendering, reschedule it after + bool m_renderingActive = false; - bool m_ratsScheduled = false; - CTimer m_lastPresentationTimer; + bool m_ratsScheduled = false; + CTimer m_lastPresentationTimer; + std::optional m_estimatedNextVblank; - bool m_isBeingLeased = false; + bool m_isBeingLeased = false; - Config::CMonitorRule m_activeMonitorRule; + Config::CMonitorRule m_activeMonitorRule; - SP m_splash; - SP m_background; + SP m_splash; + SP m_background; // explicit sync Hyprutils::OS::CFileDescriptor m_inFence; // TODO: remove when aq uses CFileDescriptor diff --git a/src/render/GLRenderer.cpp b/src/render/GLRenderer.cpp index 35aca7db8..13e80baa1 100644 --- a/src/render/GLRenderer.cpp +++ b/src/render/GLRenderer.cpp @@ -11,6 +11,7 @@ #include "../protocols/core/Compositor.hpp" #include "../debug/Overlay.hpp" #include "../helpers/Monitor.hpp" +#include "../helpers/Drm.hpp" #include "pass/TexPassElement.hpp" #include "pass/SurfacePassElement.hpp" #include "../debug/log/Logger.hpp" @@ -49,7 +50,7 @@ void CHyprGLRenderer::initRender() { bool CHyprGLRenderer::initRenderBuffer(SP buffer, uint32_t fmt) { try { - m_currentRenderbuffer = getOrCreateRenderbuffer(m_currentBuffer, fmt); + m_currentRenderbuffer = getOrCreateRenderbuffer(m_currentBuffer.buffer, fmt); } catch (std::exception& e) { Log::logger->log(Log::ERR, "getOrCreateRenderbuffer failed for {}", NFormatUtils::drmFormatName(fmt)); return false; @@ -72,6 +73,7 @@ bool CHyprGLRenderer::beginFullFakeRenderInternal(PHLMONITOR pMonitor, CRegion& bool CHyprGLRenderer::beginRenderInternal(PHLMONITOR pMonitor, CRegion& damage, bool simple) { + m_currentBuffer.fence = DRM::exportFence(m_currentBuffer.buffer->dmabuf().fds[0]); m_currentRenderbuffer->bind(); if (simple) g_pHyprOpenGL->beginSimple(pMonitor, damage, m_currentRenderbuffer); @@ -91,7 +93,7 @@ void CHyprGLRenderer::endRender(const std::function& renderingDoneCallba if (m_currentRenderbuffer) m_currentRenderbuffer->unbind(); m_currentRenderbuffer = nullptr; - m_currentBuffer = nullptr; + m_currentBuffer = {}; }); if (m_renderMode != RENDER_MODE_TO_BUFFER_READ_ONLY) @@ -106,7 +108,7 @@ void CHyprGLRenderer::endRender(const std::function& renderingDoneCallba return; if (m_renderMode == RENDER_MODE_NORMAL) - PMONITOR->m_output->state->setBuffer(m_currentBuffer); + PMONITOR->m_output->state->setBuffer(m_currentBuffer.buffer); if (!explicitSyncSupported()) { Log::logger->log(Log::TRACE, "renderer: Explicit sync unsupported, falling back to implicit in endRender"); diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index fa1a8a061..8fe79dd77 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -35,6 +35,7 @@ #include "../helpers/CursorShapes.hpp" #include "../helpers/MainLoopExecutor.hpp" #include "../helpers/Monitor.hpp" +#include "../helpers/Drm.hpp" #include "macros.hpp" #include "../managers/screenshare/ScreenshareManager.hpp" #include "pass/TexPassElement.hpp" @@ -1654,17 +1655,17 @@ bool IHyprRenderer::beginRender(PHLMONITOR pMonitor, CRegion& damage, eRenderMod int bufferAge = 0; if (!buffer) { - m_currentBuffer = pMonitor->m_output->swapchain->next(&bufferAge); - if (!m_currentBuffer) { + m_currentBuffer.buffer = pMonitor->m_output->swapchain->next(&bufferAge); + if (!m_currentBuffer.buffer) { Log::logger->log(Log::ERR, "Failed to acquire swapchain buffer for {}", pMonitor->m_name); return false; } } else - m_currentBuffer = buffer; + m_currentBuffer.buffer = buffer; initRender(); - if (!initRenderBuffer(m_currentBuffer, pMonitor->m_output->state->state().drmFormat)) { + if (!initRenderBuffer(m_currentBuffer.buffer, pMonitor->m_output->state->state().drmFormat)) { Log::logger->log(Log::ERR, "failed to start a render pass for output {}, no RBO could be obtained", pMonitor->m_name); return false; } @@ -2348,6 +2349,13 @@ void IHyprRenderer::handleFullscreenSettings(PHLMONITOR pMonitor) { bool IHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor) { handleFullscreenSettings(pMonitor); + auto deadline = + pMonitor->m_vrrActive || pMonitor->m_tearingState.activelyTearing || !pMonitor->m_estimatedNextVblank.has_value() ? Time::steadyNow() : pMonitor->m_estimatedNextVblank; + if (deadline) { + DRM::setDeadline(deadline.value(), m_currentBuffer.fence); + pMonitor->m_estimatedNextVblank = std::nullopt; + } + bool ok = pMonitor->m_state.commit(); if (!ok) { if (pMonitor->m_inFence.isValid()) { diff --git a/src/render/Renderer.hpp b/src/render/Renderer.hpp index f52b9e3a0..2a0579184 100644 --- a/src/render/Renderer.hpp +++ b/src/render/Renderer.hpp @@ -271,13 +271,17 @@ namespace Render { bool m_cursorHidden = false; bool m_cursorHiddenByCondition = false; bool m_cursorHasSurface = false; - SP m_currentBuffer = nullptr; eRenderMode m_renderMode = RENDER_MODE_NORMAL; bool m_nvidia = false; bool m_intel = false; bool m_software = false; bool m_mgpu = false; + struct SBuffer { + SP buffer = nullptr; + Hyprutils::OS::CFileDescriptor fence; + } m_currentBuffer; + struct { bool hiddenOnTouch = false; bool hiddenOnTablet = false; From fba2e931a41ec15e5a0dd401c332c72452abe69a Mon Sep 17 00:00:00 2001 From: Tom Englund Date: Tue, 14 Apr 2026 18:12:55 +0200 Subject: [PATCH 4/9] fences: set deadline on client buffers attempt to set deadline on acquire fences and dmabuf fd fences. --- src/protocols/core/Compositor.cpp | 49 ++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/src/protocols/core/Compositor.cpp b/src/protocols/core/Compositor.cpp index b675b2825..aae49e173 100644 --- a/src/protocols/core/Compositor.cpp +++ b/src/protocols/core/Compositor.cpp @@ -8,6 +8,7 @@ #include "Subcompositor.hpp" #include "../Viewporter.hpp" #include "../../helpers/Monitor.hpp" +#include "../../helpers/Drm.hpp" #include "../PresentationTime.hpp" #include "../DRMSyncobj.hpp" #include "../types/DMABuffer.hpp" @@ -148,10 +149,50 @@ CWLSurfaceResource::CWLSurfaceResource(SP resource_) : m_resource(re // fifo and fences first m_events.stateCommit.emit(state); - if (state->buffer && state->buffer->type() == Aquamarine::BUFFER_TYPE_DMABUF && state->buffer->dmabuf().success && !state->updated.bits.acquire) { - state->buffer->m_syncFds = dc(state->buffer.m_buffer.get())->exportSyncFiles(); - if (!state->buffer->m_syncFds.empty()) - m_stateQueue.lock(state, LOCK_REASON_FENCE); + if (state->buffer && state->buffer->type() == Aquamarine::BUFFER_TYPE_DMABUF && state->buffer->dmabuf().success) { + Time::steady_tp deadline = Time::steadyNow(); + if (g_pHyprRenderer->explicitSyncSupported()) { + if (m_enteredOutputs.empty() && m_hlSurface) { + for (const auto& m : g_pCompositor->m_monitors) { + if (!m || !m->m_enabled) + continue; + + auto box = m_hlSurface->getSurfaceBoxGlobal(); + if (box && !box->intersection({m->m_position, m->m_size}).empty()) { + if (m->m_tearingState.activelyTearing || m->m_vrrActive || !m->m_estimatedNextVblank || !m->m_estimatedNextVblank.has_value()) + continue; // dont estimate vblank on tearing or vrr. + + deadline = m->m_estimatedNextVblank.value(); + break; + } + } + } else { + for (const auto& m : m_enteredOutputs) { + if (!m) + continue; + + if (m->m_tearingState.activelyTearing || m->m_vrrActive || !m->m_estimatedNextVblank || !m->m_estimatedNextVblank.has_value()) + continue; // dont estimate vblank on tearing or vrr + + deadline = m->m_estimatedNextVblank.value(); + break; + } + } + + if (state->updated.bits.acquire) { + DRM::setDeadline(deadline, state->acquire.exportAsFD()); + } else { + state->buffer->m_syncFds = dc(state->buffer.m_buffer.get())->exportSyncFiles(); + if (!state->buffer->m_syncFds.empty()) { + for (auto& fence : state->buffer->m_syncFds) { + if (fence.isValid()) + DRM::setDeadline(deadline, fence); + } + + m_stateQueue.lock(state, LOCK_REASON_FENCE); + } + } + } } // now for timer. From 395896fe88b55f87be6380c2df0bc67cfe27cb08 Mon Sep 17 00:00:00 2001 From: Tom Englund Date: Sun, 19 Apr 2026 21:14:48 +0200 Subject: [PATCH 5/9] deadline: put deadline behind config options put deadline behind config options to allow one to set it only on main buffer, or client buffers. default them off. --- src/config/values/ConfigValues.cpp | 2 ++ src/protocols/core/Compositor.cpp | 51 ++++++++++++++++-------------- src/render/Renderer.cpp | 14 +++++--- 3 files changed, 39 insertions(+), 28 deletions(-) diff --git a/src/config/values/ConfigValues.cpp b/src/config/values/ConfigValues.cpp index 943c9c832..443ff07fe 100644 --- a/src/config/values/ConfigValues.cpp +++ b/src/config/values/ConfigValues.cpp @@ -673,6 +673,8 @@ std::vector> Values::getConfigValues() { */ MS("experimental:wp_cm_1_2", "Allow wp-cm-v1 version 2", false), + MS("experimental:deadline_client_buffer", "set deadline on client buffers", false), + MS("experimental:deadline_main_buffer", "set deadline on main buffers", false), /* * quirks: diff --git a/src/protocols/core/Compositor.cpp b/src/protocols/core/Compositor.cpp index aae49e173..a2c660752 100644 --- a/src/protocols/core/Compositor.cpp +++ b/src/protocols/core/Compositor.cpp @@ -150,43 +150,48 @@ CWLSurfaceResource::CWLSurfaceResource(SP resource_) : m_resource(re m_events.stateCommit.emit(state); if (state->buffer && state->buffer->type() == Aquamarine::BUFFER_TYPE_DMABUF && state->buffer->dmabuf().success) { - Time::steady_tp deadline = Time::steadyNow(); + static const auto DEADLINE = CConfigValue("experimental:deadline_client_buffer"); + Time::steady_tp deadline = Time::steadyNow(); if (g_pHyprRenderer->explicitSyncSupported()) { - if (m_enteredOutputs.empty() && m_hlSurface) { - for (const auto& m : g_pCompositor->m_monitors) { - if (!m || !m->m_enabled) - continue; + if (*DEADLINE) { + if (m_enteredOutputs.empty() && m_hlSurface) { + for (const auto& m : g_pCompositor->m_monitors) { + if (!m || !m->m_enabled) + continue; + + auto box = m_hlSurface->getSurfaceBoxGlobal(); + if (box && !box->intersection({m->m_position, m->m_size}).empty()) { + if (m->m_tearingState.activelyTearing || m->m_vrrActive || !m->m_estimatedNextVblank || !m->m_estimatedNextVblank.has_value()) + continue; // dont estimate vblank on tearing or vrr. + + deadline = m->m_estimatedNextVblank.value(); + break; + } + } + } else { + for (const auto& m : m_enteredOutputs) { + if (!m) + continue; - auto box = m_hlSurface->getSurfaceBoxGlobal(); - if (box && !box->intersection({m->m_position, m->m_size}).empty()) { if (m->m_tearingState.activelyTearing || m->m_vrrActive || !m->m_estimatedNextVblank || !m->m_estimatedNextVblank.has_value()) - continue; // dont estimate vblank on tearing or vrr. + continue; // dont estimate vblank on tearing or vrr deadline = m->m_estimatedNextVblank.value(); break; } } - } else { - for (const auto& m : m_enteredOutputs) { - if (!m) - continue; - - if (m->m_tearingState.activelyTearing || m->m_vrrActive || !m->m_estimatedNextVblank || !m->m_estimatedNextVblank.has_value()) - continue; // dont estimate vblank on tearing or vrr - - deadline = m->m_estimatedNextVblank.value(); - break; - } } - if (state->updated.bits.acquire) { + if (state->updated.bits.acquire && *DEADLINE) { DRM::setDeadline(deadline, state->acquire.exportAsFD()); } else { state->buffer->m_syncFds = dc(state->buffer.m_buffer.get())->exportSyncFiles(); if (!state->buffer->m_syncFds.empty()) { - for (auto& fence : state->buffer->m_syncFds) { - if (fence.isValid()) - DRM::setDeadline(deadline, fence); + if (*DEADLINE) { + for (auto& fence : state->buffer->m_syncFds) { + if (fence.isValid()) + DRM::setDeadline(deadline, fence); + } } m_stateQueue.lock(state, LOCK_REASON_FENCE); diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index 8fe79dd77..9e7e4cf15 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -2349,11 +2349,15 @@ void IHyprRenderer::handleFullscreenSettings(PHLMONITOR pMonitor) { bool IHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor) { handleFullscreenSettings(pMonitor); - auto deadline = - pMonitor->m_vrrActive || pMonitor->m_tearingState.activelyTearing || !pMonitor->m_estimatedNextVblank.has_value() ? Time::steadyNow() : pMonitor->m_estimatedNextVblank; - if (deadline) { - DRM::setDeadline(deadline.value(), m_currentBuffer.fence); - pMonitor->m_estimatedNextVblank = std::nullopt; + static const auto DEADLINE = CConfigValue("experimental:deadline_main_buffer"); + + if (*DEADLINE) { + auto deadline = + pMonitor->m_vrrActive || pMonitor->m_tearingState.activelyTearing || !pMonitor->m_estimatedNextVblank.has_value() ? Time::steadyNow() : pMonitor->m_estimatedNextVblank; + if (deadline) { + DRM::setDeadline(deadline.value(), m_currentBuffer.fence); + pMonitor->m_estimatedNextVblank = std::nullopt; + } } bool ok = pMonitor->m_state.commit(); From 84ecfda38c62e4fc7e86870c6ae1dc8396c2fe06 Mon Sep 17 00:00:00 2001 From: Tom Englund Date: Wed, 22 Apr 2026 10:56:09 +0200 Subject: [PATCH 6/9] deadline: cast deadline, add 0.1 ms to deadline add a 0.1 ms to the now() time, to schedule with high priority meaning it wont potentially hit already passed timestamps or even reach 0 deadlines. as uapi.h comments. "deadline_ns = (t.tv_sec * 1000000000L) + t.tv_nsec + ns_until_deadline" --- src/helpers/Drm.cpp | 2 +- src/protocols/core/Compositor.cpp | 32 ++----------------------------- src/render/Renderer.cpp | 5 +++-- 3 files changed, 6 insertions(+), 33 deletions(-) diff --git a/src/helpers/Drm.cpp b/src/helpers/Drm.cpp index 38a854367..7903b24c1 100644 --- a/src/helpers/Drm.cpp +++ b/src/helpers/Drm.cpp @@ -129,7 +129,7 @@ void DRM::setDeadline(const Time::steady_tp& deadline, const Hyprutils::OS::CFil return; sync_set_deadline args{ - .deadline_ns = uint64_t(deadline.time_since_epoch().count()), + .deadline_ns = uint64_t(duration_cast(deadline.time_since_epoch()).count()), .pad = 0, }; diff --git a/src/protocols/core/Compositor.cpp b/src/protocols/core/Compositor.cpp index a2c660752..fc96c3e30 100644 --- a/src/protocols/core/Compositor.cpp +++ b/src/protocols/core/Compositor.cpp @@ -151,43 +151,15 @@ CWLSurfaceResource::CWLSurfaceResource(SP resource_) : m_resource(re if (state->buffer && state->buffer->type() == Aquamarine::BUFFER_TYPE_DMABUF && state->buffer->dmabuf().success) { static const auto DEADLINE = CConfigValue("experimental:deadline_client_buffer"); - Time::steady_tp deadline = Time::steadyNow(); if (g_pHyprRenderer->explicitSyncSupported()) { - if (*DEADLINE) { - if (m_enteredOutputs.empty() && m_hlSurface) { - for (const auto& m : g_pCompositor->m_monitors) { - if (!m || !m->m_enabled) - continue; - - auto box = m_hlSurface->getSurfaceBoxGlobal(); - if (box && !box->intersection({m->m_position, m->m_size}).empty()) { - if (m->m_tearingState.activelyTearing || m->m_vrrActive || !m->m_estimatedNextVblank || !m->m_estimatedNextVblank.has_value()) - continue; // dont estimate vblank on tearing or vrr. - - deadline = m->m_estimatedNextVblank.value(); - break; - } - } - } else { - for (const auto& m : m_enteredOutputs) { - if (!m) - continue; - - if (m->m_tearingState.activelyTearing || m->m_vrrActive || !m->m_estimatedNextVblank || !m->m_estimatedNextVblank.has_value()) - continue; // dont estimate vblank on tearing or vrr - - deadline = m->m_estimatedNextVblank.value(); - break; - } - } - } - if (state->updated.bits.acquire && *DEADLINE) { + Time::steady_tp deadline = Time::steadyNow() + std::chrono::microseconds(100); DRM::setDeadline(deadline, state->acquire.exportAsFD()); } else { state->buffer->m_syncFds = dc(state->buffer.m_buffer.get())->exportSyncFiles(); if (!state->buffer->m_syncFds.empty()) { if (*DEADLINE) { + Time::steady_tp deadline = Time::steadyNow() + std::chrono::microseconds(100); for (auto& fence : state->buffer->m_syncFds) { if (fence.isValid()) DRM::setDeadline(deadline, fence); diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index 9e7e4cf15..f64cc5128 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -2352,8 +2352,9 @@ bool IHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor) { static const auto DEADLINE = CConfigValue("experimental:deadline_main_buffer"); if (*DEADLINE) { - auto deadline = - pMonitor->m_vrrActive || pMonitor->m_tearingState.activelyTearing || !pMonitor->m_estimatedNextVblank.has_value() ? Time::steadyNow() : pMonitor->m_estimatedNextVblank; + auto deadline = pMonitor->m_vrrActive || pMonitor->m_tearingState.activelyTearing || !pMonitor->m_estimatedNextVblank.has_value() ? + Time::steadyNow() + std::chrono::microseconds(100) : + pMonitor->m_estimatedNextVblank; if (deadline) { DRM::setDeadline(deadline.value(), m_currentBuffer.fence); pMonitor->m_estimatedNextVblank = std::nullopt; From f3dc9760de2f50ce8a6d0e66c66c3fd0bd30e331 Mon Sep 17 00:00:00 2001 From: Tom Englund Date: Tue, 28 Apr 2026 18:08:37 +0200 Subject: [PATCH 7/9] deadline: set mainbuffer high prio on multigpu in multigpu scenarios its going to blit later, which means it needs to get done asap, so the other gpu can begin its work. --- src/render/Renderer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index f64cc5128..0ecfd8d83 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -2352,7 +2352,7 @@ bool IHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor) { static const auto DEADLINE = CConfigValue("experimental:deadline_main_buffer"); if (*DEADLINE) { - auto deadline = pMonitor->m_vrrActive || pMonitor->m_tearingState.activelyTearing || !pMonitor->m_estimatedNextVblank.has_value() ? + auto deadline = pMonitor->isMultiGPU() || pMonitor->m_vrrActive || pMonitor->m_tearingState.activelyTearing || !pMonitor->m_estimatedNextVblank.has_value() ? Time::steadyNow() + std::chrono::microseconds(100) : pMonitor->m_estimatedNextVblank; if (deadline) { From aa09740100943eec1a93ff4ad01faf279434558d Mon Sep 17 00:00:00 2001 From: Tom Englund Date: Wed, 29 Apr 2026 08:05:37 +0200 Subject: [PATCH 8/9] deadline: only deadline clients that are solitary deadlining clients is a bit tricky, multiple surfaces tiled will be fighting over priority and just slow things down, lets only do it on solitary clients. --- src/protocols/core/Compositor.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/protocols/core/Compositor.cpp b/src/protocols/core/Compositor.cpp index fc96c3e30..322141450 100644 --- a/src/protocols/core/Compositor.cpp +++ b/src/protocols/core/Compositor.cpp @@ -151,14 +151,15 @@ CWLSurfaceResource::CWLSurfaceResource(SP resource_) : m_resource(re if (state->buffer && state->buffer->type() == Aquamarine::BUFFER_TYPE_DMABUF && state->buffer->dmabuf().success) { static const auto DEADLINE = CConfigValue("experimental:deadline_client_buffer"); + auto const WINDOW = m_hlSurface ? Desktop::View::CWindow::fromView(m_hlSurface->view()) : nullptr; if (g_pHyprRenderer->explicitSyncSupported()) { - if (state->updated.bits.acquire && *DEADLINE) { + if (state->updated.bits.acquire && *DEADLINE && WINDOW && WINDOW->m_monitor && WINDOW->m_monitor->m_solitaryClient == WINDOW) { Time::steady_tp deadline = Time::steadyNow() + std::chrono::microseconds(100); DRM::setDeadline(deadline, state->acquire.exportAsFD()); } else { state->buffer->m_syncFds = dc(state->buffer.m_buffer.get())->exportSyncFiles(); if (!state->buffer->m_syncFds.empty()) { - if (*DEADLINE) { + if (*DEADLINE && WINDOW && WINDOW->m_monitor && WINDOW->m_monitor->m_solitaryClient == WINDOW) { Time::steady_tp deadline = Time::steadyNow() + std::chrono::microseconds(100); for (auto& fence : state->buffer->m_syncFds) { if (fence.isValid()) From 2991eb79348cdf606dd64686ad79ea9339102cd8 Mon Sep 17 00:00:00 2001 From: Tom Englund Date: Wed, 29 Apr 2026 16:23:23 +0200 Subject: [PATCH 9/9] deadline: account for vrr, vfr and tearing. account for vrr, vfr, and tearing. --- meta/hl.meta.lua | 4 ++++ src/helpers/Monitor.cpp | 7 +++++-- src/render/GLRenderer.cpp | 8 +++----- src/render/Renderer.cpp | 22 +++++++++++----------- src/render/Renderer.hpp | 6 +----- 5 files changed, 24 insertions(+), 23 deletions(-) diff --git a/meta/hl.meta.lua b/meta/hl.meta.lua index 4e31d4a34..b78ef4a66 100644 --- a/meta/hl.meta.lua +++ b/meta/hl.meta.lua @@ -148,6 +148,8 @@ ---| "ecosystem.enforce_permissions" ---| "ecosystem.no_donation_nag" ---| "ecosystem.no_update_news" +---| "experimental.deadline_client_buffer" +---| "experimental.deadline_main_buffer" ---| "experimental.wp_cm_1_2" ---| "general.allow_tearing" ---| "general.border_size" @@ -957,6 +959,8 @@ hl = {} ---@field ['ecosystem.enforce_permissions'] boolean ---@field ['ecosystem.no_donation_nag'] boolean ---@field ['ecosystem.no_update_news'] boolean +---@field ['experimental.deadline_client_buffer'] boolean +---@field ['experimental.deadline_main_buffer'] boolean ---@field ['experimental.wp_cm_1_2'] boolean ---@field ['general.allow_tearing'] boolean ---@field ['general.border_size'] integer|boolean diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp index fdb52b3ca..678b3e4eb 100644 --- a/src/helpers/Monitor.cpp +++ b/src/helpers/Monitor.cpp @@ -158,8 +158,11 @@ void CMonitor::onConnect(bool noRule) { }); } - auto minVBlankInterval = std::chrono::duration_cast(std::chrono::duration(1.0 / m_refreshRate)); - m_estimatedNextVblank = (ts ? Time::fromTimespec(ts) : Time::steadyNow()) + minVBlankInterval; + if (m_pendingFrame) { // if no pending frame, VFR etc we cant estimate this. + auto minVBlankInterval = std::chrono::duration_cast(std::chrono::duration(1.0 / m_refreshRate)); + m_estimatedNextVblank = (ts ? Time::fromTimespec(ts) + minVBlankInterval : Time::steadyNow()) + minVBlankInterval; + } else + m_estimatedNextVblank = std::nullopt; m_frameScheduler->onPresented(); diff --git a/src/render/GLRenderer.cpp b/src/render/GLRenderer.cpp index 13e80baa1..1e1640db6 100644 --- a/src/render/GLRenderer.cpp +++ b/src/render/GLRenderer.cpp @@ -50,7 +50,7 @@ void CHyprGLRenderer::initRender() { bool CHyprGLRenderer::initRenderBuffer(SP buffer, uint32_t fmt) { try { - m_currentRenderbuffer = getOrCreateRenderbuffer(m_currentBuffer.buffer, fmt); + m_currentRenderbuffer = getOrCreateRenderbuffer(m_currentBuffer, fmt); } catch (std::exception& e) { Log::logger->log(Log::ERR, "getOrCreateRenderbuffer failed for {}", NFormatUtils::drmFormatName(fmt)); return false; @@ -72,8 +72,6 @@ bool CHyprGLRenderer::beginFullFakeRenderInternal(PHLMONITOR pMonitor, CRegion& } bool CHyprGLRenderer::beginRenderInternal(PHLMONITOR pMonitor, CRegion& damage, bool simple) { - - m_currentBuffer.fence = DRM::exportFence(m_currentBuffer.buffer->dmabuf().fds[0]); m_currentRenderbuffer->bind(); if (simple) g_pHyprOpenGL->beginSimple(pMonitor, damage, m_currentRenderbuffer); @@ -93,7 +91,7 @@ void CHyprGLRenderer::endRender(const std::function& renderingDoneCallba if (m_currentRenderbuffer) m_currentRenderbuffer->unbind(); m_currentRenderbuffer = nullptr; - m_currentBuffer = {}; + m_currentBuffer = nullptr; }); if (m_renderMode != RENDER_MODE_TO_BUFFER_READ_ONLY) @@ -108,7 +106,7 @@ void CHyprGLRenderer::endRender(const std::function& renderingDoneCallba return; if (m_renderMode == RENDER_MODE_NORMAL) - PMONITOR->m_output->state->setBuffer(m_currentBuffer.buffer); + PMONITOR->m_output->state->setBuffer(m_currentBuffer); if (!explicitSyncSupported()) { Log::logger->log(Log::TRACE, "renderer: Explicit sync unsupported, falling back to implicit in endRender"); diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index 0ecfd8d83..5f8cc614a 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -1655,17 +1655,17 @@ bool IHyprRenderer::beginRender(PHLMONITOR pMonitor, CRegion& damage, eRenderMod int bufferAge = 0; if (!buffer) { - m_currentBuffer.buffer = pMonitor->m_output->swapchain->next(&bufferAge); - if (!m_currentBuffer.buffer) { + m_currentBuffer = pMonitor->m_output->swapchain->next(&bufferAge); + if (!m_currentBuffer) { Log::logger->log(Log::ERR, "Failed to acquire swapchain buffer for {}", pMonitor->m_name); return false; } } else - m_currentBuffer.buffer = buffer; + m_currentBuffer = buffer; initRender(); - if (!initRenderBuffer(m_currentBuffer.buffer, pMonitor->m_output->state->state().drmFormat)) { + if (!initRenderBuffer(m_currentBuffer, pMonitor->m_output->state->state().drmFormat)) { Log::logger->log(Log::ERR, "failed to start a render pass for output {}, no RBO could be obtained", pMonitor->m_name); return false; } @@ -1916,6 +1916,8 @@ void IHyprRenderer::renderMonitor(PHLMONITOR pMonitor, bool commit) { const float ZOOMFACTOR = pMonitor->m_cursorZoom->value(); + auto cleanup = CScopeGuard([pMonitor]() { pMonitor->m_estimatedNextVblank = std::nullopt; }); + if (pMonitor->m_pixelSize.x < 1 || pMonitor->m_pixelSize.y < 1) { Log::logger->log(Log::ERR, "Refusing to render a monitor because of an invalid pixel size: {}", pMonitor->m_pixelSize); return; @@ -2350,15 +2352,13 @@ bool IHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor) { handleFullscreenSettings(pMonitor); static const auto DEADLINE = CConfigValue("experimental:deadline_main_buffer"); - - if (*DEADLINE) { + if (*DEADLINE && m_currentBuffer) { auto deadline = pMonitor->isMultiGPU() || pMonitor->m_vrrActive || pMonitor->m_tearingState.activelyTearing || !pMonitor->m_estimatedNextVblank.has_value() ? Time::steadyNow() + std::chrono::microseconds(100) : - pMonitor->m_estimatedNextVblank; - if (deadline) { - DRM::setDeadline(deadline.value(), m_currentBuffer.fence); - pMonitor->m_estimatedNextVblank = std::nullopt; - } + pMonitor->m_estimatedNextVblank.value(); + auto fence = DRM::exportFence(m_currentBuffer->dmabuf().fds[0]); + + DRM::setDeadline(deadline, fence); } bool ok = pMonitor->m_state.commit(); diff --git a/src/render/Renderer.hpp b/src/render/Renderer.hpp index 2a0579184..fff08a211 100644 --- a/src/render/Renderer.hpp +++ b/src/render/Renderer.hpp @@ -276,11 +276,7 @@ namespace Render { bool m_intel = false; bool m_software = false; bool m_mgpu = false; - - struct SBuffer { - SP buffer = nullptr; - Hyprutils::OS::CFileDescriptor fence; - } m_currentBuffer; + SP m_currentBuffer = nullptr; struct { bool hiddenOnTouch = false;