renderer: store used buffers per monitor

instead of storing used async bufs in the renderer and dropping them on
the first monitor pageflip, store them per monitor and schedule drop on
the proper fence in the new CBufferReleaseManager.
This commit is contained in:
Tom Englund 2025-11-13 02:24:55 +01:00
parent 315806f598
commit fd68fd8937
8 changed files with 173 additions and 43 deletions

View file

@ -8,6 +8,7 @@
#include "helpers/Splashes.hpp" #include "helpers/Splashes.hpp"
#include "config/ConfigValue.hpp" #include "config/ConfigValue.hpp"
#include "config/ConfigWatcher.hpp" #include "config/ConfigWatcher.hpp"
#include "managers/BufferReleaseManager.hpp"
#include "managers/CursorManager.hpp" #include "managers/CursorManager.hpp"
#include "managers/TokenManager.hpp" #include "managers/TokenManager.hpp"
#include "managers/PointerManager.hpp" #include "managers/PointerManager.hpp"
@ -661,6 +662,9 @@ void CCompositor::initManagers(eManagersInitStage stage) {
Log::logger->log(Log::DEBUG, "Creating the SeatManager!"); Log::logger->log(Log::DEBUG, "Creating the SeatManager!");
g_pSeatManager = makeUnique<CSeatManager>(); g_pSeatManager = makeUnique<CSeatManager>();
Log::logger->log(Log::DEBUG, "Creating the CBufferReleaseManager!");
g_pBufferReleaseManager = makeUnique<CBufferReleaseManager>();
} break; } break;
case STAGE_LATE: { case STAGE_LATE: {
Log::logger->log(Log::DEBUG, "Creating CHyprCtl"); Log::logger->log(Log::DEBUG, "Creating CHyprCtl");

View file

@ -26,6 +26,7 @@
#include "../managers/animation/AnimationManager.hpp" #include "../managers/animation/AnimationManager.hpp"
#include "../managers/animation/DesktopAnimationManager.hpp" #include "../managers/animation/DesktopAnimationManager.hpp"
#include "../managers/input/InputManager.hpp" #include "../managers/input/InputManager.hpp"
#include "../managers/BufferReleaseManager.hpp"
#include "../hyprerror/HyprError.hpp" #include "../hyprerror/HyprError.hpp"
#include "../i18n/Engine.hpp" #include "../i18n/Engine.hpp"
#include "sync/SyncTimeline.hpp" #include "sync/SyncTimeline.hpp"
@ -68,6 +69,9 @@ CMonitor::~CMonitor() {
m_events.destroy.emit(); m_events.destroy.emit();
if (g_pHyprOpenGL) if (g_pHyprOpenGL)
g_pHyprOpenGL->destroyMonitorResources(m_self); g_pHyprOpenGL->destroyMonitorResources(m_self);
if (g_pBufferReleaseManager)
g_pBufferReleaseManager->destroy(m_self);
} }
void CMonitor::onConnect(bool noRule) { void CMonitor::onConnect(bool noRule) {

View file

@ -0,0 +1,87 @@
#include "BufferReleaseManager.hpp"
#include "../helpers/Monitor.hpp"
#include "managers/eventLoop/EventLoopManager.hpp"
#include "render/OpenGL.hpp"
#include <sys/ioctl.h>
using namespace Hyprutils::OS;
#if defined(__linux__)
#include <linux/sync_file.h>
#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);
}

View file

@ -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<PHLMONITORREF, std::vector<CHLBufferReference>> m_buffers;
};
inline UP<CBufferReleaseManager> g_pBufferReleaseManager;

View file

@ -31,6 +31,7 @@ class IHLBuffer : public Aquamarine::IBuffer {
SP<CWLBufferResource> m_resource; SP<CWLBufferResource> m_resource;
std::vector<UP<CSyncReleaser>> m_syncReleasers; std::vector<UP<CSyncReleaser>> m_syncReleasers;
Hyprutils::OS::CFileDescriptor m_syncFd; Hyprutils::OS::CFileDescriptor m_syncFd;
Hyprutils::OS::CFileDescriptor m_syncDropFd;
struct { struct {
CHyprSignalListener backendRelease; CHyprSignalListener backendRelease;

View file

@ -16,6 +16,7 @@
#include "../desktop/view/LayerSurface.hpp" #include "../desktop/view/LayerSurface.hpp"
#include "../desktop/view/GlobalViewMethods.hpp" #include "../desktop/view/GlobalViewMethods.hpp"
#include "../desktop/state/FocusState.hpp" #include "../desktop/state/FocusState.hpp"
#include "../managers/BufferReleaseManager.hpp"
#include "../protocols/SessionLock.hpp" #include "../protocols/SessionLock.hpp"
#include "../protocols/LayerShell.hpp" #include "../protocols/LayerShell.hpp"
#include "../protocols/XDGShell.hpp" #include "../protocols/XDGShell.hpp"
@ -1674,6 +1675,27 @@ bool CHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor) {
pMonitor->m_previousFSWindow = FS_WINDOW; pMonitor->m_previousFSWindow = FS_WINDOW;
if (!g_pHyprOpenGL->explicitSyncSupported()) {
static auto PNVIDIAANTIFLICKER = CConfigValue<Hyprlang::INT>("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(); bool ok = pMonitor->m_state.commit();
if (!ok) { if (!ok) {
if (pMonitor->m_inFence.isValid()) { if (pMonitor->m_inFence.isValid()) {
@ -2358,42 +2380,29 @@ void CHyprRenderer::endRender(const std::function<void()>& renderingDoneCallback
else else
glFlush(); // mark an implicit sync point glFlush(); // mark an implicit sync point
m_usedAsyncBuffers.clear(); // release all buffer refs and hope implicit sync works
if (renderingDoneCallback) if (renderingDoneCallback)
renderingDoneCallback(); renderingDoneCallback();
} else {
UP<CEGLSync> eglSync = CEGLSync::create();
if (eglSync && eglSync->isValid()) {
if (renderingDoneCallback) {
g_pEventLoopManager->doOnReadable(eglSync->fd().duplicate(), [renderingDoneCallback]() {
if (renderingDoneCallback)
renderingDoneCallback();
});
}
return; if (m_renderMode == RENDER_MODE_NORMAL) {
} PMONITOR->m_inFence = eglSync->takeFd();
g_pBufferReleaseManager->addFence(PMONITOR);
UP<CEGLSync> eglSync = CEGLSync::create(); }
if (eglSync && eglSync->isValid()) { } else {
for (auto const& buf : m_usedAsyncBuffers) { Log::logger->log(Log::ERR, "renderer: Explicit sync failed, calling renderingDoneCallback without sync");
for (const auto& releaser : buf->m_syncReleasers) { if (renderingDoneCallback) {
releaser->addSyncFileFd(eglSync->fd()); 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();
} }
} }

View file

@ -94,18 +94,16 @@ class CHyprRenderer {
bool m_bBlockSurfaceFeedback = false; bool m_bBlockSurfaceFeedback = false;
bool m_bRenderingSnapshot = false; bool m_bRenderingSnapshot = false;
PHLMONITORREF m_mostHzMonitor; PHLMONITORREF m_mostHzMonitor;
bool m_directScanoutBlocked = false; bool m_directScanoutBlocked = false;
void setSurfaceScanoutMode(SP<CWLSurfaceResource> surface, PHLMONITOR monitor); // nullptr monitor resets void setSurfaceScanoutMode(SP<CWLSurfaceResource> surface, PHLMONITOR monitor); // nullptr monitor resets
void initiateManualCrash(); void initiateManualCrash();
bool m_crashingInProgress = false; bool m_crashingInProgress = false;
float m_crashingDistort = 0.5f; float m_crashingDistort = 0.5f;
wl_event_source* m_crashingLoop = nullptr; wl_event_source* m_crashingLoop = nullptr;
wl_event_source* m_cursorTicker = nullptr; wl_event_source* m_cursorTicker = nullptr;
std::vector<CHLBufferReference> m_usedAsyncBuffers;
struct { struct {
int hotspotX = 0; int hotspotX = 0;

View file

@ -5,7 +5,9 @@
#include "../../protocols/core/Compositor.hpp" #include "../../protocols/core/Compositor.hpp"
#include "../../protocols/DRMSyncobj.hpp" #include "../../protocols/DRMSyncobj.hpp"
#include "../../managers/input/InputManager.hpp" #include "../../managers/input/InputManager.hpp"
#include "../../managers/BufferReleaseManager.hpp"
#include "../Renderer.hpp" #include "../Renderer.hpp"
#include "../../Compositor.hpp"
#include <hyprutils/math/Box.hpp> #include <hyprutils/math/Box.hpp>
#include <hyprutils/math/Vector2D.hpp> #include <hyprutils/math/Vector2D.hpp>
@ -154,8 +156,15 @@ void CSurfacePassElement::draw(const CRegion& damage) {
// add async (dmabuf) buffers to usedBuffers so we can handle release later // 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 // 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()) 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); 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); g_pHyprOpenGL->blend(true);
} }