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
This commit is contained in:
parent
5d005f11fa
commit
2ee5118d7a
14 changed files with 119 additions and 161 deletions
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -150,17 +150,20 @@ struct SCurrentRenderData {
|
|||
|
||||
class CEGLSync {
|
||||
public:
|
||||
static UP<CEGLSync> create();
|
||||
|
||||
~CEGLSync();
|
||||
|
||||
EGLSyncKHR sync = nullptr;
|
||||
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
UP<CEGLSync> eglSync = CEGLSync::create();
|
||||
if (eglSync && eglSync->isValid()) {
|
||||
for (auto const& buf : usedAsyncBuffers) {
|
||||
for (const auto& releaser : buf->syncReleasers) {
|
||||
releaser->addSyncFileFd(eglSync->fd());
|
||||
}
|
||||
}
|
||||
|
||||
// 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(); });
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue