diff options
| author | Ikalco <73481042+ikalco@users.noreply.github.com> | 2025-04-30 11:35:25 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-04-30 18:35:25 +0200 |
| commit | 2ee5118d7a4a59d3ccfaed305bfc05c79cea7637 (patch) | |
| tree | 7c113e64071edbc260060df7e60af9c915b3986a /src/render | |
| parent | 5d005f11fa218d26ed2cf6ce75cd9aac9c2c00e5 (diff) | |
render: properly release rendered buffers (#9807)
* cleanup eglSync
* properly release buffers in renderer
* add renderingDoneCallback and use it in screencopy
* use static constructor for CEGLSync
Diffstat (limited to 'src/render')
| -rw-r--r-- | src/render/OpenGL.cpp | 79 | ||||
| -rw-r--r-- | src/render/OpenGL.hpp | 12 | ||||
| -rw-r--r-- | src/render/Renderer.cpp | 70 | ||||
| -rw-r--r-- | src/render/Renderer.hpp | 22 | ||||
| -rw-r--r-- | src/render/pass/SurfacePassElement.cpp | 5 |
5 files changed, 86 insertions, 102 deletions
diff --git a/src/render/OpenGL.cpp b/src/render/OpenGL.cpp index a6cc176a..2a0cbe5a 100644 --- a/src/render/OpenGL.cpp +++ b/src/render/OpenGL.cpp @@ -3090,44 +3090,6 @@ std::vector<SDRMFormat> CHyprOpenGLImpl::getDRMFormats() { return drmFormats; } -SP<CEGLSync> CHyprOpenGLImpl::createEGLSync(int fenceFD) { - std::vector<EGLint> attribs; - CFileDescriptor dupFd; - if (fenceFD >= 0) { - dupFd = CFileDescriptor{fcntl(fenceFD, F_DUPFD_CLOEXEC, 0)}; - if (!dupFd.isValid()) { - Debug::log(ERR, "createEGLSync: dup failed"); - return nullptr; - } - // reserve number of elements to avoid reallocations - attribs.reserve(3); - attribs.push_back(EGL_SYNC_NATIVE_FENCE_FD_ANDROID); - attribs.push_back(dupFd.get()); - attribs.push_back(EGL_NONE); - } - - EGLSyncKHR sync = m_sProc.eglCreateSyncKHR(m_pEglDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs.data()); - if (sync == EGL_NO_SYNC_KHR) { - Debug::log(ERR, "eglCreateSyncKHR failed"); - return nullptr; - } else - dupFd.take(); // eglCreateSyncKHR only takes ownership on success - - // we need to flush otherwise we might not get a valid fd - glFlush(); - - int fd = g_pHyprOpenGL->m_sProc.eglDupNativeFenceFDANDROID(g_pHyprOpenGL->m_pEglDisplay, sync); - if (fd == EGL_NO_NATIVE_FENCE_FD_ANDROID) { - Debug::log(ERR, "eglDupNativeFenceFDANDROID failed"); - return nullptr; - } - - auto eglsync = SP<CEGLSync>(new CEGLSync); - eglsync->sync = sync; - eglsync->m_iFd = CFileDescriptor{fd}; - return eglsync; -} - void SRenderModifData::applyToBox(CBox& box) { if (!enabled) return; @@ -3189,18 +3151,47 @@ float SRenderModifData::combinedScale() { return scale; } +UP<CEGLSync> CEGLSync::create() { + EGLSyncKHR sync = g_pHyprOpenGL->m_sProc.eglCreateSyncKHR(g_pHyprOpenGL->m_pEglDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr); + + if (sync == EGL_NO_SYNC_KHR) { + Debug::log(ERR, "eglCreateSyncKHR failed"); + return nullptr; + } + + // we need to flush otherwise we might not get a valid fd + glFlush(); + + int fd = g_pHyprOpenGL->m_sProc.eglDupNativeFenceFDANDROID(g_pHyprOpenGL->m_pEglDisplay, sync); + if (fd == EGL_NO_NATIVE_FENCE_FD_ANDROID) { + Debug::log(ERR, "eglDupNativeFenceFDANDROID failed"); + return nullptr; + } + + UP<CEGLSync> eglSync(new CEGLSync); + eglSync->m_fd = CFileDescriptor(fd); + eglSync->m_sync = sync; + eglSync->m_valid = true; + + return eglSync; +} + CEGLSync::~CEGLSync() { - if (sync == EGL_NO_SYNC_KHR) + if (m_sync == EGL_NO_SYNC_KHR) return; - if (g_pHyprOpenGL && g_pHyprOpenGL->m_sProc.eglDestroySyncKHR(g_pHyprOpenGL->m_pEglDisplay, sync) != EGL_TRUE) + if (g_pHyprOpenGL && g_pHyprOpenGL->m_sProc.eglDestroySyncKHR(g_pHyprOpenGL->m_pEglDisplay, m_sync) != EGL_TRUE) Debug::log(ERR, "eglDestroySyncKHR failed"); } -CFileDescriptor&& CEGLSync::takeFD() { - return std::move(m_iFd); +CFileDescriptor& CEGLSync::fd() { + return m_fd; } -CFileDescriptor& CEGLSync::fd() { - return m_iFd; +CFileDescriptor&& CEGLSync::takeFd() { + return std::move(m_fd); +} + +bool CEGLSync::isValid() { + return m_valid && m_sync != EGL_NO_SYNC_KHR && m_fd.isValid(); } diff --git a/src/render/OpenGL.hpp b/src/render/OpenGL.hpp index 414ba103..21602d07 100644 --- a/src/render/OpenGL.hpp +++ b/src/render/OpenGL.hpp @@ -150,17 +150,20 @@ struct SCurrentRenderData { class CEGLSync { public: - ~CEGLSync(); + static UP<CEGLSync> create(); - EGLSyncKHR sync = nullptr; + ~CEGLSync(); - Hyprutils::OS::CFileDescriptor&& takeFD(); Hyprutils::OS::CFileDescriptor& fd(); + Hyprutils::OS::CFileDescriptor&& takeFd(); + bool isValid(); private: CEGLSync() = default; - Hyprutils::OS::CFileDescriptor m_iFd; + Hyprutils::OS::CFileDescriptor m_fd; + EGLSyncKHR m_sync = EGL_NO_SYNC_KHR; + bool m_valid = false; friend class CHyprOpenGLImpl; }; @@ -234,7 +237,6 @@ class CHyprOpenGLImpl { uint32_t getPreferredReadFormat(PHLMONITOR pMonitor); std::vector<SDRMFormat> getDRMFormats(); EGLImageKHR createEGLImage(const Aquamarine::SDMABUFAttrs& attrs); - SP<CEGLSync> createEGLSync(int fence = -1); bool initShaders(); bool m_bShadersInitialized = false; diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index 0db305af..93d3620f 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -1,7 +1,6 @@ #include "Renderer.hpp" #include "../Compositor.hpp" #include "../helpers/math/Math.hpp" -#include "../helpers/sync/SyncReleaser.hpp" #include <algorithm> #include <aquamarine/output/Output.hpp> #include <filesystem> @@ -1571,25 +1570,6 @@ bool CHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor) { } } - auto explicitOptions = getExplicitSyncSettings(pMonitor->output); - if (!explicitOptions.explicitEnabled) - return ok; - - Debug::log(TRACE, "Explicit: {} presented", explicitPresented.size()); - - if (!pMonitor->eglSync) - Debug::log(TRACE, "Explicit: can't add sync, monitor has no EGLSync"); - else { - for (auto const& e : explicitPresented) { - if (!e->current.buffer || !e->current.buffer->syncReleaser) - continue; - - e->current.buffer->syncReleaser->addReleaseSync(pMonitor->eglSync); - } - } - - explicitPresented.clear(); - return ok; } @@ -2275,7 +2255,7 @@ bool CHyprRenderer::beginRender(PHLMONITOR pMonitor, CRegion& damage, eRenderMod return true; } -void CHyprRenderer::endRender() { +void CHyprRenderer::endRender(const std::function<void()>& renderingDoneCallback) { const auto PMONITOR = g_pHyprOpenGL->m_RenderData.pMonitor; static auto PNVIDIAANTIFLICKER = CConfigValue<Hyprlang::INT>("opengl:nvidia_anti_flicker"); @@ -2296,42 +2276,48 @@ void CHyprRenderer::endRender() { g_pHyprOpenGL->m_RenderData.mouseZoomUseMouse = true; } + // send all queued opengl commands so rendering starts happening immediately + glFlush(); + if (m_eRenderMode == RENDER_MODE_FULL_FAKE) return; if (m_eRenderMode == RENDER_MODE_NORMAL) PMONITOR->output->state->setBuffer(m_pCurrentBuffer); - auto explicitOptions = getExplicitSyncSettings(PMONITOR->output); - - if (PMONITOR->inTimeline && explicitOptions.explicitEnabled) { - PMONITOR->eglSync = g_pHyprOpenGL->createEGLSync(); - if (!PMONITOR->eglSync) { - Debug::log(ERR, "renderer: couldn't create an EGLSync for out in endRender"); - return; + UP<CEGLSync> eglSync = CEGLSync::create(); + if (eglSync && eglSync->isValid()) { + for (auto const& buf : usedAsyncBuffers) { + for (const auto& releaser : buf->syncReleasers) { + releaser->addSyncFileFd(eglSync->fd()); + } } - PMONITOR->inTimelinePoint++; - bool ok = PMONITOR->inTimeline->importFromSyncFileFD(PMONITOR->inTimelinePoint, PMONITOR->eglSync->fd()); - if (!ok) { - Debug::log(ERR, "renderer: couldn't import from sync file fd in endRender"); - return; - } + // release buffer refs with release points now, since syncReleaser handles actual buffer release based on EGLSync + std::erase_if(usedAsyncBuffers, [](const auto& buf) { return !buf->syncReleasers.empty(); }); - if (m_eRenderMode == RENDER_MODE_NORMAL && explicitOptions.explicitKMSEnabled) { - PMONITOR->inFence = CFileDescriptor{PMONITOR->inTimeline->exportAsSyncFileFD(PMONITOR->inTimelinePoint)}; - if (!PMONITOR->inFence.isValid()) { - Debug::log(ERR, "renderer: couldn't export from sync timeline in endRender"); - return; - } + // release buffer refs without release points when EGLSync sync_file/fence is signalled + g_pEventLoopManager->doOnReadable(eglSync->fd().duplicate(), [renderingDoneCallback, prevbfs = std::move(usedAsyncBuffers)]() mutable { + prevbfs.clear(); + if (renderingDoneCallback) + renderingDoneCallback(); + }); + usedAsyncBuffers.clear(); + if (m_eRenderMode == RENDER_MODE_NORMAL) { + PMONITOR->inFence = eglSync->takeFd(); PMONITOR->output->state->setExplicitInFence(PMONITOR->inFence.get()); } } else { + Debug::log(ERR, "renderer: couldn't use EGLSync for explicit gpu synchronization"); + + // nvidia doesn't have implicit sync, so we have to explicitly wait here if (isNvidia() && *PNVIDIAANTIFLICKER) glFinish(); - else - glFlush(); + + 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 553965a4..37e1e91f 100644 --- a/src/render/Renderer.hpp +++ b/src/render/Renderer.hpp @@ -87,24 +87,24 @@ class CHyprRenderer { // if RENDER_MODE_NORMAL, provided damage will be written to. // otherwise, it will be the one used. bool beginRender(PHLMONITOR pMonitor, CRegion& damage, eRenderMode mode = RENDER_MODE_NORMAL, SP<IHLBuffer> buffer = {}, CFramebuffer* fb = nullptr, bool simple = false); - void endRender(); + void endRender(const std::function<void()>& renderingDoneCallback = {}); bool m_bBlockSurfaceFeedback = false; bool m_bRenderingSnapshot = false; - PHLMONITORREF m_pMostHzMonitor; - bool m_bDirectScanoutBlocked = false; + PHLMONITORREF m_pMostHzMonitor; + bool m_bDirectScanoutBlocked = false; - void setSurfaceScanoutMode(SP<CWLSurfaceResource> surface, PHLMONITOR monitor); // nullptr monitor resets - void initiateManualCrash(); + void setSurfaceScanoutMode(SP<CWLSurfaceResource> surface, PHLMONITOR monitor); // nullptr monitor resets + void initiateManualCrash(); - bool m_bCrashingInProgress = false; - float m_fCrashingDistort = 0.5f; - wl_event_source* m_pCrashingLoop = nullptr; - wl_event_source* m_pCursorTicker = nullptr; + bool m_bCrashingInProgress = false; + float m_fCrashingDistort = 0.5f; + wl_event_source* m_pCrashingLoop = nullptr; + wl_event_source* m_pCursorTicker = nullptr; - CTimer m_tRenderTimer; + CTimer m_tRenderTimer; - std::vector<SP<CWLSurfaceResource>> explicitPresented; + std::vector<CHLBufferReference> usedAsyncBuffers; struct { int hotspotX = 0; diff --git a/src/render/pass/SurfacePassElement.cpp b/src/render/pass/SurfacePassElement.cpp index c47c76ce..22e61a23 100644 --- a/src/render/pass/SurfacePassElement.cpp +++ b/src/render/pass/SurfacePassElement.cpp @@ -129,6 +129,11 @@ void CSurfacePassElement::draw(const CRegion& damage) { if (!g_pHyprRenderer->m_bBlockSurfaceFeedback) data.surface->presentFeedback(data.when, data.pMonitor->self.lock()); + // 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 (data.surface->current.buffer && !data.surface->current.buffer->isSynchronous()) + g_pHyprRenderer->usedAsyncBuffers.emplace_back(data.surface->current.buffer); + g_pHyprOpenGL->blend(true); } |
