mirror of
https://github.com/hyprwm/Hyprland
synced 2025-12-20 07:10:02 +01:00
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:
parent
315806f598
commit
fd68fd8937
8 changed files with 173 additions and 43 deletions
|
|
@ -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<CSeatManager>();
|
||||
|
||||
Log::logger->log(Log::DEBUG, "Creating the CBufferReleaseManager!");
|
||||
g_pBufferReleaseManager = makeUnique<CBufferReleaseManager>();
|
||||
} break;
|
||||
case STAGE_LATE: {
|
||||
Log::logger->log(Log::DEBUG, "Creating CHyprCtl");
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
87
src/managers/BufferReleaseManager.cpp
Normal file
87
src/managers/BufferReleaseManager.cpp
Normal 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);
|
||||
}
|
||||
18
src/managers/BufferReleaseManager.hpp
Normal file
18
src/managers/BufferReleaseManager.hpp
Normal 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;
|
||||
|
|
@ -31,6 +31,7 @@ class IHLBuffer : public Aquamarine::IBuffer {
|
|||
SP<CWLBufferResource> m_resource;
|
||||
std::vector<UP<CSyncReleaser>> m_syncReleasers;
|
||||
Hyprutils::OS::CFileDescriptor m_syncFd;
|
||||
Hyprutils::OS::CFileDescriptor m_syncDropFd;
|
||||
|
||||
struct {
|
||||
CHyprSignalListener backendRelease;
|
||||
|
|
|
|||
|
|
@ -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<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();
|
||||
if (!ok) {
|
||||
if (pMonitor->m_inFence.isValid()) {
|
||||
|
|
@ -2358,43 +2380,30 @@ void CHyprRenderer::endRender(const std::function<void()>& renderingDoneCallback
|
|||
else
|
||||
glFlush(); // mark an implicit sync point
|
||||
|
||||
m_usedAsyncBuffers.clear(); // release all buffer refs and hope implicit sync works
|
||||
if (renderingDoneCallback)
|
||||
renderingDoneCallback();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
} else {
|
||||
UP<CEGLSync> eglSync = CEGLSync::create();
|
||||
if (eglSync && eglSync->isValid()) {
|
||||
for (auto const& buf : m_usedAsyncBuffers) {
|
||||
for (const auto& releaser : buf->m_syncReleasers) {
|
||||
releaser->addSyncFileFd(eglSync->fd());
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
g_pEventLoopManager->doOnReadable(eglSync->fd().duplicate(), [renderingDoneCallback]() {
|
||||
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());
|
||||
g_pBufferReleaseManager->addFence(PMONITOR);
|
||||
}
|
||||
} 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)
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CHyprRenderer::onRenderbufferDestroy(CRenderbuffer* rb) {
|
||||
|
|
|
|||
|
|
@ -105,8 +105,6 @@ class CHyprRenderer {
|
|||
wl_event_source* m_crashingLoop = nullptr;
|
||||
wl_event_source* m_cursorTicker = nullptr;
|
||||
|
||||
std::vector<CHLBufferReference> m_usedAsyncBuffers;
|
||||
|
||||
struct {
|
||||
int hotspotX = 0;
|
||||
int hotspotY = 0;
|
||||
|
|
|
|||
|
|
@ -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 <hyprutils/math/Box.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
|
||||
// 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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue