diff --git a/src/Compositor.cpp b/src/Compositor.cpp index 7b5e03247..c341ce93d 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -8,6 +8,7 @@ #include "helpers/Splashes.hpp" #include "config/ConfigValue.hpp" #include "config/ConfigWatcher.hpp" +#include "managers/BufferReleaseManager.hpp" #include "managers/CursorManager.hpp" #include "managers/TokenManager.hpp" #include "managers/PointerManager.hpp" @@ -661,6 +662,9 @@ void CCompositor::initManagers(eManagersInitStage stage) { Log::logger->log(Log::DEBUG, "Creating the SeatManager!"); g_pSeatManager = makeUnique(); + + Log::logger->log(Log::DEBUG, "Creating the CBufferReleaseManager!"); + g_pBufferReleaseManager = makeUnique(); } break; case STAGE_LATE: { Log::logger->log(Log::DEBUG, "Creating CHyprCtl"); diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp index 37e119089..d3ae1a1dd 100644 --- a/src/helpers/Monitor.cpp +++ b/src/helpers/Monitor.cpp @@ -26,6 +26,7 @@ #include "../managers/animation/AnimationManager.hpp" #include "../managers/animation/DesktopAnimationManager.hpp" #include "../managers/input/InputManager.hpp" +#include "../managers/BufferReleaseManager.hpp" #include "../hyprerror/HyprError.hpp" #include "../i18n/Engine.hpp" #include "sync/SyncTimeline.hpp" @@ -68,6 +69,9 @@ CMonitor::~CMonitor() { m_events.destroy.emit(); if (g_pHyprOpenGL) g_pHyprOpenGL->destroyMonitorResources(m_self); + + if (g_pBufferReleaseManager) + g_pBufferReleaseManager->destroy(m_self); } void CMonitor::onConnect(bool noRule) { diff --git a/src/managers/BufferReleaseManager.cpp b/src/managers/BufferReleaseManager.cpp new file mode 100644 index 000000000..bd72e5158 --- /dev/null +++ b/src/managers/BufferReleaseManager.cpp @@ -0,0 +1,87 @@ +#include "BufferReleaseManager.hpp" +#include "../helpers/Monitor.hpp" +#include "managers/eventLoop/EventLoopManager.hpp" +#include "render/OpenGL.hpp" + +#include + +using namespace Hyprutils::OS; + +#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 + +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); +} + +bool CBufferReleaseManager::addBuffer(PHLMONITORREF monitor, const CHLBufferReference& buf) { + if (!monitor) + return false; + + auto it = std::ranges::find_if(m_buffers[monitor], [&buf](auto& b) { return b == buf; }); + + if (it != m_buffers[monitor].end()) + return false; + + m_buffers[monitor].emplace_back(buf); + return true; +} + +void CBufferReleaseManager::addFence(PHLMONITORREF monitor) { + if (g_pHyprOpenGL->explicitSyncSupported() && monitor->m_inFence.isValid()) { + for (auto& b : m_buffers[monitor]) { + if (!b->m_syncReleasers.empty()) { + for (auto& releaser : b->m_syncReleasers) { + releaser->addSyncFileFd(monitor->m_inFence); + } + } else { + if (b->m_syncDropFd.isValid()) + b->m_syncDropFd = mergeSyncFds(b->m_syncDropFd, monitor->m_inFence.duplicate()); + else + b->m_syncDropFd = monitor->m_inFence.duplicate(); + } + } + } +} + +void CBufferReleaseManager::dropBuffers(PHLMONITORREF monitor) { + if (g_pHyprOpenGL->explicitSyncSupported()) { + std::erase_if(m_buffers[monitor], [](auto& b) { return !b->m_syncReleasers.empty(); }); + for (auto& b : m_buffers[monitor]) { + g_pEventLoopManager->doOnReadable(b->m_syncDropFd.duplicate(), [buf = b] mutable {}); + } + } + + m_buffers[monitor].clear(); + std::erase_if(m_buffers, [](auto& b) { return !b.first; }); // if monitor is gone. +} + +void CBufferReleaseManager::destroy(PHLMONITORREF monitor) { + m_buffers.erase(monitor); +} diff --git a/src/managers/BufferReleaseManager.hpp b/src/managers/BufferReleaseManager.hpp new file mode 100644 index 000000000..5963dd739 --- /dev/null +++ b/src/managers/BufferReleaseManager.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include "../defines.hpp" +#include "protocols/types/Buffer.hpp" + +class CBufferReleaseManager { + public: + CBufferReleaseManager() = default; + bool addBuffer(PHLMONITORREF monitor, const CHLBufferReference& buf); + void addFence(PHLMONITORREF monitor); + void dropBuffers(PHLMONITORREF monitor); + void destroy(PHLMONITORREF monitor); + + private: + std::unordered_map> m_buffers; +}; + +inline UP g_pBufferReleaseManager; diff --git a/src/protocols/types/Buffer.hpp b/src/protocols/types/Buffer.hpp index f85670ef8..c93de8c7e 100644 --- a/src/protocols/types/Buffer.hpp +++ b/src/protocols/types/Buffer.hpp @@ -31,6 +31,7 @@ class IHLBuffer : public Aquamarine::IBuffer { SP m_resource; std::vector> m_syncReleasers; Hyprutils::OS::CFileDescriptor m_syncFd; + Hyprutils::OS::CFileDescriptor m_syncDropFd; struct { CHyprSignalListener backendRelease; diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index 1ee65a0fb..f8df46261 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -16,6 +16,7 @@ #include "../desktop/view/LayerSurface.hpp" #include "../desktop/view/GlobalViewMethods.hpp" #include "../desktop/state/FocusState.hpp" +#include "../managers/BufferReleaseManager.hpp" #include "../protocols/SessionLock.hpp" #include "../protocols/LayerShell.hpp" #include "../protocols/XDGShell.hpp" @@ -1674,6 +1675,27 @@ bool CHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor) { pMonitor->m_previousFSWindow = FS_WINDOW; + if (!g_pHyprOpenGL->explicitSyncSupported()) { + static auto PNVIDIAANTIFLICKER = CConfigValue("opengl:nvidia_anti_flicker"); + Log::logger->log(Log::TRACE, "renderer: Explicit sync unsupported, falling back to implicit in endRender"); + + // nvidia doesn't have implicit sync, so we have to explicitly wait here, llvmpipe and other software renderer seems to bug out aswell. + if ((isNvidia() && *PNVIDIAANTIFLICKER) || isSoftware()) + glFinish(); + else + glFlush(); // mark an implicit sync point + + pMonitor->m_inFence.reset(); + } else { + if (pMonitor->m_inFence.isValid()) { + if (m_renderMode == RENDER_MODE_NORMAL) { + pMonitor->m_output->state->setExplicitInFence(pMonitor->m_inFence.get()); + } + } + } + + g_pBufferReleaseManager->dropBuffers(pMonitor); + bool ok = pMonitor->m_state.commit(); if (!ok) { if (pMonitor->m_inFence.isValid()) { @@ -2358,42 +2380,29 @@ void CHyprRenderer::endRender(const std::function& renderingDoneCallback else glFlush(); // mark an implicit sync point - m_usedAsyncBuffers.clear(); // release all buffer refs and hope implicit sync works if (renderingDoneCallback) renderingDoneCallback(); + } else { + UP eglSync = CEGLSync::create(); + if (eglSync && eglSync->isValid()) { + if (renderingDoneCallback) { + g_pEventLoopManager->doOnReadable(eglSync->fd().duplicate(), [renderingDoneCallback]() { + if (renderingDoneCallback) + renderingDoneCallback(); + }); + } - return; - } - - UP eglSync = CEGLSync::create(); - if (eglSync && eglSync->isValid()) { - for (auto const& buf : m_usedAsyncBuffers) { - for (const auto& releaser : buf->m_syncReleasers) { - releaser->addSyncFileFd(eglSync->fd()); + if (m_renderMode == RENDER_MODE_NORMAL) { + PMONITOR->m_inFence = eglSync->takeFd(); + g_pBufferReleaseManager->addFence(PMONITOR); + } + } else { + Log::logger->log(Log::ERR, "renderer: Explicit sync failed, calling renderingDoneCallback without sync"); + if (renderingDoneCallback) { + Log::logger->log(Log::ERR, "renderer: Explicit sync failed, calling renderingDoneCallback without sync"); + renderingDoneCallback(); } } - - // release buffer refs with release points now, since syncReleaser handles actual buffer release based on EGLSync - std::erase_if(m_usedAsyncBuffers, [](const auto& buf) { return !buf->m_syncReleasers.empty(); }); - - // release buffer refs without release points when EGLSync sync_file/fence is signalled - g_pEventLoopManager->doOnReadable(eglSync->fd().duplicate(), [renderingDoneCallback, prevbfs = std::move(m_usedAsyncBuffers)]() mutable { - prevbfs.clear(); - if (renderingDoneCallback) - renderingDoneCallback(); - }); - m_usedAsyncBuffers.clear(); - - if (m_renderMode == RENDER_MODE_NORMAL) { - PMONITOR->m_inFence = eglSync->takeFd(); - PMONITOR->m_output->state->setExplicitInFence(PMONITOR->m_inFence.get()); - } - } else { - Log::logger->log(Log::ERR, "renderer: Explicit sync failed, releasing resources"); - - m_usedAsyncBuffers.clear(); // release all buffer refs and hope implicit sync works - if (renderingDoneCallback) - renderingDoneCallback(); } } diff --git a/src/render/Renderer.hpp b/src/render/Renderer.hpp index f2377b3bf..69680a443 100644 --- a/src/render/Renderer.hpp +++ b/src/render/Renderer.hpp @@ -94,18 +94,16 @@ class CHyprRenderer { bool m_bBlockSurfaceFeedback = false; bool m_bRenderingSnapshot = false; - PHLMONITORREF m_mostHzMonitor; - bool m_directScanoutBlocked = false; + PHLMONITORREF m_mostHzMonitor; + bool m_directScanoutBlocked = false; - void setSurfaceScanoutMode(SP surface, PHLMONITOR monitor); // nullptr monitor resets - void initiateManualCrash(); + void setSurfaceScanoutMode(SP surface, PHLMONITOR monitor); // nullptr monitor resets + void initiateManualCrash(); - bool m_crashingInProgress = false; - float m_crashingDistort = 0.5f; - wl_event_source* m_crashingLoop = nullptr; - wl_event_source* m_cursorTicker = nullptr; - - std::vector m_usedAsyncBuffers; + bool m_crashingInProgress = false; + float m_crashingDistort = 0.5f; + wl_event_source* m_crashingLoop = nullptr; + wl_event_source* m_cursorTicker = nullptr; struct { int hotspotX = 0; diff --git a/src/render/pass/SurfacePassElement.cpp b/src/render/pass/SurfacePassElement.cpp index d30272905..d8d979125 100644 --- a/src/render/pass/SurfacePassElement.cpp +++ b/src/render/pass/SurfacePassElement.cpp @@ -5,7 +5,9 @@ #include "../../protocols/core/Compositor.hpp" #include "../../protocols/DRMSyncobj.hpp" #include "../../managers/input/InputManager.hpp" +#include "../../managers/BufferReleaseManager.hpp" #include "../Renderer.hpp" +#include "../../Compositor.hpp" #include #include @@ -154,8 +156,15 @@ void CSurfacePassElement::draw(const CRegion& damage) { // add async (dmabuf) buffers to usedBuffers so we can handle release later // sync (shm) buffers will be released in commitState, so no need to track them here - if (m_data.surface->m_current.buffer && !m_data.surface->m_current.buffer->isSynchronous()) - g_pHyprRenderer->m_usedAsyncBuffers.emplace_back(m_data.surface->m_current.buffer); + if (m_data.surface->m_current.buffer && !m_data.surface->m_current.buffer->isSynchronous()) { + for (auto& m : g_pCompositor->m_monitors) { + if (!m) + continue; + + if (m_data.pMonitor == m || (m_data.pWindow && m_data.pWindow->visibleOnMonitor(m))) + g_pBufferReleaseManager->addBuffer(m, m_data.surface->m_current.buffer); + } + } g_pHyprOpenGL->blend(true); }